use super::types::*;
use crate::error::Result;
use crate::object::UnityObject;
use crate::reader::BinaryReader;
use crate::unity_version::UnityVersion;
use indexmap::IndexMap;
use unity_asset_core::UnityValue;
pub struct MeshParser {
version: UnityVersion,
}
impl MeshParser {
pub fn new(version: UnityVersion) -> Self {
Self { version }
}
pub fn parse_from_unity_object(&self, obj: &UnityObject) -> Result<MeshResult> {
let mesh = self
.parse_from_typetree(obj.class.properties())
.or_else(|_| self.parse_from_binary_data(obj.raw_data()))?;
Ok(MeshResult::new(mesh))
}
pub fn parse_from_typetree(&self, properties: &IndexMap<String, UnityValue>) -> Result<Mesh> {
let mut mesh = Mesh::default();
if let Some(UnityValue::String(name)) = properties.get("m_Name") {
mesh.name = name.clone();
}
if let Some(sub_meshes_value) = properties.get("m_SubMeshes") {
self.extract_sub_meshes(&mut mesh, sub_meshes_value)?;
}
if let Some(vertex_data_value) = properties.get("m_VertexData") {
self.extract_vertex_data(&mut mesh, vertex_data_value)?;
}
if let Some(index_buffer_value) = properties.get("m_IndexBuffer") {
self.extract_index_buffer(&mut mesh, index_buffer_value)?;
}
if let Some(UnityValue::Bool(is_readable)) = properties.get("m_IsReadable") {
mesh.is_readable = *is_readable;
}
if let Some(local_aabb_value) = properties.get("m_LocalAABB") {
self.extract_local_aabb(&mut mesh, local_aabb_value)?;
}
if let Some(UnityValue::Integer(compression)) = properties.get("m_MeshCompression") {
mesh.mesh_compression = *compression as u8;
}
if let Some(stream_data) = properties.get("m_StreamData") {
mesh.stream_data = self.extract_stream_data(stream_data)?;
}
if let Some(blend_shapes_value) = properties.get("m_Shapes") {
mesh.blend_shape_data = self.extract_blend_shapes(blend_shapes_value)?;
}
if let Some(bind_poses_value) = properties.get("m_BindPose") {
self.extract_bind_poses(&mut mesh, bind_poses_value)?;
}
Ok(mesh)
}
#[allow(clippy::field_reassign_with_default)]
pub fn parse_from_binary_data(&self, data: &[u8]) -> Result<Mesh> {
let mut reader = BinaryReader::new(data, crate::reader::ByteOrder::Little);
let mut mesh = Mesh::default();
mesh.name = reader.read_aligned_string()?;
mesh.is_readable = reader.read_bool()?;
mesh.keep_vertices = reader.read_bool()?;
mesh.keep_indices = reader.read_bool()?;
mesh.index_format = reader.read_i32()?;
let index_buffer_size = reader.read_i32()? as usize;
if index_buffer_size > 0 {
mesh.index_buffer = reader.read_bytes(index_buffer_size)?;
}
mesh.mesh_compression = reader.read_u8()?;
Ok(mesh)
}
fn extract_sub_meshes(&self, mesh: &mut Mesh, value: &UnityValue) -> Result<()> {
if let UnityValue::Array(sub_meshes_array) = value {
mesh.sub_meshes.clear();
for sub_mesh_value in sub_meshes_array {
if let UnityValue::Object(sub_mesh_obj) = sub_mesh_value {
let mut sub_mesh = SubMesh::default();
if let Some(UnityValue::Integer(first_byte)) = sub_mesh_obj.get("firstByte") {
sub_mesh.first_byte = *first_byte as u32;
}
if let Some(UnityValue::Integer(index_count)) = sub_mesh_obj.get("indexCount") {
sub_mesh.index_count = *index_count as u32;
}
if let Some(UnityValue::Integer(topology)) = sub_mesh_obj.get("topology") {
sub_mesh.topology = *topology as i32;
}
if let Some(UnityValue::Integer(triangle_count)) =
sub_mesh_obj.get("triangleCount")
{
sub_mesh.triangle_count = *triangle_count as u32;
}
mesh.sub_meshes.push(sub_mesh);
}
}
}
Ok(())
}
fn extract_vertex_data(&self, mesh: &mut Mesh, value: &UnityValue) -> Result<()> {
if let UnityValue::Object(vertex_data_obj) = value {
if let Some(UnityValue::Integer(vertex_count)) = vertex_data_obj.get("m_VertexCount") {
mesh.vertex_data.vertex_count = *vertex_count as u32;
}
if let Some(channels_value) = vertex_data_obj.get("m_Channels") {
self.extract_vertex_channels(&mut mesh.vertex_data, channels_value)?;
}
if let Some(data_size_value) = vertex_data_obj.get("m_DataSize") {
match data_size_value {
UnityValue::Bytes(b) => {
mesh.vertex_data.data_size = b.clone();
}
UnityValue::Array(arr) => {
mesh.vertex_data.data_size.clear();
for item in arr {
if let UnityValue::Integer(byte_val) = item {
mesh.vertex_data.data_size.push(*byte_val as u8);
}
}
}
_ => {}
}
}
}
Ok(())
}
fn extract_vertex_channels(
&self,
vertex_data: &mut VertexData,
value: &UnityValue,
) -> Result<()> {
if let UnityValue::Array(channels_array) = value {
vertex_data.channels.clear();
for channel_value in channels_array {
if let UnityValue::Object(channel_obj) = channel_value {
let mut channel = ChannelInfo::default();
if let Some(UnityValue::Integer(stream)) = channel_obj.get("stream") {
channel.stream = *stream as u8;
}
if let Some(UnityValue::Integer(offset)) = channel_obj.get("offset") {
channel.offset = *offset as u8;
}
if let Some(UnityValue::Integer(format)) = channel_obj.get("format") {
channel.format = *format as u8;
}
if let Some(UnityValue::Integer(dimension)) = channel_obj.get("dimension") {
channel.dimension = *dimension as u8;
}
vertex_data.channels.push(channel);
}
}
}
Ok(())
}
fn extract_index_buffer(&self, mesh: &mut Mesh, value: &UnityValue) -> Result<()> {
match value {
UnityValue::Bytes(b) => {
mesh.index_buffer = b.clone();
}
UnityValue::Array(arr) => {
mesh.index_buffer.clear();
for item in arr {
if let UnityValue::Integer(byte_val) = item {
mesh.index_buffer.push(*byte_val as u8);
}
}
}
_ => {
}
}
Ok(())
}
fn extract_local_aabb(&self, mesh: &mut Mesh, value: &UnityValue) -> Result<()> {
if let UnityValue::Object(aabb_obj) = value {
if let Some(UnityValue::Object(center_obj)) = aabb_obj.get("m_Center") {
if let Some(UnityValue::Float(x)) = center_obj.get("x") {
mesh.local_aabb.center_x = *x as f32;
}
if let Some(UnityValue::Float(y)) = center_obj.get("y") {
mesh.local_aabb.center_y = *y as f32;
}
if let Some(UnityValue::Float(z)) = center_obj.get("z") {
mesh.local_aabb.center_z = *z as f32;
}
}
if let Some(UnityValue::Object(extent_obj)) = aabb_obj.get("m_Extent") {
if let Some(UnityValue::Float(x)) = extent_obj.get("x") {
mesh.local_aabb.extent_x = *x as f32;
}
if let Some(UnityValue::Float(y)) = extent_obj.get("y") {
mesh.local_aabb.extent_y = *y as f32;
}
if let Some(UnityValue::Float(z)) = extent_obj.get("z") {
mesh.local_aabb.extent_z = *z as f32;
}
}
}
Ok(())
}
fn extract_stream_data(&self, value: &UnityValue) -> Result<Option<StreamingInfo>> {
if let UnityValue::Object(stream_obj) = value {
let mut stream_info = StreamingInfo::default();
if let Some(UnityValue::Integer(offset)) = stream_obj.get("offset") {
stream_info.offset = *offset as u64;
}
if let Some(UnityValue::Integer(size)) = stream_obj.get("size") {
stream_info.size = *size as u32;
}
if let Some(UnityValue::String(path)) = stream_obj.get("path") {
stream_info.path = path.clone();
}
if stream_info.size > 0 || !stream_info.path.is_empty() {
return Ok(Some(stream_info));
}
}
Ok(None)
}
fn extract_blend_shapes(&self, _value: &UnityValue) -> Result<Option<BlendShapeData>> {
Ok(None)
}
fn extract_bind_poses(&self, mesh: &mut Mesh, value: &UnityValue) -> Result<()> {
if let UnityValue::Array(bind_poses_array) = value {
mesh.bind_pose.clear();
for bind_pose_value in bind_poses_array {
if let UnityValue::Object(matrix_obj) = bind_pose_value {
let mut matrix = [0.0f32; 16];
for (i, matrix_element) in matrix.iter_mut().enumerate() {
let key = format!("e{:02}", i);
if let Some(UnityValue::Float(val)) = matrix_obj.get(&key) {
*matrix_element = *val as f32;
}
}
mesh.bind_pose.push(matrix);
}
}
}
Ok(())
}
pub fn version(&self) -> &UnityVersion {
&self.version
}
pub fn set_version(&mut self, version: UnityVersion) {
self.version = version;
}
}
impl Default for MeshParser {
fn default() -> Self {
Self::new(UnityVersion::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parser_creation() {
let parser = MeshParser::new(UnityVersion::default());
assert_eq!(parser.version(), &UnityVersion::default());
}
#[test]
fn test_extract_local_aabb() {
let parser = MeshParser::default();
let mut mesh = Mesh::default();
let mut center_obj = IndexMap::new();
center_obj.insert("x".to_string(), UnityValue::Float(1.0));
center_obj.insert("y".to_string(), UnityValue::Float(2.0));
center_obj.insert("z".to_string(), UnityValue::Float(3.0));
let mut extent_obj = IndexMap::new();
extent_obj.insert("x".to_string(), UnityValue::Float(0.5));
extent_obj.insert("y".to_string(), UnityValue::Float(1.0));
extent_obj.insert("z".to_string(), UnityValue::Float(1.5));
let mut aabb_obj = IndexMap::new();
aabb_obj.insert("m_Center".to_string(), UnityValue::Object(center_obj));
aabb_obj.insert("m_Extent".to_string(), UnityValue::Object(extent_obj));
let aabb_value = UnityValue::Object(aabb_obj);
parser.extract_local_aabb(&mut mesh, &aabb_value).unwrap();
assert_eq!(mesh.local_aabb.center_x, 1.0);
assert_eq!(mesh.local_aabb.center_y, 2.0);
assert_eq!(mesh.local_aabb.center_z, 3.0);
assert_eq!(mesh.local_aabb.extent_x, 0.5);
assert_eq!(mesh.local_aabb.extent_y, 1.0);
assert_eq!(mesh.local_aabb.extent_z, 1.5);
}
}