1
Fork 0
mirror of https://github.com/redstrate/Physis.git synced 2025-04-20 11:47:46 +00:00
physis/src/model.rs

561 lines
16 KiB
Rust
Raw Normal View History

// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
use std::io::{Cursor, Seek, SeekFrom};
use binrw::{BinResult, binrw};
2022-08-16 11:52:07 -04:00
use binrw::BinRead;
2022-10-20 11:45:55 -04:00
use binrw::BinReaderExt;
use half::f16;
use crate::gamedata::MemoryBuffer;
2022-07-19 19:29:41 -04:00
#[binrw]
#[derive(Debug)]
#[brw(little)]
pub struct ModelFileHeader {
2022-07-19 19:29:41 -04:00
pub(crate) version: u32,
pub stack_size: u32,
pub runtime_size: u32,
pub vertex_declaration_count: u16,
pub material_count: u16,
pub vertex_offsets: [u32; 3],
pub index_offsets: [u32; 3],
pub vertex_buffer_size: [u32; 3],
pub index_buffer_size: [u32; 3],
pub lod_count: u8,
#[br(map = | x: u8 | x != 0)]
#[bw(map = | x: & bool | -> u8 { if * x { 1 } else { 0 } })]
pub index_buffer_streaming_enabled: bool,
#[br(map = | x: u8 | x != 0)]
#[bw(map = | x: & bool | -> u8 { if * x { 1 } else { 0 } })]
#[brw(pad_after = 1)]
pub has_edge_geometry: bool,
}
#[binrw]
#[brw(repr = u8)]
#[derive(Debug)]
enum ModelFlags1 {
DustOcclusionEnabled = 0x80,
SnowOcclusionEnabled = 0x40,
RainOcclusionEnabled = 0x20,
Unknown1 = 0x10,
LightingReflectionEnabled = 0x08,
WavingAnimationDisabled = 0x04,
LightShadowDisabled = 0x02,
2022-08-16 11:52:07 -04:00
ShadowDisabled = 0x01,
}
#[binrw]
#[brw(repr = u8)]
#[derive(Debug)]
enum ModelFlags2 {
None = 0x0,
Unknown2 = 0x80,
BgUvScrollEnabled = 0x40,
EnableForceNonResident = 0x20,
ExtraLodEnabled = 0x10,
ShadowMaskEnabled = 0x08,
ForceLodRangeEnabled = 0x04,
EdgeGeometryEnabled = 0x02,
2022-08-16 11:52:07 -04:00
Unknown3 = 0x01,
}
#[binrw]
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
pub struct ModelHeader {
#[br(pad_after = 2)]
2022-08-16 11:52:07 -04:00
string_count: u16,
string_size: u32,
#[br(count = string_size)]
2022-08-16 11:52:07 -04:00
strings: Vec<u8>,
2022-08-16 11:52:07 -04:00
radius: f32,
2022-08-16 11:52:07 -04:00
mesh_count: u16,
attribute_count: u16,
submesh_count: u16,
material_count: u16,
bone_count: u16,
bone_table_count: u16,
shape_count: u16,
shape_mesh_count: u16,
shape_value_count: u16,
2022-08-16 11:52:07 -04:00
lod_count: u8,
2022-08-16 11:52:07 -04:00
flags1: ModelFlags1,
2022-08-16 11:52:07 -04:00
element_id_count: u16,
terrain_shadow_mesh_count: u8,
#[br(err_context("radius = {}", radius))]
2022-08-16 11:52:07 -04:00
flags2: ModelFlags2,
2022-08-16 11:52:07 -04:00
model_clip_out_of_distance: f32,
shadow_clip_out_of_distance: f32,
#[br(pad_before = 2)]
#[br(pad_after = 2)]
2022-08-16 11:52:07 -04:00
terrain_shadow_submesh_count: u16,
2022-08-16 11:52:07 -04:00
bg_change_material_index: u8,
#[br(pad_after = 12)]
2022-08-16 11:52:07 -04:00
bg_crest_change_material_index: u8,
}
#[binrw]
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
struct MeshLod {
2022-08-16 11:52:07 -04:00
mesh_index: u16,
mesh_count: u16,
2022-08-16 11:52:07 -04:00
model_lod_range: f32,
texture_lod_range: f32,
2022-08-16 11:52:07 -04:00
water_mesh_index: u16,
water_mesh_count: u16,
2022-08-16 11:52:07 -04:00
shadow_mesh_index: u16,
shadow_mesh_count: u16,
2022-08-16 11:52:07 -04:00
terrain_shadow_mesh_count: u16,
terrain_shadow_mesh_index: u16,
2022-08-16 11:52:07 -04:00
vertical_fog_mesh_index: u16,
vertical_fog_mesh_count: u16,
// unused on win32 according to lumina devs
2022-08-16 11:52:07 -04:00
edge_geometry_size: u32,
edge_geometry_data_offset: u32,
#[br(pad_after = 4)]
2022-08-16 11:52:07 -04:00
polygon_count: u32,
2022-08-16 11:52:07 -04:00
vertex_buffer_size: u32,
index_buffer_size: u32,
vertex_data_offset: u32,
index_data_offset: u32,
}
#[binrw]
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
struct Mesh {
#[br(pad_after = 2)]
2022-08-16 11:52:07 -04:00
vertex_count: u16,
index_count: u32,
2022-08-16 11:52:07 -04:00
material_index: u16,
submesh_index: u16,
submesh_count: u16,
2022-08-16 11:52:07 -04:00
bone_table_index: u16,
start_index: u32,
2022-08-16 11:52:07 -04:00
vertex_buffer_offsets: [u32; 3],
vertex_buffer_strides: [u8; 3],
2022-08-16 11:52:07 -04:00
vertex_stream_count: u8,
}
#[binrw]
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
struct Submesh {
2022-08-16 11:52:07 -04:00
index_offset: i32,
index_count: i32,
2022-08-16 11:52:07 -04:00
attribute_index_mask: u32,
2022-08-16 11:52:07 -04:00
bone_start_index: u16,
bone_count: u16,
}
#[binrw]
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
struct BoneTable {
2022-08-16 11:52:07 -04:00
bone_indices: [u16; 64],
#[br(pad_after = 3)]
2022-08-16 11:52:07 -04:00
bone_count: u8,
}
#[binrw]
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
struct BoundingBox {
2022-08-16 11:52:07 -04:00
min: [f32; 4],
max: [f32; 4],
}
#[binrw]
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
#[brw(little)]
struct ModelData {
2022-08-16 11:52:07 -04:00
header: ModelHeader,
#[br(count = header.element_id_count)]
2022-08-16 11:52:07 -04:00
element_ids: Vec<ElementId>,
#[br(count = 3)]
2022-08-16 11:52:07 -04:00
lods: Vec<MeshLod>,
#[br(count = header.mesh_count)]
2022-08-16 11:52:07 -04:00
meshes: Vec<Mesh>,
#[br(count = header.attribute_count)]
2022-08-16 11:52:07 -04:00
attribute_name_offsets: Vec<u32>,
// TODO: implement terrain shadow meshes
#[br(count = header.submesh_count)]
2022-08-16 11:52:07 -04:00
submeshes: Vec<Submesh>,
// TODO: implement terrain shadow submeshes
#[br(count = header.material_count)]
2022-08-16 11:52:07 -04:00
material_name_offsets: Vec<u32>,
#[br(count = header.bone_count)]
2022-08-16 11:52:07 -04:00
bone_name_offsets: Vec<u32>,
#[br(count = header.bone_table_count)]
2022-08-16 11:52:07 -04:00
bone_tables: Vec<BoneTable>,
// TODO: implement shapes
#[br(temp)]
#[bw(ignore)]
2022-08-16 11:52:07 -04:00
submesh_bone_map_size: u32,
#[br(count = submesh_bone_map_size / 2, err_context("lods = {:#?}", lods))]
2022-08-16 11:52:07 -04:00
submesh_bone_map: Vec<u16>,
#[br(temp)]
#[bw(ignore)]
2022-08-16 11:52:07 -04:00
padding_amount: u8,
#[br(pad_before = padding_amount)]
2022-08-16 11:52:07 -04:00
bounding_box: BoundingBox,
model_bounding_box: BoundingBox,
water_bounding_box: BoundingBox,
vertical_fog_bounding_box: BoundingBox,
#[br(count = header.bone_count)]
2022-08-16 11:52:07 -04:00
bone_bounding_boxes: Vec<BoundingBox>,
}
#[binrw]
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
struct ElementId {
2022-08-16 11:52:07 -04:00
element_id: u32,
parent_bone_name: u32,
translate: [f32; 3],
rotate: [f32; 3],
}
#[binrw]
#[brw(repr = u8)]
#[derive(Copy, Clone, Debug, PartialEq)]
enum VertexType {
Invalid = 0,
Single3 = 2,
Single4 = 3,
UInt = 5,
ByteFloat4 = 8,
Half2 = 13,
2022-08-16 11:52:07 -04:00
Half4 = 14,
}
#[binrw]
#[brw(repr = u8)]
#[derive(Copy, Clone, Debug)]
enum VertexUsage {
Position = 0,
BlendWeights = 1,
BlendIndices = 2,
Normal = 3,
UV = 4,
Tangent2 = 5,
Tangent1 = 6,
Color = 7,
}
#[binrw]
#[derive(Copy, Clone, Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
#[brw(little)]
struct VertexElement {
2022-08-16 11:52:07 -04:00
stream: u8,
offset: u8,
vertex_type: VertexType,
vertex_usage: VertexUsage,
#[br(pad_after = 3)]
2022-08-16 11:52:07 -04:00
usage_index: u8,
}
#[derive(Clone)]
#[repr(C)]
pub struct Vertex {
2022-08-16 11:52:07 -04:00
pub position: [f32; 3],
pub uv: [f32; 2],
pub normal: [f32; 3],
pub bone_weight: [f32; 4],
2022-08-16 11:52:07 -04:00
pub bone_id: [u8; 4],
}
pub struct Part {
2022-08-16 11:52:07 -04:00
pub vertices: Vec<Vertex>,
pub indices: Vec<u16>,
2023-03-31 21:31:15 -04:00
pub material_index: u16
}
pub struct Lod {
2022-08-16 11:52:07 -04:00
pub parts: Vec<Part>,
}
pub struct MDL {
2022-08-16 11:52:07 -04:00
pub lods: Vec<Lod>,
pub affected_bone_names: Vec<String>,
pub material_names: Vec<String>
}
impl MDL {
2022-08-16 11:52:07 -04:00
pub fn from_existing(buffer: &MemoryBuffer) -> Option<MDL> {
let mut cursor = Cursor::new(buffer);
let model_file_header = ModelFileHeader::read(&mut cursor).unwrap();
#[derive(Clone)]
struct VertexDeclaration {
2022-08-16 11:52:07 -04:00
elements: Vec<VertexElement>,
}
2022-08-16 11:52:07 -04:00
let mut vertex_declarations: Vec<VertexDeclaration> =
vec![
VertexDeclaration { elements: vec![] };
model_file_header.vertex_declaration_count as usize
];
for declaration in &mut vertex_declarations {
let mut element = VertexElement::read(&mut cursor).unwrap();
loop {
declaration.elements.push(element);
element = VertexElement::read(&mut cursor).unwrap();
if element.stream == 255 {
break;
}
2022-08-16 11:52:07 -04:00
}
let to_seek = 17 * 8 - (declaration.elements.len() + 1) * 8;
cursor.seek(SeekFrom::Current(to_seek as i64)).ok()?;
}
let model = ModelData::read(&mut cursor).unwrap();
2022-08-10 14:51:50 -04:00
let mut affected_bone_names = vec![];
for mut offset in model.bone_name_offsets {
let mut string = String::new();
let mut next_char = model.header.strings[offset as usize] as char;
while next_char != '\0' {
string.push(next_char);
offset += 1;
next_char = model.header.strings[offset as usize] as char;
}
affected_bone_names.push(string);
}
let mut material_names = vec![];
for mut offset in model.material_name_offsets {
let mut string = String::new();
let mut next_char = model.header.strings[offset as usize] as char;
while next_char != '\0' {
string.push(next_char);
offset += 1;
next_char = model.header.strings[offset as usize] as char;
}
material_names.push(string);
}
let mut lods = vec![];
for i in 0..model.header.lod_count {
let mut parts = vec![];
2022-08-16 11:52:07 -04:00
for j in model.lods[i as usize].mesh_index
..model.lods[i as usize].mesh_index + model.lods[i as usize].mesh_count
{
let declaration = &vertex_declarations[j as usize];
let vertex_count = model.meshes[j as usize].vertex_count;
2023-03-31 21:31:15 -04:00
let material_index = model.meshes[j as usize].material_index;
let default_vertex = Vertex {
position: [0.0; 3],
uv: [0.0; 2],
normal: [0.0; 3],
bone_weight: [0.0; 4],
2022-08-16 11:52:07 -04:00
bone_id: [0u8; 4],
};
let mut vertices: Vec<Vertex> = vec![default_vertex; vertex_count as usize];
for k in 0..vertex_count {
for element in &declaration.elements {
2022-08-16 11:52:07 -04:00
cursor
.seek(SeekFrom::Start(
(model.lods[i as usize].vertex_data_offset
+ model.meshes[j as usize].vertex_buffer_offsets
[element.stream as usize]
+ element.offset as u32
+ model.meshes[i as usize].vertex_buffer_strides
[element.stream as usize]
as u32
* k as u32) as u64,
))
.ok()?;
match element.vertex_usage {
VertexUsage::Position => {
match element.vertex_type {
VertexType::Half4 => {
vertices[k as usize].position.clone_from_slice(&MDL::read_half4(&mut cursor).unwrap()[0..3]);
}
VertexType::Single3 => {
vertices[k as usize].position = MDL::read_single3(&mut cursor).unwrap();
}
_ => {
panic!("Unexpected vertex type for position: {:#?}", element.vertex_type);
}
}
}
VertexUsage::BlendWeights => {
match element.vertex_type {
VertexType::ByteFloat4 => {
vertices[k as usize].bone_weight = MDL::read_byte_float4(&mut cursor).unwrap();
}
_ => {
panic!("Unexpected vertex type for blendweight: {:#?}", element.vertex_type);
}
}
}
VertexUsage::BlendIndices => {
match element.vertex_type {
VertexType::UInt => {
vertices[k as usize].bone_id = MDL::read_uint(&mut cursor).unwrap();
}
_ => {
panic!("Unexpected vertex type for blendindice: {:#?}", element.vertex_type);
}
}
}
VertexUsage::Normal => {
match element.vertex_type {
VertexType::Half4 => {
vertices[k as usize].normal.clone_from_slice(&MDL::read_half4(&mut cursor).unwrap()[0..3]);
}
VertexType::Single3 => {
vertices[k as usize].normal = MDL::read_single3(&mut cursor).unwrap();
}
_ => {
panic!("Unexpected vertex type for normal: {:#?}", element.vertex_type);
}
}
}
VertexUsage::UV => {
match element.vertex_type {
VertexType::Half4 => {
vertices[k as usize].uv.clone_from_slice(&MDL::read_half4(&mut cursor).unwrap()[0..2]);
}
VertexType::Single4 => {
vertices[k as usize].uv.clone_from_slice(&MDL::read_single4(&mut cursor).unwrap()[0..2]);
}
_ => {
panic!("Unexpected vertex type for uv: {:#?}", element.vertex_type);
}
}
}
VertexUsage::Tangent2 => {}
VertexUsage::Tangent1 => {}
VertexUsage::Color => {}
}
}
}
2022-08-16 11:52:07 -04:00
cursor
.seek(SeekFrom::Start(
(model_file_header.index_offsets[i as usize]
+ (model.meshes[j as usize].start_index * 2))
as u64,
))
.ok()?;
// TODO: optimize!
2022-08-16 11:52:07 -04:00
let mut indices: Vec<u16> =
Vec::with_capacity(model.meshes[j as usize].index_count as usize);
for _ in 0..model.meshes[j as usize].index_count {
indices.push(cursor.read_le::<u16>().ok()?);
}
2023-03-31 21:31:15 -04:00
parts.push(Part { vertices, indices, material_index });
}
2022-08-16 11:52:07 -04:00
lods.push(Lod { parts });
}
2022-07-19 19:29:41 -04:00
Some(MDL {
2022-08-10 14:51:50 -04:00
lods,
2022-08-16 11:52:07 -04:00
affected_bone_names,
material_names
})
}
fn read_byte_float4(cursor: &mut Cursor<&MemoryBuffer>) -> Option<[f32; 4]> {
let mut arr: [f32; 4] = [0.0; 4];
for i in 0..4 {
arr[i] = f32::from(cursor.read_le::<u8>().ok()?) / 255.0;
}
Some(arr)
}
fn read_half4(cursor: &mut Cursor<&MemoryBuffer>) -> Option<[f32; 4]> {
let mut arr: [f32; 4] = [0.0; 4];
for i in 0..3 {
arr[i] = f16::from_bits(cursor.read_le::<u16>().ok()?).to_f32();
}
Some(arr)
}
fn read_uint(cursor: &mut Cursor<&MemoryBuffer>) -> BinResult<[u8; 4]> {
cursor.read_le::<[u8; 4]>()
}
fn read_single3(cursor: &mut Cursor<&MemoryBuffer>) -> BinResult<[f32; 3]> {
cursor.read_le::<[f32; 3]>()
}
fn read_single4(cursor: &mut Cursor<&MemoryBuffer>) -> BinResult<[f32; 4]> {
cursor.read_le::<[f32; 4]>()
}
2022-08-16 11:52:07 -04:00
}