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