1use std::collections::HashMap;
2
3type SDF = Fn(usize, usize, usize) -> f32;
7
8pub fn surface_net(
11 resolution: usize,
12 signed_distance_field: &SDF,
13 memoize: bool,
14) -> (Vec<[f32; 3]>, Vec<[f32; 3]>, Vec<usize>) {
15 if memoize {
16 let axis_length = resolution + 1;
17 let arr = coords(axis_length)
18 .map(|(x, y, z)| signed_distance_field(x, y, z))
19 .collect::<Vec<_>>();
20 surface_net_impl(resolution, &move |x, y, z| {
21 arr[z * axis_length * axis_length + y * axis_length + x]
22 })
23 } else {
24 surface_net_impl(resolution, signed_distance_field)
25 }
26}
27
28fn surface_net_impl(
30 resolution: usize,
31 grid_values: &SDF,
32) -> (Vec<[f32; 3]>, Vec<[f32; 3]>, Vec<usize>) {
33 let mut vertex_positions = Vec::new();
34 let mut normals = Vec::new();
35 let mut grid_to_index = HashMap::new();
36 for coords in coords(resolution) {
39 if let Some((center, normal)) = find_center(grid_values, coords) {
40 grid_to_index.insert(coords, vertex_positions.len());
41 vertex_positions.push(center);
42 normals.push(normal);
43 }
44 }
45 let mut indicies = Vec::new();
47 make_all_triangles(
48 grid_values,
49 resolution,
50 &grid_to_index,
51 &vertex_positions,
52 &mut indicies,
53 );
54 (vertex_positions, normals, indicies)
55}
56
57fn coords(size: usize) -> impl Iterator<Item = (usize, usize, usize)> {
59 (0..size)
60 .flat_map(move |x| (0..size).map(move |y| (x, y)))
61 .flat_map(move |(x, y)| (0..size).map(move |z| (x, y, z)))
62}
63
64const OFFSETS: [(usize, usize); 12] = [
66 (0b000, 0b001), (0b000, 0b010), (0b000, 0b100), (0b001, 0b011), (0b001, 0b101), (0b010, 0b011), (0b010, 0b110), (0b011, 0b111), (0b100, 0b101), (0b100, 0b110), (0b101, 0b111), (0b110, 0b111), ];
79
80fn find_center(
92 grid_values: &SDF,
93 coord: (usize, usize, usize),
94) -> Option<([f32; 3], [f32; 3])> {
95 let mut values = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
96 for (x, value) in values.iter_mut().enumerate() {
97 *value = grid_values(
98 coord.0 + (x & 1),
99 coord.1 + ((x >> 1) & 1),
100 coord.2 + ((x >> 2) & 1),
101 );
102 }
103 let edges = OFFSETS.iter().filter_map(|&(offset1, offset2)| {
104 find_edge(offset1, offset2, values[offset1], values[offset2])
105 });
106 let mut count = 0;
107 let mut sum = [0.0, 0.0, 0.0];
108 for edge in edges {
109 count += 1;
110 sum[0] += edge[0];
111 sum[1] += edge[1];
112 sum[2] += edge[2];
113 }
114 if count == 0 {
115 None
116 } else {
117 let normal_x = (values[0b001] + values[0b011] + values[0b101] + values[0b111])
118 - (values[0b000] + values[0b010] + values[0b100] + values[0b110]);
119 let normal_y = (values[0b010] + values[0b011] + values[0b110] + values[0b111])
120 - (values[0b000] + values[0b001] + values[0b100] + values[0b101]);
121 let normal_z = (values[0b100] + values[0b101] + values[0b110] + values[0b111])
122 - (values[0b000] + values[0b001] + values[0b010] + values[0b011]);
123 let normal_len = (normal_x * normal_x + normal_y * normal_y + normal_z * normal_z).sqrt();
124 Some((
125 [
126 sum[0] / count as f32 + coord.0 as f32,
127 sum[1] / count as f32 + coord.1 as f32,
128 sum[2] / count as f32 + coord.2 as f32,
129 ],
130 [
131 normal_x / normal_len,
132 normal_y / normal_len,
133 normal_z / normal_len,
134 ],
135 ))
136 }
137}
138
139fn find_edge(offset1: usize, offset2: usize, value1: f32, value2: f32) -> Option<[f32; 3]> {
144 if (value1 < 0.0) == (value2 < 0.0) {
145 return None;
146 }
147 let interp = value1 / (value1 - value2);
148 let point = [
149 (offset1 & 1) as f32 * (1.0 - interp) + (offset2 & 1) as f32 * interp,
150 ((offset1 >> 1) & 1) as f32 * (1.0 - interp) + ((offset2 >> 1) & 1) as f32 * interp,
151 ((offset1 >> 2) & 1) as f32 * (1.0 - interp) + ((offset2 >> 2) & 1) as f32 * interp,
152 ];
153 Some(point)
154}
155
156fn make_all_triangles(
163 grid_values: &SDF,
164 resolution: usize,
165 grid_to_index: &HashMap<(usize, usize, usize), usize>,
166 vertex_positions: &[[f32; 3]],
167 indicies: &mut Vec<usize>,
168) {
169 for coord in coords(resolution) {
170 if coord.1 != 0 && coord.2 != 0 {
173 make_triangle(
174 grid_values,
175 grid_to_index,
176 vertex_positions,
177 indicies,
178 coord,
179 (1, 0, 0),
180 (0, 1, 0),
181 (0, 0, 1),
182 );
183 }
184 if coord.0 != 0 && coord.2 != 0 {
186 make_triangle(
187 grid_values,
188 grid_to_index,
189 vertex_positions,
190 indicies,
191 coord,
192 (0, 1, 0),
193 (0, 0, 1),
194 (1, 0, 0),
195 );
196 }
197 if coord.0 != 0 && coord.1 != 0 {
199 make_triangle(
200 grid_values,
201 grid_to_index,
202 vertex_positions,
203 indicies,
204 coord,
205 (0, 0, 1),
206 (1, 0, 0),
207 (0, 1, 0),
208 );
209 }
210 }
211}
212
213fn make_triangle(
214 grid_values: &SDF,
215 grid_to_index: &HashMap<(usize, usize, usize), usize>,
216 vertex_positions: &[[f32; 3]],
217 indicies: &mut Vec<usize>,
218 coord: (usize, usize, usize),
219 offset: (usize, usize, usize),
220 axis1: (usize, usize, usize),
221 axis2: (usize, usize, usize),
222) {
223 let face_result = is_face(grid_values, coord, offset);
224 if let FaceResult::NoFace = face_result {
225 return;
226 }
227 let v1 = *grid_to_index.get(&(coord.0, coord.1, coord.2)).unwrap();
231 let v2 = *grid_to_index
232 .get(&(coord.0 - axis1.0, coord.1 - axis1.1, coord.2 - axis1.2))
233 .unwrap();
234 let v3 = *grid_to_index
235 .get(&(coord.0 - axis2.0, coord.1 - axis2.1, coord.2 - axis2.2))
236 .unwrap();
237 let v4 = *grid_to_index
238 .get(&(
239 coord.0 - axis1.0 - axis2.0,
240 coord.1 - axis1.1 - axis2.1,
241 coord.2 - axis1.2 - axis2.2,
242 )).unwrap();
243 let p1 = vertex_positions[v1];
245 let p2 = vertex_positions[v2];
246 let p3 = vertex_positions[v3];
247 let p4 = vertex_positions[v4];
248 fn dist(a: [f32; 3], b: [f32; 3]) -> f32 {
249 let d = [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
250 d[0] * d[0] + d[1] * d[1] + d[2] * d[2]
251 }
252 let d14 = dist(p1, p4);
253 let d23 = dist(p2, p3);
254 if d14 < d23 {
256 match face_result {
257 FaceResult::NoFace => (),
258 FaceResult::FacePositive => {
259 indicies.push(v1);
260 indicies.push(v2);
261 indicies.push(v4);
262
263 indicies.push(v1);
264 indicies.push(v4);
265 indicies.push(v3);
266 }
267 FaceResult::FaceNegative => {
268 indicies.push(v1);
269 indicies.push(v4);
270 indicies.push(v2);
271
272 indicies.push(v1);
273 indicies.push(v3);
274 indicies.push(v4);
275 }
276 }
277 } else {
278 match face_result {
279 FaceResult::NoFace => (),
280 FaceResult::FacePositive => {
281 indicies.push(v2);
282 indicies.push(v4);
283 indicies.push(v3);
284
285 indicies.push(v2);
286 indicies.push(v3);
287 indicies.push(v1);
288 }
289 FaceResult::FaceNegative => {
290 indicies.push(v2);
291 indicies.push(v3);
292 indicies.push(v4);
293
294 indicies.push(v2);
295 indicies.push(v1);
296 indicies.push(v3);
297 }
298 }
299 }
300}
301
302enum FaceResult {
303 NoFace,
304 FacePositive,
305 FaceNegative,
306}
307
308fn is_face(
310 grid_values: &SDF,
311 coord: (usize, usize, usize),
312 offset: (usize, usize, usize),
313) -> FaceResult {
314 let other = (coord.0 + offset.0, coord.1 + offset.1, coord.2 + offset.2);
315 match (
316 grid_values(coord.0, coord.1, coord.2) < 0.0,
317 grid_values(other.0, other.1, other.2) < 0.0,
318 ) {
319 (true, false) => FaceResult::FacePositive,
320 (false, true) => FaceResult::FaceNegative,
321 _ => FaceResult::NoFace,
322 }
323}