diff options
Diffstat (limited to 'src/classes/mesh.rs')
-rw-r--r-- | src/classes/mesh.rs | 148 |
1 files changed, 125 insertions, 23 deletions
diff --git a/src/classes/mesh.rs b/src/classes/mesh.rs index 7df7f8d..3883535 100644 --- a/src/classes/mesh.rs +++ b/src/classes/mesh.rs @@ -1,8 +1,9 @@ use super::streaminginfo::StreamingInfo; use crate::object::{Value, parser::FromValue}; -use anyhow::Result; +use anyhow::{Result, anyhow, bail}; use glam::Mat4; use serde::Serialize; +use std::mem::transmute; #[derive(Debug, Serialize)] pub struct Mesh { @@ -13,6 +14,7 @@ pub struct Mesh { pub index_buffer: Vec<u8>, pub sub_meshes: Vec<SubMesh>, pub stream_data: StreamingInfo, + pub vertex_data: VertexData, } #[derive(Debug, Serialize)] @@ -28,14 +30,14 @@ pub struct SubMesh { #[derive(Debug, Serialize)] pub struct VertexData { pub channels: Vec<ChannelInfo>, - pub data_size: Vec<u8>, + pub data: Vec<u8>, pub vertex_count: u32, } #[derive(Debug, Serialize)] pub struct ChannelInfo { pub dimension: u8, - pub format: u8, + pub format: VertexFormat, pub offset: u8, pub stream: u8, } @@ -44,8 +46,10 @@ impl FromValue for Mesh { fn from_value(v: Value) -> Result<Self> { let mut fields = v.as_class("Mesh").unwrap(); Ok(Mesh { - name: fields.remove("m_Name").unwrap().parse().unwrap(), - index_format: fields.remove("m_IndexFormat").unwrap().parse().unwrap(), + name: fields.field("m_Name")?, + index_format: fields.field("m_IndexFormat")?, + vertex_data: fields.field("m_VertexData")?, + stream_data: fields.field("m_StreamData")?, index_buffer: fields .remove("m_IndexBuffer") .unwrap() @@ -62,7 +66,6 @@ impl FromValue for Mesh { .into_iter() .map(|e| e.parse().unwrap()) .collect(), - stream_data: fields.remove("m_StreamData").unwrap().parse().unwrap(), bind_pose: fields .remove("m_BindPose") .unwrap() @@ -82,25 +85,86 @@ impl FromValue for Mesh { }) } } +impl Mesh { + pub fn read_indecies(&self) -> Vec<[u32; 3]> { + if self.index_format == 0 { + self.index_buffer + .array_chunks::<2>() + .map(|x| u16::from_le_bytes(*x) as u32) + .array_chunks() + .collect() + } else { + self.index_buffer + .array_chunks::<4>() + .map(|x| u32::from_le_bytes(*x)) + .array_chunks() + .collect() + } + } +} impl FromValue for VertexData { fn from_value(v: Value) -> Result<Self> { let mut fields = v.as_class("VertexData").unwrap(); Ok(VertexData { - vertex_count: fields.remove("vertexCount").unwrap().parse().unwrap(), + vertex_count: fields.field("m_VertexCount")?, + data: fields.remove("m_DataSize").unwrap().as_typeless().unwrap(), channels: fields .remove("m_Channels") .unwrap() - .as_array() + .as_vector() .unwrap() .into_iter() .map(|e| e.parse().unwrap()) .collect(), - data_size: fields.remove("m_DataSize").unwrap().as_typeless().unwrap(), }) } } +impl VertexData { + /// Returns (offset, stride) for each stream. + pub fn stream_layout(&self) -> Vec<(usize, usize)> { + let stream_count = self.channels.iter().map(|c| c.stream).max().unwrap() + 1; + let mut streams = Vec::new(); + let mut offset = 0; + for si in 0..stream_count { + let stride = self + .channels + .iter() + .filter(|c| c.stream == si) + .map(|c| c.dimension as usize * c.format.component_size()) + .sum(); + streams.push((offset, stride)); + offset += stride * self.vertex_count as usize + } + streams + } + /// Reads a vertex channel and returns dimension count and data converted to floats + pub fn read_channel(&self, channel: VertexDataChannel) -> Option<(usize, Vec<f32>)> { + let channel = &self.channels[channel as u8 as usize]; + if channel.dimension == 0 { + return None; + } + let component_offset = channel.offset as usize; + let component_size = channel.format.component_size(); + let (offset, stride) = self.stream_layout()[channel.stream as usize]; + + let mut out = Vec::new(); + for vi in 0..self.vertex_count as usize { + for di in 0..channel.dimension as usize { + let off = offset + vi * stride + component_offset + component_size * di; + let e = &self.data[off..]; + out.push(match channel.format { + VertexFormat::Float => f32::from_le_bytes([e[0], e[1], e[2], e[3]]), + VertexFormat::Float16 => f16::from_le_bytes([e[0], e[1]]) as f32, + x => todo!("vertex format {x:?}"), + }) + } + } + Some((channel.dimension as usize, out)) + } +} + impl FromValue for ChannelInfo { fn from_value(v: Value) -> Result<Self> { let mut fields = v.as_class("ChannelInfo").unwrap(); @@ -130,18 +194,56 @@ impl FromValue for SubMesh { #[repr(u8)] #[derive(Debug, Serialize, Clone, Copy, PartialEq)] pub enum VertexDataChannel { - Position = 0, - Normal = 1, - Tangent = 2, - Color = 3, - TexCoord0 = 4, - TexCoord1 = 5, - TexCoord2 = 6, - TexCoord3 = 7, - TexCoord4 = 8, - TexCoord5 = 9, - TexCoord6 = 10, - TexCoord7 = 11, - BlendWeight = 12, - BlendIndices = 13, + Position, + Normal, + Tangent, + Color, + TexCoord0, + TexCoord1, + TexCoord2, + TexCoord3, + TexCoord4, + TexCoord5, + TexCoord6, + TexCoord7, + BlendWeight, + BlendIndices, +} + +#[repr(u8)] +#[derive(Debug, Serialize, Clone, Copy, PartialEq)] +pub enum VertexFormat { + Float, + Float16, + UNorm8, + SNorm8, + UNorm16, + SNorm16, + UInt8, + SInt8, + UInt16, + SInt16, + UInt32, + SInt32, +} + +impl FromValue for VertexFormat { + fn from_value(v: Value) -> Result<Self> { + let x = v.as_u8().ok_or(anyhow!("expected u8 vertex format"))?; + if x < 12 { + Ok(unsafe { transmute(x) }) + } else { + bail!("unknown vertex format") + } + } +} +impl VertexFormat { + pub fn component_size(&self) -> usize { + use VertexFormat::*; + match self { + Float | UInt32 | SInt32 => 4, + Float16 | UInt16 | SInt16 | UNorm16 | SNorm16 => 2, + UInt8 | SInt8 | UNorm8 | SNorm8 => 1, + } + } } |