Files
jak-project/goal_src/jak1/engine/collide/collide-cache.gc
T
2026-07-28 12:00:35 -07:00

4689 lines
200 KiB
Common Lisp

;;-*-Lisp-*-
(in-package goal)
(bundles "ENGINE.CGO" "GAME.CGO")
(require "engine/gfx/background/subdivide.gc")
(require "engine/collide/collide-func.gc")
(require "examples/debug-collide.gc")
(require "engine/collide/collide-probe.gc")
(require "engine/geometry/bounding-box.gc")
(require "engine/collide/collide-mesh-h.gc")
(require "engine/collide/main-collide.gc")
(require "engine/common-obs/water-h.gc")
(require "engine/collide/collide-shape-h.gc")
(require "engine/debug/debug.gc")
;; A collide-cache gathers the small world-space working set used by one collision query. Background
;; traversal contributes packed collision fragments, foreground connection lists contribute dynamic
;; primitives, and water may contribute a two-triangle bottom plane. The narrow-phase probes then
;; examine only the cached primitives and triangles.
;;
;; Box, vertical Y-probe, and swept line-sphere queries share the same cache. The swept query builds
;; an oriented box around the motion and keeps its inverse transform in *collide-work*; the import
;; pass can therefore perform the same integer AABB reject in that local frame. Multi-sphere queries
;; first fill from the spheres' combined bounds and then test each accepted primitive against the
;; individual spheres.
;; DECOMP BEGINS
(defmethod debug-draw ((obj collide-cache))
"Draw the cached triangles with their surface colors and the cached foreground spheres."
(let ((tri (the-as collide-cache-tri (-> obj tris))))
(countdown (tris-left (-> obj num-tris))
(let ((tri-color (copy-and-set-field (-> *pat-mode-info* (-> tri pat mode) color) a 64)))
(add-debug-flat-triangle #t
(bucket-id debug-no-zbuf)
(the-as vector (-> tri vertex))
(-> tri vertex 1)
(-> tri vertex 2)
tri-color)
(#when PC_PORT
(add-debug-outline-triangle #t
(bucket-id debug-no-zbuf)
(-> tri vertex 0)
(-> tri vertex 1)
(-> tri vertex 2)
(static-rgba 0 0 0 64))))
(&+! tri 64)))
(let ((prim (the-as collide-cache-prim (-> obj prims))))
(countdown (prims-left (-> obj num-prims))
(when (= (-> prim prim-core prim-type) -1)
(let ((prim-color (copy-and-set-field (-> *pat-mode-info* (-> (the-as collide-shape-prim-sphere (-> prim prim)) pat mode) color) a 64)))
(add-debug-sphere #t
(bucket-id debug-no-zbuf)
(the-as vector (-> prim prim-core))
(-> prim prim-core world-sphere w)
prim-color)))
(&+! prim 48)))
0
(none))
;;;;;;;;;;;;;;;;;;;;;;;;
;; Setup
;;;;;;;;;;;;;;;;;;;;;;;;
(define *already-printed-exeeded-max-cache-tris* #f)
(defmethod initialize ((obj collide-cache))
"Empty this cache and clear its querying process."
(set! (-> obj num-tris) 0)
(set! (-> obj num-prims) 0)
(set! (-> obj proc) #f)
(set! *already-printed-exeeded-max-cache-tris* #f)
(none))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Fill using Bounding Box
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Background mesh fill has four stages:
;; 1. Gather fragments with BSP traversal or the line-sphere collide probe.
;; 2. Stream each fragment's packed vertices through VIF0 into VU0 data memory.
;; 3. Unpack and transform the vertices from VU0 into scratchpad triangles.
;; 4. Reject triangles outside the active query and append the survivors to the cache.
;;
;; The EE overlaps stages 2 through 4 across two VU0 buffers. The PC path follows the same data flow
;; through host implementations.
(#when PC_PORT
(defmethod-mips2c "(method 26 collide-cache)" 26 collide-cache)
(defmethod-mips2c "(method 27 collide-cache)" 27 collide-cache)
(defmethod-mips2c "(method 28 collide-cache)" 28 collide-cache)
(defmethod-mips2c "(method 29 collide-cache)" 29 collide-cache)
(defmethod-mips2c "(method 32 collide-cache)" 32 collide-cache)
(defmethod-mips2c "(method 12 collide-shape-prim-mesh)" 12 collide-shape-prim-mesh)
(defmethod-mips2c "(method 14 collide-shape-prim-mesh)" 14 collide-shape-prim-mesh)
(defmethod-mips2c "(method 13 collide-shape-prim-mesh)" 13 collide-shape-prim-mesh)
(defmethod-mips2c "(method 30 collide-cache)" 30 collide-cache)
(defmethod-mips2c "(method 9 collide-cache-prim)" 9 collide-cache-prim)
(defmethod-mips2c "(method 10 collide-cache-prim)" 10 collide-cache-prim)
(defmethod-mips2c "(method 9 collide-puss-work)" 9 collide-puss-work)
(defmethod-mips2c "(method 10 collide-puss-work)" 10 collide-puss-work)
(def-mips2c __pc-upload-collide-frag (function object object object none)))
;; Scratchpad vertex records occupy 32 bytes. The first quadword is the vertex truncated to
;; integer world coordinates and the second is the same vertex in floating-point coordinates.
;; The line-sphere transform fills a second integer bank at scratchpad+0x1000 after rotating the
;; query into its axis-aligned working frame. Triangle strip bytes select three of these records;
;; a negative control byte reverses winding while continuing the strip.
(#unless PC_PORT
(defconstant COLLIDE-SPAD-BASE #x70000000)
(defconstant COLLIDE-SPAD-LINE-INTEGER-BANK #x1000)
(defconstant COLLIDE-SPAD-VERTEX-SIZE #x20)
;; VIF0 alternates UNPACK destinations 0 and 128. While one bank is being unpacked, the previous
;; bank is transformed into scratchpad and its triangles are imported into the cache.
(defconstant COLLIDE-VU0-BANK-QW #x80)
(defconstant COLLIDE-VIF0-CHANNEL #x10008000)
;; CHCR STR | TTE. The VIF tag transfer bit is needed because the DMA tag carries the UNPACK
;; command which selects the destination VU0 bank.
(defconstant COLLIDE-VIF0-START-TTE #x80002000)
;; VIF UNPACK V3-16 adds this exponent bias to packed xyz. Subtracting the converted mesh base
;; translation recovers ordinary floating-point coordinates without scalar halfword conversion.
(defconstant COLLIDE-PACKED-VERTEX-BIAS-X #x4d000000)
;; The box and Y-probe importers are identical. Their callers prepare different query bounds, but
;; both consume the same scratchpad vertex/strip representation and write collide-cache-tri records.
(defmacro import-spad-triangles-in-query-box ()
`(begin
(add.i sp sp -112)
(s.d ra sp)
(s.d fp sp 8)
(m fp t9)
(s.q s1 sp 16)
(s.q s2 sp 32)
(s.q s3 sp 48)
(s.q s4 sp 64)
(s.q s5 sp 80)
(s.q gp sp 96)
;; Reserve the remaining collide-cache-tri slots and form the output cursor. BASIC field
;; addresses are four bytes below their displayed offsets, hence 60/76 for collide-box4w.
(nop!)
(add.i v1 r0 460)
(l.wu a2 a0)
(sub t0 v1 a2)
(add.i a3 r0 64)
(mult3 a3 a2 a3)
(b.lt t0 r0 cache-full :delay (m t0 a0))
(nop!)
;; Locate the strip stream, PAT index stream, query bounds, and output record.
(l.bu t2 a1 25)
(add.i t0 t0 #x132c)
(l.wu t1 a1)
(sll t2 t2 4)
(l.hu t3 a1 8)
(add a3 t0 a3)
(l.q t0 a0 60)
(add t1 t1 t2)
(l.q t2 a0 76)
(nop!)
(l.wu a1 a1 4)
(add t3 t1 t3)
(l.wu t4 a0 8)
(label next-strip)
(lui t5 #x7000)
(l.b t7 t1)
(nop!)
(l.b t8 t1 1)
(b.lt t7 r0 finish-strip :delay (l.b t6 t1 2))
;; Load the first triangle. Integer vertices are the first quadword of each 32-byte record.
(sll t7 t7 5)
(sll t8 t8 5)
(sll t9 t6 5)
(add t6 t7 t5)
(add t7 t8 t5)
(l.q t8 t6)
(add ra t9 t5)
(l.q t9 t7)
(min.w s4 t8 t9)
(l.q gp ra)
(max.w s5 t8 t9)
(mmi-nop!)
(min.w s4 s4 gp)
(mmi-nop!)
(max.w s5 s5 gp)
(mmi-nop!)
;; Reject when any xyz minimum is above query max or any maximum is below query min. ppach
;; collapses the packed comparison lanes; shifting by sixteen discards the unused w result.
(pcgt.w s4 s4 t2)
(mmi-nop!)
(pcgt.w s5 t0 s5)
(mmi-nop!)
(or.q s4 s4 s5)
(l.bu s5 t3)
(ppach s4 r0 s4)
(mmi-nop!)
(sll s4 s4 16)
(sll s5 s5 2)
(b.nz s4 advance-triangle :delay (add s5 s5 a1))
(b.eq a2 v1 cache-full :delay (l.wu s5 s5))
(and s4 s5 t4)
(nop!)
(b.nz s4 advance-triangle :delay (nop!))
(nop!)
;; PAT passes the cache ignore mask. Copy floating vertices from record+16 and overlay PAT in
;; the destination record's final quadword.
(s.q r0 a3 48)
(add.i a2 a2 1)
(s.w s5 a3 48)
(add.i a3 a3 64)
(l.q s5 t6 16)
(nop!)
(l.q s4 t7 16)
(nop!)
(l.q s3 ra 16)
(nop!)
(s.q s5 a3 -64)
(nop!)
(s.q s4 a3 -48)
(nop!)
(s.q s3 a3 -32)
(label advance-triangle)
(add.i t3 t3 1)
(add.i t1 t1 3)
(add.i s5 r0 16)
(nop!)
;; Each following strip byte supplies the new vertex. Its sign chooses which old edge is
;; retained, preserving the strip's winding without rebuilding an explicit index triple.
(label strip-loop)
(nop!)
(l.b s4 t1)
(add.i t1 t1 1)
(nop!)
(b.z s4 next-strip :delay (nop!))
(b.ltl s4 r0 negative-strip-index :delay (sub s4 r0 s4))
(m.q t8 t9)
(m t6 t7)
(sub s5 r0 s5)
(nop!)
(label negative-strip-index)
(add.i s4 s4 -1)
(m t7 ra)
(m.q t9 gp)
(sll ra s4 5)
(add ra ra t5)
(l.bu s4 t3)
(min.w s2 t8 t9)
(l.q gp ra)
(max.w s3 t8 t9)
(mmi-nop!)
(min.w s2 s2 gp)
(mmi-nop!)
(max.w s3 s3 gp)
(mmi-nop!)
(pcgt.w s2 s2 t2)
(mmi-nop!)
(pcgt.w s3 t0 s3)
(mmi-nop!)
(or.q s3 s2 s3)
(mmi-nop!)
(ppach s3 r0 s3)
(mmi-nop!)
(sll s3 s3 16)
(sll s4 s4 2)
(b.nz s3 strip-loop :delay (add.i t3 t3 1))
(add s4 s4 a1)
(nop! :count 2)
(l.wu s4 s4)
(and s3 s4 t4)
(nop!)
(b.nz s3 strip-loop :delay (nop!))
(b.eq a2 v1 cache-full :delay (nop!))
(nop!)
(s.q r0 a3 48)
(add.i a2 a2 1)
(s.w s4 a3 48)
(add.i s2 a3 16)
(l.q s1 t7 16)
(add.i a3 a3 64)
(l.q s4 t6 16)
(nop!)
(l.q s3 ra 16)
(nop!)
(s.q s1 s2)
(sub s1 s2 s5)
(add s2 s2 s5)
(nop!)
(s.q s4 s1)
(b strip-loop :delay (s.q s3 s2))
(label finish-strip)
(nop!)
(s.w a2 a0)
(b importer-return :delay (nop!))
(label cache-full)
(nop!)
(s.w v1 a0)
(m! v1 *already-printed-exeeded-max-cache-tris*)
(b.ne s7 v1 skip-cache-warning :delay (m v1 s7))
(add.i v1 s7 #t)
(set! *already-printed-exeeded-max-cache-tris* v1)
(add.i v1 s7 debug)
(m! a0 *cheat-mode*)
(b.ne a0 v1 skip-cache-warning :delay (m v1 s7))
(m! t9 format)
(m! a0 *stdcon*)
(add.i a1 fp L305)
(jalr ra t9 :delay (sll v0 ra 0))
(m v1 v0)
(label skip-cache-warning)
(b importer-return :delay (nop!))
(label importer-return)
(m v0 r0)
(l.d ra sp)
(l.d fp sp 8)
(l.q gp sp 96)
(l.q s5 sp 80)
(l.q s4 sp 64)
(l.q s3 sp 48)
(l.q s2 sp 32)
(l.q s1 sp 16)
(jr ra :delay (add.i sp sp 112))
(nop! :count 3))))
(#unless PC_PORT
(defmethod load-mesh-from-spad-in-box ((cache collide-cache) (mesh collide-frag-mesh))
"Append scratchpad triangles which overlap collide-box4w and are not masked by ignore-mask."
(rlet ((cache-reg :reg a0 :type collide-cache)
(mesh-reg :reg a1 :type collide-frag-mesh))
(import-spad-triangles-in-query-box))))
(#unless PC_PORT
(defmethod transform-collide-mesh-in-spad ((cache collide-cache) (mesh collide-frag-mesh))
"Transform the scratchpad vertex bank into the line-sphere query frame and write the
integer-coordinate copy at scratchpad+0x1000."
(rlet ((matrix-x :reg vf1)
(matrix-y :reg vf2)
(matrix-z :reg vf3)
(matrix-t :reg vf4)
(point-0 :reg vf5)
(point-1 :reg vf6)
(point-2 :reg vf7)
(point-3 :reg vf8)
(point-4 :reg vf9)
(point-5 :reg vf10)
(point-6 :reg vf11)
(point-7 :reg vf12))
(nop! :count 2)
(m! a0 *collide-work*)
(lui v1 #x7000)
(l.bu a1 a1 24)
(nop!)
;; inv-mat maps world vertices into the axis-aligned frame constructed for the swept sphere.
(l.vf matrix-x a0 48)
(nop!)
(l.vf matrix-y a0 64)
(nop!)
(l.vf matrix-z a0 80)
(nop!)
(l.vf matrix-t a0 96)
(nop!)
;; Eight vertices are kept in flight. Float vertices remain in the low scratchpad bank;
;; this function writes only their integer query-frame copies to the bank at +0x1000.
(l.vf point-0 v1 16)
(nop!)
(l.vf point-1 v1 48)
(nop!)
(l.vf point-2 v1 80)
(nop!)
(l.vf point-3 v1 112)
(mula.w.vf matrix-t vf0)
(l.vf point-4 v1 144)
(madda.x.vf matrix-x point-0)
(l.vf point-5 v1 176)
(madda.y.vf matrix-y point-0)
(l.vf point-6 v1 208)
(madd.z.vf point-0 matrix-z point-0)
(l.vf point-7 v1 240)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x point-1)
(nop!)
(madda.y.vf matrix-y point-1)
(nop!)
(madd.z.vf point-1 matrix-z point-1)
(nop!)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x point-2)
(nop!)
(madda.y.vf matrix-y point-2)
(nop!)
(madd.z.vf point-2 matrix-z point-2)
(nop!)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x point-3)
(nop!)
(madda.y.vf matrix-y point-3)
(nop!)
(madd.z.vf point-3 matrix-z point-3)
(nop!)
(ftoi.vf point-0 point-0)
(nop!)
(ftoi.vf point-1 point-1)
(nop!)
(ftoi.vf point-2 point-2)
(nop!)
(ftoi.vf point-3 point-3)
(nop!)
(label transform-eight)
(mula.w.vf matrix-t vf0)
(s.vf point-0 v1 #x1000)
(madda.x.vf matrix-x point-4)
(s.vf point-1 v1 #x1020)
(madda.y.vf matrix-y point-4)
(s.vf point-2 v1 #x1040)
(madd.z.vf point-4 matrix-z point-4)
(s.vf point-3 v1 #x1060)
(mula.w.vf matrix-t vf0)
(l.vf point-0 v1 272)
(madda.x.vf matrix-x point-5)
(l.vf point-1 v1 304)
(madda.y.vf matrix-y point-5)
(l.vf point-2 v1 336)
(madd.z.vf point-5 matrix-z point-5)
(l.vf point-3 v1 368)
(add.i v1 v1 256)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x point-6)
(add.i a0 a1 -4)
(madda.y.vf matrix-y point-6)
(b.le a0 r0 transformed-all :delay (madd.z.vf point-6 matrix-z point-6))
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x point-7)
(nop!)
(madda.y.vf matrix-y point-7)
(nop!)
(madd.z.vf point-7 matrix-z point-7)
(nop!)
(ftoi.vf point-4 point-4)
(nop!)
(ftoi.vf point-5 point-5)
(nop!)
(ftoi.vf point-6 point-6)
(nop!)
(ftoi.vf point-7 point-7)
(nop!)
(mula.w.vf matrix-t vf0)
(s.vf point-4 v1 3968)
(madda.x.vf matrix-x point-0)
(s.vf point-5 v1 4000)
(madda.y.vf matrix-y point-0)
(s.vf point-6 v1 4032)
(madd.z.vf point-0 matrix-z point-0)
(s.vf point-7 v1 4064)
(nop!)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x point-1)
(add.i a1 a0 -4)
(madda.y.vf matrix-y point-1)
(b.le a1 r0 transformed-all :delay (madd.z.vf point-1 matrix-z point-1))
(mula.w.vf matrix-t vf0)
(l.vf point-4 v1 144)
(madda.x.vf matrix-x point-2)
(l.vf point-5 v1 176)
(madda.y.vf matrix-y point-2)
(l.vf point-6 v1 208)
(madd.z.vf point-2 matrix-z point-2)
(l.vf point-7 v1 240)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x point-3)
(nop!)
(madda.y.vf matrix-y point-3)
(nop!)
(madd.z.vf point-3 matrix-z point-3)
(nop!)
(ftoi.vf point-0 point-0)
(nop!)
(ftoi.vf point-1 point-1)
(nop!)
(ftoi.vf point-2 point-2)
(nop!)
(ftoi.vf point-3 point-3)
(nop!)
(b transform-eight :delay (nop!))
(label transformed-all)
(m v0 r0)
(jr ra :delay (add sp sp r0))
(nop! :count 3))))
(#unless PC_PORT
(defmethod fill-from-background ((cache collide-cache) (find-mesh (function bsp-header int collide-list none)) (import-mesh (function collide-cache collide-frag-mesh none)))
"Gather background fragments, overlap their VIF0 uploads with VU0 unpacking, and import the
triangles accepted by the active query."
(rlet ((cache-reg :reg gp :type collide-cache)
(find-mesh-reg :reg s4)
(import-mesh-reg :reg s5))
(add.i sp sp -96)
(s.d ra sp)
(s.d fp sp 8)
(m fp t9)
(s.q s2 sp 16)
(s.q s3 sp 32)
(s.q s4 sp 48)
(s.q s5 sp 64)
(s.q gp sp 80)
(m cache-reg a0)
(m find-mesh-reg a1)
(m import-mesh-reg a2)
(m! v1 *collide-list*)
(s.w r0 v1)
;; The line-sphere query already has a collide-probe traversal. Other queries ask each
;; active level BSP to append its matching fragments.
(m! v1 bsp-header)
(l.wu v1 v1 68)
(b.ne find-mesh-reg v1 gather-from-bsps :delay (nop!))
(add.i s4 r0 0)
(b probe-level-test :delay (nop!))
(label probe-level-loop)
(add.i v1 r0 2608)
(mult3 v1 v1 s4)
(add.i v1 v1 96)
(m! a0 *level*)
(add a0 v1 a0)
(add.i v1 s7 active)
(l.wu a1 a0 16)
(b.ne a1 v1 probe-next-level :delay (m v1 s7))
;; Performance counter 14 brackets the collide-probe traversal.
(m! v1 *perf-stats*)
(add.i v1 v1 740)
(l.wu a1 v1 28)
(l.wu a2 v1 4)
(add.i a2 a2 1)
(s.w a2 v1 4)
(b.z a1 probe-counter-ready :delay (nop!))
(mtc0 Perf r0)
(sync.l)
(sync.p)
(mtpc pcr0 r0)
(mtpc pcr1 r0)
(sync.l)
(sync.p)
(mtc0 Perf a1)
(sync.l)
(sync.p)
(label probe-counter-ready)
(m v1 r0)
(m! t9 collide-probe-make-list)
(m! a1 *collide-list*)
(jalr ra t9 :delay (sll v0 ra 0))
(m! v1 *perf-stats*)
(add.i v1 v1 740)
(l.wu a0 v1 28)
(b.z a0 probe-next-level :delay (nop!))
(mtc0 Perf r0)
(sync.l)
(sync.p)
(mfpc a0 pcr0)
(l.wu a1 v1 32)
(add a0 a1 a0)
(s.w a0 v1 32)
(mfpc a0 pcr1)
(l.wu a1 v1 36)
(add a0 a1 a0)
(s.w a0 v1 36)
(label probe-next-level)
(m v1 r0)
(add.i s4 s4 1)
(label probe-level-test)
(m! v1 *level*)
(l.w v1 v1)
(slt v1 s4 v1)
(b.nz v1 probe-level-loop :delay (nop!))
(m v1 s7)
(m v1 s7)
(b fragments-ready :delay (nop!))
(label gather-from-bsps)
(add.i s3 r0 0)
(b bsp-level-test :delay (nop!))
(label bsp-level-loop)
(add.i v1 r0 2608)
(mult3 v1 v1 s3)
(add.i v1 v1 96)
(m! a0 *level*)
(add v1 v1 a0)
(add.i a0 s7 active)
(l.wu a1 v1 16)
(b.ne a1 a0 bsp-next-level :delay (m a0 s7))
(m t9 find-mesh-reg)
(l.wu a0 v1 44)
(add.i a1 r0 0)
(m! a2 *collide-list*)
(jalr ra t9 :delay (sll v0 ra 0))
(m a0 v0)
(label bsp-next-level)
(add.i s3 s3 1)
(label bsp-level-test)
(m! v1 *level*)
(l.w v1 v1)
(slt v1 s3 v1)
(b.nz v1 bsp-level-loop :delay (nop!))
(m v1 s7)
(m v1 s7)
(label fragments-ready)
(m! v1 *collide-list*)
(l.w v1 v1)
(slt v1 r0 v1)
(b.z v1 background-fill-done :delay (m v1 s7))
;; Build the fixed VIF packet at scratchpad+0x2000. STROW supplies the unpack exponent bias,
;; STMOD/STMASK preserve w, and STCYCL expands V3-16 input to one vector per vertex.
(add.i v1 r0 #x2000)
(lui a0 #x7000)
(add v1 v1 a0)
(lui a0 #x3000)
(s.d a0 v1)
(s.w r0 v1 8)
(lui a0 #x6900)
(ori a0 a0 #x4000)
(s.w a0 v1 12)
(add.i v1 v1 16)
(lui a0 #x7000)
(s.d a0 v1)
(s.d r0 v1 8)
(add.i s4 r0 0)
(m! v1 *collide-list*)
(add.i s3 v1 16)
(l.wu v1 s3)
(add.i a0 r0 #x2000)
(lui a1 #x7000)
(add s2 a0 a1)
(l.bu a0 v1 25)
(l.wu a1 v1)
(l.bu v1 v1 24)
(s.h a0 s2)
(s.w a1 s2 4)
(s.b v1 s2 14)
;; Start the first upload, then enter the two-bank pipeline.
(lui v1 #x1000)
(ori v1 v1 #x8000)
(m a0 v1)
(label wait-first-vif0)
(l.w a1 a0)
(nop! :count 3)
(and.i a1 a1 256)
(nop!)
(b.z a1 first-vif0-idle :delay (nop!))
(nop! :count 10)
(b wait-first-vif0 :delay (nop!))
(label first-vif0-idle)
(m a0 r0)
(s.w r0 v1 32)
(l.d a0 fp collide-vif0-start-tte)
(s.w a0 v1 48)
(sync.l)
(add.i a0 r0 325)
(s.w a0 v1)
(sync.l)
(m v1 r0)
(b fragment-pipeline-test :delay (nop!))
(label fragment-pipeline-loop)
;; Queue fragment N+1 into the opposite VU0 bank before consuming fragment N.
(add.i a2 s4 1)
(m! v1 *collide-list*)
(l.w v1 v1)
(slt v1 a2 v1)
(b.z v1 drain-vif0 :delay (nop!))
(m v1 s3)
(add.i v1 v1 16)
(l.wu a1 v1)
(l.bu v1 a1 25)
(l.wu a0 a1)
(l.bu a1 a1 24)
(and.i a2 a2 1)
(sll a3 a2 7)
(lui a2 #x1000)
(ori a2 a2 #x8000)
(m t0 a2)
(label wait-next-vif0)
(l.w t1 t0)
(nop! :count 3)
(and.i t1 t1 256)
(nop!)
(b.z t1 next-vif0-idle :delay (nop!))
(nop! :count 10)
(b wait-next-vif0 :delay (nop!))
(label next-vif0-idle)
(m t0 r0)
(s.h v1 s2)
(s.w a0 s2 4)
(s.b a1 s2 14)
(s.h a3 s2 12)
(sync.l)
(s.w r0 a2 32)
(l.d v1 fp collide-vif0-start-tte)
(s.w v1 a2 48)
(sync.l)
(add.i v1 r0 325)
(s.w v1 a2)
(sync.l)
(m v1 r0)
(b consume-current-fragment :delay (nop!))
(label drain-vif0)
(lui v1 #x1000)
(ori v1 v1 #x8000)
(label wait-vif0-drain)
(l.w a0 v1)
(nop! :count 3)
(and.i a0 a0 256)
(nop!)
(b.z a0 consume-current-fragment :delay (nop!))
(nop! :count 10)
(b wait-vif0-drain :delay (nop!))
(label consume-current-fragment)
(m v1 r0)
(add.i a3 r0 0)
(and.i v1 s4 1)
(b.z v1 current-vu0-bank-ready :delay (m v1 s7))
(add.i a3 r0 128)
(m v1 a3)
(label current-vu0-bank-ready)
(m a0 cache-reg)
(l.wu v1 a0 -4)
(l.wu t9 v1 144)
(l.wu a1 s3)
(l.wu a2 s3 4)
(jalr ra t9 :delay (sll v0 ra 0))
(m v1 v0)
(m t9 import-mesh-reg)
(m a0 cache-reg)
(l.wu a1 s3)
(jalr ra t9 :delay (sll v0 ra 0))
(add.i s4 s4 1)
(add.i s3 s3 16)
(label fragment-pipeline-test)
(m! v1 *collide-list*)
(l.w v1 v1)
(slt v1 s4 v1)
(b.nz v1 fragment-pipeline-loop :delay (nop!))
(m v1 s7)
;; Background triangles are exposed as one synthetic cached mesh primitive.
(l.w a0 cache-reg)
(slt v1 r0 a0)
(b.z v1 background-fill-done :delay (m a1 s7))
(add.i v1 cache-reg 108)
(m! a1 *collide-shape-prim-backgnd*)
(s.h a0 v1 42)
(s.w a1 v1 36)
(add.i a0 r0 1)
(s.w a0 cache-reg 4)
(s.h r0 v1 40)
(s.w cache-reg v1 32)
(l.q a0 a1 12)
(s.q a0 v1)
(l.q a1 a1 28)
(s.q a1 v1 16)
(label background-fill-done)
(m v0 r0)
(l.d ra sp)
(l.d fp sp 8)
(l.q gp sp 80)
(l.q s5 sp 64)
(l.q s4 sp 48)
(l.q s3 sp 32)
(l.q s2 sp 16)
(jr ra :delay (add.i sp sp 96))
(nop! :count 2)
(asm-data (label collide-vif0-start-tte) (word #x80002000 0)))))
(#unless PC_PORT
(defmethod unpack-background-collide-mesh ((cache collide-cache) (mesh collide-frag-mesh) (inst basic) (vu-bank int))
"Expand VIF-unpacked vertices from VU0 memory into the scratchpad float/integer vertex layout.
Apply the compressed TIE instance transform when inst is nonfalse."
(rlet ((packed-bias :reg vf13)
(mesh-base :reg vf14)
(matrix-x :reg vf15)
(matrix-y :reg vf16)
(matrix-z :reg vf17)
(matrix-t :reg vf18))
(add.i sp sp -16)
(s.d fp sp 8)
(m fp t9)
(add.i a0 fp collide-packed-vertex-bias)
(nop!)
(b.eq a2 s7 unpack-uninstanced :delay (nop!))
;; Decode the compressed instance basis. Rows are signed Q12; translation is widened
;; separately and added to the instance origin before the vertex batches begin.
(lui v1 #x7000)
(l.vf mesh-base a1 12)
(vmove.xyzw vf1 vf0)
(l.vf packed-bias a0)
(vmove.xyzw vf2 vf0)
(l.bu a0 a1 24)
(m vi1 a3)
(vmove.xyzw vf3 vf0)
(itof.vf mesh-base mesh-base)
(vmove.xyzw vf4 vf0)
(vmove.xyzw vf5 vf0)
(nop!)
(vmove.xyzw vf6 vf0)
(nop!)
(vmove.xyzw vf7 vf0)
(nop!)
(vmove.xyzw vf8 vf0)
(nop! :count 2)
(l.d a1 a2 52)
(nop!)
(l.d t1 a2 28)
(pextlh a1 a1 r0)
(l.d a3 a2 36)
(sra.w t0 a1 10)
(l.d a1 a2 44)
(pextlh t1 t1 r0)
(mmi-nop!)
(sra.w t1 t1 16)
(mmi-nop!)
(pextlh a3 a3 r0)
(m matrix-t t0)
(sra.w a3 a3 16)
(m matrix-x t1)
(pextlh a1 a1 r0)
(m matrix-y a3)
(sra.w a1 a1 16)
(l.vf vf1 a2 12)
(nop!)
(m matrix-z a1)
(itof.vf matrix-t matrix-t)
(nop!)
(itof.vf matrix-x matrix-x :fixed 12)
(nop!)
(itof.vf matrix-y matrix-y :fixed 12)
(nop!)
(itof.vf matrix-z matrix-z :fixed 12)
(nop!)
(add.vf.xyz matrix-t matrix-t vf1)
(nop!)
(sub.vf packed-bias packed-bias mesh-base)
;; Prime eight vertices from the selected VU0 bank.
(lqi.vf.xyz vf1 vi1)
(add.i a0 a0 -4)
(lqi.vf.xyz vf2 vi1)
(nop!)
(lqi.vf.xyz vf3 vi1)
(nop!)
(lqi.vf.xyz vf4 vi1)
(sub.vf.xyz vf1 vf1 packed-bias)
(lqi.vf.xyz vf5 vi1)
(sub.vf.xyz vf2 vf2 packed-bias)
(lqi.vf.xyz vf6 vi1)
(sub.vf.xyz vf3 vf3 packed-bias)
(lqi.vf.xyz vf7 vi1)
(sub.vf.xyz vf4 vf4 packed-bias)
(lqi.vf.xyz vf8 vi1)
;; Transform the first four while the second four wait in vf5..vf8.
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x vf1)
(nop!)
(madda.y.vf matrix-y vf1)
(nop!)
(madd.z.vf vf1 matrix-z vf1)
(nop!)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x vf2)
(nop!)
(madda.y.vf matrix-y vf2)
(nop!)
(madd.z.vf vf2 matrix-z vf2)
(nop!)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x vf3)
(nop!)
(madda.y.vf matrix-y vf3)
(nop!)
(madd.z.vf vf3 matrix-z vf3)
(nop!)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x vf4)
(nop!)
(madda.y.vf matrix-y vf4)
(nop!)
(madd.z.vf vf4 matrix-z vf4)
(nop!)
(ftoi.vf vf9 vf1)
(s.vf vf1 v1 16)
(ftoi.vf vf10 vf2)
(s.vf vf2 v1 48)
(ftoi.vf vf11 vf3)
(s.vf vf3 v1 80)
(ftoi.vf vf12 vf4)
(s.vf vf4 v1 112)
(sub.vf.xyz vf5 vf5 packed-bias)
(s.vf vf9 v1)
(sub.vf.xyz vf6 vf6 packed-bias)
(s.vf vf10 v1 32)
(sub.vf.xyz vf7 vf7 packed-bias)
(s.vf vf11 v1 64)
(sub.vf.xyz vf8 vf8 packed-bias)
(s.vf vf12 v1 96)
(mula.w.vf matrix-t vf0)
(nop!)
(b.le a0 r0 unpack-done :delay (madda.x.vf matrix-x vf5))
(madda.y.vf matrix-y vf5)
(nop!)
(madd.z.vf vf5 matrix-z vf5)
(nop!)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x vf6)
(nop!)
(madda.y.vf matrix-y vf6)
(nop!)
(madd.z.vf vf6 matrix-z vf6)
(nop!)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x vf7)
(nop!)
(madda.y.vf matrix-y vf7)
(nop!)
(madd.z.vf vf7 matrix-z vf7)
(add.i v1 v1 256)
(mula.w.vf matrix-t vf0)
(lqi.vf.xyz vf1 vi1)
(madda.x.vf matrix-x vf8)
(lqi.vf.xyz vf2 vi1)
(madda.y.vf matrix-y vf8)
(lqi.vf.xyz vf3 vi1)
(madd.z.vf vf8 matrix-z vf8)
(lqi.vf.xyz vf4 vi1)
(ftoi.vf vf9 vf5)
(s.vf vf5 v1 -112)
(ftoi.vf vf10 vf6)
(s.vf vf6 v1 -80)
(ftoi.vf vf11 vf7)
(s.vf vf7 v1 -48)
(ftoi.vf vf12 vf8)
(s.vf vf8 v1 -16)
(add.i a0 a0 -4)
(s.vf vf9 v1 -128)
(nop!)
(s.vf vf10 v1 -96)
(nop!)
(s.vf vf11 v1 -64)
(b.le a0 r0 unpack-done :delay (s.vf vf12 v1 -32))
;; Steady state: consume four, transform them, and overlap the next four VU0 loads.
(label unpack-instance-loop)
(sub.vf.xyz vf1 vf1 packed-bias)
(lqi.vf.xyz vf5 vi1)
(sub.vf.xyz vf2 vf2 packed-bias)
(lqi.vf.xyz vf6 vi1)
(sub.vf.xyz vf3 vf3 packed-bias)
(lqi.vf.xyz vf7 vi1)
(sub.vf.xyz vf4 vf4 packed-bias)
(lqi.vf.xyz vf8 vi1)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x vf1)
(nop!)
(madda.y.vf matrix-y vf1)
(nop!)
(madd.z.vf vf1 matrix-z vf1)
(nop!)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x vf2)
(nop!)
(madda.y.vf matrix-y vf2)
(nop!)
(madd.z.vf vf2 matrix-z vf2)
(nop!)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x vf3)
(nop!)
(madda.y.vf matrix-y vf3)
(nop!)
(madd.z.vf vf3 matrix-z vf3)
(nop!)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x vf4)
(nop!)
(madda.y.vf matrix-y vf4)
(nop!)
(madd.z.vf vf4 matrix-z vf4)
(nop!)
(ftoi.vf vf9 vf1)
(s.vf vf1 v1 16)
(ftoi.vf vf10 vf2)
(s.vf vf2 v1 48)
(ftoi.vf vf11 vf3)
(s.vf vf3 v1 80)
(ftoi.vf vf12 vf4)
(s.vf vf4 v1 112)
(sub.vf.xyz vf5 vf5 packed-bias)
(s.vf vf9 v1)
(sub.vf.xyz vf6 vf6 packed-bias)
(s.vf vf10 v1 32)
(sub.vf.xyz vf7 vf7 packed-bias)
(s.vf vf11 v1 64)
(sub.vf.xyz vf8 vf8 packed-bias)
(s.vf vf12 v1 96)
(add.i a0 a0 -4)
(mula.w.vf matrix-t vf0)
(b.le a0 r0 finish-instance-tail :delay (madda.x.vf matrix-x vf5))
(madda.y.vf matrix-y vf5)
(nop!)
(madd.z.vf vf5 matrix-z vf5)
(nop!)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x vf6)
(nop!)
(madda.y.vf matrix-y vf6)
(nop!)
(madd.z.vf vf6 matrix-z vf6)
(nop!)
(mula.w.vf matrix-t vf0)
(nop!)
(madda.x.vf matrix-x vf7)
(nop!)
(madda.y.vf matrix-y vf7)
(nop!)
(madd.z.vf vf7 matrix-z vf7)
(add.i v1 v1 256)
(mula.w.vf matrix-t vf0)
(lqi.vf.xyz vf1 vi1)
(madda.x.vf matrix-x vf8)
(lqi.vf.xyz vf2 vi1)
(madda.y.vf matrix-y vf8)
(lqi.vf.xyz vf3 vi1)
(madd.z.vf vf8 matrix-z vf8)
(lqi.vf.xyz vf4 vi1)
(ftoi.vf vf9 vf5)
(s.vf vf5 v1 -112)
(ftoi.vf vf10 vf6)
(s.vf vf6 v1 -80)
(ftoi.vf vf11 vf7)
(s.vf vf7 v1 -48)
(ftoi.vf vf12 vf8)
(s.vf vf8 v1 -16)
(nop!)
(s.vf vf9 v1 -128)
(add.i a0 a0 -4)
(s.vf vf10 v1 -96)
(nop!)
(s.vf vf11 v1 -64)
(b.gt a0 r0 unpack-instance-loop :delay (s.vf vf12 v1 -32))
(label finish-instance-tail)
(b unpack-done :delay (nop!))
;; Uninstanced fragments need only exponent-bias removal; eight vertices are converted and
;; stored per loop iteration.
(label unpack-uninstanced)
(lui v1 #x7000)
(l.vf mesh-base a1 12)
(vmove.xyzw vf1 vf0)
(l.vf packed-bias a0)
(vmove.xyzw vf2 vf0)
(l.bu a0 a1 24)
(m vi1 a3)
(vmove.xyzw vf3 vf0)
(itof.vf mesh-base mesh-base)
(vmove.xyzw vf4 vf0)
(nop!)
(vmove.xyzw vf5 vf0)
(nop!)
(vmove.xyzw vf6 vf0)
(nop!)
(vmove.xyzw vf7 vf0)
(sub.vf packed-bias packed-bias mesh-base)
(vmove.xyzw vf8 vf0)
(label unpack-uninstanced-loop)
(nop!)
(lqi.vf.xyz vf1 vi1)
(nop!)
(lqi.vf.xyz vf2 vi1)
(nop!)
(lqi.vf.xyz vf3 vi1)
(add.i v1 v1 256)
(lqi.vf.xyz vf4 vi1)
(sub.vf.xyz vf1 vf1 packed-bias)
(lqi.vf.xyz vf5 vi1)
(sub.vf.xyz vf2 vf2 packed-bias)
(lqi.vf.xyz vf6 vi1)
(sub.vf.xyz vf3 vf3 packed-bias)
(lqi.vf.xyz vf7 vi1)
(sub.vf.xyz vf4 vf4 packed-bias)
(lqi.vf.xyz vf8 vi1)
(ftoi.vf vf9 vf1)
(s.vf vf1 v1 -240)
(ftoi.vf vf10 vf2)
(s.vf vf2 v1 -208)
(ftoi.vf vf11 vf3)
(s.vf vf3 v1 -176)
(ftoi.vf vf12 vf4)
(s.vf vf4 v1 -144)
(sub.vf.xyz vf5 vf5 packed-bias)
(s.vf vf9 v1 -256)
(sub.vf.xyz vf6 vf6 packed-bias)
(s.vf vf10 v1 -224)
(sub.vf.xyz vf7 vf7 packed-bias)
(s.vf vf11 v1 -192)
(sub.vf.xyz vf8 vf8 packed-bias)
(s.vf vf12 v1 -160)
(ftoi.vf vf9 vf5)
(s.vf vf5 v1 -112)
(ftoi.vf vf10 vf6)
(s.vf vf6 v1 -80)
(ftoi.vf vf11 vf7)
(s.vf vf7 v1 -48)
(ftoi.vf vf12 vf8)
(s.vf vf8 v1 -16)
(nop!)
(s.vf vf9 v1 -128)
(add.i a0 a0 -8)
(s.vf vf10 v1 -96)
(nop!)
(s.vf vf11 v1 -64)
(b.gt a0 r0 unpack-uninstanced-loop :delay (s.vf vf12 v1 -32))
(label unpack-done)
(m v0 r0)
(l.d fp sp 8)
(jr ra :delay (add.i sp sp 16))
(nop! :count 2)
(asm-data (label collide-packed-vertex-bias) (word #x4d000000 #x4d000000 #x4d000000 0)))))
(#unless PC_PORT
(defmethod load-mesh-from-spad-in-line-sphere ((cache collide-cache) (mesh collide-frag-mesh))
"Rotate scratchpad vertices into the line-sphere frame, then append triangles whose rotated
integer bounds overlap collide-work.collide-box4w."
(rlet ((cache-reg :reg gp :type collide-cache)
(mesh-reg :reg s5 :type collide-frag-mesh))
(add.i sp sp -112)
(s.d ra sp)
(s.d fp sp 8)
(m fp t9)
(s.q s1 sp 16)
(s.q s2 sp 32)
(s.q s3 sp 48)
(s.q s4 sp 64)
(s.q s5 sp 80)
(s.q gp sp 96)
(m cache-reg a0)
(m mesh-reg a1)
;; Method slot 29 builds the integer bank at scratchpad+0x1000.
(m a0 cache-reg)
(l.wu v1 a0 -4)
(l.wu t9 v1 132)
(m a1 mesh-reg)
(jalr ra t9 :delay (sll v0 ra 0))
(m v1 v0)
(nop!)
(m! t0 *collide-work*)
(add.i v1 r0 460)
(l.wu a0 cache-reg)
(sub a2 v1 a0)
(sll a1 a0 6)
(b.lt a2 r0 line-cache-full :delay (m a2 cache-reg))
(nop!)
(l.bu t1 mesh-reg 25)
(add.i a2 a2 #x132c)
(l.wu a3 mesh-reg)
(sll t1 t1 4)
(l.hu t2 mesh-reg 8)
(add a1 a2 a1)
(l.q a2 t0 16)
(add a3 a3 t1)
(l.q t0 t0 32)
(nop!)
(l.wu t1 mesh-reg 4)
(add t2 a3 t2)
(l.wu t3 cache-reg 8)
(label line-next-strip)
(lui t4 #x7000)
(l.b t6 a3)
(nop!)
(l.b t7 a3 1)
(b.lt t6 r0 line-finish-strip :delay (l.b t5 a3 2))
(sll t6 t6 5)
(sll t7 t7 5)
(sll t8 t5 5)
(add t5 t6 t4)
(add t6 t7 t4)
(l.q t7 t5 #x1000)
(add t9 t8 t4)
(l.q t8 t6 #x1000)
(min.w s4 t7 t8)
(l.q ra t9 #x1000)
(max.w s5 t7 t8)
(mmi-nop!)
(min.w s4 s4 ra)
(mmi-nop!)
(max.w s5 s5 ra)
(mmi-nop!)
(pcgt.w s4 s4 t0)
(mmi-nop!)
(pcgt.w s5 a2 s5)
(mmi-nop!)
(or.q s4 s4 s5)
(l.bu s5 t2)
(ppach s4 r0 s4)
(mmi-nop!)
(sll s4 s4 16)
(sll s5 s5 2)
(b.nz s4 line-advance-triangle :delay (add s5 s5 t1))
(b.eq a0 v1 line-cache-full :delay (l.wu s5 s5))
(and s4 s5 t3)
(nop!)
(b.nz s4 line-advance-triangle :delay (nop!))
(nop!)
;; Bounds use the rotated integer bank, but accepted triangles retain their original
;; world-space floating vertices from record+16.
(s.q r0 a1 48)
(add.i a0 a0 1)
(s.w s5 a1 48)
(add.i a1 a1 64)
(l.q s5 t5 16)
(nop!)
(l.q s4 t6 16)
(nop!)
(l.q s3 t9 16)
(nop!)
(s.q s5 a1 -64)
(nop!)
(s.q s4 a1 -48)
(nop!)
(s.q s3 a1 -32)
(label line-advance-triangle)
(add.i t2 t2 1)
(add.i a3 a3 3)
(add.i s5 r0 16)
(nop!)
(label line-strip-loop)
(nop!)
(l.b s4 a3)
(add.i a3 a3 1)
(nop!)
(b.z s4 line-next-strip :delay (nop!))
(b.ltl s4 r0 line-negative-index :delay (sub s4 r0 s4))
(m.q t7 t8)
(m t5 t6)
(sub s5 r0 s5)
(nop!)
(label line-negative-index)
(add.i s4 s4 -1)
(m t6 t9)
(m.q t8 ra)
(sll t9 s4 5)
(add t9 t9 t4)
(l.bu s4 t2)
(min.w s2 t7 t8)
(l.q ra t9 #x1000)
(max.w s3 t7 t8)
(mmi-nop!)
(min.w s2 s2 ra)
(mmi-nop!)
(max.w s3 s3 ra)
(mmi-nop!)
(pcgt.w s2 s2 t0)
(mmi-nop!)
(pcgt.w s3 a2 s3)
(mmi-nop!)
(or.q s3 s2 s3)
(mmi-nop!)
(ppach s3 r0 s3)
(mmi-nop!)
(sll s3 s3 16)
(sll s4 s4 2)
(b.nz s3 line-strip-loop :delay (add.i t2 t2 1))
(add s4 s4 t1)
(nop! :count 2)
(l.wu s4 s4)
(and s3 s4 t3)
(nop!)
(b.nz s3 line-strip-loop :delay (nop!))
(b.eq a0 v1 line-cache-full :delay (nop!))
(nop!)
(s.q r0 a1 48)
(add.i a0 a0 1)
(s.w s4 a1 48)
(add.i s2 a1 16)
(l.q s1 t6 16)
(add.i a1 a1 64)
(l.q s4 t5 16)
(nop!)
(l.q s3 t9 16)
(nop!)
(s.q s1 s2)
(sub s1 s2 s5)
(add s2 s2 s5)
(nop!)
(s.q s4 s1)
(b line-strip-loop :delay (s.q s3 s2))
(label line-finish-strip)
(nop!)
(s.w a0 cache-reg)
(b line-import-return :delay (nop!))
(label line-cache-full)
(nop!)
(s.w v1 cache-reg)
(m! v1 *already-printed-exeeded-max-cache-tris*)
(b.ne s7 v1 line-skip-warning :delay (m v1 s7))
(add.i v1 s7 #t)
(set! *already-printed-exeeded-max-cache-tris* v1)
(add.i v1 s7 debug)
(m! a0 *cheat-mode*)
(b.ne a0 v1 line-skip-warning :delay (m v1 s7))
(m! t9 format)
(m! a0 *stdcon*)
(add.i a1 fp L305)
(jalr ra t9 :delay (sll v0 ra 0))
(m v1 v0)
(nop!)
(label line-skip-warning)
(b line-import-return :delay (nop!))
(label line-import-return)
(m v0 r0)
(l.d ra sp)
(l.d fp sp 8)
(l.q gp sp 96)
(l.q s5 sp 80)
(l.q s4 sp 64)
(l.q s3 sp 48)
(l.q s2 sp 32)
(l.q s1 sp 16)
(jr ra :delay (add.i sp sp 112))
(nop! :count 3))))
(#unless PC_PORT
(defmethod load-mesh-from-spad-in-y-probe ((cache collide-cache) (mesh collide-frag-mesh))
"Append scratchpad triangles which overlap the active vertical-probe box."
(rlet ((cache-reg :reg a0 :type collide-cache)
(mesh-reg :reg a1 :type collide-frag-mesh))
(import-spad-triangles-in-query-box))))
(#unless PC_PORT
;; collide-cache is BASIC, so an EE pointer is four bytes past the allocation's type word.
(defconstant COLLIDE-CACHE-BASIC-BIAS 4)
(defconstant COLLIDE-CACHE-TRIS (- (offset-of collide-cache tris) COLLIDE-CACHE-BASIC-BIAS)) ;; 4908
(defconstant COLLIDE-CACHE-TRI-STRIDE (type-size collide-cache-tri)) ;; 64
(defconstant COLLIDE-PUSS-SPHERE-STRIDE (type-size collide-puss-sphere)) ;; 48
(defconstant COLLIDE-PUSS-FOUR-SPHERE-STRIDE (* 4 COLLIDE-PUSS-SPHERE-STRIDE)) ;; 192
;; A successful sphere hit is represented by a small triangle in its tangent plane. These
;; one-meter offsets are rotated into the contact basis and translated to the contact point.
(asm-data (label collide-cache-sphere-contact-triangle)
(word #x00000000 #x45800000 #x00000000 #x3f800000)
(word #x00000000 #xc5800000 #x45800000 #x3f800000)
(word #x00000000 #xc5800000 #xc5800000 #x3f800000)
(label collide-cache-default-best-u)
(word #x40000000) ;; 2.0
(label collide-cache-miss)
(word #xccbebc20)) ;; -100000000.0
(defmethod check-sphere-prim-against-spheres ((this collide-puss-work) (sphere-prim collide-cache-prim) (params collide-using-spheres-params))
"Return true when sphere-prim overlaps any query sphere."
(declare (asm-func symbol))
(rlet ((work :reg a0 :type collide-puss-work)
(prim :reg a1 :type collide-cache-prim)
(params-reg :reg a2 :type collide-using-spheres-params)
(sphere-count :reg v1)
(prim-sphere :reg vf1))
;; Reject the primitive against the union of all query-sphere boxes before entering the
;; four-at-a-time distance loop. The speculative four-sphere loads are safe because the work
;; area has fixed storage for 64 records.
(asm-block reject-union-box
(nop! :count 2)
(l.wu.field sphere-count params-reg (num-spheres))
(add.i params-reg work (offset-of collide-puss-work spheres))
(l.vf prim-sphere prim (offset-of collide-cache-prim world-sphere))
(b.z sphere-count no-hit :delay (l.q a1 work (offset-of collide-puss-work spheres-bbox4w min)))))
(rlet ((work :reg a0 :type collide-puss-work)
(tested-count :reg a0)
(sphere-cursor :reg a2 :type collide-puss-sphere)
(sphere-count :reg v1)
(union-min :reg a1 :class i128)
(union-max :reg a3 :class i128)
(prim-min :reg vf2)
(prim-max :reg vf3)
(zero :reg vf4)
(prim-sphere :reg vf1)
(sphere-0 :reg vf5)
(sphere-1 :reg vf6)
(sphere-2 :reg vf7)
(sphere-3 :reg vf8)
(delta-0 :reg vf9)
(delta-1 :reg vf10)
(delta-2 :reg vf11)
(delta-3 :reg vf12))
(asm-block prepare-four-sphere-loop
(max.w.vf zero vf0 vf0)
(l.q union-max work (offset-of collide-puss-work spheres-bbox4w max))
(sub.w.vf.xyz prim-min prim-sphere prim-sphere)
(m tested-count r0)
(add.w.vf.xyz prim-max prim-sphere prim-sphere)
(l.vf sphere-0 sphere-cursor (offset-of collide-puss-sphere bsphere))
(ftoi.vf prim-min prim-min)
(l.vf sphere-1 sphere-cursor COLLIDE-PUSS-SPHERE-STRIDE)
(ftoi.vf prim-max prim-max)
(l.vf sphere-2 sphere-cursor (* 2 COLLIDE-PUSS-SPHERE-STRIDE))
(nop!)
;; Negating the stored radius in w makes delta.w carry the combined-radius term used below.
(sub.w.vf.w prim-sphere vf0 prim-sphere)
(m t0 prim-min)
(l.vf sphere-3 sphere-cursor (* 3 COLLIDE-PUSS-SPHERE-STRIDE))
(m t1 prim-max)
(nop!)
(pcgt.w union-min union-min t1)
(mmi-nop!)
(pcgt.w union-max t0 union-max)
(mmi-nop!)
(or.q union-min union-min union-max)
(mmi-nop!)
(ppach union-min r0 union-min)
(mmi-nop!)
(sll union-min union-min 16)
(nop!)
(b.nz union-min no-hit :delay (nop!)))
;; Each VU expression leaves distance-squared minus combined-radius-squared in w. Moving the
;; vector to an EE register therefore makes a signed <= 0 test sufficient for overlap.
(asm-block test-four-spheres
(label next-four)
(sub.vf delta-0 sphere-0 prim-sphere)
(add.i sphere-cursor sphere-cursor COLLIDE-PUSS-FOUR-SPHERE-STRIDE)
(sub.vf delta-1 sphere-1 prim-sphere)
(l.vf sphere-0 sphere-cursor (offset-of collide-puss-sphere bsphere))
(sub.vf delta-2 sphere-2 prim-sphere)
(l.vf sphere-1 sphere-cursor COLLIDE-PUSS-SPHERE-STRIDE)
(sub.vf delta-3 sphere-3 prim-sphere)
(l.vf sphere-2 sphere-cursor (* 2 COLLIDE-PUSS-SPHERE-STRIDE))
(mul.vf delta-0 delta-0 delta-0)
(l.vf sphere-3 sphere-cursor (* 3 COLLIDE-PUSS-SPHERE-STRIDE))
(mul.vf delta-1 delta-1 delta-1)
(nop!)
(mul.vf delta-2 delta-2 delta-2)
(nop!)
(mul.vf delta-3 delta-3 delta-3)
(nop!)
(mula.x.vf zero delta-0)
(nop!)
(madda.y.vf zero delta-0)
(nop!)
(madda.z.vf zero delta-0)
(nop!)
(msub.w.vf delta-0 zero delta-0)
(nop!)
(mula.x.vf zero delta-1)
(nop!)
(madda.y.vf zero delta-1)
(nop!)
(madda.z.vf zero delta-1)
(nop!)
(msub.w.vf delta-1 zero delta-1)
(nop!)
(m a1 delta-0)
(add.i tested-count tested-count 1)
(b.le a1 r0 hit :delay (mula.x.vf zero delta-2))
(b.eq tested-count sphere-count no-hit :delay (madda.y.vf zero delta-2))
(madda.z.vf zero delta-2)
(nop!)
(msub.w.vf delta-2 zero delta-2)
(nop!)
(m a1 delta-1)
(add.i tested-count tested-count 1)
(b.le a1 r0 hit :delay (mula.x.vf zero delta-3))
(b.eq tested-count sphere-count no-hit :delay (madda.y.vf zero delta-3))
(madda.z.vf zero delta-3)
(nop!)
(msub.w.vf delta-3 zero delta-3)
(nop!)
(m a1 delta-2)
(add.i tested-count tested-count 1)
(b.le a1 r0 hit :delay (nop!))
(b.eq tested-count sphere-count no-hit :delay (nop!))
(m a1 delta-3)
(add.i tested-count tested-count 1)
(b.le a1 r0 hit :delay (nop!))
(b.ne tested-count sphere-count next-four :delay (nop!))
(b no-hit :delay (nop!)))
(asm-block return-result
(label hit)
(add.i v1 s7 #t)
(m v0 v1)
(b done-sphere-test :delay (nop!))
(label no-hit)
(m v0 s7)
(b done-sphere-test :delay (nop!))
(m v0 r0)
(label done-sphere-test)
(jr ra :delay (add sp sp r0))
(nop! :count 2))))
(defmethod check-mesh-prim-against-spheres ((this collide-puss-work) (mesh-prim collide-cache-prim) (params collide-using-spheres-params))
"Return true when any query sphere overlaps a triangle in mesh-prim. Cache each candidate
triangle's box and unit normal in this while testing its spheres."
(declare (asm-func symbol))
(rlet ((work :reg gp :type collide-puss-work)
(params-reg :reg s5 :type collide-using-spheres-params)
(tri-cursor :reg s3 :type collide-cache-tri)
(tri-count :reg s4)
(sphere-cursor :reg s2 :type collide-puss-sphere)
(sphere-count :reg s1)
(sphere-index :reg s0)
(vertex-a :reg vf1)
(vertex-b :reg vf2)
(vertex-c :reg vf3)
(edge-ab :reg vf4)
(edge-ac :reg vf5)
(tri-min :reg vf6)
(tri-max :reg vf7)
(tri-normal :reg vf8)
(normal-length-squared :reg vf9)
(closest-point :reg vf10)
(query-sphere :reg vf11)
(distance-squared :reg vf9)
(Q :reg Q))
(asm-block save-state
(add.i sp sp -128)
(s.d ra sp)
(s.q s0 sp 16)
(s.q s1 sp 32)
(s.q s2 sp 48)
(s.q s3 sp 64)
(s.q s4 sp 80)
(s.q s5 sp 96)
(s.q gp sp 112)
(m work a0)
(m params-reg a2)
(nop!))
(rlet ((prim :reg a1 :type collide-cache-prim)
(cache :reg a0 :type collide-cache)
(first-tri :reg v1)
(first-tri-offset :reg v1))
(asm-block find-first-triangle
(l.wu.field cache prim (ccache))
(nop!)
(l.hu.field first-tri prim (first-tri))
(add.i cache cache COLLIDE-CACHE-TRIS)
(l.hu.field tri-count prim (num-tris))
(sll first-tri-offset first-tri 6)
(nop!)
(add tri-cursor cache first-tri-offset)
(nop!)))
;; The union box rejects most triangles. A surviving triangle gets a normalized face normal,
;; then its own box is compared with each sphere box before the closest-point calculation.
(asm-block prepare-triangle
(label next-triangle)
(b.z tri-count no-mesh-hit :delay (l.vf vertex-a tri-cursor))
(add.i tri-count tri-count -1)
(l.vf vertex-b tri-cursor 16)
(nop!)
(l.vf vertex-c tri-cursor 32)
(sub.vf edge-ab vertex-b vertex-a)
(l.q a1 work (offset-of collide-puss-work spheres-bbox4w min))
(sub.vf edge-ac vertex-c vertex-a)
(l.q v1 work (offset-of collide-puss-work spheres-bbox4w max))
(min.vf tri-min vertex-a vertex-b)
(nop!)
(max.vf tri-max vertex-a vertex-b)
(nop!)
(outer.product.a.vf acc edge-ab edge-ac)
(nop!)
(move.vf tri-normal vf0)
(nop!)
(min.vf tri-min tri-min vertex-c)
(nop!)
(max.vf tri-max tri-max vertex-c)
(nop!)
(outer.product.b.vf tri-normal edge-ac edge-ab acc)
(nop!)
(ftoi.vf tri-min tri-min)
(nop!)
(ftoi.vf tri-max tri-max)
(nop!)
(mul.vf normal-length-squared tri-normal tri-normal)
(nop!)
(m a0 tri-min)
(nop!)
(m a2 tri-max)
(nop!)
(mula.x.vf.w vf0 normal-length-squared)
(s.vf tri-min work (offset-of collide-puss-work tri-bbox4w min))
(madda.y.vf.w vf0 normal-length-squared)
(s.vf tri-max work (offset-of collide-puss-work tri-bbox4w max))
(madd.z.vf.w normal-length-squared vf0 normal-length-squared)
(nop!)
(pcgt.w a1 a1 a2)
(mmi-nop!)
(pcgt.w v1 a0 v1)
(mmi-nop!)
(or.q v1 a1 v1)
(mmi-nop!)
(rsqrt.w.w Q vf0 normal-length-squared)
(nop!)
(ppach v1 r0 v1)
(mmi-nop!)
(sll v1 v1 16)
(nop!)
(b.nzl v1 next-triangle :delay (add.i tri-cursor tri-cursor COLLIDE-CACHE-TRI-STRIDE))
(waitq)
(nop!)
(mulq.vf.xyz tri-normal tri-normal Q)
(add.i sphere-cursor work (offset-of collide-puss-work spheres))
(nop!)
(l.wu.field sphere-count params-reg (num-spheres))
(m sphere-index r0)
(s.vf tri-normal work (offset-of collide-puss-work tri-normal)))
(asm-block test-spheres-against-triangle
(label next-sphere)
(b.eql sphere-index sphere-count next-triangle :delay (add.i tri-cursor tri-cursor COLLIDE-CACHE-TRI-STRIDE))
(add.i sphere-index sphere-index 1)
(l.q a1 sphere-cursor (offset-of collide-puss-sphere bbox4w min))
(nop!)
(l.q a2 work (offset-of collide-puss-work tri-bbox4w max))
(nop!)
(l.q v1 sphere-cursor (offset-of collide-puss-sphere bbox4w max))
(nop!)
(l.q a0 work (offset-of collide-puss-work tri-bbox4w min))
(pcgt.w a1 a1 a2)
(mmi-nop!)
(pcgt.w v1 a0 v1)
(mmi-nop!)
(or.q v1 a1 v1)
(mmi-nop!)
(ppach v1 r0 v1)
(mmi-nop!)
(sll v1 v1 16)
(nop!)
(b.nzl v1 next-sphere :delay (add.i sphere-cursor sphere-cursor COLLIDE-PUSS-SPHERE-STRIDE))
(m! t9 closest-pt-in-triangle)
(m a0 work)
(m a1 sphere-cursor)
(m a2 tri-cursor)
(add.i a3 work (offset-of collide-puss-work tri-normal))
(jalr ra t9 :delay (sll v0 ra 0))
(l.vf closest-point work (offset-of collide-puss-work closest-pt))
(l.vf query-sphere sphere-cursor (offset-of collide-puss-sphere bsphere))
(add.i sphere-cursor sphere-cursor COLLIDE-PUSS-SPHERE-STRIDE)
(sub.vf.xyz distance-squared closest-point query-sphere)
;; Put radius squared in w while leaving the sphere center's other lanes untouched.
(mul.w.vf query-sphere query-sphere query-sphere)
(mul.vf distance-squared distance-squared distance-squared)
(mula.x.vf.w vf0 distance-squared)
(madda.y.vf.w vf0 distance-squared)
(madd.z.vf.w distance-squared vf0 distance-squared)
(sub.w.vf distance-squared distance-squared query-sphere)
(m v1 distance-squared)
;; The signed distance result is in w, so move its upper doubleword down before branching.
(pcpyud v1 v1 v1)
(b.gtl v1 r0 next-sphere :delay (nop!))
(add.i v1 s7 #t)
(m v0 v1)
(b finish-mesh-sphere-test :delay (nop!)))
(asm-block return-mesh-result
(label no-mesh-hit)
(m v0 s7)
(b finish-mesh-sphere-test :delay (nop!))
(m v0 r0)
(label finish-mesh-sphere-test)
(l.d ra sp)
(l.q gp sp 112)
(l.q s5 sp 96)
(l.q s4 sp 80)
(l.q s3 sp 64)
(l.q s2 sp 48)
(l.q s1 sp 32)
(l.q s0 sp 16)
(jr ra :delay (add.i sp sp 128))
(nop! :count 3))))
(defmethod resolve-moving-sphere-tri ((this collide-cache-prim) (result collide-tri-result) (moving-sphere collide-prim-core) (move vector) (best-u float) (action collide-action))
"Sweep moving-sphere against this cached primitive's triangles. Keep the nearest compatible
hit before best-u, update result, and return its fraction or COLLISION_MISS."
(declare (asm-func float))
(rlet ((function-base :reg fp)
(result-reg :reg gp :type collide-tri-result)
(sphere-reg :reg s5 :type collide-prim-core)
(move-reg :reg s4 :type vector)
(work :reg s3 :type lsmi-work)
(tri-cursor :reg s2 :type collide-cache-tri)
(remaining :reg s1)
(zero :reg f0)
(current-best :reg f1)
(candidate :reg f2)
(facing :reg f3)
(side :reg f4)
(move-vf :reg vf1)
(hit-normal :reg vf2)
(hit-point :reg vf3)
(sphere-center :reg vf4)
(move-dot-normal :reg vf5)
(side-dot-normal :reg vf6)
(center-to-hit :reg vf7)
(vertex-a :reg vf8)
(vertex-b :reg vf9)
(vertex-c :reg vf10))
(asm-block save-state-and-find-triangles
(add.i sp sp -224)
(s.d ra sp)
(s.d function-base sp 8)
(m function-base t9)
(s.q s1 sp 128)
(s.q s2 sp 144)
(s.q s3 sp 160)
(s.q s4 sp 176)
(s.q s5 sp 192)
(s.q gp sp 208)
(m result-reg a1)
(m sphere-reg a2)
(m move-reg a3)
(add.i work sp 16)
(nop!)
(m zero r0)
(s.w.field t1 work (action))
(m current-best t0)
(l.hu a1 a0 (offset-of collide-cache-prim first-tri))
(nop!)
(l.wu v1 a0 (offset-of collide-cache-prim ccache))
(c.le.s zero current-best)
(sll a1 a1 6)
(b.fpt best-ready :delay (add.i v1 v1 COLLIDE-CACHE-TRIS))
(l.s current-best function-base collide-cache-default-best-u)
(nop!)
(label best-ready)
(add tri-cursor v1 a1)
(s.s current-best work (offset-of lsmi-work best-u))
(nop!)
(l.hu remaining a0 (offset-of collide-cache-prim num-tris))
(nop!)
(s.s current-best work (offset-of lsmi-work orig-best-u)))
;; moving-sphere-triangle-intersect writes the contact point and face normal into the result
;; workspace embedded in lsmi-work. Negative fractions and contacts no earlier than the best
;; one already found are discarded.
(asm-block intersect-triangles
(label next-cached-triangle)
(b.z remaining finished-triangles :delay (add.i remaining remaining -1))
(m! t9 moving-sphere-triangle-intersect)
(m a0 sphere-reg)
(m a1 move-reg)
(l.s zero sphere-reg (offset-of collide-prim-core world-sphere 3))
(m a2 zero)
(m a3 tri-cursor)
(add.i t0 work (offset-of lsmi-work tri-out intersect))
(add.i t1 work (offset-of lsmi-work tri-out normal))
(jalr ra t9 :delay (sll v0 ra 0))
(m v1 v0)
(nop!)
(m zero r0)
(l.vf move-vf move-reg)
(m candidate v1)
(l.s current-best work (offset-of lsmi-work best-u))
(c.lt.s candidate zero)
(l.vf hit-normal work (offset-of lsmi-work tri-out normal))
(b.fptl next-cached-triangle :delay (add.i tri-cursor tri-cursor COLLIDE-CACHE-TRI-STRIDE))
(c.le.s current-best candidate)
(l.vf hit-point work (offset-of lsmi-work tri-out intersect))
(b.fptl next-cached-triangle :delay (add.i tri-cursor tri-cursor COLLIDE-CACHE-TRI-STRIDE)))
;; Solid contacts must approach the triangle's front side and start on that side. Non-solid
;; actions accept the intersection without this one-sided test.
(asm-block check-one-sided-contact
(mul.vf move-dot-normal move-vf hit-normal)
(l.vf sphere-center sphere-reg (offset-of collide-prim-core world-sphere))
(nop!)
(l.wu v1 work (offset-of lsmi-work action))
(sub.vf center-to-hit sphere-center hit-point)
(and.i v1 v1 1)
(b.z v1 save-triangle :delay (add.y.vf.x move-dot-normal move-dot-normal move-dot-normal))
(mul.vf side-dot-normal center-to-hit hit-normal)
(nop!)
(add.z.vf.x move-dot-normal move-dot-normal move-dot-normal)
(nop!)
(add.y.vf.x side-dot-normal side-dot-normal side-dot-normal)
(nop!)
(m v1 move-dot-normal)
(nop!)
(m facing v1)
(nop!)
(c.le.s zero facing)
(nop!)
(b.fptl next-cached-triangle :delay (add.i tri-cursor tri-cursor COLLIDE-CACHE-TRI-STRIDE))
(add.z.vf.x side-dot-normal side-dot-normal side-dot-normal)
(nop!)
(m v1 side-dot-normal)
(nop!)
(m side v1)
(nop!)
(c.lt.s side zero)
(nop!)
(b.fptl next-cached-triangle :delay (add.i tri-cursor tri-cursor COLLIDE-CACHE-TRI-STRIDE)))
(asm-block save-triangle-result
(label save-triangle)
(l.vf vertex-a tri-cursor (offset-of collide-cache-tri vertex 0))
(l.vf vertex-b tri-cursor (offset-of collide-cache-tri vertex 1))
(l.vf vertex-c tri-cursor (offset-of collide-cache-tri vertex 2))
(l.wu v1 tri-cursor (offset-of collide-cache-tri pat))
(s.s candidate work (offset-of lsmi-work best-u))
(s.vf hit-point result-reg (offset-of collide-tri-result intersect))
(s.vf hit-normal result-reg (offset-of collide-tri-result normal))
(s.vf vertex-a result-reg (offset-of collide-tri-result vertex 0))
(s.vf vertex-b result-reg (offset-of collide-tri-result vertex 1))
(s.vf vertex-c result-reg (offset-of collide-tri-result vertex 2))
(s.w v1 result-reg (offset-of collide-tri-result pat))
(b next-cached-triangle :delay (add.i tri-cursor tri-cursor COLLIDE-CACHE-TRI-STRIDE)))
(asm-block return-triangle-result
(label finished-triangles)
(l.s current-best work (offset-of lsmi-work best-u))
(l.s candidate work (offset-of lsmi-work orig-best-u))
(c.eq.s current-best candidate)
(b.fpfl restore-triangle-state :delay (m v0 current-best))
(l.w v0 function-base collide-cache-miss)
(label restore-triangle-state)
(l.d ra sp)
(l.d function-base sp 8)
(l.q gp sp 208)
(l.q s5 sp 192)
(l.q s4 sp 176)
(l.q s3 sp 160)
(l.q s2 sp 144)
(l.q s1 sp 128)
(jr ra :delay (add.i sp sp 224))
(nop! :count 3))))
(defmethod resolve-moving-sphere-sphere ((this collide-cache-prim) (result collide-tri-result) (moving-sphere collide-prim-core) (move vector) (best-u float) (action collide-action))
"Sweep moving-sphere against this cached sphere primitive. Keep a compatible hit before best-u,
write a tangent triangle and contact data to result, and return its fraction or COLLISION_MISS."
(declare (asm-func float))
(rlet ((prim :reg s3 :type collide-cache-prim)
(result-reg :reg gp :type collide-tri-result)
(move-reg :reg s5 :type vector)
(best-u-bits :reg s2)
(action-reg :reg s4)
(contact-out :reg s1)
(function-base :reg fp)
(fraction :reg f3)
(miss :reg f4)
(zero :reg f0)
(fraction-copy :reg f1)
(best-u-float :reg f2)
(contact-point :reg vf4)
(prim-sphere :reg vf5)
(contact-normal :reg vf1)
(move-vf :reg vf15)
(facing-products :reg vf16)
(normal-length-squared :reg vf12)
(absolute-normal :reg vf13)
(basis-length-squared :reg vf14)
(tangent :reg vf2)
(bitangent :reg vf3)
(triangle-a :reg vf9)
(triangle-b :reg vf10)
(triangle-c :reg vf11)
(Q :reg Q))
(asm-block save-state-and-intersect
(add.i sp sp -128)
(s.d ra sp)
(s.d function-base sp 8)
(m function-base t9)
(s.q s1 sp 32)
(s.q s2 sp 48)
(s.q s3 sp 64)
(s.q s4 sp 80)
(s.q s5 sp 96)
(s.q gp sp 112)
(m prim a0)
(m result-reg a1)
(m move-reg a3)
(m best-u-bits t0)
(m action-reg t1)
(add.i contact-out sp 16)
(m! t9 moving-sphere-sphere-intersect)
(m a0 a2)
(m a1 move-reg)
(m a2 prim)
(m a3 contact-out)
(jalr ra t9 :delay (sll v0 ra 0))
(m fraction v0)
(l.s miss function-base collide-cache-miss)
(m zero r0)
(l.vf contact-point contact-out)
(mov.s fraction-copy fraction)
(l.vf prim-sphere prim (offset-of collide-cache-prim world-sphere))
(c.lt.s fraction-copy zero)
(move.vf contact-normal vf0)
(b.fpt restore-sphere-state :delay (m best-u-float best-u-bits))
(c.lt.s best-u-float zero)
(sub.vf.xyz contact-normal contact-point prim-sphere))
(rlet ((source-prim :reg v1 :type collide-shape-prim-sphere))
(asm-block reject-late-or-back-contact
(b.fpt no-best-limit :delay (l.wu source-prim prim (offset-of collide-cache-prim prim)))
(c.le.s best-u-float fraction-copy)
(and.i a0 action-reg 1)
(b.fptl restore-sphere-state :delay (mov.s fraction miss))
(label no-best-limit)
(b.z a0 build-sphere-result :delay (l.vf move-vf move-reg))
(mul.vf facing-products move-vf contact-normal)
(add.x.vf.y facing-products facing-products facing-products)
(add.z.vf.y facing-products facing-products facing-products)
(m a0 facing-products)
(b.gel a0 r0 restore-sphere-state :delay (mov.s fraction miss))
(label build-sphere-result)
(add.i a0 function-base collide-cache-sphere-contact-triangle)
(nop!)
(mul.vf normal-length-squared contact-normal contact-normal)
(s.vf contact-point result-reg (offset-of collide-tri-result intersect))
(mula.x.vf.w vf0 normal-length-squared)
(l.vf triangle-a a0)
(madda.y.vf.w vf0 normal-length-squared)
(l.vf triangle-b a0 16)
(madd.z.vf.w normal-length-squared vf0 normal-length-squared)
(l.vf triangle-c a0 32)
(rsqrt.w.w Q vf0 normal-length-squared)
(nop!)
(waitq)
(l.wu.field v1 source-prim (pat))
(mulq.vf.xyz contact-normal contact-normal Q)
(nop!)
(mul.vf basis-length-squared contact-normal contact-normal)
(s.vf contact-normal result-reg (offset-of collide-tri-result normal))
(abs.vf absolute-normal contact-normal)
(s.w v1 result-reg (offset-of collide-tri-result pat))
(move.vf tangent vf0)
(nop!)
(add.y.vf.x basis-length-squared basis-length-squared basis-length-squared)
(m v1 absolute-normal)
(b.eql v1 r0 tangent-from-z :delay (add.z.vf.x tangent vf0 contact-normal))
(sub.y.vf.x tangent vf0 contact-normal)
(nop!)
(rsqrt.w.x Q vf0 basis-length-squared)
(nop!)
(add.x.vf.y tangent vf0 contact-normal)
(nop!)
(waitq)
(nop!)
(mulq.vf.xy tangent tangent Q)
(nop!)
(label tangent-from-z)
(outer.product.a.vf acc contact-normal tangent)
(nop!)
(outer.product.b.vf bitangent tangent contact-normal acc)
(nop!)))
;; Rotate the template by the contact-normal/tangent/bitangent basis. vf0.w translates every
;; point by contact-point while the xyz lanes apply the three basis vectors.
(asm-block build-contact-triangle
(mula.w.vf contact-point vf0)
(nop!)
(madda.x.vf contact-normal triangle-a)
(nop!)
(madda.y.vf tangent triangle-a)
(nop!)
(madd.z.vf.xyz triangle-a bitangent triangle-a)
(nop!)
(mula.w.vf contact-point vf0)
(nop!)
(madda.x.vf contact-normal triangle-b)
(nop!)
(madda.y.vf tangent triangle-b)
(nop!)
(madd.z.vf.xyz triangle-b bitangent triangle-b)
(nop!)
(mula.w.vf contact-point vf0)
(nop!)
(madda.x.vf contact-normal triangle-c)
(nop!)
(madda.y.vf tangent triangle-c)
(nop!)
(madd.z.vf.xyz triangle-c bitangent triangle-c)
(nop! :count 2)
(s.vf triangle-a result-reg (offset-of collide-tri-result vertex 0))
(nop!)
(s.vf triangle-b result-reg (offset-of collide-tri-result vertex 1))
(nop!)
(s.vf triangle-c result-reg (offset-of collide-tri-result vertex 2))
(m v1 r0))
(asm-block return-sphere-result
(label restore-sphere-state)
(m v0 fraction)
(l.d ra sp)
(l.d function-base sp 8)
(l.q gp sp 112)
(l.q s5 sp 96)
(l.q s4 sp 80)
(l.q s3 sp 64)
(l.q s2 sp 48)
(l.q s1 sp 32)
(jr ra :delay (add.i sp sp 128))
(nop! :count 2))))
(defmethod puyp-mesh ((this collide-cache) (work collide-puyp-work) (mesh-prim collide-cache-prim))
"Cast work's downward probe through mesh-prim's triangles and retain the nearest front-face hit
before work.best-u."
(declare (asm-func none))
(rlet ((cache :reg a0 :type collide-cache)
(work-reg :reg a1 :type collide-puyp-work)
(prim :reg a2 :type collide-cache-prim)
(first-tri :reg a3)
(remaining :reg v1)
(first-tri-offset :reg a2)
(best-u-float :reg f4))
(asm-block find-probe-triangles
(nop!)
(add.i cache cache COLLIDE-CACHE-TRIS)
(l.hu.field first-tri prim (first-tri))
(nop!)
(l.hu.field remaining prim (num-tris))
(sll first-tri-offset first-tri 6)
(l.s.field best-u-float work-reg (best-u))
(add cache cache first-tri-offset)
(nop!)))
(rlet ((tri-cursor :reg a0 :type collide-cache-tri)
(work-reg :reg a1 :type collide-puyp-work)
(remaining :reg v1)
(best-u-float :reg f4)
(fraction-numerator :reg f1)
(fraction-denominator :reg f2)
(fraction :reg f3)
(zero :reg f6)
(approach :reg f5)
(vertex-a :reg vf1)
(vertex-b :reg vf2)
(vertex-c :reg vf3)
(probe-start :reg vf4)
(edge-ab :reg vf5)
(edge-cb :reg vf6)
(start-to-b :reg vf7)
(move-length-squared :reg vf8)
(unit-normal :reg vf9)
(normal-length-squared :reg vf10)
(move-dot-normal :reg vf11)
(fraction-vf :reg vf12)
(intersection :reg vf13)
(b-to-intersection :reg vf14)
(intersection-to-c :reg vf15)
(intersection-to-a :reg vf16)
(edge-test-0 :reg vf17)
(edge-test-1 :reg vf18)
(edge-test-2 :reg vf19)
(probe-move :reg vf20)
(approach-products :reg vf21)
(Q :reg Q))
;; The PAT mask is checked before any division. The triangle cursor advances early, so later
;; stores intentionally address the accepted triangle at -64 and its PAT at -16.
(asm-block intersect-probe-plane
(label next-probe-triangle)
(b.z remaining finish-y-probe :delay (l.vf vertex-a tri-cursor))
(add.i remaining remaining -1)
(l.vf vertex-b tri-cursor (offset-of collide-cache-tri vertex 1))
(sub.vf edge-ab vertex-b vertex-a)
(l.vf vertex-c tri-cursor (offset-of collide-cache-tri vertex 2))
(sub.vf edge-cb vertex-b vertex-c)
(l.vf probe-start work-reg (offset-of collide-puyp-work start-pos))
(nop!)
(l.vf probe-move work-reg (offset-of collide-puyp-work move-dist))
(sub.vf start-to-b vertex-b probe-start)
(l.wu a2 tri-cursor (offset-of collide-cache-tri pat))
(mul.vf move-length-squared probe-move probe-move)
(l.wu.field a3 work-reg (ignore-pat))
(outer.product.a.vf acc edge-cb edge-ab)
(add.i tri-cursor tri-cursor COLLIDE-CACHE-TRI-STRIDE)
(outer.product.b.vf unit-normal edge-ab edge-cb acc)
(and a2 a2 a3)
(b.nz a2 next-probe-triangle :delay (add.y.vf.x move-length-squared move-length-squared move-length-squared))
(mul.vf normal-length-squared unit-normal unit-normal)
(nop!)
(mul.vf start-to-b start-to-b unit-normal)
(nop!)
(mul.vf move-dot-normal probe-move unit-normal)
(nop!)
(add.z.vf.x move-length-squared move-length-squared move-length-squared)
(nop!)
(add.y.vf.x normal-length-squared normal-length-squared normal-length-squared)
(nop!)
(add.y.vf.x start-to-b start-to-b start-to-b)
(nop!)
(add.y.vf.x move-dot-normal move-dot-normal move-dot-normal)
(nop!)
(add.z.vf.x normal-length-squared normal-length-squared normal-length-squared)
(nop!)
(add.z.vf.x start-to-b start-to-b start-to-b)
(nop!)
(add.z.vf.x move-dot-normal move-dot-normal move-dot-normal)
(nop!)
(rsqrt.w.x Q vf0 normal-length-squared)
(nop!)
(m a2 move-dot-normal)
(nop!)
(m a3 start-to-b)
(m fraction-denominator a2)
(m fraction-numerator a3)
(div.s fraction fraction-numerator fraction-denominator)
(move.w.vf unit-normal vf0)
(sll a2 a2 1)
(b.z a2 next-probe-triangle :delay (nop!))
(waitq)
(nop!)
(mulq.vf.xyz unit-normal unit-normal Q)
(m a2 fraction)
(b.lt a2 r0 next-probe-triangle :delay (nop!))
(m fraction-vf a2)
(nop!)
(c.le.s best-u-float fraction)
(nop!)
(b.fpt next-probe-triangle :delay (mula.x.vf probe-move fraction-vf))
(madd.w.vf intersection probe-start vf0)
(nop!))
;; Intersecting the plane is not enough: require motion into the triangle's front face and
;; require the point to lie on the nonnegative side of all three oriented edges.
(asm-block test-triangle-interior
(sub.vf b-to-intersection vertex-b intersection)
(nop!)
(sub.vf intersection-to-c intersection vertex-c)
(nop!)
(sub.vf intersection-to-a intersection vertex-a)
(nop!)
(outer.product.a.vf acc edge-cb b-to-intersection)
(nop!)
(mul.vf approach-products probe-move unit-normal)
(nop!)
(outer.product.b.vf edge-test-0 b-to-intersection edge-cb acc)
(nop!)
(add.y.vf.x approach-products approach-products approach-products)
(nop!)
(outer.product.a.vf acc b-to-intersection edge-ab)
(nop!)
(add.z.vf.x approach-products approach-products approach-products)
(nop!)
(m zero r0)
(nop!)
(outer.product.b.vf edge-test-1 edge-ab b-to-intersection acc)
(nop!)
(m a2 approach-products)
(nop!)
(m approach a2)
(nop!)
(c.le.s zero approach)
(nop!)
(b.fpt next-probe-triangle :delay (outer.product.a.vf acc intersection-to-c intersection-to-a))
(outer.product.b.vf edge-test-2 intersection-to-a intersection-to-c acc)
(nop!)
(mul.vf.xyz edge-test-0 edge-test-0 unit-normal)
(nop!)
(mul.vf.xyz edge-test-1 edge-test-1 unit-normal)
(nop!)
(mul.vf.xyz edge-test-2 edge-test-2 unit-normal)
(nop!)
(add.x.vf.y edge-test-0 edge-test-0 edge-test-0)
(nop!)
(add.x.vf.y edge-test-1 edge-test-1 edge-test-1)
(nop!)
(add.x.vf.y edge-test-2 edge-test-2 edge-test-2)
(nop!)
(add.z.vf.y edge-test-0 edge-test-0 edge-test-0)
(nop!)
(add.z.vf.y edge-test-1 edge-test-1 edge-test-1)
(nop!)
(add.z.vf.y edge-test-2 edge-test-2 edge-test-2)
(nop!)
(m a3 edge-test-0)
(nop!)
(m t0 edge-test-1)
(nop!)
(m a2 edge-test-2)
(nop!)
(or a3 a3 t0)
(nop!)
(or a3 a3 a2)
(l.wu.field a2 work-reg (tri-out))
(b.lt a3
r0
next-probe-triangle
:delay
(l.wu a3 tri-cursor (- (offset-of collide-cache-tri pat) COLLIDE-CACHE-TRI-STRIDE))))
(asm-block save-probe-hit
(nop!)
(s.s.field fraction work-reg (best-u))
(mov.s best-u-float fraction)
(s.vf vertex-a a2 (offset-of collide-tri-result vertex 0))
(nop!)
(s.vf vertex-b a2 (offset-of collide-tri-result vertex 1))
(nop!)
(s.vf vertex-c a2 (offset-of collide-tri-result vertex 2))
(nop!)
(s.vf unit-normal a2 (offset-of collide-tri-result normal))
(nop!)
(s.w a3 a2 (offset-of collide-tri-result pat))
(b next-probe-triangle :delay (s.vf intersection a2 (offset-of collide-tri-result intersect))))
(asm-block finish-probe (label finish-y-probe) (m v0 r0) (jr ra :delay (add sp sp r0)) (nop! :count 3)))))
;; A mesh primitive keeps its triangles in local space. Before filtering, the mesh vertex
;; buffer is transformed into scratchpad records containing each world-space vertex followed by
;; its integer form. Box and Y-probe fills use the ordinary joint transform. The line-sphere
;; fill also applies *collide-work*'s inverse query transform to the integer copy, so the same
;; packed AABB test runs in the oriented query's local frame while the float copy remains in world
;; space for the collision cache.
;;
;; Each accepted triangle appends one collide-cache-tri: three float vertices, the PAT word, and
;; the owning cache-primitive index. A mesh cache primitive is appended only when at least one
;; triangle survives. The EE limits are 100 primitives and 460 triangles. Primitive
;; overflow is always reported; triangle overflow is reported once through the shared warning
;; latch.
(#unless PC_PORT
(defmethod add-fg-prim-using-box ((obj collide-shape-prim-mesh) (cache collide-cache))
"Transform this mesh to world space, append triangles whose integer bounds overlap the active
box and whose PAT is not ignored, then append one cache primitive for the accepted range."
(rlet ((mesh-prim :reg s5 :type collide-shape-prim-mesh)
(ccache :reg gp :type collide-cache)
(cache-prim :reg s4 :type collide-cache-prim))
(asm-block enter
(add.i sp sp -80)
(s.d ra sp)
(s.d fp sp 8)
(m fp t9)
(s.q s3 sp 16)
(s.q s4 sp 32)
(s.q s5 sp 48)
(s.q gp sp 64)
(m mesh-prim a0)
(m ccache a1)
(nop! :count 2))
;; Reserve the cache-primitive slot before transforming. A null mesh contributes nothing.
(rlet ((mesh :reg s3 :type collide-mesh))
(asm-block prepare-mesh
(l.wu mesh (-> mesh-prim mesh))
(lea.field v1 ccache (prims))
(l.wu a0 (-> ccache num-prims))
(b.eq mesh s7 return :delay (nop!))
(add.i a1 r0 100)
(sll a2 a0 1)
(b.eq a0 a1 primitive-capacity-error :delay (add a0 a2 a0))
(sll a0 a0 4)
(add cache-prim v1 a0)
;; transform-verts-1-matrix! writes [float vertex, integer vertex] pairs to scratchpad.
(m a0 mesh)
(l.wu v1 a0 -4)
(l.wu t9 v1 (method-offset collide-mesh transform-verts-1-matrix!))
(rlet ((shape :reg v1 :type collide-shape)) (l.wu shape (-> mesh-prim cshape)) (l.wu v1 (-> shape process)))
(rlet ((draw-process :reg v1 :type process-drawable)) (l.wu v1 (-> draw-process node-list)))
(l.b a1 (-> mesh-prim transform-index))
(sll a1 a1 5)
(add v1 v1 a1)
(l.wu v1 v1 (offset-of cspace param2))
(add a1 r0 v1)
(lui a2 #x7000)
(jalr ra t9 :delay (sll v0 ra 0))
(m v1 v0)
(nop! :count 2)
(rlet ((total-tris :reg v1)
(first-tri :reg a0)
(max-tris :reg a1)
(mesh-tri :reg a2 :type collide-mesh-tri)
(tris-left :reg a3)
(cache-tri :reg t1 :type collide-cache-tri)
(query-min :reg t0 :class i128)
(query-max :reg t2 :class i128)
(scratch-base :reg t3)
(ignore-mask :reg t4)
(cache-meta :reg vf1 :class vf))
(asm-block prepare-filter
;; a0 first carries the primitive index for cache-meta, then the remaining
;; triangle capacity. It becomes first-tri after the capacity check.
(l.wu a0 (-> ccache num-prims))
(add.i max-tris r0 460)
(l.wu total-tris (-> ccache num-tris))
(sll32 a0 a0 0)
(nop!)
(m cache-meta a0)
(nop!)
(sub a0 max-tris total-tris)
(sll cache-tri total-tris 6)
(b.lt a0 r0 triangle-capacity-error :delay (lea.field mesh-tri mesh (tris)))
(m first-tri total-tris)
(l.wu tris-left (-> mesh num-tris))
(lea.field t2 ccache (tris))
(l.q query-min (-> ccache collide-box4w min quad))
(add cache-tri t2 cache-tri)
(l.q query-max (-> ccache collide-box4w max quad))
(lui scratch-base #x7000)
(l.wu ignore-mask (-> ccache ignore-mask))
;; cache-meta is [0, primitive-index, 0, 0]. PAT overwrites the first word.
(vsub.zw cache-meta vf0 vf0)
(nop!))
(rlet ((vertex0 :reg t6 :class i128)
(vertex1 :reg t7 :class i128)
(vertex2 :reg t5 :class i128)
(vertex1-int :reg s3 :class i128)
(bbox-min :reg ra :class i128)
(bbox-max :reg t9 :class i128)
(surface :reg t8))
(asm-block filter-triangles
(label triangle-loop)
(b.z tris-left finish-triangles :delay (l.bu t5 (-> mesh-tri vertex-index 0)))
(add.i tris-left tris-left -1)
(l.bu t6 (-> mesh-tri vertex-index 1))
(sll t8 t5 5)
(l.bu t5 (-> mesh-tri vertex-index 2))
(sll t7 t6 5)
(add vertex0 t8 scratch-base)
(sll t5 t5 5)
(nop!)
(add vertex1 t7 scratch-base)
(l.q bbox-max vertex0 16)
(add vertex2 t5 scratch-base)
(l.q vertex1-int vertex1 16)
(min.w bbox-min bbox-max vertex1-int)
(l.q t8 vertex2 16)
(max.w bbox-max bbox-max vertex1-int)
(mmi-nop!)
(min.w bbox-min bbox-min t8)
(mmi-nop!)
(max.w t8 bbox-max t8)
(mmi-nop!)
;; Reject when any integer axis lies wholly outside the query bounds.
(pcgt.w bbox-max bbox-min query-max)
(mmi-nop!)
(pcgt.w bbox-min query-min t8)
(l.wu surface (-> mesh-tri pat))
(or.q bbox-max bbox-max bbox-min)
(l.q vertex0 vertex0)
(ppach bbox-max r0 bbox-max)
(l.q vertex1 vertex1)
(sll bbox-max bbox-max 16)
(l.q vertex2 vertex2)
(b.nz bbox-max triangle-loop :delay (add.i mesh-tri mesh-tri (type-size collide-mesh-tri)))
;; The delay slot initializes the metadata for the candidate slot. When the
;; cache is full, the branch prevents the triangle count and range from advancing.
(b.eq total-tris max-tris triangle-capacity-error :delay (s.vf cache-meta (-> cache-tri extra-quad)))
(and bbox-max surface ignore-mask)
(s.w surface (-> cache-tri pat))
(b.nz bbox-max triangle-loop :delay (s.q vertex0 (-> cache-tri vertex 0 quad)))
(nop!)
(s.q vertex1 (-> cache-tri vertex 1 quad))
(add.i total-tris total-tris 1)
(s.q vertex2 (-> cache-tri vertex 2 quad))
(b triangle-loop :delay (add.i cache-tri cache-tri (type-size collide-cache-tri)))))
;; Publish one cache primitive only if this mesh supplied at least one triangle.
(asm-block publish-range
(label finish-triangles)
(sub tris-left total-tris first-tri)
(l.wu t0 (-> ccache num-prims))
(b.z tris-left return :delay (l.q a1 (-> mesh-prim prim-core quad 0)))
(nop!)
(l.q a2 (-> mesh-prim prim-core quad 1))
(nop!)
(s.q r0 (-> cache-prim extra-quad))
(nop!)
(s.h tris-left (-> cache-prim num-tris))
(nop!)
(s.w ccache (-> cache-prim ccache))
(nop!)
(s.w mesh-prim (-> cache-prim prim))
(nop!)
(s.h first-tri (-> cache-prim first-tri))
(add.i a0 t0 1)
(s.q a1 (-> cache-prim prim-core quad 0))
(nop!)
(s.q a2 (-> cache-prim prim-core quad 1))
(nop!)
(s.w a0 (-> ccache num-prims))
(b return :delay (s.w total-tris (-> ccache num-tris)))))))
(asm-block errors
(label primitive-capacity-error)
(m! t9 format)
(add.i a0 r0 0)
(add.i a1 fp collide-cache-primitive-capacity-message)
(jalr ra t9 :delay (sll v0 ra 0))
(b return :delay (nop!))
(label triangle-capacity-error)
(m! v1 *already-printed-exeeded-max-cache-tris*)
(b.ne s7 v1 return :delay (m v1 s7))
(add.i v1 s7 #t)
(set! *already-printed-exeeded-max-cache-tris* v1)
(add.i v1 s7 debug)
(m! a0 *cheat-mode*)
(b.ne a0 v1 return :delay (m v1 s7))
(m! t9 format)
(m! a0 *stdcon*)
(add.i a1 fp collide-cache-triangle-capacity-message)
(jalr ra t9 :delay (sll v0 ra 0))
(m v1 v0))
(asm-block leave
(label return)
(m v0 r0)
(l.d ra sp)
(l.d fp sp 8)
(l.q gp sp 64)
(l.q s5 sp 48)
(l.q s4 sp 32)
(l.q s3 sp 16)
(jr ra :delay (add.i sp sp 80))
(nop! :count 3)))))
(#unless PC_PORT
(defmethod add-fg-prim-using-line-sphere ((obj collide-shape-prim-mesh) (cache collide-cache))
"Transform this mesh to world space and into the swept-sphere query frame, append triangles
overlapping the oriented query whose PAT is not ignored, then append their cache primitive."
(rlet ((mesh-prim :reg s5 :type collide-shape-prim-mesh)
(ccache :reg gp :type collide-cache)
(cache-prim :reg s4 :type collide-cache-prim))
(asm-block enter
(add.i sp sp -80)
(s.d ra sp)
(s.d fp sp 8)
(m fp t9)
(s.q s3 sp 16)
(s.q s4 sp 32)
(s.q s5 sp 48)
(s.q gp sp 64)
(m mesh-prim a0)
(m ccache a1)
(nop! :count 2))
(rlet ((mesh :reg s3 :type collide-mesh))
(asm-block prepare-mesh
(l.wu mesh (-> mesh-prim mesh))
(lea.field v1 ccache (prims))
(l.wu a0 (-> ccache num-prims))
(b.eq mesh s7 line-return :delay (nop!))
(add.i a1 r0 100)
(sll a2 a0 1)
(b.eq a0 a1 line-primitive-capacity-error :delay (add a0 a2 a0))
(sll a0 a0 4)
(add cache-prim v1 a0)
;; transform-verts-2-matrix! keeps world-space floats and creates integer coordinates
;; after the query inverse transform in the paired scratchpad slots.
(m a0 mesh)
(l.wu v1 a0 -4)
(l.wu t9 v1 (method-offset collide-mesh transform-verts-2-matrix!))
(rlet ((shape :reg v1 :type collide-shape)) (l.wu shape (-> mesh-prim cshape)) (l.wu v1 (-> shape process)))
(rlet ((draw-process :reg v1 :type process-drawable)) (l.wu v1 (-> draw-process node-list)))
(l.b a1 (-> mesh-prim transform-index))
(sll a1 a1 5)
(add v1 v1 a1)
(l.wu v1 v1 (offset-of cspace param2))
(add a1 r0 v1)
(m! v1 *collide-work*)
(rlet ((query-work :reg v1 :type collide-work)) (lea.field a2 query-work (inv-mat)))
(lui a3 #x7000)
(jalr ra t9 :delay (sll v0 ra 0))
(m v1 v0)
(m! t2 *collide-work*)
(nop!)
(rlet ((total-tris :reg v1)
(first-tri :reg a0)
(max-tris :reg a1)
(mesh-tri :reg a2 :type collide-mesh-tri)
(tris-left :reg a3)
(cache-tri :reg t1 :type collide-cache-tri)
(query-min :reg t0 :class i128)
(query-max :reg t2 :class i128)
(scratch-base :reg t3)
(ignore-mask :reg t4)
(cache-meta :reg vf1 :class vf))
(asm-block prepare-filter
;; Pack the primitive index into cache-meta before reusing a0 for capacity and the
;; first accepted triangle index.
(l.wu a0 (-> ccache num-prims))
(add.i max-tris r0 460)
(l.wu total-tris (-> ccache num-tris))
(sll32 a0 a0 0)
(nop!)
(m cache-meta a0)
(nop!)
(sub a0 max-tris total-tris)
(sll cache-tri total-tris 6)
(b.lt a0 r0 line-triangle-capacity-error :delay (lea.field mesh-tri mesh (tris)))
(m first-tri total-tris)
(l.wu tris-left (-> mesh num-tris))
(lea.field t3 ccache (tris))
(l.q query-min t2 (offset-of collide-work collide-box4w min quad))
(add cache-tri t3 cache-tri)
(l.q query-max t2 (offset-of collide-work collide-box4w max quad))
(lui scratch-base #x7000)
(l.wu ignore-mask (-> ccache ignore-mask))
(vsub.zw cache-meta vf0 vf0)
(nop!))
(rlet ((vertex0 :reg t6 :class i128)
(vertex1 :reg t7 :class i128)
(vertex2 :reg t5 :class i128)
(vertex1-int :reg s3 :class i128)
(bbox-min :reg ra :class i128)
(bbox-max :reg t9 :class i128)
(surface :reg t8))
(asm-block filter-triangles
(label line-triangle-loop)
(b.z tris-left line-finish-triangles :delay (l.bu t5 (-> mesh-tri vertex-index 0)))
(add.i tris-left tris-left -1)
(l.bu t6 (-> mesh-tri vertex-index 1))
(sll t8 t5 5)
(l.bu t5 (-> mesh-tri vertex-index 2))
(sll t7 t6 5)
(add vertex0 t8 scratch-base)
(sll t5 t5 5)
(nop!)
(add vertex1 t7 scratch-base)
(l.q bbox-max vertex0 16)
(add vertex2 t5 scratch-base)
(l.q vertex1-int vertex1 16)
(nop!)
(l.q t8 vertex2 16)
(min.w bbox-min bbox-max vertex1-int)
(mmi-nop!)
(max.w bbox-max bbox-max vertex1-int)
(mmi-nop!)
(min.w bbox-min bbox-min t8)
(mmi-nop!)
(max.w t8 bbox-max t8)
(mmi-nop!)
(pcgt.w bbox-max bbox-min query-max)
(mmi-nop!)
(pcgt.w bbox-min query-min t8)
(l.wu surface (-> mesh-tri pat))
(or.q bbox-max bbox-max bbox-min)
(l.q vertex0 vertex0)
(ppach bbox-max r0 bbox-max)
(l.q vertex1 vertex1)
(sll bbox-max bbox-max 16)
(l.q vertex2 vertex2)
(b.nz bbox-max line-triangle-loop :delay (add.i mesh-tri mesh-tri (type-size collide-mesh-tri)))
(b.eq total-tris max-tris line-triangle-capacity-error :delay (s.vf cache-meta (-> cache-tri extra-quad)))
(and bbox-max surface ignore-mask)
(s.w surface (-> cache-tri pat))
(b.nz bbox-max line-triangle-loop :delay (s.q vertex0 (-> cache-tri vertex 0 quad)))
(add.i total-tris total-tris 1)
(s.q vertex1 (-> cache-tri vertex 1 quad))
(nop!)
(s.q vertex2 (-> cache-tri vertex 2 quad))
(b line-triangle-loop :delay (add.i cache-tri cache-tri (type-size collide-cache-tri)))))
(asm-block publish-range
(label line-finish-triangles)
(sub tris-left total-tris first-tri)
(l.wu t0 (-> ccache num-prims))
(b.z tris-left line-return :delay (l.q a1 (-> mesh-prim prim-core quad 0)))
(nop!)
(l.q a2 (-> mesh-prim prim-core quad 1))
(nop!)
(s.q r0 (-> cache-prim extra-quad))
(nop!)
(s.h tris-left (-> cache-prim num-tris))
(nop!)
(s.w ccache (-> cache-prim ccache))
(nop!)
(s.w mesh-prim (-> cache-prim prim))
(nop!)
(s.h first-tri (-> cache-prim first-tri))
(add.i a0 t0 1)
(s.q a1 (-> cache-prim prim-core quad 0))
(nop!)
(s.q a2 (-> cache-prim prim-core quad 1))
(nop!)
(s.w a0 (-> ccache num-prims))
(b line-return :delay (s.w total-tris (-> ccache num-tris)))))))
(asm-block errors
(label line-primitive-capacity-error)
(m! t9 format)
(add.i a0 r0 0)
(add.i a1 fp collide-cache-primitive-capacity-message)
(jalr ra t9 :delay (sll v0 ra 0))
(b line-return :delay (nop!))
(label line-triangle-capacity-error)
(m! v1 *already-printed-exeeded-max-cache-tris*)
(b.ne s7 v1 line-return :delay (m v1 s7))
(add.i v1 s7 #t)
(set! *already-printed-exeeded-max-cache-tris* v1)
(add.i v1 s7 debug)
(m! a0 *cheat-mode*)
(b.ne a0 v1 line-return :delay (m v1 s7))
(m! t9 format)
(m! a0 *stdcon*)
(add.i a1 fp collide-cache-triangle-capacity-message)
(jalr ra t9 :delay (sll v0 ra 0))
(m v1 v0))
(asm-block leave
(label line-return)
(m v0 r0)
(l.d ra sp)
(l.d fp sp 8)
(l.q gp sp 64)
(l.q s5 sp 48)
(l.q s4 sp 32)
(l.q s3 sp 16)
(jr ra :delay (add.i sp sp 80))
(nop! :count 3)))))
(#unless PC_PORT
(defmethod add-fg-prim-using-y-probe ((obj collide-shape-prim-mesh) (cache collide-cache))
"Transform this mesh to world space, append triangles whose integer bounds overlap the active
vertical probe and whose PAT is not ignored, then append their cache primitive."
(rlet ((mesh-prim :reg s5 :type collide-shape-prim-mesh)
(ccache :reg gp :type collide-cache)
(cache-prim :reg s4 :type collide-cache-prim))
(asm-block enter
(add.i sp sp -80)
(s.d ra sp)
(s.d fp sp 8)
(m fp t9)
(s.q s3 sp 16)
(s.q s4 sp 32)
(s.q s5 sp 48)
(s.q gp sp 64)
(m mesh-prim a0)
(m ccache a1)
(nop! :count 2))
(rlet ((mesh :reg s3 :type collide-mesh))
(asm-block prepare-mesh
(l.wu mesh (-> mesh-prim mesh))
(lea.field v1 ccache (prims))
(l.wu a0 (-> ccache num-prims))
(b.eq mesh s7 y-return :delay (nop!))
(add.i a1 r0 100)
(sll a2 a0 1)
(b.eq a0 a1 y-primitive-capacity-error :delay (add a0 a2 a0))
(sll a0 a0 4)
(add cache-prim v1 a0)
(m a0 mesh)
(l.wu v1 a0 -4)
(l.wu t9 v1 (method-offset collide-mesh transform-verts-1-matrix!))
(rlet ((shape :reg v1 :type collide-shape)) (l.wu shape (-> mesh-prim cshape)) (l.wu v1 (-> shape process)))
(rlet ((draw-process :reg v1 :type process-drawable)) (l.wu v1 (-> draw-process node-list)))
(l.b a1 (-> mesh-prim transform-index))
(sll a1 a1 5)
(add v1 v1 a1)
(l.wu v1 v1 (offset-of cspace param2))
(add a1 r0 v1)
(lui a2 #x7000)
(jalr ra t9 :delay (sll v0 ra 0))
(m v1 v0)
(nop! :count 2)
(rlet ((total-tris :reg v1)
(first-tri :reg a0)
(max-tris :reg a1)
(mesh-tri :reg a2 :type collide-mesh-tri)
(tris-left :reg a3)
(cache-tri :reg t1 :type collide-cache-tri)
(query-min :reg t0 :class i128)
(query-max :reg t2 :class i128)
(scratch-base :reg t3)
(ignore-mask :reg t4)
(cache-meta :reg vf1 :class vf))
(asm-block prepare-filter
;; Pack the primitive index into cache-meta before reusing a0 for capacity and the
;; first accepted triangle index.
(l.wu a0 (-> ccache num-prims))
(add.i max-tris r0 460)
(l.wu total-tris (-> ccache num-tris))
(sll32 a0 a0 0)
(nop!)
(m cache-meta a0)
(nop!)
(sub a0 max-tris total-tris)
(sll cache-tri total-tris 6)
(b.lt a0 r0 y-triangle-capacity-error :delay (lea.field mesh-tri mesh (tris)))
(m first-tri total-tris)
(l.wu tris-left (-> mesh num-tris))
(lea.field t2 ccache (tris))
(l.q query-min (-> ccache collide-box4w min quad))
(add cache-tri t2 cache-tri)
(l.q query-max (-> ccache collide-box4w max quad))
(lui scratch-base #x7000)
(l.wu ignore-mask (-> ccache ignore-mask))
(vsub.zw cache-meta vf0 vf0)
(nop!))
(rlet ((vertex0 :reg t6 :class i128)
(vertex1 :reg t7 :class i128)
(vertex2 :reg t5 :class i128)
(vertex1-int :reg s3 :class i128)
(bbox-min :reg ra :class i128)
(bbox-max :reg t9 :class i128)
(surface :reg t8))
(asm-block filter-triangles
(label y-triangle-loop)
(b.z tris-left y-finish-triangles :delay (l.bu t5 (-> mesh-tri vertex-index 0)))
(add.i tris-left tris-left -1)
(l.bu t6 (-> mesh-tri vertex-index 1))
(sll t8 t5 5)
(l.bu t5 (-> mesh-tri vertex-index 2))
(sll t7 t6 5)
(add vertex0 t8 scratch-base)
(sll t5 t5 5)
(nop!)
(add vertex1 t7 scratch-base)
(l.q bbox-max vertex0 16)
(add vertex2 t5 scratch-base)
(l.q vertex1-int vertex1 16)
(min.w bbox-min bbox-max vertex1-int)
(l.q t8 vertex2 16)
(max.w bbox-max bbox-max vertex1-int)
(mmi-nop!)
(min.w bbox-min bbox-min t8)
(mmi-nop!)
(max.w t8 bbox-max t8)
(mmi-nop!)
(pcgt.w bbox-max bbox-min query-max)
(mmi-nop!)
(pcgt.w bbox-min query-min t8)
(l.wu surface (-> mesh-tri pat))
(or.q bbox-max bbox-max bbox-min)
(l.q vertex0 vertex0)
(ppach bbox-max r0 bbox-max)
(l.q vertex1 vertex1)
(sll bbox-max bbox-max 16)
(l.q vertex2 vertex2)
(b.nz bbox-max y-triangle-loop :delay (add.i mesh-tri mesh-tri (type-size collide-mesh-tri)))
(b.eq total-tris max-tris y-triangle-capacity-error :delay (s.vf cache-meta (-> cache-tri extra-quad)))
(and bbox-max surface ignore-mask)
(s.w surface (-> cache-tri pat))
(b.nz bbox-max y-triangle-loop :delay (s.q vertex0 (-> cache-tri vertex 0 quad)))
(add.i total-tris total-tris 1)
(s.q vertex1 (-> cache-tri vertex 1 quad))
(nop!)
(s.q vertex2 (-> cache-tri vertex 2 quad))
(b y-triangle-loop :delay (add.i cache-tri cache-tri (type-size collide-cache-tri)))))
(asm-block publish-range
(label y-finish-triangles)
(sub tris-left total-tris first-tri)
(l.wu t0 (-> ccache num-prims))
(b.z tris-left y-return :delay (l.q a1 (-> mesh-prim prim-core quad 0)))
(nop!)
(l.q a2 (-> mesh-prim prim-core quad 1))
(nop!)
(s.q r0 (-> cache-prim extra-quad))
(nop!)
(s.h tris-left (-> cache-prim num-tris))
(nop!)
(s.w ccache (-> cache-prim ccache))
(nop!)
(s.w mesh-prim (-> cache-prim prim))
(nop!)
(s.h first-tri (-> cache-prim first-tri))
(add.i a0 t0 1)
(s.q a1 (-> cache-prim prim-core quad 0))
(nop!)
(s.q a2 (-> cache-prim prim-core quad 1))
(nop!)
(s.w a0 (-> ccache num-prims))
(b y-return :delay (s.w total-tris (-> ccache num-tris)))))))
(asm-block errors
(label y-primitive-capacity-error)
(m! t9 format)
(add.i a0 r0 0)
(add.i a1 fp collide-cache-primitive-capacity-message)
(jalr ra t9 :delay (sll v0 ra 0))
(b y-return :delay (nop!))
(label y-triangle-capacity-error)
(m! v1 *already-printed-exeeded-max-cache-tris*)
(b.ne s7 v1 y-return :delay (m v1 s7))
(add.i v1 s7 #t)
(set! *already-printed-exeeded-max-cache-tris* v1)
(add.i v1 s7 debug)
(m! a0 *cheat-mode*)
(b.ne a0 v1 y-return :delay (m v1 s7))
(m! t9 format)
(m! a0 *stdcon*)
(add.i a1 fp collide-cache-triangle-capacity-message)
(jalr ra t9 :delay (sll v0 ra 0))
(m v1 v0))
(asm-block leave
(label y-return)
(m v0 r0)
(l.d ra sp)
(l.d fp sp 8)
(l.q gp sp 64)
(l.q s5 sp 48)
(l.q s4 sp 32)
(l.q s3 sp 16)
(jr ra :delay (add.i sp sp 80))
(nop! :count 3)))))
(#unless PC_PORT
;; Function-relative strings referenced by the EE blocks above.
(asm-data (label collide-cache-triangle-capacity-message)
(word #x27 #x65637845 #x64656465 #x6c6f6320 #x6564696c #x63616320 #x6d206568 #x23207861 #x20666f20 #x73697274 #x257e21)
(label collide-cache-primitive-capacity-message)
(word #x34
#x4f525245
#x45203a52
#x65656378
#x20646564
#x2078616d
#x626d756e
#x6f207265
#x6f632066
#x64696c6c
#x61632d65
#x20656863
#x6d697270
#x257e2173
0)))
(#when PC_PORT
(defmethod fill-from-background ((obj collide-cache) (bsp-find-mesh-func (function bsp-header int collide-list none)) (import-mesh-func (function collide-cache collide-frag-mesh none)))
"Collect background fragments with bsp-find-mesh-func, unpack their packed vertices, and pass
each scratchpad mesh to import-mesh-func. The cache must be empty on entry; all accepted
triangles are represented by the background primitive in slot zero."
(local-vars (a0-4 int) (a0-6 int))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Step 1: Build Collide List
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; this is a list of fragments that we will look at.
(set! (-> *collide-list* num-items) 0)
(cond
((= bsp-find-mesh-func (method-of-type bsp-header collide-ray))
;; The swept-sphere probe builds its fragment list with the optimized collision traversal.
(dotimes (probe-level-index (-> *level* length))
(let ((probe-level (-> *level* level probe-level-index)))
(when (= (-> probe-level status) 'active)
(reset! (-> *perf-stats* data 14))
(collide-probe-make-list probe-level *collide-list*)
(read! (-> *perf-stats* data 14))))))
(else
;; Box and Y-probe fills use the supplied BSP method.
(dotimes (bsp-level-index (-> *level* length))
(let ((bsp-level (-> *level* level bsp-level-index)))
(if (= (-> bsp-level status) 'active) (bsp-find-mesh-func (-> bsp-level bsp) 0 *collide-list*))))))
(when (> (-> *collide-list* num-items) 0)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Step 2: Upload to VU0 Data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; VIF0 and VU0 are emulated synchronously on PC, so process one complete fragment at a time.
(dotimes (i (-> *collide-list* num-items))
(let ((frag (-> *collide-list* items i)))
;; Upload packed vertices to VU0 memory.
(__pc-upload-collide-frag (-> frag mesh packed-data) (-> frag mesh vertex-data-qwc) (-> frag mesh vertex-count))
;; Unpack VU0 vertices into scratchpad.
(unpack-background-collide-mesh obj (-> frag mesh) (-> frag inst) 0)
;; Import accepted scratchpad triangles into the cache.
(import-mesh-func obj (-> frag mesh))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Represent the imported triangles as one primitive
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Background fill starts on an empty cache, so slot zero can own every imported triangle.
(let ((tri-count (-> obj num-tris)))
(when (> tri-count 0)
(let ((prims (-> obj prims))
(background-prim *collide-shape-prim-backgnd*))
(set! (-> prims 0 num-tris) (the-as uint tri-count))
(set! (-> prims 0 prim) background-prim)
(set! (-> obj num-prims) 1)
(set! (-> prims 0 first-tri) (the-as uint 0))
(set! (-> prims 0 ccache) obj)
(set! (-> prims 0 prim-core world-sphere quad) (-> background-prim prim-core world-sphere quad))
(set! (-> prims 0 prim-core quad 1) (-> background-prim prim-core quad 1))))))
0
(none)))
(defmethod fill-from-water ((obj collide-cache) (water water-control))
"When active water collision is enabled and capacity remains, append two waterbottom triangles
covering the active query box in XZ, plus one cached water primitive which owns them. Racers use
0.2 meters below the surface, swim-ground uses the surface minus swim-height, and other water
uses base-height minus bottom-height; jump-out water is ignored."
(rlet ((vf0 :class vf)
(vf1 :class vf)
(vf2 :class vf)
(vf3 :class vf)
(vf4 :class vf)
(vf5 :class vf))
(init-vf0-vector)
(when (= (-> obj num-prims) 100)
(if (= *cheat-mode* 'debug) (format 0 "ERROR: Exceeded max number of collide-cache prims!~%"))
(return #f))
(when (< *collide-cache-max-tris* (+ (-> obj num-tris) 2))
(when (not *already-printed-exeeded-max-cache-tris*)
(set! *already-printed-exeeded-max-cache-tris* #t)
(if (= *cheat-mode* 'debug) (format *stdcon* "Exceeded collide cache max # of tris!~%")))
(return #f))
(if (not (and (logtest? (-> water flags) (water-flag active))
(logtest? (-> water flags) (water-flag swim-ground wt07))
(not (logtest? (water-flag jump-out) (-> water flags)))))
(return #f))
(let ((bottom-y (cond
((logtest? (-> *target* control root-prim prim-core action) (collide-action racer)) (+ -819.2 (-> water height)))
((logtest? (-> water flags) (water-flag swim-ground)) (- (-> water height) (-> water swim-height)))
(else (- (-> water base-height) (-> water bottom-height))))))
(.lvf vf1 (&-> obj collide-box min quad))
(.lvf vf3 (&-> obj collide-box max quad))
(let ((prim-index (-> obj num-prims-u))
(triangles (the-as (inline-array collide-cache-tri) (-> obj tris (-> obj num-tris)))))
(.mov vf5 bottom-y)
(.add.x.vf.y vf1 vf0 vf5)
(set! (-> triangles 0 extra-quad) (the-as uint128 0))
(set! (-> triangles 0 prim-index) prim-index)
(.add.x.vf.y vf3 vf0 vf5)
(set! (-> triangles 1 extra-quad) (the-as uint128 0))
(set! (-> triangles 1 prim-index) prim-index)
(.mov.vf vf2 vf1)
(.mov.vf vf4 vf1)
(.add.z.vf.z vf2 vf0 vf3)
(.add.x.vf.x vf4 vf0 vf3)
(.svf (&-> triangles 0 vertex 0 quad) vf1)
(.svf (&-> triangles 0 vertex 1 quad) vf2)
(.svf (&-> triangles 0 vertex 2 quad) vf3)
(set! (-> triangles 0 pat) (new 'static 'pat-surface :material (pat-material waterbottom)))
(.svf (&-> triangles 1 vertex 0 quad) vf1)
(.svf (&-> triangles 1 vertex 1 quad) vf3)
(.svf (&-> triangles 1 vertex 2 quad) vf4)
(set! (-> triangles 1 pat) (new 'static 'pat-surface :material (pat-material waterbottom)))))
(let ((water-prim *collide-shape-prim-water*)
(cache-prim (-> obj prims (-> obj num-prims))))
(set! (-> cache-prim first-tri) (the-as uint (-> obj num-tris)))
(set! (-> cache-prim num-tris) (the-as uint 2))
(set! (-> cache-prim prim) water-prim)
(set! (-> cache-prim ccache) obj)
(set! (-> cache-prim prim-core world-sphere quad) (-> water-prim prim-core world-sphere quad))
(set! (-> cache-prim prim-core quad 1) (-> water-prim prim-core quad 1)))
(+! (-> obj num-prims) 1)
(+! (-> obj num-tris) 2)
(none)))
(defmethod fill-using-bounding-box ((obj collide-cache) (box bounding-box) (collide-with collide-kind) (proc process-drawable) (ignore-pat pat-surface))
"Refill the cache with anything of the given kinds touching box, skipping proc's own prims and
surfaces matching ignore-pat. Background, water and foreground are filled separately, each working
from the box state left in *collide-work*."
(rlet ((Q :class vf)
(vf0 :class vf)
(box-min :class vf)
(box-max :class vf)
(box-min-int :class vf)
(box-max-int :class vf)
(center :class vf)
(half :class vf)
(radius-sq :class vf))
(init-vf0-vector)
;; Derive a bounding sphere from the box for the quick reject: center is the corner average,
;; radius the half-diagonal. The radius is stored negated in w, so the per-candidate test gets the
;; center difference and the sum of the radii out of a single subtract. See
;; collide-cache-using-box-test.
;;
;; An integer copy of the box goes in collide-box4w. The fragment tests use that one, since pcgtw
;; compares all four lanes at once.
(let ((one-half (new 'static 'vector :x 0.5)))
(nop!)
(.lvf box-min (&-> box min quad))
(nop!)
(.lvf box-max (&-> box max quad))
(nop!)
(set! (-> obj proc) proc)
(.mov.vf.w box-min vf0)
(let ((work *collide-work*))
(.mov.vf.w box-max vf0)
(set! (-> obj ignore-mask) ignore-pat)
(.ftoi.vf box-min-int box-min)
(.lvf half (&-> one-half quad))
(.ftoi.vf box-max-int box-max)
(set! (-> obj num-tris) 0)
(.add.vf.xyz center box-max box-min)
(.svf (&-> obj collide-box min quad) box-min)
(.mul.x.vf.xyz center center half)
(.svf (&-> obj collide-box max quad) box-max)
(.sub.vf.xyz radius-sq center box-min)
(.sub.vf.w center center center)
(.mul.vf.xyz radius-sq radius-sq radius-sq)
(.svf (&-> obj collide-box4w min quad) box-min-int)
(.add.y.vf.x radius-sq radius-sq radius-sq)
(.svf (&-> obj collide-box4w max quad) box-max-int)
(.add.z.vf.x radius-sq radius-sq radius-sq)
(.svf (&-> work collide-box4w min quad) box-min-int)
(.sqrt.vf Q radius-sq :ftf #b0)
(.svf (&-> work collide-box4w max quad) box-max-int)
(set! *already-printed-exeeded-max-cache-tris* #f)
(nop!)
(.wait.vf)
(set! (-> obj num-prims) 0)
(.sub.vf.w center center Q)
(set! (-> obj collide-with) collide-with)
(nop!)
(.svf (&-> work collide-sphere-neg-r quad) center)))
0
(if (logtest? collide-with (collide-kind background))
(fill-from-background obj
(method-of-type bsp-header collide-with-box)
(method-of-type collide-cache load-mesh-from-spad-in-box)))
(if (logtest? collide-with (collide-kind water)) (fill-from-water obj (-> proc water)))
(if (logtest? collide-with (collide-kind cak-1 cak-2 cak-3 target)) (fill-from-foreground-using-box obj))
0
(none)))
(defun collide-cache-using-box-test ((bsphere vector))
"Sphere/sphere reject against the cache's bounding sphere. bsphere's radius is in w, so this
is d^2 <= (r1+r2)^2 with no branches."
(local-vars (result-bits int))
(rlet ((acc :class vf)
(vf0 :class vf)
(delta :class vf)
(cache-sphere :class vf)
(ones :class vf))
(init-vf0-vector)
(nop!)
(.max.w.vf ones vf0 vf0)
(let ((work *collide-work*))
(nop!)
(.lvf delta (&-> bsphere quad))
(nop!)
(.lvf cache-sphere (&-> work collide-sphere-neg-r quad)))
;; The work sphere holds -r in w, so this yields dxyz and r1+r2 together.
(.sub.vf delta delta cache-sphere)
(nop!)
(.mul.vf delta delta delta)
(nop!)
;; dx^2 + dy^2 + dz^2 - (r1+r2)^2, one lane at a time against an all-ones vector.
(.mul.x.vf acc ones delta)
(nop!)
(.add.mul.y.vf acc ones delta acc)
(nop!)
(.add.mul.z.vf acc ones delta acc)
(nop!)
(.sub.mul.w.vf delta ones delta acc)
(nop!)
;; Sign test in the integer unit: a negative float has its sign bit set, so it reads as a negative
;; int. Avoids the FPU compare and the branch on the coprocessor flag.
(.mov result-bits delta)
(<= (the-as int result-bits) 0)))
(defmethod collide-with-box ((obj collide-fragment) (count int) (clist collide-list))
"Append each fragment in this contiguous range whose bounding sphere and stored bounds intersect
the active box query."
(let ((cwork *collide-work*))
(dotimes (i count)
(when (and (collide-cache-using-box-test (-> obj bsphere)) (drawable-sphere-box-intersect? obj (-> cwork collide-box4w)))
(let ((item (-> clist items (-> clist num-items)))) (set! (-> item mesh) (-> obj mesh)) (set! (-> item inst) #f))
; (add-debug-sphere #t (bucket-id debug) (-> obj bsphere) (-> obj bsphere w) (new 'static 'rgba :g #x80 :a #x80))
; (format 0 "~f~%" (-> obj bsphere w))
(+! (-> clist num-items) 1))
(&+! obj 32)))
0
(none))
(defmethod collide-with-box ((obj instance-tie) (count int) (clist collide-list))
"Append every collision fragment of each enabled TIE instance in this contiguous range whose
transformed bounds intersect the active box query."
(dotimes (instance-index count)
(when (and (zero? (logand (-> obj flags) 1))
(collide-cache-using-box-test (-> obj bsphere))
(drawable-sphere-box-intersect? obj (-> *collide-work* collide-box4w)))
(let ((frag-list (-> obj bucket-ptr collide-frag)))
(when (nonzero? frag-list)
(let ((frag (the-as object (-> frag-list data))))
(dotimes (fragment-index (-> frag-list length))
(when (instance-sphere-box-intersect? (the-as collide-fragment frag) obj (-> *collide-work* collide-box4w))
(let ((item (-> clist items (-> clist num-items))))
(set! (-> item mesh) (-> (the-as collide-fragment frag) mesh))
(set! (-> item inst) obj))
(+! (-> clist num-items) 1))
(set! frag (-> (the-as (inline-array collide-fragment) frag) 1)))))))
(&+! obj 64))
0
(none))
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Y PROBE
;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod fill-using-y-probe ((obj collide-cache) (probe-origin vector) (probe-length float) (kinds collide-kind) (proc process-drawable) (ignore-pat pat-surface))
"Refill the cache for a vertical probe centered at start and extending probe-length in both Y
directions for broad-phase collection. Skip proc's own foreground primitives and surfaces
matching ignore-pat."
(rlet ((vf1 :class vf)
(vf2 :class vf)
(vf3 :class vf)
(vf4 :class vf)
(vf5 :class vf))
(set! *already-printed-exeeded-max-cache-tris* #f)
(.mov vf5 probe-length)
(.lvf vf2 (&-> probe-origin quad))
(nop!)
(let ((cwork *collide-work*))
(.mov.vf vf1 vf2)
(set! (-> obj ignore-mask) (the-as pat-surface ignore-pat))
(.add.x.vf.y vf2 vf2 vf5)
(set! (-> obj num-tris) 0)
(.sub.x.vf.y vf1 vf1 vf5)
(set! (-> obj num-prims) 0)
(nop!)
(set! (-> obj collide-with) kinds)
(nop!)
(set! (-> obj proc) proc)
(.ftoi.vf vf4 vf2)
(.svf (&-> obj collide-box max quad) vf2)
(.ftoi.vf vf3 vf1)
(.svf (&-> obj collide-box min quad) vf1)
(nop!)
(.svf (&-> obj collide-box4w max quad) vf4)
(nop!)
(.svf (&-> obj collide-box4w min quad) vf3)
(nop!)
(.svf (&-> cwork collide-box4w max quad) vf4)
(nop!)
(.svf (&-> cwork collide-box4w min quad) vf3))
(if (logtest? kinds (collide-kind background))
(fill-from-background obj
(method-of-type bsp-header collide-y-probe)
(method-of-type collide-cache load-mesh-from-spad-in-y-probe)))
(if (logtest? kinds (collide-kind water)) (fill-from-water obj (-> proc water)))
(if (logtest? kinds (collide-kind cak-1 cak-2 cak-3 target)) (fill-from-foreground-using-y-probe obj))
0
(none)))
(defun collide-cache-using-y-probe-test ((bsphere vector))
"Return whether bsphere overlaps the active vertical-probe box."
(local-vars
(zero uint128)
(v1-1 uint128)
(v1-2 uint128)
(v1-3 uint128)
(a0-1 uint128)
(a1-2 uint128)
(a2-0 uint128)
(f31-0 none))
(rlet ((vf1 :class vf)
(vf2 :class vf)
(vf3 :class vf)
(vf4 :class vf)
(vf5 :class vf))
(set! zero (the-as uint128 0))
(nop!)
(let ((a1-0 *collide-work*))
(nop!)
(.lvf vf1 (&-> bsphere quad))
(nop!)
(let ((v1-0 (-> a1-0 collide-box4w min quad)))
(.sub.w.vf.xyz vf2 vf1 vf1)
(let ((a1-1 (-> a1-0 collide-box4w max quad)))
(.add.w.vf.xyz vf3 vf1 vf1)
(nop!)
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a2-0 vf4)
(nop!)
(.mov a0-1 vf5)
(nop!)
(.pcgtw a1-2 a2-0 a1-1))
(.pcgtw v1-1 v1-0 a0-1)))
(.por v1-2 a1-2 v1-1)
(.ppach v1-3 zero v1-2)
(let ((v1-4 (shl (the-as int v1-3) 16))) (nop!) (zero? v1-4))))
(defmethod collide-y-probe ((obj collide-fragment) (count int) (clist collide-list))
"Append each fragment in this contiguous range whose bounding sphere intersects the active
vertical probe."
*collide-work*
(dotimes (i count)
(when (collide-cache-using-y-probe-test (-> obj bsphere))
(let ((item (-> clist items (-> clist num-items)))) (set! (-> item mesh) (-> obj mesh)) (set! (-> item inst) #f))
(+! (-> clist num-items) 1))
(&+! obj 32))
0
(none))
(defmethod collide-y-probe ((obj instance-tie) (count int) (clist collide-list))
"Append every collision fragment of each enabled TIE instance in this contiguous range whose
transformed bounds intersect the active vertical probe."
(dotimes (instance-index count)
(when (and (zero? (logand (-> obj flags) 1)) (collide-cache-using-y-probe-test (-> obj bsphere)))
(let ((frag-list (-> obj bucket-ptr collide-frag)))
(when (nonzero? frag-list)
(let ((frag (the-as object (-> frag-list data))))
(dotimes (fragment-index (-> frag-list length))
(when (instance-sphere-box-intersect? (the-as collide-fragment frag) obj (-> *collide-work* collide-box4w))
(let ((item (-> clist items (-> clist num-items))))
(set! (-> item mesh) (-> (the-as collide-fragment frag) mesh))
(set! (-> item inst) obj))
(+! (-> clist num-items) 1))
(set! frag (-> (the-as (inline-array collide-fragment) frag) 1)))))))
(&+! obj 64))
0
(none))
;;;;;;;;;;;;;;;;;;;;;
;; Line Sphere Test
;;;;;;;;;;;;;;;;;;;;;
;; In this mode, we collide a line of spheres.
;; The first pass sets up a non-axis-aligned bounding box in *collide-work*
;; The box is stored as an axis-aligned box, and an inv-rot to transform from world to aligned-box coordinates.
(#when PC_PORT
(defmacro pabsw-hack (out in)
`(let ((temp (new 'stack-no-clear 'array 'int32 4)))
(set! (-> (the (pointer uint128) temp)) ,in)
(set! (-> temp 0) (abs (-> temp 0)))
(set! (-> temp 1) (abs (-> temp 1)))
(set! (-> temp 2) (abs (-> temp 2)))
(set! (-> temp 3) (abs (-> temp 3)))
(set! ,out (-> (the (pointer uint128) temp))))))
(#unless PC_PORT
(defmacro pabsw-hack (out in)
`(abs.w ,out ,in)))
(defmethod fill-using-line-sphere ((obj collide-cache) (start-pt vector) (move-vec vector) (radius float) (kinds collide-kind) (proc process-drawable) (ignore-pat pat-surface))
"Refill the cache for a sphere of radius moving from start by move. Fall back to an ordinary
bounding box when every movement component is at most one meter; otherwise construct an oriented
sweep box and its inverse transform for broad-phase rejection. Skip proc's own foreground
primitives and surfaces matching ignore-pat."
(local-vars
(zero uint128)
(long-axis-mask uint128)
(packed-long-axis-mask uint128)
(direction-x-bits int)
(move-words uint128)
(abs-move-words uint128))
(rlet ((acc :class vf)
(Q :class vf)
(vf0 :class vf)
(vf1 :class vf)
(vf10 :class vf)
(vf11 :class vf)
(vf12 :class vf)
(vf13 :class vf)
(vf14 :class vf)
(vf15 :class vf)
(vf16 :class vf)
(vf17 :class vf)
(vf18 :class vf)
(vf19 :class vf)
(vf2 :class vf)
(vf20 :class vf)
(vf21 :class vf)
(vf22 :class vf)
(vf23 :class vf)
(vf3 :class vf)
(vf4 :class vf)
(vf5 :class vf)
(vf6 :class vf)
(vf7 :class vf)
(vf8 :class vf)
(vf9 :class vf))
(set! zero (the uint128 0))
(init-vf0-vector)
(let ((axis-threshold (new 'static 'vector4w :x #x1000 :y #x1000 :z #x1000)))
(nop!)
(.mov vf9 radius)
(.lvf vf3 (&-> move-vec quad))
(nop!)
(let ((threshold-words (-> axis-threshold quad)))
(.ftoi.vf vf21 vf3)
(.lvf vf1 (&-> start-pt quad))
(.mul.vf vf8 vf3 vf3)
(let ((cwork *collide-work*))
(.add.vf vf2 vf1 vf3)
(set! (-> obj ignore-mask) ignore-pat)
(.mov move-words vf21)
(nop!)
(pabsw-hack abs-move-words move-words)
(.pcgtw long-axis-mask abs-move-words threshold-words)
(.ppach packed-long-axis-mask zero long-axis-mask)
(let ((has-long-axis? (shl (the-as int packed-long-axis-mask) 16))) (b! (nonzero? has-long-axis?) cfg-3))
(let ((query-box (new 'stack-no-clear 'bounding-box)))
(set-from-point-offset-pad! query-box (the-as vector3s start-pt) (the-as vector3s move-vec) radius)
(fill-using-bounding-box obj query-box kinds proc ignore-pat))
(b! #t cfg-13 :delay (nop!))
(set! cwork (the-as collide-work 0))
(label cfg-3)
(.add.y.vf.x vf8 vf8 vf8)
(set! (-> obj num-tris) 0)
(.min.vf vf4 vf1 vf2)
(set! (-> obj num-prims) 0)
(.max.vf vf5 vf1 vf2)
(set! (-> obj collide-with) kinds)
(.sub.x.vf.xyz vf10 vf0 vf9)
(set! (-> obj proc) proc)
(.add.z.vf.x vf8 vf8 vf8)
(.sub.x.vf.xyz vf4 vf4 vf9)
(.add.x.vf.xyz vf5 vf5 vf9)
(.ftoi.vf vf15 vf10)
(.isqrt.vf Q vf0 vf8 :fsf #b11 :ftf #b0)
(.add.x.vf.xyz vf11 vf0 vf9)
(.svf (&-> cwork collide-box4w min quad) vf15)
(.ftoi.vf vf6 vf4)
(.svf (&-> obj collide-box min quad) vf4)
(.ftoi.vf vf7 vf5)
(.svf (&-> obj collide-box max quad) vf5)
(.mov.vf vf13 vf0)
(.svf (&-> obj collide-box4w min quad) vf6)
(.svf (&-> obj collide-box4w max quad) vf7)
(.add.vf.x vf8 vf0 Q)
(.mul.x.vf vf12 vf3 vf8)
(.div.vf Q vf0 vf8 :fsf #b11 :ftf #b0)
(.mul.vf vf22 vf12 vf12)
(.abs.vf vf23 vf12)
(.add.y.vf.x vf22 vf22 vf22)
(.mov direction-x-bits vf23)
(.wait.vf)
(.add.vf.x vf8 vf0 Q)
(b! (zero? direction-x-bits) cfg-6 :likely-delay (.add.z.vf.x vf13 vf0 vf12))
(.sub.y.vf.x vf13 vf0 vf12)
(.isqrt.vf Q vf0 vf22 :fsf #b11 :ftf #b0)
(.add.x.vf.y vf13 vf0 vf12)
(.wait.vf)
(.mul.vf.xy vf13 vf13 Q)
(label cfg-6)
(.outer.product.a.vf acc vf12 vf13)
(.add.x.vf.x vf11 vf11 vf8)
(.outer.product.b.vf vf14 vf13 vf12 acc)
(.ftoi.vf vf16 vf11)
(.mov.vf vf17 vf12)
(.mov.vf vf18 vf13)
(.mov.vf vf19 vf14)
(.mov.vf.yzw vf17 vf0)
(.svf (&-> cwork collide-box4w max quad) vf16)
(.mov.vf.xzw vf18 vf0)
(.mov.vf.xyw vf19 vf0)
(.add.x.vf.y vf17 vf17 vf13)
(.add.y.vf.x vf18 vf18 vf12)
(.add.z.vf.x vf19 vf19 vf12)
(.add.x.vf.z vf17 vf17 vf14)
(.add.y.vf.z vf18 vf18 vf14)
(.add.z.vf.y vf19 vf19 vf13)
(.mul.x.vf acc vf17 vf1)
(.add.mul.y.vf acc vf18 vf1 acc)
(.svf (&-> cwork inv-mat vector 0 quad) vf17)
(.add.mul.z.vf vf20 vf19 vf1 acc)
(.svf (&-> cwork inv-mat vector 1 quad) vf18)
(.sub.vf vf20 vf0 vf20)
(.svf (&-> cwork inv-mat vector 2 quad) vf19)
(.svf (&-> cwork inv-mat vector 3 quad) vf20))))
0
(if (logtest? kinds (collide-kind background))
(fill-from-background obj
(method-of-type bsp-header collide-ray)
(method-of-type collide-cache load-mesh-from-spad-in-line-sphere)))
(if (logtest? kinds (collide-kind water)) (fill-from-water obj (-> proc water)))
(if (logtest? kinds (collide-kind cak-1 cak-2 cak-3 target)) (fill-from-foreground-using-line-sphere obj))
0
(label cfg-13)
(none)))
(defun collide-cache-using-line-sphere-test ((bsphere vector))
"Check if bsphere is in the rotated bounding box volume of the current
line-sphere query."
(local-vars (v1-1 uint128) (v1-2 uint128) (v1-3 uint128) (a0-1 uint128) (a1-2 uint128) (a2-0 uint128) (zero uint128))
(rlet ((acc :class vf)
(vf0 :class vf)
(vf1 :class vf)
(vf10 :class vf)
(vf2 :class vf)
(vf3 :class vf)
(vf4 :class vf)
(vf5 :class vf)
(vf6 :class vf)
(vf7 :class vf)
(vf8 :class vf)
(vf9 :class vf))
(init-vf0-vector)
(set! zero (the uint128 0))
(let ((cwork *collide-work*))
(.lvf vf5 (&-> bsphere quad))
(.lvf vf4 (&-> cwork inv-mat vector 3 quad))
(.lvf vf1 (&-> cwork inv-mat vector 0 quad))
(.mul.w.vf acc vf4 vf0)
(.lvf vf2 (&-> cwork inv-mat vector 1 quad))
(.add.mul.x.vf acc vf1 vf5 acc)
(.lvf vf3 (&-> cwork inv-mat vector 2 quad))
(.add.mul.y.vf acc vf2 vf5 acc)
(let ((v1-0 (-> cwork collide-box4w min quad)))
(.add.mul.z.vf vf10 vf3 vf5 acc)
(let ((a1-1 (-> cwork collide-box4w max quad)))
(.sub.w.vf.xyz vf6 vf10 vf5)
(.add.w.vf.xyz vf7 vf10 vf5)
(.ftoi.vf vf8 vf6)
(.ftoi.vf vf9 vf7)
(.mov a2-0 vf8)
(.mov a0-1 vf9)
(.pcgtw a1-2 a2-0 a1-1))
(.pcgtw v1-1 v1-0 a0-1)))
(.por v1-2 a1-2 v1-1)
(.ppach v1-3 zero v1-2)
(let ((v1-4 (shl (the-as int v1-3) 16))) (nop!) (zero? v1-4))))
(defun make-collide-list-using-line-sphere-inst-test ((frag collide-fragment) (inst instance-tie))
"Check if a collide-fragment at a given instance is in the rotated bounding box volume
of the current line-sphere query."
(local-vars
(v1-1 uint128)
(v1-2 uint128)
(v1-3 uint128)
(v1-4 uint128)
(a1-2 uint128)
(a2-1 uint128)
(a3-1 uint128)
(a3-3 uint128)
(a3-4 uint128)
(t0-1 uint128)
(t0-2 uint128)
(t1-0 uint128)
(t2-1 uint128)
(t2-2 uint128)
(zero uint128))
(rlet ((acc :class vf)
(vf0 :class vf)
(vf1 :class vf)
(vf10 :class vf)
(vf11 :class vf)
(vf12 :class vf)
(vf13 :class vf)
(vf14 :class vf)
(vf15 :class vf)
(vf2 :class vf)
(vf3 :class vf)
(vf4 :class vf)
(vf5 :class vf)
(vf6 :class vf)
(vf7 :class vf)
(vf8 :class vf)
(vf9 :class vf))
(init-vf0-vector)
(set! zero (the uint128 0))
(let ((v1-0 *collide-work*))
(let ((a2-0 (-> inst max-scale)))
(let ((a3-0 (the-as uint128 (-> inst origin vector4h 3 long))))
(let ((t2-0 (the-as uint128 (-> inst origin vector4h 0 long))))
(.pextlh a3-1 a3-0 zero)
(let ((t0-0 (the-as uint128 (-> inst origin vector4h 1 long))))
(.pw.sra t1-0 a3-1 10)
(let ((a3-2 (the-as uint128 (-> inst origin vector4h 2 long))))
(.pextlh t2-1 t2-0 zero)
(.pw.sra t2-2 t2-1 16)
(.pextlh t0-1 t0-0 zero)
(.mov vf4 t1-0)
(.pw.sra t0-2 t0-1 16)
(.mov vf1 t2-2)
(.pextlh a3-3 a3-2 zero)))))
(.mov vf2 t0-2)
(.pw.sra a3-4 a3-3 16)
(.lvf vf5 (&-> inst bsphere quad))
(.mov vf3 a3-4)
(.mov vf6 a2-0))
(.itof.vf vf4 vf4)
(vitof12.xyzw vf1 vf1)
(vitof12.xyzw vf2 vf2)
(vitof12.xyzw vf3 vf3)
(.add.vf.xyz vf4 vf4 vf5)
(.lvf vf5 (&-> frag bsphere quad))
(vitof12.xyzw vf6 vf6)
(.mul.x.vf acc vf1 vf5)
(.add.mul.y.vf acc vf2 vf5 acc)
(.add.mul.z.vf acc vf3 vf5 acc)
(.add.mul.w.vf vf11 vf4 vf0 acc)
(.lvf vf7 (&-> v1-0 inv-mat vector 0 quad))
(.lvf vf8 (&-> v1-0 inv-mat vector 1 quad))
(.lvf vf9 (&-> v1-0 inv-mat vector 2 quad))
(.lvf vf10 (&-> v1-0 inv-mat vector 3 quad))
(.mul.x.vf acc vf7 vf11)
(let ((a0-1 (-> v1-0 collide-box4w min quad)))
(.add.mul.y.vf acc vf8 vf11 acc)
(let ((a1-1 (-> v1-0 collide-box4w max quad)))
(.add.mul.z.vf acc vf9 vf11 acc)
(.add.mul.w.vf vf11 vf10 vf0 acc)
(.mul.w.vf.x vf15 vf6 vf5)
(.add.x.vf.xyz vf12 vf11 vf15)
(.sub.x.vf.xyz vf11 vf11 vf15)
(.ftoi.vf vf14 vf12)
(.ftoi.vf vf13 vf11)
(.mov v1-1 vf14)
(.mov a2-1 vf13)
(.pcgtw a1-2 a2-1 a1-1))
(.pcgtw v1-2 a0-1 v1-1)))
(.por v1-3 a1-2 v1-2)
(.ppach v1-4 zero v1-3)
(let ((v1-5 (shl (the-as int v1-4) 16))) (nop!) (zero? v1-5))))
(defmethod collide-ray ((obj collide-fragment) (count int) (result collide-list))
"Inline-array function to do line-sphere with non-instanced fragments.
If the bsphere of the mesh is in the non-aligned bounding box, the mesh will be added
to the given collide-list.
Note: collide-probe is the faster implementation of this."
*collide-work*
;; just iterate over all and check their bsphere.
(dotimes (s3-0 count)
(when (collide-cache-using-line-sphere-test (-> obj bsphere))
(let ((v1-5 (-> result items (-> result num-items))))
(set! (-> v1-5 mesh) (-> obj mesh))
(set! (-> v1-5 inst) #f) ;; non-instanced.
)
(+! (-> result num-items) 1))
(&+! obj 32))
0
(none))
(defmethod collide-ray ((obj instance-tie) (count int) (result collide-list))
"Inline-array function to do line-sphere with TIE instances.
If the bsphere of the mesh is in the non-aligned bounding box, the mesh will be added
to the given collide-list.
Note: collide-probe is the faster implementation of this."
;; loop over instance-ties
(dotimes (s3-0 count)
;; first check the instance-tie's bsphere
(when (and (zero? (logand (-> obj flags) 1)) (collide-cache-using-line-sphere-test (-> obj bsphere)))
;; now, find the collide-frag
(let ((s2-0 (-> obj bucket-ptr collide-frag)))
(when (nonzero? s2-0)
(let ((s1-0 (the-as object (-> s2-0 data))))
;; and check each mesh in the collide-frag
(dotimes (s0-0 (-> s2-0 length))
(when (make-collide-list-using-line-sphere-inst-test (the-as collide-fragment s1-0) obj)
(let ((v1-10 (-> result items (-> result num-items))))
(set! (-> v1-10 mesh) (-> (the-as collide-fragment s1-0) mesh))
(set! (-> v1-10 inst) obj))
(+! (-> result num-items) 1))
(set! s1-0 (-> (the-as (inline-array collide-fragment) s1-0) 1)))))))
(&+! obj 64))
0
(none))
;;;;;;;;;;;;;;;;;;;
;; foreground box
;;;;;;;;;;;;;;;;;;;
;; for an unknown reason, the goal compiler went insane on this function
(defmethod fill-from-foreground-using-box ((obj collide-cache))
"Walk the requested foreground connection lists and add compatible primitives whose world bounds
overlap the active box query, excluding the querying process."
(local-vars
(zero uint128)
(v1-2 uint128)
(v1-15 uint128)
(v1-18 uint128)
(v1-31 uint128)
(v1-44 uint128)
(a1-1 uint128)
(a1-6 uint128)
(a1-11 uint128)
(a1-16 uint128)
(a2-0 uint128)
(a2-1 uint128)
(a2-2 uint128)
(a2-3 uint128)
(a2-5 uint128)
(a2-6 uint128)
(a2-7 uint128)
(a2-8 uint128)
(a2-10 uint128)
(a2-11 uint128)
(a2-12 uint128)
(a2-13 uint128)
(a2-15 uint128)
(a2-16 uint128)
(a2-17 uint128)
(a2-18 uint128)
(a3-0 uint128)
(a3-1 uint128)
(a3-2 uint128)
(a3-3 uint128)
(a3-4 uint128)
(a3-5 uint128)
(a3-6 uint128)
(a3-7 uint128)
(f31-0 none))
(rlet ((vf1 :class vf)
(vf2 :class vf)
(vf3 :class vf)
(vf4 :class vf)
(vf5 :class vf))
(set! zero (the-as uint128 0))
(let ((s5-0 (the-as uint128 (-> obj collide-with)))
(s4-0 (-> obj collide-box4w min quad))
(s3-0 (-> obj collide-box4w max quad)))
(let ((v1-1 (the-as uint128 (make-u128 0 16)))) (.pand v1-2 v1-1 s5-0))
(when (nonzero? (the-as int v1-2))
(iterate-engine-connections (v1-5 *collide-player-list*)
(let* ((v1-6 (the-as collide-shape (-> (the-as connection v1-5) param1)))
(a0-3 (-> v1-6 root-prim)))
(let ((a1-0 (the-as uint128 (-> a0-3 prim-core collide-as)))) (.pand a1-1 s5-0 a1-0))
(b! (zero? (the-as int a1-1)) cfg-7)
(nop!)
(.lvf vf1 (&-> a0-3 prim-core world-sphere quad))
(.sub.w.vf vf2 vf1 vf1)
(nop!)
(.add.w.vf vf3 vf1 vf1)
(nop!)
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a2-0 vf4)
(let ((v1-7 (-> v1-6 process)))
(.mov a3-0 vf5)
(let ((a1-3 (-> obj proc)))
(.pcgtw a2-1 a2-0 s3-0)
(.pcgtw a3-1 s4-0 a3-0)
(.por a2-2 a2-1 a3-1)
(.ppach a2-3 zero a2-2)
(let ((a2-4 (shl (the-as int a2-3) 16))) (nop!) (b! (nonzero? a2-4) cfg-6 :delay (nop!)))
(b! (= a1-3 v1-7) cfg-6 :delay (nop!))))
(add-fg-prim-using-box a0-3 obj))
(label cfg-6)
0
(label cfg-7)))
(let ((v1-14 (the-as uint128 (make-u128 0 14)))) (.pand v1-15 v1-14 s5-0))
(when (nonzero? (the-as int v1-15))
(let ((v1-17 (the-as uint128 (make-u128 0 2)))) (.pand v1-18 v1-17 s5-0))
(when (nonzero? (the-as int v1-18))
(iterate-engine-connections (v1-21 *collide-hit-by-player-list*)
(let* ((v1-22 (the-as collide-shape (-> (the-as connection v1-21) param1)))
(a0-10 (-> v1-22 root-prim)))
(let ((a1-5 (the-as uint128 (-> a0-10 prim-core collide-as)))) (.pand a1-6 s5-0 a1-5))
(b! (zero? (the-as int a1-6)) cfg-18)
(nop!)
(.lvf vf1 (&-> a0-10 prim-core world-sphere quad))
(.sub.w.vf vf2 vf1 vf1)
(nop!)
(.add.w.vf vf3 vf1 vf1)
(nop!)
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a2-5 vf4)
(let ((v1-23 (-> v1-22 process)))
(.mov a3-2 vf5)
(let ((a1-8 (-> obj proc)))
(.pcgtw a2-6 a2-5 s3-0)
(.pcgtw a3-3 s4-0 a3-2)
(.por a2-7 a2-6 a3-3)
(.ppach a2-8 zero a2-7)
(let ((a2-9 (shl (the-as int a2-8) 16))) (nop!) (b! (nonzero? a2-9) cfg-17 :delay (nop!)))
(b! (= a1-8 v1-23) cfg-17 :delay (nop!))))
(add-fg-prim-using-box a0-10 obj))
(label cfg-17)
0
(label cfg-18)))
(let ((v1-30 (the-as uint128 (make-u128 0 4)))) (.pand v1-31 v1-30 s5-0))
(when (nonzero? (the-as int v1-31))
(iterate-engine-connections (v1-34 *collide-usually-hit-by-player-list*)
(let* ((v1-35 (the-as collide-shape (-> (the-as connection v1-34) param1)))
(a0-17 (-> v1-35 root-prim)))
(let ((a1-10 (the-as uint128 (-> a0-17 prim-core collide-as)))) (.pand a1-11 s5-0 a1-10))
(b! (zero? (the-as int a1-11)) cfg-28)
(nop!)
(nop!)
(.lvf vf1 (&-> a0-17 prim-core world-sphere quad))
(.sub.w.vf vf2 vf1 vf1)
(nop!)
(.add.w.vf vf3 vf1 vf1)
(nop!)
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a2-10 vf4)
(let ((v1-36 (-> v1-35 process)))
(.mov a3-4 vf5)
(let ((a1-13 (-> obj proc)))
(.pcgtw a2-11 a2-10 s3-0)
(.pcgtw a3-5 s4-0 a3-4)
(.por a2-12 a2-11 a3-5)
(.ppach a2-13 zero a2-12)
(let ((a2-14 (shl (the-as int a2-13) 16))) (nop!) (b! (nonzero? a2-14) cfg-27 :delay (nop!)))
(b! (= a1-13 v1-36) cfg-27 :delay (nop!))))
(add-fg-prim-using-box a0-17 obj))
(label cfg-27)
0
(label cfg-28)))
(let ((v1-43 (the-as uint128 (make-u128 0 8)))) (.pand v1-44 v1-43 s5-0))
(when (nonzero? (the-as int v1-44))
(iterate-engine-connections (v1-46 *collide-hit-by-others-list*)
(let* ((v1-47 (the-as collide-shape (-> (the-as connection v1-46) param1)))
(a0-24 (-> v1-47 root-prim)))
(let ((a1-15 (the-as uint128 (-> a0-24 prim-core collide-as)))) (.pand a1-16 s5-0 a1-15))
(b! (zero? (the-as int a1-16)) cfg-38)
(nop!)
(nop!)
(.lvf vf1 (&-> a0-24 prim-core world-sphere quad))
(.sub.w.vf vf2 vf1 vf1)
(nop!)
(.add.w.vf vf3 vf1 vf1)
(nop!)
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a2-15 vf4)
(let ((v1-48 (-> v1-47 process)))
(.mov a3-6 vf5)
(let ((a1-18 (-> obj proc)))
(.pcgtw a2-16 a2-15 s3-0)
(.pcgtw a3-7 s4-0 a3-6)
(.por a2-17 a2-16 a3-7)
(.ppach a2-18 zero a2-17)
(let ((a2-19 (shl (the-as int a2-18) 16))) (nop!) (b! (nonzero? a2-19) cfg-37 :delay (nop!)))
(b! (= a1-18 v1-48) cfg-37 :delay (nop!))))
(add-fg-prim-using-box a0-24 obj))
(label cfg-37)
0
(label cfg-38)))))
(none)))
(defmethod add-fg-prim-using-box ((obj collide-shape-prim) (cache collide-cache))
"Add this primitive to the active box query. Sphere leaves are copied into the cache, mesh leaves
import their overlapping triangles, groups recurse through compatible children, and the base
implementation reports an invalid primitive type."
(format 0 "ERROR: Illegal collide-shape-prim type passed to collide-shape-prim::add-fg-prim-using-box!~%")
(none))
(defmethod add-fg-prim-using-box ((obj collide-shape-prim-sphere) (cache collide-cache))
"Add this primitive to the active box query. Sphere leaves are copied into the cache, mesh leaves
import their overlapping triangles, groups recurse through compatible children, and the base
implementation reports an invalid primitive type."
(local-vars (t1-1 uint))
(nop!)
(let* ((t0-0 (-> cache prims))
(a3-0 (-> cache num-prims-u))
(t1-0 100)
(v1-0 (-> obj prim-core world-sphere quad))
(t2-0 (* a3-0 2))
(a2-0 (-> obj prim-core quad 1)))
(b! (= a3-0 t1-0) cfg-2 :delay (set! t1-1 (+ t2-0 a3-0)))
(let ((t0-1 (the-as object (&-> t0-0 0 prim-core quad t1-1))))
(let ((a3-1 (+ a3-0 1)))
(set! (-> (the-as (pointer uint128) t0-1) 2) (the-as uint128 0))
(nop!)
(set! (-> (the-as collide-cache-prim t0-1) prim) obj)
(nop!)
(set! (-> (the-as collide-cache-prim t0-1) prim-core world-sphere quad) v1-0)
(nop!)
(set! (-> (the-as collide-cache-prim t0-1) prim-core quad 1) a2-0)
(nop!)
(set! (-> cache num-prims) (the-as int a3-1)))
(nop!)
(set! (-> (the-as collide-cache-prim t0-1) ccache) cache)))
(b! #t cfg-3 :delay (nop!))
(label cfg-2)
(format 0 "ERROR: Exceeded max number of collide-cache prims!~%")
(label cfg-3)
(none))
(defmethod add-fg-prim-using-box ((obj collide-shape-prim-group) (cache collide-cache))
"Add this primitive to the active box query. Sphere leaves are copied into the cache, mesh leaves
import their overlapping triangles, groups recurse through compatible children, and the base
implementation reports an invalid primitive type."
(local-vars
(zero uint128)
(v1-2 uint128)
(v1-3 uint128)
(v1-4 uint128)
(v1-5 uint128)
(a0-1 collide-shape-prim)
(a1-1 uint128)
(a1-2 uint128))
(set! zero (the-as uint128 0))
(rlet ((vf1 :class vf)
(vf2 :class vf)
(vf3 :class vf)
(vf4 :class vf)
(vf5 :class vf))
(nop!)
(let ((s5-0 (-> obj prims))
(s4-0 (-> obj num-prims-u)))
(nop!)
(let ((s3-0 (-> cache collide-box4w min quad)))
(nop!)
(let ((s2-0 (-> cache collide-box4w max quad)))
(nop!)
(let ((s1-0 (-> s5-0 0)))
(nop!)
(let ((s0-0 (-> cache collide-with)))
(label cfg-1)
(b! (zero? s4-0) cfg-5 :delay (set! a0-1 s1-0))
(label cfg-2)
(+! s4-0 -1)
(.lvf vf1 (&-> a0-1 prim-core world-sphere quad))
(nop!)
(let ((v1-0 (-> a0-1 prim-core collide-as)))
(.sub.w.vf vf2 vf1 vf1)
(set! s5-0 (&-> s5-0 1))
(.add.w.vf vf3 vf1 vf1)
(set! s1-0 (-> s5-0 0))
(let ((v1-1 (logand s0-0 v1-0))) (nop!) (b! (zero? v1-1) cfg-1 :delay (nop!)))))
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a1-1 vf4)
(nop!)
(.mov v1-2 vf5)
(nop!)
(.pcgtw a1-2 a1-1 s2-0)
(.pcgtw v1-3 s3-0 v1-2)
(.por v1-4 a1-2 v1-3)
(.ppach v1-5 zero v1-4)
(let ((v1-6 (shl (the-as int v1-5) 16))) (nop!) (b! (nonzero? v1-6) cfg-1 :delay (nop!)))
(add-fg-prim-using-box a0-1 cache)
(b! (nonzero? s4-0) cfg-2 :delay (set! a0-1 s1-0))))))
(label cfg-5)
0
(none)))
;;;;;;;;;;;;;;;;;;;;;;;;;
;; Foreground Y PROBE
;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod fill-from-foreground-using-y-probe ((obj collide-cache))
"Walk the requested foreground connection lists and add compatible primitives whose world bounds
overlap the active vertical probe, excluding the querying process."
(local-vars
(zero uint128)
(v1-2 uint128)
(v1-15 uint128)
(v1-18 uint128)
(v1-31 uint128)
(v1-44 uint128)
(a1-1 uint128)
(a1-6 uint128)
(a1-11 uint128)
(a1-16 uint128)
(a2-0 uint128)
(a2-1 uint128)
(a2-2 uint128)
(a2-3 uint128)
(a2-5 uint128)
(a2-6 uint128)
(a2-7 uint128)
(a2-8 uint128)
(a2-10 uint128)
(a2-11 uint128)
(a2-12 uint128)
(a2-13 uint128)
(a2-15 uint128)
(a2-16 uint128)
(a2-17 uint128)
(a2-18 uint128)
(a3-0 uint128)
(a3-1 uint128)
(a3-2 uint128)
(a3-3 uint128)
(a3-4 uint128)
(a3-5 uint128)
(a3-6 uint128)
(a3-7 uint128))
(set! zero (the uint128 0))
(rlet ((vf1 :class vf)
(vf2 :class vf)
(vf3 :class vf)
(vf4 :class vf)
(vf5 :class vf))
(let ((s5-0 (the-as uint128 (-> obj collide-with)))
(s4-0 (-> obj collide-box4w min quad))
(s3-0 (-> obj collide-box4w max quad)))
(let ((v1-1 (the-as uint128 (make-u128 0 16)))) (.pand v1-2 v1-1 s5-0))
(when (nonzero? (the-as int v1-2))
(iterate-engine-connections (v1-5 *collide-player-list*)
(let* ((v1-6 (the-as collide-shape (-> (the-as connection v1-5) param1)))
(a0-3 (-> v1-6 root-prim)))
(let ((a1-0 (the-as uint128 (-> a0-3 prim-core collide-as)))) (.pand a1-1 s5-0 a1-0))
(b! (zero? (the-as int a1-1)) cfg-7)
(.lvf vf1 (&-> a0-3 prim-core world-sphere quad))
(.sub.w.vf vf2 vf1 vf1)
(.add.w.vf vf3 vf1 vf1)
(.ftoi.vf vf4 vf2)
(.ftoi.vf vf5 vf3)
(.mov a2-0 vf4)
(let ((v1-7 (-> v1-6 process)))
(.mov a3-0 vf5)
(let ((a1-3 (-> obj proc)))
(.pcgtw a2-1 a2-0 s3-0)
(.pcgtw a3-1 s4-0 a3-0)
(.por a2-2 a2-1 a3-1)
(.ppach a2-3 zero a2-2)
(let ((a2-4 (shl (the-as int a2-3) 16))) (nop!) (b! (nonzero? a2-4) cfg-6 :delay (nop!)))
(b! (= a1-3 v1-7) cfg-6 :delay (nop!))))
(add-fg-prim-using-y-probe a0-3 obj))
(label cfg-6)
0
(label cfg-7)))
(let ((v1-14 (the-as uint128 (make-u128 0 14)))) (.pand v1-15 v1-14 s5-0))
(when (nonzero? (the-as int v1-15))
(let ((v1-17 (the-as uint128 (make-u128 0 2)))) (.pand v1-18 v1-17 s5-0))
(when (nonzero? (the-as int v1-18))
(iterate-engine-connections (v1-21 *collide-hit-by-player-list*)
(let* ((v1-22 (the-as collide-shape (-> (the-as connection v1-21) param1)))
(a0-10 (-> v1-22 root-prim)))
(let ((a1-5 (the-as uint128 (-> a0-10 prim-core collide-as)))) (.pand a1-6 s5-0 a1-5))
(b! (zero? (the-as int a1-6)) cfg-18)
(nop!)
(.lvf vf1 (&-> a0-10 prim-core world-sphere quad))
(.sub.w.vf vf2 vf1 vf1)
(nop!)
(.add.w.vf vf3 vf1 vf1)
(nop!)
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a2-5 vf4)
(let ((v1-23 (-> v1-22 process)))
(.mov a3-2 vf5)
(let ((a1-8 (-> obj proc)))
(.pcgtw a2-6 a2-5 s3-0)
(.pcgtw a3-3 s4-0 a3-2)
(.por a2-7 a2-6 a3-3)
(.ppach a2-8 zero a2-7)
(let ((a2-9 (shl (the-as int a2-8) 16))) (nop!) (b! (nonzero? a2-9) cfg-17 :delay (nop!)))
(b! (= a1-8 v1-23) cfg-17 :delay (nop!))))
(add-fg-prim-using-y-probe a0-10 obj))
(label cfg-17)
0
(label cfg-18)))
(let ((v1-30 (the-as uint128 (make-u128 0 4)))) (.pand v1-31 v1-30 s5-0))
(when (nonzero? (the-as int v1-31))
(iterate-engine-connections (v1-34 *collide-usually-hit-by-player-list*)
(let* ((v1-35 (the-as collide-shape (-> (the-as connection v1-34) param1)))
(a0-17 (-> v1-35 root-prim)))
(let ((a1-10 (the-as uint128 (-> a0-17 prim-core collide-as)))) (.pand a1-11 s5-0 a1-10))
(b! (zero? (the-as int a1-11)) cfg-28)
(nop!)
(nop!)
(.lvf vf1 (&-> a0-17 prim-core world-sphere quad))
(.sub.w.vf vf2 vf1 vf1)
(nop!)
(.add.w.vf vf3 vf1 vf1)
(nop!)
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a2-10 vf4)
(let ((v1-36 (-> v1-35 process)))
(.mov a3-4 vf5)
(let ((a1-13 (-> obj proc)))
(.pcgtw a2-11 a2-10 s3-0)
(.pcgtw a3-5 s4-0 a3-4)
(.por a2-12 a2-11 a3-5)
(.ppach a2-13 zero a2-12)
(let ((a2-14 (shl (the-as int a2-13) 16))) (nop!) (b! (nonzero? a2-14) cfg-27 :delay (nop!)))
(b! (= a1-13 v1-36) cfg-27 :delay (nop!))))
(add-fg-prim-using-y-probe a0-17 obj))
(label cfg-27)
0
(label cfg-28)))
(let ((v1-43 (the-as uint128 (make-u128 0 8)))) (.pand v1-44 v1-43 s5-0))
(when (nonzero? (the-as int v1-44))
(iterate-engine-connections (v1-46 *collide-hit-by-others-list*)
(let* ((v1-47 (the-as collide-shape (-> (the-as connection v1-46) param1)))
(a0-24 (-> v1-47 root-prim)))
(let ((a1-15 (the-as uint128 (-> a0-24 prim-core collide-as)))) (.pand a1-16 s5-0 a1-15))
(b! (zero? (the-as int a1-16)) cfg-38)
(nop!)
(nop!)
(.lvf vf1 (&-> a0-24 prim-core world-sphere quad))
(.sub.w.vf vf2 vf1 vf1)
(nop!)
(.add.w.vf vf3 vf1 vf1)
(nop!)
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a2-15 vf4)
(let ((v1-48 (-> v1-47 process)))
(.mov a3-6 vf5)
(let ((a1-18 (-> obj proc)))
(.pcgtw a2-16 a2-15 s3-0)
(.pcgtw a3-7 s4-0 a3-6)
(.por a2-17 a2-16 a3-7)
(.ppach a2-18 zero a2-17)
(let ((a2-19 (shl (the-as int a2-18) 16))) (nop!) (b! (nonzero? a2-19) cfg-37 :delay (nop!)))
(b! (= a1-18 v1-48) cfg-37 :delay (nop!))))
(add-fg-prim-using-y-probe a0-24 obj))
(label cfg-37)
0
(label cfg-38)))))
(none)))
(defmethod add-fg-prim-using-y-probe ((obj collide-shape-prim) (cache collide-cache))
"Add this primitive to the active vertical-probe query. Sphere leaves are copied into the cache,
mesh leaves import their overlapping triangles, groups recurse through compatible children, and
the base implementation reports an invalid primitive type."
(format 0 "ERROR: Illegal collide-shape-prim type passed to collide-shape-prim::add-fg-prim-using-y-probe!~%")
(none))
(defmethod add-fg-prim-using-y-probe ((obj collide-shape-prim-sphere) (cache collide-cache))
"Add this primitive to the active vertical-probe query. Sphere leaves are copied into the cache,
mesh leaves import their overlapping triangles, groups recurse through compatible children, and
the base implementation reports an invalid primitive type."
(local-vars (t1-1 uint))
(nop!)
(let* ((t0-0 (-> cache prims))
(a3-0 (-> cache num-prims-u))
(t1-0 100)
(v1-0 (-> obj prim-core world-sphere quad))
(t2-0 (* a3-0 2))
(a2-0 (-> obj prim-core quad 1)))
(b! (= a3-0 t1-0) cfg-2 :delay (set! t1-1 (+ t2-0 a3-0)))
(let ((t0-1 (the-as object (&-> t0-0 0 prim-core quad t1-1))))
(let ((a3-1 (+ a3-0 1)))
(set! (-> (the-as collide-cache-prim t0-1) extra-quad) (the-as uint128 0))
(nop!)
(set! (-> (the-as collide-cache-prim t0-1) prim) obj)
(nop!)
(set! (-> (the-as collide-cache-prim t0-1) prim-core world-sphere quad) v1-0)
(nop!)
(set! (-> (the-as collide-cache-prim t0-1) prim-core quad 1) a2-0)
(nop!)
(set! (-> cache num-prims) (the-as int a3-1)))
(nop!)
(set! (-> (the-as collide-cache-prim t0-1) ccache) cache)))
(b! #t cfg-3 :delay (nop!))
(label cfg-2)
(format 0 "ERROR: Exceeded max number of collide-cache prims!~%")
(label cfg-3)
(none))
(defmethod add-fg-prim-using-y-probe ((obj collide-shape-prim-group) (cache collide-cache))
"Add this primitive to the active vertical-probe query. Sphere leaves are copied into the cache,
mesh leaves import their overlapping triangles, groups recurse through compatible children, and
the base implementation reports an invalid primitive type."
(local-vars
(r0-0 int)
(r0-1 int)
(r0-2 uint128)
(r0-3 int)
(v1-2 uint128)
(v1-3 uint128)
(v1-4 uint128)
(v1-5 uint128)
(a0-1 collide-shape-prim)
(a1-1 uint128)
(a1-2 uint128)
(f31-0 none))
(rlet ((vf1 :class vf)
(vf2 :class vf)
(vf3 :class vf)
(vf4 :class vf)
(vf5 :class vf))
(nop!)
(let ((s5-0 (-> obj prims))
(s4-0 (-> obj num-prims-u)))
(nop!)
(let ((s3-0 (-> cache collide-box4w min quad)))
(nop!)
(let ((s2-0 (-> cache collide-box4w max quad)))
(nop!)
(let ((s1-0 (-> s5-0 0)))
(nop!)
(let ((s0-0 (-> cache collide-with)))
(label cfg-1)
(b! (zero? s4-0) cfg-5 :delay (set! a0-1 s1-0))
(label cfg-2)
(+! s4-0 -1)
(.lvf vf1 (&-> a0-1 prim-core world-sphere quad))
(nop!)
(let ((v1-0 (-> a0-1 prim-core collide-as)))
(.sub.w.vf vf2 vf1 vf1)
(set! s5-0 (&-> s5-0 1))
(.add.w.vf vf3 vf1 vf1)
(set! s1-0 (-> s5-0 0))
(let ((v1-1 (logand s0-0 v1-0))) (nop!) (b! (zero? v1-1) cfg-1 :delay (nop!)))))
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a1-1 vf4)
(nop!)
(.mov v1-2 vf5)
(nop!)
(.pcgtw a1-2 a1-1 s2-0)
(.mov r0-0 f31-0)
(.pcgtw v1-3 s3-0 v1-2)
(.mov r0-1 f31-0)
(.por v1-4 a1-2 v1-3)
(.mov r0-2 f31-0)
(.ppach v1-5 r0-2 v1-4)
(.mov r0-3 f31-0)
(let ((v1-6 (shl (the-as int v1-5) 16))) (nop!) (b! (nonzero? v1-6) cfg-1 :delay (nop!)))
(add-fg-prim-using-y-probe a0-1 cache)
(b! (nonzero? s4-0) cfg-2 :delay (set! a0-1 s1-0))))))
(label cfg-5)
0
(none)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; foreground Line Sphere
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Host calls do not preserve the four VU registers holding the swept-query transform.
(#when PC_PORT
(deftype pc-fffuls-work (structure)
((reg-vf28 vector :inline)
(reg-vf29 vector :inline)
(reg-vf30 vector :inline)
(reg-vf31 vector :inline)))
(define *pc-fffuls-work* (new 'global 'pc-fffuls-work))
(defmacro save-fffuls-work ()
`(begin
(.svf (&-> *pc-fffuls-work* reg-vf31 quad) vf31)
(.svf (&-> *pc-fffuls-work* reg-vf30 quad) vf30)
(.svf (&-> *pc-fffuls-work* reg-vf29 quad) vf29)
(.svf (&-> *pc-fffuls-work* reg-vf28 quad) vf28)))
(defmacro load-fffuls-work ()
`(begin
(.lvf vf28 (&-> *pc-fffuls-work* reg-vf28 quad))
(.lvf vf29 (&-> *pc-fffuls-work* reg-vf29 quad))
(.lvf vf30 (&-> *pc-fffuls-work* reg-vf30 quad))
(.lvf vf31 (&-> *pc-fffuls-work* reg-vf31 quad)))))
(defmethod fill-from-foreground-using-line-sphere ((obj collide-cache))
"Walk the requested foreground connection lists and add compatible primitives whose world bounds
overlap the active oriented swept-sphere box, excluding the querying process."
(local-vars
(zero uint128)
(v1-1 float)
(v1-4 uint128)
(v1-17 uint128)
(v1-20 uint128)
(v1-33 uint128)
(v1-46 uint128)
(a1-1 uint128)
(a1-6 uint128)
(a1-11 uint128)
(a1-16 uint128)
(a2-0 uint128)
(a2-1 uint128)
(a2-2 uint128)
(a2-3 uint128)
(a2-5 uint128)
(a2-6 uint128)
(a2-7 uint128)
(a2-8 uint128)
(a2-10 uint128)
(a2-11 uint128)
(a2-12 uint128)
(a2-13 uint128)
(a2-15 uint128)
(a2-16 uint128)
(a2-17 uint128)
(a2-18 uint128)
(a3-0 uint128)
(a3-1 uint128)
(a3-2 uint128)
(a3-3 uint128)
(a3-4 uint128)
(a3-5 uint128)
(a3-6 uint128)
(a3-7 uint128)
(f31-0 none))
(rlet ((acc :class vf)
(vf0 :class vf)
(vf1 :class vf)
(vf2 :class vf)
(vf28 :class vf)
(vf29 :class vf)
(vf3 :class vf)
(vf30 :class vf)
(vf31 :class vf)
(vf4 :class vf)
(vf5 :class vf)
(vf6 :class vf))
(init-vf0-vector)
(set! zero (the uint128 0))
(let* ((v1-0 *collide-work*)
(s5-0 (-> v1-0 collide-box4w min quad))
(s4-0 (-> v1-0 collide-box4w max quad))
(s3-0 (the-as uint128 (-> obj collide-with))))
(.lvf vf28 (&-> v1-0 inv-mat vector 0 quad))
(.lvf vf29 (&-> v1-0 inv-mat vector 1 quad))
(.lvf vf30 (&-> v1-0 inv-mat vector 2 quad))
(.lvf vf31 (&-> v1-0 inv-mat vector 3 quad))
(#when PC_PORT
(save-fffuls-work))
(.mov v1-1 vf31)
(let ((v1-3 (the-as uint128 (make-u128 0 16)))) (.pand v1-4 v1-3 s3-0))
(when (nonzero? (the-as int v1-4))
(iterate-engine-connections (v1-7 *collide-player-list*)
(let* ((v1-8 (the-as collide-shape (-> (the-as connection v1-7) param1)))
(a0-3 (-> v1-8 root-prim)))
(let ((a1-0 (the-as uint128 (-> a0-3 prim-core collide-as)))) (.pand a1-1 s3-0 a1-0))
(b! (zero? (the-as int a1-1)) cfg-7)
(.mul.w.vf acc vf31 vf0)
(.lvf vf1 (&-> a0-3 prim-core world-sphere quad))
(.add.mul.x.vf acc vf28 vf1 acc)
(nop!)
(.add.mul.y.vf acc vf29 vf1 acc)
(nop!)
(.add.mul.z.vf vf6 vf30 vf1 acc)
(nop!)
(.sub.w.vf.xyz vf2 vf6 vf1)
(nop!)
(.add.w.vf.xyz vf3 vf6 vf1)
(nop!)
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a2-0 vf4)
(let ((v1-9 (-> v1-8 process)))
(.mov a3-0 vf5)
(let ((a1-3 (-> obj proc)))
(.pcgtw a2-1 a2-0 s4-0)
(.pcgtw a3-1 s5-0 a3-0)
(.por a2-2 a2-1 a3-1)
(.ppach a2-3 zero a2-2)
(let ((a2-4 (shl (the-as int a2-3) 16))) (nop!) (b! (nonzero? a2-4) cfg-6 :delay (nop!)))
(b! (= a1-3 v1-9) cfg-6 :delay (nop!))))
(add-fg-prim-using-line-sphere a0-3 obj))
(label cfg-6)
0
(label cfg-7)))
(let ((v1-16 (the-as uint128 (make-u128 0 14)))) (.pand v1-17 v1-16 s3-0))
(when (nonzero? (the-as int v1-17))
(let ((v1-19 (the-as uint128 (make-u128 0 2)))) (.pand v1-20 v1-19 s3-0))
(when (nonzero? (the-as int v1-20))
(iterate-engine-connections (v1-23 *collide-hit-by-player-list*)
(let* ((v1-24 (the-as collide-shape (-> (the-as connection v1-23) param1)))
(a0-10 (-> v1-24 root-prim)))
(let ((a1-5 (the-as uint128 (-> a0-10 prim-core collide-as)))) (.pand a1-6 s3-0 a1-5))
(b! (zero? (the-as int a1-6)) cfg-18)
(.mul.w.vf acc vf31 vf0)
(.lvf vf1 (&-> a0-10 prim-core world-sphere quad))
(.add.mul.x.vf acc vf28 vf1 acc)
(nop!)
(.add.mul.y.vf acc vf29 vf1 acc)
(nop!)
(.add.mul.z.vf vf6 vf30 vf1 acc)
(nop!)
(.sub.w.vf.xyz vf2 vf6 vf1)
(nop!)
(.add.w.vf.xyz vf3 vf6 vf1)
(nop!)
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a2-5 vf4)
(let ((v1-25 (-> v1-24 process)))
(.mov a3-2 vf5)
(let ((a1-8 (-> obj proc)))
(.pcgtw a2-6 a2-5 s4-0)
(.pcgtw a3-3 s5-0 a3-2)
(.por a2-7 a2-6 a3-3)
(.ppach a2-8 zero a2-7)
(let ((a2-9 (shl (the-as int a2-8) 16))) (nop!) (b! (nonzero? a2-9) cfg-17 :delay (nop!)))
(b! (= a1-8 v1-25) cfg-17 :delay (nop!))))
(add-fg-prim-using-line-sphere a0-10 obj))
(label cfg-17)
0
(label cfg-18)))
(let ((v1-32 (the-as uint128 (make-u128 0 4)))) (.pand v1-33 v1-32 s3-0))
(when (nonzero? (the-as int v1-33))
(iterate-engine-connections (v1-36 *collide-usually-hit-by-player-list*)
(let* ((v1-37 (the-as collide-shape (-> (the-as connection v1-36) param1)))
(a0-17 (-> v1-37 root-prim)))
(let ((a1-10 (the-as uint128 (-> a0-17 prim-core collide-as)))) (.pand a1-11 s3-0 a1-10))
(b! (zero? (the-as int a1-11)) cfg-28)
(nop!)
(.mul.w.vf acc vf31 vf0)
(.lvf vf1 (&-> a0-17 prim-core world-sphere quad))
(.add.mul.x.vf acc vf28 vf1 acc)
(nop!)
(.add.mul.y.vf acc vf29 vf1 acc)
(nop!)
(.add.mul.z.vf vf6 vf30 vf1 acc)
(nop!)
(.sub.w.vf.xyz vf2 vf6 vf1)
(nop!)
(.add.w.vf.xyz vf3 vf6 vf1)
(nop!)
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a2-10 vf4)
(let ((v1-38 (the-as basic (-> v1-37 process))))
(.mov a3-4 vf5)
(let ((a1-13 (-> obj proc)))
(.pcgtw a2-11 a2-10 s4-0)
(.pcgtw a3-5 s5-0 a3-4)
(.por a2-12 a2-11 a3-5)
(.ppach a2-13 zero a2-12)
(let ((a2-14 (shl (the-as int a2-13) 16))) (nop!) (b! (nonzero? a2-14) cfg-27 :delay (nop!)))
(b! (= a1-13 (the-as process-drawable v1-38)) cfg-27 :delay (nop!))))
(add-fg-prim-using-line-sphere a0-17 obj))
(label cfg-27)
0
(label cfg-28)))
(let ((v1-45 (the-as uint128 (make-u128 0 8)))) (.pand v1-46 v1-45 s3-0))
(when (nonzero? (the-as int v1-46))
(iterate-engine-connections (v1-48 *collide-hit-by-others-list*)
(let* ((v1-49 (the-as collide-shape (-> (the-as connection v1-48) param1)))
(a0-24 (-> v1-49 root-prim)))
(let ((a1-15 (the-as uint128 (-> a0-24 prim-core collide-as)))) (.pand a1-16 s3-0 a1-15))
(b! (zero? (the-as int a1-16)) cfg-38)
(nop!)
(.mul.w.vf acc vf31 vf0)
(.lvf vf1 (&-> a0-24 prim-core world-sphere quad))
(.add.mul.x.vf acc vf28 vf1 acc)
(nop!)
(.add.mul.y.vf acc vf29 vf1 acc)
(nop!)
(.add.mul.z.vf vf6 vf30 vf1 acc)
(nop!)
(.sub.w.vf.xyz vf2 vf6 vf1)
(nop!)
(.add.w.vf.xyz vf3 vf6 vf1)
(nop!)
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a2-15 vf4)
(let ((v1-50 (-> v1-49 process)))
(.mov a3-6 vf5)
(let ((a1-18 (-> obj proc)))
(.pcgtw a2-16 a2-15 s4-0)
(.pcgtw a3-7 s5-0 a3-6)
(.por a2-17 a2-16 a3-7)
(.ppach a2-18 zero a2-17)
(let ((a2-19 (shl (the-as int a2-18) 16))) (nop!) (b! (nonzero? a2-19) cfg-37 :delay (nop!)))
(b! (= a1-18 v1-50) cfg-37 :delay (nop!))))
(add-fg-prim-using-line-sphere a0-24 obj))
(label cfg-37)
0
(label cfg-38)))))
(none)))
(defmethod add-fg-prim-using-line-sphere ((obj collide-shape-prim) (cache collide-cache))
"Add this primitive to the active swept-sphere query. Sphere leaves are copied into the cache,
mesh leaves import triangles overlapping the oriented sweep box, groups recurse through
compatible children, and the base implementation reports an invalid primitive type."
(format 0 "ERROR: Illegal collide-shape-prim type passed to collide-shape-prim::add-fg-prim-using-line-sphere!~%")
(none))
(defmethod add-fg-prim-using-line-sphere ((obj collide-shape-prim-sphere) (cache collide-cache))
"Add this primitive to the active swept-sphere query. Sphere leaves are copied into the cache,
mesh leaves import triangles overlapping the oriented sweep box, groups recurse through
compatible children, and the base implementation reports an invalid primitive type."
(local-vars (t1-1 uint))
(nop!)
(let* ((t0-0 (-> cache prims))
(a3-0 (-> cache num-prims-u))
(t1-0 100)
(v1-0 (-> obj prim-core world-sphere quad))
(t2-0 (* a3-0 2))
(a2-0 (-> obj prim-core quad 1)))
(b! (= a3-0 t1-0) cfg-2 :delay (set! t1-1 (+ t2-0 a3-0)))
(let ((t0-1 (the-as object (&-> t0-0 0 prim-core quad t1-1))))
(let ((a3-1 (+ a3-0 1)))
(set! (-> (the-as collide-cache-prim t0-1) extra-quad) (the-as uint128 0))
(nop!)
(set! (-> (the-as collide-cache-prim t0-1) prim) obj)
(nop!)
(set! (-> (the-as collide-cache-prim t0-1) prim-core world-sphere quad) v1-0)
(nop!)
(set! (-> (the-as collide-cache-prim t0-1) prim-core quad 1) a2-0)
(nop!)
(set! (-> cache num-prims) (the-as int a3-1)))
(nop!)
(set! (-> (the-as collide-cache-prim t0-1) ccache) cache)))
(b! #t cfg-3 :delay (nop!))
(label cfg-2)
(format 0 "ERROR: Exceeded max number of collide-cache prims!~%")
(label cfg-3)
(none))
(defmethod add-fg-prim-using-line-sphere ((obj collide-shape-prim-group) (cache collide-cache))
"Add this primitive to the active swept-sphere query. Sphere leaves are copied into the cache,
mesh leaves import triangles overlapping the oriented sweep box, groups recurse through
compatible children, and the base implementation reports an invalid primitive type."
(local-vars
(zero uint128)
(v1-3 uint128)
(v1-4 uint128)
(v1-5 uint128)
(v1-6 uint128)
(a0-1 collide-shape-prim)
(a1-1 uint128)
(a1-2 uint128))
(rlet ((acc :class vf)
(vf0 :class vf)
(vf1 :class vf)
(vf10 :class vf)
(vf2 :class vf)
(vf28 :class vf)
(vf29 :class vf)
(vf3 :class vf)
(vf30 :class vf)
(vf31 :class vf)
(vf4 :class vf)
(vf5 :class vf))
(init-vf0-vector)
(#when PC_PORT
(load-fffuls-work))
(set! zero (the-as uint128 0))
(let ((v1-0 *collide-work*)
(s5-0 (-> obj prims))
(s4-0 (-> obj num-prims-u)))
(nop!)
(let ((s3-0 (-> v1-0 collide-box4w min quad)))
(nop!)
(let ((s2-0 (-> v1-0 collide-box4w max quad)))
(nop!)
(let ((s1-0 (-> s5-0 0)))
(nop!)
(let ((s0-0 (-> cache collide-with)))
(label cfg-1)
(b! (zero? s4-0) cfg-5 :delay (set! a0-1 s1-0))
(label cfg-2)
(+! s4-0 -1)
(.lvf vf1 (&-> a0-1 prim-core world-sphere quad))
(nop!)
(let ((v1-1 (-> a0-1 prim-core collide-as)))
(.mul.w.vf acc vf31 vf0)
(set! s5-0 (&-> s5-0 1))
(.add.mul.x.vf acc vf28 vf1 acc)
(set! s1-0 (-> s5-0 0))
(let ((v1-2 (logand s0-0 v1-1)))
(.add.mul.y.vf acc vf29 vf1 acc)
(b! (zero? v1-2) cfg-1 :delay (.add.mul.z.vf vf10 vf30 vf1 acc)))))
(.sub.w.vf.xyz vf2 vf10 vf1)
(nop!)
(.add.w.vf.xyz vf3 vf10 vf1)
(nop!)
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(.mov a1-1 vf4)
(nop!)
(.mov v1-3 vf5)
(nop!)
(.pcgtw a1-2 a1-1 s2-0)
(.pcgtw v1-4 s3-0 v1-3)
(.por v1-5 a1-2 v1-4)
(.ppach v1-6 zero v1-5)
(let ((v1-7 (shl (the-as int v1-6) 16))) (nop!) (b! (nonzero? v1-7) cfg-1 :delay (nop!)))
(add-fg-prim-using-line-sphere a0-1 cache)
(b! (nonzero? s4-0) cfg-2 :delay (set! a0-1 s1-0))))))
(label cfg-5)
0
(none)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PROBE ;; PROBE ;; PROBE ;; PROBE ;; PROBE ;; PROBE ;; PROBE ;; PROBE ;; PROBE ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(deftype lsmi-work (structure)
"Scratch state for a moving-sphere mesh test. It tracks the current and incoming best hit
fractions, required action bits, and the candidate triangle result."
((best-u float)
(orig-best-u float)
(action uint32)
(tri-out collide-tri-result :inline)))
(defmethod fill-and-probe-using-y-probe ((obj collide-cache) (probe-origin vector) (probe-length float) (kinds collide-kind) (proc process-drawable) (tri-out collide-tri-result) (ignore-pat pat-surface))
"Refill the cache around the vertical probe centered at start, then cast downward by
probe-length. Skip proc's own primitives and surfaces matching ignore-pat; write the nearest
compatible solid hit to result and return its fraction, or -100000000.0 when nothing is hit."
(fill-using-y-probe obj probe-origin probe-length kinds proc ignore-pat)
(probe-using-y-probe obj probe-origin probe-length kinds tri-out ignore-pat))
(defmethod probe-using-y-probe ((obj collide-cache) (probe-origin vector) (probe-length float) (kinds collide-kind) (tri-out collide-tri-result) (ignore-pat pat-surface))
"Cast downward from start by probe-length against compatible solid cached primitives. Ignore
matching surfaces, write the nearest hit to result, and return its fraction, or -100000000.0
when nothing is hit."
(rlet ((vf0 :class vf)
(vf1 :class vf)
(vf3 :class vf))
(init-vf0-vector)
(let ((work (new 'stack-no-clear 'collide-puyp-work)))
(.mov vf3 probe-length)
(.lvf vf1 (&-> probe-origin quad))
(set! (-> work best-u) 2.0)
(set! (-> work ignore-pat) ignore-pat)
(set! (-> work tri-out) tri-out)
(.sub.x.vf.y vf3 vf0 vf3)
(.svf (&-> work start-pos quad) vf1)
(.mov.vf.xzw vf3 vf0)
(.svf (&-> work move-dist quad) vf3)
(let ((cprim (the-as object (-> obj prims))))
(countdown (prims-left (-> obj num-prims))
(when (and (logtest? kinds (-> (the-as collide-cache-prim cprim) prim-core collide-as))
(logtest? (-> (the-as collide-cache-prim cprim) prim-core action) (collide-action solid)))
(cond
((>= (-> (the-as collide-cache-prim cprim) prim-core prim-type) 0)
(puyp-mesh obj work (the-as collide-cache-prim cprim)))
(else
(if (zero? (logand ignore-pat (-> (the-as collide-shape-prim-sphere (-> (the-as collide-cache-prim cprim) prim)) pat)))
(puyp-sphere obj work (the-as collide-cache-prim cprim))))))
(set! cprim (-> (the-as (inline-array collide-cache-prim) cprim) 1))))
(let ((best-u (-> work best-u))) (if (< 1.0 best-u) (set! best-u -100000000.0)) best-u))))
(defmethod puyp-sphere ((obj collide-cache) (work collide-puyp-work) (cprim collide-cache-prim))
"Intersect the downward probe in work with prim's sphere. When the hit precedes work.best-u,
update the result point, outward normal, surface, and representative tangent triangle."
(let ((hit-u (ray-sphere-intersect (-> work start-pos)
(-> work move-dist)
(the-as vector (-> cprim prim-core))
(-> cprim prim-core world-sphere w))))
(when (and (>= hit-u 0.0) (< hit-u (-> work best-u)))
(set! (-> work best-u) hit-u)
(let ((tri-out (-> work tri-out)))
(set! (-> tri-out pat) (-> (the-as collide-shape-prim-sphere (-> cprim prim)) pat))
(vector+*! (-> tri-out intersect) (-> work start-pos) (-> work move-dist) hit-u)
(vector-! (-> tri-out normal) (-> tri-out intersect) (the-as vector (-> cprim prim-core)))
(vector-normalize! (-> tri-out normal) 1.0)
(set! (-> tri-out vertex 0 quad) (-> tri-out intersect quad))
(point-in-plane-<-point+normal! (-> tri-out vertex 1) (-> tri-out intersect) (-> tri-out normal))
(let* ((tangent (vector-normalize! (vector-! (new 'stack-no-clear 'vector) (-> tri-out vertex 1) (the-as vector (-> tri-out vertex))) 1.0))
(bitangent (vector-cross! (new 'stack-no-clear 'vector) (-> tri-out normal) tangent)))
(vector+*! (-> tri-out vertex 2) (-> tri-out intersect) bitangent 4096.0))))))
(defmethod fill-and-probe-using-line-sphere ((obj collide-cache) (start-pt vector) (move-vec vector) (radius float) (kinds collide-kind) (proc process) (tri-out collide-tri-result) (ignore-pat pat-surface))
"Refill the cache for a sphere of radius moving from start by move, then find the nearest
compatible solid hit. Skip proc's own primitives and surfaces matching ignore-pat; write a hit
to result and return its movement fraction, or -100000000.0 when nothing is hit."
(fill-using-line-sphere obj start-pt move-vec radius kinds (the-as process-drawable proc) ignore-pat)
(probe-using-line-sphere obj start-pt move-vec radius kinds tri-out ignore-pat))
(deftype collide-puls-work (structure)
"Inputs and output for a moving-sphere probe over cached primitives. bsphere holds the starting
center and radius, move-dist holds the sweep vector, and tri-out receives the nearest hit."
((ignore-pat pat-surface :offset-assert 0)
(tri-out collide-tri-result :offset-assert 4)
(bsphere sphere :inline :offset-assert 16)
(move-dist vector :inline :offset-assert 32))
:method-count-assert 9
:size-assert #x30
:flag-assert #x900000030)
(defmethod probe-using-line-sphere ((obj collide-cache) (start-pt vector) (move-vec vector) (radius float) (kinds collide-kind) (tri-out collide-tri-result) (ignore-pat pat-surface))
"Sweep a sphere of radius from start by move against compatible solid cached primitives. Ignore
matching surfaces, write the nearest hit to result, and return its movement fraction, or
-100000000.0 when nothing is hit."
(rlet ((vf0 :class vf)
(vf2 :class vf)
(vf3 :class vf)
(vf4 :class vf))
(init-vf0-vector)
(let ((work (new 'stack-no-clear 'collide-puls-work)))
(.mov vf4 radius)
(.lvf vf3 (&-> start-pt quad))
(.lvf vf2 (&-> move-vec quad))
(set! (-> work ignore-pat) ignore-pat)
(.mul.x.vf.w vf3 vf0 vf4)
(set! (-> work tri-out) tri-out)
(.svf (&-> work move-dist quad) vf2)
(.svf (&-> work bsphere quad) vf3)
(let ((cprim (the-as object (-> obj prims)))
(best-u -100000000.0))
(countdown (prims-left (-> obj num-prims))
(when (and (logtest? kinds (-> (the-as collide-cache-prim cprim) prim-core collide-as))
(logtest? (-> (the-as collide-cache-prim cprim) prim-core action) (collide-action solid)))
(cond
((>= (-> (the-as collide-cache-prim cprim) prim-core prim-type) 0)
(let ((hit-u ((method-of-type collide-cache-prim resolve-moving-sphere-tri)
(the-as collide-cache-prim cprim)
(-> work tri-out)
(the-as collide-prim-core (-> work bsphere))
(-> work move-dist)
best-u
(collide-action solid))))
(if (>= hit-u 0.0) (set! best-u hit-u))))
(else
(when (zero? (logand ignore-pat (-> (the-as collide-shape-prim-sphere (-> (the-as collide-cache-prim cprim) prim)) pat)))
(let ((hit-u ((method-of-type collide-cache-prim resolve-moving-sphere-sphere)
(the-as collide-cache-prim cprim)
(-> work tri-out)
(the-as collide-prim-core (-> work bsphere))
(-> work move-dist)
best-u
(collide-action solid))))
(if (>= hit-u 0.0) (set! best-u hit-u)))))))
(set! cprim (-> (the-as (inline-array collide-cache-prim) cprim) 1)))
best-u))))
(defmethod fill-and-probe-using-spheres ((obj collide-cache) (params collide-using-spheres-params))
"Refill the cache around params' sphere set, then return whether any compatible cached primitive
overlaps one of the spheres."
(fill-using-spheres obj params)
(probe-using-spheres obj params))
(defmethod fill-using-spheres ((obj collide-cache) (params collide-using-spheres-params))
"Refill the cache from the bounding box enclosing params' sphere set."
(let ((query-box (new 'stack-no-clear 'bounding-box)))
(set-from-spheres! query-box (-> params spheres) (the-as int (-> params num-spheres)))
;; this box looks correct
(fill-using-bounding-box obj
query-box
(-> params collide-with)
(-> params proc)
(the-as pat-surface (-> params ignore-pat))))
(none))
(defmethod probe-using-spheres ((obj collide-cache) (params collide-using-spheres-params))
"Return true on the first cached primitive which overlaps one of params' spheres and satisfies
its collision-kind and optional solid-only filters. Reject sets larger than 64 spheres."
(local-vars (v1-12 symbol))
(rlet ((vf1 :class vf)
(vf2 :class vf)
(vf3 :class vf)
(vf4 :class vf)
(vf5 :class vf))
(let ((s5-0 (scratchpad-object collide-puss-work))
(a3-0 64)
(a2-0 (-> params num-spheres)))
(let ((v1-0 (-> s5-0 spheres))
(a1-1 (-> params spheres)))
(let ((a3-1 (- a2-0 (the-as uint a3-0)))) (nop!) (b! (> (the-as int a3-1) 0) cfg-20 :delay (nop!)))
(b! (zero? a2-0) cfg-5 :delay (.lvf vf1 a1-1))
(let ((a2-1 (+ a2-0 -1))
(a1-2 (the-as (inline-array sphere) (-> a1-1 1))))
(.sub.w.vf.xyz vf2 vf1 vf1)
(.svf (&-> v1-0 0 bsphere quad) vf1)
(.add.w.vf.xyz vf3 vf1 vf1)
(let ((v1-1 (-> v1-0 1)))
(.ftoi.vf vf4 vf2)
(nop!)
(.ftoi.vf vf5 vf3)
(nop!)
(nop!)
(.svf (+ (the int v1-1) -32) vf4)
(nop!)
(.svf (+ (the int v1-1) -16) vf5)
(label cfg-3)
(b! (zero? a2-1) cfg-5 :delay (.lvf vf1 a1-2))
(+! a2-1 -1)
(set! a1-2 (the-as (inline-array sphere) (-> a1-2 1)))
(.sub.w.vf.xyz vf4 vf1 vf1)
(.svf (&-> v1-1 bsphere quad) vf1)
(.add.w.vf.xyz vf5 vf1 vf1)
(nop!)
(.min.vf.xyz vf2 vf2 vf4)
(nop!)
(.max.vf.xyz vf3 vf3 vf5)
(nop!)
(.ftoi.vf vf4 vf4)
(nop!)
(.ftoi.vf vf5 vf5)
(nop!)
(nop!)
(.svf (&-> v1-1 bbox4w min quad) vf4)
(nop!)
(.svf (&-> v1-1 bbox4w max quad) vf5)
(b! #t cfg-3 :delay (set! v1-1 (&+ v1-1 48))))))
(label cfg-5)
(.ftoi.vf vf2 vf2)
(nop!)
(.ftoi.vf vf3 vf3)
(nop!)
(nop!)
(.svf (&-> s5-0 spheres-bbox4w min quad) vf2)
(nop!)
(.svf (&-> s5-0 spheres-bbox4w max quad) vf3)
(let ((s4-0 (the-as collide-cache-prim (-> obj prims)))
(s3-0 (-> params collide-with))
(s2-0 (-> obj num-prims)))
(b! #t cfg-18 :delay (nop!))
(label cfg-6)
(+! s2-0 -1)
(when (logtest? s3-0 (-> s4-0 prim-core collide-as))
(when (or (not (-> params solid-only)) (logtest? (-> s4-0 prim-core action) (collide-action solid)))
(if (>= (-> s4-0 prim-core prim-type) 0)
(set! v1-12 (check-mesh-prim-against-spheres s5-0 s4-0 params))
(set! v1-12 (check-sphere-prim-against-spheres s5-0 s4-0 params)))
(when v1-12
;; uncomment to view the point that blocks you from exiting duck.
;;(add-debug-point #t (bucket-id debug-no-zbuf) (-> s5-0 closest-pt))
(return #t))))
(&+! s4-0 48)
(label cfg-18)
(b! (nonzero? s2-0) cfg-6 :delay (nop!)))
(b! #t cfg-21 :delay (nop!))
(label cfg-20)
;; fixed bad format string
(format 0 "ERROR: Exceeded max # of spheres in collide-cache::probe-using-spheres!~%"))
(label cfg-21)
#f))
(defun test-closest-pt-in-triangle ((cache collide-cache))
"Exercise closest-point calculation for every cached triangle against the target position,
retaining the nearest point in temporary debug storage. Return false."
(let ((nearest-point (new 'stack-no-clear 'vector)))
(let ((best-distance-squared -1.0)
(tri (the-as object (-> cache tris)))
(target-position (new 'stack-no-clear 'vector))
(closest-point (new 'stack-no-clear 'vector))
(normal (new 'stack-no-clear 'vector))
(triangles-left (-> cache num-tris)))
(vector-copy! target-position (target-pos 0))
(while (nonzero? triangles-left)
(+! triangles-left -1)
(normal-of-plane normal
(-> (the-as collide-cache-tri tri) vertex 0)
(-> (the-as collide-cache-tri tri) vertex 1)
(-> (the-as collide-cache-tri tri) vertex 2))
(closest-pt-in-triangle closest-point target-position (the-as matrix (-> (the-as collide-cache-tri tri) vertex)) normal)
(let ((distance-squared (vector-vector-distance-squared closest-point target-position)))
(when (or (< best-distance-squared 0.0) (< distance-squared best-distance-squared))
(set! best-distance-squared distance-squared)
(vector-copy! nearest-point closest-point)))
(set! tri (-> (the-as (inline-array collide-cache-tri) tri) 1)))))
#f)