code wiki / (root) / nx_motion_rig.nx

nx_motion_rig.nx source

↩ module page · 298 lines · 17244 B

1// nx_motion_rig.nx -- THE JOB BEHIND /motion (2026-09-06): one upload directory in; a rigged, rendered, exported result out, 2// every composed organ's own report kept beside it, a sha256 of the rig, and a done marker written LAST. 3// nx_motion_rig rig <jobdir> [cells] 4// nx_motion_rig anim <jobdir> <donor.nxa> <pose_id> <clipkey> 5// The jobdir holds in.<kind> and kind (written by the door, nx_motion_serve). Nothing here re-implements anything: the 6// converters, the auto-rig, the UV unwrap, the rasteriser, the exporter, the retarget and the player are the estate's 7// PROMOTED organs, forked with their stdout kept as log_<stage>.txt. A stage that refuses leaves its exit code in the 8// status rows and the job still finishes with `done`, so the page reads FAILED at a named stage -- never green, never hung. 9// MEASURED ON THE FIRST REAL JOB (house mesh, 2026-09-06, 16.9 s): the textured rasteriser REFUSES an asset without a TEXC 10// section ("run nx_nxa_texc first"), so the render stage now composes nx_nxa_texc (a bone-anchored UV unwrap from the fresh 11// SKEL+SKIN) before nx_nxa_texbake view; and nx_filehash lives at the serving root, not in _offc, so every fork resolves 12// _offc/<name>.elf first and ./<name>.elf second -- a 127 from an absent twin is not a property of the organ. 13// HONEST LIMIT, stated here and on the page: the promoted nx_autorig_mesh applies [cells] to the WEIGHT solve; its skeleton 14// runs at the estate's conf resolution until the one-call organ takes the ladder for both stages (motion.plan MO12). 15// license_tier: ORIGINAL expect_exit: 0 16import "nx_syscalls.nx" 17import "nx_motion_lib.nx" 18 19const MR_EXIT_OK: i64 = 0 20const MR_EXIT_USAGE: i64 = 2 21const MR_EXIT_REFUSE: i64 = 3 22const MR_CELLS_DEFAULT: i64 = 192 // the referee-measured rung on the house rig (923 permil); a caller's cells wins 23const MR_STATUS_CAP: i64 = 8192 24const MR_RC_CONVERT: i64 = 0 25const MR_RC_RIG: i64 = 1 26const MR_RC_TEXC: i64 = 2 27const MR_RC_VIEW: i64 = 3 28const MR_RC_GLB: i64 = 4 29const MR_RC_HASH: i64 = 5 30const MR_RC_WORDS: i64 = 6 31const MR_N_VERTS: i64 = 0 32const MR_N_TRIS: i64 = 1 33const MR_N_LIMBS: i64 = 2 34const MR_N_JOINTS: i64 = 3 35const MR_N_BRANCHES: i64 = 4 36const MR_N_MIXED: i64 = 5 37const MR_N_WORDS: i64 = 6 38const MR_SHA_LEN: i64 = 64 39const MR_RC_KIND_REFUSED: i64 = 415 // the HTTP status the door would answer for the same refusal, kept as one vocabulary 40 41func mrw(s: *u8) -> i64 { sys_write(1, s, mo_slen(s)); return 0 } 42// the organ's elf: the _offc twin when it exists (the runners' root), else the serving root (where registered tools live) 43func mr_elf(name: *u8) -> *u8 { 44 let p: *u8 = sys_mmap(MO_PATH_CAP) 45 var o: i64 = mo_cat(p, 0, "_offc/" as *u8); o = mo_cat(p, o, name); o = mo_cat(p, o, ".elf" as *u8); p[o] = 0 as u8 46 if mo_exists(p) == 1 { return p } 47 o = mo_cat(p, 0, "./" as *u8); o = mo_cat(p, o, name); o = mo_cat(p, o, ".elf" as *u8); p[o] = 0 as u8 48 return p 49} 50func mr_kv(b: *u8, o: i64, k: *u8, v: i64) -> i64 { var p: i64 = mo_cat(b, o, k); b[p] = 61 as u8; p = mo_num(b, p + 1, v); b[p] = 10 as u8; return p + 1 } 51func mr_ks(b: *u8, o: i64, k: *u8, s: *u8) -> i64 { var p: i64 = mo_cat(b, o, k); b[p] = 61 as u8; p = mo_cat(b, p + 1, s); b[p] = 10 as u8; return p + 1 } 52// the status rows, truncate-written at EVERY stage so a job that dies leaves its last stage named 53func mr_status(jobdir: *u8, kind: *u8, cells: i64, stage: *u8, ok: i64, rcs: *i64, nums: *i64, sha: *u8, ms: i64) -> i64 { 54 let b: *u8 = sys_mmap(MR_STATUS_CAP) 55 var o: i64 = 0 56 o = mr_ks(b, o, "stage" as *u8, stage) 57 o = mr_kv(b, o, "ok" as *u8, ok) 58 o = mr_ks(b, o, "kind" as *u8, kind) 59 o = mr_kv(b, o, "cells" as *u8, cells) 60 o = mr_kv(b, o, "rc_convert" as *u8, rcs[MR_RC_CONVERT]) 61 o = mr_kv(b, o, "rc_rig" as *u8, rcs[MR_RC_RIG]) 62 o = mr_kv(b, o, "rc_texc" as *u8, rcs[MR_RC_TEXC]) 63 o = mr_kv(b, o, "rc_view" as *u8, rcs[MR_RC_VIEW]) 64 o = mr_kv(b, o, "rc_glb" as *u8, rcs[MR_RC_GLB]) 65 o = mr_kv(b, o, "rc_hash" as *u8, rcs[MR_RC_HASH]) 66 o = mr_kv(b, o, "verts" as *u8, nums[MR_N_VERTS]) 67 o = mr_kv(b, o, "tris" as *u8, nums[MR_N_TRIS]) 68 o = mr_kv(b, o, "limbs" as *u8, nums[MR_N_LIMBS]) 69 o = mr_kv(b, o, "joints" as *u8, nums[MR_N_JOINTS]) 70 o = mr_kv(b, o, "branches" as *u8, nums[MR_N_BRANCHES]) 71 o = mr_kv(b, o, "mixed_verts" as *u8, nums[MR_N_MIXED]) 72 o = mr_ks(b, o, "rig_sha256" as *u8, sha) 73 o = mr_kv(b, o, "ms" as *u8, ms) 74 let r: i64 = mo_write_all(mo_join(jobdir, "status" as *u8), b, o) 75 sys_munmap(b, MR_STATUS_CAP) 76 return r 77} 78func mr_done(jobdir: *u8) -> i64 { return mo_write_all(mo_join(jobdir, "done" as *u8), "1\n" as *u8, 2) } 79// the log's reported integer for key, or -1 80func mr_logv(path: *u8, key: *u8) -> i64 { 81 let lp: *i64 = sys_mmap(16) as *i64 82 let b: *u8 = mo_read(path, lp) 83 if (b as i64) == 0 { return 0 - 1 } 84 return mo_log_field(b, lp[0], key) 85} 86// nx_filehash prints {"organ":"nx_filehash","sha256":"<64 hex>",...}; copy the 64 hex into out (NUL-terminated), or "-" 87func mr_sha_from_log(path: *u8, out: *u8) -> i64 { 88 let lp: *i64 = sys_mmap(16) as *i64 89 let b: *u8 = mo_read(path, lp) 90 out[0] = 45 as u8; out[1] = 0 as u8 91 if (b as i64) == 0 { return 0 } 92 let p: i64 = mo_find(b, lp[0], "\"sha256\":\"" as *u8) 93 if p < 0 { return 0 } 94 let s: i64 = p + 10 95 if s + MR_SHA_LEN > lp[0] { return 0 } 96 var i: i64 = 0 97 while i < MR_SHA_LEN { out[i] = b[s + i]; i = i + 1 } 98 out[MR_SHA_LEN] = 0 as u8 99 return MR_SHA_LEN 100} 101// render an asset from three sides: TEXC first (the rasteriser refuses without it), then the textured view. Returns the 102// view's exit code and stores the unwrap's in rc_texc; a render failure never fails the rig, the page shows the codes. 103func mr_render(jobdir: *u8, src: *u8, texp: *u8, png: *u8, logt: *u8, logv: *u8, rc_texc: *i64) -> i64 { 104 let words: *i64 = sys_mmap(MO_ARGV_CAP * 8) as *i64 105 words[0] = src as i64; words[1] = texp as i64 106 rc_texc[0] = mo_run(mr_elf("nx_nxa_texc" as *u8), words, 2, logt) 107 var viewsrc: *u8 = texp 108 if rc_texc[0] != 0 { viewsrc = src } 109 let vview: *u8 = "view" as *u8 110 words[0] = vview as i64; words[1] = viewsrc as i64; words[2] = png as i64 111 return mo_run(mr_elf("nx_nxa_texbake" as *u8), words, 3, logv) 112} 113func mr_rig(jobdir: *u8, cells: i64) -> i64 { 114 let t0: i64 = sys_clock_now_us() 115 let lp: *i64 = sys_mmap(16) as *i64 116 let rcs: *i64 = sys_mmap(MR_RC_WORDS * 8 + 8) as *i64 117 let nums: *i64 = sys_mmap(MR_N_WORDS * 8 + 8) as *i64 118 var k: i64 = 0 119 while k < MR_RC_WORDS { rcs[k] = 0 - 1; k = k + 1 } 120 k = 0 121 while k < MR_N_WORDS { nums[k] = 0 - 1; k = k + 1 } 122 let sha: *u8 = sys_mmap(MR_SHA_LEN + 8) 123 sha[0] = 45 as u8; sha[1] = 0 as u8 124 // the kind the door sniffed, one word on one line 125 let kb: *u8 = mo_read(mo_join(jobdir, "kind" as *u8), lp) 126 let kind: *u8 = sys_mmap(16) 127 var kn: i64 = 0 128 if (kb as i64) != 0 { while kn < lp[0] { if kn < 12 { let c: i64 = kb[kn] as i64; if c == 10 { break } if c == 13 { break } kind[kn] = kb[kn]; kn = kn + 1 } else { break } } } 129 kind[kn] = 0 as u8 130 let kk: i64 = mo_kind_from_name(kind) 131 mr_status(jobdir, kind, cells, "convert" as *u8, 0, rcs, nums, sha, 0) 132 // convert: in.<kind> -> bare.nxa through the estate's own readers, or a byte copy for an NXA 133 let inname: *u8 = sys_mmap(32) 134 var io: i64 = mo_cat(inname, 0, "in." as *u8) 135 io = mo_cat(inname, io, kind) 136 inname[io] = 0 as u8 137 let inpath: *u8 = mo_join(jobdir, inname) 138 let bare: *u8 = mo_join(jobdir, "bare.nxa" as *u8) 139 let logc: *u8 = mo_join(jobdir, "log_convert.txt" as *u8) 140 let words: *i64 = sys_mmap(MO_ARGV_CAP * 8) as *i64 141 var rc: i64 = 0 - 1 142 if kk == MO_KIND_GLB { words[0] = inpath as i64; words[1] = bare as i64; rc = mo_run(mr_elf("nx_gltf2mesh" as *u8), words, 2, logc) } 143 if kk == MO_KIND_FBX { words[0] = inpath as i64; words[1] = bare as i64; rc = mo_run(mr_elf("nx_fbx2nxa" as *u8), words, 2, logc) } 144 if kk == MO_KIND_NXA { 145 let ib: *u8 = mo_read(inpath, lp) 146 if (ib as i64) == 0 { rc = 3; mo_write_all(logc, "COPY REFUSED: the uploaded NXA could not be read back\n" as *u8, 54) } else { 147 let w: i64 = mo_write_all(bare, ib, lp[0]) 148 if w == lp[0] { rc = 0 } else { rc = 6 } 149 let lb: *u8 = sys_mmap(128) 150 var lo: i64 = mo_cat(lb, 0, "COPY bytes=" as *u8); lo = mo_num(lb, lo, lp[0]); lo = mo_cat(lb, lo, " (an NXA needs no reader: the container is already ours)\n" as *u8) 151 mo_write_all(logc, lb, lo) 152 } 153 } 154 if rc == (0 - 1) { 155 rc = MR_RC_KIND_REFUSED 156 let lb: *u8 = sys_mmap(256) 157 var lo: i64 = mo_cat(lb, 0, "CONVERT REFUSED BY NAME: kind=" as *u8); lo = mo_cat(lb, lo, kind); lo = mo_cat(lb, lo, " has no sovereign reader composed on this door (OBJ and unknown bytes are refused, never guessed)\n" as *u8) 158 mo_write_all(logc, lb, lo) 159 } 160 rcs[MR_RC_CONVERT] = rc 161 if rc != 0 { 162 mr_status(jobdir, kind, cells, "convert" as *u8, 0, rcs, nums, sha, (sys_clock_now_us() - t0) / 1000) 163 mr_done(jobdir) 164 mrw("MOTION-RIG FAILED stage=convert rc=" as *u8); let nb: *u8 = sys_mmap(32); let nn: i64 = mo_num(nb, 0, rc); sys_write(1, nb, nn); mrw("\n" as *u8) 165 return MR_EXIT_REFUSE 166 } 167 // rig: the one-call auto-rig, skeleton then weights 168 mr_status(jobdir, kind, cells, "rig" as *u8, 0, rcs, nums, sha, (sys_clock_now_us() - t0) / 1000) 169 let skel: *u8 = mo_join(jobdir, "skel.nxa" as *u8) 170 let rig: *u8 = mo_join(jobdir, "rig.nxa" as *u8) 171 let logr: *u8 = mo_join(jobdir, "log_rig.txt" as *u8) 172 let cellstr: *u8 = sys_mmap(32) 173 let cl: i64 = mo_num(cellstr, 0, cells) 174 cellstr[cl] = 0 as u8 175 words[0] = bare as i64; words[1] = skel as i64; words[2] = rig as i64; words[3] = cellstr as i64 176 rc = mo_run(mr_elf("nx_autorig_mesh" as *u8), words, 4, logr) 177 rcs[MR_RC_RIG] = rc 178 nums[MR_N_VERTS] = mr_logv(logr, "verts" as *u8) 179 nums[MR_N_TRIS] = mr_logv(logr, "tris" as *u8) 180 nums[MR_N_LIMBS] = mr_logv(logr, "limbs" as *u8) 181 nums[MR_N_JOINTS] = mr_logv(logr, "joints" as *u8) 182 nums[MR_N_BRANCHES] = mr_logv(logr, "branches" as *u8) 183 nums[MR_N_MIXED] = mr_logv(logr, "mixed_verts" as *u8) 184 if rc != 0 { 185 mr_status(jobdir, kind, cells, "rig" as *u8, 0, rcs, nums, sha, (sys_clock_now_us() - t0) / 1000) 186 mr_done(jobdir) 187 mrw("MOTION-RIG FAILED stage=rig\n" as *u8) 188 return MR_EXIT_REFUSE 189 } 190 // view: UV unwrap then the sovereign three-view render (a failure here does not fail the rig; the page shows the codes) 191 mr_status(jobdir, kind, cells, "view" as *u8, 0, rcs, nums, sha, (sys_clock_now_us() - t0) / 1000) 192 let rct: *i64 = sys_mmap(16) as *i64 193 rcs[MR_RC_VIEW] = mr_render(jobdir, rig, mo_join(jobdir, "rig_tex.nxa" as *u8), mo_join(jobdir, "rig_view.png" as *u8), mo_join(jobdir, "log_texc.txt" as *u8), mo_join(jobdir, "log_view.txt" as *u8), rct) 194 rcs[MR_RC_TEXC] = rct[0] 195 // glb: the engine-ready export with its skin 196 mr_status(jobdir, kind, cells, "glb" as *u8, 0, rcs, nums, sha, (sys_clock_now_us() - t0) / 1000) 197 let glb: *u8 = mo_join(jobdir, "rig.glb" as *u8) 198 words[0] = rig as i64; words[1] = glb as i64 199 rcs[MR_RC_GLB] = mo_run(mr_elf("nx_mesh2glb" as *u8), words, 2, mo_join(jobdir, "log_glb.txt" as *u8)) 200 // hash: the determinism witness 201 let logh: *u8 = mo_join(jobdir, "log_hash.txt" as *u8) 202 words[0] = rig as i64 203 rcs[MR_RC_HASH] = mo_run(mr_elf("nx_filehash" as *u8), words, 1, logh) 204 mr_sha_from_log(logh, sha) 205 let ms: i64 = (sys_clock_now_us() - t0) / 1000 206 mr_status(jobdir, kind, cells, "done" as *u8, 1, rcs, nums, sha, ms) 207 mr_done(jobdir) 208 mrw("MOTION-RIG OK limbs=" as *u8) 209 let nb2: *u8 = sys_mmap(256) 210 var no: i64 = mo_num(nb2, 0, nums[MR_N_LIMBS]); no = mo_cat(nb2, no, " joints=" as *u8); no = mo_num(nb2, no, nums[MR_N_JOINTS]); no = mo_cat(nb2, no, " sha256=" as *u8); no = mo_cat(nb2, no, sha); no = mo_cat(nb2, no, " ms=" as *u8); no = mo_num(nb2, no, ms); no = mo_cat(nb2, no, "\n" as *u8) 211 sys_write(1, nb2, no) 212 return MR_EXIT_OK 213} 214func mr_anim(jobdir: *u8, donor: *u8, pose: *u8, key: *u8) -> i64 { 215 if mo_safe_name(key) == 0 { mrw("MOTION-ANIM REFUSED: clip key is not a safe name\n" as *u8); return MR_EXIT_REFUSE } 216 if mo_digits_only(pose) == 0 { mrw("MOTION-ANIM REFUSED: pose id is not a number\n" as *u8); return MR_EXIT_REFUSE } 217 let rig: *u8 = mo_join(jobdir, "rig.nxa" as *u8) 218 if mo_exists(rig) == 0 { mrw("MOTION-ANIM REFUSED: the job has no rig.nxa yet\n" as *u8); return MR_EXIT_REFUSE } 219 if mo_exists(donor) == 0 { mrw("MOTION-ANIM REFUSED: donor absent\n" as *u8); return MR_EXIT_REFUSE } 220 let nb: *u8 = sys_mmap(MO_PATH_CAP) 221 var o: i64 = mo_cat(nb, 0, "anim_" as *u8); o = mo_cat(nb, o, key); o = mo_cat(nb, o, ".nxa" as *u8); nb[o] = 0 as u8 222 let anim: *u8 = mo_join(jobdir, nb) 223 o = mo_cat(nb, 0, "posed_" as *u8); o = mo_cat(nb, o, key); o = mo_cat(nb, o, ".nxa" as *u8); nb[o] = 0 as u8 224 let posed: *u8 = mo_join(jobdir, nb) 225 o = mo_cat(nb, 0, "posed_" as *u8); o = mo_cat(nb, o, key); o = mo_cat(nb, o, "_tex.nxa" as *u8); nb[o] = 0 as u8 226 let posedtex: *u8 = mo_join(jobdir, nb) 227 o = mo_cat(nb, 0, "posed_" as *u8); o = mo_cat(nb, o, key); o = mo_cat(nb, o, ".png" as *u8); nb[o] = 0 as u8 228 let png: *u8 = mo_join(jobdir, nb) 229 o = mo_cat(nb, 0, "posed_" as *u8); o = mo_cat(nb, o, key); o = mo_cat(nb, o, ".glb" as *u8); nb[o] = 0 as u8 230 let glb: *u8 = mo_join(jobdir, nb) 231 o = mo_cat(nb, 0, "log_ret_" as *u8); o = mo_cat(nb, o, key); o = mo_cat(nb, o, ".txt" as *u8); nb[o] = 0 as u8 232 let logr: *u8 = mo_join(jobdir, nb) 233 o = mo_cat(nb, 0, "log_play_" as *u8); o = mo_cat(nb, o, key); o = mo_cat(nb, o, ".txt" as *u8); nb[o] = 0 as u8 234 let logp: *u8 = mo_join(jobdir, nb) 235 o = mo_cat(nb, 0, "log_texc_" as *u8); o = mo_cat(nb, o, key); o = mo_cat(nb, o, ".txt" as *u8); nb[o] = 0 as u8 236 let logt: *u8 = mo_join(jobdir, nb) 237 o = mo_cat(nb, 0, "log_view_" as *u8); o = mo_cat(nb, o, key); o = mo_cat(nb, o, ".txt" as *u8); nb[o] = 0 as u8 238 let logv: *u8 = mo_join(jobdir, nb) 239 o = mo_cat(nb, 0, "log_glb_" as *u8); o = mo_cat(nb, o, key); o = mo_cat(nb, o, ".txt" as *u8); nb[o] = 0 as u8 240 let logg: *u8 = mo_join(jobdir, nb) 241 let words: *i64 = sys_mmap(MO_ARGV_CAP * 8) as *i64 242 let vret: *u8 = "retarget" as *u8 243 let vemit: *u8 = "emit=pose" as *u8 244 words[0] = vret as i64; words[1] = donor as i64; words[2] = pose as i64; words[3] = rig as i64; words[4] = anim as i64; words[5] = vemit as i64 245 let rc1: i64 = mo_run(mr_elf("nx_nxa_retarget" as *u8), words, 6, logr) 246 let vfull: *u8 = "1000" as *u8 247 words[0] = anim as i64; words[1] = pose as i64; words[2] = posed as i64; words[3] = vfull as i64 248 var rc2: i64 = 0 - 1 249 if rc1 == 0 { rc2 = mo_run(mr_elf("nx_nxa_play" as *u8), words, 4, logp) } 250 let jp: i64 = mr_logv(logp, "joints_posed" as *u8) 251 let vm: i64 = mr_logv(logp, "verts_moved" as *u8) 252 let md: i64 = mr_logv(logp, "max_disp_umm" as *u8) 253 var rc3: i64 = 0 - 1 254 var rc4: i64 = 0 - 1 255 let rct: *i64 = sys_mmap(16) as *i64 256 rct[0] = 0 - 1 257 if rc2 == 0 { 258 rc3 = mr_render(jobdir, posed, posedtex, png, logt, logv, rct) 259 words[0] = posed as i64; words[1] = glb as i64 260 rc4 = mo_run(mr_elf("nx_mesh2glb" as *u8), words, 2, logg) 261 } 262 // the applied-clip row: key|rc_retarget|rc_play|joints_posed|verts_moved|max_disp_umm|rc_view|rc_glb|rc_texc 263 let row: *u8 = sys_mmap(MO_VAL_CAP) 264 var r: i64 = mo_cat(row, 0, key) 265 row[r] = 124 as u8; r = mo_num(row, r + 1, rc1) 266 row[r] = 124 as u8; r = mo_num(row, r + 1, rc2) 267 row[r] = 124 as u8; r = mo_num(row, r + 1, jp) 268 row[r] = 124 as u8; r = mo_num(row, r + 1, vm) 269 row[r] = 124 as u8; r = mo_num(row, r + 1, md) 270 row[r] = 124 as u8; r = mo_num(row, r + 1, rc3) 271 row[r] = 124 as u8; r = mo_num(row, r + 1, rc4) 272 row[r] = 124 as u8; r = mo_num(row, r + 1, rct[0]) 273 row[r] = 10 as u8; r = r + 1 274 mo_append(mo_join(jobdir, "clips" as *u8), row, r) 275 mrw("MOTION-ANIM " as *u8); sys_write(1, row, r) 276 if rc1 != 0 { return MR_EXIT_REFUSE } 277 if rc2 != 0 { return MR_EXIT_REFUSE } 278 return MR_EXIT_OK 279} 280func main(argc: i64, argv: *i64) -> i64 { 281 if argc < 3 { 282 mrw("usage: nx_motion_rig rig <jobdir> [cells] | anim <jobdir> <donor.nxa> <pose_id> <clipkey>\n" as *u8) 283 return MR_EXIT_USAGE 284 } 285 let verb: *u8 = argv[1] as *u8 286 if mo_streq(verb, "rig" as *u8) == 1 { 287 var cells: i64 = MR_CELLS_DEFAULT 288 if argc >= 4 { let a: *u8 = argv[3] as *u8; cells = mo_atoi(a, mo_slen(a)) } 289 if cells < 1 { cells = MR_CELLS_DEFAULT } 290 return mr_rig(argv[2] as *u8, cells) 291 } 292 if mo_streq(verb, "anim" as *u8) == 1 { 293 if argc < 6 { mrw("usage: nx_motion_rig anim <jobdir> <donor.nxa> <pose_id> <clipkey>\n" as *u8); return MR_EXIT_USAGE } 294 return mr_anim(argv[2] as *u8, argv[3] as *u8, argv[4] as *u8, argv[5] as *u8) 295 } 296 mrw("usage: nx_motion_rig rig <jobdir> [cells] | anim <jobdir> <donor.nxa> <pose_id> <clipkey>\n" as *u8) 297 return MR_EXIT_USAGE 298}