unity_asset_decode/mesh/
processor.rs1use super::parser::MeshParser;
7use super::types::*;
8use crate::error::Result;
9use crate::object::UnityObject;
10use crate::unity_version::UnityVersion;
11
12pub struct MeshProcessor {
17 parser: MeshParser,
18 config: MeshConfig,
19}
20
21impl MeshProcessor {
22 pub fn new(version: UnityVersion) -> Self {
24 Self {
25 parser: MeshParser::new(version),
26 config: MeshConfig::default(),
27 }
28 }
29
30 pub fn with_config(version: UnityVersion, config: MeshConfig) -> Self {
32 Self {
33 parser: MeshParser::new(version),
34 config,
35 }
36 }
37
38 pub fn parse_mesh(&self, object: &UnityObject) -> Result<MeshResult> {
40 let mut result = self.parser.parse_from_unity_object(object)?;
41
42 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 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 pub fn validate_mesh(&self, mesh: &Mesh) -> Result<()> {
63 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 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 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 pub fn export_to_obj(&self, mesh: &Mesh) -> Result<String> {
100 let mut obj_data = String::new();
101
102 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 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 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 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 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 for j in 0..sub_mesh.triangle_count {
138 let base = j * 3 + 1; 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 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 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 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 pub fn is_feature_supported(&self, feature: &str) -> bool {
230 self.get_supported_features().contains(&feature)
231 }
232
233 pub fn config(&self) -> &MeshConfig {
235 &self.config
236 }
237
238 pub fn set_config(&mut self, config: MeshConfig) {
240 self.config = config;
241 }
242
243 pub fn version(&self) -> &UnityVersion {
245 self.parser.version()
246 }
247
248 pub fn set_version(&mut self, version: UnityVersion) {
250 self.parser.set_version(version);
251 }
252
253 pub fn extract_vertex_positions(&self, _mesh: &Mesh) -> Result<Vec<[f32; 3]>> {
255 Ok(Vec::new())
258 }
259
260 pub fn extract_vertex_normals(&self, _mesh: &Mesh) -> Result<Vec<[f32; 3]>> {
262 Ok(Vec::new())
265 }
266
267 pub fn extract_uv_coordinates(&self, _mesh: &Mesh) -> Result<Vec<[f32; 2]>> {
269 Ok(Vec::new())
272 }
273
274 pub fn extract_triangle_indices(&self, _mesh: &Mesh) -> Result<Vec<u32>> {
276 Ok(Vec::new())
279 }
280}
281
282impl Default for MeshProcessor {
283 fn default() -> Self {
284 Self::new(UnityVersion::default())
285 }
286}
287
288#[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, pub medium_poly_meshes: usize, pub high_poly_meshes: usize, }
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 assert!(processor.validate_mesh(&mesh).is_err());
336
337 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}