mirror of
https://codeberg.org/icewind/vbspview.git
synced 2026-06-03 18:24:09 +02:00
no intermedia meshes
This commit is contained in:
parent
84ba312d30
commit
ec62b4e509
2 changed files with 139 additions and 132 deletions
131
src/bsp.rs
Normal file
131
src/bsp.rs
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
use crate::{Error, Loader};
|
||||
use cgmath::{vec4, Matrix, SquareMatrix};
|
||||
use itertools::Either;
|
||||
use three_d::{CPUMesh, Indices, Mat4};
|
||||
use vbsp::{Bsp, Handle, StaticPropLump};
|
||||
use vmdl::mdl::Mdl;
|
||||
use vmdl::vtx::Vtx;
|
||||
use vmdl::vvd::Vvd;
|
||||
|
||||
pub fn load_map(data: &[u8]) -> Result<Vec<CPUMesh>, Error> {
|
||||
let (cpu_mesh, bsp) = load_world(data)?;
|
||||
let loader = Loader::new(bsp.pack.clone())?;
|
||||
let merged_props = load_props(&loader, bsp.static_props())?;
|
||||
Ok(vec![cpu_mesh, merged_props])
|
||||
}
|
||||
|
||||
fn apply_transform(coord: [f32; 3], transform: Mat4) -> [f32; 3] {
|
||||
let coord = (transform * vec4(coord[0], coord[1], coord[2], 1.0)).truncate();
|
||||
[coord.x, coord.y, coord.z]
|
||||
}
|
||||
|
||||
fn map_coords<C: Into<[f32; 3]>>(vec: C) -> [f32; 3] {
|
||||
let vec = vec.into();
|
||||
[
|
||||
vec[1] * UNIT_SCALE,
|
||||
vec[2] * UNIT_SCALE,
|
||||
vec[0] * UNIT_SCALE,
|
||||
]
|
||||
}
|
||||
|
||||
// 1 hammer unit is ~1.905cm
|
||||
const UNIT_SCALE: f32 = 1.0 / (1.905 * 100.0);
|
||||
|
||||
fn model_to_mesh(model: Handle<vbsp::data::Model>) -> CPUMesh {
|
||||
let positions: Vec<f32> = model
|
||||
.faces()
|
||||
.filter(|face| face.is_visible())
|
||||
.flat_map(|face| {
|
||||
face.displacement()
|
||||
.map(|displacement| displacement.triangulated_displaced_vertices())
|
||||
.map(Either::Left)
|
||||
.unwrap_or_else(|| Either::Right(face.triangulate().flatten()))
|
||||
})
|
||||
.flat_map(map_coords)
|
||||
.collect();
|
||||
|
||||
let mut mesh = CPUMesh {
|
||||
positions,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
mesh.compute_normals();
|
||||
|
||||
mesh
|
||||
}
|
||||
|
||||
fn load_props<'a, I: Iterator<Item = Handle<'a, StaticPropLump>>>(
|
||||
loader: &Loader,
|
||||
props: I,
|
||||
) -> Result<CPUMesh, Error> {
|
||||
merge_models(props.map(|prop| {
|
||||
let model = load_prop(loader, prop.model())?;
|
||||
let transform =
|
||||
Mat4::from_translation(map_coords(prop.origin).into()) * Mat4::from(prop.rotation());
|
||||
Ok(ModelData { model, transform })
|
||||
}))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(loader))]
|
||||
fn load_prop(loader: &Loader, name: &str) -> Result<vmdl::Model, Error> {
|
||||
let mdl = Mdl::read(&loader.load(name)?)?;
|
||||
let vtx = Vtx::read(&loader.load(&name.replace(".mdl", ".dx90.vtx"))?)?;
|
||||
let vvd = Vvd::read(&loader.load(&name.replace(".mdl", ".vvd"))?)?;
|
||||
|
||||
Ok(vmdl::Model::from_parts(mdl, vtx, vvd))
|
||||
}
|
||||
|
||||
struct ModelData {
|
||||
model: vmdl::Model,
|
||||
transform: Mat4,
|
||||
}
|
||||
|
||||
fn merge_models<I: Iterator<Item = Result<ModelData, Error>>>(props: I) -> Result<CPUMesh, Error> {
|
||||
let mut positions = Vec::new();
|
||||
let mut normals = Vec::new();
|
||||
let mut indices = Vec::new();
|
||||
|
||||
for prop in props {
|
||||
let prop = prop?;
|
||||
let transform = prop.transform;
|
||||
let normal_transform = transform.invert().unwrap().transpose() * -1.0;
|
||||
let model = prop.model;
|
||||
|
||||
let offset = positions.len() as u32 / 3;
|
||||
|
||||
positions.extend(
|
||||
model
|
||||
.vertices()
|
||||
.iter()
|
||||
.map(|v| map_coords(v.position))
|
||||
.flat_map(|v| apply_transform(v, transform)),
|
||||
);
|
||||
normals.extend(
|
||||
model
|
||||
.vertices()
|
||||
.iter()
|
||||
.map(|v| map_coords(v.normal))
|
||||
.flat_map(|v| apply_transform(v, normal_transform)),
|
||||
);
|
||||
indices.extend(
|
||||
model
|
||||
.vertex_strip_indices()
|
||||
.flat_map(|strip| strip.map(|index| index as u32))
|
||||
.map(|index| index + offset),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CPUMesh {
|
||||
positions,
|
||||
normals: Some(normals),
|
||||
indices: Some(Indices::U32(indices)),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
fn load_world(data: &[u8]) -> Result<(CPUMesh, Bsp), Error> {
|
||||
let bsp = Bsp::read(data)?;
|
||||
let world_model = bsp.models().next().ok_or(Error::Other("No world model"))?;
|
||||
|
||||
Ok((model_to_mesh(world_model), bsp))
|
||||
}
|
||||
140
src/main.rs
140
src/main.rs
|
|
@ -1,23 +1,19 @@
|
|||
mod bsp;
|
||||
mod camera;
|
||||
mod loader;
|
||||
mod renderer;
|
||||
mod ui;
|
||||
|
||||
use crate::bsp::load_map;
|
||||
use crate::renderer::Renderer;
|
||||
use crate::ui::DebugUI;
|
||||
use camera::FirstPerson;
|
||||
use itertools::Either;
|
||||
use loader::Loader;
|
||||
use std::env::args;
|
||||
use std::path::Path;
|
||||
use thiserror::Error;
|
||||
use three_d::*;
|
||||
use tracing_subscriber::{prelude::*, EnvFilter};
|
||||
use tracing_tree::HierarchicalLayer;
|
||||
use vbsp::{Bsp, Handle, StaticPropLump};
|
||||
use vmdl::mdl::Mdl;
|
||||
use vmdl::vtx::Vtx;
|
||||
use vmdl::vvd::Vvd;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
|
|
@ -75,7 +71,8 @@ fn main() -> Result<(), Error> {
|
|||
|
||||
let mut renderer = Renderer::new(&window)?;
|
||||
|
||||
let (cpu_mesh, bsp) = load_world(file.as_ref())?;
|
||||
let map = std::fs::read(&file)?;
|
||||
let meshes = load_map(&map)?;
|
||||
let material = PhysicalMaterial {
|
||||
albedo: Color {
|
||||
r: 128,
|
||||
|
|
@ -86,133 +83,12 @@ fn main() -> Result<(), Error> {
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
let loader = Loader::new(bsp.pack.clone())?;
|
||||
let model = Model::new_with_material(&renderer.context, &cpu_mesh, material.clone())?;
|
||||
let merged_props = load_props(&loader, bsp.static_props())?;
|
||||
let props_model = Model::new_with_material(&renderer.context, &merged_props, material)?;
|
||||
renderer.models = vec![model, props_model];
|
||||
renderer.models = meshes
|
||||
.into_iter()
|
||||
.map(|mesh| Model::new_with_material(&renderer.context, &mesh, material.clone()))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
window.render_loop(move |frame_input| renderer.render(frame_input).unwrap())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn map_coords<C: Into<[f32; 3]>>(vec: C) -> [f32; 3] {
|
||||
let vec = vec.into();
|
||||
[
|
||||
vec[1] * UNIT_SCALE,
|
||||
vec[2] * UNIT_SCALE,
|
||||
vec[0] * UNIT_SCALE,
|
||||
]
|
||||
}
|
||||
|
||||
// 1 hammer unit is ~1.905cm
|
||||
const UNIT_SCALE: f32 = 1.0 / (1.905 * 100.0);
|
||||
|
||||
fn model_to_mesh(model: Handle<vbsp::data::Model>) -> CPUMesh {
|
||||
let positions: Vec<f32> = model
|
||||
.faces()
|
||||
.filter(|face| face.is_visible())
|
||||
.flat_map(|face| {
|
||||
face.displacement()
|
||||
.map(|displacement| displacement.triangulated_displaced_vertices())
|
||||
.map(Either::Left)
|
||||
.unwrap_or_else(|| Either::Right(face.triangulate().flatten()))
|
||||
})
|
||||
.flat_map(map_coords)
|
||||
.collect();
|
||||
|
||||
let mut mesh = CPUMesh {
|
||||
positions,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
mesh.compute_normals();
|
||||
|
||||
mesh
|
||||
}
|
||||
|
||||
fn load_props<'a, I: Iterator<Item = Handle<'a, StaticPropLump>>>(
|
||||
loader: &Loader,
|
||||
props: I,
|
||||
) -> Result<CPUMesh, Error> {
|
||||
merge_meshes(props.map(|prop| {
|
||||
let mut mesh = load_prop_mesh(loader, prop.model())?;
|
||||
|
||||
let transform =
|
||||
Mat4::from_translation(map_coords(prop.origin).into()) * Mat4::from(prop.rotation());
|
||||
mesh.transform(&transform);
|
||||
Ok(mesh)
|
||||
}))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(loader))]
|
||||
fn load_prop_mesh(loader: &Loader, name: &str) -> Result<CPUMesh, Error> {
|
||||
let mdl = Mdl::read(&loader.load(name)?)?;
|
||||
let vtx = Vtx::read(&loader.load(&name.replace(".mdl", ".dx90.vtx"))?)?;
|
||||
let vvd = Vvd::read(&loader.load(&name.replace(".mdl", ".vvd"))?)?;
|
||||
|
||||
let model = vmdl::Model::from_parts(mdl, vtx, vvd);
|
||||
Ok(prop_to_mesh(&model))
|
||||
}
|
||||
|
||||
fn prop_to_mesh(model: &vmdl::Model) -> CPUMesh {
|
||||
let positions: Vec<f32> = model
|
||||
.vertices()
|
||||
.iter()
|
||||
.flat_map(|v| map_coords(v.position))
|
||||
.collect();
|
||||
let normals: Vec<f32> = model
|
||||
.vertices()
|
||||
.iter()
|
||||
.flat_map(|vertex| map_coords(vertex.normal))
|
||||
.collect();
|
||||
let indices = Indices::U32(
|
||||
model
|
||||
.vertex_strip_indices()
|
||||
.flat_map(|strip| strip.map(|index| index as u32))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
CPUMesh {
|
||||
positions,
|
||||
normals: Some(normals),
|
||||
indices: Some(indices),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn load_world(path: &Path) -> Result<(CPUMesh, Bsp), Error> {
|
||||
let map = std::fs::read(path)?;
|
||||
let bsp = Bsp::read(map.as_ref())?;
|
||||
let world_model = bsp.models().next().ok_or(Error::Other("No world model"))?;
|
||||
|
||||
Ok((model_to_mesh(world_model), bsp))
|
||||
}
|
||||
|
||||
fn merge_meshes<I: IntoIterator<Item = Result<CPUMesh, Error>>>(
|
||||
models: I,
|
||||
) -> Result<CPUMesh, Error> {
|
||||
let mut positions = Vec::new();
|
||||
let mut normals = Vec::new();
|
||||
let mut indices = Vec::new();
|
||||
|
||||
for mesh in models {
|
||||
let mesh = mesh?;
|
||||
let offset = positions.len() as u32 / 3;
|
||||
positions.extend_from_slice(&mesh.positions);
|
||||
normals.extend_from_slice(&mesh.normals.unwrap());
|
||||
if let Indices::U32(mesh_indices) = mesh.indices.unwrap() {
|
||||
indices.extend(mesh_indices.into_iter().map(|index| index + offset));
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CPUMesh {
|
||||
positions,
|
||||
normals: Some(normals),
|
||||
indices: Some(Indices::U32(indices)),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue