nx_nxa_strands.nx source
↩ module page · 247 lines · 9565 B
1// nx_nxa_strands.nx -- USC-HairSalon STRAND GROOM INGEST (dev oracle, license-clean for
2// development): reads a .data hairstyle (i32 nstrands; per strand i32 npts + npts x 3 f32,
3// y-up meters), downsamples to <=2200 strands x EXACTLY 8 points each, converts y-up->z-up,
4// scales meters->units, and FITS the root cloud to the character's head BY MEASUREMENT
5// (root-cloud center+width matched to the scalp band) -- tooling, no hand constants. Appends
6// section HSTR: [nstrands] then per strand 24 words (8 points x xyz, mesh units, world space).
7// The vendor/any sim is DISCARDED by design: our viewer's verlet drives these strands.
8// usage: nx_nxa_strands <hairstyle.data> <in.nxa> <out.nxa>
9// rc: 0 ok, 2 usage, 3 data unreadable, 5 bad nxa, 9 io
10// license_tier: ORIGINAL
11import "nx_syscalls.nx"
12import "nx_nxa.nx"
13const K_MAGIC_8388607: i64 = 8388607
14const K_MAGIC_8388608: i64 = 8388608
15const K_MAGIC_200000: i64 = 200000
16const K_MAGIC_2200: i64 = 2200
17const K_MAGIC_100000: i64 = 100000
18const K_MAGIC_4611686018427387903: i64 = 4611686018427387903
19const K_MAGIC_4096: i64 = 4096
20
21func sw9(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
22func sn9(v: i64) -> i64 {
23 let t: *u8 = sys_mmap(32) as *u8
24 var m: i64 = v; var w: i64 = 0
25 if m<0 { t[w]=45 as u8; w=w+1; m=0-m }
26 if m==0 { t[0]=48 as u8; sys_write(1,t,1); return 0 }
27 let d: *u8 = sys_mmap(32) as *u8
28 var k: i64=0
29 while m>0 { d[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
30 var j: i64=0
31 while j<k { t[w]=d[k-1-j]; w=w+1; j=j+1 }
32 sys_write(1,t,w); return 0
33}
34func s_u32(b: *u8, o: i64) -> i64 {
35 return ((b[o] & 0xff) as i64) | (((b[o+1] & 0xff) as i64) << 8)
36 | (((b[o+2] & 0xff) as i64) << 16) | (((b[o+3] & 0xff) as i64) << 24)
37}
38// f32 -> value x scale
39func s_f32(b: *u8, o: i64, scale: i64) -> i64 {
40 let w: i64 = s_u32(b, o)
41 let sign: i64 = (w >> 31) & 1
42 let expo: i64 = (w >> 23) & 255
43 if expo == 0 { return 0 }
44 var mant: i64 = (w & K_MAGIC_8388607) | K_MAGIC_8388608
45 let sh: i64 = expo - 127
46 var v: i64 = 0
47 if sh >= 23 { v = mant * scale * (1 << (sh - 23)) }
48 if sh < 23 { v = (mant * scale) >> (23 - sh) }
49 if sign == 1 { return 0 - v }
50 return v
51}
52
53func main(argc: i64, argv: *i64) -> i64 {
54 if argc < 4 { sw9("usage: nx_nxa_strands <hairstyle.data> <in.nxa> <out.nxa>\n" as *u8); return 2 }
55 let lp: *i64 = sys_mmap(16) as *i64
56 let db: *u8 = sys_map_file(argv[1] as *u8, lp)
57 let dlen: i64 = lp[0]
58 if dlen < 8 { sw9("data unreadable\n" as *u8); return 3 }
59 let nstr: i64 = s_u32(db, 0)
60 sw9("strands_in_file=" as *u8); sn9(nstr); sw9("\n" as *u8)
61 if nstr < 1 { return 3 }
62 if nstr > K_MAGIC_200000 { return 3 }
63 // pass 1: index strand offsets (byte offset of each strand's point block + npts)
64 let soff: *i64 = sys_mmap(nstr*8 + 64) as *i64
65 let snp: *i64 = sys_mmap(nstr*8 + 64) as *i64
66 var off: i64 = 4
67 var si: i64 = 0
68 var valid: i64 = 0
69 while si < nstr {
70 if off + 4 > dlen { si = nstr }
71 if si < nstr {
72 let np: i64 = s_u32(db, off)
73 off = off + 4
74 soff[si] = off
75 snp[si] = np
76 if np >= 2 { valid = valid + 1 }
77 off = off + np*12
78 si = si + 1
79 }
80 }
81 sw9("valid_strands=" as *u8); sn9(valid); sw9("\n" as *u8)
82 if valid < 10 { sw9("too few strands\n" as *u8); return 3 }
83 // choose <=2200 strands evenly among valid ones; resample each to 8 points; convert
84 // y-up meters -> z-up units: (x, y, z) -> (x, -z, y), meters x100000 = 10um units
85 var take: i64 = valid
86 if take > K_MAGIC_2200 { take = K_MAGIC_2200 }
87 let step: i64 = valid*1000/take
88 let sp2: *i64 = sys_mmap(take*24*8 + 64) as *i64
89 var got: i64 = 0
90 var seen: i64 = 0
91 var acc: i64 = 0
92 var s2: i64 = 0
93 while s2 < nstr {
94 if snp[s2] >= 2 {
95 seen = seen + 1
96 if seen*1000 >= acc + step { if got < take {
97 acc = acc + step
98 let np: i64 = snp[s2]
99 var k: i64 = 0
100 while k < 8 {
101 var idx: i64 = k*(np-1)/7
102 if idx >= np { idx = np - 1 }
103 let po: i64 = soff[s2] + idx*12
104 let mx: i64 = s_f32(db, po, K_MAGIC_100000)
105 let my: i64 = s_f32(db, po+4, K_MAGIC_100000)
106 let mz: i64 = s_f32(db, po+8, K_MAGIC_100000)
107 sp2[got*24 + k*3] = mx
108 sp2[got*24 + k*3 + 1] = 0 - mz
109 sp2[got*24 + k*3 + 2] = my
110 k = k + 1
111 }
112 got = got + 1
113 } }
114 }
115 s2 = s2 + 1
116 }
117 sw9("took=" as *u8); sn9(got); sw9("\n" as *u8)
118 // measure root cloud (point 0 of each strand)
119 var rminx: i64 = K_MAGIC_4611686018427387903
120 var rmaxx: i64 = 0 - K_MAGIC_4611686018427387903
121 var rminy: i64 = K_MAGIC_4611686018427387903
122 var rmaxy: i64 = 0 - K_MAGIC_4611686018427387903
123 var rminz: i64 = K_MAGIC_4611686018427387903
124 var rmaxz: i64 = 0 - K_MAGIC_4611686018427387903
125 var r0: i64 = 0
126 while r0 < got {
127 let rx: i64 = sp2[r0*24]
128 let ry: i64 = sp2[r0*24+1]
129 let rz: i64 = sp2[r0*24+2]
130 if rx < rminx { rminx = rx }
131 if rx > rmaxx { rmaxx = rx }
132 if ry < rminy { rminy = ry }
133 if ry > rmaxy { rmaxy = ry }
134 if rz < rminz { rminz = rz }
135 if rz > rmaxz { rmaxz = rz }
136 r0 = r0 + 1
137 }
138 // measure OUR head from the nxa: up axis = largest extent; scalp = top 10pct verts
139 let lpn: *i64 = sys_mmap(16) as *i64
140 let nb: *u8 = sys_map_file(argv[2] as *u8, lpn)
141 let nlen2: i64 = lpn[0]
142 let vwo: i64 = nxa_find(nb, nlen2, nxa_tag4("VERT" as *u8))
143 if vwo < 0 { sw9("bad nxa\n" as *u8); return 5 }
144 let nh: *i64 = nb as *i64
145 let nv: i64 = nh[vwo]
146 let vx: *i64 = ((nb as i64) + vwo*8 + 8) as *i64
147 var mn0: i64 = K_MAGIC_4611686018427387903
148 var mx0: i64 = 0 - K_MAGIC_4611686018427387903
149 var mn1: i64 = K_MAGIC_4611686018427387903
150 var mx1: i64 = 0 - K_MAGIC_4611686018427387903
151 var mn2: i64 = K_MAGIC_4611686018427387903
152 var mx2: i64 = 0 - K_MAGIC_4611686018427387903
153 var i0: i64 = 0
154 while i0 < nv {
155 let a: i64 = vx[i0*3]
156 let b2: i64 = vx[i0*3+1]
157 let c: i64 = vx[i0*3+2]
158 if a < mn0 { mn0 = a }
159 if a > mx0 { mx0 = a }
160 if b2 < mn1 { mn1 = b2 }
161 if b2 > mx1 { mx1 = b2 }
162 if c < mn2 { mn2 = c }
163 if c > mx2 { mx2 = c }
164 i0 = i0 + 1
165 }
166 // this pipeline's models are z-up (verified); scalp band = top 8pct in z
167 let H: i64 = mx2 - mn2
168 var smnx: i64 = K_MAGIC_4611686018427387903
169 var smxx: i64 = 0 - K_MAGIC_4611686018427387903
170 var smny: i64 = K_MAGIC_4611686018427387903
171 var smxy: i64 = 0 - K_MAGIC_4611686018427387903
172 var smxz: i64 = 0 - K_MAGIC_4611686018427387903
173 var i1: i64 = 0
174 while i1 < nv {
175 if (vx[i1*3+2] - mn2)*1000/H > 920 {
176 if vx[i1*3] < smnx { smnx = vx[i1*3] }
177 if vx[i1*3] > smxx { smxx = vx[i1*3] }
178 if vx[i1*3+1] < smny { smny = vx[i1*3+1] }
179 if vx[i1*3+1] > smxy { smxy = vx[i1*3+1] }
180 if vx[i1*3+2] > smxz { smxz = vx[i1*3+2] }
181 i1 = i1 + 1
182 }
183 if (vx[i1*3+2] - mn2)*1000/H <= 920 { i1 = i1 + 1 }
184 }
185 // fit: scale so root-cloud X width matches scalp X width; center X/Y; crown to scalp top
186 var rw: i64 = rmaxx - rminx
187 if rw < 1 { rw = 1 }
188 var tw: i64 = smxx - smnx
189 let sc: i64 = tw*K_MAGIC_4096/rw
190 let rcx: i64 = (rminx + rmaxx)/2
191 let rcy: i64 = (rminy + rmaxy)/2
192 let tcx: i64 = (smnx + smxx)/2
193 let tcy: i64 = (smny + smxy)/2
194 // root cloud top (max z of roots) -> scalp top
195 let rtz: i64 = rmaxz
196 var p0: i64 = 0
197 while p0 < got*8 {
198 sp2[p0*3] = tcx + (sp2[p0*3] - rcx)*sc/K_MAGIC_4096
199 sp2[p0*3+1] = tcy + (sp2[p0*3+1] - rcy)*sc/K_MAGIC_4096
200 sp2[p0*3+2] = smxz + (sp2[p0*3+2] - rtz)*sc/K_MAGIC_4096
201 p0 = p0 + 1
202 }
203 sw9("fit scale4096=" as *u8); sn9(sc)
204 sw9(" scalp_w=" as *u8); sn9(tw); sw9("\n" as *u8)
205 // append HSTR
206 let ons: i64 = nh[2]
207 let otoc: *i64 = ((nb as i64) + 32) as *i64
208 let ns2: i64 = ons + 1
209 let hdr: *i64 = sys_mmap(64) as *i64
210 let toc: *i64 = sys_mmap(ns2*32 + 64) as *i64
211 var o: i64 = 32 + ns2*32
212 var ti: i64 = 0
213 while ti < ons {
214 toc[ti*4] = otoc[ti*4]
215 toc[ti*4+1] = o
216 toc[ti*4+2] = otoc[ti*4+2]
217 toc[ti*4+3] = otoc[ti*4+3]
218 o = o + otoc[ti*4+2]*8
219 ti = ti + 1
220 }
221 let hsl: i64 = 1 + got*24
222 let hp2: *i64 = sys_mmap(hsl*8 + 64) as *i64
223 hp2[0] = got
224 var c0: i64 = 0
225 while c0 < got*24 { hp2[1 + c0] = sp2[c0]; c0 = c0 + 1 }
226 toc[ti*4] = nxa_tag4("HSTR" as *u8)
227 toc[ti*4+1] = o
228 toc[ti*4+2] = hsl
229 toc[ti*4+3] = nxa_check2(1, hp2, hsl)
230 hdr[0] = nxa_magic()
231 hdr[1] = NXA_VER
232 hdr[2] = ns2
233 hdr[3] = nxa_check2(1, toc, ns2*4)
234 let fd: i64 = sys_openat_wr(argv[3] as *u8, 0x1a4)
235 if fd < 0 { sw9("open out failed\n" as *u8); return 9 }
236 sys_write(fd, hdr as *u8, 32)
237 sys_write(fd, toc as *u8, ns2*32)
238 var ci: i64 = 0
239 while ci < ons {
240 sys_write(fd, ((nb as i64) + otoc[ci*4+1]) as *u8, otoc[ci*4+2]*8)
241 ci = ci + 1
242 }
243 sys_write(fd, hp2 as *u8, hsl*8)
244 sys_close(fd)
245 sw9("NXA+HSTR written strands=" as *u8); sn9(got); sw9("\n" as *u8)
246 return 0
247}