nx_nxa_dyna.nx source
↩ module page · 473 lines · 24228 B
1// nx_nxa_dyna.nx -- WRITE THE DYNA SECTION: per-bone soft-tissue physics DECLARED IN THE ASSET,
2// with the lateral breast-bone pair DERIVED FROM THE MESH'S OWN GEOMETRY.
3//
4// WHY THIS EXISTS. nx_asset_floor_gate measured our shipped character and found 5 midline chest
5// joints and ZERO lateral pair. Real breast bones come in L/R pairs OFF the midline; midline joints
6// at chest height ARE THE SPINE CHAIN, so driving them swings the whole torso -- the wobble the
7// operator kept reporting. With no bone to drive, chest motion had to be faked at the shader vertex
8// band, and the engine's own joint picker (|bind.x| < 9000, i.e. near-midline) was selecting spine
9// joints as stand-ins using a superseded height normalisation (z/171530, which ignores the mesh
10// origin -- the same error that once put the nipples below the legs).
11//
12// THE FIX IS A FORMAT CAPABILITY, NOT ANOTHER SHADER PATCH. FBX and its class carry no physics at
13// all: a rig travels as geometry plus a joint tree, and every consumer re-invents the dynamics in
14// engine code. DYNA makes the ASSET carry its own soft-tissue contract -- anchor, axis, stiffness,
15// damping, travel clamp, influence radius, falloff -- so the engine DRIVES DECLARED DATA instead of
16// hardcoding bands. That is the difference between a mannequin and a body.
17//
18// ANCHORS ARE MEASURED, NEVER GUESSED. For each side we take the CENTROID of the front-hemisphere
19// vertices inside the bust band. Anatomy therefore comes from the mesh in front of us, so a
20// different body type yields different anchors -- and the firm/bouncy RANGE the operator asked for
21// falls out of geometry rather than a table of magic numbers.
22//
23// Physics constants are NOT invented here: they are the banked, in-band values already proven by
24// the chest-motion oracle (2.5 Hz, 3.2 cm travel, 0.0 cm torso, 0.89 coherence) and by nx_softbind
25// (chest K base 60 step 25, C base 100 step 16). Moving them out of code and into the asset is the
26// point -- rule 11, and the recombinator wants them as data.
27//
28// nx_nxa_dyna <in.nxa> <out.nxa>
29//
30// ADDITIVE AND IDEMPOTENT: the input is never modified; an existing DYNA is REPLACED, not doubled,
31// so running twice is identical to running once.
32//
33// DYNA payload (i64 words): [0]=n_bones [1]=stride(12), then per bone:
34// 0 side(-1 L,+1 R,0 mid) 1 anchor_x 2 anchor_y 3 anchor_z
35// 4 k_q8 5 c_q8 6 max_disp 7 influence_radius 8 falloff_q8
36// 9 axis_mask(1=x,2=y,4=z) 10 k_step_q8 11 c_step_q8
37// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
38import "nx_syscalls.nx"
39import "nx_nxa.nx"
40const ND_MAGIC_1710: i64 = 1710
41const ND_MAGIC_4096: i64 = 4096
42
43const ND_HDR: i64 = 32
44const ND_TOCE: i64 = 32
45const ND_STRIDE: i64 = 12
46const ND_MAXSEC: i64 = 64
47const ND_MODE: i64 = 0x1a4
48const ND_ERRFD: i64 = 2
49const ND_EXIT_USAGE: i64 = 2
50const ND_EXIT_BAD: i64 = 3
51const ND_EXIT_IO: i64 = 1
52const ND_EXIT_NOPAIR: i64 = 4
53// bust band: NIP at 744 permil of stature, derived 2026-08-02 from anthropometric ratios mapped
54// through the asset's own pelvis(580)/head(917) anchors. Half-width 40 permil brackets the band
55// the shader already used (0.085 smoothstep width).
56// Admissible left/right vertex-count imbalance for a bust band, in permil. See the balance check
57// in main() for the derivation and the observed separation it is set from.
58const ND_BAL_MAX_PERMIL: i64 = 250
59const ND_NIP_PERMIL: i64 = 744
60const ND_BAND_PERMIL: i64 = 40
61// R7 REGIONS (2026-08-26): belly/glute/thigh bands from the same permil arithmetic as the
62// bust band -- pelvis anchor 580, so the belly soft mass sits just above it, the glutes just
63// below it on the POSTERIOR side, the thigh pair below that. Constants K/C are the SAME
64// pinned tissue bands (gamefeel_oracle tissue_fn 4000-5200 mHz, zeta 350-600 permil are
65// tissue-class rows, not breast-specific per Haake-Scurr's forced-damped model); regions
66// differ by MEASURED GEOMETRY (anchor, influence), never by tasted constants.
67const ND_BELLY_PERMIL: i64 = 520
68const ND_GLUTE_PERMIL: i64 = 555
69const ND_THIGH_PERMIL: i64 = 420
70const ND_RBAND_PERMIL: i64 = 45
71// banked oracle values -- see nx_softbind + gamefeel_oracle.conf. NOT tuned here.
72const ND_K_BASE: i64 = 60
73const ND_K_STEP: i64 = 25
74const ND_C_BASE: i64 = 100
75const ND_C_STEP: i64 = 16
76const ND_FALLOFF_Q8: i64 = 256
77const ND_AXIS_ALL: i64 = 7
78
79func nd_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
80func nd_werr(s: *u8) -> i64 { sys_write(ND_ERRFD, s, nd_slen(s)); return 0 }
81func nd_out(s: *u8) -> i64 { sys_write(1, s, nd_slen(s)); return 0 }
82func nd_num(v: i64) -> i64 {
83 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
84 var m: i64 = v
85 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
86 let t: *u8 = sys_mmap(32)
87 var k: i64 = 0
88 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
89 let o: *u8 = sys_mmap(32)
90 var i: i64 = 0
91 while i < k { o[i] = t[k - 1 - i]; i = i + 1 }
92 sys_write(1, o, k)
93 return 0
94}
95func nd_rd64(b: *u8, off: i64) -> i64 {
96 var v: i64 = 0
97 var i: i64 = 7
98 while i >= 0 { v = v*256 + ((b[off + i] & 0xff) as i64); i = i - 1 }
99 return v
100}
101func nd_wr64(b: *u8, off: i64, v: i64) -> i64 {
102 var m: i64 = v
103 var i: i64 = 0
104 while i < 8 { b[off + i] = (m & 0xff) as u8; m = m >> 8; i = i + 1 }
105 return 0
106}
107func nd_tageq(b: *u8, off: i64, t: *u8) -> i64 {
108 var i: i64 = 0
109 while i < 4 { if b[off + i] != t[i] { return 0 } i = i + 1 }
110 return 1
111}
112
113// R7 helpers: centroid of band vertices, split L/R (pair) or single (midline, one hemisphere).
114// out layout pair: [lx,ly,lz,ln, rx,ry,rz,rn, llat,rlat]; mid: [x,y,z,n]. Report-only on empty.
115func nd_band_pair(b: *u8, vo: i64, nv: i64, la: i64, aa: i64, sa: i64, zmin: i64, span: i64, plo: i64, phi: i64, asign: i64, out: *i64) -> i64 {
116 var k: i64 = 0
117 while k < 10 { out[k] = 0; k = k + 1 }
118 var j: i64 = 0
119 while j < nv {
120 let ox: i64 = nd_rd64(b, vo + 8 + (j*3)*8)
121 let oy: i64 = nd_rd64(b, vo + 8 + (j*3 + 1)*8)
122 let oz: i64 = nd_rd64(b, vo + 8 + (j*3 + 2)*8)
123 let plat: i64 = nd_rd64(b, vo + 8 + (j*3 + la)*8)
124 let pant: i64 = nd_rd64(b, vo + 8 + (j*3 + aa)*8)
125 let pst: i64 = nd_rd64(b, vo + 8 + (j*3 + sa)*8)
126 let pm: i64 = (pst - zmin)*1000/span
127 if pm >= plo { if pm <= phi { if pant*asign < 0 {
128 if plat < 0 { out[0] = out[0] + ox; out[1] = out[1] + oy; out[2] = out[2] + oz; out[8] = out[8] + plat; out[3] = out[3] + 1 }
129 if plat > 0 { out[4] = out[4] + ox; out[5] = out[5] + oy; out[6] = out[6] + oz; out[9] = out[9] + plat; out[7] = out[7] + 1 }
130 } } }
131 j = j + 1
132 }
133 if out[3] > 0 { if out[7] > 0 { return 1 } }
134 return 0
135}
136func nd_band_mid(b: *u8, vo: i64, nv: i64, aa: i64, sa: i64, zmin: i64, span: i64, plo: i64, phi: i64, asign: i64, out: *i64) -> i64 {
137 out[0] = 0; out[1] = 0; out[2] = 0; out[3] = 0
138 var j: i64 = 0
139 while j < nv {
140 let ox: i64 = nd_rd64(b, vo + 8 + (j*3)*8)
141 let oy: i64 = nd_rd64(b, vo + 8 + (j*3 + 1)*8)
142 let oz: i64 = nd_rd64(b, vo + 8 + (j*3 + 2)*8)
143 let pant: i64 = nd_rd64(b, vo + 8 + (j*3 + aa)*8)
144 let pst: i64 = nd_rd64(b, vo + 8 + (j*3 + sa)*8)
145 let pm: i64 = (pst - zmin)*1000/span
146 if pm >= plo { if pm <= phi { if pant*asign < 0 {
147 out[0] = out[0] + ox; out[1] = out[1] + oy; out[2] = out[2] + oz; out[3] = out[3] + 1
148 } } }
149 j = j + 1
150 }
151 if out[3] > 0 { return 1 }
152 return 0
153}
154// emit one DYNA row at wo; returns the new wo. side/anchor/k/c/max/infl/falloff/axis/ksts/csts.
155func nd_row(nb: *u8, wo0: i64, side: i64, ax: i64, ay: i64, az: i64, k: i64, c: i64, maxd: i64, infl: i64) -> i64 {
156 var wo: i64 = wo0
157 nd_wr64(nb, wo, side); wo = wo + 8
158 nd_wr64(nb, wo, ax); wo = wo + 8
159 nd_wr64(nb, wo, ay); wo = wo + 8
160 nd_wr64(nb, wo, az); wo = wo + 8
161 nd_wr64(nb, wo, k); wo = wo + 8
162 nd_wr64(nb, wo, c); wo = wo + 8
163 nd_wr64(nb, wo, maxd); wo = wo + 8
164 nd_wr64(nb, wo, infl); wo = wo + 8
165 nd_wr64(nb, wo, ND_FALLOFF_Q8); wo = wo + 8
166 nd_wr64(nb, wo, ND_AXIS_ALL); wo = wo + 8
167 nd_wr64(nb, wo, ND_K_STEP); wo = wo + 8
168 nd_wr64(nb, wo, ND_C_STEP); wo = wo + 8
169 return wo
170}
171
172func main(argc: i64, argv: *i64) -> i64 {
173 if argc < 3 {
174 nd_werr("usage: nx_nxa_dyna <in.nxa> <out.nxa>\n" as *u8)
175 nd_werr(" derives the lateral soft-tissue bone pair from the mesh and writes a DYNA section.\n" as *u8)
176 nd_werr(" <in.nxa> is never modified; an existing DYNA is replaced, so re-runs are identical.\n" as *u8)
177 sys_exit(ND_EXIT_USAGE)
178 return ND_EXIT_USAGE
179 }
180 let inp: *u8 = argv[1] as *u8
181 let outp: *u8 = argv[2] as *u8
182 let lp: *i64 = sys_mmap(16) as *i64
183 let b: *u8 = sys_read_file(inp, lp)
184 if (b as i64) == 0 { nd_werr("DYNA-RED cannot read input NXA\n" as *u8); sys_exit(ND_EXIT_IO); return ND_EXIT_IO }
185 let flen: i64 = lp[0]
186 if flen < ND_HDR { nd_werr("DYNA-RED file too short to be an NXA\n" as *u8); sys_exit(ND_EXIT_BAD); return ND_EXIT_BAD }
187 if nd_rd64(b, 0) != nxa_magic() { nd_werr("DYNA-RED not an NXA (bad magic)\n" as *u8); sys_exit(ND_EXIT_BAD); return ND_EXIT_BAD }
188 if nd_rd64(b, 8) > NXA_VER { nd_werr("DYNA-RED future version -- refusing\n" as *u8); sys_exit(ND_EXIT_BAD); return ND_EXIT_BAD }
189 let ns: i64 = nd_rd64(b, 16)
190 if ns < 1 { nd_werr("DYNA-RED section count invalid\n" as *u8); sys_exit(ND_EXIT_BAD); return ND_EXIT_BAD }
191 if ns >= ND_MAXSEC { nd_werr("DYNA-RED section table full\n" as *u8); sys_exit(ND_EXIT_BAD); return ND_EXIT_BAD }
192
193 // ---- locate VERT and any existing DYNA ----
194 var vo: i64 = 0 - 1
195 var dyi: i64 = 0 - 1
196 var s: i64 = 0
197 while s < ns {
198 let e: i64 = ND_HDR + s*ND_TOCE
199 if nd_tageq(b, e, "VERT" as *u8) == 1 { vo = nd_rd64(b, e + 8) }
200 if nd_tageq(b, e, "DYNA" as *u8) == 1 { dyi = s }
201 s = s + 1
202 }
203 if vo < 0 { nd_werr("DYNA-RED no VERT section -- cannot derive anatomy\n" as *u8); sys_exit(ND_EXIT_BAD); return ND_EXIT_BAD }
204 let nv: i64 = nd_rd64(b, vo)
205
206 // ---- pass 1: the BODY AXES ARE DERIVED FROM THE MESH'S OWN EXTENTS, never assumed ----
207 // Stature is the largest extent. Of the remaining two the LATERAL axis is the larger (a
208 // humanoid is wider across the shoulders than it is deep front-to-back) and the ANTERIOR axis
209 // is the smaller. Both assumptions are DECLARED here rather than buried.
210 // WHY THIS REPLACED A HARDCODED CONVENTION: this reader assumed z-is-stature and y-is-front.
211 // Our own NXA assets are Z-up, but FBX and VRM donors are authored Y-UP -- so the band test
212 // selected nothing on every donor and the organ refused all four with "no left-side front
213 // vertices in the bust band". The refusal was correct; the axis assumption was not. Measured
214 // 2026-08-23; the sibling renderer lane derived the same axis from vertex extents the same day.
215 // Stature from the VERTEX span (the same basis the browser probe uses; a first version of the
216 // floor gate normalised on the JOINT span and the two instruments then CONTRADICTED each other
217 // on the same asset, because the joint ladder is shorter than the mesh -- feet and crown carry
218 // no bones).
219 var mnc0: i64 = 0
220 var mxc0: i64 = 0
221 var mnc1: i64 = 0
222 var mxc1: i64 = 0
223 var mnc2: i64 = 0
224 var mxc2: i64 = 0
225 var first: i64 = 1
226 var i: i64 = 0
227 while i < nv {
228 let c0: i64 = nd_rd64(b, vo + 8 + (i*3)*8)
229 let c1: i64 = nd_rd64(b, vo + 8 + (i*3 + 1)*8)
230 let c2: i64 = nd_rd64(b, vo + 8 + (i*3 + 2)*8)
231 if first == 1 { mnc0 = c0; mxc0 = c0; mnc1 = c1; mxc1 = c1; mnc2 = c2; mxc2 = c2; first = 0 }
232 if c0 < mnc0 { mnc0 = c0 }
233 if c0 > mxc0 { mxc0 = c0 }
234 if c1 < mnc1 { mnc1 = c1 }
235 if c1 > mxc1 { mxc1 = c1 }
236 if c2 < mnc2 { mnc2 = c2 }
237 if c2 > mxc2 { mxc2 = c2 }
238 i = i + 1
239 }
240 let ex0: i64 = mxc0 - mnc0
241 let ex1: i64 = mxc1 - mnc1
242 let ex2: i64 = mxc2 - mnc2
243 var sa: i64 = 2
244 if ex0 >= ex1 { if ex0 >= ex2 { sa = 0 } }
245 if ex1 > ex0 { if ex1 >= ex2 { sa = 1 } }
246 var la: i64 = 0
247 var aa: i64 = 1
248 if sa == 0 { la = 1; aa = 2; if ex2 > ex1 { la = 2; aa = 1 } }
249 if sa == 1 { la = 0; aa = 2; if ex2 > ex0 { la = 2; aa = 0 } }
250 if sa == 2 { la = 0; aa = 1; if ex1 > ex0 { la = 1; aa = 0 } }
251 var zmin: i64 = mnc2
252 if sa == 0 { zmin = mnc0 }
253 if sa == 1 { zmin = mnc1 }
254 var span: i64 = ex2
255 if sa == 0 { span = ex0 }
256 if sa == 1 { span = ex1 }
257 nd_out(" axes DERIVED: stature=" as *u8); nd_num(sa)
258 nd_out(" lateral=" as *u8); nd_num(la)
259 nd_out(" anterior=" as *u8); nd_num(aa)
260 nd_out(" (0=x 1=y 2=z)
261" as *u8)
262 if span <= 0 { nd_werr("DYNA-RED degenerate vertex span\n" as *u8); sys_exit(ND_EXIT_BAD); return ND_EXIT_BAD }
263
264 // ---- pass 2: per-side centroid of FRONT-HEMISPHERE vertices inside the bust band ----
265 // front hemisphere is y<0 in this asset's frame (the floor gate and the shader agree on this).
266 var lx: i64 = 0
267 var ly: i64 = 0
268 var lz: i64 = 0
269 var ln: i64 = 0
270 var rx: i64 = 0
271 var ry: i64 = 0
272 var rz: i64 = 0
273 var rn: i64 = 0
274 let plo: i64 = ND_NIP_PERMIL - ND_BAND_PERMIL
275 let phi: i64 = ND_NIP_PERMIL + ND_BAND_PERMIL
276 // The ANTERIOR SIGN is derived by TRIAL, not assumed: we accumulate with front = negative
277 // anterior first (the convention this organ shipped with, so a Z-up asset is unchanged and the
278 // neutrality of this rewrite is provable), and only if that yields no pair do we try the
279 // opposite sign. Refusal is preserved for the case where neither sign produces both lobes --
280 // a fabricated anchor is still worse than none.
281 var llat: i64 = 0
282 var rlat: i64 = 0
283 var asign: i64 = 1
284 var trial: i64 = 0
285 while trial < 2 {
286 if ln == 0 {
287 if rn == 0 {
288 if trial == 1 { asign = 0 - 1 }
289 lx = 0; ly = 0; lz = 0; llat = 0
290 rx = 0; ry = 0; rz = 0; rlat = 0
291 var j: i64 = 0
292 while j < nv {
293 let ox: i64 = nd_rd64(b, vo + 8 + (j*3)*8)
294 let oy: i64 = nd_rd64(b, vo + 8 + (j*3 + 1)*8)
295 let oz: i64 = nd_rd64(b, vo + 8 + (j*3 + 2)*8)
296 let plat: i64 = nd_rd64(b, vo + 8 + (j*3 + la)*8)
297 let pant: i64 = nd_rd64(b, vo + 8 + (j*3 + aa)*8)
298 let pst: i64 = nd_rd64(b, vo + 8 + (j*3 + sa)*8)
299 let pm: i64 = (pst - zmin)*1000/span
300 if pm >= plo { if pm <= phi { if pant*asign < 0 {
301 if plat < 0 { lx = lx + ox; ly = ly + oy; lz = lz + oz; llat = llat + plat; ln = ln + 1 }
302 if plat > 0 { rx = rx + ox; ry = ry + oy; rz = rz + oz; rlat = rlat + plat; rn = rn + 1 }
303 } } }
304 j = j + 1
305 }
306 }
307 }
308 trial = trial + 1
309 }
310 nd_out(" anterior sign chosen by trial: " as *u8); nd_num(asign)
311 nd_out(" (left=" as *u8); nd_num(ln); nd_out(" right=" as *u8); nd_num(rn); nd_out(")
312" as *u8)
313 // REFUSE rather than emit a fabricated pair. A DYNA section naming anchors we could not measure
314 // would be worse than none: the engine would drive declared-looking data that is actually a
315 // guess, and every downstream reader would treat it as measured.
316 if ln == 0 { nd_werr("DYNA-RED no left-side front vertices in the bust band -- refusing to fabricate an anchor\n" as *u8); sys_exit(ND_EXIT_NOPAIR); return ND_EXIT_NOPAIR }
317 if rn == 0 { nd_werr("DYNA-RED no right-side front vertices in the bust band -- refusing to fabricate an anchor\n" as *u8); sys_exit(ND_EXIT_NOPAIR); return ND_EXIT_NOPAIR }
318 // ---- L/R BALANCE: is what we found actually TISSUE? -------------------------------------
319 // The anchor search proves a pair EXISTS; it never proved the pair is a BUST. On a costumed
320 // donor the bust band happily selects a hat brim, a collar or a dress panel, and the organ
321 // then emits a confident, measured-looking anchor for a GARMENT. Measured 2026-08-23 on the
322 // real corpus: paladin 499/497 bound verts (0.4 percent imbalance) against dark_witch 8/44
323 // (450 percent) and toon3d8 16197/8299 (95 percent), with derived influence radii swinging
324 // 744 to 34290 -- the imbalance is exactly the signal that separates tissue from costume.
325 // A human bust is bilaterally symmetric to a few percent. ND_BAL_MAX_PERMIL is set an ORDER
326 // OF MAGNITUDE looser than that physiological asymmetry, so this refuses only GROSS
327 // contamination and never a real body: at 250 permil it admits paladin (4 permil) and refuses
328 // both donors above. It is a separation bound, and the observed separation is published here
329 // rather than left implicit.
330 var bal_hi: i64 = ln
331 var bal_lo: i64 = rn
332 if rn > ln { bal_hi = rn; bal_lo = ln }
333 let imb_permil: i64 = (bal_hi - bal_lo)*1000/bal_hi
334 nd_out(" L/R balance: left=" as *u8); nd_num(ln); nd_out(" right=" as *u8); nd_num(rn)
335 nd_out(" imbalance_permil=" as *u8); nd_num(imb_permil)
336 nd_out(" bound=" as *u8); nd_num(ND_BAL_MAX_PERMIL); nd_out("\n" as *u8)
337 if imb_permil > ND_BAL_MAX_PERMIL {
338 nd_werr("DYNA-RED bust band is NOT bilaterally symmetric -- the anchor found costume, not tissue. Refusing to bind soft-tissue physics to a garment; strip or exclude non-body geometry, or declare the region by hand.\n" as *u8)
339 sys_exit(ND_EXIT_NOPAIR)
340 return ND_EXIT_NOPAIR
341 }
342 let lax: i64 = lx/ln
343 let lay: i64 = ly/ln
344 let laz: i64 = lz/ln
345 let rax: i64 = rx/rn
346 let ray: i64 = ry/rn
347 let raz: i64 = rz/rn
348 // influence radius = half the gap between the two anchors, so the L and R fields meet at the
349 // midline and neither reaches across it. Derived from the body, not chosen.
350 // gap measured along the DERIVED lateral axis, not along x -- on a Y-up donor those differ.
351 var gap: i64 = rlat/rn - llat/ln
352 if gap < 0 { gap = 0 - gap }
353 let infl: i64 = gap/2
354 // travel clamp from the oracle: 3.2 cm measured, and the asset's stature is `span` units for a
355 // ~1.71 m figure, so the clamp scales with the body instead of being a fixed constant.
356 let maxd: i64 = span*32/ND_MAGIC_1710
357 // ---- R7 regions: belly (anterior mid), glutes (posterior mid), thigh pair ----
358 let rb: *i64 = sys_mmap(16*8) as *i64
359 let rlo1: i64 = ND_BELLY_PERMIL - ND_RBAND_PERMIL
360 let rhi1: i64 = ND_BELLY_PERMIL + ND_RBAND_PERMIL
361 let have_belly: i64 = nd_band_mid(b, vo, nv, aa, sa, zmin, span, rlo1, rhi1, asign, rb)
362 let rg: *i64 = sys_mmap(16*8) as *i64
363 let rlo2: i64 = ND_GLUTE_PERMIL - ND_RBAND_PERMIL
364 let rhi2: i64 = ND_GLUTE_PERMIL + ND_RBAND_PERMIL
365 let have_glute: i64 = nd_band_mid(b, vo, nv, aa, sa, zmin, span, rlo2, rhi2, 0 - asign, rg)
366 let rt: *i64 = sys_mmap(16*8) as *i64
367 let rlo3: i64 = ND_THIGH_PERMIL - ND_RBAND_PERMIL
368 let rhi3: i64 = ND_THIGH_PERMIL + ND_RBAND_PERMIL
369 let have_thigh: i64 = nd_band_pair(b, vo, nv, la, aa, sa, zmin, span, rlo3, rhi3, asign, rt)
370 var nbones: i64 = 2
371 if have_belly == 1 { nbones = nbones + 1 }
372 if have_glute == 1 { nbones = nbones + 1 }
373 if have_thigh == 1 { nbones = nbones + 2 }
374 nd_out(" R7 regions: belly=" as *u8); nd_num(have_belly)
375 nd_out(" glutes=" as *u8); nd_num(have_glute)
376 nd_out(" thighs=" as *u8); nd_num(have_thigh)
377 nd_out(" -> bones=" as *u8); nd_num(nbones); nd_out("\n" as *u8)
378
379 // ---- build the new file ----
380 var nsec: i64 = ns
381 if dyi < 0 { nsec = ns + 1 }
382 let dwords: i64 = 2 + nbones*ND_STRIDE
383 let toclen: i64 = ND_HDR + nsec*ND_TOCE
384 var total: i64 = toclen
385 // measure: every kept payload, then ours
386 var s2: i64 = 0
387 while s2 < ns {
388 if s2 != dyi {
389 let e2: i64 = ND_HDR + s2*ND_TOCE
390 total = total + nd_rd64(b, e2 + 16)*8
391 }
392 s2 = s2 + 1
393 }
394 total = total + dwords*8
395 let nb: *u8 = sys_mmap(total + ND_MAGIC_4096)
396 if (nb as i64) == 0 { nd_werr("DYNA-RED cannot allocate output\n" as *u8); sys_exit(ND_EXIT_IO); return ND_EXIT_IO }
397 nd_wr64(nb, 0, nxa_magic())
398 nd_wr64(nb, 8, NXA_VER)
399 nd_wr64(nb, 16, nsec)
400 // copy kept sections, laying payloads out contiguously after the (grown) TOC
401 var wo: i64 = toclen
402 var ti: i64 = 0
403 var s3: i64 = 0
404 while s3 < ns {
405 if s3 != dyi {
406 let e3: i64 = ND_HDR + s3*ND_TOCE
407 let oldoff: i64 = nd_rd64(b, e3 + 8)
408 let wl: i64 = nd_rd64(b, e3 + 16)
409 let te: i64 = ND_HDR + ti*ND_TOCE
410 nd_wr64(nb, te, nd_rd64(b, e3))
411 nd_wr64(nb, te + 8, wo)
412 nd_wr64(nb, te + 16, wl)
413 var k2: i64 = 0
414 while k2 < wl*8 { nb[wo + k2] = b[oldoff + k2]; k2 = k2 + 1 }
415 // recompute rather than copy the old checksum: a copied checksum would still verify if
416 // the copy loop were wrong, so it would validate its own bug.
417 let pw: *i64 = ((nb as i64) + wo) as *i64
418 nd_wr64(nb, te + 24, nxa_check2(1, pw, wl))
419 wo = wo + wl*8
420 ti = ti + 1
421 }
422 s3 = s3 + 1
423 }
424 // ---- the DYNA payload ----
425 let dstart: i64 = wo
426 nd_wr64(nb, wo, nbones); wo = wo + 8
427 nd_wr64(nb, wo, ND_STRIDE); wo = wo + 8
428 wo = nd_row(nb, wo, 0 - 1, lax, lay, laz, ND_K_BASE, ND_C_BASE, maxd, infl)
429 wo = nd_row(nb, wo, 1, rax, ray, raz, ND_K_BASE, ND_C_BASE, maxd, infl)
430 // region rows: influence from each band's own vertical half-width (span*band/1000);
431 // travel clamp shared (the oracle's 3.2 cm body-scaled bound applies to the tissue class)
432 let rinfl: i64 = span*ND_RBAND_PERMIL/1000
433 if have_belly == 1 { wo = nd_row(nb, wo, 0, rb[0]/rb[3], rb[1]/rb[3], rb[2]/rb[3], ND_K_BASE, ND_C_BASE, maxd, rinfl) }
434 if have_glute == 1 { wo = nd_row(nb, wo, 0, rg[0]/rg[3], rg[1]/rg[3], rg[2]/rg[3], ND_K_BASE, ND_C_BASE, maxd, rinfl) }
435 if have_thigh == 1 {
436 wo = nd_row(nb, wo, 0 - 1, rt[0]/rt[3], rt[1]/rt[3], rt[2]/rt[3], ND_K_BASE, ND_C_BASE, maxd, rinfl)
437 wo = nd_row(nb, wo, 1, rt[4]/rt[7], rt[5]/rt[7], rt[6]/rt[7], ND_K_BASE, ND_C_BASE, maxd, rinfl)
438 }
439 let dte: i64 = ND_HDR + ti*ND_TOCE
440 nd_wr64(nb, dte, nxa_tag4("DYNA" as *u8))
441 nd_wr64(nb, dte + 8, dstart)
442 nd_wr64(nb, dte + 16, dwords)
443 let dpw: *i64 = ((nb as i64) + dstart) as *i64
444 nd_wr64(nb, dte + 24, nxa_check2(1, dpw, dwords))
445 // TOC checksum LAST -- it covers the table we just finished writing
446 let tbp: *i64 = ((nb as i64) + ND_HDR) as *i64
447 nd_wr64(nb, 24, nxa_check2(1, tbp, nsec*4))
448
449 let fd: i64 = sys_openat_wr(outp, ND_MODE)
450 if fd < 0 { nd_werr("DYNA-RED cannot open output for write\n" as *u8); sys_exit(ND_EXIT_IO); return ND_EXIT_IO }
451 var w2: i64 = 0
452 while w2 < wo {
453 let kk: i64 = sys_write(fd, ((nb as i64) + w2) as *u8, wo - w2)
454 if kk <= 0 { sys_close(fd); nd_werr("DYNA-RED short write\n" as *u8); sys_exit(ND_EXIT_IO); return ND_EXIT_IO }
455 w2 = w2 + kk
456 }
457 sys_close(fd)
458
459 nd_out("DYNA-GREEN sections " as *u8); nd_num(ns); nd_out(" -> " as *u8); nd_num(nsec)
460 if dyi >= 0 { nd_out(" (replaced existing DYNA)" as *u8) }
461 nd_out("\n stature span=" as *u8); nd_num(span)
462 nd_out(" bust band permil " as *u8); nd_num(plo); nd_out(".." as *u8); nd_num(phi)
463 nd_out("\n LEFT anchor from " as *u8); nd_num(ln); nd_out(" verts: x=" as *u8); nd_num(lax)
464 nd_out(" y=" as *u8); nd_num(lay); nd_out(" z=" as *u8); nd_num(laz)
465 nd_out("\n RIGHT anchor from " as *u8); nd_num(rn); nd_out(" verts: x=" as *u8); nd_num(rax)
466 nd_out(" y=" as *u8); nd_num(ray); nd_out(" z=" as *u8); nd_num(raz)
467 nd_out("\n lateral gap=" as *u8); nd_num(gap); nd_out(" influence=" as *u8); nd_num(infl)
468 nd_out(" travel clamp=" as *u8); nd_num(maxd)
469 nd_out("\n bytes=" as *u8); nd_num(wo)
470 nd_out("\n <- the pair is LATERAL and MEASURED: soft tissue now has a bone that is not the spine.\n" as *u8)
471 sys_exit(0)
472 return 0
473}