nx_nxa_dyna.nx
buildroot/runtime/nx_nxa_dyna.nx
about
nx_nxa_dyna.nx -- WRITE THE DYNA SECTION: per-bone soft-tissue physics DECLARED IN THE ASSET,
with the lateral breast-bone pair DERIVED FROM THE MESH'S OWN GEOMETRY.
WHY THIS EXISTS. nx_asset_floor_gate measured our shipped character and found 5 midline chest
joints and ZERO lateral pair. Real breast bones come in L/R pairs OFF the midline; midline joints
at chest height ARE THE SPINE CHAIN, so driving them swings the whole torso -- the wobble the
operator kept reporting. With no bone to drive, chest motion had to be faked at the shader vertex
band, and the engine's own joint picker (|bind.x| < 9000, i.e. near-midline) was selecting spine
joints as stand-ins using a superseded height normalisation (z/171530, which ignores the mesh
origin -- the same error that once put the nipples below the legs).
THE FIX IS A FORMAT CAPABILITY, NOT ANOTHER SHADER PATCH. FBX and its class carry no physics at
all: a rig travels as geometry plus a joint tree, and every consumer re-invents the dynamics in
engine code. DYNA makes the ASSET carry its own soft-tissue contract -- anchor, axis, stiffness,
damping, travel clamp, influence radius, falloff -- so the engine DRIVES DECLARED DATA instead of
hardcoding bands. That is the difference between a mannequin and a body.
ANCHORS ARE MEASURED, NEVER GUESSED. For each side we take the CENTROID of the front-hemisphere
vertices inside the bust band. Anatomy therefore comes from the mesh in front of us, so a
different body type yields different anchors -- and the firm/bouncy RANGE the operator asked for
falls out of geometry rather than a table of magic numbers.
Physics constants are NOT invented here: they are the banked, in-band values already proven by
the chest-motion oracle (2.5 Hz, 3.2 cm travel, 0.0 cm torso, 0.89 coherence) and by nx_softbind
(chest K base 60 step 25, C base 100 step 16). Moving them out of code and into the asset is the
point -- rule 11, and the recombinator wants them as data.
nx_nxa_dyna <in.nxa> <out.nxa>
ADDITIVE AND IDEMPOTENT: the input is never modified; an existing DYNA is REPLACED, not doubled,
so running twice is identical to running once.
DYNA payload (i64 words): [0]=n_bones [1]=stride(12), then per bone:
0 side(-1 L,+1 R,0 mid) 1 anchor_x 2 anchor_y 3 anchor_z
4 k_q8 5 c_q8 6 max_disp 7 influence_radius 8 falloff_q8
9 axis_mask(1=x,2=y,4=z) 10 k_step_q8 11 c_step_q8
license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
dependencies 2 imports · 0 importers
imports: nx_syscalls.nxnx_nxa.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 40 | const ND_MAGIC_1710: i64 = 1710 |
| 41 | const ND_MAGIC_4096: i64 = 4096 |
| 43 | const ND_HDR: i64 = 32 |
| 44 | const ND_TOCE: i64 = 32 |
| 45 | const ND_STRIDE: i64 = 12 |
| 46 | const ND_MAXSEC: i64 = 64 |
| 47 | const ND_MODE: i64 = 0x1a4 |
| 48 | const ND_ERRFD: i64 = 2 |
| 49 | const ND_EXIT_USAGE: i64 = 2 |
| 50 | const ND_EXIT_BAD: i64 = 3 |
| 51 | const ND_EXIT_IO: i64 = 1 |
| 52 | const ND_EXIT_NOPAIR: i64 = 4 |
| 56 | const ND_NIP_PERMIL: i64 = 744 |
| 57 | const ND_BAND_PERMIL: i64 = 40 |
| 59 | const ND_K_BASE: i64 = 60 |
| 60 | const ND_K_STEP: i64 = 25 |
| 61 | const ND_C_BASE: i64 = 100 |
| 62 | const ND_C_STEP: i64 = 16 |
| 63 | const ND_FALLOFF_Q8: i64 = 256 |
| 64 | const ND_AXIS_ALL: i64 = 7 |
functions
| 66 | func nd_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 67 | func nd_werr(s: *u8) -> i64 { sys_write(ND_ERRFD, s, nd_slen(s)); return 0 } |
| 68 | func nd_out(s: *u8) -> i64 { sys_write(1, s, nd_slen(s)); return 0 } |
| 69 | func nd_num(v: i64) -> i64 |
| 82 | func nd_rd64(b: *u8, off: i64) -> i64 called by 1: main |
| 88 | func nd_wr64(b: *u8, off: i64, v: i64) -> i64 called by 1: main |
| 94 | func nd_tageq(b: *u8, off: i64, t: *u8) -> i64 called by 1: main |
| 100 | func main(argc: i64, argv: *i64) -> i64 |