code wiki / (root) / nx_nxa_strands.nx

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}