diff --git a/.gitignore b/.gitignore index 867d4b2..fd336db 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,3 @@ # ---> Platform Files *.sh -*.*.dblite diff --git a/src/.sconsign.dblite b/src/.sconsign.dblite index dc9a5b0..05d8171 100644 Binary files a/src/.sconsign.dblite and b/src/.sconsign.dblite differ diff --git a/src/liborng/nodes/mesh_editing_library.cpp b/src/liborng/nodes/mesh_editing_library.cpp new file mode 100644 index 0000000..a798068 --- /dev/null +++ b/src/liborng/nodes/mesh_editing_library.cpp @@ -0,0 +1,242 @@ +/* + * ©2023 Batty Bovine Productions, LLC. All Rights Reserved. + * + * If this source code makes it to the public internet, this software can be + * considered to be protected by the MIT licence. Have fun with it. + */ + +#include "mesh_editing_library.h" + +#include + +#include "godot_cpp/variant/utility_functions.hpp" +using namespace godot; + + +MeshEditingLibrary::MeshEditingLibrary() +{ +} + +MeshEditingLibrary::~MeshEditingLibrary() +{ +} + + +bool MeshEditingLibrary::slice_mesh(const Mesh *original_mesh, const Plane &local_plane, Mesh *out_first_half, Mesh *out_other_half, const MeshSliceCapUVOption cap_option, const StandardMaterial3D *cap_material, Vector3 impact_point) +{ + const uint8_t num_surfaces = original_mesh->get_surface_count(); + + for (uint8_t surface = 0; surface < num_surfaces; surface++) + { + const Array &surface_arrays = original_mesh->surface_get_arrays(0); + const PackedVector3Array &surface_vertex_array = surface_arrays[Mesh::ARRAY_VERTEX]; + const PackedVector3Array &surface_normal_array = surface_arrays[Mesh::ARRAY_NORMAL]; + const PackedFloat32Array &surface_tangent_array = surface_arrays[Mesh::ARRAY_TANGENT]; + const PackedVector2Array &surface_uv_array = surface_arrays[Mesh::ARRAY_TEX_UV]; + const PackedVector2Array &surface_uv2_array = surface_arrays[Mesh::ARRAY_TEX_UV2]; + const PackedColorArray &surface_colour_array = surface_arrays[Mesh::ARRAY_COLOR]; + const PackedInt32Array &surface_bone_array = surface_arrays[Mesh::ARRAY_BONES]; + const PackedFloat32Array &surface_weight_array = surface_arrays[Mesh::ARRAY_WEIGHTS]; + + const Vector3 *surface_vertex_array_ptr = surface_vertex_array.ptr(); + + const uint8_t num_vertices = surface_vertex_array.size(); + + std::vector vertex_distance; + vertex_distance.reserve(num_vertices); + + std::map base_to_sliced_vert_index; + std::map base_to_other_sliced_vert_index; + + + const bool has_normal = surface_normal_array.size() >= num_vertices; + const bool has_tangent = surface_tangent_array.size() >= num_vertices * 4; + const bool has_uv = surface_uv_array.size() >= num_vertices; + const bool has_uv2 = surface_uv2_array.size() >= num_vertices; + const bool has_colour = surface_colour_array.size() >= num_vertices; + const bool has_bone = surface_bone_array.size() >= num_vertices * 4; + const bool has_weight = surface_weight_array.size() >= num_vertices * 4; + + const uint32_t bone_array_size = surface_bone_array.size(); + const uint8_t num_bones_in_array = bone_array_size / num_vertices; + + PackedVector3Array first_half_section_vertices; + PackedVector3Array first_half_section_normals; + PackedFloat32Array first_half_section_tangents; + PackedVector2Array first_half_section_uvs; + PackedVector2Array first_half_section_uv2s; + PackedColorArray first_half_section_colours; + PackedInt32Array first_half_section_bones; + PackedFloat32Array first_half_section_weights; + uint32_t num_first_half_verts = 0; + + PackedVector3Array other_half_section_vertices; + PackedVector3Array other_half_section_normals; + PackedFloat32Array other_half_section_tangents; + PackedVector2Array other_half_section_uvs; + PackedVector2Array other_half_section_uv2s; + PackedColorArray other_half_section_colours; + PackedInt32Array other_half_section_bones; + PackedFloat32Array other_half_section_weights; + uint32_t num_other_half_verts = 0; + for (uint8_t vertex = 0; vertex < num_vertices; vertex++) + { + vertex_distance[vertex] = local_plane.distance_to(surface_vertex_array_ptr[vertex]); + + if (vertex_distance[vertex] >= 0.0f) + { + base_to_sliced_vert_index[vertex] = num_first_half_verts; + num_first_half_verts++; + + first_half_section_vertices.append(surface_vertex_array_ptr[vertex]); + if (has_normal) { first_half_section_normals.append(surface_normal_array.ptr()[vertex]); } + if (has_uv) { first_half_section_uvs.append(surface_uv_array.ptr()[vertex]); } + if (has_uv2) { first_half_section_uv2s.append(surface_uv2_array.ptr()[vertex]); } + if (has_colour) { first_half_section_colours.append(surface_colour_array.ptr()[vertex]); } + if (has_tangent) + { + first_half_section_tangents.append(surface_tangent_array.ptr()[vertex * 4]); + first_half_section_tangents.append(surface_tangent_array.ptr()[(vertex * 4) + 1]); + first_half_section_tangents.append(surface_tangent_array.ptr()[(vertex * 4) + 2]); + first_half_section_tangents.append(surface_tangent_array.ptr()[(vertex * 4) + 3]); + } + if (has_bone) + { + for (uint8_t i = 0; i < num_bones_in_array; i++) + { + first_half_section_bones.append(surface_bone_array.ptr()[(vertex * num_bones_in_array) + i]); + first_half_section_weights.append(surface_weight_array.ptr()[(vertex * num_bones_in_array) + i]); + } + } + } + else + { + base_to_other_sliced_vert_index[vertex] = num_other_half_verts; + num_other_half_verts++; + + other_half_section_vertices.append(surface_vertex_array_ptr[vertex]); + if (has_normal) { other_half_section_normals.append(surface_normal_array.ptr()[vertex]); } + if (has_uv) { other_half_section_uvs.append(surface_uv_array.ptr()[vertex]); } + if (has_uv2) { other_half_section_uv2s.append(surface_uv2_array.ptr()[vertex]); } + if (has_colour) { other_half_section_colours.append(surface_colour_array.ptr()[vertex]); } + if (has_tangent) + { + other_half_section_tangents.append(surface_tangent_array.ptr()[vertex * 4]); + other_half_section_tangents.append(surface_tangent_array.ptr()[(vertex * 4) + 1]); + other_half_section_tangents.append(surface_tangent_array.ptr()[(vertex * 4) + 2]); + other_half_section_tangents.append(surface_tangent_array.ptr()[(vertex * 4) + 3]); + } + if (has_bone) + { + for (uint8_t i = 0; i < num_bones_in_array; i++) + { + other_half_section_bones.append(surface_bone_array.ptr()[(vertex * num_bones_in_array) + i]); + other_half_section_weights.append(surface_weight_array.ptr()[(vertex * num_bones_in_array) + i]); + } + } + } + } + + // std::vector<> clip_edges + + PackedInt32Array first_half_section_indices; + PackedInt32Array other_half_section_indices; + + const PackedInt32Array &surface_index_array = surface_arrays[Mesh::ARRAY_INDEX]; + const uint32_t num_triangles = surface_index_array.size(); + for (uint32_t triangle_index = 0; triangle_index < num_triangles; triangle_index += 3) + { + uint32_t base_v[3]; + std::map::iterator sliced_v[3]; + std::map::iterator sliced_other_v[3]; + + const std::map::iterator base_to_sliced_end = base_to_sliced_vert_index.end(); + const std::map::iterator base_to_other_sliced_end = base_to_other_sliced_vert_index.end(); + for (uint32_t i = 0; i < 3; i++) + { + base_v[i] = surface_index_array.ptr()[triangle_index + i]; + sliced_v[i] = base_to_sliced_vert_index.find(base_v[i]); + sliced_other_v[i] = base_to_other_sliced_vert_index.find(base_v[i]); + if ((sliced_v[i] != base_to_sliced_vert_index.end()) == (sliced_other_v[i] != base_to_other_sliced_vert_index.end())) + { + UtilityFunctions::push_error("A vertex does not appear in either slice half. This is a severe error. We're leaving now", -1); + return false; + } + } + + if (sliced_v[0] != base_to_sliced_end && + sliced_v[1] != base_to_sliced_end && + sliced_v[2] != base_to_sliced_end) + { // If the triangle is entirely in the first slice, send all the vertices to the first slice. + first_half_section_indices.append(sliced_v[0]->second); + first_half_section_indices.append(sliced_v[1]->second); + first_half_section_indices.append(sliced_v[2]->second); + } + else if (sliced_other_v[0] != base_to_other_sliced_end && + sliced_other_v[1] != base_to_other_sliced_end && + sliced_other_v[2] != base_to_other_sliced_end) + { // If the triangle is entirely in the second slice, send all the vertices to the second slice. + other_half_section_indices.append(sliced_other_v[0]->second); + other_half_section_indices.append(sliced_other_v[1]->second); + other_half_section_indices.append(sliced_other_v[2]->second); + } + else + { // If the triangle is split by the slice plane, then slice the overlapping edges. + uint32_t final_verts[4] = { 0, 0, 0, 0 }; + uint8_t num_final_verts = 0; + + uint32_t other_final_verts[4] = { 0, 0, 0, 0 }; + uint8_t num_other_final_verts = 0; + + //FUtilEdge3D new_clip_edge; + uint8_t clipped_edges = 0; + + float plane_distance[3] = { + vertex_distance[base_v[0]], + vertex_distance[base_v[1]], + vertex_distance[base_v[2]] + }; + + for (uint32_t this_vert = 0; this_vert < 3; this_vert++) + { + if (sliced_v[this_vert] != base_to_sliced_end) + { + final_verts[num_final_verts] = sliced_v[this_vert]->second; + num_final_verts++; + } + else + { + other_final_verts[num_other_final_verts] = sliced_other_v[this_vert]->second; + num_other_final_verts++; + } + + uint32_t next_vert = (this_vert + 1) % 3; + if ((sliced_v[this_vert] == base_to_sliced_end) != (sliced_v[next_vert] == base_to_sliced_end)) + { + float alpha = UtilityFunctions::clampf(-plane_distance[this_vert] / (plane_distance[next_vert] - plane_distance[this_vert]), 0.0f, 1.0f); + + const Vector3 interp_vert = surface_vertex_array_ptr[base_v[this_vert]].lerp( + surface_vertex_array_ptr[base_v[next_vert]], alpha); + + const Vector3 interp_normal = surface_normal_array.ptr()[base_v[this_vert]].lerp( + surface_normal_array.ptr()[base_v[next_vert]], alpha); + + const Vector2 interp_uv = surface_uv_array.ptr()[base_v[this_vert]].lerp( + surface_uv_array.ptr()[base_v[next_vert]], alpha); + const Vector2 interp_uv2 = surface_uv2_array.ptr()[base_v[this_vert]].lerp( + surface_uv2_array.ptr()[base_v[next_vert]], alpha); + + const Color interp_colour = surface_colour_array.ptr()[base_v[this_vert]].lerp( + surface_colour_array.ptr()[base_v[this_vert]], alpha); + + // Bones and weights will be interpolated here later + + UtilityFunctions::print("If you can see this millions of times, then this code is running fine. :)"); + } + } + } + } + } + + return false; +} diff --git a/src/liborng/nodes/mesh_editing_library.h b/src/liborng/nodes/mesh_editing_library.h new file mode 100644 index 0000000..65559ba --- /dev/null +++ b/src/liborng/nodes/mesh_editing_library.h @@ -0,0 +1,45 @@ +/* + * ©2023 Batty Bovine Productions, LLC. All Rights Reserved. + * + * If this source code makes it to the public internet, this software can be + * considered to be protected by the MIT licence. Have fun with it. + */ + +#pragma once + +#include +#include +#include +#include +#include +using namespace godot; + + +class MeshEditingLibrary : public Node +{ + GDCLASS(MeshEditingLibrary, Node); + +public: + enum MeshSliceCapUVOption + { + FILL_MESH_BOUNDS, + FILL_CAP_BOUNDS + }; + +public: + MeshEditingLibrary(); + ~MeshEditingLibrary(); + + static bool slice_mesh(const Mesh *original_mesh, const Plane &local_plane, Mesh *out_first_half, Mesh *out_other_half, const MeshSliceCapUVOption cap_option, const StandardMaterial3D *cap_material, Vector3 impact_point); + +private: + + // Godot boilerplate below +protected: + static void _bind_methods() + { + ClassDB::bind_static_method("MeshEditingLibrary", D_METHOD("slice_mesh", "original_mesh", "local_plane", "out_first_half", "out_other_half", "cap_option", "cap_material", "impact_point"), &MeshEditingLibrary::slice_mesh); + } +}; + +VARIANT_ENUM_CAST(MeshEditingLibrary::MeshSliceCapUVOption); diff --git a/src/liborng/register_types.cpp b/src/liborng/register_types.cpp index fc1091e..ada301e 100644 --- a/src/liborng/register_types.cpp +++ b/src/liborng/register_types.cpp @@ -25,6 +25,8 @@ #include "resources/level_metadata_map.h" #include "resources/level_metadata_resource.h" +#include "nodes/mesh_editing_library.h" + #include "singletons/input_handler.h" #include "singletons/save_manager.h" #include "singletons/scene_loader.h" @@ -66,6 +68,8 @@ void initialize_orng_module(ModuleInitializationLevel p_level) { ClassDB::register_class(); ClassDB::register_class(); + + ClassDB::register_class(); GDSINGLETON_REGISTER_CLASS(InputHandler, _input_handler_singleton); GDSINGLETON_REGISTER_CLASS(SaveManager, _save_manager_singleton);