nx_obj_export.nx source
↩ module page · 76 lines · 4245 B
1// nx_obj_export.nx -- sovereign Wavefront OBJ writer for the bestiary meshes (game/DCC reuse: engines + Blender
2// import OBJ+normals far cleaner than the print-only STL). Takes the nx_meshgen verts+tris, computes SMOOTH
3// per-vertex normals (accumulate face normals -> normalize), and emits v / vn / f (with //vn indices), 1-indexed.
4// Buffered (one write). Verts stay in the mesh's fx1024 units (importers rescale). license_tier: ORIGINAL
5import "nx_syscalls.nx"
6import "nx_vecmath.nx"
7import "nx_vnormals_lib.nx"
8
9// (oe_isqrt removed 2026-08-25: it was a one-line wrapper over vm_isqrt whose ONLY caller was the
10// duplicated normalise pass now retired onto nx_vnormals_lib. Leaving it would have added a row to the
11// defined-and-never-called census as a side effect of a consolidation.)
12func oe_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){d[o+i]=s[i]; i=i+1} return o+i }
13func oe_num(d: *u8, o: i64, v: i64) -> i64 {
14 var x: i64 = v
15 if x < 0 { d[o]=45 as u8; o=o+1; x=0-x }
16 if x == 0 { d[o]=48 as u8; return o+1 }
17 let t: *u8 = sys_mmap(24); var k: i64 = 0
18 while x > 0 { t[k]=(48+(x%10)) as u8; x=x/10; k=k+1 }
19 var i: i64 = 0
20 while i < k { d[o+i]=t[k-1-i]; i=i+1 }
21 return o + k
22}
23
24// returns bytes written, or -1. cap of buf handled by caller (size >= nv*70 + nf*48 + 64).
25func write_obj(vbuf: *i64, fbuf: *i64, nv: i64, nf: i64, path: *u8) -> i64 {
26 // RETIRED ONTO nx_vnormals_lib.vn_smooth_area, 2026-08-25. This block WAS a third copy of the
27 // area-weighted per-vertex normal computation -- character for character the same arithmetic as the
28 // former body of nx_gltf_export.gl_vnormals, but INLINE, which is why the estate's nx_retire_onto
29 // organ could not take it: that organ retires a named private FUNCTION onto an owner, and there was
30 // no function here to name. The owner also does the normalise-and-default pass that used to sit in
31 // the emit loop below, so the values written out are final.
32 let nrm: *i64 = sys_mmap(nv * VN_V3 * 8) as *i64
33 vn_smooth_area(vbuf, fbuf, nv, nf, nrm)
34 let cap: i64 = nv * 80 + nf * 52 + 128
35 let buf: *u8 = sys_mmap(cap)
36 var o: i64 = oe_cat(buf, 0, "# nishi_variant OBJ (sovereign nx_cc)\n" as *u8)
37 // vertices
38 var i: i64 = 0
39 while i < nv {
40 o = oe_cat(buf, o, "v " as *u8)
41 o = oe_num(buf, o, vbuf[i*3]); buf[o]=32 as u8; o=o+1
42 o = oe_num(buf, o, vbuf[i*3+1]); buf[o]=32 as u8; o=o+1
43 o = oe_num(buf, o, vbuf[i*3+2]); buf[o]=10 as u8; o=o+1
44 i = i + 1
45 }
46 // normals: smoothed, normalised to VN_SCALE and defaulted to +Y up by vn_smooth_area above. The
47 // normalise-and-default arithmetic that used to sit HERE was the second half of the duplicated copy;
48 // it moved to the owner with the first half, so this loop now only emits.
49 i = 0
50 while i < nv {
51 o = oe_cat(buf, o, "vn " as *u8)
52 o = oe_num(buf, o, nrm[i*VN_V3+VN_X]); buf[o]=32 as u8; o=o+1
53 o = oe_num(buf, o, nrm[i*VN_V3+VN_Y]); buf[o]=32 as u8; o=o+1
54 o = oe_num(buf, o, nrm[i*VN_V3+VN_Z]); buf[o]=10 as u8; o=o+1
55 i = i + 1
56 }
57 // faces (1-indexed; vertex index == normal index)
58 // `t` is DECLARED here now. It used to be declared by the normal-accumulation loop at the top of this
59 // function and merely reassigned here -- so retiring that loop onto vn_smooth_area took this loop's
60 // declaration with it. The compiler caught it; it is recorded because it is the ordinary hazard of
61 // removing code that shared a cursor with code that stays.
62 var t: i64 = 0
63 while t < nf {
64 let a: i64 = fbuf[t*3]+1; let b: i64 = fbuf[t*3+1]+1; let c: i64 = fbuf[t*3+2]+1
65 o = oe_cat(buf, o, "f " as *u8)
66 o = oe_num(buf, o, a); o = oe_cat(buf, o, "//" as *u8); o = oe_num(buf, o, a); buf[o]=32 as u8; o=o+1
67 o = oe_num(buf, o, b); o = oe_cat(buf, o, "//" as *u8); o = oe_num(buf, o, b); buf[o]=32 as u8; o=o+1
68 o = oe_num(buf, o, c); o = oe_cat(buf, o, "//" as *u8); o = oe_num(buf, o, c); buf[o]=10 as u8; o=o+1
69 t = t + 1
70 }
71 let fd: i64 = sys_openat_wr(path, 0x1a4)
72 if fd < 0 { return 0 - 1 }
73 sys_write(fd, buf, o)
74 sys_close(fd)
75 return o
76}