nx_softtissue.nx
buildroot/runtime/nx_softtissue.nx
about
nx_softtissue.nx -- SOVEREIGN soft-tissue solver: XPBD over a tetrahedral physics cage
with per-layer compliance. THE tier our body physics did not have.
WHY THIS EXISTS (measured, 2026-07-30): the rigmesh viewer drives breasts with a 2-mass
EMA spring (a damped oscillator with a calibrated gain). An oscillator has no VOLUME, no
LAYERS and no relationship to gravity DIRECTION -- so it physically cannot reproduce the
one behavior a human notices instantly: when a woman lies down, soft heavy tissue FLATTENS
and spreads toward the chest wall, and firm tissue does so much less. Our own ecosystem
already MEASURES that behavior -- nx_breast_drape_pose.nx bands LAYING_BACK at
aspect 716..921 / centroid 410..614 against STANDING_FACING 1024..1331 / 614..768 -- so a
ruler existed for a simulator that did not. This organ is the simulator; that validator is
its oracle (see nx_softtissue_gate.nx). Same solver serves buttocks, thigh and abdomen:
the tissue differs only by PROFILE ROWS, never by code (rule 11, rule 25).
METHOD (XPBD, Macklin et al. -- position-based, so it cannot blow up like a force solver):
per substep: predict -> solve constraints N times -> derive velocity -> damp
constraints: distance (edges) | volume (Neo-Hookean incompressibility, C = 6V - 6V0)
| contact (sphere/capsule = a finger or a surgical instrument)
| attachment (chest-wall particles pinned, inverse mass 0)
compliance: alpha_tilde = alpha / dt^2, per LAYER, from data rows. alpha 0 = rigid
(muscle, Cooper's ligament), alpha large = soft (adipose).
WHY IT CANNOT INVERT: the volume constraint's correction magnitude scales as 1/(6V) as the
element collapses, so resistance rises without bound as det F -> 0. Tissue gets HARDER the
harder it is pressed -- the same qualitative guarantee the Neo-Hookean strain-energy
barrier gives, which is why a deep press dimples instead of exploding. Proven by tooth, not
asserted: the gate presses a capsule 60% into the cage and asserts every 6V stays > 0.
UNITS: positions in cmm (0.01 mm) -- the NXA VERT unit, so cage and mesh share a frame.
H=171530 cmm = 1.7153 m. Scalars q12 (4096=1.0), compliance q20 (1048576=1.0).
Velocity cmm/s. dt in microseconds. Frame: +x lateral, +y cranial, +z anterior.
Gravity is a UNIT VECTOR in that body frame -- standing (0,-1,0), supine (0,0,-1).
That single input is what makes lying-down flattening emergent, not scripted.
QUANTIZATION (honest): positions are integer cmm, so one substep's displacement carries
~1 cmm of rounding. st_div_r rounds to NEAREST (not toward zero) so the error does
not bias into fake damping -- a truncating divide would silently drain energy and
read as viscosity we did not model.
license_tier: ORIGINAL No hw writes (Rule 26).
dependencies 3 imports · 14 importers
diagram shows first 10 each side; +0 more imports, +4 more importers in the complete lists below.
imports: nx_syscalls.nxnx_proportion_guard.nxnx_vecmath.nx
imported by: nx_contactforce_gate.nxnx_contactfriction_gate.nxnx_dyna_bind_skin.nxnx_engorge_gate.nxnx_gm_cloth_lib.nxnx_gm_cloth_lib_t138.nxnx_gm_xpbd_drape_t138.nxnx_gm_xpbd_gate.nxnx_proportion_guard_gate.nxnx_selfcontact_gate.nxnx_softtissue_diss_gate.nxnx_softtissue_gate.nxnx_softtissue_rot_gate.nxnx_tissuematerial_gate.nx
structs
| none |
consts
| 42 | const ST_MAGIC_1229: i64 = 1229 |
| 43 | const ST_MAGIC_12288: i64 = 12288 |
| 44 | const ST_MAGIC_20972: i64 = 20972 |
| 45 | const ST_MAGIC_1434: i64 = 1434 |
| 46 | const ST_MAGIC_2654435761: i64 = 2654435761 |
| 47 | const ST_MAGIC_1013904223: i64 = 1013904223 |
| 48 | const ST_MAGIC_1442695040888963407: i64 = 1442695040888963407 |
| 49 | const ST_MAGIC_2246822519: i64 = 2246822519 |
| 50 | const ST_MAGIC_288230376151711743: i64 = 288230376151711743 |
| 51 | const ST_MAGIC_1050: i64 = 1050 |
| 52 | const ST_MAGIC_9216: i64 = 9216 |
| 53 | const ST_MAGIC_1024: i64 = 1024 |
| 54 | const ST_MAGIC_4096: i64 = 4096 |
| 55 | const ST_MAGIC_22528: i64 = 22528 |
| 56 | const ST_MAGIC_2048: i64 = 2048 |
| 57 | const ST_MAGIC_8192: i64 = 8192 |
| 58 | const ST_MAGIC_24576: i64 = 24576 |
| 59 | const ST_MAGIC_20480: i64 = 20480 |
| 60 | const ST_MAGIC_1000000: i64 = 1000000 |
| 61 | const ST_MAGIC_999999999: i64 = 999999999 |
| 62 | const ST_MAGIC_999999999999999: i64 = 999999999999999 |
| 65 | const ST_MAXP: i64 = 8192 |
| 66 | const ST_MAXT: i64 = 16384 |
| 67 | const ST_MAXE: i64 = 49152 |
| 70 | const ST_LAY_MUSCLE: i64 = 0 |
| 71 | const ST_LAY_GLAND: i64 = 1 |
| 72 | const ST_LAY_ADIPOSE: i64 = 2 |
| 73 | const ST_LAY_SKIN: i64 = 3 |
| 74 | const ST_LAY_LIGAMENT: i64 = 4 |
| 75 | const ST_LAY_N: i64 = 5 |
| 78 | const ST_W_NP: i64 = 0 |
| 79 | const ST_W_NT: i64 = 1 |
| 80 | const ST_W_NE: i64 = 2 |
| 81 | const ST_W_PX: i64 = 3 |
| 82 | const ST_W_PY: i64 = 4 |
| 83 | const ST_W_PZ: i64 = 5 |
| 84 | const ST_W_QX: i64 = 6 |
| 85 | const ST_W_QY: i64 = 7 |
| 86 | const ST_W_QZ: i64 = 8 |
| 87 | const ST_W_VX: i64 = 9 |
| 88 | const ST_W_VY: i64 = 10 |
| 89 | const ST_W_VZ: i64 = 11 |
| 90 | const ST_W_IW: i64 = 12 |
| 91 | const ST_W_LAY: i64 = 13 |
| 92 | const ST_W_TET: i64 = 14 |
| 93 | const ST_W_TRV: i64 = 15 |
| 94 | const ST_W_TLA: i64 = 16 |
| 95 | const ST_W_TLM: i64 = 17 |
| 96 | const ST_W_EDG: i64 = 18 |
| 97 | const ST_W_ERL: i64 = 19 |
| 98 | const ST_W_ELA: i64 = 20 |
| 99 | const ST_W_ELM: i64 = 21 |
| 100 | const ST_W_SRF: i64 = 22 |
| 101 | const ST_W_TON: i64 = 23 |
| 102 | const ST_W_TCX: i64 = 24 |
| 103 | const ST_W_TCY: i64 = 25 |
| 104 | const ST_W_TCZ: i64 = 26 |
| 105 | const ST_W_TR: i64 = 27 |
| 106 | const ST_W_PROF:i64 = 28 |
| 117 | const ST_W_CDEP: i64 = 29 // sum of push-out distance over the substep, cmm |
| 118 | const ST_W_CN: i64 = 30 // particles the probe engaged on the last iteration |
| 129 | const ST_W_MU: i64 = 31 |
| 130 | const ST_MU_UNSET: i64 = 0 - 1 |
| 136 | const ST_W_TDX: i64 = 32 |
| 137 | const ST_W_TDY: i64 = 33 |
| 138 | const ST_W_TDZ: i64 = 34 |
| 143 | const ST_W_SCN: i64 = 35 |
| 144 | const ST_W_SCT: i64 = 36 |
| 150 | const ST_W_SCP: i64 = 37 |
| 155 | const ST_W_ERL0: i64 = 38 |
| 156 | const ST_W_TRV0: i64 = 39 |
| 157 | const ST_W_SLOTS: i64 = 44 |
| 160 | const ST_PF_A_MM: i64 = 0 |
| 161 | const ST_PF_B_MM: i64 = 1 |
| 162 | const ST_PF_C_MM: i64 = 2 |
| 163 | const ST_PF_GLAND_PCT: i64 = 3 |
| 164 | const ST_PF_LIG_ALPHA: i64 = 4 |
| 165 | const ST_PF_FAT_ALPHA: i64 = 5 |
| 166 | const ST_PF_FAT_DAMP: i64 = 6 |
| 167 | const ST_PF_RIB_R_MM: i64 = 7 |
| 176 | const ST_PF_SKIN_ALPHA: i64 = 8 |
| 177 | const ST_PF_N: i64 = 9 |
| 179 | const ST_PROF_LARGE_SOFT: i64 = 0 |
| 180 | const ST_PROF_SMALL_FIRM: i64 = 1 |
| 181 | const ST_PROF_GLUTE: i64 = 2 |
| 184 | const ST_PROF_SOFT_TAUT: i64 = 3 |
| 191 | const ST_PROF_SEED_BASE: i64 = 1000 |
| 193 | const ST_Q12: i64 = 4096 |
| 194 | const ST_Q20: i64 = 1048576 |
| 195 | const ST_GRAV_CMM: i64 = 981000 |
| 201 | const ST_GRAD_SHIFT: i64 = 1 |
| 205 | const ST_DT_REF_US: i64 = 4167 |
| 249 | const ST_GA_RATIO_PERMIL: i64 = 2106 // median of the four cited within-study MRE ratios |
| 250 | const ST_ALPHA_ADIPOSE: i64 = 12288 // THE ANCHOR: unchanged, for the measured reason above |
| 1100 | const ST_M_ASPECT: i64 = 0 |
| 1101 | const ST_M_CENTROID: i64 = 1 |
| 1102 | const ST_M_MINVOL: i64 = 2 |
| 1103 | const ST_M_SUMVOL: i64 = 3 |
| 1104 | const ST_M_KIN: i64 = 4 |
| 1105 | const ST_M_NSURF: i64 = 5 |
| 1106 | const ST_M_PROJ: i64 = 6 |
| 1107 | const ST_M_N: i64 = 8 |
functions
| 208 | func st_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } |
| 209 | func st_min(a: i64, b: i64) -> i64 { if a < b { return a } return b } |
| 210 | func st_max(a: i64, b: i64) -> i64 { if a > b { return a } return b } |
| 214 | func st_div_r(a: i64, b: i64) -> i64 |
| 220 | func st_isqrt(n: i64) -> i64 { return vm_isqrt(n) } |
| 252 | func st_alpha_gland() -> i64 { return ST_ALPHA_ADIPOSE * 1000 / ST_GA_RATIO_PERMIL } called by 1: st_layer_alpha_dist |
| 254 | func st_layer_alpha_dist(l: i64) -> i64 |
| 273 | func st_layer_alpha_vol(l: i64) -> i64 called by 1: st_new |
| 283 | func st_layer_damp(l: i64) -> i64 called by 1: st_eff_damp |
| 301 | func st_hash(seed: i64, salt: i64) -> i64 |
| 311 | func st_span(seed: i64, salt: i64, lo: i64, hi: i64) -> i64 |
| 324 | func st_profile_seeded(seed: i64, field: i64) -> i64 |
| 350 | func st_profile(prof: i64, field: i64) -> i64 called by 7: dbs_derive_cellmainst_profilest_eff_alpha_distst_eff_dampst_new+1 calls 2: st_profile_seededst_profile |
| 397 | func st_wall_drop(xm: i64, rib_r: i64) -> i64 |
| 406 | func st_eff_alpha_dist(W: *i64, layer: i64) -> i64 |
| 413 | func st_eff_damp(W: *i64, layer: i64) -> i64 |
| 426 | func st_alpha_tilde(alpha_q12: i64, dsum: i64, dt_us: i64) -> i64 |
| 438 | func st_inside(xm: i64, ym: i64, zm: i64, a: i64, b: i64, c: i64) -> i64 called by 1: st_new |
| 446 | func st_six_vol(W: *i64, i0: i64, i1: i64, i2: i64, i3: i64) -> i64 |
| 463 | func st_six_vol_q(W: *i64, i0: i64, i1: i64, i2: i64, i3: i64) -> i64 calls 1: st_div_r |
| 479 | func st_add_edge(W: *i64, htab: *i64, hidx: *i64, hmask: i64, |
| 526 | func st_new(prof: i64, h_mm: i64) -> *i64 |
| 791 | func st_reset_state(W: *i64) -> i64 |
| 800 | func st_set_touch(W: *i64, on: i64, cx: i64, cy: i64, cz: i64, r: i64) -> i64 |
| 819 | func st_substep(W: *i64, gx: i64, gy: i64, gz: i64, dt_us: i64, iters: i64) -> i64 called by 3: dbs_ringdown_tdbs_evalst_run calls 5: st_div_rst_isqrtst_alpha_tildest_eff_alpha_distst_eff_damp |
| 1093 | func st_run(W: *i64, gx: i64, gy: i64, gz: i64, steps: i64, dt_us: i64, iters: i64) -> i64 |
| 1153 | func st_surface_spacing(W: *i64) -> i64 |
| 1197 | func st_sc_bounds(W: *i64, pad: i64, out: *i64) -> i64 |
| 1241 | func st_self_contact(WA: *i64, WB: *i64, dist: i64, iters: i64, dt_us: i64) -> i64 |
| 1387 | func st_sc_hits(W: *i64) -> i64 { return W[ST_W_SCN] } called by 1: main |
| 1390 | func st_sc_peak(W: *i64) -> i64 { return W[ST_W_SCP] } called by 1: main |
| 1392 | func st_sc_tested(W: *i64) -> i64 { return W[ST_W_SCT] } called by 1: main |
| 1397 | func st_sc_min_sep(WA: *i64, WB: *i64) -> i64 |
| 1449 | func st_inflate_rest_volume(W: *i64, layer: i64, linear_permil: i64) -> i64 |
| 1498 | func st_inflated(W: *i64) -> i64 |
| 1508 | func st_set_friction(W: *i64, mu256: i64) -> i64 { W[ST_W_MU] = mu256; return 0 } |
| 1509 | func st_friction(W: *i64) -> i64 { return W[ST_W_MU] } called by 1: main |
| 1511 | func st_contact_depth(W: *i64) -> i64 { return W[ST_W_CDEP] } called by 1: cf_press |
| 1514 | func st_contact_n(W: *i64) -> i64 { return W[ST_W_CN] } |
| 1516 | func st_contact_impulse(W: *i64, dt_us: i64) -> i64 |
| 1526 | func st_contact_force(W: *i64, dt_us: i64) -> i64 |
| 1531 | func st_measure(W: *i64, out: *i64) -> i64 |