This commit is contained in:
2023-05-29 19:32:10 +02:00
parent 7e01ff3440
commit 527a1ef0c7
14 changed files with 516 additions and 292 deletions

View File

@@ -8,11 +8,10 @@ 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));
let path = format!("sar://{}", still.name.replace('\\', "/").replace(':', ":/"));
let mut image: Gd<Image> = load(path);
image.set_name(still.name.clone().into());
target.insert(clip.index, image);
}
x => {
godot_error!("TODO: Clip chunk {:?}", x)

View File

@@ -0,0 +1,92 @@
use crate::lwo::material::MaterialUvInfo;
use crate::lwo::surface_info::SurfaceInfo;
use godot::builtin::{Array, Dictionary, Vector2, Vector3};
use godot::engine::mesh::{ArrayFormat, PrimitiveType};
use godot::engine::{ArrayMesh, SurfaceTool};
use godot::obj::{Gd, Share};
use itertools::Itertools;
use lightwave_3d::lwo2::tags::polygon_list::PolygonList;
use std::collections::HashMap;
pub type SurfaceMapping<T> = HashMap<i32, HashMap<i32, T>>;
#[derive(Default)]
pub struct IntermediateLayer {
pub name: String,
pub pivot: Vector3,
pub parent: Option<u16>,
pub id: u16,
pub points: Vec<Vector3>,
pub uv_mappings: Vec<(String, SurfaceMapping<Vector2>)>,
pub weight_mappings: SurfaceMapping<f32>,
pub polygons: Vec<PolygonList>,
pub material_mappings: HashMap<i32, u16>,
}
impl IntermediateLayer {
pub fn commit(mut self, materials: &HashMap<u16, MaterialUvInfo>) -> Gd<ArrayMesh> {
let mut mesh = ArrayMesh::new();
mesh.set_name(self.name.clone().into());
let mut surface_material_ids = Vec::<u16>::new();
self.uv_mappings.sort_by(|a, b| a.0.cmp(&b.0));
for material_id in self.material_mappings.values().unique() {
let material = &materials[material_id];
let surface_info = SurfaceInfo::collect_from_layer(&self, material);
if !surface_info.is_empty() {
mesh.add_surface_from_arrays(
PrimitiveType::PRIMITIVE_TRIANGLES,
surface_info.commit_to_arrays(),
Array::new(),
Dictionary::new(),
ArrayFormat::ARRAY_FORMAT_NORMAL,
);
surface_material_ids.push(*material_id);
}
}
let mut final_mesh = post_process_mesh(
mesh,
materials,
surface_material_ids,
&self.material_mappings,
);
final_mesh.set_name(self.name.into());
final_mesh
}
}
fn post_process_mesh(
mesh: Gd<ArrayMesh>,
materials: &HashMap<u16, MaterialUvInfo>,
material_ids: Vec<u16>,
material_mappings: &HashMap<i32, u16>,
) -> Gd<ArrayMesh> {
let mut out_mesh = ArrayMesh::new();
debug_assert_eq!(mesh.get_surface_count() as usize, material_ids.len());
for (surface_idx, surface_id) in material_ids.into_iter().enumerate() {
let mut tool = SurfaceTool::new();
tool.create_from(mesh.share().upcast(), surface_idx as i64);
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(&material_mappings[&(surface_id as i32)]) {
out_mesh.surface_set_material(surface_idx as i64, mat.material.share().upcast())
}
}
out_mesh
}

View File

@@ -1,6 +1,4 @@
use godot::builtin::{
Basis, Color, EulerOrder, Quaternion, ToVariant, Transform3D, Variant, Vector3,
};
use godot::builtin::{Basis, Color, EulerOrder, ToVariant, Transform3D, Variant, Vector3};
use godot::engine::{load, Image, ImageTexture, ShaderMaterial};
use godot::log::{godot_error, godot_print};
use godot::obj::{Gd, Share};
@@ -17,20 +15,23 @@ use lightwave_3d::lwo2::sub_tags::surface_parameters::SurfaceParameterSubChunk;
use lightwave_3d::lwo2::tags::surface_definition::SurfaceDefinition;
use std::collections::HashMap;
#[derive(Debug)]
pub struct MaterialUvInfo {
pub diffuse_channel: Option<String>,
pub color_channel: Option<String>,
pub material: Gd<ShaderMaterial>,
pub id: u16,
}
impl MaterialUvInfo {
pub fn collect(surface: SurfaceDefinition, images: &HashMap<u32, Gd<Image>>) -> Self {
pub fn collect(surface: SurfaceDefinition, images: &HashMap<u32, Gd<Image>>, id: u16) -> Self {
let mut m = MaterialUvInfo {
diffuse_channel: None,
color_channel: None,
material: ShaderMaterial::new(),
id,
};
m.material.set_name(surface.name.to_string().into());
m.material.set_name(surface.name.into());
m.material
.set_shader(load("res://starforce/starforce.gdshader"));
@@ -41,6 +42,7 @@ impl MaterialUvInfo {
let mut texture = ImageTexture::new();
let mut chan = TextureChannel::Color;
let mut uv_channel = None;
let mut major_axis = 0;
let mut projection_mode = ProjectionMode::UV;
let mut mapping_info = Vec::<(&str, Variant)>::new();
for attr in header.data.block_attributes {
@@ -66,6 +68,7 @@ impl MaterialUvInfo {
match attr {
SurfaceBlockImageTextureSubChunk::ImageMap(r) => {
if let Some(i) = images.get(&r.texture_image) {
godot_print!("{}", i.get_name());
texture.set_image(i.share());
} else {
godot_error!("Missing texture {:?}", r);
@@ -75,7 +78,7 @@ impl MaterialUvInfo {
projection_mode = projection.data;
}
SurfaceBlockImageTextureSubChunk::UvVertexMap(map) => {
uv_channel = Some(map.txuv_map_name.to_string());
uv_channel = Some(map.txuv_map_name.clone());
}
SurfaceBlockImageTextureSubChunk::TextureMapping(mapping) => {
let mut pos = Vector3::default();
@@ -85,33 +88,33 @@ impl MaterialUvInfo {
match mapping_param {
TextureMappingSubChunk::Center(it) => {
pos = Vector3 {
x: it.base_color[0],
z: it.base_color[0],
y: it.base_color[1],
z: it.base_color[2],
x: it.base_color[2],
};
}
TextureMappingSubChunk::Rotation(it) => {
rot = Vector3 {
x: it.base_color[0],
z: it.base_color[0],
y: it.base_color[1],
z: it.base_color[2],
x: it.base_color[2],
}
.normalized();
}
TextureMappingSubChunk::Size(it) => {
size = Vector3 {
x: it.base_color[0],
z: it.base_color[0],
y: it.base_color[1],
z: it.base_color[2],
x: it.base_color[2],
};
}
TextureMappingSubChunk::Falloff(it) => {
mapping_info.push((
"falloff",
Vector3 {
x: it.vector[0],
z: it.vector[0],
y: it.vector[1],
z: it.vector[2],
x: it.vector[2],
}
.to_variant(),
));
@@ -138,10 +141,8 @@ impl MaterialUvInfo {
));
}
TextureMappingSubChunk::ReferenceObject(it) => {
if !matches!(
it.object_name.to_string().as_str(),
"" | "(none)"
) {
if !matches!(it.object_name.as_str(), "" | "(none)")
{
godot_error!("Reference object '{}': not supported for texture mapping", it.object_name)
}
}
@@ -151,15 +152,15 @@ impl MaterialUvInfo {
mapping_info.push((
"transform",
Transform3D {
basis: Basis::from_euler(EulerOrder::XYZ, rot)
basis: Basis::from_euler(EulerOrder::ZYX, rot)
.scaled(size),
origin: pos,
}
.to_variant(),
));
}
SurfaceBlockImageTextureSubChunk::MajorAxis(_) => {
// TODO;
SurfaceBlockImageTextureSubChunk::MajorAxis(axis) => {
major_axis = axis.data.texture_axis;
}
SurfaceBlockImageTextureSubChunk::ImageWrapOptions(_) => {
// TODO;
@@ -181,19 +182,31 @@ impl MaterialUvInfo {
}
}
}
godot_print!("TX: {:?} @ UV{:?}", chan, uv_channel);
let channel_name = match chan {
/*godot_print!(
"TX: {:?} ({:?}) @ UV{:?}",
chan,
projection_mode,
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",
x => {
godot_error!("TODO: Texture channel {:?} is not supported", x);
"color"
} /*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_{}_axis", channel_name).into(),
major_axis.to_variant(),
);
m.material.set_shader_parameter(
format!("tex_{}_projection", channel_name).into(),
match projection_mode {

View File

@@ -6,10 +6,12 @@ use godot::obj::Gd;
use lightwave_3d::LightWaveObject;
pub(crate) mod clips;
pub(crate) mod intermediate_layer;
pub(crate) mod mapping;
pub(crate) mod material;
pub(crate) mod object;
pub(crate) mod surface;
pub(crate) mod surface_info;
pub(crate) mod unique_vertex;
#[derive(GodotClass)]
#[class(init)]

View File

@@ -1,52 +1,59 @@
use crate::lwo::clips::collect_clip;
use crate::lwo::intermediate_layer::IntermediateLayer;
use crate::lwo::mapping::{collect_discontinuous_mappings, collect_mappings};
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 itertools::Itertools;
use lightwave_3d::lwo2::tags::Tag;
use lightwave_3d::LightWaveObject;
use std::collections::HashMap;
pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<PackedScene> {
let mut materials = vec![];
let mut surfaces = HashMap::<u16, MaterialUvInfo>::new();
let mut images = HashMap::<u32, Gd<Image>>::new();
let mut layers = vec![];
let mut tag_strings = vec![];
for tag in lightwave.data {
match tag {
Tag::TagStrings(it) => {
tag_strings = it.data.tag_strings.into_iter().collect();
godot_print!("{:?}", tag_strings);
}
Tag::Layer(layer_tag) => {
layers.push(IntermediateLayer {
name: layer_tag.name.to_string(),
name: layer_tag.name.clone(),
parent: layer_tag.parent,
id: layer_tag.number,
pivot: Vector3 {
x: layer_tag.pivot[0],
z: layer_tag.pivot[0],
y: layer_tag.pivot[1],
z: layer_tag.pivot[2],
x: layer_tag.pivot[2],
},
..IntermediateLayer::default()
});
}
Tag::PointList(points_chunk) => {
debug_assert_eq!(layers.last().unwrap().points.len(), 0);
layers.last_mut().unwrap().points = points_chunk
.data
.point_location
.into_iter()
.map(|p| Vector3 {
x: p[0],
z: p[0],
y: p[1],
z: p[2],
x: p[2],
})
.collect();
}
Tag::DiscontinuousVertexMapping(vmad) => match &vmad.kind {
b"TXUV" => {
debug_assert!(vmad.data.mappings[0].values.len() == 2);
let name = vmad.name.to_string();
let name = vmad.name.clone();
let layer = layers.last_mut().unwrap();
let map = if let Some(mappings) =
@@ -76,7 +83,7 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<PackedScene> {
Tag::VertexMapping(vmap) => match &vmap.kind {
b"TXUV" => {
debug_assert!(vmap.data.mapping[0].value.len() == 2);
let name = vmap.name.to_string();
let name = vmap.name.clone();
let layer = layers.last_mut().unwrap();
let map = if let Some(mappings) =
@@ -109,7 +116,7 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<PackedScene> {
layers
.last_mut()
.unwrap()
.surfaces
.material_mappings
.insert(surf.poly as i32, surf.tag);
}
}
@@ -120,14 +127,24 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<PackedScene> {
},
Tag::PolygonList(polygon_lists) => match &polygon_lists.kind {
b"FACE" => {
debug_assert_eq!(layers.last().unwrap().polygons.len(), 0);
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) => {
godot_print!("Def: '{}' -> '{}'", surf.source, surf.name);
materials.push(MaterialUvInfo::collect(surf.data, &images));
let surf_name = surf.name.clone();
let (tag_index, _) = tag_strings
.iter()
.find_position(|name| name == &&surf_name)
.expect("Invalid File");
godot_print!("'{}': {}", surf_name, tag_index);
surfaces.insert(
tag_index as u16,
MaterialUvInfo::collect(surf.data, &images, tag_index as u16),
);
}
Tag::BoundingBox(_) => (),
x => {
@@ -136,12 +153,25 @@ pub fn lightwave_to_gd(lightwave: LightWaveObject) -> Gd<PackedScene> {
}
}
/*godot_print!(
"{:?}",
surfaces
.iter()
.map(|(k, v)| (
k,
tag_strings[*k as usize].clone(),
v.material.get_shader_parameter("tex_diffuse".into()),
v.material.get_shader_parameter("tex_color".into())
))
.collect_vec()
);*/
let mut root_node = Node3D::new_alloc();
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());
instance.set_mesh(layer.commit(&surfaces).upcast());
root_node.add_child(
instance.share().upcast(),

View File

@@ -1,193 +0,0 @@
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, 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;
#[derive(Hash, Eq, PartialEq)]
struct UniqueVertex {
vert: i32,
uv: Vec<Option<[[u8; 4]; 2]>>,
weight: [u8; 4],
}
pub type SurfaceMapping<T> = HashMap<i32, HashMap<i32, T>>;
#[derive(Default)]
pub struct IntermediateLayer {
pub name: String,
pub pivot: Vector3,
pub parent: Option<u16>,
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>,
}
impl IntermediateLayer {
pub fn commit(mut self, materials: &[MaterialUvInfo]) -> Gd<ArrayMesh> {
let mut mesh = ArrayMesh::new();
mesh.set_name(self.name.clone().into());
let mut surface_materials = Vec::<u16>::new();
self.uv_mappings.sort_by(|a, b| a.0.cmp(&b.0));
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::<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))
})
})
.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())
}
}
out_mesh
}
}

View File

@@ -0,0 +1,144 @@
use crate::lwo::intermediate_layer::IntermediateLayer;
use crate::lwo::mapping::find_mapping;
use crate::lwo::material::MaterialUvInfo;
use crate::lwo::unique_vertex::UniqueVertex;
use godot::builtin::{
PackedFloat32Array, PackedInt32Array, PackedVector2Array, PackedVector3Array, ToVariant,
VariantArray, Vector2, Vector3,
};
use godot::engine::mesh::ArrayType;
use godot::log::godot_error;
use godot::obj::EngineEnum;
use itertools::Itertools;
use std::collections::HashMap;
fn triangulate(poly: &[u32]) -> Vec<i32> {
let mut poly = poly.iter().collect_vec();
let fan_v = *poly.pop().unwrap() as i32;
poly.reverse();
poly.windows(2)
.flat_map(move |w| [*w[1] as i32, *w[0] as i32, fan_v])
.collect()
}
#[derive(Default)]
pub struct SurfaceInfo {
vertex_map: HashMap<UniqueVertex, i32>,
material_incomplete: Vec<i32>,
pub material_id: u16,
pub vertices: PackedVector3Array,
pub uv_sets: Vec<Option<PackedVector2Array>>,
pub weights: PackedFloat32Array,
pub indices: PackedInt32Array,
}
impl SurfaceInfo {
pub fn is_empty(&self) -> bool {
self.vertices.is_empty()
}
pub fn commit_to_arrays(self) -> VariantArray {
let mut arrays = VariantArray::new();
arrays.resize(ArrayType::ARRAY_MAX.ord() as usize);
arrays.set(
ArrayType::ARRAY_VERTEX.ord() as usize,
self.vertices.to_variant(),
);
for (i, uv) in self.uv_sets.into_iter().enumerate() {
if let Some(uv) = uv {
arrays.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(),
);*/
arrays.set(
ArrayType::ARRAY_INDEX.ord() as usize,
self.indices.to_variant(),
);
#[cfg(debug_assertions)]
for (i, inc) in self.material_incomplete.into_iter().enumerate() {
if inc != 0 {
godot_error!(
"{} ({}%) incomplete UVs",
inc,
(inc as f32 / self.vertices.len() as f32) * 100.0
);
}
}
arrays
}
pub fn collect_from_layer(layer: &IntermediateLayer, material: &MaterialUvInfo) -> Self {
let material_uv_names = [
material.diffuse_channel.as_ref(),
material.color_channel.as_ref(),
];
let materials_subset = material_uv_names
.iter()
.map(|it| it.and_then(|it| layer.uv_mappings.iter().find(|(name, _)| name == it)))
.collect_vec();
let mut surface_info = SurfaceInfo {
uv_sets: materials_subset
.iter()
.map(|it| it.map(|_| PackedVector2Array::new()))
.collect(),
material_incomplete: material_uv_names.iter().map(|_| 0).collect_vec(),
..SurfaceInfo::default()
};
let mut surface_polygons = layer.polygons.iter().enumerate().filter(|(id, _)| {
layer.material_mappings.get(&(*id as i32)).unwrap_or(&0) == &material.id
});
for (id, poly) in surface_polygons {
for index in triangulate(&poly.vert) {
let uv = materials_subset
.iter()
.map(|it| it.and_then(|(_, it)| find_mapping(it, id, index)))
.collect_vec();
// TODO: let weight = find_mapping(weight_mappings, id, vert);
let vert = layer.points.get(index as usize).unwrap();
surface_info.push_index(vert, &uv, 0f32);
}
}
surface_info
}
fn push_index(&mut self, vert: &Vector3, uvs: &[Option<Vector2>], weight: f32) {
let index = *self
.vertex_map
.entry(UniqueVertex::from_point(vert, uvs, 0f32, self.material_id))
.or_insert_with(|| {
self.vertices.push(*vert);
for (i, uv) in uvs.iter().enumerate() {
if let Some(uv_set) = &mut self.uv_sets[i] {
uv_set.push(uv.unwrap_or_else(|| {
self.material_incomplete[i] += 1;
Vector2::ZERO
}));
}
}
self.weights.push(weight);
self.vertices.len() as i32 - 1
});
self.indices.push(index);
}
}

View File

@@ -0,0 +1,32 @@
use godot::builtin::{Vector2, Vector3};
#[derive(Hash, Eq, PartialEq)]
pub struct UniqueVertex {
vert: [[u8; 4]; 3],
material_id: u16,
uv: Vec<Option<[[u8; 4]; 2]>>,
weight: [u8; 4],
}
impl UniqueVertex {
pub fn from_point(
vert: &Vector3,
uvs: &[Option<Vector2>],
weight: f32,
material_id: u16,
) -> Self {
Self {
vert: [
vert.x.to_ne_bytes(),
vert.y.to_ne_bytes(),
vert.z.to_ne_bytes(),
],
material_id,
uv: uvs
.iter()
.map(|it| it.map(|uv| [uv.x.to_ne_bytes(), uv.y.to_ne_bytes()]))
.collect(),
weight: weight.to_ne_bytes(),
}
}
}