code wiki / (root) / nx_figure_render.nx

nx_figure_render.nx source

↩ module page · 240 lines · 10501 B

1// nx_figure_render.nx -- the ELARA RIG (17-joint humanoid skeleton, bones-as-data) + a fast software renderer 2// that draws the POSED skeleton as a shaded humanoid (tapered capsules between joints + a head), with a per-pixel 3// Z-BUFFER so limbs occlude correctly regardless of draw order. This is the MOTION-v0 render surface (swap+motion 4// census SCOPE B): forward-kinematics from nx_skeleton give correct joint motion for ANY authored move; the 5// figure is a mannequin preview -- skinning it with the photoreal SDF body / a rigged mesh is the deep rung (S5/S6). 6// ALL INTEGER (deterministic, VM-vettable). Rig consts are shared by nx_movelib (which poses the bones) and this 7// renderer (which draws them). Reuses nx_skeleton FK + nx_itrig. license_tier: ORIGINAL 8import "nx_syscalls.nx" 9import "nx_skeleton.nx" 10import "nx_itrig.nx" 11const RIG_MAGIC_65536: i64 = 65536 12const RIG_MAGIC_4096: i64 = 4096 13const RIG_MAGIC_2000000000: i64 = 2000000000 14const RIG_MAGIC_6000: i64 = 6000 15const RIG_MAGIC_1024: i64 = 1024 16 17func fr_dbg(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 18 19// ---- the rig: 17 joints, parent-first (a joint's parent index is always < its own) -------------------- 20const RIG_NB: i64 = 17 21const RG_PELVIS: i64 = 0 22const RG_SPINE: i64 = 1 23const RG_CHEST: i64 = 2 24const RG_NECK: i64 = 3 25const RG_HEAD: i64 = 4 26const RG_SHL: i64 = 5 27const RG_ELL: i64 = 6 28const RG_WRL: i64 = 7 29const RG_SHR: i64 = 8 30const RG_ELR: i64 = 9 31const RG_WRR: i64 = 10 32const RG_HIPL: i64 = 11 33const RG_KNL: i64 = 12 34const RG_ANL: i64 = 13 35const RG_HIPR: i64 = 14 36const RG_KNR: i64 = 15 37const RG_ANR: i64 = 16 38 39// build the rest rig into a skeleton arena (offsets are child_rest - parent_rest, in body world units; 40// arms carry a small +z so they render in FRONT of the torso in a 3/4 view). parent-before-child order. 41func rig_build(sk: i64) -> i64 { 42 sk_init(sk) 43 sk_add_bone(sk, 0 - 1, 0, 80, 0) // 0 PELVIS root 44 sk_add_bone(sk, RG_PELVIS, 0, 420, 0) // 1 SPINE 45 sk_add_bone(sk, RG_SPINE, 0, 280, 0) // 2 CHEST 46 sk_add_bone(sk, RG_CHEST, 0, 230, 0) // 3 NECK 47 sk_add_bone(sk, RG_NECK, 0, 240, 0) // 4 HEAD 48 sk_add_bone(sk, RG_CHEST, 0-330, 20, 80) // 5 SHOULDER_L 49 sk_add_bone(sk, RG_SHL, 0-230, 0-330, 0) // 6 ELBOW_L 50 sk_add_bone(sk, RG_ELL, 0-130, 0-320, 0) // 7 WRIST_L 51 sk_add_bone(sk, RG_CHEST, 330, 20, 80) // 8 SHOULDER_R 52 sk_add_bone(sk, RG_SHR, 230, 0-330, 0) // 9 ELBOW_R 53 sk_add_bone(sk, RG_ELR, 130, 0-320, 0) // 10 WRIST_R 54 sk_add_bone(sk, RG_PELVIS, 0-195, 0-40, 0) // 11 HIP_L 55 sk_add_bone(sk, RG_HIPL, 0, 0-560, 0) // 12 KNEE_L 56 sk_add_bone(sk, RG_KNL, 0, 0-600, 0) // 13 ANKLE_L 57 sk_add_bone(sk, RG_PELVIS, 195, 0-40, 0) // 14 HIP_R 58 sk_add_bone(sk, RG_HIPR, 0, 0-560, 0) // 15 KNEE_R 59 sk_add_bone(sk, RG_KNR, 0, 0-600, 0) // 16 ANKLE_R 60 return 0 61} 62 63// per-bone draw radii (screen-space before scale): r at the PARENT end, r at THIS end. Bone i drawn parent->i. 64func rig_r0(i: i64) -> i64 { 65 if i == RG_SPINE { return 205 } 66 if i == RG_CHEST { return 175 } 67 if i == RG_NECK { return 120 } 68 if i == RG_HEAD { return 70 } 69 if i == RG_SHL { return 100 } 70 if i == RG_SHR { return 100 } 71 if i == RG_ELL { return 72 } 72 if i == RG_ELR { return 72 } 73 if i == RG_WRL { return 55 } 74 if i == RG_WRR { return 55 } 75 if i == RG_HIPL { return 150 } 76 if i == RG_HIPR { return 150 } 77 if i == RG_KNL { return 118 } 78 if i == RG_KNR { return 118 } 79 if i == RG_ANL { return 92 } 80 if i == RG_ANR { return 92 } 81 return 80 82} 83func rig_r1(i: i64) -> i64 { 84 if i == RG_SPINE { return 175 } 85 if i == RG_CHEST { return 192 } 86 if i == RG_NECK { return 72 } 87 if i == RG_HEAD { return 60 } 88 if i == RG_SHL { return 72 } 89 if i == RG_SHR { return 72 } 90 if i == RG_ELL { return 55 } 91 if i == RG_ELR { return 55 } 92 if i == RG_WRL { return 42 } 93 if i == RG_WRR { return 42 } 94 if i == RG_HIPL { return 120 } 95 if i == RG_HIPR { return 120 } 96 if i == RG_KNL { return 95 } 97 if i == RG_KNR { return 95 } 98 if i == RG_ANL { return 70 } 99 if i == RG_ANR { return 70 } 100 return 70 101} 102 103func fr_abs(a: i64) -> i64 { if a < 0 { return 0 - a } return a } 104func fr_clamp(v: i64, lo: i64, hi: i64) -> i64 { if v < lo { return lo } if v > hi { return hi } return v } 105 106// skin base color 107const SK_R: i64 = 236 108const SK_G: i64 = 182 109const SK_B: i64 = 158 110 111// stamp one shaded disc (a sphere-ish blob) into fb with z-test. c[0..3] = {cx,cy,cr,cd} (packed to keep the 112// arg count <=5 -- NishiLang mis-passes calls with >~6 args, which segfaulted the 8-arg form). 113func fr_disc(fb: *i64, zb: *i64, w: i64, h: i64, c: *i64) -> i64 { 114 let cx: i64 = c[0]; let cy: i64 = c[1]; let cr: i64 = c[2]; let cd: i64 = c[3] 115 if cr < 1 { return 0 } 116 let rr: i64 = cr * cr 117 var py: i64 = cy - cr 118 let pymax: i64 = cy + cr 119 while py <= pymax { 120 if py >= 0 { if py < h { 121 let dy: i64 = py - cy 122 var px: i64 = cx - cr 123 let pxmax: i64 = cx + cr 124 while px <= pxmax { 125 if px >= 0 { if px < w { 126 let dx: i64 = px - cx 127 let dd: i64 = dx * dx + dy * dy 128 if dd <= rr { 129 let ff: i64 = (rr - dd) * 256 / rr // 256 center .. 0 edge (round falloff) 130 let dir: i64 = ((0 - dx) * 70 + (0 - dy) * 95) / cr // light upper-left 131 var lit: i64 = 66 + ff * 128 / 256 + 78 + dir 132 lit = fr_clamp(lit, 26, 255) 133 let depth: i64 = cd * 16 + ff / 8 // cd dominates -> adjacent discs blend (less beading) 134 let idx: i64 = py * w + px 135 if depth > zb[idx] { 136 zb[idx] = depth 137 var r: i64 = SK_R * lit / 255 138 var g: i64 = SK_G * lit / 255 139 var b: i64 = SK_B * lit / 255 140 if r > 255 { r = 255 } 141 if g > 255 { g = 255 } 142 if b > 255 { b = 255 } 143 fb[idx] = r + g * 256 + b * RIG_MAGIC_65536 144 } 145 } 146 } } 147 px = px + 1 148 } 149 } } 150 py = py + 1 151 } 152 return 0 153} 154 155// draw a tapered capsule between two projected endpoints by stamping overlapping discs along the segment. 156// endpoints packed in e[0..7] = {sx0,sy0,d0,r0, sx1,sy1,d1,r1} -> keeps the arg count <=8 (NishiLang ABI: 157// functions with >8 args mis-pass on the stack -> segfault; the 12-arg form crashed here). 158func fr_capsule(fb: *i64, zb: *i64, w: i64, h: i64, e: *i64) -> i64 { 159 let sx0: i64 = e[0]; let sy0: i64 = e[1]; let d0: i64 = e[2]; let r0: i64 = e[3] 160 let sx1: i64 = e[4]; let sy1: i64 = e[5]; let d1: i64 = e[6]; let r1: i64 = e[7] 161 let dxl: i64 = sx1 - sx0 162 let dyl: i64 = sy1 - sy0 163 var l: i64 = fr_abs(dxl) 164 if fr_abs(dyl) > l { l = fr_abs(dyl) } 165 var n: i64 = l / 2 + 1 // tight disc spacing -> overlapping discs read as a smooth limb 166 if n > 900 { n = 900 } 167 let c: *i64 = sys_mmap(4 * 8) as *i64 168 var s: i64 = 0 169 while s <= n { 170 let t: i64 = s * RIG_MAGIC_4096 / n 171 c[0] = sx0 + dxl * t / RIG_MAGIC_4096 172 c[1] = sy0 + dyl * t / RIG_MAGIC_4096 173 c[2] = r0 + (r1 - r0) * t / RIG_MAGIC_4096 174 c[3] = d0 + (d1 - d0) * t / RIG_MAGIC_4096 175 fr_disc(fb, zb, w, h, c) 176 s = s + 1 177 } 178 return 0 179} 180 181// project + draw the whole posed skeleton. Call after sk_update(sk). Clears fb (bg gradient) + zbuf first. 182// camyaw = orbit angle (rad*4096), scale = body-units->px numerator (screen px = worldunit*scale/1024). 183// zb = caller-allocated w*h i64 depth buffer (passed, not global -- keeps the renderer reentrant). 184func rig_draw(sk: i64, fb: *i64, zb: *i64, w: i64, h: i64, camyaw: i64, scale: i64) -> i64 { 185 let cy4: i64 = it_cos4096(camyaw) 186 let sy4: i64 = it_sin4096(camyaw) 187 let midy: i64 = h * 52 / 100 188 let hw: i64 = w / 2 189 // clear 190 var i: i64 = 0 191 let npx: i64 = w * h 192 while i < npx { 193 let yy: i64 = i / w 194 fb[i] = (20 + yy * 26 / h) + (24 + yy * 24 / h) * 256 + (38 + yy * 30 / h) * RIG_MAGIC_65536 195 zb[i] = 0 - RIG_MAGIC_2000000000 196 i = i + 1 197 } 198 // draw each bone parent->child (skip the root, which has no parent segment) 199 let sx: *i64 = sys_mmap(RIG_NB * 8) as *i64 200 let sy: *i64 = sys_mmap(RIG_NB * 8) as *i64 201 let sd: *i64 = sys_mmap(RIG_NB * 8) as *i64 202 var b: i64 = 0 203 while b < RIG_NB { 204 let bp: *i64 = sk_bone(sk, b) 205 // clamp world coords: the deep FK chain in fx256 can hit a worst-case matmul blow-up at some angle 206 // alignments; clamp keeps any such joint at the frame edge instead of streaking off to infinity 207 // (render stays clean). Higher-precision (fx1024) or renormalized FK is the refinement rung. 208 let wx: i64 = fr_clamp(bp[15], 0 - RIG_MAGIC_6000, RIG_MAGIC_6000) 209 let wy: i64 = fr_clamp(bp[16], 0 - RIG_MAGIC_6000, RIG_MAGIC_6000) 210 let wz: i64 = fr_clamp(bp[17], 0 - RIG_MAGIC_6000, RIG_MAGIC_6000) 211 let rx: i64 = (wx * cy4 + wz * sy4) / RIG_MAGIC_4096 212 let rz: i64 = (0 - wx * sy4 + wz * cy4) / RIG_MAGIC_4096 213 sx[b] = hw + rx * scale / RIG_MAGIC_1024 214 sy[b] = midy - wy * scale / RIG_MAGIC_1024 215 sd[b] = rz 216 b = b + 1 217 } 218 let e: *i64 = sys_mmap(8 * 8) as *i64 219 var j: i64 = 1 220 while j < RIG_NB { 221 let bpj: *i64 = sk_bone(sk, j) 222 let par: i64 = bpj[0] 223 let r0v: i64 = rig_r0(j) * scale / RIG_MAGIC_1024 224 let r1v: i64 = rig_r1(j) * scale / RIG_MAGIC_1024 225 let spar: i64 = sx[par] 226 let ypar: i64 = sy[par] 227 let dpar: i64 = sd[par] 228 let sj: i64 = sx[j] 229 let yj: i64 = sy[j] 230 let dj: i64 = sd[j] 231 e[0] = spar; e[1] = ypar; e[2] = dpar; e[3] = r0v 232 e[4] = sj; e[5] = yj; e[6] = dj; e[7] = r1v 233 fr_capsule(fb, zb, w, h, e) 234 j = j + 1 235 } 236 // head blob (bigger sphere at the head joint) 237 e[0] = sx[RG_HEAD]; e[1] = sy[RG_HEAD]; e[2] = 200 * scale / RIG_MAGIC_1024; e[3] = sd[RG_HEAD] 238 fr_disc(fb, zb, w, h, e) 239 return 0 240}