nx_stl_write.nx source
↩ module page · 108 lines · 4742 B
1// nx_stl_write.nx -- SOVEREIGN DCC export: procedural mesh -> binary STL.
2//
3// The creator/writer half (nx_stl is the reader). Emits the exact 1980s binary
4// STL the existing slicer->gcode->klipper pipeline consumes: 80B header + u32
5// n_tris + per-tri 50B (12B normal + 9 f32 verts + 2B attr). Floats via the new
6// f32_from_i64 encoder; bytes via nx_le_write_u32. This is the model->print seam:
7// geometry authored here feeds nx_stl_load_binary -> nx_slice_* -> nx_gcode_* ->
8// nx_klipper_gcode_validator. No dup (nx_stl had no writer). license_tier: ORIGINAL.
9
10import "nx_syscalls.nx"
11import "nx_le.nx"
12import "nx_f32_encode.nx"
13import "nx_mesh.nx"
14
15// write one vertex (x,y,z mm) as 3 little-endian f32 at off; returns off+12
16func sw_put_vert(buf: *u8, off: i64, x: i64, y: i64, z: i64) -> i64 {
17 nx_le_write_u32(buf, off, f32_from_i64(x))
18 nx_le_write_u32(buf, off + 4, f32_from_i64(y))
19 nx_le_write_u32(buf, off + 8, f32_from_i64(z))
20 return off + 12
21}
22
23// write a binary STL of a cube of side S (mm) into buf; returns total bytes (684)
24func nx_stl_write_cube(buf: *u8, S: i64) -> i64 {
25 let cx: *i64 = (sys_mmap(8 * 8)) as *i64
26 let cy: *i64 = (sys_mmap(8 * 8)) as *i64
27 let cz: *i64 = (sys_mmap(8 * 8)) as *i64
28 cx[0] = 0; cy[0] = 0; cz[0] = 0
29 cx[1] = S; cy[1] = 0; cz[1] = 0
30 cx[2] = S; cy[2] = S; cz[2] = 0
31 cx[3] = 0; cy[3] = S; cz[3] = 0
32 cx[4] = 0; cy[4] = 0; cz[4] = S
33 cx[5] = S; cy[5] = 0; cz[5] = S
34 cx[6] = S; cy[6] = S; cz[6] = S
35 cx[7] = 0; cy[7] = S; cz[7] = S
36 let ta: *i64 = (sys_mmap(12 * 8)) as *i64
37 let tb: *i64 = (sys_mmap(12 * 8)) as *i64
38 let tc: *i64 = (sys_mmap(12 * 8)) as *i64
39 ta[0]=0;tb[0]=1;tc[0]=2; ta[1]=0;tb[1]=2;tc[1]=3
40 ta[2]=5;tb[2]=4;tc[2]=7; ta[3]=5;tb[3]=7;tc[3]=6
41 ta[4]=4;tb[4]=0;tc[4]=3; ta[5]=4;tb[5]=3;tc[5]=7
42 ta[6]=1;tb[6]=5;tc[6]=6; ta[7]=1;tb[7]=6;tc[7]=2
43 ta[8]=4;tb[8]=5;tc[8]=1; ta[9]=4;tb[9]=1;tc[9]=0
44 ta[10]=3;tb[10]=2;tc[10]=6; ta[11]=3;tb[11]=6;tc[11]=7
45
46 var i: i64 = 0
47 while i < 80 { buf[i] = 0 as u8; i = i + 1 } // 80-byte header (ignored)
48 nx_le_write_u32(buf, 80, 12) // n_tris
49 var off: i64 = 84
50 var t: i64 = 0
51 while t < 12 {
52 nx_le_write_u32(buf, off, 0) // normal = (0,0,0) f32 (reader ignores)
53 nx_le_write_u32(buf, off + 4, 0)
54 nx_le_write_u32(buf, off + 8, 0)
55 off = off + 12
56 let a: i64 = ta[t]; let b: i64 = tb[t]; let c: i64 = tc[t]
57 off = sw_put_vert(buf, off, cx[a], cy[a], cz[a])
58 off = sw_put_vert(buf, off, cx[b], cy[b], cz[b])
59 off = sw_put_vert(buf, off, cx[c], cy[c], cz[c])
60 nx_le_write_u16(buf, off, 0) // attribute byte count
61 off = off + 2
62 t = t + 1
63 }
64 return off
65}
66
67// ===== GENERAL mesh export: ANY NxMesh -> binary STL =================
68//
69// The cube-only writer above proved the seam; this writes an ARBITRARY
70// authored mesh (the output of the DCC editor / CAD kernel) to the exact
71// binary STL the slicer consumes. Vertices are read from the mesh in its
72// native Q14-mm units and encoded with f32_from_q14 (exact), so sub-mm
73// geometry survives. Triangle corners are emitted unindexed (STL has no
74// shared-vertex table); the existing nx_stl_load_binary re-dedups them on
75// read. buf must hold >= 84 + 50*m.n_tris bytes. Returns bytes written.
76
77// write vertex `vidx` of mesh `m` (Q14 mm) as 3 little-endian f32; off+12
78func sw_put_mesh_vert(buf: *u8, off: i64, m: *NxMesh, vidx: i64) -> i64 {
79 nx_le_write_u32(buf, off, f32_from_q14(nx_mesh_get_vertex_x(m, vidx)))
80 nx_le_write_u32(buf, off + 4, f32_from_q14(nx_mesh_get_vertex_y(m, vidx)))
81 nx_le_write_u32(buf, off + 8, f32_from_q14(nx_mesh_get_vertex_z(m, vidx)))
82 return off + 12
83}
84
85func nx_stl_write_mesh(buf: *u8, m: *NxMesh) -> i64 {
86 var i: i64 = 0
87 while i < 80 { buf[i] = 0 as u8; i = i + 1 } // 80-byte header (ignored)
88 let nt: i64 = m.n_tris
89 nx_le_write_u32(buf, 80, nt) // n_tris
90 var off: i64 = 84
91 var t: i64 = 0
92 while t < nt {
93 nx_le_write_u32(buf, off, 0) // normal = (0,0,0) f32 (reader trusts winding)
94 nx_le_write_u32(buf, off + 4, 0)
95 nx_le_write_u32(buf, off + 8, 0)
96 off = off + 12
97 let a: i64 = m.indices[t * 3 + 0]
98 let b: i64 = m.indices[t * 3 + 1]
99 let c: i64 = m.indices[t * 3 + 2]
100 off = sw_put_mesh_vert(buf, off, m, a)
101 off = sw_put_mesh_vert(buf, off, m, b)
102 off = sw_put_mesh_vert(buf, off, m, c)
103 nx_le_write_u16(buf, off, 0) // attribute byte count
104 off = off + 2
105 t = t + 1
106 }
107 return off
108}