code wiki / _hdl_build / nx_anat_sov.nx
nx_anat_sov.nx source
↩ module page · 1094 lines · 61323 B
1// nx_anat_sov.nx -- ★SOVEREIGN NishiLang render of the 8-layer anatomical being (NXMSH2), the PRODUCT half of
2// GX-11: the integer twin of the CUDA benchmark (/world/anatomy_gpu). 100% integer, own f32 decoder, own
3// barycentric z-buffer rasterizer, own PNG -- NO CUDA, NO GPU, NO 3rd-party. Measured against the CUDA
4// reference by the SAME invariants: intact occludes the interior, explode shows all 8 systems, stereo has
5// horizontal disparity. Stereo is OFF-AXIS (parallel, convergence at the model) -- an UPGRADE over the
6// benchmark's toe-in (no keystone). Reads knowledge/being_full.nxmesh; writes a PNG + a JSON stats line.
7// nx_anat_sov <mesh> <intact|explode|skin|layerN> <C|L|R> <W> <H> <out.png>
8// license_tier: ORIGINAL expect_exit: 0
9import "nx_syscalls.nx"
10import "nx_png.nx"
11const AS_LIGHT_FALLOFF_K: i64 = 40000
12const AS_PERFUSION_DEN: i64 = 100000
13const AS_BHASKARA_K: i64 = 40500
14const AS_F32_MANT_MASK: i64 = 8388607
15const AS_F32_MANT_SCALE: i64 = 8388608
16const AS_HASH_P1: i64 = 73856093
17const AS_HASH_P2: i64 = 19349663
18const AS_HASH_P3: i64 = 83492791
19const AS_I32_MAX: i64 = 2147483647
20const AS_NOISE_A: i64 = 15731
21const AS_NOISE_B: i64 = 789221
22const AS_NOISE_C: i64 = 1376312589
23const AS_Q16_ONE: i64 = 65536
24const AS_MAGIC_2000000000: i64 = 2000000000
25const AS_KEY_LIGHT_X: i64 = 5423
26const AS_KEY_LIGHT_Y: i64 = 9487
27const AS_KEY_LIGHT_Z: i64 = 12206
28const AS_FILL_LIGHT_X: i64 = 10160
29const AS_FILL_LIGHT_Y: i64 = 4064
30const AS_FILL_LIGHT_Z: i64 = 12190
31const AS_LIT_CEIL: i64 = 1280
32const AS_Q10_ONE: i64 = 1024
33
34// ★seq630 ROOT FIX: positions were decoded to WHOLE model units (mul=1), so any mesh authored in metres
35// rather than millimetres collapsed EVERY coordinate to 0 -> degenerate AABB -> rad=0 -> smE=0 -> integer
36// divide-by-zero -> SIGFPE. (Found on Vh-f-heart, whose coords all lie within +-0.5.) Decoding positions at
37// posq sub-units keeps sub-unit meshes alive; the renderer auto-frames, so absolute scale is irrelevant
38// and only precision matters. Texture lookups divide back to model units so texel size is unchanged.
39const AS_POSQ0: i64 = 4096
40const AS_TARGET: i64 = 200000
41const AS_ZFAR: i64 = 2000000000
42const AS_Q14: i64 = 16384
43// ★A CRASH ON NO ARGS IS INDISTINGUISHABLE FROM A BROKEN CAPABILITY. The usage line at the head of this
44// file declares six REQUIRED arguments -- <mesh> <mode> <eye> <W> <H> <out.png> -- so the highest argv
45// index main must dereference unconditionally is 6 (outp). argc counts argv[0], so the minimum admissible
46// argc is that index + 1. DERIVED from the arity above, not chosen: change the usage line and this moves.
47const AS_ARGV_LAST_REQUIRED: i64 = 6
48const AS_ARGC_MIN: i64 = AS_ARGV_LAST_REQUIRED + 1
49const AS_EXIT_USAGE: i64 = 3 // 0 ok - 2 operational (cannot read mesh) - 3 usage, the estate's usage/SKIP code
50
51// ★AROUSAL PERFUSION HOOK (permil, 0 = OFF and the renderer behaves EXACTLY as before).
52// Declared at the top because a module flag read by a function defined above it resolves to
53// GARBAGE in this compiler -- the documented forward-static-ref trap.
54//
55// Physiology, not a tint: rising perfusion raises dermal oxyhaemoglobin, which absorbs strongly in
56// BLUE (Soret ~415nm) and GREEN (542/577nm) and only weakly above 600nm. So the arousal signature
57// is G and B falling while R barely moves -- the same Beer-Lambert asymmetry nx_arousal_skin_lib
58// derives, applied here to the REAL generated body instead of a private SDF proxy.
59// Set via as_set_perfusion(); every existing caller leaves it 0 and is unaffected.
60static AS_PERF: i64
61func as_set_perfusion(p: i64) -> i64 {
62 var v: i64 = p
63 if v < 0 { v = 0 }
64 if v > 1000 { v = 1000 }
65 AS_PERF = v
66 return AS_PERF
67}
68func as_get_perfusion() -> i64 { return AS_PERF }
69
70// ★THE FLUSH HAS A PROPAGATION ORDER; A GLOBAL TINT IS NOT AROUSAL. The first cut of the hook above
71// scaled every skin pixel by the same AS_PERF, which reddens the whole body at once -- exactly the
72// "visually inert" defect nx_arousal_skin_lib warns about, reproduced in the renderer. Masters &
73// Johnson flush appears in a FIXED ORDER (genital -> submammary -> breast -> torso -> face ->
74// extremities), and that order is the diagnostic signal a therapist reads. Rendering it as one
75// uniform wash throws the information away.
76//
77// ★WHY STATURE FRACTION AND NOT ANCHOR LITERALS: nx_arousal_skin_lib's sk_anchor_* are fx1024
78// literals welded to the SDF proxy, which is why that lib could never be pointed at another rig.
79// Site here is derived from NORMALISED HEIGHT over the mesh's own AABB -- the same per-mille-of-
80// stature convention the anthropometric canon already uses -- so it is subject-independent BY
81// CONSTRUCTION: any body, any stature, any species with a canon reads its own sites correctly.
82// Thresholds are the SAME numbers sk_thresh() returns, so the two cannot drift.
83static AS_YMIN: i64
84static AS_YSPAN: i64
85static AS_XCTR: i64
86static AS_XHALF: i64
87func as_set_body_span(ymin: i64, ymax: i64, xctr: i64, xhalf: i64) -> i64 {
88 AS_YMIN = ymin
89 AS_YSPAN = ymax - ymin
90 if AS_YSPAN < 1 { AS_YSPAN = 1 }
91 AS_XCTR = xctr
92 AS_XHALF = xhalf
93 if AS_XHALF < 1 { AS_XHALF = 1 }
94 return AS_YSPAN
95}
96// M&J site thresholds (identical to sk_thresh) at their stature fractions, inverse-distance weighted
97// so boundaries are CONTINUOUS -- a hard per-site lookup paints rectangular bands on skin.
98// ★THE FLUSH-SITE CANON IS DATA (2026-08-14). These two functions were an if-ladder: a five-row lookup
99// table written as control flow. Ten anatomical numbers that could not be revised, reviewed or varied
100// without a rebuild -- and a sixth site could not be expressed at all, because the ladder ends in an
101// unconditional return. They now live in knowledge/anat_flush_sites.conf, and the site COUNT comes from
102// the file, so adding a site is a data edit.
103// ★THE WEIGHTING ARITHMETIC BELOW IS UNCHANGED TERM FOR TERM: the acceptance proof for this move is a
104// BIT-IDENTICAL render against the banked pre-refactor PNG.
105static AS_NSITE: i64
106static AS_ASF: i64
107static AS_ATH: i64
108static AS_EXTTH: i64
109static AS_FBTH: i64
110func as_rdint(b: *u8, pos: *i64, end: i64) -> i64 {
111 var i: i64 = pos[0]
112 var go: i64 = 1
113 while go == 1 {
114 if i >= end { go = 0 } else {
115 let c: i64 = b[i] as i64
116 if c == 45 { go = 0 } else { if c >= 48 { if c <= 57 { go = 0 } else { i = i+1 } } else { i = i+1 } }
117 }
118 }
119 var sg: i64 = 1
120 if i < end { if (b[i] as i64) == 45 { sg = 0-1; i = i+1 } }
121 var v: i64 = 0
122 var g2: i64 = 1
123 while g2 == 1 {
124 if i >= end { g2 = 0 } else {
125 let c2: i64 = b[i] as i64
126 if c2 >= 48 { if c2 <= 57 { v = v*10 + (c2-48); i = i+1 } else { g2 = 0 } } else { g2 = 0 }
127 }
128 }
129 pos[0] = i
130 return v*sg
131}
132func as_load_sites() -> i64 {
133 if AS_NSITE > 0 { return AS_NSITE }
134 let asf: *i64 = sys_mmap(64*8) as *i64
135 let ath: *i64 = sys_mmap(64*8) as *i64
136 AS_ASF = asf as i64
137 AS_ATH = ath as i64
138 let ln: *i64 = sys_mmap(16) as *i64
139 let b: *u8 = sys_read_file("knowledge/anat_flush_sites.conf" as *u8, ln)
140 if (b as i64) == 0 {
141 as_hw("ANAT-REFUSE cannot read knowledge/anat_flush_sites.conf -- the flush-site canon is REQUIRED, refusing to render on an empty site table\n" as *u8)
142 sys_exit(7)
143 }
144 let len: i64 = ln[0]
145 let pos: *i64 = sys_mmap(16) as *i64
146 var n: i64 = 0
147 var i: i64 = 0
148 var bol: i64 = 1
149 while i < len {
150 if bol == 1 {
151 if (b[i] as i64) == 65 {
152 pos[0] = i+1
153 let idx: i64 = as_rdint(b,pos,len)
154 let sfv: i64 = as_rdint(b,pos,len)
155 let thv: i64 = as_rdint(b,pos,len)
156 if idx >= 0 { if idx < 64 {
157 asf[idx] = sfv
158 ath[idx] = thv
159 if idx + 1 > n { n = idx + 1 }
160 } }
161 i = pos[0]
162 }
163 if (b[i] as i64) == 88 {
164 pos[0] = i+1
165 AS_EXTTH = as_rdint(b,pos,len)
166 AS_FBTH = as_rdint(b,pos,len)
167 i = pos[0]
168 }
169 }
170 if (b[i] as i64) == 10 { bol = 1 } else { bol = 0 }
171 i = i + 1
172 }
173 if n < 1 {
174 as_hw("ANAT-REFUSE anat_flush_sites.conf declares no A rows -- refusing to render on an empty site table\n" as *u8)
175 sys_exit(7)
176 }
177 AS_NSITE = n
178 return n
179}
180func as_anchor_sf(i: i64) -> i64 { let a: *i64 = AS_ASF as *i64; return a[i] }
181func as_anchor_th(i: i64) -> i64 { let a: *i64 = AS_ATH as *i64; return a[i] }
182// sf = stature fraction permil (0 sole, 1000 crown); latp = lateral fraction permil from midline.
183// Extremities (threshold 800) flush LAST: limbs are reached by blending toward that anchor with
184// lateral distance, so an arm held away from the torso is correctly late, not painted by height.
185func as_flush_thresh(sf: i64, latp: i64) -> i64 {
186 // the site COUNT is the file's, not a literal 5: a canon that declares a sixth site now gets one.
187 let ns: i64 = as_load_sites()
188 var num: i64 = 0
189 var den: i64 = 0
190 var i: i64 = 0
191 while i < ns {
192 let d: i64 = (sf - as_anchor_sf(i)) / 8
193 let d2: i64 = d*d + 4
194 let w: i64 = AS_LIGHT_FALLOFF_K / d2
195 num = num + w * as_anchor_th(i)
196 den = den + w
197 i = i + 1
198 }
199 var th: i64 = AS_FBTH
200 if den > 0 { th = num / den }
201 // lateral blend toward the extremity threshold
202 var lb: i64 = latp
203 if lb > 1000 { lb = 1000 }
204 if lb < 0 { lb = 0 }
205 return (th * (1000 - lb) + AS_EXTTH * lb) / 1000
206}
207
208func as_streq(a: *u8, b: *u8) -> i64 {
209 var i: i64 = 0
210 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
211 if b[i] != (0 as u8) { return 0 }
212 return 1
213}
214func as_satoi(s: *u8) -> i64 {
215 var i: i64 = 0; var n: i64 = 0; var sg: i64 = 1
216 if s[0] == (45 as u8) { sg = 0 - 1; i = 1 }
217 while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { n = n*10 + (c-48) } } i = i + 1 }
218 return n*sg
219}
220func as_hw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n+1 } sys_write(1, s, n); return 0 }
221func as_pn(v: i64) -> i64 {
222 let b: *u8 = sys_mmap(32); var x: i64 = v; var ng: i64 = 0
223 if x < 0 { ng = 1; x = 0 - x }
224 var i: i64 = 31
225 if x == 0 { b[i] = 48 as u8; i = i - 1 }
226 while x > 0 { b[i] = (48 + x%10) as u8; x = x/10; i = i - 1 }
227 if ng == 1 { b[i] = 45 as u8; i = i - 1 }
228 sys_write(1, (b as i64 + i + 1) as *u8, 31 - i); return 0
229}
230func as_rdbits(b: *u8, o: i64) -> i64 {
231 return (b[o] as i64) | ((b[o+1] as i64)<<8) | ((b[o+2] as i64)<<16) | ((b[o+3] as i64)<<24)
232}
233// decode IEEE-754 float32 at byte offset o, return round(value * mul) as integer
234func as_f32mul(b: *u8, o: i64, mul: i64) -> i64 {
235 let bits: i64 = as_rdbits(b, o)
236 let sign: i64 = (bits>>31) & 1
237 let exp: i64 = (bits>>23) & 255
238 let mant: i64 = bits & AS_F32_MANT_MASK
239 if exp == 0 { return 0 }
240 let m: i64 = (mant | AS_F32_MANT_SCALE) * mul
241 var e: i64 = exp - 127 - 23
242 var v: i64 = 0
243 if e >= 0 { v = m << e } else { let sh: i64 = 0 - e; v = (m + (1 << (sh-1))) >> sh }
244 if sign == 1 { v = 0 - v }
245 return v
246}
247func 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 }
248// ---- GX-13 integer texture engine: lattice value-noise FBM over WORLD position ----
249// The hardened grader (ns_assess2) names scale (multi-scale detail) + MSCN as our weak axes; real surfaces
250// carry detail at every octave. 4 octaves of smooth value noise = coherent (NOT white noise) so the
251// coherence gate is respected by construction.
252func tx_h3(ix: i64, iy: i64, iz: i64) -> i64 {
253 var v: i64 = (ix*AS_HASH_P1) ^ (iy*AS_HASH_P2) ^ (iz*AS_HASH_P3)
254 v = v & AS_I32_MAX
255 v = (v * (v*v*AS_NOISE_A + AS_NOISE_B) + AS_NOISE_C) & AS_I32_MAX
256 return (v >> 16) & 255
257}
258func tx_floordiv(x: i64, s: i64) -> i64 { var q: i64 = x/s; if x < 0 { if x % s != 0 { q = q - 1 } } return q }
259func tx_lerp(a: i64, b: i64, t8: i64) -> i64 { return a + (b-a)*t8/256 }
260func tx_vnoise(x: i64, y: i64, z: i64, s: i64) -> i64 {
261 let lx: i64 = tx_floordiv(x,s); let ly: i64 = tx_floordiv(y,s); let lz: i64 = tx_floordiv(z,s)
262 var fx: i64 = (x - lx*s)*256/s; var fy: i64 = (y - ly*s)*256/s; var fz: i64 = (z - lz*s)*256/s
263 fx = fx*fx*(768 - 2*fx)/AS_Q16_ONE; fy = fy*fy*(768 - 2*fy)/AS_Q16_ONE; fz = fz*fz*(768 - 2*fz)/AS_Q16_ONE
264 let c000: i64 = tx_h3(lx,ly,lz); let c100: i64 = tx_h3(lx+1,ly,lz)
265 let c010: i64 = tx_h3(lx,ly+1,lz); let c110: i64 = tx_h3(lx+1,ly+1,lz)
266 let c001: i64 = tx_h3(lx,ly,lz+1); let c101: i64 = tx_h3(lx+1,ly,lz+1)
267 let c011: i64 = tx_h3(lx,ly+1,lz+1); let c111: i64 = tx_h3(lx+1,ly+1,lz+1)
268 let a0: i64 = tx_lerp(tx_lerp(c000,c100,fx), tx_lerp(c010,c110,fx), fy)
269 let a1: i64 = tx_lerp(tx_lerp(c001,c101,fx), tx_lerp(c011,c111,fx), fy)
270 return tx_lerp(a0, a1, fz)
271}
272func tx_fbm(x: i64, y: i64, z: i64) -> i64 {
273 let n1: i64 = tx_vnoise(x,y,z,128)
274 let n2: i64 = tx_vnoise(x,y,z,40)
275 let n3: i64 = tx_vnoise(x,y,z,12)
276 let n4: i64 = tx_vnoise(x,y,z,4)
277 return (n1*3 + n2*3 + n3*2 + n4*2) / 10
278}
279// ★MULTI-VIEW judge support: Bhaskara-I degree sine, Q14, exact at 0/30/90/150/180. A single view is blind
280// to depth-axis error entirely -- body THICKNESS never appears in a front silhouette -- so the judge must be
281// able to turn the model. Our own integer trig, no float, no lookup-table literals.
282func as_sin_fill(t: *i64) -> i64 {
283 var d: i64 = 0
284 while d < 180 { let P: i64 = d*(180-d); t[d] = AS_Q14*4*P/AS_BHASKARA_K; t[d+180] = 0-t[d]; d = d+1 }
285 return 0
286}
287func as_wrap(d: i64) -> i64 { var x: i64 = d % 360; if x < 0 { x = x + 360 } return x }
288func as_min(a: i64, b: i64) -> i64 { if a < b { return a } return b }
289func as_max(a: i64, b: i64) -> i64 { if a > b { return a } return b }
290
291// ---- GX-39 piece 2: SCREEN-SPACE AMBIENT OCCLUSION over the finished z-buffer (contact darkening) ----
292// PORTED from nx_swgpu sg_ssao (the opposite-pairs design) per the debt row -- port, not rewrite.
293// Why pairs: a naive "is my neighbour nearer" test darkens every receding plane (the classic SSAO false
294// positive that greys floors). In a genuine cavity BOTH opposite samples are nearer; on a plane or slope
295// one is nearer and the other farther, so it scores zero. No normal buffer, nothing added to the raster
296// inner loop -- a pure post-pass, so it cannot regress the rasterizer.
297// SCALE-FREE bounds (bounds are DERIVED, never taste): the bias (depth-noise floor) and far cutoff
298// (a distant surface is a DIFFERENT object, not an occluder -- this is what stops silhouettes being
299// ringed with false shadow) derive from the MEASURED body depth range of the frame (lb>=0 pixels only,
300// so the receding floor cannot inflate the windows), and the ring radii derive from H, so one code path
301// grounds any mesh scale at any render size. AO still APPLIES to floor pixels -- that is exactly what
302// plants the feet (contact darkening where body meets ground).
303// Runs ONLY on the shaded path (never nmap: the mechanistic shape-judge input stays pure geometry), so
304// the geometric bench cannot move BY CONSTRUCTION -- and it is proven anyway by byte-identical
305// nx_bodybench output before/after.
306const AS_AO_STRENGTH: i64 = 140 // occ 8/8 attenuates to (256-140)/256 before the floor below
307const AS_AO_ATT_FLOOR: i64 = 64 // never crush a cavity to black (the donor's floor, kept)
308
309// separable integer box blur over an N x N grid (edge-clamped) -- the soft-shadow field builder
310func as_boxblur(src: *i64, tmp: *i64, dst: *i64, N: i64, r: i64) -> i64 {
311 let span: i64 = 2*r + 1
312 var z4: i64 = 0
313 while z4 < N {
314 var x4: i64 = 0
315 while x4 < N {
316 var acc: i64 = 0
317 var k4: i64 = 0 - r
318 while k4 <= r {
319 var xx: i64 = x4 + k4
320 if xx < 0 { xx = 0 }
321 if xx >= N { xx = N - 1 }
322 acc = acc + src[z4*N + xx]
323 k4 = k4 + 1
324 }
325 tmp[z4*N + x4] = acc/span
326 x4 = x4 + 1
327 }
328 z4 = z4 + 1
329 }
330 z4 = 0
331 while z4 < N {
332 var x4: i64 = 0
333 while x4 < N {
334 var acc: i64 = 0
335 var k4: i64 = 0 - r
336 while k4 <= r {
337 var zz: i64 = z4 + k4
338 if zz < 0 { zz = 0 }
339 if zz >= N { zz = N - 1 }
340 acc = acc + tmp[zz*N + x4]
341 k4 = k4 + 1
342 }
343 dst[z4*N + x4] = acc/span
344 x4 = x4 + 1
345 }
346 z4 = z4 + 1
347 }
348 return 0
349}
350func as_ssao(fb: *i64, zb: *i64, lb: *i64, W2: i64, H2: i64) -> i64 {
351 let npx2: i64 = W2*H2
352 var zmn: i64 = AS_ZFAR; var zmx: i64 = 0
353 var q2: i64 = 0
354 while q2 < npx2 {
355 if lb[q2] >= 0 {
356 let d0: i64 = zb[q2]
357 if d0 < zmn { zmn = d0 }
358 if d0 > zmx { zmx = d0 }
359 }
360 q2 = q2 + 1
361 }
362 let rng: i64 = zmx - zmn
363 if rng < 128 { return 0 } // degenerate/flat frame: nothing meaningful to occlude
364 let bias: i64 = rng/128 // below ~0.8pct of body depth it is depth noise, not a cavity
365 let far2: i64 = rng/6 // beyond ~17pct of body depth it is a different surface
366 var r1: i64 = H2/128; if r1 < 2 { r1 = 2 } // tight ring: contact seams
367 var rw: i64 = H2/56; if rw < 4 { rw = 4 } // wide ring: broad cavities
368 let d1r: i64 = r1*707/1000 + 1
369 let dwr: i64 = rw*707/1000 + 1
370 let ax: *i64 = sys_mmap(16*8) as *i64
371 let ay: *i64 = sys_mmap(16*8) as *i64
372 // 8 opposite pairs: entries s and s+8 are mirrors (the donor's layout, radii derived above)
373 ax[0]=r1; ay[0]=0; ax[1]=d1r; ay[1]=d1r; ax[2]=0; ay[2]=r1; ax[3]=0-d1r; ay[3]=d1r
374 ax[4]=rw; ay[4]=0; ax[5]=dwr; ay[5]=dwr; ax[6]=0; ay[6]=rw; ax[7]=0-dwr; ay[7]=dwr
375 ax[8]=0-r1; ay[8]=0; ax[9]=0-d1r; ay[9]=0-d1r; ax[10]=0; ay[10]=0-r1; ax[11]=d1r; ay[11]=0-d1r
376 ax[12]=0-rw; ay[12]=0; ax[13]=0-dwr; ay[13]=0-dwr; ax[14]=0; ay[14]=0-rw; ax[15]=dwr; ay[15]=0-dwr
377 var y3: i64 = 0
378 while y3 < H2 {
379 var x3: i64 = 0
380 while x3 < W2 {
381 let idx3: i64 = y3*W2 + x3
382 let d: i64 = zb[idx3]
383 if d < AS_ZFAR {
384 var occ: i64 = 0
385 var s: i64 = 0
386 while s < 8 {
387 var near1: i64 = 0
388 var near2: i64 = 0
389 let x1: i64 = x3 + ax[s]
390 let y1: i64 = y3 + ay[s]
391 if x1 >= 0 { if x1 < W2 { if y1 >= 0 { if y1 < H2 {
392 let dd1: i64 = zb[y1*W2 + x1]
393 if dd1 < AS_ZFAR { let df: i64 = d - dd1; if df > bias { if df < far2 { near1 = 1 } } }
394 }}}}
395 let x2: i64 = x3 + ax[s+8]
396 let y2: i64 = y3 + ay[s+8]
397 if x2 >= 0 { if x2 < W2 { if y2 >= 0 { if y2 < H2 {
398 let dd2: i64 = zb[y2*W2 + x2]
399 if dd2 < AS_ZFAR { let df2: i64 = d - dd2; if df2 > bias { if df2 < far2 { near2 = 1 } } }
400 }}}}
401 if near1 == 1 { if near2 == 1 { occ = occ + 1 } } // BOTH sides nearer = a real cavity
402 s = s + 1
403 }
404 if occ > 0 {
405 var att: i64 = 256 - occ*AS_AO_STRENGTH/8
406 if att < AS_AO_ATT_FLOOR { att = AS_AO_ATT_FLOOR }
407 let v: i64 = fb[idx3]
408 let r: i64 = (v % 256)*att/256
409 let g: i64 = ((v/256) % 256)*att/256
410 let b: i64 = ((v/AS_Q16_ONE) % 256)*att/256
411 fb[idx3] = r + g*256 + b*AS_Q16_ONE
412 }
413 }
414 x3 = x3 + 1
415 }
416 y3 = y3 + 1
417 }
418 return 0
419}
420
421// ---- GX-39 piece 4: DEPTH OF FIELD keyed on the finished z-buffer ----
422// The last light-transport piece the debt names. Photographic focus: the BODY (the measured lb>=0
423// depth band) stays sharp; floor and backdrop blur with distance from the focus plane, radius derived
424// from H and the measured body depth range (bounds-derived, never taste). Gather guard: a sample only
425// contributes if ITS OWN circle of confusion reaches the target pixel, so the sharp subject can never
426// smear into the background (the classic DoF halo artifact, foreclosed by construction). Shaded path
427// only -- the nmap shape-judge input stays pure geometry.
428func as_dof(fb: *i64, zb: *i64, lb: *i64, W2: i64, H2: i64) -> i64 {
429 let npx2: i64 = W2*H2
430 var zmn: i64 = AS_ZFAR; var zmx: i64 = 0; var zsum: i64 = 0; var zn: i64 = 0
431 var q2: i64 = 0
432 while q2 < npx2 {
433 if lb[q2] >= 0 {
434 let d0: i64 = zb[q2]
435 if d0 < zmn { zmn = d0 }
436 if d0 > zmx { zmx = d0 }
437 zsum = zsum + d0; zn = zn + 1
438 }
439 q2 = q2 + 1
440 }
441 if zn < 1 { return 0 }
442 let rng: i64 = zmx - zmn
443 if rng < 128 { return 0 }
444 let focus: i64 = zsum/zn
445 var maxr: i64 = H2/192; if maxr < 2 { maxr = 2 }
446 let cocb: *i64 = sys_mmap(npx2*8) as *i64 // circle of confusion per pixel (0 = in focus)
447 let srcb: *i64 = sys_mmap(npx2*8) as *i64 // blur reads the unmodified frame
448 q2 = 0
449 while q2 < npx2 {
450 srcb[q2] = fb[q2]
451 var dz: i64 = zb[q2] - focus
452 if dz < 0 { dz = 0 - dz }
453 var coc: i64 = 0
454 if dz > rng/2 { coc = (dz - rng/2)*maxr/(rng*2) }
455 if coc > maxr { coc = maxr }
456 cocb[q2] = coc
457 q2 = q2 + 1
458 }
459 var y3: i64 = 0
460 while y3 < H2 {
461 var x3: i64 = 0
462 while x3 < W2 {
463 let idx3: i64 = y3*W2 + x3
464 let c0: i64 = cocb[idx3]
465 if c0 >= 1 {
466 var ar: i64 = 0; var ag: i64 = 0; var ab: i64 = 0; var an: i64 = 0
467 var dy6: i64 = 0 - c0
468 while dy6 <= c0 {
469 var dx6: i64 = 0 - c0
470 while dx6 <= c0 {
471 let x6: i64 = x3 + dx6
472 let y6: i64 = y3 + dy6
473 if x6 >= 0 { if x6 < W2 { if y6 >= 0 { if y6 < H2 {
474 let si: i64 = y6*W2 + x6
475 var rr: i64 = dx6; if rr < 0 { rr = 0 - rr }
476 var r7: i64 = dy6; if r7 < 0 { r7 = 0 - r7 }
477 if r7 > rr { rr = r7 }
478 if cocb[si] >= rr {
479 let v: i64 = srcb[si]
480 ar = ar + (v % 256)
481 ag = ag + ((v/256) % 256)
482 ab = ab + ((v/AS_Q16_ONE) % 256)
483 an = an + 1
484 }
485 }}}}
486 dx6 = dx6 + 1
487 }
488 dy6 = dy6 + 1
489 }
490 if an > 0 { fb[idx3] = ar/an + (ag/an)*256 + (ab/an)*AS_Q16_ONE }
491 }
492 x3 = x3 + 1
493 }
494 y3 = y3 + 1
495 }
496 return 0
497}
498
499func main(argc: i64, argv: *i64) -> i64 {
500 // ★GUARD argc BEFORE THE FIRST argv DEREFERENCE. Without this, `nx_anat_sov` with no arguments read
501 // argv[1] off the end of the vector and died with SIGSEGV -- and a registered organ that crashes on
502 // its usage path reads to every caller as "this capability is broken", not as "you called it wrong".
503 if argc < AS_ARGC_MIN {
504 as_hw("usage: nx_anat_sov <mesh> <intact|explode|skin|layerN> <C|L|R> <W> <H> <out.png> [nmap] [yawdeg] [fit] [perfusion_permil]\n" as *u8)
505 return AS_EXIT_USAGE
506 }
507 let meshp: *u8 = argv[1] as *u8
508 let mode: *u8 = argv[2] as *u8
509 // argv[7]=="nmap" -> emit a NORMAL MAP instead of a shaded image (mechanistic shape-judge input)
510 var nmap: i64 = 0
511 if argc > 7 { let np7: *u8 = argv[7] as *u8; if np7[0] == (110 as u8) { nmap = 1 } }
512 // argv[8] = yaw degrees about the vertical axis (multi-view judge)
513 var yawd: i64 = 0
514 if argc > 8 { yawd = as_satoi(argv[8] as *u8) }
515 // argv[9]=="fit" -> height-only framing (fair, scale-normalised comparison for the judges)
516 var fith: i64 = 0
517 if argc > 9 { let f9: *u8 = argv[9] as *u8; if f9[0] == (102 as u8) { fith = 1 } }
518 // argv[10] = AROUSAL PERFUSION in permil (absent or 0 => renderer behaves EXACTLY as before).
519 // Physiology, not a tint: added dermal oxyhaemoglobin absorbs strongly in blue (Soret ~415nm)
520 // and green (542/577nm) and only weakly above 600nm, so flush is G and B FALLING rather than
521 // red being added. Colour-only, so the shape and detail judges cannot move.
522 if argc > 10 { as_set_perfusion(as_satoi(argv[10] as *u8)) }
523 let sinT: *i64 = sys_mmap(400*8) as *i64
524 as_sin_fill(sinT)
525 let ysin: i64 = sinT[as_wrap(yawd)]
526 let ycos: i64 = sinT[as_wrap(yawd+90)]
527 let eye: *u8 = argv[3] as *u8
528 let W: i64 = as_satoi(argv[4] as *u8)
529 let H: i64 = as_satoi(argv[5] as *u8)
530 let outp: *u8 = argv[6] as *u8
531
532 let lenp: *i64 = sys_mmap(16) as *i64
533 let mb: *u8 = sys_read_file(meshp, lenp)
534 if (mb as i64) == 0 { as_hw("{\x22error\x22:\x22cannot read mesh\x22}\n" as *u8); return 2 }
535 // header
536 let nlayers: i64 = as_rdbits(mb, 8)
537 let ntris: i64 = as_rdbits(mb, 12)
538 // layer table: name16 off4 cnt4 ; tris base after it
539 let triBase: i64 = 16 + nlayers*24
540 let lidBase: i64 = triBase + ntris*84
541 // per-layer visibility + explode offset (x)
542 let vis: *i64 = sys_mmap(nlayers*8) as *i64
543 let loffx: *i64 = sys_mmap(nlayers*8) as *i64
544 var L0: i64 = 0
545 while L0 < nlayers { vis[L0] = 1; loffx[L0] = 0; L0 = L0 + 1 }
546 var only: i64 = 0 - 1
547 if as_streq(mode, "skin" as *u8) == 1 { only = 0 }
548 if mode[0] == (108 as u8) { if mode[1] == (97 as u8) { only = as_satoi((mode as i64 + 5) as *u8) } } // "layerN"
549 if only >= 0 { var i: i64 = 0; while i < nlayers { vis[i] = 0; i = i + 1 } vis[only] = 1 }
550
551 // ★seq630 pass 0: MEASURE the mesh at a fixed high precision, then choose the working precision so the
552 // model always spans ~AS_TARGET integer units. A metre-scale mesh and a millimetre-scale mesh then get
553 // the SAME effective resolution -- scale-invariant by construction instead of assuming millimetres.
554 var q0mn: i64 = AS_MAGIC_2000000000
555 var q0mx: i64 = 0-AS_MAGIC_2000000000
556 var t0: i64 = 0
557 while t0 < ntris {
558 let o0: i64 = triBase + t0*84
559 var c0: i64 = 0
560 while c0 < 3 {
561 let vq: i64 = as_f32mul(mb, o0 + c0*4, AS_POSQ0)
562 if vq < q0mn { q0mn = vq }
563 if vq > q0mx { q0mx = vq }
564 c0 = c0 + 1
565 }
566 t0 = t0 + 1
567 }
568 var span0: i64 = q0mx - q0mn
569 if span0 < 1 { span0 = 1 }
570 var posq: i64 = AS_POSQ0 * AS_TARGET / span0
571 if posq < 1 { posq = 1 }
572
573 // pass 1: full AABB over all verts -> centroid + radius
574 var mnx: i64 = AS_MAGIC_2000000000; var mny: i64 = AS_MAGIC_2000000000; var mnz: i64 = AS_MAGIC_2000000000
575 var mxx: i64 = 0-AS_MAGIC_2000000000; var mxy: i64 = 0-AS_MAGIC_2000000000; var mxz: i64 = 0-AS_MAGIC_2000000000
576 var t: i64 = 0
577 while t < ntris {
578 var v: i64 = 0
579 while v < 3 {
580 let o: i64 = triBase + t*84 + v*12
581 let x: i64 = as_f32mul(mb, o, posq); let y: i64 = as_f32mul(mb, o+4, posq); let z: i64 = as_f32mul(mb, o+8, posq)
582 if x < mnx { mnx = x } if x > mxx { mxx = x }
583 if y < mny { mny = y } if y > mxy { mxy = y }
584 if z < mnz { mnz = z } if z > mxz { mxz = z }
585 v = v + 1
586 }
587 t = t + 1
588 }
589 let cx0: i64 = (mnx+mxx)/2; let cy0: i64 = (mny+mxy)/2; let cz0: i64 = (mnz+mxz)/2
590 var rad: i64 = (mxx-mnx)/2
591 if (mxy-mny)/2 > rad { rad = (mxy-mny)/2 }
592 if (mxz-mnz)/2 > rad { rad = (mxz-mnz)/2 }
593 if rad < 1 { rad = 1 } // seq630: a degenerate mesh must never reach a divisor
594 // explode offsets (fan along X), step 0.62*rad, centered
595 if as_streq(mode, "explode" as *u8) == 1 {
596 let step: i64 = (rad*62)/100
597 var i: i64 = 0
598 while i < nlayers { loffx[i] = (i*2 - (nlayers-1)) * step / 2; i = i + 1 }
599 }
600
601 // pass 2: visible AABB (centered + offset) -> framing
602 var vmnx: i64 = AS_MAGIC_2000000000; var vmny: i64 = AS_MAGIC_2000000000; var vmnz: i64 = AS_MAGIC_2000000000
603 var vmxx: i64 = 0-AS_MAGIC_2000000000; var vmxy: i64 = 0-AS_MAGIC_2000000000; var vmxz: i64 = 0-AS_MAGIC_2000000000
604 t = 0
605 while t < ntris {
606 let L: i64 = as_rdbits(mb, lidBase + t*4) & 255
607 if vis[L] == 1 {
608 var v: i64 = 0
609 while v < 3 {
610 let o: i64 = triBase + t*84 + v*12
611 let x: i64 = as_f32mul(mb,o,posq) - cx0 + loffx[L]
612 let y: i64 = as_f32mul(mb,o+4,posq) - cy0
613 let z: i64 = as_f32mul(mb,o+8,posq) - cz0
614 if x < vmnx { vmnx = x } if x > vmxx { vmxx = x }
615 if y < vmny { vmny = y } if y > vmxy { vmxy = y }
616 if z < vmnz { vmnz = z } if z > vmxz { vmxz = z }
617 v = v + 1
618 }
619 }
620 t = t + 1
621 }
622 let halfW: i64 = (vmxx-vmnx)/2; let halfH: i64 = (vmxy-vmny)/2
623 let vcx: i64 = (vmnx+vmxx)/2; let vcy: i64 = (vmny+vmxy)/2
624 // arousal sites are read off THIS mesh's own extent, so the field follows whatever body is loaded
625 as_set_body_span(vmny, vmxy, vcx, halfW)
626 let FOCAL: i64 = H // tan(halffov_v)=0.5
627 var dist: i64 = 2*halfH
628 let dw: i64 = 2*halfW*H/W
629 if dw > dist { dist = dw }
630 dist = dist*118/100 + (vmxz-vmnz)/2
631 // ★FAIR-RULER FIX: auto-framing that depends on WIDTH or DEPTH silently rescales the render, so a change
632 // to body thickness moved the camera and shrank the figure -- the front silhouette score fell 834->624 from
633 // a depth edit that cannot affect a front outline. That was the RULER moving, not the shape. 'fit' frames
634 // on HEIGHT ALONE, so two meshes of the same stature render at the same scale and IoU measures shape only.
635 if fith == 1 { dist = 2*halfH*118/100 }
636 if dist < 1 { dist = 1 } // seq630: never divide by a degenerate framing distance
637 // off-axis (parallel) stereo eye offset in X
638 var ex: i64 = 0
639 if eye[0] == (76 as u8) { ex = 0 - (dist*32)/1000 } // 'L'
640 if eye[0] == (82 as u8) { ex = (dist*32)/1000 } // 'R'
641
642 // framebuffers
643 let npx: i64 = W*H
644 let fb: *i64 = sys_mmap(npx*8) as *i64
645 let zb: *i64 = sys_mmap(npx*8) as *i64
646 let lb: *i64 = sys_mmap(npx*8) as *i64
647 var p: i64 = 0
648 while p < npx {
649 let py: i64 = p / W
650 let px1: i64 = p % W
651 // smooth full-precision vertical gradient (the old 10-level g caused visible BANDING -- eyeball-found)
652 // + subtle backdrop grain (paper-texture scale, +-4 levels; coherent value noise, not white noise)
653 let bgt: i64 = tx_vnoise(px1*4, py*4, 17, 24) / 32 - 4
654 var br: i64 = 34 + (56*(H-py))/H + bgt
655 var bgc: i64 = 41 + (69*(H-py))/H + bgt
656 var bbl: i64 = 56 + (94*(H-py))/H + bgt
657 if br<0 {br=0} if bgc<0 {bgc=0} if bbl<0 {bbl=0}
658 fb[p] = br + bgc*256 + bbl*AS_Q16_ONE
659 zb[p] = AS_ZFAR; lb[p] = 0 - 1; p = p + 1
660 }
661
662 // lights (Q14 unit)
663 let l1x: i64 = 0-AS_KEY_LIGHT_X; let l1y: i64 = AS_KEY_LIGHT_Y; let l1z: i64 = AS_KEY_LIGHT_Z
664 let l2x: i64 = AS_FILL_LIGHT_X; let l2y: i64 = 0-AS_FILL_LIGHT_Y; let l2z: i64 = AS_FILL_LIGHT_Z
665 let cxh: i64 = W/2; let cyh: i64 = H/2
666 // half-vector for specular (headlight view (0,0,1)), computed ONCE
667 var hx: i64 = l1x; var hy: i64 = l1y; var hz: i64 = l1z + AS_Q14
668 let hl: i64 = as_isqrt(hx*hx + hy*hy + hz*hz)
669 hx = hx*AS_Q14/hl; hy = hy*AS_Q14/hl; hz = hz*AS_Q14/hl
670
671 // ---- GX-15 rung A: GROUND PLANE + PROJECTED CONTACT SHADOW (the floating-figure fix) ----
672 // Real geometry-derived shadow: every visible triangle is projected along light L1 onto the floor
673 // plane y=floorY and splatted into a floor-space mask; floor pixels darken where masked.
674 let floorY: i64 = vmny
675 let smN: i64 = 192
676 var smE: i64 = rad*3
677 if smE < 1 { smE = 1 } // seq630: fail-safe -- the shadow grid divides by 2*smE
678 let sm: *i64 = sys_mmap(smN*smN*8) as *i64
679 let qgx: *i64 = sys_mmap(64) as *i64
680 let qgz: *i64 = sys_mmap(64) as *i64
681 let qgy: *i64 = sys_mmap(64) as *i64
682 t = 0
683 while t < ntris {
684 let L: i64 = as_rdbits(mb, lidBase + t*4) & 255
685 if vis[L] == 1 {
686 let ob: i64 = triBase + t*84
687 var v: i64 = 0
688 while v < 3 {
689 let o: i64 = ob + v*12
690 let px3: i64 = as_f32mul(mb,o,posq) - cx0 + loffx[L]
691 let py3: i64 = as_f32mul(mb,o+4,posq) - cy0
692 let pz3: i64 = as_f32mul(mb,o+8,posq) - cz0
693 let sx3: i64 = px3 - (py3 - floorY)*l1x/l1y
694 let sz3: i64 = pz3 - (py3 - floorY)*l1z/l1y
695 qgx[v] = (sx3 + smE)*smN/(2*smE)
696 qgz[v] = (sz3 + smE)*smN/(2*smE)
697 qgy[v] = py3 - floorY
698 v = v + 1
699 }
700 var a2: i64 = (qgx[1]-qgx[0])*(qgz[2]-qgz[0]) - (qgx[2]-qgx[0])*(qgz[1]-qgz[0])
701 var sg2: i64 = 1
702 if a2 < 0 { sg2 = 0 - 1 }
703 if a2 != 0 {
704 var gxmn: i64 = as_max(0, as_min(qgx[0], as_min(qgx[1], qgx[2])))
705 var gxmx: i64 = as_min(smN-1, as_max(qgx[0], as_max(qgx[1], qgx[2])))
706 var gzmn: i64 = as_max(0, as_min(qgz[0], as_min(qgz[1], qgz[2])))
707 var gzmx: i64 = as_min(smN-1, as_max(qgz[0], as_max(qgz[1], qgz[2])))
708 var gz: i64 = gzmn
709 while gz <= gzmx {
710 var gx: i64 = gxmn
711 while gx <= gxmx {
712 let f0: i64 = ((qgx[2]-qgx[1])*(gz-qgz[1]) - (qgz[2]-qgz[1])*(gx-qgx[1]))*sg2
713 let f1: i64 = ((qgx[0]-qgx[2])*(gz-qgz[2]) - (qgz[0]-qgz[2])*(gx-qgx[2]))*sg2
714 let f2: i64 = ((qgx[1]-qgx[0])*(gz-qgz[0]) - (qgz[1]-qgz[0])*(gx-qgx[0]))*sg2
715 if f0 >= 0 { if f1 >= 0 { if f2 >= 0 {
716 // GX-39 piece 3: store MAX OCCLUDER HEIGHT per cell (was a 1-bit mask) so
717 // the penumbra can grow with height like a real area light
718 var hrel: i64 = qgy[0]
719 if qgy[1] > hrel { hrel = qgy[1] }
720 if qgy[2] > hrel { hrel = qgy[2] }
721 if hrel < 1 { hrel = 1 }
722 if hrel > sm[gz*smN+gx] { sm[gz*smN+gx] = hrel }
723 }}}
724 gx = gx + 1
725 }
726 gz = gz + 1
727 }
728 }
729 }
730 t = t + 1
731 }
732 // ---- GX-39 piece 3: SOFT AREA SHADOW fields ----
733 // sm now stores MAX OCCLUDER HEIGHT per cell (0 = unshadowed). Two separable integer box blurs of
734 // the coverage build a SHARP field (kills grid-cell blockiness only) and a WIDE field (the
735 // penumbra); each floor pixel blends between them by the LOCAL occluder height -- feet stay crisply
736 // planted while the head's shadow feathers wide, the real area-light signature. nmap renders keep
737 // the legacy hard 4-tap lookup so the mechanistic shape-judge input stays bit-stable.
738 let smc: i64 = smN*smN
739 let cov: *i64 = sys_mmap(smc*8) as *i64
740 let bt1: *i64 = sys_mmap(smc*8) as *i64
741 let bs1: *i64 = sys_mmap(smc*8) as *i64
742 let bs2: *i64 = sys_mmap(smc*8) as *i64
743 var ci: i64 = 0
744 while ci < smc { if sm[ci] > 0 { cov[ci] = AS_Q10_ONE } ci = ci + 1 }
745 as_boxblur(cov, bt1, bs1, smN, 1)
746 as_boxblur(cov, bt1, bs2, smN, 4)
747
748 // analytic floor pass: per-pixel ray/plane, textured, shadow-masked, distance-faded to bg
749 var fp: i64 = 0
750 while fp < npx {
751 let py2: i64 = fp / W
752 let px2: i64 = fp % W
753 let rdy: i64 = cyh - py2
754 if rdy < 0 {
755 let t16: i64 = floorY*AS_Q16_ONE/rdy
756 let wx: i64 = ex + t16*(px2-cxh)/AS_Q16_ONE
757 let wz: i64 = dist + t16*(0-FOCAL)/AS_Q16_ONE
758 if wx > 0-smE { if wx < smE { if wz > 0-smE { if wz < smE {
759 let depth: i64 = dist - wz
760 if depth > 0 {
761 let tv: i64 = tx_fbm(wx/posq, floorY/posq, wz/posq)
762 let f: i64 = 928 + tv/2
763 var fr: i64 = 118*f/AS_Q10_ONE; var fg: i64 = 110*f/AS_Q10_ONE; var fbv: i64 = 101*f/AS_Q10_ONE
764 let gx: i64 = (wx + smE)*smN/(2*smE)
765 let gz: i64 = (wz + smE)*smN/(2*smE)
766 var dk: i64 = AS_Q10_ONE
767 if nmap == 0 {
768 // soft area shadow: blend sharp->wide by local occluder height (3x3 max so the
769 // penumbra field has no holes), then darken by the blended coverage
770 var hL: i64 = 0
771 var dz5: i64 = 0 - 1
772 while dz5 <= 1 {
773 var dx5: i64 = 0 - 1
774 while dx5 <= 1 {
775 let gx5: i64 = gx + dx5
776 let gz5: i64 = gz + dz5
777 if gx5 >= 0 { if gx5 < smN { if gz5 >= 0 { if gz5 < smN {
778 if sm[gz5*smN+gx5] > hL { hL = sm[gz5*smN+gx5] }
779 }}}}
780 dx5 = dx5 + 1
781 }
782 dz5 = dz5 + 1
783 }
784 // a shadow cast from a third of stature up is fully soft (height-derived, not taste)
785 var hpen: i64 = (vmxy - vmny)/3
786 if hpen < 1 { hpen = 1 }
787 var wgt: i64 = hL*AS_Q10_ONE/hpen
788 if wgt > AS_Q10_ONE { wgt = AS_Q10_ONE }
789 let sv: i64 = (bs1[gz*smN+gx]*(AS_Q10_ONE-wgt) + bs2[gz*smN+gx]*wgt)/AS_Q10_ONE
790 dk = AS_Q10_ONE - sv*44/100
791 } else {
792 // legacy hard 4-tap, bit-stable for the shape-judge floor (sm holds heights now,
793 // so count occupied cells rather than summing raw values)
794 var sh: i64 = 0
795 if sm[gz*smN+gx] > 0 { sh = sh + 1 }
796 if gx+1 < smN { if sm[gz*smN+gx+1] > 0 { sh = sh + 1 } }
797 if gz+1 < smN { if sm[(gz+1)*smN+gx] > 0 { sh = sh + 1 } }
798 if gx > 0 { if sm[gz*smN+gx-1] > 0 { sh = sh + 1 } }
799 dk = AS_Q10_ONE - sh*110
800 }
801 fr = fr*dk/AS_Q10_ONE; fg = fg*dk/AS_Q10_ONE; fbv = fbv*dk/AS_Q10_ONE
802 // distance fade toward the backdrop colour
803 let dxtra: i64 = t16/AS_Q16_ONE
804 let ff: i64 = smE*AS_Q10_ONE/(smE + dxtra*3)
805 let obr: i64 = 34 + (56*(H-py2))/H
806 let obg: i64 = 41 + (69*(H-py2))/H
807 let obb: i64 = 56 + (94*(H-py2))/H
808 fr = (fr*ff + obr*(AS_Q10_ONE-ff))/AS_Q10_ONE
809 fg = (fg*ff + obg*(AS_Q10_ONE-ff))/AS_Q10_ONE
810 fbv = (fbv*ff + obb*(AS_Q10_ONE-ff))/AS_Q10_ONE
811 if fr>255 {fr=255} if fg>255 {fg=255} if fbv>255 {fbv=255}
812 fb[fp] = fr + fg*256 + fbv*AS_Q16_ONE
813 zb[fp] = depth
814 }
815 }}}}
816 }
817 fp = fp + 1
818 }
819
820 // pass 3: rasterize
821 t = 0
822 while t < ntris {
823 let L: i64 = as_rdbits(mb, lidBase + t*4) & 255
824 if vis[L] == 1 {
825 let ob: i64 = triBase + t*84
826 // 3 verts: centered pos + offset ; normal Q14 ; color 0..255 (per-vertex, we use v0's colour = flat)
827 let rx0v: i64 = as_f32mul(mb,ob,posq) - cx0 + loffx[L]; let y0: i64 = as_f32mul(mb,ob+4,posq) - cy0; let rz0v: i64 = as_f32mul(mb,ob+8,posq) - cz0
828 let rx1v: i64 = as_f32mul(mb,ob+12,posq)- cx0 + loffx[L]; let y1: i64 = as_f32mul(mb,ob+16,posq)- cy0; let rz1v: i64 = as_f32mul(mb,ob+20,posq)- cz0
829 let rx2v: i64 = as_f32mul(mb,ob+24,posq)- cx0 + loffx[L]; let y2: i64 = as_f32mul(mb,ob+28,posq)- cy0; let rz2v: i64 = as_f32mul(mb,ob+32,posq)- cz0
830 // multi-view: yaw about the vertical axis so the judge can inspect the depth axis a front view hides
831 let x0: i64 = (rx0v*ycos + rz0v*ysin)/AS_Q14; let z0: i64 = (rz0v*ycos - rx0v*ysin)/AS_Q14
832 let x1: i64 = (rx1v*ycos + rz1v*ysin)/AS_Q14; let z1: i64 = (rz1v*ycos - rx1v*ysin)/AS_Q14
833 let x2: i64 = (rx2v*ycos + rz2v*ysin)/AS_Q14; let z2: i64 = (rz2v*ycos - rx2v*ysin)/AS_Q14
834 // depths from camera (looking -z, camera at +z=dist)
835 let d0: i64 = dist - z0; let d1: i64 = dist - z1; let d2: i64 = dist - z2
836 if d0 > 0 { if d1 > 0 { if d2 > 0 {
837 // project (off-axis stereo)
838 let conv: i64 = ex*FOCAL/dist
839 let sx0: i64 = cxh + ((x0-ex)*FOCAL)/d0 + conv; let sy0: i64 = cyh - (y0*FOCAL)/d0
840 let sx1: i64 = cxh + ((x1-ex)*FOCAL)/d1 + conv; let sy1: i64 = cyh - (y1*FOCAL)/d1
841 let sx2: i64 = cxh + ((x2-ex)*FOCAL)/d2 + conv; let sy2: i64 = cyh - (y2*FOCAL)/d2
842 var area: i64 = (sx1-sx0)*(sy2-sy0) - (sx2-sx0)*(sy1-sy0)
843 if area != 0 {
844 // GX-15 rung B: per-VERTEX normals decoded once per tri, interpolated per PIXEL (kills faceting)
845 let q0x: i64 = as_f32mul(mb,ob+36,AS_Q14); let n0y: i64 = as_f32mul(mb,ob+40,AS_Q14); let q0z: i64 = as_f32mul(mb,ob+44,AS_Q14)
846 let q1x: i64 = as_f32mul(mb,ob+48,AS_Q14); let n1y: i64 = as_f32mul(mb,ob+52,AS_Q14); let q1z: i64 = as_f32mul(mb,ob+56,AS_Q14)
847 let q2x: i64 = as_f32mul(mb,ob+60,AS_Q14); let n2y: i64 = as_f32mul(mb,ob+64,AS_Q14); let q2z: i64 = as_f32mul(mb,ob+68,AS_Q14)
848 // normals must yaw with the geometry, or lighting/normal-maps detach from the surface
849 let n0x: i64 = (q0x*ycos + q0z*ysin)/AS_Q14; let n0z: i64 = (q0z*ycos - q0x*ysin)/AS_Q14
850 let n1x: i64 = (q1x*ycos + q1z*ysin)/AS_Q14; let n1z: i64 = (q1z*ycos - q1x*ysin)/AS_Q14
851 let n2x: i64 = (q2x*ycos + q2z*ysin)/AS_Q14; let n2z: i64 = (q2z*ycos - q2x*ysin)/AS_Q14
852 let cr: i64 = as_f32mul(mb,ob+72,255); let cg: i64 = as_f32mul(mb,ob+76,255); let cbb: i64 = as_f32mul(mb,ob+80,255)
853 // bbox
854 var bxmn: i64 = as_max(0, as_min(sx0, as_min(sx1,sx2)))
855 var bxmx: i64 = as_min(W-1, as_max(sx0, as_max(sx1,sx2)))
856 var bymn: i64 = as_max(0, as_min(sy0, as_min(sy1,sy2)))
857 var bymx: i64 = as_min(H-1, as_max(sy0, as_max(sy1,sy2)))
858 var sgn: i64 = 1
859 if area < 0 { sgn = 0 - 1 }
860 let aabs: i64 = area*sgn
861 var py2: i64 = bymn
862 while py2 <= bymx {
863 var px2: i64 = bxmn
864 while px2 <= bxmx {
865 let e0: i64 = ((sx2-sx1)*(py2-sy1) - (sy2-sy1)*(px2-sx1))*sgn
866 let e1: i64 = ((sx0-sx2)*(py2-sy2) - (sy0-sy2)*(px2-sx2))*sgn
867 let e2: i64 = ((sx1-sx0)*(py2-sy0) - (sy1-sy0)*(px2-sx0))*sgn
868 if e0 >= 0 { if e1 >= 0 { if e2 >= 0 {
869 let depth: i64 = (e0*d0 + e1*d1 + e2*d2)/aabs
870 let idx: i64 = py2*W + px2
871 if depth < zb[idx] {
872 // per-pixel WORLD position -> multi-octave texture (GX-13)
873 let wx: i64 = (e0*x0 + e1*x1 + e2*x2)/aabs
874 let wy: i64 = (e0*y0 + e1*y1 + e2*y2)/aabs
875 let wz: i64 = (e0*z0 + e1*z1 + e2*z2)/aabs
876 let tv: i64 = tx_fbm(wx/posq, wy/posq, wz/posq)
877 let f: i64 = 896 + tv
878 // GX-15 rung B: smooth interpolated normal per pixel
879 var nx: i64 = (e0*n0x + e1*n1x + e2*n2x)/aabs
880 var ny: i64 = (e0*n0y + e1*n1y + e2*n2y)/aabs
881 var nz: i64 = (e0*n0z + e1*n1z + e2*n2z)/aabs
882 var nl: i64 = as_isqrt(nx*nx + ny*ny + nz*nz)
883 if nl < 1 { nl = 1 }
884 nx = nx*AS_Q14/nl; ny = ny*AS_Q14/nl; nz = nz*AS_Q14/nl
885 if nz < 0 { nx = 0-nx; ny = 0-ny; nz = 0-nz }
886 // ★SKIN MICRO-BUMP: perturb the shading normal by a high-frequency noise
887 // gradient over world position -- pore/wrinkle-scale relief that catches
888 // light, so skin stops reading as smooth plastic. Renderer-only (the mesh
889 // and the bench are untouched); two octaves for skin, coarser everywhere.
890 if L == 0 {
891 let mx: i64 = wx/posq; let my: i64 = wy/posq; let mz: i64 = wz/posq
892 let gx1: i64 = tx_vnoise(mx+1,my,mz,3) - tx_vnoise(mx-1,my,mz,3)
893 let gy1: i64 = tx_vnoise(mx,my+1,mz,3) - tx_vnoise(mx,my-1,mz,3)
894 let gx2: i64 = tx_vnoise(mx+2,my,mz,7) - tx_vnoise(mx-2,my,mz,7)
895 let gy2: i64 = tx_vnoise(mx,my+2,mz,7) - tx_vnoise(mx,my-2,mz,7)
896 nx = nx + (gx1*16 + gx2*10)/10 // fine micro-relief only -- subtle, not mottled
897 ny = ny + (gy1*16 + gy2*10)/10
898 var nl2: i64 = as_isqrt(nx*nx + ny*ny + nz*nz)
899 if nl2 < 1 { nl2 = 1 }
900 nx = nx*AS_Q14/nl2; ny = ny*AS_Q14/nl2; nz = nz*AS_Q14/nl2
901 }
902 let dot1: i64 = (nx*l1x + ny*l1y + nz*l1z)/AS_Q14
903 var d1l: i64 = as_max(0, dot1)
904 // GX-15 rung C: SKIN responds like flesh -- wrap diffuse (SSS signature, proven Gx rung)
905 if L == 0 {
906 d1l = (dot1 + 4*AS_Q14/10)*717/AS_Q10_ONE
907 if d1l < 0 { d1l = 0 }
908 }
909 let d2l: i64 = as_max(0, (nx*l2x + ny*l2y + nz*l2z)/AS_Q14)
910 // ★ENVIRONMENT AMBIENT (GX-38). The ambient term was the CONSTANT 225 --
911 // every surface got the same fill regardless of which way it faced, which
912 // is the single strongest reason these renders read as CG on a backdrop.
913 // Real ambient is a hemisphere: a surface facing up sees sky, one facing
914 // down sees only the dim bounce off the ground. This is the cosine-weighted
915 // sky-visibility term of an environment light, integer and sovereign --
916 // shading only, so it cannot move the geometric bench by construction.
917 let skyw: i64 = (AS_Q14 + ny)/2
918 let ambi: i64 = 96 + 258*skyw/AS_Q14
919 var lit: i64 = ambi + 676*d1l/AS_Q14 + 256*d2l/AS_Q14
920 // soft FRONT FILL for skin: camera-facing surfaces get a gentle lift so
921 // downward/shadowed features (the mouth, under-chin) don't crush to black.
922 if L == 0 { lit = lit + 210*nz/AS_Q14 }
923 if lit > AS_LIT_CEIL { lit = AS_LIT_CEIL }
924 var r2: i64 = cr*lit*f/(AS_Q10_ONE*AS_Q10_ONE)
925 var g2: i64 = cg*lit*f/(AS_Q10_ONE*AS_Q10_ONE)
926 var b2: i64 = cbb*lit*f/(AS_Q10_ONE*AS_Q10_ONE)
927 // PER-CHANNEL ENVIRONMENT (GX-39). The hemisphere above is a SCALAR, so it
928 // brightens up-facing surfaces but cannot carry what actually reads as real
929 // light: sky is COOL and ground bounce is WARM, so a real body is subtly blue
930 // toward the top and orange underneath. Applied as a fraction of the surface
931 // colour itself, so it is scale-free and tints rather than washes out.
932 // Shading only, so the geometric bench must not move.
933 let gndw: i64 = AS_Q14 - skyw
934 let warm: i64 = (gndw*90 - skyw*25)/AS_Q14
935 let cool: i64 = (skyw*95 - gndw*30)/AS_Q14
936 r2 = r2 + r2*warm/1000
937 g2 = g2 + g2*(warm/3 + cool/4)/1000
938 b2 = b2 + b2*cool/1000
939 // ★SUBSURFACE SCATTERING (skin=layer0): light scatters INSIDE flesh and red
940 // penetrates deepest, so the terminator and the shadowed side glow warm/red
941 // instead of going dead-grey -- the single biggest "skin not clay" signature.
942 // sss rises smoothly as the surface turns away from the key light.
943 if L == 0 {
944 var sss: i64 = AS_Q14*55/100 - dot1
945 if sss < 0 { sss = 0 }
946 if sss > AS_Q14 { sss = AS_Q14 }
947 r2 = r2 + cr*sss*46/(AS_Q14*100) // red bleed (deepest) -- strong
948 g2 = g2 + cg*sss*14/(AS_Q14*100) // a little green
949 b2 = b2 - cbb*sss*10/(AS_Q14*100) // pull blue -> warmer shadows
950 // ★AROUSAL PERFUSION: added haemoglobin absorbs G and B far
951 // more than R, so flush is G/B FALLING, not red being added.
952 // AS_PERF defaults 0 -> this block is a no-op for every
953 // existing caller and the geometric bench cannot move.
954 // ★GATED BY THE PROPAGATION FIELD, not applied flat: local flush is how
955 // far systemic perfusion EXCEEDS this site's threshold, so at low drive
956 // only genital/submammary tissue colours and the face/limbs stay clear,
957 // and the order emerges instead of the whole body reddening at once.
958 if AS_PERF > 0 {
959 let sfr: i64 = (wy - AS_YMIN)*1000/AS_YSPAN
960 var lxd: i64 = wx - AS_XCTR
961 if lxd < 0 { lxd = 0 - lxd }
962 let latf: i64 = lxd*1000/AS_XHALF
963 let thr: i64 = as_flush_thresh(sfr, latf)
964 var fl: i64 = 0
965 if AS_PERF > thr { fl = (AS_PERF - thr)*1000/(1000 - thr) }
966 if fl > 1000 { fl = 1000 }
967 r2 = r2 - r2*fl*4/AS_PERFUSION_DEN
968 g2 = g2 - g2*fl*30/AS_PERFUSION_DEN
969 b2 = b2 - b2*fl*40/AS_PERFUSION_DEN
970 }
971 // ★real skin is EVEN, not blotchy: only a whisper of fine grain (the coarse
972 // blotch variation read as diseased mottling at body scale = a regression, removed)
973 let cf: i64 = tx_vnoise(wx*3/posq, wy*3/posq, wz*3/posq, 5)
974 r2 = r2 + (cf-128)*4/255
975 g2 = g2 + (cf-128)*2/255
976 // ★SUBCUTANEOUS VEINS shown THROUGH skin translucency (blue-green: flesh
977 // absorbs red). A domain-warped LEVEL-SET = branching wavy lines, gated by
978 // a low-freq THIN-SKIN mask so veins only surface in patches (forearms/
979 // chest/hands), not everywhere. Inside-out -- real vasculature under the
980 // skin, which Infinigen's single-surface creatures cannot show.
981 let vmx: i64 = wx/posq; let vmy: i64 = wy/posq; let vmz: i64 = wz/posq
982 // coarse + x-COMPRESSED (vein LINES run lengthwise along the limb/torso) +
983 // strong domain warp so the contour meanders -> long thin veins, not speckle
984 let warp: i64 = (tx_vnoise(vmx, vmy, vmz, 26) - 128)*3
985 let vf: i64 = tx_vnoise(vmx*3 + warp, vmy + warp/3, vmz, 40)
986 var ridge: i64 = vf - 128; if ridge < 0 { ridge = 0-ridge }
987 let thin: i64 = tx_vnoise(vmx, vmy, vmz, 80)
988 if ridge < 10 { if thin > 140 {
989 let vein: i64 = (10 - ridge)*(thin - 140)/10
990 r2 = r2 - vein*20/100
991 g2 = g2 - vein*4/100
992 b2 = b2 + vein*15/100
993 }}
994 }
995 // ★BONE MATERIAL (layer 2), colour-only, same doctrine as the skin stack
996 // (sec27/30): the geometric bench cannot move. Operator 2026-08-10: "it looks
997 // like a cartoon" -- root cause was that the ENTIRE material stack applied only
998 // to skin, so the skull rendered flat diffuse + weak sheen = toon shading.
999 if L == 2 {
1000 // (1) porosity/trabecular grain at two scales; blue dampened so the
1001 // grain reads mineral, not noise
1002 let bng1: i64 = tx_vnoise(wx*5/posq, wy*5/posq, wz*5/posq, 11)
1003 let bng2: i64 = tx_vnoise(wx*13/posq, wy*13/posq, wz*13/posq, 7)
1004 let bgrain: i64 = (bng1-128)*10/255 + (bng2-128)*6/255
1005 r2 = r2 + bgrain; g2 = g2 + bgrain; b2 = b2 + bgrain*2/3
1006 // (2) shallow subsurface warmth at the terminator: bone is translucent
1007 // too, far shallower than flesh -- a faint warm lift, no red flood
1008 var bss: i64 = AS_Q14*45/100 - dot1
1009 if bss < 0 { bss = 0 }
1010 if bss > AS_Q14 { bss = AS_Q14 }
1011 r2 = r2 + cr*bss*18/(AS_Q14*100)
1012 g2 = g2 + cg*bss*9/(AS_Q14*100)
1013 b2 = b2 - cbb*bss*6/(AS_Q14*100)
1014 // (3) matte mineral, not plastic: desaturate toward grey where the sky
1015 // light dies instead of holding a uniform ivory
1016 let bdk: i64 = AS_Q14 - skyw
1017 g2 = g2 - g2*bdk*4/(AS_Q14*100)
1018 b2 = b2 - b2*bdk*8/(AS_Q14*100)
1019 }
1020 // subtle Blinn specular sheen (half-vector precomputed)
1021 let ndh: i64 = as_max(0, (nx*hx + ny*hy + nz*hz)/AS_Q14)
1022 let s2s: i64 = ndh*ndh/AS_Q14
1023 let s4s: i64 = s2s*s2s/AS_Q14
1024 let s8s: i64 = s4s*s4s/AS_Q14
1025 var spq: i64 = s8s*52/AS_Q14
1026 if L == 0 { spq = s8s*80/AS_Q14 }
1027 // bone: wider satin lobe at lower energy -- mineral sheen, not plastic gloss
1028 if L == 2 { spq = s4s*38/AS_Q14 }
1029 r2 = r2 + spq; g2 = g2 + spq; b2 = b2 + spq
1030 if r2>255 {r2=255} if g2>255 {g2=255} if b2>255 {b2=255}
1031 if r2<0 {r2=0} if g2<0 {g2=0} if b2<0 {b2=0}
1032 // ★MECHANISTIC SHAPE JUDGE (no VLM): 'nmap' writes the surface NORMAL as
1033 // colour instead of shading it. Silhouette IoU is provably blind to depth
1034 // and to surface shape -- two bodies with identical outlines can have
1035 // completely different surfaces. Comparing normal FIELDS measures the shape
1036 // itself, mechanically, with no learned model anywhere in the loop.
1037 if nmap == 1 {
1038 r2 = (nx + AS_Q14)*255/(2*AS_Q14)
1039 g2 = (ny + AS_Q14)*255/(2*AS_Q14)
1040 b2 = (nz + AS_Q14)*255/(2*AS_Q14)
1041 }
1042 zb[idx] = depth; fb[idx] = r2 + g2*256 + b2*AS_Q16_ONE; lb[idx] = L
1043 }
1044 }}}
1045 px2 = px2 + 1
1046 }
1047 py2 = py2 + 1
1048 }
1049 }
1050 }}}
1051 }
1052 t = t + 1
1053 }
1054
1055 // GX-39 piece 2: contact-darkening AO post-pass -- shaded path ONLY (the nmap shape-judge input
1056 // must stay pure geometry, which is also why the geometric bench cannot move)
1057 if nmap == 0 { as_ssao(fb, zb, lb, W, H) }
1058 // GX-39 piece 4: depth of field -- photographic focus on the body; floor/backdrop blur by distance
1059 if nmap == 0 { as_dof(fb, zb, lb, W, H) }
1060
1061 write_png(fb, W, H, outp)
1062
1063 // stats
1064 let lpx: *i64 = sys_mmap(nlayers*8) as *i64
1065 var i2: i64 = 0
1066 while i2 < nlayers { lpx[i2] = 0; i2 = i2 + 1 }
1067 var bg: i64 = 0; var fg: i64 = 0; var cxs: i64 = 0
1068 var dmn: i64 = AS_ZFAR; var dmx: i64 = 0; var dsm: i64 = 0
1069 var q: i64 = 0
1070 while q < npx {
1071 let L: i64 = lb[q]
1072 if L < 0 { bg = bg + 1 } else {
1073 lpx[L] = lpx[L] + 1; fg = fg + 1; cxs = cxs + (q % W)
1074 let d: i64 = zb[q]
1075 if d < dmn { dmn = d } if d > dmx { dmx = d } dsm = dsm + d
1076 }
1077 q = q + 1
1078 }
1079 var cxv: i64 = 0 - 1; var dmean: i64 = 0 - 1
1080 if fg > 0 { cxv = cxs/fg; dmean = dsm/fg }
1081 as_hw("{\x22impl\x22:\x22sovereign-nishilang-integer\x22,\x22mode\x22:\x22" as *u8); as_hw(mode)
1082 as_hw("\x22,\x22eye\x22:\x22" as *u8); as_hw(eye)
1083 as_hw("\x22,\x22W\x22:" as *u8); as_pn(W); as_hw(",\x22H\x22:" as *u8); as_pn(H)
1084 as_hw(",\x22ntris\x22:" as *u8); as_pn(ntris)
1085 as_hw(",\x22bg_frac_permil\x22:" as *u8); as_pn(bg*1000/npx)
1086 as_hw(",\x22fg_pix\x22:" as *u8); as_pn(fg)
1087 as_hw(",\x22centroid_x\x22:" as *u8); as_pn(cxv)
1088 as_hw(",\x22depth_min\x22:" as *u8); as_pn(dmn); as_hw(",\x22depth_mean\x22:" as *u8); as_pn(dmean); as_hw(",\x22depth_max\x22:" as *u8); as_pn(dmx)
1089 as_hw(",\x22layer_pix\x22:[" as *u8)
1090 var k: i64 = 0
1091 while k < nlayers { if k > 0 { as_hw("," as *u8) } as_pn(lpx[k]); k = k + 1 }
1092 as_hw("]}\n" as *u8)
1093 return 0
1094}