unity_asset_decode/mesh/
processor.rs

1//! Mesh processing implementation
2//!
3//! This module provides high-level mesh processing functionality including
4//! mesh export and optimization.
5
6use super::parser::MeshParser;
7use super::types::*;
8use crate::error::Result;
9use crate::object::UnityObject;
10use crate::unity_version::UnityVersion;
11
12/// Mesh processor
13///
14/// This struct provides high-level methods for processing Unity Mesh objects,
15/// including parsing, validation, and export functionality.
16pub struct MeshProcessor {
17    parser: MeshParser,
18    config: MeshConfig,
19}
20
21impl MeshProcessor {
22    /// Create a new Mesh processor
23    pub fn new(version: UnityVersion) -> Self {
24        Self {
25            parser: MeshParser::new(version),
26            config: MeshConfig::default(),
27        }
28    }
29
30    /// Create a Mesh processor with custom configuration
31    pub fn with_config(version: UnityVersion, config: MeshConfig) -> Self {
32        Self {
33            parser: MeshParser::new(version),
34            config,
35        }
36    }
37
38    /// Parse Mesh from Unity object
39    pub fn parse_mesh(&self, object: &UnityObject) -> Result<MeshResult> {
40        let mut result = self.parser.parse_from_unity_object(object)?;
41
42        // Apply configuration-based processing
43        if let Some(max_vertices) = self.config.max_vertex_count
44            && result.mesh.vertex_count() > max_vertices
45        {
46            result.add_warning(format!(
47                "Mesh has {} vertices, exceeding limit of {}",
48                result.mesh.vertex_count(),
49                max_vertices
50            ));
51        }
52
53        // Validate mesh if needed
54        if let Err(e) = self.validate_mesh(&result.mesh) {
55            result.add_warning(format!("Mesh validation failed: {}", e));
56        }
57
58        Ok(result)
59    }
60
61    /// Validate mesh data
62    pub fn validate_mesh(&self, mesh: &Mesh) -> Result<()> {
63        // Check basic validity
64        if mesh.name.is_empty() {
65            return Err(crate::error::BinaryError::invalid_data("Mesh has no name"));
66        }
67
68        if mesh.vertex_data.vertex_count == 0 {
69            return Err(crate::error::BinaryError::invalid_data(
70                "Mesh has no vertices",
71            ));
72        }
73
74        // Check submeshes
75        for (i, submesh) in mesh.sub_meshes.iter().enumerate() {
76            if !submesh.is_valid() {
77                return Err(crate::error::BinaryError::invalid_data(format!(
78                    "SubMesh {} is invalid",
79                    i
80                )));
81            }
82        }
83
84        // Check vertex count limits
85        if let Some(max_vertices) = self.config.max_vertex_count
86            && mesh.vertex_count() > max_vertices
87        {
88            return Err(crate::error::BinaryError::invalid_data(format!(
89                "Mesh vertex count {} exceeds limit {}",
90                mesh.vertex_count(),
91                max_vertices
92            )));
93        }
94
95        Ok(())
96    }
97
98    /// Export mesh to OBJ format
99    pub fn export_to_obj(&self, mesh: &Mesh) -> Result<String> {
100        let mut obj_data = String::new();
101
102        // Header
103        obj_data.push_str("# Exported from Unity Asset Parser\n");
104        obj_data.push_str(&format!("# Mesh: {}\n", mesh.name));
105        obj_data.push_str(&format!("# Vertices: {}\n", mesh.vertex_count()));
106        obj_data.push_str(&format!("# SubMeshes: {}\n", mesh.sub_meshes.len()));
107        obj_data.push('\n');
108
109        // Export vertices (placeholder - would need actual vertex data parsing)
110        obj_data.push_str("# Vertices\n");
111        for _i in 0..mesh.vertex_count() {
112            obj_data.push_str("v 0.0 0.0 0.0\n");
113        }
114
115        // Export normals (placeholder)
116        obj_data.push_str("\n# Normals\n");
117        for _i in 0..mesh.vertex_count() {
118            obj_data.push_str("vn 0.0 1.0 0.0\n");
119        }
120
121        // Export UV coordinates (placeholder)
122        obj_data.push_str("\n# UV Coordinates\n");
123        for _i in 0..mesh.vertex_count() {
124            obj_data.push_str("vt 0.0 0.0\n");
125        }
126
127        // Export faces (placeholder)
128        obj_data.push_str("\n# Faces\n");
129        if !mesh.sub_meshes.is_empty() {
130            for (i, sub_mesh) in mesh.sub_meshes.iter().enumerate() {
131                obj_data.push_str(&format!(
132                    "# SubMesh {}: {} triangles\n",
133                    i, sub_mesh.triangle_count
134                ));
135
136                // Would need to parse actual index data here
137                for j in 0..sub_mesh.triangle_count {
138                    let base = j * 3 + 1; // OBJ indices are 1-based
139                    obj_data.push_str(&format!(
140                        "f {}/{}/{} {}/{}/{} {}/{}/{}\n",
141                        base,
142                        base,
143                        base,
144                        base + 1,
145                        base + 1,
146                        base + 1,
147                        base + 2,
148                        base + 2,
149                        base + 2
150                    ));
151                }
152            }
153        }
154
155        Ok(obj_data)
156    }
157
158    /// Get mesh statistics
159    pub fn get_mesh_stats(&self, meshes: &[&Mesh]) -> MeshStats {
160        let mut stats = MeshStats {
161            total_meshes: meshes.len(),
162            ..Default::default()
163        };
164
165        for mesh in meshes {
166            stats.total_vertices += mesh.vertex_count();
167            stats.total_triangles += mesh.triangle_count();
168            stats.total_submeshes += mesh.sub_meshes.len() as u32;
169
170            if mesh.has_blend_shapes() {
171                stats.meshes_with_blend_shapes += 1;
172            }
173
174            if mesh.is_compressed() {
175                stats.compressed_meshes += 1;
176            }
177
178            if mesh.has_streaming_data() {
179                stats.streaming_meshes += 1;
180            }
181
182            // Track complexity
183            let vertex_count = mesh.vertex_count();
184            if vertex_count < 1000 {
185                stats.low_poly_meshes += 1;
186            } else if vertex_count < 10000 {
187                stats.medium_poly_meshes += 1;
188            } else {
189                stats.high_poly_meshes += 1;
190            }
191        }
192
193        if !meshes.is_empty() {
194            stats.average_vertices = stats.total_vertices as f32 / meshes.len() as f32;
195            stats.average_triangles = stats.total_triangles as f32 / meshes.len() as f32;
196        }
197
198        stats
199    }
200
201    /// Get supported mesh features for this Unity version
202    pub fn get_supported_features(&self) -> Vec<&'static str> {
203        let version = self.parser.version();
204        let mut features = vec!["basic_mesh", "sub_meshes", "vertex_data"];
205
206        if version.major >= 5 {
207            features.push("blend_shapes");
208            features.push("compressed_mesh");
209        }
210
211        if version.major >= 2017 {
212            features.push("mesh_optimization");
213            features.push("streaming_info");
214        }
215
216        if version.major >= 2018 {
217            features.push("mesh_usage_flags");
218        }
219
220        if version.major >= 2019 {
221            features.push("mesh_topology");
222            features.push("vertex_attributes");
223        }
224
225        features
226    }
227
228    /// Check if a feature is supported
229    pub fn is_feature_supported(&self, feature: &str) -> bool {
230        self.get_supported_features().contains(&feature)
231    }
232
233    /// Get the current configuration
234    pub fn config(&self) -> &MeshConfig {
235        &self.config
236    }
237
238    /// Set the configuration
239    pub fn set_config(&mut self, config: MeshConfig) {
240        self.config = config;
241    }
242
243    /// Get the Unity version
244    pub fn version(&self) -> &UnityVersion {
245        self.parser.version()
246    }
247
248    /// Set the Unity version
249    pub fn set_version(&mut self, version: UnityVersion) {
250        self.parser.set_version(version);
251    }
252
253    /// Extract vertex positions (if available)
254    pub fn extract_vertex_positions(&self, _mesh: &Mesh) -> Result<Vec<[f32; 3]>> {
255        // This would require parsing the actual vertex data
256        // For now, return empty vector
257        Ok(Vec::new())
258    }
259
260    /// Extract vertex normals (if available)
261    pub fn extract_vertex_normals(&self, _mesh: &Mesh) -> Result<Vec<[f32; 3]>> {
262        // This would require parsing the actual vertex data
263        // For now, return empty vector
264        Ok(Vec::new())
265    }
266
267    /// Extract UV coordinates (if available)
268    pub fn extract_uv_coordinates(&self, _mesh: &Mesh) -> Result<Vec<[f32; 2]>> {
269        // This would require parsing the actual vertex data
270        // For now, return empty vector
271        Ok(Vec::new())
272    }
273
274    /// Extract triangle indices
275    pub fn extract_triangle_indices(&self, _mesh: &Mesh) -> Result<Vec<u32>> {
276        // This would require parsing the index buffer
277        // For now, return empty vector
278        Ok(Vec::new())
279    }
280}
281
282impl Default for MeshProcessor {
283    fn default() -> Self {
284        Self::new(UnityVersion::default())
285    }
286}
287
288/// Mesh processing statistics
289#[derive(Debug, Clone, Default)]
290pub struct MeshStats {
291    pub total_meshes: usize,
292    pub total_vertices: u32,
293    pub total_triangles: u32,
294    pub total_submeshes: u32,
295    pub average_vertices: f32,
296    pub average_triangles: f32,
297    pub meshes_with_blend_shapes: usize,
298    pub compressed_meshes: usize,
299    pub streaming_meshes: usize,
300    pub low_poly_meshes: usize,    // < 1K vertices
301    pub medium_poly_meshes: usize, // 1K-10K vertices
302    pub high_poly_meshes: usize,   // > 10K vertices
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308
309    #[test]
310    fn test_processor_creation() {
311        let version = UnityVersion::default();
312        let processor = MeshProcessor::new(version);
313        assert_eq!(processor.version(), &UnityVersion::default());
314    }
315
316    #[test]
317    fn test_supported_features() {
318        let version = UnityVersion::parse_version("2020.3.12f1").unwrap();
319        let processor = MeshProcessor::new(version);
320
321        let features = processor.get_supported_features();
322        assert!(features.contains(&"basic_mesh"));
323        assert!(features.contains(&"blend_shapes"));
324        assert!(features.contains(&"mesh_optimization"));
325        assert!(features.contains(&"mesh_usage_flags"));
326        assert!(processor.is_feature_supported("vertex_attributes"));
327    }
328
329    #[test]
330    fn test_mesh_validation() {
331        let processor = MeshProcessor::default();
332        let mut mesh = Mesh::default();
333
334        // Invalid mesh (no name, no vertices)
335        assert!(processor.validate_mesh(&mesh).is_err());
336
337        // Valid mesh
338        mesh.name = "TestMesh".to_string();
339        mesh.vertex_data.vertex_count = 100;
340        assert!(processor.validate_mesh(&mesh).is_ok());
341    }
342
343    #[test]
344    fn test_mesh_stats() {
345        let processor = MeshProcessor::default();
346        let mut mesh1 = Mesh::default();
347        mesh1.vertex_data.vertex_count = 1000;
348        mesh1.sub_meshes.push(SubMesh {
349            triangle_count: 500,
350            ..Default::default()
351        });
352
353        let mut mesh2 = Mesh::default();
354        mesh2.vertex_data.vertex_count = 2000;
355        mesh2.sub_meshes.push(SubMesh {
356            triangle_count: 1000,
357            ..Default::default()
358        });
359
360        let meshes = vec![&mesh1, &mesh2];
361        let stats = processor.get_mesh_stats(&meshes);
362
363        assert_eq!(stats.total_meshes, 2);
364        assert_eq!(stats.total_vertices, 3000);
365        assert_eq!(stats.total_triangles, 1500);
366        assert_eq!(stats.average_vertices, 1500.0);
367    }
368}