code wiki / (root) / nx_hairdyn_lib.nx

nx_hairdyn_lib.nx

buildroot/runtime/nx_hairdyn_lib.nx

27947 B556 linesdepth 4pulls 7 transitivereach 202 importersview sourcekind librarytopic hairdyn
docsdependenciesstructsconstsfunctions

about

nx_hairdyn_lib.nx -- LIB: HAIR AS A DRIVEN CHAIN ON THE ONE SOLVER, WITH ITS PLANT MEASURED IN THE UNITS OF ITS OWN CITED BAND (2026-08-25). WHY THIS EXISTS. knowledge/gamefeel_oracle.conf has carried, since the oracle table was banked: hair_k_q10 120 420 per1024 secondary-motion-hair-2to5hz and NOTHING WAS EVER DERIVED FROM IT. The shipping hair plant is two hand-picked integers in nx_softbind (SB_K_HAIR=200, SB_C_HAIR=60) that sit inside that band by inspection, driving a SINGLE point -- so hair on /world/beach is one lagging joint offset underneath a painted skull band, and the cited row is a BAR WITH NO PLANT BEHIND IT. This lib is the plant, and its own gate measures it rather than asserting it. THE CHAIN. Hair is a strand, not a point. The game-convention lineage for this class is the SPRING-BONE CHAIN -- a bone pulled toward its rest pose RELATIVE TO ITS PARENT under a spring and a damper, with the parent's motion propagating down the chain (artellblender/springbones, https://github.com/artellblender/springbones, position and rotation over bone chains). hd_tick is exactly that, and it is why a strand reads as hair instead of as a wobbling head. That is a CONVENTION citation, not a physiological one, and it is labelled as such: it licenses the SHAPE of the model, never any number in it. ONE SOLVER, TWO CONSUMERS. Every segment is stepped by nx_softdyn's sd_step_axq -- the SAME integrator the soft-tissue lane reaches through sd_step_ax. A second spring integrator for hair would be the duplicate-ruler defect the estate has already paid for twice. K comes from sb_k_sym (nx_softbind's frequency->stiffness ruler) and C from sb_c_for_zeta (its damping ruler), so a hair constant CANNOT drift from a chest constant: both are the same two functions evaluated at different arguments. license_tier: ORIGINAL No hw writes (Rule 26).

dependencies 5 imports · 68 importers

nx_syscalls.nx nx_softdyn.nx nx_softbind.nx nx_ringdown_lib.nx nx_vecmath.nx nx_hairdyn_lib.nx nx_game_page_affine_candidate_t361 nx_game_page_authored_material_t54 nx_game_page_body_t138.nx nx_game_page_canonical_t135.nx nx_game_page_catwalk_lighting_cand nx_game_page_emit.nx nx_game_page_emit_sand_review.nx nx_game_page_eye_t138.nx nx_game_page_gait_candidate_t139.n nx_game_page_garment_t132.nx

diagram shows first 10 each side; +0 more imports, +58 more importers in the complete lists below.

imports: nx_syscalls.nxnx_softdyn.nxnx_softbind.nxnx_ringdown_lib.nxnx_vecmath.nx

imported by: nx_game_page_affine_candidate_t361.nxnx_game_page_authored_material_t54.nxnx_game_page_body_t138.nxnx_game_page_canonical_t135.nxnx_game_page_catwalk_lighting_candidate_t316.nxnx_game_page_emit.nxnx_game_page_emit_sand_review.nxnx_game_page_eye_t138.nxnx_game_page_gait_candidate_t139.nxnx_game_page_garment_t132.nxnx_game_page_gpu_timing_current_candidate.nxnx_game_page_gpu_timing_pool_candidate.nxnx_game_page_hair_attachment_candidate_t263.nxnx_game_page_hair_attachment_t283.nxnx_game_page_hair_guides_candidate_t263.nxnx_game_page_hair_t138.nxnx_game_page_host_candidate_t341.nxnx_game_page_identity_candidate.nxnx_game_page_imported_body_t45.nxnx_game_page_imported_t262.nxnx_game_page_inspection_distance_t235.nxnx_game_page_inspection_t142.nxnx_game_page_integrated_t73.nxnx_game_page_knight_bind_t25.nxnx_game_page_knight_feedback_t28.nxnx_game_page_knight_feedback_t29.nxnx_game_page_knight_support_t31.nxnx_game_page_knight_support_t31_before_t44.nxnx_game_page_length_diag_t140.nxnx_game_page_lit_candidate_t181.nxnx_game_page_material_body_t61.nxnx_game_page_material_candidate_t181.nxnx_game_page_material_compose_t55.nxnx_game_page_material_schema_candidate_t181.nxnx_game_page_metadata_candidate_t220.nxnx_game_page_mip_candidate_t241.nxnx_game_page_motion_candidate_t328.nxnx_game_page_motion_inventory_candidate_t330.nxnx_game_page_move_t72.nxnx_game_page_owned_candidate_t181.nxnx_game_page_pipeline_t64.nxnx_game_page_pre_inspection_t142.nxnx_game_page_pre_toon_region_t143.nxnx_game_page_procedural_review_candidate_t333.nxnx_game_page_recovery_feedback_t28.nxnx_game_page_recovery_t23.nxnx_game_page_rig_comparison_t332.nxnx_game_page_rig_inspection_t230.nxnx_game_page_rig_observation_candidate_t331.nxnx_game_page_scene_light_candidate_t364.nxnx_game_page_search_t53.nxnx_game_page_shared_candidate_t340.nxnx_game_page_skin_t138.nxnx_game_page_static_candidate_t338.nxnx_game_page_toon_region_candidate_t143.nxnx_game_page_visitors_inspection_t142.nxnx_game_page_visitors_t140.nxnx_game_page_workbench_candidate_t342.nxnx_game_page_workspace_body_t49.nxnx_game_page_workspace_body_t59.nxnx_game_page_workspace_t48.nxnx_hairbake_probe.nxnx_haircollide_gate.nxnx_hairdyn_gate.nxnx_hairdyn_pose_batch_gate_t284.nxnx_hairdyn_pose_gate_t263.nxnx_movement_base_page_t139.nxnx_strand_groom_gate.nx

structs

none

consts

53const HD_FN_LO_MHZ: i64 = 2000
54const HD_FN_HI_MHZ: i64 = 5000
77const GR_MAXS: i64 = 512 // strands in the table
78const GR_MAXG: i64 = 64 // guides
79const GR_MAXSEG: i64 = 32
80const GR_KNN: i64 = 3 // guides a child interpolates
81const GR_WQ: i64 = 1024 // weight scale
82const GR_SWAY_PERMIL: i64 = 400 // a guide's sway at the tip, permil of seglen
83const GR_HANG_PERMIL: i64 = 980 // vertical run per segment, permil of seglen (the rest is sway)
84const GR_JIT_CAP_PERMIL: i64 = 300 // the most jitter a caller may ask for, permil of seglen at the tip
85const GR_LCG_A: i64 = 48271
86const GR_LCG_M: i64 = 2147483647
87const GR_DIR_Q: i64 = 1024
88const GR_DIR_MIN2: i64 = 262144 // |p|^2 >= (0.5 Q)^2: rejection floor so a normalised direction is well conditioned
89const GR_DIR_MAX2: i64 = 1048576 // |p|^2 <= Q^2
90const GR_ERRFD: i64 = 2
91const GR_E_ARG: i64 = 3
447const HD_RD_SAMPLES: i64 = 768
448const HD_RD_SETTLE: i64 = 240
454const HD_QUANT_Q8: i64 = 1

functions

63func hd_ticks_per_s() -> i64 { return sd_isqrt(SB_MHZ_TICK_SQ)/RD_MHZ_PER_HZ }
called by 4: mainhg_res_mhzmainhd_dt_us calls 1: sd_isqrt
64func hd_dt_us() -> i64 { return RD_US_PER_S/hd_ticks_per_s() }
100func gr_err(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(GR_ERRFD, s, n); return 0 }
called by 1: gr_strand calls 1: sys_write
101func gr_rand() -> i64
called by 2: gr_rangegr_strand
106func gr_range(lo: i64, hi: i64) -> i64 { return lo + gr_rand() % (hi - lo + 1) }
called by 2: gr_scalp_rootgr_shape_guide calls 1: gr_rand
107func gr_alloc() -> i64
called by 1: gr_strand calls 1: sys_mmap
115func gr_count() -> i64 { return GR_N }
116func gr_guides() -> i64 { return GR_NG }
117func gr_nseg() -> i64 { return GR_NSEG }
118func gr_level(k: i64) -> i64 { let l: *i64 = GR_LVL as *i64; return l[k] }
called by 1: main
119func gr_rootx(k: i64) -> i64 { let r: *i64 = GR_ROOT as *i64; return r[k*3] }
120func gr_rooty(k: i64) -> i64 { let r: *i64 = GR_ROOT as *i64; return r[k*3+1] }
121func gr_rootz(k: i64) -> i64 { let r: *i64 = GR_ROOT as *i64; return r[k*3+2] }
122func gr_x(k: i64, j: i64) -> i64 { let b: *i64 = GR_BUF as *i64; return b[(k*GR_MAXSEG + j)*3] }
123func gr_y(k: i64, j: i64) -> i64 { let b: *i64 = GR_BUF as *i64; return b[(k*GR_MAXSEG + j)*3 + 1] }
124func gr_z(k: i64, j: i64) -> i64 { let b: *i64 = GR_BUF as *i64; return b[(k*GR_MAXSEG + j)*3 + 2] }
125func gr_set(k: i64, j: i64, x: i64, y: i64, z: i64) -> i64
131func gr_scalp_root(cx: i64, cy: i64, cz: i64, r: i64, out: *i64) -> i64
called by 1: gr_strand calls 2: gr_rangesd_isqrt
154func gr_shape_guide(k: i64, nseg: i64, seglen: i64) -> i64
169func gr_child_at(rx: i64, ry: i64, rz: i64, jitter_permil: i64, jseed: i64, out: *i64) -> i64
238func gr_strand(seed: i64, cx: i64, cy: i64, cz: i64, scalp_r: i64, nguides: i64, nchild: i64, nseg: i64, seglen: i64, jitter_permil: i64) -> i64
282func gr_to_chain(st: *i64, k: i64) -> i64
called by 1: main calls 3: gr_xgr_ygr_z
297func gr_seglen_at(k: i64, j: i64) -> i64
309func hd_k(fn_mhz: i64) -> i64 { return sb_k_sym(fn_mhz) }
called by 1: hd_ringdown_t calls 1: sb_k_sym
310func hd_c(K: i64, zeta_permil: i64) -> i64 { return sb_c_for_zeta(K, zeta_permil) }
called by 1: hd_ringdown_t calls 1: sb_c_for_zeta
314func hd_alloc(nseg: i64) -> *i64 { return sd_alloc(nseg) }
315func hd_x_q8(st: *i64, i: i64) -> i64 { return st[i*SD_STRIDE] }
316func hd_y_q8(st: *i64, i: i64) -> i64 { return st[i*SD_STRIDE + 1] }
317func hd_tip_x_q8(st: *i64, nseg: i64) -> i64 { return st[(nseg - 1)*SD_STRIDE] }
320func hd_seat(st: *i64, nseg: i64, rx: i64, ry: i64, rz: i64, seglen: i64) -> i64
340func hd_tick(st: *i64, nseg: i64, rx: i64, ry: i64, rz: i64, seglen: i64,
386func hd_tick_collide(st: *i64, nseg: i64, rx: i64, ry: i64, rz: i64, seglen: i64,
called by 1: main calls 2: sd_step_axqvm_capsule_push
425func hd_tick_rigid(st: *i64, nseg: i64, rx: i64, ry: i64, rz: i64, seglen: i64) -> i64
460func hd_amp_q8() -> i64 { return SB_MAXD_HAIR*SD_Q8/2 }
467func hd_ringdown_kc_t(nseg: i64, seglen: i64, K: i64, C: i64,
513func hd_ringdown_kc(nseg: i64, seglen: i64, K: i64, C: i64, out: *i64) -> i64
called by 1: main calls 1: hd_ringdown_kc_t
519func hd_ringdown_t(nseg: i64, seglen: i64, fn_mhz: i64, zeta_permil: i64,
525func hd_ringdown(nseg: i64, seglen: i64, fn_mhz: i64, zeta_permil: i64, out: *i64) -> i64
called by 1: main calls 1: hd_ringdown_t
530func hd_ringdown_rigid(nseg: i64, seglen: i64, out: *i64) -> i64