Public feed #887686

Perlin stuff

public text v1 · immutable
Raw Edit
·published 2008-02-01 15:06 UTC
rendered paste body

(defmacro let-values (bindings &body body)
  "LET-like wrapper for MULTIPLE-VALUE-BIND."
  (let ((let-form nil))
    (dolist (binding bindings)
      (destructuring-bind (vals form) binding
	(if let-form
	    (setf let-form `(multiple-value-bind ,vals ,form ,let-form))
	  (setf let-form `(multiple-value-bind ,vals ,form ,@body)))))
    let-form))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Primitive functions
;;
;; We define primitive functions as functions that take a grt-vector and
;; possibly additional parameters and return a float from -1.0 to 1.0.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar +noise+
  (make-array 512
              :element-type '(unsigned-byte 8)
              :initial-contents
              (vector	       
               151 160 137 91 90 15 131 13 201 95 96 53 194 233 7 225
               140 36 103 30 69 142 8 99 37 240 21 10 23 190 6 148 247
               120 234 75 0 26 197 62 94 252 219 203 117 35 11 32 57
               177 33 88 237 149 56 87 174 20 125 136 171 168 68 175
               74 165 71 134 139 48 27 166 77 146 158 231 83 111 229
               122 60 211 133 230 220 105 92 41 55 46 245 40 244 102
               143 54 65 25 63 161 1 216 80 73 209 76 132 187 208 89
               18 169 200 196 135 130 116 188 159 86 164 100 109 198
               173 186 3 64 52 217 226 250 124 123 5 202 38 147 118
               126 255 82 85 212 207 206 59 227 47 16 58 17 182 189 28
               42 223 183 170 213 119 248 152 2 44 154 163 70 221 153
               101 155 167 43 172 9 129 22 39 253 19 98 108 110 79 113
               224 232 178 185 112 104 218 246 97 228 251 34 242 193
               238 210 144 12 191 179 162 241 81 51 145 235 249 14 239
               107 49 192 214 31 181 199 106 157 184 84 204 176 115
               121 50 45 127 4 150 254 138 236 205 93 222 114 67 29 24
               72 243 141 128 195 78 66 215 61 156 180 151 160 137 91
               90 15 131 13 201 95 96 53 194 233 7 225 140 36 103 30
               69 142 8 99 37 240 21 10 23 190 6 148 247 120 234 75 0
               26 197 62 94 252 219 203 117 35 11 32 57 177 33 88 237
               149 56 87 174 20 125 136 171 168 68 175 74 165 71 134
               139 48 27 166 77 146 158 231 83 111 229 122 60 211 133
               230 220 105 92 41 55 46 245 40 244 102 143 54 65 25 63
               161 1 216 80 73 209 76 132 187 208 89 18 169 200 196
               135 130 116 188 159 86 164 100 109 198 173 186 3 64 52
               217 226 250 124 123 5 202 38 147 118 126 255 82 85 212
               207 206 59 227 47 16 58 17 182 189 28 42 223 183 170
               213 119 248 152 2 44 154 163 70 221 153 101 155 167 43
               172 9 129 22 39 253 19 98 108 110 79 113 224 232 178
               185 112 104 218 246 97 228 251 34 242 193 238 210 144
               12 191 179 162 241 81 51 145 235 249 14 239 107 49 192
               214 31 181 199 106 157 184 84 204 176 115 121 50 45 127
               4 150 254 138 236 205 93 222 114 67 29 24 72 243 141
               128 195 78 66 215 61 156 180)))

(defun linear-interpolate (r f1 f2)
  (declare
   (inline linear-interpolate))
  "Linear interpolation between f1 and f2."
  (+ (* r (- f2 f1)) f1))

(defun noise2 (x y)
  "2-dimensional noise. Implementation based on Ken Perlin's paper
   'Improving Noise' (http://mrl.nyu.edu/~perlin/paper445.pdf)
   and his reference implementation (http://mrl.nyu.edu/~perlin/noise).
   An interesting property -- not a bug -- of this implementation is that
   noise *seems* to be 0.0 at each point on a unit-square lattice."
  (let-values (((cx x) (floor x))
               ((cy y) (floor y)))
              (let ((cx (logand cx 255))   
                    (cy (logand cy 255)))
                (declare (type (unsigned-byte 16) cx cy))
                (flet ((fade (e)
                         (+ (* 6 (expt e 5)) (* -15 (expt e 4)) (* 10 (expt e 3))))
                       (grad (hash x y)
                                        ; Low 2bits hash code
                                        ; into 4 grad.dirs
                         (let* ((h (logand hash 3)))                                                               
                           (+ (if (= 0 (logand h 1)) x (- x))
                              (if (= 0 (logand h 2)) y (- y))))))
                  (let ((u (fade x))                  ; Fade curves  for each main axis
                        (v (fade y)))                    
                    (let* ((A  (aref +noise+ cx)) ; Hash coordinates
                           (AA (+ (aref +noise+ A) cy)) ; for cube corners
                           (AB (+ (aref +noise+ (1+ A)) cy))
                           (B   (aref +noise+ (1+ cx)))
                           (BA (+ (aref +noise+ B) cy))
                           (BB (+ (aref +noise+ (1+ B)) cy)))
                      (linear-interpolate v ; Blend and add
                                          (linear-interpolate u ; results from
                                                              (grad (aref +noise+ AA) x y) ; corners
                                                              (grad (aref +noise+ BA) (- x 1) y))
                                          (linear-interpolate u
                                                              (grad (aref +noise+ AB) x (- y 1))
                                                              (grad (aref +noise+ BB) (- x 1) (- y 1))))))))))

(defun noise3 (x y z)
  "3-dimensional noise. Implementation based on Ken Perlin's paper
   'Improving Noise' (http://mrl.nyu.edu/~perlin/paper445.pdf)
   and his reference implementation (http://mrl.nyu.edu/~perlin/noise).
   An interesting property -- not a bug -- of this implementation is that
   noise *seems* to be 0.0 at each point on a unit-cube lattice."
  (let-values (((cx x) (floor x))
               ((cy y) (floor y))
               ((cz z) (floor z)))
              (let ((cx (logand cx 255))   
                    (cy (logand cy 255))   
                    (cz (logand cz 255)))
                (declare (type (unsigned-byte 16) cx cy cz))
                (flet ((fade (e)
                         (+ (* 6 (expt e 5)) (* -15 (expt e 4)) (* 10 (expt e 3))))
                       (grad (hash x y z)
                         (declare (type (unsigned-byte 16) hash))
                                        ; Low 4bits hash code
                                        ; into 12 grad.dirs
                         (let* ((h (logand hash 15))
                                (u (if (or (< h 8) (= h 12) (= h 13)) 
                                       x
                                       y))
                                (v (if (or (< h 4) (= h 12) (= h 13))
                                       y
                                       z)))
                           (+ (if (= 0 (logand h 1)) u (- u))
                              (if (= 0 (logand h 2)) v (- v))))))
                  (let ((u (fade x))                  ; Fade curves
                        (v (fade y))    ; for each main axis
                        (w (fade z)))                    
                    (let* ((A  (+ (aref +noise+ cx) cy)) ; Hash coordinates
                           (AA (+ (aref +noise+ A) cz)) ; for cube corners
                           (AB (+ (aref +noise+ (1+ A)) cz))
                           (B  (+ (aref +noise+ (1+ cx)) cy))
                           (BA (+ (aref +noise+ B) cz))
                           (BB (+ (aref +noise+ (1+ B)) cz)))
                      (linear-interpolate w
                                          (linear-interpolate v ; Blend and add
                                                              (linear-interpolate u ; results from
                                                                                  (grad (aref +noise+ AA) x y z) ; corners
                                                                                  (grad (aref +noise+ BA) (- x 1) y z))
                                                              (linear-interpolate u
                                                                                  (grad (aref +noise+ AB) x (- y 1) z)
                                                                                  (grad (aref +noise+ BB) (- x 1) (- y 1) z)))
                                          (linear-interpolate v
                                                              (linear-interpolate u
                                                                                  (grad (aref +noise+ (1+ AA)) x y (- z 1))
                                                                                  (grad (aref +noise+ (1+ BA)) (- x 1) y (- z 1)))
                                                              (linear-interpolate u
                                                                                  (grad (aref +noise+ (1+ AB)) x (- y 1) (- z 1))
                                                                                  (grad (aref +noise+ (1+ BB)) (- x 1) (- y 1) (- z 1))))))))))))

(defun turbulence (point lo hi)
"Turbulance function. Based on Ken Perlin's SIGGRAPH 92 course notes."
(let ((p point)
      (r 0.0))  
  (labels ((loop (freq)              
              (when (< freq hi)
                (incf r (/ (abs (noise3 p)) freq))
                (setq p (vector-mul p 2.0))
                (loop (* 2.0 freq)))))
    (loop lo)
    r)))


(defun perlin-noise-two (x y octaves persistence)
  (let ((total 0.0))
    (dotimes (i octaves total)
      (let ((freq (expt 2.0 i))
            (ampl (expt persistence i)))
        (incf total (* ampl (noise2 ( * x freq) (* y freq))))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Pattern functions
;;
;; We define pattern as a function that returns a float in range of
;; 0.0 to 1.0 when given a grt-vector.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


(defun noise (pos)
"Noise pattern. NOISE3 scaled to 0.0 - 1.0 range."
(/ (+ (noise3 pos) 1.0) 2.0))

(defun gradient (pos)
"Gradient pattern. Oriented along Y-axis."
(mod (y pos) 1))

(defun checker (point)
"Checker pattern with values 0.0 and 1.0."
(if (oddp (+ (floor (+ +grt-epsilon+ (x point)))
             (floor (+ +grt-epsilon+ (y point)))
             (floor (+ +grt-epsilon+ (z point)))))
    0.0
    1.0))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Pattern generators
;;
;; Functions returning pattern functions.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun make-solid (v)
"Retruns a solid pattern with the given value."
(declare (type grt-float v))
(lambda (pos)
  (declare (ignore pos))
  v))


(defun make-perlin-noise-two (octaves persistence)
  (lambda (x y)
    (/ (+ (perlin-noise-two x y octaves persistence) 1.0) 2.0)))

(defun make-turbulence (&key (low 0.25) (high 8.0))
"Returns a turbulence pattern function with the specified parameters."

(lambda (pos)  
  (mod (the (grt-float -1.0 1.0) (turbulence pos low high)) 1)))

(defclass height-map ()
  ((map-data :accessor points-of :initform (make-array (list 512 512) :element-type 'single-float :initial-element 0.0))))

(defmethod point-of ((map height-map) i j)
  (aref (points-of map) i j))

(defmethod (setf point-of) (value (map height-map) i j)
  (setf (aref (points-of map) i j) value))

(defun make-terrain (octaves)
  (let* ((persistence 0.5)
        (noise (make-perlin-noise-two octaves persistence))
        (terrain (make-instance 'height-map)))
    (loop 
       for i from 0 below 64
       do
       (loop 
          for j from 0 below 64
          do
            (setf (point-of terrain i j) 
                  (funcall noise 
                           (coerce (/  i 64) 'single-float)
                           (coerce (/  j 64) 'single-float)))))
    terrain))

(defmethod draw ((map height-map) stream)
  (loop
       for i from 0 below 64
       do
       (progn 
         (format stream "~%")
         (loop
            for j from 0 below 64
            do
              (let* ((point (point-of map i j))
                    (point-char 
                     (cond 
                       ((< point 0.3) #\~)
                       ((> point 0.7) #\^)
                       (t #\.))))
                (format stream "~C" point-char))))))