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}