mirror of
https://codeberg.org/icewind/vbsp-to-gltf.git
synced 2026-06-03 18:24:07 +02:00
updates
This commit is contained in:
parent
e393b88ece
commit
14d12fea4c
8 changed files with 1241 additions and 870 deletions
|
|
@ -10,7 +10,7 @@ use gltf_json::validation::USize64;
|
|||
use gltf_json::{Buffer, Index, Node, Root, Scene};
|
||||
use std::borrow::Cow;
|
||||
use tf_asset_loader::Loader;
|
||||
use vbsp::Bsp;
|
||||
use vbsp::{Bsp, Entity};
|
||||
|
||||
pub fn export(bsp: Bsp, loader: &Loader, options: ConvertOptions) -> Result<Glb<'static>, Error> {
|
||||
let mut buffer = Vec::new();
|
||||
|
|
@ -22,37 +22,49 @@ pub fn export(bsp: Bsp, loader: &Loader, options: ConvertOptions) -> Result<Glb<
|
|||
root.nodes.push(node);
|
||||
}
|
||||
|
||||
for prop in bsp.static_props() {
|
||||
let mesh = push_or_get_model(
|
||||
let entity_props =
|
||||
bsp.entities
|
||||
.iter()
|
||||
.flat_map(|ent| ent.parse())
|
||||
.filter_map(|ent| match ent {
|
||||
Entity::PropDynamic(prop) => Some(prop.as_prop_placement()),
|
||||
Entity::PropPhysics(prop) => Some(prop.as_prop_placement()),
|
||||
Entity::PropDynamicOverride(prop) => Some(prop.as_prop_placement()),
|
||||
_ => None,
|
||||
});
|
||||
let static_props = bsp.static_props().map(|prop| prop.as_prop_placement());
|
||||
for prop in static_props.chain(entity_props) {
|
||||
if let Some(mesh) = push_or_get_model(
|
||||
&mut buffer,
|
||||
&mut root,
|
||||
loader,
|
||||
prop.model(),
|
||||
prop.model,
|
||||
prop.skin,
|
||||
&options,
|
||||
);
|
||||
let rotation = prop.rotation();
|
||||
) {
|
||||
let rotation = prop.rotation;
|
||||
|
||||
let node = Node {
|
||||
camera: None,
|
||||
children: None,
|
||||
extensions: Default::default(),
|
||||
extras: Default::default(),
|
||||
matrix: None,
|
||||
mesh: Some(mesh),
|
||||
name: Some(prop.model().into()),
|
||||
rotation: Some(UnitQuaternion([
|
||||
rotation.v.x,
|
||||
rotation.v.y,
|
||||
rotation.v.z,
|
||||
rotation.s,
|
||||
])),
|
||||
scale: None,
|
||||
translation: Some(map_coords(prop.origin)),
|
||||
skin: None,
|
||||
weights: None,
|
||||
};
|
||||
root.nodes.push(node);
|
||||
let node = Node {
|
||||
camera: None,
|
||||
children: None,
|
||||
extensions: Default::default(),
|
||||
extras: Default::default(),
|
||||
matrix: None,
|
||||
mesh: Some(mesh),
|
||||
name: Some(prop.model.into()),
|
||||
rotation: Some(UnitQuaternion([
|
||||
rotation.v.x,
|
||||
rotation.v.y,
|
||||
rotation.v.z,
|
||||
rotation.s,
|
||||
])),
|
||||
scale: None,
|
||||
translation: Some(map_coords(prop.origin)),
|
||||
skin: None,
|
||||
weights: None,
|
||||
};
|
||||
root.nodes.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
let node_indices = 0..root.nodes.len();
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ use gltf_json::texture::Info;
|
|||
use gltf_json::validation::Checked::Valid;
|
||||
use gltf_json::validation::USize64;
|
||||
use gltf_json::{Extras, Image, Index, Material, Root, Texture};
|
||||
use image::png::PngEncoder;
|
||||
use image::{ColorType, DynamicImage, GenericImageView};
|
||||
use image::codecs::png::PngEncoder;
|
||||
use image::{ColorType, DynamicImage, ImageEncoder};
|
||||
use std::f32::consts::PI;
|
||||
use tf_asset_loader::Loader;
|
||||
|
||||
|
|
@ -125,15 +125,17 @@ fn push_texture(buffer: &mut Vec<u8>, gltf: &mut Root, texture: TextureData) ->
|
|||
let buffer_start = buffer.len() as u64;
|
||||
let view_start = gltf.buffer_views.len() as u32;
|
||||
let image_start = gltf.images.len() as u32;
|
||||
let image_buffer_size =
|
||||
(image.color().bits_per_pixel() / 8) as u32 * image.width() * image.height();
|
||||
|
||||
let mut png_buffer = Vec::new();
|
||||
let encoder = PngEncoder::new(&mut png_buffer);
|
||||
encoder
|
||||
.encode(
|
||||
image.as_bytes(),
|
||||
.write_image(
|
||||
&image.as_bytes()[0..image_buffer_size as usize],
|
||||
image.width(),
|
||||
image.height(),
|
||||
image.color(),
|
||||
image.color().into(),
|
||||
)
|
||||
.expect("failed to encode");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{ConvertOptions, Error};
|
||||
use image::imageops::FilterType;
|
||||
use image::{DynamicImage, GenericImageView};
|
||||
use image::DynamicImage;
|
||||
use tf_asset_loader::Loader;
|
||||
use tracing::{error, instrument};
|
||||
use vmt_parser::material::{Material, WaterMaterial};
|
||||
|
|
@ -140,10 +140,10 @@ fn load_texture(
|
|||
"materials/{}.vtf",
|
||||
name.trim_end_matches(".vtf").trim_start_matches('/')
|
||||
);
|
||||
let mut raw = loader
|
||||
let raw = loader
|
||||
.load(&path)?
|
||||
.ok_or(Error::Other(format!("Can't find file {}", path)))?;
|
||||
let vtf = VTF::read(&mut raw)?;
|
||||
let vtf = VTF::read(&raw)?;
|
||||
let image = vtf.highres_image.decode(0)?;
|
||||
if options.texture_scale != 1.0 {
|
||||
Ok(image.resize(
|
||||
|
|
|
|||
28
src/prop.rs
28
src/prop.rs
|
|
@ -20,10 +20,10 @@ pub struct ModelVertex {
|
|||
uv: [f32; 2],
|
||||
}
|
||||
|
||||
impl From<&vmdl::vvd::Vertex> for ModelVertex {
|
||||
fn from(vertex: &vmdl::vvd::Vertex) -> Self {
|
||||
impl ModelVertex {
|
||||
fn from(vertex: &vmdl::vvd::Vertex, model: &Model) -> Self {
|
||||
ModelVertex {
|
||||
position: map_coords(vertex.position),
|
||||
position: map_coords(model.apply_root_transform(vertex.position)),
|
||||
uv: vertex.texture_coordinates,
|
||||
normal: vertex.normal.into(),
|
||||
}
|
||||
|
|
@ -36,13 +36,13 @@ fn push_vertices(buffer: &mut Vec<u8>, gltf: &mut Root, model: &Model) {
|
|||
let vertex_count = model.vertices().len() as u64;
|
||||
|
||||
let (min, max) = model.bounding_box();
|
||||
let min = map_coords(min);
|
||||
let max = map_coords(max);
|
||||
let min = map_coords(model.apply_root_transform(min));
|
||||
let max = map_coords(model.apply_root_transform(max));
|
||||
|
||||
let vertex_data = model
|
||||
.vertices()
|
||||
.iter()
|
||||
.map(ModelVertex::from)
|
||||
.map(|vert| ModelVertex::from(vert, model))
|
||||
.flat_map(bytemuck::cast::<_, [u8; size_of::<ModelVertex>()]>);
|
||||
buffer.extend(vertex_data);
|
||||
|
||||
|
|
@ -126,16 +126,20 @@ pub fn push_or_get_model(
|
|||
model: &str,
|
||||
skin: i32,
|
||||
options: &ConvertOptions,
|
||||
) -> Index<Mesh> {
|
||||
) -> Option<Index<Mesh>> {
|
||||
let skinned_name = format!("{model}_{skin}");
|
||||
match get_mesh_index(&gltf.meshes, &skinned_name) {
|
||||
Some(index) => index,
|
||||
Some(index) => Some(index),
|
||||
None => {
|
||||
let prop = load_prop(loader, model).expect("failed to load prop");
|
||||
let index = gltf.meshes.len() as u32;
|
||||
let material = push_model(buffer, gltf, loader, &prop, skin, skinned_name, options);
|
||||
gltf.meshes.push(material);
|
||||
Index::new(index)
|
||||
if prop.vertices().is_empty() {
|
||||
None
|
||||
} else {
|
||||
let index = gltf.meshes.len() as u32;
|
||||
let material = push_model(buffer, gltf, loader, &prop, skin, skinned_name, options);
|
||||
gltf.meshes.push(material);
|
||||
Some(Index::new(index))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue