improved lwo loading

This commit is contained in:
2023-05-28 20:37:28 +02:00
parent bd3b2d9492
commit 7f5d874be9
5 changed files with 144 additions and 107 deletions

View File

@@ -0,0 +1,22 @@
use godot::engine::{load, Image};
use godot::log::godot_error;
use godot::obj::Gd;
use lightwave_3d::lwo2::tags::image_clip::{ImageClip, ImageClipSubChunk};
use std::collections::HashMap;
pub fn collect_clip(target: &mut HashMap<u32, Gd<Image>>, clip: ImageClip) {
for img in clip.attributes.iter() {
match img {
ImageClipSubChunk::StillImage(still) => {
let path = format!(
"sar://{}",
still.name.to_string().replace('\\', "/").replace(':', ":/")
);
target.insert(clip.index, load(path));
}
x => {
godot_error!("TODO: Clip chunk {:?}", x)
}
}
}
}

View File

@@ -1,36 +1,25 @@
use godot::builtin::Color; use godot::builtin::Color;
use godot::engine::base_material_3d::TextureParam; use godot::engine::base_material_3d::{DiffuseMode, TextureParam};
use godot::engine::{load, Image, ImageTexture, StandardMaterial3D}; use godot::engine::{Image, ImageTexture, StandardMaterial3D};
use godot::log::{godot_error, godot_print, godot_warn}; use godot::log::{godot_error, godot_warn};
use godot::obj::Gd; use godot::obj::{Gd, Share};
use lightwave_3d::lwo2::sub_tags::blocks::image_texture::SurfaceBlockImageTextureSubChunk; use lightwave_3d::lwo2::sub_tags::blocks::image_texture::{
ProjectionMode, SurfaceBlockImageTextureSubChunk,
};
use lightwave_3d::lwo2::sub_tags::blocks::{ use lightwave_3d::lwo2::sub_tags::blocks::{
SurfaceBlockHeaderSubChunk, SurfaceBlocks, TextureChannel, SurfaceBlockHeaderSubChunk, SurfaceBlocks, TextureChannel,
}; };
use lightwave_3d::lwo2::sub_tags::surface_parameters::SurfaceParameterSubChunk; use lightwave_3d::lwo2::sub_tags::surface_parameters::SurfaceParameterSubChunk;
use lightwave_3d::lwo2::tags::image_clip::{ImageClip, ImageClipSubChunk};
use lightwave_3d::lwo2::tags::surface_definition::SurfaceDefinition; use lightwave_3d::lwo2::tags::surface_definition::SurfaceDefinition;
use std::collections::HashMap;
pub fn collect_material(surface: SurfaceDefinition, clip: ImageClip) -> Gd<StandardMaterial3D> { pub fn collect_material(
surface: SurfaceDefinition,
images: &HashMap<u32, Gd<Image>>,
) -> Gd<StandardMaterial3D> {
let mut material = StandardMaterial3D::new(); let mut material = StandardMaterial3D::new();
material.set_name(surface.name.to_string().into()); material.set_name(surface.name.to_string().into());
material.set_diffuse_mode(DiffuseMode::DIFFUSE_TOON);
let mut i: Option<Gd<Image>> = None;
for img in clip.attributes {
match img {
ImageClipSubChunk::StillImage(still) => {
let path = format!(
"sar://{}",
still.name.to_string().replace('\\', "/").replace(':', ":/")
);
godot_print!("Loading {}", &path);
i = Some(load(path));
}
x => {
godot_warn!("Invalid clip chunk {:?}", x)
}
}
}
for attr in surface.attributes { for attr in surface.attributes {
match attr { match attr {
@@ -64,15 +53,28 @@ pub fn collect_material(surface: SurfaceDefinition, clip: ImageClip) -> Gd<Stand
for attr in attributes { for attr in attributes {
match attr { match attr {
SurfaceBlockImageTextureSubChunk::ImageMap(r) => { SurfaceBlockImageTextureSubChunk::ImageMap(r) => {
if let Some(i) = i { if let Some(i) = images.get(&r.texture_image) {
texture.set_image(i); texture.set_image(i.share());
} else { } else {
godot_error!("Missing texture {:?}", r); godot_error!("Missing texture {:?}", r);
} }
i = None; }
SurfaceBlockImageTextureSubChunk::ProjectionMode(projection) => {
match projection.data {
ProjectionMode::UV => {}
_ => {
godot_error!("TODO: Projection mode {:?}", projection)
}
}
}
SurfaceBlockImageTextureSubChunk::UvVertexMap(map) => {
godot_error!(
"TODO: UV maps (this one is using '{}')",
map.txuv_map_name
)
} }
x => { x => {
godot_warn!("Invalid image texture chunk {:?}", x) godot_error!("TODO: Image texture chunk {:?}", x)
} }
} }
} }
@@ -85,17 +87,23 @@ pub fn collect_material(surface: SurfaceDefinition, clip: ImageClip) -> Gd<Stand
b: base_color.base_color[2], b: base_color.base_color[2],
a: 1.0, a: 1.0,
}), }),
SurfaceParameterSubChunk::BaseShadingValueSpecular(base_specular) => { SurfaceParameterSubChunk::BaseShadingValueDiffuse(base_diffuse) => {
material.set_specular(base_specular.value as f64); if base_diffuse.envelope != 0 || base_diffuse.value != 1.0 {
if base_specular.envelope != 0 {
godot_error!( godot_error!(
"Not implemented: Specular envelope {}", "TODO: Diffuse {{envelope: {}, value: {}}}",
base_specular.envelope base_diffuse.envelope,
base_diffuse.value
); );
} }
} }
SurfaceParameterSubChunk::BaseShadingValueSpecular(base_specular) => {
material.set_specular(base_specular.value as f64);
if base_specular.envelope != 0 {
godot_error!("TODO: Specular envelope {}", base_specular.envelope);
}
}
x => { x => {
godot_error!("Invalid Surface Chunk {:?}", x) godot_error!("TODO: Surface Chunk {:?}", x)
} }
} }
} }

View File

@@ -5,6 +5,7 @@ use godot::engine::ArrayMesh;
use godot::obj::Gd; use godot::obj::Gd;
use lightwave_3d::LightWaveObject; use lightwave_3d::LightWaveObject;
pub(crate) mod clips;
pub(crate) mod mapping; pub(crate) mod mapping;
pub(crate) mod material; pub(crate) mod material;
pub(crate) mod object; pub(crate) mod object;

View File

@@ -1,12 +1,12 @@
use crate::lwo::clips::collect_clip;
use crate::lwo::mapping::{collect_discontinuous_mappings, collect_mappings}; use crate::lwo::mapping::{collect_discontinuous_mappings, collect_mappings};
use crate::lwo::material::collect_material; use crate::lwo::material::collect_material;
use crate::lwo::surface::try_commit; use crate::lwo::surface::try_commit;
use godot::builtin::{Array, Dictionary, Vector2, Vector3}; use godot::builtin::{Array, Dictionary, Vector2, Vector3};
use godot::engine::mesh::{ArrayFormat, PrimitiveType}; use godot::engine::mesh::{ArrayFormat, PrimitiveType};
use godot::engine::{ArrayMesh, SurfaceTool}; use godot::engine::{ArrayMesh, Image, SurfaceTool};
use godot::log::{godot_error, godot_warn}; use godot::log::{godot_error, godot_warn};
use godot::obj::{Gd, Share}; use godot::obj::{Gd, Share};
use lightwave_3d::lwo2::tags::image_clip::ImageClip;
use lightwave_3d::lwo2::tags::polygon_list::PolygonList; use lightwave_3d::lwo2::tags::polygon_list::PolygonList;
use lightwave_3d::lwo2::tags::Tag; use lightwave_3d::lwo2::tags::Tag;
use lightwave_3d::LightWaveObject; use lightwave_3d::LightWaveObject;
@@ -16,12 +16,15 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<ArrayMesh> {
let mut mesh = ArrayMesh::new(); let mut mesh = ArrayMesh::new();
let mut materials = vec![]; let mut materials = vec![];
let mut images: Option<ImageClip> = None; let mut images = HashMap::<u32, Gd<Image>>::new();
let mut points = Vec::<Vector3>::new(); let mut points = Vec::<Vector3>::new();
let mut uv_mappings = HashMap::<i32, HashMap<i32, Vector2>>::new(); let mut uv_mappings = HashMap::<i32, HashMap<i32, Vector2>>::new();
let mut weight_mappings = HashMap::<i32, HashMap<i32, f32>>::new(); let mut weight_mappings = HashMap::<i32, HashMap<i32, f32>>::new();
let mut polygons = Vec::<PolygonList>::new(); let mut polygons = Vec::<PolygonList>::new();
let mut surfaces = HashMap::<i32, u16>::new();
let mut surface_materials = Vec::<u16>::new();
for tag in lightwave.data { for tag in lightwave.data {
match tag { match tag {
@@ -32,6 +35,8 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<ArrayMesh> {
&mut uv_mappings, &mut uv_mappings,
&mut weight_mappings, &mut weight_mappings,
&mut polygons, &mut polygons,
&mut surfaces,
&mut surface_materials,
); );
} }
Tag::PointList(points_chunk) => { Tag::PointList(points_chunk) => {
@@ -76,14 +81,8 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<ArrayMesh> {
todo!(); todo!();
},*/ },*/
b"SURF" => { b"SURF" => {
let surfaces = ptag for surf in ptag.data.mappings {
.data surfaces.insert(surf.poly as i32, surf.tag);
.mappings
.iter()
.map(|map| map.tag)
.collect::<HashSet<u16>>();
if surfaces.len() > 1 {
godot_error!("Too many surfaces: {:?}", surfaces)
} }
} }
x => godot_warn!( x => godot_warn!(
@@ -97,17 +96,10 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<ArrayMesh> {
} }
x => godot_warn!("{}", String::from_utf8(x.to_vec()).unwrap()), x => godot_warn!("{}", String::from_utf8(x.to_vec()).unwrap()),
}, },
Tag::ImageClip(clip) => { Tag::ImageClip(clip) => collect_clip(&mut images, clip.data),
images = Some(clip.data);
}
Tag::SurfaceDefinition(surf) => { Tag::SurfaceDefinition(surf) => {
if let Some(img) = images { let mat = collect_material(surf.data, &images);
let mat = collect_material(surf.data, img); materials.push(mat);
images = None;
materials.push(mat);
} else {
godot_error!("Missing images for surface {}", surf.name)
}
} }
Tag::BoundingBox(_) => (), Tag::BoundingBox(_) => (),
x => { x => {
@@ -122,9 +114,10 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<ArrayMesh> {
&mut uv_mappings, &mut uv_mappings,
&mut weight_mappings, &mut weight_mappings,
&mut polygons, &mut polygons,
&mut surfaces,
&mut surface_materials,
); );
let mut out_mesh = ArrayMesh::new(); let mut out_mesh = ArrayMesh::new();
let mut mats = materials.into_iter();
for i in 0..mesh.get_surface_count() { for i in 0..mesh.get_surface_count() {
let mut tool = SurfaceTool::new(); let mut tool = SurfaceTool::new();
@@ -140,8 +133,8 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<ArrayMesh> {
ArrayFormat::ARRAY_FORMAT_NORMAL, ArrayFormat::ARRAY_FORMAT_NORMAL,
); );
if let Some(mat) = mats.next() { if let Some(mat) = materials.get(surfaces[&(i as i32)] as usize) {
out_mesh.surface_set_material(i, mat.upcast()) out_mesh.surface_set_material(i, mat.share().upcast())
} }
} }
out_mesh out_mesh

View File

@@ -6,6 +6,7 @@ use godot::builtin::{
use godot::engine::mesh::{ArrayFormat, ArrayType, PrimitiveType}; use godot::engine::mesh::{ArrayFormat, ArrayType, PrimitiveType};
use godot::engine::ArrayMesh; use godot::engine::ArrayMesh;
use godot::obj::EngineEnum; use godot::obj::EngineEnum;
use itertools::Itertools;
use lightwave_3d::lwo2::tags::polygon_list::PolygonList; use lightwave_3d::lwo2::tags::polygon_list::PolygonList;
use std::collections::HashMap; use std::collections::HashMap;
@@ -22,63 +23,75 @@ pub fn try_commit(
uv_mappings: &mut HashMap<i32, HashMap<i32, Vector2>>, uv_mappings: &mut HashMap<i32, HashMap<i32, Vector2>>,
weight_mappings: &mut HashMap<i32, HashMap<i32, f32>>, weight_mappings: &mut HashMap<i32, HashMap<i32, f32>>,
polygons: &mut Vec<PolygonList>, polygons: &mut Vec<PolygonList>,
surfaces: &mut HashMap<i32, u16>,
surface_materials: &mut Vec<u16>,
) { ) {
if polygons.is_empty() { if polygons.is_empty() {
return; return;
} }
let mut vertex_map = HashMap::<UniqueVertex, i32>::new(); for surface_id in surfaces.values().unique() {
let mut vertices = PackedVector3Array::new(); let mut vertex_map = HashMap::<UniqueVertex, i32>::new();
let mut uvs = PackedVector2Array::new(); let mut vertices = PackedVector3Array::new();
let mut weights = PackedFloat32Array::new(); let mut uvs = PackedVector2Array::new();
let mut indices = PackedInt32Array::new(); let mut weights = PackedFloat32Array::new();
let mut indices = PackedInt32Array::new();
for (id, poly) in polygons.iter_mut().enumerate() { for (id, poly) in polygons
let fan_v = poly.vert.remove(0) as i32; .iter_mut()
let fan = poly .enumerate()
.vert .filter(|(id, _)| surfaces.get(&(*id as i32)).unwrap_or(&0) == surface_id)
.windows(2) {
.flat_map(|w| [w[1] as i32, w[0] as i32, fan_v]); let fan_v = poly.vert.remove(0) as i32;
let fan = poly
.vert
.windows(2)
.flat_map(|w| [w[1] as i32, w[0] as i32, fan_v]);
for vert in fan { for vert in fan {
let uv = find_mapping(uv_mappings, id, vert); let uv = find_mapping(uv_mappings, id, vert);
// let weight = find_mapping(weight_mappings, id, vert); // let weight = find_mapping(weight_mappings, id, vert);
indices.push( indices.push(
*vertex_map *vertex_map
.entry(UniqueVertex { .entry(UniqueVertex {
vert, vert,
uv: [uv.x.to_ne_bytes(), uv.y.to_ne_bytes()], uv: [uv.x.to_ne_bytes(), uv.y.to_ne_bytes()],
weight: (0.0f32).to_ne_bytes(), weight: (0.0f32).to_ne_bytes(),
}) })
.or_insert_with(|| { .or_insert_with(|| {
vertices.push(*points.get(vert as usize).unwrap()); vertices.push(*points.get(vert as usize).unwrap());
uvs.push(uv); uvs.push(uv);
weights.push(0.0); weights.push(0.0);
vertices.len() as i32 - 1 vertices.len() as i32 - 1
}), }),
); );
}
} }
let mut surface = VariantArray::new();
surface.resize(ArrayType::ARRAY_MAX.ord() as usize);
surface.set(
ArrayType::ARRAY_VERTEX.ord() as usize,
vertices.to_variant(),
);
surface.set(ArrayType::ARRAY_TEX_UV.ord() as usize, uvs.to_variant());
/*TODO: surface.set(
ArrayType::ARRAY_WEIGHTS.ord() as usize,
weights.to_variant(),
);*/
surface.set(ArrayType::ARRAY_INDEX.ord() as usize, indices.to_variant());
if vertices.is_empty() {
continue;
}
mesh.add_surface_from_arrays(
PrimitiveType::PRIMITIVE_TRIANGLES,
surface,
Array::new(),
Dictionary::new(),
ArrayFormat::ARRAY_FORMAT_NORMAL,
);
surface_materials.push(*surface_id);
} }
let mut surface = VariantArray::new();
surface.resize(ArrayType::ARRAY_MAX.ord() as usize);
surface.set(
ArrayType::ARRAY_VERTEX.ord() as usize,
vertices.to_variant(),
);
surface.set(ArrayType::ARRAY_TEX_UV.ord() as usize, uvs.to_variant());
/*TODO: surface.set(
ArrayType::ARRAY_WEIGHTS.ord() as usize,
weights.to_variant(),
);*/
surface.set(ArrayType::ARRAY_INDEX.ord() as usize, indices.to_variant());
mesh.add_surface_from_arrays(
PrimitiveType::PRIMITIVE_TRIANGLES,
surface,
Array::new(),
Dictionary::new(),
ArrayFormat::ARRAY_FORMAT_NORMAL,
);
} }