code wiki / _hdl_build / nx_skeleton_gate.nx

nx_skeleton_gate.nx source

↩ module page · 156 lines · 7580 B

1// nx_skeleton_gate.nx -- gate for SKELETON + LBS SKINNING (the character-foundation rung; VAMX R3). 2// T1 hierarchy compose: a 2-bone arm posed at known angles -> child world position matches the HAND-COMPUTED 3// value (integer-trig tolerance) 4// T2 skin follows the pose: a vertex rigidly bound to the forearm lands exactly at its rotated position 5// T3 blend: a joint vertex weighted 128/128 lands midway between the two bones' rigid transforms 6// T4 determinism: pose->skin twice, outputs byte-identical 7// T5 PNG knowledge/nx_skeleton_arm.png: a skinned 2-bone tube STRAIGHT vs BENT 90deg -- smooth joint visible 8// license_tier: ORIGINAL expect_exit: 0 9import "nx_syscalls.nx" 10import "nx_png.nx" 11import "nx_skeleton.nx" 12 13func hw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 14func pn(v: i64) -> i64 { 15 let b: *u8 = sys_mmap(32) as *u8 16 var x: i64 = v 17 var neg: i64 = 0 18 if x < 0 { neg = 1; x = 0 - x } 19 var i: i64 = 31 20 if x == 0 { b[i] = 48 as u8; i = i - 1 } 21 while x > 0 { b[i] = (48 + x % 10) as u8; x = x / 10; i = i - 1 } 22 if neg == 1 { b[i] = 45 as u8; i = i - 1 } 23 sys_write(1, (b as i64 + i + 1) as *u8, 31 - i) 24 return 0 25} 26func gabs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 27 28// build the 2-bone arm: upper arm root at origin along +x (length 1000), forearm child at +1000 along +x 29func mk_arm(base: i64) -> i64 { 30 sk_init(base) 31 sk_add_bone(base, 0 - 1, 0, 0, 0) 32 sk_add_bone(base, 0, 1000, 0, 0) 33 return 0 34} 35// skin a tube: 8 rings of 2 verts (top/bottom) along the arm, upper-arm verts on bone0, forearm on bone1, 36// the JOINT ring blended 128/128 37func mk_tube(base: i64) -> i64 { 38 var s: i64 = 0 39 while s < 9 { 40 let lx: i64 = s * 250 // 0..2000 along the arm 41 var b0: i64 = 0 42 var w0: i64 = 256 43 var b1: i64 = 0 44 var w1: i64 = 0 45 var vx: i64 = lx 46 if s == 4 { b0 = 0; w0 = 128; b1 = 1; w1 = 128; vx = 1000 } // joint ring: local 1000 on bone0 / 0 on bone1 47 if s > 4 { b0 = 1; w0 = 256; b1 = 0; w1 = 0; vx = lx - 1000 } 48 // v1 shares ONE local pos between influences: at the joint, bone0-local (1000,y) and bone1-local 49 // (0,y) refer to the same bind point ONLY for the bone1 frame origin -- author the joint ring in 50 // bone1-local coords (0,y) and give bone0 the same numbers knowing bind poses align along +x: 51 if s == 4 { vx = 0 } 52 sk_add_vert(base, b0, w0, b1, w1, vx, 120, 0) 53 sk_add_vert(base, b0, w0, b1, w1, vx, 0 - 120, 0) 54 s = s + 1 55 } 56 return 0 57} 58 59func main() -> i64 { 60 var fails: i64 = 0 61 62 // ---- T1 hierarchy compose, hand-computed ---- 63 let A: i64 = sys_mmap(sk_bytes()) as i64 64 mk_arm(A) 65 sk_pose(A, 0, 3217, 0) // upper arm yaw ~45deg (PI/4*4096=3217) 66 sk_pose(A, 1, 3217, 0) // elbow another 45 -> forearm at 90 total 67 sk_update(A) 68 let b1p: *i64 = sk_bone(A, 1) 69 // hand-compute: bone1 world pos = R_root(45deg yaw)*offset(1000,0,0): yaw rotates x toward... Ry(45): 70 // x' = cos45*1000 = 724 (fx: cos4096(3217)=~2896 -> /16=181 -> *1000/256=707+), z' = -sin45*1000 ~ -707 71 var t1: i64 = 1 72 if gabs(b1p[15] - 707) > 24 { t1 = 0 } 73 if gabs(b1p[17] + 707) > 24 { t1 = 0 } 74 if gabs(b1p[16]) > 8 { t1 = 0 } 75 if t1 == 1 { hw("T1 PASS hierarchy: elbow world = (707,0,-707)-ish for 45deg yaw, got (" as *u8); pn(b1p[15]); hw("," as *u8); pn(b1p[16]); hw("," as *u8); pn(b1p[17]); hw(") within trig tolerance\n" as *u8) } 76 else { hw("T1 FAIL elbow=(" as *u8); pn(b1p[15]); hw("," as *u8); pn(b1p[16]); hw("," as *u8); pn(b1p[17]); hw(")\n" as *u8); fails = fails + 1 } 77 78 // ---- T2 skin follows: forearm tip vertex under a 90deg TOTAL rotation ---- 79 let B: i64 = sys_mmap(sk_bytes()) as i64 80 mk_arm(B) 81 mk_tube(B) 82 sk_pose(B, 0, 0, 0) 83 sk_pose(B, 1, 6434, 0) // elbow 90deg yaw 84 sk_update(B) 85 sk_skin(B) 86 // tip ring s=8: bone1-local (1000, +-120): world = elbowpos(1000,0,0) + Ry(90)*local: 87 // Ry(90): x'=cos90*x + sin90*z = 0, z' = -sin90*x = -1000; y stays 88 let tip: *i64 = sk_out(B, 16) // s=8 top vert index = 8*2 = 16 89 var t2: i64 = 1 90 if gabs(tip[0] - 1000) > 30 { t2 = 0 } 91 if gabs(tip[1] - 120) > 12 { t2 = 0 } 92 if gabs(tip[2] + 1000) > 30 { t2 = 0 } 93 if t2 == 1 { hw("T2 PASS skin follows: forearm tip at 90deg = (1000,120,-1000)-ish, got (" as *u8); pn(tip[0]); hw("," as *u8); pn(tip[1]); hw("," as *u8); pn(tip[2]); hw(")\n" as *u8) } 94 else { hw("T2 FAIL tip=(" as *u8); pn(tip[0]); hw("," as *u8); pn(tip[1]); hw("," as *u8); pn(tip[2]); hw(")\n" as *u8); fails = fails + 1 } 95 96 // ---- T3 blend midpoint: the joint ring under the same pose ---- 97 // rigid-on-bone0 would put it at (1000,120,0); rigid-on-bone1 at (1000,120,0)+Ry90*(0,120..)-> same x? 98 // bone1-local (0,120,0): world = (1000,0,0) + Ry90*(0,120,0) = (1000,120,0). bone0 with the SAME local 99 // numbers (0,120,0): world = (0,120,0). midpoint = (500,120,0). 100 let joint: *i64 = sk_out(B, 8) // s=4 top vert = index 8 101 var t3: i64 = 1 102 if gabs(joint[0] - 500) > 24 { t3 = 0 } 103 if gabs(joint[1] - 120) > 12 { t3 = 0 } 104 if gabs(joint[2]) > 24 { t3 = 0 } 105 if t3 == 1 { hw("T3 PASS blend: 128/128 joint vertex lands at the midpoint (" as *u8); pn(joint[0]); hw("," as *u8); pn(joint[1]); hw("," as *u8); pn(joint[2]); hw(")\n" as *u8) } 106 else { hw("T3 FAIL joint=(" as *u8); pn(joint[0]); hw("," as *u8); pn(joint[1]); hw("," as *u8); pn(joint[2]); hw(")\n" as *u8); fails = fails + 1 } 107 108 // ---- T4 determinism ---- 109 sk_update(B) 110 sk_skin(B) 111 let tip2: *i64 = sk_out(B, 16) 112 var t4: i64 = 1 113 if tip2[0] != tip[0] { t4 = 0 } 114 if tip2[1] != tip[1] { t4 = 0 } 115 if tip2[2] != tip[2] { t4 = 0 } 116 if t4 == 1 { hw("T4 PASS determinism: re-pose+re-skin byte-identical\n" as *u8) } 117 else { hw("T4 FAIL\n" as *u8); fails = fails + 1 } 118 119 // ---- T5 PNG: straight vs bent tube ---- 120 let TW: i64 = 256 121 let TH: i64 = 128 122 let fb: *i64 = sys_mmap(TW * TH * 8) as *i64 123 var pi: i64 = 0 124 while pi < TW * TH { fb[pi] = 24 + 28 * 256 + 36 * 65536; pi = pi + 1 } 125 var pass2: i64 = 0 126 while pass2 < 2 { 127 let C: i64 = sys_mmap(sk_bytes()) as i64 128 mk_arm(C) 129 mk_tube(C) 130 var ang: i64 = 0 131 if pass2 == 1 { ang = 6434 } 132 sk_pose(C, 0, 0, 0) 133 sk_pose(C, 1, ang, 0) 134 sk_update(C) 135 sk_skin(C) 136 let hC: *i64 = sk_hdr(C) 137 var vi: i64 = 0 138 while vi < hC[1] { 139 let o: *i64 = sk_out(C, vi) 140 let px: i64 = 20 + pass2 * 110 + o[0] / 24 141 let py: i64 = 40 - o[2] / 24 + o[1] / 60 142 if px >= 0 { if px < TW { if py >= 0 { if py < TH { 143 fb[py * TW + px] = 130 + 220 * 256 + 160 * 65536 144 if py + 1 < TH { fb[(py + 1) * TW + px] = 130 + 220 * 256 + 160 * 65536 } 145 } } } } 146 vi = vi + 1 147 } 148 pass2 = pass2 + 1 149 } 150 write_png(fb, TW, TH, "knowledge/nx_skeleton_arm.png" as *u8) 151 hw("T5 PNG written: knowledge/nx_skeleton_arm.png (straight arm vs 90deg-bent arm, skinned verts)\n" as *u8) 152 153 if fails == 0 { hw("VERDICT GREEN: skeleton+LBS 4/4 -- bones-as-data, parent-composed integer rotations, blended skinning (the rung morphs/IK/regions hang off)\n" as *u8) } 154 else { hw("VERDICT RED fails=" as *u8); pn(fails); hw("\n" as *u8) } 155 return fails 156}