the code is embedded in the url

https://squint-cljs.github.io/squint/?src=gzip%3AH4sIAAAAAAAAE81Z3XLbNha%2B91OcdW%2FIZEmTsiXb8qZtGie7nm1n22SyvfDkAhJBEREFcknIIp3JTB%2Bi77L3%2Byh9kh38EABJyFKvEjVTS8T5zjk4%2FwC9BKewQWVQk0cM03jin3jdow2q1zCNY%2F%2Fk5OYG%2Fvj9tz9%2B%2Fw1%2BroolTrYVyoHhqkKEqpUv9e9EKEw7bYIMk1XG4L6B9sMJgJdjBve0Ae8MGr1T%2FwTUh7Z8pXWslAQ%2B1mc%2FIZaFP99xVgDec%2FCewXkEXrdSE8of0QbOoSS%2Br%2BGcbjqma2ESXo4oJ1P%2Bf028LGrFdCpIXWwuQiXQ5hNfjUi955wPbX2YjcTGfbFmL9cDsZ1CLVw5hEZ7uMQXe7WPHcrPYI8RJvFedeLrAZ%2BrKf%2BhQkKGQvDtssiLCu4zEw8VeMuCJuD9DTI4n%2FoAk6l2fPcRi9PIB7iI3Iszvhhf7VmNo8jn9j%2BP5M4CjQgnRuMB5mIqMJdXBiP4ROFsL%2BZSYuIoMhjOJw7jfZhJLHWLLywM5xOHUwdmjvMai2%2BTSWSWVyMzmu1mMHGyMlbl4uNLLZ7jn4DMOsh0qiGcjVvhngMuBw6Iw6tD9o8vJkP7nx%2B2v9FM8InCy4P2nwzsHzl1M%2Fb3nsPE8rPgI3QzoIXLKyo4Mjg%2FwiuX0Z%2F1ypVl4iO9MkyLOIwOecWGHOuVCxsjvTI95JWZjZFemR30Svy0V5Y52pTgpRTuHz6AtyEUJjyUCWXw4Pu%2B6jALwoKikn%2FrjKQsyHHKBOnkoq%2FCkMaTIhY%2BxLOjKFc%2BXA0I5UIlSqqcBGQR3aBS1N%2F3hLKrl1WF2pDvVk8Oun36aoAQNZejNOx8cgB34iUFIxtcw73px6Jm6%2BemgytziWqe8W8b1EA0sKs3GAoaaO1mAQAkaVRXt0YAaJQ3ADxUY2bZgNNnfm9R71Tw6vecjIvrzU%2B3%2F%2FoJasy25Rcem56eqIRZl4g%2BoBq8cFlhxPDrHG8wZXwoSorlVnw%2FlTSnwl5ejdlfwAsDkoB87sPpQ9HgPOjIbKqatTkOy6ImjBTUIFLS4MRFyorSUEUuChHcT5M83tEEN4bo%2Bvr62kWXkLrMUWsIF3mxXEvKEJUlpsmrjOQJRyyKpLXN4ncovzMloWnxtCET8jC2IodpG%2FIfDk0lkYjI086Yc2HDG1aU8zgqmxtuF%2FlNhOX8mzRNbxZouV5VxZYm82q1QF70V%2FFfOPNvSpQkhK7ms7IBAVsUVYKroEIJ2dbzi7K5SQvK5vGkbGBT0KIu0RLfPAaEG3ceR1EU3ZQFoQxXAX7AlNVzWlDcV5%2Fhhr0qKOP7Vzv99eW7W3gBm%2BIBw%2F%2F%2BC7%2BcvYYXwLYV5b%2Fenr2BFyoXj3WE4NtLwHfLCmMKi22a4urrSkJVcWuhYZAghvgmESs28GmuspEfWUhuKth8yRr1tf%2BcbFaCxfD5YpteuegX2%2FR84ni%2B01%2BtMXeemaefzcBdYV4%2FA7kBuDfzti4lK8xU%2BP%2FQ3iX9FBiVCvlRCvCMoBRXv5KEZRy4IzQpdoYu69P9Q5Z8Q6g6Rhd9O8GnS2%2FFR6%2BqhtEtq5KvNtNZXGxHRHDTkcLpRNYu3WA6N3TZf7dBK3zLn3A%2BO9NN%2BGexTfUuVIR6ocR3jLpJAcCrsFC3Hy%2F2x4oa%2BWU4wpjoWbJmtKh1776MKHQwDWeDxTYdzUsmxEYjgZNcR95utKTDL%2FusRpVe6PknXoiS5DWvPD%2BSmmGKKxMJcCqJT%2Fvx2qsS%2F%2BaxCKKsQYVpgqsvWCy69EoqtAsecMXIEuVBTiiGe2lTFQI7PuHwNtkuCsbLxrLIRRLuMkz5iGsv2iHdMj1A8bmJk%2FXGpHahFhRUh2BeFCXcrxUV6zuqSFM%2BIKn5imktfcmsMaFsqbiGdmGLlkOW3KZiuCzyHkWFl9uK81zCWszvik7Lkx9Tpbg%2FA1lupIvvl2gT8KlgE%2FCWvwkyQHSVY8iKijwW1FSyT%2FM1bmtZBHR%2BiDSQKu4g%2B%2FAZvnflZE1omREV%2FvpCRMixjglFPSDitx0DonqJchxkAJOLKDRlOSE1Q3SJ4SqyH9frNpDjaNS8eTO7uJ7dvlLFMExJnivNNZkdGBlJEszvyD7WZ3dWwnbl1B7OCewsf8rBWcEJn4T7MfMof8WWonITOGfokT93BMej3mMvAGRZLmrJ03vWWfFxfGarCdVUyiEOqpJPTEEjb%2FwC%2Fo9z9wWaT3eboNkHajuQkCRhIq4cgEqcFeQJRIo4IEECWqOWIn9KStLNB94ZR2ihao%2B69%2FUwrQvTdlt0Ygh90Jb1zrgDgVtMhqqV6U8Eje3OWqrtPdfO8J4BgaRxHMT5p2779K2ib%2FfQbxpzNEb8toSXpLrx9dX3HljrgrUHYajk%2BeqhVe84KUvjxj57bvZtsJXDgWLRJZabNhODomLPQ0uUNKEEn4kfHv2usg0c08u2rBZCXSI8Ry%2FqCiDpgE7VpP760LzHAk6Zw5JSy7I%2B7gTPeaEQdUS0A1VSojCK%2BPWwLkSh6E7D%2BUpUc0NTbll%2FZNNVP4JoeLp%2FhTa4Ql%2FXqeKpo8ZS6HsC8M3HGj7Nu0oBk%2BnM1OV5636sZmSAyyvrqeyb%2FGAQ6iv1uQo24Ld2YfS5u1sitNwyLT0tqh2qkh%2FQcs3%2Fcn9pBjyl3wpx9tNteVvsaJ%2BwKNbvS0hRXuPeQ0EpHmvxOapZkFZog7tjlhfSYscnxVvEzF0WomSDGKGr7xSZYNP3%2FN85F9HZvrhvD42R2zJBDAfS9%2FYpje%2B9bwEzWrDuRpQvf28sx%2B85o%2FPucCUrBy3YC55XxmnC0T7oVwf6nCWjRapipPGiMFgT0l0swxgSprN6oMEwpEZ6iH2L6wbJfw8gYfao2infjBQH0ZatFVHcBPveS7DB5nplTLNv97Jv97MX78Yc7N32UQm01z36GOzyT39RGbDPceyZMFAZKilGAlWh2COxvypene6VYelxtJTgKCkyaFzzg%2B1690jgnB9sl7ph8qLu2PlBz%2BbSHt9yOsVBvOEY%2Bs4RfEPn2gy0IdQFhFVHacFPW12lWaEN5r4oVZXpVR5xgzc%2Bi6mDiZ1CvYB3BuW4WozcKMSRFDz%2BduXY%2BiB4Hy5nPbI9OaXUdEW%2FtWRB1c4%2B1mcV%2Fs8W1%2BylbEMFfSNMrY3bd4bVrFSb6vWpO8776%2B1Rrn6VIZrkOFjjFu7LCtc1Tr4D%2BWJIHfwSzO0n%2Flo96wHlYF64kRQ0%2BIFPbqpl8VelXbrw0HgBgs%2FpP3H766lvfr%2BsqmL3vjw142OXKnuCyOPyz8VLzT383434c%2Ff%2FSQnBkyJejkT8iFPmEDEKbsE8for37Yi3YHA086DP3eL8lnPuwP18EsiJ9arYxr05gAv2AV%2FbwF6K6qBxwX4ZwmyJGmj3pu6hOV5U4v3ILU7RNmcmfAV5QAsG35uMNmV6nOys2lqx7yjNg9nWkB5RYdT1Wei6VDUX%2BGvcJjyAwStRxQjK7dQV%2Bu27mO3x2JanXC0HD3vuvqNELIvN8ftfRyP5c23kmCbyf4cliyYwJwAA

  • ☆ Yσɠƚԋσʂ ☆@lemmy.mlOPM
    link
    fedilink
    arrow-up
    1
    ·
    edit-2
    5 months ago
    (def map-size 512)
    (def map-mask 511)
    
    ;; ── Procedural terrain ──────────────────────────────────────────
    
    (defn terrain-height [x y]
      (let [nx (/ x map-size)
            ny (/ y map-size)
            pi js/Math.PI]
        (+ (* 30 (js/Math.sin (* nx 3 pi)))
           (* 35 (js/Math.sin (* ny 2.7 pi)))
           (* 25 (* (js/Math.cos (* nx 5 pi)) (js/Math.sin (* ny 4.3 pi))))
           (* 18 (js/Math.sin (* (+ nx ny) 6 pi)))
           (* 15 (* (js/Math.sin (* nx 9 pi)) (js/Math.cos (* ny 8 pi))))
           (* 10 (* (js/Math.sin (* nx 14 pi)) (js/Math.sin (* ny 13 pi))))
           (* 6  (* (js/Math.cos (* nx 21 pi)) (js/Math.cos (* ny 19 pi))))
           85)))
    
    (defn height->color [h]
      (let [r (cond (< h 35)  25
                    (< h 50)  40
                    (< h 60)  180
                    (< h 100) (+ 30  (* (- h 60)  1.2))
                    (< h 145) (+ 78  (* (- h 100) 0.6))
                    (< h 175) (+ 100 (* (- h 145) 1.1))
                    (< h 210) (+ 140 (* (- h 175) 1.5))
                    :else     220)
            g (cond (< h 35)  (+ 30  (* h 2.5))
                    (< h 50)  (+ 117 (* (- h 35) 2.5))
                    (< h 60)  (+ 155 (* (- h 50) 1.5))
                    (< h 100) (+ 70  (* (- h 60) 1.8))
                    (< h 145) (+ 142 (* (- h 100) 0.3))
                    (< h 175) (+ 105 (* (- h 145) 0.7))
                    (< h 210) (+ 120 (* (- h 175) 0.8))
                    :else     (+ 200 (* (- h 210) 0.3)))
            b (cond (< h 35)  (+ 100 (* h 3.5))
                    (< h 50)  (+ 170 (* (- h 35) 2.5))
                    (< h 60)  (+ 80  (* (- h 50) 1.5))
                    (< h 100) (+ 30  (* (- h 60) 1.0))
                    (< h 145) (+ 30  (* (- h 100) 0.3))
                    (< h 175) (+ 40  (* (- h 145) 0.5))
                    (< h 210) (+ 60  (* (- h 175) 0.6))
                    :else     (+ 210 (* (- h 210) 0.3)))
            clamp (fn [v] (min 255 (int v)))]
        (bit-or (bit-shift-left 255 24)
                (bit-shift-left (clamp b) 16)
                (bit-shift-left (clamp g) 8)
                (clamp r))))
    
    (def heightmap (js/Uint8Array. (* map-size map-size)))
    (def colormap  (js/Uint32Array. (* map-size map-size)))
    
    (dotimes [y map-size]
      (dotimes [x map-size]
        (let [h   (max 0 (min 255 (int (terrain-height x y))))
              idx (+ (* y map-size) x)]
          (aset heightmap idx h)
          (aset colormap idx (height->color h)))))
    
    ;; ── DOM setup ───────────────────────────────────────────────────
    
    (let [canvas (.createElement js/document "canvas")]
      (set! (.-id canvas) "voxel-canvas")
      (set! (.-style.position canvas) "fixed")
      (set! (.-style.top canvas) "0")
      (set! (.-style.left canvas) "0")
      (set! (.-style.zIndex canvas) "9999")
      (set! (.-style.display canvas) "block")
      (.appendChild (.-body js/document) canvas))
    
    (let [info (.createElement js/document "div")]
      (set! (.-id info) "voxel-info")
      (set! (.-style info)
        "position:fixed;top:10px;left:10px;color:#fff;background:rgba(0,0,0,0.6);padding:6px 10px;border-radius:4px;font:12px monospace;z-index:10000;pointer-events:none")
      (set! (.-textContent info) "WASD = move · Q/E = turn · R/F = height")
      (.appendChild (.-body js/document) info))
    
    ;; ── Screen buffer ───────────────────────────────────────────────
    
    (def screen-data
      (atom {:canvas    nil
             :ctx       nil
             :img-data  nil
             :buf8      nil
             :buf32     nil
             :w         0
             :h         0}))
    
    (defn resize-screen []
      (let [canvas (.getElementById js/document "voxel-canvas")
            w      (.-innerWidth js/window)
            h      (.-innerHeight js/window)]
        (set! (.-width canvas) w)
        (set! (.-height canvas) h)
        (let [ctx      (.getContext canvas "2d")
              img-data (.createImageData ctx w h)
              buf      (.-buffer (.-data img-data))]
          (reset! screen-data
                  {:canvas   canvas
                   :ctx      ctx
                   :img-data img-data
                   :buf8     (js/Uint8Array. buf)
                   :buf32    (js/Uint32Array. buf)
                   :w        w
                   :h        h}))))
    
    (resize-screen)
    (.addEventListener js/window "resize" resize-screen)
    
    ;; ── Voxel space renderer ────────────────────────────────────────
    
    (defn draw-vertical-line [buf32 screen-w x ytop ybottom col]
      (when (< ytop ybottom)
        (let [yt (max 0 (int ytop))
              yb (int ybottom)]
          (loop [k      yt
                 offset (+ (* yt screen-w) (int x))]
            (when (< k yb)
              (aset buf32 offset col)
              (recur (inc k) (+ offset screen-w)))))))
    
    (defn render-voxel-space [cam-x cam-y cam-h angle horizon]
      (let [{:keys [ctx img-data buf8 buf32 w h]} @screen-data
            sinphi   (js/Math.sin angle)
            cosphi   (js/Math.cos angle)
            scale-h  240.0
            distance 800.0
            sky-color 0xFF6496DC]
        (.fill buf32 sky-color)
        (let [hiddeny (js/Int32Array. w)]
          (dotimes [i w]
            (aset hiddeny i h))
          (loop [z      1.0
                 deltaz 1.0]
            (when (< z distance)
              (let [cosz     (* cosphi z)
                    sinz     (* sinphi z)
                    pleft-x  (+ (- (- cosz) sinz) cam-x)
                    pleft-y  (+ (- sinz cosz) cam-y)
                    pright-x (+ (- cosz sinz) cam-x)
                    pright-y (+ (- (- sinz) cosz) cam-y)
                    dx       (/ (- pright-x pleft-x) w)
                    dy       (/ (- pright-y pleft-y) w)
                    invz     (* (/ 1.0 z) scale-h)]
                (dotimes [i w]
                  (let [sx     (+ pleft-x (* i dx))
                        sy     (+ pleft-y (* i dy))
                        mx     (bit-and (int sx) map-mask)
                        my     (bit-and (int sy) map-mask)
                        map-h  (aget heightmap (+ (* my map-size) mx))
                        ybuf   (aget hiddeny i)
                        hs     (+ (* (- cam-h map-h) invz) horizon)]
                    (when (< hs ybuf)
                      (draw-vertical-line buf32 w i hs ybuf
                        (aget colormap (+ (* my map-size) mx)))
                      (aset hiddeny i hs)))))
              (recur (+ z deltaz) (+ deltaz 0.005))))
          (.set (.-data img-data) buf8)
          (.putImageData ctx img-data 0 0))))
    
    ;; ── Camera ──────────────────────────────────────────────────────
    
    (def camera
      #js {:x        256.0
           :y        256.0
           :height   78.0
           :angle    0.5
           :horizon  100.0})
    
    (def input
      #js {:forwardBackward 0.0
           :leftRight 0.0
           :upDown 0.0
           :lookUp false
           :lookDown false})
    
    (def last-frame  (atom (.now js/Date)))
    (def animating? (atom false))
    
    ;; ── Game loop ───────────────────────────────────────────────────
    
    (defn update-camera []
      (let [now (.now js/Date)
            dt  (* (- now @last-frame) 0.03)]
        (when (not= (.-leftRight input) 0)
          (set! (.-angle camera)
            (+ (.-angle camera) (* (.-leftRight input) 0.1 dt))))
        (when (not= (.-forwardBackward input) 0)
          (let [move (* (.-forwardBackward input) dt)]
            (set! (.-x camera)
              (- (.-x camera) (* move (js/Math.sin (.-angle camera)))))
            (set! (.-y camera)
              (- (.-y camera) (* move (js/Math.cos (.-angle camera)))))))
        (when (not= (.-upDown input) 0)
          (set! (.-height camera)
            (+ (.-height camera) (* (.-upDown input) dt))))
        (when (.-lookUp input)
          (set! (.-horizon camera)
            (+ (.-horizon camera) (* 2 dt))))
        (when (.-lookDown input)
          (set! (.-horizon camera)
            (- (.-horizon camera) (* 2 dt))))
        (let [mx     (bit-and (int (.-x camera)) map-mask)
              my     (bit-and (int (.-y camera)) map-mask)
              ground (aget heightmap (+ (* my map-size) mx))]
          (when (> (+ ground 10) (.-height camera))
            (set! (.-height camera) (+ ground 10))))
        (reset! last-frame now)))
    
    (defn game-loop []
      (update-camera)
      (render-voxel-space
        (.-x camera) (.-y camera) (.-height camera)
        (.-angle camera) (.-horizon camera))
      (if (or (not= (.-forwardBackward input) 0)
              (not= (.-leftRight input) 0)
              (not= (.-upDown input) 0)
              (.-lookUp input)
              (.-lookDown input))
        (js/requestAnimationFrame game-loop)
        (reset! animating? false)))
    
    ;; ── Input ───────────────────────────────────────────────────────
    
    (defn handle-key [pressed? e]
      (let [code (.-code e)
            val  (fn [v] (if pressed? v 0))]
        (cond
          (or (= code "KeyW") (= code "ArrowUp"))
          (set! (.-forwardBackward input) (val 3.0))
    
          (or (= code "KeyS") (= code "ArrowDown"))
          (set! (.-forwardBackward input) (val -3.0))
    
          (or (= code "KeyA") (= code "ArrowLeft"))
          (set! (.-leftRight input) (val 1.0))
    
          (or (= code "KeyD") (= code "ArrowRight"))
          (set! (.-leftRight input) (val -1.0))
    
          (= code "KeyR") (set! (.-upDown input) (val 2.0))
          (= code "KeyF") (set! (.-upDown input) (val -2.0))
          (= code "KeyE") (set! (.-lookUp input) pressed?)
          (= code "KeyQ") (set! (.-lookDown input) pressed?))
        (when pressed?
          (.preventDefault e)
          (when-not @animating?
            (reset! animating? true)
            (reset! last-frame (.now js/Date))
            (js/requestAnimationFrame game-loop)))))
    
    (.addEventListener js/document "keydown" (partial handle-key true))
    (.addEventListener js/document "keyup"   (partial handle-key false))
    
    ;; Initial frame
    (render-voxel-space
      (.-x camera) (.-y camera) (.-height camera)
      (.-angle camera) (.-horizon camera))