diff --git a/src/liborng/interfaces/i_damageable.h b/src/liborng/interfaces/i_damageable.h index 05f1295..e38c3a5 100644 --- a/src/liborng/interfaces/i_damageable.h +++ b/src/liborng/interfaces/i_damageable.h @@ -7,12 +7,9 @@ #pragma once -#include -using namespace godot; - class IDamageable { public: - virtual void take_damage(const Dictionary &hit_data) = 0; + virtual void take_damage(const struct HurtboxDamage &hit_data) = 0; }; diff --git a/src/liborng/nodes/character_nodes/character_3d.cpp b/src/liborng/nodes/character_nodes/character_3d.cpp index bd33dd1..396ba28 100644 --- a/src/liborng/nodes/character_nodes/character_3d.cpp +++ b/src/liborng/nodes/character_nodes/character_3d.cpp @@ -11,9 +11,10 @@ #include "nodes/debug_draw3d.h" #include "nodes/mesh_editing_library.h" +#include "nodes/hurtbox/hurtbox_base.h" #include -#include +#include #include #include #include @@ -24,134 +25,82 @@ using namespace godot; void Character3D::_ready() { + this->skeleton_list.clear(); if (this->character_skeleton) { - if (const Skeleton3D *skeleton = this->character_skeleton->get_skeleton()) - { - TypedArray child_mesh_candidates = skeleton->get_children(); - const uint32_t num_children = child_mesh_candidates.size(); - - const Vector3 &full_aabb_position = this->full_aabb.get_position(); - const Vector3 &full_aabb_size = this->full_aabb.get_size(); - - for (uint32_t i = 0; i < num_children; i++) - { - if (MeshInstance3D *mesh_instance = cast_to(child_mesh_candidates[i])) - { - this->meshes.emplace_back(mesh_instance); - - 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) - )); - } - } - } - } -#ifdef DEBUG_ENABLED - else - { - PRINT_ERROR(CHARACTER3D_TAG, "No skeleton has been set, and there is currently no way to seek one out in the hierarchy. This will be a problem."); - } -#endif // DEBUG_ENABLED -} - - -void Character3D::take_damage(const Dictionary &hit_data) -{ - if (this->character_skeleton) - { - if (const Skeleton3D *skeleton = this->character_skeleton->get_skeleton()) - { - PRINT_LOG(CHARACTER3D_TAG, "Taking damage: ", hit_data["hit_points"]); - PRINT_LOG(CHARACTER3D_TAG, "Hit bones: ", hit_data["bone_hits"]); - - const Transform3D &global_hit_transform = (Transform3D)hit_data["hit_transform"]; - const Plane &slice_plane = skeleton->get_global_transform().inverse().xform(Plane(global_hit_transform.get_basis().get_column(1), global_hit_transform.origin)); - const uint32_t num_meshes = this->meshes.size(); - std::vector meshes_to_add; - std::vector meshes_to_remove; - for (uint32_t i = 0; i < num_meshes; i++) - { - MeshInstance3D *original_mesh_instance = this->meshes[i]; - Ref first_half; first_half.instantiate(); - Ref other_half; other_half.instantiate(); - PackedVector3Array &impact_points = MeshEditingLibrary::slice_mesh(original_mesh_instance, skeleton, this->full_aabb, slice_plane, first_half, other_half, MeshEditingLibrary::CAP_UV_FILL_MESH_BOUNDS, this->inner_materials); - - if (first_half.is_valid()) - { - original_mesh_instance->set_mesh(first_half); - meshes_to_add.emplace_back(original_mesh_instance); - } - - if (other_half.is_valid()) - { - RandomNumberGenerator *rng = memnew(RandomNumberGenerator); - const float random_x = rng->randf_range(-0.5f, 0.5f); - const float random_z = rng->randf_range(-0.5f, 0.5f); - - 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)); - - Skeleton3D *new_skeleton = new_character_skeleton->get_skeleton(); - const uint16_t child_count = new_skeleton->get_child_count(); - TypedArray children = new_skeleton->get_children(); - for (uint16_t child = 0; child < child_count; child++) - { - if (MeshInstance3D *new_mesh_instance = cast_to(children[child])) - { - new_mesh_instance->set_skeleton_path(new_mesh_instance->get_path_to(new_skeleton)); - new_mesh_instance->set_skin(original_mesh_instance->get_skin()); - new_mesh_instance->set_mesh(other_half); - meshes_to_add.emplace_back(new_mesh_instance); - break; - } - } - } - - meshes_to_remove.emplace(meshes_to_remove.begin(), i); - } - - for (const uint32_t i : meshes_to_remove) - { - this->meshes.erase(this->meshes.begin() + i); - } - - for (MeshInstance3D *mesh : meshes_to_add) - { - this->meshes.emplace_back(mesh); - } - } + this->_rebuild_mesh_instances(); } } - -void Character3D::_activate_ragdoll() +void Character3D::set_meshes(TypedArray m) { - if (this->character_skeleton) + this->meshes = m; + this->full_aabb = AABB(); + this->_rebuild_mesh_instances(); +} + +void Character3D::set_character_skeleton(CharacterSkeleton3D *cs) +{ + this->character_skeleton = cs; + this->_rebuild_mesh_instances(); +} + + +void Character3D::take_damage(const HurtboxDamage &hit_data) +{ + for (const auto &[character_skeleton, bones] : hit_data.bones) { - if (PhysicalBoneSimulator3D *physical_bone_simulator = this->character_skeleton->get_physical_bone_simulator()) + 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)); + + std::vector 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; + for (uint32_t i = 0; i < num_mesh_instances; i++) { - const TypedArray &physical_bones = physical_bone_simulator->get_children(); - const uint32_t num_physical_bones = physical_bones.size(); - for (uint32_t i = 0; i < num_physical_bones; i++) + MeshInstance3D *original_mesh_instance = sliced_mesh_instances[i]; + Ref first_half; first_half.instantiate(); + Ref other_half; other_half.instantiate(); + PackedVector3Array &impact_points = MeshEditingLibrary::slice_mesh(original_mesh_instance, skeleton, this->full_aabb, local_slice_plane, first_half, other_half, MeshEditingLibrary::CAP_UV_FILL_MESH_BOUNDS, this->inner_materials); + + if (first_half->get_surface_count() <= 0 && other_half->get_surface_count() > 0) { - if (PhysicalBone3D *bone = cast_to(physical_bones[i])) - { - bone->set_collision_layer(0); - } + Ref temp = first_half; + first_half = other_half; + other_half = temp; } - physical_bone_simulator->physical_bones_start_simulation(); + + if (first_half->get_surface_count() > 0) + { + first_half_meshes_to_add.append(first_half); + } + + if (other_half->get_surface_count() > 0) + { + other_half_meshes_to_add.append(other_half); + } + } + + character_skeleton->set_meshes(first_half_meshes_to_add); + if (other_half_meshes_to_add.size() > 0) + { + RandomNumberGenerator *rng = memnew(RandomNumberGenerator); + const float random_x = rng->randf_range(-0.05f, 0.05f); + const float random_z = rng->randf_range(-0.05f, 0.05f); + + 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); } } } @@ -161,3 +110,41 @@ void Character3D::_on_fell_out_of_world() { this->emit_signal("fell_out_of_world"); } + + +void Character3D::_rebuild_mesh_instances() +{ + if (!this->is_node_ready()) return; + + uint16_t num_mesh_instances = this->_mesh_instances.size(); + for (uint16_t i = 0; i < num_mesh_instances; i++) + { + this->_mesh_instances[i]->queue_free(); + } + + if (!this->character_skeleton) return; + + this->_mesh_instances = this->character_skeleton->set_meshes(this->meshes); + const uint32_t num_meshes = this->_mesh_instances.size(); + for (const MeshInstance3D *mesh_instance : this->_mesh_instances) + { + 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/character_nodes/character_3d.h b/src/liborng/nodes/character_nodes/character_3d.h index 55d1d40..9c878fe 100644 --- a/src/liborng/nodes/character_nodes/character_3d.h +++ b/src/liborng/nodes/character_nodes/character_3d.h @@ -16,6 +16,7 @@ #include #include #include +#include #include using namespace godot; @@ -29,8 +30,9 @@ class Character3D : public CharacterBody3D, public IDamageable public: virtual void _ready() override; - virtual void take_damage(const Dictionary &hit_data) 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; } @@ -38,19 +40,22 @@ public: virtual void _on_fell_out_of_world(); protected: - void set_character_skeleton(CharacterSkeleton3D *cs) { this->character_skeleton = cs; } + void set_meshes(TypedArray m); + void set_character_skeleton(CharacterSkeleton3D *cs); void set_inner_materials(TypedArray materials) { this->inner_materials = materials; } private: - void _activate_ragdoll(); + void _rebuild_mesh_instances(); + TypedArray meshes; CharacterSkeleton3D *character_skeleton = nullptr; AABB full_aabb; TypedArray inner_materials; - std::vector meshes; + std::vector _mesh_instances; + std::vector skeleton_list; // Godot boilerplate below protected: @@ -60,10 +65,8 @@ protected: ADD_SIGNAL(MethodInfo("fell_out_of_world")); - ClassDB::bind_method(D_METHOD("get_character_skeleton"), &Character3D::get_character_skeleton); - ClassDB::bind_method(D_METHOD("set_character_skeleton", "character_skeleton"), &Character3D::set_character_skeleton); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "character_skeleton", PROPERTY_HINT_NODE_TYPE, "CharacterSkeleton3D"), "set_character_skeleton", "get_character_skeleton"); - + 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/character_nodes/character_skeleton3d.cpp index 75d2a30..d8ff732 100644 --- a/src/liborng/nodes/character_nodes/character_skeleton3d.cpp +++ b/src/liborng/nodes/character_nodes/character_skeleton3d.cpp @@ -14,6 +14,33 @@ using namespace godot; +std::vector CharacterSkeleton3D::set_meshes(TypedArray meshes) +{ + if (this->skeleton) + { + uint16_t num_mesh_instances = this->_mesh_instances.size(); + for (uint16_t i = 0; i < num_mesh_instances; i++) + { + this->_mesh_instances[i]->queue_free(); + } + this->_mesh_instances.clear(); + + uint16_t num_new_meshes = meshes.size(); + for (uint16_t i = 0; i < num_new_meshes; i++) + { + MeshInstance3D *mesh_instance = memnew(MeshInstance3D); + this->skeleton->add_child(mesh_instance, false, Node::INTERNAL_MODE_BACK); + mesh_instance->set_owner(this->get_owner()); + mesh_instance->set_mesh(meshes[i]); + mesh_instance->set_skeleton_path(this->skeleton->get_path()); + this->_mesh_instances.emplace_back(mesh_instance); + } + } + + return this->_mesh_instances; +} + + void CharacterSkeleton3D::_activate_ragdoll() { if (this->physical_bone_simulator) diff --git a/src/liborng/nodes/character_nodes/character_skeleton3d.h b/src/liborng/nodes/character_nodes/character_skeleton3d.h index a16f7f4..8bd6b8f 100644 --- a/src/liborng/nodes/character_nodes/character_skeleton3d.h +++ b/src/liborng/nodes/character_nodes/character_skeleton3d.h @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include #include @@ -24,11 +26,16 @@ class CharacterSkeleton3D : public Node3D GDCLASS(CharacterSkeleton3D, Node3D); public: + std::vector set_meshes(TypedArray meshes); + 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) { this->_mesh_instances[index]->set_mesh(new_mesh); } + std::vector get_mesh_instances() const { return this->_mesh_instances; } + protected: void set_skeleton(Skeleton3D *s) { this->skeleton = s; } void set_physical_bone_simulator(PhysicalBoneSimulator3D *pbs) { this->physical_bone_simulator = pbs; } @@ -45,6 +52,8 @@ private: AnimationPlayer *animation_player = nullptr; AnimationTree *animation_tree = nullptr; + std::vector _mesh_instances; + // 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 cd717b4..7e5a65c 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 "character_3d.h" +#include "nodes/character_nodes/character_3d.h" #include #include @@ -24,7 +24,7 @@ void HurtboxBase::_enter_tree() { this->collision_shape = memnew(CollisionShape3D); this->collision_shape->set_shape(this->shape); - this->add_child(this->collision_shape); + this->add_child(this->collision_shape, false, Node::INTERNAL_MODE_BACK); } Area3D::_enter_tree(); @@ -58,44 +58,48 @@ void HurtboxBase::_physics_process(double delta) const TypedArray &bodies = this->get_overlapping_bodies(); const uint32_t num_bodies = bodies.size(); + std::vector new_hit_bodies; + std::vector new_hit_skeletons; + for (uint32_t i = 0; i < num_bodies; i++) { if (PhysicalBone3D *character_bone = cast_to(bodies[i])) { if (CharacterSkeleton3D *character_skeleton = cast_to(character_bone->get_owner())) { - Character3D *body = cast_to(character_skeleton->get_parent()); - if (body && !this->hit_character_bodies.has(body)) + if (Character3D *body = cast_to(character_skeleton->get_parent())) { - this->hit_character_bodies.append(body); - TypedArray bone_hits; - - for (uint32_t bone_index = 0; bone_index < num_bodies; bone_index++) + if (std::find(this->hit_bodies.begin(), this->hit_bodies.end(), body) == this->hit_bodies.end()) { - if (PhysicalBone3D *bone = cast_to(bodies[bone_index])) + new_hit_bodies.emplace_back(body); + + if (std::find(new_hit_skeletons.begin(), new_hit_skeletons.end(), character_skeleton) == new_hit_skeletons.end()) { - if (String(bone->get_path()).begins_with(String(body->get_path()))) + HurtboxDamage character_damage; + character_damage.hit_points = this->hit_points; + character_damage.global_hit_transform = this->get_global_transform(); + + for (uint32_t bone_index = 0; bone_index < num_bodies; bone_index++) { - bone_hits.append(bone->get_bone_id()); - PRINT_LOG(HURTBOX_BASE_TAG, "Bone hit: ", bone->get_name(), " (", bone->get_class(), " : ", bone->get_rid(), ")"); + if (PhysicalBone3D *bone = cast_to(bodies[bone_index])) + { + if (String(bone->get_path()).begins_with(character_skeleton->get_path())) + character_damage.bones[character_skeleton].emplace_back(bone->get_bone_id()); + } } + + this->hit_data_queue[body].emplace_back(character_damage); + new_hit_skeletons.emplace_back(character_skeleton); } } - - PRINT_LOG(HURTBOX_BASE_TAG, "Character hit: ", body->get_name(), " (", body->get_class(), " : ", body->get_rid(), ")"); - - Dictionary hit_data; - hit_data["body"] = body; - hit_data["bone_hits"] = bone_hits; - hit_data["hit_points"] = this->hit_points; - hit_data["hit_transform"] = this->get_global_transform(); - - this->hit_data_queue.emplace_back(hit_data); } } } } + for (IDamageable *damageable : new_hit_bodies) + this->hit_bodies.emplace_back(damageable); + if (this->remaining_life <= 0.0f) this->collision_shape->set_disabled(true); else @@ -106,18 +110,20 @@ void HurtboxBase::_send_next_in_queue() { if (!this->hit_data_queue.empty()) { - const Dictionary &hit_data = this->hit_data_queue.front(); - Character3D *body = cast_to(hit_data["body"]); - body->take_damage(hit_data); + const std::map>::iterator &first_in_queue = this->hit_data_queue.begin(); + for (const HurtboxDamage &damage : first_in_queue->second) + { + first_in_queue->first->take_damage(damage); + } this->hit_data_queue.erase(this->hit_data_queue.begin()); } - if (this->remaining_life > 0.0f) - { - CALL_NEXT_FRAME(callable_mp(this, &HurtboxBase::_send_next_in_queue)); - } - else if (this->hit_data_queue.empty()) + if (this->hit_data_queue.empty() && this->remaining_life <= 0.0f) { this->queue_free(); } + else + { + CALL_NEXT_FRAME(callable_mp(this, &HurtboxBase::_send_next_in_queue)); + } } diff --git a/src/liborng/nodes/hurtbox/hurtbox_base.h b/src/liborng/nodes/hurtbox/hurtbox_base.h index fb371f2..84ffb72 100644 --- a/src/liborng/nodes/hurtbox/hurtbox_base.h +++ b/src/liborng/nodes/hurtbox/hurtbox_base.h @@ -9,7 +9,8 @@ #include "orng_macros.h" -#include "character_3d.h" +#include "nodes/character_nodes/character_3d.h" +#include "nodes/character_nodes/character_skeleton3d.h" #include #include @@ -19,6 +20,14 @@ using namespace godot; #define HURTBOX_BASE_TAG "HurtboxBase" +struct HurtboxDamage +{ + std::map> bones; + int32_t hit_points = 0; + Transform3D global_hit_transform; +}; + + class HurtboxBase : public Area3D { GDCLASS(HurtboxBase, Area3D); @@ -39,7 +48,7 @@ public: int32_t get_hit_points() const { return this->hit_points; } protected: - std::vector hit_data_queue; + std::map> hit_data_queue; private: Dictionary _create_hit_data_struct() const; @@ -52,7 +61,7 @@ private: float remaining_life = 0.0f; int32_t hit_points = 0; - TypedArray hit_character_bodies; + std::vector hit_bodies; // Godot boilerplate below diff --git a/src/liborng/nodes/mesh_editing_library.cpp b/src/liborng/nodes/mesh_editing_library.cpp index 8d6dd5f..41f78a3 100644 --- a/src/liborng/nodes/mesh_editing_library.cpp +++ b/src/liborng/nodes/mesh_editing_library.cpp @@ -516,7 +516,8 @@ PackedVector3Array MeshEditingLibrary::slice_mesh(MeshInstance3D *original_mesh_ for (const MeshEditVert2D &vertex : polygon_set[polygon_index].vertices) { const Vector3 &position = first_half_vertices[vertex.surface][vertex.index]; - const Vector3 &local_plane_transformed_normal = skinned_vertex_transforms[vertex.surface][vertex.index].xform(local_plane_normal).normalized(); + const Transform3D &skinned_vertex_transform = skinned_vertex_transforms[vertex.surface][vertex.index]; + const Vector3 &local_plane_transformed_normal = skinned_vertex_transform.xform(local_plane_normal).normalized(); cap_section_vertices.append(position); if (surface_has_normal[vertex.surface]) @@ -607,9 +608,9 @@ PackedVector3Array MeshEditingLibrary::slice_mesh(MeshInstance3D *original_mesh_ if (cap_section_weights.size()) cap_other_mesh_section[ArrayMesh::ARRAY_WEIGHTS] = cap_section_weights; out_other_half->add_surface_from_arrays(ArrayMesh::PRIMITIVE_TRIANGLES, cap_other_mesh_section); out_other_half->surface_set_material(out_other_half->get_surface_count()-1, cap_materials[uv_plane]); - } - PRINT_LOG(MESH_EDITING_LIBRARY_TAG, "Generated final cap surface at ", time->get_ticks_msec(), "ms"); + PRINT_LOG(MESH_EDITING_LIBRARY_TAG, "Generated final cap surface at ", time->get_ticks_msec(), "ms"); + } return impact_points; }