1pub mod parser;
37pub mod processor;
38pub mod types;
39
40pub use parser::MeshParser;
42pub use processor::{MeshProcessor, MeshStats};
43pub use types::{
44 AABB,
45 BlendShape,
46 BlendShapeChannel,
47 BlendShapeData,
49 BlendShapeVertex,
50 ChannelInfo,
51 CompressedMesh,
53 Mesh,
55 MeshConfig,
57 MeshInfo,
58 MeshResult,
59 PackedFloatVector,
60 PackedIntVector,
61 StreamingInfo,
63 SubMesh,
64 VertexData,
65};
66
67pub struct MeshManager {
72 processor: MeshProcessor,
73}
74
75impl MeshManager {
76 pub fn new(version: crate::unity_version::UnityVersion) -> Self {
78 Self {
79 processor: MeshProcessor::new(version),
80 }
81 }
82
83 pub fn with_config(version: crate::unity_version::UnityVersion, config: MeshConfig) -> Self {
85 Self {
86 processor: MeshProcessor::with_config(version, config),
87 }
88 }
89
90 pub fn process_mesh(
92 &self,
93 object: &crate::object::UnityObject,
94 ) -> crate::error::Result<MeshResult> {
95 self.processor.parse_mesh(object)
96 }
97
98 pub fn export_to_obj(&self, mesh: &Mesh) -> crate::error::Result<String> {
100 self.processor.export_to_obj(mesh)
101 }
102
103 pub fn get_statistics(&self, meshes: &[&Mesh]) -> MeshStats {
105 self.processor.get_mesh_stats(meshes)
106 }
107
108 pub fn validate_mesh(&self, mesh: &Mesh) -> crate::error::Result<()> {
110 self.processor.validate_mesh(mesh)
111 }
112
113 pub fn get_supported_features(&self) -> Vec<&'static str> {
115 self.processor.get_supported_features()
116 }
117
118 pub fn is_feature_supported(&self, feature: &str) -> bool {
120 self.processor.is_feature_supported(feature)
121 }
122
123 pub fn config(&self) -> &MeshConfig {
125 self.processor.config()
126 }
127
128 pub fn set_config(&mut self, config: MeshConfig) {
130 self.processor.set_config(config);
131 }
132
133 pub fn version(&self) -> &crate::unity_version::UnityVersion {
135 self.processor.version()
136 }
137
138 pub fn set_version(&mut self, version: crate::unity_version::UnityVersion) {
140 self.processor.set_version(version);
141 }
142
143 pub fn extract_vertices(&self, mesh: &Mesh) -> crate::error::Result<Vec<[f32; 3]>> {
145 self.processor.extract_vertex_positions(mesh)
146 }
147
148 pub fn extract_normals(&self, mesh: &Mesh) -> crate::error::Result<Vec<[f32; 3]>> {
150 self.processor.extract_vertex_normals(mesh)
151 }
152
153 pub fn extract_uvs(&self, mesh: &Mesh) -> crate::error::Result<Vec<[f32; 2]>> {
155 self.processor.extract_uv_coordinates(mesh)
156 }
157
158 pub fn extract_indices(&self, mesh: &Mesh) -> crate::error::Result<Vec<u32>> {
160 self.processor.extract_triangle_indices(mesh)
161 }
162}
163
164impl Default for MeshManager {
165 fn default() -> Self {
166 Self::new(crate::unity_version::UnityVersion::default())
167 }
168}
169
170pub fn create_manager(version: crate::unity_version::UnityVersion) -> MeshManager {
173 MeshManager::new(version)
174}
175
176pub fn create_performance_manager(version: crate::unity_version::UnityVersion) -> MeshManager {
178 let config = MeshConfig {
179 extract_vertices: false,
180 extract_indices: false,
181 process_blend_shapes: false,
182 decompress_meshes: false,
183 max_vertex_count: Some(10000),
184 };
185 MeshManager::with_config(version, config)
186}
187
188pub fn create_full_manager(version: crate::unity_version::UnityVersion) -> MeshManager {
190 let config = MeshConfig {
191 extract_vertices: true,
192 extract_indices: true,
193 process_blend_shapes: true,
194 decompress_meshes: true,
195 max_vertex_count: None,
196 };
197 MeshManager::with_config(version, config)
198}
199
200pub fn parse_mesh(
202 object: &crate::object::UnityObject,
203 version: &crate::unity_version::UnityVersion,
204) -> crate::error::Result<Mesh> {
205 let parser = MeshParser::new(version.clone());
206 let result = parser.parse_from_unity_object(object)?;
207 Ok(result.mesh)
208}
209
210pub fn export_mesh_to_obj(
212 mesh: &Mesh,
213 version: &crate::unity_version::UnityVersion,
214) -> crate::error::Result<String> {
215 let processor = MeshProcessor::new(version.clone());
216 processor.export_to_obj(mesh)
217}
218
219pub fn validate_mesh(mesh: &Mesh) -> crate::error::Result<()> {
221 let processor = MeshProcessor::default();
222 processor.validate_mesh(mesh)
223}
224
225pub fn get_vertex_count(mesh: &Mesh) -> u32 {
227 mesh.vertex_count()
228}
229
230pub fn get_triangle_count(mesh: &Mesh) -> u32 {
232 mesh.triangle_count()
233}
234
235pub fn has_blend_shapes(mesh: &Mesh) -> bool {
237 mesh.has_blend_shapes()
238}
239
240pub fn is_compressed_mesh(mesh: &Mesh) -> bool {
242 mesh.is_compressed()
243}
244
245pub fn has_streaming_data(mesh: &Mesh) -> bool {
247 mesh.has_streaming_data()
248}
249
250pub fn get_mesh_bounds(mesh: &Mesh) -> &AABB {
252 mesh.bounds()
253}
254
255pub fn is_mesh_feature_supported(
257 version: &crate::unity_version::UnityVersion,
258 feature: &str,
259) -> bool {
260 match feature {
261 "basic_mesh" | "sub_meshes" | "vertex_data" => true,
262 "blend_shapes" | "compressed_mesh" => version.major >= 5,
263 "mesh_optimization" | "streaming_info" => version.major >= 2017,
264 "mesh_usage_flags" => version.major >= 2018,
265 "mesh_topology" | "vertex_attributes" => version.major >= 2019,
266 _ => false,
267 }
268}
269
270pub fn get_recommended_config(version: &crate::unity_version::UnityVersion) -> MeshConfig {
272 if version.major >= 2019 {
273 MeshConfig {
275 extract_vertices: true,
276 extract_indices: true,
277 process_blend_shapes: true,
278 decompress_meshes: true,
279 max_vertex_count: None,
280 }
281 } else if version.major >= 2017 {
282 MeshConfig {
284 extract_vertices: true,
285 extract_indices: true,
286 process_blend_shapes: true,
287 decompress_meshes: true,
288 max_vertex_count: Some(100000),
289 }
290 } else if version.major >= 5 {
291 MeshConfig {
293 extract_vertices: true,
294 extract_indices: true,
295 process_blend_shapes: false,
296 decompress_meshes: false,
297 max_vertex_count: Some(50000),
298 }
299 } else {
300 MeshConfig {
302 extract_vertices: false,
303 extract_indices: false,
304 process_blend_shapes: false,
305 decompress_meshes: false,
306 max_vertex_count: Some(10000),
307 }
308 }
309}
310
311#[derive(Debug, Clone)]
313pub struct ProcessingOptions {
314 pub parallel_processing: bool,
315 pub cache_results: bool,
316 pub validate_meshes: bool,
317 pub generate_lods: bool,
318}
319
320impl Default for ProcessingOptions {
321 fn default() -> Self {
322 Self {
323 parallel_processing: false,
324 cache_results: true,
325 validate_meshes: true,
326 generate_lods: false,
327 }
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334
335 #[test]
336 fn test_manager_creation() {
337 let version = crate::unity_version::UnityVersion::default();
338 let manager = create_manager(version);
339 assert!(manager.get_supported_features().contains(&"basic_mesh"));
340 }
341
342 #[test]
343 fn test_performance_manager() {
344 let version = crate::unity_version::UnityVersion::default();
345 let manager = create_performance_manager(version);
346 assert!(!manager.config().extract_vertices);
347 assert!(!manager.config().process_blend_shapes);
348 }
349
350 #[test]
351 fn test_full_manager() {
352 let version = crate::unity_version::UnityVersion::default();
353 let manager = create_full_manager(version);
354 assert!(manager.config().extract_vertices);
355 assert!(manager.config().process_blend_shapes);
356 }
357
358 #[test]
359 fn test_feature_support() {
360 let version_2020 =
361 crate::unity_version::UnityVersion::parse_version("2020.3.12f1").unwrap();
362 assert!(is_mesh_feature_supported(&version_2020, "basic_mesh"));
363 assert!(is_mesh_feature_supported(&version_2020, "blend_shapes"));
364 assert!(is_mesh_feature_supported(
365 &version_2020,
366 "vertex_attributes"
367 ));
368
369 let version_2017 =
370 crate::unity_version::UnityVersion::parse_version("2017.4.40f1").unwrap();
371 assert!(is_mesh_feature_supported(&version_2017, "streaming_info"));
372 assert!(!is_mesh_feature_supported(
373 &version_2017,
374 "vertex_attributes"
375 ));
376 }
377
378 #[test]
379 fn test_recommended_config() {
380 let version_2020 =
381 crate::unity_version::UnityVersion::parse_version("2020.3.12f1").unwrap();
382 let config = get_recommended_config(&version_2020);
383 assert!(config.extract_vertices);
384 assert!(config.process_blend_shapes);
385 assert!(config.decompress_meshes);
386
387 let version_5 = crate::unity_version::UnityVersion::parse_version("5.6.7f1").unwrap();
388 let config = get_recommended_config(&version_5);
389 assert!(config.extract_vertices);
390 assert!(!config.process_blend_shapes);
391 }
392}