nx_nxa_hair.nx source
↩ module page · 426 lines · 14999 B
1// nx_nxa_hair.nx -- HAIR MESH INGEST (SOTA rung 3): extracts the character's REAL groomed
2// hair asset (FBX Geometry connected to a Hair-named Model; Eyelash excluded) and appends it
3// to the NXA as HVRT/HTRI sections in the SAME mesh space as VERT -- viewers attach it via
4// the head joint's delta matrix. TOOLING not handcraft: candidates are enumerated with names
5// and sizes, the largest hair geometry wins, every step printed.
6// usage: nx_nxa_hair <in.fbx> <in.nxa> <out.nxa>
7// rc: 0 ok, 2 usage, 3 fbx unreadable, 4 no hair mesh found, 5 bad nxa, 9 io
8// license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "nx_zlib_wrap.nx"
11import "nx_nxa.nx"
12const B_MAGIC_2047: i64 = 2047
13const B_MAGIC_1048575: i64 = 1048575
14const B_MAGIC_4503599627370496: i64 = 4503599627370496
15const B_MAGIC_99990: i64 = 99990
16const B_MAGIC_4096: i64 = 4096
17const B_MAGIC_7500: i64 = 7500
18const B_MAGIC_500000: i64 = 500000
19const B_MAGIC_2147483647: i64 = 2147483647
20const B_MAGIC_4294967296: i64 = 4294967296
21
22// G: 0=mcount 1=gcount 2=conncount
23const B_MO: i64 = 16 // model ids x512
24const B_MN: i64 = 528 // model name off x512
25const B_ML: i64 = 1040 // model name len x512
26const B_GI: i64 = 1552 // geometry ids x512
27const B_GV: i64 = 2064 // geometry Vertices meta (off,alen,enc,clen) x512x4
28const B_GP: i64 = 4112 // geometry PolygonVertexIndex meta x512x4
29const B_CONN: i64 = 6160 // conns (src,dst) x100000x2
30const B_WORDS: i64 = 210000
31
32func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
33func hn(v: i64) -> i64 {
34 let t: *u8 = sys_mmap(32) as *u8
35 var m: i64 = v; var w: i64 = 0
36 if m<0 { t[w]=45 as u8; w=w+1; m=0-m }
37 if m==0 { t[0]=48 as u8; sys_write(1,t,1); return 0 }
38 let d: *u8 = sys_mmap(32) as *u8
39 var k: i64=0
40 while m>0 { d[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
41 var j: i64=0
42 while j<k { t[w]=d[k-1-j]; w=w+1; j=j+1 }
43 sys_write(1,t,w); return 0
44}
45func h_u32(b: *u8, o: i64) -> i64 {
46 return ((b[o] & 0xff) as i64) | (((b[o+1] & 0xff) as i64) << 8)
47 | (((b[o+2] & 0xff) as i64) << 16) | (((b[o+3] & 0xff) as i64) << 24)
48}
49func h_u64(b: *u8, o: i64) -> i64 { return h_u32(b, o) | (h_u32(b, o + 4) << 32) }
50func h_f64(b: *u8, o: i64, scale: i64) -> i64 {
51 let lo: i64 = h_u32(b, o)
52 let hi: i64 = h_u32(b, o + 4)
53 let sign: i64 = (hi >> 31) & 1
54 let expo: i64 = (hi >> 20) & B_MAGIC_2047
55 if expo == 0 { return 0 }
56 var mant: i64 = ((hi & B_MAGIC_1048575) << 32) | lo
57 mant = mant | B_MAGIC_4503599627370496
58 let sh: i64 = expo - 1023
59 var v: i64 = 0
60 if sh >= 52 { v = mant * scale * (1 << (sh - 52)) }
61 if sh < 52 {
62 let rs: i64 = 52 - sh
63 if rs > 62 { return 0 }
64 v = (mant * scale) >> rs
65 }
66 if sign == 1 { return 0 - v }
67 return v
68}
69func h_namehas(b: *u8, off: i64, len: i64, pat: *u8) -> i64 {
70 var pl: i64 = 0
71 while pat[pl] != (0 as u8) { pl = pl + 1 }
72 if pl == 0 { return 0 }
73 var i: i64 = 0
74 while i + pl <= len {
75 var k: i64 = 0
76 var hit: i64 = 1
77 while k < pl {
78 if (b[off+i+k] & 0xff) != (pat[k] & 0xff) { hit = 0; k = pl }
79 k = k + 1
80 }
81 if hit == 1 { return 1 }
82 i = i + 1
83 }
84 return 0
85}
86func h_wname(b: *u8, off: i64, len: i64) -> i64 {
87 var n: i64 = 0
88 while n < len {
89 if (b[off+n] & 0xff) == 0 { len = n }
90 if n < len { n = n + 1 }
91 }
92 if n > 40 { n = 40 }
93 sys_write(1, ((b as i64) + off) as *u8, n)
94 return 0
95}
96
97func h_walk(b: *u8, flen: i64, off0: i64, big: i64, G: *i64, gctx: i64) -> i64 {
98 let off: i64 = off0
99 var endo: i64 = 0
100 var nprops: i64 = 0
101 var plen: i64 = 0
102 var nlen: i64 = 0
103 var p: i64 = 0
104 if big == 1 {
105 endo = h_u64(b, off)
106 nprops = h_u64(b, off + 8)
107 plen = h_u64(b, off + 16)
108 nlen = (b[off + 24] & 0xff) as i64
109 p = off + 25
110 }
111 if big == 0 {
112 endo = h_u32(b, off)
113 nprops = h_u32(b, off + 4)
114 plen = h_u32(b, off + 8)
115 nlen = (b[off + 12] & 0xff) as i64
116 p = off + 13
117 }
118 if endo == 0 { return off }
119 if endo > flen { return flen }
120 var kind: i64 = 0
121 if nlen == 5 { if (b[p]&0xff)==77 { if (b[p+4]&0xff)==108 { kind = 1 } } } // Model
122 if nlen == 8 {
123 if (b[p]&0xff)==71 { if (b[p+7]&0xff)==121 { kind = 2 } } // Geometry
124 if (b[p]&0xff)==86 { if (b[p+7]&0xff)==115 { kind = 3 } } // Vertices
125 }
126 if nlen == 18 { if (b[p]&0xff)==80 { if (b[p+17]&0xff)==120 { kind = 4 } } } // PolygonVertexIndex
127 if nlen == 1 { if (b[p]&0xff)==67 { kind = 5 } } // C
128 p = p + nlen
129 let pstart: i64 = p
130 var id1: i64 = 0
131 var lseen: i64 = 0
132 var s1l: i64 = 0
133 var s1o: i64 = 0
134 var sseen: i64 = 0
135 var arro: i64 = 0
136 var arrl: i64 = 0
137 var arre: i64 = 0
138 var arrc: i64 = 0
139 var id2: i64 = 0
140 var pi: i64 = 0
141 var stop: i64 = 0
142 while pi < nprops {
143 if stop == 0 {
144 let t: i64 = (b[p] & 0xff) as i64
145 p = p + 1
146 var adv: i64 = 0 - 1
147 if t == 83 {
148 let l: i64 = h_u32(b, p)
149 sseen = sseen + 1
150 if sseen == 1 { s1o = p + 4; s1l = l }
151 adv = 4 + l
152 }
153 if t == 82 { adv = 4 + h_u32(b, p) }
154 if t == 89 { adv = 2 }
155 if t == 67 { adv = 1 }
156 if t == 73 { adv = 4 }
157 if t == 70 { adv = 4 }
158 if t == 68 { adv = 8 }
159 if t == 76 {
160 lseen = lseen + 1
161 if lseen == 1 { id1 = h_u64(b, p) }
162 if lseen == 2 { id2 = h_u64(b, p) }
163 adv = 8
164 }
165 var isarr: i64 = 0
166 if t == 100 { isarr = 1 }
167 if t == 102 { isarr = 1 }
168 if t == 108 { isarr = 1 }
169 if t == 105 { isarr = 1 }
170 if t == 98 { isarr = 1 }
171 if isarr == 1 {
172 arrl = h_u32(b, p)
173 arre = h_u32(b, p + 4)
174 arrc = h_u32(b, p + 8)
175 arro = p + 12
176 adv = 12 + arrc
177 }
178 if adv < 0 { stop = 1 }
179 if adv >= 0 { p = p + adv }
180 }
181 pi = pi + 1
182 }
183 var childg: i64 = gctx
184 if kind == 1 { if G[0] < 510 {
185 G[B_MO + G[0]] = id1
186 G[B_MN + G[0]] = s1o
187 G[B_ML + G[0]] = s1l
188 G[0] = G[0] + 1
189 } }
190 if kind == 2 { if G[1] < 510 {
191 G[B_GI + G[1]] = id1
192 childg = G[1]
193 G[1] = G[1] + 1
194 } }
195 if kind == 3 { if gctx >= 0 { if G[B_GV + gctx*4 + 1] == 0 {
196 G[B_GV + gctx*4] = arro
197 G[B_GV + gctx*4 + 1] = arrl
198 G[B_GV + gctx*4 + 2] = arre
199 G[B_GV + gctx*4 + 3] = arrc
200 } } }
201 if kind == 4 { if gctx >= 0 { if G[B_GP + gctx*4 + 1] == 0 {
202 G[B_GP + gctx*4] = arro
203 G[B_GP + gctx*4 + 1] = arrl
204 G[B_GP + gctx*4 + 2] = arre
205 G[B_GP + gctx*4 + 3] = arrc
206 } } }
207 if kind == 5 { if G[2] < B_MAGIC_99990 {
208 G[B_CONN + G[2]*2] = id1
209 G[B_CONN + G[2]*2 + 1] = id2
210 G[2] = G[2] + 1
211 } }
212 p = pstart + plen
213 var sent: i64 = 13
214 if big == 1 { sent = 25 }
215 while p < endo - sent {
216 p = h_walk(b, flen, p, big, G, childg)
217 if p >= flen { return flen }
218 }
219 return endo
220}
221
222func h_arr(b: *u8, off: i64, alen: i64, enc: i64, clen: i64, esz: i64) -> *u8 {
223 if enc == 0 { return ((b as i64) + off) as *u8 }
224 let zr: *NxZlibResult = nx_zlib_inflate(((b as i64) + off) as *u8, clen, alen*esz + B_MAGIC_4096)
225 if zr.error_code != 0 { return 0 as *u8 }
226 return zr.output_data
227}
228
229func main(argc: i64, argv: *i64) -> i64 {
230 if argc < 4 { hw("usage: nx_nxa_hair <in.fbx> <in.nxa> <out.nxa>\n" as *u8); return 2 }
231 let lpf: *i64 = sys_mmap(16) as *i64
232 let fb: *u8 = sys_map_file(argv[1] as *u8, lpf)
233 let flen: i64 = lpf[0]
234 if flen < 64 { hw("fbx unreadable\n" as *u8); return 3 }
235 let ver: i64 = h_u32(fb, 23)
236 var big: i64 = 0
237 if ver >= B_MAGIC_7500 { big = 1 }
238 let G: *i64 = sys_mmap(B_WORDS*8) as *i64
239 var pos: i64 = 27
240 var guard: i64 = 0
241 while pos < flen - 200 {
242 if guard > B_MAGIC_500000 { break }
243 let e: i64 = h_walk(fb, flen, pos, big, G, 0 - 1)
244 if e <= pos { break }
245 pos = e
246 guard = guard + 1
247 }
248 let mc: i64 = G[0]
249 let gc: i64 = G[1]
250 let nc: i64 = G[2]
251 hw("models=" as *u8); hn(mc)
252 hw(" geoms=" as *u8); hn(gc)
253 hw(" conns=" as *u8); hn(nc); hw("\n" as *u8)
254 // enumerate EVERY geometry with its connected model name + vert count (tool honesty)
255 var ge0: i64 = 0
256 while ge0 < gc {
257 if G[B_GV + ge0*4 + 1]/3 >= 1000 {
258 hw("GEO " as *u8); hn(ge0)
259 hw(" verts=" as *u8); hn(G[B_GV + ge0*4 + 1]/3)
260 var km: i64 = 0
261 while km < nc {
262 if G[B_CONN + km*2] == G[B_GI + ge0] {
263 var mm0: i64 = 0
264 while mm0 < mc {
265 if G[B_MO + mm0] == G[B_CONN + km*2 + 1] {
266 hw(" model=" as *u8)
267 h_wname(fb, G[B_MN + mm0], G[B_ML + mm0])
268 mm0 = mc
269 km = nc
270 }
271 mm0 = mm0 + 1
272 }
273 }
274 km = km + 1
275 }
276 hw("\n" as *u8)
277 }
278 ge0 = ge0 + 1
279 }
280 // hair models: name contains Hair, not Eyelash; find each one's geometry via C geo->model
281 var best: i64 = 0 - 1
282 var bestn: i64 = 0
283 var m0: i64 = 0
284 while m0 < mc {
285 if h_namehas(fb, G[B_MN + m0], G[B_ML + m0], "Hair" as *u8) == 1 {
286 if h_namehas(fb, G[B_MN + m0], G[B_ML + m0], "Eyelash" as *u8) == 0 {
287 let mid: i64 = G[B_MO + m0]
288 var gi: i64 = 0 - 1
289 var k0: i64 = 0
290 while k0 < nc {
291 if G[B_CONN + k0*2 + 1] == mid {
292 var g0: i64 = 0
293 while g0 < gc {
294 if G[B_GI + g0] == G[B_CONN + k0*2] { gi = g0; g0 = gc; k0 = nc }
295 g0 = g0 + 1
296 }
297 }
298 k0 = k0 + 1
299 }
300 // group node: geometry may hang off a CHILD model (one hop down)
301 if gi < 0 {
302 var k1: i64 = 0
303 while k1 < nc {
304 if G[B_CONN + k1*2 + 1] == mid {
305 let cid: i64 = G[B_CONN + k1*2]
306 var k2: i64 = 0
307 while k2 < nc {
308 if G[B_CONN + k2*2 + 1] == cid {
309 var g2: i64 = 0
310 while g2 < gc {
311 if G[B_GI + g2] == G[B_CONN + k2*2] { gi = g2; g2 = gc; k2 = nc; k1 = nc }
312 g2 = g2 + 1
313 }
314 }
315 k2 = k2 + 1
316 }
317 }
318 k1 = k1 + 1
319 }
320 }
321 hw("HAIR-CAND " as *u8)
322 h_wname(fb, G[B_MN + m0], G[B_ML + m0])
323 hw(" geo=" as *u8); hn(gi)
324 if gi >= 0 {
325 hw(" verts=" as *u8); hn(G[B_GV + gi*4 + 1]/3)
326 if G[B_GV + gi*4 + 1] > bestn { bestn = G[B_GV + gi*4 + 1]; best = gi }
327 }
328 hw("\n" as *u8)
329 }
330 }
331 m0 = m0 + 1
332 }
333 if best < 0 { hw("no hair mesh found\n" as *u8); return 4 }
334 let nvh: i64 = bestn/3
335 let va: *u8 = h_arr(fb, G[B_GV + best*4], bestn, G[B_GV + best*4 + 2], G[B_GV + best*4 + 3], 8)
336 let npi: i64 = G[B_GP + best*4 + 1]
337 let pa: *u8 = h_arr(fb, G[B_GP + best*4], npi, G[B_GP + best*4 + 2], G[B_GP + best*4 + 3], 4)
338 if (va as i64) == 0 { hw("vert inflate failed\n" as *u8); return 4 }
339 if (pa as i64) == 0 { hw("pvi inflate failed\n" as *u8); return 4 }
340 // verts -> units
341 let hv: *i64 = sys_mmap((1 + nvh*3)*8 + 64) as *i64
342 hv[0] = nvh
343 var v0: i64 = 0
344 while v0 < nvh*3 {
345 hv[1 + v0] = h_f64(va, v0*8, 1000)
346 v0 = v0 + 1
347 }
348 // fan-triangulate PVI (negative = last index of polygon, value xor -1)
349 let ht: *i64 = sys_mmap((1 + npi*3)*8 + 64) as *i64
350 var nt: i64 = 0
351 var fanA: i64 = 0 - 1
352 var prev: i64 = 0 - 1
353 var pi0: i64 = 0
354 while pi0 < npi {
355 var ix: i64 = h_u32(pa, pi0*4)
356 if ix > B_MAGIC_2147483647 { ix = ix - B_MAGIC_4294967296 }
357 var last: i64 = 0
358 if ix < 0 { ix = 0 - ix - 1; last = 1 }
359 if fanA < 0 { fanA = ix }
360 if fanA >= 0 { if prev >= 0 { if ix != fanA {
361 if prev != fanA {
362 ht[1 + nt*3] = fanA
363 ht[1 + nt*3 + 1] = prev
364 ht[1 + nt*3 + 2] = ix
365 nt = nt + 1
366 }
367 } } }
368 prev = ix
369 if last == 1 { fanA = 0 - 1; prev = 0 - 1 }
370 pi0 = pi0 + 1
371 }
372 ht[0] = nt
373 hw("hair verts=" as *u8); hn(nvh)
374 hw(" tris=" as *u8); hn(nt); hw("\n" as *u8)
375 // append HVRT/HTRI to the nxa
376 let lpn: *i64 = sys_mmap(16) as *i64
377 let nb: *u8 = sys_map_file(argv[2] as *u8, lpn)
378 let nlen2: i64 = lpn[0]
379 let nh: *i64 = nb as *i64
380 if nh[0] != nxa_magic() { hw("in.nxa bad\n" as *u8); return 5 }
381 let ons: i64 = nh[2]
382 let otoc: *i64 = ((nb as i64) + 32) as *i64
383 let ns2: i64 = ons + 2
384 let hdr: *i64 = sys_mmap(64) as *i64
385 let toc: *i64 = sys_mmap(ns2*32 + 64) as *i64
386 var o: i64 = 32 + ns2*32
387 var ti: i64 = 0
388 while ti < ons {
389 toc[ti*4] = otoc[ti*4]
390 toc[ti*4+1] = o
391 toc[ti*4+2] = otoc[ti*4+2]
392 toc[ti*4+3] = otoc[ti*4+3]
393 o = o + otoc[ti*4+2]*8
394 ti = ti + 1
395 }
396 let hvl: i64 = 1 + nvh*3
397 let htl: i64 = 1 + nt*3
398 toc[ti*4] = nxa_tag4("HVRT" as *u8)
399 toc[ti*4+1] = o
400 toc[ti*4+2] = hvl
401 toc[ti*4+3] = nxa_check2(1, hv, hvl)
402 o = o + hvl*8
403 ti = ti + 1
404 toc[ti*4] = nxa_tag4("HTRI" as *u8)
405 toc[ti*4+1] = o
406 toc[ti*4+2] = htl
407 toc[ti*4+3] = nxa_check2(1, ht, htl)
408 hdr[0] = nxa_magic()
409 hdr[1] = NXA_VER
410 hdr[2] = ns2
411 hdr[3] = nxa_check2(1, toc, ns2*4)
412 let fd: i64 = sys_openat_wr(argv[3] as *u8, 0x1a4)
413 if fd < 0 { hw("open out failed\n" as *u8); return 9 }
414 sys_write(fd, hdr as *u8, 32)
415 sys_write(fd, toc as *u8, ns2*32)
416 var ci: i64 = 0
417 while ci < ons {
418 sys_write(fd, ((nb as i64) + otoc[ci*4+1]) as *u8, otoc[ci*4+2]*8)
419 ci = ci + 1
420 }
421 sys_write(fd, hv as *u8, hvl*8)
422 sys_write(fd, ht as *u8, htl*8)
423 sys_close(fd)
424 hw("NXA+HAIR written sections=" as *u8); hn(ns2); hw("\n" as *u8)
425 return 0
426}