code wiki / (root) / nx_nxa_texbake_region_edge_t278.nx

nx_nxa_texbake_region_edge_t278.nx source

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