1use crate::edge_collapse::{EdgeCost, HalfEdgeMesh, INVALID};
9use priority_queue::PriorityQueue;
10use serde::{Deserialize, Serialize};
11use std::collections::HashSet;
12use threecrate_core::{Error, Point3f, Result, TriangleMesh, Vector3f};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct VertexSplit {
21 pub vertex_s: usize,
23 pub vertex_t: usize,
25 pub position_s: Point3f,
27 pub position_t: Point3f,
29 pub normal_s: Option<Vector3f>,
31 pub normal_t: Option<Vector3f>,
33 pub color_s: Option<[u8; 3]>,
35 pub color_t: Option<[u8; 3]>,
37 pub face_left: Option<[usize; 3]>,
39 pub face_right: Option<[usize; 3]>,
41 pub modified_faces: Vec<(usize, [usize; 3])>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct ProgressiveMesh {
51 pub base_mesh: TriangleMesh,
53 pub vertex_splits: Vec<VertexSplit>,
55 pub full_vertex_count: usize,
57 pub full_face_count: usize,
59}
60
61struct CollapseRecord {
63 vertex_s: usize,
65 vertex_t: usize,
67 position_s_before: Point3f,
69 position_t_before: Point3f,
71 normal_s_before: Option<Vector3f>,
73 normal_t_before: Option<Vector3f>,
75 color_s_before: Option<[u8; 3]>,
77 color_t_before: Option<[u8; 3]>,
79 removed_faces: Vec<(usize, [usize; 3])>,
81 rewritten_faces: Vec<(usize, [usize; 3])>,
83}
84
85impl ProgressiveMesh {
86 pub fn from_mesh(mesh: &TriangleMesh, base_face_ratio: f32) -> Result<Self> {
92 if mesh.is_empty() {
93 return Err(Error::InvalidData("Mesh is empty".to_string()));
94 }
95 let base_face_ratio = base_face_ratio.clamp(0.01, 1.0);
96 let target_faces = (base_face_ratio * mesh.faces.len() as f32).max(1.0) as usize;
97
98 let full_vertex_count = mesh.vertex_count();
99 let full_face_count = mesh.face_count();
100
101 let mut hem = HalfEdgeMesh::from_triangle_mesh(mesh);
102 let mut collapse_records: Vec<CollapseRecord> = Vec::new();
103
104 let mut queue = build_queue(&hem);
105
106 while hem.active_face_count > target_faces && !queue.is_empty() {
107 let (_, edge_cost) = match queue.pop() {
108 Some(item) => item,
109 None => break,
110 };
111
112 let v1 = edge_cost.v1;
113 let v2 = edge_cost.v2;
114
115 if hem.vertex_removed[v1]
116 || hem.vertex_removed[v2]
117 || hem.vertex_edge[v1] == INVALID
118 || hem.vertex_edge[v2] == INVALID
119 {
120 continue;
121 }
122
123 if hem.find_half_edge(v1, v2).is_none() {
124 continue;
125 }
126
127 if !hem.check_link_condition(v1, v2) {
128 continue;
129 }
130
131 let position_s_before = hem.positions[v1];
133 let position_t_before = hem.positions[v2];
134 let normal_s_before = hem.normals.as_ref().map(|n| n[v1]);
135 let normal_t_before = hem.normals.as_ref().map(|n| n[v2]);
136 let color_s_before = hem.colors.as_ref().map(|c| c[v1]);
137 let color_t_before = hem.colors.as_ref().map(|c| c[v2]);
138
139 let faces_before = snapshot_faces_around_edge(&hem, v1, v2);
142
143 let (pos, _cost) = hem.compute_collapse_cost(v1, v2);
144
145 if !hem.collapse_edge(v1, v2, pos) {
146 continue;
147 }
148
149 let (removed_faces, rewritten_faces) =
151 classify_face_changes(&hem, &faces_before, v1, v2);
152
153 collapse_records.push(CollapseRecord {
154 vertex_s: v1,
155 vertex_t: v2,
156 position_s_before,
157 position_t_before,
158 normal_s_before,
159 normal_t_before,
160 color_s_before,
161 color_t_before,
162 removed_faces,
163 rewritten_faces,
164 });
165
166 if collapse_records.len() % 100 == 0 {
168 queue = rebuild_queue(&hem, collapse_records.len() * 1000);
169 }
170 }
171
172 let base_mesh = hem.to_triangle_mesh();
173
174 let vertex_splits: Vec<VertexSplit> = collapse_records
176 .into_iter()
177 .rev()
178 .map(|rec| {
179 let face_left = rec.removed_faces.first().map(|(_, f)| *f);
180 let face_right = rec.removed_faces.get(1).map(|(_, f)| *f);
181
182 let modified_faces: Vec<(usize, [usize; 3])> = rec
183 .rewritten_faces
184 .iter()
185 .map(|(fi, old_verts)| (*fi, *old_verts))
186 .collect();
187
188 VertexSplit {
189 vertex_s: rec.vertex_s,
190 vertex_t: rec.vertex_t,
191 position_s: rec.position_s_before,
192 position_t: rec.position_t_before,
193 normal_s: rec.normal_s_before,
194 normal_t: rec.normal_t_before,
195 color_s: rec.color_s_before,
196 color_t: rec.color_t_before,
197 face_left,
198 face_right,
199 modified_faces,
200 }
201 })
202 .collect();
203
204 Ok(ProgressiveMesh {
205 base_mesh,
206 vertex_splits,
207 full_vertex_count,
208 full_face_count,
209 })
210 }
211
212 pub fn reconstruct_at_level(&self, level: usize) -> TriangleMesh {
217 let level = level.min(self.vertex_splits.len());
218 if level == 0 {
219 return self.base_mesh.clone();
220 }
221
222 let mut vertices = self.base_mesh.vertices.clone();
225 let mut faces = self.base_mesh.faces.clone();
226 let mut normals = self.base_mesh.normals.clone();
227 let mut colors = self.base_mesh.colors.clone();
228
229 for split in self.vertex_splits.iter().take(level) {
234 while vertices.len() <= split.vertex_t.max(split.vertex_s) {
236 vertices.push(Point3f::origin());
237 if let Some(ref mut n) = normals {
238 n.push(Vector3f::zeros());
239 }
240 if let Some(ref mut c) = colors {
241 c.push([0, 0, 0]);
242 }
243 }
244
245 vertices[split.vertex_s] = split.position_s;
247 vertices[split.vertex_t] = split.position_t;
248
249 if let Some(ref mut n) = normals {
251 if let Some(ns) = split.normal_s {
252 n[split.vertex_s] = ns;
253 }
254 if let Some(nt) = split.normal_t {
255 n[split.vertex_t] = nt;
256 }
257 }
258
259 if let Some(ref mut c) = colors {
261 if let Some(cs) = split.color_s {
262 c[split.vertex_s] = cs;
263 }
264 if let Some(ct) = split.color_t {
265 c[split.vertex_t] = ct;
266 }
267 }
268
269 for &(fi, old_verts) in &split.modified_faces {
271 while faces.len() <= fi {
272 faces.push([0, 0, 0]);
273 }
274 faces[fi] = old_verts;
275 }
276
277 if let Some(face) = split.face_left {
279 faces.push(face);
280 }
281 if let Some(face) = split.face_right {
282 faces.push(face);
283 }
284 }
285
286 let faces: Vec<[usize; 3]> = faces
288 .into_iter()
289 .filter(|f| {
290 f[0] != f[1]
291 && f[1] != f[2]
292 && f[2] != f[0]
293 && f[0] < vertices.len()
294 && f[1] < vertices.len()
295 && f[2] < vertices.len()
296 })
297 .collect();
298
299 let mut mesh = TriangleMesh::from_vertices_and_faces(vertices, faces);
300 if let Some(n) = normals {
301 mesh.set_normals(n);
302 }
303 if let Some(c) = colors {
304 mesh.set_colors(c);
305 }
306 mesh
307 }
308
309 pub fn reconstruct_at_ratio(&self, detail_ratio: f32) -> TriangleMesh {
313 let detail_ratio = detail_ratio.clamp(0.0, 1.0);
314 let level = (detail_ratio * self.vertex_splits.len() as f32).round() as usize;
315 self.reconstruct_at_level(level)
316 }
317
318 pub fn base(&self) -> &TriangleMesh {
320 &self.base_mesh
321 }
322
323 pub fn num_levels(&self) -> usize {
325 self.vertex_splits.len()
326 }
327
328 pub fn serialize_to_bytes(&self) -> Result<Vec<u8>> {
330 bincode::serialize(self)
331 .map_err(|e| Error::InvalidData(format!("Serialization failed: {}", e)))
332 }
333
334 pub fn deserialize_from_bytes(data: &[u8]) -> Result<Self> {
336 bincode::deserialize(data)
337 .map_err(|e| Error::InvalidData(format!("Deserialization failed: {}", e)))
338 }
339}
340
341fn snapshot_faces_around_edge(
348 hem: &HalfEdgeMesh,
349 v1: usize,
350 v2: usize,
351) -> Vec<(usize, [usize; 3])> {
352 let mut result = Vec::new();
353 let mut seen = HashSet::new();
354
355 for &v in &[v1, v2] {
356 for &he in &hem.outgoing_half_edges(v) {
357 let face = hem.half_edges[he].face;
358 if face == INVALID || !seen.insert(face) {
359 continue;
360 }
361 let he0 = hem.face_edge[face];
362 if he0 == INVALID {
363 continue;
364 }
365 let he1 = hem.half_edges[he0].next;
366 let fv0 = hem.source(he0);
367 let fv1 = hem.half_edges[he0].target;
368 let fv2 = hem.half_edges[he1].target;
369 result.push((face, [fv0, fv1, fv2]));
370 }
371 }
372
373 result
374}
375
376fn classify_face_changes(
378 hem: &HalfEdgeMesh,
379 faces_before: &[(usize, [usize; 3])],
380 _v1: usize,
381 v2: usize,
382) -> (Vec<(usize, [usize; 3])>, Vec<(usize, [usize; 3])>) {
383 let mut removed = Vec::new();
384 let mut rewritten = Vec::new();
385
386 for &(fi, verts) in faces_before {
387 if hem.face_edge[fi] == INVALID {
388 removed.push((fi, verts));
390 } else {
391 let has_v2 = verts[0] == v2 || verts[1] == v2 || verts[2] == v2;
393 if has_v2 {
394 rewritten.push((fi, verts));
395 }
396 }
397 }
398
399 (removed, rewritten)
400}
401
402fn build_queue(hem: &HalfEdgeMesh) -> PriorityQueue<usize, EdgeCost> {
404 let mut queue = PriorityQueue::new();
405 let mut seen_edges: HashSet<(usize, usize)> = HashSet::new();
406 let mut edge_id = 0usize;
407
408 for vi in 0..hem.positions.len() {
409 if hem.vertex_removed[vi] {
410 continue;
411 }
412 for &he in &hem.outgoing_half_edges(vi) {
413 let target = hem.half_edges[he].target;
414 let key = (vi.min(target), vi.max(target));
415 if !seen_edges.insert(key) {
416 continue;
417 }
418
419 let (pos, cost) = hem.compute_collapse_cost(vi, target);
420
421 queue.push(
422 edge_id,
423 EdgeCost {
424 v1: vi,
425 v2: target,
426 position: pos,
427 cost,
428 },
429 );
430 edge_id += 1;
431 }
432 }
433
434 queue
435}
436
437fn rebuild_queue(hem: &HalfEdgeMesh, id_offset: usize) -> PriorityQueue<usize, EdgeCost> {
439 let mut queue = PriorityQueue::new();
440 let mut seen_edges: HashSet<(usize, usize)> = HashSet::new();
441 let mut edge_id = id_offset;
442
443 for vi in 0..hem.positions.len() {
444 if hem.vertex_removed[vi] || hem.vertex_edge[vi] == INVALID {
445 continue;
446 }
447 for &he in &hem.outgoing_half_edges(vi) {
448 if hem.half_edges[he].face == INVALID {
449 continue;
450 }
451 let target = hem.half_edges[he].target;
452 let key = (vi.min(target), vi.max(target));
453 if !seen_edges.insert(key) {
454 continue;
455 }
456
457 let (pos, cost) = hem.compute_collapse_cost(vi, target);
458
459 queue.push(
460 edge_id,
461 EdgeCost {
462 v1: vi,
463 v2: target,
464 position: pos,
465 cost,
466 },
467 );
468 edge_id += 1;
469 }
470 }
471
472 queue
473}
474
475#[cfg(test)]
476mod tests {
477 use super::*;
478 use nalgebra::Point3;
479
480 fn make_tetrahedron() -> TriangleMesh {
481 TriangleMesh::from_vertices_and_faces(
482 vec![
483 Point3::new(0.0, 0.0, 0.0),
484 Point3::new(1.0, 0.0, 0.0),
485 Point3::new(0.5, 1.0, 0.0),
486 Point3::new(0.5, 0.5, 1.0),
487 ],
488 vec![[0, 2, 1], [0, 1, 3], [0, 3, 2], [1, 2, 3]],
489 )
490 }
491
492 fn make_plane_grid(size: usize) -> TriangleMesh {
493 let mut vertices = Vec::new();
494 for y in 0..size {
495 for x in 0..size {
496 vertices.push(Point3::new(x as f32, y as f32, 0.0));
497 }
498 }
499 let mut faces = Vec::new();
500 for y in 0..(size - 1) {
501 for x in 0..(size - 1) {
502 let tl = y * size + x;
503 let tr = tl + 1;
504 let bl = (y + 1) * size + x;
505 let br = bl + 1;
506 faces.push([tl, bl, tr]);
507 faces.push([tr, bl, br]);
508 }
509 }
510 TriangleMesh::from_vertices_and_faces(vertices, faces)
511 }
512
513 #[test]
514 fn test_progressive_from_empty_mesh() {
515 let mesh = TriangleMesh::new();
516 assert!(ProgressiveMesh::from_mesh(&mesh, 0.5).is_err());
517 }
518
519 #[test]
520 fn test_progressive_from_tetrahedron() {
521 let mesh = make_tetrahedron();
522 let pm = ProgressiveMesh::from_mesh(&mesh, 0.5).unwrap();
523 assert_eq!(pm.full_vertex_count, 4);
524 assert_eq!(pm.full_face_count, 4);
525 assert!(pm.base_mesh.face_count() <= mesh.face_count());
526 }
527
528 #[test]
529 fn test_progressive_from_grid() {
530 let mesh = make_plane_grid(6);
531 let pm = ProgressiveMesh::from_mesh(&mesh, 0.3).unwrap();
532
533 assert!(
535 pm.base_mesh.face_count() < mesh.face_count(),
536 "base should have fewer faces: {} vs {}",
537 pm.base_mesh.face_count(),
538 mesh.face_count()
539 );
540
541 assert!(
543 !pm.vertex_splits.is_empty(),
544 "should have vertex splits recorded"
545 );
546 }
547
548 #[test]
549 fn test_progressive_base_access() {
550 let mesh = make_plane_grid(6);
551 let pm = ProgressiveMesh::from_mesh(&mesh, 0.3).unwrap();
552
553 let base = pm.base();
554 assert_eq!(base.face_count(), pm.base_mesh.face_count());
555 }
556
557 #[test]
558 fn test_progressive_num_levels() {
559 let mesh = make_plane_grid(6);
560 let pm = ProgressiveMesh::from_mesh(&mesh, 0.3).unwrap();
561
562 assert!(pm.num_levels() > 0);
563 }
564
565 #[test]
566 fn test_progressive_reconstruct_level_zero_is_base() {
567 let mesh = make_plane_grid(6);
568 let pm = ProgressiveMesh::from_mesh(&mesh, 0.3).unwrap();
569
570 let level0 = pm.reconstruct_at_level(0);
571 assert_eq!(level0.vertex_count(), pm.base_mesh.vertex_count());
572 assert_eq!(level0.face_count(), pm.base_mesh.face_count());
573 }
574
575 #[test]
576 fn test_progressive_reconstruct_ratio_zero_is_base() {
577 let mesh = make_plane_grid(6);
578 let pm = ProgressiveMesh::from_mesh(&mesh, 0.3).unwrap();
579
580 let r0 = pm.reconstruct_at_ratio(0.0);
581 assert_eq!(r0.vertex_count(), pm.base_mesh.vertex_count());
582 assert_eq!(r0.face_count(), pm.base_mesh.face_count());
583 }
584
585 #[test]
586 fn test_progressive_monotonic_detail() {
587 let mesh = make_plane_grid(8);
588 let pm = ProgressiveMesh::from_mesh(&mesh, 0.2).unwrap();
589
590 if pm.num_levels() < 2 {
591 return; }
593
594 let mut prev_faces = pm.base_mesh.face_count();
595 let step = (pm.num_levels() / 4).max(1);
596
597 for level in (step..=pm.num_levels()).step_by(step) {
598 let reconstructed = pm.reconstruct_at_level(level);
599 assert!(
600 reconstructed.face_count() >= prev_faces,
601 "face count should monotonically increase: level {} has {} faces, prev had {}",
602 level,
603 reconstructed.face_count(),
604 prev_faces
605 );
606 prev_faces = reconstructed.face_count();
607 }
608 }
609
610 #[test]
611 fn test_progressive_with_normals() {
612 let mut mesh = make_plane_grid(5);
613 let normals: Vec<Vector3f> = (0..mesh.vertex_count())
614 .map(|_| Vector3f::new(0.0, 0.0, 1.0))
615 .collect();
616 mesh.set_normals(normals);
617
618 let pm = ProgressiveMesh::from_mesh(&mesh, 0.3).unwrap();
619 assert!(pm.base_mesh.normals.is_some());
620 }
621
622 #[test]
623 fn test_progressive_with_colors() {
624 let mut mesh = make_plane_grid(5);
625 let colors: Vec<[u8; 3]> = (0..mesh.vertex_count()).map(|_| [128, 64, 200]).collect();
626 mesh.set_colors(colors);
627
628 let pm = ProgressiveMesh::from_mesh(&mesh, 0.3).unwrap();
629 assert!(pm.base_mesh.colors.is_some());
630 }
631
632 #[test]
633 fn test_progressive_serialization_roundtrip() {
634 let mesh = make_tetrahedron();
635 let pm = ProgressiveMesh::from_mesh(&mesh, 0.5).unwrap();
636
637 let bytes = pm.serialize_to_bytes().unwrap();
638 assert!(!bytes.is_empty());
639
640 let pm2 = ProgressiveMesh::deserialize_from_bytes(&bytes).unwrap();
641 assert_eq!(pm2.full_vertex_count, pm.full_vertex_count);
642 assert_eq!(pm2.full_face_count, pm.full_face_count);
643 assert_eq!(pm2.vertex_splits.len(), pm.vertex_splits.len());
644 assert_eq!(pm2.base_mesh.face_count(), pm.base_mesh.face_count());
645 }
646
647 #[test]
648 fn test_progressive_clamp_ratio() {
649 let mesh = make_plane_grid(6);
650 let pm = ProgressiveMesh::from_mesh(&mesh, 0.3).unwrap();
651
652 let _ = pm.reconstruct_at_ratio(-1.0);
654 let _ = pm.reconstruct_at_ratio(2.0);
655 }
656
657 #[test]
658 fn test_progressive_clamp_level() {
659 let mesh = make_plane_grid(6);
660 let pm = ProgressiveMesh::from_mesh(&mesh, 0.3).unwrap();
661
662 let _ = pm.reconstruct_at_level(999999);
664 }
665}