1#![allow(
7 clippy::unreadable_literal,
8 clippy::too_many_lines,
9 clippy::too_many_arguments,
10 clippy::cast_sign_loss,
11 clippy::cast_possible_truncation,
12 clippy::cast_precision_loss
13)]
14
15use glam::Vec3;
16
17#[derive(Debug, Clone, Default)]
19pub struct McmMesh {
20 pub vertices: Vec<Vec3>,
22 pub normals: Vec<Vec3>,
24 pub indices: Vec<u32>,
26}
27
28impl McmMesh {
29 #[must_use]
31 pub fn num_triangles(&self) -> usize {
32 self.indices.len() / 3
33 }
34
35 #[must_use]
37 pub fn is_empty(&self) -> bool {
38 self.indices.is_empty()
39 }
40}
41
42#[must_use]
57pub fn marching_cubes(field: &[f32], isoval: f32, nx: u32, ny: u32, nz: u32) -> McmMesh {
58 assert!(
59 field.len() == (nx as usize) * (ny as usize) * (nz as usize),
60 "Field size {} does not match dimensions {}x{}x{} = {}",
61 field.len(),
62 nx,
63 ny,
64 nz,
65 (nx as usize) * (ny as usize) * (nz as usize)
66 );
67 assert!(nx >= 2 && ny >= 2 && nz >= 2, "All dimensions must be >= 2");
68
69 let mut mesh = McmMesh {
70 vertices: Vec::with_capacity(100_000),
71 normals: Vec::with_capacity(100_000),
72 indices: Vec::with_capacity(400_000),
73 };
74
75 let size = [nx, ny, nz];
76 let slab_len = (nx as usize) * (ny as usize) * 2;
79 let mut slab_inds: Vec<[u32; 3]> = vec![[0; 3]; slab_len];
80
81 let mut vs = [0.0_f32; 8];
82 let mut edge_indices = [0_u32; 12];
83
84 for z in 0..nz - 1 {
85 for y in 0..ny - 1 {
86 for x in 0..nx - 1 {
87 vs[0] = -isoval + field[to_index_1d(x, y, z, &size)];
89 vs[1] = -isoval + field[to_index_1d(x + 1, y, z, &size)];
90 vs[2] = -isoval + field[to_index_1d(x, y + 1, z, &size)];
91 vs[3] = -isoval + field[to_index_1d(x + 1, y + 1, z, &size)];
92 vs[4] = -isoval + field[to_index_1d(x, y, z + 1, &size)];
93 vs[5] = -isoval + field[to_index_1d(x + 1, y, z + 1, &size)];
94 vs[6] = -isoval + field[to_index_1d(x, y + 1, z + 1, &size)];
95 vs[7] = -isoval + field[to_index_1d(x + 1, y + 1, z + 1, &size)];
96
97 #[allow(clippy::cast_possible_truncation)]
99 let config_n = (i32::from(vs[0] < 0.0))
100 | (i32::from(vs[1] < 0.0) << 1)
101 | (i32::from(vs[2] < 0.0) << 2)
102 | (i32::from(vs[3] < 0.0) << 3)
103 | (i32::from(vs[4] < 0.0) << 4)
104 | (i32::from(vs[5] < 0.0) << 5)
105 | (i32::from(vs[6] < 0.0) << 6)
106 | (i32::from(vs[7] < 0.0) << 7);
107
108 if config_n == 0 || config_n == 255 {
110 continue;
111 }
112
113 if y == 0 && z == 0 {
116 compute_edge(&mut slab_inds, &mut mesh, vs[0], vs[1], 0, x, y, z, &size);
117 }
118 if z == 0 {
119 compute_edge(
120 &mut slab_inds,
121 &mut mesh,
122 vs[2],
123 vs[3],
124 0,
125 x,
126 y + 1,
127 z,
128 &size,
129 );
130 }
131 if y == 0 {
132 compute_edge(
133 &mut slab_inds,
134 &mut mesh,
135 vs[4],
136 vs[5],
137 0,
138 x,
139 y,
140 z + 1,
141 &size,
142 );
143 }
144 compute_edge(
145 &mut slab_inds,
146 &mut mesh,
147 vs[6],
148 vs[7],
149 0,
150 x,
151 y + 1,
152 z + 1,
153 &size,
154 );
155
156 if x == 0 && z == 0 {
158 compute_edge(&mut slab_inds, &mut mesh, vs[0], vs[2], 1, x, y, z, &size);
159 }
160 if z == 0 {
161 compute_edge(
162 &mut slab_inds,
163 &mut mesh,
164 vs[1],
165 vs[3],
166 1,
167 x + 1,
168 y,
169 z,
170 &size,
171 );
172 }
173 if x == 0 {
174 compute_edge(
175 &mut slab_inds,
176 &mut mesh,
177 vs[4],
178 vs[6],
179 1,
180 x,
181 y,
182 z + 1,
183 &size,
184 );
185 }
186 compute_edge(
187 &mut slab_inds,
188 &mut mesh,
189 vs[5],
190 vs[7],
191 1,
192 x + 1,
193 y,
194 z + 1,
195 &size,
196 );
197
198 if x == 0 && y == 0 {
200 compute_edge(&mut slab_inds, &mut mesh, vs[0], vs[4], 2, x, y, z, &size);
201 }
202 if y == 0 {
203 compute_edge(
204 &mut slab_inds,
205 &mut mesh,
206 vs[1],
207 vs[5],
208 2,
209 x + 1,
210 y,
211 z,
212 &size,
213 );
214 }
215 if x == 0 {
216 compute_edge(
217 &mut slab_inds,
218 &mut mesh,
219 vs[2],
220 vs[6],
221 2,
222 x,
223 y + 1,
224 z,
225 &size,
226 );
227 }
228 compute_edge(
229 &mut slab_inds,
230 &mut mesh,
231 vs[3],
232 vs[7],
233 2,
234 x + 1,
235 y + 1,
236 z,
237 &size,
238 );
239
240 edge_indices[0] = slab_inds[to_index_1d_slab(x, y, z, &size)][0];
242 edge_indices[1] = slab_inds[to_index_1d_slab(x, y + 1, z, &size)][0];
243 edge_indices[2] = slab_inds[to_index_1d_slab(x, y, z + 1, &size)][0];
244 edge_indices[3] = slab_inds[to_index_1d_slab(x, y + 1, z + 1, &size)][0];
245 edge_indices[4] = slab_inds[to_index_1d_slab(x, y, z, &size)][1];
246 edge_indices[5] = slab_inds[to_index_1d_slab(x + 1, y, z, &size)][1];
247 edge_indices[6] = slab_inds[to_index_1d_slab(x, y, z + 1, &size)][1];
248 edge_indices[7] = slab_inds[to_index_1d_slab(x + 1, y, z + 1, &size)][1];
249 edge_indices[8] = slab_inds[to_index_1d_slab(x, y, z, &size)][2];
250 edge_indices[9] = slab_inds[to_index_1d_slab(x + 1, y, z, &size)][2];
251 edge_indices[10] = slab_inds[to_index_1d_slab(x, y + 1, z, &size)][2];
252 edge_indices[11] = slab_inds[to_index_1d_slab(x + 1, y + 1, z, &size)][2];
253
254 let config = MC_TRIS[config_n as usize];
256 let n_triangles = (config & 0xF) as usize;
257 let n_indices = n_triangles * 3;
258 let index_base = mesh.indices.len();
259
260 let mut offset = 4;
262 for _ in 0..n_indices {
263 let edge = ((config >> offset) & 0xF) as usize;
264 mesh.indices.push(edge_indices[edge]);
265 offset += 4;
266 }
267
268 for i in 0..n_triangles {
270 let ia = mesh.indices[index_base + i * 3];
271 let ib = mesh.indices[index_base + i * 3 + 1];
272 let ic = mesh.indices[index_base + i * 3 + 2];
273 accumulate_normal(&mut mesh, ia, ib, ic);
274 }
275 }
276 }
277 }
278
279 for normal in &mut mesh.normals {
281 let len = normal.length();
282 if len > 1e-10 {
283 *normal /= len;
284 }
285 }
286
287 mesh
288}
289
290#[inline]
293fn to_index_1d(i: u32, j: u32, k: u32, size: &[u32; 3]) -> usize {
294 ((i as usize) * (size[1] as usize) + (j as usize)) * (size[2] as usize) + (k as usize)
295}
296
297#[inline]
300fn to_index_1d_slab(i: u32, j: u32, k: u32, size: &[u32; 3]) -> usize {
301 (size[0] as usize) * (size[1] as usize) * ((k as usize) % 2)
302 + (j as usize) * (size[0] as usize)
303 + (i as usize)
304}
305
306#[inline]
309fn compute_edge(
310 slab_inds: &mut [[u32; 3]],
311 mesh: &mut McmMesh,
312 va: f32,
313 vb: f32,
314 axis: usize,
315 x: u32,
316 y: u32,
317 z: u32,
318 size: &[u32; 3],
319) {
320 if (va < 0.0) == (vb < 0.0) {
322 return;
323 }
324 let mut v = Vec3::new(x as f32, y as f32, z as f32);
325 v[axis] += va / (va - vb);
326 let idx = mesh.vertices.len() as u32;
327 slab_inds[to_index_1d_slab(x, y, z, size)][axis] = idx;
328 mesh.vertices.push(v);
329 mesh.normals.push(Vec3::ZERO);
330}
331
332#[inline]
334fn accumulate_normal(mesh: &mut McmMesh, a: u32, b: u32, c: u32) {
335 let va = mesh.vertices[a as usize];
336 let vb = mesh.vertices[b as usize];
337 let vc = mesh.vertices[c as usize];
338 let ab = va - vb;
339 let cb = vc - vb;
340 let n = cb.cross(ab);
341 mesh.normals[a as usize] += n;
342 mesh.normals[b as usize] += n;
343 mesh.normals[c as usize] += n;
344}
345
346#[rustfmt::skip]
354static MC_TRIS: [u64; 256] = [
355 0, 33793, 36945, 159668546,
356 18961, 144771090, 5851666, 595283255635,
357 20913, 67640146, 193993474, 655980856339,
358 88782242, 736732689667, 797430812739, 194554754,
359 26657, 104867330, 136709522, 298069416227,
360 109224258, 8877909667, 318136408323, 1567994331701604,
361 189884450, 350847647843, 559958167731, 3256298596865604,
362 447393122899, 651646838401572, 2538311371089956, 737032694307,
363 29329, 43484162, 91358498, 374810899075,
364 158485010, 178117478419, 88675058979, 433581536604804,
365 158486962, 649105605635, 4866906995, 3220959471609924,
366 649165714851, 3184943915608436, 570691368417972, 595804498035,
367 124295042, 431498018963, 508238522371, 91518530,
368 318240155763, 291789778348404, 1830001131721892, 375363605923,
369 777781811075, 1136111028516116, 3097834205243396, 508001629971,
370 2663607373704004, 680242583802939237, 333380770766129845, 179746658,
371 42545, 138437538, 93365810, 713842853011,
372 73602098, 69575510115, 23964357683, 868078761575828,
373 28681778, 713778574611, 250912709379, 2323825233181284,
374 302080811955, 3184439127991172, 1694042660682596, 796909779811,
375 176306722, 150327278147, 619854856867, 1005252473234484,
376 211025400963, 36712706, 360743481544788, 150627258963,
377 117482600995, 1024968212107700, 2535169275963444, 4734473194086550421,
378 628107696687956, 9399128243, 5198438490361643573, 194220594,
379 104474994, 566996932387, 427920028243, 2014821863433780,
380 492093858627, 147361150235284, 2005882975110676, 9671606099636618005,
381 777701008947, 3185463219618820, 482784926917540, 2900953068249785909,
382 1754182023747364, 4274848857537943333, 13198752741767688709, 2015093490989156,
383 591272318771, 2659758091419812, 1531044293118596, 298306479155,
384 408509245114388, 210504348563, 9248164405801223541, 91321106,
385 2660352816454484, 680170263324308757, 8333659837799955077, 482966828984116,
386 4274926723105633605, 3184439197724820, 192104450, 15217,
387 45937, 129205250, 129208402, 529245952323,
388 169097138, 770695537027, 382310500883, 2838550742137652,
389 122763026, 277045793139, 81608128403, 1991870397907988,
390 362778151475, 2059003085103236, 2132572377842852, 655681091891,
391 58419234, 239280858627, 529092143139, 1568257451898804,
392 447235128115, 679678845236084, 2167161349491220, 1554184567314086709,
393 165479003923, 1428768988226596, 977710670185060, 10550024711307499077,
394 1305410032576132, 11779770265620358997, 333446212255967269, 978168444447012,
395 162736434, 35596216627, 138295313843, 891861543990356,
396 692616541075, 3151866750863876, 100103641866564, 6572336607016932133,
397 215036012883, 726936420696196, 52433666, 82160664963,
398 2588613720361524, 5802089162353039525, 214799000387, 144876322,
399 668013605731, 110616894681956, 1601657732871812, 430945547955,
400 3156382366321172, 7644494644932993285, 3928124806469601813, 3155990846772900,
401 339991010498708, 10743689387941597493, 5103845475, 105070898,
402 3928064910068824213, 156265010, 1305138421793636, 27185,
403 195459938, 567044449971, 382447549283, 2175279159592324,
404 443529919251, 195059004769796, 2165424908404116, 1554158691063110021,
405 504228368803, 1436350466655236, 27584723588724, 1900945754488837749,
406 122971970, 443829749251, 302601798803, 108558722,
407 724700725875, 43570095105972, 2295263717447940, 2860446751369014181,
408 2165106202149444, 69275726195, 2860543885641537797, 2165106320445780,
409 2280890014640004, 11820349930268368933, 8721082628082003989, 127050770,
410 503707084675, 122834978, 2538193642857604, 10129,
411 801441490467, 2923200302876740, 1443359556281892, 2901063790822564949,
412 2728339631923524, 7103874718248233397, 12775311047932294245, 95520290,
413 2623783208098404, 1900908618382410757, 137742672547, 2323440239468964,
414 362478212387, 727199575803140, 73425410, 34337,
415 163101314, 668566030659, 801204361987, 73030562,
416 591509145619, 162574594, 100608342969108, 5553,
417 724147968595, 1436604830452292, 176259090, 42001,
418 143955266, 2385, 18433, 0,
419];
420
421#[cfg(test)]
422mod tests {
423 use super::*;
424
425 #[test]
426 fn test_empty_field() {
427 let field = vec![1.0; 3 * 3 * 3];
429 let mesh = marching_cubes(&field, 0.0, 3, 3, 3);
430 assert!(mesh.is_empty());
431 }
432
433 #[test]
434 fn test_constant_below() {
435 let field = vec![-1.0; 3 * 3 * 3];
437 let mesh = marching_cubes(&field, 0.0, 3, 3, 3);
438 assert!(mesh.is_empty());
439 }
440
441 #[test]
442 fn test_sphere_sdf() {
443 let n = 20_u32;
445 let center = Vec3::splat(n as f32 / 2.0);
446 let radius = n as f32 / 4.0;
447 let mut field = vec![0.0_f32; (n * n * n) as usize];
448 for i in 0..n {
449 for j in 0..n {
450 for k in 0..n {
451 let p = Vec3::new(i as f32, j as f32, k as f32);
452 let dist = (p - center).length() - radius;
453 field[to_index_1d(i, j, k, &[n, n, n])] = dist;
454 }
455 }
456 }
457
458 let mesh = marching_cubes(&field, 0.0, n, n, n);
459
460 assert!(
462 mesh.num_triangles() > 100,
463 "Expected >100 triangles, got {}",
464 mesh.num_triangles()
465 );
466 assert_eq!(mesh.vertices.len(), mesh.normals.len());
467 assert_eq!(mesh.indices.len() % 3, 0);
468
469 for &idx in &mesh.indices {
471 assert!(
472 (idx as usize) < mesh.vertices.len(),
473 "Index {} out of range ({})",
474 idx,
475 mesh.vertices.len()
476 );
477 }
478
479 for normal in &mesh.normals {
481 let len = normal.length();
482 assert!((len - 1.0).abs() < 0.01, "Normal length {len} is not unit",);
483 }
484
485 for v in &mesh.vertices {
487 let dist = (Vec3::new(v.z, v.y, v.x) - center).length(); assert!(
489 (dist - radius).abs() < 2.0,
490 "Vertex {v:?} is {dist} from sphere (radius {radius})",
491 );
492 }
493 }
494
495 #[test]
496 fn test_single_crossing() {
497 let mut field = vec![1.0_f32; 8];
499 field[0] = -1.0; let mesh = marching_cubes(&field, 0.0, 2, 2, 2);
501
502 assert_eq!(mesh.num_triangles(), 1);
504 assert_eq!(mesh.indices.len(), 3);
505 }
506
507 #[test]
508 #[should_panic(expected = "Field size")]
509 fn test_wrong_field_size() {
510 let field = vec![0.0; 10]; let _ = marching_cubes(&field, 0.0, 3, 3, 3);
512 }
513
514 #[test]
515 #[should_panic(expected = "dimensions must be >= 2")]
516 fn test_dimension_too_small() {
517 let field = vec![0.0; 1];
518 let _ = marching_cubes(&field, 0.0, 1, 1, 1);
519 }
520}