viewer wip

This commit is contained in:
Robin Appelman 2022-03-11 20:29:11 +01:00
commit 88bcdb35e7
8 changed files with 329 additions and 5 deletions

View file

@ -9,4 +9,8 @@ arrayvec = "0.7.2"
binrw = "0.8.0" binrw = "0.8.0"
thiserror = "1.0.30" thiserror = "1.0.30"
static_assertions = "1.1.0" static_assertions = "1.1.0"
bitflags = "1.0.4" bitflags = "1.0.4"
[dev-dependencies]
three-d = "0.10.2"
miette = { version = "4.2.1", features = ["fancy"] }

View file

@ -1,14 +1,31 @@
use std::env::args; use std::env::args;
use std::fs; use std::fs;
use std::path::PathBuf;
use vmdl::mdl::Mdl; use vmdl::mdl::Mdl;
use vmdl::vtx::Vtx;
use vmdl::vvd::Vvd;
use vmdl::Model;
fn main() -> Result<(), vmdl::ModelError> { fn main() -> Result<(), vmdl::ModelError> {
let mut args = args(); let mut args = args();
let _ = args.next(); let _ = args.next();
let data = fs::read(args.next().expect("No demo file provided"))?; let path = PathBuf::from(args.next().expect("No demo file provided"));
let mdl = Mdl::read(&data)?;
dbg!(mdl.header); let data = fs::read(&path)?;
let mdl = Mdl::read(&data)?;
let data = fs::read(path.with_extension("dx90.vtx"))?;
let vtx = Vtx::read(&data)?;
let data = fs::read(path.with_extension("vvd"))?;
let vvd = Vvd::read(&data)?;
dbg!(vvd.vertices.last());
let model = Model::from_parts(mdl, vtx, vvd);
for strip in model.vertex_strips() {
for vertex in strip {
println!("{:?}", vertex);
}
println!("")
}
Ok(()) Ok(())
} }

252
examples/view.rs Normal file
View file

@ -0,0 +1,252 @@
use std::env::args_os;
use std::fs;
use std::fs::read;
use std::path::{Path, PathBuf};
use thiserror::Error;
use three_d::*;
use vmdl::mdl::Mdl;
use vmdl::vtx::Vtx;
use vmdl::vvd::Vvd;
use vmdl::Model;
#[derive(Debug, Error)]
enum Error {
#[error(transparent)]
Three(#[from] Box<dyn std::error::Error>),
#[error(transparent)]
Mdl(#[from] vmdl::ModelError),
#[error(transparent)]
IO(#[from] std::io::Error),
#[error("{0}")]
Other(&'static str),
}
fn main() -> Result<(), Error> {
miette::set_panic_hook();
let mut args = args_os();
let _ = args.next();
let path = PathBuf::from(args.next().expect("No demo file provided"));
let model = load(&path).unwrap();
let window = Window::new(WindowSettings {
title: path.display().to_string(),
min_size: (512, 512),
max_size: Some((1280, 720)),
..Default::default()
})
.unwrap();
let context = window.gl().unwrap();
let forward_pipeline = ForwardPipeline::new(&context).unwrap();
let mut camera = Camera::new_perspective(
&context,
window.viewport().unwrap(),
vec3(2.0, 2.0, 5.0),
vec3(0.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
degrees(90.0),
0.01,
300.0,
)
.unwrap();
let mut control = OrbitControl::new(*camera.target(), 1.0, 100.0);
let mut gui = three_d::GUI::new(&context).unwrap();
let mut cpu_mesh = model_to_mesh(&model);
let material = PhysicalMaterial {
albedo: Color {
r: 128,
g: 128,
b: 128,
a: 255,
},
..Default::default()
};
let mut model = three_d::Model::new_with_material(&context, &cpu_mesh, material)?;
model.set_transformation(Mat4::from_angle_x(degrees(90.0)));
let mut lights = Lights {
ambient: Some(AmbientLight {
color: Color::WHITE,
intensity: 0.2,
..Default::default()
}),
directional: vec![
DirectionalLight::new(&context, 1.0, Color::WHITE, &vec3(0.0, -1.0, 0.0))?,
DirectionalLight::new(&context, 1.0, Color::WHITE, &vec3(0.0, 1.0, 0.0))?,
],
..Default::default()
};
// main loop
let mut shadows_enabled = true;
let mut directional_intensity = lights.directional[0].intensity();
let mut depth_max = 30.0;
let mut fov = 60.0;
let mut debug_type = DebugType::NONE;
window.render_loop(move |mut frame_input| {
let mut change = frame_input.first_frame;
let mut panel_width = frame_input.viewport.width;
change |= gui
.update(&mut frame_input, |gui_context| {
use three_d::egui::*;
SidePanel::left("side_panel").show(gui_context, |ui| {
ui.heading("Debug Panel");
ui.label("Light options");
ui.add(
Slider::new(&mut lights.ambient.as_mut().unwrap().intensity, 0.0..=1.0)
.text("Ambient intensity"),
);
ui.add(
Slider::new(&mut directional_intensity, 0.0..=1.0)
.text("Directional intensity"),
);
lights.directional[0].set_intensity(directional_intensity);
lights.directional[1].set_intensity(directional_intensity);
if ui.checkbox(&mut shadows_enabled, "Shadows").clicked() {
if !shadows_enabled {
lights.directional[0].clear_shadow_map();
lights.directional[1].clear_shadow_map();
}
}
ui.label("Debug options");
ui.radio_value(&mut debug_type, DebugType::NONE, "None");
ui.radio_value(&mut debug_type, DebugType::POSITION, "Position");
ui.radio_value(&mut debug_type, DebugType::NORMAL, "Normal");
ui.radio_value(&mut debug_type, DebugType::COLOR, "Color");
ui.radio_value(&mut debug_type, DebugType::DEPTH, "Depth");
ui.radio_value(&mut debug_type, DebugType::ORM, "ORM");
ui.label("View options");
ui.add(Slider::new(&mut depth_max, 1.0..=30.0).text("Depth max"));
ui.add(Slider::new(&mut fov, 45.0..=90.0).text("FOV"));
ui.label("Position");
ui.add(Label::new(format!("\tx: {}", camera.position().x)));
ui.add(Label::new(format!("\ty: {}", camera.position().y)));
ui.add(Label::new(format!("\tz: {}", camera.position().z)));
});
panel_width = gui_context.used_size().x as u32;
})
.unwrap();
let viewport = Viewport {
x: panel_width as i32,
y: 0,
width: frame_input.viewport.width - panel_width,
height: frame_input.viewport.height,
};
change |= camera.set_viewport(viewport).unwrap();
change |= control
.handle_events(&mut camera, &mut frame_input.events)
.unwrap();
// Draw
{
camera
.set_perspective_projection(degrees(fov), camera.z_near(), camera.z_far())
.unwrap();
if shadows_enabled {
lights.directional[0]
.generate_shadow_map(4.0, 1024, 1024, &[&model])
.unwrap();
lights.directional[1]
.generate_shadow_map(4.0, 1024, 1024, &[&model])
.unwrap();
}
// Light pass
Screen::write(&context, ClearState::default(), || {
match debug_type {
DebugType::NORMAL => {
model.render_with_material(
&NormalMaterial::from_physical_material(&model.material),
&camera,
&lights,
)?;
}
DebugType::DEPTH => {
let mut depth_material = DepthMaterial::default();
depth_material.max_distance = Some(depth_max);
model.render_with_material(&depth_material, &camera, &lights)?;
}
DebugType::ORM => {
model.render_with_material(
&ORMMaterial::from_physical_material(&model.material),
&camera,
&lights,
)?;
}
DebugType::POSITION => {
let position_material = PositionMaterial::default();
model.render_with_material(&position_material, &camera, &lights)?;
}
DebugType::UV => {
let uv_material = UVMaterial::default();
model.render_with_material(&uv_material, &camera, &lights)?;
}
DebugType::COLOR => {
model.render_with_material(
&ColorMaterial::from_physical_material(&model.material),
&camera,
&lights,
)?;
}
DebugType::NONE => forward_pipeline.render_pass(&camera, &[&model], &lights)?,
};
gui.render()?;
Ok(())
})
.unwrap();
}
let _ = change;
FrameOutput::default()
})?;
Ok(())
}
fn load(path: &Path) -> Result<Model, vmdl::ModelError> {
let data = fs::read(path)?;
let mdl = Mdl::read(&data)?;
let data = fs::read(path.with_extension("dx90.vtx"))?;
let vtx = Vtx::read(&data)?;
let data = fs::read(path.with_extension("vvd"))?;
let vvd = Vvd::read(&data)?;
Ok(Model::from_parts(mdl, vtx, vvd))
}
// 1 hammer unit is ~1.905cm
const UNIT_SCALE: f32 = 1.0 / (1.905 * 100.0);
fn model_to_mesh(model: &Model) -> CPUMesh {
let positions: Vec<f32> = model
.vertex_strips()
.flat_map(|mut strip| {
let mut a = strip.next().unwrap();
let mut b = strip.next().unwrap();
strip
.flat_map(move |c| {
let tri = [a, b, c];
a = b;
b = c;
tri
})
.flat_map(|vec| vec.iter())
})
.collect();
let mut mesh = CPUMesh {
positions,
..Default::default()
};
mesh.compute_normals();
mesh
}

View file

@ -5,12 +5,44 @@ mod shared;
pub mod vtx; pub mod vtx;
pub mod vvd; pub mod vvd;
use crate::mdl::Mdl;
use crate::vtx::Vtx;
use crate::vvd::Vvd;
use binrw::{BinRead, BinReaderExt}; use binrw::{BinRead, BinReaderExt};
pub use error::*; pub use error::*;
pub use handle::Handle; pub use handle::Handle;
pub use shared::*; pub use shared::*;
use std::any::type_name;
use std::io::Cursor; use std::io::Cursor;
pub struct Model {
mdl: Mdl,
vtx: Vtx,
vvd: Vvd,
}
impl Model {
pub fn from_parts(mdl: Mdl, vtx: Vtx, vvd: Vvd) -> Self {
Model { mdl, vtx, vvd }
}
pub fn vertex_strips(&self) -> impl Iterator<Item = impl Iterator<Item = Vector> + '_> {
self.vtx
.body_parts
.iter()
.flat_map(|part| part.models.iter())
.flat_map(|model| model.lods.iter().next())
.flat_map(|lod| lod.meshes.iter())
.flat_map(|mesh| mesh.strip_groups.iter())
.map(|strip_group| {
strip_group
.indices
.iter()
.map(|index| self.vvd.vertices[(*index) as usize].position)
})
}
}
fn read_indexes<'a, I: Iterator<Item = usize> + 'static, T: BinRead>( fn read_indexes<'a, I: Iterator<Item = usize> + 'static, T: BinRead>(
indexes: I, indexes: I,
data: &'a [u8], data: &'a [u8],
@ -21,7 +53,7 @@ where
indexes indexes
.map(|index| { .map(|index| {
data.get(index..).ok_or_else(|| ModelError::OutOfBounds { data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
data: "Bone", data: type_name::<T>(),
offset: index, offset: index,
}) })
}) })

View file

@ -11,6 +11,12 @@ pub struct Vector {
pub z: f32, pub z: f32,
} }
impl Vector {
pub fn iter(&self) -> impl Iterator<Item = f32> {
[self.x, self.y, self.z].into_iter()
}
}
impl From<Vector> for [f32; 3] { impl From<Vector> for [f32; 3] {
fn from(vector: Vector) -> Self { fn from(vector: Vector) -> Self {
[vector.x, vector.y, vector.z] [vector.x, vector.y, vector.z]

View file

@ -142,6 +142,7 @@ impl Mesh {
pub struct StripGroup { pub struct StripGroup {
// todo vertex indexes // todo vertex indexes
// todo topologies // todo topologies
pub indices: Vec<u16>,
pub vertices: Vec<Vertex>, pub vertices: Vec<Vertex>,
pub strips: Vec<Strip>, pub strips: Vec<Strip>,
pub flags: StripGroupFlags, pub flags: StripGroupFlags,
@ -173,6 +174,17 @@ impl StripGroup {
Strip::read(data, header) Strip::read(data, header)
}) })
.collect::<Result<_>>()?, .collect::<Result<_>>()?,
indices: header
.index_indexes()
.map(|index| {
let data = data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
data: "VertexIndex",
offset: index,
})?;
let mut reader = Cursor::new(data);
Ok(reader.read_le()?)
})
.collect::<Result<_>>()?,
flags: header.flags, flags: header.flags,
}) })
} }

View file

@ -123,7 +123,7 @@ impl StripGroupHeader {
} }
pub fn index_indexes(&self) -> impl Iterator<Item = usize> { pub fn index_indexes(&self) -> impl Iterator<Item = usize> {
index_range(self.index_offset, self.index_count, size_of::<Vertex>()) index_range(self.index_offset, self.index_count, size_of::<u16>())
} }
pub fn strip_indexes(&self) -> impl Iterator<Item = usize> { pub fn strip_indexes(&self) -> impl Iterator<Item = usize> {

View file

@ -2,6 +2,7 @@ use std::fs::read;
use vmdl::mdl::Mdl; use vmdl::mdl::Mdl;
use vmdl::vtx::Vtx; use vmdl::vtx::Vtx;
use vmdl::vvd::Vvd; use vmdl::vvd::Vvd;
use vmdl::Model;
#[test] #[test]
fn parse_mdl() { fn parse_mdl() {