diff --git a/rust/Cargo.lock b/rust/Cargo.lock index b2fb60e..17cadd8 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -492,6 +492,7 @@ dependencies = [ "godot", "itertools", "lightwave-3d", + "powerrender-3d", "springylib", "starforcelib", ] diff --git a/rust/mhgd/Cargo.toml b/rust/mhgd/Cargo.toml index 382fa0a..66adf77 100644 --- a/rust/mhgd/Cargo.toml +++ b/rust/mhgd/Cargo.toml @@ -14,3 +14,4 @@ itertools = "0.10.5" dds-rs = "0.7.0" starforcelib = { path = "../starforcelib" } springylib = { path = "../springylib" } +powerrender-3d = { path = "../powerrender-3d" } diff --git a/rust/mhgd/src/lib.rs b/rust/mhgd/src/lib.rs index 55c7abb..7f9bb1f 100644 --- a/rust/mhgd/src/lib.rs +++ b/rust/mhgd/src/lib.rs @@ -7,6 +7,7 @@ use ::godot::prelude::{ExtensionLibrary, Gd, InitHandle, InitLevel, Share}; pub mod data_installer; pub mod lightwave_object; +pub mod pr3d; pub mod sproing; pub mod starforce; diff --git a/rust/mhgd/src/pr3d/mod.rs b/rust/mhgd/src/pr3d/mod.rs new file mode 100644 index 0000000..c38fe99 --- /dev/null +++ b/rust/mhgd/src/pr3d/mod.rs @@ -0,0 +1,21 @@ +use crate::pr3d::pro::pro_to_gd; +use godot::bind::godot_api; +use godot::builtin::GodotString; +use godot::engine::PackedScene; +use godot::obj::Gd; +use godot::prelude::GodotClass; +use powerrender_3d::pro::PowerRenderObject; + +pub mod pro; + +#[derive(GodotClass)] +#[class(init)] +pub struct ProLoader {} + +#[godot_api] +impl ProLoader { + #[func] + pub fn load(path: GodotString) -> Gd { + pro_to_gd(PowerRenderObject::from_file(path.to_string()).unwrap()) + } +} diff --git a/rust/mhgd/src/pr3d/pro.rs b/rust/mhgd/src/pr3d/pro.rs new file mode 100644 index 0000000..d705b90 --- /dev/null +++ b/rust/mhgd/src/pr3d/pro.rs @@ -0,0 +1,129 @@ +use godot::builtin::{ + Array, Color, Dictionary, PackedByteArray, PackedColorArray, PackedInt32Array, + PackedVector2Array, PackedVector3Array, ToVariant, VariantArray, Vector2, Vector3, +}; +use godot::engine::base_material_3d::{CullMode, TextureParam, Transparency}; +use godot::engine::image::AlphaMode; +use godot::engine::mesh::{ArrayFormat, ArrayType, PrimitiveType}; +use godot::engine::node::InternalMode; +use godot::engine::{ + ArrayMesh, Image, ImageTexture, MeshInstance3D, Node3D, PackedScene, StandardMaterial3D, +}; +use godot::obj::{EngineEnum, Gd, Share}; +use powerrender_3d::pro::PowerRenderObject; +use std::fs::File; +use std::io::Read; + +pub fn pro_to_gd(pro: PowerRenderObject) -> Gd { + let mut root = Node3D::new_alloc(); + root.set_name(pro.name.into()); + + let materials: Vec> = pro + .materials + .into_iter() + .map(|m| { + let mut material = StandardMaterial3D::new(); + material.set_name(m.name.into()); + let pr_tex = &pro.textures[m.texture_index]; + + let mut image_file = + File::open(format!("E:\\Games\\Moorhuhn Kart\\data\\{}", pr_tex.name)).unwrap(); + let mut image = Image::new(); + let mut buffer = vec![]; + image_file.read_to_end(&mut buffer).unwrap(); + image.load_tga_from_buffer(PackedByteArray::from(buffer.as_slice())); + let mut texture = ImageTexture::new(); + texture.set_name(pr_tex.name.clone().into()); + texture.set_image(image); + material.set_texture(TextureParam::TEXTURE_ALBEDO, texture.upcast()); + if pr_tex.has_alpha { + material.set_transparency(Transparency::TRANSPARENCY_ALPHA_SCISSOR); + material.set_alpha_scissor_threshold(0.5); + } + + material.set_cull_mode(if m.two_sided { + CullMode::CULL_DISABLED + } else { + CullMode::CULL_BACK + }); + material + }) + .collect(); + + for segment in pro.segments { + let mut mesh = ArrayMesh::new(); + mesh.set_name(segment.name.clone().into()); + + for surface in segment.surfaces { + let mut arrays = VariantArray::new(); + arrays.resize(ArrayType::ARRAY_MAX.ord() as usize); + arrays.set( + ArrayType::ARRAY_VERTEX.ord() as usize, + PackedVector3Array::from_iter(surface.vertices.into_iter().map(|v| Vector3 { + x: v[0], + y: v[1], + z: v[2], + })) + .to_variant(), + ); + arrays.set( + ArrayType::ARRAY_INDEX.ord() as usize, + PackedInt32Array::from_iter(surface.indices.into_iter()).to_variant(), + ); + arrays.set( + ArrayType::ARRAY_NORMAL.ord() as usize, + PackedVector3Array::from_iter(surface.normals.into_iter().map(|n| Vector3 { + x: n[0], + y: n[1], + z: n[2], + })) + .to_variant(), + ); + arrays.set( + ArrayType::ARRAY_COLOR.ord() as usize, + PackedColorArray::from_iter(surface.colors.into_iter().map(|c| Color { + r: c[0], + g: c[1], + b: c[2], + a: 1.0, + })) + .to_variant(), + ); + arrays.set( + ArrayType::ARRAY_TEX_UV.ord() as usize, + PackedVector2Array::from_iter( + surface + .uvs + .into_iter() + .map(|uv| Vector2 { x: uv[0], y: uv[1] }), + ) + .to_variant(), + ); + + mesh.add_surface_from_arrays( + PrimitiveType::PRIMITIVE_TRIANGLES, + arrays, + Array::new(), + Dictionary::new(), + ArrayFormat::default(), + ); + let surf_idx = mesh.get_surface_count() - 1; + mesh.surface_set_material(surf_idx, materials[surface.material_index].share().upcast()); + } + + let mut instance = MeshInstance3D::new_alloc(); + instance.set_name(segment.name.into()); + instance.set_mesh(mesh.upcast()); + root.add_child( + instance.share().upcast(), + false, + InternalMode::INTERNAL_MODE_DISABLED, + ); + instance.set_owner(root.share().upcast()); + } + + let mut scene = PackedScene::new(); + scene.pack(root.share().upcast()); + root.queue_free(); + scene +} diff --git a/rust/powerrender-3d/Cargo.toml b/rust/powerrender-3d/Cargo.toml index ad1ca4d..dfc4f93 100644 --- a/rust/powerrender-3d/Cargo.toml +++ b/rust/powerrender-3d/Cargo.toml @@ -3,8 +3,6 @@ name = "powerrender-3d" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] binrw = "0.11.1" half = "2.2.1" diff --git a/rust/powerrender-3d/src/lib.rs b/rust/powerrender-3d/src/lib.rs new file mode 100644 index 0000000..68a27f5 --- /dev/null +++ b/rust/powerrender-3d/src/lib.rs @@ -0,0 +1 @@ +pub mod pro; diff --git a/rust/powerrender-3d/src/pro/internal/surface.rs b/rust/powerrender-3d/src/pro/internal/surface.rs index b0d4766..d63c282 100644 --- a/rust/powerrender-3d/src/pro/internal/surface.rs +++ b/rust/powerrender-3d/src/pro/internal/surface.rs @@ -51,9 +51,9 @@ impl PrVertices { *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()), + f32::from_ne_bytes(point.pos[1].to_ne_bytes()), + f32::from_ne_bytes(point.pos[0].to_ne_bytes()), ]); surface.normals.push([ point.normal[0] as f32 / 1024.0, diff --git a/rust/powerrender-3d/src/pro/mod.rs b/rust/powerrender-3d/src/pro/mod.rs index 70fa306..935bc5c 100644 --- a/rust/powerrender-3d/src/pro/mod.rs +++ b/rust/powerrender-3d/src/pro/mod.rs @@ -8,7 +8,9 @@ use binrw::BinRead; use binrw::BinResult; use binrw::Endian; use std::fmt::Debug; +use std::fs::File; use std::io::{Read, Seek}; +use std::path::Path; pub(crate) mod chunk; pub(crate) mod internal; @@ -57,6 +59,13 @@ pub struct PowerRenderMaterial { pub vert_shader: Option, } +impl PowerRenderObject { + pub fn from_file>(path: P) -> BinResult { + let mut file = File::open(path).map_err(binrw::Error::Io)?; + Self::read(&mut file) + } +} + impl ReadEndian for PowerRenderObject { const ENDIAN: EndianKind = EndianKind::Endian(Endian::Little); }