Skip to content

Commit

Permalink
add convert options
Browse files Browse the repository at this point in the history
  • Loading branch information
icewind1991 committed Dec 27, 2023
1 parent 1161fcf commit 16d20a5
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 41 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,25 @@ gltf-json = { version = "1.4.0", features = ["KHR_texture_transform"] }
gltf = "1.4.0"
cgmath = "0.18.0"
bytemuck = { version = "1.14.0", features = ["derive"] }
texpresso = { version = "2.0.1", features = ["rayon"] }
serde = "1.0.193"

url = { version = "2.5.0", optional = true, features = ["serde"] }
serde = { version = "1.0.193", optional = true }
toml = { version = "0.8.8", optional = true }
axum = { version = "0.7.2", optional = true, features = ["macros"] }
tokio = { version = "1.35.1", features = ["full"], optional = true }
reqwest = { version = "0.11.23", optional = true, default-features = false, features = ["rustls-tls-webpki-roots"] }
async-tempfile = { version = "0.5.0", optional = true }
tower-http = { version = "0.5.0", optional = true, features = ["cors"] }
http = { version = "1.0.0", optional = true }
ahash = { version = "0.8.6", optional = true }

[features]
server = ["url", "serde", "toml", "axum", "tokio", "reqwest", "async-tempfile", "tower-http", "http"]
server = ["url", "toml", "axum", "tokio", "reqwest", "async-tempfile", "tower-http", "http", "ahash"]

[profile.dev.package."*"]
opt-level = 2
opt-level = 2

[profile.release]
codegen-units = 1
lto = true
19 changes: 16 additions & 3 deletions src/bsp.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::convert::map_coords;
use crate::error::Error;
use crate::gltf_builder::push_or_get_material;
use crate::ConvertOptions;
use bytemuck::{offset_of, Pod, Zeroable};
use gltf_json::accessor::{ComponentType, GenericComponentType, Type};
use gltf_json::buffer::{Stride, Target, View};
Expand Down Expand Up @@ -73,11 +74,12 @@ pub fn push_bsp_model(
loader: &Loader,
model: &Handle<Model>,
offset: Vector,
options: &ConvertOptions,
) -> Node {
let primitives = model
.faces()
.filter(|face| face.is_visible())
.map(|face| push_bsp_face(buffer, gltf, loader, &face))
.map(|face| push_bsp_face(buffer, gltf, loader, &face, options))
.collect();

let mesh = Mesh {
Expand Down Expand Up @@ -112,6 +114,7 @@ pub fn push_bsp_face(
gltf: &mut Root,
loader: &Loader,
face: &Handle<Face>,
options: &ConvertOptions,
) -> Primitive {
let vertex_count = face.vertex_positions().count() as u64;

Expand Down Expand Up @@ -175,7 +178,17 @@ pub fn push_bsp_face(
gltf.accessors.push(positions);
gltf.accessors.push(uvs);

let material_index = push_or_get_material(buffer, gltf, loader, face.texture().name());
let material_index = if options.textures {
Some(push_or_get_material(
buffer,
gltf,
loader,
face.texture().name(),
options,
))
} else {
None
};

Primitive {
attributes: {
Expand All @@ -190,7 +203,7 @@ pub fn push_bsp_face(
extensions: Default::default(),
extras: Default::default(),
indices: None,
material: Some(material_index),
material: material_index,
mode: Valid(Mode::Triangles),
targets: None,
}
Expand Down
4 changes: 2 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::EnvFilter;
use tracing_tree::HierarchicalLayer;
use vbsp::Bsp;
use vbsp_to_gltf::{export, Error};
use vbsp_to_gltf::{export, ConvertOptions, Error};

fn setup() {
miette::set_panic_hook();
Expand Down Expand Up @@ -43,7 +43,7 @@ fn main() -> miette::Result<()> {
let map = Bsp::read(&data).map_err(Error::from)?;
loader.add_source(map.pack.clone().into_zip());

let glb = export(map, &loader)?;
let glb = export(map, &loader, ConvertOptions::default())?;

let writer = File::create(&args.target)
.map_err(Error::from)
Expand Down
15 changes: 11 additions & 4 deletions src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use gltf_json as json;

use crate::bsp::{bsp_models, push_bsp_model};
use crate::prop::push_or_get_model;
use crate::Error;
use crate::{ConvertOptions, Error};
use cgmath::{Deg, Quaternion, Rotation3};
use gltf::Glb;
use gltf_json::scene::UnitQuaternion;
Expand All @@ -12,18 +12,25 @@ use std::borrow::Cow;
use tf_asset_loader::Loader;
use vbsp::Bsp;

pub fn export(bsp: Bsp, loader: &Loader) -> Result<Glb<'static>, Error> {
pub fn export(bsp: Bsp, loader: &Loader, options: ConvertOptions) -> Result<Glb<'static>, Error> {
let mut buffer = Vec::new();

let mut root = Root::default();

for (model, offset) in bsp_models(&bsp)? {
let node = push_bsp_model(&mut buffer, &mut root, loader, &model, offset);
let node = push_bsp_model(&mut buffer, &mut root, loader, &model, offset, &options);
root.nodes.push(node);
}

for prop in bsp.static_props() {
let mesh = push_or_get_model(&mut buffer, &mut root, loader, prop.model(), prop.skin);
let mesh = push_or_get_model(
&mut buffer,
&mut root,
loader,
prop.model(),
prop.skin,
&options,
);
let rotation = prop.rotation();

let node = Node {
Expand Down
4 changes: 3 additions & 1 deletion src/gltf_builder.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::convert::pad_byte_vector;
use crate::materials::{load_material_fallback, MaterialData, TextureData};
use crate::ConvertOptions;
use gltf_json::buffer::View;
use gltf_json::extensions::texture::{
TextureTransform, TextureTransformOffset, TextureTransformRotation, TextureTransformScale,
Expand All @@ -20,12 +21,13 @@ pub fn push_or_get_material(
gltf: &mut Root,
loader: &Loader,
material: &str,
options: &ConvertOptions,
) -> Index<Material> {
let material = material.to_ascii_lowercase();
match get_material_index(&gltf.materials, &material) {
Some(index) => index,
None => {
let material = load_material_fallback(&material, &[String::new()], loader);
let material = load_material_fallback(&material, &[String::new()], loader, options);
let index = gltf.materials.len() as u32;
let material = push_material(buffer, gltf, material);
gltf.materials.push(material);
Expand Down
37 changes: 37 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,42 @@ pub mod gltf_builder;
mod materials;
mod prop;

use ahash::RandomState;
pub use convert::export;
pub use error::Error;
use serde::Deserialize;
use std::hash::{BuildHasher, Hash, Hasher};

#[derive(Debug, Deserialize, Clone)]
pub struct ConvertOptions {
#[serde(default = "default_enable")]
pub textures: bool,
#[serde(default = "default_scale")]
pub texture_scale: f32,
}

impl ConvertOptions {
pub fn key(&self) -> u64 {
let mut hasher = RandomState::with_seeds(1, 2, 3, 4).build_hasher();
self.textures.hash(&mut hasher);
self.texture_scale.to_le_bytes().hash(&mut hasher);
hasher.finish()
}
}

impl Default for ConvertOptions {
fn default() -> Self {
ConvertOptions {
textures: true,
texture_scale: 1.0,
}
}
}

fn default_enable() -> bool {
true
}

fn default_scale() -> f32 {
1.0
}
35 changes: 27 additions & 8 deletions src/materials.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
use crate::Error;
use image::DynamicImage;
use crate::{ConvertOptions, Error};
use image::imageops::FilterType;
use image::{DynamicImage, GenericImageView};
use tf_asset_loader::Loader;
use tracing::{error, instrument};
use vmt_parser::material::{Material, WaterMaterial};
use vmt_parser::{from_str, TextureTransform};
use vtf::vtf::VTF;

pub fn load_material_fallback(name: &str, search_dirs: &[String], loader: &Loader) -> MaterialData {
match load_material(name, search_dirs, loader) {
pub fn load_material_fallback(
name: &str,
search_dirs: &[String],
loader: &Loader,
options: &ConvertOptions,
) -> MaterialData {
match load_material(name, search_dirs, loader, options) {
Ok(mat) => mat,
Err(e) => {
error!(error = ?e, "failed to load material");
Expand Down Expand Up @@ -44,6 +50,7 @@ pub fn load_material(
name: &str,
search_dirs: &[String],
loader: &Loader,
options: &ConvertOptions,
) -> Result<MaterialData, Error> {
let dirs = search_dirs
.iter()
Expand Down Expand Up @@ -94,11 +101,11 @@ pub fn load_material(
let translucent = material.translucent();
let glass = material.surface_prop() == Some("glass");
let alpha_test = material.alpha_test();
let texture = load_texture(base_texture, loader)?;
let texture = load_texture(base_texture, loader, options)?;

let bump_map = material.bump_map().and_then(|path| {
Some(TextureData {
image: load_texture(path, loader).ok()?,
image: load_texture(path, loader, options).ok()?,
name: path.into(),
})
});
Expand All @@ -124,7 +131,11 @@ pub fn load_material(
})
}

fn load_texture(name: &str, loader: &Loader) -> Result<DynamicImage, Error> {
fn load_texture(
name: &str,
loader: &Loader,
options: &ConvertOptions,
) -> Result<DynamicImage, Error> {
let path = format!(
"materials/{}.vtf",
name.trim_end_matches(".vtf").trim_start_matches('/')
Expand All @@ -134,5 +145,13 @@ fn load_texture(name: &str, loader: &Loader) -> Result<DynamicImage, Error> {
.ok_or(Error::Other(format!("Can't find file {}", path)))?;
let vtf = VTF::read(&mut raw)?;
let image = vtf.highres_image.decode(0)?;
Ok(image)
if options.texture_scale != 1.0 {
Ok(image.resize(
(image.width() as f32 * options.texture_scale) as u32,
(image.height() as f32 * options.texture_scale) as u32,
FilterType::CatmullRom,
))
} else {
Ok(image)
}
}
Loading

0 comments on commit 16d20a5

Please sign in to comment.