Skip to content

Instantly share code, notes, and snippets.

@binji
Created October 20, 2019 21:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save binji/fea1d2e81e9c32c2829b156252cce43a to your computer and use it in GitHub Desktop.
Save binji/fea1d2e81e9c32c2829b156252cce43a to your computer and use it in GitHub Desktop.
(import "Math" "sin" (func $sin (param f32) (result f32)))
(memory (export "mem") 6)
(data (i32.const 0)
;; top wall
"\00\00\40\c1" ;; -12.0
"\00\00\40\41" ;; +12.0
"\00\00\40\41" ;; +12.0
"\00\00\40\41" ;; +12.0
"\00\00\40\41" ;; scale=+12
;; right wall
"\00\00\40\41" ;; +12.0
"\00\00\40\41" ;; +12.0
"\00\00\40\41" ;; +12.0
"\00\00\40\c1" ;; -12.0
"\00\00\40\41" ;; scale=12
;; bottom wall
"\00\00\40\41" ;; +12.0
"\00\00\40\c1" ;; -12.0
"\00\00\40\c1" ;; -12.0
"\00\00\40\c1" ;; -12.0
"\00\00\40\41" ;; scale=12
;; left wall
"\00\00\40\c1" ;; -12.0
"\00\00\40\c1" ;; -12.0
"\00\00\40\c1" ;; -12.0
"\00\00\40\41" ;; +12.0
"\00\00\40\41" ;; scale=12
)
;; Position and direction vectors. Direction is updated from angle, which is
;; expressed in radians.
(global $Px (mut f32) (f32.const 0))
(global $Py (mut f32) (f32.const 8))
(global $angle (mut f32) (f32.const 0.7853981633974483))
(global $t2 (mut f32) (f32.const 0))
(func (export "init"))
(func $fmod (param $x f32) (param $y f32) (result f32)
(f32.sub
(local.get $x)
(f32.mul
(f32.trunc
(f32.div
(local.get $x)
(local.get $y)))
(local.get $y))))
;; Ray/line segment intersection. see https://rootllama.wordpress.com/2014/06/20/ray-line-segment-intersection-test-in-2d/
;;
;; ray is defined by [Px,Py] -> [Dx,Dy].
;; line segment is [sx,sy] -> [ex,ey].
;;
;; Returns distance to the segment, or inf if it doesn't hit.
(func $ray-line
(param $Dx f32) (param $Dy f32)
(param $sx f32) (param $sy f32) (param $ex f32) (param $ey f32)
(result f32)
(local $v1x f32)
(local $v1y f32)
(local $v2x f32)
(local $v2y f32)
(local $v3x f32)
(local $v3y f32)
(local $inv-v2.v3 f32)
(local $t1 f32)
(local $t2 f32)
;; v1 = P - s
(local.set $v1x (f32.sub (global.get $Px) (local.get $sx)))
(local.set $v1y (f32.sub (global.get $Py) (local.get $sy)))
;; v2 = e - s
(local.set $v2x (f32.sub (local.get $ex) (local.get $sx)))
(local.set $v2y (f32.sub (local.get $ey) (local.get $sy)))
;; v3 = (-Dy, Dx)
(local.set $v3x (f32.neg (local.get $Dy)))
(local.set $v3y (local.get $Dx))
(local.set $inv-v2.v3
(f32.div
(f32.const 1)
(f32.add
(f32.mul (local.get $v2x) (local.get $v3x))
(f32.mul (local.get $v2y) (local.get $v3y)))))
;; t2 is intersection "time" between s and e.
(local.set $t2
(f32.mul
(f32.add
(f32.mul (local.get $v1x) (local.get $v3x))
(f32.mul (local.get $v1y) (local.get $v3y)))
(local.get $inv-v2.v3)))
;; t2 must be between [0, 1].
(if
(i32.and
(f32.ge (local.get $t2) (f32.const 0))
(f32.le (local.get $t2) (f32.const 1)))
(then
;; t1 is distance along ray.
(local.set $t1
(f32.mul
(f32.sub
(f32.mul (local.get $v2x) (local.get $v1y))
(f32.mul (local.get $v1x) (local.get $v2y)))
(local.get $inv-v2.v3)))
(if (f32.ge (local.get $t1) (f32.const 0))
(then
;; return intersection time as global.
(return (local.get $t1))))))
(f32.const inf))
(func (export "run")
(local $x i32)
(local $y i32)
(local $wall i32)
(local $xproj f32)
(local $Dx f32)
(local $Dy f32)
(local $mindist f32)
(local $mint2 f32)
(local $dist f32)
(local $height i32)
(local $miny i32)
(local $maxy i32)
(local $addr i32)
(local $bot-addr i32)
(local $rotate f32)
(local $ray-x f32)
(local $ray-y f32)
;; rotate
(local.set $rotate
(f32.mul
(f32.convert_i32_s
(i32.sub
(i32.load8_u (i32.const 0x100))
(i32.load8_u (i32.const 0x101))))
(f32.const 0.08)))
(global.set $angle
(call $fmod (f32.add (global.get $angle) (local.get $rotate))
(f32.const 6.283185307179586)))
(local.set $Dx (call $sin (global.get $angle)))
(local.set $Dy (call $sin (f32.add (global.get $angle) (f32.const 1.5707963267948966))))
;; move forward
(if (i32.load8_u (i32.const 0x102))
(then
(global.set $Px
(f32.add (global.get $Px) (f32.mul (local.get $Dx) (f32.const 0.10))))
(global.set $Py
(f32.add (global.get $Py) (f32.mul (local.get $Dy) (f32.const 0.10))))))
;; Loop for each column.
(loop $x-loop
(local.set $xproj
(f32.sub
(f32.div
(f32.convert_i32_s (local.get $x))
(f32.const 160))
(f32.const 1)))
;; for each wall
(local.set $mindist (f32.const inf))
(local.set $wall (i32.const 0))
(loop $wall-loop
;; Shoot a ray against a wall. Use rays projected onto screen plane.
;; choose the shortest distance.
(local.set $ray-x
(f32.add (local.get $Dx) (f32.mul (local.get $xproj) (f32.neg (local.get $Dy)))))
(local.set $ray-y
(f32.add (local.get $Dy) (f32.mul (local.get $xproj) (local.get $Dx))))
(local.set $dist
(call $ray-line
(local.get $ray-x)
(local.get $ray-y)
(f32.load (local.get $wall))
(f32.load offset=4 (local.get $wall))
(f32.load offset=8 (local.get $wall))
(f32.load offset=12 (local.get $wall))))
(if (f32.lt (local.get $dist) (local.get $mindist))
(then
(local.set $mindist (local.get $dist))
(local.set $mint2
(f32.mul (global.get $t2) (f32.load offset=16 (local.get $wall))))))
(br_if $wall-loop
(i32.lt_s
(local.tee $wall (i32.add (local.get $wall) (i32.const 20)))
(i32.const 80))))
(local.set $height
(i32.trunc_f32_s
(f32.div
(f32.const 120) ;; screen height / 2.
(local.get $mindist))))
(local.set $miny (i32.sub (i32.const 120) (local.get $height)))
(local.set $maxy (i32.add (i32.const 120) (local.get $height)))
;; clamp miny and maxy
(if (i32.le_s (local.get $miny) (i32.const 0))
(then (local.set $miny (i32.const 0))))
(if (i32.ge_s (local.get $maxy) (i32.const 240))
(then (local.set $maxy (i32.const 240))))
;; Start at middle of column.
(local.set $y (i32.const 0))
(local.set $addr (i32.shl (local.get $x) (i32.const 2)))
;; draw ceiling and floor
(local.set $bot-addr (i32.add (local.get $addr) (i32.const 307200)))
(if (local.get $miny)
(then
(local.set $y (local.get $miny))
(loop $loop
;; draw ceiling (decrement after)
(i32.store offset=0x3000
(local.get $addr)
(i32.const 0xff000000))
(local.set $addr (i32.add (local.get $addr) (i32.const 1280)))
;; draw-floor (decrement before)
(local.set $bot-addr (i32.sub (local.get $bot-addr) (i32.const 1280)))
(i32.store offset=0x3000
(local.get $bot-addr)
(i32.const 0xff000000))
(br_if $loop
(local.tee $y (i32.sub (local.get $y) (i32.const 1)))))))
;; draw wall
(if (local.get $height)
(then
(local.set $y (local.get $miny))
(loop $y-loop
(i32.store offset=0x3000 (local.get $addr)
(i32.const 0xffffffff))
(local.set $addr (i32.add (local.get $addr) (i32.const 1280)))
(br_if $y-loop
(i32.lt_s
(local.tee $y (i32.add (local.get $y) (i32.const 1)))
(local.get $maxy))))))
;; loop on x
(br_if $x-loop
(i32.lt_s
(local.tee $x (i32.add (local.get $x) (i32.const 1)))
(i32.const 320))))
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment