nx_nxa_tier_region_candidate_t143.nx source
↩ module page · 588 lines · 27317 B
1// nx_nxa_tier.nx -- THE INGEST TIER LADDER FOR NXA ASSETS (/compare/modding MD5 ingest_tier_emit, 2026-09-05).
2//
3// WHY (measured 2026-09-05 on the served /world/beach cast through nx_nxa_play): every visiting-cast donor
4// carries a COMPLETE rig (zero_weight_verts=0 on all five assets), and the cast still fails to arrive --
5// toon3d8.nxa is 61,939,208 B with 370 joints and 423,919 verts against the house girl's 11,728,120 B and
6// 14,164 verts, and NOTHING on the ingest side emits a smaller tier, so the heaviest rip is served at full
7// weight or not at all. This organ turns one NXA into a SMALLER NXA under a declared vertex budget, so a
8// world can pick a door for every device (the scale-from-a-sensor-to-a-supercomputer law applied to assets).
9//
10// HOW: dense-grid vertex clustering (Rossignac-Borrel 1993, the same idea nx_mesh_decimate applies to an
11// NxMesh) applied DIRECTLY to the NXA sections, with the cell size DERIVED by binary search so the result
12// is a measured grid whose occupied-cell count fits the budget -- never a hand-picked cell. Each occupied
13// cell becomes one vertex at the integer centroid of its members; the member NEAREST that centroid lends
14// its SKIN row (four joints and q12 weights summing 4096, so no weight is ever invented) and its TEXC row
15// (the bone-anchored UV, so the TEXM atlas still samples). Triangles are remapped and the collapsed ones
16// (two corners in one cell) dropped. Sections that do not index vertices or triangles (SKEL ANIM POSE MATL
17// TEXM HSTR) are copied verbatim with their checksums RECOMPUTED, never copied; any other section is
18// DROPPED BY NAME and counted, because a per-vertex or per-triangle section this rung does not understand
19// would silently desynchronise from the new VERT -- CLUS, MORF and the garment sections are the known
20// cases and each is a later rung, not a silent loss.
21//
22// REFUSALS, each by name and each writing NOTHING: unreadable or corrupt input, no VERT or TRIS, a budget
23// at or above the source vertex count (a tier is never an upsample), a budget under 3, a degenerate bbox.
24//
25// Usage: nx_nxa_tier <in.nxa> <out.nxa> <max_verts>
26// nx_nxa_tier ladder <in.nxa> <out_prefix> [ladder.conf] rows tier|<id>|<max_verts>|<why>
27// Exits: 0 ok | 2 usage | 3 refused-by-name | 5 missing section
28// 100% sovereign, integer only. No hw writes (Rule 26). license_tier: ORIGINAL expect_exit: 0
29import "nx_syscalls.nx"
30import "nx_nxa.nx"
31
32const TR_HDR: i64 = 32
33const TR_TOCE: i64 = 32
34const TR_TOC_W: i64 = 4
35const TR_MAXSEC: i64 = 64
36const TR_MODE: i64 = 0x1a4 // 0644 on the published asset, matching every sibling NXA writer
37const TR_PAD: i64 = 4096
38const TR_SKIN_W: i64 = 8 // SKIN row: 4 joints + 4 q12 weights (spec)
39const TR_Q12: i64 = 4096
40const TR_TEXC_HDR: i64 = 4 // TEXC payload header words: nverts, stride, grid, 0 (nx_nxa_texc_lib)
41const TR_MIN_VERTS: i64 = 3
42const TR_HASH_A: i64 = 73856093 // the classic spatial-hash primes (Teschner 2003), integer only
43const TR_HASH_B: i64 = 19349663
44const TR_HASH_C: i64 = 83492791
45const TR_TAG_BYTES: i64 = 4
46const TR_ERRFD: i64 = 2
47const TR_OUTFD: i64 = 1
48const TR_EXIT_USAGE: i64 = 2
49const TR_EXIT_REFUSE: i64 = 3
50const TR_EXIT_NOSEC: i64 = 5
51const TR_LADDER_DEFAULT: *u8 = "knowledge/nxa_tier_ladder.conf"
52const TR_MAXTIERS: i64 = 16
53const TR_PATH_CAP: i64 = 1024
54const TR_ASCII_ZERO: i64 = 48
55const TR_ASCII_NINE: i64 = 57
56const TR_ASCII_NL: i64 = 10
57const TR_ASCII_PIPE: i64 = 124
58const TR_ASCII_HASH: i64 = 35
59const TR_ASCII_US: i64 = 95
60const TR_ASCII_DOT: i64 = 46
61const TR_ASCII_MINUS: i64 = 45
62const TR_ASCII_T: i64 = 116
63const TR_ASCII_I: i64 = 105
64const TR_ASCII_E: i64 = 101
65const TR_ASCII_R: i64 = 114
66const TR_ASCII_L: i64 = 108
67const TR_ASCII_A: i64 = 97
68const TR_ASCII_D: i64 = 100
69const TR_ASCII_N: i64 = 110
70const TR_ASCII_X: i64 = 120
71const TR_LADDER_F: i64 = 4 // tier|id|max_verts|why
72
73func tr_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
74func tr_out(s: *u8) -> i64 { sys_write(TR_OUTFD, s, tr_slen(s)); return 0 }
75func tr_err(s: *u8) -> i64 { sys_write(TR_ERRFD, s, tr_slen(s)); return 0 }
76func tr_num(v: i64) -> i64 {
77 let b: *u8 = sys_mmap(32)
78 var x: i64 = v
79 if x < 0 { b[0] = TR_ASCII_MINUS as u8; sys_write(TR_OUTFD, b, 1); x = 0 - x }
80 if x == 0 { b[0] = TR_ASCII_ZERO as u8; sys_write(TR_OUTFD, b, 1); return 0 }
81 var d: i64 = 0
82 var y: i64 = x
83 while y > 0 { d = d + 1; y = y / 10 }
84 var i: i64 = d
85 while i > 0 { i = i - 1; b[i] = ((x % 10) + TR_ASCII_ZERO) as u8; x = x / 10 }
86 sys_write(TR_OUTFD, b, d)
87 return 0
88}
89func tr_atoi(s: *u8) -> i64 {
90 var v: i64 = 0
91 var i: i64 = 0
92 while s[i] != (0 as u8) {
93 let c: i64 = s[i] as i64
94 if c >= TR_ASCII_ZERO { if c <= TR_ASCII_NINE { v = v * 10 + (c - TR_ASCII_ZERO) } }
95 i = i + 1
96 }
97 return v
98}
99func tr_rd64(b: *u8, off: i64) -> i64 { let p: *i64 = ((b as i64) + off) as *i64; return p[0] }
100func tr_wr64(b: *u8, off: i64, v: i64) -> i64 { let p: *i64 = ((b as i64) + off) as *i64; p[0] = v; return 0 }
101func tr_tagout(tag: i64) -> i64 {
102 let t: *u8 = sys_mmap(8)
103 t[0] = (tag & 255) as u8
104 t[1] = ((tag >> 8) & 255) as u8
105 t[2] = ((tag >> 16) & 255) as u8
106 t[3] = ((tag >> 24) & 255) as u8
107 sys_write(TR_OUTFD, t, TR_TAG_BYTES)
108 return 0
109}
110// a section whose payload never indexes VERT or TRIS may be copied verbatim into a tier
111func tr_copyable(tag: i64) -> i64 {
112 if tag == nxa_tag4("SKEL" as *u8) { return 1 }
113 if tag == nxa_tag4("ANIM" as *u8) { return 1 }
114 if tag == nxa_tag4("POSE" as *u8) { return 1 }
115 if tag == nxa_tag4("MATL" as *u8) { return 1 }
116 if tag == nxa_tag4("TEXM" as *u8) { return 1 }
117 if tag == nxa_tag4("HSTR" as *u8) { return 1 }
118 return 0
119}
120
121// ---- the spatial hash: open addressing over (cx,cy,cz), table size a power of two >= 2*nv ----
122func tr_pow2_at_least(n: i64) -> i64 { var p: i64 = 1; while p < n { p = p * 2 } return p }
123func tr_hslot(hx: *i64, hy: *i64, hz: *i64, hu: *i64, mask: i64, cx: i64, cy: i64, cz: i64, hr: *i64, region: i64) -> i64 {
124 var h: i64 = cx * TR_HASH_A + cy * TR_HASH_B + cz * TR_HASH_C
125 if h < 0 { h = 0 - h }
126 var s: i64 = h & mask
127 while hu[s] == 1 {
128 if hx[s] == cx { if hy[s] == cy { if hz[s] == cz { if hr[s] == region { return s } } } }
129 s = (s + 1) & mask
130 }
131 return s
132}
133// occupied-cell count for one cell size; the table is cleared here so the caller never forgets
134func tr_occupied(vw: *i64, nv: i64, minx: i64, miny: i64, minz: i64, cell: i64, hx: *i64, hy: *i64, hz: *i64, hu: *i64, tsize: i64, hr: *i64, regions: *i64) -> i64 {
135 var i: i64 = 0
136 while i < tsize { hu[i] = 0; i = i + 1 }
137 let mask: i64 = tsize - 1
138 var occ: i64 = 0
139 var v: i64 = 0
140 while v < nv {
141 let cx: i64 = (vw[v*3] - minx) / cell
142 let cy: i64 = (vw[v*3 + 1] - miny) / cell
143 let cz: i64 = (vw[v*3 + 2] - minz) / cell
144 let s: i64 = tr_hslot(hx, hy, hz, hu, mask, cx, cy, cz, hr, regions[v])
145 if hu[s] == 0 { hu[s] = 1; hx[s] = cx; hy[s] = cy; hz[s] = cz; hr[s] = regions[v]; occ = occ + 1 }
146 v = v + 1
147 }
148 return occ
149}
150
151func tr_section_words(b:*u8,tag:i64)->i64 {
152 let ns:i64=tr_rd64(b,16);var i:i64=0
153 while i<ns {let e:i64=TR_HDR+i*TR_TOCE;if tr_rd64(b,e)==tag{return tr_rd64(b,e+16)};i=i+1}
154 return 0-1
155}
156
157// ---- ONE tier: read, cluster, remap, write. Returns the exit code; prints its own receipt. ----
158func tr_tier(inp: *u8, outp: *u8, target: i64) -> i64 {
159 if target < TR_MIN_VERTS { tr_err("NXA-TIER-REFUSE budget under 3 vertices\n" as *u8); return TR_EXIT_REFUSE }
160 let lp: *i64 = sys_mmap(64) as *i64
161 let b: *u8 = sys_read_file(inp, lp)
162 if (b as i64) == 0 { tr_err("NXA-TIER-REFUSE cannot read input\n" as *u8); return TR_EXIT_REFUSE }
163 let flen: i64 = lp[0]
164 let w: *i64 = b as *i64
165 let vwo: i64 = nxa_find(b, flen, nxa_tag4("VERT" as *u8))
166 if vwo == 0 - 2 { tr_err("NXA-TIER-REFUSE future NXA version\n" as *u8); return TR_EXIT_REFUSE }
167 if vwo == 0 - 3 { tr_err("NXA-TIER-REFUSE corrupt NXA (TOC or VERT checksum failed)\n" as *u8); return TR_EXIT_REFUSE }
168 if vwo < 0 { tr_err("NXA-TIER-REFUSE no VERT section\n" as *u8); return TR_EXIT_NOSEC }
169 let two: i64 = nxa_find(b, flen, nxa_tag4("TRIS" as *u8))
170 if two == 0 - 3 { tr_err("NXA-TIER-REFUSE corrupt NXA (TRIS checksum failed)\n" as *u8); return TR_EXIT_REFUSE }
171 if two < 0 { tr_err("NXA-TIER-REFUSE no TRIS section\n" as *u8); return TR_EXIT_NOSEC }
172 let kwo: i64 = nxa_find(b, flen, nxa_tag4("SKIN" as *u8))
173 if kwo == 0 - 3 { tr_err("NXA-TIER-REFUSE corrupt NXA (SKIN checksum failed)\n" as *u8); return TR_EXIT_REFUSE }
174 let xwo: i64 = nxa_find(b, flen, nxa_tag4("TEXC" as *u8))
175 if xwo == 0 - 3 { tr_err("NXA-TIER-REFUSE corrupt NXA (TEXC checksum failed)\n" as *u8); return TR_EXIT_REFUSE }
176 let nv: i64 = w[vwo]
177 let nt: i64 = w[two]
178 if nv < TR_MIN_VERTS { tr_err("NXA-TIER-REFUSE source has fewer than 3 vertices\n" as *u8); return TR_EXIT_REFUSE }
179 if nt < 1 { tr_err("NXA-TIER-REFUSE source has no triangles\n" as *u8); return TR_EXIT_REFUSE }
180 if target >= nv { tr_err("NXA-TIER-REFUSE budget at or above the source vertex count -- a tier is never an upsample\n" as *u8); return TR_EXIT_REFUSE }
181 if kwo >= 0 { if w[kwo] != nv { tr_err("NXA-TIER-REFUSE SKIN vertex count does not match VERT\n" as *u8); return TR_EXIT_REFUSE } }
182 var xstride: i64 = 0
183 if xwo >= 0 {
184 if w[xwo] != nv { tr_err("NXA-TIER-REFUSE TEXC vertex count does not match VERT\n" as *u8); return TR_EXIT_REFUSE }
185 xstride = w[xwo + 1]
186 if xstride < 1 { tr_err("NXA-TIER-REFUSE TEXC stride invalid\n" as *u8); return TR_EXIT_REFUSE }
187 }
188 let vwords:i64=tr_section_words(b,nxa_tag4("VERT" as *u8))
189 let twords:i64=tr_section_words(b,nxa_tag4("TRIS" as *u8))
190 if nv>(vwords-1)/3||nt>(twords-1)/3 {tr_err("NXA-TIER-REFUSE geometry count exceeds section extent\n" as *u8);return TR_EXIT_REFUSE}
191 if kwo>=0 {if nv>(tr_section_words(b,nxa_tag4("SKIN" as *u8))-1)/TR_SKIN_W {tr_err("NXA-TIER-REFUSE SKIN rows exceed section extent\n" as *u8);return TR_EXIT_REFUSE}}
192 if xwo>=0 {if nv>(tr_section_words(b,nxa_tag4("TEXC" as *u8))-TR_TEXC_HDR)/xstride {tr_err("NXA-TIER-REFUSE TEXC rows exceed section extent\n" as *u8);return TR_EXIT_REFUSE}}
193 var checked_tri:i64=0
194 while checked_tri<nt*3 {let index:i64=w[two+1+checked_tri];if index<0||index>=nv {tr_err("NXA-TIER-REFUSE triangle index outside VERT\n" as *u8);return TR_EXIT_REFUSE};checked_tri=checked_tri+1}
195 let vw: *i64 = ((w as i64) + (vwo + 1) * 8) as *i64
196 let tw: *i64 = ((w as i64) + (two + 1) * 8) as *i64
197 var minx: i64 = vw[0]
198 var maxx: i64 = vw[0]
199 var miny: i64 = vw[1]
200 var maxy: i64 = vw[1]
201 var minz: i64 = vw[2]
202 var maxz: i64 = vw[2]
203 var v: i64 = 1
204 while v < nv {
205 let x: i64 = vw[v*3]
206 let y: i64 = vw[v*3 + 1]
207 let z: i64 = vw[v*3 + 2]
208 if x < minx { minx = x }
209 if x > maxx { maxx = x }
210 if y < miny { miny = y }
211 if y > maxy { maxy = y }
212 if z < minz { minz = z }
213 if z > maxz { maxz = z }
214 v = v + 1
215 }
216 var ext: i64 = maxx - minx
217 if maxy - miny > ext { ext = maxy - miny }
218 if maxz - minz > ext { ext = maxz - minz }
219 if ext < 1 { tr_err("NXA-TIER-REFUSE degenerate bbox (every vertex at one point)\n" as *u8); return TR_EXIT_REFUSE }
220 let tsize: i64 = tr_pow2_at_least(nv * 2)
221 let hx: *i64 = sys_mmap(tsize * 8) as *i64
222 let hy: *i64 = sys_mmap(tsize * 8) as *i64
223 let hz: *i64 = sys_mmap(tsize * 8) as *i64
224 let hu: *i64 = sys_mmap(tsize * 8) as *i64
225 let hrep: *i64 = sys_mmap(tsize * 8) as *i64
226 let hr: *i64 = sys_mmap(tsize * 8) as *i64
227 let regions: *i64 = sys_mmap(nv * 8) as *i64
228 if (hx as i64)<=0||(hy as i64)<=0||(hz as i64)<=0||(hu as i64)<=0||(hrep as i64)<=0||(hr as i64)<=0||(regions as i64)<=0 { tr_err("NXA-TIER-REFUSE cannot allocate region clustering\n" as *u8); return TR_EXIT_REFUSE }
229 // TEXC word 2 in each stride-3 row is an opaque ownership tag.
230 // Near-coincident surfaces must not borrow another region's UV/skin representative.
231 v = 0
232 while v < nv {
233 regions[v] = 0
234 if xwo >= 0 { if xstride == 3 { regions[v] = w[xwo + TR_TEXC_HDR + v*xstride + 2] } }
235 v = v + 1
236 }
237 let coarse: i64 = tr_occupied(vw,nv,minx,miny,minz,ext+1,hx,hy,hz,hu,tsize,hr,regions)
238 if coarse > target { tr_err("NXA-TIER-REFUSE budget incompatible with retained TEXC region ownership\n" as *u8); return TR_EXIT_REFUSE }
239 // Search a budget-fitting grid; aligned occupancy is not globally monotone,
240 // so no finest-grid claim is made. The final count is measured before output.
241 var lo: i64 = 1
242 var hi: i64 = ext + 1
243 var probes: i64 = 0
244 while lo < hi {
245 let mid: i64 = (lo + hi) / 2
246 let occ: i64 = tr_occupied(vw, nv, minx, miny, minz, mid, hx, hy, hz, hu, tsize, hr, regions)
247 probes = probes + 1
248 if occ <= target { hi = mid } else { lo = mid + 1 }
249 }
250 let cell: i64 = lo
251 let measured: i64 = tr_occupied(vw,nv,minx,miny,minz,cell,hx,hy,hz,hu,tsize,hr,regions)
252 if measured > target { tr_err("NXA-TIER-REFUSE measured region clusters exceed requested budget\n" as *u8); return TR_EXIT_REFUSE }
253 var i: i64 = 0
254 while i < tsize { hu[i] = 0; i = i + 1 }
255 let mask: i64 = tsize - 1
256 let cap: i64 = target + 1
257 let sx: *i64 = sys_mmap(cap * 8) as *i64
258 let sy: *i64 = sys_mmap(cap * 8) as *i64
259 let sz: *i64 = sys_mmap(cap * 8) as *i64
260 let cnt: *i64 = sys_mmap(cap * 8) as *i64
261 let vrep: *i64 = sys_mmap(nv * 8) as *i64
262 var nout: i64 = 0
263 v = 0
264 while v < nv {
265 let cx: i64 = (vw[v*3] - minx) / cell
266 let cy: i64 = (vw[v*3 + 1] - miny) / cell
267 let cz: i64 = (vw[v*3 + 2] - minz) / cell
268 let s: i64 = tr_hslot(hx, hy, hz, hu, mask, cx, cy, cz, hr, regions[v])
269 if hu[s] == 0 {
270 if nout >= cap { tr_err("NXA-TIER-REFUSE occupied cells exceeded the searched budget (internal)\n" as *u8); return TR_EXIT_REFUSE }
271 hu[s] = 1
272 hx[s] = cx
273 hy[s] = cy
274 hz[s] = cz
275 hr[s] = regions[v]
276 hrep[s] = nout
277 nout = nout + 1
278 }
279 let r: i64 = hrep[s]
280 vrep[v] = r
281 sx[r] = sx[r] + vw[v*3]
282 sy[r] = sy[r] + vw[v*3 + 1]
283 sz[r] = sz[r] + vw[v*3 + 2]
284 cnt[r] = cnt[r] + 1
285 v = v + 1
286 }
287 let cxv: *i64 = sys_mmap(cap * 8) as *i64
288 let cyv: *i64 = sys_mmap(cap * 8) as *i64
289 let czv: *i64 = sys_mmap(cap * 8) as *i64
290 let bestd: *i64 = sys_mmap(cap * 8) as *i64
291 let bestv: *i64 = sys_mmap(cap * 8) as *i64
292 var r2: i64 = 0
293 while r2 < nout {
294 cxv[r2] = sx[r2] / cnt[r2]
295 cyv[r2] = sy[r2] / cnt[r2]
296 czv[r2] = sz[r2] / cnt[r2]
297 bestd[r2] = 0 - 1
298 bestv[r2] = 0 - 1
299 r2 = r2 + 1
300 }
301 v = 0
302 while v < nv {
303 let r: i64 = vrep[v]
304 let dx: i64 = vw[v*3] - cxv[r]
305 let dy: i64 = vw[v*3 + 1] - cyv[r]
306 let dz: i64 = vw[v*3 + 2] - czv[r]
307 let d2: i64 = dx*dx + dy*dy + dz*dz
308 if bestd[r] < 0 { bestd[r] = d2; bestv[r] = v }
309 if d2 < bestd[r] { bestd[r] = d2; bestv[r] = v }
310 v = v + 1
311 }
312 var tkeep: i64 = 0
313 var t: i64 = 0
314 while t < nt {
315 let a: i64 = vrep[tw[t*3]]
316 let bb: i64 = vrep[tw[t*3 + 1]]
317 let c: i64 = vrep[tw[t*3 + 2]]
318 if a != bb { if bb != c { if a != c { tkeep = tkeep + 1 } } }
319 t = t + 1
320 }
321 if tkeep < 1 { tr_err("NXA-TIER-REFUSE every triangle collapsed at this budget\n" as *u8); return TR_EXIT_REFUSE }
322 // ---- output layout: walk the input TOC in order; remap VERT TRIS SKIN TEXC, copy the copyable, drop the rest ----
323 let ns: i64 = w[2]
324 var nsec: i64 = 0
325 var dropped: i64 = 0
326 var copied: i64 = 0
327 var remapped: i64 = 0
328 var total: i64 = 0
329 var s0: i64 = 0
330 while s0 < ns {
331 let e: i64 = TR_HDR + s0 * TR_TOCE
332 let tag: i64 = tr_rd64(b, e)
333 let wl: i64 = tr_rd64(b, e + 16)
334 var handled: i64 = 0
335 if tag == nxa_tag4("VERT" as *u8) { handled = 1; nsec = nsec + 1; remapped = remapped + 1; total = total + (1 + nout*3) * 8 }
336 if tag == nxa_tag4("TRIS" as *u8) { handled = 1; nsec = nsec + 1; remapped = remapped + 1; total = total + (1 + tkeep*3) * 8 }
337 if tag == nxa_tag4("SKIN" as *u8) { handled = 1; nsec = nsec + 1; remapped = remapped + 1; total = total + (1 + nout*TR_SKIN_W) * 8 }
338 if tag == nxa_tag4("TEXC" as *u8) { handled = 1; nsec = nsec + 1; remapped = remapped + 1; total = total + (TR_TEXC_HDR + nout*xstride) * 8 }
339 if handled == 0 { if tr_copyable(tag) == 1 { handled = 1; nsec = nsec + 1; copied = copied + 1; total = total + wl * 8 } }
340 if handled == 0 {
341 dropped = dropped + 1
342 tr_out("NXA-TIER-DROPPED tag=" as *u8)
343 tr_tagout(tag)
344 tr_out(" (a per-vertex or per-triangle section this rung does not carry; a later rung remaps it)\n" as *u8)
345 }
346 s0 = s0 + 1
347 }
348 if nsec < 1 { tr_err("NXA-TIER-REFUSE nothing to emit\n" as *u8); return TR_EXIT_REFUSE }
349 let toclen: i64 = TR_HDR + nsec * TR_TOCE
350 total = total + toclen
351 let nb: *u8 = sys_mmap(total + TR_PAD)
352 if (nb as i64) == 0 { tr_err("NXA-TIER-REFUSE cannot allocate output\n" as *u8); return TR_EXIT_REFUSE }
353 tr_wr64(nb, 0, nxa_magic())
354 tr_wr64(nb, 8, NXA_VER)
355 tr_wr64(nb, 16, nsec)
356 var wo: i64 = toclen
357 var ti: i64 = 0
358 var s1: i64 = 0
359 while s1 < ns {
360 let e: i64 = TR_HDR + s1 * TR_TOCE
361 let tag: i64 = tr_rd64(b, e)
362 let oldoff: i64 = tr_rd64(b, e + 8)
363 let wl: i64 = tr_rd64(b, e + 16)
364 var emit: i64 = 0
365 var nw: i64 = 0
366 let te: i64 = TR_HDR + ti * TR_TOCE
367 if tag == nxa_tag4("VERT" as *u8) {
368 emit = 1
369 nw = 1 + nout * 3
370 tr_wr64(nb, wo, nout)
371 var r: i64 = 0
372 while r < nout {
373 tr_wr64(nb, wo + (1 + r*3)*8, cxv[r])
374 tr_wr64(nb, wo + (2 + r*3)*8, cyv[r])
375 tr_wr64(nb, wo + (3 + r*3)*8, czv[r])
376 r = r + 1
377 }
378 }
379 if tag == nxa_tag4("TRIS" as *u8) {
380 emit = 1
381 nw = 1 + tkeep * 3
382 tr_wr64(nb, wo, tkeep)
383 var k: i64 = 0
384 var t2: i64 = 0
385 while t2 < nt {
386 let a: i64 = vrep[tw[t2*3]]
387 let bb: i64 = vrep[tw[t2*3 + 1]]
388 let c: i64 = vrep[tw[t2*3 + 2]]
389 if a != bb { if bb != c { if a != c {
390 tr_wr64(nb, wo + (1 + k*3)*8, a)
391 tr_wr64(nb, wo + (2 + k*3)*8, bb)
392 tr_wr64(nb, wo + (3 + k*3)*8, c)
393 k = k + 1
394 } } }
395 t2 = t2 + 1
396 }
397 }
398 if tag == nxa_tag4("SKIN" as *u8) {
399 emit = 1
400 nw = 1 + nout * TR_SKIN_W
401 tr_wr64(nb, wo, nout)
402 var r: i64 = 0
403 while r < nout {
404 let src: i64 = bestv[r]
405 var f: i64 = 0
406 while f < TR_SKIN_W { tr_wr64(nb, wo + (1 + r*TR_SKIN_W + f)*8, w[kwo + 1 + src*TR_SKIN_W + f]); f = f + 1 }
407 r = r + 1
408 }
409 }
410 if tag == nxa_tag4("TEXC" as *u8) {
411 emit = 1
412 nw = TR_TEXC_HDR + nout * xstride
413 tr_wr64(nb, wo, nout)
414 tr_wr64(nb, wo + 8, xstride)
415 tr_wr64(nb, wo + 16, w[xwo + 2])
416 tr_wr64(nb, wo + 24, w[xwo + 3])
417 var r: i64 = 0
418 while r < nout {
419 let src: i64 = bestv[r]
420 var f: i64 = 0
421 while f < xstride { tr_wr64(nb, wo + (TR_TEXC_HDR + r*xstride + f)*8, w[xwo + TR_TEXC_HDR + src*xstride + f]); f = f + 1 }
422 r = r + 1
423 }
424 }
425 if emit == 0 { if tr_copyable(tag) == 1 {
426 emit = 1
427 nw = wl
428 var k2: i64 = 0
429 while k2 < wl * 8 { nb[wo + k2] = b[oldoff + k2]; k2 = k2 + 1 }
430 } }
431 if emit == 1 {
432 tr_wr64(nb, te, tag)
433 tr_wr64(nb, te + 8, wo)
434 tr_wr64(nb, te + 16, nw)
435 let pw: *i64 = ((nb as i64) + wo) as *i64
436 tr_wr64(nb, te + 24, nxa_check2(1, pw, nw))
437 wo = wo + nw * 8
438 ti = ti + 1
439 }
440 s1 = s1 + 1
441 }
442 let tb: *i64 = ((nb as i64) + TR_HDR) as *i64
443 tr_wr64(nb, 24, nxa_check2(1, tb, nsec * TR_TOC_W))
444 let fd: i64 = sys_openat_wr(outp, TR_MODE)
445 if fd < 0 { tr_err("NXA-TIER-REFUSE cannot open output\n" as *u8); return TR_EXIT_REFUSE }
446 sys_write(fd, nb, wo)
447 sys_close(fd)
448 tr_out("NXA-TIER-OK in_verts=" as *u8); tr_num(nv)
449 tr_out(" out_verts=" as *u8); tr_num(nout)
450 tr_out(" in_tris=" as *u8); tr_num(nt)
451 tr_out(" out_tris=" as *u8); tr_num(tkeep)
452 tr_out(" collapsed_tris=" as *u8); tr_num(nt - tkeep)
453 tr_out(" region_ownership=" as *u8); if xstride == 3 { tr_num(1) } else { tr_num(0) }
454 tr_out(" region_floor=" as *u8); tr_num(coarse)
455 tr_out(" budget=" as *u8); tr_num(target)
456 tr_out(" cell_umm=" as *u8); tr_num(cell)
457 tr_out(" search_probes=" as *u8); tr_num(probes)
458 tr_out(" sections_in=" as *u8); tr_num(ns)
459 tr_out(" remapped=" as *u8); tr_num(remapped)
460 tr_out(" copied=" as *u8); tr_num(copied)
461 tr_out(" dropped=" as *u8); tr_num(dropped)
462 tr_out(" sum=" as *u8); tr_num(remapped + copied + dropped)
463 tr_out(" bytes=" as *u8); tr_num(wo)
464 tr_out("\n" as *u8)
465 return 0
466}
467
468// ---- the ladder verb: one output per conf tier under the source, the partition printed ----
469func tr_ladder(inp: *u8, prefix: *u8, conf: *u8) -> i64 {
470 let lp: *i64 = sys_mmap(64) as *i64
471 let cb: *u8 = sys_read_file(conf, lp)
472 if (cb as i64) == 0 { tr_err("NXA-TIER-REFUSE cannot read the ladder conf\n" as *u8); return TR_EXIT_REFUSE }
473 let cn: i64 = lp[0]
474 let lp2: *i64 = sys_mmap(64) as *i64
475 let b: *u8 = sys_read_file(inp, lp2)
476 if (b as i64) == 0 { tr_err("NXA-TIER-REFUSE cannot read input\n" as *u8); return TR_EXIT_REFUSE }
477 let flen: i64 = lp2[0]
478 let vwo: i64 = nxa_find(b, flen, nxa_tag4("VERT" as *u8))
479 if vwo < 0 { tr_err("NXA-TIER-REFUSE no valid VERT section\n" as *u8); return TR_EXIT_NOSEC }
480 let w: *i64 = b as *i64
481 let nv: i64 = w[vwo]
482 var emitted: i64 = 0
483 var skipped: i64 = 0
484 var failed: i64 = 0
485 var rows: i64 = 0
486 let idb: *u8 = sys_mmap(TR_PATH_CAP)
487 let outb: *u8 = sys_mmap(TR_PATH_CAP)
488 var p: i64 = 0
489 while p < cn {
490 var le: i64 = p
491 while le < cn { if cb[le] == (TR_ASCII_NL as u8) { break } le = le + 1 }
492 if le > p { if cb[p] != (TR_ASCII_HASH as u8) {
493 var f0: i64 = p
494 var fi: i64 = 0
495 var idlen: i64 = 0
496 var maxv: i64 = 0
497 var istier: i64 = 0
498 var q: i64 = p
499 while q <= le {
500 var atend: i64 = 0
501 if q == le { atend = 1 } else { if cb[q] == (TR_ASCII_PIPE as u8) { atend = 1 } }
502 if atend == 1 {
503 if fi == 0 { if q - f0 == TR_TAG_BYTES { if cb[f0] == (TR_ASCII_T as u8) { if cb[f0+1] == (TR_ASCII_I as u8) { if cb[f0+2] == (TR_ASCII_E as u8) { if cb[f0+3] == (TR_ASCII_R as u8) { istier = 1 } } } } } }
504 if fi == 1 {
505 idlen = q - f0
506 var c2: i64 = 0
507 while c2 < idlen { if c2 < TR_PATH_CAP - 1 { idb[c2] = cb[f0 + c2] } c2 = c2 + 1 }
508 if idlen > TR_PATH_CAP - 1 { idlen = TR_PATH_CAP - 1 }
509 idb[idlen] = 0 as u8
510 }
511 if fi == 2 {
512 var vv: i64 = 0
513 var c3: i64 = f0
514 while c3 < q {
515 let ch: i64 = cb[c3] as i64
516 if ch >= TR_ASCII_ZERO { if ch <= TR_ASCII_NINE { vv = vv * 10 + (ch - TR_ASCII_ZERO) } }
517 c3 = c3 + 1
518 }
519 maxv = vv
520 }
521 fi = fi + 1
522 f0 = q + 1
523 }
524 q = q + 1
525 }
526 if istier == 1 { if fi >= TR_LADDER_F {
527 rows = rows + 1
528 if maxv >= nv {
529 skipped = skipped + 1
530 tr_out("NXA-TIER-SKIP tier=" as *u8); tr_out(idb); tr_out(" max_verts=" as *u8); tr_num(maxv); tr_out(" is not under the source " as *u8); tr_num(nv); tr_out(" (a tier is never an upsample)\n" as *u8)
531 } else {
532 var o: i64 = 0
533 var c4: i64 = 0
534 while prefix[c4] != (0 as u8) { if o < TR_PATH_CAP - 8 { outb[o] = prefix[c4]; o = o + 1 } c4 = c4 + 1 }
535 outb[o] = TR_ASCII_US as u8
536 o = o + 1
537 c4 = 0
538 while idb[c4] != (0 as u8) { if o < TR_PATH_CAP - 8 { outb[o] = idb[c4]; o = o + 1 } c4 = c4 + 1 }
539 outb[o] = TR_ASCII_DOT as u8
540 outb[o+1] = TR_ASCII_N as u8
541 outb[o+2] = TR_ASCII_X as u8
542 outb[o+3] = TR_ASCII_A as u8
543 outb[o+4] = 0 as u8
544 tr_out("NXA-TIER-LADDER tier=" as *u8); tr_out(idb); tr_out(" out=" as *u8); tr_out(outb); tr_out("\n" as *u8)
545 let rc: i64 = tr_tier(inp, outb, maxv)
546 if rc == 0 { emitted = emitted + 1 } else { failed = failed + 1 }
547 }
548 } }
549 } }
550 p = le + 1
551 }
552 tr_out("NXA-TIER-LADDER-DONE tiers=" as *u8); tr_num(rows)
553 tr_out(" emitted=" as *u8); tr_num(emitted)
554 tr_out(" skipped=" as *u8); tr_num(skipped)
555 tr_out(" failed=" as *u8); tr_num(failed)
556 tr_out(" sum=" as *u8); tr_num(emitted + skipped + failed)
557 tr_out(" source_verts=" as *u8); tr_num(nv)
558 tr_out("\n" as *u8)
559 if rows < 1 { tr_err("NXA-TIER-REFUSE the ladder conf declares no tier rows\n" as *u8); return TR_EXIT_REFUSE }
560 if failed > 0 { return TR_EXIT_REFUSE }
561 return 0
562}
563
564func tr_is_ladder(a: *u8) -> i64 {
565 if a[0] != (TR_ASCII_L as u8) { return 0 }
566 if a[1] != (TR_ASCII_A as u8) { return 0 }
567 if a[2] != (TR_ASCII_D as u8) { return 0 }
568 if a[3] != (TR_ASCII_D as u8) { return 0 }
569 if a[4] != (TR_ASCII_E as u8) { return 0 }
570 if a[5] != (TR_ASCII_R as u8) { return 0 }
571 if a[6] != (0 as u8) { return 0 }
572 return 1
573}
574
575func main(argc: i64, argv: *i64) -> i64 {
576 if argc < 4 {
577 tr_err("usage: nx_nxa_tier <in.nxa> <out.nxa> <max_verts> | nx_nxa_tier ladder <in.nxa> <out_prefix> [ladder.conf]\n" as *u8)
578 return TR_EXIT_USAGE
579 }
580 let a1: *u8 = argv[1] as *u8
581 if tr_is_ladder(a1) == 1 {
582 var conf: *u8 = TR_LADDER_DEFAULT
583 if argc >= 5 { conf = argv[4] as *u8 }
584 return tr_ladder(argv[2] as *u8, argv[3] as *u8, conf)
585 }
586 let target: i64 = tr_atoi(argv[3] as *u8)
587 return tr_tier(a1, argv[2] as *u8, target)
588}