no intermedia meshes

This commit is contained in:
Robin Appelman 2022-03-20 22:50:23 +01:00
commit ec62b4e509
2 changed files with 139 additions and 132 deletions

131
src/bsp.rs Normal file
View 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))
}

View file

@ -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()
})
}