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