update threed

This commit is contained in:
Robin Appelman 2023-12-13 16:02:43 +01:00
commit 7dec8aa2c6
9 changed files with 4997 additions and 550 deletions

View file

@ -56,5 +56,5 @@ jobs:
- run: nix build .#${{ matrix.target }}
- uses: actions/upload-artifact@v3
with:
name: vbspview-${{ matrix.asset_suffix }}
name: vbspview-${{ matrix.target }}
path: result/bin/vbspview${{ matrix.artifact_suffix }}

695
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -6,9 +6,10 @@ authors = ["Robin Appelman <robin@icewind.nl>"]
license = "MIT"
[dependencies]
three-d = { version = "0.14.0", features = ["egui-gui"] }
vbsp = { version = "0.2.0", git = "https://github.com/icewind1991/vbsp" }
#vbsp = { version = "0.2.0", path = "../bsp" }
three-d = { version = "0.16.3", features = ["egui-gui"] }
three-d-asset = { version = "0.6" }
#vbsp = { version = "0.2.0", git = "https://github.com/icewind1991/vbsp" }
vbsp = { version = "0.2.0", path = "../bsp" }
miette = { version = "5.5.0", features = ["fancy"] }
thiserror = "1.0.37"
delaunator = "1.0.1"

View file

@ -35,22 +35,24 @@
hostTarget = pkgs.hostPlatform.config;
targets = ["x86_64-unknown-linux-musl" "x86_64-pc-windows-gnu" hostTarget];
artifactSuffixForTarget = cross-naersk'.execSufficForTarget;
assetSuffixForTarget = target: "${builtins.replaceStrings ["-unknown" "-gnu" "-musl" "eabihf" "-pc"] ["" "" "" "" ""] target}${cross-naersk'.execSufficForTarget target}";
hostNaersk = naerskForTarget hostTarget;
cross-naersk' = pkgs.callPackage cross-naersk {inherit naersk;};
src = lib.sources.sourceByRegex (lib.cleanSource ./.) ["Cargo.*" "(src)(/.*)?"];
nearskOpt = {
pname = "vbspview";
root = src;
nativeBuildInputs = buildDependencies;
nativeBuildInputs = (buildDependencies pkgs) ++ (runtimeDependencies pkgs);
};
buildDependencies = with pkgs; [
freetype
pkg-config
cmake
fontconfig
crossOpts = {
crossArgs = {
"x86_64-unknown-linux-musl" = {
# targetNativeBuildInputs = buildDependencies;
# buildInputs = runtimeDependencies pkgs.pkgsCross.musl64;
};
};
};
runtimeDependencies = pkgsForPlatform: with pkgsForPlatform; [
xorg.libX11
xorg.libXcursor
xorg.libXrandr
@ -59,12 +61,17 @@
egl-wayland
libGL
];
buildDependencies = pkgsForPlatform: with pkgsForPlatform; [
fontconfig
freetype
pkg-config
cmake
];
buildMatrix = targets: {
include = builtins.map (target: {
inherit target;
artifact_suffix = artifactSuffixForTarget target;
asset_suffix = assetSuffixForTarget target;
artifact_suffix = cross-naersk'.execSufficForTarget target;
}) targets;
};
in rec {
@ -72,9 +79,11 @@
vbspview = packages.${hostTarget};
check = hostNaersk.buildPackage (nearskOpt // {
mode = "check";
buildInputs = buildDependencies pkgs;
});
clippy = hostNaersk.buildPackage (nearskOpt // {
mode = "clippy";
buildInputs = buildDependencies pkgs;
});
default = vbspview;
};
@ -84,7 +93,7 @@
inherit targets;
devShells.default = pkgs.mkShell {
nativeBuildInputs = with pkgs; [
nativeBuildInputs = (with pkgs; [
pkgs.rust-bin.stable.latest.default
bacon
cargo-edit
@ -92,7 +101,7 @@
clippy
cargo-audit
cargo-msrv
] ++ buildDependencies;
]) ++ (buildDependencies pkgs) ++ (runtimeDependencies pkgs);
LD_LIBRARY_PATH = with pkgs; "/run/opengl-driver/lib/:${lib.makeLibraryPath ([libGL libGLU])}";
};

4605
mats.txt Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,12 @@
use crate::material::load_material_fallback;
use crate::prop::load_props;
use crate::{Error, Loader};
use cgmath::vec4;
use std::collections::HashMap;
use three_d::{CpuMesh, CpuModel, Mat4, Positions, Vec2, Vec3};
use vbsp::{Bsp, Face, Handle};
use cgmath::Matrix4;
use itertools::Itertools;
use std::collections::{HashMap, HashSet};
use three_d::{CpuModel, Positions, Vec3};
use three_d_asset::{Geometry, Primitive, TriMesh};
use vbsp::{Bsp, Handle};
pub fn load_map(data: &[u8], loader: &mut Loader) -> Result<Vec<CpuModel>, Error> {
let (world, bsp) = load_world(data, loader)?;
@ -12,11 +14,6 @@ pub fn load_map(data: &[u8], loader: &mut Loader) -> Result<Vec<CpuModel>, Error
Ok(vec![world, props])
}
pub fn apply_transform<C: Into<Vec3>>(coord: C, transform: Mat4) -> Vec3 {
let coord = coord.into();
(transform * vec4(coord.x, coord.y, coord.z, 1.0)).truncate()
}
pub fn map_coords<C: Into<Vec3>>(vec: C) -> Vec3 {
let vec = vec.into();
Vec3 {
@ -29,66 +26,66 @@ pub fn map_coords<C: Into<Vec3>>(vec: C) -> Vec3 {
// 1 hammer unit is ~1.905cm
pub const UNIT_SCALE: f32 = 1.0 / (1.905 * 100.0);
fn face_to_mesh(face: &Handle<Face>) -> CpuMesh {
let texture = face.texture();
let positions = face.vertex_positions().map(map_coords).collect();
let uvs = face
.vertex_positions()
.map(|pos| Vec2 {
x: texture.u(pos),
y: texture.v(pos),
})
.collect();
let mut mesh = CpuMesh {
positions: Positions::F32(positions),
uvs: Some(uvs),
material_name: Some(texture.name().into()),
..Default::default()
};
mesh.compute_normals();
mesh.compute_tangents();
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 textures: HashSet<&str> = model.textures().map(|texture| texture.name()).collect();
let textures: Vec<&str> = textures.into_iter().collect();
let geometries = faces_by_texture
.values()
let faces_by_texture: HashMap<&str, _> = model
.faces()
.filter(|face| face.is_visible())
.map(|face| (face.texture().name(), face))
.into_group_map();
let geometries: Vec<_> = faces_by_texture
.into_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());
}
}
let positions: Vec<_> = faces
.iter()
.flat_map(|face| face.vertex_positions())
.map(map_coords)
.collect();
let uvs: Vec<_> = faces
.iter()
.flat_map(|face| {
let texture = face.texture();
face.vertex_positions()
.map(move |position| texture.uv(position))
})
.map(|uv| uv.into())
.collect();
let mut mesh = TriMesh {
positions: Positions::F32(positions),
uvs: Some(uvs),
..Default::default()
};
mesh.compute_normals();
mesh
mesh.compute_tangents();
let texture = faces.first().unwrap().texture().name();
let material_index = textures
.iter()
.enumerate()
.find_map(|(i, tex)| (*tex == texture).then_some(i));
Primitive {
name: "".to_string(),
transformation: Matrix4::from_scale(1.0),
animations: vec![],
geometry: Geometry::Triangles(mesh),
material_index,
}
})
.collect();
let materials = faces_by_texture
.values()
.map(|face| {
let texture = face.first().unwrap().texture();
load_material_fallback(texture.name(), &["".into()], loader)
})
let materials: Vec<_> = textures
.iter()
.map(|texture| load_material_fallback(texture, &["".into()], loader))
.collect();
CpuModel {
name: "".to_string(),
geometries,
materials,
}

View file

@ -2,7 +2,8 @@ use crate::loader::Loader;
use crate::Error;
use std::str::FromStr;
use steamy_vdf::{Entry, Table};
use three_d::{Color, CpuMaterial, CpuTexture, TextureData};
use three_d::{CpuMaterial, CpuTexture, TextureData};
use three_d_asset::Srgba;
use tracing::error;
use vtf::vtf::VTF;
@ -16,7 +17,7 @@ pub fn load_material_fallback(name: &str, search_dirs: &[String], loader: &Loade
"failed to load material, falling back"
);
CpuMaterial {
albedo: Color {
albedo: Srgba {
r: 255,
g: 0,
b: 255,
@ -61,7 +62,7 @@ pub fn load_material(
if material_type == "water" {
return Ok(CpuMaterial {
albedo: Color {
albedo: Srgba {
r: 82,
g: 180,
b: 217,
@ -109,7 +110,7 @@ pub fn load_material(
Ok(CpuMaterial {
name: name.into(),
albedo: Color::WHITE,
albedo: Srgba::WHITE,
albedo_texture: Some(texture),
alpha_cutout: alpha_test.then_some(alpha_cutout),
normal_texture: bump_map,
@ -159,16 +160,15 @@ fn resolve_vmt_patch(vmt: Table, loader: &Loader) -> Result<Table, Error> {
if let Some(Entry::Table(patch)) = vmt.get("patch") {
let include = patch
.get("include")
.expect("no include in patch")
.as_value()
.expect("include is not a value")
.to_string();
.ok_or(Error::Other("no include in patch"))?
.as_str()
.ok_or(Error::Other("include is not a string"))?;
let _replace = patch
.get("replace")
.expect("no replace in patch")
.ok_or(Error::Other("no replace in patch"))?
.as_table()
.expect("replace is not a table");
let included_raw = loader.load(&include)?.to_ascii_lowercase();
.ok_or(Error::Other("replace is not a table"))?;
let included_raw = loader.load(include)?.to_ascii_lowercase();
// todo actually patch
parse_vdf(&included_raw)

View file

@ -1,9 +1,9 @@
use crate::bsp::{apply_transform, map_coords};
use crate::bsp::map_coords;
use crate::material::load_material_fallback;
use crate::{Error, Loader};
use cgmath::{Matrix, SquareMatrix};
use std::collections::HashMap;
use three_d::{CpuMaterial, CpuMesh, CpuModel, Mat4, Positions, Vec2, Vec3, Vec4};
use three_d::{CpuMaterial, CpuModel, Mat4, Positions, Vec2, Vec3, Vec4};
use three_d_asset::{Geometry, Primitive, TriMesh};
use tracing::warn;
use vbsp::{Handle, StaticPropLump};
use vmdl::mdl::{Mdl, TextureInfo};
@ -35,18 +35,25 @@ pub fn load_props<'a, I: Iterator<Item = Handle<'a, StaticPropLump>>>(
})
.collect::<Result<_, _>>()?;
let geometries = props.iter().flat_map(prop_to_meshes).collect();
let textures: HashMap<_, _> = props
let materials: HashMap<_, _> = props
.iter()
.flat_map(|prop| prop.model.textures())
.map(|tex| (tex.name.as_str(), tex))
.collect();
let materials: Vec<_> = textures
.into_values()
let materials: Vec<_> = materials.into_values().collect();
let geometries = props
.iter()
.flat_map(|prop| prop_to_meshes(prop, materials.as_slice()))
.collect();
let materials: Vec<_> = materials
.into_iter()
.map(|tex| prop_texture_to_material(tex, loader))
.collect();
Ok(CpuModel {
name: "props".into(),
geometries,
materials,
})
@ -58,9 +65,11 @@ struct PropData {
skin: i32,
}
fn prop_to_meshes(prop: &PropData) -> impl Iterator<Item = CpuMesh> + '_ {
fn prop_to_meshes<'a>(
prop: &'a PropData,
textures: &'a [&TextureInfo],
) -> impl Iterator<Item = Primitive> + 'a {
let transform = prop.transform;
let normal_transform = transform.invert().unwrap().transpose() * -1.0;
let model = &prop.model;
let skin = match model.skin_tables().nth(prop.skin as usize) {
@ -75,16 +84,20 @@ fn prop_to_meshes(prop: &PropData) -> impl Iterator<Item = CpuMesh> + '_ {
let texture = skin
.texture(mesh.material_index())
.expect("texture out of bounds");
let material_index = textures
.iter()
.enumerate()
.find_map(|(i, texture_info)| (texture_info.name == texture).then_some(i));
let positions: Vec<Vec3> = mesh
.vertices()
.map(|vertex| map_coords(vertex.position))
.map(|v| apply_transform(v, transform))
// .map(|v| apply_transform(v, transform))
.collect();
let normals: Vec<Vec3> = mesh
.vertices()
.map(|vertex| map_coords(vertex.normal))
.map(|v| apply_transform(v, normal_transform))
// .map(|v| apply_transform(v, normal_transform))
.collect();
let uvs: Vec<Vec2> = mesh
.vertices()
@ -93,13 +106,21 @@ fn prop_to_meshes(prop: &PropData) -> impl Iterator<Item = CpuMesh> + '_ {
let tangents: Vec<Vec4> = mesh.tangents().map(|tangent| tangent.into()).collect();
CpuMesh {
let geometry = Geometry::Triangles(TriMesh {
positions: Positions::F32(positions),
indices: Default::default(),
normals: Some(normals),
uvs: Some(uvs),
material_name: Some(texture.into()),
tangents: Some(tangents),
..Default::default()
colors: None,
});
Primitive {
name: "".to_string(),
transformation: transform,
animations: vec![],
geometry,
material_index,
}
})
}

View file

@ -24,17 +24,17 @@ impl<C: Control> Renderer<C> {
vec3(0.0, 1.0, 0.0),
degrees(60.0),
0.1,
30.0,
45.0,
);
let ambient_lights = vec![AmbientLight {
color: Color::WHITE,
color: Srgba::WHITE,
intensity: 0.2,
..Default::default()
}];
let directional_lights = 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)),
DirectionalLight::new(&context, 1.0, Srgba::WHITE, &vec3(0.0, -1.0, 0.0)),
DirectionalLight::new(&context, 1.0, Srgba::WHITE, &vec3(0.0, 1.0, 0.0)),
];
// let control = FirstPerson::new(0.1);
@ -68,11 +68,8 @@ impl<C: Control> Renderer<C> {
self.directional_lights[0].intensity = self.gui.directional_intensity;
self.directional_lights[1].intensity = self.gui.directional_intensity;
self.ambient_lights[0].intensity = self.gui.ambient_intensity;
self.camera.set_perspective_projection(
degrees(self.gui.fov),
self.camera.z_near(),
self.camera.z_far(),
);
self.camera
.set_perspective_projection(degrees(self.gui.fov), 0.1, 45.0);
}
let viewport = Viewport {