code wiki / (root) / nx_explode3d.nx

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}