code wiki / (root) / nx_nxa_texbake_region_capacity_t278.nx

nx_nxa_texbake_region_capacity_t278.nx source

↩ module page · 1629 lines · 78033 B

1// nx_nxa_texbake_region_lib.nx -- PER-REGION SKIN ATLAS + PERIODIC MICRO-RELIEF TILE, written INTO the 2// asset as a TEXR section (2026-08-30, /compare/graphics GR29 gpe_skin_microsurface -- the ASSET half). 3// 4// THE OPERATOR'S BAR, VERBATIM: "for per region we need texture ... make sure we aren't just doing more 5// smooth surface painting." So this organ bakes RELIEF -- a 16-bit height channel in MICRONS and a normal 6// DERIVED from that height by finite differences -- and it MEASURES, per region, what the chart footprint 7// can actually carry before it prints carried=1 for anything. 8// 9// WHAT WAS MEASURED BEFORE A BYTE WAS BAKED. The estate's adequacy ruler (nx_craft_emit cw_atlas_adequate) 10// is span-based: texel = 1.7m / res, 830um at the shipped 2048. But TEXC (nx_nxa_texc_lib) is a per-JOINT 11// cylindrical unwrap: each bone owns one tile of a g x g grid, u runs the FULL circumference, v the bone 12// length. The physical footprint of a texel is therefore (bone circumference / tile px) on u, and a chest 13// bone's circumference is a metre. The `measure` verb below solves the UV->3D Jacobian on every triangle 14// and reports, per region, the area-weighted footprint and the FRACTION OF SURFACE AREA that carries each 15// band -- the honest number. A per-region UNIQUE atlas at 2048 raises the tile px by ~g/gR and carries 16// the primary band on more of the body; it CANNOT carry the 250um secondary band around a torso at any 17// resolution this estate ships. That is arithmetic, and it is why the three finer bands ride a PERIODIC 18// MICRO TILE (nx_relief_lib rlf_relief_um2_tile): a texel pitch that is physical by construction (two per 19// pore orifice), repeated by the shader per chart at (chart extent / tile repeat) using the per-joint 20// circumference and length this section publishes. This is the field's practice for exactly this case: 21// Epic's skin guidance keeps wrinkles in the unique normal and puts pore detail in a tiling texture 22// [@ue-skin], MetaHuman carries a separate Micro Skin Detail normal layer [@mh-materials], the scan vendors 23// ship the micro band as its own displacement channel [@txyz-vface], Disney's procedural micro-wrinkle 24// synthesis places pores first and grows the furrow graph between them [@disney23], and Nagano et al. 25// measured that microstructure is a DISPLACEMENT that smooths under stretch and roughens under compression 26// [@nagano15] -- the next contract this file names (ntr_tension_attenuate) and does not fake. 27// 28// THE AREOLA IS ANATOMY, NOT A DISC. The torso region's albedo and height carry the nipple-areola complex 29// at the MESH-DERIVED bust apex (nx_nxa_dyna's lateral anchors, then the most anterior vertex near each): 30// nipple radius and projection from published anthropometry and the areola from the published 1:3 31// nipple-to-areola ratio [@nac-review21], the areola darker than the surrounding skin by a lightness step 32// DERIVED from the estate's own referee band against a published breast-skin tone [@nac-tattoo24], 33// Montgomery glands as a discrete dome field. Every one of those numbers is a ROW in 34// knowledge/areola_relief.conf with its source; this organ READS them (nx_lane_conf, the shared getter) 35// and REFUSES to bake anatomy when a row is missing -- an unsourced default is the magic number rule 11 36// forbids. The registry row nx_anatomy an_add(AF_MONTGOMERY ...) is the vocabulary; the ledger is the 37// provenance. 38// 39// TEXR PAYLOAD (i64 words). ADDITIVE: TEXM (map set 0) is carried byte-for-byte, never touched -- the 40// shipped page reads TEXM's blob at a hard-coded +144, so growing TEXM would break the live sampler. 41// hdr[16]: nsets res model seed hue blob_words nmaps_per_set micro_px micro_repeat_um disp_range_um 42// nj set_words joint_words version 0 0 43// joints[nj*4]: region rank circ_um len_um (rank -1 = joint owns no tile; circ/len feed the page's 44// per-chart micro repeat counts: circ/repeat, len/repeat) 45// sets[nsets*set_words]: region gR njr tile_px foot_mean_um foot_worst_um bands_unique bands_micro 46// pore_period_um apexLx apexLy apexRx apexRy areola_r_px areola_dl_e3 carried_area_permil 47// then 5 map records [id off len]: 0 albedo 1 disp16 2 normal 3 tile_disp16 4 tile_normal 48// blob: PNGs. disp16 is RGB8 PNG with R=hi G=lo of (h_um*2 + 32768) and B=255 on mesh-covered texels. 49// license_tier: ORIGINAL No hw writes (Rule 26). 50import "nx_syscalls.nx" 51import "nx_nxa.nx" 52import "nx_nxa_texc_lib.nx" 53import "nx_nxa_texbake_lib.nx" 54import "nx_relief_lib.nx" 55import "nx_png.nx" 56import "nx_skin_ita.nx" 57import "nx_lane_conf.nx" 58import "nx_fsops_lib.nx" 59 60// ---- section geometry ---- 61const NTR_HDR_WORDS: i64 = 16 62const NTR_JOINT_WORDS: i64 = 4 63const NTR_SET_META: i64 = 16 64const NTR_NMAPS: i64 = 5 65const NTR_MAPREC: i64 = 3 66const NTR_VERSION: i64 = 1 67const NTR_REGIONS: i64 = 4 68const NTR_MAP_ALBEDO: i64 = 0 69const NTR_MAP_DISP: i64 = 1 70const NTR_MAP_NORMAL: i64 = 2 71const NTR_MAP_TDISP: i64 = 3 72const NTR_MAP_TNORMAL: i64 = 4 73// header word slots 74const NTR_W_NSETS: i64 = 0 75const NTR_W_RES: i64 = 1 76const NTR_W_MODEL: i64 = 2 77const NTR_W_SEED: i64 = 3 78const NTR_W_HUE: i64 = 4 79const NTR_W_BLOBW: i64 = 5 80const NTR_W_NMAPS: i64 = 6 81const NTR_W_MPX: i64 = 7 82const NTR_W_MREP: i64 = 8 83const NTR_W_RANGE: i64 = 9 84const NTR_W_NJ: i64 = 10 85const NTR_W_SETW: i64 = 11 86const NTR_W_JW: i64 = 12 87const NTR_W_VER: i64 = 13 88// set meta slots 89const NTR_S_REGION: i64 = 0 90const NTR_S_GR: i64 = 1 91const NTR_S_NJR: i64 = 2 92const NTR_S_TPX: i64 = 3 93const NTR_S_FMEAN: i64 = 4 94const NTR_S_FWORST: i64 = 5 95const NTR_S_BANDS_U: i64 = 6 96const NTR_S_BANDS_M: i64 = 7 97const NTR_S_PERIOD: i64 = 8 98const NTR_S_ALX: i64 = 9 99const NTR_S_ALY: i64 = 10 100const NTR_S_ARX: i64 = 11 101const NTR_S_ARY: i64 = 12 102const NTR_S_ARPX: i64 = 13 103const NTR_S_DL: i64 = 14 104const NTR_S_CARRIED: i64 = 15 105// the PBR model word TEXM uses (spec-gloss), restated so the two sections agree by value 106const NTR_MODEL_SPECGLOSS: i64 = 1 107// ---- encoding ---- 108// signed range of the 16-bit height channel, microns. DERIVED: the smallest power of two above the 109// cited nipple projection (9000um, nac-review21 via the ledger), so nipple dome, gland dome and pore pit 110// share ONE channel at 0.5um per LSB (65536 levels over 32768um). A pit of 30um is 60 levels. 111const NTR_DISP_RANGE_UM: i64 = 16384 112const NTR_Q16: i64 = 65536 113const NTR_U16_HALF: i64 = 32768 114const NTR_U16_MAX: i64 = 65535 115const NTR_BYTE: i64 = 256 116const NTR_BYTE_MAX: i64 = 255 117const NTR_RGB: i64 = 3 118const NTR_WORD: i64 = 8 119const NTR_SUB: i64 = 16 // rasterizer sub-pixel scale 120const NTR_PI_E3: i64 = 3142 121const NTR_E3: i64 = 1000 122const NTR_E6: i64 = 1000000 123const NTR_MISS: i64 = 0 - 1 124// the two-texel bar every band is judged by (rlf_band_resolvable), restated as the divisor it is 125const NTR_TEXELS_PER_BAND: i64 = 2 126// the 16-gon used to place gland centres: within 2 percent of a circle at these radii, no trig table 127const NTR_NGON: i64 = 16 128// ---- control bits (the gate's controls differ from the subject ONLY in these) ---- 129const NTR_CTL_RELIEF: i64 = 1 // unique-atlas micro-relief bands 130const NTR_CTL_ANATOMY: i64 = 2 // nipple dome + Montgomery glands in the height channel 131const NTR_CTL_PIGMENT: i64 = 4 // areola lightness step in the albedo 132const NTR_CTL_MICRO: i64 = 8 // micro tile carries secondary+pore (else primary only = smooth control) 133const NTR_CTL_SETS: i64 = 16 // write the region sets at all (0 = nsets 0, the adequacy neg-control) 134const NTR_CTL_ALL: i64 = 31 135const NTR_CTL_ONLY_TORSO: i64 = 32 // bake only the torso set (the gate's 2048 probe run); NOT in ALL 136// ---- refusals ---- 137const NTR_OK: i64 = 0 138const NTR_E_IO: i64 = 1 139const NTR_E_BAD: i64 = 3 140const NTR_E_NOSEC: i64 = 4 141const NTR_E_MISMATCH: i64 = 5 142const NTR_E_CONF: i64 = 6 143// ---- the provenance ledger (rule 11: numbers live in conf with their source) ---- 144const NTR_CONF_REL: *u8 = "knowledge/areola_relief.conf" 145const NTR_CONF_ABS: *u8 = "/volume1/homes/elderwesto/nishihost/knowledge/areola_relief.conf" 146// anatomy parameter slots (the ledger row order) 147const NTR_AN_RA: i64 = 0 148const NTR_AN_RN: i64 = 1 149const NTR_AN_HN: i64 = 2 150const NTR_AN_DL: i64 = 3 151const NTR_AN_NMONT: i64 = 4 152const NTR_AN_RG: i64 = 5 153const NTR_AN_HG: i64 = 6 154const NTR_AN_SEED: i64 = 7 155const NTR_AN_N: i64 = 8 156// box-mean window pad for the pore-period measurement: 2 x pit radius + this, in texels 157const NTR_HP_PAD: i64 = 1 158// ---- the run context (one array instead of twenty parameters) ---- 159const CX_W: i64 = 0 160const CX_VWO: i64 = 1 161const CX_TWO: i64 = 2 162const CX_XWO: i64 = 3 163const CX_SWO: i64 = 4 164const CX_NV: i64 = 5 165const CX_NTR: i64 = 6 166const CX_NJ: i64 = 7 167const CX_G: i64 = 8 168const CX_RES: i64 = 9 169const CX_UNUM: i64 = 10 170const CX_UDEN: i64 = 11 171const CX_REGS: i64 = 12 172const CX_JRANK: i64 = 13 173const CX_AX: i64 = 14 174const CX_APEX: i64 = 15 175const CX_N: i64 = 16 176 177// ---- PROBES for the gate: when NTR_PROBE_ON is 1 the torso set's raw maps and the micro tile are copied 178// out before PNG encoding, so the gate measures the SAME arrays the PNGs were made from (lossless), in 179// process, without a decoder. Zero when off; the CLI never sets them. 180static NTR_PROBE_ON: i64 181static NTR_PROBE_RES: i64 182static NTR_PROBE_RGB: i64 183static NTR_PROBE_NRM: i64 184static NTR_PROBE_HQ: i64 185static NTR_PROBE_COV: i64 186static NTR_PROBE_FA: i64 187static NTR_PROBE_FB: i64 188static NTR_PROBE_META: i64 189static NTR_PROBE_SETW: i64 190static NTR_PROBE_THQ: i64 191static NTR_PROBE_TNRM: i64 192static NTR_PROBE_MPX: i64 193 194// ------------------------------------------------------------------------------------------------ 195// small helpers 196func ntr_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 197func ntr_max(a: i64, b: i64) -> i64 { if a > b { return a } return b } 198func ntr_min(a: i64, b: i64) -> i64 { if a < b { return a } return b } 199func ntr_clamp(v: i64, lo: i64, hi: i64) -> i64 { if v < lo { return lo } if v > hi { return hi } return v } 200func ntr_copy(dst: *u8, src: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { dst[i] = src[i]; i = i + 1 } return 0 } 201func ntr_dup(src: *u8, n: i64) -> i64 { let d: *u8 = sys_mmap(n + 64); ntr_copy(d, src, n); return d as i64 } 202// ledger read: MISS when the file or the row is absent -- the caller REFUSES, never defaults 203func ntr_conf(key: *u8) -> i64 { return lc_geti(NTR_CONF_REL, NTR_CONF_ABS, key, NTR_MISS) } 204// region name for receipts 205func ntr_region_name(r: i64) -> *u8 { 206 if r == NTB_FACE { return "face" as *u8 } 207 if r == NTB_TORSO { return "torso" as *u8 } 208 if r == NTB_LIMBS { return "limbs" as *u8 } 209 return "gens" as *u8 210} 211// bands carried at a footprint of f um per texel: same predicate as rlf_band_resolvable_span, applied to 212// a MEASURED footprint instead of a span/res pair 213func ntr_bands_at_footprint(f_um: i64) -> i64 { 214 var m: i64 = 0 215 if f_um <= 0 { return 0 } 216 if RLF_RELIEF_LAMBDA_UM / f_um >= NTR_TEXELS_PER_BAND { m = m + RLF_BAND_PRIMARY } 217 if RLF_SEC_LAMBDA_UM / f_um >= NTR_TEXELS_PER_BAND { m = m + RLF_BAND_SECONDARY } 218 if RLF_PORE_PITCH_UM / f_um >= NTR_TEXELS_PER_BAND { m = m + RLF_BAND_POREGRID } 219 if RLF_PORE_DIA_UM / f_um >= NTR_TEXELS_PER_BAND { m = m + RLF_BAND_ORIFICE } 220 return m 221} 222// 16-bit height encode/decode, microns <-> unsigned 16 223func ntr_h_enc(h_um: i64) -> i64 { 224 return ntr_clamp(h_um * NTR_U16_HALF / NTR_DISP_RANGE_UM + NTR_U16_HALF, 0, NTR_U16_MAX) 225} 226func ntr_h_dec(v: i64) -> i64 { return (v - NTR_U16_HALF) * NTR_DISP_RANGE_UM / NTR_U16_HALF } 227// pack an RGB8 map into write_png_buf's pixel words and encode; returns PNG bytes or <=0 228// Validate source section extents before any per-vertex or per-joint access. 229// This boundary does not establish physical units, calibrated material values, or visual quality. 230func ntr_source_safe(b:*u8,flen:i64)->i64 { 231 if (b as i64)<=0 || flen<NT_HDR{return 0} 232 let w:*i64=b as *i64 233 let vo:i64=nxa_counted_section(b,flen,nxa_tag4("VERT"),3) 234 let tr:i64=nxa_counted_section(b,flen,nxa_tag4("TRIS"),3) 235 let sk:i64=nxa_counted_section(b,flen,nxa_tag4("SKEL"),8) 236 if vo<0 || tr<0 || sk<0{return 0} 237 let nv:i64=w[vo];let nj:i64=w[sk];let nt:i64=w[tr] 238 if nv<=0 || nj<=0 || nj>NTB_MAGIC_4095{return 0} 239 let xe:i64=nxa_section_entry(b,flen,nxa_tag4("TEXC"));if xe<0{return 0} 240 let xo:i64=w[xe+1]/NTR_WORD;let xwords:i64=w[xe+2] 241 if xwords<4 || nv>(xwords-4)/3{return 0} 242 if w[xo]!=nv || w[xo+1]!=3 || w[xo+2]<=0{return 0} 243 var j:i64=0 244 while j<nj{let parent:i64=w[sk+1+j*8];if parent<(0-1) || parent>=nj{return 0};j=j+1} 245 var v:i64=0 246 while v<nv{ 247 let at:i64=xo+4+v*3 248 if w[at]<0 || w[at]>NTR_Q16 || w[at+1]<0 || w[at+1]>NTR_Q16{return 0} 249 if w[at+2]<0 || w[at+2]>=nj{return 0} 250 v=v+1 251 } 252 var t:i64=0;while t<nt*3{if w[tr+1+t]<0 || w[tr+1+t]>=nv{return 0};t=t+1} 253 let de:i64=nxa_section_entry(b,flen,nxa_tag4("DYNA")) 254 if de>=0{ 255 let d:i64=w[de+1]/NTR_WORD;let dw:i64=w[de+2] 256 if dw<2{return 0};let count:i64=w[d];let stride:i64=w[d+1] 257 if count<0 || stride<8{return 0};if count>(dw-2)/stride{return 0} 258 } else {if de!=(0-1){return 0}} 259 let me:i64=nxa_section_entry(b,flen,nxa_tag4("TEXM")) 260 if me>=0{if w[me+2]<2{return 0}}else{if me!=(0-1){return 0}} 261 let ns:i64=w[2];var s:i64=0 262 while s<ns{ 263 let tag:i64=w[4+s*4];if nxa_section_entry(b,flen,tag)<0{return 0} 264 var k:i64=0;while k<s{if w[4+k*4]==tag{return 0};k=k+1} 265 s=s+1 266 } 267 return 1 268} 269func ntr_blob_capacity(res:i64,mpx:i64,nsets:i64)->i64 { 270 let a:i64=png_buf_cap(res,res);let b:i64=png_buf_cap(mpx,mpx) 271 let imax:i64=9223372036854775807 272 if a<0 || b<0 || nsets<0 || nsets>NTR_REGIONS{return 0-1} 273 if a>imax/3 || b>imax/2{return 0-1} 274 if 3*a>imax-2*b{return 0-1} 275 let per:i64=3*a+2*b 276 if nsets>0{if per>(imax-NT_PAD)/nsets{return 0-1}} 277 return nsets*per+NT_PAD 278} 279func ntr_png_checked(rgb:*u8,w:i64,h:i64,fbp:*i64,fb_pixels:i64,out:*u8,capacity:i64)->i64 { 280 if (rgb as i64)<=0 || (fbp as i64)<=0 || (out as i64)<=0{return 0-1} 281 let needed:i64=png_buf_cap(w,h) 282 if needed<0 || capacity<needed || fb_pixels<w*h{return 0-1} 283 var p:i64=0;let n:i64=w*h 284 while p<n{ 285 fbp[p]=(rgb[p*NTR_RGB] as i64)+(rgb[p*NTR_RGB+1] as i64)*NTR_BYTE+(rgb[p*NTR_RGB+2] as i64)*NTR_Q16;p=p+1 286 } 287 return write_png_buf_checked(fbp,fb_pixels,w,h,out,capacity) 288} 289 290func ntr_png(rgb: *u8, w: i64, h: i64, fbp: *i64, out: *u8) -> i64 { 291 var p: i64 = 0 292 let n: i64 = w*h 293 while p < n { 294 fbp[p] = (rgb[p*NTR_RGB] as i64) + (rgb[p*NTR_RGB+1] as i64)*NTR_BYTE + (rgb[p*NTR_RGB+2] as i64)*NTR_Q16 295 p = p + 1 296 } 297 return write_png_buf(fbp, w, h, out) 298} 299// 16-gon unit vectors x1000 300func ntr_cos16(s: i64) -> i64 { 301 if s == 0 { return 1000 } 302 if s == 1 { return 924 } 303 if s == 2 { return 707 } 304 if s == 3 { return 383 } 305 if s == 4 { return 0 } 306 if s == 5 { return 0 - 383 } 307 if s == 6 { return 0 - 707 } 308 if s == 7 { return 0 - 924 } 309 if s == 8 { return 0 - 1000 } 310 if s == 9 { return 0 - 924 } 311 if s == 10 { return 0 - 707 } 312 if s == 11 { return 0 - 383 } 313 if s == 12 { return 0 } 314 if s == 13 { return 383 } 315 if s == 14 { return 707 } 316 return 924 317} 318func ntr_sin16(s: i64) -> i64 { return ntr_cos16((s + 12) % NTR_NGON) } 319 320// ------------------------------------------------------------------------------------------------ 321// BODY AXES, derived from the mesh's own extents (the nx_nxa_dyna derivation, restated so the apex search 322// below reads the SAME lateral/anterior/stature axes the DYNA anchors were measured on). 323// out: [0]=sa [1]=la [2]=aa [3]=min_s [4]=span_s [5]=mid_a 324func ntr_axes(w: *i64, vwo: i64, nv: i64, out: *i64) -> i64 { 325 let e0: i64 = ntb_axspan(w, vwo, nv, 0) 326 let e1: i64 = ntb_axspan(w, vwo, nv, 1) 327 let e2: i64 = ntb_axspan(w, vwo, nv, 2) 328 var sa: i64 = 2 329 if e0 >= e1 { if e0 >= e2 { sa = 0 } } 330 if e1 > e0 { if e1 >= e2 { sa = 1 } } 331 var la: i64 = 0 332 var aa: i64 = 1 333 if sa == 0 { la = 1; aa = 2; if e2 > e1 { la = 2; aa = 1 } } 334 if sa == 1 { la = 0; aa = 2; if e2 > e0 { la = 2; aa = 0 } } 335 if sa == 2 { la = 0; aa = 1; if e1 > e0 { la = 1; aa = 0 } } 336 var mns: i64 = w[vwo + 1 + sa] 337 var mxs: i64 = mns 338 var mna: i64 = w[vwo + 1 + aa] 339 var mxa: i64 = mna 340 var i: i64 = 0 341 while i < nv { 342 let s: i64 = w[vwo + 1 + i*3 + sa] 343 let a: i64 = w[vwo + 1 + i*3 + aa] 344 if s < mns { mns = s } 345 if s > mxs { mxs = s } 346 if a < mna { mna = a } 347 if a > mxa { mxa = a } 348 i = i + 1 349 } 350 out[0] = sa 351 out[1] = la 352 out[2] = aa 353 out[3] = mns 354 out[4] = mxs - mns 355 out[5] = (mna + mxa) / 2 356 if out[4] <= 0 { return 0 } 357 return 1 358} 359 360// ------------------------------------------------------------------------------------------------ 361// REGION LAYOUT: joint -> (region, rank). gR[r] = grid edge for region r, njr[r] = tiles in region r, 362// rj[r*nj + rank] = joint owning that tile. Returns nj or a negative refusal. Regions come from 363// ntb_regions, the SAME classifier the whole-body bake uses -- one owner of the grouping. 364func ntr_layout(b: *u8, flen: i64, regs: *i64, jrank: *i64, gR: *i64, njr: *i64, rj: *i64) -> i64 { 365 let perms: *i64 = sys_mmap(NTB_MAGIC_4096*NTR_WORD) as *i64 366 let nj: i64 = ntb_regions(b, flen, regs, perms) 367 if nj < 0 { return nj } 368 if nj > NTB_MAGIC_4095 { nt_err("TEXR-REFUSE joint count over classifier capacity\n" as *u8); return 0 - NTR_E_BAD } 369 var r: i64 = 0 370 while r < NTR_REGIONS { njr[r] = 0; r = r + 1 } 371 var j: i64 = 0 372 while j < nj { 373 let rg: i64 = regs[j] 374 jrank[j] = NTR_MISS 375 if rg >= 0 { if rg < NTR_REGIONS { 376 jrank[j] = njr[rg] 377 rj[rg*nj + njr[rg]] = j 378 njr[rg] = njr[rg] + 1 379 } } 380 j = j + 1 381 } 382 r = 0 383 while r < NTR_REGIONS { gR[r] = nt_grid(njr[r]); if njr[r] == 0 { gR[r] = 0 } r = r + 1 } 384 return nj 385} 386 387// ------------------------------------------------------------------------------------------------ 388// PER-JOINT PHYSICAL EXTENTS, microns: circumference (2 pi x mean radial distance of the joint's own 389// TEXC-owned vertices from its bind bone axis) and bone length. These are what the page divides by the 390// tile repeat to get per-chart micro repeat counts on u and v -- the anisotropy of the unwrap is corrected 391// there, not hidden. unit: pos_um = pos_units * unum / uden (stature declared = RLF_BODY_UM, the same 392// assumption the whole-body ruler already makes). 393func ntr_joint_extents(cx: *i64, upax: i64, circ: *i64, blen: *i64, nown: *i64) -> i64 { 394 let w: *i64 = cx[CX_W] as *i64 395 let vwo: i64 = cx[CX_VWO] 396 let xwo: i64 = cx[CX_XWO] 397 let swo: i64 = cx[CX_SWO] 398 let nv: i64 = cx[CX_NV] 399 let nj: i64 = cx[CX_NJ] 400 let unum: i64 = cx[CX_UNUM] 401 let uden: i64 = cx[CX_UDEN] 402 let sumr: *i64 = sys_mmap(nj*NTR_WORD + 64) as *i64 403 var j: i64 = 0 404 while j < nj { sumr[j] = 0; nown[j] = 0; circ[j] = 0; blen[j] = 0; j = j + 1 } 405 var i: i64 = 0 406 while i < nv { 407 let pj: i64 = w[xwo + 4 + i*3 + 2] 408 if pj >= 0 { if pj < nj { 409 let par: i64 = w[swo + 1 + pj*8] 410 let jx: i64 = w[swo + 1 + pj*8 + 1] 411 let jy: i64 = w[swo + 1 + pj*8 + 2] 412 let jz: i64 = w[swo + 1 + pj*8 + 3] 413 var ax: i64 = 0 414 var ay: i64 = 0 415 var az: i64 = 0 416 if upax == 1 { ay = NTR_E3 } else { az = NTR_E3 } 417 if par >= 0 { if par < nj { 418 ax = jx - w[swo + 1 + par*8 + 1] 419 ay = jy - w[swo + 1 + par*8 + 2] 420 az = jz - w[swo + 1 + par*8 + 3] 421 } } 422 if ax == 0 { if ay == 0 { if az == 0 { if upax == 1 { ay = NTR_E3 } else { az = NTR_E3 } } } } 423 let dx: i64 = w[vwo + 1 + i*3] - jx 424 let dy: i64 = w[vwo + 1 + i*3 + 1] - jy 425 let dz: i64 = w[vwo + 1 + i*3 + 2] - jz 426 let sh: i64 = NT_PRESH 427 let dxp: i64 = dx / sh 428 let dyp: i64 = dy / sh 429 let dzp: i64 = dz / sh 430 let axp: i64 = ax / sh 431 let ayp: i64 = ay / sh 432 let azp: i64 = az / sh 433 let a2: i64 = axp*axp + ayp*ayp + azp*azp 434 let d2: i64 = dxp*dxp + dyp*dyp + dzp*dzp 435 var perp2: i64 = d2 436 if a2 > 0 { 437 let dot: i64 = dxp*axp + dyp*ayp + dzp*azp 438 perp2 = d2 - dot*dot/a2 439 if perp2 < 0 { perp2 = 0 } 440 } 441 sumr[pj] = sumr[pj] + tm_isqrt(perp2) * sh 442 nown[pj] = nown[pj] + 1 443 } } 444 i = i + 1 445 } 446 j = 0 447 while j < nj { 448 let par: i64 = w[swo + 1 + j*8] 449 var lenu: i64 = 0 450 if par >= 0 { if par < nj { 451 let ax: i64 = w[swo + 1 + j*8 + 1] - w[swo + 1 + par*8 + 1] 452 let ay: i64 = w[swo + 1 + j*8 + 2] - w[swo + 1 + par*8 + 2] 453 let az: i64 = w[swo + 1 + j*8 + 3] - w[swo + 1 + par*8 + 3] 454 let sh: i64 = NT_PRESH 455 lenu = tm_isqrt((ax/sh)*(ax/sh) + (ay/sh)*(ay/sh) + (az/sh)*(az/sh)) * sh 456 } } 457 blen[j] = lenu * unum / uden 458 if nown[j] > 0 { 459 let rmean: i64 = sumr[j] / nown[j] 460 circ[j] = 2 * NTR_PI_E3 * rmean / NTR_E3 * unum / uden 461 } 462 j = j + 1 463 } 464 sys_munmap(sumr as *u8, nj*NTR_WORD + 64) 465 return 0 466} 467 468// ------------------------------------------------------------------------------------------------ 469// TEXC uv -> atlas pixel (SUB-pixel units) in one of two layouts. mode 0 = the whole-body layout the 470// incumbent samples (px = u * res / Q16); mode 1 = the per-region layout (the joint's tile moved to its 471// rank slot in a gR grid at tile_px per tile). axis 0 = u/x, 1 = v/y. 472func ntr_px_sub(uv: i64, j: i64, axis: i64, mode: i64, g: i64, gR: i64, tpR: i64, rank: i64, res: i64) -> i64 { 473 if mode == 0 { return uv * res * NTR_SUB / NTR_Q16 } 474 let tw: i64 = NTR_Q16 / g 475 var t: i64 = j % g 476 if axis == 1 { t = j / g } 477 var rk: i64 = rank % gR 478 if axis == 1 { rk = rank / gR } 479 let local: i64 = uv - t*tw 480 return rk*tpR*NTR_SUB + local*tpR*NTR_SUB/tw 481} 482 483// ------------------------------------------------------------------------------------------------ 484// THE HONEST RULER: per-triangle UV->3D Jacobian in a layout. For every triangle whose three vertices 485// share one joint (a chart-interior triangle), solve dP/du and dP/dv in um per PIXEL and take the worse 486// axis as that triangle's footprint. Accumulates per JOINT (area-weighted mean, worst) and per REGION the 487// surface-area fraction carrying each band. Straddling triangles (verts in different charts) are counted 488// and printed -- never silently folded into either bucket. Areas in sub-px^2 of the layout. 489func ntr_footprint(cx: *i64, mode: i64, gRt: *i64, tpRt: *i64, fsum: *i64, asum: *i64, fmax: *i64, carea: *i64, rarea: *i64, straddle: *i64) -> i64 { 490 let w: *i64 = cx[CX_W] as *i64 491 let vwo: i64 = cx[CX_VWO] 492 let two: i64 = cx[CX_TWO] 493 let xwo: i64 = cx[CX_XWO] 494 let nv: i64 = cx[CX_NV] 495 let ntr: i64 = cx[CX_NTR] 496 let nj: i64 = cx[CX_NJ] 497 let g: i64 = cx[CX_G] 498 let res: i64 = cx[CX_RES] 499 let unum: i64 = cx[CX_UNUM] 500 let uden: i64 = cx[CX_UDEN] 501 let regs: *i64 = cx[CX_REGS] as *i64 502 let jrank: *i64 = cx[CX_JRANK] as *i64 503 var j: i64 = 0 504 while j < nj { fsum[j] = 0; asum[j] = 0; fmax[j] = 0; j = j + 1 } 505 var r: i64 = 0 506 while r < NTR_REGIONS { rarea[r] = 0; straddle[r] = 0; var bnd: i64 = 0; while bnd < 4 { carea[r*4+bnd] = 0; bnd = bnd + 1 } r = r + 1 } 507 var t: i64 = 0 508 while t < ntr { 509 let i0: i64 = w[two + 1 + t*3] 510 let i1: i64 = w[two + 1 + t*3 + 1] 511 let i2: i64 = w[two + 1 + t*3 + 2] 512 if ntb_idx3(i0, i1, i2, nv) == 1 { 513 let j0: i64 = w[xwo + 4 + i0*3 + 2] 514 let j1: i64 = w[xwo + 4 + i1*3 + 2] 515 let j2: i64 = w[xwo + 4 + i2*3 + 2] 516 var same: i64 = 0 517 if j0 == j1 { if j1 == j2 { if j0 >= 0 { if j0 < nj { same = 1 } } } } 518 var reg: i64 = NTR_MISS 519 if j0 >= 0 { if j0 < nj { reg = regs[j0] } } 520 if same == 1 { if jrank[j0] >= 0 { if reg >= 0 { if reg < NTR_REGIONS { 521 let rk: i64 = jrank[j0] 522 let x0: i64 = ntr_px_sub(w[xwo + 4 + i0*3], j0, 0, mode, g, gRt[reg], tpRt[reg], rk, res) 523 let y0: i64 = ntr_px_sub(w[xwo + 4 + i0*3 + 1], j0, 1, mode, g, gRt[reg], tpRt[reg], rk, res) 524 let x1: i64 = ntr_px_sub(w[xwo + 4 + i1*3], j0, 0, mode, g, gRt[reg], tpRt[reg], rk, res) 525 let y1: i64 = ntr_px_sub(w[xwo + 4 + i1*3 + 1], j0, 1, mode, g, gRt[reg], tpRt[reg], rk, res) 526 let x2: i64 = ntr_px_sub(w[xwo + 4 + i2*3], j0, 0, mode, g, gRt[reg], tpRt[reg], rk, res) 527 let y2: i64 = ntr_px_sub(w[xwo + 4 + i2*3 + 1], j0, 1, mode, g, gRt[reg], tpRt[reg], rk, res) 528 let du1: i64 = x1 - x0 529 let dv1: i64 = y1 - y0 530 let du2: i64 = x2 - x0 531 let dv2: i64 = y2 - y0 532 let det: i64 = du1*dv2 - du2*dv1 533 if det != 0 { 534 var c: i64 = 0 535 var su: i64 = 0 536 var sv: i64 = 0 537 while c < 3 { 538 let p0: i64 = w[vwo + 1 + i0*3 + c] * unum / uden 539 let p1: i64 = w[vwo + 1 + i1*3 + c] * unum / uden - p0 540 let p2: i64 = w[vwo + 1 + i2*3 + c] * unum / uden - p0 541 // dP/du = (P1*dv2 - P2*dv1)/det ; dP/dv = (P2*du1 - P1*du2)/det (um per sub-px) 542 let gu: i64 = (p1*dv2 - p2*dv1) * NTR_SUB / det 543 let gv: i64 = (p2*du1 - p1*du2) * NTR_SUB / det 544 su = su + gu*gu 545 sv = sv + gv*gv 546 c = c + 1 547 } 548 let f: i64 = ntr_max(tm_isqrt(su), tm_isqrt(sv)) // um per px, worse axis 549 var area: i64 = det 550 if area < 0 { area = 0 - area } 551 fsum[j0] = fsum[j0] + f*area 552 asum[j0] = asum[j0] + area 553 if f > fmax[j0] { fmax[j0] = f } 554 rarea[reg] = rarea[reg] + area 555 let m: i64 = ntr_bands_at_footprint(f) 556 if (m & RLF_BAND_PRIMARY) == RLF_BAND_PRIMARY { carea[reg*4] = carea[reg*4] + area } 557 if (m & RLF_BAND_SECONDARY) == RLF_BAND_SECONDARY { carea[reg*4+1] = carea[reg*4+1] + area } 558 if (m & RLF_BAND_POREGRID) == RLF_BAND_POREGRID { carea[reg*4+2] = carea[reg*4+2] + area } 559 if (m & RLF_BAND_ORIFICE) == RLF_BAND_ORIFICE { carea[reg*4+3] = carea[reg*4+3] + area } 560 } 561 } } } } 562 if same == 0 { if reg >= 0 { if reg < NTR_REGIONS { straddle[reg] = straddle[reg] + 1 } } } 563 } 564 t = t + 1 565 } 566 return 0 567} 568// area-weighted mean footprint over a region's joints, and its worst tile mean; out[0]=mean out[1]=worst 569func ntr_region_footprint(r: i64, nj: i64, regs: *i64, fsum: *i64, asum: *i64, out: *i64) -> i64 { 570 var fs: i64 = 0 571 var as2: i64 = 0 572 var worst: i64 = 0 573 var j: i64 = 0 574 while j < nj { 575 if regs[j] == r { if asum[j] > 0 { 576 fs = fs + fsum[j] 577 as2 = as2 + asum[j] 578 let fm: i64 = fsum[j] / asum[j] 579 if fm > worst { worst = fm } 580 } } 581 j = j + 1 582 } 583 out[0] = 0 584 if as2 > 0 { out[0] = fs / as2 } 585 out[1] = worst 586 return 0 587} 588 589// ------------------------------------------------------------------------------------------------ 590// THE BUST APEX from the asset's own DYNA anchors. The first side=-1 row and the first side=+1 row are 591// the lateral bust pair (nx_nxa_dyna emits them first; the R7 region rows follow). From each anchor, the 592// apex is the MOST ANTERIOR vertex within the anchor's own influence radius laterally and in stature -- 593// the DYNA-derived half gap, never a fixed band. out: [0..2]=L apex units, [3..5]=R apex, [6]=found 594// count, [7]=front sign (+1 if the anchors lie above the anterior midplane). 595func ntr_apex(cx: *i64, dwo: i64, out: *i64) -> i64 { 596 let w: *i64 = cx[CX_W] as *i64 597 let vwo: i64 = cx[CX_VWO] 598 let nv: i64 = cx[CX_NV] 599 let ax: *i64 = cx[CX_AX] as *i64 600 var k: i64 = 0 601 while k < 8 { out[k] = 0; k = k + 1 } 602 if dwo < 0 { return 0 } 603 let ndb: i64 = w[dwo] 604 let dst: i64 = w[dwo + 1] 605 if ndb < 2 { return 0 } 606 if dst < 8 { return 0 } 607 let sa: i64 = ax[0] 608 let la: i64 = ax[1] 609 let aa: i64 = ax[2] 610 let mida: i64 = ax[5] 611 let anc: *i64 = sys_mmap(3*NTR_WORD) as *i64 612 var side: i64 = 0 613 var found: i64 = 0 614 while side < 2 { 615 var want: i64 = 0 - 1 616 if side == 1 { want = 1 } 617 var row: i64 = NTR_MISS 618 var d: i64 = 0 619 while d < ndb { 620 if row < 0 { if w[dwo + 2 + d*dst] == want { row = d } } 621 d = d + 1 622 } 623 if row >= 0 { 624 let base: i64 = dwo + 2 + row*dst 625 anc[0] = w[base + 1] 626 anc[1] = w[base + 2] 627 anc[2] = w[base + 3] 628 let infl: i64 = w[base + 7] 629 var fsign: i64 = 1 630 if anc[aa] < mida { fsign = 0 - 1 } 631 out[7] = fsign 632 var best: i64 = NTR_MISS 633 var bestv: i64 = 0 634 var i: i64 = 0 635 while i < nv { 636 let pl: i64 = w[vwo + 1 + i*3 + la] 637 let ps: i64 = w[vwo + 1 + i*3 + sa] 638 if ntr_abs(pl - anc[la]) <= infl { if ntr_abs(ps - anc[sa]) <= infl { 639 let pa: i64 = w[vwo + 1 + i*3 + aa] * fsign 640 if best < 0 { best = i; bestv = pa } 641 if pa > bestv { best = i; bestv = pa } 642 } } 643 i = i + 1 644 } 645 if best >= 0 { 646 out[side*3] = w[vwo + 1 + best*3] 647 out[side*3 + 1] = w[vwo + 1 + best*3 + 1] 648 out[side*3 + 2] = w[vwo + 1 + best*3 + 2] 649 found = found + 1 650 } 651 } 652 side = side + 1 653 } 654 out[6] = found 655 sys_munmap(anc as *u8, 3*NTR_WORD) 656 return found 657} 658 659// ------------------------------------------------------------------------------------------------ 660// RASTERIZE the region's chart-interior triangles into two per-texel fields: the LATERAL and STATURE 661// offsets (um) of the surface point from the NEARER bust apex (a tangent-plane frame at the chest -- 662// the apex projects anteriorly, so lateral/stature span the areola disc), plus a coverage byte. Texels 663// no chart-interior triangle touches stay uncovered: they still receive skin and relief, never anatomy. 664// Integer edge functions in SUB-pixel units; either winding accepted. 665func ntr_raster(cx: *i64, reg: i64, gRr: i64, tpRr: i64, fa: *i64, fb: *i64, cov: *u8) -> i64 { 666 let w: *i64 = cx[CX_W] as *i64 667 let vwo: i64 = cx[CX_VWO] 668 let two: i64 = cx[CX_TWO] 669 let xwo: i64 = cx[CX_XWO] 670 let nv: i64 = cx[CX_NV] 671 let ntr: i64 = cx[CX_NTR] 672 let nj: i64 = cx[CX_NJ] 673 let g: i64 = cx[CX_G] 674 let res: i64 = cx[CX_RES] 675 let unum: i64 = cx[CX_UNUM] 676 let uden: i64 = cx[CX_UDEN] 677 let regs: *i64 = cx[CX_REGS] as *i64 678 let jrank: *i64 = cx[CX_JRANK] as *i64 679 let ax: *i64 = cx[CX_AX] as *i64 680 let apex: *i64 = cx[CX_APEX] as *i64 681 let la: i64 = ax[1] 682 let sa: i64 = ax[0] 683 // per-vertex offsets to the nearer apex, um 684 let va: *i64 = sys_mmap(nv*NTR_WORD + 64) as *i64 685 let vb: *i64 = sys_mmap(nv*NTR_WORD + 64) as *i64 686 var i: i64 = 0 687 while i < nv { 688 var bestd: i64 = NTR_MISS 689 var s: i64 = 0 690 while s < 2 { 691 let dl: i64 = (w[vwo + 1 + i*3 + la] - apex[s*3 + la]) * unum / uden 692 let ds: i64 = (w[vwo + 1 + i*3 + sa] - apex[s*3 + sa]) * unum / uden 693 let dx: i64 = (w[vwo + 1 + i*3] - apex[s*3]) * unum / uden 694 let dy: i64 = (w[vwo + 1 + i*3 + 1] - apex[s*3 + 1]) * unum / uden 695 let dz: i64 = (w[vwo + 1 + i*3 + 2] - apex[s*3 + 2]) * unum / uden 696 let d2: i64 = dx*dx + dy*dy + dz*dz 697 var take: i64 = 0 698 if bestd < 0 { take = 1 } 699 if bestd >= 0 { if d2 < bestd { take = 1 } } 700 if take == 1 { bestd = d2; va[i] = dl; vb[i] = ds } 701 s = s + 1 702 } 703 i = i + 1 704 } 705 var t: i64 = 0 706 while t < ntr { 707 let i0: i64 = w[two + 1 + t*3] 708 var i1: i64 = w[two + 1 + t*3 + 1] 709 var i2: i64 = w[two + 1 + t*3 + 2] 710 if ntb_idx3(i0, i1, i2, nv) == 1 { 711 let j0: i64 = w[xwo + 4 + i0*3 + 2] 712 var ok: i64 = 0 713 if j0 >= 0 { if j0 < nj { if regs[j0] == reg { if jrank[j0] >= 0 { 714 if w[xwo + 4 + i1*3 + 2] == j0 { if w[xwo + 4 + i2*3 + 2] == j0 { ok = 1 } } 715 } } } } 716 if ok == 1 { 717 let rk: i64 = jrank[j0] 718 let x0: i64 = ntr_px_sub(w[xwo + 4 + i0*3], j0, 0, 1, g, gRr, tpRr, rk, res) 719 let y0: i64 = ntr_px_sub(w[xwo + 4 + i0*3 + 1], j0, 1, 1, g, gRr, tpRr, rk, res) 720 var x1: i64 = ntr_px_sub(w[xwo + 4 + i1*3], j0, 0, 1, g, gRr, tpRr, rk, res) 721 var y1: i64 = ntr_px_sub(w[xwo + 4 + i1*3 + 1], j0, 1, 1, g, gRr, tpRr, rk, res) 722 var x2: i64 = ntr_px_sub(w[xwo + 4 + i2*3], j0, 0, 1, g, gRr, tpRr, rk, res) 723 var y2: i64 = ntr_px_sub(w[xwo + 4 + i2*3 + 1], j0, 1, 1, g, gRr, tpRr, rk, res) 724 var area2: i64 = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0) 725 if area2 < 0 { 726 let tx: i64 = x1 727 x1 = x2 728 x2 = tx 729 let ty: i64 = y1 730 y1 = y2 731 y2 = ty 732 let ti: i64 = i1 733 i1 = i2 734 i2 = ti 735 area2 = 0 - area2 736 } 737 if area2 > 0 { 738 let bx0: i64 = ntr_clamp(ntr_min(ntr_min(x0, x1), x2) / NTR_SUB, 0, res - 1) 739 let bx1: i64 = ntr_clamp(ntr_max(ntr_max(x0, x1), x2) / NTR_SUB, 0, res - 1) 740 let by0: i64 = ntr_clamp(ntr_min(ntr_min(y0, y1), y2) / NTR_SUB, 0, res - 1) 741 let by1: i64 = ntr_clamp(ntr_max(ntr_max(y0, y1), y2) / NTR_SUB, 0, res - 1) 742 var py: i64 = by0 743 while py <= by1 { 744 let cy: i64 = py*NTR_SUB + NTR_SUB/2 745 var px: i64 = bx0 746 while px <= bx1 { 747 let cxp: i64 = px*NTR_SUB + NTR_SUB/2 748 let w0: i64 = (x1 - cxp)*(y2 - cy) - (x2 - cxp)*(y1 - cy) 749 let w1: i64 = (x2 - cxp)*(y0 - cy) - (x0 - cxp)*(y2 - cy) 750 let w2: i64 = (x0 - cxp)*(y1 - cy) - (x1 - cxp)*(y0 - cy) 751 if w0 >= 0 { if w1 >= 0 { if w2 >= 0 { 752 let o: i64 = py*res + px 753 fa[o] = (w0*va[i0] + w1*va[i1] + w2*va[i2]) / area2 754 fb[o] = (w0*vb[i0] + w1*vb[i1] + w2*vb[i2]) / area2 755 cov[o] = NTR_BYTE_MAX as u8 756 } } } 757 px = px + 1 758 } 759 py = py + 1 760 } 761 } 762 } 763 } 764 t = t + 1 765 } 766 sys_munmap(va as *u8, nv*NTR_WORD + 64) 767 sys_munmap(vb as *u8, nv*NTR_WORD + 64) 768 return 0 769} 770 771// ------------------------------------------------------------------------------------------------ 772// ANATOMY HEIGHT at a covered texel, microns above the skin: nipple dome + Montgomery gland domes. 773// a,b = lateral/stature offsets from the apex (um). an = ledger slots (radii already halved from the 774// diameters): RA areola radius, RN nipple radius, HN nipple projection, NMONT, RG gland radius, HG gland 775// height, SEED. Domes are paraboloid caps -- a declared model shape; the SIZES are the cited ones. 776// Gland centres sit on the areola annulus at hashed angle+radius from the registry seed, so two bakes 777// agree and two genomes differ. 778func ntr_anat_h(a: i64, b: i64, an: *i64) -> i64 { 779 let ra: i64 = an[NTR_AN_RA] 780 let rn: i64 = an[NTR_AN_RN] 781 let hn: i64 = an[NTR_AN_HN] 782 let nmont: i64 = an[NTR_AN_NMONT] 783 let rg: i64 = an[NTR_AN_RG] 784 let hg: i64 = an[NTR_AN_HG] 785 let gseed: i64 = an[NTR_AN_SEED] 786 let d2: i64 = a*a + b*b 787 var h: i64 = 0 788 if rn > 0 { if d2 < rn*rn { h = hn * (rn*rn - d2) / (rn*rn) } } 789 if d2 < ra*ra { if d2 >= rn*rn { if nmont > 0 { if rg > 0 { 790 let ring: i64 = ra - rn - 2*rg 791 var k: i64 = 0 792 while k < nmont { 793 let hj: i64 = rlf_hash3(gseed, k, 1) 794 let hr: i64 = rlf_hash3(gseed, k, 2) 795 // angle in RLF_UNIT turns: k/N of a turn plus up to a third of a slot of jitter 796 let angq: i64 = (k * RLF_UNIT / nmont + (hj % (RLF_UNIT / (nmont*3) + 1))) % RLF_UNIT 797 var rad: i64 = rn + rg 798 if ring > 0 { rad = rad + hr % (ring + 1) } 799 let sector: i64 = angq * NTR_NGON / RLF_UNIT 800 let gx: i64 = rad * ntr_cos16(sector) / NTR_E3 801 let gy: i64 = rad * ntr_sin16(sector) / NTR_E3 802 let ex: i64 = a - gx 803 let ey: i64 = b - gy 804 let e2: i64 = ex*ex + ey*ey 805 if e2 < rg*rg { 806 let hk: i64 = hg * (rg*rg - e2) / (rg*rg) 807 if hk > h { h = hk } 808 } 809 k = k + 1 810 } 811 } } } } 812 return h 813} 814 815// ------------------------------------------------------------------------------------------------ 816// THE MICRO TILE: height (um, quantised through the 16-bit channel so a reader decodes exactly what the 817// normal was derived from) and the normal derived from it with periodic neighbours. mask = bands. 818func ntr_bake_tile(seed: i64, mask: i64, hq: *i64, nrgb: *u8, drgb: *u8) -> i64 { 819 let px: i64 = rlf_tile_px() 820 var y: i64 = 0 821 while y < px { 822 var x: i64 = 0 823 while x < px { 824 let h: i64 = rlf_relief_um2_tile_bands(x, y, seed, mask) 825 let v: i64 = ntr_h_enc(h) 826 hq[y*px + x] = ntr_h_dec(v) 827 let o: i64 = (y*px + x)*NTR_RGB 828 drgb[o] = (v / NTR_BYTE) as u8 829 drgb[o+1] = (v % NTR_BYTE) as u8 830 drgb[o+2] = NTR_BYTE_MAX as u8 831 x = x + 1 832 } 833 y = y + 1 834 } 835 let n3: *i64 = sys_mmap(3*NTR_WORD) as *i64 836 let run: i64 = 2 * rlf_tile_texel_um() 837 y = 0 838 while y < px { 839 var x: i64 = 0 840 while x < px { 841 let xp: i64 = rlf_wrap(x + 1, px) 842 let xm: i64 = rlf_wrap(x - 1, px) 843 let yp: i64 = rlf_wrap(y + 1, px) 844 let ym: i64 = rlf_wrap(y - 1, px) 845 ntb_encode_normal_um(hq[y*px + xp] - hq[y*px + xm], hq[yp*px + x] - hq[ym*px + x], run, n3) 846 let o: i64 = (y*px + x)*NTR_RGB 847 nrgb[o] = n3[0] as u8 848 nrgb[o+1] = n3[1] as u8 849 nrgb[o+2] = n3[2] as u8 850 x = x + 1 851 } 852 y = y + 1 853 } 854 sys_munmap(n3 as *u8, 3*NTR_WORD) 855 return 0 856} 857// mean gradient magnitude of a height field, in permil of slope (um per um x 1000): (|dx|+|dy|)/(2 run) 858// averaged over the field. Periodic neighbours when per=1. THE GEOMETRY-NOT-PAINT RULER. 859func ntr_mean_slope_e3(hq: *i64, px: i64, run_um: i64, per: i64) -> i64 { 860 var acc: i64 = 0 861 var y: i64 = 0 862 while y < px { 863 var x: i64 = 0 864 while x < px { 865 var xp: i64 = x + 1 866 var xm: i64 = x - 1 867 var yp: i64 = y + 1 868 var ym: i64 = y - 1 869 if per == 1 { xp = rlf_wrap(xp, px); xm = rlf_wrap(xm, px); yp = rlf_wrap(yp, px); ym = rlf_wrap(ym, px) } 870 if per == 0 { xp = ntr_clamp(xp, 0, px-1); xm = ntr_clamp(xm, 0, px-1); yp = ntr_clamp(yp, 0, px-1); ym = ntr_clamp(ym, 0, px-1) } 871 acc = acc + ntr_abs(hq[y*px + xp] - hq[y*px + xm]) + ntr_abs(hq[yp*px + x] - hq[ym*px + x]) 872 x = x + 1 873 } 874 y = y + 1 875 } 876 if run_um <= 0 { return 0 } 877 return acc * NTR_E3 / (2 * run_um) / (px*px) 878} 879// mean slope over a MASKED window of a res x res height field (non-periodic): texels where sel[o] != 0. 880// Returns permil slope; -1 when the selection is empty (an empty set is not evidence). 881func ntr_mean_slope_sel_e3(hq: *i64, res: i64, run_um: i64, sel: *u8) -> i64 { 882 var acc: i64 = 0 883 var n: i64 = 0 884 var y: i64 = 0 885 while y < res { 886 var x: i64 = 0 887 while x < res { 888 if sel[y*res + x] != (0 as u8) { 889 let xp: i64 = ntr_clamp(x + 1, 0, res-1) 890 let xm: i64 = ntr_clamp(x - 1, 0, res-1) 891 let yp: i64 = ntr_clamp(y + 1, 0, res-1) 892 let ym: i64 = ntr_clamp(y - 1, 0, res-1) 893 acc = acc + ntr_abs(hq[y*res + xp] - hq[y*res + xm]) + ntr_abs(hq[yp*res + x] - hq[ym*res + x]) 894 n = n + 1 895 } 896 x = x + 1 897 } 898 y = y + 1 899 } 900 if n == 0 { return NTR_MISS } 901 if run_um <= 0 { return NTR_MISS } 902 return acc * NTR_E3 / (2 * run_um) / n 903} 904// THE PORE PERIOD, measured on the tile's height field: high-pass by a box mean over a window derived 905// from the pit radius, count texels that are strict local minima below half the pit depth (each plateau 906// once, ties broken by scan order), then period = repeat / sqrt(count). out[0]=count out[1]=period_um. 907func ntr_pore_period(hq: *i64, px: i64, repeat_um: i64, out: *i64) -> i64 { 908 let cell: i64 = rlf_tile_lat(RLF_PORE_PITCH_UM) 909 var rpx: i64 = rlf_pore_radius() * cell / RLF_UNIT 910 if rpx < 1 { rpx = 1 } 911 let win: i64 = 2*rpx + NTR_HP_PAD 912 let hp: *i64 = sys_mmap(px*px*NTR_WORD + 64) as *i64 913 var y: i64 = 0 914 while y < px { 915 var x: i64 = 0 916 while x < px { 917 var s: i64 = 0 918 var n: i64 = 0 919 var dy: i64 = 0 - win 920 while dy <= win { 921 var dx: i64 = 0 - win 922 while dx <= win { 923 s = s + hq[rlf_wrap(y+dy, px)*px + rlf_wrap(x+dx, px)] 924 n = n + 1 925 dx = dx + 1 926 } 927 dy = dy + 1 928 } 929 hp[y*px + x] = hq[y*px + x] - s/n 930 x = x + 1 931 } 932 y = y + 1 933 } 934 let thr: i64 = 0 - RLF_PORE_UM / 2 935 var cnt: i64 = 0 936 y = 0 937 while y < px { 938 var x: i64 = 0 939 while x < px { 940 let v: i64 = hp[y*px + x] 941 if v <= thr { 942 var ismin: i64 = 1 943 var dy: i64 = 0 - 1 944 while dy <= 1 { 945 var dx: i64 = 0 - 1 946 while dx <= 1 { 947 var self: i64 = 0 948 if dx == 0 { if dy == 0 { self = 1 } } 949 if self == 0 { if ismin == 1 { 950 let q: i64 = hp[rlf_wrap(y+dy, px)*px + rlf_wrap(x+dx, px)] 951 if q < v { ismin = 0 } 952 if q == v { 953 // a tie later in scan order owns the plateau; an earlier tie already counted it 954 if dy > 0 { ismin = 0 } 955 if dy == 0 { if dx > 0 { ismin = 0 } } 956 } 957 } } 958 dx = dx + 1 959 } 960 dy = dy + 1 961 } 962 if ismin == 1 { cnt = cnt + 1 } 963 } 964 x = x + 1 965 } 966 y = y + 1 967 } 968 sys_munmap(hp as *u8, px*px*NTR_WORD + 64) 969 out[0] = cnt 970 out[1] = 0 971 if cnt > 0 { out[1] = repeat_um * NTR_E3 / tm_isqrt(cnt * NTR_E6) } 972 return 0 973} 974 975// ------------------------------------------------------------------------------------------------ 976// locate a section's TOC index without checksum verification (for a section this organ is about to 977// replace). nxa_find verifies; this is the cheap TOC scan for existence. 978func ntr_toc_index(b: *u8, ns: i64, tag: *u8) -> i64 { 979 var s: i64 = 0 980 while s < ns { if nt_tageq(b, NT_HDR + s*NT_TOCE, tag) == 1 { return s } s = s + 1 } 981 return NTR_MISS 982} 983// read the anatomy ledger into an[]: 1 = complete, 0 = a row is missing (the caller refuses) 984func ntr_read_ledger(an: *i64) -> i64 { 985 an[NTR_AN_RA] = ntr_conf("areola_dia_um" as *u8) 986 an[NTR_AN_RN] = ntr_conf("nipple_dia_um" as *u8) 987 an[NTR_AN_HN] = ntr_conf("nipple_h_um" as *u8) 988 an[NTR_AN_DL] = ntr_conf("areola_dl_e3" as *u8) 989 an[NTR_AN_NMONT] = ntr_conf("mont_count" as *u8) 990 an[NTR_AN_RG] = ntr_conf("mont_dia_um" as *u8) 991 an[NTR_AN_HG] = ntr_conf("mont_h_um" as *u8) 992 an[NTR_AN_SEED] = ntr_conf("mont_seed" as *u8) 993 var k: i64 = 0 994 while k < NTR_AN_N { if an[k] < 0 { return 0 } k = k + 1 } 995 if an[NTR_AN_RA] == 0 { return 0 } 996 if an[NTR_AN_RN] == 0 { return 0 } 997 if an[NTR_AN_RG] == 0 { return 0 } 998 // diameters -> radii 999 an[NTR_AN_RA] = an[NTR_AN_RA] / 2 1000 an[NTR_AN_RN] = an[NTR_AN_RN] / 2 1001 an[NTR_AN_RG] = an[NTR_AN_RG] / 2 1002 return 1 1003} 1004 1005// ------------------------------------------------------------------------------------------------ 1006// THE MEASURE + BAKE CORE. mode: 0 = measure only (prints the ruler, writes nothing), 1 = bake and write. 1007// res: unique-atlas px (0 = the resolution the asset's TEXM was baked at, read from its header word 1; 1008// refuses if there is no TEXM and no res). ctl: NTR_CTL_* bits. Returns NTR_OK or a named refusal. 1009func ntr_run(inp: *u8, outp: *u8, res_in: i64, seed: i64, hue: i64, ctl: i64, mode: i64) -> i64 { 1010 let lp: *i64 = sys_mmap(NTR_WORD*2) as *i64 1011 let b: *u8 = sys_read_file(inp, lp) 1012 if (b as i64) == 0 { nt_err("TEXR-REFUSE cannot read input NXA\n" as *u8); return NTR_E_IO } 1013 let flen: i64 = lp[0] 1014 if ntr_source_safe(b,flen)==0 {nt_err("TEXR-REFUSE source section extent or index invalid\n" as *u8);return NTR_E_BAD} 1015 if flen < NT_HDR { nt_err("TEXR-REFUSE file too short to be an NXA\n" as *u8); return NTR_E_BAD } 1016 if nt_rd64(b, 0) != nxa_magic() { nt_err("TEXR-REFUSE not an NXA (bad magic)\n" as *u8); return NTR_E_BAD } 1017 let ns: i64 = nt_rd64(b, 16) 1018 if ns < 1 { nt_err("TEXR-REFUSE section count invalid\n" as *u8); return NTR_E_BAD } 1019 if ns >= NT_MAXSEC { nt_err("TEXR-REFUSE section table full\n" as *u8); return NTR_E_BAD } 1020 let w: *i64 = b as *i64 1021 let vwo: i64 = nxa_find(b, flen, nxa_tag4("VERT" as *u8)) 1022 let two: i64 = nxa_find(b, flen, nxa_tag4("TRIS" as *u8)) 1023 let xwo: i64 = nxa_find(b, flen, nxa_tag4("TEXC" as *u8)) 1024 let swo: i64 = nxa_find(b, flen, nxa_tag4("SKEL" as *u8)) 1025 let dwo: i64 = nxa_find(b, flen, nxa_tag4("DYNA" as *u8)) 1026 let mwo: i64 = nxa_find(b, flen, nxa_tag4("TEXM" as *u8)) 1027 if vwo < 0 { nt_err("TEXR-REFUSE no valid VERT section\n" as *u8); return NTR_E_NOSEC } 1028 if two < 0 { nt_err("TEXR-REFUSE no valid TRIS section\n" as *u8); return NTR_E_NOSEC } 1029 if xwo < 0 { nt_err("TEXR-REFUSE no TEXC section -- run nx_nxa_texc first, there are no charts to lay out\n" as *u8); return NTR_E_NOSEC } 1030 if swo < 0 { nt_err("TEXR-REFUSE no valid SKEL section\n" as *u8); return NTR_E_NOSEC } 1031 let nv: i64 = w[vwo] 1032 let ntr: i64 = w[two] 1033 let nj: i64 = w[swo] 1034 let g: i64 = w[xwo + 2] 1035 if w[xwo] != nv { nt_err("TEXR-REFUSE TEXC count does not match VERT\n" as *u8); return NTR_E_MISMATCH } 1036 if g < 1 { nt_err("TEXR-REFUSE TEXC grid invalid\n" as *u8); return NTR_E_BAD } 1037 var res: i64 = res_in 1038 if res <= 0 { if mwo >= 0 { res = w[mwo + 1] } } 1039 if res <= 0 { nt_err("TEXR-REFUSE no resolution given and no TEXM to read one from -- refusing to invent one\n" as *u8); return NTR_E_BAD } 1040 if png_buf_cap(res,res)<0 || res*res>(9223372036854775807-64)/NTR_WORD {nt_err("TEXR-REFUSE resolution byte extent overflow\n" as *u8);return NTR_E_BAD} 1041 if res<g {nt_err("TEXR-REFUSE resolution cannot represent source chart grid\n" as *u8);return NTR_E_BAD} 1042 // axes + unit 1043 let ax: *i64 = sys_mmap(8*NTR_WORD) as *i64 1044 if ntr_axes(w, vwo, nv, ax) == 0 { nt_err("TEXR-REFUSE degenerate vertex span\n" as *u8); return NTR_E_BAD } 1045 let upax: i64 = ntb_up_axis(w, vwo, nv) 1046 // layout 1047 let regs: *i64 = sys_mmap(NTB_MAGIC_4096*NTR_WORD) as *i64 1048 let jrank: *i64 = sys_mmap(NTB_MAGIC_4096*NTR_WORD) as *i64 1049 let gR: *i64 = sys_mmap(NTR_REGIONS*NTR_WORD) as *i64 1050 let njr: *i64 = sys_mmap(NTR_REGIONS*NTR_WORD) as *i64 1051 let rj: *i64 = sys_mmap(NTR_REGIONS*NTB_MAGIC_4096*NTR_WORD) as *i64 1052 let nj2: i64 = ntr_layout(b, flen, regs, jrank, gR, njr, rj) 1053 if nj2 < 0 { return 0 - nj2 } 1054 let apex: *i64 = sys_mmap(8*NTR_WORD) as *i64 1055 // the context 1056 let cx: *i64 = sys_mmap(CX_N*NTR_WORD + 64) as *i64 1057 cx[CX_W] = w as i64 1058 cx[CX_VWO] = vwo 1059 cx[CX_TWO] = two 1060 cx[CX_XWO] = xwo 1061 cx[CX_SWO] = swo 1062 cx[CX_NV] = nv 1063 cx[CX_NTR] = ntr 1064 cx[CX_NJ] = nj 1065 cx[CX_G] = g 1066 cx[CX_RES] = res 1067 cx[CX_UNUM] = RLF_BODY_UM 1068 cx[CX_UDEN] = ax[4] 1069 cx[CX_REGS] = regs as i64 1070 cx[CX_JRANK] = jrank as i64 1071 cx[CX_AX] = ax as i64 1072 cx[CX_APEX] = apex as i64 1073 let tpR: *i64 = sys_mmap(NTR_REGIONS*NTR_WORD) as *i64 1074 let tpW: *i64 = sys_mmap(NTR_REGIONS*NTR_WORD) as *i64 1075 let gW: *i64 = sys_mmap(NTR_REGIONS*NTR_WORD) as *i64 1076 var r: i64 = 0 1077 while r < NTR_REGIONS { tpR[r] = 0; if gR[r] > 0 { tpR[r] = res / gR[r] } tpW[r] = res / g; gW[r] = g; r = r + 1 } 1078 // joint extents 1079 let circ: *i64 = sys_mmap(nj*NTR_WORD + 64) as *i64 1080 let blen: *i64 = sys_mmap(nj*NTR_WORD + 64) as *i64 1081 let nown: *i64 = sys_mmap(nj*NTR_WORD + 64) as *i64 1082 ntr_joint_extents(cx, upax, circ, blen, nown) 1083 // footprints, both layouts 1084 let fsum: *i64 = sys_mmap(nj*NTR_WORD + 64) as *i64 1085 let asum: *i64 = sys_mmap(nj*NTR_WORD + 64) as *i64 1086 let fmax: *i64 = sys_mmap(nj*NTR_WORD + 64) as *i64 1087 let carea: *i64 = sys_mmap(NTR_REGIONS*4*NTR_WORD) as *i64 1088 let rarea: *i64 = sys_mmap(NTR_REGIONS*NTR_WORD) as *i64 1089 let strad: *i64 = sys_mmap(NTR_REGIONS*NTR_WORD) as *i64 1090 let fsumW: *i64 = sys_mmap(nj*NTR_WORD + 64) as *i64 1091 let asumW: *i64 = sys_mmap(nj*NTR_WORD + 64) as *i64 1092 let fmaxW: *i64 = sys_mmap(nj*NTR_WORD + 64) as *i64 1093 let careaW: *i64 = sys_mmap(NTR_REGIONS*4*NTR_WORD) as *i64 1094 let rareaW: *i64 = sys_mmap(NTR_REGIONS*NTR_WORD) as *i64 1095 let stradW: *i64 = sys_mmap(NTR_REGIONS*NTR_WORD) as *i64 1096 ntr_footprint(cx, 0, gW, tpW, fsumW, asumW, fmaxW, careaW, rareaW, stradW) 1097 ntr_footprint(cx, 1, gR, tpR, fsum, asum, fmax, carea, rarea, strad) 1098 let fo: *i64 = sys_mmap(2*NTR_WORD) as *i64 1099 let foW: *i64 = sys_mmap(2*NTR_WORD) as *i64 1100 let mpx: i64 = rlf_tile_px() 1101 if png_buf_cap(mpx,mpx)<0 || mpx*mpx>(9223372036854775807-64)/NTR_WORD {nt_err("TEXR-REFUSE microtile extent overflow\n" as *u8);return NTR_E_BAD} 1102 let mrep: i64 = rlf_tile_repeat_um() 1103 var mmask: i64 = rlf_tile_bands_carried() 1104 if (ctl & NTR_CTL_MICRO) == 0 { mmask = RLF_BAND_PRIMARY } 1105 // ---- THE RULER, printed for every region in both layouts ---- 1106 nt_outs("TEXR-RULER stature_units=" as *u8); nt_outn(ax[4]) 1107 nt_outs(" declared_um=" as *u8); nt_outn(RLF_BODY_UM) 1108 nt_outs(" nj=" as *u8); nt_outn(nj) 1109 nt_outs(" texc_grid=" as *u8); nt_outn(g) 1110 nt_outs(" res=" as *u8); nt_outn(res) 1111 nt_outs(" span_ruler_texel_um=" as *u8); nt_outn(rlf_texel_um(res)) 1112 nt_outs(" (the span ruler; the per-tile Jacobian below is what the chart actually samples)\n" as *u8) 1113 r = 0 1114 while r < NTR_REGIONS { 1115 ntr_region_footprint(r, nj, regs, fsumW, asumW, foW) 1116 ntr_region_footprint(r, nj, regs, fsum, asum, fo) 1117 nt_outs(" region=" as *u8); nt_outs(ntr_region_name(r)) 1118 nt_outs(" tiles=" as *u8); nt_outn(njr[r]) 1119 nt_outs(" whole-body tile_px=" as *u8); nt_outn(tpW[r]) 1120 nt_outs(" footprint_mean_um=" as *u8); nt_outn(foW[0]) 1121 nt_outs(" worst_tile_um=" as *u8); nt_outn(foW[1]) 1122 nt_outs(" | per-region gR=" as *u8); nt_outn(gR[r]) 1123 nt_outs(" tile_px=" as *u8); nt_outn(tpR[r]) 1124 nt_outs(" footprint_mean_um=" as *u8); nt_outn(fo[0]) 1125 nt_outs(" worst_tile_um=" as *u8); nt_outn(fo[1]) 1126 nt_outs(" straddling_tris=" as *u8); nt_outn(strad[r]) 1127 nt_outs("\n UNIQUE adequacy (area permil carrying each band at its own footprint): primary=" as *u8) 1128 if rarea[r] > 0 { nt_outn(carea[r*4] * NTR_E3 / rarea[r]) } else { nt_outn(0) } 1129 nt_outs(" secondary=" as *u8); if rarea[r] > 0 { nt_outn(carea[r*4+1] * NTR_E3 / rarea[r]) } else { nt_outn(0) } 1130 nt_outs(" pore-grid=" as *u8); if rarea[r] > 0 { nt_outn(carea[r*4+2] * NTR_E3 / rarea[r]) } else { nt_outn(0) } 1131 nt_outs(" pore-orif=" as *u8); if rarea[r] > 0 { nt_outn(carea[r*4+3] * NTR_E3 / rarea[r]) } else { nt_outn(0) } 1132 nt_outs(" (whole-body primary=" as *u8); if rareaW[r] > 0 { nt_outn(careaW[r*4] * NTR_E3 / rareaW[r]) } else { nt_outn(0) } 1133 nt_outs(" secondary=" as *u8); if rareaW[r] > 0 { nt_outn(careaW[r*4+1] * NTR_E3 / rareaW[r]) } else { nt_outn(0) } 1134 nt_outs(")\n MICRO adequacy: tile_px=" as *u8); nt_outn(mpx) 1135 nt_outs(" texel_um=" as *u8); nt_outn(rlf_tile_texel_um()) 1136 nt_outs(" repeat_um=" as *u8); nt_outn(mrep) 1137 nt_outs(" primary carried=" as *u8); nt_outn((mmask & RLF_BAND_PRIMARY) / RLF_BAND_PRIMARY) 1138 nt_outs(" secondary carried=" as *u8); nt_outn((mmask & RLF_BAND_SECONDARY) / RLF_BAND_SECONDARY) 1139 nt_outs(" pore-grid carried=" as *u8); nt_outn((mmask & RLF_BAND_POREGRID) / RLF_BAND_POREGRID) 1140 nt_outs(" pore-orif carried=" as *u8); nt_outn((mmask & RLF_BAND_ORIFICE) / RLF_BAND_ORIFICE) 1141 nt_outs(" (declared recipe sampling; physical calibration and renderer integration unverified)\n" as *u8) 1142 r = r + 1 1143 } 1144 if mode == 0 { return NTR_OK } 1145 1146 // ---- ledger (anatomy numbers), refused rather than defaulted ---- 1147 let an: *i64 = sys_mmap(NTR_AN_N*NTR_WORD + 64) as *i64 1148 var k0: i64 = 0 1149 while k0 < NTR_AN_N { an[k0] = 0; k0 = k0 + 1 } 1150 if (ctl & (NTR_CTL_ANATOMY + NTR_CTL_PIGMENT)) != 0 { 1151 if ntr_read_ledger(an) == 0 { nt_err("TEXR-REFUSE knowledge/areola_relief.conf is missing or a row is unsourced (areola_dia_um nipple_dia_um nipple_h_um areola_dl_e3 mont_count mont_dia_um mont_h_um mont_seed) -- REFUSING to bake anatomy from a number nobody cited\n" as *u8); return NTR_E_CONF } 1152 } 1153 // ---- apex ---- 1154 let nap: i64 = ntr_apex(cx, dwo, apex) 1155 nt_outs("TEXR-APEX found=" as *u8); nt_outn(nap) 1156 nt_outs(" L=" as *u8); nt_outn(apex[0]); nt_outs("," as *u8); nt_outn(apex[1]); nt_outs("," as *u8); nt_outn(apex[2]) 1157 nt_outs(" R=" as *u8); nt_outn(apex[3]); nt_outs("," as *u8); nt_outn(apex[4]); nt_outs("," as *u8); nt_outn(apex[5]) 1158 nt_outs(" front_sign=" as *u8); nt_outn(apex[7]) 1159 if nap < 2 { nt_outs(" (no DYNA bust pair: anatomy relief is SKIPPED, named, not faked)" as *u8) } 1160 nt_outs("\n" as *u8) 1161 // ---- allocations shared across regions ---- 1162 let npx: i64 = res*res 1163 let rgb: *u8 = sys_mmap(npx*NTR_RGB + 64) 1164 let hq: *i64 = sys_mmap(npx*NTR_WORD + 64) as *i64 1165 let fa: *i64 = sys_mmap(npx*NTR_WORD + 64) as *i64 1166 let fb: *i64 = sys_mmap(npx*NTR_WORD + 64) as *i64 1167 let cov: *u8 = sys_mmap(npx + 64) 1168 let fb_pixels: i64 = ntr_max(npx,mpx*mpx) 1169 let fbp: *i64 = sys_mmap(fb_pixels*NTR_WORD + 64) as *i64 1170 let thq: *i64 = sys_mmap(mpx*mpx*NTR_WORD + 64) as *i64 1171 let tdr: *u8 = sys_mmap(mpx*mpx*NTR_RGB + 64) 1172 let tnr: *u8 = sys_mmap(mpx*mpx*NTR_RGB + 64) 1173 let n3: *i64 = sys_mmap(3*NTR_WORD) as *i64 1174 let pal: *i64 = sys_mmap(3*NTR_WORD) as *i64 1175 let lab: *i64 = sys_mmap(3*NTR_WORD) as *i64 1176 let only_torso: i64 = (ctl & NTR_CTL_ONLY_TORSO) / NTR_CTL_ONLY_TORSO 1177 var nsets: i64 = 0 1178 if (ctl & NTR_CTL_SETS) != 0 { 1179 r = 0 1180 while r < NTR_REGIONS { 1181 var take: i64 = 0 1182 if njr[r] > 0 { take = 1 } 1183 if only_torso == 1 { if r != NTB_TORSO { take = 0 } } 1184 nsets = nsets + take 1185 r = r + 1 1186 } 1187 } 1188 let setw: i64 = NTR_SET_META + NTR_NMAPS*NTR_MAPREC 1189 let meta: *i64 = sys_mmap(NTR_REGIONS*setw*NTR_WORD + 64) as *i64 1190 // Capacity derives from the shared encoder bound for every emitted map. 1191 let blobcap: i64 = ntr_blob_capacity(res,mpx,nsets) 1192 if blobcap<0 || blobcap>9223372036854775807-64 {nt_err("TEXR-REFUSE blob extent overflow\n" as *u8);return NTR_E_BAD} 1193 let blob: *u8 = sys_mmap(blobcap + 64) 1194 var bo: i64 = 0 1195 // the micro tile is region-independent: bake ONCE, store per set (the page may later want per-region 1196 // seeds; the section format already carries a record per set so that is a data change, not a format one) 1197 ntr_bake_tile(seed, mmask, thq, tnr, tdr) 1198 let pp: *i64 = sys_mmap(2*NTR_WORD) as *i64 1199 ntr_pore_period(thq, mpx, mrep, pp) 1200 let tslope: i64 = ntr_mean_slope_e3(thq, mpx, 2*rlf_tile_texel_um(), 1) 1201 nt_outs("TEXR-MICRO tile_px=" as *u8); nt_outn(mpx) 1202 nt_outs(" repeat_um=" as *u8); nt_outn(mrep) 1203 nt_outs(" bands_mask=" as *u8); nt_outn(mmask) 1204 nt_outs(" pits=" as *u8); nt_outn(pp[0]) 1205 nt_outs(" pore_period_um=" as *u8); nt_outn(pp[1]) 1206 nt_outs(" (law pitch " as *u8); nt_outn(RLF_PORE_PITCH_UM); nt_outs("um) mean_slope_permil=" as *u8); nt_outn(tslope) 1207 nt_outs("\n" as *u8) 1208 if NTR_PROBE_ON == 1 { 1209 NTR_PROBE_THQ = ntr_dup(thq as *u8, mpx*mpx*NTR_WORD) 1210 NTR_PROBE_TNRM = ntr_dup(tnr, mpx*mpx*NTR_RGB) 1211 NTR_PROBE_MPX = mpx 1212 NTR_PROBE_RES = res 1213 NTR_PROBE_SETW = setw 1214 } 1215 var setidx: i64 = 0 1216 r = 0 1217 while r < NTR_REGIONS { 1218 var doreg: i64 = 0 1219 if nsets > 0 { if njr[r] > 0 { doreg = 1 } } 1220 if only_torso == 1 { if r != NTB_TORSO { doreg = 0 } } 1221 if doreg == 1 { 1222 let tp: i64 = tpR[r] 1223 let gr: i64 = gR[r] 1224 let mb: i64 = setidx*setw 1225 ntr_region_footprint(r, nj, regs, fsum, asum, fo) 1226 meta[mb + NTR_S_REGION] = r 1227 meta[mb + NTR_S_GR] = gr 1228 meta[mb + NTR_S_NJR] = njr[r] 1229 meta[mb + NTR_S_TPX] = tp 1230 meta[mb + NTR_S_FMEAN] = fo[0] 1231 meta[mb + NTR_S_FWORST] = fo[1] 1232 meta[mb + NTR_S_BANDS_U] = ntr_bands_at_footprint(fo[0]) 1233 meta[mb + NTR_S_BANDS_M] = mmask 1234 meta[mb + NTR_S_PERIOD] = pp[1] 1235 meta[mb + NTR_S_ALX] = NTR_MISS 1236 meta[mb + NTR_S_ALY] = NTR_MISS 1237 meta[mb + NTR_S_ARX] = NTR_MISS 1238 meta[mb + NTR_S_ARY] = NTR_MISS 1239 meta[mb + NTR_S_ARPX] = 0 1240 meta[mb + NTR_S_DL] = 0 1241 var capm: i64 = 0 1242 if rarea[r] > 0 { capm = carea[r*4] * NTR_E3 / rarea[r] } 1243 meta[mb + NTR_S_CARRIED] = capm 1244 // coverage + apex frame fields 1245 var q: i64 = 0 1246 while q < npx { cov[q] = 0 as u8; fa[q] = 0; fb[q] = 0; q = q + 1 } 1247 var anat_here: i64 = 0 1248 if r == NTB_TORSO { if nap >= 2 { anat_here = 1 } } 1249 if anat_here == 1 { ntr_raster(cx, r, gr, tp, fa, fb, cov) } 1250 // region base albedo ONCE (the locus does root-finding) 1251 ntb_albedo_genome(hue, r, pal) 1252 let lbase: i64 = si_lstar_of_gene(hue, si_dl_of_region(r)) 1253 let ra: i64 = an[NTR_AN_RA] 1254 let dl: i64 = an[NTR_AN_DL] 1255 var apxl: i64 = NTR_MISS 1256 var apxbest: i64 = 0 1257 // ---- ALBEDO ---- 1258 var y: i64 = 0 1259 while y < res { 1260 let ty: i64 = y / tp 1261 var x: i64 = 0 1262 while x < res { 1263 let tx: i64 = x / tp 1264 let rk: i64 = ty*gr + tx 1265 var cr: i64 = NTB_VOID_R 1266 var cg: i64 = NTB_VOID_G 1267 var cb: i64 = NTB_VOID_B 1268 if tx < gr { if rk < njr[r] { 1269 cr = pal[0]; cg = pal[1]; cb = pal[2] 1270 let o: i64 = y*res + x 1271 if (ctl & NTR_CTL_PIGMENT) != 0 { if cov[o] != (0 as u8) { 1272 let d2: i64 = fa[o]*fa[o] + fb[o]*fb[o] 1273 let edge: i64 = RLF_PORE_PITCH_UM 1274 let rout: i64 = ra + edge 1275 if d2 < rout*rout { 1276 let d: i64 = tm_isqrt(d2) 1277 var wgt: i64 = NTR_E3 1278 if d > ra { wgt = (rout - d) * NTR_E3 / edge } 1279 si_srgb_of_lab(lbase - dl*wgt/NTR_E3, SI_A_STAR, SI_B_STAR, lab) 1280 cr = lab[0]; cg = lab[1]; cb = lab[2] 1281 if apxl < 0 { apxl = o; apxbest = d2 } 1282 if d2 < apxbest { apxl = o; apxbest = d2 } 1283 } 1284 } } 1285 let hh: i64 = x*NTB_HASH_A + y*NTB_HASH_B + seed*NTB_HASH_C 1286 let dd: i64 = (hh >> 10) % 7 - 3 1287 cr = ntr_clamp(cr + dd, 0, NTR_BYTE_MAX) 1288 cg = ntr_clamp(cg + dd, 0, NTR_BYTE_MAX) 1289 cb = ntr_clamp(cb + dd, 0, NTR_BYTE_MAX) 1290 } } 1291 let oo: i64 = (y*res + x)*NTR_RGB 1292 rgb[oo] = cr as u8 1293 rgb[oo+1] = cg as u8 1294 rgb[oo+2] = cb as u8 1295 x = x + 1 1296 } 1297 y = y + 1 1298 } 1299 if apxl >= 0 { meta[mb + NTR_S_ALX] = apxl % res; meta[mb + NTR_S_ALY] = apxl / res; meta[mb + NTR_S_DL] = dl } 1300 // areola radius in px of THIS region's mean footprint (a receipt number, the gate re-derives) 1301 if fo[0] > 0 { meta[mb + NTR_S_ARPX] = ra / fo[0] } 1302 if NTR_PROBE_ON == 1 { if r == NTB_TORSO { NTR_PROBE_RGB = ntr_dup(rgb, npx*NTR_RGB) } } 1303 let alen: i64 = ntr_png_checked(rgb, res, res, fbp, fb_pixels, ((blob as i64) + bo) as *u8, blobcap-bo) 1304 if alen <= 0 { nt_err("TEXR-REFUSE albedo PNG encode produced nothing\n" as *u8); return NTR_E_BAD } 1305 if bo + alen > blobcap { nt_err("TEXR-REFUSE blob reserve exceeded -- refusing rather than truncating a map\n" as *u8); return NTR_E_BAD } 1306 meta[mb + NTR_SET_META + NTR_MAP_ALBEDO*NTR_MAPREC] = NTR_MAP_ALBEDO 1307 meta[mb + NTR_SET_META + NTR_MAP_ALBEDO*NTR_MAPREC + 1] = bo 1308 meta[mb + NTR_SET_META + NTR_MAP_ALBEDO*NTR_MAPREC + 2] = alen 1309 bo = bo + alen 1310 // ---- HEIGHT (um -> quantised) ---- 1311 y = 0 1312 while y < res { 1313 let ty: i64 = y / tp 1314 var x: i64 = 0 1315 while x < res { 1316 let tx: i64 = x / tp 1317 let rk: i64 = ty*gr + tx 1318 var h: i64 = 0 1319 var covered: i64 = 0 1320 if tx < gr { if rk < njr[r] { 1321 let jj: i64 = rj[r*nj + rk] 1322 var ft: i64 = fo[0] 1323 if asum[jj] > 0 { ft = fsum[jj] / asum[jj] } 1324 if ft < 1 { ft = 1 } 1325 if (ctl & NTR_CTL_RELIEF) != 0 { h = rlf_relief_um2_span(x, y, ft*tp, tp, seed) } 1326 let o: i64 = y*res + x 1327 if cov[o] != (0 as u8) { 1328 covered = 1 1329 if (ctl & NTR_CTL_ANATOMY) != 0 { h = h + ntr_anat_h(fa[o], fb[o], an) } 1330 } 1331 } } 1332 let v: i64 = ntr_h_enc(h) 1333 hq[y*res + x] = ntr_h_dec(v) 1334 let oo: i64 = (y*res + x)*NTR_RGB 1335 rgb[oo] = (v / NTR_BYTE) as u8 1336 rgb[oo+1] = (v % NTR_BYTE) as u8 1337 rgb[oo+2] = 0 as u8 1338 if covered == 1 { rgb[oo+2] = NTR_BYTE_MAX as u8 } 1339 x = x + 1 1340 } 1341 y = y + 1 1342 } 1343 if NTR_PROBE_ON == 1 { if r == NTB_TORSO { 1344 NTR_PROBE_HQ = ntr_dup(hq as *u8, npx*NTR_WORD) 1345 NTR_PROBE_COV = ntr_dup(cov, npx) 1346 NTR_PROBE_FA = ntr_dup(fa as *u8, npx*NTR_WORD) 1347 NTR_PROBE_FB = ntr_dup(fb as *u8, npx*NTR_WORD) 1348 } } 1349 let dlen: i64 = ntr_png_checked(rgb, res, res, fbp, fb_pixels, ((blob as i64) + bo) as *u8, blobcap-bo) 1350 if dlen <= 0 { nt_err("TEXR-REFUSE height PNG encode produced nothing\n" as *u8); return NTR_E_BAD } 1351 if bo + dlen > blobcap { nt_err("TEXR-REFUSE blob reserve exceeded -- refusing rather than truncating a map\n" as *u8); return NTR_E_BAD } 1352 meta[mb + NTR_SET_META + NTR_MAP_DISP*NTR_MAPREC] = NTR_MAP_DISP 1353 meta[mb + NTR_SET_META + NTR_MAP_DISP*NTR_MAPREC + 1] = bo 1354 meta[mb + NTR_SET_META + NTR_MAP_DISP*NTR_MAPREC + 2] = dlen 1355 bo = bo + dlen 1356 // ---- NORMAL, derived from the quantised height with the tile's own texel run ---- 1357 y = 0 1358 while y < res { 1359 let ty: i64 = y / tp 1360 var x: i64 = 0 1361 while x < res { 1362 let tx: i64 = x / tp 1363 let rk: i64 = ty*gr + tx 1364 var er: i64 = NTB_N_MID 1365 var eg: i64 = NTB_N_MID 1366 var eb: i64 = NTB_N_MID + NTB_N_SCALE 1367 if tx < gr { if rk < njr[r] { 1368 let jj: i64 = rj[r*nj + rk] 1369 var ft: i64 = fo[0] 1370 if asum[jj] > 0 { ft = fsum[jj] / asum[jj] } 1371 if ft < 1 { ft = 1 } 1372 let xp: i64 = ntr_clamp(x + 1, 0, res - 1) 1373 let xm: i64 = ntr_clamp(x - 1, 0, res - 1) 1374 let yp: i64 = ntr_clamp(y + 1, 0, res - 1) 1375 let ym: i64 = ntr_clamp(y - 1, 0, res - 1) 1376 ntb_encode_normal_um(hq[y*res + xp] - hq[y*res + xm], hq[yp*res + x] - hq[ym*res + x], 2*ft, n3) 1377 er = n3[0]; eg = n3[1]; eb = n3[2] 1378 } } 1379 let oo: i64 = (y*res + x)*NTR_RGB 1380 rgb[oo] = er as u8 1381 rgb[oo+1] = eg as u8 1382 rgb[oo+2] = eb as u8 1383 x = x + 1 1384 } 1385 y = y + 1 1386 } 1387 if NTR_PROBE_ON == 1 { if r == NTB_TORSO { NTR_PROBE_NRM = ntr_dup(rgb, npx*NTR_RGB) } } 1388 let nlen: i64 = ntr_png_checked(rgb, res, res, fbp, fb_pixels, ((blob as i64) + bo) as *u8, blobcap-bo) 1389 if nlen <= 0 { nt_err("TEXR-REFUSE normal PNG encode produced nothing\n" as *u8); return NTR_E_BAD } 1390 if bo + nlen > blobcap { nt_err("TEXR-REFUSE blob reserve exceeded -- refusing rather than truncating a map\n" as *u8); return NTR_E_BAD } 1391 meta[mb + NTR_SET_META + NTR_MAP_NORMAL*NTR_MAPREC] = NTR_MAP_NORMAL 1392 meta[mb + NTR_SET_META + NTR_MAP_NORMAL*NTR_MAPREC + 1] = bo 1393 meta[mb + NTR_SET_META + NTR_MAP_NORMAL*NTR_MAPREC + 2] = nlen 1394 bo = bo + nlen 1395 // ---- MICRO TILE maps (encoded per set so each set is self-contained) ---- 1396 let tdl: i64 = ntr_png_checked(tdr, mpx, mpx, fbp, fb_pixels, ((blob as i64) + bo) as *u8, blobcap-bo) 1397 if tdl <= 0 { nt_err("TEXR-REFUSE tile height PNG encode produced nothing\n" as *u8); return NTR_E_BAD } 1398 meta[mb + NTR_SET_META + NTR_MAP_TDISP*NTR_MAPREC] = NTR_MAP_TDISP 1399 meta[mb + NTR_SET_META + NTR_MAP_TDISP*NTR_MAPREC + 1] = bo 1400 meta[mb + NTR_SET_META + NTR_MAP_TDISP*NTR_MAPREC + 2] = tdl 1401 bo = bo + tdl 1402 let tnl: i64 = ntr_png_checked(tnr, mpx, mpx, fbp, fb_pixels, ((blob as i64) + bo) as *u8, blobcap-bo) 1403 if tnl <= 0 { nt_err("TEXR-REFUSE tile normal PNG encode produced nothing\n" as *u8); return NTR_E_BAD } 1404 if bo + tnl > blobcap { nt_err("TEXR-REFUSE blob reserve exceeded -- refusing rather than truncating a map\n" as *u8); return NTR_E_BAD } 1405 meta[mb + NTR_SET_META + NTR_MAP_TNORMAL*NTR_MAPREC] = NTR_MAP_TNORMAL 1406 meta[mb + NTR_SET_META + NTR_MAP_TNORMAL*NTR_MAPREC + 1] = bo 1407 meta[mb + NTR_SET_META + NTR_MAP_TNORMAL*NTR_MAPREC + 2] = tnl 1408 bo = bo + tnl 1409 if NTR_PROBE_ON == 1 { if r == NTB_TORSO { NTR_PROBE_META = ntr_dup(((meta as i64) + mb*NTR_WORD) as *u8, setw*NTR_WORD) } } 1410 nt_outs("TEXR-SET region=" as *u8); nt_outs(ntr_region_name(r)) 1411 nt_outs(" gR=" as *u8); nt_outn(gr); nt_outs(" tile_px=" as *u8); nt_outn(tp) 1412 nt_outs(" footprint_mean_um=" as *u8); nt_outn(fo[0]) 1413 nt_outs(" bands_unique=" as *u8); nt_outn(meta[mb + NTR_S_BANDS_U]) 1414 nt_outs(" bands_micro=" as *u8); nt_outn(mmask) 1415 nt_outs(" albedo_bytes=" as *u8); nt_outn(alen) 1416 nt_outs(" height_bytes=" as *u8); nt_outn(dlen) 1417 nt_outs(" normal_bytes=" as *u8); nt_outn(nlen) 1418 nt_outs(" tile_height_bytes=" as *u8); nt_outn(tdl) 1419 nt_outs(" tile_normal_bytes=" as *u8); nt_outn(tnl) 1420 if anat_here == 1 { nt_outs(" areola_apex_px=" as *u8); nt_outn(meta[mb + NTR_S_ALX]); nt_outs("," as *u8); nt_outn(meta[mb + NTR_S_ALY]); nt_outs(" areola_r_px=" as *u8); nt_outn(meta[mb + NTR_S_ARPX]) } 1421 nt_outs("\n" as *u8) 1422 setidx = setidx + 1 1423 } 1424 r = r + 1 1425 } 1426 // ---- assemble TEXR words ---- 1427 let blobbytes: i64 = bo 1428 var blobwords: i64 = blobbytes / NTR_WORD 1429 if blobwords*NTR_WORD < blobbytes { blobwords = blobwords + 1 } 1430 let jwords: i64 = nj*NTR_JOINT_WORDS 1431 let twords: i64 = NTR_HDR_WORDS + jwords + nsets*setw + blobwords 1432 // ---- rebuild the container: every section forward, TEXR replaced not duplicated ---- 1433 let tri: i64 = ntr_toc_index(b, ns, "TEXR" as *u8) 1434 var nsec: i64 = ns 1435 if tri < 0 { nsec = ns + 1 } 1436 if nsec >= NT_MAXSEC { nt_err("TEXR-REFUSE section table full\n" as *u8); return NTR_E_BAD } 1437 let toclen: i64 = NT_HDR + nsec*NT_TOCE 1438 var total: i64 = toclen 1439 var s2: i64 = 0 1440 while s2 < ns { 1441 if s2 != tri { 1442 let carried_words:i64=nt_rd64(b,NT_HDR+s2*NT_TOCE+16) 1443 if carried_words>(9223372036854775807-total)/NTR_WORD {nt_err("TEXR-REFUSE carried extent overflow\n" as *u8);return NTR_E_BAD} 1444 total=total+carried_words*NTR_WORD 1445 } 1446 s2 = s2 + 1 1447 } 1448 if total>9223372036854775807-NT_PAD {nt_err("TEXR-REFUSE output padding overflow\n" as *u8);return NTR_E_BAD} 1449 if twords>(9223372036854775807-total-NT_PAD)/NTR_WORD {nt_err("TEXR-REFUSE output extent overflow\n" as *u8);return NTR_E_BAD} 1450 total = total + twords*NTR_WORD 1451 let nb: *u8 = sys_mmap_try(total + NT_PAD) 1452 if (nb as i64) == 0 { nt_err("TEXR-REFUSE cannot allocate output\n" as *u8); return NTR_E_IO } 1453 nt_wr64(nb, 0, nxa_magic()) 1454 nt_wr64(nb, 8, NXA_VER) 1455 nt_wr64(nb, 16, nsec) 1456 var wo: i64 = toclen 1457 var ti: i64 = 0 1458 var s3: i64 = 0 1459 var carried: i64 = 0 1460 while s3 < ns { 1461 if s3 != tri { 1462 let e3: i64 = NT_HDR + s3*NT_TOCE 1463 let oldoff: i64 = nt_rd64(b, e3 + 8) 1464 let wl: i64 = nt_rd64(b, e3 + 16) 1465 let te: i64 = NT_HDR + ti*NT_TOCE 1466 nt_wr64(nb, te, nt_rd64(b, e3)) 1467 nt_wr64(nb, te + 8, wo) 1468 nt_wr64(nb, te + 16, wl) 1469 var k2: i64 = 0 1470 while k2 < wl*NTR_WORD { nb[wo + k2] = b[oldoff + k2]; k2 = k2 + 1 } 1471 let pw2: *i64 = ((nb as i64) + wo) as *i64 1472 nt_wr64(nb, te + 24, nxa_check2(1, pw2, wl)) 1473 wo = wo + wl*NTR_WORD 1474 ti = ti + 1 1475 carried = carried + 1 1476 } 1477 s3 = s3 + 1 1478 } 1479 let te4: i64 = NT_HDR + ti*NT_TOCE 1480 nt_wr64(nb, te4, nxa_tag4("TEXR" as *u8)) 1481 nt_wr64(nb, te4 + 8, wo) 1482 nt_wr64(nb, te4 + 16, twords) 1483 var z: i64 = 0 1484 while z < twords*NTR_WORD { nb[wo + z] = 0 as u8; z = z + 1 } 1485 nt_wr64(nb, wo + NTR_W_NSETS*NTR_WORD, nsets) 1486 nt_wr64(nb, wo + NTR_W_RES*NTR_WORD, res) 1487 nt_wr64(nb, wo + NTR_W_MODEL*NTR_WORD, NTR_MODEL_SPECGLOSS) 1488 nt_wr64(nb, wo + NTR_W_SEED*NTR_WORD, seed) 1489 nt_wr64(nb, wo + NTR_W_HUE*NTR_WORD, hue) 1490 nt_wr64(nb, wo + NTR_W_BLOBW*NTR_WORD, blobwords) 1491 nt_wr64(nb, wo + NTR_W_NMAPS*NTR_WORD, NTR_NMAPS) 1492 nt_wr64(nb, wo + NTR_W_MPX*NTR_WORD, mpx) 1493 nt_wr64(nb, wo + NTR_W_MREP*NTR_WORD, mrep) 1494 nt_wr64(nb, wo + NTR_W_RANGE*NTR_WORD, NTR_DISP_RANGE_UM) 1495 nt_wr64(nb, wo + NTR_W_NJ*NTR_WORD, nj) 1496 nt_wr64(nb, wo + NTR_W_SETW*NTR_WORD, setw) 1497 nt_wr64(nb, wo + NTR_W_JW*NTR_WORD, NTR_JOINT_WORDS) 1498 nt_wr64(nb, wo + NTR_W_VER*NTR_WORD, NTR_VERSION) 1499 var j: i64 = 0 1500 while j < nj { 1501 let jo: i64 = wo + (NTR_HDR_WORDS + j*NTR_JOINT_WORDS)*NTR_WORD 1502 nt_wr64(nb, jo, regs[j]) 1503 nt_wr64(nb, jo + NTR_WORD, jrank[j]) 1504 nt_wr64(nb, jo + NTR_WORD*2, circ[j]) 1505 nt_wr64(nb, jo + NTR_WORD*3, blen[j]) 1506 j = j + 1 1507 } 1508 var m: i64 = 0 1509 while m < nsets*setw { 1510 nt_wr64(nb, wo + (NTR_HDR_WORDS + jwords + m)*NTR_WORD, meta[m]) 1511 m = m + 1 1512 } 1513 let blobbase: i64 = wo + (NTR_HDR_WORDS + jwords + nsets*setw)*NTR_WORD 1514 var c3: i64 = 0 1515 while c3 < blobbytes { nb[blobbase + c3] = blob[c3]; c3 = c3 + 1 } 1516 let pw3: *i64 = ((nb as i64) + wo) as *i64 1517 nt_wr64(nb, te4 + 24, nxa_check2(1, pw3, twords)) 1518 wo = wo + twords*NTR_WORD 1519 let tb2: *i64 = ((nb as i64) + NT_HDR) as *i64 1520 nt_wr64(nb, 24, nxa_check2(1, tb2, nsec*4)) 1521 let wrote:i64=fsx_write(outp,nb,wo) 1522 if wrote!=wo {nt_err("TEXR-REFUSE atomic output write failed\n" as *u8);return NTR_E_IO} 1523 nt_outs("TEXR-OK sets=" as *u8); nt_outn(nsets) 1524 nt_outs(" res=" as *u8); nt_outn(res) 1525 nt_outs(" micro_px=" as *u8); nt_outn(mpx) 1526 nt_outs(" sections_carried=" as *u8); nt_outn(carried) 1527 nt_outs(" sections_out=" as *u8); nt_outn(nsec) 1528 nt_outs(" texr_bytes=" as *u8); nt_outn(twords*NTR_WORD) 1529 nt_outs(" file_bytes=" as *u8); nt_outn(wo) 1530 nt_outs(" (TEXM carried byte-for-byte; map set 0 unchanged)\n" as *u8) 1531 return NTR_OK 1532} 1533 1534// public verbs 1535func ntr_measure(inp: *u8, res: i64) -> i64 { return ntr_run(inp, inp, res, 0, 0, NTR_CTL_ALL, 0) } 1536func ntr_apply(inp: *u8, outp: *u8, res: i64, seed: i64, hue: i64) -> i64 { return ntr_run(inp, outp, res, seed, hue, NTR_CTL_ALL, 1) } 1537func ntr_apply_ctl(inp: *u8, outp: *u8, res: i64, seed: i64, hue: i64, ctl: i64) -> i64 { return ntr_run(inp, outp, res, seed, hue, ctl, 1) } 1538 1539// ------------------------------------------------------------------------------------------------ 1540// READERS for gates and the page-half handoff: TEXR word offset (verified), and the ADEQUACY PREDICATE: 1541// 1 iff every region set is present (nsets == NTR_REGIONS) and every set's MICRO band mask carries the 1542// three coarser bands. Bound to the region count by construction -- zero sets cannot pass. 1543// TEXR structural boundary: reuse validated NXA section extent before reading records. 1544// Payload integrity is not a claim about decoded image quality or anatomical adequacy. 1545func ntr_texr_validate(b:*u8,flen:i64)->i64{ 1546 let entry:i64=nxa_section_entry(b,flen,nxa_tag4("TEXR")) 1547 if entry<0{return entry} 1548 let w:*i64=b as *i64 1549 let words:i64=w[entry+2] 1550 if words<NTR_HDR_WORDS{return 0-3} 1551 let o:i64=w[entry+1]/NTR_WORD 1552 if w[o+NTR_W_VER]>NTR_VERSION{return 0-2} 1553 if w[o+NTR_W_VER]!=NTR_VERSION{return 0-3} 1554 if w[o+NTR_W_MODEL]!=NTR_MODEL_SPECGLOSS{return 0-3} 1555 if w[o+NTR_W_NMAPS]!=NTR_NMAPS{return 0-3} 1556 if w[o+NTR_W_JW]!=NTR_JOINT_WORDS{return 0-3} 1557 let stride:i64=NTR_SET_META+NTR_NMAPS*NTR_MAPREC 1558 if w[o+NTR_W_SETW]!=stride{return 0-3} 1559 if w[o+NTR_W_RES]<=0{return 0-3} 1560 if w[o+NTR_W_MPX]<=0{return 0-3} 1561 if w[o+NTR_W_MREP]<=0{return 0-3} 1562 if w[o+NTR_W_RANGE]<=0{return 0-3} 1563 let nj:i64=w[o+NTR_W_NJ] 1564 let ns:i64=w[o+NTR_W_NSETS] 1565 if nj<0{return 0-3} 1566 if ns<0{return 0-3};if ns>NTR_REGIONS{return 0-3} 1567 var remain:i64=words-NTR_HDR_WORDS 1568 if nj>remain/NTR_JOINT_WORDS{return 0-3} 1569 let jw:i64=nj*NTR_JOINT_WORDS 1570 remain=remain-jw 1571 if ns>remain/stride{return 0-3} 1572 remain=remain-ns*stride 1573 if w[o+NTR_W_BLOBW]!=remain{return 0-3} 1574 let blobBytes:i64=remain*NTR_WORD 1575 var seen:i64=0;var s:i64=0 1576 while s<ns{ 1577 let at:i64=o+NTR_HDR_WORDS+jw+s*stride 1578 let region:i64=w[at+NTR_S_REGION] 1579 if region<0{return 0-3};if region>=NTR_REGIONS{return 0-3} 1580 let bit:i64=1<<region 1581 if (seen&bit)!=0{return 0-3};seen=seen|bit 1582 var m:i64=0 1583 while m<NTR_NMAPS{ 1584 let rec:i64=at+NTR_SET_META+m*NTR_MAPREC 1585 if w[rec]!=m{return 0-3} 1586 let off:i64=w[rec+1];let bytes:i64=w[rec+2] 1587 if off<0{return 0-3};if off>blobBytes{return 0-3} 1588 if bytes<=0{return 0-3};if bytes>blobBytes-off{return 0-3} 1589 m=m+1 1590 } 1591 s=s+1 1592 } 1593 return o 1594} 1595 1596func ntr_texr_find(b: *u8, flen: i64) -> i64 { return ntr_texr_validate(b, flen) } 1597func ntr_texr_adequate(b: *u8, flen: i64) -> i64 { 1598 let o: i64 = ntr_texr_find(b, flen) 1599 if o < 0 { return 0 } 1600 let w: *i64 = b as *i64 1601 let nsets: i64 = w[o + NTR_W_NSETS] 1602 if nsets != NTR_REGIONS { return 0 } 1603 let nj: i64 = w[o + NTR_W_NJ] 1604 let setw: i64 = w[o + NTR_W_SETW] 1605 let want: i64 = RLF_BAND_PRIMARY + RLF_BAND_SECONDARY + RLF_BAND_POREGRID 1606 var s: i64 = 0 1607 while s < nsets { 1608 let mb: i64 = o + NTR_HDR_WORDS + nj*NTR_JOINT_WORDS + s*setw 1609 if (w[mb + NTR_S_BANDS_M] & want) != want { return 0 } 1610 s = s + 1 1611 } 1612 return 1 1613} 1614// the set record base (word offset) for a region, or -1 1615func ntr_texr_set(b: *u8, flen: i64, region: i64) -> i64 { 1616 let o: i64 = ntr_texr_find(b, flen) 1617 if o < 0 { return NTR_MISS } 1618 let w: *i64 = b as *i64 1619 let nsets: i64 = w[o + NTR_W_NSETS] 1620 let nj: i64 = w[o + NTR_W_NJ] 1621 let setw: i64 = w[o + NTR_W_SETW] 1622 var s: i64 = 0 1623 while s < nsets { 1624 let mb: i64 = o + NTR_HDR_WORDS + nj*NTR_JOINT_WORDS + s*setw 1625 if w[mb + NTR_S_REGION] == region { return mb } 1626 s = s + 1 1627 } 1628 return NTR_MISS 1629}