code wiki / (root) / nx_trimesh.nx

nx_trimesh.nx source

↩ module page · 667 lines · 38892 B

1// nx_trimesh.nx -- ★SOVEREIGN TRIANGLE-MESH RASTERIZER (operator 2026-07-10, frank feedback: "we just have circles, 2// no triangle or other capabilities for shapes to make more and more real things"). Correct: everything so far is 3// axis-aligned ELLIPSOIDS (smooth blobs) -- no flat faces, sharp edges, or arbitrary form. This adds the UNIVERSAL 4// primitive: the TRIANGLE. A real integer rasterizer -- transform -> perspective project -> barycentric fill with a 5// Z-BUFFER + flat per-face shading -> arbitrary polygon meshes with FLAT FACES and SHARP EDGES. Plus a primitive 6// library (cube/tetra/octa/pyramid/prism/icosahedron/uv-sphere) and composition (build real things from many meshes). 7// 100% integer, sovereign (own trig, own raster). license_tier: ORIGINAL 8import "nx_syscalls.nx" 9import "nx_itrig.nx" 10const TM_MAGIC_1024: i64 = 1024 11const TM_MAGIC_1000000000: i64 = 1000000000 12const TM_MAGIC_374761393: i64 = 374761393 13const TM_MAGIC_668265263: i64 = 668265263 14const TM_MAGIC_1610612741: i64 = 1610612741 15const TM_MAGIC_1274126177: i64 = 1274126177 16const TM_MAGIC_1048576: i64 = 1048576 17const TM_MAGIC_4096: i64 = 4096 18const TM_MAGIC_1618: i64 = 1618 19const TM_MAGIC_65536: i64 = 65536 20const TM_MAGIC_1300: i64 = 1300 21const TM_MAGIC_2400: i64 = 2400 22const TM_MAGIC_3000: i64 = 3000 23const TM_MAGIC_100000: i64 = 100000 24const TM_MAGIC_90000: i64 = 90000 25const TM_MAGIC_65535: i64 = 65535 26const TM_MAGIC_37777: i64 = 37777 27const TM_MAGIC_91131: i64 = 91131 28const TM_MAGIC_6144: i64 = 6144 29const TM_MAGIC_81111: i64 = 81111 30const TM_MAGIC_2000000000: i64 = 2000000000 31 32// ★CAPACITY IS DERIVED, NOT TYPED. Surface nets emits at most one vertex per grid cell, so a mesh from 33// an N^3 grid cannot exceed N^3 vertices -- but the surface is 2-DIMENSIONAL, so the real bound is the 34// number of cells the surface passes through, which scales as N^2. TM_GRID is the largest grid any 35// caller polygonizes; everything else follows from it, with a safety factor for a folded surface that 36// crosses a cell column more than once. Change the grid and the caps move with it. 37// (The previous 48k was typed, and silently truncated a skull at 2mm cells -- the organ then reported a 38// bounding box measured from an INCOMPLETE mesh, a wrong number that looks like a right one.) 39const TM_GRID: i64 = 256 // max polygonizer grid resolution supported 40const TM_FOLD: i64 = 6 // surface-crossings per cell column, worst case 41const TM_VCAP: i64 = TM_GRID*TM_GRID*TM_FOLD 42const TM_TCAP: i64 = TM_VCAP*2 // surface nets emits ~2 triangles per vertex 43static TM_VX: i64 // model-space vert x,y,z (3 arrays interleaved via ptrs) 44static TM_VY: i64 45static TM_VZ: i64 46static TM_TA: i64 // tri vert index a,b,c + colour (4 arrays) 47static TM_TB: i64 48static TM_TC: i64 49static TM_TCOL: i64 50static TM_VNX: i64 // ★F3: per-vertex model-space normal (for Gouraud smooth shading) 51static TM_VNY: i64 52static TM_VNZ: i64 53static TM_VCOL: i64 // per-vertex colour (for multi-material meshes, e.g. the meshed anatomy) 54static TM_NV: i64 55static TM_NT: i64 56static TM_OVF: i64 // set to 1 if a vertex/triangle add hit the capacity (so callers detect truncation) 57func tm_ovf() -> i64 { return TM_OVF } 58static TM_SPEC: i64 // ★F3 Blinn-Phong specular strength (0 = off/matte; ~110 = shiny). opt-in, zero-cost when 0. 59func tm_set_spec(v: i64) -> i64 { TM_SPEC = v; return 0 } 60static TM_TEX: i64 // ★F3 procedural 3D solid-noise texture: lattice scale in units (0 = off). opt-in. 61func tm_set_tex(scale: i64) -> i64 { TM_TEX = scale; return 0 } 62// ★Q2 SUN SHADOWS (2026-07-12, the quality ladder): a world-space orthographic SHADOW MAP. Bake once per scene 63// (model space -- camera-independent, so one bake serves every view), then the pixel path darkens fragments whose 64// sun-depth is behind the nearest blocker. Opt-in: tm_set_sun + tm_shadow_bake + tm_set_shadow(1); zero-cost off. 65const TM_SHRES: i64 = 576 66static TM_SUNX: i64 // direction TOWARD the sun, normalized *1024 67static TM_SUNY: i64 68static TM_SUNZ: i64 69static TM_SHON: i64 // 1 = apply the baked map in the pixel path 70static TM_SH: i64 // the map: TM_SHRES^2 i64 sun-depths (max = nearest blocker) 71static TM_SRX: i64 // ortho basis R (*1024) 72static TM_SRY: i64 73static TM_SRZ: i64 74static TM_SUX: i64 // ortho basis U (*1024) 75static TM_SUY: i64 76static TM_SUZ: i64 77static TM_SHA0: i64 // a-min / b-min / spans (world units) -> texel mapping 78static TM_SHB0: i64 79static TM_SHSA: i64 80static TM_SHSB: i64 81func tm_set_sun(x: i64, y: i64, z: i64) -> i64 { 82 let l: i64 = tm_isqrt(x*x + y*y + z*z) + 1 83 TM_SUNX = x*TM_MAGIC_1024/l 84 TM_SUNY = y*TM_MAGIC_1024/l 85 TM_SUNZ = z*TM_MAGIC_1024/l 86 return 0 87} 88func tm_set_shadow(v: i64) -> i64 { TM_SHON = v; return 0 } 89func tm_shadow_bake() -> i64 { 90 if TM_SH == 0 { TM_SH = sys_mmap(TM_SHRES*TM_SHRES*8) as i64 } 91 let sh: *i64 = TM_SH as *i64 92 var i: i64 = 0 93 while i < TM_SHRES*TM_SHRES { sh[i] = 0 - TM_MAGIC_1000000000; i = i + 1 } 94 // ortho basis from the sun dir: R = S x up (fallback x-axis), U = S x R 95 var rx: i64 = TM_SUNZ 96 var ry: i64 = 0 97 var rz: i64 = 0 - TM_SUNX 98 var rl: i64 = tm_isqrt(rx*rx + rz*rz) 99 if rl < 40 { rx = TM_MAGIC_1024; ry = 0; rz = 0; rl = TM_MAGIC_1024 } 100 rx = rx*TM_MAGIC_1024/rl; rz = rz*TM_MAGIC_1024/rl 101 let ux: i64 = (TM_SUNY*rz - TM_SUNZ*ry)/TM_MAGIC_1024 102 let uy: i64 = (TM_SUNZ*rx - TM_SUNX*rz)/TM_MAGIC_1024 103 let uz: i64 = (TM_SUNX*ry - TM_SUNY*rx)/TM_MAGIC_1024 104 TM_SRX = rx; TM_SRY = ry; TM_SRZ = rz 105 TM_SUX = ux; TM_SUY = uy; TM_SUZ = uz 106 // project all verts to (a,b,d) sun space 107 let px: *i64 = TM_VX as *i64 108 let py: *i64 = TM_VY as *i64 109 let pz: *i64 = TM_VZ as *i64 110 let sa: *i64 = sys_mmap(TM_NV*8) as *i64 111 let sb: *i64 = sys_mmap(TM_NV*8) as *i64 112 let sd: *i64 = sys_mmap(TM_NV*8) as *i64 113 var amin: i64 = TM_MAGIC_1000000000 114 var amax: i64 = 0-TM_MAGIC_1000000000 115 var bmin: i64 = TM_MAGIC_1000000000 116 var bmax: i64 = 0-TM_MAGIC_1000000000 117 i = 0 118 while i < TM_NV { 119 let a: i64 = (px[i]*rx + py[i]*ry + pz[i]*rz)/TM_MAGIC_1024 120 let b: i64 = (px[i]*ux + py[i]*uy + pz[i]*uz)/TM_MAGIC_1024 121 let d: i64 = (px[i]*TM_SUNX + py[i]*TM_SUNY + pz[i]*TM_SUNZ)/TM_MAGIC_1024 122 sa[i] = a; sb[i] = b; sd[i] = d 123 if a < amin { amin = a } if a > amax { amax = a } 124 if b < bmin { bmin = b } if b > bmax { bmax = b } 125 i = i + 1 126 } 127 TM_SHA0 = amin 128 TM_SHB0 = bmin 129 TM_SHSA = amax - amin + 1 130 TM_SHSB = bmax - bmin + 1 131 // rasterize every triangle into the map, keeping MAX sun-depth (nearest blocker) 132 let TA: *i64 = TM_TA as *i64 133 let TB: *i64 = TM_TB as *i64 134 let TC: *i64 = TM_TC as *i64 135 var t: i64 = 0 136 while t < TM_NT { 137 let i0: i64 = TA[t]; let i1: i64 = TB[t]; let i2: i64 = TC[t] 138 let ax: i64 = (sa[i0]-amin)*TM_SHRES/TM_SHSA; let ay: i64 = (sb[i0]-bmin)*TM_SHRES/TM_SHSB 139 let bx: i64 = (sa[i1]-amin)*TM_SHRES/TM_SHSA; let by: i64 = (sb[i1]-bmin)*TM_SHRES/TM_SHSB 140 let cx: i64 = (sa[i2]-amin)*TM_SHRES/TM_SHSA; let cy: i64 = (sb[i2]-bmin)*TM_SHRES/TM_SHSB 141 let area: i64 = (bx-ax)*(cy-ay) - (by-ay)*(cx-ax) 142 if area != 0 { 143 var minx: i64 = tm_min3(ax,bx,cx); var maxx: i64 = tm_max3(ax,bx,cx) 144 var miny: i64 = tm_min3(ay,by,cy); var maxy: i64 = tm_max3(ay,by,cy) 145 if minx < 0 { minx = 0 } if miny < 0 { miny = 0 } 146 if maxx > TM_SHRES-1 { maxx = TM_SHRES-1 } if maxy > TM_SHRES-1 { maxy = TM_SHRES-1 } 147 var qy: i64 = miny 148 while qy <= maxy { 149 var qx: i64 = minx 150 while qx <= maxx { 151 let e0: i64 = (cx-bx)*(qy-by) - (cy-by)*(qx-bx) 152 let e1: i64 = (ax-cx)*(qy-cy) - (ay-cy)*(qx-cx) 153 let e2: i64 = (bx-ax)*(qy-ay) - (by-ay)*(qx-ax) 154 var inside: i64 = 0 155 if area > 0 { if e0>=0 { if e1>=0 { if e2>=0 { inside=1 } } } } 156 else { if e0<=0 { if e1<=0 { if e2<=0 { inside=1 } } } } 157 if inside == 1 { 158 let dd: i64 = (e0*sd[i0] + e1*sd[i1] + e2*sd[i2]) / area 159 let mi: i64 = qy*TM_SHRES + qx 160 if dd > sh[mi] { sh[mi] = dd } 161 } 162 qx = qx + 1 163 } 164 qy = qy + 1 165 } 166 } 167 t = t + 1 168 } 169 return 0 170} 171// shadow factor at a model-space point: 1024 = lit, else attenuated. Bias scales with texel size (acne guard). 172func tm_shadow_at(wx: i64, wy: i64, wz: i64) -> i64 { 173 let a: i64 = (wx*TM_SRX + wy*TM_SRY + wz*TM_SRZ)/TM_MAGIC_1024 174 let b: i64 = (wx*TM_SUX + wy*TM_SUY + wz*TM_SUZ)/TM_MAGIC_1024 175 let tx: i64 = (a - TM_SHA0)*TM_SHRES/TM_SHSA 176 let ty: i64 = (b - TM_SHB0)*TM_SHRES/TM_SHSB 177 if tx < 0 { return TM_MAGIC_1024 } 178 if ty < 0 { return TM_MAGIC_1024 } 179 if tx > TM_SHRES-1 { return TM_MAGIC_1024 } 180 if ty > TM_SHRES-1 { return TM_MAGIC_1024 } 181 let d: i64 = (wx*TM_SUNX + wy*TM_SUNY + wz*TM_SUNZ)/TM_MAGIC_1024 182 let sh: *i64 = TM_SH as *i64 183 var bias: i64 = TM_SHSA/TM_SHRES*3 184 if bias < 22 { bias = 22 } 185 if sh[ty*TM_SHRES + tx] > d + bias { return 340 } 186 return TM_MAGIC_1024 187} 188static TM_IMG: i64 // ★F3 IMAGE texture (packed w*h*3 RGB) sampled via per-vertex UV. 0 = off. 189static TM_IMGW: i64 190static TM_IMGH: i64 191func tm_set_image(rgb: *u8, w: i64, h: i64) -> i64 { TM_IMG = rgb as i64; TM_IMGW = w; TM_IMGH = h; return 0 } 192static TM_VU: i64 // per-vertex UV, Q16 (65536 = 1.0) 193static TM_VV: i64 194func tm_vuv(idx: i64, u: i64, v: i64) -> i64 { let pu: *i64 = TM_VU as *i64; let pv: *i64 = TM_VV as *i64; pu[idx] = u; pv[idx] = v; return 0 } 195func tm_hash3(a: i64, b: i64, c: i64) -> i64 { 196 var h: i64 = a*TM_MAGIC_374761393 + b*TM_MAGIC_668265263 + c*TM_MAGIC_1610612741 197 h = h ^ (h >> 13); h = h * TM_MAGIC_1274126177; h = h ^ (h >> 16) 198 if h < 0 { h = 0 - h } 199 return h % TM_MAGIC_1024 200} 201// trilinear value noise at 3D position, lattice scale S. Returns 0..1023 (smooth). Offset keeps floor-division positive. 202func tm_noise3(x: i64, y: i64, z: i64, S: i64) -> i64 { 203 let ox: i64 = x + TM_MAGIC_1048576; let oy: i64 = y + TM_MAGIC_1048576; let oz: i64 = z + TM_MAGIC_1048576 204 let gx: i64 = ox/S; let gy: i64 = oy/S; let gz: i64 = oz/S 205 let fx: i64 = (ox - gx*S)*TM_MAGIC_1024/S; let fy: i64 = (oy - gy*S)*TM_MAGIC_1024/S; let fz: i64 = (oz - gz*S)*TM_MAGIC_1024/S 206 let c000: i64 = tm_hash3(gx,gy,gz); let c100: i64 = tm_hash3(gx+1,gy,gz) 207 let c010: i64 = tm_hash3(gx,gy+1,gz); let c110: i64 = tm_hash3(gx+1,gy+1,gz) 208 let c001: i64 = tm_hash3(gx,gy,gz+1); let c101: i64 = tm_hash3(gx+1,gy,gz+1) 209 let c011: i64 = tm_hash3(gx,gy+1,gz+1); let c111: i64 = tm_hash3(gx+1,gy+1,gz+1) 210 let x00: i64 = c000 + (c100-c000)*fx/TM_MAGIC_1024 211 let x10: i64 = c010 + (c110-c010)*fx/TM_MAGIC_1024 212 let x01: i64 = c001 + (c101-c001)*fx/TM_MAGIC_1024 213 let x11: i64 = c011 + (c111-c011)*fx/TM_MAGIC_1024 214 let y0: i64 = x00 + (x10-x00)*fy/TM_MAGIC_1024 215 let y1: i64 = x01 + (x11-x01)*fy/TM_MAGIC_1024 216 return y0 + (y1-y0)*fz/TM_MAGIC_1024 217} 218 219func tm_isqrt(v: i64) -> i64 { if v <= 0 { return 0 } var x: i64 = v; var y: i64 = (x+1)/2; while y < x { x=y; y=(x+v/x)/2 } return x } 220func tm_min(a: i64, b: i64) -> i64 { if a<b {return a} return b } 221func tm_max(a: i64, b: i64) -> i64 { if a>b {return a} return b } 222func tm_min3(a: i64, b: i64, c: i64) -> i64 { return tm_min(tm_min(a,b),c) } 223func tm_max3(a: i64, b: i64, c: i64) -> i64 { return tm_max(tm_max(a,b),c) } 224 225func tm_reset() -> i64 { 226 if TM_VX == 0 { 227 TM_VX = sys_mmap(TM_VCAP*8) as i64; TM_VY = sys_mmap(TM_VCAP*8) as i64; TM_VZ = sys_mmap(TM_VCAP*8) as i64 228 TM_TA = sys_mmap(TM_TCAP*8) as i64; TM_TB = sys_mmap(TM_TCAP*8) as i64; TM_TC = sys_mmap(TM_TCAP*8) as i64; TM_TCOL = sys_mmap(TM_TCAP*8) as i64 229 TM_VNX = sys_mmap(TM_VCAP*8) as i64; TM_VNY = sys_mmap(TM_VCAP*8) as i64; TM_VNZ = sys_mmap(TM_VCAP*8) as i64 230 TM_VCOL = sys_mmap(TM_VCAP*8) as i64 231 TM_VU = sys_mmap(TM_VCAP*8) as i64; TM_VV = sys_mmap(TM_VCAP*8) as i64 232 } 233 TM_NV = 0; TM_NT = 0; TM_OVF = 0 234 return 0 235} 236func tm_vcol(idx: i64, col: i64) -> i64 { let p: *i64 = TM_VCOL as *i64; p[idx] = col; return 0 } 237// NOTE: declare the pointer up front -- inline-cast-index READ `(TM_VX as *i64)[i]` miscompiles to the base pointer. 238func tm_vpos(idx: i64, out: *i64) -> i64 { let px: *i64=TM_VX as *i64; let py: *i64=TM_VY as *i64; let pz: *i64=TM_VZ as *i64; out[0]=px[idx]; out[1]=py[idx]; out[2]=pz[idx]; return 0 } 239func tm_vnorm(idx: i64, out: *i64) -> i64 { let px: *i64=TM_VNX as *i64; let py: *i64=TM_VNY as *i64; let pz: *i64=TM_VNZ as *i64; out[0]=px[idx]; out[1]=py[idx]; out[2]=pz[idx]; return 0 } 240func tm_getvcol(idx: i64) -> i64 { let p: *i64=TM_VCOL as *i64; return p[idx] } 241// ★read-only accessors so a caller can EXPORT what surface_nets built (e.g. write NXMSH2 and then look 242// at it). Without these the polygonizer could fill these buffers and nothing could get the geometry 243// back out, which is how meshes got produced that nobody could view. 244func tm_vx(i: i64) -> i64 { let p: *i64=TM_VX as *i64; return p[i] } 245func tm_vy(i: i64) -> i64 { let p: *i64=TM_VY as *i64; return p[i] } 246func tm_vz(i: i64) -> i64 { let p: *i64=TM_VZ as *i64; return p[i] } 247func tm_vnx(i: i64) -> i64 { let p: *i64=TM_VNX as *i64; return p[i] } 248func tm_vny(i: i64) -> i64 { let p: *i64=TM_VNY as *i64; return p[i] } 249func tm_vnz(i: i64) -> i64 { let p: *i64=TM_VNZ as *i64; return p[i] } 250func tm_ta(i: i64) -> i64 { let p: *i64=TM_TA as *i64; return p[i] } 251func tm_tb(i: i64) -> i64 { let p: *i64=TM_TB as *i64; return p[i] } 252func tm_tc(i: i64) -> i64 { let p: *i64=TM_TC as *i64; return p[i] } 253func tm_tget(t: i64, out: *i64) -> i64 { let pa: *i64=TM_TA as *i64; let pb: *i64=TM_TB as *i64; let pc: *i64=TM_TC as *i64; out[0]=pa[t]; out[1]=pb[t]; out[2]=pc[t]; return 0 } 254// translate (and optionally uniform-scale, mil=1000 -> 1.0) vertices [from,to) -- scene placement of a loaded asset 255func tm_place(from: i64, to: i64, dx: i64, dy: i64, dz: i64, mil: i64) -> i64 { 256 let px: *i64 = TM_VX as *i64; let py: *i64 = TM_VY as *i64; let pz: *i64 = TM_VZ as *i64 257 var i: i64 = from 258 while i < to { 259 px[i] = px[i]*mil/1000 + dx 260 py[i] = py[i]*mil/1000 + dy 261 pz[i] = pz[i]*mil/1000 + dz 262 i = i + 1 263 } 264 return 0 265} 266// ★F3: compute per-vertex normals = normalized sum of adjacent FACE normals (model space). Enables Gouraud smooth shading. 267func tm_compute_normals() -> i64 { 268 let nx: *i64 = TM_VNX as *i64; let ny: *i64 = TM_VNY as *i64; let nz: *i64 = TM_VNZ as *i64 269 let vx: *i64 = TM_VX as *i64; let vy: *i64 = TM_VY as *i64; let vz: *i64 = TM_VZ as *i64 270 var i: i64 = 0 271 while i < TM_NV { nx[i]=0; ny[i]=0; nz[i]=0; i=i+1 } 272 let TA: *i64 = TM_TA as *i64; let TB: *i64 = TM_TB as *i64; let TC: *i64 = TM_TC as *i64 273 var t: i64 = 0 274 while t < TM_NT { 275 let a: i64 = TA[t]; let b: i64 = TB[t]; let c: i64 = TC[t] 276 let ux: i64 = vx[b]-vx[a]; let uy: i64 = vy[b]-vy[a]; let uz: i64 = vz[b]-vz[a] 277 let wx: i64 = vx[c]-vx[a]; let wy: i64 = vy[c]-vy[a]; let wz: i64 = vz[c]-vz[a] 278 var fnx: i64 = uy*wz-uz*wy; var fny: i64 = uz*wx-ux*wz; var fnz: i64 = ux*wy-uy*wx 279 let fl: i64 = tm_isqrt(fnx*fnx+fny*fny+fnz*fnz) + 1 // unit-ish (256) so big tris don't dominate the average 280 fnx = fnx*256/fl; fny = fny*256/fl; fnz = fnz*256/fl 281 nx[a]=nx[a]+fnx; ny[a]=ny[a]+fny; nz[a]=nz[a]+fnz 282 nx[b]=nx[b]+fnx; ny[b]=ny[b]+fny; nz[b]=nz[b]+fnz 283 nx[c]=nx[c]+fnx; ny[c]=ny[c]+fny; nz[c]=nz[c]+fnz 284 t = t + 1 285 } 286 i = 0 287 while i < TM_NV { 288 let l: i64 = tm_isqrt(nx[i]*nx[i]+ny[i]*ny[i]+nz[i]*nz[i]) + 1 289 nx[i]=nx[i]*TM_MAGIC_4096/l; ny[i]=ny[i]*TM_MAGIC_4096/l; nz[i]=nz[i]*TM_MAGIC_4096/l // unit normal fx4096 290 i = i + 1 291 } 292 return 0 293} 294func tm_vert(x: i64, y: i64, z: i64) -> i64 { 295 if TM_NV >= TM_VCAP { TM_OVF = 1; return TM_VCAP-1 } // capacity guard: never write OOB 296 let i: i64 = TM_NV 297 let px: *i64 = TM_VX as *i64; let py: *i64 = TM_VY as *i64; let pz: *i64 = TM_VZ as *i64 298 px[i] = x; py[i] = y; pz[i] = z 299 TM_NV = TM_NV + 1 300 return i 301} 302// add a vertex WITH an explicit per-vertex normal (for isosurface: SDF-gradient normals, winding-independent smooth shading) 303func tm_vert_n(x: i64, y: i64, z: i64, nx: i64, ny: i64, nz: i64) -> i64 { 304 if TM_NV >= TM_VCAP { TM_OVF = 1; return TM_VCAP-1 } // capacity guard 305 let i: i64 = TM_NV 306 let px: *i64 = TM_VX as *i64; let py: *i64 = TM_VY as *i64; let pz: *i64 = TM_VZ as *i64 307 let qx: *i64 = TM_VNX as *i64; let qy: *i64 = TM_VNY as *i64; let qz: *i64 = TM_VNZ as *i64 308 px[i]=x; py[i]=y; pz[i]=z; qx[i]=nx; qy[i]=ny; qz[i]=nz 309 TM_NV = TM_NV + 1 310 return i 311} 312func tm_tri(a: i64, b: i64, c: i64, col: i64) -> i64 { 313 if TM_NT >= TM_TCAP { TM_OVF = 1; return TM_TCAP-1 } // capacity guard 314 let i: i64 = TM_NT 315 let pa: *i64 = TM_TA as *i64; let pb: *i64 = TM_TB as *i64; let pc: *i64 = TM_TC as *i64; let pcol: *i64 = TM_TCOL as *i64 316 pa[i] = a; pb[i] = b; pc[i] = c; pcol[i] = col 317 TM_NT = TM_NT + 1 318 return i 319} 320func tm_quad(a: i64, b: i64, c: i64, d: i64, col: i64) -> i64 { tm_tri(a,b,c,col); tm_tri(a,c,d,col); return 0 } 321func tm_nt() -> i64 { return TM_NT } 322func tm_nv() -> i64 { return TM_NV } 323 324// ---- primitive mesh builders (append at center cx,cy,cz, half-size s, colour col) ---- 325func tm_cube(cx: i64, cy: i64, cz: i64, s: i64, col: i64) -> i64 { 326 let b: i64 = TM_NV 327 tm_vert(cx-s,cy-s,cz-s); tm_vert(cx+s,cy-s,cz-s); tm_vert(cx+s,cy+s,cz-s); tm_vert(cx-s,cy+s,cz-s) // back z- 328 tm_vert(cx-s,cy-s,cz+s); tm_vert(cx+s,cy-s,cz+s); tm_vert(cx+s,cy+s,cz+s); tm_vert(cx-s,cy+s,cz+s) // front z+ 329 tm_quad(b+4,b+5,b+6,b+7,col) // front 330 tm_quad(b+1,b+0,b+3,b+2,col) // back 331 tm_quad(b+0,b+4,b+7,b+3,col) // left 332 tm_quad(b+5,b+1,b+2,b+6,col) // right 333 tm_quad(b+3,b+7,b+6,b+2,col) // top 334 tm_quad(b+0,b+1,b+5,b+4,col) // bottom 335 return 0 336} 337func tm_tetra(cx: i64, cy: i64, cz: i64, s: i64, col: i64) -> i64 { 338 let b: i64 = TM_NV 339 tm_vert(cx+s,cy+s,cz+s); tm_vert(cx+s,cy-s,cz-s); tm_vert(cx-s,cy+s,cz-s); tm_vert(cx-s,cy-s,cz+s) // alternating cube corners = regular tetra 340 tm_tri(b+0,b+1,b+2,col); tm_tri(b+0,b+3,b+1,col); tm_tri(b+0,b+2,b+3,col); tm_tri(b+1,b+3,b+2,col) 341 return 0 342} 343func tm_octa(cx: i64, cy: i64, cz: i64, s: i64, col: i64) -> i64 { 344 let b: i64 = TM_NV 345 tm_vert(cx+s,cy,cz); tm_vert(cx-s,cy,cz); tm_vert(cx,cy+s,cz); tm_vert(cx,cy-s,cz); tm_vert(cx,cy,cz+s); tm_vert(cx,cy,cz-s) // +x -x +y -y +z -z 346 tm_tri(b+2,b+0,b+4,col); tm_tri(b+2,b+4,b+1,col); tm_tri(b+2,b+1,b+5,col); tm_tri(b+2,b+5,b+0,col) // top 4 347 tm_tri(b+3,b+4,b+0,col); tm_tri(b+3,b+1,b+4,col); tm_tri(b+3,b+5,b+1,col); tm_tri(b+3,b+0,b+5,col) // bottom 4 348 return 0 349} 350func tm_pyramid(cx: i64, cy: i64, cz: i64, s: i64, col: i64) -> i64 { 351 let b: i64 = TM_NV 352 tm_vert(cx-s,cy-s,cz-s); tm_vert(cx+s,cy-s,cz-s); tm_vert(cx+s,cy-s,cz+s); tm_vert(cx-s,cy-s,cz+s) // square base 353 tm_vert(cx,cy+s,cz) // apex 354 tm_tri(b+0,b+1,b+4,col); tm_tri(b+1,b+2,b+4,col); tm_tri(b+2,b+3,b+4,col); tm_tri(b+3,b+0,b+4,col) // 4 sides 355 tm_quad(b+0,b+3,b+2,b+1,col) // base 356 return 0 357} 358func tm_prism(cx: i64, cy: i64, cz: i64, s: i64, col: i64) -> i64 { // triangular prism (extruded triangle along z) 359 let b: i64 = TM_NV 360 tm_vert(cx-s,cy-s,cz-s); tm_vert(cx+s,cy-s,cz-s); tm_vert(cx,cy+s,cz-s) // back triangle 361 tm_vert(cx-s,cy-s,cz+s); tm_vert(cx+s,cy-s,cz+s); tm_vert(cx,cy+s,cz+s) // front triangle 362 tm_tri(b+3,b+4,b+5,col); tm_tri(b+1,b+0,b+2,col) // caps 363 tm_quad(b+0,b+1,b+4,b+3,col); tm_quad(b+1,b+2,b+5,b+4,col); tm_quad(b+2,b+0,b+3,b+5,col) // 3 rects 364 return 0 365} 366// icosahedron (12 verts via golden ratio phi~1.618, integer *1000). 20 triangular faces -- the iconic polyhedron. 367func tm_icosa(cx: i64, cy: i64, cz: i64, s: i64, col: i64) -> i64 { 368 let b: i64 = TM_NV 369 let p: i64 = s*TM_MAGIC_1618/1000 370 tm_vert(cx,cy+s,cz+p); tm_vert(cx,cy-s,cz+p); tm_vert(cx,cy+s,cz-p); tm_vert(cx,cy-s,cz-p) // 0..3 (0,±1,±phi) 371 tm_vert(cx+s,cy+p,cz); tm_vert(cx-s,cy+p,cz); tm_vert(cx+s,cy-p,cz); tm_vert(cx-s,cy-p,cz) // 4..7 (±1,±phi,0) 372 tm_vert(cx+p,cy,cz+s); tm_vert(cx-p,cy,cz+s); tm_vert(cx+p,cy,cz-s); tm_vert(cx-p,cy,cz-s) // 8..11 (±phi,0,±1) 373 tm_tri(b+0,b+1,b+8,col); tm_tri(b+0,b+8,b+4,col); tm_tri(b+0,b+4,b+5,col); tm_tri(b+0,b+5,b+9,col); tm_tri(b+0,b+9,b+1,col) 374 tm_tri(b+1,b+6,b+8,col); tm_tri(b+8,b+6,b+10,col); tm_tri(b+8,b+10,b+4,col); tm_tri(b+4,b+10,b+2,col); tm_tri(b+4,b+2,b+5,col) 375 tm_tri(b+5,b+2,b+11,col); tm_tri(b+5,b+11,b+9,col); tm_tri(b+9,b+11,b+7,col); tm_tri(b+9,b+7,b+1,col); tm_tri(b+1,b+7,b+6,col) 376 tm_tri(b+3,b+6,b+7,col); tm_tri(b+3,b+7,b+11,col); tm_tri(b+3,b+11,b+2,col); tm_tri(b+3,b+2,b+10,col); tm_tri(b+3,b+10,b+6,col) 377 return 0 378} 379// triangulated UV-sphere (rings x segs) -- shows even a ROUND shape is now real triangles, not an SDF blob. 380func tm_uvsphere(cx: i64, cy: i64, cz: i64, s: i64, rings: i64, segs: i64, col: i64) -> i64 { 381 let b: i64 = TM_NV 382 var r: i64 = 0 383 while r <= rings { 384 let theta: i64 = IT_PI * r / rings // 0..PI (lat) 385 let st: i64 = it_sin4096(theta); let ct: i64 = it_cos4096(theta) 386 var sg: i64 = 0 387 while sg <= segs { 388 let phi: i64 = 2*IT_PI * sg / segs // 0..2PI (lon) 389 let sp: i64 = it_sin4096(phi); let cp: i64 = it_cos4096(phi) 390 let x: i64 = cx + s*st/TM_MAGIC_4096*cp/TM_MAGIC_4096 391 let y: i64 = cy + s*ct/TM_MAGIC_4096 392 let z: i64 = cz + s*st/TM_MAGIC_4096*sp/TM_MAGIC_4096 393 tm_vert(x,y,z) 394 sg = sg + 1 395 } 396 r = r + 1 397 } 398 let w: i64 = segs+1 399 r = 0 400 while r < rings { 401 var sg: i64 = 0 402 while sg < segs { 403 let v0: i64 = b + r*w + sg 404 let v1: i64 = b + r*w + sg+1 405 let v2: i64 = b + (r+1)*w + sg 406 let v3: i64 = b + (r+1)*w + sg+1 407 tm_quad(v0,v1,v3,v2,col) 408 sg = sg + 1 409 } 410 r = r + 1 411 } 412 return 0 413} 414// ★compose a RECOGNIZABLE thing from primitives: a low-poly humanoid (boxes) -- the shape kernel applied to a figure, 415// the angular counterpart to the ellipsoid being. Proves we can build real things from triangles. 416func tm_figure(cx: i64, cy: i64, cz: i64, col: i64) -> i64 { 417 let skin: i64 = 214 + 170*256 + 150*TM_MAGIC_65536 418 tm_cube(cx, cy+300, cz, 78, skin) // head 419 tm_cube(cx, cy+120, cz, 96, col) // torso (taller via two stacked -> keep one for low-poly) 420 tm_cube(cx, cy-10, cz, 88, col) // lower torso 421 tm_cube(cx-150, cy+150, cz, 42, skin) // L upper arm 422 tm_cube(cx+150, cy+150, cz, 42, skin) // R upper arm 423 tm_cube(cx-150, cy-30, cz, 40, skin) // L forearm 424 tm_cube(cx+150, cy-30, cz, 40, skin) // R forearm 425 tm_cube(cx-56, cy-230, cz, 52, col) // L thigh 426 tm_cube(cx+56, cy-230, cz, 52, col) // R thigh 427 tm_cube(cx-56, cy-410, cz, 48, col) // L shin 428 tm_cube(cx+56, cy-410, cz, 48, col) // R shin 429 return 0 430} 431 432// ---- the rasterizer: transform -> project -> barycentric fill + Z-BUFFER + flat per-face shading ---- 433// fb, zbuf sized W*H. yaw/pitch it4096. camz shifts model into +Z. Background left as-is by caller. 434func trimesh_render(fb: *i64, zbuf: *i64, W: i64, H: i64, yaw: i64, pitch: i64, camz: i64, focal: i64, smooth: i64) -> i64 { 435 let cyw: i64 = it_cos4096(yaw); let syw: i64 = it_sin4096(yaw) 436 let cpt: i64 = it_cos4096(pitch); let spt: i64 = it_sin4096(pitch) 437 // light dir (view space), upper-left-front 438 let lx: i64 = 0-TM_MAGIC_1300; let ly: i64 = TM_MAGIC_2400; let lz: i64 = 0-TM_MAGIC_3000 439 let llen: i64 = tm_isqrt(lx*lx+ly*ly+lz*lz) + 1 440 // per-vertex projected screen + view-space 441 let SX: *i64 = sys_mmap(TM_NV*8) as *i64 442 let SY: *i64 = sys_mmap(TM_NV*8) as *i64 443 let VX: *i64 = sys_mmap(TM_NV*8) as *i64 444 let VY: *i64 = sys_mmap(TM_NV*8) as *i64 445 let VZ: *i64 = sys_mmap(TM_NV*8) as *i64 446 let mvx: *i64 = TM_VX as *i64; let mvy: *i64 = TM_VY as *i64; let mvz: *i64 = TM_VZ as *i64 447 var i: i64 = 0 448 while i < TM_NV { 449 let x: i64 = mvx[i]; let y: i64 = mvy[i]; let z: i64 = mvz[i] 450 let x1: i64 = (x*cyw + z*syw)/TM_MAGIC_4096 451 let z1: i64 = (0-x*syw + z*cyw)/TM_MAGIC_4096 452 let y2: i64 = (y*cpt - z1*spt)/TM_MAGIC_4096 453 let z2: i64 = (y*spt + z1*cpt)/TM_MAGIC_4096 454 let vz: i64 = z2 + camz 455 VX[i] = x1; VY[i] = y2; VZ[i] = vz 456 if vz > 20 { SX[i] = W/2 + focal*x1/vz; SY[i] = H/2 - focal*y2/vz } 457 else { SX[i] = 0-TM_MAGIC_100000; SY[i] = 0-TM_MAGIC_100000 } 458 i = i + 1 459 } 460 // ★F3 GOURAUD: per-vertex Lambert (rotate model normal into view space, dot with light). fx1024, 0..1024. 461 // ★F3 BLINN-PHONG: per-vertex specular = (N.H)^16 * TM_SPEC, H = halfway(light, view); view dir toward camera = -z. 462 let LAMV: *i64 = sys_mmap(TM_NV*8) as *i64 463 let SPECV: *i64 = sys_mmap(TM_NV*8) as *i64 464 let hx: i64 = lx*TM_MAGIC_1024/llen 465 let hy: i64 = ly*TM_MAGIC_1024/llen 466 let hz: i64 = lz*TM_MAGIC_1024/llen - TM_MAGIC_1024 // + Vhat=(0,0,-1) 467 let hlen: i64 = tm_isqrt(hx*hx + hy*hy + hz*hz) + 1 468 if smooth >= 1 { // needed for BOTH smooth==1 (Gouraud) and smooth==2 (per-vertex colour) 469 let mnx: *i64 = TM_VNX as *i64; let mny: *i64 = TM_VNY as *i64; let mnz: *i64 = TM_VNZ as *i64 470 var q: i64 = 0 471 while q < TM_NV { 472 let mx: i64 = mnx[q]; let my: i64 = mny[q]; let mz: i64 = mnz[q] 473 let nx1: i64 = (mx*cyw + mz*syw)/TM_MAGIC_4096 474 let nz1: i64 = (0-mx*syw + mz*cyw)/TM_MAGIC_4096 475 let nvy: i64 = (my*cpt - nz1*spt)/TM_MAGIC_4096 476 let nvz: i64 = (my*spt + nz1*cpt)/TM_MAGIC_4096 477 let nvx: i64 = nx1 478 var lam: i64 = (nvx*lx + nvy*ly + nvz*lz) / (4*llen) // |n|=TM_MAGIC_4096 -> /(TM_MAGIC_4096*llen/TM_MAGIC_1024)=/(4*llen) 479 if lam < 0 { lam = 0 } 480 if lam > TM_MAGIC_1024 { lam = TM_MAGIC_1024 } 481 LAMV[q] = lam 482 var sv: i64 = 0 483 if TM_SPEC > 0 { 484 var sc: i64 = (nvx*hx + nvy*hy + nvz*hz) / (4*hlen) // N.H, fx1024 (|N|=TM_MAGIC_4096) 485 if sc < 0 { sc = 0 } 486 if sc > TM_MAGIC_1024 { sc = TM_MAGIC_1024 } 487 let c2: i64 = sc*sc/TM_MAGIC_1024; let c4: i64 = c2*c2/TM_MAGIC_1024; let c8: i64 = c4*c4/TM_MAGIC_1024; let c16: i64 = c8*c8/TM_MAGIC_1024 488 sv = TM_SPEC*c16/TM_MAGIC_1024 489 } 490 SPECV[q] = sv 491 q = q + 1 492 } 493 } 494 let TA: *i64 = TM_TA as *i64; let TB: *i64 = TM_TB as *i64; let TC: *i64 = TM_TC as *i64; let TCOL: *i64 = TM_TCOL as *i64 495 let VCOL: *i64 = TM_VCOL as *i64 496 var t: i64 = 0 497 while t < TM_NT { 498 let i0: i64 = TA[t]; let i1: i64 = TB[t]; let i2: i64 = TC[t] 499 let ax: i64 = SX[i0]; let ay: i64 = SY[i0] 500 let bx: i64 = SX[i1]; let by: i64 = SY[i1] 501 let cx: i64 = SX[i2]; let cy: i64 = SY[i2] 502 var skip: i64 = 0 503 if ax < 0-TM_MAGIC_90000 { skip = 1 } if bx < 0-TM_MAGIC_90000 { skip = 1 } if cx < 0-TM_MAGIC_90000 { skip = 1 } 504 let area: i64 = (bx-ax)*(cy-ay) - (by-ay)*(cx-ax) 505 if area == 0 { skip = 1 } 506 if skip == 0 { 507 // face normal (view space), oriented toward the camera (-Z) 508 let ux: i64 = VX[i1]-VX[i0]; let uy: i64 = VY[i1]-VY[i0]; let uz: i64 = VZ[i1]-VZ[i0] 509 let wx: i64 = VX[i2]-VX[i0]; let wy: i64 = VY[i2]-VY[i0]; let wz: i64 = VZ[i2]-VZ[i0] 510 var nx: i64 = uy*wz - uz*wy; var ny: i64 = uz*wx - ux*wz; var nz: i64 = ux*wy - uy*wx 511 if nz > 0 { nx = 0-nx; ny = 0-ny; nz = 0-nz } 512 let nl: i64 = tm_isqrt(nx*nx+ny*ny+nz*nz) + 1 513 var lam: i64 = (nx*lx + ny*ly + nz*lz) / (nl*llen/TM_MAGIC_1024) 514 if lam < 0 { lam = 0 } 515 let col: i64 = TCOL[t] 516 let br: i64 = col&255; let bg: i64 = (col>>8)&255; let bb: i64 = (col>>16)&255 517 let sh: i64 = 42 + 86*lam/TM_MAGIC_1024 518 var cr: i64 = br*sh/128; var cg: i64 = bg*sh/128; var cb: i64 = bb*sh/128 519 if cr>255 {cr=255} if cg>255 {cg=255} if cb>255 {cb=255} 520 let fcol: i64 = cr + cg*256 + cb*TM_MAGIC_65536 521 // per-vertex colours (smooth==2 multi-material meshes) 522 let k0: i64 = VCOL[i0]; let k1: i64 = VCOL[i1]; let k2: i64 = VCOL[i2] 523 let k0r: i64 = k0&255; let k0g: i64 = (k0>>8)&255; let k0b: i64 = (k0>>16)&255 524 let k1r: i64 = k1&255; let k1g: i64 = (k1>>8)&255; let k1b: i64 = (k1>>16)&255 525 let k2r: i64 = k2&255; let k2g: i64 = (k2>>8)&255; let k2b: i64 = (k2>>16)&255 526 // per-vertex UVs (Q16) for image-texture sampling 527 let TVU: *i64 = TM_VU as *i64 528 let TVV: *i64 = TM_VV as *i64 529 let u0: i64 = TVU[i0]; let u1: i64 = TVU[i1]; let u2: i64 = TVU[i2] 530 let w0: i64 = TVV[i0]; let w1: i64 = TVV[i1]; let w2: i64 = TVV[i2] 531 // bbox (clamped) 532 var minx: i64 = tm_min3(ax,bx,cx); var maxx: i64 = tm_max3(ax,bx,cx) 533 var miny: i64 = tm_min3(ay,by,cy); var maxy: i64 = tm_max3(ay,by,cy) 534 if minx < 0 { minx = 0 } if miny < 0 { miny = 0 } 535 if maxx > W-1 { maxx = W-1 } if maxy > H-1 { maxy = H-1 } 536 var py: i64 = miny 537 while py <= maxy { 538 var px: i64 = minx 539 while px <= maxx { 540 let e0: i64 = (cx-bx)*(py-by) - (cy-by)*(px-bx) // weight of vertex i0 541 let e1: i64 = (ax-cx)*(py-cy) - (ay-cy)*(px-cx) // weight of i1 542 let e2: i64 = (bx-ax)*(py-ay) - (by-ay)*(px-ax) // weight of i2 543 var inside: i64 = 0 544 if area > 0 { if e0>=0 { if e1>=0 { if e2>=0 { inside=1 } } } } 545 else { if e0<=0 { if e1<=0 { if e2<=0 { inside=1 } } } } 546 if inside == 1 { 547 let zz: i64 = (e0*VZ[i0] + e1*VZ[i1] + e2*VZ[i2]) / area // interpolated view depth 548 let idx: i64 = py*W + px 549 if zz < zbuf[idx] { 550 zbuf[idx] = zz 551 if smooth == 1 { 552 var l2: i64 = (e0*LAMV[i0] + e1*LAMV[i1] + e2*LAMV[i2]) / area // interpolated (Gouraud) 553 if l2 < 0 { l2 = 0 } 554 var sp2: i64 = (e0*SPECV[i0] + e1*SPECV[i1] + e2*SPECV[i2]) / area // specular highlight 555 if sp2 < 0 { sp2 = 0 } 556 if TM_SHON == 1 { // ★Q2 sun shadow 557 let wx2: i64 = (e0*mvx[i0] + e1*mvx[i1] + e2*mvx[i2]) / area 558 let wy2: i64 = (e0*mvy[i0] + e1*mvy[i1] + e2*mvy[i2]) / area 559 let wz2: i64 = (e0*mvz[i0] + e1*mvz[i1] + e2*mvz[i2]) / area 560 let sf2: i64 = tm_shadow_at(wx2, wy2, wz2) 561 l2 = l2*sf2/TM_MAGIC_1024 562 sp2 = sp2*sf2/TM_MAGIC_1024 563 } 564 let s2: i64 = 42 + 86*l2/TM_MAGIC_1024 565 var r2: i64 = br*s2/128 + sp2; var g2: i64 = bg*s2/128 + sp2; var b2: i64 = bb*s2/128 + sp2 566 if r2>255 {r2=255} if g2>255 {g2=255} if b2>255 {b2=255} 567 fb[idx] = r2 + g2*256 + b2*TM_MAGIC_65536 568 } else { if smooth == 2 { 569 var l3: i64 = (e0*LAMV[i0] + e1*LAMV[i1] + e2*LAMV[i2]) / area // Gouraud shade 570 if l3 < 0 { l3 = 0 } 571 var sp3: i64 = (e0*SPECV[i0] + e1*SPECV[i1] + e2*SPECV[i2]) / area 572 if sp3 < 0 { sp3 = 0 } 573 var mr: i64 = (e0*k0r + e1*k1r + e2*k2r) / area // interpolated per-vertex colour 574 var mg: i64 = (e0*k0g + e1*k1g + e2*k2g) / area 575 var mb: i64 = (e0*k0b + e1*k1b + e2*k2b) / area 576 if TM_IMG != 0 { // ★IMAGE texture via interpolated UV (REPEAT wrap) 577 var iu: i64 = ((e0*u0 + e1*u1 + e2*u2) / area) & TM_MAGIC_65535 578 var iv: i64 = ((e0*w0 + e1*w1 + e2*w2) / area) & TM_MAGIC_65535 579 let tx: i64 = iu*TM_IMGW/TM_MAGIC_65536 580 let ty: i64 = iv*TM_IMGH/TM_MAGIC_65536 581 let timg: *u8 = TM_IMG as *u8 582 let toff: i64 = (ty*TM_IMGW + tx)*3 583 mr = timg[toff] as i64 584 mg = timg[toff+1] as i64 585 mb = timg[toff+2] as i64 586 } 587 var needw: i64 = 0 588 if TM_TEX > 0 { needw = 1 } 589 if TM_SHON == 1 { needw = 1 } 590 if needw == 1 { 591 let wx: i64 = (e0*mvx[i0] + e1*mvx[i1] + e2*mvx[i2]) / area 592 let wy: i64 = (e0*mvy[i0] + e1*mvy[i1] + e2*mvy[i2]) / area 593 let wz: i64 = (e0*mvz[i0] + e1*mvz[i1] + e2*mvz[i2]) / area 594 if TM_TEX > 0 { 595 // ★Q1 MULTI-OCTAVE ALBEDO (not one grey mottle): 3 octaves of brightness 596 // + a decorrelated HUE octave (warm/cool tonal variation) -- the natural 597 // self-similar detail the photoreal judge (ns_assess MSCN/scale) measures. 598 let n1: i64 = tm_noise3(wx, wy, wz, TM_TEX as i64) 599 let n2: i64 = tm_noise3(wx, wy+TM_MAGIC_37777, wz, TM_TEX/3+1) 600 let n3: i64 = tm_noise3(wx+TM_MAGIC_91131, wy, wz, TM_TEX/9+1) 601 let tf: i64 = 620 + (n1*3 + n2*2 + n3)*470/TM_MAGIC_6144 // 0.60..1.06 602 let hue: i64 = tm_noise3(wx, wy+TM_MAGIC_81111, wz, TM_TEX*2) - 512 // warm/cool +- 603 mr = mr*tf/TM_MAGIC_1024 + hue*22/512 604 mg = mg*tf/TM_MAGIC_1024 + hue*8/512 605 mb = mb*tf/TM_MAGIC_1024 - hue*20/512 606 if mr < 0 { mr = 0 } 607 if mg < 0 { mg = 0 } 608 if mb < 0 { mb = 0 } 609 } 610 if TM_SHON == 1 { // ★Q2 sun shadow 611 let sf: i64 = tm_shadow_at(wx, wy, wz) 612 l3 = l3*sf/TM_MAGIC_1024 613 sp3 = sp3*sf/TM_MAGIC_1024 614 } 615 } 616 let s3: i64 = 42 + 86*l3/TM_MAGIC_1024 617 var r3: i64 = mr*s3/128 + sp3; var g3: i64 = mg*s3/128 + sp3; var b3: i64 = mb*s3/128 + sp3 618 if r3>255 {r3=255} if g3>255 {g3=255} if b3>255 {b3=255} 619 fb[idx] = r3 + g3*256 + b3*TM_MAGIC_65536 620 } else { if smooth == 3 { 621 // UNLIT per-vertex colour (radiosity display: the vertex colour IS the lighting) 622 var pr: i64 = (e0*k0r + e1*k1r + e2*k2r) / area 623 var pg: i64 = (e0*k0g + e1*k1g + e2*k2g) / area 624 var pb: i64 = (e0*k0b + e1*k1b + e2*k2b) / area 625 if pr<0 {pr=0} if pg<0 {pg=0} if pb<0 {pb=0} 626 if pr>255 {pr=255} if pg>255 {pg=255} if pb>255 {pb=255} 627 fb[idx] = pr + pg*256 + pb*TM_MAGIC_65536 628 } else { fb[idx] = fcol } } } 629 } 630 } 631 px = px + 1 632 } 633 py = py + 1 634 } 635 } 636 t = t + 1 637 } 638 return 0 639} 640// clear a z-buffer to +far 641func trimesh_zclear(zbuf: *i64, n: i64) -> i64 { var i: i64=0; while i<n { zbuf[i] = TM_MAGIC_2000000000; i=i+1 } return 0 } 642// ★F3 ANTI-ALIASING (SSAA): render at 2x resolution, box-downsample into fb -> smooth edges. fb must be pre-cleared to bg. 643func trimesh_render_aa(fb: *i64, W: i64, H: i64, yaw: i64, pitch: i64, camz: i64, focal: i64, smooth: i64) -> i64 { 644 let W2: i64 = W*2; let H2: i64 = H*2 645 let big: *i64 = sys_mmap(W2*H2*8) as *i64 646 let bz: *i64 = sys_mmap(W2*H2*8) as *i64 647 let bg: i64 = fb[0] 648 var i: i64 = 0 649 while i < W2*H2 { big[i] = bg; i = i+1 } 650 trimesh_zclear(bz, W2*H2) 651 trimesh_render(big, bz, W2, H2, yaw, pitch, camz, focal*2, smooth) // focal*2 keeps the 2x framing identical 652 var y: i64 = 0 653 while y < H { 654 var x: i64 = 0 655 while x < W { 656 let p0: i64 = big[(2*y)*W2 + 2*x]; let p1: i64 = big[(2*y)*W2 + 2*x+1] 657 let p2: i64 = big[(2*y+1)*W2 + 2*x]; let p3: i64 = big[(2*y+1)*W2 + 2*x+1] 658 let r: i64 = ((p0&255)+(p1&255)+(p2&255)+(p3&255))/4 659 let g: i64 = (((p0>>8)&255)+((p1>>8)&255)+((p2>>8)&255)+((p3>>8)&255))/4 660 let b: i64 = (((p0>>16)&255)+((p1>>16)&255)+((p2>>16)&255)+((p3>>16)&255))/4 661 fb[y*W + x] = r + g*256 + b*TM_MAGIC_65536 662 x = x + 1 663 } 664 y = y + 1 665 } 666 return 0 667}