code wiki / (root) / nx_nxa_anim_lib.nx

nx_nxa_anim_lib.nx

buildroot/runtime/nx_nxa_anim_lib.nx

19581 B427 linesdepth 2pulls 2 transitivereach 154 importersview sourcekind librarytopic nxa
docsdependenciesstructsconstsfunctions

about

nx_nxa_anim_lib.nx -- THE ONE NXA CLIP EVALUATOR: channel-2 packed world-delta keys, sampled at a clip time, blended toward an idle POSE, and written as the DUAL-QUATERNION ROWS the cast shader skins with. Integer-only and syscall-free so the wasm engine imports it; every consumer (engine, gates, native players) reads ONE implementation of the format's own runtime form. WHY IT EXISTS (GE23, measured 2026-09-06 on the served beach page): the WebGPU cast pipeline already skins on the GPU from a per-frame joint table, and the ONLY per-frame page logic left was animEval -- hand-written JavaScript sampling ANIM tracks with shortest-path nlerp, subtracting a fitted linear root drift, blending toward the frozen stand pose, then forming T = b + dt - D b and the dual half T x real / 2. That is the format's normative runtime form (knowledge/nxa_format_spec.md: M x = D (x - b) + b + dt, shortest-path lerp between keys) expressed in the page instead of the engine. This lib is that arithmetic in NishiLang, driven by the ENGINE's own stride phase rather than a page clock. THE UNIT TRAP, NAMED (spec + nx_nxa_play's audit): ANIM channel-2 dt LANES are MILLIMETRES packed /100 to fit i16, while SKEL binds and the rows are in 0.01 mm model units, so dt lanes are scaled by NA_DT_MM_TO_MODEL here. POSE entry dt words are ALREADY model units (nx_nxa_pose applies the x100 at freeze time; debt 1785380029) and are NOT scaled again. Two sections, two conventions, both cited. KEY LAYOUT (spec, channel 2, two words per key, little-endian lanes): w0 = [t_ms u16][dqx i16][dqy i16][dqz i16] w1 = [dqw i16][dtx i16][dty i16][dtz i16] ROW LAYOUT (what nxGpuCast uploads, per joint): real = q (q12), dual = (T x q)/2 with T in model units, laid out as out[0..nj*4) real rows then out[nj*4..nj*8) dual rows; the page divides both by 4096. license_tier: ORIGINAL No hw writes (Rule 26).

dependencies 1 imports · 39 importers

nx_nxa_fk.nx nx_nxa_anim_lib.nx nx_craft_before_placement_t316.nx nx_gate_rig_motion_20260909.nx nx_gate_rig_motion_context_t140.nx nx_gate_rig_motion_palette_t140.nx nx_gate_rig_motion_stream_t140.nx nx_movement_base_engine_t139.nx nx_nxa_anim_bounds_gate.nx nx_nxa_anim_gate.nx nx_nxa_anim_legacy_gate_t33.nx nx_nxa_anim_lookup_gate.nx

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

imports: nx_nxa_fk.nx

imported by: nx_craft_before_placement_t316.nxnx_gate_rig_motion_20260909.nxnx_gate_rig_motion_context_t140.nxnx_gate_rig_motion_palette_t140.nxnx_gate_rig_motion_stream_t140.nxnx_movement_base_engine_t139.nxnx_nxa_anim_bounds_gate.nxnx_nxa_anim_gate.nxnx_nxa_anim_legacy_gate_t33.nxnx_nxa_anim_lookup_gate.nxnx_nxa_rig_motion_stream.nxnx_nxa_rig_motion_stream_t140.nxnx_rigbench_motion.nxnx_rigbench_motion_t21.nxnx_wasm_craft.nxnx_wasm_craft_body_t137.nxnx_wasm_craft_contact_candidate.nxnx_wasm_craft_dunes_t70.nxnx_wasm_craft_gait_candidate.nxnx_wasm_craft_imported_body_t41.nxnx_wasm_craft_imported_body_t42.nxnx_wasm_craft_imported_body_t49.nxnx_wasm_craft_imported_t262.nxnx_wasm_craft_inspection_bounds_t181.nxnx_wasm_craft_inspection_distance_t218.nxnx_wasm_craft_inspection_t142.nxnx_wasm_craft_journal_index_t21.nxnx_wasm_craft_journal_index_t21_before_t44.nxnx_wasm_craft_lookup_candidate.nxnx_wasm_craft_move_t72.nxnx_wasm_craft_pre_inspection_t142.nxnx_wasm_craft_recovery_t20.nxnx_wasm_craft_relief_adopt_t143.nxnx_wasm_craft_relief_save_t143.nxnx_wasm_craft_relief_t143.nxnx_wasm_craft_residency_t44.nxnx_wasm_craft_visitors_canonical_t142.nxnx_wasm_craft_visitors_inspection_t142.nxnx_wasm_craft_visitors_t140.nx

structs

none

consts

26const NA_Q12: i64 = 4096
27const NA_LANE_MASK: i64 = 65535
28const NA_LANE_HALF: i64 = 32767
29const NA_LANE_FULL: i64 = 65536
30const NA_LANE_B1: i64 = 16
31const NA_LANE_B2: i64 = 32
32const NA_LANE_B3: i64 = 48
33const NA_DT_MM_TO_MODEL: i64 = 100 // spec: ANIM dt lanes are mm packed /100; binds are 0.01 mm
34const NA_TRK_HDR_W: i64 = 3 // per track: [joint][channel][nkeys]
35const NA_CH2: i64 = 2
36const NA_CH2_KEY_W: i64 = 2
37const NA_CH0_KEY_W: i64 = 4 // legacy verbose T keys, 32 bytes -- skipped, never sampled
38const NA_CH1_KEY_W: i64 = 5 // legacy verbose R keys, 40 bytes -- skipped, never sampled
39const NA_ROW_W: i64 = 8 // real x4 + dual x4 per joint
40const NA_IDX_W: i64 = 2 // per joint: key word offset (or -1), nkeys
41const NA_SKEL_W: i64 = 8 // spec: [parent][tx ty tz][qx qy qz qw]
42const NA_SKEL_TX: i64 = 1
43const NA_SKEL_TY: i64 = 2
44const NA_SKEL_TZ: i64 = 3
45const NA_POSE_ENTRY_W: i64 = 8 // spec: [joint][q x4][dt x3]
46const NA_RV_W: i64 = 6 // x0 y0 vx16 vy16 t0 span
47const NA_RV_FX: i64 = 65536 // drift velocity carried as units per ms x 65536
48const NA_ROOT_Z: i64 = 89000 // the page's root rule: pelvis-region bind nearest (0,0,.52H)
49const NA_ROOT_XW: i64 = 2 // ... weighting |x| twice, verbatim from loadNPC
50const NA_MPB_DEFAULT: i64 = 700 // the page's shipped default ms-per-block before a travel fit
51const NA_TRAVEL_MIN_PERMIL: i64 = 1500 // the page fits cadence only when the root travels > 1.5 blocks
52const NA_FIG_UNITS: i64 = 171530 // the reference figure's VERT extent, 0.01 mm units (1.7153 m)
53const NA_FIG_BLOCKS_CENTI: i64 = 178 // ... which the page maps to 1.78 world blocks
54const NA_SCR_W: i64 = 64 // scratch words na_rows needs from its caller
55const NA_RC_BAD_TRACKS: i64 = 0 - 1
56const NA_RC_OVERRUN: i64 = 0 - 2
341const NA_GAIT_FOOT_W: i64 = 9
342const NA_GAIT_INPUT_W: i64 = 18
343const NA_GAIT_HEADER_W: i64 = 8
344const NA_GAIT_ROW_W: i64 = 3
345const NA_GAIT_DISTANCE: i64 = 1
346const NA_GAIT_CONTACT: i64 = 2

functions

59func na_i16(w: i64, sh: i64) -> i64
64func na_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
69func na_index(anim: *i64, nw: i64, nj: i64, tidx: *i64) -> i64
102func na_key_t(anim: *i64, ko: i64, i: i64) -> i64 { return anim[ko + i*NA_CH2_KEY_W] & NA_LANE_MASK }
103func na_key_q(anim: *i64, ko: i64, i: i64, q: *i64) -> i64
called by 2: na_eval_keyg_eval_legacy calls 1: na_i16
112func na_key_d(anim: *i64, ko: i64, i: i64, d: *i64) -> i64
125func na_index_order(anim: *i64, tidx: *i64, nj: i64, order: *i64) -> i64
144func na_key_linear(anim: *i64, ko: i64, nk: i64, t: i64) -> i64
called by 1: na_eval_track calls 1: na_key_t
152func na_key_ordered(anim: *i64, ko: i64, nk: i64, t: i64) -> i64
called by 1: na_eval_track_ordered calls 1: na_key_t
163func na_eval_track(anim: *i64, ko: i64, nk: i64, t: i64, q: *i64, d: *i64, scr: *i64) -> i64
167func na_eval_track_ordered(anim: *i64, ko: i64, nk: i64, t: i64, q: *i64, d: *i64, scr: *i64) -> i64
171func na_eval_key(anim: *i64, ko: i64, nk: i64, c: i64, t: i64, q: *i64, d: *i64, scr: *i64) -> i64
200func na_root_joint(skel: *i64, nj: i64) -> i64
called by 4: mainmainmainmain calls 1: na_abs
215func na_fit_root(anim: *i64, tidx: *i64, rj: i64, rv: *i64, scr: *i64) -> i64
called by 4: mainmainmainmain calls 2: na_key_dna_key_t
242func na_mpb(anim: *i64, tidx: *i64, rj: i64, dur: i64, scr: *i64) -> i64
called by 4: mainmainmainmain calls 2: na_key_dnf_isqrt
278func na_rows(anim: *i64, tidx: *i64, nj: i64, skel: *i64, pq: *i64, pd: *i64, haspose: i64, rv: *i64, t: i64, iw: i64, out: *i64, scr: *i64) -> i64
282func na_rows_indexed(anim: *i64, tidx: *i64, order: *i64, nj: i64, skel: *i64, pq: *i64, pd: *i64, haspose: i64, rv: *i64, t: i64, iw: i64, out: *i64, scr: *i64) -> i64
347func na_gait_sole(anim: *i64, ko: i64, nk: i64, skel: *i64, foot: *i64, t: i64, rv: *i64, out: *i64, scr: *i64) -> i64
373func na_gait_fit(anim: *i64,tidx: *i64,order: *i64,skel: *i64,nj: i64,rv: *i64,feet: *i64,header: *i64,table: *i64,capacity: i64,scr: *i64) -> i64
415func na_gait_time(table: *i64,n: i64,distance: i64) -> i64