From 7e01ff3440bae751817dae49c365f6b37fb94869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thea=20Sch=C3=B6bl?= Date: Mon, 29 May 2023 03:08:40 +0200 Subject: [PATCH] cleanup --- godot/starforce/test.tscn | 14 +- rust/mhgd/src/lwo/mapping.rs | 13 +- rust/mhgd/src/lwo/material.rs | 346 +++++++++++++++++++++++++--------- rust/mhgd/src/lwo/mod.rs | 4 +- rust/mhgd/src/lwo/object.rs | 147 ++++++++------- rust/mhgd/src/lwo/surface.rs | 238 ++++++++++++++++------- 6 files changed, 524 insertions(+), 238 deletions(-) diff --git a/godot/starforce/test.tscn b/godot/starforce/test.tscn index 2ea30f0..1453a1b 100644 --- a/godot/starforce/test.tscn +++ b/godot/starforce/test.tscn @@ -1,8 +1,14 @@ [gd_scene load_steps=2 format=3 uid="uid://kyw4wuusc33g"] -[ext_resource type="ArrayMesh" path="sar://D:/Moorhuhnkart/3dobjects_cars/moorhuhn.lwo" id="2_ab234"] +[sub_resource type="GDScript" id="GDScript_jhapx"] +resource_name = "load" +script/source = "@tool +extends Node3D + +func _ready(): + var scene = load(\"sar://D:/Moorhuhnkart/3dobjects_tracks/track07_ufo/boden.lwo\") + add_child(scene.instantiate()) +" [node name="test" type="Node3D"] - -[node name="MeshInstance3D" type="MeshInstance3D" parent="."] -mesh = ExtResource("2_ab234") +script = SubResource("GDScript_jhapx") diff --git a/rust/mhgd/src/lwo/mapping.rs b/rust/mhgd/src/lwo/mapping.rs index bf8db4a..4c06085 100644 --- a/rust/mhgd/src/lwo/mapping.rs +++ b/rust/mhgd/src/lwo/mapping.rs @@ -8,7 +8,7 @@ pub fn find_mapping( target: &HashMap>, poly: usize, vert: i32, -) -> T { +) -> Option { target .get(&(poly as i32)) .and_then(|mapping| mapping.get(&vert).copied()) @@ -17,17 +17,6 @@ pub fn find_mapping( .get(&-1) .and_then(|mapping| mapping.get(&vert).copied()) }) - .unwrap_or_else(|| { - godot_error!( - "Missing VX Mapping for {{vert: {}, poly: {}}}; {:?}", - vert, - poly, - target - .get(&(poly as i32)) - .map(|p| p.keys().collect::>()) - ); - T::default() - }) } pub fn collect_discontinuous_mappings( diff --git a/rust/mhgd/src/lwo/material.rs b/rust/mhgd/src/lwo/material.rs index 5eb1ca3..bd03d6b 100644 --- a/rust/mhgd/src/lwo/material.rs +++ b/rust/mhgd/src/lwo/material.rs @@ -1,11 +1,15 @@ -use godot::builtin::Color; -use godot::engine::base_material_3d::{DiffuseMode, TextureParam}; -use godot::engine::{Image, ImageTexture, StandardMaterial3D}; -use godot::log::{godot_error, godot_warn}; +use godot::builtin::{ + Basis, Color, EulerOrder, Quaternion, ToVariant, Transform3D, Variant, Vector3, +}; +use godot::engine::{load, Image, ImageTexture, ShaderMaterial}; +use godot::log::{godot_error, godot_print}; use godot::obj::{Gd, Share}; use lightwave_3d::lwo2::sub_tags::blocks::image_texture::{ ProjectionMode, SurfaceBlockImageTextureSubChunk, }; +use lightwave_3d::lwo2::sub_tags::blocks::texture_mapping::{ + CoordinateSystem, FalloffType, TextureMappingSubChunk, +}; use lightwave_3d::lwo2::sub_tags::blocks::{ SurfaceBlockHeaderSubChunk, SurfaceBlocks, TextureChannel, }; @@ -13,100 +17,274 @@ use lightwave_3d::lwo2::sub_tags::surface_parameters::SurfaceParameterSubChunk; use lightwave_3d::lwo2::tags::surface_definition::SurfaceDefinition; use std::collections::HashMap; -pub fn collect_material( - surface: SurfaceDefinition, - images: &HashMap>, -) -> Gd { - let mut material = StandardMaterial3D::new(); - material.set_name(surface.name.to_string().into()); - material.set_diffuse_mode(DiffuseMode::DIFFUSE_TOON); +pub struct MaterialUvInfo { + pub diffuse_channel: Option, + pub color_channel: Option, + pub material: Gd, +} - for attr in surface.attributes { - match attr { - SurfaceParameterSubChunk::Blocks(blocks) => { - if let SurfaceBlocks::ImageMapTexture { header, attributes } = blocks.data { - let mut texture = ImageTexture::new(); - let mut chan = TextureParam::TEXTURE_ALBEDO; - for attr in header.data.block_attributes { - match attr { - SurfaceBlockHeaderSubChunk::Channel(c) => { - chan = match c.data.texture_channel { - TextureChannel::Color => TextureParam::TEXTURE_ALBEDO, - TextureChannel::Diffuse => TextureParam::TEXTURE_ALBEDO, - TextureChannel::Bump => TextureParam::TEXTURE_HEIGHTMAP, - TextureChannel::RefractiveIndex => { - TextureParam::TEXTURE_REFRACTION - } - TextureChannel::Specular => TextureParam::TEXTURE_METALLIC, - TextureChannel::Glossy => TextureParam::TEXTURE_ROUGHNESS, - x => { - godot_error!("Invalid channel {:?}", x); - TextureParam::TEXTURE_ORM - } +impl MaterialUvInfo { + pub fn collect(surface: SurfaceDefinition, images: &HashMap>) -> Self { + let mut m = MaterialUvInfo { + diffuse_channel: None, + color_channel: None, + material: ShaderMaterial::new(), + }; + m.material.set_name(surface.name.to_string().into()); + m.material + .set_shader(load("res://starforce/starforce.gdshader")); + + for attr in surface.attributes { + match attr { + SurfaceParameterSubChunk::Blocks(blocks) => { + if let SurfaceBlocks::ImageMapTexture { header, attributes } = blocks.data { + let mut texture = ImageTexture::new(); + let mut chan = TextureChannel::Color; + let mut uv_channel = None; + let mut projection_mode = ProjectionMode::UV; + let mut mapping_info = Vec::<(&str, Variant)>::new(); + for attr in header.data.block_attributes { + match attr { + SurfaceBlockHeaderSubChunk::Channel(c) => { + chan = c.data.texture_channel; + } + SurfaceBlockHeaderSubChunk::Opacity(_) => { + // TODO; + } + SurfaceBlockHeaderSubChunk::EnabledState(_) => { + // TODO; + } + SurfaceBlockHeaderSubChunk::Negative(_) => { + // TODO; + } + x => { + godot_error!("Invalid surface header chunk {:?}", x) } - } - x => { - godot_error!("Invalid surface header chunk {:?}", x) } } - } - for attr in attributes { - match attr { - SurfaceBlockImageTextureSubChunk::ImageMap(r) => { - if let Some(i) = images.get(&r.texture_image) { - texture.set_image(i.share()); - } else { - godot_error!("Missing texture {:?}", r); - } - } - SurfaceBlockImageTextureSubChunk::ProjectionMode(projection) => { - match projection.data { - ProjectionMode::UV => {} - _ => { - godot_error!("TODO: Projection mode {:?}", projection) + for attr in attributes { + match attr { + SurfaceBlockImageTextureSubChunk::ImageMap(r) => { + if let Some(i) = images.get(&r.texture_image) { + texture.set_image(i.share()); + } else { + godot_error!("Missing texture {:?}", r); } } - } - SurfaceBlockImageTextureSubChunk::UvVertexMap(map) => { - godot_error!( - "TODO: UV maps (this one is using '{}')", - map.txuv_map_name - ) - } - x => { - godot_error!("TODO: Image texture chunk {:?}", x) + SurfaceBlockImageTextureSubChunk::ProjectionMode(projection) => { + projection_mode = projection.data; + } + SurfaceBlockImageTextureSubChunk::UvVertexMap(map) => { + uv_channel = Some(map.txuv_map_name.to_string()); + } + SurfaceBlockImageTextureSubChunk::TextureMapping(mapping) => { + let mut pos = Vector3::default(); + let mut rot = Vector3::default(); + let mut size = Vector3::default(); + for mapping_param in mapping.data.attributes { + match mapping_param { + TextureMappingSubChunk::Center(it) => { + pos = Vector3 { + x: it.base_color[0], + y: it.base_color[1], + z: it.base_color[2], + }; + } + TextureMappingSubChunk::Rotation(it) => { + rot = Vector3 { + x: it.base_color[0], + y: it.base_color[1], + z: it.base_color[2], + } + .normalized(); + } + TextureMappingSubChunk::Size(it) => { + size = Vector3 { + x: it.base_color[0], + y: it.base_color[1], + z: it.base_color[2], + }; + } + TextureMappingSubChunk::Falloff(it) => { + mapping_info.push(( + "falloff", + Vector3 { + x: it.vector[0], + y: it.vector[1], + z: it.vector[2], + } + .to_variant(), + )); + mapping_info.push(( + "falloff_type", + match it.kind { + FalloffType::Cubic => 0, + FalloffType::Spherical => 1, + FalloffType::LinearX => 2, + FalloffType::LinearY => 3, + FalloffType::LinearZ => 4, + } + .to_variant(), + )); + } + TextureMappingSubChunk::CoordinateSystem(it) => { + mapping_info.push(( + "world_coords", + matches!( + it.data, + CoordinateSystem::WorldCoordinates + ) + .to_variant(), + )); + } + TextureMappingSubChunk::ReferenceObject(it) => { + if !matches!( + it.object_name.to_string().as_str(), + "" | "(none)" + ) { + godot_error!("Reference object '{}': not supported for texture mapping", it.object_name) + } + } + } + } + + mapping_info.push(( + "transform", + Transform3D { + basis: Basis::from_euler(EulerOrder::XYZ, rot) + .scaled(size), + origin: pos, + } + .to_variant(), + )); + } + SurfaceBlockImageTextureSubChunk::MajorAxis(_) => { + // TODO; + } + SurfaceBlockImageTextureSubChunk::ImageWrapOptions(_) => { + // TODO; + } + SurfaceBlockImageTextureSubChunk::ImageWrapAmountWidth(_) => { + // TODO; + } + SurfaceBlockImageTextureSubChunk::ImageWrapAmountHeight(_) => { + // TODO; + } + SurfaceBlockImageTextureSubChunk::AntialiasingStrength(_) => { + // TODO; + } + SurfaceBlockImageTextureSubChunk::PixelBlending(_) => { + // TODO; + } + x => { + godot_error!("TODO: Image texture chunk {:?}", x) + } } } + godot_print!("TX: {:?} @ UV{:?}", chan, uv_channel); + let channel_name = match chan { + TextureChannel::Color => "color", + TextureChannel::Diffuse => "diffuse", + TextureChannel::Luminosity => "luminosity", + TextureChannel::Specular => "specular", + TextureChannel::Glossy => "glossy", + TextureChannel::Reflectivity => "reflectivity", + TextureChannel::Transparency => "transparency", + TextureChannel::RefractiveIndex => "refractive_index", + TextureChannel::Translucency => "translucency", + TextureChannel::Bump => "bump", + }; + m.material.set_shader_parameter( + format!("tex_{}_projection", channel_name).into(), + match projection_mode { + ProjectionMode::Planar => 0, + ProjectionMode::Cylindrical => 1, + ProjectionMode::Spherical => 2, + ProjectionMode::Cubic => 3, + ProjectionMode::FrontProjection => 4, + ProjectionMode::UV => 5, + } + .to_variant(), + ); + m.material.set_shader_parameter( + format!("tex_{}", channel_name).into(), + texture.to_variant(), + ); + + for (name, value) in mapping_info { + m.material.set_shader_parameter( + format!("tex_{}_projection_{}", channel_name, name).into(), + value, + ); + } + + match chan { + TextureChannel::Diffuse => m.diffuse_channel = uv_channel, + TextureChannel::Color => m.color_channel = uv_channel, + _ => (), + } } - material.set_texture(chan, texture.upcast()); } - } - SurfaceParameterSubChunk::BaseColor(base_color) => material.set_albedo(Color { - r: base_color.base_color[0], - g: base_color.base_color[1], - b: base_color.base_color[2], - a: 1.0, - }), - SurfaceParameterSubChunk::BaseShadingValueDiffuse(base_diffuse) => { - if base_diffuse.envelope != 0 || base_diffuse.value != 1.0 { - godot_error!( - "TODO: Diffuse {{envelope: {}, value: {}}}", - base_diffuse.envelope, - base_diffuse.value + SurfaceParameterSubChunk::BaseColor(base_color) => m.material.set_shader_parameter( + "color".into(), + Color { + r: base_color.base_color[0], + g: base_color.base_color[1], + b: base_color.base_color[2], + a: 1.0, + } + .to_variant(), + ), + SurfaceParameterSubChunk::BaseShadingValueDiffuse(it) => { + m.material + .set_shader_parameter("diffuse".into(), it.value.to_variant()); + m.material + .set_shader_parameter("diffuse_envelope".into(), it.envelope.to_variant()); + } + SurfaceParameterSubChunk::BaseShadingValueSpecular(it) => { + m.material + .set_shader_parameter("specular".into(), it.value.to_variant()); + m.material + .set_shader_parameter("specular_envelope".into(), it.envelope.to_variant()); + } + SurfaceParameterSubChunk::BaseShadingValueLuminosity(it) => { + m.material + .set_shader_parameter("luminosity".into(), it.value.to_variant()); + m.material.set_shader_parameter( + "luminosity_envelope".into(), + it.envelope.to_variant(), ); } - } - 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); + SurfaceParameterSubChunk::BaseShadingValueReflectivity(it) => { + m.material + .set_shader_parameter("reflectivity".into(), it.value.to_variant()); + m.material.set_shader_parameter( + "reflectivity_envelope".into(), + it.envelope.to_variant(), + ); + } + SurfaceParameterSubChunk::BaseShadingValueTranslucency(it) => { + m.material + .set_shader_parameter("translucency".into(), it.value.to_variant()); + m.material.set_shader_parameter( + "translucency_envelope".into(), + it.envelope.to_variant(), + ); + } + SurfaceParameterSubChunk::BaseShadingValueTransparency(it) => { + m.material + .set_shader_parameter("transparency".into(), it.value.to_variant()); + m.material.set_shader_parameter( + "transparency_envelope".into(), + it.envelope.to_variant(), + ); + } + x => { + godot_error!("TODO: Surface Chunk {:?}", x) } } - x => { - godot_error!("TODO: Surface Chunk {:?}", x) - } } - } - material + m + } } diff --git a/rust/mhgd/src/lwo/mod.rs b/rust/mhgd/src/lwo/mod.rs index afa2fdc..a0b3f47 100644 --- a/rust/mhgd/src/lwo/mod.rs +++ b/rust/mhgd/src/lwo/mod.rs @@ -1,7 +1,7 @@ use crate::lwo::object::lightwave_to_gd; use godot::bind::{godot_api, GodotClass}; use godot::builtin::GodotString; -use godot::engine::ArrayMesh; +use godot::engine::{ArrayMesh, PackedScene}; use godot::obj::Gd; use lightwave_3d::LightWaveObject; @@ -18,7 +18,7 @@ struct Lwo {} #[godot_api] impl Lwo { #[func] - pub fn get_mesh(path: GodotString) -> Gd { + pub fn get_mesh(path: GodotString) -> Gd { lightwave_to_gd(LightWaveObject::read_file(path.to_string()).unwrap()) } } diff --git a/rust/mhgd/src/lwo/object.rs b/rust/mhgd/src/lwo/object.rs index 9ee8731..15aedc6 100644 --- a/rust/mhgd/src/lwo/object.rs +++ b/rust/mhgd/src/lwo/object.rs @@ -1,46 +1,38 @@ use crate::lwo::clips::collect_clip; use crate::lwo::mapping::{collect_discontinuous_mappings, collect_mappings}; -use crate::lwo::material::collect_material; -use crate::lwo::surface::try_commit; -use godot::builtin::{Array, Dictionary, Vector2, Vector3}; -use godot::engine::mesh::{ArrayFormat, PrimitiveType}; -use godot::engine::{ArrayMesh, Image, SurfaceTool}; -use godot::log::{godot_error, godot_warn}; +use crate::lwo::material::MaterialUvInfo; +use crate::lwo::surface::IntermediateLayer; +use godot::builtin::{Vector2, Vector3}; +use godot::engine::node::InternalMode; +use godot::engine::{Image, MeshInstance3D, Node3D, PackedScene}; +use godot::log::{godot_error, godot_print, godot_warn}; use godot::obj::{Gd, Share}; -use lightwave_3d::lwo2::tags::polygon_list::PolygonList; use lightwave_3d::lwo2::tags::Tag; use lightwave_3d::LightWaveObject; -use std::collections::{HashMap, HashSet}; - -pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd { - let mut mesh = ArrayMesh::new(); +use std::collections::HashMap; +pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd { let mut materials = vec![]; let mut images = HashMap::>::new(); - - let mut points = Vec::::new(); - let mut uv_mappings = HashMap::>::new(); - let mut weight_mappings = HashMap::>::new(); - let mut polygons = Vec::::new(); - let mut surfaces = HashMap::::new(); - - let mut surface_materials = Vec::::new(); + let mut layers = vec![]; for tag in lightwave.data { match tag { - Tag::Layer(layer) => { - try_commit( - &mut mesh, - &mut points, - &mut uv_mappings, - &mut weight_mappings, - &mut polygons, - &mut surfaces, - &mut surface_materials, - ); + Tag::Layer(layer_tag) => { + layers.push(IntermediateLayer { + name: layer_tag.name.to_string(), + parent: layer_tag.parent, + id: layer_tag.number, + pivot: Vector3 { + x: layer_tag.pivot[0], + y: layer_tag.pivot[1], + z: layer_tag.pivot[2], + }, + ..IntermediateLayer::default() + }); } Tag::PointList(points_chunk) => { - points = points_chunk + layers.last_mut().unwrap().points = points_chunk .data .point_location .into_iter() @@ -54,12 +46,28 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd { Tag::DiscontinuousVertexMapping(vmad) => match &vmad.kind { b"TXUV" => { debug_assert!(vmad.data.mappings[0].values.len() == 2); - collect_discontinuous_mappings(&mut uv_mappings, vmad, |uv| Vector2 { + let name = vmad.name.to_string(); + + let layer = layers.last_mut().unwrap(); + let map = if let Some(mappings) = + layer.uv_mappings.iter_mut().find(|(n, _)| n == &name) + { + mappings + } else { + layer.uv_mappings.push((name, HashMap::new())); + layer.uv_mappings.last_mut().unwrap() + }; + + collect_discontinuous_mappings(&mut map.1, vmad, |uv| Vector2 { x: uv[0], y: uv[1], - }) + }); } - b"WGHT" => collect_discontinuous_mappings(&mut weight_mappings, vmad, |it| it[0]), + b"WGHT" => collect_discontinuous_mappings( + &mut layers.last_mut().unwrap().weight_mappings, + vmad, + |it| it[0], + ), x => godot_error!( "Not Implemented: Discontinuous Vertex Mapping: {}", String::from_utf8(x.to_vec()).unwrap() @@ -68,9 +76,25 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd { Tag::VertexMapping(vmap) => match &vmap.kind { b"TXUV" => { debug_assert!(vmap.data.mapping[0].value.len() == 2); - collect_mappings(&mut uv_mappings, vmap, |uv| Vector2 { x: uv[0], y: uv[1] }) + let name = vmap.name.to_string(); + + let layer = layers.last_mut().unwrap(); + let map = if let Some(mappings) = + layer.uv_mappings.iter_mut().find(|(n, _)| n == &name) + { + mappings + } else { + layer.uv_mappings.push((name, HashMap::new())); + layer.uv_mappings.last_mut().unwrap() + }; + + collect_mappings(&mut map.1, vmap, |uv| Vector2 { x: uv[0], y: uv[1] }); } - b"WGHT" => collect_mappings(&mut weight_mappings, vmap, |it| it[0]), + b"WGHT" => collect_mappings( + &mut layers.last_mut().unwrap().weight_mappings, + vmap, + |it| it[0], + ), x => godot_error!( "Not Implemented: Vertex Mapping: {}", String::from_utf8(x.to_vec()).unwrap() @@ -82,7 +106,11 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd { },*/ b"SURF" => { for surf in ptag.data.mappings { - surfaces.insert(surf.poly as i32, surf.tag); + layers + .last_mut() + .unwrap() + .surfaces + .insert(surf.poly as i32, surf.tag); } } x => godot_warn!( @@ -92,14 +120,14 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd { }, Tag::PolygonList(polygon_lists) => match &polygon_lists.kind { b"FACE" => { - polygons = polygon_lists.data.polygons; + layers.last_mut().unwrap().polygons = polygon_lists.data.polygons; } x => godot_warn!("{}", String::from_utf8(x.to_vec()).unwrap()), }, Tag::ImageClip(clip) => collect_clip(&mut images, clip.data), Tag::SurfaceDefinition(surf) => { - let mat = collect_material(surf.data, &images); - materials.push(mat); + godot_print!("Def: '{}' -> '{}'", surf.source, surf.name); + materials.push(MaterialUvInfo::collect(surf.data, &images)); } Tag::BoundingBox(_) => (), x => { @@ -108,34 +136,23 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd { } } - try_commit( - &mut mesh, - &mut points, - &mut uv_mappings, - &mut weight_mappings, - &mut polygons, - &mut surfaces, - &mut surface_materials, - ); - let mut out_mesh = ArrayMesh::new(); - for i in 0..mesh.get_surface_count() { - let mut tool = SurfaceTool::new(); + let mut root_node = Node3D::new_alloc(); - tool.create_from(mesh.share().upcast(), i); - tool.generate_normals(false); - tool.generate_tangents(); + for layer in layers { + let mut instance = MeshInstance3D::new_alloc(); + instance.set_name(layer.name.clone().into()); + instance.set_mesh(layer.commit(&mut materials).upcast()); - out_mesh.add_surface_from_arrays( - PrimitiveType::PRIMITIVE_TRIANGLES, - tool.commit_to_arrays(), - Array::new(), - Dictionary::new(), - ArrayFormat::ARRAY_FORMAT_NORMAL, + root_node.add_child( + instance.share().upcast(), + false, + InternalMode::INTERNAL_MODE_DISABLED, ); - - if let Some(mat) = materials.get(surfaces[&(i as i32)] as usize) { - out_mesh.surface_set_material(i, mat.share().upcast()) - } + instance.set_owner(root_node.share().upcast()); } - out_mesh + + let mut scene = PackedScene::new(); + scene.pack(root_node.share().upcast()); + root_node.queue_free(); + scene } diff --git a/rust/mhgd/src/lwo/surface.rs b/rust/mhgd/src/lwo/surface.rs index 50d8484..3b576cf 100644 --- a/rust/mhgd/src/lwo/surface.rs +++ b/rust/mhgd/src/lwo/surface.rs @@ -1,11 +1,13 @@ use crate::lwo::mapping::find_mapping; +use crate::lwo::material::MaterialUvInfo; use godot::builtin::{ Array, Dictionary, PackedFloat32Array, PackedInt32Array, PackedVector2Array, PackedVector3Array, ToVariant, VariantArray, Vector2, Vector3, }; use godot::engine::mesh::{ArrayFormat, ArrayType, PrimitiveType}; -use godot::engine::ArrayMesh; -use godot::obj::EngineEnum; +use godot::engine::{ArrayMesh, SurfaceTool}; +use godot::obj::{EngineEnum, Gd, Share}; +use godot::prelude::godot_error; use itertools::Itertools; use lightwave_3d::lwo2::tags::polygon_list::PolygonList; use std::collections::HashMap; @@ -13,85 +15,179 @@ use std::collections::HashMap; #[derive(Hash, Eq, PartialEq)] struct UniqueVertex { vert: i32, - uv: [[u8; 4]; 2], + uv: Vec>, weight: [u8; 4], } -pub fn try_commit( - mesh: &mut ArrayMesh, - points: &mut [Vector3], - uv_mappings: &mut HashMap>, - weight_mappings: &mut HashMap>, - polygons: &mut Vec, - surfaces: &mut HashMap, - surface_materials: &mut Vec, -) { - if polygons.is_empty() { - return; - } +pub type SurfaceMapping = HashMap>; - for surface_id in surfaces.values().unique() { - let mut vertex_map = HashMap::::new(); - let mut vertices = PackedVector3Array::new(); - let mut uvs = PackedVector2Array::new(); - let mut weights = PackedFloat32Array::new(); - let mut indices = PackedInt32Array::new(); +#[derive(Default)] +pub struct IntermediateLayer { + pub name: String, + pub pivot: Vector3, + pub parent: Option, + pub id: u16, + pub points: Vec, + pub uv_mappings: Vec<(String, SurfaceMapping)>, + pub weight_mappings: SurfaceMapping, + pub polygons: Vec, + pub surfaces: HashMap, +} - for (id, poly) in polygons - .iter_mut() - .enumerate() - .filter(|(id, _)| surfaces.get(&(*id as i32)).unwrap_or(&0) == surface_id) - { - 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]); +impl IntermediateLayer { + pub fn commit(mut self, materials: &[MaterialUvInfo]) -> Gd { + let mut mesh = ArrayMesh::new(); + mesh.set_name(self.name.clone().into()); + let mut surface_materials = Vec::::new(); - for vert in fan { - let uv = find_mapping(uv_mappings, id, vert); - // let weight = find_mapping(weight_mappings, id, vert); + self.uv_mappings.sort_by(|a, b| a.0.cmp(&b.0)); - indices.push( - *vertex_map - .entry(UniqueVertex { - vert, - uv: [uv.x.to_ne_bytes(), uv.y.to_ne_bytes()], - weight: (0.0f32).to_ne_bytes(), + for surface_id in self.surfaces.values().unique() { + let material = &materials[*surface_id as usize]; + let material_uv_names = [ + material.diffuse_channel.as_ref(), + material.color_channel.as_ref(), + ]; + let mut material_incomplete = material_uv_names.iter().map(|_| 0).collect_vec(); + let materials_subset = material_uv_names + .iter() + .map(|it| it.and_then(|it| self.uv_mappings.iter().find(|(name, _)| name == it))) + .collect_vec(); + + let mut vertex_map = HashMap::::new(); + let mut vertices = PackedVector3Array::new(); + let mut uvs = materials_subset + .iter() + .map(|it| it.map(|_| PackedVector2Array::new())) + .collect_vec(); + let mut weights = PackedFloat32Array::new(); + let mut indices = PackedInt32Array::new(); + + for (id, poly) in self + .polygons + .iter_mut() + .enumerate() + .filter(|(id, _)| self.surfaces.get(&(*id as i32)).unwrap_or(&0) == surface_id) + { + 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 { + let uv = materials_subset + .iter() + .map(|it| { + it.and_then(|(name, it)| { + find_mapping(it, id, vert).map(|it| (name, it)) + }) }) - .or_insert_with(|| { - vertices.push(*points.get(vert as usize).unwrap()); - uvs.push(uv); - weights.push(0.0); - vertices.len() as i32 - 1 - }), - ); + .collect_vec(); + // let weight = find_mapping(weight_mappings, id, vert); + + indices.push( + *vertex_map + .entry(UniqueVertex { + vert, + uv: uv + .iter() + .map(|it| { + it.map(|(_, uv)| [uv.x.to_ne_bytes(), uv.y.to_ne_bytes()]) + }) + .collect(), + weight: (0.0f32).to_ne_bytes(), + }) + .or_insert_with(|| { + vertices.push(*self.points.get(vert as usize).unwrap()); + for (i, uv) in uv.iter().enumerate() { + if let Some(uvs) = &mut uvs[i] { + uvs.push(uv.map(|(_, it)| it).unwrap_or_else(|| { + material_incomplete[i] += 1; + Vector2::ZERO + })); + } + } + weights.push(0.0); + 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(), + ); + for (i, uv) in uvs.iter().enumerate() { + if let Some(uv) = uv { + surface.set( + match i { + 0 => ArrayType::ARRAY_TEX_UV, + 1 => ArrayType::ARRAY_TEX_UV2, + _ => todo!(), + } + .ord() as usize, + uv.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()); + + for (i, inc) in material_incomplete.into_iter().enumerate() { + if inc != 0 { + godot_error!( + "{}:{}'s UV map '{}' has {} ({}%) incomplete UVs", + self.name, + surface_id, + material_uv_names[i].unwrap(), + inc, + (inc as f32 / vertices.len() as f32) * 100.0 + ); + } + } + + 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 out_mesh = ArrayMesh::new(); + out_mesh.set_name(self.name.into()); + for i in 0..mesh.get_surface_count() { + let mut tool = SurfaceTool::new(); + + tool.create_from(mesh.share().upcast(), i); + tool.generate_normals(false); + tool.generate_tangents(); + + out_mesh.add_surface_from_arrays( + PrimitiveType::PRIMITIVE_TRIANGLES, + tool.commit_to_arrays(), + Array::new(), + Dictionary::new(), + ArrayFormat::ARRAY_FORMAT_NORMAL, + ); + + if let Some(mat) = materials.get(self.surfaces[&(i as i32)] as usize) { + out_mesh.surface_set_material(i, mat.material.share().upcast()) } } - 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); + out_mesh } }