unity_asset_binary/mesh/
parser.rs

1//! Mesh parsing implementation
2//!
3//! This module provides the main parsing logic for Unity Mesh objects.
4
5use super::types::*;
6use crate::error::Result;
7use crate::object::UnityObject;
8use crate::reader::BinaryReader;
9use crate::unity_version::UnityVersion;
10use indexmap::IndexMap;
11use unity_asset_core::UnityValue;
12
13/// Mesh parser
14///
15/// This struct provides methods for parsing Unity Mesh objects from
16/// various data sources including TypeTree and binary data.
17pub struct MeshParser {
18    version: UnityVersion,
19}
20
21impl MeshParser {
22    /// Create a new mesh parser
23    pub fn new(version: UnityVersion) -> Self {
24        Self { version }
25    }
26
27    /// Parse Mesh from UnityObject
28    pub fn parse_from_unity_object(&self, obj: &UnityObject) -> Result<MeshResult> {
29        let mesh = if let Some(type_tree) = &obj.info.type_tree {
30            let properties = obj.parse_with_typetree(type_tree)?;
31            self.parse_from_typetree(&properties)?
32        } else {
33            self.parse_from_binary_data(&obj.info.data)?
34        };
35
36        Ok(MeshResult::new(mesh))
37    }
38
39    /// Parse Mesh from TypeTree properties
40    pub fn parse_from_typetree(&self, properties: &IndexMap<String, UnityValue>) -> Result<Mesh> {
41        let mut mesh = Mesh::default();
42
43        // Extract name
44        if let Some(UnityValue::String(name)) = properties.get("m_Name") {
45            mesh.name = name.clone();
46        }
47
48        // Extract sub meshes
49        if let Some(sub_meshes_value) = properties.get("m_SubMeshes") {
50            self.extract_sub_meshes(&mut mesh, sub_meshes_value)?;
51        }
52
53        // Extract vertex data
54        if let Some(vertex_data_value) = properties.get("m_VertexData") {
55            self.extract_vertex_data(&mut mesh, vertex_data_value)?;
56        }
57
58        // Extract index buffer
59        if let Some(index_buffer_value) = properties.get("m_IndexBuffer") {
60            self.extract_index_buffer(&mut mesh, index_buffer_value)?;
61        }
62
63        // Extract readable flag
64        if let Some(UnityValue::Bool(is_readable)) = properties.get("m_IsReadable") {
65            mesh.is_readable = *is_readable;
66        }
67
68        // Extract local AABB
69        if let Some(local_aabb_value) = properties.get("m_LocalAABB") {
70            self.extract_local_aabb(&mut mesh, local_aabb_value)?;
71        }
72
73        // Extract mesh compression
74        if let Some(UnityValue::Integer(compression)) = properties.get("m_MeshCompression") {
75            mesh.mesh_compression = *compression as u8;
76        }
77
78        // Extract streaming info if present
79        if let Some(stream_data) = properties.get("m_StreamData") {
80            mesh.stream_data = self.extract_stream_data(stream_data)?;
81        }
82
83        // Extract blend shape data
84        if let Some(blend_shapes_value) = properties.get("m_Shapes") {
85            mesh.blend_shape_data = self.extract_blend_shapes(blend_shapes_value)?;
86        }
87
88        // Extract bind poses
89        if let Some(bind_poses_value) = properties.get("m_BindPose") {
90            self.extract_bind_poses(&mut mesh, bind_poses_value)?;
91        }
92
93        Ok(mesh)
94    }
95
96    /// Parse Mesh from raw binary data (fallback method)
97    #[allow(clippy::field_reassign_with_default)]
98    pub fn parse_from_binary_data(&self, data: &[u8]) -> Result<Mesh> {
99        let mut reader = BinaryReader::new(data, crate::reader::ByteOrder::Little);
100        let mut mesh = Mesh::default();
101
102        // Read name (aligned string)
103        mesh.name = reader.read_aligned_string()?;
104
105        // Read basic properties
106        mesh.is_readable = reader.read_bool()?;
107        mesh.keep_vertices = reader.read_bool()?;
108        mesh.keep_indices = reader.read_bool()?;
109
110        // Read index format
111        mesh.index_format = reader.read_i32()?;
112
113        // Read index buffer size and data
114        let index_buffer_size = reader.read_i32()? as usize;
115        if index_buffer_size > 0 {
116            mesh.index_buffer = reader.read_bytes(index_buffer_size)?;
117        }
118
119        // Read mesh compression
120        mesh.mesh_compression = reader.read_u8()?;
121
122        Ok(mesh)
123    }
124
125    /// Extract sub meshes from UnityValue
126    fn extract_sub_meshes(&self, mesh: &mut Mesh, value: &UnityValue) -> Result<()> {
127        if let UnityValue::Array(sub_meshes_array) = value {
128            mesh.sub_meshes.clear();
129            for sub_mesh_value in sub_meshes_array {
130                if let UnityValue::Object(sub_mesh_obj) = sub_mesh_value {
131                    let mut sub_mesh = SubMesh::default();
132
133                    if let Some(UnityValue::Integer(first_byte)) = sub_mesh_obj.get("firstByte") {
134                        sub_mesh.first_byte = *first_byte as u32;
135                    }
136                    if let Some(UnityValue::Integer(index_count)) = sub_mesh_obj.get("indexCount") {
137                        sub_mesh.index_count = *index_count as u32;
138                    }
139                    if let Some(UnityValue::Integer(topology)) = sub_mesh_obj.get("topology") {
140                        sub_mesh.topology = *topology as i32;
141                    }
142                    if let Some(UnityValue::Integer(triangle_count)) =
143                        sub_mesh_obj.get("triangleCount")
144                    {
145                        sub_mesh.triangle_count = *triangle_count as u32;
146                    }
147
148                    mesh.sub_meshes.push(sub_mesh);
149                }
150            }
151        }
152        Ok(())
153    }
154
155    /// Extract vertex data from UnityValue
156    fn extract_vertex_data(&self, mesh: &mut Mesh, value: &UnityValue) -> Result<()> {
157        if let UnityValue::Object(vertex_data_obj) = value {
158            if let Some(UnityValue::Integer(vertex_count)) = vertex_data_obj.get("m_VertexCount") {
159                mesh.vertex_data.vertex_count = *vertex_count as u32;
160            }
161
162            // Extract channels
163            if let Some(channels_value) = vertex_data_obj.get("m_Channels") {
164                self.extract_vertex_channels(&mut mesh.vertex_data, channels_value)?;
165            }
166
167            // Extract data size
168            if let Some(data_size_value) = vertex_data_obj.get("m_DataSize")
169                && let UnityValue::Array(data_array) = data_size_value
170            {
171                mesh.vertex_data.data_size.clear();
172                for data_item in data_array {
173                    if let UnityValue::Integer(byte_val) = data_item {
174                        mesh.vertex_data.data_size.push(*byte_val as u8);
175                    }
176                }
177            }
178        }
179        Ok(())
180    }
181
182    /// Extract vertex channels from UnityValue
183    fn extract_vertex_channels(
184        &self,
185        vertex_data: &mut VertexData,
186        value: &UnityValue,
187    ) -> Result<()> {
188        if let UnityValue::Array(channels_array) = value {
189            vertex_data.channels.clear();
190            for channel_value in channels_array {
191                if let UnityValue::Object(channel_obj) = channel_value {
192                    let mut channel = ChannelInfo::default();
193
194                    if let Some(UnityValue::Integer(stream)) = channel_obj.get("stream") {
195                        channel.stream = *stream as u8;
196                    }
197                    if let Some(UnityValue::Integer(offset)) = channel_obj.get("offset") {
198                        channel.offset = *offset as u8;
199                    }
200                    if let Some(UnityValue::Integer(format)) = channel_obj.get("format") {
201                        channel.format = *format as u8;
202                    }
203                    if let Some(UnityValue::Integer(dimension)) = channel_obj.get("dimension") {
204                        channel.dimension = *dimension as u8;
205                    }
206
207                    vertex_data.channels.push(channel);
208                }
209            }
210        }
211        Ok(())
212    }
213
214    /// Extract index buffer from UnityValue
215    fn extract_index_buffer(&self, mesh: &mut Mesh, value: &UnityValue) -> Result<()> {
216        match value {
217            UnityValue::Array(arr) => {
218                mesh.index_buffer.clear();
219                for item in arr {
220                    if let UnityValue::Integer(byte_val) = item {
221                        mesh.index_buffer.push(*byte_val as u8);
222                    }
223                }
224            }
225            _ => {
226                // Handle other formats if needed
227            }
228        }
229        Ok(())
230    }
231
232    /// Extract local AABB from UnityValue
233    fn extract_local_aabb(&self, mesh: &mut Mesh, value: &UnityValue) -> Result<()> {
234        if let UnityValue::Object(aabb_obj) = value {
235            // Extract center
236            if let Some(center_value) = aabb_obj.get("m_Center")
237                && let UnityValue::Object(center_obj) = center_value
238            {
239                if let Some(UnityValue::Float(x)) = center_obj.get("x") {
240                    mesh.local_aabb.center_x = *x as f32;
241                }
242                if let Some(UnityValue::Float(y)) = center_obj.get("y") {
243                    mesh.local_aabb.center_y = *y as f32;
244                }
245                if let Some(UnityValue::Float(z)) = center_obj.get("z") {
246                    mesh.local_aabb.center_z = *z as f32;
247                }
248            }
249
250            // Extract extent
251            if let Some(extent_value) = aabb_obj.get("m_Extent")
252                && let UnityValue::Object(extent_obj) = extent_value
253            {
254                if let Some(UnityValue::Float(x)) = extent_obj.get("x") {
255                    mesh.local_aabb.extent_x = *x as f32;
256                }
257                if let Some(UnityValue::Float(y)) = extent_obj.get("y") {
258                    mesh.local_aabb.extent_y = *y as f32;
259                }
260                if let Some(UnityValue::Float(z)) = extent_obj.get("z") {
261                    mesh.local_aabb.extent_z = *z as f32;
262                }
263            }
264        }
265        Ok(())
266    }
267
268    /// Extract streaming data from UnityValue
269    fn extract_stream_data(&self, value: &UnityValue) -> Result<Option<StreamingInfo>> {
270        if let UnityValue::Object(stream_obj) = value {
271            let mut stream_info = StreamingInfo::default();
272
273            if let Some(UnityValue::Integer(offset)) = stream_obj.get("offset") {
274                stream_info.offset = *offset as u64;
275            }
276            if let Some(UnityValue::Integer(size)) = stream_obj.get("size") {
277                stream_info.size = *size as u32;
278            }
279            if let Some(UnityValue::String(path)) = stream_obj.get("path") {
280                stream_info.path = path.clone();
281            }
282
283            // Only return stream info if it has valid data
284            if stream_info.size > 0 || !stream_info.path.is_empty() {
285                return Ok(Some(stream_info));
286            }
287        }
288        Ok(None)
289    }
290
291    /// Extract blend shapes from UnityValue
292    fn extract_blend_shapes(&self, _value: &UnityValue) -> Result<Option<BlendShapeData>> {
293        // Blend shapes are complex structures
294        // This is a placeholder implementation
295        Ok(None)
296    }
297
298    /// Extract bind poses from UnityValue
299    fn extract_bind_poses(&self, mesh: &mut Mesh, value: &UnityValue) -> Result<()> {
300        if let UnityValue::Array(bind_poses_array) = value {
301            mesh.bind_pose.clear();
302            for bind_pose_value in bind_poses_array {
303                if let UnityValue::Object(matrix_obj) = bind_pose_value {
304                    let mut matrix = [0.0f32; 16];
305
306                    // Extract matrix elements (simplified)
307                    for (i, matrix_element) in matrix.iter_mut().enumerate() {
308                        let key = format!("e{:02}", i);
309                        if let Some(UnityValue::Float(val)) = matrix_obj.get(&key) {
310                            *matrix_element = *val as f32;
311                        }
312                    }
313
314                    mesh.bind_pose.push(matrix);
315                }
316            }
317        }
318        Ok(())
319    }
320
321    /// Get the Unity version
322    pub fn version(&self) -> &UnityVersion {
323        &self.version
324    }
325
326    /// Set the Unity version
327    pub fn set_version(&mut self, version: UnityVersion) {
328        self.version = version;
329    }
330}
331
332impl Default for MeshParser {
333    fn default() -> Self {
334        Self::new(UnityVersion::default())
335    }
336}
337
338#[cfg(test)]
339mod tests {
340    use super::*;
341
342    #[test]
343    fn test_parser_creation() {
344        let parser = MeshParser::new(UnityVersion::default());
345        assert_eq!(parser.version(), &UnityVersion::default());
346    }
347
348    #[test]
349    fn test_extract_local_aabb() {
350        let parser = MeshParser::default();
351        let mut mesh = Mesh::default();
352
353        let mut center_obj = IndexMap::new();
354        center_obj.insert("x".to_string(), UnityValue::Float(1.0));
355        center_obj.insert("y".to_string(), UnityValue::Float(2.0));
356        center_obj.insert("z".to_string(), UnityValue::Float(3.0));
357
358        let mut extent_obj = IndexMap::new();
359        extent_obj.insert("x".to_string(), UnityValue::Float(0.5));
360        extent_obj.insert("y".to_string(), UnityValue::Float(1.0));
361        extent_obj.insert("z".to_string(), UnityValue::Float(1.5));
362
363        let mut aabb_obj = IndexMap::new();
364        aabb_obj.insert("m_Center".to_string(), UnityValue::Object(center_obj));
365        aabb_obj.insert("m_Extent".to_string(), UnityValue::Object(extent_obj));
366
367        let aabb_value = UnityValue::Object(aabb_obj);
368        parser.extract_local_aabb(&mut mesh, &aabb_value).unwrap();
369
370        assert_eq!(mesh.local_aabb.center_x, 1.0);
371        assert_eq!(mesh.local_aabb.center_y, 2.0);
372        assert_eq!(mesh.local_aabb.center_z, 3.0);
373        assert_eq!(mesh.local_aabb.extent_x, 0.5);
374        assert_eq!(mesh.local_aabb.extent_y, 1.0);
375        assert_eq!(mesh.local_aabb.extent_z, 1.5);
376    }
377}