code wiki / (root) / nx_anatstack.nx

nx_anatstack.nx source

↩ module page · 410 lines · 26292 B

1// nx_anatstack.nx -- ★LAYERED ANATOMICAL MODEL (operator 2026-07-08: "stop building circles on circles -- you 2// need the skeleton and muscles and all that so that as you get to the top layer it's anatomically PERFECT for the 3// final layer brush through"). The RIGHT inside-out architecture: don't eyeball skin ellipsoids -- build the real 4// stack and let the skin EMERGE from it: 5// LAYER 0 SKELETON -- the skull's bony forms (cranium, temporals, zygomatics, maxilla, mandible, nasal, brow) 6// LAYER 1 MUSCLE -- the facial muscles as CAPSULES (temporalis, masseter, zygomaticus, orbicularis, ...) 7// LAYER 2 FAT/SOFT -- the fat pads (malar, buccal, periorbital, labial, submental) 8// -> SKIN = the smooth-union ENVELOPE over all layers + a dermal offset = anatomically DERIVED, not sculpted. 9// Self-contained (own SDF + integer ray-march) so it doesn't destabilize the sdf_render pipeline. Capsules are a 10// real muscle shape (beyond ellipsoids). Every position is anatomically reasoned in the faceanat landmark frame 11// (y up: chin -676, mouth -390, subnasale -150, eye +178, brow +322, crown +760; -z = front/toward camera). 12// license_tier: ORIGINAL 13import "nx_syscalls.nx" 14import "nx_itrig.nx" 15const AS_MAGIC_1024: i64 = 1024 16const AS_MAGIC_374761393: i64 = 374761393 17const AS_MAGIC_668265263: i64 = 668265263 18const AS_MAGIC_1274126177: i64 = 1274126177 19const AS_MAGIC_2654435761: i64 = 2654435761 20const AS_MAGIC_1013904223: i64 = 1013904223 21const AS_MAGIC_4096: i64 = 4096 22const AS_MAGIC_1048576: i64 = 1048576 23const AS_MAGIC_65536: i64 = 65536 24const AS_MAGIC_1000000: i64 = 1000000 25const AS_MAGIC_1000000000: i64 = 1000000000 26const AS_MAGIC_1400: i64 = 1400 27const AS_MAGIC_16777215: i64 = 16777215 28 29const AS_STRIDE: i64 = 12 // per-part: layer,type, p1(3), r(3), p2(3), _ 30const AS_BONE: i64 = 0 31const AS_MUSCLE: i64 = 1 32const AS_FAT: i64 = 2 33const AS_ELLIP: i64 = 0 34const AS_CAPS: i64 = 1 35const AS_W: i64 = 512 36const AS_H: i64 = 512 37const AS_FOCAL: i64 = 640 38const AS_K: i64 = 46 // smooth-union blend radius (units) 39 40func as_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 } 41func as_iabs(v: i64) -> i64 { if v < 0 { return 0-v } return v } 42func as_min(a: i64, b: i64) -> i64 { if a < b { return a } return b } 43func as_clamp(v: i64, lo: i64, hi: i64) -> i64 { if v<lo {return lo} if v>hi {return hi} return v } 44 45func as_ellip(P: *i64, i: i64, cx: i64, cy: i64, cz: i64, rx: i64, ry: i64, rz: i64, layer: i64) -> i64 { 46 P[i]=layer; P[i+1]=AS_ELLIP; P[i+2]=cx; P[i+3]=cy; P[i+4]=cz; P[i+5]=rx; P[i+6]=ry; P[i+7]=rz; P[i+8]=0; P[i+9]=0; P[i+10]=0; P[i+11]=0 47 return i + AS_STRIDE 48} 49func as_caps(P: *i64, i: i64, ax: i64, ay: i64, az: i64, bx: i64, by: i64, bz: i64, r: i64, layer: i64) -> i64 { 50 P[i]=layer; P[i+1]=AS_CAPS; P[i+2]=ax; P[i+3]=ay; P[i+4]=az; P[i+5]=r; P[i+6]=0; P[i+7]=0; P[i+8]=bx; P[i+9]=by; P[i+10]=bz; P[i+11]=0 51 return i + AS_STRIDE 52} 53 54// ★PHASE-1 GEOMETRY: per-part COLOUR + flag BITS in the spare slot (11). Backwards-compatible: 0 (the default) 55// keeps today's behaviour exactly. 56// bits 0..23 rgb -> part colour (0 = inherit the base skin colour) 57// bit 24 AS_FEAT -> FEATURE part (eye/lip/nose/ear): CRISP union (plain min, no smooth-mush) + 58// inflate/4, so small features stay sharp instead of ballooning under the dermal 59// offset; rgb 0 + flag = a crisp feature in the skin colour (e.g. a nose). 60// bit 25 AS_FUZZ -> FUZZY part (fur/fleece): the surface is DISPLACED by integer 3D value-noise -> 61// detail lives in the GEOMETRY (silhouette + shading), not just colour. The noise 62// is only evaluated near the part's surface (pd < 26) so far parts cost nothing. 63const AS_FEAT: i64 = 16777216 64const AS_FUZZ: i64 = 33554432 65const AS_FUZZAMP: i64 = 7 // displacement amplitude (units) 66const AS_FUZZCELL: i64 = 13 // noise lattice cell (units) 67// ★P2 MATERIALS: bits 26-28 of slot 11 = material class (0 default SKIN). Add the const to the rgb when colouring. 68// SKIN (0) soft broad sheen + light-wrap + terminator subsurface warmth 69// GLOSS (AS_GLOSS) tight bright specular (eyes, hooves, nose leather, lips) -- the life-giving glint 70// MATTE (AS_MATTE) cloth/wool: no specular, tight diffuse wrap (FUZZY parts read matte automatically) 71// HAIRM (AS_HAIRM) medium anisotropic-ish sheen 72const AS_GLOSS: i64 = 67108864 73const AS_MATTE: i64 = 134217728 74const AS_HAIRM: i64 = 201326592 75 76// ★P4-R3 NEURAL SKIN hook: an optional albedo RATIO tile (baked from the trained skin-family net) modulates 77// base-colour surfaces (prgb==0) MULTIPLICATIVELY -- adds the trained texture variation while PRESERVING the 78// seeded skin tone (a texel of 128 = x1.0). 0 = off (default, byte-identical behaviour). 79static AS_SKINTEX: i64 // tile ptr (tw*tw i64, each = ratio_r + ratio_g*256 + ratio_b*65536) 80static AS_SKINTEXW: i64 // tile width (texels) 81static AS_SKINTEXS: i64 // world units per texel 82func as_set_skintex(ptr: i64, w: i64, scale: i64) -> i64 { AS_SKINTEX = ptr; AS_SKINTEXW = w; AS_SKINTEXS = scale; return 0 } 83func as_color_last(P: *i64, i_after: i64, rgb: i64) -> i64 { P[i_after - AS_STRIDE + 11] = rgb; return i_after } 84func as_feature_last(P: *i64, i_after: i64, rgb: i64) -> i64 { P[i_after - AS_STRIDE + 11] = rgb + AS_FEAT; return i_after } 85func as_fuzz_last(P: *i64, i_after: i64) -> i64 { P[i_after - AS_STRIDE + 11] = P[i_after - AS_STRIDE + 11] + AS_FUZZ; return i_after } 86 87// ★FACE EXPRESSIONS (R-face): muscle-driven morphs on the soft-tissue parts. The facial muscles are already modelled 88// (zygomaticus, frontalis, orbicularis) so an expression = contracting one and letting the overlying fat/skin follow -- 89// anatomically DERIVED, not a surface hack. Set anat_set_expr(kind, intensity) BEFORE anatstack_build. Deterministic. 90static AS_EXPR: i64 // 0 neutral, 1 smile, 2 surprise, 3 frown 91static AS_EXPRI: i64 // intensity 0..1024 92func anat_set_expr(e: i64, inten: i64) -> i64 { AS_EXPR = e; AS_EXPRI = inten; return 0 } 93func as_shifty(P: *i64, idx: i64, dy: i64) -> i64 { let b: i64 = idx*AS_STRIDE; P[b+3]=P[b+3]+dy; if P[b+1]==AS_CAPS { P[b+9]=P[b+9]+dy } return 0 } 94func as_shiftz(P: *i64, idx: i64, dz: i64) -> i64 { let b: i64 = idx*AS_STRIDE; P[b+4]=P[b+4]+dz; if P[b+1]==AS_CAPS { P[b+10]=P[b+10]+dz } return 0 } 95func as_growx(P: *i64, idx: i64, dx: i64) -> i64 { P[idx*AS_STRIDE+5]=P[idx*AS_STRIDE+5]+dx; return 0 } 96func as_growy(P: *i64, idx: i64, dy: i64) -> i64 { P[idx*AS_STRIDE+6]=P[idx*AS_STRIDE+6]+dy; return 0 } 97// part indices in anatstack_build order: brow ridge=8, mandible body=9, L/R ramus=10/11, chin=12, frontalis=22, 98// L/R malar fat=25/26, L/R periorbital fat=29/30, upper lip=31, lower lip=32. 99func as_apply_expr(P: *i64) -> i64 { 100 let e: i64 = AS_EXPR 101 let t: i64 = AS_EXPRI 102 if e == 0 { return 0 } 103 if t == 0 { return 0 } 104 if e == 1 { // SMILE: zygomaticus lifts malar fat up+back, mouth widens+rises 105 as_shifty(P,25, 55*t/AS_MAGIC_1024); as_shiftz(P,25, 12*t/AS_MAGIC_1024) 106 as_shifty(P,26, 55*t/AS_MAGIC_1024); as_shiftz(P,26, 12*t/AS_MAGIC_1024) 107 as_growx(P,31, 40*t/AS_MAGIC_1024); as_shifty(P,31, 16*t/AS_MAGIC_1024) 108 as_growx(P,32, 36*t/AS_MAGIC_1024); as_shifty(P,32, 10*t/AS_MAGIC_1024) 109 } 110 if e == 2 { // SURPRISE: jaw drops (mouth opens), brow+frontalis raise, eyes widen 111 as_shifty(P,9, 0-90*t/AS_MAGIC_1024); as_shifty(P,10, 0-90*t/AS_MAGIC_1024); as_shifty(P,11, 0-90*t/AS_MAGIC_1024); as_shifty(P,12, 0-90*t/AS_MAGIC_1024) 112 as_shifty(P,32, 0-120*t/AS_MAGIC_1024) 113 as_shifty(P,8, 45*t/AS_MAGIC_1024); as_shifty(P,22, 40*t/AS_MAGIC_1024) 114 as_growy(P,29, 15*t/AS_MAGIC_1024); as_growy(P,30, 15*t/AS_MAGIC_1024) 115 } 116 if e == 3 { // FROWN: corrugator draws the brow down, depressors pull mouth corners down 117 as_shifty(P,8, 0-32*t/AS_MAGIC_1024); as_shifty(P,22, 0-20*t/AS_MAGIC_1024) 118 as_shifty(P,31, 0-22*t/AS_MAGIC_1024); as_shifty(P,32, 0-28*t/AS_MAGIC_1024) 119 } 120 return 0 121} 122 123// integer 3D value noise (trilinear over a hashed lattice) -> 0..AS_FUZZAMP. Deterministic. 124func as_h3(ix: i64, iy: i64, iz: i64) -> i64 { 125 var h: i64 = ix*AS_MAGIC_374761393 + iy*AS_MAGIC_668265263 + iz*AS_MAGIC_1274126177 126 h = h*AS_MAGIC_2654435761 + AS_MAGIC_1013904223 127 if h < 0 { h = 0 - h } 128 return h % AS_MAGIC_4096 129} 130func as_noise3(x: i64, y: i64, z: i64) -> i64 { 131 let xx: i64 = x + AS_MAGIC_1048576 // keep the lattice positive (divide truncates toward 0) 132 let yy: i64 = y + AS_MAGIC_1048576 133 let zz: i64 = z + AS_MAGIC_1048576 134 let ix: i64 = xx / AS_FUZZCELL 135 let iy: i64 = yy / AS_FUZZCELL 136 let iz: i64 = zz / AS_FUZZCELL 137 let fx: i64 = (xx - ix*AS_FUZZCELL) * AS_MAGIC_1024 / AS_FUZZCELL 138 let fy: i64 = (yy - iy*AS_FUZZCELL) * AS_MAGIC_1024 / AS_FUZZCELL 139 let fz: i64 = (zz - iz*AS_FUZZCELL) * AS_MAGIC_1024 / AS_FUZZCELL 140 let c00: i64 = as_h3(ix,iy,iz) + (as_h3(ix+1,iy,iz) - as_h3(ix,iy,iz)) * fx / AS_MAGIC_1024 141 let c10: i64 = as_h3(ix,iy+1,iz) + (as_h3(ix+1,iy+1,iz) - as_h3(ix,iy+1,iz)) * fx / AS_MAGIC_1024 142 let c01: i64 = as_h3(ix,iy,iz+1) + (as_h3(ix+1,iy,iz+1) - as_h3(ix,iy,iz+1)) * fx / AS_MAGIC_1024 143 let c11: i64 = as_h3(ix,iy+1,iz+1) + (as_h3(ix+1,iy+1,iz+1) - as_h3(ix,iy+1,iz+1)) * fx / AS_MAGIC_1024 144 let c0: i64 = c00 + (c10 - c00) * fy / AS_MAGIC_1024 145 let c1: i64 = c01 + (c11 - c01) * fy / AS_MAGIC_1024 146 let v: i64 = c0 + (c1 - c0) * fz / AS_MAGIC_1024 147 return v * AS_FUZZAMP / AS_MAGIC_4096 148} 149 150// ANATOMICAL HEAD. Returns the number of parts. (adult female proportions, faceanat frame) 151func anatstack_build(P: *i64) -> i64 { 152 var i: i64 = 0 153 // ---- LAYER 0: SKELETON ---- 154 i = as_ellip(P, i, 0, 175, 30, 445, 620, 545, AS_BONE) // cranial vault (Q6: canon facial thirds -- the golem cranium was too small) 155 i = as_ellip(P, i, 0, 150, 330, 360, 380, 300, AS_BONE) // occiput (back) 156 i = as_ellip(P, i, 0-430, 130, 110, 140, 300, 300, AS_BONE) // L temporal bone 157 i = as_ellip(P, i, 430, 130, 110, 140, 300, 300, AS_BONE) // R temporal bone 158 i = as_ellip(P, i, 0-300, 30, 0-360, 150, 120, 150, AS_BONE) // L zygomatic (cheekbone) 159 i = as_ellip(P, i, 300, 30, 0-360, 150, 120, 150, AS_BONE) // R zygomatic 160 i = as_ellip(P, i, 0, 0-170, 0-350, 300, 210, 240, AS_BONE) // maxilla (midface / upper jaw) 161 i = as_ellip(P, i, 0, 60, 0-540, 70, 175, 120, AS_BONE) // nasal bone (bridge) 162 i = as_ellip(P, i, 0, 322, 0-470, 300, 55, 120, AS_BONE) // brow ridge (supraorbital) 163 i = as_caps(P, i, 0-300, 0-400, 0-110, 300, 0-400, 0-110, 92, AS_BONE) // mandible body (jaw arch) 164 i = as_caps(P, i, 0-320, 0-400, 0-60, 0-345, 0-90, 90, 72, AS_BONE) // L mandible ramus 165 i = as_caps(P, i, 320, 0-400, 0-60, 345, 0-90, 90, 72, AS_BONE) // R mandible ramus 166 i = as_ellip(P, i, 0, 0-560, 0-370, 175, 150, 215, AS_BONE) // chin (mental eminence) 167 // ---- LAYER 1: MUSCLE (capsules = real elongated muscle bellies) ---- 168 i = as_caps(P, i, 0-380, 355, 40, 0-360, 70, 0-170, 88, AS_MUSCLE) // L temporalis (fan on temple) 169 i = as_caps(P, i, 380, 355, 40, 360, 70, 0-170, 88, AS_MUSCLE) // R temporalis 170 i = as_caps(P, i, 0-330, 0-360, 0-130, 0-300, 0-40, 0-250, 86, AS_MUSCLE) // L masseter (ramus->zygomatic) 171 i = as_caps(P, i, 330, 0-360, 0-130, 300, 0-40, 0-250, 86, AS_MUSCLE) // R masseter 172 i = as_caps(P, i, 0-300, 20, 0-400, 0-130, 0-350, 0-520, 44, AS_MUSCLE) // L zygomaticus major 173 i = as_caps(P, i, 300, 20, 0-400, 130, 0-350, 0-520, 44, AS_MUSCLE) // R zygomaticus major 174 i = as_ellip(P, i, 0, 0-385, 0-555, 205, 125, 95, AS_MUSCLE) // orbicularis oris (mouth ring) 175 i = as_ellip(P, i, 0-258, 180, 0-500, 195, 155, 72, AS_MUSCLE) // L orbicularis oculi (eye ring) 176 i = as_ellip(P, i, 258, 180, 0-500, 195, 155, 72, AS_MUSCLE) // R orbicularis oculi 177 i = as_ellip(P, i, 0, 430, 0-455, 380, 175, 90, AS_MUSCLE) // frontalis (forehead sheet) 178 i = as_caps(P, i, 0-240, 0-250, 0-360, 0-120, 0-300, 0-470, 66, AS_MUSCLE) // L buccinator (cheek) 179 i = as_caps(P, i, 240, 0-250, 0-360, 120, 0-300, 0-470, 66, AS_MUSCLE) // R buccinator 180 // ---- LAYER 2: FAT / SOFT TISSUE ---- 181 i = as_ellip(P, i, 0-262, 0-120, 0-475, 175, 175, 135, AS_FAT) // L malar (cheek) fat pad 182 i = as_ellip(P, i, 262, 0-120, 0-475, 175, 175, 135, AS_FAT) // R malar fat pad 183 i = as_ellip(P, i, 0-232, 0-300, 0-420, 128, 140, 120, AS_FAT) // L buccal fat 184 i = as_ellip(P, i, 232, 0-300, 0-420, 128, 140, 120, AS_FAT) // R buccal fat 185 i = as_ellip(P, i, 0-258, 118, 0-500, 185, 115, 82, AS_FAT) // L periorbital fat 186 i = as_ellip(P, i, 258, 118, 0-500, 185, 115, 82, AS_FAT) // R periorbital fat 187 i = as_ellip(P, i, 0, 0-330, 0-600, 158, 72, 92, AS_FAT) // upper lip 188 i = as_ellip(P, i, 0, 0-442, 0-585, 140, 92, 92, AS_FAT) // lower lip 189 i = as_ellip(P, i, 0, 0-600, 0-360, 205, 120, 150, AS_FAT) // submental (under chin) 190 i = as_ellip(P, i, 0, 0-120, 0-620, 86, 105, 130, AS_FAT) // nasal tip (soft, projects) 191 // ---- ★Q6 FACE SCULPT (2026-07-12): EYES (sclera+iris+pupil, crisp+gloss, proud of the socket) + LIP SEAM ---- 192 i = as_ellip(P, i, 0-258, 178, 0-560, 64, 64, 62, AS_FAT) // L eyeball (sclera) 193 i = as_feature_last(P, i, 246 + 244*256 + 238*AS_MAGIC_65536 + AS_GLOSS) 194 i = as_ellip(P, i, 258, 178, 0-560, 64, 64, 62, AS_FAT) // R eyeball 195 i = as_feature_last(P, i, 246 + 244*256 + 238*AS_MAGIC_65536 + AS_GLOSS) 196 i = as_ellip(P, i, 0-258, 178, 0-628, 30, 30, 16, AS_FAT) // L iris 197 i = as_feature_last(P, i, 96 + 118*256 + 74*AS_MAGIC_65536 + AS_GLOSS) 198 i = as_ellip(P, i, 258, 178, 0-628, 30, 30, 16, AS_FAT) // R iris 199 i = as_feature_last(P, i, 96 + 118*256 + 74*AS_MAGIC_65536 + AS_GLOSS) 200 i = as_ellip(P, i, 0-258, 178, 0-643, 13, 13, 8, AS_FAT) // L pupil 201 i = as_feature_last(P, i, 22 + 18*256 + 20*AS_MAGIC_65536 + AS_GLOSS) 202 i = as_ellip(P, i, 258, 178, 0-643, 13, 13, 8, AS_FAT) // R pupil 203 i = as_feature_last(P, i, 22 + 18*256 + 20*AS_MAGIC_65536 + AS_GLOSS) 204 i = as_ellip(P, i, 0, 0-386, 0-668, 150, 13, 46, AS_FAT) // lip seam (the mouth line) 205 i = as_feature_last(P, i, 128 + 62*256 + 58*AS_MAGIC_65536) 206 as_apply_expr(P) // ★muscle-driven expression morph (neutral = no-op) 207 return i / AS_STRIDE 208} 209 210// signed distance of one part 211func as_part_sdf(P: *i64, k: i64, x: i64, y: i64, z: i64) -> i64 { 212 let i: i64 = k * AS_STRIDE 213 if P[i+1] == AS_ELLIP { 214 let dx: i64 = x - P[i+2]; let dy: i64 = y - P[i+3]; let dz: i64 = z - P[i+4] 215 let rx: i64 = P[i+5]; let ry: i64 = P[i+6]; let rz: i64 = P[i+7] 216 let sd: i64 = as_isqrt((dx*AS_MAGIC_1024/rx)*(dx*AS_MAGIC_1024/rx) + (dy*AS_MAGIC_1024/ry)*(dy*AS_MAGIC_1024/ry) + (dz*AS_MAGIC_1024/rz)*(dz*AS_MAGIC_1024/rz)) 217 let rmin: i64 = as_min(as_min(rx, ry), rz) 218 return (sd - AS_MAGIC_1024) * rmin / AS_MAGIC_1024 219 } 220 // capsule: distance to segment p1-p2 minus radius 221 let ax: i64 = P[i+2]; let ay: i64 = P[i+3]; let az: i64 = P[i+4]; let r: i64 = P[i+5] 222 let bx: i64 = P[i+8]; let by: i64 = P[i+9]; let bz: i64 = P[i+10] 223 let pax: i64 = x-ax; let pay: i64 = y-ay; let paz: i64 = z-az 224 let bax: i64 = bx-ax; let bay: i64 = by-ay; let baz: i64 = bz-az 225 let bb: i64 = bax*bax + bay*bay + baz*baz 226 var h: i64 = 0 227 if bb > 0 { h = as_clamp((pax*bax + pay*bay + paz*baz)*AS_MAGIC_1024/bb, 0, AS_MAGIC_1024) } 228 let cx: i64 = pax - bax*h/AS_MAGIC_1024; let cy: i64 = pay - bay*h/AS_MAGIC_1024; let cz: i64 = paz - baz*h/AS_MAGIC_1024 229 return as_isqrt(cx*cx + cy*cy + cz*cz) - r 230} 231 232func as_smin(a: i64, b: i64, k: i64) -> i64 { 233 var hh: i64 = k - as_iabs(a-b) 234 if hh < 0 { hh = 0 } 235 return as_min(a, b) - hh*hh/(4*k) 236} 237 238// SDF of the model up to `maxlayer`, inflated by `inflate` (the dermal offset for the skin envelope). 239// FEATURE parts (AS_FEAT) join CRISP (plain min) with inflate/4 -- sharp eyes/lips/ears, not dermal balloons. 240// FUZZY parts (AS_FUZZ) get 3D-noise surface displacement near their surface -- fur detail in the geometry. 241func anatstack_sdf(P: *i64, np: i64, x: i64, y: i64, z: i64, maxlayer: i64, inflate: i64) -> i64 { 242 var d: i64 = AS_MAGIC_1000000 243 var k: i64 = 0 244 while k < np { 245 if P[k*AS_STRIDE] <= maxlayer { 246 let fl: i64 = P[k*AS_STRIDE+11] 247 if (fl & AS_FEAT) != 0 { 248 let pd: i64 = as_part_sdf(P, k, x, y, z) - inflate/4 249 d = as_min(d, pd) 250 } else { 251 var pd: i64 = as_part_sdf(P, k, x, y, z) - inflate 252 if (fl & AS_FUZZ) != 0 { if pd < 26 { pd = pd - as_noise3(x, y, z) } } 253 d = as_smin(d, pd, AS_K) 254 } 255 } 256 k = k + 1 257 } 258 return d 259} 260 261// FULL slot-11 word (rgb + flags + material bits) of the NEAREST part at a point (argmin of the per-part 262// inflated sdf). rgb 0 = inherit the base skin colour. 263func anatstack_nearest_fl(P: *i64, np: i64, x: i64, y: i64, z: i64, maxlayer: i64, inflate: i64) -> i64 { 264 var best: i64 = AS_MAGIC_1000000000 265 var out: i64 = 0 266 var k: i64 = 0 267 while k < np { 268 if P[k*AS_STRIDE] <= maxlayer { 269 let fl: i64 = P[k*AS_STRIDE+11] 270 var inf2: i64 = inflate 271 if (fl & AS_FEAT) != 0 { inf2 = inflate/4 } 272 let pd: i64 = as_part_sdf(P, k, x, y, z) - inf2 273 if pd < best { best = pd; out = fl } 274 } 275 k = k + 1 276 } 277 return out 278} 279 280// integer sphere-trace renderer. colour by a passed base rgb + Lambert + subsurface rim. Writes fb (AS_W*AS_H). 281func anatstack_render(P: *i64, np: i64, yaw: i64, camz: i64, maxlayer: i64, inflate: i64, cr: i64, cg: i64, cb: i64, fb: *i64) -> i64 { 282 let sy4: i64 = it_sin4096(yaw) 283 let cy4: i64 = it_cos4096(yaw) 284 let R: i64 = camz * AS_MAGIC_1024 285 let ox: i64 = 0 - sy4*R/AS_MAGIC_4096 286 let oz: i64 = 0 - cy4*R/AS_MAGIC_4096 287 // light dir (upper-front-left), fx1024 unit-ish 288 let lx: i64 = 360; let ly: i64 = 560; let lz: i64 = 0-740 // upper-RIGHT-front = matches the worldgen sun (scene-coherent shadows) 289 let ll: i64 = as_isqrt(lx*lx+ly*ly+lz*lz) 290 var py: i64 = 0 291 while py < AS_H { 292 var px: i64 = 0 293 while px < AS_W { 294 let ndcx: i64 = px - AS_W/2 295 let ndcy: i64 = AS_H/2 - py 296 // ray dir = forward*FOCAL + right*ndcx + up*ndcy ; forward=(sy4,0,cy4)/4096, right=(cy4,0,-sy4)/4096 297 var dx: i64 = sy4*AS_FOCAL/AS_MAGIC_4096 + cy4*ndcx/AS_MAGIC_4096 298 var dy: i64 = ndcy 299 var dz: i64 = cy4*AS_FOCAL/AS_MAGIC_4096 - sy4*ndcx/AS_MAGIC_4096 300 let dl: i64 = as_isqrt(dx*dx+dy*dy+dz*dz) 301 if dl > 0 { dx = dx*AS_MAGIC_1024/dl; dy = dy*AS_MAGIC_1024/dl; dz = dz*AS_MAGIC_1024/dl } 302 var t: i64 = R - AS_MAGIC_1400 303 if t < 0 { t = 0 } 304 var hit: i64 = 0 305 var step: i64 = 0 306 while step < 96 { 307 let sx: i64 = ox + dx*t/AS_MAGIC_1024 308 let sy: i64 = dy*t/AS_MAGIC_1024 309 let sz: i64 = oz + dz*t/AS_MAGIC_1024 310 let d: i64 = anatstack_sdf(P, np, sx, sy, sz, maxlayer, inflate) 311 if d < 2 { hit = 1; step = 999 } 312 else { t = t + d*7/10; if t > R + AS_MAGIC_1400 { step = 999 } } 313 if step != 999 { step = step + 1 } 314 } 315 var col: i64 = 26 + 28*256 + 44*AS_MAGIC_65536 // bg 316 if hit == 1 { 317 let hx: i64 = ox + dx*t/AS_MAGIC_1024 318 let hy: i64 = dy*t/AS_MAGIC_1024 319 let hz: i64 = oz + dz*t/AS_MAGIC_1024 320 let e: i64 = 4 321 let gx: i64 = anatstack_sdf(P,np,hx+e,hy,hz,maxlayer,inflate) - anatstack_sdf(P,np,hx-e,hy,hz,maxlayer,inflate) 322 let gy: i64 = anatstack_sdf(P,np,hx,hy+e,hz,maxlayer,inflate) - anatstack_sdf(P,np,hx,hy-e,hz,maxlayer,inflate) 323 let gz: i64 = anatstack_sdf(P,np,hx,hy,hz+e,maxlayer,inflate) - anatstack_sdf(P,np,hx,hy,hz-e,maxlayer,inflate) 324 let gl: i64 = as_isqrt(gx*gx+gy*gy+gz*gz) 325 var nnx: i64 = 0; var nny: i64 = AS_MAGIC_1024; var nnz: i64 = 0 // unit normal (fx1024) 326 if gl > 0 { nnx=gx*AS_MAGIC_1024/gl; nny=gy*AS_MAGIC_1024/gl; nnz=gz*AS_MAGIC_1024/gl } 327 var kd: i64 = (nnx*lx + nny*ly + nnz*lz)/ll // key light (Lambert), ~[-AS_MAGIC_1024,AS_MAGIC_1024] 328 if kd < 0 { kd = 0 } 329 var fd: i64 = (nnx*300 + nny*520 + nnz*640)/862 // soft fill (upper-back) 330 if fd < 0 { fd = 0 } 331 // SDF ambient occlusion: geometry near the surface along the normal -> crevices darken (real form) 332 let s1: i64 = anatstack_sdf(P,np, hx+nnx*30/AS_MAGIC_1024, hy+nny*30/AS_MAGIC_1024, hz+nnz*30/AS_MAGIC_1024, maxlayer, inflate) 333 let s2: i64 = anatstack_sdf(P,np, hx+nnx*72/AS_MAGIC_1024, hy+nny*72/AS_MAGIC_1024, hz+nnz*72/AS_MAGIC_1024, maxlayer, inflate) 334 var o1: i64 = s1*AS_MAGIC_1024/30; if o1<0{o1=0} if o1>AS_MAGIC_1024{o1=AS_MAGIC_1024} 335 var o2: i64 = s2*AS_MAGIC_1024/72; if o2<0{o2=0} if o2>AS_MAGIC_1024{o2=AS_MAGIC_1024} 336 let ao: i64 = (o1+o2)/2 337 // rim / fresnel edge sheen 338 var nv: i64 = (nnx*(0-dx) + nny*(0-dy) + nnz*(0-dz))/AS_MAGIC_1024; if nv<0{nv=0} 339 var rim: i64 = AS_MAGIC_1024-nv; rim = rim*rim/AS_MAGIC_1024 340 // ---- ★P2 MATERIALS: per-part albedo + material class ---- 341 let pfl: i64 = anatstack_nearest_fl(P, np, hx, hy, hz, maxlayer, inflate) 342 let prgb: i64 = pfl & AS_MAGIC_16777215 343 var pmat: i64 = (pfl >> 26) & 7 344 if (pfl & AS_FUZZ) != 0 { pmat = 2 } // fur reads MATTE (wool) 345 var ucr: i64 = cr 346 var ucg: i64 = cg 347 var ucb: i64 = cb 348 if prgb != 0 { ucr = prgb & 255; ucg = (prgb >> 8) & 255; ucb = (prgb >> 16) & 255 } 349 if prgb == 0 { if AS_SKINTEX != 0 { 350 // neural albedo tile: planar (x,y) sample, ratio around 128 = x1.0 (tone-preserving) 351 let tp: *i64 = AS_SKINTEX as *i64 352 let tu: i64 = ((hx + AS_MAGIC_1048576) / AS_SKINTEXS) % AS_SKINTEXW 353 let tv: i64 = ((hy + AS_MAGIC_1048576) / AS_SKINTEXS) % AS_SKINTEXW 354 let tx: i64 = tp[tv*AS_SKINTEXW + tu] 355 ucr = ucr * (tx & 255) / 128 356 ucg = ucg * ((tx >> 8) & 255) / 128 357 ucb = ucb * ((tx >> 16) & 255) / 128 358 if ucr > 255 { ucr = 255 } 359 if ucg > 255 { ucg = 255 } 360 if ucb > 255 { ucb = 255 } 361 } } 362 // diffuse LIGHT-WRAP by material (skin wraps most = the soft subsurface falloff) 363 var wrap: i64 = 300 364 if pmat == 1 { wrap = 120 } 365 if pmat == 2 { wrap = 90 } 366 if pmat == 3 { wrap = 170 } 367 var kdw: i64 = (kd + wrap)*AS_MAGIC_1024/(AS_MAGIC_1024 + wrap) 368 if kdw < 0 { kdw = 0 } 369 var lit: i64 = 24*ao/AS_MAGIC_1024 + kdw*76/AS_MAGIC_1024 + fd*20/AS_MAGIC_1024 // ambient*AO + wrapped key + fill 370 if lit < 12 { lit = 12 } 371 if lit > 135 { lit = 135 } 372 // SPECULAR: R = 2(N.L)N - L ; spec = (R.V)^n * strength, n + strength by material 373 var spec: i64 = 0 374 if kd > 0 { 375 let lux: i64 = lx*AS_MAGIC_1024/ll 376 let luy: i64 = ly*AS_MAGIC_1024/ll 377 let luz: i64 = lz*AS_MAGIC_1024/ll 378 let rx2: i64 = 2*kd*nnx/AS_MAGIC_1024 - lux 379 let ry2: i64 = 2*kd*nny/AS_MAGIC_1024 - luy 380 let rz2: i64 = 2*kd*nnz/AS_MAGIC_1024 - luz 381 var rv: i64 = (rx2*(0-dx) + ry2*(0-dy) + rz2*(0-dz))/AS_MAGIC_1024 382 if rv < 0 { rv = 0 } 383 if rv > AS_MAGIC_1024 { rv = AS_MAGIC_1024 } 384 var s: i64 = rv*rv/AS_MAGIC_1024 385 s = s*s/AS_MAGIC_1024 // ^4 = skin's soft sheen 386 var sstr: i64 = 26 387 if pmat == 1 { s = s*s/AS_MAGIC_1024; sstr = 100 } // gloss: ^8 glint (integer 388 if pmat == 2 { sstr = 5 } // normals speckle at ^16) 389 if pmat == 3 { s = s*s/AS_MAGIC_1024; sstr = 60 } // hair: ^8 sheen 390 spec = s*sstr/AS_MAGIC_1024 391 } 392 // skin SUBSURFACE: red bleeds at the light terminator (peaks where light grazes out) 393 var sssr: i64 = 0 394 if pmat == 0 { sssr = (AS_MAGIC_1024 - as_iabs(kd*2 - AS_MAGIC_1024))*16/AS_MAGIC_1024 } 395 var r: i64 = ucr*lit/100 + rim*30/AS_MAGIC_1024 + spec + sssr + kd*6/AS_MAGIC_1024 396 var g: i64 = ucg*lit/100 + rim*28/AS_MAGIC_1024 + spec*88/100 397 var b: i64 = ucb*lit/100 + rim*28/AS_MAGIC_1024 + spec*82/100 - kd*4/AS_MAGIC_1024 398 if r<0{r=0} if g<0{g=0} if b<0{b=0} 399 if r>255{r=255} if g>255{g=255} if b>255{b=255} 400 col = r + g*256 + b*AS_MAGIC_65536 401 } 402 fb[py*AS_W+px] = col 403 px = px + 1 404 } 405 py = py + 1 406 } 407 return 0 408} 409func as_w() -> i64 { return AS_W } 410func as_h() -> i64 { return AS_H }