nx_meshgen_domain_candidate_t337.nx source
↩ module page · 191 lines · 10373 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_box(base: i64, F: *i64, origin: i64, step: i64) -> i64 {
25 if step <= 0 { return 1 }
26 // --- sample the field at every corner ---
27 var i: i64 = 0
28 while i <= MG_N {
29 var j: i64 = 0
30 while j <= MG_N {
31 var k: i64 = 0
32 while k <= MG_N {
33 F[mg_fi(i, j, k)] = sdf_eval(base, origin+i*step, origin+j*step, origin+k*step)
34 k = k + 1
35 }
36 j = j + 1
37 }
38 i = i + 1
39 }
40 return 0
41}
42// extract a mesh from a PRE-FILLED corner grid F (works for ANY field: SDF, or an image-inflation field).
43func mg_extract_box(F: *i64, cubevi: *i64, vbuf: *i64, fbuf: *i64, out: *i64, origin: i64, step: i64) -> i64 {
44 out[0]=0;out[1]=0
45 if step <= 0 { return 1 }
46 // --- pass 1: a vertex per surface-straddling cube, at the mean of its 12 edge crossings ---
47 var nv: i64 = 0
48 var i: i64 = 0
49 while i < MG_N * MG_N * MG_N { cubevi[i] = 0 - 1; i = i + 1 }
50 i = 0
51 while i < MG_N {
52 var j: i64 = 0
53 while j < MG_N {
54 var k: i64 = 0
55 while k < MG_N {
56 // 8 corner values
57 let c000: i64 = F[mg_fi(i, j, k)]
58 let c100: i64 = F[mg_fi(i + 1, j, k)]
59 let c010: i64 = F[mg_fi(i, j + 1, k)]
60 let c110: i64 = F[mg_fi(i + 1, j + 1, k)]
61 let c001: i64 = F[mg_fi(i, j, k + 1)]
62 let c101: i64 = F[mg_fi(i + 1, j, k + 1)]
63 let c011: i64 = F[mg_fi(i, j + 1, k + 1)]
64 let c111: i64 = F[mg_fi(i + 1, j + 1, k + 1)]
65 var mn: i64 = c000
66 var mx: i64 = c000
67 if c100 < mn { mn = c100 }
68 if c100 > mx { mx = c100 }
69 if c010 < mn { mn = c010 }
70 if c010 > mx { mx = c010 }
71 if c110 < mn { mn = c110 }
72 if c110 > mx { mx = c110 }
73 if c001 < mn { mn = c001 }
74 if c001 > mx { mx = c001 }
75 if c101 < mn { mn = c101 }
76 if c101 > mx { mx = c101 }
77 if c011 < mn { mn = c011 }
78 if c011 > mx { mx = c011 }
79 if c111 < mn { mn = c111 }
80 if c111 > mx { mx = c111 }
81 if mn < 0 { if mx >= 0 {
82 // this cube straddles the surface -> place a vertex at the mean of the 12 edge crossings
83 let x0: i64 = origin+i*step
84 let y0: i64 = origin+j*step
85 let z0: i64 = origin+k*step
86 var sx: i64 = 0
87 var sy: i64 = 0
88 var sz: i64 = 0
89 var nc: i64 = 0
90 // 12 edges: interpolate zero crossing t = a/(a-b) along the edge
91 // x-edges (4)
92 if (c000 < 0) != (c100 < 0) { sx = sx + x0 + c000 * step / (c000 - c100); sy = sy + y0; sz = sz + z0; nc = nc + 1 }
93 if (c010 < 0) != (c110 < 0) { sx = sx + x0 + c010 * step / (c010 - c110); sy = sy + y0 + step; sz = sz + z0; nc = nc + 1 }
94 if (c001 < 0) != (c101 < 0) { sx = sx + x0 + c001 * step / (c001 - c101); sy = sy + y0; sz = sz + z0 + step; nc = nc + 1 }
95 if (c011 < 0) != (c111 < 0) { sx = sx + x0 + c011 * step / (c011 - c111); sy = sy + y0 + step; sz = sz + z0 + step; nc = nc + 1 }
96 // y-edges (4)
97 if (c000 < 0) != (c010 < 0) { sx = sx + x0; sy = sy + y0 + c000 * step / (c000 - c010); sz = sz + z0; nc = nc + 1 }
98 if (c100 < 0) != (c110 < 0) { sx = sx + x0 + step; sy = sy + y0 + c100 * step / (c100 - c110); sz = sz + z0; nc = nc + 1 }
99 if (c001 < 0) != (c011 < 0) { sx = sx + x0; sy = sy + y0 + c001 * step / (c001 - c011); sz = sz + z0 + step; nc = nc + 1 }
100 if (c101 < 0) != (c111 < 0) { sx = sx + x0 + step; sy = sy + y0 + c101 * step / (c101 - c111); sz = sz + z0 + step; nc = nc + 1 }
101 // z-edges (4)
102 if (c000 < 0) != (c001 < 0) { sx = sx + x0; sy = sy + y0; sz = sz + z0 + c000 * step / (c000 - c001); nc = nc + 1 }
103 if (c100 < 0) != (c101 < 0) { sx = sx + x0 + step; sy = sy + y0; sz = sz + z0 + c100 * step / (c100 - c101); nc = nc + 1 }
104 if (c010 < 0) != (c011 < 0) { sx = sx + x0; sy = sy + y0 + step; sz = sz + z0 + c010 * step / (c010 - c011); nc = nc + 1 }
105 if (c110 < 0) != (c111 < 0) { sx = sx + x0 + step; sy = sy + y0 + step; sz = sz + z0 + c110 * step / (c110 - c111); nc = nc + 1 }
106 if nc < 1 { nc = 1 }
107 if nv < MG_MAXV {
108 vbuf[nv * 3] = sx / nc
109 vbuf[nv * 3 + 1] = sy / nc
110 vbuf[nv * 3 + 2] = sz / nc
111 cubevi[mg_ci(i, j, k)] = nv
112 nv = nv + 1
113 }
114 } }
115 k = k + 1
116 }
117 j = j + 1
118 }
119 i = i + 1
120 }
121 // --- pass 2: a quad per surface-crossing grid edge, linking the 4 surrounding cubes' vertices ---
122 var nf: i64 = 0
123 i = 1
124 while i < MG_N {
125 var j: i64 = 1
126 while j < MG_N {
127 var k: i64 = 1
128 while k < MG_N {
129 let f0: i64 = F[mg_fi(i, j, k)]
130 // +x edge -> quad of cubes (i, j-1..j, k-1..k)
131 if (f0 < 0) != (F[mg_fi(i + 1, j, k)] < 0) {
132 let a: i64 = cubevi[mg_ci(i, j - 1, k - 1)]
133 let b: i64 = cubevi[mg_ci(i, j, k - 1)]
134 let c: i64 = cubevi[mg_ci(i, j, k)]
135 let d: i64 = cubevi[mg_ci(i, j - 1, k)]
136 if a >= 0 { if b >= 0 { if c >= 0 { if d >= 0 { if nf + 2 <= MG_MAXF {
137 // Outward traversal follows the inside-to-outside edge; the Y-axis base quad is reversed.
138 var qb:i64=b;var qd:i64=d
139 if f0 >= 0 {qb=d;qd=b}
140 fbuf[nf * 3] = a; fbuf[nf * 3 + 1] = qb; fbuf[nf * 3 + 2] = c; nf = nf + 1
141 fbuf[nf * 3] = a; fbuf[nf * 3 + 1] = c; fbuf[nf * 3 + 2] = qd; nf = nf + 1
142 } } } } }
143 }
144 // +y edge -> quad of cubes (i-1..i, j, k-1..k)
145 if (f0 < 0) != (F[mg_fi(i, j + 1, k)] < 0) {
146 let a: i64 = cubevi[mg_ci(i - 1, j, k - 1)]
147 let b: i64 = cubevi[mg_ci(i, j, k - 1)]
148 let c: i64 = cubevi[mg_ci(i, j, k)]
149 let d: i64 = cubevi[mg_ci(i - 1, j, k)]
150 if a >= 0 { if b >= 0 { if c >= 0 { if d >= 0 { if nf + 2 <= MG_MAXF {
151 // Outward traversal follows the inside-to-outside edge; the Y-axis base quad is reversed.
152 var qb:i64=b;var qd:i64=d
153 if f0 < 0 {qb=d;qd=b}
154 fbuf[nf * 3] = a; fbuf[nf * 3 + 1] = qb; fbuf[nf * 3 + 2] = c; nf = nf + 1
155 fbuf[nf * 3] = a; fbuf[nf * 3 + 1] = c; fbuf[nf * 3 + 2] = qd; nf = nf + 1
156 } } } } }
157 }
158 // +z edge -> quad of cubes (i-1..i, j-1..j, k)
159 if (f0 < 0) != (F[mg_fi(i, j, k + 1)] < 0) {
160 let a: i64 = cubevi[mg_ci(i - 1, j - 1, k)]
161 let b: i64 = cubevi[mg_ci(i, j - 1, k)]
162 let c: i64 = cubevi[mg_ci(i, j, k)]
163 let d: i64 = cubevi[mg_ci(i - 1, j, k)]
164 if a >= 0 { if b >= 0 { if c >= 0 { if d >= 0 { if nf + 2 <= MG_MAXF {
165 // Outward traversal follows the inside-to-outside edge; the Y-axis base quad is reversed.
166 var qb:i64=b;var qd:i64=d
167 if f0 >= 0 {qb=d;qd=b}
168 fbuf[nf * 3] = a; fbuf[nf * 3 + 1] = qb; fbuf[nf * 3 + 2] = c; nf = nf + 1
169 fbuf[nf * 3] = a; fbuf[nf * 3 + 1] = c; fbuf[nf * 3 + 2] = qd; nf = nf + 1
170 } } } } }
171 }
172 k = k + 1
173 }
174 j = j + 1
175 }
176 i = i + 1
177 }
178 out[0] = nv
179 out[1] = nf
180 return 0
181}
182// convenience: sample the SDF field then extract (the original one-call path).
183func mg_build(base: i64, F: *i64, cubevi: *i64, vbuf: *i64, fbuf: *i64, out: *i64) -> i64 {
184 mg_sample_sdf(base, F)
185 mg_extract(F, cubevi, vbuf, fbuf, out)
186 return 0
187}
188
189// Legacy callers retain the exact original sampling domain.
190func mg_sample_sdf(base:i64,F:*i64)->i64{return mg_sample_sdf_box(base,F,0-MG_GR,mg_step())}
191func mg_extract(F:*i64,cubevi:*i64,vbuf:*i64,fbuf:*i64,out:*i64)->i64{return mg_extract_box(F,cubevi,vbuf,fbuf,out,0-MG_GR,mg_step())}