update vdf and special case eye textures for now

This commit is contained in:
Robin Appelman 2023-12-11 20:34:13 +01:00
commit 9e2ca40d29
4 changed files with 237 additions and 80 deletions

106
Cargo.lock generated
View file

@ -221,7 +221,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.40",
] ]
[[package]] [[package]]
@ -488,7 +488,7 @@ dependencies = [
"ciborium", "ciborium",
"clap", "clap",
"criterion-plot", "criterion-plot",
"itertools", "itertools 0.10.5",
"lazy_static", "lazy_static",
"num-traits", "num-traits",
"oorandom", "oorandom",
@ -509,7 +509,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [ dependencies = [
"cast", "cast",
"itertools", "itertools 0.10.5",
] ]
[[package]] [[package]]
@ -848,7 +848,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.40",
] ]
[[package]] [[package]]
@ -1146,6 +1146,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"
@ -1215,9 +1224,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.150" version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]] [[package]]
name = "libloading" name = "libloading"
@ -1349,7 +1358,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.40",
] ]
[[package]] [[package]]
@ -1442,7 +1451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c"
dependencies = [ dependencies = [
"darling", "darling",
"proc-macro-crate", "proc-macro-crate 1.3.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 1.0.109",
@ -1574,7 +1583,7 @@ version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate 1.3.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 1.0.109",
@ -1586,10 +1595,10 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate 2.0.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.40",
] ]
[[package]] [[package]]
@ -1704,7 +1713,7 @@ dependencies = [
"regex", "regex",
"regex-syntax 0.7.5", "regex-syntax 0.7.5",
"structmeta", "structmeta",
"syn 2.0.39", "syn 2.0.40",
] ]
[[package]] [[package]]
@ -1750,7 +1759,7 @@ dependencies = [
"pest_meta", "pest_meta",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.40",
] ]
[[package]] [[package]]
@ -1836,7 +1845,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"toml_edit", "toml_edit 0.19.15",
]
[[package]]
name = "proc-macro-crate"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a"
dependencies = [
"toml_datetime",
"toml_edit 0.20.2",
] ]
[[package]] [[package]]
@ -2078,7 +2097,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.40",
] ]
[[package]] [[package]]
@ -2208,6 +2227,14 @@ dependencies = [
"winreg", "winreg",
] ]
[[package]]
name = "steamy-vdf"
version = "0.3.0"
source = "git+https://github.com/icewind1991/steamy?branch=nom7#56b737b329ec27c198669d1f4fef9d827be80470"
dependencies = [
"nom",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.10.0" version = "0.10.0"
@ -2223,7 +2250,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"structmeta-derive", "structmeta-derive",
"syn 2.0.39", "syn 2.0.40",
] ]
[[package]] [[package]]
@ -2234,7 +2261,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.40",
] ]
[[package]] [[package]]
@ -2278,9 +2305,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.39" version = "2.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2352,7 +2379,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.40",
] ]
[[package]] [[package]]
@ -2447,9 +2474,9 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.5" version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
@ -2462,6 +2489,17 @@ dependencies = [
"winnow", "winnow",
] ]
[[package]]
name = "toml_edit"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
dependencies = [
"indexmap 2.1.0",
"toml_datetime",
"winnow",
]
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.40" version = "0.1.40"
@ -2481,7 +2519,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.40",
] ]
[[package]] [[package]]
@ -2589,10 +2627,11 @@ dependencies = [
"cgmath", "cgmath",
"criterion", "criterion",
"iai", "iai",
"itertools", "itertools 0.12.0",
"miette", "miette",
"static_assertions", "static_assertions",
"steamlocate", "steamlocate",
"steamy-vdf",
"thiserror", "thiserror",
"three-d", "three-d",
"tracing", "tracing",
@ -2663,7 +2702,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.40",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2685,7 +2724,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.40",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -3036,9 +3075,9 @@ dependencies = [
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.5.26" version = "0.5.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" checksum = "cb877ca3232bec99a6472ed63f7241de2a250165260908b2d24c09d867907a85"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@ -3074,12 +3113,9 @@ dependencies = [
[[package]] [[package]]
name = "xcursor" name = "xcursor"
version = "0.3.4" version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911"
dependencies = [
"nom",
]
[[package]] [[package]]
name = "xml-rs" name = "xml-rs"
@ -3114,7 +3150,7 @@ checksum = "855e0f6af9cd72b87d8a6c586f3cb583f5cdcc62c2c80869d8cd7e96fdf7ee20"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.40",
] ]
[[package]] [[package]]
@ -3125,5 +3161,5 @@ checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.40",
] ]

View file

@ -9,7 +9,7 @@ arrayvec = "0.7.2"
thiserror = "1.0.37" thiserror = "1.0.37"
static_assertions = "1.1.0" static_assertions = "1.1.0"
bitflags = "1.3.2" bitflags = "1.3.2"
itertools = "0.10.5" itertools = "0.12.0"
tracing = "0.1.37" tracing = "0.1.37"
bytemuck = { version = "1.12.3", features = ["derive"] } bytemuck = { version = "1.12.3", features = ["derive"] }
cgmath = "0.18.0" cgmath = "0.18.0"
@ -24,6 +24,7 @@ vtf = "0.1.6"
vpk = "0.2.0" vpk = "0.2.0"
tracing = "0.1.40" tracing = "0.1.40"
tracing-subscriber = "0.3.18" tracing-subscriber = "0.3.18"
steamy-vdf = { version = "0.3.0", git = "https://github.com/icewind1991/steamy", branch = "nom7" }
[[bench]] [[bench]]
name = "parse" name = "parse"

View file

@ -1,6 +1,8 @@
mod loader; mod loader;
mod material;
use crate::loader::{LoadError, Loader}; use crate::loader::{LoadError, Loader};
use crate::material::load_material_fallback;
use std::env::args_os; use std::env::args_os;
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -11,10 +13,9 @@ use vmdl::mdl::Mdl;
use vmdl::vtx::Vtx; use vmdl::vtx::Vtx;
use vmdl::vvd::Vvd; use vmdl::vvd::Vvd;
use vmdl::{Model, Vector}; use vmdl::{Model, Vector};
use vtf::vtf::VTF;
#[derive(Debug, Error)] #[derive(Debug, Error)]
enum Error { pub enum Error {
#[error(transparent)] #[error(transparent)]
Three(#[from] Box<dyn std::error::Error>), Three(#[from] Box<dyn std::error::Error>),
#[error(transparent)] #[error(transparent)]
@ -27,6 +28,8 @@ enum Error {
Loader(#[from] LoadError), Loader(#[from] LoadError),
#[error(transparent)] #[error(transparent)]
Vtf(#[from] vtf::Error), Vtf(#[from] vtf::Error),
#[error("{0}")]
Other(&'static str),
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
@ -306,30 +309,7 @@ fn model_to_model(model: &Model, loader: &Loader, skin: usize) -> CpuModel {
let materials = model let materials = model
.textures() .textures()
.iter() .iter()
.map(|texture| { .map(|texture| load_material_fallback(&texture.name, model.texture_directories(), loader))
let dirs = model.texture_directories();
match load_texture(&texture.name, dirs, loader) {
Ok(texture) => CpuMaterial {
albedo: Color::default(),
name: texture.name.clone(),
albedo_texture: Some(texture),
..Default::default()
},
Err(e) => {
error!("{:#}", e);
CpuMaterial {
albedo: Color {
r: 255,
g: 0,
b: 255,
a: 255,
},
name: texture.name.clone(),
..Default::default()
}
}
}
})
.collect(); .collect();
CpuModel { CpuModel {
@ -337,21 +317,3 @@ fn model_to_model(model: &Model, loader: &Loader, skin: usize) -> CpuModel {
geometries, geometries,
} }
} }
fn load_texture(name: &str, dirs: &[String], loader: &Loader) -> Result<CpuTexture, Error> {
let dirs = dirs
.iter()
.map(|dir| format!("materials/{}", dir))
.collect::<Vec<_>>();
let path = format!("{}.vtf", name);
let mut raw = loader.load_from_paths(&path, &dirs)?;
let vtf = VTF::read(&mut raw)?;
let image = vtf.highres_image.decode(0)?;
Ok(CpuTexture {
name: name.into(),
data: TextureData::RgbaU8(image.into_rgba8().pixels().map(|pixel| pixel.0).collect()),
height: vtf.header.height as u32,
width: vtf.header.width as u32,
..CpuTexture::default()
})
}

158
examples/view/material.rs Normal file
View file

@ -0,0 +1,158 @@
use crate::loader::Loader;
use crate::Error;
use steamy_vdf::{Entry, Table};
use three_d::{Color, CpuMaterial, CpuTexture, TextureData};
use tracing::error;
use vtf::vtf::VTF;
pub fn load_material_fallback(name: &str, search_dirs: &[String], loader: &Loader) -> CpuMaterial {
match load_material(name, search_dirs, loader) {
Ok(material) => material,
Err(e) => {
error!(
material = name,
error = ?e,
"failed to load material, falling back"
);
CpuMaterial {
albedo: Color {
r: 255,
g: 0,
b: 255,
a: 255,
},
name: name.into(),
..Default::default()
}
}
}
}
pub fn load_material(
name: &str,
search_dirs: &[String],
loader: &Loader,
) -> Result<CpuMaterial, Error> {
let dirs = search_dirs
.iter()
.map(|dir| {
format!(
"materials/{}",
dir.to_ascii_lowercase().trim_start_matches("/")
)
})
.collect::<Vec<_>>();
let path = format!("{}.vmt", name.to_ascii_lowercase().trim_end_matches(".vmt"));
let raw = loader.load_from_paths(&path, &dirs)?;
let vmt = parse_vdf(raw)?;
let vmt = resolve_vmt_patch(vmt, loader)?;
let material_type = vmt
.keys()
.next()
.ok_or(Error::Other("empty vmt"))?
.to_ascii_lowercase();
if material_type == "water" {
return Ok(CpuMaterial {
albedo: Color {
r: 82,
g: 180,
b: 217,
a: 128,
},
name: name.into(),
..Default::default()
});
}
let table = vmt
.values()
.next()
.ok_or(Error::Other("empty vmt"))?
.as_table()
.ok_or(Error::Other("vmt not a table"))?;
let base_texture = table
.iter()
.find_map(|(key, value)| (key.to_ascii_lowercase() == "$basetexture").then_some(value))
.or_else(|| {
table
.iter()
.find_map(|(key, value)| (key.to_ascii_lowercase() == "eyes_dx8").then_some(value))
.and_then(|eyes| eyes.as_table())
.and_then(|eyes| {
eyes.iter().find_map(|(key, value)| {
(key.to_ascii_lowercase() == "$basetexture").then_some(value)
})
})
})
.ok_or(Error::Other("no $basetexture"))?
.as_value()
.ok_or(Error::Other("$basetexture not a value"))?
.to_string()
.to_ascii_lowercase()
.replace('\\', "/")
.replace('\t', "/t");
let texture = load_texture(base_texture.as_str(), loader)?;
Ok(CpuMaterial {
name: name.into(),
albedo: Color::WHITE,
albedo_texture: Some(texture),
..CpuMaterial::default()
})
}
fn parse_vdf(bytes: Vec<u8>) -> Result<Table, Error> {
let mut reader = steamy_vdf::Reader::from(bytes.as_slice());
Table::load(&mut reader).map_err(|e| {
println!("{}", String::from_utf8_lossy(&bytes));
error!(
source = String::from_utf8_lossy(&bytes).to_string(),
error = ?e,
"failed to parse vmt"
);
Error::Other("failed to parse vdf")
})
}
fn load_texture(name: &str, loader: &Loader) -> Result<CpuTexture, Error> {
let path = format!(
"materials/{}.vtf",
name.trim_end_matches(".vtf").trim_start_matches("/")
);
let mut raw = loader.load(&path)?;
let vtf = VTF::read(&mut raw)?;
let image = vtf.highres_image.decode(0)?;
Ok(CpuTexture {
name: name.into(),
data: TextureData::RgbaU8(image.into_rgba8().pixels().map(|pixel| pixel.0).collect()),
height: vtf.header.height as u32,
width: vtf.header.width as u32,
..CpuTexture::default()
})
}
fn resolve_vmt_patch(vmt: Table, loader: &Loader) -> Result<Table, Error> {
if vmt.len() != 1 {
panic!("vmt with more than 1 item?");
}
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();
let _replace = patch
.get("replace")
.expect("no replace in patch")
.as_table()
.expect("replace is not a table");
let included_raw = loader.load(&include.to_ascii_lowercase())?;
// todo actually patch
parse_vdf(included_raw)
} else {
Ok(vmt)
}
}