code wiki / (root) / nx_meshview_wasm.nx

nx_meshview_wasm.nx source

↩ module page · 1126 lines · 51080 B

1// nx_meshview_wasm.nx -- R3: the sovereign general-mesh renderer EMITTED to WASM (runs in ANY browser via OUR 2// software rasterizer -- zero WebGL, zero NVIDIA, zero Emscripten). Composes the HAL-free nx_render_core 3// (z-buffered Pineda raster + Q14 perspective/mat4/Bhaskara-trig) over a loaded vertex+index mesh held at fixed 4// linear-memory offsets (the proven nx_pets3d_wasm/nx_wasmcube wasm memory model: NO sys_mmap -- wasm has no 5// syscalls). The browser's ONLY job is to call mv_render(angle) and blit the framebuffer to a <canvas>; ALL 6// transform/projection/rasterization is sovereign Nishi. Mirrors the native nx_meshrender logic so the wasm 7// build is bit-faithful to native (proven by nx_meshview_wasm_gate via nx_wasm_vm). Compiles through the 8// SOVEREIGN chain: nx_compile_wat -> nx_wat_compiler -> .wasm. license_tier: ORIGINAL 9import "nx_render_core.nx" 10import "nx_vecmath.nx" 11 12const MV_W: i64 = 64 13const MV_H: i64 = 64 14const MV_N: i64 = 4096 // 64*64 15const VQ: i64 = 16384 // Q14 one (== RC_Q) 16const DIST: i64 = 65536 // 4.0 Q14: push the model down -Z 17const FOVH: i64 = 30 // half field-of-view degrees 18const FARQ: i64 = 16384000 // far plane Q14 19const BG: i64 = 4280295456 // r32 g32 b32 a255 20const RED: i64 = 4278190335 // r255 g0 b0 a255 21const BLU: i64 = 4294901760 // r0 g0 b255 a255 22 23// fixed BYTE offsets into wasm linear memory (all 8-byte aligned; total < 67KB, inside the default memory). 24const O_FB: i64 = 0 // MV_N i64 framebuffer (-> 32768) 25const O_ZB: i64 = 32768 // MV_N i64 z-buffer (-> 65536) 26const O_PROJ: i64 = 65536 // 16 i64 27const O_ROTY: i64 = 65664 28const O_MV: i64 = 65792 29const O_TRANS: i64 = 65920 30const O_MVP: i64 = 66048 31const O_VB: i64 = 66176 // 4 i64 vertex scratch 32const O_CLIP: i64 = 66208 // 4 i64 clip scratch 33const O_SCR: i64 = 66240 // 6 verts * 4 i64 (-> 66432) 34const O_TRI: i64 = 66432 // 12 i64 tri scratch (-> 66528) 35const O_VERTS: i64 = 66528 // 6 verts * 4 i64 (-> 66720) 36const O_IDX: i64 = 66720 // 2 tris * 3 i64 (-> 66768) 37// --- lit cube (CAP-PBR visual) --- 38const O_CVERTS: i64 = 66768 // 8 cube verts * 3 i64 (-> 66960) 39const O_CTRIS: i64 = 66960 // 12 tris * 7 i64 [i0,i1,i2,nx,ny,nz,color] (-> 67632) 40const O_CSCR: i64 = 67632 // 8 verts * 4 i64 projected scratch (-> 67888) 41const C_HALF: i64 = 9830 // 0.6 * Q14 cube half-extent 42const C_BASE: i64 = 4292004040 // base material (200,200,210,255) -- shading reveals the form 43// --- skinned (bending) cube: CAP-SKELETAL-ANIM visual --- 44const O_SKIN: i64 = 67888 // 8 skinned verts * 4 i64 (-> 68144) 45const O_M0: i64 = 68144 // bone0 mat4 (-> 68272) 46const O_M1: i64 = 68272 // bone1 mat4 (-> 68400) 47const O_REST: i64 = 68400 // rest vec4 scratch (-> 68432) 48const O_STMP: i64 = 68432 // skin tmp vec4 scratch (-> 68464) 49// --- smooth (Gouraud) lit+specular SPHERE: CAP-PBR per-vertex-normal shading (kills the faceted look) --- 50const SP_RINGS: i64 = 8 51const SP_SECT: i64 = 12 52const SP_NV: i64 = 117 // (RINGS+1)*(SECT+1) 53const SP_NT: i64 = 192 // RINGS*SECT*2 54const SP_R: i64 = 9830 // radius 0.6*Q 55const SPHERE_BASE: i64 = 4293299280 // base material (80,140,230,255) 56const O_SPV: i64 = 68480 // sphere verts SP_NV*3 (-> 71288) 57const O_SPN: i64 = 71288 // sphere normals SP_NV*3 (-> 74096) 58const O_SPC: i64 = 74096 // sphere per-vertex colours SP_NV (-> 75032) 59const O_SPS: i64 = 75032 // sphere projected scr SP_NV*4 (-> 78776) 60const O_SPI: i64 = 78776 // sphere indices SP_NT*3 (-> 83384) 61const O_PBUF: i64 = 83392 // phong tri buffer 28 i64 (-> 83616) 62// --- generic loaded/torus mesh buffers (sized for <=256 verts / 512 tris) --- 63const O_MVV: i64 = 83616 // mesh verts 256*3 (-> 89760) 64const O_MVN: i64 = 89760 // mesh normals 256*3 (-> 95904) 65const O_MVC: i64 = 95904 // mesh colours 256 (-> 97952) 66const O_MVS: i64 = 97952 // mesh projected 256*4 (-> 106144) 67const O_MVI: i64 = 106144 // mesh indices 512*3 (-> 118432) 68const MESH_BASE: i64 = 4282158310 // mesh material (230,140,60,255) -- orange 69const TR_SECT: i64 = 12 70const TR_RING: i64 = 8 71const TR_NV: i64 = 96 // SECT*RING 72const TR_NT: i64 = 192 // SECT*RING*2 73const TR_MAJ: i64 = 7000 // major (centre) radius, Q14 74const TR_MIN: i64 = 3600 // tube radius, Q14 (chunky donut, still a clear hole) 75 76func mv_w() -> i64 { return MV_W } 77func mv_h() -> i64 { return MV_H } 78func mv_fb_off() -> i64 { return O_FB } // byte offset of the framebuffer (JS reads it stride-8) 79 80// bake the demo z-test scene (RED triangle nearer + BLUE triangle farther, same screen footprint) into memory. 81func mv_scene() -> i64 { 82 let v: *i64 = (O_VERTS as i64) as *i64 83 v[0]=0-13107; v[1]=0-13107; v[2]=8192; v[3]=RED 84 v[4]=13107; v[5]=0-13107; v[6]=8192; v[7]=RED 85 v[8]=0; v[9]=13107; v[10]=8192; v[11]=RED 86 v[12]=0-13107; v[13]=0-13107; v[14]=0-8192; v[15]=BLU 87 v[16]=13107; v[17]=0-13107; v[18]=0-8192; v[19]=BLU 88 v[20]=0; v[21]=13107; v[22]=0-8192; v[23]=BLU 89 let ix: *i64 = (O_IDX as i64) as *i64 90 ix[0]=0; ix[1]=1; ix[2]=2; ix[3]=3; ix[4]=4; ix[5]=5 91 return 0 92} 93 94// project one model vertex (Q14) through mvp -> scr[base..base+3] = [screen_x, screen_y, depth, visible]. 95func mv_proj1(mvp: *i64, vx: i64, vy: i64, vz: i64, scr: *i64, base: i64) -> i64 { 96 let vb: *i64 = (O_VB as i64) as *i64 97 let cl: *i64 = (O_CLIP as i64) as *i64 98 vb[0]=vx; vb[1]=vy; vb[2]=vz; vb[3]=VQ 99 rc_mat4_vec4(mvp, vb, cl) 100 let cw: i64 = cl[3] 101 if cw <= 0 { scr[base+3]=0; return 0 } 102 scr[base+0] = (cl[0]*VQ/cw + VQ) * MV_W / (2*VQ) 103 scr[base+1] = (VQ - cl[1]*VQ/cw) * MV_H / (2*VQ) 104 scr[base+2] = cl[2]*VQ/cw + VQ 105 scr[base+3] = 1 106 return 0 107} 108 109// render the baked mesh at orbit angle (degrees). Returns triangles drawn. EXPORTED -> JS calls this per frame. 110func mv_render(angle: i64) -> i64 { 111 let fb: *i64 = (O_FB as i64) as *i64 112 let zb: *i64 = (O_ZB as i64) as *i64 113 rc_clear(fb, MV_W, MV_H, BG) 114 rc_zclear(zb, MV_W, MV_H) 115 mv_scene() 116 let proj: *i64 = (O_PROJ as i64) as *i64 117 let roty: *i64 = (O_ROTY as i64) as *i64 118 let mvm: *i64 = (O_MV as i64) as *i64 119 let trans: *i64 = (O_TRANS as i64) as *i64 120 let mvp: *i64 = (O_MVP as i64) as *i64 121 rc_perspective_cs(rc_cos_q14(FOVH), rc_sin_q14(FOVH), MV_W*VQ/MV_H, VQ, FARQ, proj) 122 let cc: i64 = rc_cos_q14(angle) 123 let ss: i64 = rc_sin_q14(angle) 124 var z: i64 = 0 125 while z < 16 { roty[z]=0; z=z+1 } 126 roty[0]=cc; roty[2]=ss; roty[5]=VQ; roty[8]=0-ss; roty[10]=cc; roty[15]=VQ 127 rc_translation_4x4(0, 0, 0-DIST, trans) 128 rc_mat4_mul(trans, roty, mvm) 129 rc_mat4_mul(proj, mvm, mvp) 130 let verts: *i64 = (O_VERTS as i64) as *i64 131 let idx: *i64 = (O_IDX as i64) as *i64 132 let scr: *i64 = (O_SCR as i64) as *i64 133 var vi: i64 = 0 134 while vi < 6 { mv_proj1(mvp, verts[vi*4+0], verts[vi*4+1], verts[vi*4+2], scr, vi*4); vi=vi+1 } 135 let tri: *i64 = (O_TRI as i64) as *i64 136 var ti: i64 = 0 137 var drawn: i64 = 0 138 while ti < 2 { 139 let a: i64 = idx[ti*3+0] 140 let b: i64 = idx[ti*3+1] 141 let c: i64 = idx[ti*3+2] 142 if scr[a*4+3]==1 { if scr[b*4+3]==1 { if scr[c*4+3]==1 { 143 tri[0]=scr[a*4+0]; tri[1]=scr[a*4+1]; tri[2]=scr[a*4+2]; tri[3]=verts[a*4+3] 144 tri[4]=scr[b*4+0]; tri[5]=scr[b*4+1]; tri[6]=scr[b*4+2]; tri[7]=verts[b*4+3] 145 tri[8]=scr[c*4+0]; tri[9]=scr[c*4+1]; tri[10]=scr[c*4+2]; tri[11]=verts[c*4+3] 146 rc_triangle(fb, zb, MV_W, MV_H, tri) 147 drawn=drawn+1 148 } } } 149 ti=ti+1 150 } 151 return drawn 152} 153 154// EXPORTED accessors so the host (and the wasm gate) can read results without knowing the memory layout. 155func mv_pixel(i: i64) -> i64 { let fb: *i64 = (O_FB as i64) as *i64; return fb[i] } 156func mv_coverage() -> i64 { let fb: *i64 = (O_FB as i64) as *i64; var k: i64=0; var i: i64=0; while i<MV_N { if fb[i]!=BG {k=k+1} i=i+1 } return k } 157 158// ===== CAP-PBR: lit cube (the visual payoff) -- inlined Lambert shading, mirrors nx_meshrender's mr_* ===== 159func mv_dot3(ax: i64, ay: i64, az: i64, bx: i64, by: i64, bz: i64) -> i64 { return (ax*bx + ay*by + az*bz) / VQ } 160func mv_lambert(nx: i64, ny: i64, nz: i64, lx: i64, ly: i64, lz: i64) -> i64 { let d: i64 = mv_dot3(nx,ny,nz,lx,ly,lz); if d < 0 { return 0 } return d } 161func mv_rotn(c: i64, s: i64, nx: i64, ny: i64, nz: i64, out: *i64) -> i64 { out[0] = (c*nx + s*nz)/VQ; out[1] = ny; out[2] = ((0-s)*nx + c*nz)/VQ; return 0 } 162func mv_shade(rgba: i64, b: i64, amb: i64) -> i64 { var bb: i64 = b; if bb < amb { bb = amb } let r: i64 = (rgba%256)*bb/VQ; let g: i64 = ((rgba/256)%256)*bb/VQ; let bl: i64 = ((rgba/65536)%256)*bb/VQ; return r + g*256 + bl*65536 + 255*16777216 } 163func mv_setv(p: *i64, k: i64, x: i64, y: i64, z: i64) -> i64 { p[k*3]=x; p[k*3+1]=y; p[k*3+2]=z; return 0 } 164func mv_sett(p: *i64, k: i64, a: i64, b: i64, c: i64, nx: i64, ny: i64, nz: i64, col: i64) -> i64 { p[k*7]=a; p[k*7+1]=b; p[k*7+2]=c; p[k*7+3]=nx; p[k*7+4]=ny; p[k*7+5]=nz; p[k*7+6]=col; return 0 } 165func mv_cube_scene() -> i64 { 166 let v: *i64 = (O_CVERTS as i64) as *i64 167 let H: i64 = C_HALF 168 mv_setv(v,0, 0-H,0-H,0-H); mv_setv(v,1, H,0-H,0-H); mv_setv(v,2, H,H,0-H); mv_setv(v,3, 0-H,H,0-H) 169 mv_setv(v,4, 0-H,0-H,H); mv_setv(v,5, H,0-H,H); mv_setv(v,6, H,H,H); mv_setv(v,7, 0-H,H,H) 170 let t: *i64 = (O_CTRIS as i64) as *i64 171 let Q: i64 = VQ 172 mv_sett(t,0, 0,1,2, 0,0,0-Q, C_BASE); mv_sett(t,1, 0,2,3, 0,0,0-Q, C_BASE) 173 mv_sett(t,2, 4,6,5, 0,0,Q, C_BASE); mv_sett(t,3, 4,7,6, 0,0,Q, C_BASE) 174 mv_sett(t,4, 0,3,7, 0-Q,0,0, C_BASE); mv_sett(t,5, 0,7,4, 0-Q,0,0, C_BASE) 175 mv_sett(t,6, 1,5,6, Q,0,0, C_BASE); mv_sett(t,7, 1,6,2, Q,0,0, C_BASE) 176 mv_sett(t,8, 0,4,5, 0,0-Q,0, C_BASE); mv_sett(t,9, 0,5,1, 0,0-Q,0, C_BASE) 177 mv_sett(t,10, 3,2,6, 0,Q,0, C_BASE); mv_sett(t,11, 3,6,7, 0,Q,0, C_BASE) 178 return 0 179} 180// render the lit cube at orbit angle (degrees). Per-face Lambert shading (light from above-front), z-buffered. EXPORTED. 181func mv_render_cube(angle: i64) -> i64 { 182 let fb: *i64 = (O_FB as i64) as *i64 183 let zb: *i64 = (O_ZB as i64) as *i64 184 rc_clear(fb, MV_W, MV_H, BG) 185 rc_zclear(zb, MV_W, MV_H) 186 mv_cube_scene() 187 let proj: *i64 = (O_PROJ as i64) as *i64 188 let roty: *i64 = (O_ROTY as i64) as *i64 189 let mvm: *i64 = (O_MV as i64) as *i64 190 let trans: *i64 = (O_TRANS as i64) as *i64 191 let mvp: *i64 = (O_MVP as i64) as *i64 192 rc_perspective_cs(rc_cos_q14(FOVH), rc_sin_q14(FOVH), MV_W*VQ/MV_H, VQ, FARQ, proj) 193 let cc: i64 = rc_cos_q14(angle) 194 let ss: i64 = rc_sin_q14(angle) 195 var z: i64 = 0 196 while z < 16 { roty[z]=0; z=z+1 } 197 roty[0]=cc; roty[2]=ss; roty[5]=VQ; roty[8]=0-ss; roty[10]=cc; roty[15]=VQ 198 rc_translation_4x4(0, 0, 0-DIST, trans) 199 rc_mat4_mul(trans, roty, mvm) 200 rc_mat4_mul(proj, mvm, mvp) 201 let verts: *i64 = (O_CVERTS as i64) as *i64 202 let scr: *i64 = (O_CSCR as i64) as *i64 203 var vi: i64 = 0 204 while vi < 8 { mv_proj1(mvp, verts[vi*3+0], verts[vi*3+1], verts[vi*3+2], scr, vi*4); vi=vi+1 } 205 let tri: *i64 = (O_TRI as i64) as *i64 206 let nrm: *i64 = (O_VB as i64) as *i64 // vbuf free after projection -> reuse for the rotated normal 207 let tris: *i64 = (O_CTRIS as i64) as *i64 208 let LX: i64 = 9459 209 let LY: i64 = 9459 210 let LZ: i64 = 9459 // unit light from upper-right-FRONT (camera side ~(0.577,0.577,0.577)) -> lights visible faces 211 var ti: i64 = 0 212 var drawn: i64 = 0 213 while ti < 12 { 214 let a: i64 = tris[ti*7+0] 215 let b: i64 = tris[ti*7+1] 216 let c: i64 = tris[ti*7+2] 217 var vis: i64 = 0 218 if scr[a*4+3]==1 { if scr[b*4+3]==1 { if scr[c*4+3]==1 { vis=1 } } } 219 if vis==1 { 220 mv_rotn(cc, ss, tris[ti*7+3], tris[ti*7+4], tris[ti*7+5], nrm) 221 if nrm[2] > 0 { // BACK-FACE CULL: render only faces whose normal points at the camera (+Z) 222 let br: i64 = mv_lambert(nrm[0], nrm[1], nrm[2], LX, LY, LZ) 223 let col: i64 = mv_shade(tris[ti*7+6], br, 2048) 224 tri[0]=scr[a*4+0]; tri[1]=scr[a*4+1]; tri[2]=scr[a*4+2] 225 tri[3]=scr[b*4+0]; tri[4]=scr[b*4+1]; tri[5]=scr[b*4+2] 226 tri[6]=scr[c*4+0]; tri[7]=scr[c*4+1]; tri[8]=scr[c*4+2] 227 tri[9]=col 228 rc_triangle_flat(fb, zb, MV_W, MV_H, tri) 229 drawn=drawn+1 230 } 231 } 232 ti=ti+1 233 } 234 return drawn 235} 236 237// ===== CAP-SKELETAL-ANIM visual: a cube whose TOP HALF is bound to a bone that rotates -> the mesh BENDS (skinning live) ===== 238func mv_rotz(deg: i64, out: *i64) -> i64 { 239 let c: i64 = rc_cos_q14(deg) 240 let s: i64 = rc_sin_q14(deg) 241 var i: i64 = 0 242 while i < 16 { out[i]=0; i=i+1 } 243 out[0]=c; out[1]=0-s; out[4]=s; out[5]=c; out[10]=VQ; out[15]=VQ 244 return 0 245} 246// 2-bone linear blend skinning: ob[obase..obase+3] = w0*(m0*rest) + w1*(m1*rest). (mirrors nx_meshrender mr_skin2.) 247func mv_skin2(rest: *i64, m0: *i64, w0: i64, m1: *i64, w1: i64, tmp: *i64, ob: *i64, obase: i64) -> i64 { 248 rc_mat4_vec4(m0, rest, tmp) 249 ob[obase+0]=w0*tmp[0]/VQ; ob[obase+1]=w0*tmp[1]/VQ; ob[obase+2]=w0*tmp[2]/VQ; ob[obase+3]=w0*tmp[3]/VQ 250 rc_mat4_vec4(m1, rest, tmp) 251 ob[obase+0]=ob[obase+0]+w1*tmp[0]/VQ; ob[obase+1]=ob[obase+1]+w1*tmp[1]/VQ; ob[obase+2]=ob[obase+2]+w1*tmp[2]/VQ; ob[obase+3]=ob[obase+3]+w1*tmp[3]/VQ 252 return 0 253} 254// 6 distinct face colours so the bend reads clearly (computed, no magic packed values). 255func mv_facecol(f: i64) -> i64 { 256 var r: i64=200; var g: i64=200; var b: i64=200 257 if f==0 { r=235; g=70; b=60 } 258 if f==1 { r=70; g=200; b=90 } 259 if f==2 { r=70; g=120; b=235 } 260 if f==3 { r=235; g=205; b=70 } 261 if f==4 { r=210; g=90; b=200 } 262 if f==5 { r=80; g=205; b=210 } 263 return r + g*256 + b*65536 + 255*16777216 264} 265// ===== skinned-normal lighting (mirrors nx_meshrender mr_isqrt/mr_face_lambert) ===== 266// brightness from the ACTUAL (skinned/deformed) face geometry: the normal is the cross product of the live edges, 267// so lighting is CORRECT under deformation with NO pre-baked normal. The native math is proven by nx_skinnorm_gate (6/6). 268func mv_isqrt(n: i64) -> i64 { return vm_isqrt(n) } 269func mv_face_lambert(p0x: i64, p0y: i64, p0z: i64, p1x: i64, p1y: i64, p1z: i64, p2x: i64, p2y: i64, p2z: i64, lx: i64, ly: i64, lz: i64) -> i64 { 270 let e1x: i64=p1x-p0x; let e1y: i64=p1y-p0y; let e1z: i64=p1z-p0z 271 let e2x: i64=p2x-p0x; let e2y: i64=p2y-p0y; let e2z: i64=p2z-p0z 272 let nx: i64=e1y*e2z - e1z*e2y 273 let ny: i64=e1z*e2x - e1x*e2z 274 let nz: i64=e1x*e2y - e1y*e2x 275 let dotnl: i64=nx*lx + ny*ly + nz*lz 276 if dotnl <= 0 { return 0 } 277 let mag2: i64=nx*nx + ny*ny + nz*nz 278 if mag2 <= 0 { return 0 } 279 let m: i64=mv_isqrt(mag2) 280 if m <= 0 { return 0 } 281 return dotnl / m 282} 283// ===== CAP-PBR rung-2: Blinn-Phong specular (mirrors nx_meshrender mr_pow_q14/mr_face_specular) ===== 284// proven natively by nx_spec_gate (6/6). Glossy highlight from the (skinned/deformed) face normal + half-vector. 285func mv_pow_q14(base: i64, n: i64) -> i64 { 286 var r: i64 = VQ 287 var i: i64 = 0 288 while i < n { 289 r = r * base / VQ 290 i = i + 1 291 } 292 return r 293} 294func mv_face_specular(p0x: i64, p0y: i64, p0z: i64, p1x: i64, p1y: i64, p1z: i64, p2x: i64, p2y: i64, p2z: i64, lx: i64, ly: i64, lz: i64, vx: i64, vy: i64, vz: i64, shin: i64) -> i64 { 295 let e1x: i64=p1x-p0x; let e1y: i64=p1y-p0y; let e1z: i64=p1z-p0z 296 let e2x: i64=p2x-p0x; let e2y: i64=p2y-p0y; let e2z: i64=p2z-p0z 297 let nx: i64=e1y*e2z - e1z*e2y 298 let ny: i64=e1z*e2x - e1x*e2z 299 let nz: i64=e1x*e2y - e1y*e2x 300 let nmag2: i64=nx*nx + ny*ny + nz*nz 301 if nmag2 <= 0 { return 0 } 302 let nmag: i64=mv_isqrt(nmag2) 303 if nmag <= 0 { return 0 } 304 let hx: i64=lx+vx; let hy: i64=ly+vy; let hz: i64=lz+vz 305 let hmag2: i64=hx*hx + hy*hy + hz*hz 306 if hmag2 <= 0 { return 0 } 307 let hmag: i64=mv_isqrt(hmag2) 308 if hmag <= 0 { return 0 } 309 let unx: i64=nx*VQ/nmag; let uny: i64=ny*VQ/nmag; let unz: i64=nz*VQ/nmag 310 let uhx: i64=hx*VQ/hmag; let uhy: i64=hy*VQ/hmag; let uhz: i64=hz*VQ/hmag 311 let ndoth: i64=(unx*uhx + uny*uhy + unz*uhz) / VQ 312 if ndoth <= 0 { return 0 } 313 var nd: i64=ndoth 314 if nd > VQ { nd = VQ } 315 return mv_pow_q14(nd, shin) 316} 317// add a white specular highlight of intensity sp [0,VQ] onto a packed rgba (each channel clamped to 255). 318func mv_add_spec(rgba: i64, sp: i64) -> i64 { 319 let add: i64 = sp * 255 / VQ 320 var r: i64 = rgba % 256 321 var g: i64 = (rgba / 256) % 256 322 var b: i64 = (rgba / 65536) % 256 323 r = r + add; if r > 255 { r = 255 } 324 g = g + add; if g > 255 { g = 255 } 325 b = b + add; if b > 255 { b = 255 } 326 return r + g*256 + b*65536 + 255*16777216 327} 328// render the bending cube. frame -> joint bend angle (oscillates +-50deg); top verts (y>0) follow bone1=rotZ(bend), 329// bottom verts (y<0) stay on bone0=identity -> linear blend skinning deforms the cube. Fixed 3/4 view. z-buffered. EXPORTED. 330// shared skinning + projection for the 3 skincube renderers (flat/lit/spec): clears fb/zb, skins the 8 cube verts 331// by the frame's bend into O_SKIN, and projects them (fixed 25deg view) into O_CSCR. The renderers then differ ONLY 332// in the per-triangle shading (rule 15 DRY -- this was 3x duplicated setup). 333func mv_skincube_xform(frame: i64) -> i64 { 334 let fb: *i64 = (O_FB as i64) as *i64 335 let zb: *i64 = (O_ZB as i64) as *i64 336 rc_clear(fb, MV_W, MV_H, BG) 337 rc_zclear(zb, MV_W, MV_H) 338 mv_cube_scene() 339 let m0: *i64 = (O_M0 as i64) as *i64 340 let m1: *i64 = (O_M1 as i64) as *i64 341 rc_identity_4x4(m0) 342 let bend: i64 = rc_sin_q14(frame*3) * 50 / VQ // oscillate the joint +-50 degrees 343 mv_rotz(bend, m1) 344 let cv: *i64 = (O_CVERTS as i64) as *i64 345 let sk: *i64 = (O_SKIN as i64) as *i64 346 let rest: *i64 = (O_REST as i64) as *i64 347 let stmp: *i64 = (O_STMP as i64) as *i64 348 var vi: i64 = 0 349 while vi < 8 { 350 var w0: i64 = 0 351 if cv[vi*3+1] < 0 { w0 = VQ } // bottom half -> bone0 (static); top half -> bone1 (bend) 352 let w1: i64 = VQ - w0 353 rest[0]=cv[vi*3+0]; rest[1]=cv[vi*3+1]; rest[2]=cv[vi*3+2]; rest[3]=VQ 354 mv_skin2(rest, m0, w0, m1, w1, stmp, sk, vi*4) 355 vi=vi+1 356 } 357 let proj: *i64 = (O_PROJ as i64) as *i64 358 let roty: *i64 = (O_ROTY as i64) as *i64 359 let mvm: *i64 = (O_MV as i64) as *i64 360 let trans: *i64 = (O_TRANS as i64) as *i64 361 let mvp: *i64 = (O_MVP as i64) as *i64 362 rc_perspective_cs(rc_cos_q14(FOVH), rc_sin_q14(FOVH), MV_W*VQ/MV_H, VQ, FARQ, proj) 363 let cc: i64 = rc_cos_q14(25) 364 let ss: i64 = rc_sin_q14(25) 365 var z: i64 = 0 366 while z < 16 { roty[z]=0; z=z+1 } 367 roty[0]=cc; roty[2]=ss; roty[5]=VQ; roty[8]=0-ss; roty[10]=cc; roty[15]=VQ 368 rc_translation_4x4(0, 0, 0-DIST, trans) 369 rc_mat4_mul(trans, roty, mvm) 370 rc_mat4_mul(proj, mvm, mvp) 371 let scr: *i64 = (O_CSCR as i64) as *i64 372 var pi: i64 = 0 373 while pi < 8 { mv_proj1(mvp, sk[pi*4+0], sk[pi*4+1], sk[pi*4+2], scr, pi*4); pi=pi+1 } 374 return 0 375} 376// render the bending cube, FLAT per-face colours. EXPORTED. 377func mv_render_skincube(frame: i64) -> i64 { 378 mv_skincube_xform(frame) 379 let fb: *i64 = (O_FB as i64) as *i64 380 let zb: *i64 = (O_ZB as i64) as *i64 381 let scr: *i64 = (O_CSCR as i64) as *i64 382 let tri: *i64 = (O_TRI as i64) as *i64 383 let tris: *i64 = (O_CTRIS as i64) as *i64 384 var ti: i64 = 0 385 var drawn: i64 = 0 386 while ti < 12 { 387 let a: i64 = tris[ti*7+0] 388 let b: i64 = tris[ti*7+1] 389 let c: i64 = tris[ti*7+2] 390 var vis: i64 = 0 391 if scr[a*4+3]==1 { if scr[b*4+3]==1 { if scr[c*4+3]==1 { vis=1 } } } 392 if vis==1 { 393 let col: i64 = mv_facecol(ti/2) 394 tri[0]=scr[a*4+0]; tri[1]=scr[a*4+1]; tri[2]=scr[a*4+2] 395 tri[3]=scr[b*4+0]; tri[4]=scr[b*4+1]; tri[5]=scr[b*4+2] 396 tri[6]=scr[c*4+0]; tri[7]=scr[c*4+1]; tri[8]=scr[c*4+2] 397 tri[9]=col 398 rc_triangle_flat(fb, zb, MV_W, MV_H, tri) 399 drawn=drawn+1 400 } 401 ti=ti+1 402 } 403 return drawn 404} 405 406// CAP-SKELETAL-ANIM rung-2 visual: the SAME bending cube, now LIT from its SKINNED normals. Each frame the face 407// normal is recomputed (cross product of the live skinned edges) via mv_face_lambert, so the lighting is CORRECT 408// under the bend deformation -- no pre-baked normal (the gap the flat skincube left open). Verts are passed a,c,b 409// so the cross product yields the OUTWARD normal (the cube winding is inward). Light = upper-right-FRONT (model 410// space). mv_shade attenuates the face colour by the Lambert term (ambient floor 2048). z-buffered. EXPORTED. 411func mv_render_skincube_lit(frame: i64) -> i64 { 412 mv_skincube_xform(frame) 413 let fb: *i64 = (O_FB as i64) as *i64 414 let zb: *i64 = (O_ZB as i64) as *i64 415 let scr: *i64 = (O_CSCR as i64) as *i64 416 let sk: *i64 = (O_SKIN as i64) as *i64 417 let tri: *i64 = (O_TRI as i64) as *i64 418 let tris: *i64 = (O_CTRIS as i64) as *i64 419 let LX: i64 = 8192 420 let LY: i64 = 8192 421 let LZ: i64 = 11585 422 var ti: i64 = 0 423 var drawn: i64 = 0 424 while ti < 12 { 425 let a: i64 = tris[ti*7+0] 426 let b: i64 = tris[ti*7+1] 427 let c: i64 = tris[ti*7+2] 428 var vis: i64 = 0 429 if scr[a*4+3]==1 { if scr[b*4+3]==1 { if scr[c*4+3]==1 { vis=1 } } } 430 if vis==1 { 431 let br: i64 = mv_face_lambert(sk[a*4+0],sk[a*4+1],sk[a*4+2], sk[c*4+0],sk[c*4+1],sk[c*4+2], sk[b*4+0],sk[b*4+1],sk[b*4+2], LX,LY,LZ) 432 let col: i64 = mv_shade(mv_facecol(ti/2), br, 2048) 433 tri[0]=scr[a*4+0]; tri[1]=scr[a*4+1]; tri[2]=scr[a*4+2] 434 tri[3]=scr[b*4+0]; tri[4]=scr[b*4+1]; tri[5]=scr[b*4+2] 435 tri[6]=scr[c*4+0]; tri[7]=scr[c*4+1]; tri[8]=scr[c*4+2] 436 tri[9]=col 437 rc_triangle_flat(fb, zb, MV_W, MV_H, tri) 438 drawn=drawn+1 439 } 440 ti=ti+1 441 } 442 return drawn 443} 444 445// CAP-PBR rung-2 visual: the lit bending cube + a Blinn-Phong SPECULAR highlight (mv_face_specular over the skinned 446// normal + the model-space view dir) -> a GLOSSY cube. Specular recomputed per pose so the highlight tracks the bend. 447// Verts a,c,b for the OUTWARD normal (cube winding is inward); V = the camera dir in model space (undo the 25deg view 448// turn). z-buffered. EXPORTED. 449func mv_render_skincube_spec(frame: i64) -> i64 { 450 mv_skincube_xform(frame) 451 let fb: *i64 = (O_FB as i64) as *i64 452 let zb: *i64 = (O_ZB as i64) as *i64 453 let scr: *i64 = (O_CSCR as i64) as *i64 454 let sk: *i64 = (O_SKIN as i64) as *i64 455 let tri: *i64 = (O_TRI as i64) as *i64 456 let tris: *i64 = (O_CTRIS as i64) as *i64 457 let LX: i64 = 8192 458 let LY: i64 = 8192 459 let LZ: i64 = 11585 460 let VX: i64 = 0 - rc_sin_q14(25) 461 let VY: i64 = 0 462 let VZ: i64 = rc_cos_q14(25) 463 var ti: i64 = 0 464 var drawn: i64 = 0 465 while ti < 12 { 466 let a: i64 = tris[ti*7+0] 467 let b: i64 = tris[ti*7+1] 468 let c: i64 = tris[ti*7+2] 469 var vis: i64 = 0 470 if scr[a*4+3]==1 { if scr[b*4+3]==1 { if scr[c*4+3]==1 { vis=1 } } } 471 if vis==1 { 472 let br: i64 = mv_face_lambert(sk[a*4+0],sk[a*4+1],sk[a*4+2], sk[c*4+0],sk[c*4+1],sk[c*4+2], sk[b*4+0],sk[b*4+1],sk[b*4+2], LX,LY,LZ) 473 var col: i64 = mv_shade(mv_facecol(ti/2), br, 2048) 474 let sp: i64 = mv_face_specular(sk[a*4+0],sk[a*4+1],sk[a*4+2], sk[c*4+0],sk[c*4+1],sk[c*4+2], sk[b*4+0],sk[b*4+1],sk[b*4+2], LX,LY,LZ, VX,VY,VZ, 16) 475 col = mv_add_spec(col, sp) 476 tri[0]=scr[a*4+0]; tri[1]=scr[a*4+1]; tri[2]=scr[a*4+2] 477 tri[3]=scr[b*4+0]; tri[4]=scr[b*4+1]; tri[5]=scr[b*4+2] 478 tri[6]=scr[c*4+0]; tri[7]=scr[c*4+1]; tri[8]=scr[c*4+2] 479 tri[9]=col 480 rc_triangle_flat(fb, zb, MV_W, MV_H, tri) 481 drawn=drawn+1 482 } 483 ti=ti+1 484 } 485 return drawn 486} 487 488// ===== CAP-PBR rung-3: smooth Gouraud shading on a curved mesh (sphere) -- per-VERTEX unit normals + the 489// rc_triangle barycentric colour interpolation = a ROUND, smoothly-shaded ball (no facets). ===== 490// specular from a unit normal directly (per-vertex Gouraud; no cross product needed -- the normal IS the geometry). 491func mv_normal_specular(nx: i64, ny: i64, nz: i64, lx: i64, ly: i64, lz: i64, vx: i64, vy: i64, vz: i64, shin: i64) -> i64 { 492 let hx: i64=lx+vx; let hy: i64=ly+vy; let hz: i64=lz+vz 493 let hmag2: i64=hx*hx + hy*hy + hz*hz 494 if hmag2 <= 0 { return 0 } 495 let hmag: i64=mv_isqrt(hmag2) 496 if hmag <= 0 { return 0 } 497 let uhx: i64=hx*VQ/hmag; let uhy: i64=hy*VQ/hmag; let uhz: i64=hz*VQ/hmag 498 let ndoth: i64=(nx*uhx + ny*uhy + nz*uhz) / VQ 499 if ndoth <= 0 { return 0 } 500 var nd: i64=ndoth 501 if nd > VQ { nd = VQ } 502 return mv_pow_q14(nd, shin) 503} 504// build the UV sphere: per-vertex unit normal (= position/R) + position + the triangle indices (poles -> a few 505// degenerate tris that raster nothing; harmless). 506func mv_sphere_scene() -> i64 { 507 let v: *i64 = (O_SPV as i64) as *i64 508 let nrm: *i64 = (O_SPN as i64) as *i64 509 var i: i64 = 0 510 while i <= SP_RINGS { 511 let lat: i64 = i * 180 / SP_RINGS 512 let clat: i64 = rc_cos_q14(lat) 513 let slat: i64 = rc_sin_q14(lat) 514 var j: i64 = 0 515 while j <= SP_SECT { 516 let lon: i64 = j * 360 / SP_SECT 517 let clon: i64 = rc_cos_q14(lon) 518 let slon: i64 = rc_sin_q14(lon) 519 let nxx: i64 = slat * clon / VQ 520 let nyy: i64 = clat 521 let nzz: i64 = slat * slon / VQ 522 let idx: i64 = i * (SP_SECT + 1) + j 523 nrm[idx*3+0]=nxx; nrm[idx*3+1]=nyy; nrm[idx*3+2]=nzz 524 v[idx*3+0]=nxx*SP_R/VQ; v[idx*3+1]=nyy*SP_R/VQ; v[idx*3+2]=nzz*SP_R/VQ 525 j = j + 1 526 } 527 i = i + 1 528 } 529 let id: *i64 = (O_SPI as i64) as *i64 530 var ti: i64 = 0 531 var ri: i64 = 0 532 while ri < SP_RINGS { 533 var si: i64 = 0 534 while si < SP_SECT { 535 let a: i64 = ri * (SP_SECT+1) + si 536 let bb: i64 = (ri+1) * (SP_SECT+1) + si 537 let cd: i64 = (ri+1) * (SP_SECT+1) + (si+1) 538 let dd: i64 = ri * (SP_SECT+1) + (si+1) 539 id[ti*3+0]=a; id[ti*3+1]=bb; id[ti*3+2]=cd; ti=ti+1 540 id[ti*3+0]=a; id[ti*3+1]=cd; id[ti*3+2]=dd; ti=ti+1 541 si = si + 1 542 } 543 ri = ri + 1 544 } 545 return 0 546} 547// prep: scene + static view mvp + per-vertex Gouraud colour (lit+spec, with the LIGHT orbited by `angle` so a glint 548// sweeps the static sphere) -> O_SPC, and projected verts -> O_SPS. 549// shared sphere geometry (angle-independent: the sphere is STATIC, only the light orbits): scene + static-view mvp 550// + project all verts -> O_SPS. Used by the Gouraud/flat prep AND the Phong renderer (rule 15 DRY). 551func mv_sphere_xform() -> i64 { 552 mv_sphere_scene() 553 let proj: *i64 = (O_PROJ as i64) as *i64 554 let trans: *i64 = (O_TRANS as i64) as *i64 555 let mvp: *i64 = (O_MVP as i64) as *i64 556 rc_perspective_cs(rc_cos_q14(FOVH), rc_sin_q14(FOVH), MV_W*VQ/MV_H, VQ, FARQ, proj) 557 rc_translation_4x4(0, 0, 0-DIST, trans) 558 rc_mat4_mul(proj, trans, mvp) 559 let v: *i64 = (O_SPV as i64) as *i64 560 let scr: *i64 = (O_SPS as i64) as *i64 561 var k: i64 = 0 562 while k < SP_NV { mv_proj1(mvp, v[k*3+0], v[k*3+1], v[k*3+2], scr, k*4); k=k+1 } 563 return 0 564} 565// ===== R4: GENERIC arbitrary-mesh renderer (the in-browser STL-viewer core) ===== 566// renders ANY triangle mesh held in caller-supplied linear-memory buffers: per-vertex unit normals -> Gouraud lit+ 567// spec colours, static view, light orbited by `angle`. vbuf/nbuf/cbuf/sbuf/ibuf = byte offsets; nverts/ntris = 568// counts; base = material rgba; mode 0 = smooth Gouraud (rc_triangle), mode 1 = flat (rc_triangle_flat). The sphere 569// + torus + (next) a loaded STL all route through THIS one function (rule 15 DRY). EXPORTED accessors below let a 570// host write geometry into the buffers. Returns triangles drawn. 571func mv_render_mesh(angle: i64, vbuf: i64, nbuf: i64, cbuf: i64, sbuf: i64, ibuf: i64, nverts: i64, ntris: i64, base: i64, mode: i64) -> i64 { 572 let fb: *i64 = (O_FB as i64) as *i64 573 let zb: *i64 = (O_ZB as i64) as *i64 574 rc_clear(fb, MV_W, MV_H, BG) 575 rc_zclear(zb, MV_W, MV_H) 576 let proj: *i64 = (O_PROJ as i64) as *i64 577 let trans: *i64 = (O_TRANS as i64) as *i64 578 let mvp: *i64 = (O_MVP as i64) as *i64 579 rc_perspective_cs(rc_cos_q14(FOVH), rc_sin_q14(FOVH), MV_W*VQ/MV_H, VQ, FARQ, proj) 580 rc_translation_4x4(0, 0, 0-DIST, trans) 581 rc_mat4_mul(proj, trans, mvp) 582 let cc: i64 = rc_cos_q14(angle) 583 let ss: i64 = rc_sin_q14(angle) 584 let LX: i64 = (cc*8192 + ss*11585) / VQ 585 let LY: i64 = 8192 586 let LZ: i64 = ((0-ss)*8192 + cc*11585) / VQ 587 let v: *i64 = (vbuf as i64) as *i64 588 let nrm: *i64 = (nbuf as i64) as *i64 589 let col: *i64 = (cbuf as i64) as *i64 590 let scr: *i64 = (sbuf as i64) as *i64 591 var k: i64 = 0 592 while k < nverts { 593 let nxx: i64 = nrm[k*3+0] 594 let nyy: i64 = nrm[k*3+1] 595 let nzz: i64 = nrm[k*3+2] 596 let br: i64 = mv_lambert(nxx,nyy,nzz, LX,LY,LZ) 597 var c0: i64 = mv_shade(base, br, 2048) 598 let sp: i64 = mv_normal_specular(nxx,nyy,nzz, LX,LY,LZ, 0,0,VQ, 24) 599 c0 = mv_add_spec(c0, sp) 600 col[k] = c0 601 mv_proj1(mvp, v[k*3+0], v[k*3+1], v[k*3+2], scr, k*4) 602 k = k + 1 603 } 604 let id: *i64 = (ibuf as i64) as *i64 605 let tri: *i64 = (O_TRI as i64) as *i64 606 var ti: i64 = 0 607 var drawn: i64 = 0 608 while ti < ntris { 609 let a: i64 = id[ti*3+0] 610 let b: i64 = id[ti*3+1] 611 let c: i64 = id[ti*3+2] 612 var vis: i64 = 0 613 if scr[a*4+3]==1 { if scr[b*4+3]==1 { if scr[c*4+3]==1 { vis=1 } } } 614 if vis==1 { 615 if mode==0 { 616 tri[0]=scr[a*4+0]; tri[1]=scr[a*4+1]; tri[2]=scr[a*4+2]; tri[3]=col[a] 617 tri[4]=scr[b*4+0]; tri[5]=scr[b*4+1]; tri[6]=scr[b*4+2]; tri[7]=col[b] 618 tri[8]=scr[c*4+0]; tri[9]=scr[c*4+1]; tri[10]=scr[c*4+2]; tri[11]=col[c] 619 rc_triangle(fb, zb, MV_W, MV_H, tri) 620 } 621 if mode==1 { 622 tri[0]=scr[a*4+0]; tri[1]=scr[a*4+1]; tri[2]=scr[a*4+2] 623 tri[3]=scr[b*4+0]; tri[4]=scr[b*4+1]; tri[5]=scr[b*4+2] 624 tri[6]=scr[c*4+0]; tri[7]=scr[c*4+1]; tri[8]=scr[c*4+2] 625 tri[9]=col[a] 626 rc_triangle_flat(fb, zb, MV_W, MV_H, tri) 627 } 628 drawn=drawn+1 629 } 630 ti=ti+1 631 } 632 return drawn 633} 634// byte offsets of the generic mesh buffers (a host writes geometry here, then calls mv_render_loaded). EXPORTED. 635func mv_meshv_off() -> i64 { return O_MVV } 636func mv_meshn_off() -> i64 { return O_MVN } 637func mv_meshi_off() -> i64 { return O_MVI } 638// build a low-poly UV TORUS (genus-1 -- a topology a cube/sphere can't fake) into the generic mesh buffers. 639func mv_torus_scene() -> i64 { 640 let v: *i64 = (O_MVV as i64) as *i64 641 let nrm: *i64 = (O_MVN as i64) as *i64 642 var i: i64 = 0 643 while i < TR_SECT { 644 let u: i64 = i * 360 / TR_SECT 645 let cu: i64 = rc_cos_q14(u) 646 let su: i64 = rc_sin_q14(u) 647 var j: i64 = 0 648 while j < TR_RING { 649 let vv: i64 = j * 360 / TR_RING 650 let cvv: i64 = rc_cos_q14(vv) 651 let svv: i64 = rc_sin_q14(vv) 652 let rr: i64 = TR_MAJ + TR_MIN * cvv / VQ 653 let idx: i64 = i * TR_RING + j 654 v[idx*3+0] = rr * cu / VQ 655 v[idx*3+1] = TR_MIN * svv / VQ 656 v[idx*3+2] = rr * su / VQ 657 nrm[idx*3+0] = cvv * cu / VQ 658 nrm[idx*3+1] = svv 659 nrm[idx*3+2] = cvv * su / VQ 660 j = j + 1 661 } 662 i = i + 1 663 } 664 let id: *i64 = (O_MVI as i64) as *i64 665 var ti: i64 = 0 666 var si: i64 = 0 667 while si < TR_SECT { 668 let si2: i64 = (si + 1) % TR_SECT 669 var ri: i64 = 0 670 while ri < TR_RING { 671 let ri2: i64 = (ri + 1) % TR_RING 672 let a: i64 = si * TR_RING + ri 673 let b: i64 = si2 * TR_RING + ri 674 let c: i64 = si2 * TR_RING + ri2 675 let d: i64 = si * TR_RING + ri2 676 id[ti*3+0]=a; id[ti*3+1]=b; id[ti*3+2]=c; ti=ti+1 677 id[ti*3+0]=a; id[ti*3+1]=c; id[ti*3+2]=d; ti=ti+1 678 ri = ri + 1 679 } 680 si = si + 1 681 } 682 return 0 683} 684// render the torus via the generic mesh renderer. EXPORTED. 685func mv_render_torus(angle: i64) -> i64 { 686 mv_torus_scene() 687 return mv_render_mesh(angle, O_MVV, O_MVN, O_MVC, O_MVS, O_MVI, TR_NV, TR_NT, MESH_BASE, 0) 688} 689// render whatever geometry a host has written into the generic buffers (nverts/ntris supplied). EXPORTED. 690func mv_render_loaded(angle: i64, nverts: i64, ntris: i64) -> i64 { 691 return mv_render_mesh(angle, O_MVV, O_MVN, O_MVC, O_MVS, O_MVI, nverts, ntris, MESH_BASE, 0) 692} 693// Track-E rung-1: INTERACTIVE ORBIT VIEWPORT. The CAMERA orbits the loaded mesh -- model rotated by `yaw` (about Y) 694// and pushed `dist` down -Z (zoom), so the object spins on screen + scales as the page drives yaw from mouse-drag 695// and dist from scroll. The per-vertex normal is rotated by yaw too, so the world-fixed light stays put while you 696// orbit (correct orbit-camera lighting). Gouraud, z-buffered. EXPORTED. (CLEANUP DEBT: the per-vertex shade+project 697// and raster loops mirror mv_render_mesh -> extract mv_shade_project_verts + mv_raster_indexed next, route both.) 698func mv_render_loaded_cam(yaw: i64, dist: i64, nverts: i64, ntris: i64) -> i64 { 699 let fb: *i64 = (O_FB as i64) as *i64 700 let zb: *i64 = (O_ZB as i64) as *i64 701 rc_clear(fb, MV_W, MV_H, BG) 702 rc_zclear(zb, MV_W, MV_H) 703 let proj: *i64 = (O_PROJ as i64) as *i64 704 let roty: *i64 = (O_ROTY as i64) as *i64 705 let mvm: *i64 = (O_MV as i64) as *i64 706 let trans: *i64 = (O_TRANS as i64) as *i64 707 let mvp: *i64 = (O_MVP as i64) as *i64 708 rc_perspective_cs(rc_cos_q14(FOVH), rc_sin_q14(FOVH), MV_W*VQ/MV_H, VQ, FARQ, proj) 709 let cc: i64 = rc_cos_q14(yaw) 710 let ss: i64 = rc_sin_q14(yaw) 711 var z: i64 = 0 712 while z < 16 { roty[z]=0; z=z+1 } 713 roty[0]=cc; roty[2]=ss; roty[5]=VQ; roty[8]=0-ss; roty[10]=cc; roty[15]=VQ 714 rc_translation_4x4(0, 0, 0-dist, trans) 715 rc_mat4_mul(trans, roty, mvm) 716 rc_mat4_mul(proj, mvm, mvp) 717 let LX: i64 = 8192 718 let LY: i64 = 8192 719 let LZ: i64 = 11585 720 let v: *i64 = (O_MVV as i64) as *i64 721 let nrm: *i64 = (O_MVN as i64) as *i64 722 let col: *i64 = (O_MVC as i64) as *i64 723 let scr: *i64 = (O_MVS as i64) as *i64 724 var k: i64 = 0 725 while k < nverts { 726 let rnx: i64 = (cc*nrm[k*3+0] + ss*nrm[k*3+2]) / VQ 727 let rny: i64 = nrm[k*3+1] 728 let rnz: i64 = ((0-ss)*nrm[k*3+0] + cc*nrm[k*3+2]) / VQ 729 let br: i64 = mv_lambert(rnx,rny,rnz, LX,LY,LZ) 730 var c0: i64 = mv_shade(MESH_BASE, br, 2048) 731 let sp: i64 = mv_normal_specular(rnx,rny,rnz, LX,LY,LZ, 0,0,VQ, 24) 732 c0 = mv_add_spec(c0, sp) 733 col[k] = c0 734 mv_proj1(mvp, v[k*3+0], v[k*3+1], v[k*3+2], scr, k*4) 735 k = k + 1 736 } 737 let id: *i64 = (O_MVI as i64) as *i64 738 let tri: *i64 = (O_TRI as i64) as *i64 739 var ti: i64 = 0 740 var drawn: i64 = 0 741 while ti < ntris { 742 let a: i64 = id[ti*3+0] 743 let b: i64 = id[ti*3+1] 744 let c: i64 = id[ti*3+2] 745 var vis: i64 = 0 746 if scr[a*4+3]==1 { if scr[b*4+3]==1 { if scr[c*4+3]==1 { vis=1 } } } 747 if vis==1 { 748 tri[0]=scr[a*4+0]; tri[1]=scr[a*4+1]; tri[2]=scr[a*4+2]; tri[3]=col[a] 749 tri[4]=scr[b*4+0]; tri[5]=scr[b*4+1]; tri[6]=scr[b*4+2]; tri[7]=col[b] 750 tri[8]=scr[c*4+0]; tri[9]=scr[c*4+1]; tri[10]=scr[c*4+2]; tri[11]=col[c] 751 rc_triangle(fb, zb, MV_W, MV_H, tri) 752 drawn=drawn+1 753 } 754 ti=ti+1 755 } 756 return drawn 757} 758// smooth Gouraud sphere -- routes through the generic mesh renderer (mode 0). EXPORTED. 759func mv_render_sphere(angle: i64) -> i64 { 760 mv_sphere_scene() 761 return mv_render_mesh(angle, O_SPV, O_SPN, O_SPC, O_SPS, O_SPI, SP_NV, SP_NT, SPHERE_BASE, 0) 762} 763// flat sphere (mode 1) -- the gate's negative control proving smooth shading really interpolates. EXPORTED. 764func mv_render_sphere_flat(angle: i64) -> i64 { 765 mv_sphere_scene() 766 return mv_render_mesh(angle, O_SPV, O_SPN, O_SPC, O_SPS, O_SPI, SP_NV, SP_NT, SPHERE_BASE, 1) 767} 768 769// CAP-PBR rung-4: true PHONG sphere -- per-pixel normal interpolation + shading via rc_triangle_phong. Same sphere 770// + orbited light as Gouraud, but the highlight is computed at EVERY pixel -> sharp + correctly placed between 771// vertices. EXPORTED. The per-vertex NORMALS (not colours) feed the rasterizer; L,V,base,shin,amb ride in pb too. 772// shared per-pixel-Phong sphere draw. bump=0 -> smooth Phong; bump>0 -> procedural per-pixel normal perturbation 773// (a bump map: surface detail with NO extra geometry). 774func mv_sphere_phong_draw(angle: i64, bump: i64) -> i64 { 775 let fb: *i64 = (O_FB as i64) as *i64 776 let zb: *i64 = (O_ZB as i64) as *i64 777 rc_clear(fb, MV_W, MV_H, BG) 778 rc_zclear(zb, MV_W, MV_H) 779 mv_sphere_xform() 780 let cc: i64 = rc_cos_q14(angle) 781 let ss: i64 = rc_sin_q14(angle) 782 let nrm: *i64 = (O_SPN as i64) as *i64 783 let scr: *i64 = (O_SPS as i64) as *i64 784 let id: *i64 = (O_SPI as i64) as *i64 785 let pb: *i64 = (O_PBUF as i64) as *i64 786 pb[18] = (cc*8192 + ss*11585) / VQ 787 pb[19] = 8192 788 pb[20] = ((0-ss)*8192 + cc*11585) / VQ 789 pb[21] = 0; pb[22] = 0; pb[23] = VQ 790 pb[24] = SPHERE_BASE; pb[25] = 24; pb[26] = 2048; pb[27] = bump 791 var ti: i64 = 0 792 var drawn: i64 = 0 793 while ti < SP_NT { 794 let a: i64 = id[ti*3+0] 795 let b: i64 = id[ti*3+1] 796 let c: i64 = id[ti*3+2] 797 var vis: i64 = 0 798 if scr[a*4+3]==1 { if scr[b*4+3]==1 { if scr[c*4+3]==1 { vis=1 } } } 799 if vis==1 { 800 pb[0]=scr[a*4+0]; pb[1]=scr[a*4+1]; pb[2]=scr[a*4+2] 801 pb[3]=scr[b*4+0]; pb[4]=scr[b*4+1]; pb[5]=scr[b*4+2] 802 pb[6]=scr[c*4+0]; pb[7]=scr[c*4+1]; pb[8]=scr[c*4+2] 803 pb[9]=nrm[a*3+0]; pb[10]=nrm[a*3+1]; pb[11]=nrm[a*3+2] 804 pb[12]=nrm[b*3+0]; pb[13]=nrm[b*3+1]; pb[14]=nrm[b*3+2] 805 pb[15]=nrm[c*3+0]; pb[16]=nrm[c*3+1]; pb[17]=nrm[c*3+2] 806 rc_triangle_phong(fb, zb, MV_W, MV_H, pb) 807 drawn=drawn+1 808 } 809 ti=ti+1 810 } 811 return drawn 812} 813// smooth Phong sphere (bump=0). EXPORTED. 814func mv_render_sphere_phong(angle: i64) -> i64 { return mv_sphere_phong_draw(angle, 0) } 815// CAP-PBR rung-5: bump-mapped sphere -- per-pixel procedural normal perturbation adds SURFACE DETAIL (a dimpled, 816// textured look) that catches the orbiting light, with NO extra geometry / no extra triangles. EXPORTED. 817func mv_render_sphere_nmap(angle: i64) -> i64 { return mv_sphere_phong_draw(angle, 1200) } 818 819// ============================== R5v2: DANCE VIEWER (ws=dance-motion 2026-08-10) ============================== 820// SOVEREIGN in-page player, v2 wire format 'NXV2': adds per-vertex SECOND joint + Q14 blend weight so the 821// skin BLENDS across joints (v1's rigid one-joint bind tore into spikes at real dance angles -- operator- 822// observed live), and raises the stage to 640x480. The page fetches the .nxdv blob, copies it VERBATIM to 823// dv_blob_off, and calls mv_dance_render(t_ms, yaw_deg, pitch_deg, dist_q14) per frame; blit O_DFB. 824// Blob image (fixed offsets ARE the wire format; emitter = nx_dance_emit, oracle = build_blob.js): 825// header 8 i64 [magic 'NXV2'=0x3256584E, nverts, ntris, nkeys, key_ms, njoint, 0, 0] 826// verts Q14 x3 | normals Q14 x3 | colours rgba | joint1 | joint2 | weight1 Q14 | indices x3 827// parent-slot x nj | local translations Q14 x3 | inverse-bind mat4 Q14 | tracks key-major [key][bone][xyzw] 828// REFUSES (-1) on bad magic / counts over capacity. Own carve, ZERO overlap with the legacy 64x64 paths. 829const DV_W: i64 = 640 830const DV_H: i64 = 480 831const DV_MAXV: i64 = 7000 832const DV_MAXT: i64 = 14000 833const DV_NJ: i64 = 11 834const DV_MAXK: i64 = 128 835const DV_MAGIC: i64 = 0x3256584E 836const DV_DIST_MIN: i64 = 16384 837const DV_DIST_MAX: i64 = 524288 838 839const O_DFB: i64 = 131072 840const O_DZB: i64 = 2588672 841const O_DHDR: i64 = 5046272 842const O_DV: i64 = 5046336 843const O_DN: i64 = 5214336 844const O_DC: i64 = 5382336 845const O_DJ: i64 = 5438336 846const O_DJ2: i64 = 5494336 847const O_DWT: i64 = 5550336 848const O_DI: i64 = 5606336 849const O_DPAR: i64 = 5942336 850const O_DJT: i64 = 5942424 851const O_DIBM: i64 = 5942688 852const O_DTRK: i64 = 5944096 853const O_DSK: i64 = 5976864 854const O_DS: i64 = 6200864 855const O_DCL: i64 = 6424864 856const O_DPAL: i64 = 6480864 857const O_DWLD: i64 = 6482272 858const O_DLOC: i64 = 6483680 859const O_DQ: i64 = 6483808 860const O_DRX: i64 = 6483840 861const O_DRY: i64 = 6483968 862const O_DROT: i64 = 6484096 863const O_DPRJ: i64 = 6484224 864const O_DMV: i64 = 6484352 865const O_DMVP: i64 = 6484480 866const O_DVB: i64 = 6484608 867const O_DVC: i64 = 6484640 868const O_DVD: i64 = 6484672 869const O_DVE: i64 = 6484704 870 871func dv_w() -> i64 { return DV_W } 872func dv_h() -> i64 { return DV_H } 873func dv_fb_off() -> i64 { return O_DFB } 874func dv_blob_off() -> i64 { return O_DHDR } 875func dv_blob_len() -> i64 { return O_DSK - O_DHDR } 876func dv_duration_ms() -> i64 { 877 let h: *i64 = (O_DHDR as i64) as *i64 878 return h[3] * h[4] 879} 880 881// Q14 quaternion (x,y,z,w in q[0..3]) + Q14 translation -> row-major Q14 mat4 (rc_mat4_vec4 convention: 882// rotation rows 0..2, translation in column 3, m[15]=VQ). 883func dv_quatmat(q: *i64, tx: i64, ty: i64, tz: i64, out: *i64) -> i64 { 884 let xx: i64 = q[0]*q[0]/VQ 885 let yy: i64 = q[1]*q[1]/VQ 886 let zz: i64 = q[2]*q[2]/VQ 887 let xy: i64 = q[0]*q[1]/VQ 888 let xz: i64 = q[0]*q[2]/VQ 889 let yz: i64 = q[1]*q[2]/VQ 890 let wx: i64 = q[3]*q[0]/VQ 891 let wy: i64 = q[3]*q[1]/VQ 892 let wz: i64 = q[3]*q[2]/VQ 893 out[0] = VQ - 2*(yy+zz) 894 out[1] = 2*(xy-wz) 895 out[2] = 2*(xz+wy) 896 out[3] = tx 897 out[4] = 2*(xy+wz) 898 out[5] = VQ - 2*(xx+zz) 899 out[6] = 2*(yz-wx) 900 out[7] = ty 901 out[8] = 2*(xz-wy) 902 out[9] = 2*(yz+wx) 903 out[10] = VQ - 2*(xx+yy) 904 out[11] = tz 905 out[12] = 0 906 out[13] = 0 907 out[14] = 0 908 out[15] = VQ 909 return 0 910} 911 912// sample bone (0..7) at t_ms: shortest-path nlerp between neighbouring keys, renormalized to Q14 via 913// mv_isqrt -- an un-normalized quat scales the skin matrix by |q|^2 and the body visibly breathes. 914func dv_sample(bone: i64, tms: i64, qo: *i64) -> i64 { 915 let h: *i64 = (O_DHDR as i64) as *i64 916 let nkeys: i64 = h[3] 917 let keyms: i64 = h[4] 918 let trk: *i64 = (O_DTRK as i64) as *i64 919 var k: i64 = tms / keyms 920 if k < 0 { k = 0 } 921 var f: i64 = 0 922 if k >= nkeys - 1 { k = nkeys - 1 } 923 else { f = (tms - k*keyms) * VQ / keyms } 924 let a: i64 = (k*8 + bone) * 4 925 var b: i64 = a 926 if k < nkeys - 1 { b = ((k+1)*8 + bone) * 4 } 927 let dot: i64 = trk[a]*trk[b] + trk[a+1]*trk[b+1] + trk[a+2]*trk[b+2] + trk[a+3]*trk[b+3] 928 var sg: i64 = 1 929 if dot < 0 { sg = 0 - 1 } 930 let g: i64 = VQ - f 931 qo[0] = (trk[a]*g + sg*trk[b]*f) / VQ 932 qo[1] = (trk[a+1]*g + sg*trk[b+1]*f) / VQ 933 qo[2] = (trk[a+2]*g + sg*trk[b+2]*f) / VQ 934 qo[3] = (trk[a+3]*g + sg*trk[b+3]*f) / VQ 935 let n2: i64 = qo[0]*qo[0] + qo[1]*qo[1] + qo[2]*qo[2] + qo[3]*qo[3] 936 let nn: i64 = mv_isqrt(n2) 937 if nn > 0 { qo[0]=qo[0]*VQ/nn; qo[1]=qo[1]*VQ/nn; qo[2]=qo[2]*VQ/nn; qo[3]=qo[3]*VQ/nn } 938 return 0 939} 940 941// joint palette at t_ms: world[slot] = world[parent] * (T(local) * R(track)); pal = world * IBM. 942// Slots are TOPOLOGICALLY ordered by the emitter (parent < child), so one forward pass suffices. 943func dv_palette(tms: i64) -> i64 { 944 let h: *i64 = (O_DHDR as i64) as *i64 945 let nj: i64 = h[5] 946 let par: *i64 = (O_DPAR as i64) as *i64 947 let jt: *i64 = (O_DJT as i64) as *i64 948 let wld: *i64 = (O_DWLD as i64) as *i64 949 let loc: *i64 = (O_DLOC as i64) as *i64 950 let q: *i64 = (O_DQ as i64) as *i64 951 var s: i64 = 0 952 while s < nj { 953 q[0]=0; q[1]=0; q[2]=0; q[3]=VQ 954 if s >= 1 { if s <= 8 { dv_sample(s-1, tms, q) } } 955 dv_quatmat(q, jt[s*3], jt[s*3+1], jt[s*3+2], loc) 956 let wbase: i64 = s*16 957 if par[s] < 0 { 958 var ci: i64 = 0 959 while ci < 16 { wld[wbase+ci] = loc[ci]; ci = ci + 1 } 960 } else { 961 rc_mat4_mul(((O_DWLD + par[s]*128) as i64) as *i64, loc, ((O_DWLD + s*128) as i64) as *i64) 962 } 963 rc_mat4_mul(((O_DWLD + s*128) as i64) as *i64, ((O_DIBM + s*128) as i64) as *i64, ((O_DPAL + s*128) as i64) as *i64) 964 s = s + 1 965 } 966 return 0 967} 968 969// project one Q14 world vertex through O_DMVP into the DANCE screen buffer (own scratch, own W/H). 970func dv_proj1(vx: i64, vy: i64, vz: i64, scr: *i64, base: i64) -> i64 { 971 let mvp: *i64 = (O_DMVP as i64) as *i64 972 let vb: *i64 = (O_DVB as i64) as *i64 973 let cl: *i64 = (O_DVC as i64) as *i64 974 vb[0]=vx; vb[1]=vy; vb[2]=vz; vb[3]=VQ 975 rc_mat4_vec4(mvp, vb, cl) 976 let cw: i64 = cl[3] 977 if cw <= 0 { scr[base+3]=0; return 0 } 978 scr[base+0] = (cl[0]*VQ/cw + VQ) * DV_W / (2*VQ) 979 scr[base+1] = (VQ - cl[1]*VQ/cw) * DV_H / (2*VQ) 980 scr[base+2] = cl[2]*VQ/cw + VQ 981 scr[base+3] = 1 982 return 0 983} 984 985// THE EXPORTED FRAME: render the loaded skinned being at t_ms with an orbit camera. 986// yaw/pitch in integer degrees, dist Q14 (16384 = 1.0; clamped). Returns triangles drawn, -1 REFUSED. 987func mv_dance_render(tms: i64, yaw: i64, pitch: i64, dist: i64) -> i64 { 988 let h: *i64 = (O_DHDR as i64) as *i64 989 if h[0] != DV_MAGIC { return 0 - 1 } 990 let nverts: i64 = h[1] 991 let ntris: i64 = h[2] 992 if nverts < 3 { return 0 - 1 } 993 if nverts > DV_MAXV { return 0 - 1 } 994 if ntris < 1 { return 0 - 1 } 995 if ntris > DV_MAXT { return 0 - 1 } 996 if h[3] < 2 { return 0 - 1 } 997 if h[3] > DV_MAXK { return 0 - 1 } 998 if h[4] < 1 { return 0 - 1 } 999 if h[5] < 1 { return 0 - 1 } 1000 if h[5] > DV_NJ { return 0 - 1 } 1001 var dd: i64 = dist 1002 if dd < DV_DIST_MIN { dd = DV_DIST_MIN } 1003 if dd > DV_DIST_MAX { dd = DV_DIST_MAX } 1004 let fb: *i64 = (O_DFB as i64) as *i64 1005 let zb: *i64 = (O_DZB as i64) as *i64 1006 rc_clear(fb, DV_W, DV_H, BG) 1007 rc_zclear(zb, DV_W, DV_H) 1008 dv_palette(tms) 1009 // camera: rot = rotX(pitch) * rotY(yaw); mv = T(0,0,-dist) * rot; mvp = proj * mv 1010 let rx: *i64 = (O_DRX as i64) as *i64 1011 let ry: *i64 = (O_DRY as i64) as *i64 1012 let rot: *i64 = (O_DROT as i64) as *i64 1013 let prj: *i64 = (O_DPRJ as i64) as *i64 1014 let mvm: *i64 = (O_DMV as i64) as *i64 1015 let trans: *i64 = (O_TRANS as i64) as *i64 1016 let mvp: *i64 = (O_DMVP as i64) as *i64 1017 var zi: i64 = 0 1018 while zi < 16 { rx[zi]=0; ry[zi]=0; zi=zi+1 } 1019 let cy: i64 = rc_cos_q14(yaw) 1020 let sy: i64 = rc_sin_q14(yaw) 1021 ry[0]=cy; ry[2]=sy; ry[5]=VQ; ry[8]=0-sy; ry[10]=cy; ry[15]=VQ 1022 let cp: i64 = rc_cos_q14(pitch) 1023 let sp: i64 = rc_sin_q14(pitch) 1024 rx[0]=VQ; rx[5]=cp; rx[6]=0-sp; rx[9]=sp; rx[10]=cp; rx[15]=VQ 1025 rc_mat4_mul(rx, ry, rot) 1026 rc_perspective_cs(rc_cos_q14(FOVH), rc_sin_q14(FOVH), DV_W*VQ/DV_H, VQ, FARQ, prj) 1027 rc_translation_4x4(0, 0, 0-dd, trans) 1028 rc_mat4_mul(trans, rot, mvm) 1029 rc_mat4_mul(prj, mvm, mvp) 1030 // skin: TWO-BONE blend, p = (w*M1 + (VQ-w)*M2) applied to rest pos and normal (w=0 kills translation 1031 // for the normal). Then camera rotation for view-space lighting -- light fixed (.5,.5,.707) Q14. 1032 let v: *i64 = (O_DV as i64) as *i64 1033 let nrm: *i64 = (O_DN as i64) as *i64 1034 let colv: *i64 = (O_DC as i64) as *i64 1035 let jv: *i64 = (O_DJ as i64) as *i64 1036 let j2v: *i64 = (O_DJ2 as i64) as *i64 1037 let wtv: *i64 = (O_DWT as i64) as *i64 1038 let sk: *i64 = (O_DSK as i64) as *i64 1039 let scr: *i64 = (O_DS as i64) as *i64 1040 let lit: *i64 = (O_DCL as i64) as *i64 1041 let vb: *i64 = (O_DVB as i64) as *i64 1042 let vc: *i64 = (O_DVC as i64) as *i64 1043 let vd: *i64 = (O_DVD as i64) as *i64 1044 let ve: *i64 = (O_DVE as i64) as *i64 1045 let LX: i64 = 8192 1046 let LY: i64 = 8192 1047 let LZ: i64 = 11585 1048 var k: i64 = 0 1049 while k < nverts { 1050 var j1: i64 = jv[k] 1051 if j1 < 0 { j1 = 0 } 1052 if j1 >= h[5] { j1 = 0 } 1053 var j2: i64 = j2v[k] 1054 if j2 < 0 { j2 = j1 } 1055 if j2 >= h[5] { j2 = j1 } 1056 var w1: i64 = wtv[k] 1057 if w1 < 0 { w1 = 0 } 1058 if w1 > VQ { w1 = VQ } 1059 let w2: i64 = VQ - w1 1060 let pm1: *i64 = ((O_DPAL + j1*128) as i64) as *i64 1061 let pm2: *i64 = ((O_DPAL + j2*128) as i64) as *i64 1062 vb[0]=v[k*3]; vb[1]=v[k*3+1]; vb[2]=v[k*3+2]; vb[3]=VQ 1063 rc_mat4_vec4(pm1, vb, vc) 1064 rc_mat4_vec4(pm2, vb, vd) 1065 sk[k*4] = (w1*vc[0] + w2*vd[0]) / VQ 1066 sk[k*4+1] = (w1*vc[1] + w2*vd[1]) / VQ 1067 sk[k*4+2] = (w1*vc[2] + w2*vd[2]) / VQ 1068 sk[k*4+3] = VQ 1069 vb[0]=nrm[k*3]; vb[1]=nrm[k*3+1]; vb[2]=nrm[k*3+2]; vb[3]=0 1070 rc_mat4_vec4(pm1, vb, vc) 1071 rc_mat4_vec4(pm2, vb, vd) 1072 ve[0] = (w1*vc[0] + w2*vd[0]) / VQ 1073 ve[1] = (w1*vc[1] + w2*vd[1]) / VQ 1074 ve[2] = (w1*vc[2] + w2*vd[2]) / VQ 1075 ve[3] = 0 1076 rc_mat4_vec4(rot, ve, vc) 1077 // two-sided lambert (abs dot): interior faces exposed by the bridge filter's honest holes 1078 // shade like skin instead of reading as black notches 1079 var br: i64 = mv_dot3(vc[0], vc[1], vc[2], LX, LY, LZ) 1080 if br < 0 { br = 0 - br } 1081 var c0: i64 = mv_shade(colv[k], br, 3072) 1082 let spq: i64 = mv_normal_specular(vc[0], vc[1], vc[2], LX, LY, LZ, 0, 0, VQ, 16) 1083 c0 = mv_add_spec(c0, spq) 1084 // hemispheric fill: cool sky from above -- a second light direction the flat clay read lacks 1085 let sky: i64 = (vc[1] + VQ) / 2 1086 var rr2: i64 = (c0 & 255) + sky*10/VQ 1087 var gg2: i64 = ((c0 >> 8) & 255) + sky*14/VQ 1088 var bb2: i64 = ((c0 >> 16) & 255) + sky*22/VQ 1089 if rr2 > 255 { rr2 = 255 } 1090 if gg2 > 255 { gg2 = 255 } 1091 if bb2 > 255 { bb2 = 255 } 1092 c0 = rr2 + gg2*256 + bb2*65536 + 4278190080 1093 lit[k] = c0 1094 dv_proj1(sk[k*4], sk[k*4+1], sk[k*4+2], scr, k*4) 1095 k = k + 1 1096 } 1097 let id: *i64 = (O_DI as i64) as *i64 1098 let tri: *i64 = (O_TRI as i64) as *i64 1099 var ti: i64 = 0 1100 var drawn: i64 = 0 1101 while ti < ntris { 1102 let a: i64 = id[ti*3] 1103 let b: i64 = id[ti*3+1] 1104 let c: i64 = id[ti*3+2] 1105 var ok: i64 = 1 1106 if a < 0 { ok = 0 } 1107 if a >= nverts { ok = 0 } 1108 if b < 0 { ok = 0 } 1109 if b >= nverts { ok = 0 } 1110 if c < 0 { ok = 0 } 1111 if c >= nverts { ok = 0 } 1112 if ok == 1 { 1113 var vis: i64 = 0 1114 if scr[a*4+3]==1 { if scr[b*4+3]==1 { if scr[c*4+3]==1 { vis=1 } } } 1115 if vis == 1 { 1116 tri[0]=scr[a*4]; tri[1]=scr[a*4+1]; tri[2]=scr[a*4+2]; tri[3]=lit[a] 1117 tri[4]=scr[b*4]; tri[5]=scr[b*4+1]; tri[6]=scr[b*4+2]; tri[7]=lit[b] 1118 tri[8]=scr[c*4]; tri[9]=scr[c*4+1]; tri[10]=scr[c*4+2]; tri[11]=lit[c] 1119 rc_triangle(fb, zb, DV_W, DV_H, tri) 1120 drawn = drawn + 1 1121 } 1122 } 1123 ti = ti + 1 1124 } 1125 return drawn 1126}