nx_meshgen.nx source
↩ module page · 176 lines · 9304 B
1// nx_meshgen.nx -- sovereign SURFACE NETS: turn ANY signed-distance FIELD into a watertight triangle MESH
2// (generator-path R1, operator 2026-07-04: the ecosystem must EMIT geometry, not hand-craft). This is the
3// BRIDGE: the pioneer SDF (sdf_body/sdf_head) -- or a t2mesh/i2mesh-generated field -- becomes real emitted
4// verts+faces. Dual method (naive surface nets): (1) each grid CUBE straddling the 0-surface gets ONE vertex
5// at the mean of its edge zero-crossings; (2) each grid EDGE that crosses the surface emits a QUAD (2 tris)
6// linking the 4 cubes around it. Integer fx1024; deterministic. license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_sdfrender.nx"
9
10const MG_N: i64 = 56 // grid resolution (cells per axis)
11const MG_GR: i64 = 1500 // world half-range fx1024 (body fits [-1500,1500])
12const MG_MAXV: i64 = 200000
13const MG_MAXF: i64 = 400000
14
15func mg_step() -> i64 { return 2 * MG_GR / MG_N }
16// field sample index over the (N+1)^3 corner grid
17func mg_fi(i: i64, j: i64, k: i64) -> i64 { return (i * (MG_N + 1) + j) * (MG_N + 1) + k }
18func mg_ci(i: i64, j: i64, k: i64) -> i64 { return (i * MG_N + j) * MG_N + k }
19func mg_wc(idx: i64) -> i64 { return 0 - MG_GR + idx * mg_step() } // grid idx -> world coord fx1024
20
21// build the mesh from base's SDF field. verts (x,y,z fx1024) into vbuf, tri indices into fbuf.
22// out[0]=nverts out[1]=ntris. returns 0.
23// sample the SDF field (from base's parts) into the corner grid F.
24func mg_sample_sdf(base: i64, F: *i64) -> i64 {
25 // --- sample the field at every corner ---
26 var i: i64 = 0
27 while i <= MG_N {
28 var j: i64 = 0
29 while j <= MG_N {
30 var k: i64 = 0
31 while k <= MG_N {
32 F[mg_fi(i, j, k)] = sdf_eval(base, mg_wc(i), mg_wc(j), mg_wc(k))
33 k = k + 1
34 }
35 j = j + 1
36 }
37 i = i + 1
38 }
39 return 0
40}
41// extract a mesh from a PRE-FILLED corner grid F (works for ANY field: SDF, or an image-inflation field).
42func mg_extract(F: *i64, cubevi: *i64, vbuf: *i64, fbuf: *i64, out: *i64) -> i64 {
43 let step: i64 = mg_step()
44 // --- pass 1: a vertex per surface-straddling cube, at the mean of its 12 edge crossings ---
45 var nv: i64 = 0
46 var i: i64 = 0
47 while i < MG_N * MG_N * MG_N { cubevi[i] = 0 - 1; i = i + 1 }
48 i = 0
49 while i < MG_N {
50 var j: i64 = 0
51 while j < MG_N {
52 var k: i64 = 0
53 while k < MG_N {
54 // 8 corner values
55 let c000: i64 = F[mg_fi(i, j, k)]
56 let c100: i64 = F[mg_fi(i + 1, j, k)]
57 let c010: i64 = F[mg_fi(i, j + 1, k)]
58 let c110: i64 = F[mg_fi(i + 1, j + 1, k)]
59 let c001: i64 = F[mg_fi(i, j, k + 1)]
60 let c101: i64 = F[mg_fi(i + 1, j, k + 1)]
61 let c011: i64 = F[mg_fi(i, j + 1, k + 1)]
62 let c111: i64 = F[mg_fi(i + 1, j + 1, k + 1)]
63 var mn: i64 = c000
64 var mx: i64 = c000
65 if c100 < mn { mn = c100 }
66 if c100 > mx { mx = c100 }
67 if c010 < mn { mn = c010 }
68 if c010 > mx { mx = c010 }
69 if c110 < mn { mn = c110 }
70 if c110 > mx { mx = c110 }
71 if c001 < mn { mn = c001 }
72 if c001 > mx { mx = c001 }
73 if c101 < mn { mn = c101 }
74 if c101 > mx { mx = c101 }
75 if c011 < mn { mn = c011 }
76 if c011 > mx { mx = c011 }
77 if c111 < mn { mn = c111 }
78 if c111 > mx { mx = c111 }
79 if mn < 0 { if mx >= 0 {
80 // this cube straddles the surface -> place a vertex at the mean of the 12 edge crossings
81 let x0: i64 = mg_wc(i)
82 let y0: i64 = mg_wc(j)
83 let z0: i64 = mg_wc(k)
84 var sx: i64 = 0
85 var sy: i64 = 0
86 var sz: i64 = 0
87 var nc: i64 = 0
88 // 12 edges: interpolate zero crossing t = a/(a-b) along the edge
89 // x-edges (4)
90 if (c000 < 0) != (c100 < 0) { sx = sx + x0 + c000 * step / (c000 - c100); sy = sy + y0; sz = sz + z0; nc = nc + 1 }
91 if (c010 < 0) != (c110 < 0) { sx = sx + x0 + c010 * step / (c010 - c110); sy = sy + y0 + step; sz = sz + z0; nc = nc + 1 }
92 if (c001 < 0) != (c101 < 0) { sx = sx + x0 + c001 * step / (c001 - c101); sy = sy + y0; sz = sz + z0 + step; nc = nc + 1 }
93 if (c011 < 0) != (c111 < 0) { sx = sx + x0 + c011 * step / (c011 - c111); sy = sy + y0 + step; sz = sz + z0 + step; nc = nc + 1 }
94 // y-edges (4)
95 if (c000 < 0) != (c010 < 0) { sx = sx + x0; sy = sy + y0 + c000 * step / (c000 - c010); sz = sz + z0; nc = nc + 1 }
96 if (c100 < 0) != (c110 < 0) { sx = sx + x0 + step; sy = sy + y0 + c100 * step / (c100 - c110); sz = sz + z0; nc = nc + 1 }
97 if (c001 < 0) != (c011 < 0) { sx = sx + x0; sy = sy + y0 + c001 * step / (c001 - c011); sz = sz + z0 + step; nc = nc + 1 }
98 if (c101 < 0) != (c111 < 0) { sx = sx + x0 + step; sy = sy + y0 + c101 * step / (c101 - c111); sz = sz + z0 + step; nc = nc + 1 }
99 // z-edges (4)
100 if (c000 < 0) != (c001 < 0) { sx = sx + x0; sy = sy + y0; sz = sz + z0 + c000 * step / (c000 - c001); nc = nc + 1 }
101 if (c100 < 0) != (c101 < 0) { sx = sx + x0 + step; sy = sy + y0; sz = sz + z0 + c100 * step / (c100 - c101); nc = nc + 1 }
102 if (c010 < 0) != (c011 < 0) { sx = sx + x0; sy = sy + y0 + step; sz = sz + z0 + c010 * step / (c010 - c011); nc = nc + 1 }
103 if (c110 < 0) != (c111 < 0) { sx = sx + x0 + step; sy = sy + y0 + step; sz = sz + z0 + c110 * step / (c110 - c111); nc = nc + 1 }
104 if nc < 1 { nc = 1 }
105 if nv < MG_MAXV {
106 vbuf[nv * 3] = sx / nc
107 vbuf[nv * 3 + 1] = sy / nc
108 vbuf[nv * 3 + 2] = sz / nc
109 cubevi[mg_ci(i, j, k)] = nv
110 nv = nv + 1
111 }
112 } }
113 k = k + 1
114 }
115 j = j + 1
116 }
117 i = i + 1
118 }
119 // --- pass 2: a quad per surface-crossing grid edge, linking the 4 surrounding cubes' vertices ---
120 var nf: i64 = 0
121 i = 1
122 while i < MG_N {
123 var j: i64 = 1
124 while j < MG_N {
125 var k: i64 = 1
126 while k < MG_N {
127 let f0: i64 = F[mg_fi(i, j, k)]
128 // +x edge -> quad of cubes (i, j-1..j, k-1..k)
129 if (f0 < 0) != (F[mg_fi(i + 1, j, k)] < 0) {
130 let a: i64 = cubevi[mg_ci(i, j - 1, k - 1)]
131 let b: i64 = cubevi[mg_ci(i, j, k - 1)]
132 let c: i64 = cubevi[mg_ci(i, j, k)]
133 let d: i64 = cubevi[mg_ci(i, j - 1, k)]
134 if a >= 0 { if b >= 0 { if c >= 0 { if d >= 0 { if nf + 2 <= MG_MAXF {
135 fbuf[nf * 3] = a; fbuf[nf * 3 + 1] = b; fbuf[nf * 3 + 2] = c; nf = nf + 1
136 fbuf[nf * 3] = a; fbuf[nf * 3 + 1] = c; fbuf[nf * 3 + 2] = d; nf = nf + 1
137 } } } } }
138 }
139 // +y edge -> quad of cubes (i-1..i, j, k-1..k)
140 if (f0 < 0) != (F[mg_fi(i, j + 1, k)] < 0) {
141 let a: i64 = cubevi[mg_ci(i - 1, j, k - 1)]
142 let b: i64 = cubevi[mg_ci(i, j, k - 1)]
143 let c: i64 = cubevi[mg_ci(i, j, k)]
144 let d: i64 = cubevi[mg_ci(i - 1, j, k)]
145 if a >= 0 { if b >= 0 { if c >= 0 { if d >= 0 { if nf + 2 <= MG_MAXF {
146 fbuf[nf * 3] = a; fbuf[nf * 3 + 1] = b; fbuf[nf * 3 + 2] = c; nf = nf + 1
147 fbuf[nf * 3] = a; fbuf[nf * 3 + 1] = c; fbuf[nf * 3 + 2] = d; nf = nf + 1
148 } } } } }
149 }
150 // +z edge -> quad of cubes (i-1..i, j-1..j, k)
151 if (f0 < 0) != (F[mg_fi(i, j, k + 1)] < 0) {
152 let a: i64 = cubevi[mg_ci(i - 1, j - 1, k)]
153 let b: i64 = cubevi[mg_ci(i, j - 1, k)]
154 let c: i64 = cubevi[mg_ci(i, j, k)]
155 let d: i64 = cubevi[mg_ci(i - 1, j, k)]
156 if a >= 0 { if b >= 0 { if c >= 0 { if d >= 0 { if nf + 2 <= MG_MAXF {
157 fbuf[nf * 3] = a; fbuf[nf * 3 + 1] = b; fbuf[nf * 3 + 2] = c; nf = nf + 1
158 fbuf[nf * 3] = a; fbuf[nf * 3 + 1] = c; fbuf[nf * 3 + 2] = d; nf = nf + 1
159 } } } } }
160 }
161 k = k + 1
162 }
163 j = j + 1
164 }
165 i = i + 1
166 }
167 out[0] = nv
168 out[1] = nf
169 return 0
170}
171// convenience: sample the SDF field then extract (the original one-call path).
172func mg_build(base: i64, F: *i64, cubevi: *i64, vbuf: *i64, fbuf: *i64, out: *i64) -> i64 {
173 mg_sample_sdf(base, F)
174 mg_extract(F, cubevi, vbuf, fbuf, out)
175 return 0
176}