nx_dyna_bind_skin.nx
buildroot/runtime/nx_dyna_bind_skin.nx
about
nx_dyna_bind_skin.nx -- BIND THE XPBD SOFT-TISSUE CAGE TO SKIN VERTICES, AND MEASURE OUR OWN
PLANT IN THE SUBJECT'S UNITS.
WHY THIS EXISTS. `dyna_bind_skin` is OPEN on the charsim and graphics boards. DYNA already
DECLARES per-bone soft-tissue contracts in the asset (nx_nxa_dyna: anchor, axis, k, c, travel
clamp, influence radius, falloff, all derived FROM THE MESH) and nx_softtissue already SOLVES
tissue as XPBD over a tetrahedral cage with per-layer compliance. Nothing joined them: the
declared physics never reached a skin vertex, so the shipped path was still a shader band.
WHY XPBD, NOT A MASS-SPRING RIG -- the model choice, justified against the alternatives, because
the operator's bar is that the motion be best-of-breed and that previous attempts were awful:
* XPBD (Macklin, Muller, Chentanez, Kim -- "XPBD: Position-Based Simulation of Compliant
Constrained Dynamics", ACM SIGGRAPH Motion in Games 2016) integrates by POSITION PROJECTION,
so it cannot detonate the way an explicit force solver does at stiff settings. Its
compliance alpha is a genuine material parameter (inverse stiffness) and alpha_tilde =
alpha/dt^2 makes effective stiffness STEP-SIZE INDEPENDENT. That property is precisely the
cure for the buzzing/framerate-dependence failure mode, and plain PBD does not have it.
* The estate ALREADY MEASURED why the previous attempt was awful. nx_softtissue's header
records that the rigmesh viewer drove breasts with a 2-mass EMA spring, and that "an
oscillator has no VOLUME, no LAYERS and no relationship to gravity DIRECTION" -- so it
physically cannot reproduce supine flattening. A damped oscillator is a POINT; tissue is a
VOLUME. Rejected on measured evidence, not taste.
* Shape-matching lattice deformers (Muller 2005) are the other cheap production option:
unconditionally stable, but they preserve no volume and their stiffness is a blend factor
with no material meaning -- so their parameters CANNOT be sourced from tissue literature,
which this estate requires of every number. Rejected on sourcing.
* FEM production stacks (Chaos Flesh, Ziva, DMM) have better constitutive fidelity at far
higher cost and none is reachable as a sovereign integer solver. XPBD is the tier that is
both principled and ours.
WHAT "BOUND TO SKIN VERTICES" MEANS HERE. The cage is a physics proxy; the skin mesh is the
render surface. Binding is an EMBEDDING: a skin vertex inside a DYNA region takes its
displacement from the cage, weighted by that region's OWN declared falloff over its OWN declared
influence radius. A vertex outside every region is SKIN-LOCKED -- it moves with the rig and
nothing else. That split IS the capability, and it is what the gate discriminates: soft moves,
rigid does not. Both signals must be present at once or the tooth proves nothing.
FRAMES DIFFER AND THAT HAS BURNED THIS ESTATE BEFORE -- SO IT IS STATED, NOT ASSUMED.
NXA asset frame : +z is the STATURE axis, y<0 is the FRONT hemisphere (nx_nxa_dyna relies on
this and says so; the floor gate and the shader agree).
dependencies 3 imports · 0 importers
imports: nx_syscalls.nxnx_nxa.nxnx_softtissue.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
| 73 | const DBS_OUTFD: i64 = 1 |
| 74 | const DBS_ERRFD: i64 = 2 |
| 75 | const DBS_EXIT_OK: i64 = 0 |
| 76 | const DBS_EXIT_IO: i64 = 1 |
| 77 | const DBS_EXIT_USAGE: i64 = 2 |
| 78 | const DBS_EXIT_BAD: i64 = 3 |
| 80 | const DBS_Q16: i64 = 65536 |
| 81 | const DBS_Q8: i64 = 256 |
| 82 | const DBS_PERMIL: i64 = 1000 |
| 83 | const DBS_US_PER_S: i64 = 1000000 |
| 84 | const DBS_MHZ_PER_HZ: i64 = 1000 |
| 87 | const DBS_LN2_Q16: i64 = 45426 |
| 88 | const DBS_TWOPI_Q16: i64 = 411775 |
| 91 | const DBS_HDR: i64 = 32 |
| 92 | const DBS_TOCE: i64 = 32 |
| 93 | const DBS_MAXSEC: i64 = 64 |
| 95 | const DBS_DY_STRIDE: i64 = 12 |
| 96 | const DBS_DY_SIDE: i64 = 0 |
| 97 | const DBS_DY_AX: i64 = 1 |
| 98 | const DBS_DY_AY: i64 = 2 |
| 99 | const DBS_DY_AZ: i64 = 3 |
| 100 | const DBS_DY_MAXD: i64 = 6 |
| 101 | const DBS_DY_INFL: i64 = 7 |
| 102 | const DBS_DY_FALL: i64 = 8 |
| 106 | const DBS_RD_MAXSAMP: i64 = 768 |
| 107 | const DBS_RD_ITERS: i64 = 8 |
| 108 | const DBS_RD_SETTLE: i64 = 240 |
| 219 | const DBS_R_NSAMP: i64 = 0 |
| 220 | const DBS_R_EQ: i64 = 1 |
| 221 | const DBS_R_A1: i64 = 2 |
| 222 | const DBS_R_A2: i64 = 3 |
| 223 | const DBS_R_PER_US: i64 = 4 |
| 224 | const DBS_R_FN_MHZ: i64 = 5 |
| 225 | const DBS_R_ZETA: i64 = 6 |
| 226 | const DBS_R_PEAKS: i64 = 7 |
| 227 | const DBS_R_MAXABS: i64 = 8 |
| 228 | const DBS_R_RESID: i64 = 9 |
| 229 | const DBS_R_CELL: i64 = 10 |
| 230 | const DBS_R_NP: i64 = 11 |
| 231 | const DBS_R_N: i64 = 12 |
| 238 | const DBS_QUANT_CMM: i64 = 1 |
| 375 | const DBS_SVG_W: i64 = 1920 |
| 376 | const DBS_SVG_H: i64 = 1200 |
| 377 | const DBS_SVG_ROWS: i64 = 5 |
| 378 | const DBS_SVG_PLOT_L: i64 = 300 |
| 379 | const DBS_SVG_PLOT_R: i64 = 1880 |
| 380 | const DBS_SVG_TOP: i64 = 70 |
| 381 | const DBS_SVG_BOT_MARGIN: i64 = 40 |
| 382 | const DBS_SVG_LABEL_X: i64 = 40 |
| 383 | const DBS_SVG_TITLE_Y: i64 = 44 |
| 384 | const DBS_SVG_FOOT_Y: i64 = 1180 |
| 385 | const DBS_SVG_PX_TITLE: i64 = 26 |
| 386 | const DBS_SVG_PX_NAME: i64 = 19 |
| 387 | const DBS_SVG_PX_VALUE: i64 = 18 |
| 388 | const DBS_SVG_PX_SMALL: i64 = 16 |
| 389 | const DBS_SVG_TRACE_PAD: i64 = 14 |
| 390 | const DBS_SVG_DY_NAME: i64 = 18 |
| 391 | const DBS_SVG_DY_VALUE: i64 = 8 |
| 392 | const DBS_SVG_DY_PEAK: i64 = 32 |
| 393 | const DBS_SVG_DY_STATE: i64 = 54 |
| 397 | const DBS_SVG_AMP_LINEAR: i64 = 320 |
| 398 | const DBS_SVG_AMP_WALK: i64 = 3200 |
functions
| 110 | func dbs_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 111 | func dbs_out(s: *u8) -> i64 { sys_write(DBS_OUTFD, s, dbs_slen(s)); return 0 } |
| 112 | func dbs_err(s: *u8) -> i64 { sys_write(DBS_ERRFD, s, dbs_slen(s)); return 0 } |
| 113 | func dbs_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } |
| 115 | func dbs_num(v: i64) -> i64 |
| 132 | func dbs_rd64(b: *u8, off: i64) -> i64 |
| 137 | func dbs_tageq(b: *u8, off: i64, t: *u8) -> i64 |
| 142 | func dbs_isqrt(n: i64) -> i64 |
| 155 | func dbs_log2_q16(x_q16: i64) -> i64 called by 1: dbs_ln_q16 |
| 165 | func dbs_ln_q16(x_q16: i64) -> i64 { return dbs_log2_q16(x_q16) * DBS_LN2_Q16 / DBS_Q16 } |
| 170 | func dbs_zeta_permil(a1: i64, a2: i64) -> i64 |
| 186 | func dbs_free_surface_y(W: *i64) -> i64 |
| 203 | func dbs_displace_free(W: *i64, dy: i64) -> i64 called by 1: dbs_ringdown_t |
| 244 | func dbs_derive_cell(prof: i64) -> i64 |
| 268 | func dbs_ringdown_t(prof: i64, h_mm: i64, amp_cmm: i64, out: *i64, sigout: *i64) -> i64 called by 2: dbs_ringdowndbs_svg calls 8: st_newst_reset_statest_substepdbs_free_surface_ydbs_displace_freesys_mmap+2 |
| 361 | func dbs_ringdown(prof: i64, h_mm: i64, amp_cmm: i64, out: *i64) -> i64 |
| 400 | func dbs_svg() -> i64 |
| 466 | func dbs_bind_report(inp: *u8) -> i64 |
| 555 | func dbs_eval(prof: i64, drive_mhz: i64, frames: i64, dt_us: i64) -> i64 |
| 608 | func dbs_atoi(s: *u8) -> i64 |
| 623 | func main(argc: i64, argv: *i64) -> i64 |