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