This commit is contained in:
2023-05-29 03:08:40 +02:00
parent 7f5d874be9
commit 7e01ff3440
6 changed files with 524 additions and 238 deletions

View File

@@ -1,8 +1,14 @@
[gd_scene load_steps=2 format=3 uid="uid://kyw4wuusc33g"] [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="test" type="Node3D"]
script = SubResource("GDScript_jhapx")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
mesh = ExtResource("2_ab234")

View File

@@ -8,7 +8,7 @@ pub fn find_mapping<T: Default + Copy + std::fmt::Debug>(
target: &HashMap<i32, HashMap<i32, T>>, target: &HashMap<i32, HashMap<i32, T>>,
poly: usize, poly: usize,
vert: i32, vert: i32,
) -> T { ) -> Option<T> {
target target
.get(&(poly as i32)) .get(&(poly as i32))
.and_then(|mapping| mapping.get(&vert).copied()) .and_then(|mapping| mapping.get(&vert).copied())
@@ -17,17 +17,6 @@ pub fn find_mapping<T: Default + Copy + std::fmt::Debug>(
.get(&-1) .get(&-1)
.and_then(|mapping| mapping.get(&vert).copied()) .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::<Vec<&i32>>())
);
T::default()
})
} }
pub fn collect_discontinuous_mappings<T>( pub fn collect_discontinuous_mappings<T>(

View File

@@ -1,11 +1,15 @@
use godot::builtin::Color; use godot::builtin::{
use godot::engine::base_material_3d::{DiffuseMode, TextureParam}; Basis, Color, EulerOrder, Quaternion, ToVariant, Transform3D, Variant, Vector3,
use godot::engine::{Image, ImageTexture, StandardMaterial3D}; };
use godot::log::{godot_error, godot_warn}; use godot::engine::{load, Image, ImageTexture, ShaderMaterial};
use godot::log::{godot_error, godot_print};
use godot::obj::{Gd, Share}; use godot::obj::{Gd, Share};
use lightwave_3d::lwo2::sub_tags::blocks::image_texture::{ use lightwave_3d::lwo2::sub_tags::blocks::image_texture::{
ProjectionMode, SurfaceBlockImageTextureSubChunk, ProjectionMode, SurfaceBlockImageTextureSubChunk,
}; };
use lightwave_3d::lwo2::sub_tags::blocks::texture_mapping::{
CoordinateSystem, FalloffType, TextureMappingSubChunk,
};
use lightwave_3d::lwo2::sub_tags::blocks::{ use lightwave_3d::lwo2::sub_tags::blocks::{
SurfaceBlockHeaderSubChunk, SurfaceBlocks, TextureChannel, 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 lightwave_3d::lwo2::tags::surface_definition::SurfaceDefinition;
use std::collections::HashMap; use std::collections::HashMap;
pub fn collect_material( pub struct MaterialUvInfo {
surface: SurfaceDefinition, pub diffuse_channel: Option<String>,
images: &HashMap<u32, Gd<Image>>, pub color_channel: Option<String>,
) -> Gd<StandardMaterial3D> { pub material: Gd<ShaderMaterial>,
let mut material = StandardMaterial3D::new(); }
material.set_name(surface.name.to_string().into());
material.set_diffuse_mode(DiffuseMode::DIFFUSE_TOON);
for attr in surface.attributes { impl MaterialUvInfo {
match attr { pub fn collect(surface: SurfaceDefinition, images: &HashMap<u32, Gd<Image>>) -> Self {
SurfaceParameterSubChunk::Blocks(blocks) => { let mut m = MaterialUvInfo {
if let SurfaceBlocks::ImageMapTexture { header, attributes } = blocks.data { diffuse_channel: None,
let mut texture = ImageTexture::new(); color_channel: None,
let mut chan = TextureParam::TEXTURE_ALBEDO; material: ShaderMaterial::new(),
for attr in header.data.block_attributes { };
match attr { m.material.set_name(surface.name.to_string().into());
SurfaceBlockHeaderSubChunk::Channel(c) => { m.material
chan = match c.data.texture_channel { .set_shader(load("res://starforce/starforce.gdshader"));
TextureChannel::Color => TextureParam::TEXTURE_ALBEDO,
TextureChannel::Diffuse => TextureParam::TEXTURE_ALBEDO, for attr in surface.attributes {
TextureChannel::Bump => TextureParam::TEXTURE_HEIGHTMAP, match attr {
TextureChannel::RefractiveIndex => { SurfaceParameterSubChunk::Blocks(blocks) => {
TextureParam::TEXTURE_REFRACTION if let SurfaceBlocks::ImageMapTexture { header, attributes } = blocks.data {
} let mut texture = ImageTexture::new();
TextureChannel::Specular => TextureParam::TEXTURE_METALLIC, let mut chan = TextureChannel::Color;
TextureChannel::Glossy => TextureParam::TEXTURE_ROUGHNESS, let mut uv_channel = None;
x => { let mut projection_mode = ProjectionMode::UV;
godot_error!("Invalid channel {:?}", x); let mut mapping_info = Vec::<(&str, Variant)>::new();
TextureParam::TEXTURE_ORM 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 {
for attr in attributes { match attr {
match attr { SurfaceBlockImageTextureSubChunk::ImageMap(r) => {
SurfaceBlockImageTextureSubChunk::ImageMap(r) => { if let Some(i) = images.get(&r.texture_image) {
if let Some(i) = images.get(&r.texture_image) { texture.set_image(i.share());
texture.set_image(i.share()); } else {
} else { godot_error!("Missing texture {:?}", r);
godot_error!("Missing texture {:?}", r);
}
}
SurfaceBlockImageTextureSubChunk::ProjectionMode(projection) => {
match projection.data {
ProjectionMode::UV => {}
_ => {
godot_error!("TODO: Projection mode {:?}", projection)
} }
} }
} SurfaceBlockImageTextureSubChunk::ProjectionMode(projection) => {
SurfaceBlockImageTextureSubChunk::UvVertexMap(map) => { projection_mode = projection.data;
godot_error!( }
"TODO: UV maps (this one is using '{}')", SurfaceBlockImageTextureSubChunk::UvVertexMap(map) => {
map.txuv_map_name uv_channel = Some(map.txuv_map_name.to_string());
) }
} SurfaceBlockImageTextureSubChunk::TextureMapping(mapping) => {
x => { let mut pos = Vector3::default();
godot_error!("TODO: Image texture chunk {:?}", x) 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) => m.material.set_shader_parameter(
SurfaceParameterSubChunk::BaseColor(base_color) => material.set_albedo(Color { "color".into(),
r: base_color.base_color[0], Color {
g: base_color.base_color[1], r: base_color.base_color[0],
b: base_color.base_color[2], g: base_color.base_color[1],
a: 1.0, b: base_color.base_color[2],
}), a: 1.0,
SurfaceParameterSubChunk::BaseShadingValueDiffuse(base_diffuse) => { }
if base_diffuse.envelope != 0 || base_diffuse.value != 1.0 { .to_variant(),
godot_error!( ),
"TODO: Diffuse {{envelope: {}, value: {}}}", SurfaceParameterSubChunk::BaseShadingValueDiffuse(it) => {
base_diffuse.envelope, m.material
base_diffuse.value .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::BaseShadingValueReflectivity(it) => {
SurfaceParameterSubChunk::BaseShadingValueSpecular(base_specular) => { m.material
material.set_specular(base_specular.value as f64); .set_shader_parameter("reflectivity".into(), it.value.to_variant());
if base_specular.envelope != 0 { m.material.set_shader_parameter(
godot_error!("TODO: Specular envelope {}", base_specular.envelope); "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
}
} }

View File

@@ -1,7 +1,7 @@
use crate::lwo::object::lightwave_to_gd; use crate::lwo::object::lightwave_to_gd;
use godot::bind::{godot_api, GodotClass}; use godot::bind::{godot_api, GodotClass};
use godot::builtin::GodotString; use godot::builtin::GodotString;
use godot::engine::ArrayMesh; use godot::engine::{ArrayMesh, PackedScene};
use godot::obj::Gd; use godot::obj::Gd;
use lightwave_3d::LightWaveObject; use lightwave_3d::LightWaveObject;
@@ -18,7 +18,7 @@ struct Lwo {}
#[godot_api] #[godot_api]
impl Lwo { impl Lwo {
#[func] #[func]
pub fn get_mesh(path: GodotString) -> Gd<ArrayMesh> { pub fn get_mesh(path: GodotString) -> Gd<PackedScene> {
lightwave_to_gd(LightWaveObject::read_file(path.to_string()).unwrap()) lightwave_to_gd(LightWaveObject::read_file(path.to_string()).unwrap())
} }
} }

View File

@@ -1,46 +1,38 @@
use crate::lwo::clips::collect_clip; 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::MaterialUvInfo;
use crate::lwo::surface::try_commit; use crate::lwo::surface::IntermediateLayer;
use godot::builtin::{Array, Dictionary, Vector2, Vector3}; use godot::builtin::{Vector2, Vector3};
use godot::engine::mesh::{ArrayFormat, PrimitiveType}; use godot::engine::node::InternalMode;
use godot::engine::{ArrayMesh, Image, SurfaceTool}; use godot::engine::{Image, MeshInstance3D, Node3D, PackedScene};
use godot::log::{godot_error, godot_warn}; use godot::log::{godot_error, godot_print, godot_warn};
use godot::obj::{Gd, Share}; use godot::obj::{Gd, Share};
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;
use std::collections::{HashMap, HashSet}; use std::collections::HashMap;
pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<ArrayMesh> {
let mut mesh = ArrayMesh::new();
pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<PackedScene> {
let mut materials = vec![]; let mut materials = vec![];
let mut images = HashMap::<u32, Gd<Image>>::new(); let mut images = HashMap::<u32, Gd<Image>>::new();
let mut layers = vec![];
let mut points = Vec::<Vector3>::new();
let mut uv_mappings = HashMap::<i32, HashMap<i32, Vector2>>::new();
let mut weight_mappings = HashMap::<i32, HashMap<i32, f32>>::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 {
Tag::Layer(layer) => { Tag::Layer(layer_tag) => {
try_commit( layers.push(IntermediateLayer {
&mut mesh, name: layer_tag.name.to_string(),
&mut points, parent: layer_tag.parent,
&mut uv_mappings, id: layer_tag.number,
&mut weight_mappings, pivot: Vector3 {
&mut polygons, x: layer_tag.pivot[0],
&mut surfaces, y: layer_tag.pivot[1],
&mut surface_materials, z: layer_tag.pivot[2],
); },
..IntermediateLayer::default()
});
} }
Tag::PointList(points_chunk) => { Tag::PointList(points_chunk) => {
points = points_chunk layers.last_mut().unwrap().points = points_chunk
.data .data
.point_location .point_location
.into_iter() .into_iter()
@@ -54,12 +46,28 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<ArrayMesh> {
Tag::DiscontinuousVertexMapping(vmad) => match &vmad.kind { Tag::DiscontinuousVertexMapping(vmad) => match &vmad.kind {
b"TXUV" => { b"TXUV" => {
debug_assert!(vmad.data.mappings[0].values.len() == 2); 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], x: uv[0],
y: uv[1], 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!( x => godot_error!(
"Not Implemented: Discontinuous Vertex Mapping: {}", "Not Implemented: Discontinuous Vertex Mapping: {}",
String::from_utf8(x.to_vec()).unwrap() String::from_utf8(x.to_vec()).unwrap()
@@ -68,9 +76,25 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<ArrayMesh> {
Tag::VertexMapping(vmap) => match &vmap.kind { Tag::VertexMapping(vmap) => match &vmap.kind {
b"TXUV" => { b"TXUV" => {
debug_assert!(vmap.data.mapping[0].value.len() == 2); 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!( x => godot_error!(
"Not Implemented: Vertex Mapping: {}", "Not Implemented: Vertex Mapping: {}",
String::from_utf8(x.to_vec()).unwrap() String::from_utf8(x.to_vec()).unwrap()
@@ -82,7 +106,11 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<ArrayMesh> {
},*/ },*/
b"SURF" => { b"SURF" => {
for surf in ptag.data.mappings { 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!( x => godot_warn!(
@@ -92,14 +120,14 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<ArrayMesh> {
}, },
Tag::PolygonList(polygon_lists) => match &polygon_lists.kind { Tag::PolygonList(polygon_lists) => match &polygon_lists.kind {
b"FACE" => { 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()), x => godot_warn!("{}", String::from_utf8(x.to_vec()).unwrap()),
}, },
Tag::ImageClip(clip) => collect_clip(&mut images, clip.data), Tag::ImageClip(clip) => collect_clip(&mut images, clip.data),
Tag::SurfaceDefinition(surf) => { Tag::SurfaceDefinition(surf) => {
let mat = collect_material(surf.data, &images); godot_print!("Def: '{}' -> '{}'", surf.source, surf.name);
materials.push(mat); materials.push(MaterialUvInfo::collect(surf.data, &images));
} }
Tag::BoundingBox(_) => (), Tag::BoundingBox(_) => (),
x => { x => {
@@ -108,34 +136,23 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<ArrayMesh> {
} }
} }
try_commit( let mut root_node = Node3D::new_alloc();
&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();
tool.create_from(mesh.share().upcast(), i); for layer in layers {
tool.generate_normals(false); let mut instance = MeshInstance3D::new_alloc();
tool.generate_tangents(); instance.set_name(layer.name.clone().into());
instance.set_mesh(layer.commit(&mut materials).upcast());
out_mesh.add_surface_from_arrays( root_node.add_child(
PrimitiveType::PRIMITIVE_TRIANGLES, instance.share().upcast(),
tool.commit_to_arrays(), false,
Array::new(), InternalMode::INTERNAL_MODE_DISABLED,
Dictionary::new(),
ArrayFormat::ARRAY_FORMAT_NORMAL,
); );
instance.set_owner(root_node.share().upcast());
if let Some(mat) = materials.get(surfaces[&(i as i32)] as usize) {
out_mesh.surface_set_material(i, mat.share().upcast())
}
} }
out_mesh
let mut scene = PackedScene::new();
scene.pack(root_node.share().upcast());
root_node.queue_free();
scene
} }

View File

@@ -1,11 +1,13 @@
use crate::lwo::mapping::find_mapping; use crate::lwo::mapping::find_mapping;
use crate::lwo::material::MaterialUvInfo;
use godot::builtin::{ use godot::builtin::{
Array, Dictionary, PackedFloat32Array, PackedInt32Array, PackedVector2Array, Array, Dictionary, PackedFloat32Array, PackedInt32Array, PackedVector2Array,
PackedVector3Array, ToVariant, VariantArray, Vector2, Vector3, PackedVector3Array, ToVariant, VariantArray, Vector2, Vector3,
}; };
use godot::engine::mesh::{ArrayFormat, ArrayType, PrimitiveType}; use godot::engine::mesh::{ArrayFormat, ArrayType, PrimitiveType};
use godot::engine::ArrayMesh; use godot::engine::{ArrayMesh, SurfaceTool};
use godot::obj::EngineEnum; use godot::obj::{EngineEnum, Gd, Share};
use godot::prelude::godot_error;
use itertools::Itertools; 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;
@@ -13,85 +15,179 @@ use std::collections::HashMap;
#[derive(Hash, Eq, PartialEq)] #[derive(Hash, Eq, PartialEq)]
struct UniqueVertex { struct UniqueVertex {
vert: i32, vert: i32,
uv: [[u8; 4]; 2], uv: Vec<Option<[[u8; 4]; 2]>>,
weight: [u8; 4], weight: [u8; 4],
} }
pub fn try_commit( pub type SurfaceMapping<T> = HashMap<i32, HashMap<i32, T>>;
mesh: &mut ArrayMesh,
points: &mut [Vector3],
uv_mappings: &mut HashMap<i32, HashMap<i32, Vector2>>,
weight_mappings: &mut HashMap<i32, HashMap<i32, f32>>,
polygons: &mut Vec<PolygonList>,
surfaces: &mut HashMap<i32, u16>,
surface_materials: &mut Vec<u16>,
) {
if polygons.is_empty() {
return;
}
for surface_id in surfaces.values().unique() { #[derive(Default)]
let mut vertex_map = HashMap::<UniqueVertex, i32>::new(); pub struct IntermediateLayer {
let mut vertices = PackedVector3Array::new(); pub name: String,
let mut uvs = PackedVector2Array::new(); pub pivot: Vector3,
let mut weights = PackedFloat32Array::new(); pub parent: Option<u16>,
let mut indices = PackedInt32Array::new(); pub id: u16,
pub points: Vec<Vector3>,
pub uv_mappings: Vec<(String, SurfaceMapping<Vector2>)>,
pub weight_mappings: SurfaceMapping<f32>,
pub polygons: Vec<PolygonList>,
pub surfaces: HashMap<i32, u16>,
}
for (id, poly) in polygons impl IntermediateLayer {
.iter_mut() pub fn commit(mut self, materials: &[MaterialUvInfo]) -> Gd<ArrayMesh> {
.enumerate() let mut mesh = ArrayMesh::new();
.filter(|(id, _)| surfaces.get(&(*id as i32)).unwrap_or(&0) == surface_id) mesh.set_name(self.name.clone().into());
{ let mut surface_materials = Vec::<u16>::new();
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 { self.uv_mappings.sort_by(|a, b| a.0.cmp(&b.0));
let uv = find_mapping(uv_mappings, id, vert);
// let weight = find_mapping(weight_mappings, id, vert);
indices.push( for surface_id in self.surfaces.values().unique() {
*vertex_map let material = &materials[*surface_id as usize];
.entry(UniqueVertex { let material_uv_names = [
vert, material.diffuse_channel.as_ref(),
uv: [uv.x.to_ne_bytes(), uv.y.to_ne_bytes()], material.color_channel.as_ref(),
weight: (0.0f32).to_ne_bytes(), ];
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::<UniqueVertex, i32>::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(|| { .collect_vec();
vertices.push(*points.get(vert as usize).unwrap()); // let weight = find_mapping(weight_mappings, id, vert);
uvs.push(uv);
weights.push(0.0); indices.push(
vertices.len() as i32 - 1 *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(); out_mesh
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);
} }
} }