aboutsummaryrefslogtreecommitdiff
path: root/src/classes
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-03-13 14:09:36 +0100
committermetamuffin <metamuffin@disroot.org>2025-03-13 14:09:36 +0100
commit664d54952323a52132377ce9ed3c4a552c0993fa (patch)
treedb6af4ac220e6fcfcdf7954b861b0a0762cf8579 /src/classes
parente4d1504b1d7e575702895d93781f3650ff190bb3 (diff)
downloadunity-tools-664d54952323a52132377ce9ed3c4a552c0993fa.tar
unity-tools-664d54952323a52132377ce9ed3c4a552c0993fa.tar.bz2
unity-tools-664d54952323a52132377ce9ed3c4a552c0993fa.tar.zst
can extract meshes
Diffstat (limited to 'src/classes')
-rw-r--r--src/classes/mesh.rs148
-rw-r--r--src/classes/mod.rs35
2 files changed, 128 insertions, 55 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,
+ }
+ }
}
diff --git a/src/classes/mod.rs b/src/classes/mod.rs
index 501fdf9..038e701 100644
--- a/src/classes/mod.rs
+++ b/src/classes/mod.rs
@@ -53,41 +53,12 @@ impl HValue {
"GameObject" => Self::GameObject(GameObject::from_value(value)?),
"Transform" => Self::Transform(Transform::from_value(value)?),
"Texture2D" => Self::Texture2D(Texture2D::from_value(value)?),
- "StreamingInfo" => Self::StreamingInfo(StreamingInfo::from_value(value)?),
+ // "StreamingInfo" => Self::StreamingInfo(StreamingInfo::from_value(value)?),
// "SubMesh" => Self::SubMesh(SubMesh::from_value(value)?),
- "Mesh" => Self::Mesh(Mesh::from_value(value)?),
+ // "Mesh" => Self::Mesh(Mesh::from_value(value)?),
// "VertexData" => Self::VertexData(VertexData::from_value(value)?),
// "ChannelInfo" => Self::ChannelInfo(ChannelInfo::from_value(value)?),
- _ => {
- let Value::Object { class, fields } = value else {
- unreachable!()
- };
- let mut fields = fields
- .into_iter()
- .map(|(k, v)| Ok((k, HValue::from_value(v)?)))
- .collect::<Result<BTreeMap<_, _>>>()?;
- match class.as_str() {
- "map" => {
- let Self::Array(a) = fields.remove("Array").unwrap() else {
- unreachable!()
- };
- Self::Map(
- a.into_iter()
- .map(|e| {
- let Self::Pair(k, v) = e else { unreachable!() };
- (k.as_value().unwrap().clone().as_string().unwrap(), *v)
- })
- .collect(),
- )
- }
- "pair" => Self::Pair(
- Box::new(fields.remove("first").unwrap()),
- Box::new(fields.remove("second").unwrap()),
- ),
- "vector" => fields.remove("Array").unwrap(),
- _ => Self::Object { class, fields },
- }
- }
+ _ => Self::Value([value]),
}
}
Value::Array(a) => Self::Array(