diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 58377eef0f1a..6bf39072c2d9 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -188,7 +188,6 @@ #include "editor/plugins/shader_file_editor_plugin.h" #include "editor/plugins/skeleton_2d_editor_plugin.h" #include "editor/plugins/skeleton_3d_editor_plugin.h" -#include "editor/plugins/skeleton_ik_3d_editor_plugin.h" #include "editor/plugins/sprite_2d_editor_plugin.h" #include "editor/plugins/sprite_frames_editor_plugin.h" #include "editor/plugins/style_box_editor_plugin.h" @@ -7332,7 +7331,6 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(ShaderEditorPlugin)); add_editor_plugin(memnew(ShaderFileEditorPlugin)); add_editor_plugin(memnew(Skeleton3DEditorPlugin)); - add_editor_plugin(memnew(SkeletonIK3DEditorPlugin)); add_editor_plugin(memnew(SpriteFramesEditorPlugin)); add_editor_plugin(memnew(StyleBoxEditorPlugin)); add_editor_plugin(memnew(SubViewportPreviewEditorPlugin)); diff --git a/editor/plugins/skeleton_ik_3d_editor_plugin.cpp b/editor/plugins/skeleton_ik_3d_editor_plugin.cpp deleted file mode 100644 index 7dc34a0874b8..000000000000 --- a/editor/plugins/skeleton_ik_3d_editor_plugin.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/*************************************************************************/ -/* skeleton_ik_3d_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "skeleton_ik_3d_editor_plugin.h" - -#include "editor/editor_node.h" -#include "scene/3d/skeleton_ik_3d.h" - -void SkeletonIK3DEditorPlugin::_play() { - if (!skeleton_ik) { - return; - } - - if (!skeleton_ik->get_parent_skeleton()) { - return; - } - - if (play_btn->is_pressed()) { - skeleton_ik->start(); - } else { - skeleton_ik->stop(); - skeleton_ik->get_parent_skeleton()->clear_bones_global_pose_override(); - } -} - -void SkeletonIK3DEditorPlugin::edit(Object *p_object) { - if (p_object != skeleton_ik) { - if (skeleton_ik) { - play_btn->set_pressed(false); - _play(); - } - } - - SkeletonIK3D *s = Object::cast_to(p_object); - if (!s) { - return; - } - - skeleton_ik = s; -} - -bool SkeletonIK3DEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("SkeletonIK3D"); -} - -void SkeletonIK3DEditorPlugin::make_visible(bool p_visible) { - if (p_visible) { - play_btn->show(); - } else { - play_btn->hide(); - } -} - -void SkeletonIK3DEditorPlugin::_bind_methods() { -} - -SkeletonIK3DEditorPlugin::SkeletonIK3DEditorPlugin() { - play_btn = memnew(Button); - play_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); - play_btn->set_text(TTR("Play IK")); - play_btn->set_toggle_mode(true); - play_btn->hide(); - play_btn->connect("pressed", callable_mp(this, &SkeletonIK3DEditorPlugin::_play)); - add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, play_btn); - skeleton_ik = nullptr; -} - -SkeletonIK3DEditorPlugin::~SkeletonIK3DEditorPlugin() {} diff --git a/editor/plugins/skeleton_ik_3d_editor_plugin.h b/editor/plugins/skeleton_ik_3d_editor_plugin.h deleted file mode 100644 index 26aead6d6767..000000000000 --- a/editor/plugins/skeleton_ik_3d_editor_plugin.h +++ /dev/null @@ -1,61 +0,0 @@ -/*************************************************************************/ -/* skeleton_ik_3d_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SKELETON_IK_3D_EDITOR_PLUGIN_H -#define SKELETON_IK_3D_EDITOR_PLUGIN_H - -#include "editor/editor_plugin.h" - -class SkeletonIK3D; - -class SkeletonIK3DEditorPlugin : public EditorPlugin { - GDCLASS(SkeletonIK3DEditorPlugin, EditorPlugin); - - SkeletonIK3D *skeleton_ik = nullptr; - - Button *play_btn = nullptr; - - void _play(); - -protected: - static void _bind_methods(); - -public: - virtual String get_name() const override { return "SkeletonIK3D"; } - bool has_main_screen() const override { return false; } - virtual void edit(Object *p_object) override; - virtual bool handles(Object *p_object) const override; - virtual void make_visible(bool p_visible) override; - - SkeletonIK3DEditorPlugin(); - ~SkeletonIK3DEditorPlugin(); -}; - -#endif // SKELETON_IK_3D_EDITOR_PLUGIN_H diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 8f0bf2261779..c3eb88e3241f 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -540,30 +540,15 @@ Bone2D::~Bone2D() { bool Skeleton2D::_set(const StringName &p_path, const Variant &p_value) { String path = p_path; - - if (path.begins_with("modification_stack")) { - set_modification_stack(p_value); - return true; - } return true; } bool Skeleton2D::_get(const StringName &p_path, Variant &r_ret) const { String path = p_path; - - if (path.begins_with("modification_stack")) { - r_ret = get_modification_stack(); - return true; - } return true; } void Skeleton2D::_get_property_list(List *p_list) const { - p_list->push_back( - PropertyInfo(Variant::OBJECT, PNAME("modification_stack"), - PROPERTY_HINT_RESOURCE_TYPE, - "SkeletonModificationStack2D", - PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); } void Skeleton2D::_make_bone_setup_dirty() { @@ -670,24 +655,7 @@ void Skeleton2D::_notification(int p_what) { if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { RS::get_singleton()->skeleton_set_base_transform_2d(skeleton, get_global_transform()); - } else if (p_what == NOTIFICATION_INTERNAL_PROCESS) { - if (modification_stack.is_valid()) { - execute_modifications(get_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process); - } - } else if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { - if (modification_stack.is_valid()) { - execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process); - } } -#ifdef TOOLS_ENABLED - else if (p_what == NOTIFICATION_DRAW) { - if (Engine::get_singleton()->is_editor_hint()) { - if (modification_stack.is_valid()) { - modification_stack->draw_editor_gizmos(); - } - } - } -#endif // TOOLS_ENABLED } RID Skeleton2D::get_skeleton() const { @@ -706,80 +674,6 @@ Transform2D Skeleton2D::get_bone_local_pose_override(int p_bone_idx) { return bones[p_bone_idx].local_pose_override; } -void Skeleton2D::set_modification_stack(Ref p_stack) { - if (modification_stack.is_valid()) { - modification_stack->is_setup = false; - modification_stack->set_skeleton(nullptr); - - set_process_internal(false); - set_physics_process_internal(false); - } - modification_stack = p_stack; - if (modification_stack.is_valid()) { - modification_stack->set_skeleton(this); - modification_stack->setup(); - - set_process_internal(true); - set_physics_process_internal(true); - -#ifdef TOOLS_ENABLED - modification_stack->set_editor_gizmos_dirty(true); -#endif // TOOLS_ENABLED - } -} - -Ref Skeleton2D::get_modification_stack() const { - return modification_stack; -} - -void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) { - if (!modification_stack.is_valid()) { - return; - } - - // Do not cache the transform changes caused by the modifications! - for (int i = 0; i < bones.size(); i++) { - bones[i].bone->copy_transform_to_cache = false; - } - - if (modification_stack->skeleton != this) { - modification_stack->set_skeleton(this); - } - - modification_stack->execute(p_delta, p_execution_mode); - - // Only apply the local pose override on _process. Otherwise, just calculate the local_pose_override and reset the transform. - if (p_execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process) { - for (int i = 0; i < bones.size(); i++) { - if (bones[i].local_pose_override_amount > 0) { - bones[i].bone->set_meta("_local_pose_override_enabled_", true); - - Transform2D final_trans = bones[i].bone->cache_transform; - final_trans = final_trans.interpolate_with(bones[i].local_pose_override, bones[i].local_pose_override_amount); - bones[i].bone->set_transform(final_trans); - bones[i].bone->propagate_call("force_update_transform"); - - if (bones[i].local_pose_override_persistent) { - bones.write[i].local_pose_override_amount = 0.0; - } - } else { - // TODO: see if there is a way to undo the override without having to resort to setting every bone's transform. - bones[i].bone->remove_meta("_local_pose_override_enabled_"); - bones[i].bone->set_transform(bones[i].bone->cache_transform); - } - } - } - - // Cache any future transform changes - for (int i = 0; i < bones.size(); i++) { - bones[i].bone->copy_transform_to_cache = true; - } - -#ifdef TOOLS_ENABLED - modification_stack->set_editor_gizmos_dirty(true); -#endif // TOOLS_ENABLED -} - void Skeleton2D::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_bone_setup"), &Skeleton2D::_update_bone_setup); ClassDB::bind_method(D_METHOD("_update_transform"), &Skeleton2D::_update_transform); @@ -789,10 +683,6 @@ void Skeleton2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_skeleton"), &Skeleton2D::get_skeleton); - ClassDB::bind_method(D_METHOD("set_modification_stack", "modification_stack"), &Skeleton2D::set_modification_stack); - ClassDB::bind_method(D_METHOD("get_modification_stack"), &Skeleton2D::get_modification_stack); - ClassDB::bind_method(D_METHOD("execute_modifications", "delta", "execution_mode"), &Skeleton2D::execute_modifications); - ClassDB::bind_method(D_METHOD("set_bone_local_pose_override", "bone_idx", "override_pose", "strength", "persistent"), &Skeleton2D::set_bone_local_pose_override); ClassDB::bind_method(D_METHOD("get_bone_local_pose_override", "bone_idx"), &Skeleton2D::get_bone_local_pose_override); diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index 98fb867d997e..2e4c55dc44e4 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -32,7 +32,6 @@ #define SKELETON_2D_H #include "scene/2d/node_2d.h" -#include "scene/resources/skeleton_modification_2d.h" class Skeleton2D; @@ -101,8 +100,6 @@ class Bone2D : public Node2D { ~Bone2D(); }; -class SkeletonModificationStack2D; - class Skeleton2D : public Node2D { GDCLASS(Skeleton2D, Node2D); @@ -138,8 +135,6 @@ class Skeleton2D : public Node2D { RID skeleton; - Ref modification_stack; - protected: void _notification(int p_what); static void _bind_methods(); @@ -156,8 +151,6 @@ class Skeleton2D : public Node2D { void set_bone_local_pose_override(int p_bone_idx, Transform2D p_override, real_t p_amount, bool p_persistent = true); Transform2D get_bone_local_pose_override(int p_bone_idx); - Ref get_modification_stack() const; - void set_modification_stack(Ref p_stack); void execute_modifications(real_t p_delta, int p_execution_mode); Skeleton2D(); diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/2d/skeleton_modification_2d.cpp similarity index 75% rename from scene/resources/skeleton_modification_2d.cpp rename to scene/2d/skeleton_modification_2d.cpp index 885cf0f1b856..d28a39e558fa 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/2d/skeleton_modification_2d.cpp @@ -43,39 +43,12 @@ // Modification2D /////////////////////////////////////// -void SkeletonModification2D::_execute(float p_delta) { - GDVIRTUAL_CALL(_execute, p_delta); - - if (!enabled) { - return; - } -} - -void SkeletonModification2D::_setup_modification(SkeletonModificationStack2D *p_stack) { - stack = p_stack; - if (stack) { - is_setup = true; - } else { - WARN_PRINT("Could not setup modification with name " + get_name()); - } - - GDVIRTUAL_CALL(_setup_modification, Ref(p_stack)); -} - void SkeletonModification2D::_draw_editor_gizmo() { GDVIRTUAL_CALL(_draw_editor_gizmo); } void SkeletonModification2D::set_enabled(bool p_enabled) { enabled = p_enabled; - -#ifdef TOOLS_ENABLED - if (editor_draw_gizmo) { - if (stack) { - stack->set_editor_gizmos_dirty(true); - } - } -#endif // TOOLS_ENABLED } bool SkeletonModification2D::get_enabled() { @@ -151,37 +124,33 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b Bone2D *operation_bone_parent_bone = Object::cast_to(operation_bone_parent); if (operation_bone_parent_bone) { - stack->skeleton->draw_set_transform( - stack->skeleton->to_local(p_operation_bone->get_global_position()), - operation_bone_parent_bone->get_global_rotation() - stack->skeleton->get_global_rotation()); + skeleton->draw_set_transform( + skeleton->to_local(p_operation_bone->get_global_position()), + operation_bone_parent_bone->get_global_rotation() - skeleton->get_global_rotation()); } else { - stack->skeleton->draw_set_transform(stack->skeleton->to_local(p_operation_bone->get_global_position())); + skeleton->draw_set_transform(skeleton->to_local(p_operation_bone->get_global_position())); } } else { - stack->skeleton->draw_set_transform(stack->skeleton->to_local(p_operation_bone->get_global_position())); + skeleton->draw_set_transform(skeleton->to_local(p_operation_bone->get_global_position())); } if (p_constraint_inverted) { - stack->skeleton->draw_arc(Vector2(0, 0), p_operation_bone->get_length(), + skeleton->draw_arc(Vector2(0, 0), p_operation_bone->get_length(), arc_angle_min + (Math_PI * 2), arc_angle_max, 32, bone_ik_color, 1.0); } else { - stack->skeleton->draw_arc(Vector2(0, 0), p_operation_bone->get_length(), + skeleton->draw_arc(Vector2(0, 0), p_operation_bone->get_length(), arc_angle_min, arc_angle_max, 32, bone_ik_color, 1.0); } - stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(arc_angle_min), Math::sin(arc_angle_min)) * p_operation_bone->get_length(), bone_ik_color, 1.0); - stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(arc_angle_max), Math::sin(arc_angle_max)) * p_operation_bone->get_length(), bone_ik_color, 1.0); + skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(arc_angle_min), Math::sin(arc_angle_min)) * p_operation_bone->get_length(), bone_ik_color, 1.0); + skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(arc_angle_max), Math::sin(arc_angle_max)) * p_operation_bone->get_length(), bone_ik_color, 1.0); } else { - stack->skeleton->draw_set_transform(stack->skeleton->to_local(p_operation_bone->get_global_position())); - stack->skeleton->draw_arc(Vector2(0, 0), p_operation_bone->get_length(), 0, Math_PI * 2, 32, bone_ik_color, 1.0); - stack->skeleton->draw_line(Vector2(0, 0), Vector2(1, 0) * p_operation_bone->get_length(), bone_ik_color, 1.0); + skeleton->draw_set_transform(skeleton->to_local(p_operation_bone->get_global_position())); + skeleton->draw_arc(Vector2(0, 0), p_operation_bone->get_length(), 0, Math_PI * 2, 32, bone_ik_color, 1.0); + skeleton->draw_line(Vector2(0, 0), Vector2(1, 0) * p_operation_bone->get_length(), bone_ik_color, 1.0); } } -Ref SkeletonModification2D::get_modification_stack() { - return stack; -} - void SkeletonModification2D::set_is_setup(bool p_setup) { is_setup = p_setup; } @@ -200,13 +169,6 @@ int SkeletonModification2D::get_execution_mode() const { void SkeletonModification2D::set_editor_draw_gizmo(bool p_draw_gizmo) { editor_draw_gizmo = p_draw_gizmo; -#ifdef TOOLS_ENABLED - if (is_setup) { - if (stack) { - stack->set_editor_gizmos_dirty(true); - } - } -#endif // TOOLS_ENABLED } bool SkeletonModification2D::get_editor_draw_gizmo() const { @@ -214,13 +176,10 @@ bool SkeletonModification2D::get_editor_draw_gizmo() const { } void SkeletonModification2D::_bind_methods() { - GDVIRTUAL_BIND(_execute, "delta"); - GDVIRTUAL_BIND(_setup_modification, "modification_stack") GDVIRTUAL_BIND(_draw_editor_gizmo) ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification2D::set_enabled); ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification2D::get_enabled); - ClassDB::bind_method(D_METHOD("get_modification_stack"), &SkeletonModification2D::get_modification_stack); ClassDB::bind_method(D_METHOD("set_is_setup", "is_setup"), &SkeletonModification2D::set_is_setup); ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModification2D::get_is_setup); ClassDB::bind_method(D_METHOD("set_execution_mode", "execution_mode"), &SkeletonModification2D::set_execution_mode); @@ -232,8 +191,3 @@ void SkeletonModification2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "execution_mode", PROPERTY_HINT_ENUM, "process, physics_process"), "set_execution_mode", "get_execution_mode"); } - -SkeletonModification2D::SkeletonModification2D() { - stack = nullptr; - is_setup = false; -} diff --git a/scene/resources/skeleton_modification_2d.h b/scene/2d/skeleton_modification_2d.h similarity index 86% rename from scene/resources/skeleton_modification_2d.h rename to scene/2d/skeleton_modification_2d.h index 4c49119df043..f15e5681a7b1 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/2d/skeleton_modification_2d.h @@ -32,38 +32,39 @@ #define SKELETON_MODIFICATION_2D_H #include "scene/2d/skeleton_2d.h" -#include "scene/resources/skeleton_modification_stack_2d.h" /////////////////////////////////////// // SkeletonModification2D /////////////////////////////////////// -class SkeletonModificationStack2D; class Bone2D; -class SkeletonModification2D : public Resource { - GDCLASS(SkeletonModification2D, Resource); - friend class Skeleton2D; - friend class Bone2D; +class SkeletonModification2D : public Node2D { + GDCLASS(SkeletonModification2D, Node2D); protected: static void _bind_methods(); - SkeletonModificationStack2D *stack = nullptr; int execution_mode = 0; // 0 = process bool enabled = true; bool is_setup = false; + Skeleton2D *skeleton = nullptr; + NodePath skeleton_node = NodePath(".."); bool _print_execution_error(bool p_condition, String p_message); GDVIRTUAL1(_execute, double) - GDVIRTUAL1(_setup_modification, Ref) GDVIRTUAL0(_draw_editor_gizmo) public: - virtual void _execute(float _delta); - virtual void _setup_modification(SkeletonModificationStack2D *p_stack); + NodePath get_skeleton_path() const { + return skeleton_node; + } + + void set_skeleton_path(NodePath p_path) { + skeleton_node = p_path; + } virtual void _draw_editor_gizmo(); bool editor_draw_gizmo = false; @@ -73,7 +74,6 @@ class SkeletonModification2D : public Resource { void set_enabled(bool p_enabled); bool get_enabled(); - Ref get_modification_stack(); void set_is_setup(bool p_setup); bool get_is_setup() const; @@ -83,7 +83,7 @@ class SkeletonModification2D : public Resource { float clamp_angle(float p_angle, float p_min_bound, float p_max_bound, bool p_invert_clamp = false); void editor_draw_angle_constraints(Bone2D *p_operation_bone, float p_min_bound, float p_max_bound, bool p_constraint_enabled, bool p_constraint_in_localspace, bool p_constraint_inverted); - SkeletonModification2D(); + SkeletonModification2D() {} }; #endif // SKELETON_MODIFICATION_2D_H diff --git a/scene/resources/skeleton_modification_2d_ccdik.cpp b/scene/2d/skeleton_modification_2d_ccdik.cpp similarity index 85% rename from scene/resources/skeleton_modification_2d_ccdik.cpp rename to scene/2d/skeleton_modification_2d_ccdik.cpp index 96961a1fe45b..087863c53aa5 100644 --- a/scene/resources/skeleton_modification_2d_ccdik.cpp +++ b/scene/2d/skeleton_modification_2d_ccdik.cpp @@ -153,49 +153,14 @@ void SkeletonModification2DCCDIK::_get_property_list(List *p_list) #endif // TOOLS_ENABLED } -void SkeletonModification2DCCDIK::_execute(float p_delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - - if (target_node_cache.is_null()) { - WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); - update_target_cache(); - return; - } - if (tip_node_cache.is_null()) { - WARN_PRINT_ONCE("Tip cache is out of date. Attempting to update..."); - update_tip_cache(); - return; - } - - Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (!target || !target->is_inside_tree()) { - ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); - return; - } - - Node2D *tip = Object::cast_to(ObjectDB::get_instance(tip_node_cache)); - if (!tip || !tip->is_inside_tree()) { - ERR_PRINT_ONCE("Tip node is not in the scene tree. Cannot execute modification!"); - return; - } - - for (int i = 0; i < ccdik_data_chain.size(); i++) { - _execute_ccdik_joint(i, target, tip); - } -} - void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D *p_target, Node2D *p_tip) { CCDIK_Joint_Data2D ccdik_data = ccdik_data_chain[p_joint_idx]; - if (ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > stack->skeleton->get_bone_count()) { + if (ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > skeleton->get_bone_count()) { ERR_PRINT_ONCE("2D CCDIK joint: bone index not found!"); return; } - Bone2D *operation_bone = stack->skeleton->get_bone(ccdik_data.bone_idx); + Bone2D *operation_bone = skeleton->get_bone(ccdik_data.bone_idx); Transform2D operation_transform = operation_bone->get_global_transform(); if (ccdik_data.rotate_from_joint) { @@ -229,21 +194,11 @@ void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D * } // Set the local pose override, and to make sure child bones are also updated, set the transform of the bone. - stack->skeleton->set_bone_local_pose_override(ccdik_data.bone_idx, operation_transform, stack->strength, true); + skeleton->set_bone_local_pose_override(ccdik_data.bone_idx, operation_transform, 1.0, true); operation_bone->set_transform(operation_transform); operation_bone->notification(operation_bone->NOTIFICATION_TRANSFORM_CHANGED); } -void SkeletonModification2DCCDIK::_setup_modification(SkeletonModificationStack2D *p_stack) { - stack = p_stack; - - if (stack != nullptr) { - is_setup = true; - update_target_cache(); - update_tip_cache(); - } -} - void SkeletonModification2DCCDIK::_draw_editor_gizmo() { if (!enabled || !is_setup) { return; @@ -254,24 +209,24 @@ void SkeletonModification2DCCDIK::_draw_editor_gizmo() { continue; } - Bone2D *operation_bone = stack->skeleton->get_bone(ccdik_data_chain[i].bone_idx); + Bone2D *operation_bone = skeleton->get_bone(ccdik_data_chain[i].bone_idx); editor_draw_angle_constraints(operation_bone, ccdik_data_chain[i].constraint_angle_min, ccdik_data_chain[i].constraint_angle_max, ccdik_data_chain[i].enable_constraint, ccdik_data_chain[i].constraint_in_localspace, ccdik_data_chain[i].constraint_angle_invert); } } void SkeletonModification2DCCDIK::update_target_cache() { - if (!is_setup || !stack) { + if (!is_setup) { ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); return; } target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + if (skeleton) { + if (skeleton->is_inside_tree()) { + if (skeleton->has_node(target_node)) { + Node *node = skeleton->get_node(target_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, "Cannot update target cache: node is this modification's skeleton or cannot be found!"); ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Cannot update target cache: node is not in the scene tree!"); @@ -282,17 +237,17 @@ void SkeletonModification2DCCDIK::update_target_cache() { } void SkeletonModification2DCCDIK::update_tip_cache() { - if (!is_setup || !stack) { + if (!is_setup) { ERR_PRINT_ONCE("Cannot update tip cache: modification is not properly setup!"); return; } tip_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(tip_node)) { - Node *node = stack->skeleton->get_node(tip_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + if (skeleton) { + if (skeleton->is_inside_tree()) { + if (skeleton->has_node(tip_node)) { + Node *node = skeleton->get_node(tip_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, "Cannot update tip cache: node is this modification's skeleton or cannot be found!"); ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Cannot update tip cache: node is not in the scene tree!"); @@ -304,17 +259,17 @@ void SkeletonModification2DCCDIK::update_tip_cache() { void SkeletonModification2DCCDIK::ccdik_joint_update_bone2d_cache(int p_joint_idx) { ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "Cannot update bone2d cache: joint index out of range!"); - if (!is_setup || !stack) { + if (!is_setup) { ERR_PRINT_ONCE("Cannot update CCDIK Bone2D cache: modification is not properly setup!"); return; } ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(ccdik_data_chain[p_joint_idx].bone2d_node)) { - Node *node = stack->skeleton->get_node(ccdik_data_chain[p_joint_idx].bone2d_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + if (skeleton) { + if (skeleton->is_inside_tree()) { + if (skeleton->has_node(ccdik_data_chain[p_joint_idx].bone2d_node)) { + Node *node = skeleton->get_node(ccdik_data_chain[p_joint_idx].bone2d_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, "Cannot update CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: node is this modification's skeleton or cannot be found!"); ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Cannot update CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: node is not in the scene tree!"); @@ -376,11 +331,11 @@ void SkeletonModification2DCCDIK::set_ccdik_joint_bone_index(int p_joint_idx, in ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); if (is_setup) { - if (stack->skeleton) { - ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); + if (skeleton) { + ERR_FAIL_INDEX_MSG(p_bone_idx, skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; - ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); - ccdik_data_chain.write[p_joint_idx].bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); + ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = skeleton->get_bone(p_bone_idx)->get_instance_id(); + ccdik_data_chain.write[p_joint_idx].bone2d_node = skeleton->get_path_to(skeleton->get_bone(p_bone_idx)); } else { WARN_PRINT("Cannot verify the CCDIK joint " + itos(p_joint_idx) + " bone index for this modification..."); ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; @@ -412,12 +367,6 @@ void SkeletonModification2DCCDIK::set_ccdik_joint_enable_constraint(int p_joint_ ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); ccdik_data_chain.write[p_joint_idx].enable_constraint = p_constraint; notify_property_list_changed(); - -#ifdef TOOLS_ENABLED - if (stack && is_setup) { - stack->set_editor_gizmos_dirty(true); - } -#endif // TOOLS_ENABLED } bool SkeletonModification2DCCDIK::get_ccdik_joint_enable_constraint(int p_joint_idx) const { @@ -428,12 +377,6 @@ bool SkeletonModification2DCCDIK::get_ccdik_joint_enable_constraint(int p_joint_ void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_min(int p_joint_idx, float p_angle_min) { ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); ccdik_data_chain.write[p_joint_idx].constraint_angle_min = p_angle_min; - -#ifdef TOOLS_ENABLED - if (stack && is_setup) { - stack->set_editor_gizmos_dirty(true); - } -#endif // TOOLS_ENABLED } float SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_min(int p_joint_idx) const { @@ -444,12 +387,6 @@ float SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_min(int p_jo void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_max(int p_joint_idx, float p_angle_max) { ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); ccdik_data_chain.write[p_joint_idx].constraint_angle_max = p_angle_max; - -#ifdef TOOLS_ENABLED - if (stack && is_setup) { - stack->set_editor_gizmos_dirty(true); - } -#endif // TOOLS_ENABLED } float SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_max(int p_joint_idx) const { @@ -460,12 +397,6 @@ float SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_max(int p_jo void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_invert(int p_joint_idx, bool p_invert) { ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); ccdik_data_chain.write[p_joint_idx].constraint_angle_invert = p_invert; - -#ifdef TOOLS_ENABLED - if (stack && is_setup) { - stack->set_editor_gizmos_dirty(true); - } -#endif // TOOLS_ENABLED } bool SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_invert(int p_joint_idx) const { @@ -476,12 +407,6 @@ bool SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_invert(int p_ void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace) { ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); ccdik_data_chain.write[p_joint_idx].constraint_in_localspace = p_constraint_in_localspace; - -#ifdef TOOLS_ENABLED - if (stack && is_setup) { - stack->set_editor_gizmos_dirty(true); - } -#endif // TOOLS_ENABLED } bool SkeletonModification2DCCDIK::get_ccdik_joint_constraint_in_localspace(int p_joint_idx) const { @@ -492,12 +417,6 @@ bool SkeletonModification2DCCDIK::get_ccdik_joint_constraint_in_localspace(int p void SkeletonModification2DCCDIK::set_ccdik_joint_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo) { ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); ccdik_data_chain.write[p_joint_idx].editor_draw_gizmo = p_draw_gizmo; - -#ifdef TOOLS_ENABLED - if (stack && is_setup) { - stack->set_editor_gizmos_dirty(true); - } -#endif // TOOLS_ENABLED } bool SkeletonModification2DCCDIK::get_ccdik_joint_editor_draw_gizmo(int p_joint_idx) const { @@ -535,7 +454,6 @@ void SkeletonModification2DCCDIK::_bind_methods() { } SkeletonModification2DCCDIK::SkeletonModification2DCCDIK() { - stack = nullptr; is_setup = false; enabled = true; editor_draw_gizmo = true; @@ -543,3 +461,56 @@ SkeletonModification2DCCDIK::SkeletonModification2DCCDIK() { SkeletonModification2DCCDIK::~SkeletonModification2DCCDIK() { } +void SkeletonModification2DCCDIK::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + set_process_internal(false); + set_physics_process_internal(false); + if (get_execution_mode() == 0) { + set_process_internal(true); + } else if (get_execution_mode() == 1) { + set_physics_process_internal(true); + } + skeleton = cast_to(get_node_or_null(get_skeleton_path())); + is_setup = true; + update_target_cache(); + update_tip_cache(); + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: + [[fallthrough]]; + case NOTIFICATION_INTERNAL_PROCESS: { + ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr, + "Modification is not setup and therefore cannot execute!"); + if (!enabled) { + return; + } + + if (target_node_cache.is_null()) { + WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + if (tip_node_cache.is_null()) { + WARN_PRINT_ONCE("Tip cache is out of date. Attempting to update..."); + update_tip_cache(); + return; + } + + Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + if (!target || !target->is_inside_tree()) { + ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); + return; + } + + Node2D *tip = Object::cast_to(ObjectDB::get_instance(tip_node_cache)); + if (!tip || !tip->is_inside_tree()) { + ERR_PRINT_ONCE("Tip node is not in the scene tree. Cannot execute modification!"); + return; + } + + for (int i = 0; i < ccdik_data_chain.size(); i++) { + _execute_ccdik_joint(i, target, tip); + } + } break; + } +} diff --git a/scene/resources/skeleton_modification_2d_ccdik.h b/scene/2d/skeleton_modification_2d_ccdik.h similarity index 96% rename from scene/resources/skeleton_modification_2d_ccdik.h rename to scene/2d/skeleton_modification_2d_ccdik.h index e49dfca5b51f..53334cb783a8 100644 --- a/scene/resources/skeleton_modification_2d_ccdik.h +++ b/scene/2d/skeleton_modification_2d_ccdik.h @@ -32,7 +32,7 @@ #define SKELETON_MODIFICATION_2D_CCDIK_H #include "scene/2d/skeleton_2d.h" -#include "scene/resources/skeleton_modification_2d.h" +#include "scene/2d/skeleton_modification_2d.h" /////////////////////////////////////// // SkeletonModification2DCCDIK @@ -77,8 +77,7 @@ class SkeletonModification2DCCDIK : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void _execute(float p_delta) override; - void _setup_modification(SkeletonModificationStack2D *p_stack) override; + void _notification(int p_what); void _draw_editor_gizmo() override; void set_target_node(const NodePath &p_target_node); diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/2d/skeleton_modification_2d_fabrik.cpp similarity index 69% rename from scene/resources/skeleton_modification_2d_fabrik.cpp rename to scene/2d/skeleton_modification_2d_fabrik.cpp index 393d404e9b56..cd3a5fcb7186 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.cpp +++ b/scene/2d/skeleton_modification_2d_fabrik.cpp @@ -95,120 +95,6 @@ void SkeletonModification2DFABRIK::_get_property_list(List *p_list } } -void SkeletonModification2DFABRIK::_execute(float p_delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - - if (target_node_cache.is_null()) { - WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); - update_target_cache(); - return; - } - - if (fabrik_data_chain.size() <= 1) { - ERR_PRINT_ONCE("FABRIK requires at least two joints to operate! Cannot execute modification!"); - return; - } - - Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (!target || !target->is_inside_tree()) { - ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); - return; - } - target_global_pose = target->get_global_transform(); - - if (fabrik_data_chain[0].bone2d_node_cache.is_null() && !fabrik_data_chain[0].bone2d_node.is_empty()) { - fabrik_joint_update_bone2d_cache(0); - WARN_PRINT("Bone2D cache for origin joint is out of date. Updating..."); - } - - Bone2D *origin_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[0].bone2d_node_cache)); - if (!origin_bone2d_node || !origin_bone2d_node->is_inside_tree()) { - ERR_PRINT_ONCE("Origin joint's Bone2D node is not in the scene tree. Cannot execute modification!"); - return; - } - - origin_global_pose = origin_bone2d_node->get_global_transform(); - - if (fabrik_transform_chain.size() != fabrik_data_chain.size()) { - fabrik_transform_chain.resize(fabrik_data_chain.size()); - } - - for (int i = 0; i < fabrik_data_chain.size(); i++) { - // Update the transform chain - if (fabrik_data_chain[i].bone2d_node_cache.is_null() && !fabrik_data_chain[i].bone2d_node.is_empty()) { - WARN_PRINT_ONCE("Bone2D cache for joint " + itos(i) + " is out of date.. Attempting to update..."); - fabrik_joint_update_bone2d_cache(i); - } - Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); - if (!joint_bone2d_node) { - ERR_PRINT_ONCE("FABRIK Joint " + itos(i) + " does not have a Bone2D node set! Cannot execute modification!"); - return; - } - fabrik_transform_chain.write[i] = joint_bone2d_node->get_global_transform(); - } - - Bone2D *final_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[fabrik_data_chain.size() - 1].bone2d_node_cache)); - float final_bone2d_angle = final_bone2d_node->get_global_rotation(); - if (fabrik_data_chain[fabrik_data_chain.size() - 1].use_target_rotation) { - final_bone2d_angle = target_global_pose.get_rotation(); - } - Vector2 final_bone2d_direction = Vector2(Math::cos(final_bone2d_angle), Math::sin(final_bone2d_angle)); - float final_bone2d_length = final_bone2d_node->get_length() * MIN(final_bone2d_node->get_global_scale().x, final_bone2d_node->get_global_scale().y); - float target_distance = (final_bone2d_node->get_global_position() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_position()); - chain_iterations = 0; - - while (target_distance > chain_tolarance) { - chain_backwards(); - chain_forwards(); - - final_bone2d_angle = final_bone2d_node->get_global_rotation(); - if (fabrik_data_chain[fabrik_data_chain.size() - 1].use_target_rotation) { - final_bone2d_angle = target_global_pose.get_rotation(); - } - final_bone2d_direction = Vector2(Math::cos(final_bone2d_angle), Math::sin(final_bone2d_angle)); - target_distance = (final_bone2d_node->get_global_position() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_position()); - - chain_iterations += 1; - if (chain_iterations >= chain_max_iterations) { - break; - } - } - - // Apply all of the saved transforms to the Bone2D nodes - for (int i = 0; i < fabrik_data_chain.size(); i++) { - Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); - if (!joint_bone2d_node) { - ERR_PRINT_ONCE("FABRIK Joint " + itos(i) + " does not have a Bone2D node set!"); - continue; - } - Transform2D chain_trans = fabrik_transform_chain[i]; - - // Apply rotation - if (i + 1 < fabrik_data_chain.size()) { - chain_trans = chain_trans.looking_at(fabrik_transform_chain[i + 1].get_origin()); - } else { - if (fabrik_data_chain[i].use_target_rotation) { - chain_trans.set_rotation(target_global_pose.get_rotation()); - } else { - chain_trans = chain_trans.looking_at(target_global_pose.get_origin()); - } - } - // Adjust for the bone angle - chain_trans.set_rotation(chain_trans.get_rotation() - joint_bone2d_node->get_bone_angle()); - - // Reset scale - chain_trans.set_scale(joint_bone2d_node->get_global_scale()); - - // Apply to the bone, and to the override - joint_bone2d_node->set_global_transform(chain_trans); - stack->skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, joint_bone2d_node->get_transform(), stack->strength, true); - } -} - void SkeletonModification2DFABRIK::chain_backwards() { int final_joint_index = fabrik_data_chain.size() - 1; Bone2D *final_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[final_joint_index].bone2d_node_cache)); @@ -277,27 +163,19 @@ void SkeletonModification2DFABRIK::chain_forwards() { } } -void SkeletonModification2DFABRIK::_setup_modification(SkeletonModificationStack2D *p_stack) { - stack = p_stack; - - if (stack != nullptr) { - is_setup = true; - update_target_cache(); - } -} void SkeletonModification2DFABRIK::update_target_cache() { - if (!is_setup || !stack) { + if (!is_setup) { ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); return; } target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + if (skeleton) { + if (skeleton->is_inside_tree()) { + if (skeleton->has_node(target_node)) { + Node *node = skeleton->get_node(target_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, "Cannot update target cache: node is this modification's skeleton or cannot be found!"); ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Cannot update target cache: node is not in scene tree!"); @@ -309,17 +187,17 @@ void SkeletonModification2DFABRIK::update_target_cache() { void SkeletonModification2DFABRIK::fabrik_joint_update_bone2d_cache(int p_joint_idx) { ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "Cannot update bone2d cache: joint index out of range!"); - if (!is_setup || !stack) { + if (!is_setup) { ERR_PRINT_ONCE("Cannot update FABRIK Bone2D cache: modification is not properly setup!"); return; } fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(fabrik_data_chain[p_joint_idx].bone2d_node)) { - Node *node = stack->skeleton->get_node(fabrik_data_chain[p_joint_idx].bone2d_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + if (skeleton) { + if (skeleton->is_inside_tree()) { + if (skeleton->has_node(fabrik_data_chain[p_joint_idx].bone2d_node)) { + Node *node = skeleton->get_node(fabrik_data_chain[p_joint_idx].bone2d_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, "Cannot update FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: node is this modification's skeleton or cannot be found!"); ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Cannot update FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: node is not in scene tree!"); @@ -372,11 +250,11 @@ void SkeletonModification2DFABRIK::set_fabrik_joint_bone_index(int p_joint_idx, ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); if (is_setup) { - if (stack->skeleton) { - ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); + if (skeleton) { + ERR_FAIL_INDEX_MSG(p_bone_idx, skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; - fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); - fabrik_data_chain.write[p_joint_idx].bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); + fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = skeleton->get_bone(p_bone_idx)->get_instance_id(); + fabrik_data_chain.write[p_joint_idx].bone2d_node = skeleton->get_path_to(skeleton->get_bone(p_bone_idx)); } else { WARN_PRINT("Cannot verify the FABRIK joint " + itos(p_joint_idx) + " bone index for this modification..."); fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; @@ -435,7 +313,6 @@ void SkeletonModification2DFABRIK::_bind_methods() { } SkeletonModification2DFABRIK::SkeletonModification2DFABRIK() { - stack = nullptr; is_setup = false; enabled = true; editor_draw_gizmo = false; @@ -443,3 +320,137 @@ SkeletonModification2DFABRIK::SkeletonModification2DFABRIK() { SkeletonModification2DFABRIK::~SkeletonModification2DFABRIK() { } + +void SkeletonModification2DFABRIK::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + set_process_internal(false); + set_physics_process_internal(false); + if (get_execution_mode() == 0) { + set_process_internal(true); + } else if (get_execution_mode() == 1) { + set_physics_process_internal(true); + } + skeleton = cast_to(get_node_or_null(get_skeleton_path())); + is_setup = true; + update_target_cache(); + + update_target_cache(); + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: + [[fallthrough]]; + case NOTIFICATION_INTERNAL_PROCESS: { + ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr, + "Modification is not setup and therefore cannot execute!"); + if (!enabled) { + return; + } + + if (target_node_cache.is_null()) { + WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + + if (fabrik_data_chain.size() <= 1) { + ERR_PRINT_ONCE("FABRIK requires at least two joints to operate! Cannot execute modification!"); + return; + } + + Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + if (!target || !target->is_inside_tree()) { + ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); + return; + } + target_global_pose = target->get_global_transform(); + + if (fabrik_data_chain[0].bone2d_node_cache.is_null() && !fabrik_data_chain[0].bone2d_node.is_empty()) { + fabrik_joint_update_bone2d_cache(0); + WARN_PRINT("Bone2D cache for origin joint is out of date. Updating..."); + } + + Bone2D *origin_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[0].bone2d_node_cache)); + if (!origin_bone2d_node || !origin_bone2d_node->is_inside_tree()) { + ERR_PRINT_ONCE("Origin joint's Bone2D node is not in the scene tree. Cannot execute modification!"); + return; + } + + origin_global_pose = origin_bone2d_node->get_global_transform(); + + if (fabrik_transform_chain.size() != fabrik_data_chain.size()) { + fabrik_transform_chain.resize(fabrik_data_chain.size()); + } + + for (int i = 0; i < fabrik_data_chain.size(); i++) { + // Update the transform chain + if (fabrik_data_chain[i].bone2d_node_cache.is_null() && !fabrik_data_chain[i].bone2d_node.is_empty()) { + WARN_PRINT_ONCE("Bone2D cache for joint " + itos(i) + " is out of date.. Attempting to update..."); + fabrik_joint_update_bone2d_cache(i); + } + Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); + if (!joint_bone2d_node) { + ERR_PRINT_ONCE("FABRIK Joint " + itos(i) + " does not have a Bone2D node set! Cannot execute modification!"); + return; + } + fabrik_transform_chain.write[i] = joint_bone2d_node->get_global_transform(); + } + + Bone2D *final_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[fabrik_data_chain.size() - 1].bone2d_node_cache)); + float final_bone2d_angle = final_bone2d_node->get_global_rotation(); + if (fabrik_data_chain[fabrik_data_chain.size() - 1].use_target_rotation) { + final_bone2d_angle = target_global_pose.get_rotation(); + } + Vector2 final_bone2d_direction = Vector2(Math::cos(final_bone2d_angle), Math::sin(final_bone2d_angle)); + float final_bone2d_length = final_bone2d_node->get_length() * MIN(final_bone2d_node->get_global_scale().x, final_bone2d_node->get_global_scale().y); + float target_distance = (final_bone2d_node->get_global_position() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_position()); + chain_iterations = 0; + + while (target_distance > chain_tolarance) { + chain_backwards(); + chain_forwards(); + + final_bone2d_angle = final_bone2d_node->get_global_rotation(); + if (fabrik_data_chain[fabrik_data_chain.size() - 1].use_target_rotation) { + final_bone2d_angle = target_global_pose.get_rotation(); + } + final_bone2d_direction = Vector2(Math::cos(final_bone2d_angle), Math::sin(final_bone2d_angle)); + target_distance = (final_bone2d_node->get_global_position() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_position()); + + chain_iterations += 1; + if (chain_iterations >= chain_max_iterations) { + break; + } + } + + // Apply all of the saved transforms to the Bone2D nodes + for (int i = 0; i < fabrik_data_chain.size(); i++) { + Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); + if (!joint_bone2d_node) { + ERR_PRINT_ONCE("FABRIK Joint " + itos(i) + " does not have a Bone2D node set!"); + continue; + } + Transform2D chain_trans = fabrik_transform_chain[i]; + + // Apply rotation + if (i + 1 < fabrik_data_chain.size()) { + chain_trans = chain_trans.looking_at(fabrik_transform_chain[i + 1].get_origin()); + } else { + if (fabrik_data_chain[i].use_target_rotation) { + chain_trans.set_rotation(target_global_pose.get_rotation()); + } else { + chain_trans = chain_trans.looking_at(target_global_pose.get_origin()); + } + } + // Adjust for the bone angle + chain_trans.set_rotation(chain_trans.get_rotation() - joint_bone2d_node->get_bone_angle()); + + // Reset scale + chain_trans.set_scale(joint_bone2d_node->get_global_scale()); + + // Apply to the bone, and to the override + joint_bone2d_node->set_global_transform(chain_trans); + skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, joint_bone2d_node->get_transform(), 1.0, true); + } + } break; + } +} diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/2d/skeleton_modification_2d_fabrik.h similarity index 96% rename from scene/resources/skeleton_modification_2d_fabrik.h rename to scene/2d/skeleton_modification_2d_fabrik.h index 4a875d039f64..a25cf584b911 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.h +++ b/scene/2d/skeleton_modification_2d_fabrik.h @@ -32,7 +32,7 @@ #define SKELETON_MODIFICATION_2D_FABRIK_H #include "scene/2d/skeleton_2d.h" -#include "scene/resources/skeleton_modification_2d.h" +#include "scene/2d/skeleton_modification_2d.h" /////////////////////////////////////// // SkeletonModification2DFABRIK @@ -82,8 +82,7 @@ class SkeletonModification2DFABRIK : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void _execute(float p_delta) override; - void _setup_modification(SkeletonModificationStack2D *p_stack) override; + void _notification(int p_what); void set_target_node(const NodePath &p_target_node); NodePath get_target_node() const; diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/2d/skeleton_modification_2d_jiggle.cpp similarity index 88% rename from scene/resources/skeleton_modification_2d_jiggle.cpp rename to scene/2d/skeleton_modification_2d_jiggle.cpp index 092141765694..e9304d7a5880 100644 --- a/scene/resources/skeleton_modification_2d_jiggle.cpp +++ b/scene/2d/skeleton_modification_2d_jiggle.cpp @@ -130,33 +130,11 @@ void SkeletonModification2DJiggle::_get_property_list(List *p_list } } -void SkeletonModification2DJiggle::_execute(float p_delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - if (target_node_cache.is_null()) { - WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); - update_target_cache(); - return; - } - Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (!target || !target->is_inside_tree()) { - ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); - return; - } - - for (int i = 0; i < jiggle_data_chain.size(); i++) { - _execute_jiggle_joint(i, target, p_delta); - } -} - void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D *p_target, float p_delta) { // Adopted from: https://wiki.unity3d.com/index.php/JiggleBone // With modifications by TwistedTwigleg. - if (jiggle_data_chain[p_joint_idx].bone_idx <= -1 || jiggle_data_chain[p_joint_idx].bone_idx > stack->skeleton->get_bone_count()) { + if (jiggle_data_chain[p_joint_idx].bone_idx <= -1 || jiggle_data_chain[p_joint_idx].bone_idx > skeleton->get_bone_count()) { ERR_PRINT_ONCE("Jiggle joint " + itos(p_joint_idx) + " bone index is invalid. Cannot execute modification on joint..."); return; } @@ -166,7 +144,7 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D jiggle_joint_update_bone2d_cache(p_joint_idx); } - Bone2D *operation_bone = stack->skeleton->get_bone(jiggle_data_chain[p_joint_idx].bone_idx); + Bone2D *operation_bone = skeleton->get_bone(jiggle_data_chain[p_joint_idx].bone_idx); if (!operation_bone) { ERR_PRINT_ONCE("Jiggle joint " + itos(p_joint_idx) + " does not have a Bone2D node or it cannot be found!"); return; @@ -190,8 +168,8 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D // Collision detection/response if (use_colliders) { - if (execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process) { - Ref world_2d = stack->skeleton->get_world_2d(); + if (is_physics_processing() || is_physics_processing_internal()) { + Ref world_2d = skeleton->get_world_2d(); ERR_FAIL_COND(world_2d.is_null()); PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space()); PhysicsDirectSpaceState2D::RayResult ray_result; @@ -212,7 +190,7 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D jiggle_data_chain.write[p_joint_idx].last_noncollision_position = jiggle_data_chain[p_joint_idx].dynamic_position; } } else { - WARN_PRINT_ONCE("Jiggle 2D modifier: You cannot detect colliders without the stack mode being set to _physics_process!"); + WARN_PRINT_ONCE("Jiggle 2D modifier: You cannot detect colliders without the process mode being set to _physics_process!"); } } @@ -224,7 +202,7 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D operation_bone_trans.set_scale(operation_bone->get_global_scale()); operation_bone->set_global_transform(operation_bone_trans); - stack->skeleton->set_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx, operation_bone->get_transform(), stack->strength, true); + skeleton->set_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx, operation_bone->get_transform(), 1.0, true); } void SkeletonModification2DJiggle::_update_jiggle_joint_data() { @@ -239,60 +217,38 @@ void SkeletonModification2DJiggle::_update_jiggle_joint_data() { } } -void SkeletonModification2DJiggle::_setup_modification(SkeletonModificationStack2D *p_stack) { - stack = p_stack; - - if (stack) { - is_setup = true; - - if (stack->skeleton) { - for (int i = 0; i < jiggle_data_chain.size(); i++) { - int bone_idx = jiggle_data_chain[i].bone_idx; - if (bone_idx > 0 && bone_idx < stack->skeleton->get_bone_count()) { - Bone2D *bone2d_node = stack->skeleton->get_bone(bone_idx); - jiggle_data_chain.write[i].dynamic_position = bone2d_node->get_global_position(); - } - } - } - - update_target_cache(); - } -} - void SkeletonModification2DJiggle::update_target_cache() { - if (!is_setup || !stack) { + if (!is_setup) { ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); return; } target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update target cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update target cache: node is not in scene tree!"); - target_node_cache = node->get_instance_id(); - } + if (is_inside_tree()) { + if (has_node(target_node)) { + Node *node = get_node_or_null(target_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, + "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in scene tree!"); + target_node_cache = node->get_instance_id(); } } } void SkeletonModification2DJiggle::jiggle_joint_update_bone2d_cache(int p_joint_idx) { ERR_FAIL_INDEX_MSG(p_joint_idx, jiggle_data_chain.size(), "Cannot update bone2d cache: joint index out of range!"); - if (!is_setup || !stack) { + if (!is_setup) { ERR_PRINT_ONCE("Cannot update Jiggle " + itos(p_joint_idx) + " Bone2D cache: modification is not properly setup!"); return; } jiggle_data_chain.write[p_joint_idx].bone2d_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(jiggle_data_chain[p_joint_idx].bone2d_node)) { - Node *node = stack->skeleton->get_node(jiggle_data_chain[p_joint_idx].bone2d_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + if (skeleton) { + if (skeleton->is_inside_tree()) { + if (skeleton->has_node(jiggle_data_chain[p_joint_idx].bone2d_node)) { + Node *node = skeleton->get_node(jiggle_data_chain[p_joint_idx].bone2d_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, "Cannot update Jiggle joint " + itos(p_joint_idx) + " Bone2D cache: node is this modification's skeleton or cannot be found!"); ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Cannot update Jiggle joint " + itos(p_joint_idx) + " Bone2D cache: node is not in scene tree!"); @@ -413,11 +369,11 @@ void SkeletonModification2DJiggle::set_jiggle_joint_bone_index(int p_joint_idx, ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); if (is_setup) { - if (stack->skeleton) { - ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); + if (skeleton) { + ERR_FAIL_INDEX_MSG(p_bone_idx, skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; - jiggle_data_chain.write[p_joint_idx].bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); - jiggle_data_chain.write[p_joint_idx].bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); + jiggle_data_chain.write[p_joint_idx].bone2d_node_cache = skeleton->get_bone(p_bone_idx)->get_instance_id(); + jiggle_data_chain.write[p_joint_idx].bone2d_node = skeleton->get_path_to(skeleton->get_bone(p_bone_idx)); } else { WARN_PRINT("Cannot verify the Jiggle joint " + itos(p_joint_idx) + " bone index for this modification..."); jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; @@ -554,7 +510,6 @@ void SkeletonModification2DJiggle::_bind_methods() { } SkeletonModification2DJiggle::SkeletonModification2DJiggle() { - stack = nullptr; is_setup = false; jiggle_data_chain = Vector(); stiffness = 3; @@ -568,3 +523,58 @@ SkeletonModification2DJiggle::SkeletonModification2DJiggle() { SkeletonModification2DJiggle::~SkeletonModification2DJiggle() { } + +void SkeletonModification2DJiggle::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + set_process_internal(false); + set_physics_process_internal(false); + if (get_execution_mode() == 0) { + set_process_internal(true); + } else if (get_execution_mode() == 1) { + set_physics_process_internal(true); + } + skeleton = cast_to(get_node_or_null(get_skeleton_path())); + is_setup = true; + if (skeleton) { + for (int i = 0; i < jiggle_data_chain.size(); i++) { + int bone_idx = jiggle_data_chain[i].bone_idx; + if (bone_idx > 0 && bone_idx < skeleton->get_bone_count()) { + Bone2D *bone2d_node = skeleton->get_bone(bone_idx); + jiggle_data_chain.write[i].dynamic_position = bone2d_node->get_global_position(); + } + } + } + + update_target_cache(); + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: + [[fallthrough]]; + case NOTIFICATION_INTERNAL_PROCESS: { + ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr, + "Modification is not setup and therefore cannot execute!"); + if (!enabled) { + return; + } + if (target_node_cache.is_null()) { + WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + if (!target || !target->is_inside_tree()) { + ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); + return; + } + double delta = 0.0; + if (get_execution_mode() == 0) { + delta = get_process_delta_time(); + } else if (get_execution_mode() == 1) { + delta = get_physics_process_delta_time(); + } + for (int i = 0; i < jiggle_data_chain.size(); i++) { + _execute_jiggle_joint(i, target, delta); + } + } break; + } +} diff --git a/scene/resources/skeleton_modification_2d_jiggle.h b/scene/2d/skeleton_modification_2d_jiggle.h similarity index 97% rename from scene/resources/skeleton_modification_2d_jiggle.h rename to scene/2d/skeleton_modification_2d_jiggle.h index b9080eafa968..eafa76192db8 100644 --- a/scene/resources/skeleton_modification_2d_jiggle.h +++ b/scene/2d/skeleton_modification_2d_jiggle.h @@ -32,7 +32,7 @@ #define SKELETON_MODIFICATION_2D_JIGGLE_H #include "scene/2d/skeleton_2d.h" -#include "scene/resources/skeleton_modification_2d.h" +#include "scene/2d/skeleton_modification_2d.h" /////////////////////////////////////// // SkeletonModification2DJIGGLE @@ -89,8 +89,7 @@ class SkeletonModification2DJiggle : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void _execute(float p_delta) override; - void _setup_modification(SkeletonModificationStack2D *p_stack) override; + void _notification(int p_what); void set_target_node(const NodePath &p_target_node); NodePath get_target_node() const; diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/2d/skeleton_modification_2d_lookat.cpp similarity index 67% rename from scene/resources/skeleton_modification_2d_lookat.cpp rename to scene/2d/skeleton_modification_2d_lookat.cpp index d3cfffb1deac..380cd128da14 100644 --- a/scene/resources/skeleton_modification_2d_lookat.cpp +++ b/scene/2d/skeleton_modification_2d_lookat.cpp @@ -104,123 +104,41 @@ void SkeletonModification2DLookAt::_get_property_list(List *p_list #endif // TOOLS_ENABLED } -void SkeletonModification2DLookAt::_execute(float p_delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - - if (target_node_cache.is_null()) { - WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); - update_target_cache(); - return; - } - - if (bone2d_node_cache.is_null() && !bone2d_node.is_empty()) { - update_bone2d_cache(); - WARN_PRINT_ONCE("Bone2D node cache is out of date. Attempting to update..."); - return; - } - - if (target_node_reference == nullptr) { - target_node_reference = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - } - if (!target_node_reference || !target_node_reference->is_inside_tree()) { - ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); - return; - } - if (bone_idx <= -1) { - ERR_PRINT_ONCE("Bone index is invalid. Cannot execute modification!"); - return; - } - - Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx); - if (operation_bone == nullptr) { - ERR_PRINT_ONCE("bone_idx for modification does not point to a valid bone! Cannot execute modification"); - return; - } - - Transform2D operation_transform = operation_bone->get_global_transform(); - Transform2D target_trans = target_node_reference->get_global_transform(); - - // Look at the target! - operation_transform = operation_transform.looking_at(target_trans.get_origin()); - // Apply whatever scale it had prior to looking_at - operation_transform.set_scale(operation_bone->get_global_scale()); - - // Account for the direction the bone faces in: - operation_transform.set_rotation(operation_transform.get_rotation() - operation_bone->get_bone_angle()); - - // Apply additional rotation - operation_transform.set_rotation(operation_transform.get_rotation() + additional_rotation); - - // Apply constraints in globalspace: - if (enable_constraint && !constraint_in_localspace) { - operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), constraint_angle_min, constraint_angle_max, constraint_angle_invert)); - } - - // Convert from a global transform to a local transform via the Bone2D node - operation_bone->set_global_transform(operation_transform); - operation_transform = operation_bone->get_transform(); - - // Apply constraints in localspace: - if (enable_constraint && constraint_in_localspace) { - operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), constraint_angle_min, constraint_angle_max, constraint_angle_invert)); - } - - // Set the local pose override, and to make sure child bones are also updated, set the transform of the bone. - stack->skeleton->set_bone_local_pose_override(bone_idx, operation_transform, stack->strength, true); - operation_bone->set_transform(operation_transform); -} - -void SkeletonModification2DLookAt::_setup_modification(SkeletonModificationStack2D *p_stack) { - stack = p_stack; - - if (stack != nullptr) { - is_setup = true; - update_target_cache(); - update_bone2d_cache(); - } -} - void SkeletonModification2DLookAt::_draw_editor_gizmo() { if (!enabled || !is_setup) { return; } - Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx); + Bone2D *operation_bone = skeleton->get_bone(bone_idx); editor_draw_angle_constraints(operation_bone, constraint_angle_min, constraint_angle_max, enable_constraint, constraint_in_localspace, constraint_angle_invert); } void SkeletonModification2DLookAt::update_bone2d_cache() { - if (!is_setup || !stack) { + if (!is_setup) { ERR_PRINT_ONCE("Cannot update Bone2D cache: modification is not properly setup!"); return; } bone2d_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(bone2d_node)) { - Node *node = stack->skeleton->get_node(bone2d_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update Bone2D cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update Bone2D cache: node is not in the scene tree!"); - bone2d_node_cache = node->get_instance_id(); - - Bone2D *bone = Object::cast_to(node); - if (bone) { - bone_idx = bone->get_index_in_skeleton(); - } else { - ERR_FAIL_MSG("Error Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); - } - - // Set this to null so we update it - target_node_reference = nullptr; + if (is_inside_tree()) { + if (has_node(bone2d_node)) { + Node *node = get_node_or_null(bone2d_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, + "Cannot update Bone2D cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update Bone2D cache: node is not in the scene tree!"); + bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to(node); + if (bone) { + bone_idx = bone->get_index_in_skeleton(); + } else { + ERR_FAIL_MSG("Error Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } + + // Set this to null so we update it + target_node_reference = nullptr; } } } @@ -241,12 +159,12 @@ int SkeletonModification2DLookAt::get_bone_index() const { void SkeletonModification2DLookAt::set_bone_index(int p_bone_idx) { ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); - if (is_setup && stack) { - if (stack->skeleton) { - ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); + if (is_setup) { + if (skeleton) { + ERR_FAIL_INDEX_MSG(p_bone_idx, skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); bone_idx = p_bone_idx; - bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); - bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); + bone2d_node_cache = skeleton->get_bone(p_bone_idx)->get_instance_id(); + bone2d_node = skeleton->get_path_to(skeleton->get_bone(p_bone_idx)); } else { WARN_PRINT("Cannot verify the bone index for this modification..."); bone_idx = p_bone_idx; @@ -260,22 +178,20 @@ void SkeletonModification2DLookAt::set_bone_index(int p_bone_idx) { } void SkeletonModification2DLookAt::update_target_cache() { - if (!is_setup || !stack) { + if (!is_setup) { ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); return; } target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update target cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update target cache: node is not in the scene tree!"); - target_node_cache = node->get_instance_id(); - } + if (is_inside_tree()) { + if (has_node(target_node)) { + Node *node = get_node_or_null(target_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, + "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in the scene tree!"); + target_node_cache = node->get_instance_id(); } } } @@ -300,11 +216,6 @@ void SkeletonModification2DLookAt::set_additional_rotation(float p_rotation) { void SkeletonModification2DLookAt::set_enable_constraint(bool p_constraint) { enable_constraint = p_constraint; notify_property_list_changed(); -#ifdef TOOLS_ENABLED - if (stack && is_setup) { - stack->set_editor_gizmos_dirty(true); - } -#endif // TOOLS_ENABLED } bool SkeletonModification2DLookAt::get_enable_constraint() const { @@ -313,11 +224,6 @@ bool SkeletonModification2DLookAt::get_enable_constraint() const { void SkeletonModification2DLookAt::set_constraint_angle_min(float p_angle_min) { constraint_angle_min = p_angle_min; -#ifdef TOOLS_ENABLED - if (stack && is_setup) { - stack->set_editor_gizmos_dirty(true); - } -#endif // TOOLS_ENABLED } float SkeletonModification2DLookAt::get_constraint_angle_min() const { @@ -326,11 +232,6 @@ float SkeletonModification2DLookAt::get_constraint_angle_min() const { void SkeletonModification2DLookAt::set_constraint_angle_max(float p_angle_max) { constraint_angle_max = p_angle_max; -#ifdef TOOLS_ENABLED - if (stack && is_setup) { - stack->set_editor_gizmos_dirty(true); - } -#endif // TOOLS_ENABLED } float SkeletonModification2DLookAt::get_constraint_angle_max() const { @@ -339,11 +240,6 @@ float SkeletonModification2DLookAt::get_constraint_angle_max() const { void SkeletonModification2DLookAt::set_constraint_angle_invert(bool p_invert) { constraint_angle_invert = p_invert; -#ifdef TOOLS_ENABLED - if (stack && is_setup) { - stack->set_editor_gizmos_dirty(true); - } -#endif // TOOLS_ENABLED } bool SkeletonModification2DLookAt::get_constraint_angle_invert() const { @@ -352,11 +248,6 @@ bool SkeletonModification2DLookAt::get_constraint_angle_invert() const { void SkeletonModification2DLookAt::set_constraint_in_localspace(bool p_constraint_in_localspace) { constraint_in_localspace = p_constraint_in_localspace; -#ifdef TOOLS_ENABLED - if (stack && is_setup) { - stack->set_editor_gizmos_dirty(true); - } -#endif // TOOLS_ENABLED } bool SkeletonModification2DLookAt::get_constraint_in_localspace() const { @@ -390,7 +281,6 @@ void SkeletonModification2DLookAt::_bind_methods() { } SkeletonModification2DLookAt::SkeletonModification2DLookAt() { - stack = nullptr; is_setup = false; bone_idx = -1; additional_rotation = 0; @@ -405,3 +295,91 @@ SkeletonModification2DLookAt::SkeletonModification2DLookAt() { SkeletonModification2DLookAt::~SkeletonModification2DLookAt() { } +void SkeletonModification2DLookAt::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + set_process_internal(false); + set_physics_process_internal(false); + if (get_execution_mode() == 0) { + set_process_internal(true); + } else if (get_execution_mode() == 1) { + set_physics_process_internal(true); + } + skeleton = cast_to(get_node_or_null(get_skeleton_path())); + is_setup = true; + update_target_cache(); + update_bone2d_cache(); + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: + [[fallthrough]]; + case NOTIFICATION_INTERNAL_PROCESS: { + ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr, + "Modification is not setup and therefore cannot execute!"); + if (!enabled) { + return; + } + + if (target_node_cache.is_null()) { + WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + + if (bone2d_node_cache.is_null() && !bone2d_node.is_empty()) { + update_bone2d_cache(); + WARN_PRINT_ONCE("Bone2D node cache is out of date. Attempting to update..."); + return; + } + + if (target_node_reference == nullptr) { + target_node_reference = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + } + if (!target_node_reference || !target_node_reference->is_inside_tree()) { + ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); + return; + } + if (bone_idx <= -1) { + ERR_PRINT_ONCE("Bone index is invalid. Cannot execute modification!"); + return; + } + + Bone2D *operation_bone = skeleton->get_bone(bone_idx); + if (operation_bone == nullptr) { + ERR_PRINT_ONCE("bone_idx for modification does not point to a valid bone! Cannot execute modification"); + return; + } + + Transform2D operation_transform = operation_bone->get_global_transform(); + Transform2D target_trans = target_node_reference->get_global_transform(); + + // Look at the target! + operation_transform = operation_transform.looking_at(target_trans.get_origin()); + // Apply whatever scale it had prior to looking_at + operation_transform.set_scale(operation_bone->get_global_scale()); + + // Account for the direction the bone faces in: + operation_transform.set_rotation(operation_transform.get_rotation() - operation_bone->get_bone_angle()); + + // Apply additional rotation + operation_transform.set_rotation(operation_transform.get_rotation() + additional_rotation); + + // Apply constraints in globalspace: + if (enable_constraint && !constraint_in_localspace) { + operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), constraint_angle_min, constraint_angle_max, constraint_angle_invert)); + } + + // Convert from a global transform to a local transform via the Bone2D node + operation_bone->set_global_transform(operation_transform); + operation_transform = operation_bone->get_transform(); + + // Apply constraints in localspace: + if (enable_constraint && constraint_in_localspace) { + operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), constraint_angle_min, constraint_angle_max, constraint_angle_invert)); + } + + // Set the local pose override, and to make sure child bones are also updated, set the transform of the bone. + skeleton->set_bone_local_pose_override(bone_idx, operation_transform, 1.0, true); + operation_bone->set_transform(operation_transform); + } break; + } +} diff --git a/scene/resources/skeleton_modification_2d_lookat.h b/scene/2d/skeleton_modification_2d_lookat.h similarity index 96% rename from scene/resources/skeleton_modification_2d_lookat.h rename to scene/2d/skeleton_modification_2d_lookat.h index e4d7afa60505..51f32d59a1ba 100644 --- a/scene/resources/skeleton_modification_2d_lookat.h +++ b/scene/2d/skeleton_modification_2d_lookat.h @@ -32,7 +32,7 @@ #define SKELETON_MODIFICATION_2D_LOOKAT_H #include "scene/2d/skeleton_2d.h" -#include "scene/resources/skeleton_modification_2d.h" +#include "scene/2d/skeleton_modification_2d.h" /////////////////////////////////////// // SkeletonModification2DLookAt @@ -67,8 +67,7 @@ class SkeletonModification2DLookAt : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void _execute(float p_delta) override; - void _setup_modification(SkeletonModificationStack2D *p_stack) override; + void _notification(int p_what); void _draw_editor_gizmo() override; void set_bone2d_node(const NodePath &p_target_node); diff --git a/scene/resources/skeleton_modification_2d_physicalbones.cpp b/scene/2d/skeleton_modification_2d_physicalbones.cpp similarity index 75% rename from scene/resources/skeleton_modification_2d_physicalbones.cpp rename to scene/2d/skeleton_modification_2d_physicalbones.cpp index ddfd1d06b370..74b12a2bf606 100644 --- a/scene/resources/skeleton_modification_2d_physicalbones.cpp +++ b/scene/2d/skeleton_modification_2d_physicalbones.cpp @@ -99,77 +99,22 @@ void SkeletonModification2DPhysicalBones::_get_property_list(List } } -void SkeletonModification2DPhysicalBones::_execute(float p_delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - - if (_simulation_state_dirty) { - _update_simulation_state(); - } - - for (int i = 0; i < physical_bone_chain.size(); i++) { - PhysicalBone_Data2D bone_data = physical_bone_chain[i]; - if (bone_data.physical_bone_node_cache.is_null()) { - WARN_PRINT_ONCE("PhysicalBone2D cache " + itos(i) + " is out of date. Attempting to update..."); - _physical_bone_update_cache(i); - continue; - } - - PhysicalBone2D *physical_bone = Object::cast_to(ObjectDB::get_instance(bone_data.physical_bone_node_cache)); - if (!physical_bone) { - ERR_PRINT_ONCE("PhysicalBone2D not found at index " + itos(i) + "!"); - return; - } - if (physical_bone->get_bone2d_index() < 0 || physical_bone->get_bone2d_index() > stack->skeleton->get_bone_count()) { - ERR_PRINT_ONCE("PhysicalBone2D at index " + itos(i) + " has invalid Bone2D!"); - return; - } - Bone2D *bone_2d = stack->skeleton->get_bone(physical_bone->get_bone2d_index()); - - if (physical_bone->get_simulate_physics() && !physical_bone->get_follow_bone_when_simulating()) { - bone_2d->set_global_transform(physical_bone->get_global_transform()); - stack->skeleton->set_bone_local_pose_override(physical_bone->get_bone2d_index(), bone_2d->get_transform(), stack->strength, true); - } - } -} - -void SkeletonModification2DPhysicalBones::_setup_modification(SkeletonModificationStack2D *p_stack) { - stack = p_stack; - - if (stack) { - is_setup = true; - - if (stack->skeleton) { - for (int i = 0; i < physical_bone_chain.size(); i++) { - _physical_bone_update_cache(i); - } - } - } -} - void SkeletonModification2DPhysicalBones::_physical_bone_update_cache(int p_joint_idx) { ERR_FAIL_INDEX_MSG(p_joint_idx, physical_bone_chain.size(), "Cannot update PhysicalBone2D cache: joint index out of range!"); - if (!is_setup || !stack) { - if (!stack) { - ERR_PRINT_ONCE("Cannot update PhysicalBone2D cache: modification is not properly setup!"); - } + if (!is_setup) { + ERR_PRINT_ONCE("Cannot update PhysicalBone2D cache: modification is not properly setup!"); return; } physical_bone_chain.write[p_joint_idx].physical_bone_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(physical_bone_chain[p_joint_idx].physical_bone_node)) { - Node *node = stack->skeleton->get_node(physical_bone_chain[p_joint_idx].physical_bone_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update Physical Bone2D " + itos(p_joint_idx) + " cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update Physical Bone2D " + itos(p_joint_idx) + " cache: node is not in scene tree!"); - physical_bone_chain.write[p_joint_idx].physical_bone_node_cache = node->get_instance_id(); - } + if (is_inside_tree()) { + if (has_node(physical_bone_chain[p_joint_idx].physical_bone_node)) { + Node *node = get_node(physical_bone_chain[p_joint_idx].physical_bone_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, + "Cannot update Physical Bone2D " + itos(p_joint_idx) + " cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update Physical Bone2D " + itos(p_joint_idx) + " cache: node is not in scene tree!"); + physical_bone_chain.write[p_joint_idx].physical_bone_node_cache = node->get_instance_id(); } } } @@ -185,13 +130,12 @@ void SkeletonModification2DPhysicalBones::set_physical_bone_chain_length(int p_l } void SkeletonModification2DPhysicalBones::fetch_physical_bones() { - ERR_FAIL_COND_MSG(!stack, "No modification stack found! Cannot fetch physical bones!"); - ERR_FAIL_COND_MSG(!stack->skeleton, "No skeleton found! Cannot fetch physical bones!"); + ERR_FAIL_COND_MSG(!skeleton, "No skeleton found! Cannot fetch physical bones!"); physical_bone_chain.clear(); List node_queue = List(); - node_queue.push_back(stack->skeleton); + node_queue.push_back(skeleton); while (node_queue.size() > 0) { Node *node_to_process = node_queue[0]; @@ -201,7 +145,7 @@ void SkeletonModification2DPhysicalBones::fetch_physical_bones() { PhysicalBone2D *potential_bone = Object::cast_to(node_to_process); if (potential_bone) { PhysicalBone_Data2D new_data = PhysicalBone_Data2D(); - new_data.physical_bone_node = stack->skeleton->get_path_to(potential_bone); + new_data.physical_bone_node = skeleton->get_path_to(potential_bone); new_data.physical_bone_node_cache = potential_bone->get_instance_id(); physical_bone_chain.push_back(new_data); } @@ -240,7 +184,7 @@ void SkeletonModification2DPhysicalBones::_update_simulation_state() { if (_simulation_state_dirty_names.size() <= 0) { for (int i = 0; i < physical_bone_chain.size(); i++) { - PhysicalBone2D *physical_bone = Object::cast_to(stack->skeleton->get_node(physical_bone_chain[i].physical_bone_node)); + PhysicalBone2D *physical_bone = Object::cast_to(skeleton->get_node(physical_bone_chain[i].physical_bone_node)); if (!physical_bone) { continue; } @@ -286,7 +230,6 @@ void SkeletonModification2DPhysicalBones::_bind_methods() { } SkeletonModification2DPhysicalBones::SkeletonModification2DPhysicalBones() { - stack = nullptr; is_setup = false; physical_bone_chain = Vector(); enabled = true; diff --git a/scene/resources/skeleton_modification_2d_physicalbones.h b/scene/2d/skeleton_modification_2d_physicalbones.h similarity index 62% rename from scene/resources/skeleton_modification_2d_physicalbones.h rename to scene/2d/skeleton_modification_2d_physicalbones.h index 55482b2cca01..f17d3843092c 100644 --- a/scene/resources/skeleton_modification_2d_physicalbones.h +++ b/scene/2d/skeleton_modification_2d_physicalbones.h @@ -31,8 +31,9 @@ #ifndef SKELETON_MODIFICATION_2D_PHYSICALBONES_H #define SKELETON_MODIFICATION_2D_PHYSICALBONES_H +#include "scene/2d/physical_bone_2d.h" #include "scene/2d/skeleton_2d.h" -#include "scene/resources/skeleton_modification_2d.h" +#include "scene/2d/skeleton_modification_2d.h" /////////////////////////////////////// // SkeletonModification2DJIGGLE @@ -62,8 +63,64 @@ class SkeletonModification2DPhysicalBones : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void _execute(float p_delta) override; - void _setup_modification(SkeletonModificationStack2D *p_stack) override; + void _notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + set_process_internal(false); + set_physics_process_internal(false); + if (get_execution_mode() == 0) { + set_process_internal(true); + } else if (get_execution_mode() == 1) { + set_physics_process_internal(true); + } + is_setup = true; + skeleton = cast_to(get_node_or_null(get_skeleton_path())); + if (skeleton) { + for (int i = 0; i < physical_bone_chain.size(); i++) { + _physical_bone_update_cache(i); + } + } + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: + [[fallthrough]]; + case NOTIFICATION_INTERNAL_PROCESS: { + ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr, + "Modification is not setup and therefore cannot execute!"); + if (!enabled) { + return; + } + + if (_simulation_state_dirty) { + _update_simulation_state(); + } + + for (int i = 0; i < physical_bone_chain.size(); i++) { + PhysicalBone_Data2D bone_data = physical_bone_chain[i]; + if (bone_data.physical_bone_node_cache.is_null()) { + WARN_PRINT_ONCE("PhysicalBone2D cache " + itos(i) + " is out of date. Attempting to update..."); + _physical_bone_update_cache(i); + continue; + } + + PhysicalBone2D *physical_bone = Object::cast_to(ObjectDB::get_instance(bone_data.physical_bone_node_cache)); + if (!physical_bone) { + ERR_PRINT_ONCE("PhysicalBone2D not found at index " + itos(i) + "!"); + return; + } + if (physical_bone->get_bone2d_index() < 0 || physical_bone->get_bone2d_index() > skeleton->get_bone_count()) { + ERR_PRINT_ONCE("PhysicalBone2D at index " + itos(i) + " has invalid Bone2D!"); + return; + } + Bone2D *bone_2d = skeleton->get_bone(physical_bone->get_bone2d_index()); + + if (physical_bone->get_simulate_physics() && !physical_bone->get_follow_bone_when_simulating()) { + bone_2d->set_global_transform(physical_bone->get_global_transform()); + skeleton->set_bone_local_pose_override(physical_bone->get_bone2d_index(), bone_2d->get_transform(), 1.0, true); + } + } + } break; + } + } int get_physical_bone_chain_length(); void set_physical_bone_chain_length(int p_new_length); diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/2d/skeleton_modification_2d_twoboneik.cpp similarity index 67% rename from scene/resources/skeleton_modification_2d_twoboneik.cpp rename to scene/2d/skeleton_modification_2d_twoboneik.cpp index d3c62e441f61..7c09c84e192b 100644 --- a/scene/resources/skeleton_modification_2d_twoboneik.cpp +++ b/scene/2d/skeleton_modification_2d_twoboneik.cpp @@ -98,116 +98,18 @@ void SkeletonModification2DTwoBoneIK::_get_property_list(List *p_l #endif // TOOLS_ENABLED } -void SkeletonModification2DTwoBoneIK::_execute(float p_delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - - if (target_node_cache.is_null()) { - WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); - update_target_cache(); - return; - } - - if (joint_one_bone2d_node_cache.is_null() && !joint_one_bone2d_node.is_empty()) { - WARN_PRINT_ONCE("Joint one Bone2D node cache is out of date. Attempting to update..."); - update_joint_one_bone2d_cache(); - } - if (joint_two_bone2d_node_cache.is_null() && !joint_two_bone2d_node.is_empty()) { - WARN_PRINT_ONCE("Joint two Bone2D node cache is out of date. Attempting to update..."); - update_joint_two_bone2d_cache(); - } - - Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (!target || !target->is_inside_tree()) { - ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); - return; - } - - Bone2D *joint_one_bone = stack->skeleton->get_bone(joint_one_bone_idx); - if (joint_one_bone == nullptr) { - ERR_PRINT_ONCE("Joint one bone_idx does not point to a valid bone! Cannot execute modification!"); - return; - } - - Bone2D *joint_two_bone = stack->skeleton->get_bone(joint_two_bone_idx); - if (joint_two_bone == nullptr) { - ERR_PRINT_ONCE("Joint two bone_idx does not point to a valid bone! Cannot execute modification!"); - return; - } - - // Adopted from the links below: - // http://theorangeduck.com/page/simple-two-joint - // https://www.alanzucconi.com/2018/05/02/ik-2d-2/ - // With modifications by TwistedTwigleg - Vector2 target_difference = target->get_global_position() - joint_one_bone->get_global_position(); - float joint_one_to_target = target_difference.length(); - float angle_atan = target_difference.angle(); - - float bone_one_length = joint_one_bone->get_length() * MIN(joint_one_bone->get_global_scale().x, joint_one_bone->get_global_scale().y); - float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().x, joint_two_bone->get_global_scale().y); - bool override_angles_due_to_out_of_range = false; - - if (joint_one_to_target < target_minimum_distance) { - joint_one_to_target = target_minimum_distance; - } - if (joint_one_to_target > target_maximum_distance && target_maximum_distance > 0.0) { - joint_one_to_target = target_maximum_distance; - } - - if (bone_one_length + bone_two_length < joint_one_to_target) { - override_angles_due_to_out_of_range = true; - } - - if (!override_angles_due_to_out_of_range) { - float angle_0 = Math::acos(((joint_one_to_target * joint_one_to_target) + (bone_one_length * bone_one_length) - (bone_two_length * bone_two_length)) / (2.0 * joint_one_to_target * bone_one_length)); - float angle_1 = Math::acos(((bone_two_length * bone_two_length) + (bone_one_length * bone_one_length) - (joint_one_to_target * joint_one_to_target)) / (2.0 * bone_two_length * bone_one_length)); - - if (flip_bend_direction) { - angle_0 = -angle_0; - angle_1 = -angle_1; - } - - if (isnan(angle_0) || isnan(angle_1)) { - // We cannot solve for this angle! Do nothing to avoid setting the rotation (and scale) to NaN. - } else { - joint_one_bone->set_global_rotation(angle_atan - angle_0 - joint_one_bone->get_bone_angle()); - joint_two_bone->set_rotation(-Math_PI - angle_1 - joint_two_bone->get_bone_angle() + joint_one_bone->get_bone_angle()); - } - } else { - joint_one_bone->set_global_rotation(angle_atan - joint_one_bone->get_bone_angle()); - joint_two_bone->set_global_rotation(angle_atan - joint_two_bone->get_bone_angle()); - } - - stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, joint_one_bone->get_transform(), stack->strength, true); - stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, joint_two_bone->get_transform(), stack->strength, true); -} - -void SkeletonModification2DTwoBoneIK::_setup_modification(SkeletonModificationStack2D *p_stack) { - stack = p_stack; - - if (stack) { - is_setup = true; - update_target_cache(); - update_joint_one_bone2d_cache(); - update_joint_two_bone2d_cache(); - } -} - void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() { if (!enabled || !is_setup) { return; } - Bone2D *operation_bone_one = stack->skeleton->get_bone(joint_one_bone_idx); + Bone2D *operation_bone_one = skeleton->get_bone(joint_one_bone_idx); if (!operation_bone_one) { return; } - stack->skeleton->draw_set_transform( - stack->skeleton->to_local(operation_bone_one->get_global_position()), - operation_bone_one->get_global_rotation() - stack->skeleton->get_global_rotation()); + skeleton->draw_set_transform( + skeleton->to_local(operation_bone_one->get_global_position()), + operation_bone_one->get_global_rotation() - skeleton->get_global_rotation()); Color bone_ik_color = Color(1.0, 0.65, 0.0, 0.4); #ifdef TOOLS_ENABLED @@ -218,10 +120,10 @@ void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() { if (flip_bend_direction) { float angle = -(Math_PI * 0.5) + operation_bone_one->get_bone_angle(); - stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); + skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); } else { float angle = (Math_PI * 0.5) + operation_bone_one->get_bone_angle(); - stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); + skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); } #ifdef TOOLS_ENABLED @@ -230,14 +132,14 @@ void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() { if (target_maximum_distance != 0.0 || target_minimum_distance != 0.0) { Vector2 target_direction = Vector2(0, 1); if (target_node_cache.is_valid()) { - stack->skeleton->draw_set_transform(Vector2(0, 0), 0.0); + skeleton->draw_set_transform(Vector2(0, 0), 0.0); Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); target_direction = operation_bone_one->get_global_position().direction_to(target->get_global_position()); } - stack->skeleton->draw_circle(target_direction * target_minimum_distance, 8, bone_ik_color); - stack->skeleton->draw_circle(target_direction * target_maximum_distance, 8, bone_ik_color); - stack->skeleton->draw_line(target_direction * target_minimum_distance, target_direction * target_maximum_distance, bone_ik_color, 2.0); + skeleton->draw_circle(target_direction * target_minimum_distance, 8, bone_ik_color); + skeleton->draw_circle(target_direction * target_maximum_distance, 8, bone_ik_color); + skeleton->draw_line(target_direction * target_minimum_distance, target_direction * target_maximum_distance, bone_ik_color, 2.0); } } } @@ -245,17 +147,17 @@ void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() { } void SkeletonModification2DTwoBoneIK::update_target_cache() { - if (!is_setup || !stack) { + if (!is_setup) { ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); return; } target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + if (skeleton) { + if (skeleton->is_inside_tree()) { + if (skeleton->has_node(target_node)) { + Node *node = skeleton->get_node(target_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, "Cannot update target cache: node is this modification's skeleton or cannot be found!"); ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Cannot update target cache: node is not in the scene tree!"); @@ -266,17 +168,17 @@ void SkeletonModification2DTwoBoneIK::update_target_cache() { } void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { - if (!is_setup || !stack) { + if (!is_setup) { ERR_PRINT_ONCE("Cannot update joint one Bone2D cache: modification is not properly setup!"); return; } joint_one_bone2d_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(joint_one_bone2d_node)) { - Node *node = stack->skeleton->get_node(joint_one_bone2d_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + if (skeleton) { + if (skeleton->is_inside_tree()) { + if (skeleton->has_node(joint_one_bone2d_node)) { + Node *node = skeleton->get_node(joint_one_bone2d_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, "Cannot update update joint one Bone2D cache: node is this modification's skeleton or cannot be found!"); ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Cannot update update joint one Bone2D cache: node is not in the scene tree!"); @@ -294,17 +196,17 @@ void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { } void SkeletonModification2DTwoBoneIK::update_joint_two_bone2d_cache() { - if (!is_setup || !stack) { + if (!is_setup) { ERR_PRINT_ONCE("Cannot update joint two Bone2D cache: modification is not properly setup!"); return; } joint_two_bone2d_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(joint_two_bone2d_node)) { - Node *node = stack->skeleton->get_node(joint_two_bone2d_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + if (skeleton) { + if (skeleton->is_inside_tree()) { + if (skeleton->has_node(joint_two_bone2d_node)) { + Node *node = skeleton->get_node(joint_two_bone2d_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, "Cannot update update joint two Bone2D cache: node is this modification's skeleton or cannot be found!"); ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Cannot update update joint two Bone2D cache: node is not in scene tree!"); @@ -356,12 +258,6 @@ float SkeletonModification2DTwoBoneIK::get_target_maximum_distance() const { void SkeletonModification2DTwoBoneIK::set_flip_bend_direction(bool p_flip_direction) { flip_bend_direction = p_flip_direction; - -#ifdef TOOLS_ENABLED - if (stack && is_setup) { - stack->set_editor_gizmos_dirty(true); - } -#endif // TOOLS_ENABLED } bool SkeletonModification2DTwoBoneIK::get_flip_bend_direction() const { @@ -386,11 +282,11 @@ void SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) { ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); if (is_setup) { - if (stack->skeleton) { - ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); + if (skeleton) { + ERR_FAIL_INDEX_MSG(p_bone_idx, skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); joint_one_bone_idx = p_bone_idx; - joint_one_bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); - joint_one_bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); + joint_one_bone2d_node_cache = skeleton->get_bone(p_bone_idx)->get_instance_id(); + joint_one_bone2d_node = skeleton->get_path_to(skeleton->get_bone(p_bone_idx)); } else { WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint one..."); joint_one_bone_idx = p_bone_idx; @@ -411,11 +307,11 @@ void SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) { ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); if (is_setup) { - if (stack->skeleton) { - ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); + if (skeleton) { + ERR_FAIL_INDEX_MSG(p_bone_idx, skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); joint_two_bone_idx = p_bone_idx; - joint_two_bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); - joint_two_bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); + joint_two_bone2d_node_cache = skeleton->get_bone(p_bone_idx)->get_instance_id(); + joint_two_bone2d_node = skeleton->get_path_to(skeleton->get_bone(p_bone_idx)); } else { WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint two..."); joint_two_bone_idx = p_bone_idx; @@ -471,7 +367,6 @@ void SkeletonModification2DTwoBoneIK::_bind_methods() { } SkeletonModification2DTwoBoneIK::SkeletonModification2DTwoBoneIK() { - stack = nullptr; is_setup = false; enabled = true; editor_draw_gizmo = true; @@ -479,3 +374,114 @@ SkeletonModification2DTwoBoneIK::SkeletonModification2DTwoBoneIK() { SkeletonModification2DTwoBoneIK::~SkeletonModification2DTwoBoneIK() { } + +void SkeletonModification2DTwoBoneIK::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + set_process_internal(false); + set_physics_process_internal(false); + if (get_execution_mode() == 0) { + set_process_internal(true); + } else if (get_execution_mode() == 1) { + set_physics_process_internal(true); + } + skeleton = cast_to(get_node_or_null(get_skeleton_path())); + is_setup = true; + update_target_cache(); + update_joint_one_bone2d_cache(); + update_joint_two_bone2d_cache(); + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: + [[fallthrough]]; + case NOTIFICATION_INTERNAL_PROCESS: { + if (!is_setup) { + ERR_PRINT_ONCE("Modification is not setup."); + } + if (!skeleton) { + ERR_PRINT_ONCE("Modification does not have a skeleton path."); + } + if (!enabled) { + return; + } + if (target_node_cache.is_null()) { + WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + + if (joint_one_bone2d_node_cache.is_null() && !joint_one_bone2d_node.is_empty()) { + WARN_PRINT_ONCE("Joint one Bone2D node cache is out of date. Attempting to update..."); + update_joint_one_bone2d_cache(); + } + if (joint_two_bone2d_node_cache.is_null() && !joint_two_bone2d_node.is_empty()) { + WARN_PRINT_ONCE("Joint two Bone2D node cache is out of date. Attempting to update..."); + update_joint_two_bone2d_cache(); + } + + Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + if (!target || !target->is_inside_tree()) { + ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); + return; + } + + Bone2D *joint_one_bone = skeleton->get_bone(joint_one_bone_idx); + if (joint_one_bone == nullptr) { + ERR_PRINT_ONCE("Joint one bone_idx does not point to a valid bone! Cannot execute modification!"); + return; + } + + Bone2D *joint_two_bone = skeleton->get_bone(joint_two_bone_idx); + if (joint_two_bone == nullptr) { + ERR_PRINT_ONCE("Joint two bone_idx does not point to a valid bone! Cannot execute modification!"); + return; + } + + // Adopted from the links below: + // http://theorangeduck.com/page/simple-two-joint + // https://www.alanzucconi.com/2018/05/02/ik-2d-2/ + // With modifications by TwistedTwigleg + Vector2 target_difference = target->get_global_position() - joint_one_bone->get_global_position(); + float joint_one_to_target = target_difference.length(); + float angle_atan = target_difference.angle(); + + float bone_one_length = joint_one_bone->get_length() * MIN(joint_one_bone->get_global_scale().x, joint_one_bone->get_global_scale().y); + float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().x, joint_two_bone->get_global_scale().y); + bool override_angles_due_to_out_of_range = false; + + if (joint_one_to_target < target_minimum_distance) { + joint_one_to_target = target_minimum_distance; + } + if (joint_one_to_target > target_maximum_distance && target_maximum_distance > 0.0) { + joint_one_to_target = target_maximum_distance; + } + + if (bone_one_length + bone_two_length < joint_one_to_target) { + override_angles_due_to_out_of_range = true; + } + + if (!override_angles_due_to_out_of_range) { + float angle_0 = Math::acos(((joint_one_to_target * joint_one_to_target) + (bone_one_length * bone_one_length) - (bone_two_length * bone_two_length)) / (2.0 * joint_one_to_target * bone_one_length)); + float angle_1 = Math::acos(((bone_two_length * bone_two_length) + (bone_one_length * bone_one_length) - (joint_one_to_target * joint_one_to_target)) / (2.0 * bone_two_length * bone_one_length)); + + if (flip_bend_direction) { + angle_0 = -angle_0; + angle_1 = -angle_1; + } + + if (isnan(angle_0) || isnan(angle_1)) { + // We cannot solve for this angle! Do nothing to avoid setting the rotation (and scale) to NaN. + } else { + joint_one_bone->set_global_rotation(angle_atan - angle_0 - joint_one_bone->get_bone_angle()); + joint_two_bone->set_rotation(-Math_PI - angle_1 - joint_two_bone->get_bone_angle() + joint_one_bone->get_bone_angle()); + } + } else { + joint_one_bone->set_global_rotation(angle_atan - joint_one_bone->get_bone_angle()); + joint_two_bone->set_global_rotation(angle_atan - joint_two_bone->get_bone_angle()); + } + + skeleton->set_bone_local_pose_override(joint_one_bone_idx, joint_one_bone->get_transform(), 1.0, true); + skeleton->set_bone_local_pose_override(joint_two_bone_idx, joint_two_bone->get_transform(), 1.0, true); + + } break; + } +} diff --git a/scene/resources/skeleton_modification_2d_twoboneik.h b/scene/2d/skeleton_modification_2d_twoboneik.h similarity index 96% rename from scene/resources/skeleton_modification_2d_twoboneik.h rename to scene/2d/skeleton_modification_2d_twoboneik.h index e1dbb6cfda43..5088c07e0ce8 100644 --- a/scene/resources/skeleton_modification_2d_twoboneik.h +++ b/scene/2d/skeleton_modification_2d_twoboneik.h @@ -32,7 +32,7 @@ #define SKELETON_MODIFICATION_2D_TWOBONEIK_H #include "scene/2d/skeleton_2d.h" -#include "scene/resources/skeleton_modification_2d.h" +#include "scene/2d/skeleton_modification_2d.h" /////////////////////////////////////// // SkeletonModification2DJIGGLE @@ -71,8 +71,7 @@ class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void _execute(float p_delta) override; - void _setup_modification(SkeletonModificationStack2D *p_stack) override; + void _notification(int p_what); void _draw_editor_gizmo() override; void set_target_node(const NodePath &p_target_node); diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index e04e1866db8f..437df5931a9e 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -33,7 +33,7 @@ #include "core/object/message_queue.h" #include "core/variant/type_info.h" #include "scene/3d/physics_body_3d.h" -#include "scene/resources/skeleton_modification_3d.h" +#include "scene/3d/skeleton_modification_3d.h" #include "scene/resources/surface_tool.h" #include "scene/scene_string_names.h" @@ -71,13 +71,6 @@ SkinReference::~SkinReference() { bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { String path = p_path; -#ifndef _3D_DISABLED - if (path.begins_with("modification_stack")) { - set_modification_stack(p_value); - return true; - } -#endif //_3D_DISABLED - if (!path.begins_with("bones/")) { return false; } @@ -114,13 +107,6 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { String path = p_path; -#ifndef _3D_DISABLED - if (path.begins_with("modification_stack")) { - r_ret = modification_stack; - return true; - } -#endif //_3D_DISABLED - if (!path.begins_with("bones/")) { return false; } @@ -163,14 +149,6 @@ void Skeleton3D::_get_property_list(List *p_list) const { p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("scale"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } -#ifndef _3D_DISABLED - p_list->push_back( - PropertyInfo(Variant::OBJECT, "modification_stack", - PROPERTY_HINT_RESOURCE_TYPE, - "SkeletonModificationStack3D", - PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); -#endif //_3D_DISABLED - for (PropertyInfo &E : *p_list) { _validate_property(E); } @@ -332,24 +310,11 @@ void Skeleton3D::_notification(int p_what) { } } - if (modification_stack.is_valid()) { - execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_physics_process); - } - } break; - - case NOTIFICATION_INTERNAL_PROCESS: { - if (modification_stack.is_valid()) { - execute_modifications(get_process_delta_time(), SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_process); - } } break; case NOTIFICATION_READY: { set_physics_process_internal(true); set_process_internal(true); - - if (modification_stack.is_valid()) { - set_modification_stack(modification_stack); - } } break; #endif // _3D_DISABLED } @@ -1158,41 +1123,6 @@ Basis Skeleton3D::global_pose_z_forward_to_bone_forward(int p_bone_idx, Basis p_ return return_basis; } -// Modifications - -#ifndef _3D_DISABLED - -void Skeleton3D::set_modification_stack(Ref p_stack) { - if (modification_stack.is_valid()) { - modification_stack->is_setup = false; - modification_stack->set_skeleton(nullptr); - } - - modification_stack = p_stack; - if (modification_stack.is_valid()) { - modification_stack->set_skeleton(this); - modification_stack->setup(); - } -} -Ref Skeleton3D::get_modification_stack() { - return modification_stack; -} - -void Skeleton3D::execute_modifications(real_t p_delta, int p_execution_mode) { - if (!modification_stack.is_valid()) { - return; - } - - // Needed to avoid the issue where the stack looses reference to the skeleton when the scene is saved. - if (modification_stack->skeleton != this) { - modification_stack->set_skeleton(this); - } - - modification_stack->execute(p_delta, p_execution_mode); -} - -#endif // _3D_DISABLED - void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone); ClassDB::bind_method(D_METHOD("find_bone", "name"), &Skeleton3D::find_bone); @@ -1270,11 +1200,6 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton3D::physical_bones_add_collision_exception); ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton3D::physical_bones_remove_collision_exception); - // Modifications - ClassDB::bind_method(D_METHOD("set_modification_stack", "modification_stack"), &Skeleton3D::set_modification_stack); - ClassDB::bind_method(D_METHOD("get_modification_stack"), &Skeleton3D::get_modification_stack); - ClassDB::bind_method(D_METHOD("execute_modifications", "delta", "execution_mode"), &Skeleton3D::execute_modifications); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "motion_scale", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater"), "set_motion_scale", "get_motion_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_rest_only"), "set_show_rest_only", "is_show_rest_only"); #ifndef _3D_DISABLED diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 5e49dfa1f4d8..94e59e6c5a84 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -31,8 +31,8 @@ #ifndef SKELETON_3D_H #define SKELETON_3D_H +#include "core/object/ref_counted.h" #include "scene/3d/node_3d.h" -#include "scene/resources/skeleton_modification_3d.h" #include "scene/resources/skin.h" typedef int BoneId; @@ -62,8 +62,6 @@ class SkinReference : public RefCounted { ~SkinReference(); }; -class SkeletonModificationStack3D; - class Skeleton3D : public Node3D { GDCLASS(Skeleton3D, Node3D); @@ -160,10 +158,6 @@ class Skeleton3D : public Node3D { void _notification(int p_what); static void _bind_methods(); -#ifndef _3D_DISABLED - Ref modification_stack; -#endif // _3D_DISABLED - public: enum Bone_Forward_Axis { BONE_AXIS_X_FORWARD = 0, @@ -258,13 +252,6 @@ class Skeleton3D : public Node3D { Basis global_pose_z_forward_to_bone_forward(int p_bone_idx, Basis p_basis); - // Modifications -#ifndef _3D_DISABLED - Ref get_modification_stack(); - void set_modification_stack(Ref p_stack); - void execute_modifications(real_t p_delta, int p_execution_mode); -#endif // _3D_DISABLED - // Physical bone API void set_animate_physical_bones(bool p_enabled); diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp deleted file mode 100644 index f0534c8099c3..000000000000 --- a/scene/3d/skeleton_ik_3d.cpp +++ /dev/null @@ -1,574 +0,0 @@ -/*************************************************************************/ -/* skeleton_ik_3d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "skeleton_ik_3d.h" - -#ifndef _3D_DISABLED - -FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) { - for (int i = children.size() - 1; 0 <= i; --i) { - if (p_bone_id == children[i].bone) { - return &children.write[i]; - } - } - return nullptr; -} - -FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::add_child(const BoneId p_bone_id) { - const int infant_child_id = children.size(); - children.resize(infant_child_id + 1); - children.write[infant_child_id].bone = p_bone_id; - children.write[infant_child_id].parent_item = this; - return &children.write[infant_child_id]; -} - -/// Build a chain that starts from the root to tip -bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain) { - ERR_FAIL_COND_V(-1 == p_task->root_bone, false); - - Chain &chain(p_task->chain); - - chain.tips.resize(p_task->end_effectors.size()); - chain.chain_root.bone = p_task->root_bone; - chain.chain_root.initial_transform = p_task->skeleton->get_bone_global_pose(chain.chain_root.bone); - chain.chain_root.current_pos = chain.chain_root.initial_transform.origin; - chain.middle_chain_item = nullptr; - - // Holds all IDs that are composing a single chain in reverse order - Vector chain_ids; - // This is used to know the chain size - int sub_chain_size; - // Resize only one time in order to fit all joints for performance reason - chain_ids.resize(p_task->skeleton->get_bone_count()); - - for (int x = p_task->end_effectors.size() - 1; 0 <= x; --x) { - const EndEffector *ee(&p_task->end_effectors[x]); - ERR_FAIL_COND_V(p_task->root_bone >= ee->tip_bone, false); - ERR_FAIL_INDEX_V(ee->tip_bone, p_task->skeleton->get_bone_count(), false); - - sub_chain_size = 0; - // Picks all IDs that composing a single chain in reverse order (except the root) - BoneId chain_sub_tip(ee->tip_bone); - while (chain_sub_tip > p_task->root_bone) { - chain_ids.write[sub_chain_size++] = chain_sub_tip; - chain_sub_tip = p_task->skeleton->get_bone_parent(chain_sub_tip); - } - - BoneId middle_chain_item_id = (BoneId)(sub_chain_size * 0.5); - - // Build chain by reading chain ids in reverse order - // For each chain item id will be created a ChainItem if doesn't exists - ChainItem *sub_chain(&chain.chain_root); - for (int i = sub_chain_size - 1; 0 <= i; --i) { - ChainItem *child_ci(sub_chain->find_child(chain_ids[i])); - if (!child_ci) { - child_ci = sub_chain->add_child(chain_ids[i]); - - child_ci->initial_transform = p_task->skeleton->get_bone_global_pose(child_ci->bone); - child_ci->current_pos = child_ci->initial_transform.origin; - - if (child_ci->parent_item) { - child_ci->length = child_ci->parent_item->current_pos.distance_to(child_ci->current_pos); - } - } - - sub_chain = child_ci; - - if (middle_chain_item_id == i) { - chain.middle_chain_item = child_ci; - } - } - - if (!middle_chain_item_id) { - chain.middle_chain_item = nullptr; - } - - // Initialize current tip - chain.tips.write[x].chain_item = sub_chain; - chain.tips.write[x].end_effector = ee; - - if (p_force_simple_chain) { - // NOTE: - // This is a "hack" that force to create only one tip per chain since the solver of multi tip (end effector) - // is not yet created. - // Remove this code when this is done - break; - } - } - return true; -} - -void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet, Vector3 p_origin_pos) { - real_t distance_to_goal(1e4); - real_t previous_distance_to_goal(0); - int can_solve(p_task->max_iterations); - while (distance_to_goal > p_task->min_distance && Math::abs(previous_distance_to_goal - distance_to_goal) > 0.005 && can_solve) { - previous_distance_to_goal = distance_to_goal; - --can_solve; - - solve_simple_backwards(p_task->chain, p_solve_magnet); - solve_simple_forwards(p_task->chain, p_solve_magnet, p_origin_pos); - - distance_to_goal = p_task->chain.tips[0].end_effector->goal_transform.origin.distance_to(p_task->chain.tips[0].chain_item->current_pos); - } -} - -void FabrikInverseKinematic::solve_simple_backwards(const Chain &r_chain, bool p_solve_magnet) { - if (p_solve_magnet && !r_chain.middle_chain_item) { - return; - } - - Vector3 goal; - ChainItem *sub_chain_tip; - if (p_solve_magnet) { - goal = r_chain.magnet_position; - sub_chain_tip = r_chain.middle_chain_item; - } else { - goal = r_chain.tips[0].end_effector->goal_transform.origin; - sub_chain_tip = r_chain.tips[0].chain_item; - } - - while (sub_chain_tip) { - sub_chain_tip->current_pos = goal; - - if (sub_chain_tip->parent_item) { - // Not yet in the chain root - // So calculate next goal location - - const Vector3 look_parent((sub_chain_tip->parent_item->current_pos - sub_chain_tip->current_pos).normalized()); - goal = sub_chain_tip->current_pos + (look_parent * sub_chain_tip->length); - - // [TODO] Constraints goes here - } - - sub_chain_tip = sub_chain_tip->parent_item; - } -} - -void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_magnet, Vector3 p_origin_pos) { - if (p_solve_magnet && !r_chain.middle_chain_item) { - return; - } - - ChainItem *sub_chain_root(&r_chain.chain_root); - Vector3 origin = p_origin_pos; - - while (sub_chain_root) { // Reach the tip - sub_chain_root->current_pos = origin; - - if (!sub_chain_root->children.is_empty()) { - ChainItem &child(sub_chain_root->children.write[0]); - - // Is not tip - // So calculate next origin location - - // Look child - sub_chain_root->current_ori = (child.current_pos - sub_chain_root->current_pos).normalized(); - origin = sub_chain_root->current_pos + (sub_chain_root->current_ori * child.length); - - // [TODO] Constraints goes here - - if (p_solve_magnet && sub_chain_root == r_chain.middle_chain_item) { - // In case of magnet solving this is the tip - sub_chain_root = nullptr; - } else { - sub_chain_root = &child; - } - } else { - // Is tip - sub_chain_root = nullptr; - } - } -} - -FabrikInverseKinematic::Task *FabrikInverseKinematic::create_simple_task(Skeleton3D *p_sk, BoneId root_bone, BoneId tip_bone, const Transform3D &goal_transform) { - FabrikInverseKinematic::EndEffector ee; - ee.tip_bone = tip_bone; - - Task *task(memnew(Task)); - task->skeleton = p_sk; - task->root_bone = root_bone; - task->end_effectors.push_back(ee); - task->goal_global_transform = goal_transform; - - if (!build_chain(task)) { - free_task(task); - return nullptr; - } - - return task; -} - -void FabrikInverseKinematic::free_task(Task *p_task) { - if (p_task) { - memdelete(p_task); - } -} - -void FabrikInverseKinematic::set_goal(Task *p_task, const Transform3D &p_goal) { - p_task->goal_global_transform = p_goal; -} - -void FabrikInverseKinematic::make_goal(Task *p_task, const Transform3D &p_inverse_transf, real_t blending_delta) { - if (blending_delta >= 0.99f) { - // Update the end_effector (local transform) without blending - p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform; - } else { - // End effector in local transform - const Transform3D end_effector_pose(p_task->skeleton->get_bone_global_pose_no_override(p_task->end_effectors[0].tip_bone)); - - // Update the end_effector (local transform) by blending with current pose - p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta); - } -} - -void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) { - if (blending_delta <= 0.01f) { - // Before skipping, make sure we undo the global pose overrides - ChainItem *ci(&p_task->chain.chain_root); - while (ci) { - p_task->skeleton->set_bone_global_pose_override(ci->bone, ci->initial_transform, 0.0, false); - - if (!ci->children.is_empty()) { - ci = &ci->children.write[0]; - } else { - ci = nullptr; - } - } - - return; // Skip solving - } - - // Update the initial root transform so its synced with any animation changes - _update_chain(p_task->skeleton, &p_task->chain.chain_root); - - p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform3D(), 0.0, false); - Vector3 origin_pos = p_task->skeleton->get_bone_global_pose(p_task->chain.chain_root.bone).origin; - - make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse(), blending_delta); - - if (p_use_magnet && p_task->chain.middle_chain_item) { - p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.lerp(p_magnet_position, blending_delta); - solve_simple(p_task, true, origin_pos); - } - solve_simple(p_task, false, origin_pos); - - // Assign new bone position. - ChainItem *ci(&p_task->chain.chain_root); - while (ci) { - Transform3D new_bone_pose(ci->initial_transform); - new_bone_pose.origin = ci->current_pos; - - if (!ci->children.is_empty()) { - p_task->skeleton->update_bone_rest_forward_vector(ci->bone); - Vector3 forward_vector = p_task->skeleton->get_bone_axis_forward_vector(ci->bone); - // Rotate the bone towards the next bone in the chain: - new_bone_pose.basis.rotate_to_align(forward_vector, new_bone_pose.origin.direction_to(ci->children[0].current_pos)); - - } else { - // Set target orientation to tip - if (override_tip_basis) { - new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis; - } else { - new_bone_pose.basis = new_bone_pose.basis * p_task->chain.tips[0].end_effector->goal_transform.basis; - } - } - - // IK should not affect scale, so undo any scaling - new_bone_pose.basis.orthonormalize(); - new_bone_pose.basis.scale(p_task->skeleton->get_bone_global_pose(ci->bone).basis.get_scale()); - - p_task->skeleton->set_bone_global_pose_override(ci->bone, new_bone_pose, 1.0, true); - - if (!ci->children.is_empty()) { - ci = &ci->children.write[0]; - } else { - ci = nullptr; - } - } -} - -void FabrikInverseKinematic::_update_chain(const Skeleton3D *p_sk, ChainItem *p_chain_item) { - if (!p_chain_item) { - return; - } - - p_chain_item->initial_transform = p_sk->get_bone_global_pose_no_override(p_chain_item->bone); - p_chain_item->current_pos = p_chain_item->initial_transform.origin; - - ChainItem *items = p_chain_item->children.ptrw(); - for (int i = 0; i < p_chain_item->children.size(); i += 1) { - _update_chain(p_sk, items + i); - } -} - -void SkeletonIK3D::_validate_property(PropertyInfo &p_property) const { - if (p_property.name == "root_bone" || p_property.name == "tip_bone") { - if (skeleton) { - String names("--,"); - for (int i = 0; i < skeleton->get_bone_count(); i++) { - if (i > 0) { - names += ","; - } - names += skeleton->get_bone_name(i); - } - - p_property.hint = PROPERTY_HINT_ENUM; - p_property.hint_string = names; - } else { - p_property.hint = PROPERTY_HINT_NONE; - p_property.hint_string = ""; - } - } -} - -void SkeletonIK3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_root_bone", "root_bone"), &SkeletonIK3D::set_root_bone); - ClassDB::bind_method(D_METHOD("get_root_bone"), &SkeletonIK3D::get_root_bone); - - ClassDB::bind_method(D_METHOD("set_tip_bone", "tip_bone"), &SkeletonIK3D::set_tip_bone); - ClassDB::bind_method(D_METHOD("get_tip_bone"), &SkeletonIK3D::get_tip_bone); - - ClassDB::bind_method(D_METHOD("set_interpolation", "interpolation"), &SkeletonIK3D::set_interpolation); - ClassDB::bind_method(D_METHOD("get_interpolation"), &SkeletonIK3D::get_interpolation); - - ClassDB::bind_method(D_METHOD("set_target_transform", "target"), &SkeletonIK3D::set_target_transform); - ClassDB::bind_method(D_METHOD("get_target_transform"), &SkeletonIK3D::get_target_transform); - - ClassDB::bind_method(D_METHOD("set_target_node", "node"), &SkeletonIK3D::set_target_node); - ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonIK3D::get_target_node); - - ClassDB::bind_method(D_METHOD("set_override_tip_basis", "override"), &SkeletonIK3D::set_override_tip_basis); - ClassDB::bind_method(D_METHOD("is_override_tip_basis"), &SkeletonIK3D::is_override_tip_basis); - - ClassDB::bind_method(D_METHOD("set_use_magnet", "use"), &SkeletonIK3D::set_use_magnet); - ClassDB::bind_method(D_METHOD("is_using_magnet"), &SkeletonIK3D::is_using_magnet); - - ClassDB::bind_method(D_METHOD("set_magnet_position", "local_position"), &SkeletonIK3D::set_magnet_position); - ClassDB::bind_method(D_METHOD("get_magnet_position"), &SkeletonIK3D::get_magnet_position); - - ClassDB::bind_method(D_METHOD("get_parent_skeleton"), &SkeletonIK3D::get_parent_skeleton); - ClassDB::bind_method(D_METHOD("is_running"), &SkeletonIK3D::is_running); - - ClassDB::bind_method(D_METHOD("set_min_distance", "min_distance"), &SkeletonIK3D::set_min_distance); - ClassDB::bind_method(D_METHOD("get_min_distance"), &SkeletonIK3D::get_min_distance); - - ClassDB::bind_method(D_METHOD("set_max_iterations", "iterations"), &SkeletonIK3D::set_max_iterations); - ClassDB::bind_method(D_METHOD("get_max_iterations"), &SkeletonIK3D::get_max_iterations); - - ClassDB::bind_method(D_METHOD("start", "one_time"), &SkeletonIK3D::start, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("stop"), &SkeletonIK3D::stop); - - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "root_bone"), "set_root_bone", "get_root_bone"); - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "tip_bone"), "set_tip_bone", "get_tip_bone"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_interpolation", "get_interpolation"); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "target", PROPERTY_HINT_NONE, "suffix:m"), "set_target_transform", "get_target_transform"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_tip_basis"), "set_override_tip_basis", "is_override_tip_basis"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_magnet"), "set_use_magnet", "is_using_magnet"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "magnet", PROPERTY_HINT_NONE, "suffix:m"), "set_magnet_position", "get_magnet_position"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node"), "set_target_node", "get_target_node"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_distance", PROPERTY_HINT_NONE, "suffix:m"), "set_min_distance", "get_min_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "max_iterations"), "set_max_iterations", "get_max_iterations"); -} - -void SkeletonIK3D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - skeleton = Object::cast_to(get_parent()); - set_process_priority(1); - reload_chain(); - } break; - - case NOTIFICATION_INTERNAL_PROCESS: { - if (target_node_override) { - reload_goal(); - } - _solve_chain(); - } break; - - case NOTIFICATION_EXIT_TREE: { - reload_chain(); - } break; - } -} - -SkeletonIK3D::SkeletonIK3D() { -} - -SkeletonIK3D::~SkeletonIK3D() { - FabrikInverseKinematic::free_task(task); - task = nullptr; -} - -void SkeletonIK3D::set_root_bone(const StringName &p_root_bone) { - root_bone = p_root_bone; - reload_chain(); -} - -StringName SkeletonIK3D::get_root_bone() const { - return root_bone; -} - -void SkeletonIK3D::set_tip_bone(const StringName &p_tip_bone) { - tip_bone = p_tip_bone; - reload_chain(); -} - -StringName SkeletonIK3D::get_tip_bone() const { - return tip_bone; -} - -void SkeletonIK3D::set_interpolation(real_t p_interpolation) { - interpolation = p_interpolation; -} - -real_t SkeletonIK3D::get_interpolation() const { - return interpolation; -} - -void SkeletonIK3D::set_target_transform(const Transform3D &p_target) { - target = p_target; - reload_goal(); -} - -const Transform3D &SkeletonIK3D::get_target_transform() const { - return target; -} - -void SkeletonIK3D::set_target_node(const NodePath &p_node) { - target_node_path_override = p_node; - target_node_override = nullptr; - reload_goal(); -} - -NodePath SkeletonIK3D::get_target_node() { - return target_node_path_override; -} - -void SkeletonIK3D::set_override_tip_basis(bool p_override) { - override_tip_basis = p_override; -} - -bool SkeletonIK3D::is_override_tip_basis() const { - return override_tip_basis; -} - -void SkeletonIK3D::set_use_magnet(bool p_use) { - use_magnet = p_use; -} - -bool SkeletonIK3D::is_using_magnet() const { - return use_magnet; -} - -void SkeletonIK3D::set_magnet_position(const Vector3 &p_local_position) { - magnet_position = p_local_position; -} - -const Vector3 &SkeletonIK3D::get_magnet_position() const { - return magnet_position; -} - -void SkeletonIK3D::set_min_distance(real_t p_min_distance) { - min_distance = p_min_distance; -} - -void SkeletonIK3D::set_max_iterations(int p_iterations) { - max_iterations = p_iterations; -} - -bool SkeletonIK3D::is_running() { - return is_processing_internal(); -} - -void SkeletonIK3D::start(bool p_one_time) { - if (p_one_time) { - set_process_internal(false); - - if (target_node_override) { - reload_goal(); - } - - _solve_chain(); - } else { - set_process_internal(true); - } -} - -void SkeletonIK3D::stop() { - set_process_internal(false); - if (skeleton) { - skeleton->clear_bones_global_pose_override(); - } -} - -Transform3D SkeletonIK3D::_get_target_transform() { - if (!target_node_override && !target_node_path_override.is_empty()) { - target_node_override = Object::cast_to(get_node(target_node_path_override)); - } - - if (target_node_override && target_node_override->is_inside_tree()) { - return target_node_override->get_global_transform(); - } else { - return target; - } -} - -void SkeletonIK3D::reload_chain() { - FabrikInverseKinematic::free_task(task); - task = nullptr; - - if (!skeleton) { - return; - } - - task = FabrikInverseKinematic::create_simple_task(skeleton, skeleton->find_bone(root_bone), skeleton->find_bone(tip_bone), _get_target_transform()); - if (task) { - task->max_iterations = max_iterations; - task->min_distance = min_distance; - } -} - -void SkeletonIK3D::reload_goal() { - if (!task) { - return; - } - - FabrikInverseKinematic::set_goal(task, _get_target_transform()); -} - -void SkeletonIK3D::_solve_chain() { - if (!task) { - return; - } - FabrikInverseKinematic::solve(task, interpolation, override_tip_basis, use_magnet, magnet_position); -} - -#endif // _3D_DISABLED diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h deleted file mode 100644 index 097df2c400d0..000000000000 --- a/scene/3d/skeleton_ik_3d.h +++ /dev/null @@ -1,195 +0,0 @@ -/*************************************************************************/ -/* skeleton_ik_3d.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SKELETON_IK_3D_H -#define SKELETON_IK_3D_H - -#ifndef _3D_DISABLED - -#include "scene/3d/skeleton_3d.h" - -class FabrikInverseKinematic { - struct EndEffector { - BoneId tip_bone; - Transform3D goal_transform; - }; - - struct ChainItem { - Vector children; - ChainItem *parent_item = nullptr; - - // Bone info - BoneId bone = -1; - - real_t length = 0.0; - /// Positions relative to root bone - Transform3D initial_transform; - Vector3 current_pos; - // Direction from this bone to child - Vector3 current_ori; - - ChainItem *find_child(const BoneId p_bone_id); - ChainItem *add_child(const BoneId p_bone_id); - }; - - struct ChainTip { - ChainItem *chain_item = nullptr; - const EndEffector *end_effector = nullptr; - - ChainTip() {} - - ChainTip(ChainItem *p_chain_item, const EndEffector *p_end_effector) : - chain_item(p_chain_item), - end_effector(p_end_effector) {} - }; - - struct Chain { - ChainItem chain_root; - ChainItem *middle_chain_item = nullptr; - Vector tips; - Vector3 magnet_position; - }; - -public: - struct Task { - RID self; - Skeleton3D *skeleton = nullptr; - - Chain chain; - - // Settings - real_t min_distance = 0.01; - int max_iterations = 10; - - // Bone data - BoneId root_bone = -1; - Vector end_effectors; - - Transform3D goal_global_transform; - - Task() {} - }; - -private: - /// Init a chain that starts from the root to tip - static bool build_chain(Task *p_task, bool p_force_simple_chain = true); - - static void solve_simple(Task *p_task, bool p_solve_magnet, Vector3 p_origin_pos); - /// Special solvers that solve only chains with one end effector - static void solve_simple_backwards(const Chain &r_chain, bool p_solve_magnet); - static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet, Vector3 p_origin_pos); - -public: - static Task *create_simple_task(Skeleton3D *p_sk, BoneId root_bone, BoneId tip_bone, const Transform3D &goal_transform); - static void free_task(Task *p_task); - // The goal of chain should be always in local space - static void set_goal(Task *p_task, const Transform3D &p_goal); - static void make_goal(Task *p_task, const Transform3D &p_inverse_transf, real_t blending_delta); - static void solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position); - - static void _update_chain(const Skeleton3D *p_skeleton, ChainItem *p_chain_item); -}; - -class SkeletonIK3D : public Node { - GDCLASS(SkeletonIK3D, Node); - - StringName root_bone; - StringName tip_bone; - real_t interpolation = 1.0; - Transform3D target; - NodePath target_node_path_override; - bool override_tip_basis = true; - bool use_magnet = false; - Vector3 magnet_position; - - real_t min_distance = 0.01; - int max_iterations = 10; - - Skeleton3D *skeleton = nullptr; - Node3D *target_node_override = nullptr; - FabrikInverseKinematic::Task *task = nullptr; - -protected: - void _validate_property(PropertyInfo &p_property) const; - - static void _bind_methods(); - virtual void _notification(int p_what); - -public: - SkeletonIK3D(); - virtual ~SkeletonIK3D(); - - void set_root_bone(const StringName &p_root_bone); - StringName get_root_bone() const; - - void set_tip_bone(const StringName &p_tip_bone); - StringName get_tip_bone() const; - - void set_interpolation(real_t p_interpolation); - real_t get_interpolation() const; - - void set_target_transform(const Transform3D &p_target); - const Transform3D &get_target_transform() const; - - void set_target_node(const NodePath &p_node); - NodePath get_target_node(); - - void set_override_tip_basis(bool p_override); - bool is_override_tip_basis() const; - - void set_use_magnet(bool p_use); - bool is_using_magnet() const; - - void set_magnet_position(const Vector3 &p_local_position); - const Vector3 &get_magnet_position() const; - - void set_min_distance(real_t p_min_distance); - real_t get_min_distance() const { return min_distance; } - - void set_max_iterations(int p_iterations); - int get_max_iterations() const { return max_iterations; } - - Skeleton3D *get_parent_skeleton() const { return skeleton; } - - bool is_running(); - - void start(bool p_one_time = false); - void stop(); - -private: - Transform3D _get_target_transform(); - void reload_chain(); - void reload_goal(); - void _solve_chain(); -}; - -#endif // _3D_DISABLED - -#endif // SKELETON_IK_3D_H diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/3d/skeleton_modification_3d.cpp similarity index 86% rename from scene/resources/skeleton_modification_3d.cpp rename to scene/3d/skeleton_modification_3d.cpp index 2c0f6e779e3b..483d103637a0 100644 --- a/scene/resources/skeleton_modification_3d.cpp +++ b/scene/3d/skeleton_modification_3d.cpp @@ -31,25 +31,6 @@ #include "skeleton_modification_3d.h" #include "scene/3d/skeleton_3d.h" -void SkeletonModification3D::_execute(real_t p_delta) { - GDVIRTUAL_CALL(_execute, p_delta); - - if (!enabled) { - return; - } -} - -void SkeletonModification3D::_setup_modification(SkeletonModificationStack3D *p_stack) { - stack = p_stack; - if (stack) { - is_setup = true; - } else { - WARN_PRINT("Could not setup modification with name " + this->get_name()); - } - - GDVIRTUAL_CALL(_setup_modification, Ref(p_stack)); -} - void SkeletonModification3D::set_enabled(bool p_enabled) { enabled = p_enabled; } @@ -108,10 +89,6 @@ bool SkeletonModification3D::_print_execution_error(bool p_condition, String p_m return p_condition; } -Ref SkeletonModification3D::get_modification_stack() { - return stack; -} - void SkeletonModification3D::set_is_setup(bool p_is_setup) { is_setup = p_is_setup; } @@ -129,12 +106,10 @@ int SkeletonModification3D::get_execution_mode() const { } void SkeletonModification3D::_bind_methods() { - GDVIRTUAL_BIND(_execute, "delta"); - GDVIRTUAL_BIND(_setup_modification, "modification_stack") - + ClassDB::bind_method(D_METHOD("set_skeleton_path", "path"), &SkeletonModification3D::set_skeleton_path); + ClassDB::bind_method(D_METHOD("get_skeleton_path"), &SkeletonModification3D::get_skeleton_path); ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification3D::set_enabled); ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification3D::get_enabled); - ClassDB::bind_method(D_METHOD("get_modification_stack"), &SkeletonModification3D::get_modification_stack); ClassDB::bind_method(D_METHOD("set_is_setup", "is_setup"), &SkeletonModification3D::set_is_setup); ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModification3D::get_is_setup); ClassDB::bind_method(D_METHOD("set_execution_mode", "execution_mode"), &SkeletonModification3D::set_execution_mode); @@ -142,10 +117,15 @@ void SkeletonModification3D::_bind_methods() { ClassDB::bind_method(D_METHOD("clamp_angle", "angle", "min", "max", "invert"), &SkeletonModification3D::clamp_angle); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton_path"), "set_skeleton_path", "get_skeleton_path"); ADD_PROPERTY(PropertyInfo(Variant::INT, "execution_mode", PROPERTY_HINT_ENUM, "process, physics_process"), "set_execution_mode", "get_execution_mode"); } -SkeletonModification3D::SkeletonModification3D() { - stack = nullptr; - is_setup = false; +NodePath SkeletonModification3D::get_skeleton_path() const { + return skeleton_path; +} + +void SkeletonModification3D::set_skeleton_path(NodePath p_path) { + skeleton_path = p_path; + skeleton = cast_to(get_node_or_null(p_path)); } diff --git a/scene/resources/skeleton_modification_3d.h b/scene/3d/skeleton_modification_3d.h similarity index 82% rename from scene/resources/skeleton_modification_3d.h rename to scene/3d/skeleton_modification_3d.h index 6daf5efcd996..eafab8ed096a 100644 --- a/scene/resources/skeleton_modification_3d.h +++ b/scene/3d/skeleton_modification_3d.h @@ -31,35 +31,26 @@ #ifndef SKELETON_MODIFICATION_3D_H #define SKELETON_MODIFICATION_3D_H +#include "core/string/node_path.h" #include "scene/3d/skeleton_3d.h" -#include "scene/resources/skeleton_modification_stack_3d.h" -class SkeletonModificationStack3D; - -class SkeletonModification3D : public Resource { - GDCLASS(SkeletonModification3D, Resource); - friend class Skeleton3D; - friend class SkeletonModificationStack3D; +class SkeletonModification3D : public Node3D { + GDCLASS(SkeletonModification3D, Node3D); protected: static void _bind_methods(); - SkeletonModificationStack3D *stack = nullptr; int execution_mode = 0; // 0 = process bool enabled = true; bool is_setup = false; bool execution_error_found = false; + Skeleton3D *skeleton = nullptr; + NodePath skeleton_path = NodePath(".."); bool _print_execution_error(bool p_condition, String p_message); - GDVIRTUAL1(_execute, double) - GDVIRTUAL1(_setup_modification, Ref) - public: - virtual void _execute(real_t p_delta); - virtual void _setup_modification(SkeletonModificationStack3D *p_stack); - real_t clamp_angle(real_t p_angle, real_t p_min_bound, real_t p_max_bound, bool p_invert); void set_enabled(bool p_enabled); @@ -68,12 +59,13 @@ class SkeletonModification3D : public Resource { void set_execution_mode(int p_mode); int get_execution_mode() const; - Ref get_modification_stack(); - void set_is_setup(bool p_setup); bool get_is_setup() const; - SkeletonModification3D(); + NodePath get_skeleton_path() const; + void set_skeleton_path(NodePath p_path); + + SkeletonModification3D() {} }; #endif // SKELETON_MODIFICATION_3D_H diff --git a/scene/resources/skeleton_modification_3d_ccdik.cpp b/scene/3d/skeleton_modification_3d_ccdik.cpp similarity index 81% rename from scene/resources/skeleton_modification_3d_ccdik.cpp rename to scene/3d/skeleton_modification_3d_ccdik.cpp index 3251ee4189aa..fa7220ce4db7 100644 --- a/scene/resources/skeleton_modification_3d_ccdik.cpp +++ b/scene/3d/skeleton_modification_3d_ccdik.cpp @@ -28,9 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "scene/resources/skeleton_modification_3d_ccdik.h" +#include "scene/3d/skeleton_modification_3d_ccdik.h" #include "scene/3d/skeleton_3d.h" -#include "scene/resources/skeleton_modification_3d.h" +#include "scene/3d/skeleton_modification_3d.h" bool SkeletonModification3DCCDIK::_set(const StringName &p_path, const Variant &p_value) { String path = p_path; @@ -109,67 +109,17 @@ void SkeletonModification3DCCDIK::_get_property_list(List *p_list) } } -void SkeletonModification3DCCDIK::_execute(real_t p_delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - - if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update"); - update_target_cache(); - return; - } - if (tip_node_cache.is_null()) { - _print_execution_error(true, "Tip cache is out of date. Attempting to update"); - update_tip_cache(); - return; - } - - // Reset the local bone overrides for CCDIK affected nodes - for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) { - stack->skeleton->set_bone_local_pose_override(ccdik_data_chain[i].bone_idx, - stack->skeleton->get_bone_local_pose_override(ccdik_data_chain[i].bone_idx), - 0.0, false); - } - - Node3D *node_target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - Node3D *node_tip = Object::cast_to(ObjectDB::get_instance(tip_node_cache)); - - if (_print_execution_error(!node_target || !node_target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { - return; - } - if (_print_execution_error(!node_tip || !node_tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) { - return; - } - - if (use_high_quality_solve) { - for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) { - for (uint32_t j = i; j < ccdik_data_chain.size(); j++) { - _execute_ccdik_joint(j, node_target, node_tip); - } - } - } else { - for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) { - _execute_ccdik_joint(i, node_target, node_tip); - } - } - - execution_error_found = false; -} - void SkeletonModification3DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node3D *p_target, Node3D *p_tip) { CCDIK_Joint_Data ccdik_data = ccdik_data_chain[p_joint_idx]; - if (_print_execution_error(ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > stack->skeleton->get_bone_count(), + if (_print_execution_error(ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > skeleton->get_bone_count(), "CCDIK joint: bone index for joint" + itos(p_joint_idx) + " not found. Cannot execute modification!")) { return; } - Transform3D bone_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->get_bone_global_pose(ccdik_data.bone_idx)); - Transform3D tip_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->world_transform_to_global_pose(p_tip->get_global_transform())); - Transform3D target_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->world_transform_to_global_pose(p_target->get_global_transform())); + Transform3D bone_trans = skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, skeleton->get_bone_global_pose(ccdik_data.bone_idx)); + Transform3D tip_trans = skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, skeleton->world_transform_to_global_pose(p_tip->get_global_transform())); + Transform3D target_trans = skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, skeleton->world_transform_to_global_pose(p_target->get_global_transform())); if (tip_trans.origin.distance_to(target_trans.origin) <= 0.01) { return; @@ -227,63 +177,50 @@ void SkeletonModification3DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node3D * bone_trans.basis.set_axis_angle(rotation_axis, rotation_angle); } - - stack->skeleton->set_bone_local_pose_override(ccdik_data.bone_idx, bone_trans, stack->strength, true); - stack->skeleton->force_update_bone_children_transforms(ccdik_data.bone_idx); -} - -void SkeletonModification3DCCDIK::_setup_modification(SkeletonModificationStack3D *p_stack) { - stack = p_stack; - if (stack != nullptr) { - is_setup = true; - execution_error_found = false; - update_target_cache(); - update_tip_cache(); - } + skeleton->set_bone_pose_position(ccdik_data.bone_idx, bone_trans.origin); + skeleton->set_bone_pose_rotation(ccdik_data.bone_idx, bone_trans.basis.get_rotation_quaternion()); + skeleton->set_bone_pose_scale(ccdik_data.bone_idx, bone_trans.basis.get_scale()); + skeleton->force_update_bone_children_transforms(ccdik_data.bone_idx); } void SkeletonModification3DCCDIK::update_target_cache() { - if (!is_setup || !stack) { + if (!is_setup) { _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); return; } target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update target cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update target cache: node is not in scene tree!"); - target_node_cache = node->get_instance_id(); - - execution_error_found = false; - } + if (is_inside_tree()) { + if (has_node(target_node)) { + Node *node = get_node_or_null(target_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, + "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in scene tree!"); + target_node_cache = node->get_instance_id(); + + execution_error_found = false; } } } void SkeletonModification3DCCDIK::update_tip_cache() { - if (!is_setup || !stack) { + if (!is_setup) { _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!"); return; } tip_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(tip_node)) { - Node *node = stack->skeleton->get_node(tip_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update tip cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update tip cache: node is not in scene tree!"); - tip_node_cache = node->get_instance_id(); - - execution_error_found = false; - } + if (is_inside_tree()) { + if (has_node(tip_node)) { + Node *node = get_node_or_null(tip_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, + "Cannot update tip cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update tip cache: node is not in scene tree!"); + tip_node_cache = node->get_instance_id(); + + execution_error_found = false; } } } @@ -326,10 +263,8 @@ void SkeletonModification3DCCDIK::set_ccdik_joint_bone_name(int p_joint_idx, Str ERR_FAIL_INDEX(p_joint_idx, bone_chain_size); ccdik_data_chain[p_joint_idx].bone_name = p_bone_name; - if (stack) { - if (stack->skeleton) { - ccdik_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_bone_name); - } + if (skeleton) { + ccdik_data_chain[p_joint_idx].bone_idx = skeleton->find_bone(p_bone_name); } execution_error_found = false; notify_property_list_changed(); @@ -347,10 +282,8 @@ void SkeletonModification3DCCDIK::set_ccdik_joint_bone_index(int p_joint_idx, in ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); ccdik_data_chain[p_joint_idx].bone_idx = p_bone_idx; - if (stack) { - if (stack->skeleton) { - ccdik_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx); - } + if (skeleton) { + ccdik_data_chain[p_joint_idx].bone_name = skeleton->get_bone_name(p_bone_idx); } execution_error_found = false; notify_property_list_changed(); @@ -465,10 +398,73 @@ void SkeletonModification3DCCDIK::_bind_methods() { } SkeletonModification3DCCDIK::SkeletonModification3DCCDIK() { - stack = nullptr; is_setup = false; enabled = true; } SkeletonModification3DCCDIK::~SkeletonModification3DCCDIK() { } + +void SkeletonModification3DCCDIK::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + execution_error_found = false; + update_target_cache(); + update_tip_cache(); + set_process_internal(false); + set_physics_process_internal(false); + if (get_execution_mode() == 0) { + set_process_internal(true); + } else if (get_execution_mode() == 1) { + set_physics_process_internal(true); + } + skeleton = cast_to(get_node_or_null(get_skeleton_path())); + is_setup = true; + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: + [[fallthrough]]; + case NOTIFICATION_INTERNAL_PROCESS: { + if (!is_setup || skeleton == nullptr) { + ERR_PRINT_ONCE("Modification is not setup and therefore cannot execute!"); + return; + } + if (!enabled) { + return; + } + + if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update"); + update_target_cache(); + return; + } + if (tip_node_cache.is_null()) { + _print_execution_error(true, "Tip cache is out of date. Attempting to update"); + update_tip_cache(); + return; + } + Node3D *node_target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + Node3D *node_tip = Object::cast_to(ObjectDB::get_instance(tip_node_cache)); + + if (_print_execution_error(!node_target || !node_target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + return; + } + if (_print_execution_error(!node_tip || !node_tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) { + return; + } + + if (use_high_quality_solve) { + for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) { + for (uint32_t j = i; j < ccdik_data_chain.size(); j++) { + _execute_ccdik_joint(j, node_target, node_tip); + } + } + } else { + for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) { + _execute_ccdik_joint(i, node_target, node_tip); + } + } + + execution_error_found = false; + } break; + } +} diff --git a/scene/resources/skeleton_modification_3d_ccdik.h b/scene/3d/skeleton_modification_3d_ccdik.h similarity index 96% rename from scene/resources/skeleton_modification_3d_ccdik.h rename to scene/3d/skeleton_modification_3d_ccdik.h index 1fe53e94b6d3..4c3243a90296 100644 --- a/scene/resources/skeleton_modification_3d_ccdik.h +++ b/scene/3d/skeleton_modification_3d_ccdik.h @@ -33,7 +33,7 @@ #include "core/templates/local_vector.h" #include "scene/3d/skeleton_3d.h" -#include "scene/resources/skeleton_modification_3d.h" +#include "scene/3d/skeleton_modification_3d.h" class SkeletonModification3DCCDIK : public SkeletonModification3D { GDCLASS(SkeletonModification3DCCDIK, SkeletonModification3D); @@ -77,9 +77,7 @@ class SkeletonModification3DCCDIK : public SkeletonModification3D { void _get_property_list(List *p_list) const; public: - virtual void _execute(real_t p_delta) override; - virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override; - + void _notification(int p_what); void set_target_node(const NodePath &p_target_node); NodePath get_target_node() const; diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/3d/skeleton_modification_3d_fabrik.cpp similarity index 76% rename from scene/resources/skeleton_modification_3d_fabrik.cpp rename to scene/3d/skeleton_modification_3d_fabrik.cpp index 4099208f4426..7115f3310430 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.cpp +++ b/scene/3d/skeleton_modification_3d_fabrik.cpp @@ -28,9 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "scene/resources/skeleton_modification_3d_fabrik.h" +#include "scene/3d/skeleton_modification_3d_fabrik.h" #include "scene/3d/skeleton_3d.h" -#include "scene/resources/skeleton_modification_3d.h" +#include "scene/3d/skeleton_modification_3d.h" bool SkeletonModification3DFABRIK::_set(const StringName &p_path, const Variant &p_value) { String path = p_path; @@ -127,97 +127,19 @@ void SkeletonModification3DFABRIK::_get_property_list(List *p_list } } -void SkeletonModification3DFABRIK::_execute(real_t p_delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - - if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); - update_target_cache(); - return; - } - - if (_print_execution_error(fabrik_data_chain.size() <= 1, "FABRIK requires at least two joints to operate. Cannot execute modification!")) { - return; - } - - Node3D *node_target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (_print_execution_error(!node_target || !node_target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { - return; - } - - // Make sure the transform cache is the correct size - if (fabrik_transforms.size() != fabrik_data_chain.size()) { - fabrik_transforms.resize(fabrik_data_chain.size()); - } - - // Verify that all joints have a valid bone ID, and that all bone lengths are zero or more - // Also, while we are here, apply magnet positions. - for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) { - if (_print_execution_error(fabrik_data_chain[i].bone_idx < 0, "FABRIK Joint " + itos(i) + " has an invalid bone ID. Cannot execute!")) { - return; - } - - if (fabrik_data_chain[i].length < 0 && fabrik_data_chain[i].auto_calculate_length) { - fabrik_joint_auto_calculate_length(i); - } - if (_print_execution_error(fabrik_data_chain[i].length < 0, "FABRIK Joint " + itos(i) + " has an invalid joint length. Cannot execute!")) { - return; - } - fabrik_transforms[i] = stack->skeleton->get_bone_global_pose(fabrik_data_chain[i].bone_idx); - - // Apply magnet positions: - if (stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx) >= 0) { - int parent_bone_idx = stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx); - Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx)); - fabrik_transforms[i].origin += conversion_transform.basis.xform_inv(fabrik_data_chain[i].magnet_position); - } else { - fabrik_transforms[i].origin += fabrik_data_chain[i].magnet_position; - } - } - Transform3D origin_global_pose_trans = stack->skeleton->get_bone_global_pose_no_override(fabrik_data_chain[0].bone_idx); - - target_global_pose = stack->skeleton->world_transform_to_global_pose(node_target->get_global_transform()); - origin_global_pose = origin_global_pose_trans; - - final_joint_idx = fabrik_data_chain.size() - 1; - real_t target_distance = fabrik_transforms[final_joint_idx].origin.distance_to(target_global_pose.origin); - chain_iterations = 0; - - while (target_distance > chain_tolerance) { - chain_backwards(); - chain_forwards(); - - // update the target distance - target_distance = fabrik_transforms[final_joint_idx].origin.distance_to(target_global_pose.origin); - - // update chain iterations - chain_iterations += 1; - if (chain_iterations >= chain_max_iterations) { - break; - } - } - chain_apply(); - - execution_error_found = false; -} - void SkeletonModification3DFABRIK::chain_backwards() { int final_bone_idx = fabrik_data_chain[final_joint_idx].bone_idx; Transform3D final_joint_trans = fabrik_transforms[final_joint_idx]; // Get the direction the final bone is facing in. - stack->skeleton->update_bone_rest_forward_vector(final_bone_idx); + skeleton->update_bone_rest_forward_vector(final_bone_idx); Transform3D final_bone_direction_trans = final_joint_trans.looking_at(target_global_pose.origin, Vector3(0, 1, 0)); - final_bone_direction_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(final_bone_idx, final_bone_direction_trans.basis); - Vector3 direction = final_bone_direction_trans.basis.xform(stack->skeleton->get_bone_axis_forward_vector(final_bone_idx)).normalized(); + final_bone_direction_trans.basis = skeleton->global_pose_z_forward_to_bone_forward(final_bone_idx, final_bone_direction_trans.basis); + Vector3 direction = final_bone_direction_trans.basis.xform(skeleton->get_bone_axis_forward_vector(final_bone_idx)).normalized(); // If set to override, then use the target's Basis rather than the bone's if (fabrik_data_chain[final_joint_idx].use_target_basis) { - direction = target_global_pose.basis.xform(stack->skeleton->get_bone_axis_forward_vector(final_bone_idx)).normalized(); + direction = target_global_pose.basis.xform(skeleton->get_bone_axis_forward_vector(final_bone_idx)).normalized(); } // set the position of the final joint to the target position @@ -267,8 +189,8 @@ void SkeletonModification3DFABRIK::chain_apply() { if (i == fabrik_data_chain.size() - 1) { if (fabrik_data_chain[i].use_target_basis == false) { // Point to target... // Get the forward direction that the basis is facing in right now. - stack->skeleton->update_bone_rest_forward_vector(current_bone_idx); - Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(current_bone_idx); + skeleton->update_bone_rest_forward_vector(current_bone_idx); + Vector3 forward_vector = skeleton->get_bone_axis_forward_vector(current_bone_idx); // Rotate the bone towards the target: current_trans.basis.rotate_to_align(forward_vector, current_trans.origin.direction_to(target_global_pose.origin)); current_trans.basis.rotate_local(forward_vector, fabrik_data_chain[i].roll); @@ -279,50 +201,38 @@ void SkeletonModification3DFABRIK::chain_apply() { Transform3D next_trans = fabrik_transforms[i + 1]; // Get the forward direction that the basis is facing in right now. - stack->skeleton->update_bone_rest_forward_vector(current_bone_idx); - Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(current_bone_idx); + skeleton->update_bone_rest_forward_vector(current_bone_idx); + Vector3 forward_vector = skeleton->get_bone_axis_forward_vector(current_bone_idx); // Rotate the bone towards the next bone in the chain: current_trans.basis.rotate_to_align(forward_vector, current_trans.origin.direction_to(next_trans.origin)); current_trans.basis.rotate_local(forward_vector, fabrik_data_chain[i].roll); } - stack->skeleton->set_bone_local_pose_override(current_bone_idx, stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans), stack->strength, true); + Transform3D new_bone_trans_local = skeleton->global_pose_to_local_pose(current_bone_idx, current_trans); + skeleton->set_bone_pose_position(current_bone_idx, new_bone_trans_local.origin); + skeleton->set_bone_pose_rotation(current_bone_idx, new_bone_trans_local.basis.get_rotation_quaternion()); + skeleton->set_bone_pose_scale(current_bone_idx, new_bone_trans_local.basis.get_scale()); } // Update all the bones so the next modification has up-to-date data. - stack->skeleton->force_update_all_bone_transforms(); -} - -void SkeletonModification3DFABRIK::_setup_modification(SkeletonModificationStack3D *p_stack) { - stack = p_stack; - if (stack != nullptr) { - is_setup = true; - execution_error_found = false; - update_target_cache(); - - for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) { - update_joint_tip_cache(i); - } - } + skeleton->force_update_all_bone_transforms(); } void SkeletonModification3DFABRIK::update_target_cache() { - if (!is_setup || !stack) { + if (!is_setup) { _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); return; } target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree() && target_node.is_empty() == false) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update target cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update target cache: node is not in the scene tree!"); - target_node_cache = node->get_instance_id(); - - execution_error_found = false; - } + if (is_inside_tree() && target_node.is_empty() == false) { + if (has_node(target_node)) { + Node *node = get_node_or_null(target_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, + "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in the scene tree!"); + target_node_cache = node->get_instance_id(); + + execution_error_found = false; } } } @@ -330,23 +240,21 @@ void SkeletonModification3DFABRIK::update_target_cache() { void SkeletonModification3DFABRIK::update_joint_tip_cache(int p_joint_idx) { const int bone_chain_size = fabrik_data_chain.size(); ERR_FAIL_INDEX_MSG(p_joint_idx, bone_chain_size, "FABRIK joint not found"); - if (!is_setup || !stack) { + if (!is_setup) { _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!"); return; } fabrik_data_chain[p_joint_idx].tip_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree() && fabrik_data_chain[p_joint_idx].tip_node.is_empty() == false) { - if (stack->skeleton->has_node(fabrik_data_chain[p_joint_idx].tip_node)) { - Node *node = stack->skeleton->get_node(fabrik_data_chain[p_joint_idx].tip_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update tip cache for joint " + itos(p_joint_idx) + ": node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update tip cache for joint " + itos(p_joint_idx) + ": node is not in scene tree!"); - fabrik_data_chain[p_joint_idx].tip_node_cache = node->get_instance_id(); - - execution_error_found = false; - } + if (is_inside_tree() && fabrik_data_chain[p_joint_idx].tip_node.is_empty() == false) { + if (has_node(fabrik_data_chain[p_joint_idx].tip_node)) { + Node *node = get_node(fabrik_data_chain[p_joint_idx].tip_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, + "Cannot update tip cache for joint " + itos(p_joint_idx) + ": node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update tip cache for joint " + itos(p_joint_idx) + ": node is not in scene tree!"); + fabrik_data_chain[p_joint_idx].tip_node_cache = node->get_instance_id(); + + execution_error_found = false; } } } @@ -401,10 +309,8 @@ void SkeletonModification3DFABRIK::set_fabrik_joint_bone_name(int p_joint_idx, S ERR_FAIL_INDEX(p_joint_idx, bone_chain_size); fabrik_data_chain[p_joint_idx].bone_name = p_bone_name; - if (stack) { - if (stack->skeleton) { - fabrik_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_bone_name); - } + if (skeleton) { + fabrik_data_chain[p_joint_idx].bone_idx = skeleton->find_bone(p_bone_name); } execution_error_found = false; notify_property_list_changed(); @@ -422,10 +328,8 @@ void SkeletonModification3DFABRIK::set_fabrik_joint_bone_index(int p_joint_idx, ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); fabrik_data_chain[p_joint_idx].bone_idx = p_bone_idx; - if (stack) { - if (stack->skeleton) { - fabrik_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx); - } + if (skeleton) { + fabrik_data_chain[p_joint_idx].bone_name = skeleton->get_bone_name(p_bone_idx); } execution_error_found = false; notify_property_list_changed(); @@ -490,11 +394,11 @@ void SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length(int p_join return; } - if (!stack || !stack->skeleton || !is_setup) { + if (!skeleton || !is_setup) { _print_execution_error(true, "Cannot auto calculate joint length: modification is not properly setup!"); return; } - ERR_FAIL_INDEX_MSG(fabrik_data_chain[p_joint_idx].bone_idx, stack->skeleton->get_bone_count(), + ERR_FAIL_INDEX_MSG(fabrik_data_chain[p_joint_idx].bone_idx, skeleton->get_bone_count(), "Bone for joint " + itos(p_joint_idx) + " is not set or points to an unknown bone!"); if (fabrik_data_chain[p_joint_idx].use_tip_node) { // Use the tip node to update joint length. @@ -506,26 +410,26 @@ void SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length(int p_join ERR_FAIL_COND_MSG(!tip_node->is_inside_tree(), "Tip node for joint " + itos(p_joint_idx) + "is not in the scene tree. Cannot calculate length..."); Transform3D node_trans = tip_node->get_global_transform(); - node_trans = stack->skeleton->world_transform_to_global_pose(node_trans); - //node_trans = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, node_trans); + node_trans = skeleton->world_transform_to_global_pose(node_trans); + //node_trans = skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, node_trans); //fabrik_data_chain[p_joint_idx].length = node_trans.origin.length(); - fabrik_data_chain[p_joint_idx].length = stack->skeleton->get_bone_global_pose(fabrik_data_chain[p_joint_idx].bone_idx).origin.distance_to(node_trans.origin); + fabrik_data_chain[p_joint_idx].length = skeleton->get_bone_global_pose(fabrik_data_chain[p_joint_idx].bone_idx).origin.distance_to(node_trans.origin); } else { // Use child bone(s) to update joint length, if possible - Vector bone_children = stack->skeleton->get_bone_children(fabrik_data_chain[p_joint_idx].bone_idx); + Vector bone_children = skeleton->get_bone_children(fabrik_data_chain[p_joint_idx].bone_idx); if (bone_children.size() <= 0) { ERR_FAIL_MSG("Cannot calculate length for joint " + itos(p_joint_idx) + "joint uses leaf bone. \nPlease manually set the bone length or use a tip node!"); return; } - Transform3D bone_trans = stack->skeleton->get_bone_global_pose(fabrik_data_chain[p_joint_idx].bone_idx); + Transform3D bone_trans = skeleton->get_bone_global_pose(fabrik_data_chain[p_joint_idx].bone_idx); real_t final_length = 0; for (int i = 0; i < bone_children.size(); i++) { - Transform3D child_transform = stack->skeleton->get_bone_global_pose(bone_children[i]); + Transform3D child_transform = skeleton->get_bone_global_pose(bone_children[i]); final_length += bone_trans.origin.distance_to(child_transform.origin); - //final_length += stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, child_transform).origin.length(); + //final_length += skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, child_transform).origin.length(); } fabrik_data_chain[p_joint_idx].length = final_length / bone_children.size(); } @@ -619,10 +523,112 @@ void SkeletonModification3DFABRIK::_bind_methods() { } SkeletonModification3DFABRIK::SkeletonModification3DFABRIK() { - stack = nullptr; is_setup = false; enabled = true; } SkeletonModification3DFABRIK::~SkeletonModification3DFABRIK() { } + +void SkeletonModification3DFABRIK::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + execution_error_found = false; + update_target_cache(); + + for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) { + update_joint_tip_cache(i); + } + set_process_internal(false); + set_physics_process_internal(false); + if (get_execution_mode() == 0) { + set_process_internal(true); + } else if (get_execution_mode() == 1) { + set_physics_process_internal(true); + } + skeleton = cast_to(get_node_or_null(get_skeleton_path())); + is_setup = true; + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: + [[fallthrough]]; + case NOTIFICATION_INTERNAL_PROCESS: { + if (!is_setup || skeleton == nullptr) { + ERR_PRINT_ONCE("Modification is not setup and therefore cannot execute!"); + return; + } + if (!enabled) { + return; + } + + if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + + if (_print_execution_error(fabrik_data_chain.size() <= 1, "FABRIK requires at least two joints to operate. Cannot execute modification!")) { + return; + } + + Node3D *node_target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + if (_print_execution_error(!node_target || !node_target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + return; + } + + // Make sure the transform cache is the correct size + if (fabrik_transforms.size() != fabrik_data_chain.size()) { + fabrik_transforms.resize(fabrik_data_chain.size()); + } + + // Verify that all joints have a valid bone ID, and that all bone lengths are zero or more + // Also, while we are here, apply magnet positions. + for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) { + if (_print_execution_error(fabrik_data_chain[i].bone_idx < 0, "FABRIK Joint " + itos(i) + " has an invalid bone ID. Cannot execute!")) { + return; + } + + if (fabrik_data_chain[i].length < 0 && fabrik_data_chain[i].auto_calculate_length) { + fabrik_joint_auto_calculate_length(i); + } + if (_print_execution_error(fabrik_data_chain[i].length < 0, "FABRIK Joint " + itos(i) + " has an invalid joint length. Cannot execute!")) { + return; + } + fabrik_transforms[i] = skeleton->get_bone_global_pose(fabrik_data_chain[i].bone_idx); + + // Apply magnet positions: + if (skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx) >= 0) { + int parent_bone_idx = skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx); + Transform3D conversion_transform = (skeleton->get_bone_global_pose(parent_bone_idx)); + fabrik_transforms[i].origin += conversion_transform.basis.xform_inv(fabrik_data_chain[i].magnet_position); + } else { + fabrik_transforms[i].origin += fabrik_data_chain[i].magnet_position; + } + } + Transform3D origin_global_pose_trans = skeleton->get_bone_global_pose(fabrik_data_chain[0].bone_idx); + + target_global_pose = skeleton->world_transform_to_global_pose(node_target->get_global_transform()); + origin_global_pose = origin_global_pose_trans; + + final_joint_idx = fabrik_data_chain.size() - 1; + real_t target_distance = fabrik_transforms[final_joint_idx].origin.distance_to(target_global_pose.origin); + chain_iterations = 0; + + while (target_distance > chain_tolerance) { + chain_backwards(); + chain_forwards(); + + // update the target distance + target_distance = fabrik_transforms[final_joint_idx].origin.distance_to(target_global_pose.origin); + + // update chain iterations + chain_iterations += 1; + if (chain_iterations >= chain_max_iterations) { + break; + } + } + chain_apply(); + + execution_error_found = false; + } break; + } +} diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/3d/skeleton_modification_3d_fabrik.h similarity index 96% rename from scene/resources/skeleton_modification_3d_fabrik.h rename to scene/3d/skeleton_modification_3d_fabrik.h index e2e490d636f7..3105018a79b6 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.h +++ b/scene/3d/skeleton_modification_3d_fabrik.h @@ -33,7 +33,7 @@ #include "core/templates/local_vector.h" #include "scene/3d/skeleton_3d.h" -#include "scene/resources/skeleton_modification_3d.h" +#include "scene/3d/skeleton_modification_3d.h" class SkeletonModification3DFABRIK : public SkeletonModification3D { GDCLASS(SkeletonModification3DFABRIK, SkeletonModification3D); @@ -82,9 +82,7 @@ class SkeletonModification3DFABRIK : public SkeletonModification3D { void _get_property_list(List *p_list) const; public: - virtual void _execute(real_t p_delta) override; - virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override; - + void _notification(int p_what); void set_target_node(const NodePath &p_target_node); NodePath get_target_node() const; diff --git a/scene/resources/skeleton_modification_3d_jiggle.cpp b/scene/3d/skeleton_modification_3d_jiggle.cpp similarity index 85% rename from scene/resources/skeleton_modification_3d_jiggle.cpp rename to scene/3d/skeleton_modification_3d_jiggle.cpp index 64f26f3fdae6..50aacb0e7151 100644 --- a/scene/resources/skeleton_modification_3d_jiggle.cpp +++ b/scene/3d/skeleton_modification_3d_jiggle.cpp @@ -28,9 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "scene/resources/skeleton_modification_3d_jiggle.h" +#include "scene/3d/skeleton_modification_3d_jiggle.h" #include "scene/3d/skeleton_3d.h" -#include "scene/resources/skeleton_modification_3d.h" +#include "scene/3d/skeleton_modification_3d.h" bool SkeletonModification3DJiggle::_set(const StringName &p_path, const Variant &p_value) { String path = p_path; @@ -138,47 +138,20 @@ void SkeletonModification3DJiggle::_get_property_list(List *p_list } } -void SkeletonModification3DJiggle::_execute(real_t p_delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); - update_cache(); - return; - } - Node3D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - _print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!"); - - for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) { - _execute_jiggle_joint(i, target, p_delta); - } - - execution_error_found = false; -} - void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D *p_target, real_t p_delta) { // Adopted from: https://wiki.unity3d.com/index.php/JiggleBone // With modifications by TwistedTwigleg. if (jiggle_data_chain[p_joint_idx].bone_idx <= -2) { - jiggle_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(jiggle_data_chain[p_joint_idx].bone_name); + jiggle_data_chain[p_joint_idx].bone_idx = skeleton->find_bone(jiggle_data_chain[p_joint_idx].bone_name); } if (_print_execution_error( - jiggle_data_chain[p_joint_idx].bone_idx < 0 || jiggle_data_chain[p_joint_idx].bone_idx > stack->skeleton->get_bone_count(), + jiggle_data_chain[p_joint_idx].bone_idx < 0 || jiggle_data_chain[p_joint_idx].bone_idx > skeleton->get_bone_count(), "Jiggle joint " + itos(p_joint_idx) + " bone index is invalid. Cannot execute modification!")) { return; } - - Transform3D bone_local_pos = stack->skeleton->get_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx); - if (bone_local_pos == Transform3D()) { - bone_local_pos = stack->skeleton->get_bone_pose(jiggle_data_chain[p_joint_idx].bone_idx); - } - - Transform3D new_bone_trans = stack->skeleton->local_pose_to_global_pose(jiggle_data_chain[p_joint_idx].bone_idx, bone_local_pos); - Vector3 target_position = stack->skeleton->world_transform_to_global_pose(p_target->get_global_transform()).origin; + Transform3D new_bone_trans = skeleton->get_bone_global_pose(jiggle_data_chain[p_joint_idx].bone_idx); + Vector3 target_position = skeleton->world_transform_to_global_pose(p_target->get_global_transform()).origin; jiggle_data_chain[p_joint_idx].force = (target_position - jiggle_data_chain[p_joint_idx].dynamic_position) * jiggle_data_chain[p_joint_idx].stiffness * p_delta; @@ -196,15 +169,15 @@ void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D // Collision detection/response if (use_colliders) { - if (execution_mode == SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_physics_process) { - Ref world_3d = stack->skeleton->get_world_3d(); + if (is_physics_processing() || is_physics_processing_internal()) { + Ref world_3d = skeleton->get_world_3d(); ERR_FAIL_COND(world_3d.is_null()); PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space()); PhysicsDirectSpaceState3D::RayResult ray_result; // Convert to world transforms, which is what the physics server needs - Transform3D new_bone_trans_world = stack->skeleton->global_pose_to_world_transform(new_bone_trans); - Transform3D dynamic_position_world = stack->skeleton->global_pose_to_world_transform(Transform3D(Basis(), jiggle_data_chain[p_joint_idx].dynamic_position)); + Transform3D new_bone_trans_world = skeleton->global_pose_to_world_transform(new_bone_trans); + Transform3D dynamic_position_world = skeleton->global_pose_to_world_transform(Transform3D(Basis(), jiggle_data_chain[p_joint_idx].dynamic_position)); PhysicsDirectSpaceState3D::RayParameters ray_params; ray_params.from = new_bone_trans_world.origin; @@ -227,8 +200,8 @@ void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D } // Get the forward direction that the basis is facing in right now. - stack->skeleton->update_bone_rest_forward_vector(jiggle_data_chain[p_joint_idx].bone_idx); - Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(jiggle_data_chain[p_joint_idx].bone_idx); + skeleton->update_bone_rest_forward_vector(jiggle_data_chain[p_joint_idx].bone_idx); + Vector3 forward_vector = skeleton->get_bone_axis_forward_vector(jiggle_data_chain[p_joint_idx].bone_idx); // Rotate the bone using the dynamic position! new_bone_trans.basis.rotate_to_align(forward_vector, new_bone_trans.origin.direction_to(jiggle_data_chain[p_joint_idx].dynamic_position)); @@ -236,9 +209,11 @@ void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D // Roll new_bone_trans.basis.rotate_local(forward_vector, jiggle_data_chain[p_joint_idx].roll); - new_bone_trans = stack->skeleton->global_pose_to_local_pose(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans); - stack->skeleton->set_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans, stack->strength, true); - stack->skeleton->force_update_bone_children_transforms(jiggle_data_chain[p_joint_idx].bone_idx); + new_bone_trans = skeleton->global_pose_to_local_pose(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans); + skeleton->set_bone_pose_position(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans.origin); + skeleton->set_bone_pose_rotation(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans.basis.get_rotation_quaternion()); + skeleton->set_bone_pose_scale(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans.basis.get_scale()); + skeleton->force_update_bone_children_transforms(jiggle_data_chain[p_joint_idx].bone_idx); } void SkeletonModification3DJiggle::_update_jiggle_joint_data() { @@ -253,45 +228,23 @@ void SkeletonModification3DJiggle::_update_jiggle_joint_data() { } } -void SkeletonModification3DJiggle::_setup_modification(SkeletonModificationStack3D *p_stack) { - stack = p_stack; - - if (stack) { - is_setup = true; - execution_error_found = false; - - if (stack->skeleton) { - for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) { - int bone_idx = jiggle_data_chain[i].bone_idx; - if (bone_idx > 0 && bone_idx < stack->skeleton->get_bone_count()) { - jiggle_data_chain[i].dynamic_position = stack->skeleton->local_pose_to_global_pose(bone_idx, stack->skeleton->get_bone_local_pose_override(bone_idx)).origin; - } - } - } - - update_cache(); - } -} - void SkeletonModification3DJiggle::update_cache() { - if (!is_setup || !stack) { + if (!is_setup) { _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); return; } target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update target cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update target cache: node is not in the scene tree!"); - target_node_cache = node->get_instance_id(); - - execution_error_found = false; - } + if (is_inside_tree()) { + if (has_node(target_node)) { + Node *node = get_node_or_null(target_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, + "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in the scene tree!"); + target_node_cache = node->get_instance_id(); + + execution_error_found = false; } } } @@ -388,8 +341,8 @@ void SkeletonModification3DJiggle::set_jiggle_joint_bone_name(int p_joint_idx, S ERR_FAIL_INDEX(p_joint_idx, bone_chain_size); jiggle_data_chain[p_joint_idx].bone_name = p_name; - if (stack && stack->skeleton) { - jiggle_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_name); + if (skeleton) { + jiggle_data_chain[p_joint_idx].bone_idx = skeleton->find_bone(p_name); } execution_error_found = false; notify_property_list_changed(); @@ -413,10 +366,8 @@ void SkeletonModification3DJiggle::set_jiggle_joint_bone_index(int p_joint_idx, ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); jiggle_data_chain[p_joint_idx].bone_idx = p_bone_idx; - if (stack) { - if (stack->skeleton) { - jiggle_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx); - } + if (skeleton) { + jiggle_data_chain[p_joint_idx].bone_name = skeleton->get_bone_name(p_bone_idx); } execution_error_found = false; notify_property_list_changed(); @@ -567,7 +518,6 @@ void SkeletonModification3DJiggle::_bind_methods() { } SkeletonModification3DJiggle::SkeletonModification3DJiggle() { - stack = nullptr; is_setup = false; jiggle_data_chain = Vector(); stiffness = 3; @@ -580,3 +530,59 @@ SkeletonModification3DJiggle::SkeletonModification3DJiggle() { SkeletonModification3DJiggle::~SkeletonModification3DJiggle() { } + +void SkeletonModification3DJiggle::_notification(int32_t p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + execution_error_found = false; + skeleton = cast_to(get_node_or_null(get_skeleton_path())); + if (skeleton) { + for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) { + int bone_idx = jiggle_data_chain[i].bone_idx; + if (bone_idx > 0 && bone_idx < skeleton->get_bone_count()) { + jiggle_data_chain[i].dynamic_position = skeleton->local_pose_to_global_pose(bone_idx, skeleton->get_bone_local_pose_override(bone_idx)).origin; + } + } + } + + update_cache(); + set_process_internal(false); + set_physics_process_internal(false); + if (get_execution_mode() == 0) { + set_process_internal(true); + } else if (get_execution_mode() == 1) { + set_physics_process_internal(true); + } + is_setup = true; + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: + [[fallthrough]]; + case NOTIFICATION_INTERNAL_PROCESS: { + if (!is_setup || skeleton == nullptr) { + ERR_PRINT_ONCE("Modification is not setup and therefore cannot execute!"); + return; + } + if (!enabled) { + return; + } + if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + update_cache(); + return; + } + Node3D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + _print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!"); + real_t delta = 0.0f; + if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { + delta = get_physics_process_delta_time(); + } else if (p_what == NOTIFICATION_INTERNAL_PROCESS) { + delta = get_process_delta_time(); + } + for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) { + _execute_jiggle_joint(i, target, delta); + } + + execution_error_found = false; + } + } +} diff --git a/scene/resources/skeleton_modification_3d_jiggle.h b/scene/3d/skeleton_modification_3d_jiggle.h similarity index 96% rename from scene/resources/skeleton_modification_3d_jiggle.h rename to scene/3d/skeleton_modification_3d_jiggle.h index bd1ee51d93fd..63a31617fd1e 100644 --- a/scene/resources/skeleton_modification_3d_jiggle.h +++ b/scene/3d/skeleton_modification_3d_jiggle.h @@ -33,7 +33,7 @@ #include "core/templates/local_vector.h" #include "scene/3d/skeleton_3d.h" -#include "scene/resources/skeleton_modification_3d.h" +#include "scene/3d/skeleton_modification_3d.h" class SkeletonModification3DJiggle : public SkeletonModification3D { GDCLASS(SkeletonModification3DJiggle, SkeletonModification3D); @@ -83,11 +83,9 @@ class SkeletonModification3DJiggle : public SkeletonModification3D { bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); void _get_property_list(List *p_list) const; + void _notification(int32_t p_what); public: - virtual void _execute(real_t p_delta) override; - virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override; - void set_target_node(const NodePath &p_target_node); NodePath get_target_node() const; diff --git a/scene/resources/skeleton_modification_3d_lookat.cpp b/scene/3d/skeleton_modification_3d_lookat.cpp similarity index 65% rename from scene/resources/skeleton_modification_3d_lookat.cpp rename to scene/3d/skeleton_modification_3d_lookat.cpp index 69167cb308de..374a02fa0d45 100644 --- a/scene/resources/skeleton_modification_3d_lookat.cpp +++ b/scene/3d/skeleton_modification_3d_lookat.cpp @@ -28,9 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "scene/resources/skeleton_modification_3d_lookat.h" +#include "scene/3d/skeleton_modification_3d_lookat.h" #include "scene/3d/skeleton_3d.h" -#include "scene/resources/skeleton_modification_3d.h" +#include "scene/3d/skeleton_modification_3d.h" bool SkeletonModification3DLookAt::_set(const StringName &p_path, const Variant &p_value) { if (p_path == "lock_rotation_to_plane") { @@ -72,81 +72,10 @@ void SkeletonModification3DLookAt::_get_property_list(List *p_list p_list->push_back(PropertyInfo(Variant::VECTOR3, "additional_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); } -void SkeletonModification3DLookAt::_execute(real_t p_delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - - if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); - update_cache(); - return; - } - - if (bone_idx <= -2) { - bone_idx = stack->skeleton->find_bone(bone_name); - } - - Node3D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { - return; - } - if (_print_execution_error(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!")) { - return; - } - Transform3D new_bone_trans = stack->skeleton->get_bone_local_pose_override(bone_idx); - if (new_bone_trans == Transform3D()) { - new_bone_trans = stack->skeleton->get_bone_pose(bone_idx); - } - Vector3 target_pos = stack->skeleton->global_pose_to_local_pose(bone_idx, stack->skeleton->world_transform_to_global_pose(target->get_global_transform())).origin; - - // Lock the rotation to a plane relative to the bone by changing the target position - if (lock_rotation_to_plane) { - if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_X) { - target_pos.x = new_bone_trans.origin.x; - } else if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_Y) { - target_pos.y = new_bone_trans.origin.y; - } else if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_Z) { - target_pos.z = new_bone_trans.origin.z; - } - } - - // Look at the target! - new_bone_trans = new_bone_trans.looking_at(target_pos, Vector3(0, 1, 0)); - // Convert from Z-forward to whatever direction the bone faces. - stack->skeleton->update_bone_rest_forward_vector(bone_idx); - new_bone_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(bone_idx, new_bone_trans.basis); - - // Apply additional rotation - new_bone_trans.basis.rotate_local(Vector3(1, 0, 0), additional_rotation.x); - new_bone_trans.basis.rotate_local(Vector3(0, 1, 0), additional_rotation.y); - new_bone_trans.basis.rotate_local(Vector3(0, 0, 1), additional_rotation.z); - - stack->skeleton->set_bone_local_pose_override(bone_idx, new_bone_trans, stack->strength, true); - stack->skeleton->force_update_bone_children_transforms(bone_idx); - - // If we completed it successfully, then we can set execution_error_found to false - execution_error_found = false; -} - -void SkeletonModification3DLookAt::_setup_modification(SkeletonModificationStack3D *p_stack) { - stack = p_stack; - - if (stack != nullptr) { - is_setup = true; - execution_error_found = false; - update_cache(); - } -} - void SkeletonModification3DLookAt::set_bone_name(String p_name) { bone_name = p_name; - if (stack) { - if (stack->skeleton) { - bone_idx = stack->skeleton->find_bone(bone_name); - } + if (skeleton) { + bone_idx = skeleton->find_bone(bone_name); } execution_error_found = false; notify_property_list_changed(); @@ -164,34 +93,30 @@ void SkeletonModification3DLookAt::set_bone_index(int p_bone_idx) { ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); bone_idx = p_bone_idx; - if (stack) { - if (stack->skeleton) { - bone_name = stack->skeleton->get_bone_name(p_bone_idx); - } + if (skeleton) { + bone_name = skeleton->get_bone_name(p_bone_idx); } execution_error_found = false; notify_property_list_changed(); } void SkeletonModification3DLookAt::update_cache() { - if (!is_setup || !stack) { + if (!is_setup) { _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); return; } target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update target cache: Node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update target cache: Node is not in the scene tree!"); - target_node_cache = node->get_instance_id(); - - execution_error_found = false; - } + if (is_inside_tree()) { + if (has_node(target_node)) { + Node *node = get_node(target_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, + "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: Node is not in the scene tree!"); + target_node_cache = node->get_instance_id(); + + execution_error_found = false; } } } @@ -253,15 +178,87 @@ void SkeletonModification3DLookAt::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node"); } -SkeletonModification3DLookAt::SkeletonModification3DLookAt() { - stack = nullptr; - is_setup = false; - bone_name = ""; - bone_idx = -2; - additional_rotation = Vector3(); - lock_rotation_to_plane = false; - enabled = true; -} +void SkeletonModification3DLookAt::_notification(int32_t p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + execution_error_found = false; + update_cache(); + set_process_internal(false); + set_physics_process_internal(false); + if (get_execution_mode() == 0) { + set_process_internal(true); + } else if (get_execution_mode() == 1) { + set_physics_process_internal(true); + } + skeleton = cast_to(get_node_or_null(get_skeleton_path())); + is_setup = true; + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: + [[fallthrough]]; + case NOTIFICATION_INTERNAL_PROCESS: { + if (!is_setup) { + ERR_PRINT_ONCE("Modification is not setup."); + return; + } + if (!skeleton) { + ERR_PRINT_ONCE("Modification does not have a skeleton."); + return; + } + if (!enabled) { + return; + } -SkeletonModification3DLookAt::~SkeletonModification3DLookAt() { + if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + update_cache(); + return; + } + + if (bone_idx <= -2) { + bone_idx = skeleton->find_bone(bone_name); + } + + Node3D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + return; + } + if (_print_execution_error(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!")) { + return; + } + Transform3D new_bone_trans = skeleton->get_bone_local_pose_override(bone_idx); + if (new_bone_trans == Transform3D()) { + new_bone_trans = skeleton->get_bone_pose(bone_idx); + } + Vector3 target_pos = skeleton->global_pose_to_local_pose(bone_idx, skeleton->world_transform_to_global_pose(target->get_global_transform())).origin; + + // Lock the rotation to a plane relative to the bone by changing the target position + if (lock_rotation_to_plane) { + if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_X) { + target_pos.x = new_bone_trans.origin.x; + } else if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_Y) { + target_pos.y = new_bone_trans.origin.y; + } else if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_Z) { + target_pos.z = new_bone_trans.origin.z; + } + } + + // Look at the target! + new_bone_trans = new_bone_trans.looking_at(target_pos, Vector3(0, 1, 0)); + // Convert from Z-forward to whatever direction the bone faces. + skeleton->update_bone_rest_forward_vector(bone_idx); + new_bone_trans.basis = skeleton->global_pose_z_forward_to_bone_forward(bone_idx, new_bone_trans.basis); + + // Apply additional rotation + new_bone_trans.basis.rotate_local(Vector3(1, 0, 0), additional_rotation.x); + new_bone_trans.basis.rotate_local(Vector3(0, 1, 0), additional_rotation.y); + new_bone_trans.basis.rotate_local(Vector3(0, 0, 1), additional_rotation.z); + skeleton->set_bone_pose_position(bone_idx, new_bone_trans.origin); + skeleton->set_bone_pose_rotation(bone_idx, new_bone_trans.basis.get_rotation_quaternion()); + skeleton->set_bone_pose_scale(bone_idx, new_bone_trans.basis.get_scale()); + skeleton->force_update_bone_children_transforms(bone_idx); + + // If we completed it successfully, then we can set execution_error_found to false + execution_error_found = false; + } + } } diff --git a/scene/resources/skeleton_modification_3d_lookat.h b/scene/3d/skeleton_modification_3d_lookat.h similarity index 92% rename from scene/resources/skeleton_modification_3d_lookat.h rename to scene/3d/skeleton_modification_3d_lookat.h index cea63fc34fa4..70f62c9e3ccc 100644 --- a/scene/resources/skeleton_modification_3d_lookat.h +++ b/scene/3d/skeleton_modification_3d_lookat.h @@ -32,7 +32,7 @@ #define SKELETON_MODIFICATION_3D_LOOKAT_H #include "scene/3d/skeleton_3d.h" -#include "scene/resources/skeleton_modification_3d.h" +#include "scene/3d/skeleton_modification_3d.h" class SkeletonModification3DLookAt : public SkeletonModification3D { GDCLASS(SkeletonModification3DLookAt, SkeletonModification3D); @@ -43,7 +43,7 @@ class SkeletonModification3DLookAt : public SkeletonModification3D { NodePath target_node; ObjectID target_node_cache; - Vector3 additional_rotation = Vector3(1, 0, 0); + Vector3 additional_rotation = Vector3(); bool lock_rotation_to_plane = false; int lock_rotation_plane = ROTATION_PLANE_X; @@ -51,20 +51,16 @@ class SkeletonModification3DLookAt : public SkeletonModification3D { protected: static void _bind_methods(); + void _notification(int32_t p_what); bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); void _get_property_list(List *p_list) const; - public: enum ROTATION_PLANE { ROTATION_PLANE_X, ROTATION_PLANE_Y, ROTATION_PLANE_Z }; - - virtual void _execute(real_t p_delta) override; - virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override; - void set_bone_name(String p_name); String get_bone_name() const; @@ -82,8 +78,8 @@ class SkeletonModification3DLookAt : public SkeletonModification3D { void set_lock_rotation_plane(int p_plane); int get_lock_rotation_plane() const; - SkeletonModification3DLookAt(); - ~SkeletonModification3DLookAt(); + SkeletonModification3DLookAt() {} + ~SkeletonModification3DLookAt() {} }; #endif // SKELETON_MODIFICATION_3D_LOOKAT_H diff --git a/scene/resources/skeleton_modification_3d_twoboneik.cpp b/scene/3d/skeleton_modification_3d_twoboneik.cpp similarity index 54% rename from scene/resources/skeleton_modification_3d_twoboneik.cpp rename to scene/3d/skeleton_modification_3d_twoboneik.cpp index 366fcc30b7f8..cbb914f5fbd8 100644 --- a/scene/resources/skeleton_modification_3d_twoboneik.cpp +++ b/scene/3d/skeleton_modification_3d_twoboneik.cpp @@ -28,9 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "scene/resources/skeleton_modification_3d_twoboneik.h" +#include "scene/3d/skeleton_modification_3d_twoboneik.h" +#include "core/error/error_macros.h" #include "scene/3d/skeleton_3d.h" -#include "scene/resources/skeleton_modification_3d.h" +#include "scene/3d/skeleton_modification_3d.h" bool SkeletonModification3DTwoBoneIK::_set(const StringName &p_path, const Variant &p_value) { String path = p_path; @@ -126,207 +127,18 @@ void SkeletonModification3DTwoBoneIK::_get_property_list(List *p_l p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_two/roll", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT)); } -void SkeletonModification3DTwoBoneIK::_execute(real_t p_delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - - if (!enabled) { - return; - } - - if (_print_execution_error(joint_one_bone_idx < 0 || joint_two_bone_idx < 0, - "One (or more) of the bones in the modification have invalid bone indexes. Cannot execute modification!")) { - return; - } - - if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); - update_cache_target(); - return; - } - - // Update joint lengths (if needed) - if (auto_calculate_joint_length && (joint_one_length < 0 || joint_two_length < 0)) { - calculate_joint_lengths(); - } - - // Adopted from the links below: - // http://theorangeduck.com/page/simple-two-joint - // https://www.alanzucconi.com/2018/05/02/ik-2d-2/ - // With modifications by TwistedTwigleg - Node3D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { - return; - } - Transform3D target_trans = stack->skeleton->world_transform_to_global_pose(target->get_global_transform()); - - Transform3D bone_one_trans; - Transform3D bone_two_trans; - - // Make the first joint look at the pole, and the second look at the target. That way, the - // TwoBoneIK solver has to really only handle extension/contraction, which should make it align with the pole. - if (use_pole_node) { - if (pole_node_cache.is_null()) { - _print_execution_error(true, "Pole cache is out of date. Attempting to update..."); - update_cache_pole(); - return; - } - - Node3D *pole = Object::cast_to(ObjectDB::get_instance(pole_node_cache)); - if (_print_execution_error(!pole || !pole->is_inside_tree(), "Pole node is not in the scene tree. Cannot execute modification!")) { - return; - } - Transform3D pole_trans = stack->skeleton->world_transform_to_global_pose(pole->get_global_transform()); - - Transform3D bone_one_local_pos = stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx); - if (bone_one_local_pos == Transform3D()) { - bone_one_local_pos = stack->skeleton->get_bone_pose(joint_one_bone_idx); - } - Transform3D bone_two_local_pos = stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx); - if (bone_two_local_pos == Transform3D()) { - bone_two_local_pos = stack->skeleton->get_bone_pose(joint_two_bone_idx); - } - - bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, bone_one_local_pos); - bone_one_trans = bone_one_trans.looking_at(pole_trans.origin, Vector3(0, 1, 0)); - bone_one_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_one_bone_idx, bone_one_trans.basis); - stack->skeleton->update_bone_rest_forward_vector(joint_one_bone_idx); - bone_one_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_one_bone_idx), joint_one_roll); - stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans), stack->strength, true); - stack->skeleton->force_update_bone_children_transforms(joint_one_bone_idx); - - bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, bone_two_local_pos); - bone_two_trans = bone_two_trans.looking_at(target_trans.origin, Vector3(0, 1, 0)); - bone_two_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_two_bone_idx, bone_two_trans.basis); - stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx); - bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll); - stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans), stack->strength, true); - stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx); - } else { - Transform3D bone_one_local_pos = stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx); - if (bone_one_local_pos == Transform3D()) { - bone_one_local_pos = stack->skeleton->get_bone_pose(joint_one_bone_idx); - } - Transform3D bone_two_local_pos = stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx); - if (bone_two_local_pos == Transform3D()) { - bone_two_local_pos = stack->skeleton->get_bone_pose(joint_two_bone_idx); - } - - bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, bone_one_local_pos); - bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, bone_two_local_pos); - } - - Transform3D bone_two_tip_trans; - if (use_tip_node) { - if (tip_node_cache.is_null()) { - _print_execution_error(true, "Tip cache is out of date. Attempting to update..."); - update_cache_tip(); - return; - } - Node3D *tip = Object::cast_to(ObjectDB::get_instance(tip_node_cache)); - if (_print_execution_error(!tip || !tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) { - return; - } - bone_two_tip_trans = stack->skeleton->world_transform_to_global_pose(tip->get_global_transform()); - } else { - stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx); - bone_two_tip_trans = bone_two_trans; - bone_two_tip_trans.origin += bone_two_trans.basis.xform(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx)).normalized() * joint_two_length; - } - - real_t joint_one_to_target_length = bone_one_trans.origin.distance_to(target_trans.origin); - if (joint_one_length + joint_two_length < joint_one_to_target_length) { - // Set the target *just* out of reach to straighten the bones - joint_one_to_target_length = joint_one_length + joint_two_length + 0.01; - } else if (joint_one_to_target_length < joint_one_length) { - // Place the target in reach so the solver doesn't do crazy things - joint_one_to_target_length = joint_one_length; - } - - // Get the square lengths for all three sides of the triangle we'll use to calculate the angles - real_t sqr_one_length = joint_one_length * joint_one_length; - real_t sqr_two_length = joint_two_length * joint_two_length; - real_t sqr_three_length = joint_one_to_target_length * joint_one_to_target_length; - - // Calculate the angles for the first joint using the law of cosigns - real_t ac_ab_0 = Math::acos(CLAMP(bone_two_tip_trans.origin.direction_to(bone_one_trans.origin).dot(bone_two_trans.origin.direction_to(bone_one_trans.origin)), -1, 1)); - real_t ac_at_0 = Math::acos(CLAMP(bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).dot(bone_one_trans.origin.direction_to(target_trans.origin)), -1, 1)); - real_t ac_ab_1 = Math::acos(CLAMP((sqr_two_length - sqr_one_length - sqr_three_length) / (-2.0 * joint_one_length * joint_one_to_target_length), -1, 1)); - - // Calculate the angles of rotation. Angle 0 is the extension/contraction axis, while angle 1 is the rotation axis to align the triangle to the target - Vector3 axis_0 = bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).cross(bone_one_trans.origin.direction_to(bone_two_trans.origin)); - Vector3 axis_1 = bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).cross(bone_one_trans.origin.direction_to(target_trans.origin)); - - // Make a quaternion with the delta rotation needed to rotate the first joint into alignment and apply it to the transform. - Quaternion bone_one_quat = bone_one_trans.basis.get_rotation_quaternion(); - Quaternion rot_0 = Quaternion(bone_one_quat.inverse().xform(axis_0).normalized(), (ac_ab_1 - ac_ab_0)); - Quaternion rot_2 = Quaternion(bone_one_quat.inverse().xform(axis_1).normalized(), ac_at_0); - bone_one_trans.basis.set_quaternion(bone_one_quat * (rot_0 * rot_2)); - - stack->skeleton->update_bone_rest_forward_vector(joint_one_bone_idx); - bone_one_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_one_bone_idx), joint_one_roll); - - // Apply the rotation to the first joint - bone_one_trans = stack->skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans); - bone_one_trans.origin = Vector3(0, 0, 0); - stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, bone_one_trans, stack->strength, true); - stack->skeleton->force_update_bone_children_transforms(joint_one_bone_idx); - - if (use_pole_node) { - // Update bone_two_trans so its at the latest position, with the rotation of bone_one_trans taken into account, then look at the target. - bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx)); - stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx); - Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx); - bone_two_trans.basis.rotate_to_align(forward_vector, bone_two_trans.origin.direction_to(target_trans.origin)); - - stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx); - bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll); - - bone_two_trans = stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans); - stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, bone_two_trans, stack->strength, true); - stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx); - } else { - // Calculate the angles for the second joint using the law of cosigns, make a quaternion with the delta rotation needed to rotate the joint into - // alignment, and then apply it to the second joint. - real_t ba_bc_0 = Math::acos(CLAMP(bone_two_trans.origin.direction_to(bone_one_trans.origin).dot(bone_two_trans.origin.direction_to(bone_two_tip_trans.origin)), -1, 1)); - real_t ba_bc_1 = Math::acos(CLAMP((sqr_three_length - sqr_one_length - sqr_two_length) / (-2.0 * joint_one_length * joint_two_length), -1, 1)); - Quaternion bone_two_quat = bone_two_trans.basis.get_rotation_quaternion(); - Quaternion rot_1 = Quaternion(bone_two_quat.inverse().xform(axis_0).normalized(), (ba_bc_1 - ba_bc_0)); - bone_two_trans.basis.set_quaternion(bone_two_quat * rot_1); - - stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx); - bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll); - - bone_two_trans = stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans); - bone_two_trans.origin = Vector3(0, 0, 0); - stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, bone_two_trans, stack->strength, true); - stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx); - } -} - -void SkeletonModification3DTwoBoneIK::_setup_modification(SkeletonModificationStack3D *p_stack) { - stack = p_stack; - - if (stack != nullptr) { - is_setup = true; - execution_error_found = false; - update_cache_target(); - update_cache_tip(); - } -} - void SkeletonModification3DTwoBoneIK::update_cache_target() { - if (!is_setup || !stack) { + if (!is_setup) { _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); return; } target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree() && target_node.is_empty() == false) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + if (skeleton) { + if (skeleton->is_inside_tree() && target_node.is_empty() == false) { + if (skeleton->has_node(target_node)) { + Node *node = skeleton->get_node(target_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, "Cannot update target cache: Target node is this modification's skeleton or cannot be found. Cannot execute modification"); ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Cannot update target cache: Target node is not in the scene tree. Cannot execute modification!"); @@ -339,17 +151,17 @@ void SkeletonModification3DTwoBoneIK::update_cache_target() { } void SkeletonModification3DTwoBoneIK::update_cache_tip() { - if (!is_setup || !stack) { + if (!is_setup) { _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!"); return; } tip_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(tip_node)) { - Node *node = stack->skeleton->get_node(tip_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + if (skeleton) { + if (skeleton->is_inside_tree()) { + if (skeleton->has_node(tip_node)) { + Node *node = skeleton->get_node(tip_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, "Cannot update tip cache: Tip node is this modification's skeleton or cannot be found!"); ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Cannot update tip cache: Tip node is not in the scene tree. Cannot execute modification!"); @@ -362,17 +174,17 @@ void SkeletonModification3DTwoBoneIK::update_cache_tip() { } void SkeletonModification3DTwoBoneIK::update_cache_pole() { - if (!is_setup || !stack) { + if (!is_setup) { _print_execution_error(true, "Cannot update pole cache: modification is not properly setup!"); return; } pole_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(pole_node)) { - Node *node = stack->skeleton->get_node(pole_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + if (skeleton) { + if (skeleton->is_inside_tree()) { + if (skeleton->has_node(pole_node)) { + Node *node = skeleton->get_node(pole_node); + ERR_FAIL_COND_MSG(!node || skeleton == node, "Cannot update pole cache: Pole node is this modification's skeleton or cannot be found!"); ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Cannot update pole cache: Pole node is not in the scene tree. Cannot execute modification!"); @@ -445,13 +257,13 @@ void SkeletonModification3DTwoBoneIK::calculate_joint_lengths() { if (!is_setup) { return; // fail silently, as we likely just loaded the scene. } - ERR_FAIL_COND_MSG(!stack || stack->skeleton == nullptr, + ERR_FAIL_COND_MSG(skeleton == nullptr, "Modification is not setup and therefore cannot calculate joint lengths!"); ERR_FAIL_COND_MSG(joint_one_bone_idx <= -1 || joint_two_bone_idx <= -1, "One of the bones in the TwoBoneIK modification are not set! Cannot calculate joint lengths!"); - Transform3D bone_one_rest_trans = stack->skeleton->get_bone_global_pose(joint_one_bone_idx); - Transform3D bone_two_rest_trans = stack->skeleton->get_bone_global_pose(joint_two_bone_idx); + Transform3D bone_one_rest_trans = skeleton->get_bone_global_pose(joint_one_bone_idx); + Transform3D bone_two_rest_trans = skeleton->get_bone_global_pose(joint_two_bone_idx); joint_one_length = bone_one_rest_trans.origin.distance_to(bone_two_rest_trans.origin); @@ -463,17 +275,17 @@ void SkeletonModification3DTwoBoneIK::calculate_joint_lengths() { Node3D *tip = Object::cast_to(ObjectDB::get_instance(tip_node_cache)); if (tip) { - Transform3D bone_tip_trans = stack->skeleton->world_transform_to_global_pose(tip->get_global_transform()); + Transform3D bone_tip_trans = skeleton->world_transform_to_global_pose(tip->get_global_transform()); joint_two_length = bone_two_rest_trans.origin.distance_to(bone_tip_trans.origin); } } else { // Attempt to use children bones to get the length - Vector bone_two_children = stack->skeleton->get_bone_children(joint_two_bone_idx); + Vector bone_two_children = skeleton->get_bone_children(joint_two_bone_idx); if (bone_two_children.size() > 0) { joint_two_length = 0; for (int i = 0; i < bone_two_children.size(); i++) { joint_two_length += bone_two_rest_trans.origin.distance_to( - stack->skeleton->get_bone_global_pose(bone_two_children[i]).origin); + skeleton->get_bone_global_pose(bone_two_children[i]).origin); } joint_two_length = joint_two_length / bone_two_children.size(); } else { @@ -486,8 +298,8 @@ void SkeletonModification3DTwoBoneIK::calculate_joint_lengths() { void SkeletonModification3DTwoBoneIK::set_joint_one_bone_name(String p_bone_name) { joint_one_bone_name = p_bone_name; - if (stack && stack->skeleton) { - joint_one_bone_idx = stack->skeleton->find_bone(p_bone_name); + if (skeleton) { + joint_one_bone_idx = skeleton->find_bone(p_bone_name); } execution_error_found = false; notify_property_list_changed(); @@ -499,8 +311,8 @@ String SkeletonModification3DTwoBoneIK::get_joint_one_bone_name() const { void SkeletonModification3DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) { joint_one_bone_idx = p_bone_idx; - if (stack && stack->skeleton) { - joint_one_bone_name = stack->skeleton->get_bone_name(p_bone_idx); + if (skeleton) { + joint_one_bone_name = skeleton->get_bone_name(p_bone_idx); } execution_error_found = false; notify_property_list_changed(); @@ -520,8 +332,8 @@ real_t SkeletonModification3DTwoBoneIK::get_joint_one_length() const { void SkeletonModification3DTwoBoneIK::set_joint_two_bone_name(String p_bone_name) { joint_two_bone_name = p_bone_name; - if (stack && stack->skeleton) { - joint_two_bone_idx = stack->skeleton->find_bone(p_bone_name); + if (skeleton) { + joint_two_bone_idx = skeleton->find_bone(p_bone_name); } execution_error_found = false; notify_property_list_changed(); @@ -533,8 +345,8 @@ String SkeletonModification3DTwoBoneIK::get_joint_two_bone_name() const { void SkeletonModification3DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) { joint_two_bone_idx = p_bone_idx; - if (stack && stack->skeleton) { - joint_two_bone_name = stack->skeleton->get_bone_name(p_bone_idx); + if (skeleton) { + joint_two_bone_name = skeleton->get_bone_name(p_bone_idx); } execution_error_found = false; notify_property_list_changed(); @@ -609,9 +421,228 @@ void SkeletonModification3DTwoBoneIK::_bind_methods() { } SkeletonModification3DTwoBoneIK::SkeletonModification3DTwoBoneIK() { - stack = nullptr; is_setup = false; } SkeletonModification3DTwoBoneIK::~SkeletonModification3DTwoBoneIK() { } + +void SkeletonModification3DTwoBoneIK::_notification(int32_t p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + execution_error_found = false; + update_cache_target(); + update_cache_tip(); + set_process_internal(false); + set_physics_process_internal(false); + if (get_execution_mode() == 0) { + set_process_internal(true); + } else if (get_execution_mode() == 1) { + set_physics_process_internal(true); + } + skeleton = cast_to(get_node_or_null(get_skeleton_path())); + is_setup = true; + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: + [[fallthrough]]; + case NOTIFICATION_INTERNAL_PROCESS: { + if (!is_setup || skeleton == nullptr) { + ERR_PRINT_ONCE("Modification is not setup and therefore cannot execute!"); + return; + } + + if (!enabled) { + return; + } + + if (_print_execution_error(joint_one_bone_idx < 0 || joint_two_bone_idx < 0, + "One (or more) of the bones in the modification have invalid bone indexes. Cannot execute modification!")) { + return; + } + + if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + update_cache_target(); + return; + } + + // Update joint lengths (if needed) + if (auto_calculate_joint_length && (joint_one_length < 0 || joint_two_length < 0)) { + calculate_joint_lengths(); + } + + // Adopted from the links below: + // http://theorangeduck.com/page/simple-two-joint + // https://www.alanzucconi.com/2018/05/02/ik-2d-2/ + // With modifications by TwistedTwigleg + Node3D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + return; + } + Transform3D target_trans = skeleton->world_transform_to_global_pose(target->get_global_transform()); + + Transform3D bone_one_trans; + Transform3D bone_two_trans; + + // Make the first joint look at the pole, and the second look at the target. That way, the + // TwoBoneIK solver has to really only handle extension/contraction, which should make it align with the pole. + if (use_pole_node) { + if (pole_node_cache.is_null()) { + _print_execution_error(true, "Pole cache is out of date. Attempting to update..."); + update_cache_pole(); + return; + } + + Node3D *pole = Object::cast_to(ObjectDB::get_instance(pole_node_cache)); + if (_print_execution_error(!pole || !pole->is_inside_tree(), "Pole node is not in the scene tree. Cannot execute modification!")) { + return; + } + Transform3D pole_trans = skeleton->world_transform_to_global_pose(pole->get_global_transform()); + + Transform3D bone_one_global_pose = skeleton->get_bone_global_pose(joint_one_bone_idx); + Transform3D bone_one_local_pos = skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_global_pose); + if (bone_one_local_pos == Transform3D()) { + bone_one_local_pos = skeleton->get_bone_pose(joint_one_bone_idx); + } + Transform3D bone_two_global_pose = skeleton->get_bone_global_pose(joint_two_bone_idx); + Transform3D bone_two_local_pos = skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_global_pose); + if (bone_two_local_pos == Transform3D()) { + bone_two_local_pos = skeleton->get_bone_pose(joint_two_bone_idx); + } + + bone_one_trans = skeleton->local_pose_to_global_pose(joint_one_bone_idx, bone_one_local_pos); + bone_one_trans = bone_one_trans.looking_at(pole_trans.origin, Vector3(0, 1, 0)); + bone_one_trans.basis = skeleton->global_pose_z_forward_to_bone_forward(joint_one_bone_idx, bone_one_trans.basis); + skeleton->update_bone_rest_forward_vector(joint_one_bone_idx); + bone_one_trans.basis.rotate_local(skeleton->get_bone_axis_forward_vector(joint_one_bone_idx), joint_one_roll); + Transform3D bone_one_trans_local = skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans); + skeleton->set_bone_pose_position(joint_one_bone_idx, bone_one_trans_local.origin); + skeleton->set_bone_pose_rotation(joint_one_bone_idx, bone_one_trans_local.basis.get_rotation_quaternion()); + skeleton->set_bone_pose_scale(joint_one_bone_idx, bone_one_trans_local.basis.get_scale()); + + skeleton->force_update_bone_children_transforms(joint_one_bone_idx); + + bone_two_trans = skeleton->local_pose_to_global_pose(joint_two_bone_idx, bone_two_local_pos); + bone_two_trans = bone_two_trans.looking_at(target_trans.origin, Vector3(0, 1, 0)); + bone_two_trans.basis = skeleton->global_pose_z_forward_to_bone_forward(joint_two_bone_idx, bone_two_trans.basis); + skeleton->update_bone_rest_forward_vector(joint_two_bone_idx); + bone_two_trans.basis.rotate_local(skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll); + Transform3D bone_two_trans_local = skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans); + skeleton->set_bone_pose_position(joint_two_bone_idx, bone_two_trans_local.origin); + skeleton->set_bone_pose_rotation(joint_two_bone_idx, bone_two_trans_local.basis.get_rotation_quaternion()); + skeleton->set_bone_pose_scale(joint_two_bone_idx, bone_two_trans_local.basis.get_scale()); + + skeleton->force_update_bone_children_transforms(joint_two_bone_idx); + } else { + Transform3D bone_one_global_pose = skeleton->get_bone_global_pose(joint_one_bone_idx); + Transform3D bone_one_local_pos = skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_global_pose); + if (bone_one_local_pos == Transform3D()) { + bone_one_local_pos = skeleton->get_bone_pose(joint_one_bone_idx); + } + Transform3D bone_two_global_pose = skeleton->get_bone_global_pose(joint_two_bone_idx); + Transform3D bone_two_local_pos = skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_global_pose); + if (bone_two_local_pos == Transform3D()) { + bone_two_local_pos = skeleton->get_bone_pose(joint_two_bone_idx); + } + + bone_one_trans = skeleton->local_pose_to_global_pose(joint_one_bone_idx, bone_one_local_pos); + bone_two_trans = skeleton->local_pose_to_global_pose(joint_two_bone_idx, bone_two_local_pos); + } + + Transform3D bone_two_tip_trans; + if (use_tip_node) { + if (tip_node_cache.is_null()) { + _print_execution_error(true, "Tip cache is out of date. Attempting to update..."); + update_cache_tip(); + return; + } + Node3D *tip = Object::cast_to(ObjectDB::get_instance(tip_node_cache)); + if (_print_execution_error(!tip || !tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) { + return; + } + bone_two_tip_trans = skeleton->world_transform_to_global_pose(tip->get_global_transform()); + } else { + skeleton->update_bone_rest_forward_vector(joint_two_bone_idx); + bone_two_tip_trans = bone_two_trans; + bone_two_tip_trans.origin += bone_two_trans.basis.xform(skeleton->get_bone_axis_forward_vector(joint_two_bone_idx)).normalized() * joint_two_length; + } + + real_t joint_one_to_target_length = bone_one_trans.origin.distance_to(target_trans.origin); + if (joint_one_length + joint_two_length < joint_one_to_target_length) { + // Set the target *just* out of reach to straighten the bones + joint_one_to_target_length = joint_one_length + joint_two_length + 0.01; + } else if (joint_one_to_target_length < joint_one_length) { + // Place the target in reach so the solver doesn't do crazy things + joint_one_to_target_length = joint_one_length; + } + + // Get the square lengths for all three sides of the triangle we'll use to calculate the angles + real_t sqr_one_length = joint_one_length * joint_one_length; + real_t sqr_two_length = joint_two_length * joint_two_length; + real_t sqr_three_length = joint_one_to_target_length * joint_one_to_target_length; + + // Calculate the angles for the first joint using the law of cosigns + real_t ac_ab_0 = Math::acos(CLAMP(bone_two_tip_trans.origin.direction_to(bone_one_trans.origin).dot(bone_two_trans.origin.direction_to(bone_one_trans.origin)), -1, 1)); + real_t ac_at_0 = Math::acos(CLAMP(bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).dot(bone_one_trans.origin.direction_to(target_trans.origin)), -1, 1)); + real_t ac_ab_1 = Math::acos(CLAMP((sqr_two_length - sqr_one_length - sqr_three_length) / (-2.0 * joint_one_length * joint_one_to_target_length), -1, 1)); + + // Calculate the angles of rotation. Angle 0 is the extension/contraction axis, while angle 1 is the rotation axis to align the triangle to the target + Vector3 axis_0 = bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).cross(bone_one_trans.origin.direction_to(bone_two_trans.origin)); + Vector3 axis_1 = bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).cross(bone_one_trans.origin.direction_to(target_trans.origin)); + + // Make a quaternion with the delta rotation needed to rotate the first joint into alignment and apply it to the transform. + Quaternion bone_one_quat = bone_one_trans.basis.get_rotation_quaternion(); + Quaternion rot_0 = Quaternion(bone_one_quat.inverse().xform(axis_0).normalized(), (ac_ab_1 - ac_ab_0)); + Quaternion rot_2 = Quaternion(bone_one_quat.inverse().xform(axis_1).normalized(), ac_at_0); + bone_one_trans.basis.set_quaternion(bone_one_quat * (rot_0 * rot_2)); + + skeleton->update_bone_rest_forward_vector(joint_one_bone_idx); + bone_one_trans.basis.rotate_local(skeleton->get_bone_axis_forward_vector(joint_one_bone_idx), joint_one_roll); + + // Apply the rotation to the first joint + bone_one_trans = skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans); + bone_one_trans.origin = Vector3(0, 0, 0); + skeleton->set_bone_pose_position(joint_one_bone_idx, bone_one_trans.origin); + skeleton->set_bone_pose_rotation(joint_one_bone_idx, bone_one_trans.basis.get_rotation_quaternion()); + skeleton->set_bone_pose_scale(joint_one_bone_idx, bone_one_trans.basis.get_scale()); + skeleton->force_update_bone_children_transforms(joint_one_bone_idx); + + if (use_pole_node) { + // Update bone_two_trans so its at the latest position, with the rotation of bone_one_trans taken into account, then look at the target. + bone_two_trans = skeleton->get_bone_global_pose(joint_two_bone_idx); + skeleton->update_bone_rest_forward_vector(joint_two_bone_idx); + Vector3 forward_vector = skeleton->get_bone_axis_forward_vector(joint_two_bone_idx); + bone_two_trans.basis.rotate_to_align(forward_vector, bone_two_trans.origin.direction_to(target_trans.origin)); + + skeleton->update_bone_rest_forward_vector(joint_two_bone_idx); + bone_two_trans.basis.rotate_local(skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll); + + bone_two_trans = skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans); + skeleton->set_bone_pose_position(joint_two_bone_idx, bone_two_trans.origin); + skeleton->set_bone_pose_rotation(joint_two_bone_idx, bone_two_trans.basis.get_rotation_quaternion()); + skeleton->set_bone_pose_scale(joint_two_bone_idx, bone_two_trans.basis.get_scale()); + + skeleton->force_update_bone_children_transforms(joint_two_bone_idx); + } else { + // Calculate the angles for the second joint using the law of cosigns, make a quaternion with the delta rotation needed to rotate the joint into + // alignment, and then apply it to the second joint. + real_t ba_bc_0 = Math::acos(CLAMP(bone_two_trans.origin.direction_to(bone_one_trans.origin).dot(bone_two_trans.origin.direction_to(bone_two_tip_trans.origin)), -1, 1)); + real_t ba_bc_1 = Math::acos(CLAMP((sqr_three_length - sqr_one_length - sqr_two_length) / (-2.0 * joint_one_length * joint_two_length), -1, 1)); + Quaternion bone_two_quat = bone_two_trans.basis.get_rotation_quaternion(); + Quaternion rot_1 = Quaternion(bone_two_quat.inverse().xform(axis_0).normalized(), (ba_bc_1 - ba_bc_0)); + bone_two_trans.basis.set_quaternion(bone_two_quat * rot_1); + + skeleton->update_bone_rest_forward_vector(joint_two_bone_idx); + bone_two_trans.basis.rotate_local(skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll); + + bone_two_trans = skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans); + bone_two_trans.origin = Vector3(0, 0, 0); + skeleton->set_bone_pose_position(joint_two_bone_idx, bone_two_trans.origin); + skeleton->set_bone_pose_rotation(joint_two_bone_idx, bone_two_trans.basis.get_rotation_quaternion()); + skeleton->set_bone_pose_scale(joint_two_bone_idx, bone_two_trans.basis.get_scale()); + + skeleton->force_update_bone_children_transforms(joint_two_bone_idx); + } + } + } +} diff --git a/scene/resources/skeleton_modification_3d_twoboneik.h b/scene/3d/skeleton_modification_3d_twoboneik.h similarity index 96% rename from scene/resources/skeleton_modification_3d_twoboneik.h rename to scene/3d/skeleton_modification_3d_twoboneik.h index 7bd7c8291dfe..e9db6404bf6e 100644 --- a/scene/resources/skeleton_modification_3d_twoboneik.h +++ b/scene/3d/skeleton_modification_3d_twoboneik.h @@ -32,7 +32,7 @@ #define SKELETON_MODIFICATION_3D_TWOBONEIK_H #include "scene/3d/skeleton_3d.h" -#include "scene/resources/skeleton_modification_3d.h" +#include "scene/3d/skeleton_modification_3d.h" class SkeletonModification3DTwoBoneIK : public SkeletonModification3D { GDCLASS(SkeletonModification3DTwoBoneIK, SkeletonModification3D); @@ -66,15 +66,13 @@ class SkeletonModification3DTwoBoneIK : public SkeletonModification3D { void update_cache_pole(); protected: + void _notification(int32_t p_what); static void _bind_methods(); bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); void _get_property_list(List *p_list) const; public: - virtual void _execute(real_t p_delta) override; - virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override; - void set_target_node(const NodePath &p_target_node); NodePath get_target_node() const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index a9470596b207..c2780d966fab 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -66,6 +66,13 @@ #include "scene/2d/remote_transform_2d.h" #include "scene/2d/shape_cast_2d.h" #include "scene/2d/skeleton_2d.h" +#include "scene/2d/skeleton_modification_2d.h" +#include "scene/2d/skeleton_modification_2d_ccdik.h" +#include "scene/2d/skeleton_modification_2d_fabrik.h" +#include "scene/2d/skeleton_modification_2d_jiggle.h" +#include "scene/2d/skeleton_modification_2d_lookat.h" +#include "scene/2d/skeleton_modification_2d_physicalbones.h" +#include "scene/2d/skeleton_modification_2d_twoboneik.h" #include "scene/2d/sprite_2d.h" #include "scene/2d/tile_map.h" #include "scene/2d/touch_screen_button.h" @@ -174,23 +181,6 @@ #include "scene/resources/separation_ray_shape_2d.h" #include "scene/resources/separation_ray_shape_3d.h" #include "scene/resources/shader_include.h" -#include "scene/resources/skeleton_modification_2d.h" -#include "scene/resources/skeleton_modification_2d_ccdik.h" -#include "scene/resources/skeleton_modification_2d_fabrik.h" -#include "scene/resources/skeleton_modification_2d_jiggle.h" -#include "scene/resources/skeleton_modification_2d_lookat.h" -#include "scene/resources/skeleton_modification_2d_physicalbones.h" -#include "scene/resources/skeleton_modification_2d_stackholder.h" -#include "scene/resources/skeleton_modification_2d_twoboneik.h" -#include "scene/resources/skeleton_modification_3d.h" -#include "scene/resources/skeleton_modification_3d_ccdik.h" -#include "scene/resources/skeleton_modification_3d_fabrik.h" -#include "scene/resources/skeleton_modification_3d_jiggle.h" -#include "scene/resources/skeleton_modification_3d_lookat.h" -#include "scene/resources/skeleton_modification_3d_stackholder.h" -#include "scene/resources/skeleton_modification_3d_twoboneik.h" -#include "scene/resources/skeleton_modification_stack_2d.h" -#include "scene/resources/skeleton_modification_stack_3d.h" #include "scene/resources/skeleton_profile.h" #include "scene/resources/sky.h" #include "scene/resources/sky_material.h" @@ -253,7 +243,12 @@ #include "scene/3d/remote_transform_3d.h" #include "scene/3d/shape_cast_3d.h" #include "scene/3d/skeleton_3d.h" -#include "scene/3d/skeleton_ik_3d.h" +#include "scene/3d/skeleton_modification_3d.h" +#include "scene/3d/skeleton_modification_3d_ccdik.h" +#include "scene/3d/skeleton_modification_3d_fabrik.h" +#include "scene/3d/skeleton_modification_3d_jiggle.h" +#include "scene/3d/skeleton_modification_3d_lookat.h" +#include "scene/3d/skeleton_modification_3d_twoboneik.h" #include "scene/3d/soft_body_3d.h" #include "scene/3d/spring_arm_3d.h" #include "scene/3d/sprite_3d.h" @@ -547,7 +542,6 @@ void register_scene_types() { GDREGISTER_CLASS(PhysicalBone3D); GDREGISTER_CLASS(SoftBody3D); - GDREGISTER_CLASS(SkeletonIK3D); GDREGISTER_CLASS(BoneAttachment3D); GDREGISTER_CLASS(VehicleBody3D); @@ -758,14 +752,12 @@ void register_scene_types() { GDREGISTER_CLASS(TouchScreenButton); GDREGISTER_CLASS(RemoteTransform2D); - GDREGISTER_CLASS(SkeletonModificationStack2D); GDREGISTER_CLASS(SkeletonModification2D); GDREGISTER_CLASS(SkeletonModification2DLookAt); GDREGISTER_CLASS(SkeletonModification2DCCDIK); GDREGISTER_CLASS(SkeletonModification2DFABRIK); GDREGISTER_CLASS(SkeletonModification2DJiggle); GDREGISTER_CLASS(SkeletonModification2DTwoBoneIK); - GDREGISTER_CLASS(SkeletonModification2DStackHolder); GDREGISTER_CLASS(PhysicalBone2D); GDREGISTER_CLASS(SkeletonModification2DPhysicalBones); @@ -826,14 +818,12 @@ void register_scene_types() { GDREGISTER_CLASS(ConvexPolygonShape3D); GDREGISTER_CLASS(ConcavePolygonShape3D); - ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); - ClassDB::register_class(); OS::get_singleton()->yield(); // may take time to init #endif // _3D_DISABLED diff --git a/scene/resources/skeleton_modification_2d_stackholder.cpp b/scene/resources/skeleton_modification_2d_stackholder.cpp deleted file mode 100644 index 9ec343470443..000000000000 --- a/scene/resources/skeleton_modification_2d_stackholder.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/*************************************************************************/ -/* skeleton_modification_2d_stackholder.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "skeleton_modification_2d_stackholder.h" -#include "scene/2d/skeleton_2d.h" - -bool SkeletonModification2DStackHolder::_set(const StringName &p_path, const Variant &p_value) { - String path = p_path; - - if (path == "held_modification_stack") { - set_held_modification_stack(p_value); - } - -#ifdef TOOLS_ENABLED - if (path == "editor/draw_gizmo") { - set_editor_draw_gizmo(p_value); - } -#endif // TOOLS_ENABLED - - return true; -} - -bool SkeletonModification2DStackHolder::_get(const StringName &p_path, Variant &r_ret) const { - String path = p_path; - - if (path == "held_modification_stack") { - r_ret = get_held_modification_stack(); - } - -#ifdef TOOLS_ENABLED - if (path == "editor/draw_gizmo") { - r_ret = get_editor_draw_gizmo(); - } -#endif // TOOLS_ENABLED - - return true; -} - -void SkeletonModification2DStackHolder::_get_property_list(List *p_list) const { - p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); - -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); - } -#endif // TOOLS_ENABLED -} - -void SkeletonModification2DStackHolder::_execute(float p_delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - - if (held_modification_stack.is_valid()) { - held_modification_stack->execute(p_delta, execution_mode); - } -} - -void SkeletonModification2DStackHolder::_setup_modification(SkeletonModificationStack2D *p_stack) { - stack = p_stack; - - if (stack != nullptr) { - is_setup = true; - - if (held_modification_stack.is_valid()) { - held_modification_stack->set_skeleton(stack->get_skeleton()); - held_modification_stack->setup(); - } - } -} - -void SkeletonModification2DStackHolder::_draw_editor_gizmo() { - if (stack) { - if (held_modification_stack.is_valid()) { - held_modification_stack->draw_editor_gizmos(); - } - } -} - -void SkeletonModification2DStackHolder::set_held_modification_stack(Ref p_held_stack) { - held_modification_stack = p_held_stack; - - if (is_setup && held_modification_stack.is_valid()) { - held_modification_stack->set_skeleton(stack->get_skeleton()); - held_modification_stack->setup(); - } -} - -Ref SkeletonModification2DStackHolder::get_held_modification_stack() const { - return held_modification_stack; -} - -void SkeletonModification2DStackHolder::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_held_modification_stack", "held_modification_stack"), &SkeletonModification2DStackHolder::set_held_modification_stack); - ClassDB::bind_method(D_METHOD("get_held_modification_stack"), &SkeletonModification2DStackHolder::get_held_modification_stack); -} - -SkeletonModification2DStackHolder::SkeletonModification2DStackHolder() { - stack = nullptr; - is_setup = false; - enabled = true; -} - -SkeletonModification2DStackHolder::~SkeletonModification2DStackHolder() { -} diff --git a/scene/resources/skeleton_modification_2d_stackholder.h b/scene/resources/skeleton_modification_2d_stackholder.h deleted file mode 100644 index 4da9fcc1f6c2..000000000000 --- a/scene/resources/skeleton_modification_2d_stackholder.h +++ /dev/null @@ -1,64 +0,0 @@ -/*************************************************************************/ -/* skeleton_modification_2d_stackholder.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SKELETON_MODIFICATION_2D_STACKHOLDER_H -#define SKELETON_MODIFICATION_2D_STACKHOLDER_H - -#include "scene/2d/skeleton_2d.h" -#include "scene/resources/skeleton_modification_2d.h" - -/////////////////////////////////////// -// SkeletonModification2DJIGGLE -/////////////////////////////////////// - -class SkeletonModification2DStackHolder : public SkeletonModification2D { - GDCLASS(SkeletonModification2DStackHolder, SkeletonModification2D); - -protected: - static void _bind_methods(); - bool _get(const StringName &p_path, Variant &r_ret) const; - bool _set(const StringName &p_path, const Variant &p_value); - void _get_property_list(List *p_list) const; - -public: - Ref held_modification_stack; - - void _execute(float p_delta) override; - void _setup_modification(SkeletonModificationStack2D *p_stack) override; - void _draw_editor_gizmo() override; - - void set_held_modification_stack(Ref p_held_stack); - Ref get_held_modification_stack() const; - - SkeletonModification2DStackHolder(); - ~SkeletonModification2DStackHolder(); -}; - -#endif // SKELETON_MODIFICATION_2D_STACKHOLDER_H diff --git a/scene/resources/skeleton_modification_3d_stackholder.cpp b/scene/resources/skeleton_modification_3d_stackholder.cpp deleted file mode 100644 index fb2e80d21742..000000000000 --- a/scene/resources/skeleton_modification_3d_stackholder.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/*************************************************************************/ -/* skeleton_modification_3d_stackholder.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "scene/resources/skeleton_modification_3d_stackholder.h" -#include "scene/3d/skeleton_3d.h" -#include "scene/resources/skeleton_modification_3d.h" - -bool SkeletonModification3DStackHolder::_set(const StringName &p_path, const Variant &p_value) { - String path = p_path; - - if (path == "held_modification_stack") { - set_held_modification_stack(p_value); - } - return true; -} - -bool SkeletonModification3DStackHolder::_get(const StringName &p_path, Variant &r_ret) const { - String path = p_path; - - if (path == "held_modification_stack") { - r_ret = get_held_modification_stack(); - } - return true; -} - -void SkeletonModification3DStackHolder::_get_property_list(List *p_list) const { - p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); -} - -void SkeletonModification3DStackHolder::_execute(real_t p_delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - - if (held_modification_stack.is_valid()) { - held_modification_stack->execute(p_delta, execution_mode); - } -} - -void SkeletonModification3DStackHolder::_setup_modification(SkeletonModificationStack3D *p_stack) { - stack = p_stack; - - if (stack != nullptr) { - is_setup = true; - - if (held_modification_stack.is_valid()) { - held_modification_stack->set_skeleton(stack->get_skeleton()); - held_modification_stack->setup(); - } - } -} - -void SkeletonModification3DStackHolder::set_held_modification_stack(Ref p_held_stack) { - held_modification_stack = p_held_stack; - - if (is_setup && held_modification_stack.is_valid()) { - held_modification_stack->set_skeleton(stack->get_skeleton()); - held_modification_stack->setup(); - } -} - -Ref SkeletonModification3DStackHolder::get_held_modification_stack() const { - return held_modification_stack; -} - -void SkeletonModification3DStackHolder::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_held_modification_stack", "held_modification_stack"), &SkeletonModification3DStackHolder::set_held_modification_stack); - ClassDB::bind_method(D_METHOD("get_held_modification_stack"), &SkeletonModification3DStackHolder::get_held_modification_stack); -} - -SkeletonModification3DStackHolder::SkeletonModification3DStackHolder() { - stack = nullptr; - is_setup = false; - enabled = true; -} - -SkeletonModification3DStackHolder::~SkeletonModification3DStackHolder() { -} diff --git a/scene/resources/skeleton_modification_3d_stackholder.h b/scene/resources/skeleton_modification_3d_stackholder.h deleted file mode 100644 index 2071de54574c..000000000000 --- a/scene/resources/skeleton_modification_3d_stackholder.h +++ /dev/null @@ -1,59 +0,0 @@ -/*************************************************************************/ -/* skeleton_modification_3d_stackholder.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SKELETON_MODIFICATION_3D_STACKHOLDER_H -#define SKELETON_MODIFICATION_3D_STACKHOLDER_H - -#include "scene/3d/skeleton_3d.h" -#include "scene/resources/skeleton_modification_3d.h" - -class SkeletonModification3DStackHolder : public SkeletonModification3D { - GDCLASS(SkeletonModification3DStackHolder, SkeletonModification3D); - -protected: - static void _bind_methods(); - bool _get(const StringName &p_path, Variant &r_ret) const; - bool _set(const StringName &p_path, const Variant &p_value); - void _get_property_list(List *p_list) const; - -public: - Ref held_modification_stack; - - virtual void _execute(real_t p_delta) override; - virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override; - - void set_held_modification_stack(Ref p_held_stack); - Ref get_held_modification_stack() const; - - SkeletonModification3DStackHolder(); - ~SkeletonModification3DStackHolder(); -}; - -#endif // SKELETON_MODIFICATION_3D_STACKHOLDER_H diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp deleted file mode 100644 index 068c75684959..000000000000 --- a/scene/resources/skeleton_modification_stack_2d.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/*************************************************************************/ -/* skeleton_modification_stack_2d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "skeleton_modification_stack_2d.h" -#include "scene/2d/skeleton_2d.h" - -void SkeletonModificationStack2D::_get_property_list(List *p_list) const { - for (int i = 0; i < modifications.size(); i++) { - p_list->push_back( - PropertyInfo(Variant::OBJECT, "modifications/" + itos(i), - PROPERTY_HINT_RESOURCE_TYPE, - "SkeletonModification2D", - PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); - } -} - -bool SkeletonModificationStack2D::_set(const StringName &p_path, const Variant &p_value) { - String path = p_path; - - if (path.begins_with("modifications/")) { - int mod_idx = path.get_slicec('/', 1).to_int(); - set_modification(mod_idx, p_value); - return true; - } - return true; -} - -bool SkeletonModificationStack2D::_get(const StringName &p_path, Variant &r_ret) const { - String path = p_path; - - if (path.begins_with("modifications/")) { - int mod_idx = path.get_slicec('/', 1).to_int(); - r_ret = get_modification(mod_idx); - return true; - } - return true; -} - -void SkeletonModificationStack2D::setup() { - if (is_setup) { - return; - } - - if (skeleton != nullptr) { - is_setup = true; - for (int i = 0; i < modifications.size(); i++) { - if (!modifications[i].is_valid()) { - continue; - } - modifications.get(i)->_setup_modification(this); - } - -#ifdef TOOLS_ENABLED - set_editor_gizmos_dirty(true); -#endif // TOOLS_ENABLED - - } else { - WARN_PRINT("Cannot setup SkeletonModificationStack2D: no Skeleton2D set!"); - } -} - -void SkeletonModificationStack2D::execute(float p_delta, int p_execution_mode) { - ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr || is_queued_for_deletion(), - "Modification stack is not properly setup and therefore cannot execute!"); - - if (!skeleton->is_inside_tree()) { - ERR_PRINT_ONCE("Skeleton is not inside SceneTree! Cannot execute modification!"); - return; - } - - if (!enabled) { - return; - } - - for (int i = 0; i < modifications.size(); i++) { - if (!modifications[i].is_valid()) { - continue; - } - - if (modifications[i]->get_execution_mode() == p_execution_mode) { - modifications.get(i)->_execute(p_delta); - } - } -} - -void SkeletonModificationStack2D::draw_editor_gizmos() { - if (!is_setup) { - return; - } - - if (editor_gizmo_dirty) { - for (int i = 0; i < modifications.size(); i++) { - if (!modifications[i].is_valid()) { - continue; - } - - if (modifications[i]->editor_draw_gizmo) { - modifications.get(i)->_draw_editor_gizmo(); - } - } - skeleton->draw_set_transform(Vector2(0, 0)); - editor_gizmo_dirty = false; - } -} - -void SkeletonModificationStack2D::set_editor_gizmos_dirty(bool p_dirty) { - if (!is_setup) { - return; - } - - if (!editor_gizmo_dirty && p_dirty) { - editor_gizmo_dirty = p_dirty; - if (skeleton) { - skeleton->queue_redraw(); - } - } else { - editor_gizmo_dirty = p_dirty; - } -} - -void SkeletonModificationStack2D::enable_all_modifications(bool p_enabled) { - for (int i = 0; i < modifications.size(); i++) { - if (!modifications[i].is_valid()) { - continue; - } - modifications.get(i)->set_enabled(p_enabled); - } -} - -Ref SkeletonModificationStack2D::get_modification(int p_mod_idx) const { - ERR_FAIL_INDEX_V(p_mod_idx, modifications.size(), nullptr); - return modifications[p_mod_idx]; -} - -void SkeletonModificationStack2D::add_modification(Ref p_mod) { - ERR_FAIL_COND(!p_mod.is_valid()); - - p_mod->_setup_modification(this); - modifications.push_back(p_mod); - -#ifdef TOOLS_ENABLED - set_editor_gizmos_dirty(true); -#endif // TOOLS_ENABLED -} - -void SkeletonModificationStack2D::delete_modification(int p_mod_idx) { - ERR_FAIL_INDEX(p_mod_idx, modifications.size()); - modifications.remove_at(p_mod_idx); - -#ifdef TOOLS_ENABLED - set_editor_gizmos_dirty(true); -#endif // TOOLS_ENABLED -} - -void SkeletonModificationStack2D::set_modification(int p_mod_idx, Ref p_mod) { - ERR_FAIL_INDEX(p_mod_idx, modifications.size()); - - if (p_mod == nullptr) { - modifications.insert(p_mod_idx, nullptr); - } else { - p_mod->_setup_modification(this); - modifications.insert(p_mod_idx, p_mod); - } - -#ifdef TOOLS_ENABLED - set_editor_gizmos_dirty(true); -#endif // TOOLS_ENABLED -} - -void SkeletonModificationStack2D::set_modification_count(int p_count) { - ERR_FAIL_COND_MSG(p_count < 0, "Modification count cannot be less than zero."); - modifications.resize(p_count); - notify_property_list_changed(); - -#ifdef TOOLS_ENABLED - set_editor_gizmos_dirty(true); -#endif // TOOLS_ENABLED -} - -int SkeletonModificationStack2D::get_modification_count() const { - return modifications.size(); -} - -void SkeletonModificationStack2D::set_skeleton(Skeleton2D *p_skeleton) { - skeleton = p_skeleton; -} - -Skeleton2D *SkeletonModificationStack2D::get_skeleton() const { - return skeleton; -} - -bool SkeletonModificationStack2D::get_is_setup() const { - return is_setup; -} - -void SkeletonModificationStack2D::set_enabled(bool p_enabled) { - enabled = p_enabled; -} - -bool SkeletonModificationStack2D::get_enabled() const { - return enabled; -} - -void SkeletonModificationStack2D::set_strength(float p_strength) { - ERR_FAIL_COND_MSG(p_strength < 0, "Strength cannot be less than zero!"); - ERR_FAIL_COND_MSG(p_strength > 1, "Strength cannot be more than one!"); - strength = p_strength; -} - -float SkeletonModificationStack2D::get_strength() const { - return strength; -} - -void SkeletonModificationStack2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("setup"), &SkeletonModificationStack2D::setup); - ClassDB::bind_method(D_METHOD("execute", "delta", "execution_mode"), &SkeletonModificationStack2D::execute); - - ClassDB::bind_method(D_METHOD("enable_all_modifications", "enabled"), &SkeletonModificationStack2D::enable_all_modifications); - ClassDB::bind_method(D_METHOD("get_modification", "mod_idx"), &SkeletonModificationStack2D::get_modification); - ClassDB::bind_method(D_METHOD("add_modification", "modification"), &SkeletonModificationStack2D::add_modification); - ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack2D::delete_modification); - ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack2D::set_modification); - - ClassDB::bind_method(D_METHOD("set_modification_count", "count"), &SkeletonModificationStack2D::set_modification_count); - ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack2D::get_modification_count); - - ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack2D::get_is_setup); - - ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModificationStack2D::set_enabled); - ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModificationStack2D::get_enabled); - - ClassDB::bind_method(D_METHOD("set_strength", "strength"), &SkeletonModificationStack2D::set_strength); - ClassDB::bind_method(D_METHOD("get_strength"), &SkeletonModificationStack2D::get_strength); - - ClassDB::bind_method(D_METHOD("get_skeleton"), &SkeletonModificationStack2D::get_skeleton); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "set_modification_count", "get_modification_count"); -} - -SkeletonModificationStack2D::SkeletonModificationStack2D() { -} diff --git a/scene/resources/skeleton_modification_stack_2d.h b/scene/resources/skeleton_modification_stack_2d.h deleted file mode 100644 index ceffd71368fd..000000000000 --- a/scene/resources/skeleton_modification_stack_2d.h +++ /dev/null @@ -1,99 +0,0 @@ -/*************************************************************************/ -/* skeleton_modification_stack_2d.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SKELETON_MODIFICATION_STACK_2D_H -#define SKELETON_MODIFICATION_STACK_2D_H - -#include "scene/2d/skeleton_2d.h" -#include "scene/resources/skeleton_modification_2d.h" - -/////////////////////////////////////// -// SkeletonModificationStack2D -/////////////////////////////////////// - -class Skeleton2D; -class SkeletonModification2D; -class Bone2D; - -class SkeletonModificationStack2D : public Resource { - GDCLASS(SkeletonModificationStack2D, Resource); - friend class Skeleton2D; - friend class SkeletonModification2D; - -protected: - static void _bind_methods(); - void _get_property_list(List *p_list) const; - bool _set(const StringName &p_path, const Variant &p_value); - bool _get(const StringName &p_path, Variant &r_ret) const; - -public: - Skeleton2D *skeleton = nullptr; - bool is_setup = false; - bool enabled = false; - float strength = 1.0; - - enum EXECUTION_MODE { - execution_mode_process, - execution_mode_physics_process - }; - - Vector> modifications = Vector>(); - - void setup(); - void execute(float p_delta, int p_execution_mode); - - bool editor_gizmo_dirty = false; - void draw_editor_gizmos(); - void set_editor_gizmos_dirty(bool p_dirty); - - void enable_all_modifications(bool p_enable); - Ref get_modification(int p_mod_idx) const; - void add_modification(Ref p_mod); - void delete_modification(int p_mod_idx); - void set_modification(int p_mod_idx, Ref p_mod); - - void set_modification_count(int p_count); - int get_modification_count() const; - - void set_skeleton(Skeleton2D *p_skeleton); - Skeleton2D *get_skeleton() const; - - bool get_is_setup() const; - - void set_enabled(bool p_enabled); - bool get_enabled() const; - - void set_strength(float p_strength); - float get_strength() const; - - SkeletonModificationStack2D(); -}; - -#endif // SKELETON_MODIFICATION_STACK_2D_H diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp deleted file mode 100644 index 44fbfc934e68..000000000000 --- a/scene/resources/skeleton_modification_stack_3d.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/*************************************************************************/ -/* skeleton_modification_stack_3d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "skeleton_modification_stack_3d.h" -#include "scene/3d/skeleton_3d.h" - -/////////////////////////////////////// -// ModificationStack3D -/////////////////////////////////////// - -void SkeletonModificationStack3D::_get_property_list(List *p_list) const { - for (uint32_t i = 0; i < modifications.size(); i++) { - p_list->push_back( - PropertyInfo(Variant::OBJECT, "modifications/" + itos(i), - PROPERTY_HINT_RESOURCE_TYPE, - "SkeletonModification3D", - PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); - } -} - -bool SkeletonModificationStack3D::_set(const StringName &p_path, const Variant &p_value) { - String path = p_path; - - if (path.begins_with("modifications/")) { - int mod_idx = path.get_slicec('/', 1).to_int(); - set_modification(mod_idx, p_value); - return true; - } - return true; -} - -bool SkeletonModificationStack3D::_get(const StringName &p_path, Variant &r_ret) const { - String path = p_path; - - if (path.begins_with("modifications/")) { - int mod_idx = path.get_slicec('/', 1).to_int(); - r_ret = get_modification(mod_idx); - return true; - } - return true; -} - -void SkeletonModificationStack3D::setup() { - if (is_setup) { - return; - } - - if (skeleton != nullptr) { - is_setup = true; - for (uint32_t i = 0; i < modifications.size(); i++) { - if (!modifications[i].is_valid()) { - continue; - } - modifications[i]->_setup_modification(this); - } - } else { - WARN_PRINT("Cannot setup SkeletonModificationStack3D: no skeleton set!"); - } -} - -void SkeletonModificationStack3D::execute(real_t p_delta, int p_execution_mode) { - ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr || is_queued_for_deletion(), - "Modification stack is not properly setup and therefore cannot execute!"); - - if (!skeleton->is_inside_tree()) { - ERR_PRINT_ONCE("Skeleton is not inside SceneTree! Cannot execute modification!"); - return; - } - - if (!enabled) { - return; - } - - for (uint32_t i = 0; i < modifications.size(); i++) { - if (!modifications[i].is_valid()) { - continue; - } - - if (modifications[i]->get_execution_mode() == p_execution_mode) { - modifications[i]->_execute(p_delta); - } - } -} - -void SkeletonModificationStack3D::enable_all_modifications(bool p_enabled) { - for (uint32_t i = 0; i < modifications.size(); i++) { - if (!modifications[i].is_valid()) { - continue; - } - modifications[i]->set_enabled(p_enabled); - } -} - -Ref SkeletonModificationStack3D::get_modification(int p_mod_idx) const { - const int modifications_size = modifications.size(); - ERR_FAIL_INDEX_V(p_mod_idx, modifications_size, nullptr); - return modifications[p_mod_idx]; -} - -void SkeletonModificationStack3D::add_modification(Ref p_mod) { - ERR_FAIL_NULL(p_mod); - p_mod->_setup_modification(this); - modifications.push_back(p_mod); -} - -void SkeletonModificationStack3D::delete_modification(int p_mod_idx) { - const int modifications_size = modifications.size(); - ERR_FAIL_INDEX(p_mod_idx, modifications_size); - modifications.remove_at(p_mod_idx); -} - -void SkeletonModificationStack3D::set_modification(int p_mod_idx, Ref p_mod) { - const int modifications_size = modifications.size(); - ERR_FAIL_INDEX(p_mod_idx, modifications_size); - - if (p_mod == nullptr) { - modifications.remove_at(p_mod_idx); - } else { - p_mod->_setup_modification(this); - modifications[p_mod_idx] = p_mod; - } -} - -void SkeletonModificationStack3D::set_modification_count(int p_count) { - ERR_FAIL_COND_MSG(p_count < 0, "Modification count cannot be less than zero."); - modifications.resize(p_count); - notify_property_list_changed(); -} - -int SkeletonModificationStack3D::get_modification_count() const { - return modifications.size(); -} - -void SkeletonModificationStack3D::set_skeleton(Skeleton3D *p_skeleton) { - skeleton = p_skeleton; -} - -Skeleton3D *SkeletonModificationStack3D::get_skeleton() const { - return skeleton; -} - -bool SkeletonModificationStack3D::get_is_setup() const { - return is_setup; -} - -void SkeletonModificationStack3D::set_enabled(bool p_enabled) { - enabled = p_enabled; - - if (!enabled && is_setup && skeleton != nullptr) { - skeleton->clear_bones_local_pose_override(); - } -} - -bool SkeletonModificationStack3D::get_enabled() const { - return enabled; -} - -void SkeletonModificationStack3D::set_strength(real_t p_strength) { - ERR_FAIL_COND_MSG(p_strength < 0, "Strength cannot be less than zero!"); - ERR_FAIL_COND_MSG(p_strength > 1, "Strength cannot be more than one!"); - strength = p_strength; -} - -real_t SkeletonModificationStack3D::get_strength() const { - return strength; -} - -void SkeletonModificationStack3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("setup"), &SkeletonModificationStack3D::setup); - ClassDB::bind_method(D_METHOD("execute", "delta", "execution_mode"), &SkeletonModificationStack3D::execute); - - ClassDB::bind_method(D_METHOD("enable_all_modifications", "enabled"), &SkeletonModificationStack3D::enable_all_modifications); - ClassDB::bind_method(D_METHOD("get_modification", "mod_idx"), &SkeletonModificationStack3D::get_modification); - ClassDB::bind_method(D_METHOD("add_modification", "modification"), &SkeletonModificationStack3D::add_modification); - ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack3D::delete_modification); - ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack3D::set_modification); - - ClassDB::bind_method(D_METHOD("set_modification_count", "count"), &SkeletonModificationStack3D::set_modification_count); - ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack3D::get_modification_count); - - ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack3D::get_is_setup); - - ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModificationStack3D::set_enabled); - ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModificationStack3D::get_enabled); - - ClassDB::bind_method(D_METHOD("set_strength", "strength"), &SkeletonModificationStack3D::set_strength); - ClassDB::bind_method(D_METHOD("get_strength"), &SkeletonModificationStack3D::get_strength); - - ClassDB::bind_method(D_METHOD("get_skeleton"), &SkeletonModificationStack3D::get_skeleton); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "set_modification_count", "get_modification_count"); -} - -SkeletonModificationStack3D::SkeletonModificationStack3D() { -} diff --git a/scene/resources/skeleton_modification_stack_3d.h b/scene/resources/skeleton_modification_stack_3d.h deleted file mode 100644 index 4ff1bd1e3352..000000000000 --- a/scene/resources/skeleton_modification_stack_3d.h +++ /dev/null @@ -1,91 +0,0 @@ -/*************************************************************************/ -/* skeleton_modification_stack_3d.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SKELETON_MODIFICATION_STACK_3D_H -#define SKELETON_MODIFICATION_STACK_3D_H - -#include "core/templates/local_vector.h" -#include "scene/3d/skeleton_3d.h" - -class Skeleton3D; -class SkeletonModification3D; - -class SkeletonModificationStack3D : public Resource { - GDCLASS(SkeletonModificationStack3D, Resource); - friend class Skeleton3D; - friend class SkeletonModification3D; - -protected: - static void _bind_methods(); - virtual void _get_property_list(List *p_list) const; - virtual bool _set(const StringName &p_path, const Variant &p_value); - virtual bool _get(const StringName &p_path, Variant &r_ret) const; - -public: - Skeleton3D *skeleton = nullptr; - bool is_setup = false; - bool enabled = false; - real_t strength = 1.0; - - enum EXECUTION_MODE { - execution_mode_process, - execution_mode_physics_process, - }; - - LocalVector> modifications = LocalVector>(); - int modifications_count = 0; - - virtual void setup(); - virtual void execute(real_t p_delta, int p_execution_mode); - - void enable_all_modifications(bool p_enable); - Ref get_modification(int p_mod_idx) const; - void add_modification(Ref p_mod); - void delete_modification(int p_mod_idx); - void set_modification(int p_mod_idx, Ref p_mod); - - void set_modification_count(int p_count); - int get_modification_count() const; - - void set_skeleton(Skeleton3D *p_skeleton); - Skeleton3D *get_skeleton() const; - - bool get_is_setup() const; - - void set_enabled(bool p_enabled); - bool get_enabled() const; - - void set_strength(real_t p_strength); - real_t get_strength() const; - - SkeletonModificationStack3D(); -}; - -#endif // SKELETON_MODIFICATION_STACK_3D_H