From 00332be60c473f0530844dd3fc738e321489d6e3 Mon Sep 17 00:00:00 2001 From: Jamie Greunbaum Date: Sun, 4 Oct 2026 21:40:52 -0400 Subject: [PATCH] Reorganised code files and moved functions to DamageableNode3D parent class. --- src/SConstruct | 2 +- src/liborng/interfaces/i_damageable.h | 42 ---- .../nodes/character_nodes/character_3d.cpp | 192 ------------------ .../nodes/damageables/character_3d.cpp | 145 +++++++++++++ .../character_3d.h | 26 +-- .../character_skeleton3d.cpp | 29 +-- .../character_skeleton3d.h | 11 +- .../damageables/damageable_node3d.cpp} | 57 ++++-- .../nodes/damageables/damageable_node3d.h | 67 ++++++ .../damageables/sliceable_rigid_body3d.cpp | 14 ++ .../damageables/sliceable_rigid_body3d.h | 39 ++++ src/liborng/nodes/hurtbox/hurtbox_base.cpp | 8 +- src/liborng/nodes/hurtbox/hurtbox_base.h | 8 +- src/liborng/register_types.cpp | 13 +- .../mesh_editing_library.cpp | 51 ++--- .../mesh_editing_library.h | 19 +- 16 files changed, 383 insertions(+), 340 deletions(-) delete mode 100644 src/liborng/interfaces/i_damageable.h delete mode 100644 src/liborng/nodes/character_nodes/character_3d.cpp create mode 100644 src/liborng/nodes/damageables/character_3d.cpp rename src/liborng/nodes/{character_nodes => damageables}/character_3d.h (61%) rename src/liborng/nodes/{character_nodes => damageables}/character_skeleton3d.cpp (55%) rename src/liborng/nodes/{character_nodes => damageables}/character_skeleton3d.h (88%) rename src/liborng/{interfaces/i_damageable.cpp => nodes/damageables/damageable_node3d.cpp} (57%) create mode 100644 src/liborng/nodes/damageables/damageable_node3d.h create mode 100644 src/liborng/nodes/damageables/sliceable_rigid_body3d.cpp create mode 100644 src/liborng/nodes/damageables/sliceable_rigid_body3d.h rename src/liborng/{nodes => singletons}/mesh_editing_library.cpp (96%) rename src/liborng/{nodes => singletons}/mesh_editing_library.h (91%) diff --git a/src/SConstruct b/src/SConstruct index 0896346..0abdaf4 100644 --- a/src/SConstruct +++ b/src/SConstruct @@ -3,7 +3,7 @@ env.Append(CPPPATH="liborng/") env.Append(CPPPATH="liborng/data_assets") env.Append(CPPPATH="liborng/nodes/interfaces") env.Append(CPPPATH="liborng/nodes") -env.Append(CPPPATH="liborng/nodes/character_nodes") +env.Append(CPPPATH="liborng/nodes/damageables") env.Append(CPPPATH="liborng/resources") env.Append(CPPPATH="liborng/singletons") env.Append(CPPPATH="liborng/singletons/vendor_service") diff --git a/src/liborng/interfaces/i_damageable.h b/src/liborng/interfaces/i_damageable.h deleted file mode 100644 index bb097db..0000000 --- a/src/liborng/interfaces/i_damageable.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * ©2026 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 "nodes/mesh_editing_library.h" - -#include "godot_cpp/classes/node.hpp" -using namespace godot; - -#define MAX_SLICE_QUEUE_SIZE 3 - - -class IDamageable : public Node3D -{ -public: - virtual void take_damage(const struct HurtboxDamage &hit_data) = 0; - -protected: - void _slice_meshes(Ref data); - - virtual void _slice_complete(Ref data) = 0; - -private: - void _new_slice_loop(); - void _slice_loop(); - - bool _validate_input(Ref input); - - bool _is_slicing = false; - - std::vector> _slice_state_queue; - std::vector> _input_queue; - - // Godot boilerplate below -protected: - static void _bind_methods() {} -}; diff --git a/src/liborng/nodes/character_nodes/character_3d.cpp b/src/liborng/nodes/character_nodes/character_3d.cpp deleted file mode 100644 index 2251faf..0000000 --- a/src/liborng/nodes/character_nodes/character_3d.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * ©2026 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 "character_3d.h" - -#include "orng_macros.h" - -#include "nodes/debug_draw3d.h" -#include "nodes/mesh_editing_library.h" -#include "nodes/hurtbox/hurtbox_base.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -using namespace godot; - - -void Character3D::_ready() -{ - if (this->character_skeleton) - { - this->_rebuild_mesh_instances(); - } -} - -void Character3D::set_meshes(TypedArray m) -{ - this->meshes = m; - this->_full_aabb = AABB(); - this->_rebuild_mesh_instances(); -} - -void Character3D::set_character_skeleton(CharacterSkeleton3D *cs) -{ - if (cs) - { - if (this->character_skeleton) - { - this->skeleton_set.erase(this->character_skeleton); - this->character_skeleton->queue_free(); - } - this->character_skeleton = cs; - this->skeleton_set.emplace(this->character_skeleton); - this->_rebuild_mesh_instances(); - } -} - - -void Character3D::take_damage(const HurtboxDamage &hit_data) -{ - if (this->_hurtbox_queue.empty()) - CALL_NEXT_FRAME(callable_mp(this, &Character3D::_next_hurtbox_in_queue)); - this->_hurtbox_queue.emplace_back(hit_data); -} - -void Character3D::_next_hurtbox_in_queue() -{ - HurtboxDamage &hit_data = this->_hurtbox_queue.front(); - if (!hit_data.bones.empty()) - { - CharacterSkeleton3D *character_skeleton = hit_data.bones.begin()->first; - const std::vector &bone_hits = hit_data.bones.begin()->second; - hit_data.bones.erase(this->_hurtbox_queue.begin()->bones.begin()); - - Skeleton3D *skeleton = character_skeleton->get_skeleton(); - const Plane &local_slice_plane = skeleton->get_global_transform().inverse().xform(Plane(hit_data.global_hit_transform.get_basis().get_column(1), hit_data.global_hit_transform.origin)); - - TypedArray sliced_mesh_instances = character_skeleton->get_mesh_instances(); - const uint32_t num_mesh_instances = sliced_mesh_instances.size(); - TypedArray first_half_meshes_to_add; - TypedArray other_half_meshes_to_add; - - Ref input; input.instantiate(); - input->mesh_instance = cast_to(sliced_mesh_instances[0]); - input->skeleton = skeleton; - input->full_aabb = this->_full_aabb; - input->local_plane = local_slice_plane; - input->cap_option = MeshEditingLibrary::CAP_UV_FILL_MESH_BOUNDS; - input->cap_materials = this->inner_materials; - this->_slice_meshes(input); - } - else - { - this->_hurtbox_queue.erase(this->_hurtbox_queue.begin()); - } - - if (!this->_hurtbox_queue.empty()) - CALL_NEXT_FRAME(callable_mp(this, &Character3D::_next_hurtbox_in_queue)); -} - -void Character3D::_slice_complete(Ref data) -{ - if (data->main_half->get_surface_count() <= 0 && data->secondary_half->get_surface_count() > 0) - { - Ref temp = data->main_half; - data->main_half = data->secondary_half; - data->secondary_half = temp; - } - - TypedArray first_half_meshes_to_add; - TypedArray other_half_meshes_to_add; - - if (data->main_half->get_surface_count() > 0) - { - first_half_meshes_to_add.append(data->main_half); - } - - if (data->secondary_half->get_surface_count() > 0) - { - other_half_meshes_to_add.append(data->secondary_half); - } - - for (CharacterSkeleton3D *skeleton : this->skeleton_set) - { - if (skeleton->get_skeleton() == data->original_skeleton) - { - skeleton->set_meshes(first_half_meshes_to_add); - break; - } - } - - if (other_half_meshes_to_add.size() > 0) - { - RandomNumberGenerator *rng = memnew(RandomNumberGenerator); - const float random_x = rng->randf_range(-0.01f, 0.01f); - const float random_z = rng->randf_range(-0.01f, 0.01f); - - CharacterSkeleton3D *new_character_skeleton = cast_to(this->character_skeleton->duplicate()); - this->character_skeleton->add_sibling(new_character_skeleton); - new_character_skeleton->set_position(Vector3(random_x, 0.0f, random_z)); - - PRINT_LOG(CHARACTER3D_TAG, "Adding new meshes:"); - for (uint16_t i = 0; i < other_half_meshes_to_add.size(); i++) - { - const Ref &new_array_mesh = other_half_meshes_to_add[i]; - PRINT_LOG(CHARACTER3D_TAG, " Mesh ", i+1, " size: ", new_array_mesh->get_surface_count()); - } - new_character_skeleton->set_meshes(other_half_meshes_to_add); - - this->skeleton_set.emplace(new_character_skeleton); - } -} - - -void Character3D::_on_fell_out_of_world() -{ - this->emit_signal("fell_out_of_world"); -} - - -void Character3D::_rebuild_mesh_instances() -{ - if (!this->character_skeleton || !this->is_node_ready()) - return; - - TypedArray mesh_instances = this->character_skeleton->set_meshes(this->meshes); - const uint32_t num_meshes = mesh_instances.size(); - this->_full_aabb = AABB(); - for (uint32_t i = 0; i < num_meshes; i++) - { - const MeshInstance3D *mesh_instance = cast_to(mesh_instances[i]); - - const Vector3 &full_aabb_position = this->_full_aabb.get_position(); - const Vector3 &full_aabb_size = this->_full_aabb.get_size(); - - const AABB &aabb = mesh_instance->get_aabb(); - const Vector3 &aabb_position = aabb.get_position(); - const Vector3 &aabb_size = aabb.get_size(); - - this->_full_aabb.set_position(Vector3( - UtilityFunctions::minf(full_aabb_position.x, aabb_position.x), - UtilityFunctions::minf(full_aabb_position.y, aabb_position.y), - UtilityFunctions::minf(full_aabb_position.z, aabb_position.z) - )); - - this->_full_aabb.set_size(Vector3( - UtilityFunctions::maxf(full_aabb_position.x, aabb_size.x), - UtilityFunctions::maxf(full_aabb_position.y, aabb_size.y), - UtilityFunctions::maxf(full_aabb_position.z, aabb_size.z) - )); - } -} diff --git a/src/liborng/nodes/damageables/character_3d.cpp b/src/liborng/nodes/damageables/character_3d.cpp new file mode 100644 index 0000000..bb5efbf --- /dev/null +++ b/src/liborng/nodes/damageables/character_3d.cpp @@ -0,0 +1,145 @@ +/* + * ©2026 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 "character_3d.h" + +#include "orng_macros.h" + +#include "nodes/debug_draw3d.h" +#include "nodes/hurtbox/hurtbox_base.h" +#include "singletons/mesh_editing_library.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace godot; + + +void Character3D::set_character_skeleton(CharacterSkeleton3D *cs) +{ + if (cs) + { + if (this->character_skeleton) + { + this->_skeleton_set.erase(this->character_skeleton); + this->character_skeleton->queue_free(); + } + this->character_skeleton = cs; + this->_skeleton_set.emplace(this->character_skeleton); + this->_rebuild_mesh_instance(); + } +} + + +void Character3D::take_damage(const HurtboxDamage &hit_data) +{ + if (this->_hurtbox_queue.empty()) + CALL_NEXT_FRAME(callable_mp(this, &Character3D::_next_hurtbox_in_queue)); + this->_hurtbox_queue.emplace_back(hit_data); +} + +void Character3D::_next_hurtbox_in_queue() +{ + HurtboxDamage &hit_data = this->_hurtbox_queue.front(); + if (!hit_data.bones.empty()) + { + CharacterSkeleton3D *character_skeleton = hit_data.bones.begin()->first; + const std::vector &bone_hits = hit_data.bones.begin()->second; + hit_data.bones.erase(this->_hurtbox_queue.begin()->bones.begin()); + + Skeleton3D *skeleton = character_skeleton->get_skeleton(); + const Plane &local_slice_plane = skeleton->get_global_transform().inverse().xform(Plane(hit_data.global_hit_transform.get_basis().get_column(1), hit_data.global_hit_transform.origin)); + + Ref input; input.instantiate(); + input->mesh_instance = character_skeleton->get_mesh_instance(); + input->skeleton = skeleton; + input->full_aabb = this->_aabb; + input->local_plane = local_slice_plane; + input->cap_option = MeshEditingLibrary::CAP_UV_FILL_MESH_BOUNDS; + input->cap_materials = this->inner_materials; + this->_slice_meshes(input); + } + else + { + this->_hurtbox_queue.erase(this->_hurtbox_queue.begin()); + } + + if (!this->_hurtbox_queue.empty()) + CALL_NEXT_FRAME(callable_mp(this, &Character3D::_next_hurtbox_in_queue)); +} + +void Character3D::_slice_complete(Ref data) +{ + if (data->main_half->get_surface_count() <= 0) + { + if (data->secondary_half->get_surface_count() <= 0) + { + return; + } + + Ref temp = data->main_half; + data->main_half = data->secondary_half; + data->secondary_half = temp; + } + + if (data->original_skeleton) + { + for (CharacterSkeleton3D *skeleton : this->_skeleton_set) + { + if (skeleton->get_skeleton() == data->original_skeleton) + { + MeshInstance3D *new_mesh_instance = memnew(MeshInstance3D); + new_mesh_instance->set_mesh(data->main_half); + skeleton->set_mesh(new_mesh_instance); + break; + } + } + if (data->secondary_half.is_valid()) + { + RandomNumberGenerator *rng = memnew(RandomNumberGenerator); + const float random_x = rng->randf_range(-0.01f, 0.01f); + const float random_z = rng->randf_range(-0.01f, 0.01f); + + CharacterSkeleton3D *new_character_skeleton = cast_to(this->character_skeleton->duplicate()); + this->character_skeleton->add_sibling(new_character_skeleton); + new_character_skeleton->set_position(Vector3(random_x, 0.0f, random_z)); + + MeshInstance3D *new_mesh_instance = memnew(MeshInstance3D); + new_mesh_instance->set_mesh(data->secondary_half); + new_character_skeleton->set_mesh(new_mesh_instance); + + this->_skeleton_set.emplace(new_character_skeleton); + } + } + else + { + + } +} + + +void Character3D::_on_fell_out_of_world() +{ + this->emit_signal("fell_out_of_world"); +} + + +MeshInstance3D *Character3D::_rebuild_mesh_instance() +{ + if (MeshInstance3D *returned_instance = DamageableNode3D::_rebuild_mesh_instance()) + { + this->character_skeleton->set_mesh(returned_instance); + return returned_instance; + } + return nullptr; +} diff --git a/src/liborng/nodes/character_nodes/character_3d.h b/src/liborng/nodes/damageables/character_3d.h similarity index 61% rename from src/liborng/nodes/character_nodes/character_3d.h rename to src/liborng/nodes/damageables/character_3d.h index 6085198..90ce3d8 100644 --- a/src/liborng/nodes/character_nodes/character_3d.h +++ b/src/liborng/nodes/damageables/character_3d.h @@ -9,8 +9,8 @@ #include "orng_macros.h" -#include "interfaces/i_damageable.h" -#include "nodes/character_nodes/character_skeleton3d.h" +#include "nodes/damageables/damageable_node3d.h" +#include "nodes/damageables/character_skeleton3d.h" #include #include @@ -25,42 +25,30 @@ using namespace godot; #define CHARACTER3D_TAG "Character3D" -class Character3D : public IDamageable +class Character3D : public DamageableNode3D { - GDCLASS(Character3D, IDamageable); + GDCLASS(Character3D, DamageableNode3D); public: - virtual void _ready() override; - virtual void take_damage(const HurtboxDamage &hit_data) override; - TypedArray get_meshes() const { return this->meshes; } CharacterSkeleton3D *get_character_skeleton() const { return this->character_skeleton; } - TypedArray get_inner_materials() const { return this->inner_materials; } - virtual void _on_fell_out_of_world(); protected: - void set_meshes(TypedArray m); void set_character_skeleton(CharacterSkeleton3D *cs); - void set_inner_materials(TypedArray materials) { this->inner_materials = materials; } + virtual MeshInstance3D *_rebuild_mesh_instance() override; private: - void _rebuild_mesh_instances(); void _next_hurtbox_in_queue(); virtual void _slice_complete(Ref data) override; - TypedArray meshes; CharacterSkeleton3D *character_skeleton = nullptr; - std::unordered_set skeleton_set; - - AABB _full_aabb; - TypedArray inner_materials; - + std::unordered_set _skeleton_set; std::vector _hurtbox_queue; // Godot boilerplate below @@ -71,8 +59,6 @@ protected: ADD_SIGNAL(MethodInfo("fell_out_of_world")); - ADD_GETTER_SETTER_HINTED(Character3D, meshes, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, "ArrayMesh"); ADD_GETTER_SETTER_HINTED(Character3D, character_skeleton, Variant::OBJECT, PROPERTY_HINT_NODE_TYPE, "CharacterSkeleton3D"); - ADD_GETTER_SETTER_HINTED(Character3D, inner_materials, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, "Material"); } }; diff --git a/src/liborng/nodes/character_nodes/character_skeleton3d.cpp b/src/liborng/nodes/damageables/character_skeleton3d.cpp similarity index 55% rename from src/liborng/nodes/character_nodes/character_skeleton3d.cpp rename to src/liborng/nodes/damageables/character_skeleton3d.cpp index 2c805cd..9888577 100644 --- a/src/liborng/nodes/character_nodes/character_skeleton3d.cpp +++ b/src/liborng/nodes/damageables/character_skeleton3d.cpp @@ -16,34 +16,23 @@ using namespace godot; -TypedArray CharacterSkeleton3D::set_meshes(TypedArray meshes) +void CharacterSkeleton3D::set_mesh(MeshInstance3D *mesh) { if (this->skeleton) { - this->_old_mesh_instances = this->_mesh_instances.duplicate(true); - this->_mesh_instances.clear(); + this->_old_mesh_instance = this->_mesh_instance; - uint16_t num_new_meshes = meshes.size(); - this->_mesh_instances.resize(num_new_meshes); - for (uint16_t i = 0; i < num_new_meshes; i++) - { - MeshInstance3D *mesh_instance = memnew(MeshInstance3D); - mesh_instance->set_mesh(meshes[i]); - mesh_instance->set_skeleton_path(this->skeleton->get_path()); - this->skeleton->add_child(mesh_instance, false, Node::INTERNAL_MODE_BACK); - this->_mesh_instances[i] = mesh_instance; - } + this->_mesh_instance = mesh; + _mesh_instance->set_skeleton_path(this->skeleton->get_path()); + this->skeleton->add_child(this->_mesh_instance, false, Node::INTERNAL_MODE_BACK); - CALL_NEXT_FRAME(callable_mp(this, &CharacterSkeleton3D::_delete_old_meshes)); + CALL_NEXT_FRAME(callable_mp(this, &CharacterSkeleton3D::_delete_old_mesh)); } - - return this->_mesh_instances; } -void CharacterSkeleton3D::_delete_old_meshes() +void CharacterSkeleton3D::_delete_old_mesh() { - for (uint16_t i = 0; i < this->_old_mesh_instances.size(); i++) - cast_to(this->_old_mesh_instances[i])->queue_free(); - this->_old_mesh_instances.clear(); + if (this->_old_mesh_instance) + this->_old_mesh_instance->queue_free(); } diff --git a/src/liborng/nodes/character_nodes/character_skeleton3d.h b/src/liborng/nodes/damageables/character_skeleton3d.h similarity index 88% rename from src/liborng/nodes/character_nodes/character_skeleton3d.h rename to src/liborng/nodes/damageables/character_skeleton3d.h index 731f10c..59cd3f9 100644 --- a/src/liborng/nodes/character_nodes/character_skeleton3d.h +++ b/src/liborng/nodes/damageables/character_skeleton3d.h @@ -26,15 +26,14 @@ class CharacterSkeleton3D : public Node3D GDCLASS(CharacterSkeleton3D, Node3D); public: - TypedArray set_meshes(TypedArray meshes); + void set_mesh(MeshInstance3D *mesh); Skeleton3D *get_skeleton() const { return this->skeleton; } PhysicalBoneSimulator3D *get_physical_bone_simulator() const { return this->physical_bone_simulator; } AnimationPlayer *get_animation_player() const { return this->animation_player; } AnimationTree *get_animation_tree() const { return this->animation_tree; } - void replace_mesh_instance(const uint16_t index, Ref new_mesh) { cast_to(this->_mesh_instances[index])->set_mesh(new_mesh); } - TypedArray get_mesh_instances() const { return this->_mesh_instances; } + MeshInstance3D *get_mesh_instance() const { return this->_mesh_instance; } protected: void set_skeleton(Skeleton3D *s) { this->skeleton = s; } @@ -44,7 +43,7 @@ protected: private: // void _refresh_skins_and_meshes(); - void _delete_old_meshes(); + void _delete_old_mesh(); void _activate_ragdoll(); @@ -53,8 +52,8 @@ private: AnimationPlayer *animation_player = nullptr; AnimationTree *animation_tree = nullptr; - TypedArray _mesh_instances; - TypedArray _old_mesh_instances; + MeshInstance3D *_mesh_instance = nullptr; + MeshInstance3D *_old_mesh_instance = nullptr; // Godot boilerplate below protected: diff --git a/src/liborng/interfaces/i_damageable.cpp b/src/liborng/nodes/damageables/damageable_node3d.cpp similarity index 57% rename from src/liborng/interfaces/i_damageable.cpp rename to src/liborng/nodes/damageables/damageable_node3d.cpp index 8b59532..adee74e 100644 --- a/src/liborng/interfaces/i_damageable.cpp +++ b/src/liborng/nodes/damageables/damageable_node3d.cpp @@ -5,7 +5,7 @@ * considered to be protected by the MIT licence. Have fun with it. */ -#include "i_damageable.h" +#include "damageable_node3d.h" #include "orng_macros.h" @@ -13,7 +13,20 @@ #include "godot_cpp/classes/scene_tree_timer.hpp" -void IDamageable::_slice_meshes(Ref data) +void DamageableNode3D::_ready() +{ + this->_rebuild_mesh_instance(); +} + +void DamageableNode3D::set_mesh(Ref m) +{ + this->mesh = m; + this->_aabb = AABB(); + this->_rebuild_mesh_instance(); +} + + +void DamageableNode3D::_slice_meshes(Ref data) { this->_is_slicing = true; if (this->_validate_input(data)) @@ -24,17 +37,17 @@ void IDamageable::_slice_meshes(Ref data) } } -void IDamageable::_new_slice_loop() +void DamageableNode3D::_new_slice_loop() { Ref next_in_queue = this->_input_queue.front(); - Ref new_state = MeshEditingLibrary::slice_mesh(next_in_queue); + Ref new_state = MeshEditingLibrary::slice_mesh(next_in_queue); this->_slice_state_queue.emplace_back(new_state); this->_slice_loop(); } -void IDamageable::_slice_loop() +void DamageableNode3D::_slice_loop() { - Ref current_state = this->_slice_state_queue.front(); + Ref current_state = this->_slice_state_queue.front(); switch(current_state->current_stage) { case MeshEditingLibrary::MeshEditSliceStage::SLICE_STAGE_INIT: @@ -50,22 +63,29 @@ void IDamageable::_slice_loop() if (MeshEditingLibrary::slice_mesh_apply_cap(current_state) > MESH_EDITING_SLICE_LOOP_LIMIT) break; case MeshEditingLibrary::MeshEditSliceStage::SLICE_STAGE_COMPLETE: - this->_slice_complete(current_state->output); - this->_input_queue.erase(this->_input_queue.begin()); - this->_slice_state_queue.erase(this->_slice_state_queue.begin()); - if (!this->_input_queue.empty()) + if (this->_slice_loop_end(current_state)) return this->_new_slice_loop(); break; } if (!this->_input_queue.empty()) - CALL_NEXT_FRAME(callable_mp(this, &IDamageable::_slice_loop)); + CALL_NEXT_FRAME(callable_mp(this, &DamageableNode3D::_slice_loop)); else this->_is_slicing = false; } +bool DamageableNode3D::_slice_loop_end(Ref current_state) +{ + this->_slice_complete(current_state->output); + this->_input_queue.erase(this->_input_queue.begin()); + this->_slice_state_queue.erase(this->_slice_state_queue.begin()); + if (!this->_input_queue.empty()) + return true; + return false; +} -bool IDamageable::_validate_input(Ref input) + +bool DamageableNode3D::_validate_input(Ref input) { if (this->_input_queue.size() >= MAX_SLICE_QUEUE_SIZE) { @@ -82,3 +102,16 @@ bool IDamageable::_validate_input(Ref input) return true; } + + +MeshInstance3D *DamageableNode3D::_rebuild_mesh_instance() +{ + if (!this->is_node_ready()) + return nullptr; + + this->_active_mesh_instance = memnew(MeshInstance3D); + this->_active_mesh_instance->set_mesh(this->mesh); + this->_aabb = this->_active_mesh_instance->get_aabb(); + + return this->_active_mesh_instance; +} diff --git a/src/liborng/nodes/damageables/damageable_node3d.h b/src/liborng/nodes/damageables/damageable_node3d.h new file mode 100644 index 0000000..d8940e7 --- /dev/null +++ b/src/liborng/nodes/damageables/damageable_node3d.h @@ -0,0 +1,67 @@ +/* + * ©2026 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 "orng_macros.h" +#include "singletons/mesh_editing_library.h" + +#include "godot_cpp/classes/node.hpp" +using namespace godot; + +#define MAX_SLICE_QUEUE_SIZE 3 + +#define DAMAGEABLE_NODE3D_TAG "DamageableNode3D" + + +class DamageableNode3D : public Node3D +{ + GDCLASS(DamageableNode3D, Node3D); + +public: + virtual void _ready() override; + + Ref get_mesh() const { return this->mesh; } + TypedArray get_inner_materials() const { return this->inner_materials; } + + virtual void take_damage(const struct HurtboxDamage &hit_data) = 0; + +protected: + void set_mesh(Ref m); + void set_inner_materials(TypedArray materials) { this->inner_materials = materials; } + + virtual MeshInstance3D *_rebuild_mesh_instance(); + + void _slice_meshes(Ref data); + virtual void _slice_complete(Ref data) = 0; + + Ref mesh; + TypedArray inner_materials; + + AABB _aabb; + +private: + void _new_slice_loop(); + void _slice_loop(); + bool _slice_loop_end(Ref current_state); + + bool _validate_input(Ref input); + + MeshInstance3D *_active_mesh_instance = nullptr; + + bool _is_slicing = false; + std::vector> _slice_state_queue; + std::vector> _input_queue; + + // Godot boilerplate below +protected: + static void _bind_methods() + { + ADD_GETTER_SETTER_HINTED(DamageableNode3D, mesh, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "ArrayMesh"); + ADD_GETTER_SETTER_HINTED(DamageableNode3D, inner_materials, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, "Material"); + } +}; diff --git a/src/liborng/nodes/damageables/sliceable_rigid_body3d.cpp b/src/liborng/nodes/damageables/sliceable_rigid_body3d.cpp new file mode 100644 index 0000000..3b196ef --- /dev/null +++ b/src/liborng/nodes/damageables/sliceable_rigid_body3d.cpp @@ -0,0 +1,14 @@ +/* + * ©2026 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 "sliceable_rigid_body3d.h" + +#include "orng_macros.h" + +#include +#include +using namespace godot; diff --git a/src/liborng/nodes/damageables/sliceable_rigid_body3d.h b/src/liborng/nodes/damageables/sliceable_rigid_body3d.h new file mode 100644 index 0000000..c579763 --- /dev/null +++ b/src/liborng/nodes/damageables/sliceable_rigid_body3d.h @@ -0,0 +1,39 @@ +/* + * ©2026 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 "orng_macros.h" + +#include "nodes/damageables/damageable_node3d.h" + +#include +#include + +#include +#include +using namespace godot; + +#define SLICEABLE_RIGID_BODY3D_TAG "SliceableRigidBody3D" + + +class SliceableRigidBody3D : public DamageableNode3D +{ + GDCLASS(SliceableRigidBody3D, DamageableNode3D); + +public: + virtual void take_damage(const HurtboxDamage &hit_data) override {} + +protected: + +private: + virtual void _slice_complete(Ref data) override {} + +// Godot boilerplate below +protected: + static void _bind_methods(){} +}; diff --git a/src/liborng/nodes/hurtbox/hurtbox_base.cpp b/src/liborng/nodes/hurtbox/hurtbox_base.cpp index ac96e83..339c6fa 100644 --- a/src/liborng/nodes/hurtbox/hurtbox_base.cpp +++ b/src/liborng/nodes/hurtbox/hurtbox_base.cpp @@ -8,7 +8,7 @@ #include "hurtbox_base.h" #include "orng_macros.h" -#include "nodes/character_nodes/character_3d.h" +#include "nodes/damageables/character_3d.h" #include #include @@ -60,7 +60,7 @@ void HurtboxBase::_physics_process(double delta) const TypedArray &bodies = this->get_overlapping_bodies(); const uint32_t num_bodies = bodies.size(); - std::unordered_set new_hit_bodies; + std::unordered_set new_hit_bodies; std::vector new_hit_skeletons; for (uint32_t i = 0; i < num_bodies; i++) @@ -99,7 +99,7 @@ void HurtboxBase::_physics_process(double delta) } } - for (IDamageable *damageable : new_hit_bodies) + for (DamageableNode3D *damageable : new_hit_bodies) this->hit_bodies.emplace_back(damageable); if (this->remaining_life <= 0.0f) @@ -112,7 +112,7 @@ void HurtboxBase::_send_next_in_queue() { for (uint8_t i = 0; i < MAX_HITS_PER_QUEUE_LOOP && !this->hit_data_queue.empty(); i++) { - IDamageable *closest_damageable = nullptr; + DamageableNode3D *closest_damageable = nullptr; float shortest_distance_from_hurtbox = FLT_MAX; bool found_character_3d = false; for (const auto &[damageable, hit_data_list] : this->hit_data_queue) diff --git a/src/liborng/nodes/hurtbox/hurtbox_base.h b/src/liborng/nodes/hurtbox/hurtbox_base.h index ba9557c..edf6ca3 100644 --- a/src/liborng/nodes/hurtbox/hurtbox_base.h +++ b/src/liborng/nodes/hurtbox/hurtbox_base.h @@ -9,8 +9,8 @@ #include "orng_macros.h" -#include "nodes/character_nodes/character_3d.h" -#include "nodes/character_nodes/character_skeleton3d.h" +#include "nodes/damageables/character_3d.h" +#include "nodes/damageables/character_skeleton3d.h" #include #include @@ -50,7 +50,7 @@ public: int32_t get_hit_points() const { return this->hit_points; } protected: - std::map> hit_data_queue; + std::map> hit_data_queue; private: Dictionary _create_hit_data_struct() const; @@ -63,7 +63,7 @@ private: float remaining_life = 0.0f; int32_t hit_points = 0; - std::vector hit_bodies; + std::vector hit_bodies; // Godot boilerplate below diff --git a/src/liborng/register_types.cpp b/src/liborng/register_types.cpp index cde42be..1a9fb71 100644 --- a/src/liborng/register_types.cpp +++ b/src/liborng/register_types.cpp @@ -21,17 +21,17 @@ #include "nodes/player_spawn.h" #include "nodes/sublevel_loader.h" #include "nodes/sublevel_scene.h" -#include "nodes/character_nodes/character_3d.h" -#include "nodes/character_nodes/character_skeleton3d.h" +#include "nodes/damageables/character_3d.h" +#include "nodes/damageables/character_skeleton3d.h" +#include "nodes/damageables/sliceable_rigid_body3d.h" #include "nodes/hurtbox/hurtbox_base.h" #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/mesh_editing_library.h" #include "singletons/save_manager.h" #include "singletons/scene_loader.h" // #include "singletons/vendor_service/vendor_service.h" @@ -66,7 +66,10 @@ void initialize_orng_module(ModuleInitializationLevel p_level) { ClassDB::register_class(); + ClassDB::register_abstract_class(); ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -80,7 +83,7 @@ void initialize_orng_module(ModuleInitializationLevel p_level) { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); - ClassDB::register_class(); + ClassDB::register_class(); GDSINGLETON_REGISTER_CLASS(InputHandler, _input_handler_singleton); GDSINGLETON_REGISTER_CLASS(SaveManager, _save_manager_singleton); diff --git a/src/liborng/nodes/mesh_editing_library.cpp b/src/liborng/singletons/mesh_editing_library.cpp similarity index 96% rename from src/liborng/nodes/mesh_editing_library.cpp rename to src/liborng/singletons/mesh_editing_library.cpp index 69bd558..c66cecd 100644 --- a/src/liborng/nodes/mesh_editing_library.cpp +++ b/src/liborng/singletons/mesh_editing_library.cpp @@ -16,9 +16,9 @@ using namespace godot; -Ref MeshEditingLibrary::slice_mesh(const Ref input) +Ref MeshEditingLibrary::slice_mesh(const Ref input) { - Ref fresh_state; + Ref fresh_state; fresh_state.instantiate(); fresh_state->input = input; @@ -31,7 +31,7 @@ Ref MeshEditingLibrary::slice_mesh(const Ref return fresh_state; } -float MeshEditingLibrary::slice_mesh_init(Ref state) +float MeshEditingLibrary::slice_mesh_init(Ref state) { const Time *time = Time::get_singleton(); const float start_time = time->get_ticks_msec(); @@ -140,7 +140,7 @@ float MeshEditingLibrary::slice_mesh_init(Ref state) return time->get_ticks_msec() - start_time; } -float MeshEditingLibrary::slice_mesh_plane_split(Ref state) +float MeshEditingLibrary::slice_mesh_plane_split(Ref state) { const Time *time = Time::get_singleton(); const float start_time = time->get_ticks_msec(); @@ -271,7 +271,7 @@ float MeshEditingLibrary::slice_mesh_plane_split(Ref state) return time->get_ticks_msec() - start_time; } -float MeshEditingLibrary::slice_mesh_edge_split(Ref state) +float MeshEditingLibrary::slice_mesh_edge_split(Ref state) { const Time *time = Time::get_singleton(); const float start_time = time->get_ticks_msec(); @@ -559,9 +559,12 @@ float MeshEditingLibrary::slice_mesh_edge_split(Ref state) } } - const Transform3D &interp_bone_transform = surface_skinned_vertex_transforms[base_v[this_vert]].interpolate_with( - surface_skinned_vertex_transforms[base_v[next_vert]], alpha); - surface_skinned_vertex_transforms.emplace_back(interp_bone_transform); + if (state->input->skeleton) + { + const Transform3D &interp_bone_transform = surface_skinned_vertex_transforms[base_v[this_vert]].interpolate_with( + surface_skinned_vertex_transforms[base_v[next_vert]], alpha); + surface_skinned_vertex_transforms.emplace_back(interp_bone_transform); + } } } @@ -650,7 +653,7 @@ float MeshEditingLibrary::slice_mesh_edge_split(Ref state) return time->get_ticks_msec() - start_time; } -float MeshEditingLibrary::slice_mesh_apply_cap(Ref state) +float MeshEditingLibrary::slice_mesh_apply_cap(Ref state) { const Time *time = Time::get_singleton(); const float start_time = time->get_ticks_msec(); @@ -698,8 +701,8 @@ float MeshEditingLibrary::slice_mesh_apply_cap(Ref state) for (const MeshEditVert2D &vertex : polygon_set[state->element].vertices) { const Vector3 &position = state->first_half_vertices[vertex.surface][vertex.index]; - - const Transform3D &skinned_vertex_transform = state->skinned_vertex_transforms[vertex.surface][vertex.index]; + // const Vector3 &skinned_position = state->skinned_vertex_positions[vertex.surface][vertex.index]; + // const Transform3D &skinned_vertex_transform = state->skinned_vertex_transforms[vertex.surface][vertex.index]; const Vector2 &uv = MeshEditingLibrary::calculate_planar_uv(position, mesh_bounds, uv_plane); surface_tool_first_half->set_uv(uv); @@ -811,6 +814,7 @@ SkinnedVertices MeshEditingLibrary::get_skinned_vertex_positions(const uint8_t s if (surface > num_surfaces) { PRINT_ERROR(MESH_EDITING_LIBRARY_TAG, "This mesh doesn't have that many surfaces! You wrote ", surface, ", but there's only ", num_surfaces, "here!"); + return SkinnedVertices(); } SkinnedVertices skinned_vertex_array; @@ -818,7 +822,7 @@ SkinnedVertices MeshEditingLibrary::get_skinned_vertex_positions(const uint8_t s if (skeleton) { const uint16_t bone_count = skeleton->get_bone_count(); - std::vector bone_transforms; + SkinnedVertexTransforms bone_transforms; bone_transforms.resize(bone_count); for (uint8_t bone_index = 0; bone_index < bone_count; bone_index++) { @@ -872,20 +876,17 @@ SkinnedVertices MeshEditingLibrary::get_skinned_vertex_positions(const uint8_t s } else { - for (uint8_t surface = 0; surface < num_surfaces; surface++) + const Array &mesh_arrays = mesh->surface_get_arrays(surface); + + const PackedVector3Array &mesh_vertex_array = mesh_arrays[ArrayMesh::ARRAY_VERTEX]; + const uint32_t mesh_vertex_array_size = mesh_vertex_array.size(); + + SkinnedVertices skinned_vertex_array; + skinned_vertex_array.resize(mesh_vertex_array_size); + + for (uint32_t vertex_index = 0; vertex_index < mesh_vertex_array_size; vertex_index++) { - const Array &mesh_arrays = mesh->surface_get_arrays(surface); - - const PackedVector3Array &mesh_vertex_array = mesh_arrays[ArrayMesh::ARRAY_VERTEX]; - const uint32_t mesh_vertex_array_size = mesh_vertex_array.size(); - - SkinnedVertices skinned_vertex_array; - skinned_vertex_array.resize(mesh_vertex_array_size); - - for (uint32_t vertex_index = 0; vertex_index < mesh_vertex_array_size; vertex_index++) - { - skinned_vertex_array[vertex_index] = mesh_vertex_array[vertex_index]; - } + skinned_vertex_array[vertex_index] = mesh_vertex_array[vertex_index]; } return skinned_vertex_array; diff --git a/src/liborng/nodes/mesh_editing_library.h b/src/liborng/singletons/mesh_editing_library.h similarity index 91% rename from src/liborng/nodes/mesh_editing_library.h rename to src/liborng/singletons/mesh_editing_library.h index 2a4854f..3e85161 100644 --- a/src/liborng/nodes/mesh_editing_library.h +++ b/src/liborng/singletons/mesh_editing_library.h @@ -24,6 +24,7 @@ using namespace godot; typedef PackedVector3Array SkinnedVertices; typedef std::vector SkinnedSurfaces; +typedef std::vector SkinnedVertexTransforms; struct MeshEditVert3D @@ -60,7 +61,7 @@ struct MeshEditPolygon2D class MeshEditSliceInput; class MeshEditSliceOutput; -class MeshSliceState; +class MeshEditSliceState; class MeshEditingLibrary : public Node @@ -87,12 +88,12 @@ public: CAP_UV_TILED }; - static Ref slice_mesh(const Ref input); + static Ref slice_mesh(const Ref input); - static float slice_mesh_init(Ref state); - static float slice_mesh_plane_split(Ref state); - static float slice_mesh_edge_split(Ref state); - static float slice_mesh_apply_cap(Ref state); + static float slice_mesh_init(Ref state); + static float slice_mesh_plane_split(Ref state); + static float slice_mesh_edge_split(Ref state); + static float slice_mesh_apply_cap(Ref state); protected: enum MeshSlicePlaneOrientation @@ -174,9 +175,9 @@ protected: }; -class MeshSliceState : public RefCounted +class MeshEditSliceState : public RefCounted { - GDCLASS(MeshSliceState, RefCounted); + GDCLASS(MeshEditSliceState, RefCounted); public: MeshEditingLibrary::MeshEditSliceStage current_stage = MeshEditingLibrary::MeshEditSliceStage::SLICE_STAGE_INIT; @@ -195,7 +196,7 @@ public: std::vector surface_arrays_list; std::vector skinned_vertex_positions; - std::vector> skinned_vertex_transforms; + std::vector skinned_vertex_transforms; std::vector has_normal; std::vector has_tangent;