1use super::mesh::TriMesh;
7use crate::MeshError;
8use glam::Vec3;
9use std::collections::{HashMap, HashSet};
10
11#[derive(Debug, Clone)]
13pub struct SimplificationConfig {
14 pub reduction_ratio: f32,
16 pub max_error: f32,
18 pub preserve_boundaries: bool,
20 pub preserve_topology: bool,
22 pub min_triangle_area: f32,
24 pub vertex_weld_threshold: f32,
26}
27
28impl Default for SimplificationConfig {
29 fn default() -> Self {
30 Self {
31 reduction_ratio: 0.5, max_error: 0.01, preserve_boundaries: true, preserve_topology: true, min_triangle_area: 0.0001, vertex_weld_threshold: 0.001, }
38 }
39}
40
41#[derive(Debug)]
43pub struct SimplificationResult {
44 pub mesh: TriMesh,
45 pub stats: SimplificationStats,
46}
47
48#[derive(Debug, Default)]
50pub struct SimplificationStats {
51 pub original_vertices: usize,
52 pub original_triangles: usize,
53 pub final_vertices: usize,
54 pub final_triangles: usize,
55 pub vertices_welded: usize,
56 pub degenerate_removed: usize,
57 pub small_triangles_removed: usize,
58 pub max_error_introduced: f32,
59}
60
61impl SimplificationStats {
62 pub fn vertex_reduction_percent(&self) -> f32 {
64 if self.original_vertices == 0 {
65 return 0.0;
66 }
67 ((self.original_vertices - self.final_vertices) as f32 / self.original_vertices as f32)
68 * 100.0
69 }
70
71 pub fn triangle_reduction_percent(&self) -> f32 {
73 if self.original_triangles == 0 {
74 return 0.0;
75 }
76 ((self.original_triangles - self.final_triangles) as f32 / self.original_triangles as f32)
77 * 100.0
78 }
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, Hash)]
83struct Edge {
84 v0: usize,
85 v1: usize,
86}
87
88impl Edge {
89 fn new(v0: usize, v1: usize) -> Self {
90 if v0 < v1 {
92 Self { v0, v1 }
93 } else {
94 Self { v0: v1, v1: v0 }
95 }
96 }
97}
98
99#[derive(Debug, Clone, Default)]
101struct QuadricError {
102 matrix: [f64; 10],
104}
105
106impl QuadricError {
107 fn from_triangle(v0: &Vec3, v1: &Vec3, v2: &Vec3) -> Self {
109 let edge1 = *v1 - *v0;
111 let edge2 = *v2 - *v0;
112 let normal = edge1.cross(edge2).normalize();
113
114 let a = normal.x as f64;
115 let b = normal.y as f64;
116 let c = normal.z as f64;
117 let d = -(a * v0.x as f64 + b * v0.y as f64 + c * v0.z as f64);
118
119 let mut matrix = [0.0; 10];
121 matrix[0] = a * a; matrix[1] = a * b; matrix[2] = a * c; matrix[3] = a * d; matrix[4] = b * b; matrix[5] = b * c; matrix[6] = b * d; matrix[7] = c * c; matrix[8] = c * d; matrix[9] = d * d; Self { matrix }
133 }
134
135 fn add(&mut self, other: &QuadricError) {
137 for i in 0..10 {
138 self.matrix[i] += other.matrix[i];
139 }
140 }
141
142 fn error(&self, v: &Vec3) -> f64 {
144 let x = v.x as f64;
145 let y = v.y as f64;
146 let z = v.z as f64;
147
148 self.matrix[0] * x * x
150 + 2.0 * self.matrix[1] * x * y
151 + 2.0 * self.matrix[2] * x * z
152 + 2.0 * self.matrix[3] * x
153 + self.matrix[4] * y * y
154 + 2.0 * self.matrix[5] * y * z
155 + 2.0 * self.matrix[6] * y
156 + self.matrix[7] * z * z
157 + 2.0 * self.matrix[8] * z
158 + self.matrix[9]
159 }
160}
161
162pub struct MeshSimplifier {
164 config: SimplificationConfig,
165}
166
167impl MeshSimplifier {
168 pub fn new(config: SimplificationConfig) -> Self {
169 Self { config }
170 }
171
172 pub fn with_defaults() -> Self {
173 Self::new(SimplificationConfig::default())
174 }
175
176 pub fn simplify(&self, mesh: &TriMesh) -> Result<SimplificationResult, MeshError> {
178 let mut stats = SimplificationStats {
179 original_vertices: mesh.vert_count,
180 original_triangles: mesh.tri_count,
181 ..Default::default()
182 };
183
184 if mesh.tri_count == 0 {
185 return Ok(SimplificationResult {
186 mesh: mesh.clone(),
187 stats,
188 });
189 }
190
191 let mut simplified_mesh = self.preprocess_mesh(mesh, &mut stats)?;
193
194 if self.config.reduction_ratio > 0.0 {
196 simplified_mesh = self.edge_collapse_simplification(&simplified_mesh, &mut stats)?;
197 }
198
199 simplified_mesh = self.postprocess_mesh(&simplified_mesh, &mut stats)?;
201
202 stats.final_vertices = simplified_mesh.vert_count;
203 stats.final_triangles = simplified_mesh.tri_count;
204
205 Ok(SimplificationResult {
206 mesh: simplified_mesh,
207 stats,
208 })
209 }
210
211 fn preprocess_mesh(
213 &self,
214 mesh: &TriMesh,
215 stats: &mut SimplificationStats,
216 ) -> Result<TriMesh, MeshError> {
217 let (vertices, vertex_mapping) = self.weld_vertices(&mesh.vertices, mesh.vert_count)?;
219 stats.vertices_welded = mesh.vert_count - vertices.len() / 3;
220
221 let mut indices = Vec::new();
223 let mut triangle_count = 0;
224
225 for i in 0..mesh.tri_count {
226 let base = i * 3;
227 let i0 = vertex_mapping[mesh.indices[base] as usize];
228 let i1 = vertex_mapping[mesh.indices[base + 1] as usize];
229 let i2 = vertex_mapping[mesh.indices[base + 2] as usize];
230
231 if i0 != i1 && i1 != i2 && i0 != i2 {
233 let v0 = Vec3::new(vertices[i0 * 3], vertices[i0 * 3 + 1], vertices[i0 * 3 + 2]);
235 let v1 = Vec3::new(vertices[i1 * 3], vertices[i1 * 3 + 1], vertices[i1 * 3 + 2]);
236 let v2 = Vec3::new(vertices[i2 * 3], vertices[i2 * 3 + 1], vertices[i2 * 3 + 2]);
237
238 let area = Self::triangle_area(&v0, &v1, &v2);
239 if area >= self.config.min_triangle_area {
240 indices.push(i0 as i32);
241 indices.push(i1 as i32);
242 indices.push(i2 as i32);
243 triangle_count += 1;
244 } else {
245 stats.small_triangles_removed += 1;
246 }
247 } else {
248 stats.degenerate_removed += 1;
249 }
250 }
251
252 let vert_count = vertices.len() / 3;
253 Ok(TriMesh {
254 vertices,
255 indices,
256 vert_count,
257 tri_count: triangle_count,
258 })
259 }
260
261 fn weld_vertices(
263 &self,
264 vertices: &[f32],
265 vert_count: usize,
266 ) -> Result<(Vec<f32>, Vec<usize>), MeshError> {
267 let mut welded_vertices = Vec::new();
268 let mut vertex_mapping = vec![0; vert_count];
269
270 for i in 0..vert_count {
271 let v = Vec3::new(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]);
272
273 let mut found = false;
275 for (j, existing_idx) in (0..welded_vertices.len() / 3).enumerate() {
276 let existing = Vec3::new(
277 welded_vertices[existing_idx * 3],
278 welded_vertices[existing_idx * 3 + 1],
279 welded_vertices[existing_idx * 3 + 2],
280 );
281
282 if (v - existing).length() <= self.config.vertex_weld_threshold {
283 vertex_mapping[i] = j;
284 found = true;
285 break;
286 }
287 }
288
289 if !found {
290 let new_idx = welded_vertices.len() / 3;
291 welded_vertices.push(v.x);
292 welded_vertices.push(v.y);
293 welded_vertices.push(v.z);
294 vertex_mapping[i] = new_idx;
295 }
296 }
297
298 Ok((welded_vertices, vertex_mapping))
299 }
300
301 fn edge_collapse_simplification(
303 &self,
304 mesh: &TriMesh,
305 stats: &mut SimplificationStats,
306 ) -> Result<TriMesh, MeshError> {
307 if mesh.tri_count == 0 {
308 return Ok(mesh.clone());
309 }
310
311 let target_triangles =
313 ((mesh.tri_count as f32) * (1.0 - self.config.reduction_ratio)) as usize;
314 if target_triangles >= mesh.tri_count {
315 return Ok(mesh.clone());
316 }
317
318 let mut edge_to_triangles: HashMap<Edge, Vec<usize>> = HashMap::new();
320 let mut vertex_to_triangles: HashMap<usize, Vec<usize>> = HashMap::new();
321
322 for i in 0..mesh.tri_count {
323 let base = i * 3;
324 let v0 = mesh.indices[base] as usize;
325 let v1 = mesh.indices[base + 1] as usize;
326 let v2 = mesh.indices[base + 2] as usize;
327
328 for &(va, vb) in &[(v0, v1), (v1, v2), (v2, v0)] {
330 let edge = Edge::new(va, vb);
331 edge_to_triangles.entry(edge).or_default().push(i);
332 vertex_to_triangles.entry(va).or_default().push(i);
333 vertex_to_triangles.entry(vb).or_default().push(i);
334 }
335 }
336
337 let mut vertex_quadrics = vec![QuadricError::default(); mesh.vert_count];
339
340 for i in 0..mesh.tri_count {
341 let base = i * 3;
342 let v0_idx = mesh.indices[base] as usize;
343 let v1_idx = mesh.indices[base + 1] as usize;
344 let v2_idx = mesh.indices[base + 2] as usize;
345
346 let v0 = Vec3::new(
347 mesh.vertices[v0_idx * 3],
348 mesh.vertices[v0_idx * 3 + 1],
349 mesh.vertices[v0_idx * 3 + 2],
350 );
351 let v1 = Vec3::new(
352 mesh.vertices[v1_idx * 3],
353 mesh.vertices[v1_idx * 3 + 1],
354 mesh.vertices[v1_idx * 3 + 2],
355 );
356 let v2 = Vec3::new(
357 mesh.vertices[v2_idx * 3],
358 mesh.vertices[v2_idx * 3 + 1],
359 mesh.vertices[v2_idx * 3 + 2],
360 );
361
362 let quadric = QuadricError::from_triangle(&v0, &v1, &v2);
363 vertex_quadrics[v0_idx].add(&quadric);
364 vertex_quadrics[v1_idx].add(&quadric);
365 vertex_quadrics[v2_idx].add(&quadric);
366 }
367
368 let mut edge_costs: Vec<(f64, Edge, Vec3)> = Vec::new();
370
371 for edge in edge_to_triangles.keys() {
372 if self.config.preserve_boundaries {
373 if edge_to_triangles[edge].len() < 2 {
375 continue;
376 }
377 }
378
379 let v0 = Vec3::new(
380 mesh.vertices[edge.v0 * 3],
381 mesh.vertices[edge.v0 * 3 + 1],
382 mesh.vertices[edge.v0 * 3 + 2],
383 );
384 let v1 = Vec3::new(
385 mesh.vertices[edge.v1 * 3],
386 mesh.vertices[edge.v1 * 3 + 1],
387 mesh.vertices[edge.v1 * 3 + 2],
388 );
389
390 let collapse_pos = (v0 + v1) * 0.5;
392
393 let mut combined_quadric = vertex_quadrics[edge.v0].clone();
395 combined_quadric.add(&vertex_quadrics[edge.v1]);
396 let cost = combined_quadric.error(&collapse_pos);
397
398 edge_costs.push((cost, edge.clone(), collapse_pos));
399 }
400
401 edge_costs.sort_by(|a, b| a.0.total_cmp(&b.0));
403
404 let mut active_triangles: HashSet<usize> = (0..mesh.tri_count).collect();
406 let mut vertex_positions: Vec<Vec3> = (0..mesh.vert_count)
407 .map(|i| {
408 Vec3::new(
409 mesh.vertices[i * 3],
410 mesh.vertices[i * 3 + 1],
411 mesh.vertices[i * 3 + 2],
412 )
413 })
414 .collect();
415
416 let mut collapsed_edges = 0;
417 let max_collapses = mesh.tri_count - target_triangles;
418
419 for (cost, edge, collapse_pos) in edge_costs {
420 if collapsed_edges >= max_collapses {
421 break;
422 }
423
424 if cost > self.config.max_error as f64 {
425 break;
426 }
427
428 if edge.v0 >= vertex_positions.len() || edge.v1 >= vertex_positions.len() {
430 continue;
431 }
432
433 if let Some(triangles) = edge_to_triangles.get(&edge) {
435 for &tri_idx in triangles {
436 active_triangles.remove(&tri_idx);
437 }
438 collapsed_edges += triangles.len();
439 }
440
441 vertex_positions[edge.v0] = collapse_pos;
443
444 stats.max_error_introduced = stats.max_error_introduced.max(cost as f32);
449 }
450
451 let mut new_vertices = Vec::new();
453 let mut new_indices = Vec::new();
454 let mut vertex_map = HashMap::new();
455 let mut next_vertex_idx: i32 = 0;
456
457 for &tri_idx in &active_triangles {
458 let base = tri_idx * 3;
459 let mut triangle_indices: Vec<i32> = Vec::new();
460
461 for offset in 0..3 {
462 let old_idx = mesh.indices[base + offset] as usize;
463
464 if let Some(&new_idx) = vertex_map.get(&old_idx) {
465 triangle_indices.push(new_idx);
466 } else {
467 let pos = &vertex_positions[old_idx];
468 new_vertices.push(pos.x);
469 new_vertices.push(pos.y);
470 new_vertices.push(pos.z);
471 vertex_map.insert(old_idx, next_vertex_idx);
472 triangle_indices.push(next_vertex_idx);
473 next_vertex_idx += 1;
474 }
475 }
476
477 if triangle_indices[0] != triangle_indices[1]
479 && triangle_indices[1] != triangle_indices[2]
480 && triangle_indices[0] != triangle_indices[2]
481 {
482 new_indices.push(triangle_indices[0]);
483 new_indices.push(triangle_indices[1]);
484 new_indices.push(triangle_indices[2]);
485 }
486 }
487
488 let vert_count = new_vertices.len() / 3;
489 let tri_count = new_indices.len() / 3;
490 Ok(TriMesh {
491 vertices: new_vertices,
492 indices: new_indices,
493 vert_count,
494 tri_count,
495 })
496 }
497
498 fn postprocess_mesh(
500 &self,
501 mesh: &TriMesh,
502 stats: &mut SimplificationStats,
503 ) -> Result<TriMesh, MeshError> {
504 let mut new_indices = Vec::new();
506 let mut triangle_count = 0;
507
508 for i in 0..mesh.tri_count {
509 let base = i * 3;
510 let i0 = mesh.indices[base] as usize;
511 let i1 = mesh.indices[base + 1] as usize;
512 let i2 = mesh.indices[base + 2] as usize;
513
514 if i0 != i1 && i1 != i2 && i0 != i2 {
515 let v0 = Vec3::new(
516 mesh.vertices[i0 * 3],
517 mesh.vertices[i0 * 3 + 1],
518 mesh.vertices[i0 * 3 + 2],
519 );
520 let v1 = Vec3::new(
521 mesh.vertices[i1 * 3],
522 mesh.vertices[i1 * 3 + 1],
523 mesh.vertices[i1 * 3 + 2],
524 );
525 let v2 = Vec3::new(
526 mesh.vertices[i2 * 3],
527 mesh.vertices[i2 * 3 + 1],
528 mesh.vertices[i2 * 3 + 2],
529 );
530
531 let area = Self::triangle_area(&v0, &v1, &v2);
532 if area >= self.config.min_triangle_area {
533 new_indices.push(mesh.indices[base]);
534 new_indices.push(mesh.indices[base + 1]);
535 new_indices.push(mesh.indices[base + 2]);
536 triangle_count += 1;
537 } else {
538 stats.small_triangles_removed += 1;
539 }
540 } else {
541 stats.degenerate_removed += 1;
542 }
543 }
544
545 Ok(TriMesh {
546 vertices: mesh.vertices.clone(),
547 indices: new_indices,
548 vert_count: mesh.vert_count,
549 tri_count: triangle_count,
550 })
551 }
552
553 fn triangle_area(v0: &Vec3, v1: &Vec3, v2: &Vec3) -> f32 {
555 let edge1 = *v1 - *v0;
556 let edge2 = *v2 - *v0;
557 edge1.cross(edge2).length() * 0.5
558 }
559}
560
561impl MeshSimplifier {
563 pub fn analyze_mesh(mesh: &TriMesh) -> MeshComplexityAnalysis {
565 let mut analysis = MeshComplexityAnalysis::default();
566
567 if mesh.tri_count == 0 {
568 return analysis;
569 }
570
571 analysis.vertex_count = mesh.vert_count;
572 analysis.triangle_count = mesh.tri_count;
573
574 let mut total_area = 0.0;
576 let mut min_area = f32::MAX;
577 let mut max_area = f32::MIN;
578 let mut small_triangle_count = 0;
579
580 for i in 0..mesh.tri_count {
581 let base = i * 3;
582 let v0 = Vec3::new(
583 mesh.vertices[mesh.indices[base] as usize * 3],
584 mesh.vertices[mesh.indices[base] as usize * 3 + 1],
585 mesh.vertices[mesh.indices[base] as usize * 3 + 2],
586 );
587 let v1 = Vec3::new(
588 mesh.vertices[mesh.indices[base + 1] as usize * 3],
589 mesh.vertices[mesh.indices[base + 1] as usize * 3 + 1],
590 mesh.vertices[mesh.indices[base + 1] as usize * 3 + 2],
591 );
592 let v2 = Vec3::new(
593 mesh.vertices[mesh.indices[base + 2] as usize * 3],
594 mesh.vertices[mesh.indices[base + 2] as usize * 3 + 1],
595 mesh.vertices[mesh.indices[base + 2] as usize * 3 + 2],
596 );
597
598 let area = Self::triangle_area(&v0, &v1, &v2);
599 total_area += area;
600 min_area = min_area.min(area);
601 max_area = max_area.max(area);
602
603 if area < 0.001 {
604 small_triangle_count += 1;
606 }
607 }
608
609 analysis.average_triangle_area = total_area / mesh.tri_count as f32;
610 analysis.min_triangle_area = min_area;
611 analysis.max_triangle_area = max_area;
612 analysis.small_triangle_ratio = small_triangle_count as f32 / mesh.tri_count as f32;
613
614 let (bmin, bmax) = mesh.calculate_bounds();
616 let bbox_area = (bmax.x - bmin.x) * (bmax.z - bmin.z);
617 if bbox_area > 0.0 {
618 analysis.triangle_density = mesh.tri_count as f32 / bbox_area;
619 }
620
621 analysis.recommended_reduction = analysis.calculate_recommended_reduction();
623
624 analysis
625 }
626}
627
628#[derive(Debug, Default)]
630pub struct MeshComplexityAnalysis {
631 pub vertex_count: usize,
632 pub triangle_count: usize,
633 pub average_triangle_area: f32,
634 pub min_triangle_area: f32,
635 pub max_triangle_area: f32,
636 pub small_triangle_ratio: f32,
637 pub triangle_density: f32,
638 pub recommended_reduction: f32,
639}
640
641impl MeshComplexityAnalysis {
642 fn calculate_recommended_reduction(&self) -> f32 {
644 let mut reduction: f32 = 0.0;
645
646 if self.triangle_count > 100_000 {
648 reduction += 0.7;
649 } else if self.triangle_count > 50_000 {
650 reduction += 0.5;
651 } else if self.triangle_count > 10_000 {
652 reduction += 0.3;
653 }
654
655 if self.small_triangle_ratio > 0.3 {
657 reduction += 0.3;
658 } else if self.small_triangle_ratio > 0.1 {
659 reduction += 0.2;
660 }
661
662 if self.triangle_density > 1000.0 {
664 reduction += 0.2;
665 } else if self.triangle_density > 500.0 {
666 reduction += 0.1;
667 }
668
669 reduction.min(0.8) }
671}
672
673#[cfg(test)]
674mod tests {
675 use super::*;
676
677 fn create_test_mesh() -> TriMesh {
678 let mut mesh = TriMesh::new();
680
681 let vertices = vec![
683 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.5, 1.0, 0.5, ];
689
690 let indices = vec![
692 0, 1, 2, 0, 2, 3, 0, 4, 1, 1, 4, 2, 2, 4, 3, 3, 4, 0,
695 ];
696
697 mesh.vertices = vertices;
698 mesh.indices = indices;
699 mesh.vert_count = 5;
700 mesh.tri_count = 6;
701
702 mesh
703 }
704
705 #[test]
706 fn test_mesh_simplification_basic() {
707 let simplifier = MeshSimplifier::with_defaults();
708 let mesh = create_test_mesh();
709
710 let result = simplifier.simplify(&mesh).unwrap();
711
712 assert!(result.stats.final_triangles <= result.stats.original_triangles);
714 assert!(result.stats.final_vertices <= result.stats.original_vertices);
715 }
716
717 #[test]
718 fn test_vertex_welding() {
719 let config = SimplificationConfig {
720 vertex_weld_threshold: 0.1,
721 reduction_ratio: 0.0, ..Default::default()
723 };
724 let simplifier = MeshSimplifier::new(config);
725
726 let mut mesh = create_test_mesh();
727
728 mesh.vertices.extend_from_slice(&[0.05, 0.0, 0.0]); mesh.vert_count += 1;
731
732 let result = simplifier.simplify(&mesh).unwrap();
733
734 assert!(result.stats.vertices_welded > 0);
735 }
736
737 #[test]
738 fn test_mesh_analysis() {
739 let mesh = create_test_mesh();
740 let analysis = MeshSimplifier::analyze_mesh(&mesh);
741
742 assert_eq!(analysis.vertex_count, 5);
743 assert_eq!(analysis.triangle_count, 6);
744 assert!(analysis.average_triangle_area > 0.0);
745 assert!(analysis.recommended_reduction >= 0.0);
746 }
747
748 #[test]
749 fn test_degenerate_triangle_removal() {
750 let simplifier = MeshSimplifier::with_defaults();
751
752 let mut mesh = TriMesh::new();
753 mesh.vertices = vec![
754 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, ];
758 mesh.indices = vec![
759 0, 1, 2, 0, 0, 1, ];
762 mesh.vert_count = 3;
763 mesh.tri_count = 2;
764
765 let result = simplifier.simplify(&mesh).unwrap();
766
767 assert!(result.stats.degenerate_removed > 0);
768 assert_eq!(result.stats.final_triangles, 1);
769 }
770
771 #[test]
772 fn test_small_triangle_removal() {
773 let config = SimplificationConfig {
774 min_triangle_area: 0.1, reduction_ratio: 0.0, ..Default::default()
777 };
778 let simplifier = MeshSimplifier::new(config);
779
780 let mut mesh = TriMesh::new();
781 mesh.vertices = vec![
782 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.01, 0.01, 0.0, ];
787 mesh.indices = vec![
788 0, 1, 2, 0, 1, 3, ];
791 mesh.vert_count = 4;
792 mesh.tri_count = 2;
793
794 let result = simplifier.simplify(&mesh).unwrap();
795
796 assert!(result.stats.small_triangles_removed > 0);
797 }
798}