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