nx_explode3d.nx source
↩ module page · 283 lines · 10895 B
1// nx_explode3d.nx -- R2 of the CAD car-twin ladder: the 3D EXPLODED-VIEW ENGINE (LIB, no main; gated by
2// nx_explode3d_gate). Lifts the gated nx_assembly explode law (pos = home + explode_vec * t) to 3D parts in
3// nx_meshrender's vertex format (Q14 x,y,z,color stride-4). Parts carry: home centroid, UNIT explode
4// direction (radial from their parent's centroid -- the Li/Agrawala-spirit local separation), and assembly
5// DEPTH (deeper parts travel further = staged explosion, the explodeview signature look). ex3_pose writes the
6// world-space vertex array for any t in 0..1024 -- deterministic integer, so the same t is the same bytes.
7// Geometry here = box/octagon-prism STAND-INS (real STEP face tessellation = R1b, its own rung).
8// ctx e: *i64 slots: 0=lverts 1=nlverts 2=idx 3=ntris 4=nparts 5=home[] 6=dir[] 7=depth[] 8=vstart[]
9// 9=vcount[] 10=maxv 11=maxt 12=maxp. license_tier: ORIGINAL
10import "nx_syscalls.nx"
11import "nx_vecmath.nx"
12const EX3_MAGIC_3400: i64 = 3400
13const EX3_MAGIC_1500: i64 = 1500
14const EX3_MAGIC_1400: i64 = 1400
15const EX3_MAGIC_16384: i64 = 16384
16const EX3_MAGIC_11585: i64 = 11585
17const EX3_MAGIC_1024: i64 = 1024
18
19// Q14 world units (16384 = 1.0). EXPLODE_STEP = travel per assembly-depth level at full t (t=1024).
20const EX3_ONE: i64 = 16384
21const EX3_STEP: i64 = 3600
22
23func ex3_init(e: *i64, lverts: *i64, idx: *i64, home: *i64, dir: *i64, depth: *i64, vstart: *i64, vcount: *i64, maxv: i64, maxt: i64, maxp: i64, scratch: *i64) -> i64 {
24 e[0] = lverts as i64
25 e[1] = 0
26 e[2] = idx as i64
27 e[3] = 0
28 e[4] = 0
29 e[5] = home as i64
30 e[6] = dir as i64
31 e[7] = depth as i64
32 e[8] = vstart as i64
33 e[9] = vcount as i64
34 e[10] = maxv
35 e[11] = maxt
36 e[12] = maxp
37 e[13] = scratch as i64 // 2-slot scratch (wasm-safe: no mmap inside lib calls)
38 return 0
39}
40
41// assembled-layout ring radius + vertical drop per assembly depth (Q14; compact when assembled).
42// Shared by the native gate AND the explodelab tree generator (one layout law).
43func ex3_rad(d: i64) -> i64 { if d == 1 { return EX3_MAGIC_3400 } if d == 2 { return EX3_MAGIC_1500 } return 800 }
44func ex3_dyv(d: i64) -> i64 { if d == 1 { return 0 - EX3_MAGIC_1400 } if d == 2 { return 0 - 1000 } return 0 - 700 }
45
46func ex3_isqrt(n: i64) -> i64 { return vm_isqrt(n) }
47
48// octagon ring direction k (0..7), Q14 unit; out2[0]=cos out2[1]=sin
49func ex3_oct(k: i64, out2: *i64) -> i64 {
50 let m: i64 = k % 8
51 if m == 0 { out2[0] = EX3_MAGIC_16384; out2[1] = 0; return 0 }
52 if m == 1 { out2[0] = EX3_MAGIC_11585; out2[1] = EX3_MAGIC_11585; return 0 }
53 if m == 2 { out2[0] = 0; out2[1] = EX3_MAGIC_16384; return 0 }
54 if m == 3 { out2[0] = 0 - EX3_MAGIC_11585; out2[1] = EX3_MAGIC_11585; return 0 }
55 if m == 4 { out2[0] = 0 - EX3_MAGIC_16384; out2[1] = 0; return 0 }
56 if m == 5 { out2[0] = 0 - EX3_MAGIC_11585; out2[1] = 0 - EX3_MAGIC_11585; return 0 }
57 if m == 6 { out2[0] = 0; out2[1] = 0 - EX3_MAGIC_16384; return 0 }
58 out2[0] = EX3_MAGIC_11585; out2[1] = 0 - EX3_MAGIC_11585
59 return 0
60}
61
62// begin a part at home (hx,hy,hz) with RAW explode direction (dx,dy,dz) (normalized here; zero -> +y) + depth
63func ex3_part_begin(e: *i64, hx: i64, hy: i64, hz: i64, dx: i64, dy: i64, dz: i64, dep: i64) -> i64 {
64 if e[4] >= e[12] { return 0 - 1 }
65 let p: i64 = e[4]
66 let home: *i64 = e[5] as *i64
67 let dir: *i64 = e[6] as *i64
68 let depth: *i64 = e[7] as *i64
69 let vstart: *i64 = e[8] as *i64
70 let vcount: *i64 = e[9] as *i64
71 home[p * 3 + 0] = hx
72 home[p * 3 + 1] = hy
73 home[p * 3 + 2] = hz
74 let len2: i64 = dx * dx + dy * dy + dz * dz
75 let len: i64 = ex3_isqrt(len2)
76 if len == 0 {
77 dir[p * 3 + 0] = 0
78 dir[p * 3 + 1] = EX3_ONE
79 dir[p * 3 + 2] = 0
80 } else {
81 dir[p * 3 + 0] = (dx * EX3_ONE) / len
82 dir[p * 3 + 1] = (dy * EX3_ONE) / len
83 dir[p * 3 + 2] = (dz * EX3_ONE) / len
84 }
85 depth[p] = dep
86 vstart[p] = e[1]
87 vcount[p] = 0
88 e[4] = p + 1
89 return p
90}
91
92func ex3_vert(e: *i64, x: i64, y: i64, z: i64, color: i64) -> i64 {
93 if e[1] >= e[10] { return 0 - 1 }
94 let lv: *i64 = e[0] as *i64
95 let i: i64 = e[1]
96 lv[i * 4 + 0] = x
97 lv[i * 4 + 1] = y
98 lv[i * 4 + 2] = z
99 lv[i * 4 + 3] = color
100 e[1] = i + 1
101 let vcount: *i64 = e[9] as *i64
102 let p: i64 = e[4] - 1
103 if p >= 0 { vcount[p] = vcount[p] + 1 }
104 return i
105}
106
107func ex3_tri(e: *i64, a: i64, b: i64, c: i64) -> i64 {
108 if e[3] >= e[11] { return 0 - 1 }
109 let ix: *i64 = e[2] as *i64
110 let t: i64 = e[3]
111 ix[t * 3 + 0] = a
112 ix[t * 3 + 1] = b
113 ix[t * 3 + 2] = c
114 e[3] = t + 1
115 return t
116}
117
118// axis-aligned box at absolute assembled center (cx,cy,cz), half-extents (hx,hy,hz)
119func ex3_add_box(e: *i64, cx: i64, cy: i64, cz: i64, hx: i64, hy: i64, hz: i64, color: i64) -> i64 {
120 let v0: i64 = ex3_vert(e, cx - hx, cy - hy, cz - hz, color)
121 let v1: i64 = ex3_vert(e, cx + hx, cy - hy, cz - hz, color)
122 let v2: i64 = ex3_vert(e, cx + hx, cy + hy, cz - hz, color)
123 let v3: i64 = ex3_vert(e, cx - hx, cy + hy, cz - hz, color)
124 let v4: i64 = ex3_vert(e, cx - hx, cy - hy, cz + hz, color)
125 let v5: i64 = ex3_vert(e, cx + hx, cy - hy, cz + hz, color)
126 let v6: i64 = ex3_vert(e, cx + hx, cy + hy, cz + hz, color)
127 let v7: i64 = ex3_vert(e, cx - hx, cy + hy, cz + hz, color)
128 if v7 < 0 { return 0 - 1 }
129 let t1: i64 = ex3_tri(e, v0, v1, v2)
130 let t2: i64 = ex3_tri(e, v0, v2, v3)
131 let t3: i64 = ex3_tri(e, v4, v6, v5)
132 let t4: i64 = ex3_tri(e, v4, v7, v6)
133 let t5: i64 = ex3_tri(e, v0, v4, v5)
134 let t6: i64 = ex3_tri(e, v0, v5, v1)
135 let t7: i64 = ex3_tri(e, v3, v2, v6)
136 let t8: i64 = ex3_tri(e, v3, v6, v7)
137 let t9: i64 = ex3_tri(e, v0, v3, v7)
138 let ta: i64 = ex3_tri(e, v0, v7, v4)
139 let tb: i64 = ex3_tri(e, v1, v5, v6)
140 let tc: i64 = ex3_tri(e, v1, v6, v2)
141 if tc < 0 { return 0 - 1 }
142 return 0
143}
144
145// octagonal prism at absolute center; r = ring radius, hh = half height; axis 0 = y (upright), 1 = x (lying)
146func ex3_add_prism(e: *i64, cx: i64, cy: i64, cz: i64, r: i64, hh: i64, axis: i64, color: i64) -> i64 {
147 let out2: *i64 = e[13] as *i64
148 var first: i64 = 0 - 1
149 var k: i64 = 0
150 while k < 8 {
151 let ig: i64 = ex3_oct(k, out2)
152 let rx: i64 = (out2[0] * r) / EX3_ONE
153 let rz: i64 = (out2[1] * r) / EX3_ONE
154 var b: i64 = 0
155 var t: i64 = 0
156 if axis == 0 {
157 b = ex3_vert(e, cx + rx, cy - hh, cz + rz, color)
158 t = ex3_vert(e, cx + rx, cy + hh, cz + rz, color)
159 } else {
160 b = ex3_vert(e, cx - hh, cy + rx, cz + rz, color)
161 t = ex3_vert(e, cx + hh, cy + rx, cz + rz, color)
162 }
163 if k == 0 { first = b }
164 k = k + 1
165 }
166 var bc: i64 = 0
167 var tc: i64 = 0
168 if axis == 0 {
169 bc = ex3_vert(e, cx, cy - hh, cz, color)
170 tc = ex3_vert(e, cx, cy + hh, cz, color)
171 } else {
172 bc = ex3_vert(e, cx - hh, cy, cz, color)
173 tc = ex3_vert(e, cx + hh, cy, cz, color)
174 }
175 if tc < 0 { return 0 - 1 }
176 // rim vert k: bottom = first + 2k, top = first + 2k + 1
177 k = 0
178 while k < 8 {
179 let k1: i64 = (k + 1) % 8
180 let bk: i64 = first + 2 * k
181 let tk: i64 = first + 2 * k + 1
182 let bk1: i64 = first + 2 * k1
183 let tk1: i64 = first + 2 * k1 + 1
184 let s1: i64 = ex3_tri(e, bk, bk1, tk)
185 let s2: i64 = ex3_tri(e, tk, bk1, tk1)
186 let c1: i64 = ex3_tri(e, bc, bk1, bk)
187 let c2: i64 = ex3_tri(e, tc, tk, tk1)
188 if c2 < 0 { return 0 - 1 }
189 k = k + 1
190 }
191 return 0
192}
193
194// displacement magnitude (Q14) of a depth-d part at explode factor t (0..1024)
195func ex3_disp_mag(t: i64, d: i64) -> i64 { return (t * (EX3_STEP * d)) / EX3_MAGIC_1024 }
196
197// write world verts for explode factor t into wverts (same layout/order as lverts)
198func ex3_pose(e: *i64, t: i64, wverts: *i64) -> i64 {
199 let lv: *i64 = e[0] as *i64
200 let dir: *i64 = e[6] as *i64
201 let depth: *i64 = e[7] as *i64
202 let vstart: *i64 = e[8] as *i64
203 let vcount: *i64 = e[9] as *i64
204 var p: i64 = 0
205 while p < e[4] {
206 let mag: i64 = ex3_disp_mag(t, depth[p])
207 let dx: i64 = (dir[p * 3 + 0] * mag) / EX3_ONE
208 let dy: i64 = (dir[p * 3 + 1] * mag) / EX3_ONE
209 let dz: i64 = (dir[p * 3 + 2] * mag) / EX3_ONE
210 var i: i64 = vstart[p]
211 let iend: i64 = vstart[p] + vcount[p]
212 while i < iend {
213 wverts[i * 4 + 0] = lv[i * 4 + 0] + dx
214 wverts[i * 4 + 1] = lv[i * 4 + 1] + dy
215 wverts[i * 4 + 2] = lv[i * 4 + 2] + dz
216 wverts[i * 4 + 3] = lv[i * 4 + 3]
217 i = i + 1
218 }
219 p = p + 1
220 }
221 return 0
222}
223
224// mean pairwise L1 distance between part centroids at explode factor t (integer, deterministic)
225func ex3_mean_pairdist(e: *i64, t: i64) -> i64 {
226 let home: *i64 = e[5] as *i64
227 let dir: *i64 = e[6] as *i64
228 let depth: *i64 = e[7] as *i64
229 let n: i64 = e[4]
230 if n < 2 { return 0 }
231 var total: i64 = 0
232 var pairs: i64 = 0
233 var i: i64 = 0
234 while i < n {
235 let mi: i64 = ex3_disp_mag(t, depth[i])
236 let xi: i64 = home[i * 3 + 0] + (dir[i * 3 + 0] * mi) / EX3_ONE
237 let yi: i64 = home[i * 3 + 1] + (dir[i * 3 + 1] * mi) / EX3_ONE
238 let zi: i64 = home[i * 3 + 2] + (dir[i * 3 + 2] * mi) / EX3_ONE
239 var j: i64 = i + 1
240 while j < n {
241 let mj: i64 = ex3_disp_mag(t, depth[j])
242 let xj: i64 = home[j * 3 + 0] + (dir[j * 3 + 0] * mj) / EX3_ONE
243 let yj: i64 = home[j * 3 + 1] + (dir[j * 3 + 1] * mj) / EX3_ONE
244 let zj: i64 = home[j * 3 + 2] + (dir[j * 3 + 2] * mj) / EX3_ONE
245 var ax: i64 = xi - xj
246 if ax < 0 { ax = 0 - ax }
247 var ay: i64 = yi - yj
248 if ay < 0 { ay = 0 - ay }
249 var az: i64 = zi - zj
250 if az < 0 { az = 0 - az }
251 total = total + ax + ay + az
252 pairs = pairs + 1
253 j = j + 1
254 }
255 i = i + 1
256 }
257 return total / pairs
258}
259
260// mean displacement magnitude (Q14 L1 of the applied offset) over parts of exactly depth d at factor t
261func ex3_mean_disp_at_depth(e: *i64, t: i64, d: i64) -> i64 {
262 let dir: *i64 = e[6] as *i64
263 let depth: *i64 = e[7] as *i64
264 var total: i64 = 0
265 var cnt: i64 = 0
266 var p: i64 = 0
267 while p < e[4] {
268 if depth[p] == d {
269 let mag: i64 = ex3_disp_mag(t, depth[p])
270 var dx: i64 = (dir[p * 3 + 0] * mag) / EX3_ONE
271 if dx < 0 { dx = 0 - dx }
272 var dy: i64 = (dir[p * 3 + 1] * mag) / EX3_ONE
273 if dy < 0 { dy = 0 - dy }
274 var dz: i64 = (dir[p * 3 + 2] * mag) / EX3_ONE
275 if dz < 0 { dz = 0 - dz }
276 total = total + dx + dy + dz
277 cnt = cnt + 1
278 }
279 p = p + 1
280 }
281 if cnt == 0 { return 0 }
282 return total / cnt
283}