mirror of
https://codeberg.org/icewind/vbspview.git
synced 2026-06-03 18:24:09 +02:00
texture materials wip
This commit is contained in:
parent
2ff1eca534
commit
43081fedd7
4 changed files with 113 additions and 47 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
|
@ -1159,6 +1159,15 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.10"
|
version = "1.0.10"
|
||||||
|
|
@ -2423,7 +2432,7 @@ dependencies = [
|
||||||
"enumflags2",
|
"enumflags2",
|
||||||
"err-derive",
|
"err-derive",
|
||||||
"fnv",
|
"fnv",
|
||||||
"itertools",
|
"itertools 0.10.5",
|
||||||
"main_error",
|
"main_error",
|
||||||
"num-traits 0.2.17",
|
"num-traits 0.2.17",
|
||||||
"num_enum 0.5.11",
|
"num_enum 0.5.11",
|
||||||
|
|
@ -2694,11 +2703,13 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
name = "vbsp"
|
name = "vbsp"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ahash 0.8.6",
|
||||||
"arrayvec 0.7.4",
|
"arrayvec 0.7.4",
|
||||||
"binrw",
|
"binrw",
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.4.1",
|
||||||
"bv",
|
"bv",
|
||||||
"cgmath",
|
"cgmath",
|
||||||
|
"itertools 0.12.0",
|
||||||
"lzma-rs",
|
"lzma-rs",
|
||||||
"num_enum 0.7.1",
|
"num_enum 0.7.1",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
|
|
@ -2725,7 +2736,7 @@ dependencies = [
|
||||||
"cgmath",
|
"cgmath",
|
||||||
"clap",
|
"clap",
|
||||||
"delaunator",
|
"delaunator",
|
||||||
"itertools",
|
"itertools 0.10.5",
|
||||||
"miette",
|
"miette",
|
||||||
"splines",
|
"splines",
|
||||||
"steamid-ng",
|
"steamid-ng",
|
||||||
|
|
@ -2762,7 +2773,7 @@ dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cgmath",
|
"cgmath",
|
||||||
"itertools",
|
"itertools 0.10.5",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
|
||||||
116
src/bsp.rs
116
src/bsp.rs
|
|
@ -1,17 +1,16 @@
|
||||||
use crate::{Error, Loader};
|
use crate::{Error, Loader};
|
||||||
use cgmath::{vec4, Matrix, SquareMatrix};
|
use cgmath::{vec4, Matrix, SquareMatrix};
|
||||||
use itertools::Either;
|
use std::collections::HashMap;
|
||||||
use three_d::{CpuMesh, Indices, Mat4, Positions, Vec3};
|
use three_d::{Color, CpuMaterial, CpuMesh, CpuModel, Indices, Mat4, Positions, Vec2, Vec3};
|
||||||
use vbsp::{Bsp, Handle, StaticPropLump};
|
use vbsp::{Bsp, Face, Handle, StaticPropLump};
|
||||||
use vmdl::mdl::Mdl;
|
use vmdl::mdl::Mdl;
|
||||||
use vmdl::vtx::Vtx;
|
use vmdl::vtx::Vtx;
|
||||||
use vmdl::vvd::Vvd;
|
use vmdl::vvd::Vvd;
|
||||||
|
|
||||||
pub fn load_map(data: &[u8], loader: &mut Loader) -> Result<Vec<CpuMesh>, Error> {
|
pub fn load_map(data: &[u8], loader: &mut Loader) -> Result<Vec<CpuModel>, Error> {
|
||||||
let (cpu_mesh, bsp) = load_world(data)?;
|
let (cpu_model, bsp) = load_world(data, loader)?;
|
||||||
loader.set_pack(bsp.pack.clone());
|
|
||||||
let merged_props = load_props(loader, bsp.static_props())?;
|
let merged_props = load_props(loader, bsp.static_props())?;
|
||||||
Ok(vec![cpu_mesh, merged_props])
|
Ok(vec![cpu_model, merged_props])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_transform<C: Into<Vec3>>(coord: C, transform: Mat4) -> Vec3 {
|
fn apply_transform<C: Into<Vec3>>(coord: C, transform: Mat4) -> Vec3 {
|
||||||
|
|
@ -31,39 +30,105 @@ pub fn map_coords<C: Into<Vec3>>(vec: C) -> Vec3 {
|
||||||
// 1 hammer unit is ~1.905cm
|
// 1 hammer unit is ~1.905cm
|
||||||
pub const UNIT_SCALE: f32 = 1.0 / (1.905 * 100.0);
|
pub const UNIT_SCALE: f32 = 1.0 / (1.905 * 100.0);
|
||||||
|
|
||||||
fn model_to_mesh(model: Handle<vbsp::data::Model>) -> CpuMesh {
|
fn face_to_mesh(face: &Handle<Face>) -> CpuMesh {
|
||||||
let positions: Vec<Vec3> = model
|
let texture = face.texture();
|
||||||
.faces()
|
let positions = face.vertex_positions().map(map_coords).collect();
|
||||||
.filter(|face| face.is_visible())
|
let uvs = face
|
||||||
.flat_map(|face| {
|
.vertex_positions()
|
||||||
face.displacement()
|
.map(|pos| Vec2 {
|
||||||
.map(|displacement| displacement.triangulated_displaced_vertices())
|
x: texture.u(pos),
|
||||||
.map(Either::Left)
|
y: texture.v(pos),
|
||||||
.unwrap_or_else(|| Either::Right(face.triangulate().flatten()))
|
|
||||||
})
|
})
|
||||||
.map(map_coords)
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut mesh = CpuMesh {
|
let mut mesh = CpuMesh {
|
||||||
positions: Positions::F32(positions),
|
positions: Positions::F32(positions),
|
||||||
|
uvs: Some(uvs),
|
||||||
|
material_name: Some(texture.name().into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
mesh.compute_normals();
|
mesh.compute_normals();
|
||||||
|
|
||||||
mesh
|
mesh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn model_to_model(model: Handle<vbsp::data::Model>, loader: &Loader) -> CpuModel {
|
||||||
|
let mut faces_by_texture: HashMap<&str, Vec<_>> = HashMap::with_capacity(64);
|
||||||
|
for face in model.faces().filter(|face| face.is_visible()) {
|
||||||
|
faces_by_texture
|
||||||
|
.entry(face.texture().name())
|
||||||
|
.or_default()
|
||||||
|
.push(face)
|
||||||
|
}
|
||||||
|
|
||||||
|
let geometries = faces_by_texture
|
||||||
|
.values()
|
||||||
|
.map(|faces| {
|
||||||
|
let mut faces = faces.iter();
|
||||||
|
let first = faces.next().unwrap();
|
||||||
|
let mut mesh = face_to_mesh(first);
|
||||||
|
for face in faces {
|
||||||
|
let face_mesh = face_to_mesh(face);
|
||||||
|
if let Positions::F32(positions) = &mut mesh.positions {
|
||||||
|
positions.extend_from_slice(&face_mesh.positions.into_f32());
|
||||||
|
}
|
||||||
|
if let Some(uvs) = &mut mesh.uvs {
|
||||||
|
uvs.extend_from_slice(&face_mesh.uvs.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mesh.compute_normals();
|
||||||
|
mesh
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let materials = faces_by_texture
|
||||||
|
.values()
|
||||||
|
.map(|face| {
|
||||||
|
let texture = face.first().unwrap().texture();
|
||||||
|
let color = texture.texture().debug_color();
|
||||||
|
CpuMaterial {
|
||||||
|
albedo: Color {
|
||||||
|
r: color[0],
|
||||||
|
g: color[1],
|
||||||
|
b: color[2],
|
||||||
|
a: 255,
|
||||||
|
},
|
||||||
|
name: texture.name().into(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
CpuModel {
|
||||||
|
geometries,
|
||||||
|
materials,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn load_props<'a, I: Iterator<Item = Handle<'a, StaticPropLump>>>(
|
fn load_props<'a, I: Iterator<Item = Handle<'a, StaticPropLump>>>(
|
||||||
loader: &Loader,
|
loader: &Loader,
|
||||||
props: I,
|
props: I,
|
||||||
) -> Result<CpuMesh, Error> {
|
) -> Result<CpuModel, Error> {
|
||||||
merge_models(props.map(|prop| {
|
let material = CpuMaterial {
|
||||||
|
albedo: Color {
|
||||||
|
r: 128,
|
||||||
|
g: 128,
|
||||||
|
b: 128,
|
||||||
|
a: 255,
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mesh = merge_models(props.map(|prop| {
|
||||||
let model = load_prop(loader, prop.model())?;
|
let model = load_prop(loader, prop.model())?;
|
||||||
let transform =
|
let transform =
|
||||||
Mat4::from_translation(map_coords(prop.origin)) * Mat4::from(prop.rotation());
|
Mat4::from_translation(map_coords(prop.origin)) * Mat4::from(prop.rotation());
|
||||||
Ok(ModelData { model, transform })
|
Ok(ModelData { model, transform })
|
||||||
}))
|
}))?;
|
||||||
|
|
||||||
|
Ok(CpuModel {
|
||||||
|
geometries: vec![mesh],
|
||||||
|
materials: vec![material],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(loader))]
|
#[tracing::instrument(skip(loader))]
|
||||||
|
|
@ -123,9 +188,12 @@ fn merge_models<I: Iterator<Item = Result<ModelData, Error>>>(props: I) -> Resul
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_world(data: &[u8]) -> Result<(CpuMesh, Bsp), Error> {
|
fn load_world(data: &[u8], loader: &mut Loader) -> Result<(CpuModel, Bsp), Error> {
|
||||||
let bsp = Bsp::read(data)?;
|
let bsp = Bsp::read(data)?;
|
||||||
|
|
||||||
|
loader.set_pack(bsp.pack.clone());
|
||||||
|
|
||||||
let world_model = bsp.models().next().ok_or(Error::Other("No world model"))?;
|
let world_model = bsp.models().next().ok_or(Error::Other("No world model"))?;
|
||||||
|
|
||||||
Ok((model_to_mesh(world_model), bsp))
|
Ok((model_to_model(world_model, loader), bsp))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
src/main.rs
25
src/main.rs
|
|
@ -88,39 +88,26 @@ fn main() -> Result<(), Error> {
|
||||||
let mut loader = Loader::new()?;
|
let mut loader = Loader::new()?;
|
||||||
let map = loader.load(&format!("maps/{}.bsp", demo.map))?;
|
let map = loader.load(&format!("maps/{}.bsp", demo.map))?;
|
||||||
|
|
||||||
let meshes = load_map(&map, &mut loader)?;
|
let models = load_map(&map, &mut loader)?;
|
||||||
play(window, DemoCamera::new(demo), meshes)
|
play(window, DemoCamera::new(demo), models)
|
||||||
} else {
|
} else {
|
||||||
let mut loader = Loader::new()?;
|
let mut loader = Loader::new()?;
|
||||||
let map = fs::read(args.path)?;
|
let map = fs::read(args.path)?;
|
||||||
|
|
||||||
let meshes = load_map(&map, &mut loader)?;
|
let models = load_map(&map, &mut loader)?;
|
||||||
play(window, FirstPerson::new(0.1), meshes)
|
play(window, FirstPerson::new(0.1), models)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn play<C: Control + 'static>(
|
fn play<C: Control + 'static>(
|
||||||
window: Window,
|
window: Window,
|
||||||
control: C,
|
control: C,
|
||||||
meshes: Vec<CpuMesh>,
|
models: Vec<CpuModel>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut renderer = Renderer::new(&window, control);
|
let mut renderer = Renderer::new(&window, control);
|
||||||
let material = CpuMaterial {
|
|
||||||
albedo: Color {
|
|
||||||
r: 128,
|
|
||||||
g: 128,
|
|
||||||
b: 128,
|
|
||||||
a: 255,
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
renderer.models = meshes
|
renderer.models = models
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mesh| CpuModel {
|
|
||||||
geometries: vec![mesh],
|
|
||||||
materials: vec![material.clone()],
|
|
||||||
})
|
|
||||||
.map(|model| Model::new(&renderer.context, &model))
|
.map(|model| Model::new(&renderer.context, &model))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ impl DebugUI {
|
||||||
ambient_intensity: 0.2,
|
ambient_intensity: 0.2,
|
||||||
depth_max: 30.0,
|
depth_max: 30.0,
|
||||||
fov: 60.0,
|
fov: 60.0,
|
||||||
debug_type: DebugType::Normal,
|
debug_type: DebugType::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue