This commit is contained in:
Robin Appelman 2022-03-10 23:32:51 +01:00
commit b0d3245e07
13 changed files with 510 additions and 55 deletions

206
src/vtx/mod.rs Normal file
View file

@ -0,0 +1,206 @@
mod raw;
use crate::ModelError;
use binrw::BinReaderExt;
use raw::*;
pub use raw::{MeshFlags, StripFlags, StripGroupFlags, Vertex};
use std::io::Cursor;
pub const MDL_VERSION: i32 = 7;
type Result<T> = std::result::Result<T, ModelError>;
#[derive(Debug, Clone)]
pub struct Vtx {
pub header: VtxHeader,
pub body_parts: Vec<BodyPart>,
}
impl Vtx {
pub fn read(data: &[u8]) -> Result<Self> {
let mut reader = Cursor::new(data);
let header: VtxHeader = reader.read_le()?;
Ok(Vtx {
body_parts: header
.body_indexes()
.map(|index| {
let data = data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
data: "BodyPart",
offset: index,
})?;
let mut reader = Cursor::new(data);
let header = reader.read_le()?;
BodyPart::read(data, header)
})
.collect::<Result<_>>()?,
header,
})
}
}
#[derive(Debug, Clone)]
pub struct BodyPart {
pub models: Vec<Model>,
}
impl BodyPart {
fn read(data: &[u8], header: BodyPartHeader) -> Result<Self> {
Ok(BodyPart {
models: header
.model_indexes()
.map(|index| {
let data = data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
data: "Model",
offset: index,
})?;
let mut reader = Cursor::new(data);
let header = reader.read_le()?;
Model::read(data, header)
})
.collect::<Result<_>>()?,
})
}
}
#[derive(Debug, Clone)]
pub struct Model {
pub lods: Vec<ModelLod>,
}
impl Model {
fn read(data: &[u8], header: ModelHeader) -> Result<Self> {
Ok(Model {
lods: header
.lod_indexes()
.map(|index| {
let data = data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
data: "ModelLod",
offset: index,
})?;
let mut reader = Cursor::new(data);
let header = reader.read_le()?;
ModelLod::read(data, header)
})
.collect::<Result<_>>()?,
})
}
}
#[derive(Debug, Clone)]
pub struct ModelLod {
pub meshes: Vec<Mesh>,
pub switch_point: f32,
}
impl ModelLod {
fn read(data: &[u8], header: ModelLodHeader) -> Result<Self> {
Ok(ModelLod {
meshes: header
.mesh_indexes()
.map(|index| {
let data = data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
data: "Mesh",
offset: index,
})?;
let mut reader = Cursor::new(data);
let header = reader.read_le()?;
Mesh::read(data, header)
})
.collect::<Result<_>>()?,
switch_point: header.switch_point,
})
}
}
#[derive(Debug, Clone)]
pub struct Mesh {
pub strip_groups: Vec<StripGroup>,
pub flags: MeshFlags,
}
impl Mesh {
fn read(data: &[u8], header: MeshHeader) -> Result<Self> {
Ok(Mesh {
strip_groups: header
.strip_group_indexes()
.map(|index| {
let data = data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
data: "StripGroup",
offset: index,
})?;
let mut reader = Cursor::new(data);
let header = reader.read_le()?;
StripGroup::read(data, header)
})
.collect::<Result<_>>()?,
flags: header.flags,
})
}
}
#[derive(Debug, Clone)]
pub struct StripGroup {
// todo vertex indexes
// todo topologies
pub vertices: Vec<Vertex>,
pub strips: Vec<Strip>,
pub flags: StripGroupFlags,
}
impl StripGroup {
fn read(data: &[u8], header: StripGroupHeader) -> Result<Self> {
Ok(StripGroup {
vertices: header
.vertex_indexes()
.map(|index| {
let data = data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
data: "Vertex",
offset: index,
})?;
let mut reader = Cursor::new(data);
reader.read_le().map_err(ModelError::from)
})
.collect::<Result<_>>()?,
strips: header
.strip_indexes()
.map(|index| {
let data = data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
data: "Strip",
offset: index,
})?;
let mut reader = Cursor::new(data);
let header = reader.read_le()?;
Strip::read(data, header)
})
.collect::<Result<_>>()?,
flags: header.flags,
})
}
}
#[derive(Debug, Clone)]
pub struct Strip {
// todo vertex indexes
// todo bone state changes
pub vertices: Vec<Vertex>,
pub flags: StripFlags,
}
impl Strip {
fn read(data: &[u8], header: StripHeader) -> Result<Self> {
Ok(Strip {
vertices: header
.vertex_indexes()
.map(|index| {
let data = data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
data: "Vertex",
offset: index,
})?;
let mut reader = Cursor::new(data);
reader.read_le().map_err(ModelError::from)
})
.collect::<Result<_>>()?,
flags: header.flags,
})
}
}

202
src/vtx/raw.rs Normal file
View file

@ -0,0 +1,202 @@
use crate::index_range;
use binrw::BinRead;
use bitflags::bitflags;
use std::mem::size_of;
#[derive(Debug, Clone, BinRead)]
pub struct VtxHeader {
pub version: i32,
pub vertex_cache_size: i32,
pub max_bones_per_strip: u16,
pub max_bones_per_triangle: u16,
pub max_bones_per_vertex: i32,
pub checksum: [u8; 4],
pub lod_count: i32,
pub material_replacement_list: i32,
body_part_count: i32,
body_part_offset: i32,
}
static_assertions::const_assert_eq!(size_of::<VtxHeader>(), 36);
impl VtxHeader {
pub fn body_indexes(&self) -> impl Iterator<Item = usize> {
index_range(
self.body_part_offset,
self.body_part_count,
size_of::<BodyPartHeader>(),
)
}
}
#[derive(Debug, Clone, BinRead)]
pub struct BodyPartHeader {
model_count: i32,
model_offset: i32,
}
impl BodyPartHeader {
pub fn model_indexes(&self) -> impl Iterator<Item = usize> {
index_range(
self.model_offset,
self.model_count,
size_of::<ModelHeader>(),
)
}
}
#[derive(Debug, Clone, BinRead)]
pub struct ModelHeader {
lod_count: i32,
lod_offset: i32,
}
impl ModelHeader {
pub fn lod_indexes(&self) -> impl Iterator<Item = usize> {
index_range(self.lod_offset, self.lod_count, size_of::<ModelLodHeader>())
}
}
#[derive(Debug, Clone, BinRead)]
pub struct ModelLodHeader {
mesh_count: i32,
mesh_offset: i32,
pub switch_point: f32,
}
static_assertions::const_assert_eq!(size_of::<ModelLodHeader>(), 12);
impl ModelLodHeader {
pub fn mesh_indexes(&self) -> impl Iterator<Item = usize> {
index_range(self.mesh_offset, self.mesh_count, size_of::<MeshHeader>())
}
}
#[derive(Debug, Clone, Copy, BinRead)]
#[repr(packed)]
pub struct MeshHeader {
strip_group_count: i32,
strip_group_offset: i32,
pub flags: MeshFlags,
}
impl MeshHeader {
pub fn strip_group_indexes(&self) -> impl Iterator<Item = usize> {
index_range(
self.strip_group_offset,
self.strip_group_count,
size_of::<StripGroupHeader>(),
)
}
}
bitflags! {
#[derive(BinRead)]
pub struct MeshFlags: u8 {
const IS_TEETH = 0x01;
const IS_EYES = 0x02;
}
}
#[derive(Debug, Clone, Copy, BinRead)]
#[repr(packed)]
pub struct StripGroupHeader {
vertex_count: i32,
vertex_offset: i32,
index_count: i32,
index_offset: i32,
strip_count: i32,
strip_offset: i32,
pub flags: StripGroupFlags,
}
static_assertions::const_assert_eq!(size_of::<StripGroupHeader>(), 25);
impl StripGroupHeader {
/// Index into the VVD file vertexes
pub fn vertex_indexes(&self) -> impl Iterator<Item = usize> {
index_range(
self.vertex_offset,
self.vertex_count,
size_of::<u16>(), // Vertex index from .VVD's vertex array
)
}
pub fn index_indexes(&self) -> impl Iterator<Item = usize> {
index_range(self.index_offset, self.index_count, size_of::<Vertex>())
}
pub fn strip_indexes(&self) -> impl Iterator<Item = usize> {
index_range(
self.strip_offset,
self.strip_count,
size_of::<StripHeader>(),
)
}
}
bitflags! {
#[derive(BinRead)]
pub struct StripGroupFlags: u8 {
const IS_FLEXED = 0x01;
const IS_HWSKINNED = 0x02;
const IS_DELTA_FLEXED = 0x04;
const SUPPRESS_HW_MORPH = 0x08;
}
}
#[derive(Debug, Clone, Copy, BinRead)]
#[repr(packed)]
pub struct StripHeader {
index_count: i32,
index_offset: i32,
vertex_count: i32,
vertex_offset: i32,
pub flags: StripFlags,
bone_state_change_count: i32,
bone_state_change_offset: i32,
}
static_assertions::const_assert_eq!(size_of::<StripHeader>(), 25);
bitflags! {
#[derive(BinRead)]
pub struct StripFlags: u8 {
const IS_TRI_LIST = 0x01;
const IS_TRI_STRIP = 0x02;
}
}
impl StripHeader {
/// Index into the VVD file vertexes
pub fn vertex_indexes(&self) -> impl Iterator<Item = usize> {
index_range(
self.vertex_offset,
self.vertex_count,
size_of::<u16>(), // Vertex index from .VVD's vertex array
)
}
pub fn index_indexes(&self) -> impl Iterator<Item = usize> {
index_range(self.index_offset, self.index_count, size_of::<Vertex>())
}
pub fn bone_state_change_indexes(&self) -> impl Iterator<Item = usize> {
index_range(
self.bone_state_change_offset,
self.bone_state_change_count,
todo!(),
)
}
}
#[derive(Debug, Clone, Copy, BinRead)]
#[repr(packed)]
pub struct Vertex {
pub bone_weight_indexes: [u8; 3],
pub bone_count: u8,
pub original_mesh_vertex_id: u16,
pub bone_id: [u8; 3],
}
static_assertions::const_assert_eq!(size_of::<Vertex>(), 9);