1use threecrate_core::{Error, Result, TriangleMesh, Vector3f};
8
9#[cfg(test)]
10use threecrate_core::Point3f;
11
12pub type UV = [f32; 2];
14
15#[derive(Debug, Clone, Copy, PartialEq)]
17pub struct Tangent {
18 pub vector: Vector3f,
20 pub handedness: f32,
22}
23
24#[derive(Debug, Clone)]
26pub struct ExtendedTriangleMesh {
27 pub mesh: TriangleMesh,
29 pub uvs: Option<Vec<UV>>,
31 pub tangents: Option<Vec<Tangent>>,
33 pub metadata: MeshMetadata,
35}
36
37#[derive(Debug, Clone, Default)]
39pub struct MeshMetadata {
40 pub normals_computed: bool,
42 pub tangents_computed: bool,
44 pub uvs_loaded: bool,
46 pub validation_messages: Vec<String>,
48 pub source_format: Option<String>,
50 pub completeness_score: f32,
52}
53
54#[derive(Debug, Clone)]
56pub struct MeshAttributeOptions {
57 pub recompute_normals: bool,
59 pub recompute_tangents: bool,
61 pub generate_default_uvs: bool,
63 pub validate_attributes: bool,
65 pub normalize_vectors: bool,
67 pub smooth_normals: bool,
69}
70
71impl Default for MeshAttributeOptions {
72 fn default() -> Self {
73 Self {
74 recompute_normals: true,
75 recompute_tangents: false, generate_default_uvs: false,
77 validate_attributes: true,
78 normalize_vectors: true,
79 smooth_normals: true,
80 }
81 }
82}
83
84impl MeshAttributeOptions {
85 pub fn recompute_all() -> Self {
87 Self {
88 recompute_normals: true,
89 recompute_tangents: true,
90 generate_default_uvs: true,
91 validate_attributes: true,
92 normalize_vectors: true,
93 smooth_normals: true,
94 }
95 }
96
97 pub fn validate_only() -> Self {
99 Self {
100 recompute_normals: false,
101 recompute_tangents: false,
102 generate_default_uvs: false,
103 validate_attributes: true,
104 normalize_vectors: false,
105 smooth_normals: false,
106 }
107 }
108}
109
110impl Tangent {
111 pub fn new(vector: Vector3f, handedness: f32) -> Self {
113 Self { vector, handedness }
114 }
115
116 pub fn from_vector(vector: Vector3f) -> Self {
118 Self::new(vector, 1.0)
119 }
120}
121
122impl ExtendedTriangleMesh {
123 pub fn from_mesh(mesh: TriangleMesh) -> Self {
125 Self {
126 mesh,
127 uvs: None,
128 tangents: None,
129 metadata: MeshMetadata::default(),
130 }
131 }
132
133 pub fn new(mesh: TriangleMesh, uvs: Option<Vec<UV>>, tangents: Option<Vec<Tangent>>) -> Self {
135 let mut extended = Self {
136 mesh,
137 uvs,
138 tangents,
139 metadata: MeshMetadata::default(),
140 };
141 extended.update_metadata();
142 extended
143 }
144
145 pub fn vertex_count(&self) -> usize {
147 self.mesh.vertex_count()
148 }
149
150 pub fn face_count(&self) -> usize {
152 self.mesh.face_count()
153 }
154
155 pub fn is_empty(&self) -> bool {
157 self.mesh.is_empty()
158 }
159
160 pub fn set_uvs(&mut self, uvs: Vec<UV>) {
162 if uvs.len() == self.vertex_count() {
163 self.uvs = Some(uvs);
164 self.metadata.uvs_loaded = true;
165 self.update_metadata();
166 }
167 }
168
169 pub fn set_tangents(&mut self, tangents: Vec<Tangent>) {
171 if tangents.len() == self.vertex_count() {
172 self.tangents = Some(tangents);
173 self.metadata.tangents_computed = true;
174 self.update_metadata();
175 }
176 }
177
178 pub fn process_attributes(&mut self, options: &MeshAttributeOptions) -> Result<()> {
180 if options.validate_attributes {
181 self.validate_attributes()?;
182 }
183
184 if options.recompute_normals && self.mesh.normals.is_none() {
185 self.compute_normals(options.smooth_normals, options.normalize_vectors)?;
186 }
187
188 if options.generate_default_uvs && self.uvs.is_none() {
189 self.generate_default_uvs()?;
190 }
191
192 if options.recompute_tangents && self.tangents.is_none() && self.uvs.is_some() {
193 self.compute_tangents(options.normalize_vectors)?;
194 }
195
196 self.update_metadata();
197 Ok(())
198 }
199
200 pub fn validate_attributes(&mut self) -> Result<()> {
202 let vertex_count = self.vertex_count();
203 self.metadata.validation_messages.clear();
204
205 if let Some(ref normals) = self.mesh.normals {
207 if normals.len() != vertex_count {
208 let msg = format!(
209 "Normal count mismatch: {} normals for {} vertices",
210 normals.len(),
211 vertex_count
212 );
213 self.metadata.validation_messages.push(msg.clone());
214 return Err(Error::InvalidData(msg));
215 }
216
217 for (i, normal) in normals.iter().enumerate() {
219 let length_sq = normal.x * normal.x + normal.y * normal.y + normal.z * normal.z;
220 if length_sq < 1e-6 {
221 let msg = format!("Zero-length normal at vertex {}", i);
222 self.metadata.validation_messages.push(msg);
223 }
224 }
225 }
226
227 if let Some(ref uvs) = self.uvs {
229 if uvs.len() != vertex_count {
230 let msg = format!(
231 "UV count mismatch: {} UVs for {} vertices",
232 uvs.len(),
233 vertex_count
234 );
235 self.metadata.validation_messages.push(msg.clone());
236 return Err(Error::InvalidData(msg));
237 }
238
239 for (i, uv) in uvs.iter().enumerate() {
241 if !uv[0].is_finite() || !uv[1].is_finite() {
242 let msg = format!(
243 "Invalid UV coordinates at vertex {}: [{}, {}]",
244 i, uv[0], uv[1]
245 );
246 self.metadata.validation_messages.push(msg);
247 }
248 }
249 }
250
251 if let Some(ref tangents) = self.tangents {
253 if tangents.len() != vertex_count {
254 let msg = format!(
255 "Tangent count mismatch: {} tangents for {} vertices",
256 tangents.len(),
257 vertex_count
258 );
259 self.metadata.validation_messages.push(msg.clone());
260 return Err(Error::InvalidData(msg));
261 }
262
263 for (i, tangent) in tangents.iter().enumerate() {
265 let length_sq = tangent.vector.x * tangent.vector.x
266 + tangent.vector.y * tangent.vector.y
267 + tangent.vector.z * tangent.vector.z;
268 if length_sq < 1e-6 {
269 let msg = format!("Zero-length tangent at vertex {}", i);
270 self.metadata.validation_messages.push(msg);
271 }
272
273 if tangent.handedness.abs() != 1.0 {
274 let msg = format!(
275 "Invalid tangent handedness at vertex {}: {}",
276 i, tangent.handedness
277 );
278 self.metadata.validation_messages.push(msg);
279 }
280 }
281 }
282
283 Ok(())
284 }
285
286 pub fn compute_normals(&mut self, smooth: bool, normalize: bool) -> Result<()> {
288 let vertex_count = self.vertex_count();
289 let mut normals = vec![Vector3f::new(0.0, 0.0, 0.0); vertex_count];
290
291 for face in &self.mesh.faces {
293 let v0 = self.mesh.vertices[face[0]];
294 let v1 = self.mesh.vertices[face[1]];
295 let v2 = self.mesh.vertices[face[2]];
296
297 let edge1 = v1 - v0;
298 let edge2 = v2 - v0;
299 let face_normal = edge1.cross(&edge2);
300
301 if smooth {
302 normals[face[0]] = normals[face[0]] + face_normal;
304 normals[face[1]] = normals[face[1]] + face_normal;
305 normals[face[2]] = normals[face[2]] + face_normal;
306 } else {
307 normals[face[0]] = face_normal;
309 normals[face[1]] = face_normal;
310 normals[face[2]] = face_normal;
311 }
312 }
313
314 if normalize {
316 for normal in &mut normals {
317 let length =
318 (normal.x * normal.x + normal.y * normal.y + normal.z * normal.z).sqrt();
319 if length > 1e-6 {
320 *normal =
321 Vector3f::new(normal.x / length, normal.y / length, normal.z / length);
322 } else {
323 *normal = Vector3f::new(0.0, 0.0, 1.0); }
325 }
326 }
327
328 self.mesh.set_normals(normals);
329 self.metadata.normals_computed = true;
330 Ok(())
331 }
332
333 pub fn compute_tangents(&mut self, normalize: bool) -> Result<()> {
335 let uvs = self.uvs.as_ref().ok_or_else(|| {
336 Error::InvalidData("UV coordinates required for tangent computation".to_string())
337 })?;
338
339 let vertex_count = self.vertex_count();
340 let mut tan1 = vec![Vector3f::new(0.0, 0.0, 0.0); vertex_count];
341 let mut tan2 = vec![Vector3f::new(0.0, 0.0, 0.0); vertex_count];
342
343 for face in &self.mesh.faces {
345 let i1 = face[0];
346 let i2 = face[1];
347 let i3 = face[2];
348
349 let v1 = self.mesh.vertices[i1];
350 let v2 = self.mesh.vertices[i2];
351 let v3 = self.mesh.vertices[i3];
352
353 let w1 = uvs[i1];
354 let w2 = uvs[i2];
355 let w3 = uvs[i3];
356
357 let x1 = v2.x - v1.x;
358 let x2 = v3.x - v1.x;
359 let y1 = v2.y - v1.y;
360 let y2 = v3.y - v1.y;
361 let z1 = v2.z - v1.z;
362 let z2 = v3.z - v1.z;
363
364 let s1 = w2[0] - w1[0];
365 let s2 = w3[0] - w1[0];
366 let t1 = w2[1] - w1[1];
367 let t2 = w3[1] - w1[1];
368
369 let det = s1 * t2 - s2 * t1;
370 let r = if det.abs() < 1e-6 { 1.0 } else { 1.0 / det };
371
372 let sdir = Vector3f::new(
373 (t2 * x1 - t1 * x2) * r,
374 (t2 * y1 - t1 * y2) * r,
375 (t2 * z1 - t1 * z2) * r,
376 );
377
378 let tdir = Vector3f::new(
379 (s1 * x2 - s2 * x1) * r,
380 (s1 * y2 - s2 * y1) * r,
381 (s1 * z2 - s2 * z1) * r,
382 );
383
384 tan1[i1] = tan1[i1] + sdir;
385 tan1[i2] = tan1[i2] + sdir;
386 tan1[i3] = tan1[i3] + sdir;
387
388 tan2[i1] = tan2[i1] + tdir;
389 tan2[i2] = tan2[i2] + tdir;
390 tan2[i3] = tan2[i3] + tdir;
391 }
392
393 let normals = self.mesh.normals.as_ref().ok_or_else(|| {
394 Error::InvalidData("Normals required for tangent computation".to_string())
395 })?;
396
397 let mut tangents = Vec::with_capacity(vertex_count);
399
400 for i in 0..vertex_count {
401 let n = normals[i];
402 let t = tan1[i];
403
404 let tangent_vec = t - n * (n.x * t.x + n.y * t.y + n.z * t.z);
406
407 let tangent_vec = if normalize {
408 let length = (tangent_vec.x * tangent_vec.x
409 + tangent_vec.y * tangent_vec.y
410 + tangent_vec.z * tangent_vec.z)
411 .sqrt();
412 if length > 1e-6 {
413 Vector3f::new(
414 tangent_vec.x / length,
415 tangent_vec.y / length,
416 tangent_vec.z / length,
417 )
418 } else {
419 Vector3f::new(1.0, 0.0, 0.0) }
421 } else {
422 tangent_vec
423 };
424
425 let cross = n.cross(&tangent_vec);
427 let handedness =
428 if cross.x * tan2[i].x + cross.y * tan2[i].y + cross.z * tan2[i].z < 0.0 {
429 -1.0
430 } else {
431 1.0
432 };
433
434 tangents.push(Tangent::new(tangent_vec, handedness));
435 }
436
437 self.tangents = Some(tangents);
438 self.metadata.tangents_computed = true;
439 Ok(())
440 }
441
442 pub fn generate_default_uvs(&mut self) -> Result<()> {
444 let vertex_count = self.vertex_count();
445
446 let mut min_x = f32::INFINITY;
448 let mut max_x = f32::NEG_INFINITY;
449 let mut min_y = f32::INFINITY;
450 let mut max_y = f32::NEG_INFINITY;
451 let mut min_z = f32::INFINITY;
452 let mut max_z = f32::NEG_INFINITY;
453
454 for vertex in &self.mesh.vertices {
455 min_x = min_x.min(vertex.x);
456 max_x = max_x.max(vertex.x);
457 min_y = min_y.min(vertex.y);
458 max_y = max_y.max(vertex.y);
459 min_z = min_z.min(vertex.z);
460 max_z = max_z.max(vertex.z);
461 }
462
463 let size_x = max_x - min_x;
464 let size_y = max_y - min_y;
465 let size_z = max_z - min_z;
466
467 let mut uvs = Vec::with_capacity(vertex_count);
469
470 if size_x >= size_y && size_x >= size_z {
471 for vertex in &self.mesh.vertices {
473 let u = if size_y > 1e-6 {
474 (vertex.y - min_y) / size_y
475 } else {
476 0.5
477 };
478 let v = if size_z > 1e-6 {
479 (vertex.z - min_z) / size_z
480 } else {
481 0.5
482 };
483 uvs.push([u, v]);
484 }
485 } else if size_y >= size_x && size_y >= size_z {
486 for vertex in &self.mesh.vertices {
488 let u = if size_x > 1e-6 {
489 (vertex.x - min_x) / size_x
490 } else {
491 0.5
492 };
493 let v = if size_z > 1e-6 {
494 (vertex.z - min_z) / size_z
495 } else {
496 0.5
497 };
498 uvs.push([u, v]);
499 }
500 } else {
501 for vertex in &self.mesh.vertices {
503 let u = if size_x > 1e-6 {
504 (vertex.x - min_x) / size_x
505 } else {
506 0.5
507 };
508 let v = if size_y > 1e-6 {
509 (vertex.y - min_y) / size_y
510 } else {
511 0.5
512 };
513 uvs.push([u, v]);
514 }
515 }
516
517 self.uvs = Some(uvs);
518 Ok(())
519 }
520
521 fn update_metadata(&mut self) {
523 let vertex_count = self.vertex_count();
524 if vertex_count == 0 {
525 self.metadata.completeness_score = 0.0;
526 return;
527 }
528
529 let mut score = 1.0; if let Some(ref normals) = self.mesh.normals {
533 if normals.len() == vertex_count {
534 score += 1.0;
535 } else {
536 score += 0.5; }
538 }
539
540 if let Some(ref uvs) = self.uvs {
542 if uvs.len() == vertex_count {
543 score += 1.0;
544 } else {
545 score += 0.5; }
547 }
548
549 if let Some(ref tangents) = self.tangents {
551 if tangents.len() == vertex_count {
552 score += 1.0;
553 } else {
554 score += 0.5; }
556 }
557
558 self.metadata.completeness_score = score / 4.0; }
560
561 pub fn to_triangle_mesh(self) -> TriangleMesh {
563 self.mesh
564 }
565}
566
567impl MeshMetadata {
568 pub fn from_loaded(
570 format: &str,
571 _has_normals: bool,
572 has_uvs: bool,
573 _has_tangents: bool,
574 ) -> Self {
575 Self {
576 normals_computed: false,
577 tangents_computed: false,
578 uvs_loaded: has_uvs,
579 validation_messages: Vec::new(),
580 source_format: Some(format.to_string()),
581 completeness_score: 0.0, }
583 }
584
585 pub fn is_complete(&self) -> bool {
587 self.completeness_score >= 0.75 }
589
590 pub fn missing_attributes(&self) -> Vec<&'static str> {
592 let mut missing = Vec::new();
593
594 if !self.normals_computed && self.completeness_score < 0.5 {
595 missing.push("normals");
596 }
597 if !self.uvs_loaded {
598 missing.push("uvs");
599 }
600 if !self.tangents_computed {
601 missing.push("tangents");
602 }
603
604 missing
605 }
606}
607
608pub mod utils {
610 use super::*;
611
612 pub fn extend_mesh(
614 mesh: TriangleMesh,
615 options: &MeshAttributeOptions,
616 ) -> Result<ExtendedTriangleMesh> {
617 let mut extended = ExtendedTriangleMesh::from_mesh(mesh);
618 extended.process_attributes(options)?;
619 Ok(extended)
620 }
621
622 pub fn ensure_basic_attributes(mesh: &mut ExtendedTriangleMesh) -> Result<()> {
624 let options = MeshAttributeOptions {
625 recompute_normals: true,
626 recompute_tangents: false,
627 generate_default_uvs: false,
628 validate_attributes: true,
629 normalize_vectors: true,
630 smooth_normals: true,
631 };
632 mesh.process_attributes(&options)
633 }
634
635 pub fn prepare_for_serialization(mesh: &mut ExtendedTriangleMesh, format: &str) -> Result<()> {
637 let options = match format.to_lowercase().as_str() {
638 "obj" => MeshAttributeOptions {
639 recompute_normals: true,
640 recompute_tangents: false, generate_default_uvs: true, validate_attributes: true,
643 normalize_vectors: true,
644 smooth_normals: true,
645 },
646 "ply" => MeshAttributeOptions {
647 recompute_normals: true,
648 recompute_tangents: false, generate_default_uvs: false, validate_attributes: true,
651 normalize_vectors: true,
652 smooth_normals: true,
653 },
654 _ => MeshAttributeOptions::default(),
655 };
656
657 mesh.process_attributes(&options)?;
658 mesh.metadata.source_format = Some(format.to_string());
659 Ok(())
660 }
661
662 pub fn validate_round_trip(mesh: &ExtendedTriangleMesh) -> Result<Vec<String>> {
664 let mut warnings = Vec::new();
665
666 if mesh.vertex_count() == 0 {
667 warnings.push("Empty mesh - no vertices".to_string());
668 }
669
670 if mesh.face_count() == 0 {
671 warnings.push("Mesh has no faces".to_string());
672 }
673
674 if mesh.mesh.normals.is_none() {
675 warnings.push("Missing normals - may be recomputed on load".to_string());
676 }
677
678 if mesh.uvs.is_none() {
679 warnings.push("Missing UV coordinates - texture mapping not available".to_string());
680 }
681
682 if mesh.tangents.is_none() && mesh.uvs.is_some() {
683 warnings.push("Missing tangents - normal mapping may not work correctly".to_string());
684 }
685
686 if !mesh.metadata.validation_messages.is_empty() {
687 warnings.extend(mesh.metadata.validation_messages.iter().cloned());
688 }
689
690 Ok(warnings)
691 }
692}
693
694#[cfg(test)]
695mod tests {
696 use super::*;
697
698 fn create_test_triangle() -> TriangleMesh {
699 let vertices = vec![
700 Point3f::new(0.0, 0.0, 0.0),
701 Point3f::new(1.0, 0.0, 0.0),
702 Point3f::new(0.5, 1.0, 0.0),
703 ];
704 let faces = vec![[0, 1, 2]];
705 TriangleMesh::from_vertices_and_faces(vertices, faces)
706 }
707
708 #[test]
709 fn test_extended_mesh_creation() {
710 let base_mesh = create_test_triangle();
711 let extended = ExtendedTriangleMesh::from_mesh(base_mesh);
712
713 assert_eq!(extended.vertex_count(), 3);
714 assert_eq!(extended.face_count(), 1);
715 assert!(extended.uvs.is_none());
716 assert!(extended.tangents.is_none());
717 }
718
719 #[test]
720 fn test_normal_computation() {
721 let base_mesh = create_test_triangle();
722 let mut extended = ExtendedTriangleMesh::from_mesh(base_mesh);
723
724 extended.compute_normals(true, true).unwrap();
725
726 assert!(extended.mesh.normals.is_some());
727 let normals = extended.mesh.normals.unwrap();
728 assert_eq!(normals.len(), 3);
729
730 for normal in &normals {
732 assert!((normal.z - 1.0).abs() < 1e-5);
733 }
734 }
735
736 #[test]
737 fn test_uv_generation() {
738 let base_mesh = create_test_triangle();
739 let mut extended = ExtendedTriangleMesh::from_mesh(base_mesh);
740
741 extended.generate_default_uvs().unwrap();
742
743 assert!(extended.uvs.is_some());
744 let uvs = extended.uvs.unwrap();
745 assert_eq!(uvs.len(), 3);
746
747 for uv in &uvs {
749 assert!(uv[0] >= 0.0 && uv[0] <= 1.0);
750 assert!(uv[1] >= 0.0 && uv[1] <= 1.0);
751 }
752 }
753
754 #[test]
755 fn test_tangent_computation() {
756 let base_mesh = create_test_triangle();
757 let mut extended = ExtendedTriangleMesh::from_mesh(base_mesh);
758
759 extended.compute_normals(true, true).unwrap();
761 extended.generate_default_uvs().unwrap();
762 extended.compute_tangents(true).unwrap();
763
764 assert!(extended.tangents.is_some());
765 let tangents = extended.tangents.unwrap();
766 assert_eq!(tangents.len(), 3);
767
768 for tangent in &tangents {
770 let length_sq = tangent.vector.x * tangent.vector.x
771 + tangent.vector.y * tangent.vector.y
772 + tangent.vector.z * tangent.vector.z;
773 assert!((length_sq - 1.0).abs() < 1e-5); assert!(tangent.handedness.abs() == 1.0); }
776 }
777
778 #[test]
779 fn test_attribute_validation() {
780 let base_mesh = create_test_triangle();
781 let mut extended = ExtendedTriangleMesh::from_mesh(base_mesh);
782
783 assert!(extended.validate_attributes().is_ok());
785
786 extended.uvs = Some(vec![[0.0, 0.0]]); assert!(extended.validate_attributes().is_err());
789 }
790
791 #[test]
792 fn test_process_attributes() {
793 let base_mesh = create_test_triangle();
794 let mut extended = ExtendedTriangleMesh::from_mesh(base_mesh);
795
796 let options = MeshAttributeOptions::recompute_all();
797 extended.process_attributes(&options).unwrap();
798
799 assert!(extended.mesh.normals.is_some());
800 assert!(extended.uvs.is_some());
801 assert!(extended.tangents.is_some());
802 assert!(extended.metadata.normals_computed);
803 assert!(extended.metadata.tangents_computed);
804 }
805
806 #[test]
807 fn test_metadata_completeness() {
808 let base_mesh = create_test_triangle();
809 let mut extended = ExtendedTriangleMesh::from_mesh(base_mesh);
810
811 assert!(!extended.metadata.is_complete());
813
814 let options = MeshAttributeOptions::recompute_all();
816 extended.process_attributes(&options).unwrap();
817
818 assert!(extended.metadata.is_complete());
820 assert!(extended.metadata.completeness_score > 0.75);
821 }
822}