nx_garment_boundary_candidate_t128.nx source
↩ module page · 1555 lines · 71681 B
1// nx_garment_gen_lib.nx -- CONFORMING GARMENT MESHES GENERATED FROM THE BODY SURFACE (2026-08-30,
2// gameengine gpe_garment_draw paint->mesh; operator: "the clothes are paint not objects").
3//
4// WHAT IT BUILDS. For every garment id of the engine's table (1 bikini top . 2 bikini bottom . 3 one-piece
5// . 4 sarong knot . 5 sundress bodice . 6 tee . 7 shorts . 8 skirt waistband . 9 evening bodice) a SHELL:
6// the body triangles the garment covers, offset along the body's own smooth vertex normals by fabric
7// thickness + wearing ease, its open boundary CLOSED by a hem strip (the fabric's edge face, so the cloth
8// has a visible thickness), a raised binding band one ring inside every opening (the elastic / folded
9// hem), a PUSH-OUT pass that walks any vertex the offset dropped into a fold back out until no body vertex
10// sits inside the fabric, and RIBBON STRAPS between anatomical anchors for the strapped ids. Every garment
11// vertex records the BODY VERTEX it was grown from, so the page skins it with that vertex's own joint
12// weights and it rides pose, gait and the soft-tissue fields exactly as the skin under it does
13// (layering by construction: a layer that rides its underlayer's field cannot be pierced by it).
14//
15// UNITS. Mesh units: 171530 per 1.78 m (the page's own pairing) => 96.37 units per mm. Heights are in
16// DECIMIL of the mesh's own measured stature ((z - zmin) * 10000 / H). Normals and blend weights are Q12.
17// Thickness and ease are DERIVED from micrometres in gmg_mat, with the source of each number beside it.
18//
19// SECTION EMITTED: "GARM" (one tag, one checksum) =
20// header [ver=20260830, nkinds, vstride=5, tstride=3, units_per_mm_x1000, dirstride=8, total_words, 0]
21// dir nkinds x [id, nverts, ntris, voff(words from section start), toff, thickness_units, ease_units, class]
22// verts [x, y, z, src_body_vertex, flag] flag 0 shell . 1 hem . 2 strap
23// tris [a, b, c] local to the kind
24// Normals are NOT shipped: the page already accumulates area-weighted smooth normals for the skirt mesh
25// and does the same here (hem strips and straps carry their own copies of the shared vertices, so their
26// faces get their own normals). license_tier: ORIGINAL. No hw writes (Rule 26).
27import "nx_syscalls.nx"
28import "nx_nxa.nx"
29
30const GMG_Q: i64 = 4096
31const GMG_DM: i64 = 10000
32const GMG_UNITS_178: i64 = 171530
33const GMG_MM_178: i64 = 1780
34const GMG_UM_PER_MM: i64 = 1000
35const GMG_TWO_PI_MILLI: i64 = 6283
36const GMG_KINDS: i64 = 9
37const GMG_VSTRIDE: i64 = 5
38const GMG_TSTRIDE: i64 = 3
39const GMG_DIRSTRIDE: i64 = 8
40const GMG_HDR: i64 = 8
41const GMG_GARM_VER: i64 = 20260830
42const GMG_NBR_CAP: i64 = 32
43const GMG_BADJ_CAP: i64 = 4
44const GMG_OV_CAP: i64 = 16384
45const GMG_OT_CAP: i64 = 32768
46const GMG_SEC_CAP: i64 = 262144
47const GMG_STAT_N: i64 = 32
48const GMG_PUSH_PASSES: i64 = 8
49const GMG_SKEL_STRIDE: i64 = 8
50const GMG_SKIN_STRIDE: i64 = 8
51const GMG_DYN_FIELDS: i64 = 8
52const GMG_STRAP_K: i64 = 9
53const GMG_STRAP_CAP: i64 = 24
54const GMG_BIG: i64 = 4611686018427387903
55const GMG_ISQRT_HI: i64 = 3037000499
56const GMG_CROSS_SHIFT: i64 = 64
57const GMG_NRM_CAP: i64 = 1000000
58
59// flags
60const GMG_F_SHELL: i64 = 0
61const GMG_F_HEM: i64 = 1
62const GMG_F_STRAP: i64 = 2
63// material classes (the page's fabric BRDF keys on these)
64const GMG_M_SWIM: i64 = 1
65const GMG_M_WOVEN: i64 = 2
66const GMG_M_JERSEY: i64 = 3
67const GMG_M_SATIN: i64 = 4
68// refusals (negative returns; the program maps them to exit codes and NAMES them)
69const GMG_ERR_BADNXA: i64 = 0 - 5
70const GMG_ERR_REFUSED_THICK: i64 = 0 - 4
71const GMG_ERR_EMPTY: i64 = 0 - 6
72const GMG_ERR_CAP: i64 = 0 - 7
73
74// ---- anatomy (decimil of stature unless noted) -- every number names its source ----
75const GMG_TORSO_HALFW: i64 = 15000 // the page shader's own torso gate (|bind x| < 15000)
76const GMG_ARM_BIND_X: i64 = 12000 // a dominant joint this far off the midline is an arm joint
77const GMG_HEAD_DM: i64 = 8600 // above the chin line (.885 H) a dominant joint is a head joint
78const GMG_FRONT_Y: i64 = 0 - 1200 // the page's bust-side convention: front is y < -1200
79const GMG_YBLEND: i64 = 1200 // front/back blend half-width (units) for the leg/neck lines
80const GMG_HIP_DM: i64 = 5450 // hip band top (.545 H), the incumbent paint band's own edge
81const GMG_CROTCH_DM: i64 = 4750 // crotch .475 H (Drillis-Contini 1966)
82const GMG_KNEE_DM: i64 = 2850 // knee .285 H (Drillis-Contini 1966, the page's own gdJn(.285))
83const GMG_HIPJ_DM: i64 = 5200 // hip joint .52 H (Drillis-Contini) -- the anchor the page maps NIP through
84const GMG_EYE_DM: i64 = 9300 // eye line .93 H, the second anchor
85const GMG_NIP_RATIO_DM: i64 = 7200 // published nipple ratio .72 H before the per-mesh anchor mapping
86const GMG_HEADJ_PROBE_DM: i64 = 9500
87const GMG_BUST_HALF_DM: i64 = 550 // the nipple detail's own half-width (.055 H)
88const GMG_EASE_FADE_LO: i64 = 7400 // ease fades to zero over the bust->shoulder band so necklines hug
89const GMG_EASE_FADE_HI: i64 = 8000
90const GMG_MIDTHIGH_DM: i64 = 4025 // (hip .52 + knee .285) / 2 -- Drillis-Contini mid-thigh, the shorts hem
91const GMG_SHORTS_TOP_DM: i64 = 5600
92const GMG_TEE_NECK_DM: i64 = 8350 // crew neck front: .835 H (acromion .818 + a finger)
93const GMG_TEE_NECK_RISE: i64 = 300
94const GMG_SLEEVE_TOP_DM: i64 = 7400
95const GMG_SLEEVE_HALFW: i64 = 19000 // short-sleeve cap reach off the midline (units)
96const GMG_SLEEVE_BAND_LO: i64 = 7800
97const GMG_SLEEVE_BAND_HI: i64 = 8700
98const GMG_SLEEVE_X_LO: i64 = 12000
99const GMG_ONEPIECE_NECK_F: i64 = 7700 // one-piece scoop front .77 H
100const GMG_ONEPIECE_NECK_B: i64 = 7000 // one-piece back .70 H
101const GMG_STRAP_SHOULDER_DM: i64 = 8580
102const GMG_STRAP_BAND_TOL_DM: i64 = 120 // bikini back band half-height
103const GMG_SARONG_X: i64 = 13500 // the knot sits on the hip side
104const GMG_SARONG_DM: i64 = 6150
105const GMG_SARONG_R: i64 = 5000
106const GMG_WAIST_BAND_DM: i64 = 150 // skirt waistband half-height around the skirt's own top ring
107const GMG_SKIRT_TOP_TOL_DM: i64 = 300
108const GMG_STRAP_SEARCH_DM: i64 = 700
109const GMG_STRAP_AX_MAX: i64 = 14500
110const GMG_STRAP_AX_FRONT: i64 = 9000
111const GMG_STRAP_Y_FRONT: i64 = 0 - 9000
112const GMG_STRAP_AX_SHOULDER: i64 = 11400
113const GMG_STRAP_Y_SHOULDER: i64 = 2000
114const GMG_STRAP_AX_BACK: i64 = 8000
115const GMG_STRAP_Y_BACK: i64 = 9000
116const GMG_STRAP_HALFW_BIKINI_UM: i64 = 6000 // 12 mm bikini strap
117const GMG_STRAP_HALFW_DRESS_UM: i64 = 10000 // 20 mm dress / one-piece strap
118const GMG_SWIM_INNER_UM: i64 = 200 // a swim hem's inner edge is pressed to the skin (0.2 mm)
119const GMG_PUSH_MARGIN: i64 = 2
120const GMG_CLEAR_PERMIL: i64 = 900 // the clearance tooth's bar: 0.9 t (vertex-to-vertex is an
121 // UPPER bound on vertex-to-surface -- declared imprecision)
122const GMG_CLEAR_REPORT_MULT: i64 = 4
123
124// ---- fabric table sources (all DECLARED until a bench swatch measures the real blanks; garment_twin.conf
125// carries the same honesty). Thickness: ASTM D1777 handbook classes -- nylon/elastane swim tricot ~0.55 mm,
126// cotton single jersey (160-180 g/m2) ~0.7 mm, cotton poplin ~0.35 mm, polyester charmeuse ~0.25 mm.
127// Ease: Aldrich, Metric Pattern Cutting for Women's Wear; Armstrong, Patternmaking for Fashion Design --
128// a basic tee carries ~7.5 cm of bust ease, a trouser/short block ~7.6 cm (3 in) of hip ease, an evening
129// (fitted) block ~6.4 cm (2.5 in); two-way-stretch swimwear is cut with NEGATIVE ease and sits at skin +
130// fabric, so its radial ease is zero. Hem: a swim opening carries a ~1.0 mm elastic under two plies; a
131// woven/jersey hem is a two-ply fold. Circumferential ease becomes radial ease by / 2 pi.
132const GMG_T_SWIM_UM: i64 = 550
133const GMG_T_JERSEY_UM: i64 = 700
134const GMG_T_WOVEN_UM: i64 = 350
135const GMG_T_SARONG_UM: i64 = 300
136const GMG_T_SATIN_UM: i64 = 250
137const GMG_ELASTIC_UM: i64 = 1000
138const GMG_EASE_TEE_UM: i64 = 75000
139const GMG_EASE_SHORTS_UM: i64 = 76200
140const GMG_EASE_DRESS_UM: i64 = 75000
141const GMG_EASE_EVENING_UM: i64 = 63500
142const GMG_EASE_KNOT_RADIAL_UM: i64 = 30000
143
144// ---- context slots (one *i64 block; pointers stored as i64) ----
145const GMG_C_B: i64 = 0
146const GMG_C_FLEN: i64 = 1
147const GMG_C_POS: i64 = 2
148const GMG_C_NV: i64 = 3
149const GMG_C_IDX: i64 = 4
150const GMG_C_NT: i64 = 5
151const GMG_C_NRM: i64 = 6
152const GMG_C_ZMN: i64 = 7
153const GMG_C_H: i64 = 8
154const GMG_C_HH: i64 = 9
155const GMG_C_AX: i64 = 10
156const GMG_C_VF: i64 = 11
157const GMG_C_NIP: i64 = 12
158const GMG_C_DYN: i64 = 13
159const GMG_C_ND: i64 = 14
160const GMG_C_GTH: i64 = 15
161const GMG_C_GAP: i64 = 16
162const GMG_C_YMID: i64 = 17
163const GMG_C_BIND: i64 = 18
164const GMG_C_NJ: i64 = 19
165const GMG_C_VMAP: i64 = 20
166const GMG_C_INNER: i64 = 21
167const GMG_C_SEL: i64 = 22
168const GMG_C_NBR: i64 = 23
169const GMG_C_NBC: i64 = 24
170const GMG_C_DEG: i64 = 25
171const GMG_C_OV: i64 = 26
172const GMG_C_ONV: i64 = 27
173const GMG_C_OT: i64 = 28
174const GMG_C_ONT: i64 = 29
175const GMG_C_CAND: i64 = 30
176const GMG_C_NCAND: i64 = 31
177const GMG_C_TRIS: i64 = 32
178const GMG_C_NTRIS: i64 = 33
179const GMG_C_THICK_PERMIL: i64 = 34
180const GMG_C_UPMK: i64 = 35
181const GMG_C_BSET: i64 = 36
182const GMG_C_RING: i64 = 37
183const GMG_C_BADJ: i64 = 38
184const GMG_C_BDEG: i64 = 39
185const GMG_C_STACK: i64 = 40
186const GMG_C_SEEN: i64 = 41
187const GMG_C_NOPUSH: i64 = 42
188const GMG_C_NBROVER: i64 = 43
189const GMG_C_AJ: i64 = 44
190const GMG_C_AW: i64 = 45
191const GMG_C_DST: i64 = 46 // the asset's own DYNA row stride (ref9d ships 12 words, not the 8 the
192 // first cut demanded -- an exact-stride check read a present section as absent)
193const GMG_C_DIL: i64 = 47 // per-vertex pinch-dilation flags (see gmg_build steps 1-3)
194const GMG_C_SLOTS: i64 = 48
195const GMG_PINCH_PASSES: i64 = 4
196// vertex flag bits (GMG_C_VF)
197const GMG_VF_TOR: i64 = 1
198const GMG_VF_ARM: i64 = 2
199const GMG_VF_HEAD: i64 = 4
200// stat slots
201const GMG_S_NV: i64 = 0
202const GMG_S_NT: i64 = 1
203const GMG_S_SHELL: i64 = 2
204const GMG_S_HEM: i64 = 3
205const GMG_S_BEDGES: i64 = 4
206const GMG_S_PUSHED: i64 = 5
207const GMG_S_STRAPS: i64 = 6
208const GMG_S_MINCLEAR: i64 = 7
209const GMG_S_LOOPS: i64 = 8
210const GMG_S_OPEN: i64 = 9
211const GMG_S_SEL: i64 = 10
212const GMG_S_T: i64 = 11
213const GMG_S_TH: i64 = 12
214const GMG_S_EASE: i64 = 13
215const GMG_S_CLASS: i64 = 14
216const GMG_S_WITHIN: i64 = 15
217const GMG_S_SLF: i64 = 16
218const GMG_S_SLB: i64 = 17
219const GMG_S_SRF: i64 = 18
220const GMG_S_SRB: i64 = 19
221const GMG_S_NBROVER: i64 = 20
222const GMG_S_VOFF: i64 = 21
223const GMG_S_TOFF: i64 = 22
224const GMG_S_ID: i64 = 23
225const GMG_S_PINCHPASS: i64 = 24 // selection passes taken by the pinch dilation (1 = no pinch found)
226
227func gmg_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
228func gmg_pn(v: i64) -> i64 {
229 let t: *u8 = sys_mmap(32) as *u8
230 var m: i64 = v
231 var w: i64 = 0
232 if m < 0 { t[w] = 45 as u8; w = w + 1; m = 0 - m }
233 if m == 0 { t[w] = 48 as u8; sys_write(1, t, w + 1); return 0 }
234 let d: *u8 = sys_mmap(32) as *u8
235 var k: i64 = 0
236 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
237 var j: i64 = 0
238 while j < k { t[w] = d[k - 1 - j]; w = w + 1; j = j + 1 }
239 sys_write(1, t, w)
240 return 0
241}
242func gmg_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
243func gmg_min(a: i64, b: i64) -> i64 { if a < b { return a } return b }
244func gmg_max(a: i64, b: i64) -> i64 { if a > b { return a } return b }
245// floor(sqrt(v)) by Newton from a power-of-two upper bound; v < 0 reads as 0
246func gmg_isqrt(v: i64) -> i64 {
247 if v <= 0 { return 0 }
248 var g: i64 = 1
249 var grow: i64 = 1
250 while grow == 1 {
251 if g >= GMG_ISQRT_HI { grow = 0 } else {
252 if g * g >= v { grow = 0 } else { g = g * 2 }
253 }
254 }
255 var y: i64 = g
256 var it: i64 = 0
257 while it < 80 {
258 let z: i64 = (y + v / y) / 2
259 if z >= y { it = 80 } else { y = z }
260 it = it + 1
261 }
262 return y
263}
264// smoothstep(a, b, x) in Q12: 0 below a, Q above b
265func gmg_sst(a: i64, b: i64, x: i64) -> i64 {
266 if b <= a { if x >= b { return GMG_Q } return 0 }
267 var t: i64 = (x - a) * GMG_Q / (b - a)
268 if t < 0 { t = 0 }
269 if t > GMG_Q { t = GMG_Q }
270 return (t * t / GMG_Q) * (3 * GMG_Q - 2 * t) / GMG_Q
271}
272// micrometres -> mesh units through the page's own 1.78 m = 171530 pairing
273func gmg_um2u(um: i64) -> i64 { return um * GMG_UNITS_178 / (GMG_MM_178 * GMG_UM_PER_MM) }
274func gmg_circ2radial_um(circ_um: i64) -> i64 { return circ_um * 1000 / GMG_TWO_PI_MILLI }
275
276// gmg_mat: the fabric table per garment id. out[0] thickness units . out[1] hem (edge face) units .
277// out[2] radial ease units . out[3] material class. Thickness and hem scale with the ctx thick permil
278// (1000 = derived; 0 = the neg-control that must be REFUSED); ease never does.
279func gmg_mat(G: *i64, kind: i64, out: *i64) -> i64 {
280 var t: i64 = 0
281 var th: i64 = 0
282 var e: i64 = 0
283 var cl: i64 = 0
284 if kind == 1 { t = GMG_T_SWIM_UM; th = 2 * GMG_T_SWIM_UM + GMG_ELASTIC_UM; e = 0; cl = GMG_M_SWIM }
285 if kind == 2 { t = GMG_T_SWIM_UM; th = 2 * GMG_T_SWIM_UM + GMG_ELASTIC_UM; e = 0; cl = GMG_M_SWIM }
286 if kind == 3 { t = GMG_T_SWIM_UM; th = 2 * GMG_T_SWIM_UM + GMG_ELASTIC_UM; e = 0; cl = GMG_M_SWIM }
287 if kind == 4 { t = GMG_T_SARONG_UM; th = 2 * GMG_T_SARONG_UM; e = GMG_EASE_KNOT_RADIAL_UM; cl = GMG_M_WOVEN }
288 if kind == 5 { t = GMG_T_WOVEN_UM; th = 2 * GMG_T_WOVEN_UM; e = gmg_circ2radial_um(GMG_EASE_DRESS_UM); cl = GMG_M_WOVEN }
289 if kind == 6 { t = GMG_T_JERSEY_UM; th = 2 * GMG_T_JERSEY_UM; e = gmg_circ2radial_um(GMG_EASE_TEE_UM); cl = GMG_M_JERSEY }
290 if kind == 7 { t = GMG_T_WOVEN_UM; th = 2 * GMG_T_WOVEN_UM; e = gmg_circ2radial_um(GMG_EASE_SHORTS_UM); cl = GMG_M_WOVEN }
291 if kind == 8 { t = GMG_T_WOVEN_UM; th = 2 * GMG_T_WOVEN_UM; e = 0; cl = GMG_M_WOVEN }
292 if kind == 9 { t = GMG_T_SATIN_UM; th = 2 * GMG_T_SATIN_UM; e = gmg_circ2radial_um(GMG_EASE_EVENING_UM); cl = GMG_M_SATIN }
293 if cl == 0 { return 0 - 1 }
294 let pm: i64 = G[GMG_C_THICK_PERMIL]
295 out[0] = gmg_um2u(t) * pm / 1000
296 out[1] = gmg_um2u(th) * pm / 1000
297 out[2] = gmg_um2u(e)
298 // the waistband's ease is MEASURED: the gap between the skirt's own top ring and the body
299 if kind == 8 { out[2] = G[GMG_C_GAP] }
300 out[3] = cl
301 return 1
302}
303
304func gmg_pos(G: *i64) -> *i64 { return G[GMG_C_POS] as *i64 }
305func gmg_nrm(G: *i64) -> *i64 { return G[GMG_C_NRM] as *i64 }
306func gmg_hh(G: *i64) -> *i64 { return G[GMG_C_HH] as *i64 }
307func gmg_ax(G: *i64) -> *i64 { return G[GMG_C_AX] as *i64 }
308func gmg_vf(G: *i64) -> *u8 { return G[GMG_C_VF] as *u8 }
309func gmg_has(G: *i64, v: i64, bit: i64) -> i64 {
310 let f: *u8 = gmg_vf(G)
311 if ((f[v] as i64) & bit) != 0 { return 1 }
312 return 0
313}
314
315// nearest joint to height f (decimil) with a midline preference (the page's near())
316func gmg_near_joint(G: *i64, fdm: i64) -> i64 {
317 let bind: *i64 = G[GMG_C_BIND] as *i64
318 let nj: i64 = G[GMG_C_NJ]
319 let tz: i64 = G[GMG_C_ZMN] + fdm * G[GMG_C_H] / GMG_DM
320 var bj: i64 = 0
321 var bd: i64 = GMG_BIG
322 var j: i64 = 0
323 while j < nj {
324 let d: i64 = gmg_abs(bind[j * 3 + 2] - tz) + 2 * gmg_abs(bind[j * 3])
325 if d < bd { bd = d; bj = j }
326 j = j + 1
327 }
328 return bj
329}
330
331// gmg_load: parse VERT/TRIS/SKEL/SKIN (+DYNA, +GVRT), measure the body, derive the landmarks, allocate the
332// scratch. Returns the context, or 0 with the refusal NAMED on stdout.
333func gmg_load(b: *u8, flen: i64) -> *i64 {
334 let vwo: i64 = nxa_find(b, flen, nxa_tag4("VERT" as *u8))
335 let two: i64 = nxa_find(b, flen, nxa_tag4("TRIS" as *u8))
336 let swo: i64 = nxa_find(b, flen, nxa_tag4("SKEL" as *u8))
337 let kwo: i64 = nxa_find(b, flen, nxa_tag4("SKIN" as *u8))
338 if vwo < 0 { gmg_puts("GMG-REFUSED bad nxa: VERT\n" as *u8); return 0 as *i64 }
339 if two < 0 { gmg_puts("GMG-REFUSED bad nxa: TRIS\n" as *u8); return 0 as *i64 }
340 if swo < 0 { gmg_puts("GMG-REFUSED bad nxa: SKEL\n" as *u8); return 0 as *i64 }
341 if kwo < 0 { gmg_puts("GMG-REFUSED bad nxa: SKIN\n" as *u8); return 0 as *i64 }
342 let h: *i64 = b as *i64
343 let nv: i64 = h[vwo]
344 let nt: i64 = h[two]
345 let nj: i64 = h[swo]
346 let nsv: i64 = h[kwo]
347 if nsv != nv { gmg_puts("GMG-REFUSED skin/vert desync\n" as *u8); return 0 as *i64 }
348 if nv < 3 { gmg_puts("GMG-REFUSED empty mesh\n" as *u8); return 0 as *i64 }
349 let G: *i64 = sys_mmap(GMG_C_SLOTS * 8) as *i64
350 var s0: i64 = 0
351 while s0 < GMG_C_SLOTS { G[s0] = 0; s0 = s0 + 1 }
352 G[GMG_C_B] = b as i64
353 G[GMG_C_FLEN] = flen
354 let pos: *i64 = ((b as i64) + (vwo + 1) * 8) as *i64
355 let idx: *i64 = ((b as i64) + (two + 1) * 8) as *i64
356 G[GMG_C_POS] = pos as i64
357 G[GMG_C_NV] = nv
358 G[GMG_C_IDX] = idx as i64
359 G[GMG_C_NT] = nt
360 G[GMG_C_THICK_PERMIL] = 1000
361 G[GMG_C_UPMK] = GMG_UNITS_178 * 1000 / GMG_MM_178
362 // bind positions
363 let bind: *i64 = sys_mmap(nj * 3 * 8 + 64) as *i64
364 var j: i64 = 0
365 while j < nj {
366 let bw: i64 = swo + 1 + j * GMG_SKEL_STRIDE
367 bind[j * 3] = h[bw + 1]
368 bind[j * 3 + 1] = h[bw + 2]
369 bind[j * 3 + 2] = h[bw + 3]
370 j = j + 1
371 }
372 G[GMG_C_BIND] = bind as i64
373 G[GMG_C_NJ] = nj
374 // skin rows (joint ids + Q12 weights) -- the page copies these per garment vertex
375 G[GMG_C_AJ] = ((b as i64) + (kwo + 1) * 8)
376 G[GMG_C_AW] = 0
377 // extents
378 var zmn: i64 = GMG_BIG
379 var zmx: i64 = 0 - GMG_BIG
380 var v: i64 = 0
381 while v < nv {
382 let z: i64 = pos[v * 3 + 2]
383 if z < zmn { zmn = z }
384 if z > zmx { zmx = z }
385 v = v + 1
386 }
387 var H: i64 = zmx - zmn
388 if H < 1 { H = 1 }
389 G[GMG_C_ZMN] = zmn
390 G[GMG_C_H] = H
391 // per-vertex height (decimil) and |x|
392 let hh: *i64 = sys_mmap(nv * 8 + 64) as *i64
393 let ax: *i64 = sys_mmap(nv * 8 + 64) as *i64
394 var ys: i64 = 0
395 var yc: i64 = 0
396 v = 0
397 while v < nv {
398 hh[v] = (pos[v * 3 + 2] - zmn) * GMG_DM / H
399 ax[v] = gmg_abs(pos[v * 3])
400 if ax[v] < GMG_TORSO_HALFW { if hh[v] > 5000 { if hh[v] < 8000 { ys = ys + pos[v * 3 + 1]; yc = yc + 1 } } }
401 v = v + 1
402 }
403 G[GMG_C_HH] = hh as i64
404 G[GMG_C_AX] = ax as i64
405 var ymid: i64 = 0
406 if yc > 0 { ymid = ys / yc }
407 G[GMG_C_YMID] = ymid
408 // smooth vertex normals: area-weighted accumulation of face cross products (scaled down to keep
409 // the sums small), normalised to Q12, then the torso-column outwardness guard: a normal pointing
410 // INTO the column (against the radial from the axis) is flipped -- the mesh's winding is measured
411 // 96 percent consistent on the reference body, and the 4 percent would drop a shell inside the skin.
412 let acc: *i64 = sys_mmap(nv * 3 * 8 + 64) as *i64
413 var z0: i64 = 0
414 while z0 < nv * 3 { acc[z0] = 0; z0 = z0 + 1 }
415 var e: i64 = 0
416 while e < nt {
417 let a: i64 = idx[e * 3]
418 let bb: i64 = idx[e * 3 + 1]
419 let c: i64 = idx[e * 3 + 2]
420 let e1x: i64 = pos[bb * 3] - pos[a * 3]
421 let e1y: i64 = pos[bb * 3 + 1] - pos[a * 3 + 1]
422 let e1z: i64 = pos[bb * 3 + 2] - pos[a * 3 + 2]
423 let e2x: i64 = pos[c * 3] - pos[a * 3]
424 let e2y: i64 = pos[c * 3 + 1] - pos[a * 3 + 1]
425 let e2z: i64 = pos[c * 3 + 2] - pos[a * 3 + 2]
426 let fx: i64 = (e1y * e2z - e1z * e2y) / GMG_CROSS_SHIFT
427 let fy: i64 = (e1z * e2x - e1x * e2z) / GMG_CROSS_SHIFT
428 let fz: i64 = (e1x * e2y - e1y * e2x) / GMG_CROSS_SHIFT
429 acc[a * 3] = acc[a * 3] + fx; acc[a * 3 + 1] = acc[a * 3 + 1] + fy; acc[a * 3 + 2] = acc[a * 3 + 2] + fz
430 acc[bb * 3] = acc[bb * 3] + fx; acc[bb * 3 + 1] = acc[bb * 3 + 1] + fy; acc[bb * 3 + 2] = acc[bb * 3 + 2] + fz
431 acc[c * 3] = acc[c * 3] + fx; acc[c * 3 + 1] = acc[c * 3 + 1] + fy; acc[c * 3 + 2] = acc[c * 3 + 2] + fz
432 e = e + 1
433 }
434 let nrm: *i64 = sys_mmap(nv * 3 * 8 + 64) as *i64
435 v = 0
436 while v < nv {
437 var nx: i64 = acc[v * 3]
438 var ny: i64 = acc[v * 3 + 1]
439 var nz: i64 = acc[v * 3 + 2]
440 var m: i64 = gmg_max(gmg_abs(nx), gmg_max(gmg_abs(ny), gmg_abs(nz)))
441 while m > GMG_NRM_CAP { nx = nx / 16; ny = ny / 16; nz = nz / 16; m = m / 16 }
442 let ln: i64 = gmg_isqrt(nx * nx + ny * ny + nz * nz)
443 if ln < 1 { nx = 0; ny = 0; nz = GMG_Q } else { nx = nx * GMG_Q / ln; ny = ny * GMG_Q / ln; nz = nz * GMG_Q / ln }
444 if ax[v] < GMG_TORSO_HALFW { if hh[v] > 4000 { if hh[v] < 8000 {
445 let rx: i64 = pos[v * 3]
446 let ry: i64 = pos[v * 3 + 1] - ymid
447 if nx * rx + ny * ry < 0 { nx = 0 - nx; ny = 0 - ny; nz = 0 - nz }
448 } } }
449 nrm[v * 3] = nx; nrm[v * 3 + 1] = ny; nrm[v * 3 + 2] = nz
450 v = v + 1
451 }
452 G[GMG_C_NRM] = nrm as i64
453 // dominant joint per vertex -> arm / head flags; torso-column flag
454 let vf: *u8 = sys_mmap(nv + 64) as *u8
455 let skin: *i64 = G[GMG_C_AJ] as *i64
456 v = 0
457 while v < nv {
458 let row: i64 = v * GMG_SKIN_STRIDE
459 var k: i64 = 0
460 var q: i64 = 1
461 while q < 4 { if skin[row + 4 + q] > skin[row + 4 + k] { k = q } q = q + 1 }
462 var dj: i64 = skin[row + k]
463 if dj < 0 { dj = 0 }
464 if dj >= nj { dj = 0 }
465 var f: i64 = 0
466 if gmg_abs(bind[dj * 3]) > GMG_ARM_BIND_X { f = f | GMG_VF_ARM }
467 if (bind[dj * 3 + 2] - zmn) * GMG_DM / H > GMG_HEAD_DM { f = f | GMG_VF_HEAD }
468 if ax[v] < GMG_TORSO_HALFW { if (f & (GMG_VF_ARM | GMG_VF_HEAD)) == 0 { f = f | GMG_VF_TOR } }
469 vf[v] = f as u8
470 v = v + 1
471 }
472 G[GMG_C_VF] = vf as i64
473 // NIP: the published .72 ratio mapped through THIS mesh's pelvis (.52) and head (.95) anchors -- the
474 // page's own derivation, in permil-then-decimil so the two agree to the rounding
475 let pj: i64 = gmg_near_joint(G, GMG_HIPJ_DM)
476 let hj: i64 = gmg_near_joint(G, GMG_HEADJ_PROBE_DM)
477 let pv: i64 = ((bind[pj * 3 + 2] - zmn) * 1000 + H / 2) / H * 10
478 let hd: i64 = ((bind[hj * 3 + 2] - zmn) * 1000 + H / 2) / H * 10
479 var nip: i64 = GMG_NIP_RATIO_DM
480 let sl: i64 = (hd - pv) * GMG_Q / (GMG_EYE_DM - GMG_HIPJ_DM)
481 if sl > GMG_Q * 4 / 10 { if sl < GMG_Q * 16 / 10 { nip = pv + sl * (GMG_NIP_RATIO_DM - GMG_HIPJ_DM) / GMG_Q } }
482 G[GMG_C_NIP] = nip
483 // DYNA anchors (optional): rows of 8 words [side,x,y,z,k,c,max,infl]
484 let dwo: i64 = nxa_find(b, flen, nxa_tag4("DYNA" as *u8))
485 if dwo >= 0 {
486 let ndb: i64 = h[dwo]
487 let dst: i64 = h[dwo + 1]
488 if dst >= GMG_DYN_FIELDS { G[GMG_C_DYN] = ((b as i64) + (dwo + 2) * 8); G[GMG_C_ND] = ndb; G[GMG_C_DST] = dst }
489 }
490 // the skirt (GVRT) top ring: its height and its mean gap to the body -- the waistband's ease
491 G[GMG_C_GTH] = 0 - 1
492 G[GMG_C_GAP] = 0
493 let gwo: i64 = nxa_find(b, flen, nxa_tag4("GVRT" as *u8))
494 if gwo >= 0 {
495 let gnv: i64 = h[gwo]
496 let gp: *i64 = ((b as i64) + (gwo + 1) * 8) as *i64
497 var gtop: i64 = 0 - GMG_BIG
498 var i: i64 = 0
499 while i < gnv { if gp[i * 3 + 2] > gtop { gtop = gp[i * 3 + 2] } i = i + 1 }
500 let gth: i64 = (gtop - zmn) * GMG_DM / H
501 var gsum: i64 = 0
502 var gcnt: i64 = 0
503 i = 0
504 while i < gnv {
505 if gmg_abs(gp[i * 3 + 2] - gtop) < 2 {
506 var bd: i64 = GMG_BIG
507 var u: i64 = 0
508 while u < nv {
509 if gmg_abs(hh[u] - gth) <= GMG_SKIRT_TOP_TOL_DM {
510 let dx: i64 = pos[u * 3] - gp[i * 3]
511 let dy: i64 = pos[u * 3 + 1] - gp[i * 3 + 1]
512 let dz: i64 = pos[u * 3 + 2] - gp[i * 3 + 2]
513 let dd: i64 = dx * dx + dy * dy + dz * dz
514 if dd < bd { bd = dd }
515 }
516 u = u + 1
517 }
518 if bd < GMG_BIG { gsum = gsum + gmg_isqrt(bd); gcnt = gcnt + 1 }
519 }
520 i = i + 1
521 }
522 G[GMG_C_GTH] = gth
523 if gcnt > 0 { G[GMG_C_GAP] = gsum / gcnt }
524 }
525 // scratch
526 G[GMG_C_VMAP] = sys_mmap(nv * 8 + 64) as i64
527 G[GMG_C_INNER] = sys_mmap(nv * 8 + 64) as i64
528 G[GMG_C_SEL] = sys_mmap(nv + 64) as i64
529 G[GMG_C_BSET] = sys_mmap(nv + 64) as i64
530 G[GMG_C_RING] = sys_mmap(nv + 64) as i64
531 G[GMG_C_SEEN] = sys_mmap(nv + 64) as i64
532 G[GMG_C_DIL] = sys_mmap(nv + 64) as i64
533 G[GMG_C_NBR] = sys_mmap(nv * GMG_NBR_CAP * 8 + 64) as i64
534 G[GMG_C_NBC] = sys_mmap(nv * GMG_NBR_CAP * 8 + 64) as i64
535 G[GMG_C_DEG] = sys_mmap(nv * 8 + 64) as i64
536 G[GMG_C_BADJ] = sys_mmap(nv * GMG_BADJ_CAP * 8 + 64) as i64
537 G[GMG_C_BDEG] = sys_mmap(nv * 8 + 64) as i64
538 G[GMG_C_STACK] = sys_mmap(nv * 8 + 64) as i64
539 G[GMG_C_CAND] = sys_mmap(nv * 8 + 64) as i64
540 G[GMG_C_TRIS] = sys_mmap(nt * 8 + 64) as i64
541 G[GMG_C_OV] = sys_mmap(GMG_OV_CAP * GMG_VSTRIDE * 8 + 64) as i64
542 G[GMG_C_OT] = sys_mmap(GMG_OT_CAP * GMG_TSTRIDE * 8 + 64) as i64
543 return G
544}
545
546// nearest body vertex to (x,y,z) inside a height band, torso/limb only (no arm, no head), |x| bounded
547func gmg_near_body(G: *i64, x: i64, y: i64, z: i64, hlo: i64, hhi: i64, axmax: i64) -> i64 {
548 let pos: *i64 = gmg_pos(G)
549 let hh: *i64 = gmg_hh(G)
550 let ax: *i64 = gmg_ax(G)
551 let vf: *u8 = gmg_vf(G)
552 let nv: i64 = G[GMG_C_NV]
553 var bi: i64 = 0 - 1
554 var bd: i64 = GMG_BIG
555 var v: i64 = 0
556 while v < nv {
557 var ok: i64 = 1
558 if hh[v] < hlo { ok = 0 }
559 if hh[v] > hhi { ok = 0 }
560 if ax[v] > axmax { ok = 0 }
561 if ((vf[v] as i64) & (GMG_VF_ARM | GMG_VF_HEAD)) != 0 { ok = 0 }
562 if ok == 1 {
563 let dx: i64 = pos[v * 3] - x
564 let dy: i64 = pos[v * 3 + 1] - y
565 let dz: i64 = pos[v * 3 + 2] - z
566 let dd: i64 = dx * dx + dy * dy + dz * dz
567 if dd < bd { bd = dd; bi = v }
568 }
569 v = v + 1
570 }
571 return bi
572}
573
574// the leg opening line (decimil) at vertex v: crotch .475 H front rising with |x| toward the hip, higher
575// cut at the back (.445 -> .50), blended front-to-back over the y midline
576func gmg_hleg(G: *i64, v: i64) -> i64 {
577 let ax: *i64 = gmg_ax(G)
578 let pos: *i64 = gmg_pos(G)
579 var u: i64 = ax[v] * GMG_Q / GMG_TORSO_HALFW
580 if u > GMG_Q { u = GMG_Q }
581 let uu: i64 = u * u / GMG_Q
582 let f: i64 = GMG_CROTCH_DM + 250 * uu / GMG_Q
583 let bk: i64 = 4450 + 550 * uu / GMG_Q
584 return f + (bk - f) * gmg_sst(0 - GMG_YBLEND, GMG_YBLEND, pos[v * 3 + 1]) / GMG_Q
585}
586// the one-piece / sundress neckline (decimil): scoop front .77 H rising to .82 at the shoulder, back .70
587func gmg_hneck(G: *i64, v: i64) -> i64 {
588 let ax: *i64 = gmg_ax(G)
589 let pos: *i64 = gmg_pos(G)
590 var u: i64 = ax[v] * GMG_Q / GMG_TORSO_HALFW
591 if u > GMG_Q { u = GMG_Q }
592 let uu: i64 = u * u / GMG_Q
593 let f: i64 = GMG_ONEPIECE_NECK_F + 500 * uu / GMG_Q
594 let bk: i64 = GMG_ONEPIECE_NECK_B + 600 * uu / GMG_Q
595 return f + (bk - f) * gmg_sst(0 - GMG_YBLEND, GMG_YBLEND, pos[v * 3 + 1]) / GMG_Q
596}
597// the tee crew neck (decimil): .835 H, rising .03 H toward the shoulder
598func gmg_hneck_tee(G: *i64, v: i64) -> i64 {
599 let ax: *i64 = gmg_ax(G)
600 var t: i64 = (ax[v] - 3000) * GMG_Q / 9000
601 if t < 0 { t = 0 }
602 if t > GMG_Q { t = GMG_Q }
603 return GMG_TEE_NECK_DM + GMG_TEE_NECK_RISE * t / GMG_Q
604}
605func gmg_dyn_side(G: *i64, side: i64) -> i64 {
606 let nd: i64 = G[GMG_C_ND]
607 if nd == 0 { return 0 - 1 }
608 let dyn: *i64 = G[GMG_C_DYN] as *i64
609 let dst: i64 = G[GMG_C_DST]
610 var d: i64 = 0
611 while d < nd {
612 if side < 0 { if dyn[d * dst] < 0 { return d } }
613 if side > 0 { if dyn[d * dst] > 0 { return d } }
614 d = d + 1
615 }
616 return 0 - 1
617}
618func gmg_in_dyn(G: *i64, v: i64, d: i64) -> i64 {
619 if d < 0 { return 0 }
620 let dyn: *i64 = G[GMG_C_DYN] as *i64
621 let dst: i64 = G[GMG_C_DST]
622 let pos: *i64 = gmg_pos(G)
623 let dx: i64 = pos[v * 3] - dyn[d * dst + 1]
624 let dy: i64 = pos[v * 3 + 1] - dyn[d * dst + 2]
625 let dz: i64 = pos[v * 3 + 2] - dyn[d * dst + 3]
626 let r: i64 = dyn[d * dst + 7]
627 if dx * dx + dy * dy + dz * dz < r * r { return 1 }
628 return 0
629}
630
631// gmg_region: does garment `kind` cover body vertex v?
632func gmg_region(G: *i64, kind: i64, v: i64) -> i64 {
633 let hh: *i64 = gmg_hh(G)
634 let ax: *i64 = gmg_ax(G)
635 let pos: *i64 = gmg_pos(G)
636 let h: i64 = hh[v]
637 let tor: i64 = gmg_has(G, v, GMG_VF_TOR)
638 let arm: i64 = gmg_has(G, v, GMG_VF_ARM)
639 let head: i64 = gmg_has(G, v, GMG_VF_HEAD)
640 let nip: i64 = G[GMG_C_NIP]
641 if kind == 1 {
642 if tor == 0 { return 0 }
643 // the cups: the front bust regions inside the asset's OWN DYNA anchor radii; the back band: a
644 // narrow ring at the underbust line (NIP - .055 H) all the way round
645 if pos[v * 3 + 1] < GMG_FRONT_Y {
646 if gmg_in_dyn(G, v, gmg_dyn_side(G, 0 - 1)) == 1 { return 1 }
647 if gmg_in_dyn(G, v, gmg_dyn_side(G, 1)) == 1 { return 1 }
648 // DYNA-less asset: the bust band itself stands in for the cups (declared fallback)
649 if G[GMG_C_ND] == 0 { if gmg_abs(h - nip) < GMG_BUST_HALF_DM { return 1 } }
650 }
651 if gmg_abs(h - (nip - GMG_BUST_HALF_DM)) < GMG_STRAP_BAND_TOL_DM { return 1 }
652 return 0
653 }
654 if kind == 2 { if tor == 1 { if h < 5500 { if h > gmg_hleg(G, v) { return 1 } } } return 0 }
655 if kind == 3 { if tor == 1 { if h > gmg_hleg(G, v) { if h < gmg_hneck(G, v) { return 1 } } } return 0 }
656 if kind == 9 { if tor == 1 { if h > gmg_hleg(G, v) { if h < gmg_hneck(G, v) { return 1 } } } return 0 }
657 if kind == 4 {
658 let dx: i64 = pos[v * 3] - GMG_SARONG_X
659 let dy: i64 = pos[v * 3 + 1]
660 let dz: i64 = pos[v * 3 + 2] - (G[GMG_C_ZMN] + GMG_SARONG_DM * G[GMG_C_H] / GMG_DM)
661 if dx * dx + dy * dy + dz * dz < GMG_SARONG_R * GMG_SARONG_R { return 1 }
662 return 0
663 }
664 if kind == 5 { if tor == 1 { if h > GMG_HIP_DM { if h < gmg_hneck(G, v) { return 1 } } } return 0 }
665 if kind == 6 {
666 if tor == 1 { if h > GMG_HIP_DM { if h < gmg_hneck_tee(G, v) { return 1 } } }
667 if arm == 1 { if ax[v] < GMG_SLEEVE_HALFW { if h > GMG_SLEEVE_TOP_DM { return 1 } } }
668 if ax[v] > GMG_SLEEVE_X_LO { if ax[v] < GMG_SLEEVE_HALFW { if h > GMG_SLEEVE_BAND_LO { if h < GMG_SLEEVE_BAND_HI { if head == 0 { return 1 } } } } }
669 return 0
670 }
671 if kind == 7 { if ax[v] < GMG_TORSO_HALFW { if arm == 0 { if h > GMG_MIDTHIGH_DM { if h < GMG_SHORTS_TOP_DM { return 1 } } } } return 0 }
672 if kind == 8 {
673 if G[GMG_C_GTH] < 0 { return 0 }
674 if tor == 1 { if gmg_abs(h - G[GMG_C_GTH]) < GMG_WAIST_BAND_DM { return 1 } }
675 return 0
676 }
677 return 0
678}
679
680// radial ease at vertex v: the full ease below the bust, fading to zero across the bust->shoulder band so
681// necklines and straps sit on the body; the sarong knot and the waistband keep theirs everywhere
682func gmg_ease_at(G: *i64, kind: i64, mat: *i64, v: i64) -> i64 {
683 let e: i64 = mat[2]
684 if e == 0 { return 0 }
685 if kind == 4 { return e }
686 if kind == 8 { return e }
687 let hh: *i64 = gmg_hh(G)
688 return e * (GMG_Q - gmg_sst(GMG_EASE_FADE_LO, GMG_EASE_FADE_HI, hh[v])) / GMG_Q
689}
690
691// append one garment vertex grown from body vertex src at offset d along its normal; returns its index
692func gmg_addv(G: *i64, src: i64, d: i64, flag: i64) -> i64 {
693 let onv: i64 = G[GMG_C_ONV]
694 if onv >= GMG_OV_CAP { return 0 - 1 }
695 let ov: *i64 = G[GMG_C_OV] as *i64
696 let pos: *i64 = gmg_pos(G)
697 let nrm: *i64 = gmg_nrm(G)
698 let o: i64 = onv * GMG_VSTRIDE
699 ov[o] = pos[src * 3] + nrm[src * 3] * d / GMG_Q
700 ov[o + 1] = pos[src * 3 + 1] + nrm[src * 3 + 1] * d / GMG_Q
701 ov[o + 2] = pos[src * 3 + 2] + nrm[src * 3 + 2] * d / GMG_Q
702 ov[o + 3] = src
703 ov[o + 4] = flag
704 G[GMG_C_ONV] = onv + 1
705 return onv
706}
707func gmg_addv_at(G: *i64, x: i64, y: i64, z: i64, src: i64, flag: i64) -> i64 {
708 let onv: i64 = G[GMG_C_ONV]
709 if onv >= GMG_OV_CAP { return 0 - 1 }
710 let ov: *i64 = G[GMG_C_OV] as *i64
711 let o: i64 = onv * GMG_VSTRIDE
712 ov[o] = x; ov[o + 1] = y; ov[o + 2] = z; ov[o + 3] = src; ov[o + 4] = flag
713 G[GMG_C_ONV] = onv + 1
714 return onv
715}
716func gmg_addt(G: *i64, a: i64, b: i64, c: i64) -> i64 {
717 let ont: i64 = G[GMG_C_ONT]
718 if ont >= GMG_OT_CAP { return 0 - 1 }
719 let ot: *i64 = G[GMG_C_OT] as *i64
720 ot[ont * 3] = a; ot[ont * 3 + 1] = b; ot[ont * 3 + 2] = c
721 G[GMG_C_ONT] = ont + 1
722 return ont
723}
724
725// undirected edge table keyed under the LOWER vertex: count occurrences across the selected triangles
726func gmg_nbr_add(G: *i64, u0: i64, v0: i64) -> i64 {
727 var u: i64 = u0
728 var v: i64 = v0
729 if v < u { u = v0; v = u0 }
730 let nbr: *i64 = G[GMG_C_NBR] as *i64
731 let nbc: *i64 = G[GMG_C_NBC] as *i64
732 let deg: *i64 = G[GMG_C_DEG] as *i64
733 var k: i64 = 0
734 while k < deg[u] {
735 if nbr[u * GMG_NBR_CAP + k] == v { nbc[u * GMG_NBR_CAP + k] = nbc[u * GMG_NBR_CAP + k] + 1; return 1 }
736 k = k + 1
737 }
738 if deg[u] >= GMG_NBR_CAP { G[GMG_C_NBROVER] = G[GMG_C_NBROVER] + 1; return 0 }
739 nbr[u * GMG_NBR_CAP + deg[u]] = v
740 nbc[u * GMG_NBR_CAP + deg[u]] = 1
741 deg[u] = deg[u] + 1
742 return 1
743}
744func gmg_nbr_count(G: *i64, u0: i64, v0: i64) -> i64 {
745 var u: i64 = u0
746 var v: i64 = v0
747 if v < u { u = v0; v = u0 }
748 let nbr: *i64 = G[GMG_C_NBR] as *i64
749 let nbc: *i64 = G[GMG_C_NBC] as *i64
750 let deg: *i64 = G[GMG_C_DEG] as *i64
751 var k: i64 = 0
752 while k < deg[u] {
753 if nbr[u * GMG_NBR_CAP + k] == v { return nbc[u * GMG_NBR_CAP + k] }
754 k = k + 1
755 }
756 return 0
757}
758
759// one ribbon strap: front anchor -> shoulder top -> back anchor, sampled, each sample snapped to the
760// nearest body vertex in its height band and lifted by the hem thickness; two verts per sample across
761// the local tangent x normal, quads between samples. Returns the sample count; end heights into ends[0..1].
762func gmg_strap(G: *i64, mat: *i64, sx: i64, hF: i64, hB: i64, halfw: i64, ends: *i64) -> i64 {
763 let pos: *i64 = gmg_pos(G)
764 let nrm: *i64 = gmg_nrm(G)
765 let hh: *i64 = gmg_hh(G)
766 let zmn: i64 = G[GMG_C_ZMN]
767 let H: i64 = G[GMG_C_H]
768 let th: i64 = mat[1]
769 // anchors (bind space)
770 let ax0: i64 = sx * GMG_STRAP_AX_FRONT
771 let ay0: i64 = GMG_STRAP_Y_FRONT
772 let az0: i64 = zmn + hF * H / GMG_DM
773 let bx0: i64 = sx * GMG_STRAP_AX_SHOULDER
774 let by0: i64 = GMG_STRAP_Y_SHOULDER
775 let bz0: i64 = zmn + GMG_STRAP_SHOULDER_DM * H / GMG_DM
776 let cx0: i64 = sx * GMG_STRAP_AX_BACK
777 let cy0: i64 = GMG_STRAP_Y_BACK
778 let cz0: i64 = zmn + hB * H / GMG_DM
779 let ring: *i64 = sys_mmap(GMG_STRAP_CAP * 4 * 8 + 64) as *i64
780 var n: i64 = 0
781 var seg: i64 = 0
782 while seg < 2 {
783 var i: i64 = 0
784 if seg == 1 { i = 1 }
785 while i <= GMG_STRAP_K {
786 var px: i64 = 0
787 var py: i64 = 0
788 var pz: i64 = 0
789 if seg == 0 {
790 px = ax0 + (bx0 - ax0) * i / GMG_STRAP_K; py = ay0 + (by0 - ay0) * i / GMG_STRAP_K; pz = az0 + (bz0 - az0) * i / GMG_STRAP_K
791 } else {
792 px = bx0 + (cx0 - bx0) * i / GMG_STRAP_K; py = by0 + (cy0 - by0) * i / GMG_STRAP_K; pz = bz0 + (cz0 - bz0) * i / GMG_STRAP_K
793 }
794 let hq: i64 = (pz - zmn) * GMG_DM / H
795 let v: i64 = gmg_near_body(G, px, py, pz, hq - GMG_STRAP_SEARCH_DM, hq + GMG_STRAP_SEARCH_DM, GMG_STRAP_AX_MAX)
796 if v >= 0 { if n < GMG_STRAP_CAP {
797 ring[n * 4] = v
798 ring[n * 4 + 1] = pos[v * 3] + nrm[v * 3] * th / GMG_Q
799 ring[n * 4 + 2] = pos[v * 3 + 1] + nrm[v * 3 + 1] * th / GMG_Q
800 ring[n * 4 + 3] = pos[v * 3 + 2] + nrm[v * 3 + 2] * th / GMG_Q
801 n = n + 1
802 } }
803 i = i + 1
804 }
805 seg = seg + 1
806 }
807 if n < 2 { return 0 }
808 var i2: i64 = 0
809 var prev: i64 = 0 - 1
810 while i2 < n {
811 let v: i64 = ring[i2 * 4]
812 var jn: i64 = i2 + 1
813 if jn > n - 1 { jn = n - 1 }
814 var jp: i64 = i2 - 1
815 if jp < 0 { jp = 0 }
816 var tx: i64 = ring[jn * 4 + 1] - ring[jp * 4 + 1]
817 var ty: i64 = ring[jn * 4 + 2] - ring[jp * 4 + 2]
818 var tz: i64 = ring[jn * 4 + 3] - ring[jp * 4 + 3]
819 var tl: i64 = gmg_isqrt(tx * tx + ty * ty + tz * tz)
820 if tl < 1 { tl = 1 }
821 tx = tx * GMG_Q / tl; ty = ty * GMG_Q / tl; tz = tz * GMG_Q / tl
822 let nx: i64 = nrm[v * 3]
823 let ny: i64 = nrm[v * 3 + 1]
824 let nz: i64 = nrm[v * 3 + 2]
825 var sxv: i64 = (ty * nz - tz * ny) / GMG_Q
826 var syv: i64 = (tz * nx - tx * nz) / GMG_Q
827 var szv: i64 = (tx * ny - ty * nx) / GMG_Q
828 var sl: i64 = gmg_isqrt(sxv * sxv + syv * syv + szv * szv)
829 if sl < 1 { sl = 1 }
830 sxv = sxv * halfw / sl; syv = syv * halfw / sl; szv = szv * halfw / sl
831 let a: i64 = gmg_addv_at(G, ring[i2 * 4 + 1] + sxv, ring[i2 * 4 + 2] + syv, ring[i2 * 4 + 3] + szv, v, GMG_F_STRAP)
832 let c: i64 = gmg_addv_at(G, ring[i2 * 4 + 1] - sxv, ring[i2 * 4 + 2] - syv, ring[i2 * 4 + 3] - szv, v, GMG_F_STRAP)
833 if a < 0 { return 0 - 1 }
834 if c < 0 { return 0 - 1 }
835 if prev >= 0 {
836 gmg_addt(G, prev, prev + 1, a)
837 gmg_addt(G, prev + 1, c, a)
838 }
839 prev = a
840 i2 = i2 + 1
841 }
842 ends[0] = hh[ring[0]]
843 ends[1] = hh[ring[(n - 1) * 4]]
844 return n
845}
846
847// gmg_build: one garment kind into the OV/OT scratch (reset here). Returns nverts, 0 when the region
848// selects no triangle, negative on refusal. stat[] carries the counts the gate reads.
849func gmg_build(G: *i64, kind: i64, stat: *i64) -> i64 {
850 var s: i64 = 0
851 while s < GMG_STAT_N { stat[s] = 0; s = s + 1 }
852 stat[GMG_S_ID] = kind
853 let mat: *i64 = sys_mmap(4 * 8) as *i64
854 if gmg_mat(G, kind, mat) < 0 { return GMG_ERR_EMPTY }
855 let t: i64 = mat[0]
856 let th: i64 = mat[1]
857 stat[GMG_S_T] = t; stat[GMG_S_TH] = th; stat[GMG_S_EASE] = mat[2]; stat[GMG_S_CLASS] = mat[3]
858 // A ZERO-THICKNESS GARMENT IS NOT A GARMENT: refused by name, never emitted as a coincident shell
859 if t <= 0 { gmg_puts("GMG-REFUSED kind=" as *u8); gmg_pn(kind); gmg_puts(" zero-thickness: a shell coincident with the skin is paint, not an object\n" as *u8); return GMG_ERR_REFUSED_THICK }
860 let pos: *i64 = gmg_pos(G)
861 let nrm: *i64 = gmg_nrm(G)
862 let idx: *i64 = G[GMG_C_IDX] as *i64
863 let nv: i64 = G[GMG_C_NV]
864 let nt: i64 = G[GMG_C_NT]
865 let sel: *u8 = G[GMG_C_SEL] as *u8
866 let bset: *u8 = G[GMG_C_BSET] as *u8
867 let ring: *u8 = G[GMG_C_RING] as *u8
868 let vmap: *i64 = G[GMG_C_VMAP] as *i64
869 let inner: *i64 = G[GMG_C_INNER] as *i64
870 let deg: *i64 = G[GMG_C_DEG] as *i64
871 let cand: *i64 = G[GMG_C_CAND] as *i64
872 let tris: *i64 = G[GMG_C_TRIS] as *i64
873 G[GMG_C_ONV] = 0
874 G[GMG_C_ONT] = 0
875 G[GMG_C_NBROVER] = 0
876 // 1-3. region -> covered triangles + edge table -> boundary, with PINCH DILATION: where two parts of a
877 // region touch at a single vertex (the bikini cups meeting the under-bust band, measured on the
878 // served body: cup bottom 703 dm, band top 701 dm) the union's boundary has a bow-tie vertex with
879 // four boundary edges and the hem cannot close there. Real fabric BRIDGES a pinch, so every vertex
880 // of every body triangle touching a pinch joins the region and the boundary is re-derived; bounded
881 // passes, and a pinch that survives them still FAILS the closed-hems tooth (never hidden).
882 let nbr: *i64 = G[GMG_C_NBR] as *i64
883 let nbc: *i64 = G[GMG_C_NBC] as *i64
884 let bdeg0: *i64 = G[GMG_C_BDEG] as *i64
885 let dil: *u8 = G[GMG_C_DIL] as *u8
886 let mark: *u8 = G[GMG_C_SEEN] as *u8
887 var v: i64 = 0
888 while v < nv { dil[v] = 0 as u8; v = v + 1 }
889 var ncand: i64 = 0
890 var ntris: i64 = 0
891 var nb: i64 = 0
892 var pinch: i64 = 1
893 var iter: i64 = 0
894 var passes: i64 = 0
895 while pinch > 0 {
896 if iter >= GMG_PINCH_PASSES { pinch = 0 } else {
897 ncand = 0
898 v = 0
899 while v < nv {
900 sel[v] = 0 as u8; bset[v] = 0 as u8; ring[v] = 0 as u8; vmap[v] = 0 - 1; inner[v] = 0 - 1; deg[v] = 0; bdeg0[v] = 0; mark[v] = 0 as u8
901 var inr: i64 = gmg_region(G, kind, v)
902 if dil[v] == (1 as u8) { inr = 1 }
903 if inr == 1 { sel[v] = 1 as u8; cand[ncand] = v; ncand = ncand + 1 }
904 v = v + 1
905 }
906 G[GMG_C_NBROVER] = 0
907 ntris = 0
908 var e: i64 = 0
909 while e < nt {
910 let a: i64 = idx[e * 3]
911 let b: i64 = idx[e * 3 + 1]
912 let c: i64 = idx[e * 3 + 2]
913 if sel[a] == (1 as u8) { if sel[b] == (1 as u8) { if sel[c] == (1 as u8) {
914 tris[ntris] = e; ntris = ntris + 1
915 gmg_nbr_add(G, a, b); gmg_nbr_add(G, b, c); gmg_nbr_add(G, c, a)
916 } } }
917 e = e + 1
918 }
919 nb = 0
920 v = 0
921 while v < nv {
922 var k: i64 = 0
923 while k < deg[v] {
924 if nbc[v * GMG_NBR_CAP + k] == 1 {
925 let w: i64 = nbr[v * GMG_NBR_CAP + k]
926 bset[v] = 1 as u8; bset[w] = 1 as u8; nb = nb + 1
927 bdeg0[v] = bdeg0[v] + 1; bdeg0[w] = bdeg0[w] + 1
928 }
929 k = k + 1
930 }
931 v = v + 1
932 }
933 pinch = 0
934 v = 0
935 while v < nv { if bdeg0[v] > 2 { pinch = pinch + 1; mark[v] = 1 as u8 } v = v + 1 }
936 if pinch > 0 {
937 e = 0
938 while e < nt {
939 let a: i64 = idx[e * 3]
940 let b: i64 = idx[e * 3 + 1]
941 let c: i64 = idx[e * 3 + 2]
942 var hit: i64 = 0
943 if mark[a] == (1 as u8) { hit = 1 }
944 if mark[b] == (1 as u8) { hit = 1 }
945 if mark[c] == (1 as u8) { hit = 1 }
946 if hit == 1 { dil[a] = 1 as u8; dil[b] = 1 as u8; dil[c] = 1 as u8 }
947 e = e + 1
948 }
949 }
950 iter = iter + 1
951 passes = passes + 1
952 }
953 }
954 G[GMG_C_NCAND] = ncand
955 stat[GMG_S_SEL] = ncand
956 stat[GMG_S_PINCHPASS] = passes
957 G[GMG_C_NTRIS] = ntris
958 stat[GMG_S_NBROVER] = G[GMG_C_NBROVER]
959 if ntris == 0 { return 0 }
960 stat[GMG_S_BEDGES] = nb
961 v = 0
962 while v < nv {
963 var k: i64 = 0
964 while k < deg[v] {
965 let w: i64 = nbr[v * GMG_NBR_CAP + k]
966 if bset[v] == (1 as u8) { ring[w] = 1 as u8 }
967 if bset[w] == (1 as u8) { ring[v] = 1 as u8 }
968 k = k + 1
969 }
970 v = v + 1
971 }
972 // 4. the shell: every covered triangle's vertices, offset by thickness (hem thickness at the opening,
973 // half-way one ring in = the raised binding band) plus the wearing ease
974 var ti: i64 = 0
975 while ti < ntris {
976 let e2: i64 = tris[ti]
977 var q: i64 = 0
978 while q < 3 {
979 let vv: i64 = idx[e2 * 3 + q]
980 if vmap[vv] < 0 {
981 var d: i64 = t
982 if ring[vv] == (1 as u8) { d = (t + th) / 2 }
983 if bset[vv] == (1 as u8) { d = th }
984 d = d + gmg_ease_at(G, kind, mat, vv)
985 let ix: i64 = gmg_addv(G, vv, d, GMG_F_SHELL)
986 if ix < 0 { return GMG_ERR_CAP }
987 vmap[vv] = ix
988 }
989 q = q + 1
990 }
991 if gmg_addt(G, vmap[idx[e2 * 3]], vmap[idx[e2 * 3 + 1]], vmap[idx[e2 * 3 + 2]]) < 0 { return GMG_ERR_CAP }
992 ti = ti + 1
993 }
994 stat[GMG_S_SHELL] = G[GMG_C_ONV]
995 let nshell: i64 = G[GMG_C_ONV]
996 let ov: *i64 = G[GMG_C_OV] as *i64
997 // 5. PUSH-OUT: in a concave fold the normal offset lands nearer a NEIGHBOURING skin vertex than its own;
998 // walk such vertices away from the offending vertex until no covered body vertex is inside the fabric
999 var pushed: i64 = 0
1000 if G[GMG_C_NOPUSH] == 0 {
1001 var pass: i64 = 0
1002 var again: i64 = 1
1003 while pass < GMG_PUSH_PASSES {
1004 if again == 1 {
1005 again = 0
1006 var i: i64 = 0
1007 while i < nshell {
1008 let o: i64 = i * GMG_VSTRIDE
1009 let x: i64 = ov[o]
1010 let y: i64 = ov[o + 1]
1011 let z: i64 = ov[o + 2]
1012 let src: i64 = ov[o + 3]
1013 var bd: i64 = t * t
1014 var bq: i64 = 0 - 1
1015 var ci: i64 = 0
1016 while ci < ncand {
1017 let qv: i64 = cand[ci]
1018 let dz: i64 = pos[qv * 3 + 2] - z
1019 if gmg_abs(dz) < t {
1020 let dx: i64 = pos[qv * 3] - x
1021 if gmg_abs(dx) < t {
1022 let dy: i64 = pos[qv * 3 + 1] - y
1023 if gmg_abs(dy) < t {
1024 let dd: i64 = dx * dx + dy * dy + dz * dz
1025 if dd < bd { bd = dd; bq = qv }
1026 }
1027 }
1028 }
1029 ci = ci + 1
1030 }
1031 if bq >= 0 {
1032 let dcur: i64 = gmg_isqrt(bd)
1033 let k2: i64 = t - dcur + GMG_PUSH_MARGIN
1034 var mx: i64 = nrm[src * 3]
1035 var my: i64 = nrm[src * 3 + 1]
1036 var mz: i64 = nrm[src * 3 + 2]
1037 if bq != src { if dcur >= 1 {
1038 mx = (x - pos[bq * 3]) * GMG_Q / dcur
1039 my = (y - pos[bq * 3 + 1]) * GMG_Q / dcur
1040 mz = (z - pos[bq * 3 + 2]) * GMG_Q / dcur
1041 } }
1042 ov[o] = x + mx * k2 / GMG_Q
1043 ov[o + 1] = y + my * k2 / GMG_Q
1044 ov[o + 2] = z + mz * k2 / GMG_Q
1045 pushed = pushed + 1
1046 again = 1
1047 }
1048 i = i + 1
1049 }
1050 }
1051 pass = pass + 1
1052 }
1053 }
1054 stat[GMG_S_PUSHED] = pushed
1055 // 6. hems: every boundary edge grows a strip from the shell edge down to the fabric's inner face
1056 var nhem: i64 = 0
1057 var e_in_swim: i64 = gmg_um2u(GMG_SWIM_INNER_UM)
1058 ti = 0
1059 while ti < ntris {
1060 let e2: i64 = tris[ti]
1061 var q: i64 = 0
1062 while q < 3 {
1063 let u: i64 = idx[e2 * 3 + q]
1064 var q1: i64 = q + 1
1065 if q1 > 2 { q1 = 0 }
1066 let w: i64 = idx[e2 * 3 + q1]
1067 if gmg_nbr_count(G, u, w) == 1 {
1068 if inner[u] < 0 {
1069 var ein: i64 = e_in_swim
1070 if mat[2] > 0 { ein = gmg_ease_at(G, kind, mat, u) }
1071 inner[u] = gmg_addv(G, u, ein, GMG_F_HEM)
1072 if inner[u] < 0 { return GMG_ERR_CAP }
1073 }
1074 if inner[w] < 0 {
1075 var ein2: i64 = e_in_swim
1076 if mat[2] > 0 { ein2 = gmg_ease_at(G, kind, mat, w) }
1077 inner[w] = gmg_addv(G, w, ein2, GMG_F_HEM)
1078 if inner[w] < 0 { return GMG_ERR_CAP }
1079 }
1080 // the strip carries COPIES so its faces get their own normals page-side
1081 let A: i64 = vmap[u]
1082 let B: i64 = vmap[w]
1083 let Bi: i64 = inner[w]
1084 let Ai: i64 = inner[u]
1085 let base: i64 = G[GMG_C_ONV]
1086 var cpy: i64 = 0
1087 while cpy < 4 {
1088 var srcix: i64 = A
1089 if cpy == 1 { srcix = B }
1090 if cpy == 2 { srcix = Bi }
1091 if cpy == 3 { srcix = Ai }
1092 let so: i64 = srcix * GMG_VSTRIDE
1093 if gmg_addv_at(G, ov[so], ov[so + 1], ov[so + 2], ov[so + 3], GMG_F_HEM) < 0 { return GMG_ERR_CAP }
1094 cpy = cpy + 1
1095 }
1096 gmg_addt(G, base, base + 1, base + 2)
1097 gmg_addt(G, base, base + 2, base + 3)
1098 nhem = nhem + 1
1099 }
1100 q = q + 1
1101 }
1102 ti = ti + 1
1103 }
1104 stat[GMG_S_HEM] = nhem
1105 // 7. straps for the strapped ids
1106 var nstrap: i64 = 0
1107 if kind == 1 { nstrap = 1 }
1108 if kind == 3 { nstrap = 1 }
1109 if kind == 5 { nstrap = 1 }
1110 if kind == 9 { nstrap = 1 }
1111 if nstrap == 1 {
1112 nstrap = 0
1113 var hF: i64 = GMG_ONEPIECE_NECK_F
1114 var hB: i64 = GMG_ONEPIECE_NECK_B
1115 var hw: i64 = gmg_um2u(GMG_STRAP_HALFW_DRESS_UM)
1116 if kind == 1 { hF = G[GMG_C_NIP] + 500; hB = G[GMG_C_NIP] - GMG_BUST_HALF_DM; hw = gmg_um2u(GMG_STRAP_HALFW_BIKINI_UM) }
1117 let ends: *i64 = sys_mmap(4 * 8) as *i64
1118 let nL: i64 = gmg_strap(G, mat, 0 - 1, hF, hB, hw, ends)
1119 if nL < 0 { return GMG_ERR_CAP }
1120 if nL > 0 { nstrap = nstrap + 1; stat[GMG_S_SLF] = ends[0]; stat[GMG_S_SLB] = ends[1] }
1121 let nR: i64 = gmg_strap(G, mat, 1, hF, hB, hw, ends)
1122 if nR < 0 { return GMG_ERR_CAP }
1123 if nR > 0 { nstrap = nstrap + 1; stat[GMG_S_SRF] = ends[0]; stat[GMG_S_SRB] = ends[1] }
1124 }
1125 stat[GMG_S_STRAPS] = nstrap
1126 // 8. clearance: shell vertices vs every covered body vertex (the gate's tooth: none inside 0.9 t);
1127 // the reported minimum is a FLOOR capped at 4 t (the search stops there)
1128 let bar: i64 = t * GMG_CLEAR_PERMIL / 1000
1129 let rep: i64 = t * GMG_CLEAR_REPORT_MULT
1130 var within: i64 = 0
1131 var minc: i64 = rep * rep
1132 var i3: i64 = 0
1133 while i3 < nshell {
1134 let o: i64 = i3 * GMG_VSTRIDE
1135 let x: i64 = ov[o]
1136 let y: i64 = ov[o + 1]
1137 let z: i64 = ov[o + 2]
1138 var best: i64 = rep * rep
1139 var ci: i64 = 0
1140 while ci < ncand {
1141 let qv: i64 = cand[ci]
1142 let dz: i64 = pos[qv * 3 + 2] - z
1143 if gmg_abs(dz) < rep {
1144 let dx: i64 = pos[qv * 3] - x
1145 if gmg_abs(dx) < rep {
1146 let dy: i64 = pos[qv * 3 + 1] - y
1147 if gmg_abs(dy) < rep {
1148 let dd: i64 = dx * dx + dy * dy + dz * dz
1149 if dd < best { best = dd }
1150 }
1151 }
1152 }
1153 ci = ci + 1
1154 }
1155 if best < bar * bar { within = within + 1 }
1156 if best < minc { minc = best }
1157 i3 = i3 + 1
1158 }
1159 stat[GMG_S_WITHIN] = within
1160 stat[GMG_S_MINCLEAR] = gmg_isqrt(minc)
1161 // 9. boundary topology: closed hems = every boundary vertex has exactly two boundary edges and the
1162 // boundary graph's components are all cycles
1163 let badj: *i64 = G[GMG_C_BADJ] as *i64
1164 let bdeg: *i64 = G[GMG_C_BDEG] as *i64
1165 let stack: *i64 = G[GMG_C_STACK] as *i64
1166 let seen: *u8 = G[GMG_C_SEEN] as *u8
1167 v = 0
1168 while v < nv { bdeg[v] = 0; seen[v] = 0 as u8; v = v + 1 }
1169 var over: i64 = 0
1170 v = 0
1171 while v < nv {
1172 var k: i64 = 0
1173 while k < deg[v] {
1174 if nbc[v * GMG_NBR_CAP + k] == 1 {
1175 let w: i64 = nbr[v * GMG_NBR_CAP + k]
1176 if bdeg[v] < GMG_BADJ_CAP { badj[v * GMG_BADJ_CAP + bdeg[v]] = w } else { over = over + 1 }
1177 bdeg[v] = bdeg[v] + 1
1178 if bdeg[w] < GMG_BADJ_CAP { badj[w * GMG_BADJ_CAP + bdeg[w]] = v } else { over = over + 1 }
1179 bdeg[w] = bdeg[w] + 1
1180 }
1181 k = k + 1
1182 }
1183 v = v + 1
1184 }
1185 var loops: i64 = 0
1186 var openc: i64 = 0
1187 v = 0
1188 while v < nv {
1189 if bdeg[v] > 0 { if seen[v] == (0 as u8) {
1190 loops = loops + 1
1191 var odd: i64 = 0
1192 var sp: i64 = 0
1193 stack[sp] = v; sp = sp + 1; seen[v] = 1 as u8
1194 while sp > 0 {
1195 sp = sp - 1
1196 let u: i64 = stack[sp]
1197 if bdeg[u] != 2 { odd = odd + 1 }
1198 var k: i64 = 0
1199 var kl: i64 = bdeg[u]
1200 if kl > GMG_BADJ_CAP { kl = GMG_BADJ_CAP }
1201 while k < kl {
1202 let w: i64 = badj[u * GMG_BADJ_CAP + k]
1203 if seen[w] == (0 as u8) { seen[w] = 1 as u8; stack[sp] = w; sp = sp + 1 }
1204 k = k + 1
1205 }
1206 }
1207 if odd > 0 { openc = openc + 1 }
1208 } }
1209 v = v + 1
1210 }
1211 if over > 0 { openc = openc + 1 }
1212 stat[GMG_S_LOOPS] = loops
1213 stat[GMG_S_OPEN] = openc
1214 stat[GMG_S_NV] = G[GMG_C_ONV]
1215 stat[GMG_S_NT] = G[GMG_C_ONT]
1216 return G[GMG_C_ONV]
1217}
1218
1219// gmg_gen_all: every kind into one GARM section buffer (sec, GMG_SEC_CAP words). stats = 9 x GMG_STAT_N.
1220// Returns the section's word count, or a negative refusal (kind named on stdout).
1221func gmg_gen_all(G: *i64, sec: *i64, stats: *i64) -> i64 {
1222 let nk: i64 = GMG_KINDS
1223 var p: i64 = GMG_HDR + nk * GMG_DIRSTRIDE
1224 sec[0] = GMG_GARM_VER
1225 sec[1] = nk
1226 sec[2] = GMG_VSTRIDE
1227 sec[3] = GMG_TSTRIDE
1228 sec[4] = G[GMG_C_UPMK]
1229 sec[5] = GMG_DIRSTRIDE
1230 sec[6] = 0
1231 sec[7] = 0
1232 var k: i64 = 1
1233 while k <= nk {
1234 let st: *i64 = ((stats as i64) + (k - 1) * GMG_STAT_N * 8) as *i64
1235 let r: i64 = gmg_build(G, k, st)
1236 if r < 0 { gmg_puts("GMG-REFUSED kind=" as *u8); gmg_pn(k); gmg_puts(" build refused rc=" as *u8); gmg_pn(r); gmg_puts("\n" as *u8); return r }
1237 if r == 0 { gmg_puts("GMG-REFUSED kind=" as *u8); gmg_pn(k); gmg_puts(" selects no triangle (an id with no mesh would silently stay paint)\n" as *u8); return GMG_ERR_EMPTY }
1238 let onv: i64 = G[GMG_C_ONV]
1239 let ont: i64 = G[GMG_C_ONT]
1240 if p + onv * GMG_VSTRIDE + ont * GMG_TSTRIDE > GMG_SEC_CAP { gmg_puts("GMG-REFUSED section cap\n" as *u8); return GMG_ERR_CAP }
1241 let d: i64 = GMG_HDR + (k - 1) * GMG_DIRSTRIDE
1242 sec[d] = k
1243 sec[d + 1] = onv
1244 sec[d + 2] = ont
1245 sec[d + 3] = p
1246 let ov: *i64 = G[GMG_C_OV] as *i64
1247 var i: i64 = 0
1248 while i < onv * GMG_VSTRIDE { sec[p + i] = ov[i]; i = i + 1 }
1249 p = p + onv * GMG_VSTRIDE
1250 sec[d + 4] = p
1251 let ot: *i64 = G[GMG_C_OT] as *i64
1252 i = 0
1253 while i < ont * GMG_TSTRIDE { sec[p + i] = ot[i]; i = i + 1 }
1254 p = p + ont * GMG_TSTRIDE
1255 sec[d + 5] = st[GMG_S_T]
1256 sec[d + 6] = st[GMG_S_EASE]
1257 sec[d + 7] = st[GMG_S_CLASS]
1258 st[GMG_S_VOFF] = sec[d + 3]
1259 st[GMG_S_TOFF] = sec[d + 4]
1260 k = k + 1
1261 }
1262 sec[6] = p
1263 return p
1264}
1265
1266// one stats line per kind -- the receipt the program prints and the gate re-derives in-process
1267func gmg_print_stat(st: *i64) -> i64 {
1268 gmg_puts("GARM kind=" as *u8); gmg_pn(st[GMG_S_ID])
1269 gmg_puts(" class=" as *u8); gmg_pn(st[GMG_S_CLASS])
1270 gmg_puts(" verts=" as *u8); gmg_pn(st[GMG_S_NV])
1271 gmg_puts(" tris=" as *u8); gmg_pn(st[GMG_S_NT])
1272 gmg_puts(" shell=" as *u8); gmg_pn(st[GMG_S_SHELL])
1273 gmg_puts(" sel=" as *u8); gmg_pn(st[GMG_S_SEL])
1274 gmg_puts(" hem_quads=" as *u8); gmg_pn(st[GMG_S_HEM])
1275 gmg_puts(" boundary_edges=" as *u8); gmg_pn(st[GMG_S_BEDGES])
1276 gmg_puts(" loops=" as *u8); gmg_pn(st[GMG_S_LOOPS])
1277 gmg_puts(" open_chains=" as *u8); gmg_pn(st[GMG_S_OPEN])
1278 gmg_puts(" pushed=" as *u8); gmg_pn(st[GMG_S_PUSHED])
1279 gmg_puts(" straps=" as *u8); gmg_pn(st[GMG_S_STRAPS])
1280 gmg_puts(" t_units=" as *u8); gmg_pn(st[GMG_S_T])
1281 gmg_puts(" hem_units=" as *u8); gmg_pn(st[GMG_S_TH])
1282 gmg_puts(" ease_units=" as *u8); gmg_pn(st[GMG_S_EASE])
1283 gmg_puts(" within_09t=" as *u8); gmg_pn(st[GMG_S_WITHIN])
1284 gmg_puts(" minclear_floor=" as *u8); gmg_pn(st[GMG_S_MINCLEAR])
1285 gmg_puts(" strap_ends_dm=" as *u8); gmg_pn(st[GMG_S_SLF]); gmg_puts("/" as *u8); gmg_pn(st[GMG_S_SLB]); gmg_puts("/" as *u8); gmg_pn(st[GMG_S_SRF]); gmg_puts("/" as *u8); gmg_pn(st[GMG_S_SRB])
1286 gmg_puts(" nbr_overflow=" as *u8); gmg_pn(st[GMG_S_NBROVER])
1287 gmg_puts(" pinch_passes=" as *u8); gmg_pn(st[GMG_S_PINCHPASS])
1288 gmg_puts("\n" as *u8)
1289 return 0
1290}
1291
1292// gmg_write_nxa: copy every section of the input and append GARM (TOC rebuilt, checksums re-folded)
1293func gmg_write_nxa(b: *u8, flen: i64, sec: *i64, nwords: i64, outpath: *u8) -> i64 {
1294 let h: *i64 = b as *i64
1295 let ons: i64 = h[2]
1296 let otoc: *i64 = ((b as i64) + 32) as *i64
1297 // an input already carrying GARM is REPLACED, never doubled (idempotent re-runs)
1298 let gtag: i64 = nxa_tag4("GARM" as *u8)
1299 var keep: i64 = 0
1300 var ti: i64 = 0
1301 while ti < ons { if otoc[ti * 4] != gtag { keep = keep + 1 } ti = ti + 1 }
1302 let ns2: i64 = keep + 1
1303 let hdr: *i64 = sys_mmap(64) as *i64
1304 let toc: *i64 = sys_mmap(ns2 * 32 + 64) as *i64
1305 var o: i64 = 32 + ns2 * 32
1306 var w: i64 = 0
1307 ti = 0
1308 while ti < ons {
1309 if otoc[ti * 4] != gtag {
1310 toc[w * 4] = otoc[ti * 4]
1311 toc[w * 4 + 1] = o
1312 toc[w * 4 + 2] = otoc[ti * 4 + 2]
1313 toc[w * 4 + 3] = otoc[ti * 4 + 3]
1314 o = o + otoc[ti * 4 + 2] * 8
1315 w = w + 1
1316 }
1317 ti = ti + 1
1318 }
1319 toc[w * 4] = gtag
1320 toc[w * 4 + 1] = o
1321 toc[w * 4 + 2] = nwords
1322 toc[w * 4 + 3] = nxa_check2(1, sec, nwords)
1323 hdr[0] = nxa_magic()
1324 hdr[1] = NXA_VER
1325 hdr[2] = ns2
1326 hdr[3] = nxa_check2(1, toc, ns2 * 4)
1327 let fd: i64 = sys_openat_wr(outpath, MODE_0644)
1328 if fd < 0 { return 0 - 9 }
1329 sys_write(fd, hdr as *u8, 32)
1330 sys_write(fd, toc as *u8, ns2 * 32)
1331 ti = 0
1332 while ti < ons {
1333 if otoc[ti * 4] != gtag { sys_write(fd, ((b as i64) + otoc[ti * 4 + 1]) as *u8, otoc[ti * 4 + 2] * 8) }
1334 ti = ti + 1
1335 }
1336 sys_write(fd, sec as *u8, nwords * 8)
1337 sys_close(fd)
1338 return 0
1339}
1340
1341// Additive boundary contract v1: clip one source triangle by its vertex scalar samples.
1342// Output is at most four [bary0 numerator,bary1 numerator,bary2 numerator,denominator]
1343// records in original winding. Ratios are exact integers: no weight truncation or coordinate rounding.
1344// A consumer may interpolate already-deformed source vertices, preserving the current triangle surface;
1345// interpolating joint weights and then dual-quaternion skinning is NOT equivalent and is not implied.
1346// Material, source triangle identity and garment offset remain explicit caller-owned attributes.
1347const GMG_CLIP_RECORD_WORDS:i64=4
1348const GMG_CLIP_CAP_WORDS:i64=16
1349const GMG_CLIP_DISTANCE_MAX:i64=2147483647
1350const GMG_CLIP_E_INPUT:i64=0-30
1351const GMG_CLIP_E_CAP:i64=0-31
1352func gmg_clip_distance(a:i64,b:i64,c:i64,k:i64)->i64 {
1353 if k==0{return a};if k==1{return b};return c
1354}
1355func gmg_clip_vertex(out:*i64,n:i64,k:i64)->i64 {
1356 var i:i64=0;while i<3{out[n*4+i]=0;i=i+1};out[n*4+k]=1;out[n*4+3]=1;return n+1
1357}
1358func gmg_clip_crossing(out:*i64,n:i64,s:i64,e:i64,ds:i64,de:i64)->i64 {
1359 let a:i64=gmg_abs(ds);let b:i64=gmg_abs(de)
1360 var i:i64=0;while i<3{out[n*4+i]=0;i=i+1}
1361 out[n*4+s]=b;out[n*4+e]=a;out[n*4+3]=a+b;return n+1
1362}
1363func gmg_clip_triangle_bary_v1(distance:*i64,out:*i64,cap_words:i64)->i64 {
1364 if (distance as i64)==0||(out as i64)==0{return GMG_CLIP_E_INPUT}
1365 if cap_words<GMG_CLIP_CAP_WORDS{return GMG_CLIP_E_CAP}
1366 let a:i64=distance[0];let b:i64=distance[1];let c:i64=distance[2]
1367 if a<0-GMG_CLIP_DISTANCE_MAX||a>GMG_CLIP_DISTANCE_MAX{return GMG_CLIP_E_INPUT}
1368 if b<0-GMG_CLIP_DISTANCE_MAX||b>GMG_CLIP_DISTANCE_MAX{return GMG_CLIP_E_INPUT}
1369 if c<0-GMG_CLIP_DISTANCE_MAX||c>GMG_CLIP_DISTANCE_MAX{return GMG_CLIP_E_INPUT}
1370 var n:i64=0;var e:i64=0
1371 while e<3 {
1372 let s:i64=(e+2)%3;let ds:i64=gmg_clip_distance(a,b,c,s);let de:i64=gmg_clip_distance(a,b,c,e)
1373 if ds<0&&de>0{n=gmg_clip_crossing(out,n,s,e,ds,de)}
1374 if de>=0{n=gmg_clip_vertex(out,n,e)}
1375 if ds>0&&de<0{n=gmg_clip_crossing(out,n,s,e,ds,de)}
1376 e=e+1
1377 }
1378 if n<3{return 0}
1379 return n
1380}
1381
1382// Repeated halfspace clipping composes ORIGINAL source-triangle barycentrics.
1383// Caller supplies disjoint input/output/scratch buffers; refusals leave output unchanged.
1384// Checked i64 arithmetic refuses unrepresentable exact ratios instead of rounding provenance.
1385const GMG_CLIP_E_RANGE:i64=0-32
1386const GMG_CLIP_I64_MAX:i64=9223372036854775807
1387func gmg_clip_product(a:i64,b:i64)->i64 {
1388 if a<0||b<0{return -1};if b>0{if a>GMG_CLIP_I64_MAX/b{return -1}};return a*b
1389}
1390func gmg_clip_gcd(a:i64,b:i64)->i64 {var x:i64=a;var y:i64=b;while y>0{let z:i64=x%y;x=y;y=z};return x}
1391func gmg_clip_scalar(d:*i64,r:*i64,result:*i64)->i64 {
1392 let den:i64=r[3];if den<=0{return GMG_CLIP_E_INPUT}
1393 var sum:i64=0;var v:i64=0;var k:i64=0
1394 while k<3{
1395 let n:i64=r[k];if n<0||n>den-sum{return GMG_CLIP_E_INPUT};sum=sum+n
1396 if d[k]<0-GMG_CLIP_DISTANCE_MAX||d[k]>GMG_CLIP_DISTANCE_MAX{return GMG_CLIP_E_INPUT}
1397 let p:i64=gmg_clip_product(n,gmg_abs(d[k]));if p<0{return GMG_CLIP_E_RANGE}
1398 if d[k]>=0 {if v>GMG_CLIP_I64_MAX-p{return GMG_CLIP_E_RANGE};v=v+p}
1399 else{if v<0-GMG_CLIP_I64_MAX+p{return GMG_CLIP_E_RANGE};v=v-p}
1400 k=k+1
1401 }
1402 if sum!=den{return GMG_CLIP_E_INPUT};result[0]=v;return 0
1403}
1404func gmg_clip_polygon_bary_v1(d:*i64,input:*i64,n:i64,out:*i64,scratch:*i64,cap_words:i64)->i64 {
1405 if n<3||n>7{return GMG_CLIP_E_INPUT}
1406 if (d as i64)==0||(input as i64)==0||(out as i64)==0||(scratch as i64)==0{return GMG_CLIP_E_INPUT}
1407 if input==out||input==scratch||out==scratch{return GMG_CLIP_E_INPUT}
1408 if cap_words<(n+1)*4+2{return GMG_CLIP_E_CAP}
1409 var count:i64=0;var e:i64=0
1410 // Scratch capacity includes two named scalar workspace words after polygon capacity.
1411 let scalar:*i64=((scratch as i64)+(cap_words-2)*8) as *i64
1412 while e<n{
1413 let s:i64=(e+n-1)%n;let a:*i64=((input as i64)+s*32) as *i64;let b:*i64=((input as i64)+e*32) as *i64
1414 let ra:i64=gmg_clip_scalar(d,a,scalar);if ra<0{return ra}
1415 let rb:i64=gmg_clip_scalar(d,b,((scalar as i64)+8) as *i64);if rb<0{return rb}
1416 let ds:i64=scalar[0];let de:i64=scalar[1]
1417 if (ds<0&&de>0)||(ds>0&&de<0){
1418 if count>=n+1{return GMG_CLIP_E_CAP}
1419 let ad:i64=gmg_abs(ds);let bd:i64=gmg_abs(de);var div:i64=0;var k:i64=0
1420 while k<4{
1421 let x:i64=gmg_clip_product(a[k],bd);let y:i64=gmg_clip_product(b[k],ad)
1422 if x<0||y<0{return GMG_CLIP_E_RANGE};if x>GMG_CLIP_I64_MAX-y{return GMG_CLIP_E_RANGE}
1423 let v:i64=x+y;scratch[count*4+k]=v;div=gmg_clip_gcd(div,v);k=k+1
1424 }
1425 if div<=0{return GMG_CLIP_E_INPUT};k=0;while k<4{scratch[count*4+k]=scratch[count*4+k]/div;k=k+1};count=count+1
1426 }
1427 if de>=0{if count>=n+1{return GMG_CLIP_E_CAP};var k:i64=0;while k<4{scratch[count*4+k]=b[k];k=k+1};count=count+1}
1428 e=e+1
1429 }
1430 if count<3{return 0};var k:i64=0;while k<count*4{out[k]=scratch[k];k=k+1};return count
1431}
1432
1433// Twice polygon area in the canonical source triangle's (bary1,bary2) plane.
1434// Exact numerator/denominator; positive winding and <=1 means no inverted/expanded source coverage.
1435// Input records are already validated by the clipping boundary. Large common denominators refuse.
1436func gmg_clip_area_ratio_v1(poly:*i64,n:i64,result:*i64)->i64{
1437 if n<3||n>7{return GMG_CLIP_E_INPUT};var den:i64=1;var i:i64=0
1438 while i<n{if poly[i*4+3]<=0{return GMG_CLIP_E_INPUT};den=gmg_clip_product(den,poly[i*4+3]);if den<0{return GMG_CLIP_E_RANGE};i=i+1}
1439 var sum:i64=0;i=0
1440 while i<n{let j:i64=(i+1)%n;let a:i64=gmg_clip_product(poly[i*4+1],poly[j*4+2]);let b:i64=gmg_clip_product(poly[i*4+2],poly[j*4+1]);if a<0||b<0{return GMG_CLIP_E_RANGE}
1441 let pair:i64=gmg_clip_product(poly[i*4+3],poly[j*4+3]);if pair<=0{return GMG_CLIP_E_RANGE}
1442 let term:i64=gmg_clip_product(gmg_abs(a-b),den/pair);if term<0{return GMG_CLIP_E_RANGE}
1443 if a>=b{if sum>GMG_CLIP_I64_MAX-term{return GMG_CLIP_E_RANGE};sum=sum+term}
1444 else{if sum<0-GMG_CLIP_I64_MAX+term{return GMG_CLIP_E_RANGE};sum=sum-term};i=i+1
1445 }
1446 let div:i64=gmg_clip_gcd(gmg_abs(sum),den);result[0]=sum/div;result[1]=den/div;return 0
1447}
1448
1449// GAT1 v1: additive source-surface attachments; legacy GARM is unchanged.
1450// Header 12 words: version,record_count,triangle_count,record_stride,VERT_check,TRIS_check,
1451// source_nv,source_nt,kind,total_words,triangle_stride,reserved.
1452// Record 8 words: source_face,bary0,bary1,bary2,denominator,normal_offset_units,material,flags.
1453// Triangle 3 words: attachment record indices. Coefficients remain exact i64 on disk.
1454const GMG_ATTACH_HDR:i64=12
1455const GMG_ATTACH_REC:i64=8
1456const GMG_ATTACH_COORD_MAX:i64=16777216
1457const GMG_ATTACH_E_SOURCE:i64=-40
1458const GMG_ATTACH_E_SHAPE:i64=-41
1459const GMG_ATTACH_E_GEOMETRY:i64=-42
1460// Exact integer coordinates and products of differences fit i64 under this boundary.
1461func gmg_attach_source_v1(b:*u8,n:i64)->i64{
1462 if (b as i64)==0{return GMG_ATTACH_E_SOURCE}
1463 let va:i64=nxa_counted_section(b,n,nxa_tag4("VERT"),3);let ta:i64=nxa_counted_section(b,n,nxa_tag4("TRIS"),3)
1464 if va<0||ta<0{return GMG_ATTACH_E_SOURCE};let h:*i64=b as *i64;let nv:i64=h[va];let nt:i64=h[ta]
1465 if nv<3||nt<1{return GMG_ATTACH_E_SOURCE};var i:i64=0
1466 while i<nv*3{let p:i64=h[va+1+i];if p<0-GMG_ATTACH_COORD_MAX||p>GMG_ATTACH_COORD_MAX{return GMG_ATTACH_E_SOURCE};i=i+1}
1467 i=0;while i<nt*3{let v:i64=h[ta+1+i];if v<0||v>=nv{return GMG_ATTACH_E_SOURCE};i=i+1};return 0
1468}
1469func gmg_attach_face_v1(pos:*i64,idx:*i64,face:i64)->i64{
1470 let a:i64=idx[face*3]*3;let b:i64=idx[face*3+1]*3;let c:i64=idx[face*3+2]*3
1471 let x:i64=pos[b]-pos[a];let y:i64=pos[b+1]-pos[a+1];let z:i64=pos[b+2]-pos[a+2]
1472 let u:i64=pos[c]-pos[a];let v:i64=pos[c+1]-pos[a+1];let w:i64=pos[c+2]-pos[a+2]
1473 if y*w-z*v==0&&z*u-x*w==0&&x*v-y*u==0{return GMG_ATTACH_E_GEOMETRY};return 0
1474}
1475// Validate before consumer upload. Native NXA checks are integrity checks, not cryptographic authentication.
1476// Caller workspace: 14 i64 words, disjoint from section/source; no output mesh or device mutation here.
1477func gmg_attach_validate_v1(b:*u8,n:i64,s:*i64,nw:i64,work:*i64,work_words:i64)->i64{
1478 if (s as i64)==0||(work as i64)==0||nw<GMG_ATTACH_HDR||work_words<14{return GMG_ATTACH_E_SHAPE}
1479 let sr:i64=gmg_attach_source_v1(b,n);if sr<0{return sr}
1480 if s[0]!=1||s[3]!=GMG_ATTACH_REC||s[10]!=3||s[11]!=0||s[9]!=nw{return GMG_ATTACH_E_SHAPE}
1481 let nr:i64=s[1];let nt:i64=s[2];if nr<3||nt<1||nr>(nw-GMG_ATTACH_HDR)/GMG_ATTACH_REC{return GMG_ATTACH_E_SHAPE}
1482 let to:i64=GMG_ATTACH_HDR+nr*GMG_ATTACH_REC;if (nw-to)%3!=0||nt!=(nw-to)/3{return GMG_ATTACH_E_SHAPE}
1483 if s[8]<1||s[8]>GMG_KINDS{return GMG_ATTACH_E_SHAPE}
1484 let ve:i64=nxa_section_entry(b,n,nxa_tag4("VERT"));let te:i64=nxa_section_entry(b,n,nxa_tag4("TRIS"));let h:*i64=b as *i64
1485 let va:i64=h[ve+1]/8;let ta:i64=h[te+1]/8
1486 if s[4]!=h[ve+3]||s[5]!=h[te+3]||s[6]!=h[va]||s[7]!=h[ta]{return GMG_ATTACH_E_SOURCE}
1487 let pos:*i64=((b as i64)+(va+1)*8) as *i64;let idx:*i64=((b as i64)+(ta+1)*8) as *i64
1488 var i:i64=0;while i<nr{let r:i64=GMG_ATTACH_HDR+i*GMG_ATTACH_REC;let face:i64=s[r];let den:i64=s[r+4]
1489 if face<0||face>=s[7]||den<=0{return GMG_ATTACH_E_SHAPE}
1490 var sum:i64=0;var k:i64=1;while k<4{let a:i64=s[r+k];if a<0||a>den-sum{return GMG_ATTACH_E_SHAPE};sum=sum+a;k=k+1}
1491 if sum!=den||s[r+5]<0||s[r+5]>GMG_ATTACH_COORD_MAX||s[r+6]!=s[8]||s[r+7]!=0{return GMG_ATTACH_E_SHAPE}
1492 if gmg_attach_face_v1(pos,idx,face)<0{return GMG_ATTACH_E_GEOMETRY};i=i+1
1493 }
1494 i=0;while i<nt{var face:i64=-1;var k:i64=0
1495 while k<3{let v:i64=s[to+i*3+k];if v<0||v>=nr{return GMG_ATTACH_E_SHAPE};let r:i64=GMG_ATTACH_HDR+v*GMG_ATTACH_REC
1496 if k==0{face=s[r]}else{if face!=s[r]{return GMG_ATTACH_E_GEOMETRY}}
1497 var j:i64=0;while j<4{work[k*4+j]=s[r+1+j];j=j+1};k=k+1
1498 }
1499 let area:*i64=((work as i64)+96) as *i64;let ar:i64=gmg_clip_area_ratio_v1(work,3,area)
1500 if ar<0{return ar};if area[0]<=0||area[0]>area[1]{return GMG_ATTACH_E_GEOMETRY};i=i+1
1501 };return 0
1502}
1503
1504// Existing one-piece anatomical boundaries, now clipped exactly instead of whole-face selection.
1505// Caller-owned scratch: three 34-word polygons, two distance triples, four material words (112 total).
1506// out==0 measures required words. A nonzero output needs that capacity; only success is consumable.
1507func gmg_attach_onepiece_into_v1(G:*i64,out:*i64,cap:i64,scratch:*i64,sw:i64)->i64{
1508 if (G as i64)==0||(scratch as i64)==0||sw<112{return GMG_ATTACH_E_SHAPE}
1509 let b:*u8=G[GMG_C_B] as *u8;let flen:i64=G[GMG_C_FLEN];let valid:i64=gmg_attach_source_v1(b,flen);if valid<0{return valid}
1510 let a:*i64=scratch;let p:*i64=((scratch as i64)+272) as *i64;let work:*i64=((scratch as i64)+544) as *i64
1511 let d:*i64=((scratch as i64)+816) as *i64;let e:*i64=((scratch as i64)+840) as *i64;let mat:*i64=((scratch as i64)+864) as *i64
1512 gmg_mat(G,3,mat);let off:i64=mat[0];let idx:*i64=G[GMG_C_IDX] as *i64;let hh:*i64=gmg_hh(G)
1513 var totalv:i64=0;var totalt:i64=0;var pass:i64=0;var need:i64=0
1514 while pass<2{var nv:i64=0;var nt:i64=0;var face:i64=0
1515 while face<G[GMG_C_NT]{let v0:i64=idx[face*3];let v1:i64=idx[face*3+1];let v2:i64=idx[face*3+2]
1516 if gmg_has(G,v0,GMG_VF_TOR)==1&&gmg_has(G,v1,GMG_VF_TOR)==1&&gmg_has(G,v2,GMG_VF_TOR)==1{
1517 var k:i64=0;while k<3{let v:i64=idx[face*3+k];d[k]=hh[v]-gmg_hleg(G,v);e[k]=gmg_hneck(G,v)-hh[v];k=k+1}
1518 let n:i64=gmg_clip_triangle_bary_v1(d,a,16);if n<0{return n};var m:i64=0
1519 if n>0{m=gmg_clip_polygon_bary_v1(e,a,n,p,work,34);if m<0{return m}}
1520 if m>0{if gmg_attach_face_v1(G[GMG_C_POS] as *i64,idx,face)<0{return GMG_ATTACH_E_GEOMETRY}
1521 if pass==1{k=0;while k<m{let r:i64=GMG_ATTACH_HDR+(nv+k)*GMG_ATTACH_REC;out[r]=face;var j:i64=0;while j<4{out[r+1+j]=p[k*4+j];j=j+1};out[r+5]=off;out[r+6]=3;out[r+7]=0;k=k+1}
1522 k=1;while k<m-1{let t:i64=GMG_ATTACH_HDR+totalv*GMG_ATTACH_REC+nt*3;out[t]=nv;out[t+1]=nv+k;out[t+2]=nv+k+1;nt=nt+1;k=k+1}
1523 }else{nt=nt+m-2};nv=nv+m
1524 }
1525 };face=face+1
1526 }
1527 if pass==0{totalv=nv;totalt=nt;if totalv<3||totalt<1{return GMG_ATTACH_E_GEOMETRY}
1528 if totalv>(GMG_CLIP_I64_MAX-GMG_ATTACH_HDR)/GMG_ATTACH_REC{return GMG_CLIP_E_RANGE};need=GMG_ATTACH_HDR+totalv*GMG_ATTACH_REC
1529 if totalt>(GMG_CLIP_I64_MAX-need)/3{return GMG_CLIP_E_RANGE};need=need+totalt*3
1530 if (out as i64)==0{return need};if cap<need{return GMG_CLIP_E_CAP}
1531 let h:*i64=b as *i64;let ve:i64=nxa_section_entry(b,flen,nxa_tag4("VERT"));let te:i64=nxa_section_entry(b,flen,nxa_tag4("TRIS"))
1532 out[0]=1;out[1]=totalv;out[2]=totalt;out[3]=GMG_ATTACH_REC;out[4]=h[ve+3];out[5]=h[te+3]
1533 out[6]=G[GMG_C_NV];out[7]=G[GMG_C_NT];out[8]=3;out[9]=need;out[10]=3;out[11]=0
1534 }else{if nv!=totalv||nt!=totalt{return GMG_ATTACH_E_SHAPE}};pass=pass+1
1535 };return need
1536}
1537// Compose an additive section in the existing NXA container; preserve all other payload words.
1538// Disjoint buffers required. No filesystem side effects; caller publishes only validated output.
1539func gmg_attach_compose_into_v1(b:*u8,n:i64,s:*i64,nw:i64,out:*u8,cap:i64,work:*i64,ww:i64)->i64{
1540 let valid:i64=gmg_attach_validate_v1(b,n,s,nw,work,ww);if valid<0{return valid}
1541 if (out as i64)==0||out==b||(out as i64)==(s as i64){return GMG_ATTACH_E_SHAPE}
1542 let h:*i64=b as *i64;let tag:i64=nxa_tag4("GAT1");let ns:i64=h[2];var keep:i64=0;var old:i64=0;var bytes:i64=0;var i:i64=0
1543 while i<ns{let t:i64=4+i*4;let found:i64=nxa_section_entry(b,n,h[t]);if found!=t{return GMG_ATTACH_E_SOURCE}
1544 if h[t]==tag{old=old+1}else{keep=keep+1;if h[t+2]>(GMG_CLIP_I64_MAX-bytes)/8{return GMG_CLIP_E_RANGE};bytes=bytes+h[t+2]*8};i=i+1
1545 };if old>1{return GMG_ATTACH_E_SOURCE}
1546 if nw>GMG_CLIP_I64_MAX/8{return GMG_CLIP_E_RANGE};let head:i64=32+(keep+1)*32
1547 if bytes>GMG_CLIP_I64_MAX-head{return GMG_CLIP_E_RANGE};let base:i64=head+bytes
1548 if nw>(GMG_CLIP_I64_MAX-base)/8{return GMG_CLIP_E_RANGE};let need:i64=base+nw*8;if cap<need{return GMG_CLIP_E_CAP}
1549 let dst:*i64=out as *i64;dst[0]=nxa_magic();dst[1]=NXA_VER;dst[2]=keep+1
1550 var at:i64=head/8;var w:i64=0;i=0;while i<ns{let t:i64=4+i*4;if h[t]!=tag{let dt:i64=4+w*4;dst[dt]=h[t];dst[dt+1]=at*8;dst[dt+2]=h[t+2];dst[dt+3]=h[t+3]
1551 var j:i64=0;while j<h[t+2]{dst[at+j]=h[h[t+1]/8+j];j=j+1};at=at+h[t+2];w=w+1};i=i+1
1552 }
1553 let dt:i64=4+w*4;dst[dt]=tag;dst[dt+1]=at*8;dst[dt+2]=nw;dst[dt+3]=nxa_check2(1,s,nw)
1554 i=0;while i<nw{dst[at+i]=s[i];i=i+1};dst[3]=nxa_check2(1,((out as i64)+32) as *i64,(keep+1)*4);return need
1555}