unity_asset_decode/mesh/
mod.rs

1//! Unity Mesh processing module
2//!
3//! This module provides comprehensive Mesh processing capabilities,
4//! organized following UnityPy and unity-rs best practices.
5//!
6//! # Architecture
7//!
8//! The module is organized into several sub-modules:
9//! - `types` - Core data structures (Mesh, VertexData, SubMesh, etc.)
10//! - `parser` - Mesh parsing from Unity objects
11//! - `processor` - High-level mesh processing and export
12//!
13//! # Examples
14//!
15//! ```rust,no_run
16//! use unity_asset_decode::mesh::{MeshProcessor, MeshConfig};
17//! use unity_asset_decode::unity_version::UnityVersion;
18//!
19//! // Create processor with custom configuration
20//! let version = UnityVersion::parse_version("2020.3.12f1")?;
21//! let config = MeshConfig {
22//!     extract_vertices: true,
23//!     extract_indices: true,
24//!     process_blend_shapes: true,
25//!     decompress_meshes: true,
26//!     max_vertex_count: Some(100000),
27//! };
28//! let processor = MeshProcessor::with_config(version, config);
29//!
30//! // Note: In real usage, you would create a UnityObject from parsed data
31//! // For demonstration, we'll just show the processor creation
32//! println!("Mesh processed successfully");
33//! # Ok::<(), unity_asset_decode::error::BinaryError>(())
34//! ```
35
36pub mod parser;
37pub mod processor;
38pub mod types;
39
40// Re-export main types for easy access
41pub use parser::MeshParser;
42pub use processor::{MeshProcessor, MeshStats};
43pub use types::{
44    AABB,
45    BlendShape,
46    BlendShapeChannel,
47    // Blend shape types
48    BlendShapeData,
49    BlendShapeVertex,
50    ChannelInfo,
51    // Compression types
52    CompressedMesh,
53    // Core mesh types
54    Mesh,
55    // Configuration and results
56    MeshConfig,
57    MeshInfo,
58    MeshResult,
59    PackedFloatVector,
60    PackedIntVector,
61    // Streaming and info
62    StreamingInfo,
63    SubMesh,
64    VertexData,
65};
66
67/// Main mesh processing facade
68///
69/// This struct provides a high-level interface for mesh processing,
70/// combining parsing and processing functionality.
71pub struct MeshManager {
72    processor: MeshProcessor,
73}
74
75impl MeshManager {
76    /// Create a new mesh manager
77    pub fn new(version: crate::unity_version::UnityVersion) -> Self {
78        Self {
79            processor: MeshProcessor::new(version),
80        }
81    }
82
83    /// Create a mesh manager with custom configuration
84    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    /// Process mesh from Unity object
91    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    /// Export mesh to OBJ format
99    pub fn export_to_obj(&self, mesh: &Mesh) -> crate::error::Result<String> {
100        self.processor.export_to_obj(mesh)
101    }
102
103    /// Get mesh statistics
104    pub fn get_statistics(&self, meshes: &[&Mesh]) -> MeshStats {
105        self.processor.get_mesh_stats(meshes)
106    }
107
108    /// Validate mesh data
109    pub fn validate_mesh(&self, mesh: &Mesh) -> crate::error::Result<()> {
110        self.processor.validate_mesh(mesh)
111    }
112
113    /// Get supported features
114    pub fn get_supported_features(&self) -> Vec<&'static str> {
115        self.processor.get_supported_features()
116    }
117
118    /// Check if a feature is supported
119    pub fn is_feature_supported(&self, feature: &str) -> bool {
120        self.processor.is_feature_supported(feature)
121    }
122
123    /// Get the current configuration
124    pub fn config(&self) -> &MeshConfig {
125        self.processor.config()
126    }
127
128    /// Set the configuration
129    pub fn set_config(&mut self, config: MeshConfig) {
130        self.processor.set_config(config);
131    }
132
133    /// Get the Unity version
134    pub fn version(&self) -> &crate::unity_version::UnityVersion {
135        self.processor.version()
136    }
137
138    /// Set the Unity version
139    pub fn set_version(&mut self, version: crate::unity_version::UnityVersion) {
140        self.processor.set_version(version);
141    }
142
143    /// Extract vertex data
144    pub fn extract_vertices(&self, mesh: &Mesh) -> crate::error::Result<Vec<[f32; 3]>> {
145        self.processor.extract_vertex_positions(mesh)
146    }
147
148    /// Extract normals
149    pub fn extract_normals(&self, mesh: &Mesh) -> crate::error::Result<Vec<[f32; 3]>> {
150        self.processor.extract_vertex_normals(mesh)
151    }
152
153    /// Extract UV coordinates
154    pub fn extract_uvs(&self, mesh: &Mesh) -> crate::error::Result<Vec<[f32; 2]>> {
155        self.processor.extract_uv_coordinates(mesh)
156    }
157
158    /// Extract triangle indices
159    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
170/// Convenience functions for common operations
171/// Create a mesh manager with default settings
172pub fn create_manager(version: crate::unity_version::UnityVersion) -> MeshManager {
173    MeshManager::new(version)
174}
175
176/// Create a mesh manager optimized for performance
177pub 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
188/// Create a mesh manager with full features
189pub 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
200/// Parse mesh from Unity object (convenience function)
201pub 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
210/// Export mesh to OBJ format (convenience function)
211pub 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
219/// Validate mesh data (convenience function)
220pub fn validate_mesh(mesh: &Mesh) -> crate::error::Result<()> {
221    let processor = MeshProcessor::default();
222    processor.validate_mesh(mesh)
223}
224
225/// Get mesh vertex count
226pub fn get_vertex_count(mesh: &Mesh) -> u32 {
227    mesh.vertex_count()
228}
229
230/// Get mesh triangle count
231pub fn get_triangle_count(mesh: &Mesh) -> u32 {
232    mesh.triangle_count()
233}
234
235/// Check if mesh has blend shapes
236pub fn has_blend_shapes(mesh: &Mesh) -> bool {
237    mesh.has_blend_shapes()
238}
239
240/// Check if mesh is compressed
241pub fn is_compressed_mesh(mesh: &Mesh) -> bool {
242    mesh.is_compressed()
243}
244
245/// Check if mesh has streaming data
246pub fn has_streaming_data(mesh: &Mesh) -> bool {
247    mesh.has_streaming_data()
248}
249
250/// Get mesh bounds
251pub fn get_mesh_bounds(mesh: &Mesh) -> &AABB {
252    mesh.bounds()
253}
254
255/// Check if Unity version supports mesh feature
256pub 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
270/// Get recommended mesh configuration for Unity version
271pub fn get_recommended_config(version: &crate::unity_version::UnityVersion) -> MeshConfig {
272    if version.major >= 2019 {
273        // Modern Unity - full features
274        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        // Unity 2017+ - streaming support
283        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        // Unity 5+ - basic features
292        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        // Legacy Unity - minimal features
301        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/// Mesh processing options
312#[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}