From e366c5830ac63207711ac908abca4098c31c2236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thea=20Sch=C3=B6bl?= Date: Thu, 18 May 2023 14:26:03 +0200 Subject: [PATCH] pr3d --- README.md | 2 +- godot/starforce/test.gd | 12 - godot/starforce/test.tscn | 6 +- rust/Cargo.lock | 1 + rust/powerrender-3d/Cargo.toml | 1 + rust/powerrender-3d/src/main.rs | 6 +- rust/powerrender-3d/src/pro/chunk.rs | 2 +- .../src/pro/internal/materials.rs | 49 +++ rust/powerrender-3d/src/pro/internal/mod.rs | 86 +++++ .../src/pro/internal/segment.rs | 92 +++++ .../src/pro/internal/surface.rs | 83 +++++ rust/powerrender-3d/src/pro/mod.rs | 351 +++++++----------- 12 files changed, 460 insertions(+), 231 deletions(-) delete mode 100644 godot/starforce/test.gd create mode 100644 rust/powerrender-3d/src/pro/internal/materials.rs create mode 100644 rust/powerrender-3d/src/pro/internal/mod.rs create mode 100644 rust/powerrender-3d/src/pro/internal/segment.rs create mode 100644 rust/powerrender-3d/src/pro/internal/surface.rs diff --git a/README.md b/README.md index ffd003d..499b4e0 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Support of games primarily depends on who developed them - Games with structural * `.wtn` "Witan Entertainment Engine" * [ ] Moorhuhn * [ ] Moorhuhn 2 -* `.dat` Torque3D Engine +* `.dat` Power Render 3D Engine * [ ] Moorhuhn Kart * [ ] Moorhuhn Kart Extra * `data/datafile.dat` "Sproing Interactive Engine" diff --git a/godot/starforce/test.gd b/godot/starforce/test.gd deleted file mode 100644 index 54ab456..0000000 --- a/godot/starforce/test.gd +++ /dev/null @@ -1,12 +0,0 @@ -@tool -extends Node3D - -@export var click = false: - get: - return false - set(value): - var lwo = Lwo.new() - var mesh = lwo.get_mesh("E:\\Games\\Moorhuhn Kart 3\\extract\\D\\Moorhuhnkart\\3dobjects_cars\\affe.lwo") - var instance = MeshInstance3D.new() - instance.mesh = mesh - add_child(instance) diff --git a/godot/starforce/test.tscn b/godot/starforce/test.tscn index 0311418..5b84daa 100644 --- a/godot/starforce/test.tscn +++ b/godot/starforce/test.tscn @@ -1,6 +1,8 @@ [gd_scene load_steps=2 format=3 uid="uid://kyw4wuusc33g"] -[ext_resource type="Script" path="res://starforce/test.gd" id="1_uc463"] +[ext_resource type="ArrayMesh" path="sar://D:/Moorhuhnkart/3dobjects_cars/affe.lwo" id="2_ab234"] [node name="test" type="Node3D"] -script = ExtResource("1_uc463") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +mesh = ExtResource("2_ab234") diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 5d8690f..b2fb60e 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -650,6 +650,7 @@ name = "powerrender-3d" version = "0.1.0" dependencies = [ "binrw", + "half", ] [[package]] diff --git a/rust/powerrender-3d/Cargo.toml b/rust/powerrender-3d/Cargo.toml index d233aef..ad1ca4d 100644 --- a/rust/powerrender-3d/Cargo.toml +++ b/rust/powerrender-3d/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] binrw = "0.11.1" +half = "2.2.1" diff --git a/rust/powerrender-3d/src/main.rs b/rust/powerrender-3d/src/main.rs index 95c4167..99270a1 100644 --- a/rust/powerrender-3d/src/main.rs +++ b/rust/powerrender-3d/src/main.rs @@ -1,11 +1,11 @@ -use crate::pro::read_power_render_object; -use binrw::{BinRead, BinResult}; +use crate::pro::PowerRenderObject; +use binrw::BinRead; use std::fs::File; pub mod pro; fn main() { let mut file = File::open(r#"E:\Games\Moorhuhn Kart\data\alk.pro"#).unwrap(); - let result = read_power_render_object(&mut file).unwrap(); + let result = PowerRenderObject::read(&mut file).unwrap(); println!("{:#?}", result); } diff --git a/rust/powerrender-3d/src/pro/chunk.rs b/rust/powerrender-3d/src/pro/chunk.rs index 2fb9eab..d7e96f9 100644 --- a/rust/powerrender-3d/src/pro/chunk.rs +++ b/rust/powerrender-3d/src/pro/chunk.rs @@ -1,4 +1,4 @@ -use binrw::io::{TakeSeek, TakeSeekExt}; +use binrw::io::TakeSeekExt; use binrw::meta::{EndianKind, ReadEndian}; use binrw::{until_eof, BinRead, BinReaderExt, BinResult, Endian}; use std::io::{Read, Seek, SeekFrom}; diff --git a/rust/powerrender-3d/src/pro/internal/materials.rs b/rust/powerrender-3d/src/pro/internal/materials.rs new file mode 100644 index 0000000..3d23f1b --- /dev/null +++ b/rust/powerrender-3d/src/pro/internal/materials.rs @@ -0,0 +1,49 @@ +use crate::pro::Chunk; +use binrw::{binread, until_eof, NullString}; + +#[binread] +pub struct PrMaterialList { + #[br(parse_with = until_eof)] + pub chunks: Vec, +} + +#[binread] +#[derive(Debug)] +pub enum PrMaterialChunks { + #[br(magic = 0x2101u16)] + MaterialName(#[br(map = Chunk::inner)] NullString), + #[br(magic = 0x2102u16)] + MaterialMethod(#[br(map = Chunk::inner)] u32), + #[br(magic = 0x2103u16)] + MaterialTexNum(#[br(map = Chunk::inner)] u32), + #[br(magic = 0x2104u16)] + MaterialBaseColor(#[br(map = Chunk::inner)] u8), + #[br(magic = 0x2105u16)] + MaterialShades(#[br(map = Chunk::inner)] u32), + #[br(magic = 0x2106u16)] + MaterialTable(#[br(map = Chunk::inner)] u8), + #[br(magic = 0x2107u16)] + MaterialEnvMap(#[br(map = Chunk::inner)] (u8, u8)), + #[br(magic = 0x2108u16)] + MaterialMipMap(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x2109u16)] + MaterialColor(#[br(map = Chunk::inner)] [f32; 4]), + #[br(magic = 0x2110u16)] + MaterialNumStages(#[br(map = Chunk::inner)] u32), + #[br(magic = 0x2111u16)] + MaterialTexNum2(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x2112u16)] + MaterialEnvMap2(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x2113u16)] + MaterialBump(#[br(map = Chunk::inner)] (f32, [f32; 4])), + #[br(magic = 0x2114u16)] + MaterialSpecular(#[br(map = Chunk::inner)] ([f32; 4], f32)), + #[br(magic = 0x2115u16)] + MaterialTwoSided(#[br(map = Chunk::inner)] u8), + #[br(magic = 0x2116u16)] + MaterialVertexShaderName(#[br(map = Chunk::inner)] NullString), + #[br(magic = 0x2117u16)] + MaterialPixelShaderName(#[br(map = Chunk::inner)] NullString), + #[br(magic = 0x2199u16)] + MaterialEnd(#[br(map = Chunk::inner)] ()), +} diff --git a/rust/powerrender-3d/src/pro/internal/mod.rs b/rust/powerrender-3d/src/pro/internal/mod.rs new file mode 100644 index 0000000..92eaab1 --- /dev/null +++ b/rust/powerrender-3d/src/pro/internal/mod.rs @@ -0,0 +1,86 @@ +use crate::pro::chunk::Chunk; +use crate::pro::internal::materials::{PrMaterialChunks, PrMaterialList}; +use crate::pro::internal::segment::{PrSegmentChunks, PrSegmentList}; +use binrw::{binread, until_eof, BinRead, NullString}; + +pub mod materials; +pub mod segment; +pub mod surface; + +#[binread] +pub struct PrObject { + #[br(parse_with = until_eof)] + pub chunks: Vec, +} + +#[binread] +#[derive(Debug)] +pub enum PrChunks { + #[br(magic = 0x0000u16)] + Version(#[br(map = Chunk::inner)] f32), + #[br(magic = 0x0100u16)] + ObjectName(#[br(map = Chunk::inner)] NullString), + #[br(magic = 0x0101u16)] + ObjectFlags(#[br(map = Chunk::inner)] u32), + #[br(magic = 0x1000u16)] + Segments(#[br(map = |it: Chunk::| it.0.chunks)] Vec), + #[br(magic = 0x2010u16)] + TextureList(#[br(map = |it: Chunk::>| it.0.data)] Vec), + #[br(magic = 0x2011u16)] + TextureAlpha(#[br(map = |it: Chunk::>| it.0.data)] Vec), + #[br(magic = 0x2012u16)] + TextureStage(#[br(map = |it: Chunk::>| it.0.data)] Vec), + #[br(magic = 0x2013u16)] + TextureFormat(#[br(map = |it: Chunk::>| it.0.data)] Vec), + #[br(magic = 0x2014u16)] + TextureMulti(#[br(map = |it: Chunk::>| it.0.data)] Vec), + #[br(magic = 0x2100u16)] + MaterialList(#[br(map = |it: Chunk::| it.0.chunks)] Vec), + #[br(magic = 0x3000u16)] + Camera(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x3010u16)] + ObjectBbox(#[br(map = Chunk::inner)] Box), + #[br(magic = 0x3100u16)] + VertexShaderList(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x3101u16)] + VertexShaderList2(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x3102u16)] + VertexShaderList3(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x3200u16)] + PixelShaderList(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x3202u16)] + PixelShaderList3(#[br(map = Chunk::inner)] ()), // TODO +} + +#[binread] +#[derive(Debug)] +pub struct PrBoundingInfo { + pub min_x: f32, + pub max_x: f32, + pub min_y: f32, + pub max_y: f32, + pub min_z: f32, + pub max_z: f32, + /// Four corners + center vertex + pub box_center: [[f32; 3]; 9], + /// Same, but transformed + pub t_box_center: [[f32; 3]; 9], + pub radius: f32, +} + +#[binread] +pub struct VertexShaderList { + pub name: NullString, + pub flags: u32, + pub size: u32, + pub num_constants: u32, +} + +#[binread] +pub(crate) struct VecWithCount BinRead = Args> + 'static> +{ + #[br(temp)] + count: u16, + #[br(count = count)] + pub data: Vec, +} diff --git a/rust/powerrender-3d/src/pro/internal/segment.rs b/rust/powerrender-3d/src/pro/internal/segment.rs new file mode 100644 index 0000000..1ca84ac --- /dev/null +++ b/rust/powerrender-3d/src/pro/internal/segment.rs @@ -0,0 +1,92 @@ +use crate::pro::internal::PrBoundingInfo; +use crate::pro::Chunk; +use binrw::{binread, until_eof, NullString}; + +#[binread] +pub struct PrSegmentList { + pub segments: u32, + #[br(parse_with = until_eof)] + pub chunks: Vec, +} + +#[binread] +#[derive(Debug)] +pub enum PrSegmentChunks { + #[br(magic = 0x1010u16)] + SegmentName(#[br(map = Chunk::inner)] NullString), + #[br(magic = 0x1020u16)] + SegmentFlags(#[br(map = Chunk::inner)] u32), + #[br(magic = 0x1022u16)] + SegmentBbox(#[br(map = Chunk::inner)] Box), + #[br(magic = 0x1030u16)] + Vertices(#[br(map = Chunk::inner)] PrVertices), + #[br(magic = 0x1040u16)] + Faces(#[br(map = |it: Chunk| it.inner().faces)] Vec), + #[br(magic = 0x1050u16)] + SegBuf(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x1051u16)] + SegBuf2(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x1052u16)] + SegBuf3(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x1060u16)] + LodInfo(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x1F00u16)] + KeyframePivot(#[br(map = Chunk::inner)] [f32; 3]), + #[br(magic = 0x1F10u16)] + KeyframeMatrix(#[br(map = Chunk::inner)] [f32; 16]), + #[br(magic = 0x1F20u16)] + KeyframeRotKeys(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x1F30u16)] + KeyframePosKeys(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x1F40u16)] + KeyframeScaleKeys(#[br(map = Chunk::inner)] ()), // TODO + #[br(magic = 0x1F50u16)] + KeyframeLinks(#[br(map = Chunk::inner)] [i32; 3]), + #[br(magic = 0x4000u16)] + TexCoords(#[br(map = Chunk::inner)] ()), // TODO +} + +#[binread] +#[derive(Debug)] +pub struct PrVertices { + #[br(temp)] + count: i32, + #[br(count = count)] + pub vertices: Vec, +} + +#[binread] +#[derive(Debug)] +pub struct PrVertex { + pub position: [i32; 3], + pub normal: [i16; 3], +} + +#[binread] +#[derive(Debug)] +struct PrFaces { + #[br(temp)] + pub num_faces: i32, + #[br(count = num_faces)] + pub faces: Vec, +} + +#[binread] +#[derive(Debug)] +pub struct PrFace { + pub material: u16, + pub back_material: u16, + pub i0: i32, + pub i1: i32, + pub i2: i32, + pub u0: i32, + pub u1: i32, + pub u2: i32, + pub v0: i32, + pub v1: i32, + pub v2: i32, + pub color: [i32; 3], + pub normal: [u16; 3], + pub flags: u8, + pub dot_prod: u16, +} diff --git a/rust/powerrender-3d/src/pro/internal/surface.rs b/rust/powerrender-3d/src/pro/internal/surface.rs new file mode 100644 index 0000000..b0d4766 --- /dev/null +++ b/rust/powerrender-3d/src/pro/internal/surface.rs @@ -0,0 +1,83 @@ +use crate::pro::internal::segment::{PrFace, PrVertices}; +use crate::pro::PowerRenderSurface; +use std::collections::HashMap; + +#[derive(Hash, PartialEq, Eq, Copy, Clone)] +struct UniqueVertex { + pub pos: [i32; 3], + pub normal: [i16; 3], + pub color: [i32; 3], + pub uv: [i32; 2], +} + +impl PrVertices { + /// converts the relatively uncommonly used format into one that can be used more easily + /// by putting it into an array of surfaces that are separated by material + pub fn into_surfaces(self, faces: Vec) -> Vec { + let mut surfaces = HashMap::new(); + + for face in faces.into_iter() { + let surface_id = ((face.material as u32) << 16) | (face.back_material as u32); + let (index_map, surface) = if let Some(surface) = surfaces.get_mut(&surface_id) { + surface + } else { + let surface = PowerRenderSurface { + material_index: face.material as usize, + back_material_index: face.back_material as usize, + uvs: vec![], + indices: vec![], + colors: vec![], + normals: vec![], + vertices: vec![], + }; + surfaces.insert(surface_id, (HashMap::::new(), surface)); + surfaces.get_mut(&surface_id).unwrap() + }; + + let triangle = [ + (face.i0, face.u0, face.v0), + (face.i1, face.u1, face.v1), + (face.i2, face.u2, face.v2), + ]; + for (index, u, v) in triangle { + let vertex = &self.vertices[index as usize]; + let point = UniqueVertex { + pos: vertex.position, + normal: vertex.normal, + color: face.color, + uv: [u, v], + }; + let index = if let Some(index) = index_map.get(&point) { + *index + } else { + surface.vertices.push([ + f32::from_ne_bytes(point.pos[0].to_ne_bytes()), + f32::from_ne_bytes(point.pos[1].to_ne_bytes()), + f32::from_ne_bytes(point.pos[2].to_ne_bytes()), + ]); + surface.normals.push([ + point.normal[0] as f32 / 1024.0, + point.normal[1] as f32 / 1024.0, + point.normal[2] as f32 / 1024.0, + ]); + surface.uvs.push([ + f32::from_ne_bytes(point.uv[0].to_ne_bytes()), + f32::from_ne_bytes(point.uv[1].to_ne_bytes()), + ]); + surface.colors.push([ + point.color[0] as f32 / 255.0, + point.color[1] as f32 / 255.0, + point.color[2] as f32 / 255.0, + ]); + + let index = surface.vertices.len() as i32 - 1; + index_map.insert(point, index); + index + }; + surface.indices.push(index); + } + } + + surfaces.into_values().map(|(_, surface)| surface).collect() + } +} diff --git a/rust/powerrender-3d/src/pro/mod.rs b/rust/powerrender-3d/src/pro/mod.rs index 23ea421..70fa306 100644 --- a/rust/powerrender-3d/src/pro/mod.rs +++ b/rust/powerrender-3d/src/pro/mod.rs @@ -1,163 +1,67 @@ use crate::pro::chunk::Chunk; +use crate::pro::internal::materials::PrMaterialChunks; +use crate::pro::internal::segment::PrSegmentChunks; +use crate::pro::internal::{PrChunks, PrObject}; use binrw::__private::magic; -use binrw::{binread, until_eof, BinRead, BinReaderExt, BinResult, Endian, NullString}; +use binrw::meta::{EndianKind, ReadEndian}; +use binrw::BinRead; +use binrw::BinResult; +use binrw::Endian; use std::fmt::Debug; use std::io::{Read, Seek}; -pub mod chunk; +pub(crate) mod chunk; +pub(crate) mod internal; -pub fn read_power_render_object(reader: &mut R) -> BinResult> -where - R: Read + Seek, -{ - magic(reader, 0x0303u16, Endian::Little)?; - let data: Chunk = Chunk::::read(reader)?; - Ok(data.0.chunks) +#[derive(Default, Debug)] +pub struct PowerRenderObject { + pub version: f32, + pub name: String, + pub segments: Vec, + pub materials: Vec, + pub textures: Vec, } -#[binread] -struct PrObject { - #[br(parse_with = until_eof)] - pub chunks: Vec, +#[derive(Default, Debug)] +pub struct PowerRenderSegment { + pub name: String, + pub surfaces: Vec, } -#[binread] -#[derive(Debug)] -pub enum PrChunks { - #[br(magic = 0x0000u16)] - Version(#[br(map = Chunk::inner)] f32), - #[br(magic = 0x0100u16)] - ObjectName(#[br(map = Chunk::inner)] NullString), - #[br(magic = 0x0101u16)] - ObjectFlags(#[br(map = Chunk::inner)] u32), - #[br(magic = 0x1000u16)] - Segments(#[br(map = |it: Chunk::| it.0.chunks)] Vec), - #[br(magic = 0x2010u16)] - TextureList(#[br(map = |it: Chunk::>| it.0.data)] Vec), - #[br(magic = 0x2011u16)] - TextureAlpha(#[br(map = |it: Chunk::>| it.0.data)] Vec), - #[br(magic = 0x2012u16)] - TextureStage(#[br(map = |it: Chunk::>| it.0.data)] Vec), - #[br(magic = 0x2013u16)] - TextureFormat(#[br(map = |it: Chunk::>| it.0.data)] Vec), - #[br(magic = 0x2014u16)] - TextureMulti(#[br(map = |it: Chunk::>| it.0.data)] Vec), - #[br(magic = 0x2100u16)] - MaterialList(#[br(map = |it: Chunk::| it.0.chunks)] Vec), - #[br(magic = 0x3000u16)] - Camera(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x3010u16)] - ObjectBbox(#[br(map = Chunk::inner)] Box), - #[br(magic = 0x3100u16)] - VertexShaderList(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x3101u16)] - VertexShaderList2(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x3102u16)] - VertexShaderList3(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x3200u16)] - PixelShaderList(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x3202u16)] - PixelShaderList3(#[br(map = Chunk::inner)] ()), // TODO -} - -#[binread] -struct PrMaterialList { - #[br(parse_with = until_eof)] - pub chunks: Vec, -} - -#[binread] -#[derive(Debug)] -pub enum PrMaterialChunks { - #[br(magic = 0x2101u16)] - MaterialName(#[br(map = Chunk::inner)] NullString), - #[br(magic = 0x2102u16)] - MaterialMethod(#[br(map = Chunk::inner)] u32), - #[br(magic = 0x2103u16)] - MaterialTexNum(#[br(map = Chunk::inner)] u32), - #[br(magic = 0x2104u16)] - MaterialBaseColor(#[br(map = Chunk::inner)] u8), - #[br(magic = 0x2105u16)] - MaterialShades(#[br(map = Chunk::inner)] u32), - #[br(magic = 0x2106u16)] - MaterialTable(#[br(map = Chunk::inner)] u8), - #[br(magic = 0x2107u16)] - MaterialEnvMap(#[br(map = Chunk::inner)] (u8, u8)), - #[br(magic = 0x2108u16)] - MaterialMipMap(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x2109u16)] - MaterialColor(#[br(map = Chunk::inner)] [f32; 4]), - #[br(magic = 0x2110u16)] - MaterialNumStages(#[br(map = Chunk::inner)] u32), - #[br(magic = 0x2111u16)] - MaterialTexNum2(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x2112u16)] - MaterialEnvMap2(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x2113u16)] - MaterialBump(#[br(map = Chunk::inner)] (f32, [f32; 4])), - #[br(magic = 0x2114u16)] - MaterialSpecular(#[br(map = Chunk::inner)] ([f32; 4], f32)), - #[br(magic = 0x2115u16)] - MaterialTwoSided(#[br(map = Chunk::inner)] u8), - #[br(magic = 0x2116u16)] - MaterialVertexShaderName(#[br(map = Chunk::inner)] NullString), - #[br(magic = 0x2117u16)] - MaterialPixelShaderName(#[br(map = Chunk::inner)] NullString), - #[br(magic = 0x2199u16)] - MaterialEnd(#[br(map = Chunk::inner)] ()), -} - -#[binread] -pub struct PrSegmentList { - pub segments: u32, - #[br(parse_with = until_eof)] - pub chunks: Vec, -} - -#[binread] -#[derive(Debug)] -pub enum PrSegmentChunks { - #[br(magic = 0x1010u16)] - SegmentName(#[br(map = Chunk::inner)] NullString), - #[br(magic = 0x1020u16)] - SegmentFlags(#[br(map = Chunk::inner)] u32), - #[br(magic = 0x1022u16)] - SegmentBbox(#[br(map = Chunk::inner)] Box), - #[br(magic = 0x1030u16)] - Vertices(#[br(map = Chunk::inner)] PrVertices), - #[br(magic = 0x1040u16)] - Faces(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x1050u16)] - SegBuf(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x1051u16)] - SegBuf2(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x1052u16)] - SegBuf3(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x1060u16)] - LodInfo(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x1F00u16)] - KeyframePivot(#[br(map = Chunk::inner)] [f32; 3]), - #[br(magic = 0x1F10u16)] - KeyframeMatrix(#[br(map = Chunk::inner)] [f32; 16]), - #[br(magic = 0x1F20u16)] - KeyframeRotKeys(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x1F30u16)] - KeyframePosKeys(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x1F40u16)] - KeyframeScaleKeys(#[br(map = Chunk::inner)] ()), // TODO - #[br(magic = 0x1F50u16)] - KeyframeLinks(#[br(map = Chunk::inner)] [i32; 3]), - #[br(magic = 0x4000u16)] - TexCoords(#[br(map = Chunk::inner)] ()), // TODO +#[derive(Default, Debug)] +pub struct PowerRenderTexture { + pub name: String, + pub has_alpha: bool, + pub is_stage: bool, + pub is_multi: bool, + pub format: u8, } #[derive(Debug)] -pub struct PrVertices { +pub struct PowerRenderSurface { + pub material_index: usize, + pub back_material_index: usize, pub vertices: Vec<[f32; 3]>, pub normals: Vec<[f32; 3]>, + pub colors: Vec<[f32; 3]>, + pub uvs: Vec<[f32; 2]>, + pub indices: Vec, } -impl BinRead for PrVertices { +#[derive(Default, Debug)] +pub struct PowerRenderMaterial { + pub name: String, + pub texture_index: usize, + pub two_sided: bool, + pub frag_shader: Option, + pub vert_shader: Option, +} + +impl ReadEndian for PowerRenderObject { + const ENDIAN: EndianKind = EndianKind::Endian(Endian::Little); +} + +impl BinRead for PowerRenderObject { type Args<'a> = (); fn read_options( @@ -165,75 +69,98 @@ impl BinRead for PrVertices { endian: Endian, _: Self::Args<'_>, ) -> BinResult { - let count = reader.read_type::(endian)? as usize; - let mut result = PrVertices { - vertices: Vec::with_capacity(count), - normals: Vec::with_capacity(count), - }; - for _ in 0..count { - result.vertices.push(reader.read_type(endian)?); - let normal: [i16; 3] = reader.read_type(endian)?; - result.normals.push([ - normal[0] as f32 / 1024.0, - normal[1] as f32 / 1024.0, - normal[2] as f32 / 1024.0, - ]); + magic(reader, 0x0303u16, endian)?; + let chunks = Chunk::::read(reader)?; + + let mut obj = PowerRenderObject::default(); + let mut texture_alpha = vec![]; + let mut texture_format = vec![]; + let mut texture_stage = vec![]; + let mut texture_multi = vec![]; + + for chunk in chunks.0.chunks { + match chunk { + PrChunks::Version(version) => obj.version = version, + PrChunks::ObjectName(name) => obj.name = name.to_string(), + PrChunks::TextureAlpha(alpha) => texture_alpha = alpha, + PrChunks::TextureFormat(format) => texture_format = format, + PrChunks::TextureMulti(multi) => texture_multi = multi, + PrChunks::TextureStage(stage) => texture_stage = stage, + PrChunks::TextureList(texture_list) => { + let mut alpha = texture_alpha.iter(); + let mut formats = texture_format.iter(); + let mut stages = texture_stage.iter(); + let mut multi = texture_multi.iter(); + + obj.textures = texture_list + .into_iter() + .map(|name| PowerRenderTexture { + name: name.to_string(), + has_alpha: *alpha.next().unwrap() != 0, + is_stage: *stages.next().unwrap() != 0, + is_multi: *multi.next().unwrap() != 0, + format: *formats.next().unwrap(), + }) + .collect(); + } + PrChunks::MaterialList(chunks) => { + for chunk in chunks { + match chunk { + PrMaterialChunks::MaterialEnd(_) => (), + PrMaterialChunks::MaterialName(name) => { + obj.materials.push(PowerRenderMaterial { + name: name.to_string(), + ..Default::default() + }) + } + PrMaterialChunks::MaterialTexNum(id) => { + obj.materials.last_mut().unwrap().texture_index = id as usize; + } + PrMaterialChunks::MaterialTwoSided(val) => { + obj.materials.last_mut().unwrap().two_sided = val != 0 + } + PrMaterialChunks::MaterialPixelShaderName(name) => { + if !name.is_empty() { + obj.materials.last_mut().unwrap().frag_shader = + Some(name.to_string()) + } + } + PrMaterialChunks::MaterialVertexShaderName(name) => { + if !name.is_empty() { + obj.materials.last_mut().unwrap().vert_shader = + Some(name.to_string()) + } + } + x => eprintln!("TODO (Materials): {:?}", x), + } + } + } + PrChunks::Segments(segments) => { + let mut vertices = None; + for chunk in segments { + match chunk { + PrSegmentChunks::SegmentName(name) => { + obj.segments.push(PowerRenderSegment { + name: name.to_string(), + ..Default::default() + }); + } + PrSegmentChunks::Vertices(v) => { + vertices = Some(v); + } + PrSegmentChunks::Faces(faces) => { + let surfaces = vertices.unwrap().into_surfaces(faces); + vertices = None; + obj.segments.last_mut().unwrap().surfaces = surfaces; + } + x => eprintln!("TODO: {:?}", x), + } + } + } + x => eprintln!("TODO: {:?}", x), + } } - Ok(result) + + Ok(obj) } } - -#[binread] -#[derive(Debug)] -pub struct PrFace { - pub material: u16, - pub back_material: u16, - pub i0: i32, - pub i1: i32, - pub i2: i32, - pub u0: i32, - pub u1: i32, - pub u2: i32, - pub v0: i32, - pub v1: i32, - pub v2: i32, - pub c0: i32, - pub c1: i32, - pub c2: i32, - pub normal: [u16; 3], - pub flags: u8, - pub dot_prod: u16, -} - -#[binread] -#[derive(Debug)] -pub struct PrBoundingInfo { - pub min_x: f32, - pub max_x: f32, - pub min_y: f32, - pub max_y: f32, - pub min_z: f32, - pub max_z: f32, - /// Four corners + center vertex - pub box_center: [[f32; 3]; 9], - /// Same, but transformed - pub t_box_center: [[f32; 3]; 9], - pub radius: f32, -} - -#[binread] -pub struct VertexShaderList { - pub name: NullString, - pub flags: u32, - pub size: u32, - pub num_constants: u32, -} - -#[binread] -pub(crate) struct VecWithCount BinRead = Args> + 'static> -{ - #[br(temp)] - count: u16, - #[br(count = count)] - pub data: Vec, -}