1use threecrate_core::{TriangleMesh, Vector3f, Result, Error};
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(
135 mesh: TriangleMesh,
136 uvs: Option<Vec<UV>>,
137 tangents: Option<Vec<Tangent>>,
138 ) -> Self {
139 let mut extended = Self {
140 mesh,
141 uvs,
142 tangents,
143 metadata: MeshMetadata::default(),
144 };
145 extended.update_metadata();
146 extended
147 }
148
149 pub fn vertex_count(&self) -> usize {
151 self.mesh.vertex_count()
152 }
153
154 pub fn face_count(&self) -> usize {
156 self.mesh.face_count()
157 }
158
159 pub fn is_empty(&self) -> bool {
161 self.mesh.is_empty()
162 }
163
164 pub fn set_uvs(&mut self, uvs: Vec<UV>) {
166 if uvs.len() == self.vertex_count() {
167 self.uvs = Some(uvs);
168 self.metadata.uvs_loaded = true;
169 self.update_metadata();
170 }
171 }
172
173 pub fn set_tangents(&mut self, tangents: Vec<Tangent>) {
175 if tangents.len() == self.vertex_count() {
176 self.tangents = Some(tangents);
177 self.metadata.tangents_computed = true;
178 self.update_metadata();
179 }
180 }
181
182 pub fn process_attributes(&mut self, options: &MeshAttributeOptions) -> Result<()> {
184 if options.validate_attributes {
185 self.validate_attributes()?;
186 }
187
188 if options.recompute_normals && self.mesh.normals.is_none() {
189 self.compute_normals(options.smooth_normals, options.normalize_vectors)?;
190 }
191
192 if options.generate_default_uvs && self.uvs.is_none() {
193 self.generate_default_uvs()?;
194 }
195
196 if options.recompute_tangents && self.tangents.is_none() && self.uvs.is_some() {
197 self.compute_tangents(options.normalize_vectors)?;
198 }
199
200 self.update_metadata();
201 Ok(())
202 }
203
204 pub fn validate_attributes(&mut self) -> Result<()> {
206 let vertex_count = self.vertex_count();
207 self.metadata.validation_messages.clear();
208
209 if let Some(ref normals) = self.mesh.normals {
211 if normals.len() != vertex_count {
212 let msg = format!("Normal count mismatch: {} normals for {} vertices",
213 normals.len(), vertex_count);
214 self.metadata.validation_messages.push(msg.clone());
215 return Err(Error::InvalidData(msg));
216 }
217
218 for (i, normal) in normals.iter().enumerate() {
220 let length_sq = normal.x * normal.x + normal.y * normal.y + normal.z * normal.z;
221 if length_sq < 1e-6 {
222 let msg = format!("Zero-length normal at vertex {}", i);
223 self.metadata.validation_messages.push(msg);
224 }
225 }
226 }
227
228 if let Some(ref uvs) = self.uvs {
230 if uvs.len() != vertex_count {
231 let msg = format!("UV count mismatch: {} UVs for {} vertices",
232 uvs.len(), vertex_count);
233 self.metadata.validation_messages.push(msg.clone());
234 return Err(Error::InvalidData(msg));
235 }
236
237 for (i, uv) in uvs.iter().enumerate() {
239 if !uv[0].is_finite() || !uv[1].is_finite() {
240 let msg = format!("Invalid UV coordinates at vertex {}: [{}, {}]",
241 i, uv[0], uv[1]);
242 self.metadata.validation_messages.push(msg);
243 }
244 }
245 }
246
247 if let Some(ref tangents) = self.tangents {
249 if tangents.len() != vertex_count {
250 let msg = format!("Tangent count mismatch: {} tangents for {} vertices",
251 tangents.len(), vertex_count);
252 self.metadata.validation_messages.push(msg.clone());
253 return Err(Error::InvalidData(msg));
254 }
255
256 for (i, tangent) in tangents.iter().enumerate() {
258 let length_sq = tangent.vector.x * tangent.vector.x +
259 tangent.vector.y * tangent.vector.y +
260 tangent.vector.z * tangent.vector.z;
261 if length_sq < 1e-6 {
262 let msg = format!("Zero-length tangent at vertex {}", i);
263 self.metadata.validation_messages.push(msg);
264 }
265
266 if tangent.handedness.abs() != 1.0 {
267 let msg = format!("Invalid tangent handedness at vertex {}: {}",
268 i, tangent.handedness);
269 self.metadata.validation_messages.push(msg);
270 }
271 }
272 }
273
274 Ok(())
275 }
276
277 pub fn compute_normals(&mut self, smooth: bool, normalize: bool) -> Result<()> {
279 let vertex_count = self.vertex_count();
280 let mut normals = vec![Vector3f::new(0.0, 0.0, 0.0); vertex_count];
281
282 for face in &self.mesh.faces {
284 let v0 = self.mesh.vertices[face[0]];
285 let v1 = self.mesh.vertices[face[1]];
286 let v2 = self.mesh.vertices[face[2]];
287
288 let edge1 = v1 - v0;
289 let edge2 = v2 - v0;
290 let face_normal = edge1.cross(&edge2);
291
292 if smooth {
293 normals[face[0]] = normals[face[0]] + face_normal;
295 normals[face[1]] = normals[face[1]] + face_normal;
296 normals[face[2]] = normals[face[2]] + face_normal;
297 } else {
298 normals[face[0]] = face_normal;
300 normals[face[1]] = face_normal;
301 normals[face[2]] = face_normal;
302 }
303 }
304
305 if normalize {
307 for normal in &mut normals {
308 let length = (normal.x * normal.x + normal.y * normal.y + normal.z * normal.z).sqrt();
309 if length > 1e-6 {
310 *normal = Vector3f::new(
311 normal.x / length,
312 normal.y / length,
313 normal.z / length,
314 );
315 } else {
316 *normal = Vector3f::new(0.0, 0.0, 1.0); }
318 }
319 }
320
321 self.mesh.set_normals(normals);
322 self.metadata.normals_computed = true;
323 Ok(())
324 }
325
326 pub fn compute_tangents(&mut self, normalize: bool) -> Result<()> {
328 let uvs = self.uvs.as_ref()
329 .ok_or_else(|| Error::InvalidData("UV coordinates required for tangent computation".to_string()))?;
330
331 let vertex_count = self.vertex_count();
332 let mut tan1 = vec![Vector3f::new(0.0, 0.0, 0.0); vertex_count];
333 let mut tan2 = vec![Vector3f::new(0.0, 0.0, 0.0); vertex_count];
334
335 for face in &self.mesh.faces {
337 let i1 = face[0];
338 let i2 = face[1];
339 let i3 = face[2];
340
341 let v1 = self.mesh.vertices[i1];
342 let v2 = self.mesh.vertices[i2];
343 let v3 = self.mesh.vertices[i3];
344
345 let w1 = uvs[i1];
346 let w2 = uvs[i2];
347 let w3 = uvs[i3];
348
349 let x1 = v2.x - v1.x;
350 let x2 = v3.x - v1.x;
351 let y1 = v2.y - v1.y;
352 let y2 = v3.y - v1.y;
353 let z1 = v2.z - v1.z;
354 let z2 = v3.z - v1.z;
355
356 let s1 = w2[0] - w1[0];
357 let s2 = w3[0] - w1[0];
358 let t1 = w2[1] - w1[1];
359 let t2 = w3[1] - w1[1];
360
361 let det = s1 * t2 - s2 * t1;
362 let r = if det.abs() < 1e-6 { 1.0 } else { 1.0 / det };
363
364 let sdir = Vector3f::new(
365 (t2 * x1 - t1 * x2) * r,
366 (t2 * y1 - t1 * y2) * r,
367 (t2 * z1 - t1 * z2) * r,
368 );
369
370 let tdir = Vector3f::new(
371 (s1 * x2 - s2 * x1) * r,
372 (s1 * y2 - s2 * y1) * r,
373 (s1 * z2 - s2 * z1) * r,
374 );
375
376 tan1[i1] = tan1[i1] + sdir;
377 tan1[i2] = tan1[i2] + sdir;
378 tan1[i3] = tan1[i3] + sdir;
379
380 tan2[i1] = tan2[i1] + tdir;
381 tan2[i2] = tan2[i2] + tdir;
382 tan2[i3] = tan2[i3] + tdir;
383 }
384
385 let normals = self.mesh.normals.as_ref()
386 .ok_or_else(|| Error::InvalidData("Normals required for tangent computation".to_string()))?;
387
388 let mut tangents = Vec::with_capacity(vertex_count);
390
391 for i in 0..vertex_count {
392 let n = normals[i];
393 let t = tan1[i];
394
395 let tangent_vec = t - n * (n.x * t.x + n.y * t.y + n.z * t.z);
397
398 let tangent_vec = if normalize {
399 let length = (tangent_vec.x * tangent_vec.x +
400 tangent_vec.y * tangent_vec.y +
401 tangent_vec.z * tangent_vec.z).sqrt();
402 if length > 1e-6 {
403 Vector3f::new(
404 tangent_vec.x / length,
405 tangent_vec.y / length,
406 tangent_vec.z / length,
407 )
408 } else {
409 Vector3f::new(1.0, 0.0, 0.0) }
411 } else {
412 tangent_vec
413 };
414
415 let cross = n.cross(&tangent_vec);
417 let handedness = if cross.x * tan2[i].x + cross.y * tan2[i].y + cross.z * tan2[i].z < 0.0 {
418 -1.0
419 } else {
420 1.0
421 };
422
423 tangents.push(Tangent::new(tangent_vec, handedness));
424 }
425
426 self.tangents = Some(tangents);
427 self.metadata.tangents_computed = true;
428 Ok(())
429 }
430
431 pub fn generate_default_uvs(&mut self) -> Result<()> {
433 let vertex_count = self.vertex_count();
434
435 let mut min_x = f32::INFINITY;
437 let mut max_x = f32::NEG_INFINITY;
438 let mut min_y = f32::INFINITY;
439 let mut max_y = f32::NEG_INFINITY;
440 let mut min_z = f32::INFINITY;
441 let mut max_z = f32::NEG_INFINITY;
442
443 for vertex in &self.mesh.vertices {
444 min_x = min_x.min(vertex.x);
445 max_x = max_x.max(vertex.x);
446 min_y = min_y.min(vertex.y);
447 max_y = max_y.max(vertex.y);
448 min_z = min_z.min(vertex.z);
449 max_z = max_z.max(vertex.z);
450 }
451
452 let size_x = max_x - min_x;
453 let size_y = max_y - min_y;
454 let size_z = max_z - min_z;
455
456 let mut uvs = Vec::with_capacity(vertex_count);
458
459 if size_x >= size_y && size_x >= size_z {
460 for vertex in &self.mesh.vertices {
462 let u = if size_y > 1e-6 { (vertex.y - min_y) / size_y } else { 0.5 };
463 let v = if size_z > 1e-6 { (vertex.z - min_z) / size_z } else { 0.5 };
464 uvs.push([u, v]);
465 }
466 } else if size_y >= size_x && size_y >= size_z {
467 for vertex in &self.mesh.vertices {
469 let u = if size_x > 1e-6 { (vertex.x - min_x) / size_x } else { 0.5 };
470 let v = if size_z > 1e-6 { (vertex.z - min_z) / size_z } else { 0.5 };
471 uvs.push([u, v]);
472 }
473 } else {
474 for vertex in &self.mesh.vertices {
476 let u = if size_x > 1e-6 { (vertex.x - min_x) / size_x } else { 0.5 };
477 let v = if size_y > 1e-6 { (vertex.y - min_y) / size_y } else { 0.5 };
478 uvs.push([u, v]);
479 }
480 }
481
482 self.uvs = Some(uvs);
483 Ok(())
484 }
485
486 fn update_metadata(&mut self) {
488 let vertex_count = self.vertex_count();
489 if vertex_count == 0 {
490 self.metadata.completeness_score = 0.0;
491 return;
492 }
493
494 let mut score = 1.0; if let Some(ref normals) = self.mesh.normals {
498 if normals.len() == vertex_count {
499 score += 1.0;
500 } else {
501 score += 0.5; }
503 }
504
505 if let Some(ref uvs) = self.uvs {
507 if uvs.len() == vertex_count {
508 score += 1.0;
509 } else {
510 score += 0.5; }
512 }
513
514 if let Some(ref tangents) = self.tangents {
516 if tangents.len() == vertex_count {
517 score += 1.0;
518 } else {
519 score += 0.5; }
521 }
522
523 self.metadata.completeness_score = score / 4.0; }
525
526 pub fn to_triangle_mesh(self) -> TriangleMesh {
528 self.mesh
529 }
530}
531
532impl MeshMetadata {
533 pub fn from_loaded(format: &str, _has_normals: bool, has_uvs: bool, _has_tangents: bool) -> Self {
535 Self {
536 normals_computed: false,
537 tangents_computed: false,
538 uvs_loaded: has_uvs,
539 validation_messages: Vec::new(),
540 source_format: Some(format.to_string()),
541 completeness_score: 0.0, }
543 }
544
545 pub fn is_complete(&self) -> bool {
547 self.completeness_score >= 0.75 }
549
550 pub fn missing_attributes(&self) -> Vec<&'static str> {
552 let mut missing = Vec::new();
553
554 if !self.normals_computed && self.completeness_score < 0.5 {
555 missing.push("normals");
556 }
557 if !self.uvs_loaded {
558 missing.push("uvs");
559 }
560 if !self.tangents_computed {
561 missing.push("tangents");
562 }
563
564 missing
565 }
566}
567
568pub mod utils {
570 use super::*;
571
572 pub fn extend_mesh(
574 mesh: TriangleMesh,
575 options: &MeshAttributeOptions
576 ) -> Result<ExtendedTriangleMesh> {
577 let mut extended = ExtendedTriangleMesh::from_mesh(mesh);
578 extended.process_attributes(options)?;
579 Ok(extended)
580 }
581
582 pub fn ensure_basic_attributes(mesh: &mut ExtendedTriangleMesh) -> Result<()> {
584 let options = MeshAttributeOptions {
585 recompute_normals: true,
586 recompute_tangents: false,
587 generate_default_uvs: false,
588 validate_attributes: true,
589 normalize_vectors: true,
590 smooth_normals: true,
591 };
592 mesh.process_attributes(&options)
593 }
594
595 pub fn prepare_for_serialization(
597 mesh: &mut ExtendedTriangleMesh,
598 format: &str
599 ) -> Result<()> {
600 let options = match format.to_lowercase().as_str() {
601 "obj" => MeshAttributeOptions {
602 recompute_normals: true,
603 recompute_tangents: false, generate_default_uvs: true, validate_attributes: true,
606 normalize_vectors: true,
607 smooth_normals: true,
608 },
609 "ply" => MeshAttributeOptions {
610 recompute_normals: true,
611 recompute_tangents: false, generate_default_uvs: false, validate_attributes: true,
614 normalize_vectors: true,
615 smooth_normals: true,
616 },
617 _ => MeshAttributeOptions::default(),
618 };
619
620 mesh.process_attributes(&options)?;
621 mesh.metadata.source_format = Some(format.to_string());
622 Ok(())
623 }
624
625 pub fn validate_round_trip(mesh: &ExtendedTriangleMesh) -> Result<Vec<String>> {
627 let mut warnings = Vec::new();
628
629 if mesh.vertex_count() == 0 {
630 warnings.push("Empty mesh - no vertices".to_string());
631 }
632
633 if mesh.face_count() == 0 {
634 warnings.push("Mesh has no faces".to_string());
635 }
636
637 if mesh.mesh.normals.is_none() {
638 warnings.push("Missing normals - may be recomputed on load".to_string());
639 }
640
641 if mesh.uvs.is_none() {
642 warnings.push("Missing UV coordinates - texture mapping not available".to_string());
643 }
644
645 if mesh.tangents.is_none() && mesh.uvs.is_some() {
646 warnings.push("Missing tangents - normal mapping may not work correctly".to_string());
647 }
648
649 if !mesh.metadata.validation_messages.is_empty() {
650 warnings.extend(mesh.metadata.validation_messages.iter().cloned());
651 }
652
653 Ok(warnings)
654 }
655}
656
657#[cfg(test)]
658mod tests {
659 use super::*;
660
661 fn create_test_triangle() -> TriangleMesh {
662 let vertices = vec![
663 Point3f::new(0.0, 0.0, 0.0),
664 Point3f::new(1.0, 0.0, 0.0),
665 Point3f::new(0.5, 1.0, 0.0),
666 ];
667 let faces = vec![[0, 1, 2]];
668 TriangleMesh::from_vertices_and_faces(vertices, faces)
669 }
670
671 #[test]
672 fn test_extended_mesh_creation() {
673 let base_mesh = create_test_triangle();
674 let extended = ExtendedTriangleMesh::from_mesh(base_mesh);
675
676 assert_eq!(extended.vertex_count(), 3);
677 assert_eq!(extended.face_count(), 1);
678 assert!(extended.uvs.is_none());
679 assert!(extended.tangents.is_none());
680 }
681
682 #[test]
683 fn test_normal_computation() {
684 let base_mesh = create_test_triangle();
685 let mut extended = ExtendedTriangleMesh::from_mesh(base_mesh);
686
687 extended.compute_normals(true, true).unwrap();
688
689 assert!(extended.mesh.normals.is_some());
690 let normals = extended.mesh.normals.unwrap();
691 assert_eq!(normals.len(), 3);
692
693 for normal in &normals {
695 assert!((normal.z - 1.0).abs() < 1e-5);
696 }
697 }
698
699 #[test]
700 fn test_uv_generation() {
701 let base_mesh = create_test_triangle();
702 let mut extended = ExtendedTriangleMesh::from_mesh(base_mesh);
703
704 extended.generate_default_uvs().unwrap();
705
706 assert!(extended.uvs.is_some());
707 let uvs = extended.uvs.unwrap();
708 assert_eq!(uvs.len(), 3);
709
710 for uv in &uvs {
712 assert!(uv[0] >= 0.0 && uv[0] <= 1.0);
713 assert!(uv[1] >= 0.0 && uv[1] <= 1.0);
714 }
715 }
716
717 #[test]
718 fn test_tangent_computation() {
719 let base_mesh = create_test_triangle();
720 let mut extended = ExtendedTriangleMesh::from_mesh(base_mesh);
721
722 extended.compute_normals(true, true).unwrap();
724 extended.generate_default_uvs().unwrap();
725 extended.compute_tangents(true).unwrap();
726
727 assert!(extended.tangents.is_some());
728 let tangents = extended.tangents.unwrap();
729 assert_eq!(tangents.len(), 3);
730
731 for tangent in &tangents {
733 let length_sq = tangent.vector.x * tangent.vector.x +
734 tangent.vector.y * tangent.vector.y +
735 tangent.vector.z * tangent.vector.z;
736 assert!((length_sq - 1.0).abs() < 1e-5); assert!(tangent.handedness.abs() == 1.0); }
739 }
740
741 #[test]
742 fn test_attribute_validation() {
743 let base_mesh = create_test_triangle();
744 let mut extended = ExtendedTriangleMesh::from_mesh(base_mesh);
745
746 assert!(extended.validate_attributes().is_ok());
748
749 extended.uvs = Some(vec![[0.0, 0.0]]); assert!(extended.validate_attributes().is_err());
752 }
753
754 #[test]
755 fn test_process_attributes() {
756 let base_mesh = create_test_triangle();
757 let mut extended = ExtendedTriangleMesh::from_mesh(base_mesh);
758
759 let options = MeshAttributeOptions::recompute_all();
760 extended.process_attributes(&options).unwrap();
761
762 assert!(extended.mesh.normals.is_some());
763 assert!(extended.uvs.is_some());
764 assert!(extended.tangents.is_some());
765 assert!(extended.metadata.normals_computed);
766 assert!(extended.metadata.tangents_computed);
767 }
768
769 #[test]
770 fn test_metadata_completeness() {
771 let base_mesh = create_test_triangle();
772 let mut extended = ExtendedTriangleMesh::from_mesh(base_mesh);
773
774 assert!(!extended.metadata.is_complete());
776
777 let options = MeshAttributeOptions::recompute_all();
779 extended.process_attributes(&options).unwrap();
780
781 assert!(extended.metadata.is_complete());
783 assert!(extended.metadata.completeness_score > 0.75);
784 }
785}