use crate::prelude::*; use std::cmp::Eq; use std::cmp::PartialEq; use std::collections::HashMap; //use std::collections::BTreeMap; use std::hash::Hash; use std::hash::Hasher; use std::iter::zip; use std::ops::Mul; use std::vec::Vec; use godot::classes::mesh::ArrayType; use godot::classes::mesh::PrimitiveType; use godot::classes::Mesh; use godot::classes::ArrayMesh; use godot::classes::Curve3D; use godot::classes::Material; use godot::classes::MeshInstance3D; use godot::classes::IMeshInstance3D; const VERBOSE: bool = false; const ARRAY_VERTEX: usize = 0; const ARRAY_NORMAL: usize = 1; const ARRAY_TANGENT: usize = 2; const ARRAY_COLOR: usize = 3; const ARRAY_TEX_UV: usize = 4; const ARRAY_TEX_UV2: usize = 5; const ARRAY_CUSTOM0: usize = 6; const ARRAY_CUSTOM1: usize = 7; const ARRAY_CUSTOM2: usize = 8; const ARRAY_CUSTOM3: usize = 9; const ARRAY_BONES: usize = 10; const ARRAY_WEIGHTS: usize = 11; const ARRAY_INDEX: usize = 12; const ARRAY_MAX: usize = 13; const DEFAULT_PRIMITIVE: PrimitiveType = PrimitiveType::TRIANGLES; enum MeshArrayError { NoCurve, NotEnoughPoints, } /*struct TriReverseIter { step: usize temp: i32 } impl TriReverseIter { fn new() -> Self { Self { step: 0, temp: 0, } } } impl Iterator for TriReverseIter { type Item = i32; fn next(&mut self) -> Option { match step { 0 => { }*/ #[derive(PartialEq)] struct HashableF32 { pub val: f32, } impl HashableF32 { fn new(val: f32) -> Self { Self { val: val, } } } impl Hash for HashableF32 { fn hash(&self, state: &mut H) { state.write_u32(self.val.to_bits()); } } impl Eq for HashableF32{} struct PathMeshData<'a> { pub curve: &'a Gd, pub mesh_length: f32, pub whole_reps: i32, pub stretch: f32, vertical_offset: f32, horizontal_offset: f32, sample_cache: HashMap, } impl<'a> PathMeshData<'a> { pub fn new(curve: &'a Gd, repeating_mesh: &Gd, vertical_offset: f32, horizontal_offset: f32, lower_padding: f32, upper_padding: f32) -> Self { let a_mesh_length = repeating_mesh.get_aabb().size.z; let a_reps = (curve.get_baked_length() - lower_padding - upper_padding) / a_mesh_length; let a_whole_reps = a_reps.round().max(1.0) as i32; let a_stretch = a_reps / (a_whole_reps as f32); Self { curve: curve, mesh_length: a_mesh_length, whole_reps: a_whole_reps, stretch: a_stretch, vertical_offset: vertical_offset, horizontal_offset: horizontal_offset, sample_cache: HashMap::new(), } } pub fn sample(&mut self, offset: f32) -> &Transform3D { self.sample_cache.entry(HashableF32::new(offset)).or_insert_with( || -> Transform3D { let mut tf = self.curve.sample_baked_with_rotation_ex().offset(offset).done(); let x_axis = tf.basis.col_c().cross(Vector3::UP).normalized(); let y_axis = x_axis.cross(tf.basis.col_c()).normalized(); tf.basis.set_col_a(x_axis); tf.basis.set_col_b(y_axis); tf.origin += self.horizontal_offset * x_axis + self.vertical_offset * y_axis; tf } ) } } #[derive(GodotClass)] #[class(init, base=MeshInstance3D)] pub struct PathMesh3D { #[export] curve: Option>, #[export] repeating_mesh: Option>, #[export] vertical_offset: f32, #[export] horizontal_offset: f32, #[export] lower_padding: f32, #[export] upper_padding: f32, #[export] invert_faces: bool, base: Base, } #[godot_api] impl IMeshInstance3D for PathMesh3D { fn ready(&mut self) { if self.curve.is_some() && self.repeating_mesh.is_some() { self.generate_mesh(); } } } #[godot_api] impl PathMesh3D { #[func] fn get_mesh_curve(&self) -> Option> { self.curve.clone() } #[func] fn set_mesh_curve(&mut self, curve: Gd) { self.curve = Some(curve); } #[func] fn generate_mesh(&mut self) { if let Some(mesh) = &self.repeating_mesh { if let Some(curve) = &self.curve { let mut data = PathMeshData::new(curve, mesh, self.vertical_offset, self.horizontal_offset, self.lower_padding, self.upper_padding); let surface_count = mesh.get_surface_count(); if VERBOSE { godot_print!("surface_count = {}", surface_count); } let mut new_mesh = ArrayMesh::new_gd(); for i in 0..surface_count { // try to get surace details let mut primitive = DEFAULT_PRIMITIVE; let mut name = GString::new(); if let Ok(mesh) = mesh.clone().try_cast::() { primitive = mesh.surface_get_primitive_type(i); name = mesh.surface_get_name(i); } let arrays = mesh.surface_get_arrays(i); if let Ok(new_arrays) = self.process_array(&arrays, &mut data) { new_mesh.add_surface_from_arrays(primitive, new_arrays); new_mesh.surface_set_name(i, name); if let Some(material) = mesh.surface_get_material(i) { new_mesh.surface_set_material(i, material); } } } if new_mesh.get_surface_count() > 0 { if VERBOSE { godot_print!("new_mesh.surface_count = {}", new_mesh.get_surface_count()); } self.base_mut().set_mesh(new_mesh.upcast::()); } } } } fn process_array(&self, original: &Array, data: &mut PathMeshData) -> Result, MeshArrayError>{ // Optimization ideas: // 1. Often, multiple verticies have the same z coordinate. // Caching curve samples could speed up their processing. // 2. Current method results in duplicate vertices where model ends meet. // Reusing previous vertices could speed up processing and be lighter on memory. let mut processed = VariantArray::new(); processed.resize(ArrayType::MAX.ord().try_into().unwrap(), &Variant::nil()); if data.curve.get_point_count() > 1 { let vertices = original.at(ARRAY_VERTEX).to::(); //must have let new_vertices = self.generate_vertices(&vertices, data); if let Ok(normals) = original.at(ARRAY_NORMAL).try_to::() { processed.set(ARRAY_NORMAL, self.generate_normals(&normals, &vertices, data).to_variant()); } if let Ok(tangents) = original.at(ARRAY_TANGENT).try_to::() { processed.set(ARRAY_TANGENT, self.generate_tangents(&tangents, &vertices, data).to_variant()); } if let Ok(colors) = original.at(ARRAY_COLOR).try_to::() { processed.set(ARRAY_COLOR, self.generate_colors(&colors, data).to_variant()); } if let Ok(uvs) = original.at(ARRAY_TEX_UV).try_to::() { processed.set(ARRAY_TEX_UV, self.generate_uvs(&uvs, data).to_variant()); } else if let Ok(_uvs) = original.at(ARRAY_TEX_UV).try_to::() { processed.set(ARRAY_TEX_UV, self.generate_triplanar_uvs(&vertices).to_variant()); } if let Ok(uvs) = original.at(ARRAY_TEX_UV2).try_to::() { processed.set(ARRAY_TEX_UV2, self.generate_uvs(&uvs, data).to_variant()); } else if let Ok(_uvs) = original.at(ARRAY_TEX_UV2).try_to::() { processed.set(ARRAY_TEX_UV2, self.generate_triplanar_uvs(&vertices).to_variant()); } if let Ok(indecies) = original.at(ARRAY_INDEX).try_to::() { processed.set(ARRAY_INDEX, self.generate_indecies(&indecies, vertices.len() as i32, data).to_variant()); } processed.set(ARRAY_VERTEX, new_vertices.to_variant()); return Ok(processed); } return Err(MeshArrayError::NotEnoughPoints); } fn generate_vertices(&self, original: &PackedVector3Array, data: &mut PathMeshData) -> PackedVector3Array { if VERBOSE { godot_print!("old_vertices.len() = {}", original.len()); } let mut new_vertices = PackedVector3Array::new(); for i in 0..data.whole_reps { let offset = (i as f32 + 0.5) * data.mesh_length + self.lower_padding; for vertex in original.as_slice().iter() { // Sample the curve let curve_point = data.sample((vertex.z + offset) * data.stretch); // Vertex let new_vertex = curve_point.basis.col_a() * vertex.x + curve_point.basis.col_b() * vertex.y + curve_point.origin; new_vertices.push(new_vertex); } } if VERBOSE { godot_print!("new_vertices.len() = {}", new_vertices.len()); } new_vertices } fn generate_normals(&self, original: &PackedVector3Array, vertices: &PackedVector3Array, data: &mut PathMeshData) -> PackedVector3Array { if VERBOSE { godot_print!("old_normals.len() = {}", original.len()); } let mut new_normals = PackedVector3Array::new(); for i in 0..data.whole_reps { let offset = (i as f32 + 0.5) * data.mesh_length + self.lower_padding; for (normal, vertex) in zip(original.as_slice().iter(), vertices.as_slice().iter()) { // Sample the curve let curve_point = data.sample((vertex.z + offset) * data.stretch); // Normal let new_normal = curve_point.basis.mul(normal.clone()); new_normals.push(new_normal); } } if VERBOSE { godot_print!("new_normals.len() = {}", new_normals.len()); } new_normals } fn generate_tangents(&self, original: &PackedFloat32Array, vertices: &PackedVector3Array, data: &mut PathMeshData) -> PackedFloat32Array { if VERBOSE { godot_print!("old_tangents.len() = {}", original.len()); } let mut new_tangents = PackedFloat32Array::new(); for i in 0..data.whole_reps { let offset = (i as f32 + 0.5) * data.mesh_length + self.lower_padding; let mut tangent_vectors = Vec::new(); let mut tangent_binormals = Vec::new(); for j in (0..original.len()).step_by(4) { tangent_vectors.push(Vector3::new(original[j + 0], original[j + 1], original[j + 2])); tangent_binormals.push(original[j + 3]); } for ((tangent, binormal), vertex) in zip(zip(tangent_vectors.as_slice().iter(), tangent_binormals.as_slice().iter()), vertices.as_slice().iter()) { // Sample the curve let curve_point = data.sample((vertex.z + offset) * data.stretch); // Normal let new_tangent = curve_point.basis.mul(*tangent); new_tangents.push(new_tangent.x); new_tangents.push(new_tangent.y); new_tangents.push(new_tangent.z); new_tangents.push(*binormal); } } if VERBOSE { godot_print!("new_tangents.len() = {}", new_tangents.len()); } new_tangents } fn generate_colors(&self, original: &PackedColorArray, data: &mut PathMeshData) -> PackedColorArray { if VERBOSE { let color_count = original.len() as i32; godot_print!("old_colors.len() = {}", color_count); } let mut new_colors = original.clone(); for _i in 1..data.whole_reps { new_colors.extend_array(original); } if VERBOSE { godot_print!("new_colors.len() = {}", new_colors.len()); } new_colors } fn generate_uvs(&self, original: &PackedVector2Array, data: &mut PathMeshData) -> PackedVector2Array { if VERBOSE { let uv_count = original.len() as i32; godot_print!("old_uvs.len() = {}", uv_count); } let mut new_uvs = original.clone(); for _i in 1..data.whole_reps { //let repeated_section = original.as_slice().into_iter().map(|x| x + i * vertex_count); new_uvs.extend_array(original); } if VERBOSE { godot_print!("new_uvs.len() = {}", new_uvs.len()); } new_uvs } fn generate_triplanar_uvs(&self, vertices: &PackedVector3Array) -> PackedVector3Array { return vertices.clone(); } fn generate_indecies(&self, original: &PackedInt32Array, vertex_count: i32, data: &mut PathMeshData) -> PackedInt32Array { let index_count = original.len() as i32; if VERBOSE { godot_print!("old_indecies.len() = {}", index_count); } let mut new_indecies = original.clone(); for i in 1..data.whole_reps { let repeated_section = original.as_slice().into_iter().map(|x| x + i * vertex_count); new_indecies.extend(repeated_section); } if self.invert_faces { for j in (0..new_indecies.len()).step_by(3) { let temp = new_indecies[j + 1]; new_indecies[j + 1] = new_indecies[j + 2]; new_indecies[j + 2] = temp; } } if VERBOSE { godot_print!("new_indecies.len() = {}", new_indecies.len()); } new_indecies } }