initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
16
scene/resources/2d/skeleton/SCsub
Normal file
16
scene/resources/2d/skeleton/SCsub
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.scene_sources, "skeleton_modification_2d.cpp")
|
||||
env.add_source_files(env.scene_sources, "skeleton_modification_2d_ccdik.cpp")
|
||||
env.add_source_files(env.scene_sources, "skeleton_modification_2d_fabrik.cpp")
|
||||
env.add_source_files(env.scene_sources, "skeleton_modification_2d_lookat.cpp")
|
||||
env.add_source_files(env.scene_sources, "skeleton_modification_2d_stackholder.cpp")
|
||||
env.add_source_files(env.scene_sources, "skeleton_modification_2d_twoboneik.cpp")
|
||||
env.add_source_files(env.scene_sources, "skeleton_modification_stack_2d.cpp")
|
||||
|
||||
if not env["disable_physics_2d"]:
|
||||
env.add_source_files(env.scene_sources, "skeleton_modification_2d_jiggle.cpp")
|
||||
env.add_source_files(env.scene_sources, "skeleton_modification_2d_physicalbones.cpp")
|
240
scene/resources/2d/skeleton/skeleton_modification_2d.cpp
Normal file
240
scene/resources/2d/skeleton/skeleton_modification_2d.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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.h"
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/settings/editor_settings.h"
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
///////////////////////////////////////
|
||||
// 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<SkeletonModificationStack2D>(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() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
float SkeletonModification2D::clamp_angle(float p_angle, float p_min_bound, float p_max_bound, bool p_invert) {
|
||||
// Map to the 0 to 360 range (in radians though) instead of the -180 to 180 range.
|
||||
if (p_angle < 0) {
|
||||
p_angle = Math::TAU + p_angle;
|
||||
}
|
||||
|
||||
// Make min and max in the range of 0 to 360 (in radians), and make sure they are in the right order
|
||||
if (p_min_bound < 0) {
|
||||
p_min_bound = Math::TAU + p_min_bound;
|
||||
}
|
||||
if (p_max_bound < 0) {
|
||||
p_max_bound = Math::TAU + p_max_bound;
|
||||
}
|
||||
if (p_min_bound > p_max_bound) {
|
||||
SWAP(p_min_bound, p_max_bound);
|
||||
}
|
||||
|
||||
bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound);
|
||||
bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound);
|
||||
|
||||
// Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle.
|
||||
if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) {
|
||||
Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
|
||||
Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
|
||||
Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
|
||||
|
||||
if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
|
||||
p_angle = p_min_bound;
|
||||
} else {
|
||||
p_angle = p_max_bound;
|
||||
}
|
||||
}
|
||||
|
||||
return p_angle;
|
||||
}
|
||||
|
||||
void SkeletonModification2D::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) {
|
||||
if (!p_operation_bone) {
|
||||
return;
|
||||
}
|
||||
|
||||
Color bone_ik_color = Color(1.0, 0.65, 0.0, 0.4);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
bone_ik_color = EDITOR_GET("editors/2d/bone_ik_color");
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
float arc_angle_min = p_min_bound;
|
||||
float arc_angle_max = p_max_bound;
|
||||
if (arc_angle_min < 0) {
|
||||
arc_angle_min = (Math::PI * 2) + arc_angle_min;
|
||||
}
|
||||
if (arc_angle_max < 0) {
|
||||
arc_angle_max = (Math::PI * 2) + arc_angle_max;
|
||||
}
|
||||
if (arc_angle_min > arc_angle_max) {
|
||||
SWAP(arc_angle_min, arc_angle_max);
|
||||
}
|
||||
arc_angle_min += p_operation_bone->get_bone_angle();
|
||||
arc_angle_max += p_operation_bone->get_bone_angle();
|
||||
|
||||
if (p_constraint_enabled) {
|
||||
if (p_constraint_in_localspace) {
|
||||
Node *operation_bone_parent = p_operation_bone->get_parent();
|
||||
Bone2D *operation_bone_parent_bone = Object::cast_to<Bone2D>(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());
|
||||
} else {
|
||||
stack->skeleton->draw_set_transform(stack->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()));
|
||||
}
|
||||
|
||||
if (p_constraint_inverted) {
|
||||
stack->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(),
|
||||
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);
|
||||
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<SkeletonModificationStack2D> SkeletonModification2D::get_modification_stack() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
void SkeletonModification2D::set_is_setup(bool p_setup) {
|
||||
is_setup = p_setup;
|
||||
}
|
||||
|
||||
bool SkeletonModification2D::get_is_setup() const {
|
||||
return is_setup;
|
||||
}
|
||||
|
||||
void SkeletonModification2D::set_execution_mode(int p_mode) {
|
||||
execution_mode = p_mode;
|
||||
}
|
||||
|
||||
int SkeletonModification2D::get_execution_mode() const {
|
||||
return execution_mode;
|
||||
}
|
||||
|
||||
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 {
|
||||
return editor_draw_gizmo;
|
||||
}
|
||||
|
||||
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);
|
||||
ClassDB::bind_method(D_METHOD("get_execution_mode"), &SkeletonModification2D::get_execution_mode);
|
||||
ClassDB::bind_method(D_METHOD("clamp_angle", "angle", "min", "max", "invert"), &SkeletonModification2D::clamp_angle);
|
||||
ClassDB::bind_method(D_METHOD("set_editor_draw_gizmo", "draw_gizmo"), &SkeletonModification2D::set_editor_draw_gizmo);
|
||||
ClassDB::bind_method(D_METHOD("get_editor_draw_gizmo"), &SkeletonModification2D::get_editor_draw_gizmo);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
void SkeletonModification2D::reset_state() {
|
||||
stack = nullptr;
|
||||
is_setup = false;
|
||||
}
|
||||
|
||||
SkeletonModification2D::SkeletonModification2D() {
|
||||
stack = nullptr;
|
||||
is_setup = false;
|
||||
}
|
86
scene/resources/2d/skeleton/skeleton_modification_2d.h
Normal file
86
scene/resources/2d/skeleton/skeleton_modification_2d.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/resources/2d/skeleton/skeleton_modification_stack_2d.h"
|
||||
|
||||
///////////////////////////////////////
|
||||
// SkeletonModification2D
|
||||
///////////////////////////////////////
|
||||
|
||||
class Bone2D;
|
||||
|
||||
class SkeletonModification2D : public Resource {
|
||||
GDCLASS(SkeletonModification2D, Resource);
|
||||
friend class Skeleton2D;
|
||||
friend class Bone2D;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
SkeletonModificationStack2D *stack = nullptr;
|
||||
int execution_mode = 0; // 0 = process
|
||||
|
||||
bool enabled = true;
|
||||
bool is_setup = false;
|
||||
|
||||
bool _print_execution_error(bool p_condition, String p_message);
|
||||
|
||||
virtual void reset_state() override;
|
||||
|
||||
GDVIRTUAL1(_execute, double)
|
||||
GDVIRTUAL1(_setup_modification, Ref<SkeletonModificationStack2D>)
|
||||
GDVIRTUAL0(_draw_editor_gizmo)
|
||||
|
||||
public:
|
||||
virtual void _execute(float _delta);
|
||||
virtual void _setup_modification(SkeletonModificationStack2D *p_stack);
|
||||
virtual void _draw_editor_gizmo();
|
||||
|
||||
bool editor_draw_gizmo = false;
|
||||
void set_editor_draw_gizmo(bool p_draw_gizmo);
|
||||
bool get_editor_draw_gizmo() const;
|
||||
|
||||
void set_enabled(bool p_enabled);
|
||||
bool get_enabled();
|
||||
|
||||
Ref<SkeletonModificationStack2D> get_modification_stack();
|
||||
void set_is_setup(bool p_setup);
|
||||
bool get_is_setup() const;
|
||||
|
||||
void set_execution_mode(int p_mode);
|
||||
int get_execution_mode() const;
|
||||
|
||||
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();
|
||||
};
|
550
scene/resources/2d/skeleton/skeleton_modification_2d_ccdik.cpp
Normal file
550
scene/resources/2d/skeleton/skeleton_modification_2d_ccdik.cpp
Normal file
@@ -0,0 +1,550 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_ccdik.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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_ccdik.h"
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
|
||||
bool SkeletonModification2DCCDIK::_set(const StringName &p_path, const Variant &p_value) {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("joint_data/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, ccdik_data_chain.size(), false);
|
||||
|
||||
if (what == "bone2d_node") {
|
||||
set_ccdik_joint_bone2d_node(which, p_value);
|
||||
} else if (what == "bone_index") {
|
||||
set_ccdik_joint_bone_index(which, p_value);
|
||||
} else if (what == "rotate_from_joint") {
|
||||
set_ccdik_joint_rotate_from_joint(which, p_value);
|
||||
} else if (what == "enable_constraint") {
|
||||
set_ccdik_joint_enable_constraint(which, p_value);
|
||||
} else if (what == "constraint_angle_min") {
|
||||
set_ccdik_joint_constraint_angle_min(which, Math::deg_to_rad(float(p_value)));
|
||||
} else if (what == "constraint_angle_max") {
|
||||
set_ccdik_joint_constraint_angle_max(which, Math::deg_to_rad(float(p_value)));
|
||||
} else if (what == "constraint_angle_invert") {
|
||||
set_ccdik_joint_constraint_angle_invert(which, p_value);
|
||||
} else if (what == "constraint_in_localspace") {
|
||||
set_ccdik_joint_constraint_in_localspace(which, p_value);
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
else if (what.begins_with("editor_draw_gizmo")) {
|
||||
set_ccdik_joint_editor_draw_gizmo(which, p_value);
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
else if (path.begins_with("editor/draw_gizmo")) {
|
||||
set_editor_draw_gizmo(p_value);
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkeletonModification2DCCDIK::_get(const StringName &p_path, Variant &r_ret) const {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("joint_data/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, ccdik_data_chain.size(), false);
|
||||
|
||||
if (what == "bone2d_node") {
|
||||
r_ret = get_ccdik_joint_bone2d_node(which);
|
||||
} else if (what == "bone_index") {
|
||||
r_ret = get_ccdik_joint_bone_index(which);
|
||||
} else if (what == "rotate_from_joint") {
|
||||
r_ret = get_ccdik_joint_rotate_from_joint(which);
|
||||
} else if (what == "enable_constraint") {
|
||||
r_ret = get_ccdik_joint_enable_constraint(which);
|
||||
} else if (what == "constraint_angle_min") {
|
||||
r_ret = Math::rad_to_deg(get_ccdik_joint_constraint_angle_min(which));
|
||||
} else if (what == "constraint_angle_max") {
|
||||
r_ret = Math::rad_to_deg(get_ccdik_joint_constraint_angle_max(which));
|
||||
} else if (what == "constraint_angle_invert") {
|
||||
r_ret = get_ccdik_joint_constraint_angle_invert(which);
|
||||
} else if (what == "constraint_in_localspace") {
|
||||
r_ret = get_ccdik_joint_constraint_in_localspace(which);
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
else if (what.begins_with("editor_draw_gizmo")) {
|
||||
r_ret = get_ccdik_joint_editor_draw_gizmo(which);
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
else if (path.begins_with("editor/draw_gizmo")) {
|
||||
r_ret = get_editor_draw_gizmo();
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkeletonModification2DCCDIK::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
for (int i = 0; i < ccdik_data_chain.size(); i++) {
|
||||
String base_string = "joint_data/" + itos(i) + "/";
|
||||
|
||||
p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "rotate_from_joint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "enable_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
if (ccdik_data_chain[i].enable_constraint) {
|
||||
p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "constraint_angle_min", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "constraint_angle_max", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "constraint_angle_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "constraint_in_localspace", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "editor_draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
}
|
||||
|
||||
#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 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 = ObjectDB::get_instance<Node2D>(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 = ObjectDB::get_instance<Node2D>(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()) {
|
||||
ERR_PRINT_ONCE("2D CCDIK joint: bone index not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
Bone2D *operation_bone = stack->skeleton->get_bone(ccdik_data.bone_idx);
|
||||
Transform2D operation_transform = operation_bone->get_global_transform();
|
||||
|
||||
if (ccdik_data.rotate_from_joint) {
|
||||
// To rotate from the joint, simply look at the target!
|
||||
operation_transform.set_rotation(
|
||||
operation_transform.looking_at(p_target->get_global_position()).get_rotation() - operation_bone->get_bone_angle());
|
||||
} else {
|
||||
// How to rotate from the tip: get the difference of rotation needed from the tip to the target, from the perspective of the joint.
|
||||
// Because we are only using the offset, we do not need to account for the bone angle of the Bone2D node.
|
||||
float joint_to_tip = p_tip->get_global_position().angle_to_point(operation_transform.get_origin());
|
||||
float joint_to_target = p_target->get_global_position().angle_to_point(operation_transform.get_origin());
|
||||
operation_transform.set_rotation(
|
||||
operation_transform.get_rotation() + (joint_to_target - joint_to_tip));
|
||||
}
|
||||
|
||||
// Reset scale
|
||||
operation_transform.set_scale(operation_bone->get_global_scale());
|
||||
|
||||
// Apply constraints in globalspace:
|
||||
if (ccdik_data.enable_constraint && !ccdik_data.constraint_in_localspace) {
|
||||
operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), ccdik_data.constraint_angle_min, ccdik_data.constraint_angle_max, ccdik_data.constraint_angle_invert));
|
||||
}
|
||||
|
||||
// Convert from a global transform to a delta and then apply the delta to the local transform.
|
||||
operation_bone->set_global_transform(operation_transform);
|
||||
operation_transform = operation_bone->get_transform();
|
||||
|
||||
// Apply constraints in localspace:
|
||||
if (ccdik_data.enable_constraint && ccdik_data.constraint_in_localspace) {
|
||||
operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), ccdik_data.constraint_angle_min, ccdik_data.constraint_angle_max, ccdik_data.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(ccdik_data.bone_idx, operation_transform, stack->strength, 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;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ccdik_data_chain.size(); i++) {
|
||||
if (!ccdik_data_chain[i].editor_draw_gizmo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Bone2D *operation_bone = stack->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,
|
||||
"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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
"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!");
|
||||
tip_node_cache = node->get_instance_id();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
"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!");
|
||||
ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id();
|
||||
|
||||
Bone2D *bone = Object::cast_to<Bone2D>(node);
|
||||
if (bone) {
|
||||
ccdik_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton();
|
||||
} else {
|
||||
ERR_FAIL_MSG("CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModification2DCCDIK::set_target_node(const NodePath &p_target_node) {
|
||||
target_node = p_target_node;
|
||||
update_target_cache();
|
||||
}
|
||||
|
||||
NodePath SkeletonModification2DCCDIK::get_target_node() const {
|
||||
return target_node;
|
||||
}
|
||||
|
||||
void SkeletonModification2DCCDIK::set_tip_node(const NodePath &p_tip_node) {
|
||||
tip_node = p_tip_node;
|
||||
update_tip_cache();
|
||||
}
|
||||
|
||||
NodePath SkeletonModification2DCCDIK::get_tip_node() const {
|
||||
return tip_node;
|
||||
}
|
||||
|
||||
void SkeletonModification2DCCDIK::set_ccdik_data_chain_length(int p_length) {
|
||||
ccdik_data_chain.resize(p_length);
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
int SkeletonModification2DCCDIK::get_ccdik_data_chain_length() {
|
||||
return ccdik_data_chain.size();
|
||||
}
|
||||
|
||||
void SkeletonModification2DCCDIK::set_ccdik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node) {
|
||||
ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!");
|
||||
ccdik_data_chain.write[p_joint_idx].bone2d_node = p_target_node;
|
||||
ccdik_joint_update_bone2d_cache(p_joint_idx);
|
||||
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
NodePath SkeletonModification2DCCDIK::get_ccdik_joint_bone2d_node(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), NodePath(), "CCDIK joint out of range!");
|
||||
return ccdik_data_chain[p_joint_idx].bone2d_node;
|
||||
}
|
||||
|
||||
void SkeletonModification2DCCDIK::set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx) {
|
||||
ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCCDIK joint out of range!");
|
||||
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!");
|
||||
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));
|
||||
} 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;
|
||||
}
|
||||
} else {
|
||||
ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx;
|
||||
}
|
||||
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
int SkeletonModification2DCCDIK::get_ccdik_joint_bone_index(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), -1, "CCDIK joint out of range!");
|
||||
return ccdik_data_chain[p_joint_idx].bone_idx;
|
||||
}
|
||||
|
||||
void SkeletonModification2DCCDIK::set_ccdik_joint_rotate_from_joint(int p_joint_idx, bool p_rotate_from_joint) {
|
||||
ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!");
|
||||
ccdik_data_chain.write[p_joint_idx].rotate_from_joint = p_rotate_from_joint;
|
||||
}
|
||||
|
||||
bool SkeletonModification2DCCDIK::get_ccdik_joint_rotate_from_joint(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!");
|
||||
return ccdik_data_chain[p_joint_idx].rotate_from_joint;
|
||||
}
|
||||
|
||||
void SkeletonModification2DCCDIK::set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_constraint) {
|
||||
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 {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!");
|
||||
return ccdik_data_chain[p_joint_idx].enable_constraint;
|
||||
}
|
||||
|
||||
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 {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), 0.0, "CCDIK joint out of range!");
|
||||
return ccdik_data_chain[p_joint_idx].constraint_angle_min;
|
||||
}
|
||||
|
||||
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 {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), 0.0, "CCDIK joint out of range!");
|
||||
return ccdik_data_chain[p_joint_idx].constraint_angle_max;
|
||||
}
|
||||
|
||||
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 {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!");
|
||||
return ccdik_data_chain[p_joint_idx].constraint_angle_invert;
|
||||
}
|
||||
|
||||
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 {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!");
|
||||
return ccdik_data_chain[p_joint_idx].constraint_in_localspace;
|
||||
}
|
||||
|
||||
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 {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!");
|
||||
return ccdik_data_chain[p_joint_idx].editor_draw_gizmo;
|
||||
}
|
||||
|
||||
void SkeletonModification2DCCDIK::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DCCDIK::set_target_node);
|
||||
ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DCCDIK::get_target_node);
|
||||
ClassDB::bind_method(D_METHOD("set_tip_node", "tip_nodepath"), &SkeletonModification2DCCDIK::set_tip_node);
|
||||
ClassDB::bind_method(D_METHOD("get_tip_node"), &SkeletonModification2DCCDIK::get_tip_node);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_ccdik_data_chain_length", "length"), &SkeletonModification2DCCDIK::set_ccdik_data_chain_length);
|
||||
ClassDB::bind_method(D_METHOD("get_ccdik_data_chain_length"), &SkeletonModification2DCCDIK::get_ccdik_data_chain_length);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone2d_node", "joint_idx", "bone2d_nodepath"), &SkeletonModification2DCCDIK::set_ccdik_joint_bone2d_node);
|
||||
ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone2d_node", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_bone2d_node);
|
||||
ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DCCDIK::set_ccdik_joint_bone_index);
|
||||
ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone_index", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_bone_index);
|
||||
ClassDB::bind_method(D_METHOD("set_ccdik_joint_rotate_from_joint", "joint_idx", "rotate_from_joint"), &SkeletonModification2DCCDIK::set_ccdik_joint_rotate_from_joint);
|
||||
ClassDB::bind_method(D_METHOD("get_ccdik_joint_rotate_from_joint", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_rotate_from_joint);
|
||||
ClassDB::bind_method(D_METHOD("set_ccdik_joint_enable_constraint", "joint_idx", "enable_constraint"), &SkeletonModification2DCCDIK::set_ccdik_joint_enable_constraint);
|
||||
ClassDB::bind_method(D_METHOD("get_ccdik_joint_enable_constraint", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_enable_constraint);
|
||||
ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_min", "joint_idx", "angle_min"), &SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_min);
|
||||
ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_min", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_min);
|
||||
ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_max", "joint_idx", "angle_max"), &SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_max);
|
||||
ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_max", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_max);
|
||||
ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_invert", "joint_idx", "invert"), &SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_invert);
|
||||
ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_invert", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_invert);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "tip_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_tip_node", "get_tip_node");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "ccdik_data_chain_length", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_ccdik_data_chain_length", "get_ccdik_data_chain_length");
|
||||
}
|
||||
|
||||
SkeletonModification2DCCDIK::SkeletonModification2DCCDIK() {
|
||||
stack = nullptr;
|
||||
is_setup = false;
|
||||
enabled = true;
|
||||
editor_draw_gizmo = true;
|
||||
}
|
||||
|
||||
SkeletonModification2DCCDIK::~SkeletonModification2DCCDIK() {
|
||||
}
|
113
scene/resources/2d/skeleton/skeleton_modification_2d_ccdik.h
Normal file
113
scene/resources/2d/skeleton/skeleton_modification_2d_ccdik.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_ccdik.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
#include "scene/resources/2d/skeleton/skeleton_modification_2d.h"
|
||||
|
||||
///////////////////////////////////////
|
||||
// SkeletonModification2DCCDIK
|
||||
///////////////////////////////////////
|
||||
|
||||
class SkeletonModification2DCCDIK : public SkeletonModification2D {
|
||||
GDCLASS(SkeletonModification2DCCDIK, SkeletonModification2D);
|
||||
|
||||
private:
|
||||
struct CCDIK_Joint_Data2D {
|
||||
int bone_idx = -1;
|
||||
NodePath bone2d_node;
|
||||
ObjectID bone2d_node_cache;
|
||||
bool rotate_from_joint = false;
|
||||
|
||||
bool enable_constraint = false;
|
||||
float constraint_angle_min = 0;
|
||||
float constraint_angle_max = (2.0 * Math::PI);
|
||||
bool constraint_angle_invert = false;
|
||||
bool constraint_in_localspace = true;
|
||||
|
||||
bool editor_draw_gizmo = true;
|
||||
};
|
||||
|
||||
Vector<CCDIK_Joint_Data2D> ccdik_data_chain;
|
||||
|
||||
NodePath target_node;
|
||||
ObjectID target_node_cache;
|
||||
void update_target_cache();
|
||||
|
||||
NodePath tip_node;
|
||||
ObjectID tip_node_cache;
|
||||
void update_tip_cache();
|
||||
|
||||
void ccdik_joint_update_bone2d_cache(int p_joint_idx);
|
||||
void _execute_ccdik_joint(int p_joint_idx, Node2D *p_target, Node2D *p_tip);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
bool _set(const StringName &p_path, const Variant &p_value);
|
||||
bool _get(const StringName &p_path, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
public:
|
||||
void _execute(float p_delta) override;
|
||||
void _setup_modification(SkeletonModificationStack2D *p_stack) override;
|
||||
void _draw_editor_gizmo() override;
|
||||
|
||||
void set_target_node(const NodePath &p_target_node);
|
||||
NodePath get_target_node() const;
|
||||
void set_tip_node(const NodePath &p_tip_node);
|
||||
NodePath get_tip_node() const;
|
||||
|
||||
int get_ccdik_data_chain_length();
|
||||
void set_ccdik_data_chain_length(int p_new_length);
|
||||
|
||||
void set_ccdik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node);
|
||||
NodePath get_ccdik_joint_bone2d_node(int p_joint_idx) const;
|
||||
void set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx);
|
||||
int get_ccdik_joint_bone_index(int p_joint_idx) const;
|
||||
|
||||
void set_ccdik_joint_rotate_from_joint(int p_joint_idx, bool p_rotate_from_joint);
|
||||
bool get_ccdik_joint_rotate_from_joint(int p_joint_idx) const;
|
||||
void set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_constraint);
|
||||
bool get_ccdik_joint_enable_constraint(int p_joint_idx) const;
|
||||
void set_ccdik_joint_constraint_angle_min(int p_joint_idx, float p_angle_min);
|
||||
float get_ccdik_joint_constraint_angle_min(int p_joint_idx) const;
|
||||
void set_ccdik_joint_constraint_angle_max(int p_joint_idx, float p_angle_max);
|
||||
float get_ccdik_joint_constraint_angle_max(int p_joint_idx) const;
|
||||
void set_ccdik_joint_constraint_angle_invert(int p_joint_idx, bool p_invert);
|
||||
bool get_ccdik_joint_constraint_angle_invert(int p_joint_idx) const;
|
||||
void set_ccdik_joint_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace);
|
||||
bool get_ccdik_joint_constraint_in_localspace(int p_joint_idx) const;
|
||||
void set_ccdik_joint_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo);
|
||||
bool get_ccdik_joint_editor_draw_gizmo(int p_joint_idx) const;
|
||||
|
||||
SkeletonModification2DCCDIK();
|
||||
~SkeletonModification2DCCDIK();
|
||||
};
|
457
scene/resources/2d/skeleton/skeleton_modification_2d_fabrik.cpp
Normal file
457
scene/resources/2d/skeleton/skeleton_modification_2d_fabrik.cpp
Normal file
@@ -0,0 +1,457 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_fabrik.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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_fabrik.h"
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
|
||||
bool SkeletonModification2DFABRIK::_set(const StringName &p_path, const Variant &p_value) {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("joint_data/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, fabrik_data_chain.size(), false);
|
||||
|
||||
if (what == "bone2d_node") {
|
||||
set_fabrik_joint_bone2d_node(which, p_value);
|
||||
} else if (what == "bone_index") {
|
||||
set_fabrik_joint_bone_index(which, p_value);
|
||||
} else if (what == "magnet_position") {
|
||||
set_fabrik_joint_magnet_position(which, p_value);
|
||||
} else if (what == "use_target_rotation") {
|
||||
set_fabrik_joint_use_target_rotation(which, p_value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkeletonModification2DFABRIK::_get(const StringName &p_path, Variant &r_ret) const {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("joint_data/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, fabrik_data_chain.size(), false);
|
||||
|
||||
if (what == "bone2d_node") {
|
||||
r_ret = get_fabrik_joint_bone2d_node(which);
|
||||
} else if (what == "bone_index") {
|
||||
r_ret = get_fabrik_joint_bone_index(which);
|
||||
} else if (what == "magnet_position") {
|
||||
r_ret = get_fabrik_joint_magnet_position(which);
|
||||
} else if (what == "use_target_rotation") {
|
||||
r_ret = get_fabrik_joint_use_target_rotation(which);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkeletonModification2DFABRIK::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
for (int i = 0; i < fabrik_data_chain.size(); i++) {
|
||||
String base_string = "joint_data/" + itos(i) + "/";
|
||||
|
||||
p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", PROPERTY_USAGE_DEFAULT));
|
||||
|
||||
if (i > 0) {
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR2, base_string + "magnet_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
}
|
||||
if (i == fabrik_data_chain.size() - 1) {
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_target_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = ObjectDB::get_instance<Node2D>(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 = ObjectDB::get_instance<Bone2D>(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 = ObjectDB::get_instance<Bone2D>(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 = ObjectDB::get_instance<Bone2D>(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 = ObjectDB::get_instance<Bone2D>(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 = ObjectDB::get_instance<Bone2D>(fabrik_data_chain[final_joint_index].bone2d_node_cache);
|
||||
Transform2D final_bone2d_trans = fabrik_transform_chain[final_joint_index];
|
||||
|
||||
// Apply magnet position
|
||||
if (final_joint_index != 0) {
|
||||
final_bone2d_trans.set_origin(final_bone2d_trans.get_origin() + fabrik_data_chain[final_joint_index].magnet_position);
|
||||
}
|
||||
|
||||
// Set the rotation of the tip bone
|
||||
final_bone2d_trans = final_bone2d_trans.looking_at(target_global_pose.get_origin());
|
||||
|
||||
// Set the position of the tip bone
|
||||
float final_bone2d_angle = final_bone2d_trans.get_rotation();
|
||||
if (fabrik_data_chain[final_joint_index].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);
|
||||
final_bone2d_trans.set_origin(target_global_pose.get_origin() - (final_bone2d_direction * final_bone2d_length));
|
||||
|
||||
// Save the transform
|
||||
fabrik_transform_chain.write[final_joint_index] = final_bone2d_trans;
|
||||
|
||||
int i = final_joint_index;
|
||||
while (i >= 1) {
|
||||
Transform2D previous_pose = fabrik_transform_chain[i];
|
||||
i -= 1;
|
||||
Bone2D *current_bone2d_node = ObjectDB::get_instance<Bone2D>(fabrik_data_chain[i].bone2d_node_cache);
|
||||
Transform2D current_pose = fabrik_transform_chain[i];
|
||||
|
||||
// Apply magnet position
|
||||
if (i != 0) {
|
||||
current_pose.set_origin(current_pose.get_origin() + fabrik_data_chain[i].magnet_position);
|
||||
}
|
||||
|
||||
float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y);
|
||||
float length = current_bone2d_node_length / (current_pose.get_origin().distance_to(previous_pose.get_origin()));
|
||||
Vector2 finish_position = previous_pose.get_origin().lerp(current_pose.get_origin(), length);
|
||||
current_pose.set_origin(finish_position);
|
||||
|
||||
// Save the transform
|
||||
fabrik_transform_chain.write[i] = current_pose;
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModification2DFABRIK::chain_forwards() {
|
||||
Transform2D origin_bone2d_trans = fabrik_transform_chain[0];
|
||||
origin_bone2d_trans.set_origin(origin_global_pose.get_origin());
|
||||
// Save the position
|
||||
fabrik_transform_chain.write[0] = origin_bone2d_trans;
|
||||
|
||||
for (int i = 0; i < fabrik_data_chain.size() - 1; i++) {
|
||||
Bone2D *current_bone2d_node = ObjectDB::get_instance<Bone2D>(fabrik_data_chain[i].bone2d_node_cache);
|
||||
Transform2D current_pose = fabrik_transform_chain[i];
|
||||
Transform2D next_pose = fabrik_transform_chain[i + 1];
|
||||
|
||||
float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y);
|
||||
float length = current_bone2d_node_length / (next_pose.get_origin().distance_to(current_pose.get_origin()));
|
||||
Vector2 finish_position = current_pose.get_origin().lerp(next_pose.get_origin(), length);
|
||||
current_pose.set_origin(finish_position);
|
||||
|
||||
// Apply to the bone
|
||||
fabrik_transform_chain.write[i + 1] = current_pose;
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModification2DFABRIK::_setup_modification(SkeletonModificationStack2D *p_stack) {
|
||||
stack = p_stack;
|
||||
|
||||
if (stack != nullptr) {
|
||||
is_setup = true;
|
||||
|
||||
if (stack->skeleton) {
|
||||
for (int i = 0; i < fabrik_data_chain.size(); i++) {
|
||||
fabrik_joint_update_bone2d_cache(i);
|
||||
}
|
||||
}
|
||||
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,
|
||||
"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 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,
|
||||
"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!");
|
||||
fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id();
|
||||
|
||||
Bone2D *bone = Object::cast_to<Bone2D>(node);
|
||||
if (bone) {
|
||||
fabrik_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton();
|
||||
} else {
|
||||
ERR_FAIL_MSG("FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModification2DFABRIK::set_target_node(const NodePath &p_target_node) {
|
||||
target_node = p_target_node;
|
||||
update_target_cache();
|
||||
}
|
||||
|
||||
NodePath SkeletonModification2DFABRIK::get_target_node() const {
|
||||
return target_node;
|
||||
}
|
||||
|
||||
void SkeletonModification2DFABRIK::set_fabrik_data_chain_length(int p_length) {
|
||||
fabrik_data_chain.resize(p_length);
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
int SkeletonModification2DFABRIK::get_fabrik_data_chain_length() {
|
||||
return fabrik_data_chain.size();
|
||||
}
|
||||
|
||||
void SkeletonModification2DFABRIK::set_fabrik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node) {
|
||||
ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!");
|
||||
fabrik_data_chain.write[p_joint_idx].bone2d_node = p_target_node;
|
||||
fabrik_joint_update_bone2d_cache(p_joint_idx);
|
||||
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
NodePath SkeletonModification2DFABRIK::get_fabrik_joint_bone2d_node(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), NodePath(), "FABRIK joint out of range!");
|
||||
return fabrik_data_chain[p_joint_idx].bone2d_node;
|
||||
}
|
||||
|
||||
void SkeletonModification2DFABRIK::set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx) {
|
||||
ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!");
|
||||
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!");
|
||||
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));
|
||||
} 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;
|
||||
}
|
||||
} else {
|
||||
fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx;
|
||||
}
|
||||
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
int SkeletonModification2DFABRIK::get_fabrik_joint_bone_index(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), -1, "FABRIK joint out of range!");
|
||||
return fabrik_data_chain[p_joint_idx].bone_idx;
|
||||
}
|
||||
|
||||
void SkeletonModification2DFABRIK::set_fabrik_joint_magnet_position(int p_joint_idx, Vector2 p_magnet_position) {
|
||||
ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!");
|
||||
fabrik_data_chain.write[p_joint_idx].magnet_position = p_magnet_position;
|
||||
}
|
||||
|
||||
Vector2 SkeletonModification2DFABRIK::get_fabrik_joint_magnet_position(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), Vector2(), "FABRIK joint out of range!");
|
||||
return fabrik_data_chain[p_joint_idx].magnet_position;
|
||||
}
|
||||
|
||||
void SkeletonModification2DFABRIK::set_fabrik_joint_use_target_rotation(int p_joint_idx, bool p_use_target_rotation) {
|
||||
ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!");
|
||||
fabrik_data_chain.write[p_joint_idx].use_target_rotation = p_use_target_rotation;
|
||||
}
|
||||
|
||||
bool SkeletonModification2DFABRIK::get_fabrik_joint_use_target_rotation(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), false, "FABRIK joint out of range!");
|
||||
return fabrik_data_chain[p_joint_idx].use_target_rotation;
|
||||
}
|
||||
|
||||
void SkeletonModification2DFABRIK::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DFABRIK::set_target_node);
|
||||
ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DFABRIK::get_target_node);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_fabrik_data_chain_length", "length"), &SkeletonModification2DFABRIK::set_fabrik_data_chain_length);
|
||||
ClassDB::bind_method(D_METHOD("get_fabrik_data_chain_length"), &SkeletonModification2DFABRIK::get_fabrik_data_chain_length);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone2d_node", "joint_idx", "bone2d_nodepath"), &SkeletonModification2DFABRIK::set_fabrik_joint_bone2d_node);
|
||||
ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone2d_node", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_bone2d_node);
|
||||
ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DFABRIK::set_fabrik_joint_bone_index);
|
||||
ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone_index", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_bone_index);
|
||||
ClassDB::bind_method(D_METHOD("set_fabrik_joint_magnet_position", "joint_idx", "magnet_position"), &SkeletonModification2DFABRIK::set_fabrik_joint_magnet_position);
|
||||
ClassDB::bind_method(D_METHOD("get_fabrik_joint_magnet_position", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_magnet_position);
|
||||
ClassDB::bind_method(D_METHOD("set_fabrik_joint_use_target_rotation", "joint_idx", "use_target_rotation"), &SkeletonModification2DFABRIK::set_fabrik_joint_use_target_rotation);
|
||||
ClassDB::bind_method(D_METHOD("get_fabrik_joint_use_target_rotation", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_use_target_rotation);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "fabrik_data_chain_length", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_fabrik_data_chain_length", "get_fabrik_data_chain_length");
|
||||
}
|
||||
|
||||
SkeletonModification2DFABRIK::SkeletonModification2DFABRIK() {
|
||||
stack = nullptr;
|
||||
is_setup = false;
|
||||
enabled = true;
|
||||
editor_draw_gizmo = false;
|
||||
}
|
||||
|
||||
SkeletonModification2DFABRIK::~SkeletonModification2DFABRIK() {
|
||||
}
|
105
scene/resources/2d/skeleton/skeleton_modification_2d_fabrik.h
Normal file
105
scene/resources/2d/skeleton/skeleton_modification_2d_fabrik.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_fabrik.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
#include "scene/resources/2d/skeleton/skeleton_modification_2d.h"
|
||||
|
||||
///////////////////////////////////////
|
||||
// SkeletonModification2DFABRIK
|
||||
///////////////////////////////////////
|
||||
|
||||
class SkeletonModification2DFABRIK : public SkeletonModification2D {
|
||||
GDCLASS(SkeletonModification2DFABRIK, SkeletonModification2D);
|
||||
|
||||
private:
|
||||
struct FABRIK_Joint_Data2D {
|
||||
int bone_idx = -1;
|
||||
NodePath bone2d_node;
|
||||
ObjectID bone2d_node_cache;
|
||||
|
||||
Vector2 magnet_position = Vector2(0, 0);
|
||||
bool use_target_rotation = false;
|
||||
|
||||
bool editor_draw_gizmo = true;
|
||||
};
|
||||
|
||||
Vector<FABRIK_Joint_Data2D> fabrik_data_chain;
|
||||
|
||||
// Unlike in 3D, we need a vector of Transform2D objects to perform FABRIK.
|
||||
// This is because FABRIK (unlike CCDIK) needs to operate on transforms that are NOT
|
||||
// affected by each other, making the transforms stored in Bone2D unusable, as well as those in Skeleton2D.
|
||||
// For this reason, this modification stores a vector of Transform2Ds used for the calculations, which are then applied at the end.
|
||||
Vector<Transform2D> fabrik_transform_chain;
|
||||
|
||||
NodePath target_node;
|
||||
ObjectID target_node_cache;
|
||||
void update_target_cache();
|
||||
|
||||
float chain_tolarance = 0.01;
|
||||
int chain_max_iterations = 10;
|
||||
int chain_iterations = 0;
|
||||
Transform2D target_global_pose;
|
||||
Transform2D origin_global_pose;
|
||||
|
||||
void fabrik_joint_update_bone2d_cache(int p_joint_idx);
|
||||
void chain_backwards();
|
||||
void chain_forwards();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
bool _set(const StringName &p_path, const Variant &p_value);
|
||||
bool _get(const StringName &p_path, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
public:
|
||||
void _execute(float p_delta) override;
|
||||
void _setup_modification(SkeletonModificationStack2D *p_stack) override;
|
||||
|
||||
void set_target_node(const NodePath &p_target_node);
|
||||
NodePath get_target_node() const;
|
||||
|
||||
int get_fabrik_data_chain_length();
|
||||
void set_fabrik_data_chain_length(int p_new_length);
|
||||
|
||||
void set_fabrik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node);
|
||||
NodePath get_fabrik_joint_bone2d_node(int p_joint_idx) const;
|
||||
void set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx);
|
||||
int get_fabrik_joint_bone_index(int p_joint_idx) const;
|
||||
|
||||
void set_fabrik_joint_magnet_position(int p_joint_idx, Vector2 p_magnet_position);
|
||||
Vector2 get_fabrik_joint_magnet_position(int p_joint_idx) const;
|
||||
void set_fabrik_joint_use_target_rotation(int p_joint_idx, bool p_use_target_rotation);
|
||||
bool get_fabrik_joint_use_target_rotation(int p_joint_idx) const;
|
||||
|
||||
SkeletonModification2DFABRIK();
|
||||
~SkeletonModification2DFABRIK();
|
||||
};
|
577
scene/resources/2d/skeleton/skeleton_modification_2d_jiggle.cpp
Normal file
577
scene/resources/2d/skeleton/skeleton_modification_2d_jiggle.cpp
Normal file
@@ -0,0 +1,577 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_jiggle.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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_jiggle.h"
|
||||
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
#include "scene/resources/world_2d.h"
|
||||
|
||||
bool SkeletonModification2DJiggle::_set(const StringName &p_path, const Variant &p_value) {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("joint_data/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, jiggle_data_chain.size(), false);
|
||||
|
||||
if (what == "bone2d_node") {
|
||||
set_jiggle_joint_bone2d_node(which, p_value);
|
||||
} else if (what == "bone_index") {
|
||||
set_jiggle_joint_bone_index(which, p_value);
|
||||
} else if (what == "override_defaults") {
|
||||
set_jiggle_joint_override(which, p_value);
|
||||
} else if (what == "stiffness") {
|
||||
set_jiggle_joint_stiffness(which, p_value);
|
||||
} else if (what == "mass") {
|
||||
set_jiggle_joint_mass(which, p_value);
|
||||
} else if (what == "damping") {
|
||||
set_jiggle_joint_damping(which, p_value);
|
||||
} else if (what == "use_gravity") {
|
||||
set_jiggle_joint_use_gravity(which, p_value);
|
||||
} else if (what == "gravity") {
|
||||
set_jiggle_joint_gravity(which, p_value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (path == "use_colliders") {
|
||||
set_use_colliders(p_value);
|
||||
} else if (path == "collision_mask") {
|
||||
set_collision_mask(p_value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkeletonModification2DJiggle::_get(const StringName &p_path, Variant &r_ret) const {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("joint_data/")) {
|
||||
int which = path.get_slicec('/', 1).to_int();
|
||||
String what = path.get_slicec('/', 2);
|
||||
ERR_FAIL_INDEX_V(which, jiggle_data_chain.size(), false);
|
||||
|
||||
if (what == "bone2d_node") {
|
||||
r_ret = get_jiggle_joint_bone2d_node(which);
|
||||
} else if (what == "bone_index") {
|
||||
r_ret = get_jiggle_joint_bone_index(which);
|
||||
} else if (what == "override_defaults") {
|
||||
r_ret = get_jiggle_joint_override(which);
|
||||
} else if (what == "stiffness") {
|
||||
r_ret = get_jiggle_joint_stiffness(which);
|
||||
} else if (what == "mass") {
|
||||
r_ret = get_jiggle_joint_mass(which);
|
||||
} else if (what == "damping") {
|
||||
r_ret = get_jiggle_joint_damping(which);
|
||||
} else if (what == "use_gravity") {
|
||||
r_ret = get_jiggle_joint_use_gravity(which);
|
||||
} else if (what == "gravity") {
|
||||
r_ret = get_jiggle_joint_gravity(which);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (path == "use_colliders") {
|
||||
r_ret = get_use_colliders();
|
||||
} else if (path == "collision_mask") {
|
||||
r_ret = get_collision_mask();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, "use_colliders", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
if (use_colliders) {
|
||||
p_list->push_back(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS, "", PROPERTY_USAGE_DEFAULT));
|
||||
}
|
||||
|
||||
for (int i = 0; i < jiggle_data_chain.size(); i++) {
|
||||
String base_string = "joint_data/" + itos(i) + "/";
|
||||
|
||||
p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "override_defaults", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
|
||||
if (jiggle_data_chain[i].override_defaults) {
|
||||
p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "stiffness", PROPERTY_HINT_RANGE, "0, 1000, 0.01", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "mass", PROPERTY_HINT_RANGE, "0, 1000, 0.01", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "damping", PROPERTY_HINT_RANGE, "0, 1, 0.01", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_gravity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
if (jiggle_data_chain[i].use_gravity) {
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR2, base_string + "gravity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = ObjectDB::get_instance<Node2D>(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()) {
|
||||
ERR_PRINT_ONCE("Jiggle joint " + itos(p_joint_idx) + " bone index is invalid. Cannot execute modification on joint...");
|
||||
return;
|
||||
}
|
||||
|
||||
if (jiggle_data_chain[p_joint_idx].bone2d_node_cache.is_null() && !jiggle_data_chain[p_joint_idx].bone2d_node.is_empty()) {
|
||||
WARN_PRINT_ONCE("Bone2D cache for joint " + itos(p_joint_idx) + " is out of date. Updating...");
|
||||
jiggle_joint_update_bone2d_cache(p_joint_idx);
|
||||
}
|
||||
|
||||
Bone2D *operation_bone = stack->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;
|
||||
}
|
||||
|
||||
Transform2D operation_bone_trans = operation_bone->get_global_transform();
|
||||
Vector2 target_position = p_target->get_global_position();
|
||||
|
||||
jiggle_data_chain.write[p_joint_idx].force = (target_position - jiggle_data_chain[p_joint_idx].dynamic_position) * jiggle_data_chain[p_joint_idx].stiffness * p_delta;
|
||||
|
||||
if (jiggle_data_chain[p_joint_idx].use_gravity) {
|
||||
jiggle_data_chain.write[p_joint_idx].force += jiggle_data_chain[p_joint_idx].gravity * p_delta;
|
||||
}
|
||||
|
||||
jiggle_data_chain.write[p_joint_idx].acceleration = jiggle_data_chain[p_joint_idx].force / jiggle_data_chain[p_joint_idx].mass;
|
||||
jiggle_data_chain.write[p_joint_idx].velocity += jiggle_data_chain[p_joint_idx].acceleration * (1 - jiggle_data_chain[p_joint_idx].damping);
|
||||
|
||||
jiggle_data_chain.write[p_joint_idx].dynamic_position += jiggle_data_chain[p_joint_idx].velocity + jiggle_data_chain[p_joint_idx].force;
|
||||
jiggle_data_chain.write[p_joint_idx].dynamic_position += operation_bone_trans.get_origin() - jiggle_data_chain[p_joint_idx].last_position;
|
||||
jiggle_data_chain.write[p_joint_idx].last_position = operation_bone_trans.get_origin();
|
||||
|
||||
// Collision detection/response
|
||||
if (use_colliders) {
|
||||
if (execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process) {
|
||||
Ref<World2D> world_2d = stack->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;
|
||||
|
||||
PhysicsDirectSpaceState2D::RayParameters ray_params;
|
||||
ray_params.from = operation_bone_trans.get_origin();
|
||||
ray_params.to = jiggle_data_chain[p_joint_idx].dynamic_position;
|
||||
ray_params.collision_mask = collision_mask;
|
||||
|
||||
// Add exception support?
|
||||
bool ray_hit = space_state->intersect_ray(ray_params, ray_result);
|
||||
|
||||
if (ray_hit) {
|
||||
jiggle_data_chain.write[p_joint_idx].dynamic_position = jiggle_data_chain[p_joint_idx].last_noncollision_position;
|
||||
jiggle_data_chain.write[p_joint_idx].acceleration = Vector2(0, 0);
|
||||
jiggle_data_chain.write[p_joint_idx].velocity = Vector2(0, 0);
|
||||
} else {
|
||||
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!");
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate the bone using the dynamic position!
|
||||
operation_bone_trans = operation_bone_trans.looking_at(jiggle_data_chain[p_joint_idx].dynamic_position);
|
||||
operation_bone_trans.set_rotation(operation_bone_trans.get_rotation() - operation_bone->get_bone_angle());
|
||||
|
||||
// Reset scale
|
||||
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);
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::_update_jiggle_joint_data() {
|
||||
for (int i = 0; i < jiggle_data_chain.size(); i++) {
|
||||
if (!jiggle_data_chain[i].override_defaults) {
|
||||
set_jiggle_joint_stiffness(i, stiffness);
|
||||
set_jiggle_joint_mass(i, mass);
|
||||
set_jiggle_joint_damping(i, damping);
|
||||
set_jiggle_joint_use_gravity(i, use_gravity);
|
||||
set_jiggle_joint_gravity(i, gravity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
jiggle_joint_update_bone2d_cache(i);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
"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!");
|
||||
jiggle_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id();
|
||||
|
||||
Bone2D *bone = Object::cast_to<Bone2D>(node);
|
||||
if (bone) {
|
||||
jiggle_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton();
|
||||
} else {
|
||||
ERR_FAIL_MSG("Jiggle joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_target_node(const NodePath &p_target_node) {
|
||||
target_node = p_target_node;
|
||||
update_target_cache();
|
||||
}
|
||||
|
||||
NodePath SkeletonModification2DJiggle::get_target_node() const {
|
||||
return target_node;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_stiffness(float p_stiffness) {
|
||||
ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!");
|
||||
stiffness = p_stiffness;
|
||||
_update_jiggle_joint_data();
|
||||
}
|
||||
|
||||
float SkeletonModification2DJiggle::get_stiffness() const {
|
||||
return stiffness;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_mass(float p_mass) {
|
||||
ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!");
|
||||
mass = p_mass;
|
||||
_update_jiggle_joint_data();
|
||||
}
|
||||
|
||||
float SkeletonModification2DJiggle::get_mass() const {
|
||||
return mass;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_damping(float p_damping) {
|
||||
ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!");
|
||||
ERR_FAIL_COND_MSG(p_damping > 1, "Damping cannot be more than one!");
|
||||
damping = p_damping;
|
||||
_update_jiggle_joint_data();
|
||||
}
|
||||
|
||||
float SkeletonModification2DJiggle::get_damping() const {
|
||||
return damping;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_use_gravity(bool p_use_gravity) {
|
||||
use_gravity = p_use_gravity;
|
||||
_update_jiggle_joint_data();
|
||||
}
|
||||
|
||||
bool SkeletonModification2DJiggle::get_use_gravity() const {
|
||||
return use_gravity;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_gravity(Vector2 p_gravity) {
|
||||
gravity = p_gravity;
|
||||
_update_jiggle_joint_data();
|
||||
}
|
||||
|
||||
Vector2 SkeletonModification2DJiggle::get_gravity() const {
|
||||
return gravity;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_use_colliders(bool p_use_colliders) {
|
||||
use_colliders = p_use_colliders;
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
bool SkeletonModification2DJiggle::get_use_colliders() const {
|
||||
return use_colliders;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_collision_mask(int p_mask) {
|
||||
collision_mask = p_mask;
|
||||
}
|
||||
|
||||
int SkeletonModification2DJiggle::get_collision_mask() const {
|
||||
return collision_mask;
|
||||
}
|
||||
|
||||
// Jiggle joint data functions
|
||||
int SkeletonModification2DJiggle::get_jiggle_data_chain_length() {
|
||||
return jiggle_data_chain.size();
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_jiggle_data_chain_length(int p_length) {
|
||||
ERR_FAIL_COND(p_length < 0);
|
||||
jiggle_data_chain.resize(p_length);
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_jiggle_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node) {
|
||||
ERR_FAIL_INDEX_MSG(p_joint_idx, jiggle_data_chain.size(), "Jiggle joint out of range!");
|
||||
jiggle_data_chain.write[p_joint_idx].bone2d_node = p_target_node;
|
||||
jiggle_joint_update_bone2d_cache(p_joint_idx);
|
||||
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
NodePath SkeletonModification2DJiggle::get_jiggle_joint_bone2d_node(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, jiggle_data_chain.size(), NodePath(), "Jiggle joint out of range!");
|
||||
return jiggle_data_chain[p_joint_idx].bone2d_node;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_jiggle_joint_bone_index(int p_joint_idx, int p_bone_idx) {
|
||||
ERR_FAIL_INDEX_MSG(p_joint_idx, jiggle_data_chain.size(), "Jiggle joint out of range!");
|
||||
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!");
|
||||
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));
|
||||
} 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;
|
||||
}
|
||||
} else {
|
||||
jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx;
|
||||
}
|
||||
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
int SkeletonModification2DJiggle::get_jiggle_joint_bone_index(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, jiggle_data_chain.size(), -1, "Jiggle joint out of range!");
|
||||
return jiggle_data_chain[p_joint_idx].bone_idx;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_jiggle_joint_override(int p_joint_idx, bool p_override) {
|
||||
ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size());
|
||||
jiggle_data_chain.write[p_joint_idx].override_defaults = p_override;
|
||||
_update_jiggle_joint_data();
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
bool SkeletonModification2DJiggle::get_jiggle_joint_override(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), false);
|
||||
return jiggle_data_chain[p_joint_idx].override_defaults;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_jiggle_joint_stiffness(int p_joint_idx, float p_stiffness) {
|
||||
ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!");
|
||||
ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size());
|
||||
jiggle_data_chain.write[p_joint_idx].stiffness = p_stiffness;
|
||||
}
|
||||
|
||||
float SkeletonModification2DJiggle::get_jiggle_joint_stiffness(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), -1);
|
||||
return jiggle_data_chain[p_joint_idx].stiffness;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_jiggle_joint_mass(int p_joint_idx, float p_mass) {
|
||||
ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!");
|
||||
ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size());
|
||||
jiggle_data_chain.write[p_joint_idx].mass = p_mass;
|
||||
}
|
||||
|
||||
float SkeletonModification2DJiggle::get_jiggle_joint_mass(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), -1);
|
||||
return jiggle_data_chain[p_joint_idx].mass;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_jiggle_joint_damping(int p_joint_idx, float p_damping) {
|
||||
ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!");
|
||||
ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size());
|
||||
jiggle_data_chain.write[p_joint_idx].damping = p_damping;
|
||||
}
|
||||
|
||||
float SkeletonModification2DJiggle::get_jiggle_joint_damping(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), -1);
|
||||
return jiggle_data_chain[p_joint_idx].damping;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_jiggle_joint_use_gravity(int p_joint_idx, bool p_use_gravity) {
|
||||
ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size());
|
||||
jiggle_data_chain.write[p_joint_idx].use_gravity = p_use_gravity;
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
bool SkeletonModification2DJiggle::get_jiggle_joint_use_gravity(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), false);
|
||||
return jiggle_data_chain[p_joint_idx].use_gravity;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::set_jiggle_joint_gravity(int p_joint_idx, Vector2 p_gravity) {
|
||||
ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size());
|
||||
jiggle_data_chain.write[p_joint_idx].gravity = p_gravity;
|
||||
}
|
||||
|
||||
Vector2 SkeletonModification2DJiggle::get_jiggle_joint_gravity(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), Vector2(0, 0));
|
||||
return jiggle_data_chain[p_joint_idx].gravity;
|
||||
}
|
||||
|
||||
void SkeletonModification2DJiggle::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DJiggle::set_target_node);
|
||||
ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DJiggle::get_target_node);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_jiggle_data_chain_length", "length"), &SkeletonModification2DJiggle::set_jiggle_data_chain_length);
|
||||
ClassDB::bind_method(D_METHOD("get_jiggle_data_chain_length"), &SkeletonModification2DJiggle::get_jiggle_data_chain_length);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_stiffness", "stiffness"), &SkeletonModification2DJiggle::set_stiffness);
|
||||
ClassDB::bind_method(D_METHOD("get_stiffness"), &SkeletonModification2DJiggle::get_stiffness);
|
||||
ClassDB::bind_method(D_METHOD("set_mass", "mass"), &SkeletonModification2DJiggle::set_mass);
|
||||
ClassDB::bind_method(D_METHOD("get_mass"), &SkeletonModification2DJiggle::get_mass);
|
||||
ClassDB::bind_method(D_METHOD("set_damping", "damping"), &SkeletonModification2DJiggle::set_damping);
|
||||
ClassDB::bind_method(D_METHOD("get_damping"), &SkeletonModification2DJiggle::get_damping);
|
||||
ClassDB::bind_method(D_METHOD("set_use_gravity", "use_gravity"), &SkeletonModification2DJiggle::set_use_gravity);
|
||||
ClassDB::bind_method(D_METHOD("get_use_gravity"), &SkeletonModification2DJiggle::get_use_gravity);
|
||||
ClassDB::bind_method(D_METHOD("set_gravity", "gravity"), &SkeletonModification2DJiggle::set_gravity);
|
||||
ClassDB::bind_method(D_METHOD("get_gravity"), &SkeletonModification2DJiggle::get_gravity);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_use_colliders", "use_colliders"), &SkeletonModification2DJiggle::set_use_colliders);
|
||||
ClassDB::bind_method(D_METHOD("get_use_colliders"), &SkeletonModification2DJiggle::get_use_colliders);
|
||||
ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SkeletonModification2DJiggle::set_collision_mask);
|
||||
ClassDB::bind_method(D_METHOD("get_collision_mask"), &SkeletonModification2DJiggle::get_collision_mask);
|
||||
|
||||
// Jiggle joint data functions
|
||||
ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone2d_node", "joint_idx", "bone2d_node"), &SkeletonModification2DJiggle::set_jiggle_joint_bone2d_node);
|
||||
ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone2d_node", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_bone2d_node);
|
||||
ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DJiggle::set_jiggle_joint_bone_index);
|
||||
ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone_index", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_bone_index);
|
||||
ClassDB::bind_method(D_METHOD("set_jiggle_joint_override", "joint_idx", "override"), &SkeletonModification2DJiggle::set_jiggle_joint_override);
|
||||
ClassDB::bind_method(D_METHOD("get_jiggle_joint_override", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_override);
|
||||
ClassDB::bind_method(D_METHOD("set_jiggle_joint_stiffness", "joint_idx", "stiffness"), &SkeletonModification2DJiggle::set_jiggle_joint_stiffness);
|
||||
ClassDB::bind_method(D_METHOD("get_jiggle_joint_stiffness", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_stiffness);
|
||||
ClassDB::bind_method(D_METHOD("set_jiggle_joint_mass", "joint_idx", "mass"), &SkeletonModification2DJiggle::set_jiggle_joint_mass);
|
||||
ClassDB::bind_method(D_METHOD("get_jiggle_joint_mass", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_mass);
|
||||
ClassDB::bind_method(D_METHOD("set_jiggle_joint_damping", "joint_idx", "damping"), &SkeletonModification2DJiggle::set_jiggle_joint_damping);
|
||||
ClassDB::bind_method(D_METHOD("get_jiggle_joint_damping", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_damping);
|
||||
ClassDB::bind_method(D_METHOD("set_jiggle_joint_use_gravity", "joint_idx", "use_gravity"), &SkeletonModification2DJiggle::set_jiggle_joint_use_gravity);
|
||||
ClassDB::bind_method(D_METHOD("get_jiggle_joint_use_gravity", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_use_gravity);
|
||||
ClassDB::bind_method(D_METHOD("set_jiggle_joint_gravity", "joint_idx", "gravity"), &SkeletonModification2DJiggle::set_jiggle_joint_gravity);
|
||||
ClassDB::bind_method(D_METHOD("get_jiggle_joint_gravity", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_gravity);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "jiggle_data_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_jiggle_data_chain_length", "get_jiggle_data_chain_length");
|
||||
ADD_GROUP("Default Joint Settings", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "stiffness"), "set_stiffness", "get_stiffness");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass"), "set_mass", "get_mass");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping", PROPERTY_HINT_RANGE, "0, 1, 0.01"), "set_damping", "get_damping");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_gravity"), "set_use_gravity", "get_use_gravity");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity"), "set_gravity", "get_gravity");
|
||||
ADD_GROUP("", "");
|
||||
}
|
||||
|
||||
SkeletonModification2DJiggle::SkeletonModification2DJiggle() {
|
||||
stack = nullptr;
|
||||
is_setup = false;
|
||||
jiggle_data_chain = Vector<Jiggle_Joint_Data2D>();
|
||||
stiffness = 3;
|
||||
mass = 0.75;
|
||||
damping = 0.75;
|
||||
use_gravity = false;
|
||||
gravity = Vector2(0, 6.0);
|
||||
enabled = true;
|
||||
editor_draw_gizmo = false; // Nothing to really show in a gizmo right now.
|
||||
}
|
||||
|
||||
SkeletonModification2DJiggle::~SkeletonModification2DJiggle() {
|
||||
}
|
136
scene/resources/2d/skeleton/skeleton_modification_2d_jiggle.h
Normal file
136
scene/resources/2d/skeleton/skeleton_modification_2d_jiggle.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_jiggle.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
#include "scene/resources/2d/skeleton/skeleton_modification_2d.h"
|
||||
|
||||
///////////////////////////////////////
|
||||
// SkeletonModification2DJIGGLE
|
||||
///////////////////////////////////////
|
||||
|
||||
class SkeletonModification2DJiggle : public SkeletonModification2D {
|
||||
GDCLASS(SkeletonModification2DJiggle, SkeletonModification2D);
|
||||
|
||||
private:
|
||||
struct Jiggle_Joint_Data2D {
|
||||
int bone_idx = -1;
|
||||
NodePath bone2d_node;
|
||||
ObjectID bone2d_node_cache;
|
||||
|
||||
bool override_defaults = false;
|
||||
float stiffness = 3;
|
||||
float mass = 0.75;
|
||||
float damping = 0.75;
|
||||
bool use_gravity = false;
|
||||
Vector2 gravity = Vector2(0, 6.0);
|
||||
|
||||
Vector2 force = Vector2(0, 0);
|
||||
Vector2 acceleration = Vector2(0, 0);
|
||||
Vector2 velocity = Vector2(0, 0);
|
||||
Vector2 last_position = Vector2(0, 0);
|
||||
Vector2 dynamic_position = Vector2(0, 0);
|
||||
|
||||
Vector2 last_noncollision_position = Vector2(0, 0);
|
||||
};
|
||||
|
||||
Vector<Jiggle_Joint_Data2D> jiggle_data_chain;
|
||||
|
||||
NodePath target_node;
|
||||
ObjectID target_node_cache;
|
||||
void update_target_cache();
|
||||
|
||||
float stiffness = 3;
|
||||
float mass = 0.75;
|
||||
float damping = 0.75;
|
||||
bool use_gravity = false;
|
||||
Vector2 gravity = Vector2(0, 6);
|
||||
|
||||
bool use_colliders = false;
|
||||
uint32_t collision_mask = 1;
|
||||
|
||||
void jiggle_joint_update_bone2d_cache(int p_joint_idx);
|
||||
void _execute_jiggle_joint(int p_joint_idx, Node2D *p_target, float p_delta);
|
||||
void _update_jiggle_joint_data();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
bool _set(const StringName &p_path, const Variant &p_value);
|
||||
bool _get(const StringName &p_path, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
public:
|
||||
void _execute(float p_delta) override;
|
||||
void _setup_modification(SkeletonModificationStack2D *p_stack) override;
|
||||
|
||||
void set_target_node(const NodePath &p_target_node);
|
||||
NodePath get_target_node() const;
|
||||
|
||||
void set_stiffness(float p_stiffness);
|
||||
float get_stiffness() const;
|
||||
void set_mass(float p_mass);
|
||||
float get_mass() const;
|
||||
void set_damping(float p_damping);
|
||||
float get_damping() const;
|
||||
void set_use_gravity(bool p_use_gravity);
|
||||
bool get_use_gravity() const;
|
||||
void set_gravity(Vector2 p_gravity);
|
||||
Vector2 get_gravity() const;
|
||||
|
||||
void set_use_colliders(bool p_use_colliders);
|
||||
bool get_use_colliders() const;
|
||||
void set_collision_mask(int p_mask);
|
||||
int get_collision_mask() const;
|
||||
|
||||
int get_jiggle_data_chain_length();
|
||||
void set_jiggle_data_chain_length(int p_new_length);
|
||||
|
||||
void set_jiggle_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node);
|
||||
NodePath get_jiggle_joint_bone2d_node(int p_joint_idx) const;
|
||||
void set_jiggle_joint_bone_index(int p_joint_idx, int p_bone_idx);
|
||||
int get_jiggle_joint_bone_index(int p_joint_idx) const;
|
||||
|
||||
void set_jiggle_joint_override(int p_joint_idx, bool p_override);
|
||||
bool get_jiggle_joint_override(int p_joint_idx) const;
|
||||
void set_jiggle_joint_stiffness(int p_joint_idx, float p_stiffness);
|
||||
float get_jiggle_joint_stiffness(int p_joint_idx) const;
|
||||
void set_jiggle_joint_mass(int p_joint_idx, float p_mass);
|
||||
float get_jiggle_joint_mass(int p_joint_idx) const;
|
||||
void set_jiggle_joint_damping(int p_joint_idx, float p_damping);
|
||||
float get_jiggle_joint_damping(int p_joint_idx) const;
|
||||
void set_jiggle_joint_use_gravity(int p_joint_idx, bool p_use_gravity);
|
||||
bool get_jiggle_joint_use_gravity(int p_joint_idx) const;
|
||||
void set_jiggle_joint_gravity(int p_joint_idx, Vector2 p_gravity);
|
||||
Vector2 get_jiggle_joint_gravity(int p_joint_idx) const;
|
||||
|
||||
SkeletonModification2DJiggle();
|
||||
~SkeletonModification2DJiggle();
|
||||
};
|
410
scene/resources/2d/skeleton/skeleton_modification_2d_lookat.cpp
Normal file
410
scene/resources/2d/skeleton/skeleton_modification_2d_lookat.cpp
Normal file
@@ -0,0 +1,410 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_lookat.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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_lookat.h"
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
|
||||
bool SkeletonModification2DLookAt::_set(const StringName &p_path, const Variant &p_value) {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("enable_constraint")) {
|
||||
set_enable_constraint(p_value);
|
||||
} else if (path.begins_with("constraint_angle_min")) {
|
||||
set_constraint_angle_min(Math::deg_to_rad(float(p_value)));
|
||||
} else if (path.begins_with("constraint_angle_max")) {
|
||||
set_constraint_angle_max(Math::deg_to_rad(float(p_value)));
|
||||
} else if (path.begins_with("constraint_angle_invert")) {
|
||||
set_constraint_angle_invert(p_value);
|
||||
} else if (path.begins_with("constraint_in_localspace")) {
|
||||
set_constraint_in_localspace(p_value);
|
||||
} else if (path.begins_with("additional_rotation")) {
|
||||
set_additional_rotation(Math::deg_to_rad(float(p_value)));
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
else if (path.begins_with("editor/draw_gizmo")) {
|
||||
set_editor_draw_gizmo(p_value);
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkeletonModification2DLookAt::_get(const StringName &p_path, Variant &r_ret) const {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("enable_constraint")) {
|
||||
r_ret = get_enable_constraint();
|
||||
} else if (path.begins_with("constraint_angle_min")) {
|
||||
r_ret = Math::rad_to_deg(get_constraint_angle_min());
|
||||
} else if (path.begins_with("constraint_angle_max")) {
|
||||
r_ret = Math::rad_to_deg(get_constraint_angle_max());
|
||||
} else if (path.begins_with("constraint_angle_invert")) {
|
||||
r_ret = get_constraint_angle_invert();
|
||||
} else if (path.begins_with("constraint_in_localspace")) {
|
||||
r_ret = get_constraint_in_localspace();
|
||||
} else if (path.begins_with("additional_rotation")) {
|
||||
r_ret = Math::rad_to_deg(get_additional_rotation());
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
else if (path.begins_with("editor/draw_gizmo")) {
|
||||
r_ret = get_editor_draw_gizmo();
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkeletonModification2DLookAt::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, "enable_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
if (enable_constraint) {
|
||||
p_list->push_back(PropertyInfo(Variant::FLOAT, "constraint_angle_min", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::FLOAT, "constraint_angle_max", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, "constraint_angle_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, "constraint_in_localspace", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
}
|
||||
p_list->push_back(PropertyInfo(Variant::FLOAT, "additional_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
|
||||
#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 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 = ObjectDB::get_instance<Node2D>(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);
|
||||
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<Bone2D>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModification2DLookAt::set_bone2d_node(const NodePath &p_target_node) {
|
||||
bone2d_node = p_target_node;
|
||||
update_bone2d_cache();
|
||||
}
|
||||
|
||||
NodePath SkeletonModification2DLookAt::get_bone2d_node() const {
|
||||
return bone2d_node;
|
||||
}
|
||||
|
||||
int SkeletonModification2DLookAt::get_bone_index() const {
|
||||
return bone_idx;
|
||||
}
|
||||
|
||||
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!");
|
||||
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));
|
||||
} else {
|
||||
WARN_PRINT("Cannot verify the bone index for this modification...");
|
||||
bone_idx = p_bone_idx;
|
||||
}
|
||||
} else {
|
||||
bone_idx = p_bone_idx;
|
||||
}
|
||||
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModification2DLookAt::set_target_node(const NodePath &p_target_node) {
|
||||
target_node = p_target_node;
|
||||
update_target_cache();
|
||||
}
|
||||
|
||||
NodePath SkeletonModification2DLookAt::get_target_node() const {
|
||||
return target_node;
|
||||
}
|
||||
|
||||
float SkeletonModification2DLookAt::get_additional_rotation() const {
|
||||
return additional_rotation;
|
||||
}
|
||||
|
||||
void SkeletonModification2DLookAt::set_additional_rotation(float p_rotation) {
|
||||
additional_rotation = 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 {
|
||||
return enable_constraint;
|
||||
}
|
||||
|
||||
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 {
|
||||
return constraint_angle_min;
|
||||
}
|
||||
|
||||
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 {
|
||||
return constraint_angle_max;
|
||||
}
|
||||
|
||||
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 {
|
||||
return constraint_angle_invert;
|
||||
}
|
||||
|
||||
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 {
|
||||
return constraint_in_localspace;
|
||||
}
|
||||
|
||||
void SkeletonModification2DLookAt::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_bone2d_node", "bone2d_nodepath"), &SkeletonModification2DLookAt::set_bone2d_node);
|
||||
ClassDB::bind_method(D_METHOD("get_bone2d_node"), &SkeletonModification2DLookAt::get_bone2d_node);
|
||||
ClassDB::bind_method(D_METHOD("set_bone_index", "bone_idx"), &SkeletonModification2DLookAt::set_bone_index);
|
||||
ClassDB::bind_method(D_METHOD("get_bone_index"), &SkeletonModification2DLookAt::get_bone_index);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DLookAt::set_target_node);
|
||||
ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DLookAt::get_target_node);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_additional_rotation", "rotation"), &SkeletonModification2DLookAt::set_additional_rotation);
|
||||
ClassDB::bind_method(D_METHOD("get_additional_rotation"), &SkeletonModification2DLookAt::get_additional_rotation);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_enable_constraint", "enable_constraint"), &SkeletonModification2DLookAt::set_enable_constraint);
|
||||
ClassDB::bind_method(D_METHOD("get_enable_constraint"), &SkeletonModification2DLookAt::get_enable_constraint);
|
||||
ClassDB::bind_method(D_METHOD("set_constraint_angle_min", "angle_min"), &SkeletonModification2DLookAt::set_constraint_angle_min);
|
||||
ClassDB::bind_method(D_METHOD("get_constraint_angle_min"), &SkeletonModification2DLookAt::get_constraint_angle_min);
|
||||
ClassDB::bind_method(D_METHOD("set_constraint_angle_max", "angle_max"), &SkeletonModification2DLookAt::set_constraint_angle_max);
|
||||
ClassDB::bind_method(D_METHOD("get_constraint_angle_max"), &SkeletonModification2DLookAt::get_constraint_angle_max);
|
||||
ClassDB::bind_method(D_METHOD("set_constraint_angle_invert", "invert"), &SkeletonModification2DLookAt::set_constraint_angle_invert);
|
||||
ClassDB::bind_method(D_METHOD("get_constraint_angle_invert"), &SkeletonModification2DLookAt::get_constraint_angle_invert);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_index"), "set_bone_index", "get_bone_index");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D"), "set_bone2d_node", "get_bone2d_node");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node");
|
||||
}
|
||||
|
||||
SkeletonModification2DLookAt::SkeletonModification2DLookAt() {
|
||||
stack = nullptr;
|
||||
is_setup = false;
|
||||
bone_idx = -1;
|
||||
additional_rotation = 0;
|
||||
enable_constraint = false;
|
||||
constraint_angle_min = 0;
|
||||
constraint_angle_max = Math::PI * 2;
|
||||
constraint_angle_invert = false;
|
||||
enabled = true;
|
||||
|
||||
editor_draw_gizmo = true;
|
||||
}
|
||||
|
||||
SkeletonModification2DLookAt::~SkeletonModification2DLookAt() {
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_lookat.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
#include "scene/resources/2d/skeleton/skeleton_modification_2d.h"
|
||||
|
||||
///////////////////////////////////////
|
||||
// SkeletonModification2DLookAt
|
||||
///////////////////////////////////////
|
||||
|
||||
class SkeletonModification2DLookAt : public SkeletonModification2D {
|
||||
GDCLASS(SkeletonModification2DLookAt, SkeletonModification2D);
|
||||
|
||||
private:
|
||||
int bone_idx = -1;
|
||||
NodePath bone2d_node;
|
||||
ObjectID bone2d_node_cache;
|
||||
|
||||
NodePath target_node;
|
||||
ObjectID target_node_cache;
|
||||
Node2D *target_node_reference = nullptr;
|
||||
|
||||
float additional_rotation = 0;
|
||||
bool enable_constraint = false;
|
||||
float constraint_angle_min = 0;
|
||||
float constraint_angle_max = (2.0 * Math::PI);
|
||||
bool constraint_angle_invert = false;
|
||||
bool constraint_in_localspace = true;
|
||||
|
||||
void update_bone2d_cache();
|
||||
void update_target_cache();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
bool _set(const StringName &p_path, const Variant &p_value);
|
||||
bool _get(const StringName &p_path, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
public:
|
||||
void _execute(float p_delta) override;
|
||||
void _setup_modification(SkeletonModificationStack2D *p_stack) override;
|
||||
void _draw_editor_gizmo() override;
|
||||
|
||||
void set_bone2d_node(const NodePath &p_target_node);
|
||||
NodePath get_bone2d_node() const;
|
||||
void set_bone_index(int p_idx);
|
||||
int get_bone_index() const;
|
||||
|
||||
void set_target_node(const NodePath &p_target_node);
|
||||
NodePath get_target_node() const;
|
||||
|
||||
void set_additional_rotation(float p_rotation);
|
||||
float get_additional_rotation() const;
|
||||
|
||||
void set_enable_constraint(bool p_constraint);
|
||||
bool get_enable_constraint() const;
|
||||
void set_constraint_angle_min(float p_angle_min);
|
||||
float get_constraint_angle_min() const;
|
||||
void set_constraint_angle_max(float p_angle_max);
|
||||
float get_constraint_angle_max() const;
|
||||
void set_constraint_angle_invert(bool p_invert);
|
||||
bool get_constraint_angle_invert() const;
|
||||
void set_constraint_in_localspace(bool p_constraint_in_localspace);
|
||||
bool get_constraint_in_localspace() const;
|
||||
|
||||
SkeletonModification2DLookAt();
|
||||
~SkeletonModification2DLookAt();
|
||||
};
|
@@ -0,0 +1,298 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_physicalbones.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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_physicalbones.h"
|
||||
#include "scene/2d/physics/physical_bone_2d.h"
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
|
||||
bool SkeletonModification2DPhysicalBones::_set(const StringName &p_path, const Variant &p_value) {
|
||||
String path = p_path;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// Exposes a way to fetch the PhysicalBone2D nodes from the Godot editor.
|
||||
if (is_setup) {
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
if (path.begins_with("fetch_bones")) {
|
||||
fetch_physical_bones();
|
||||
notify_property_list_changed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //TOOLS_ENABLED
|
||||
|
||||
if (path.begins_with("joint_")) {
|
||||
int which = path.get_slicec('_', 1).to_int();
|
||||
String what = path.get_slicec('_', 2);
|
||||
ERR_FAIL_INDEX_V(which, physical_bone_chain.size(), false);
|
||||
|
||||
if (what == "nodepath") {
|
||||
set_physical_bone_node(which, p_value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SkeletonModification2DPhysicalBones::_get(const StringName &p_path, Variant &r_ret) const {
|
||||
String path = p_path;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
if (path.begins_with("fetch_bones")) {
|
||||
// Do nothing!
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif //TOOLS_ENABLED
|
||||
|
||||
if (path.begins_with("joint_")) {
|
||||
int which = path.get_slicec('_', 1).to_int();
|
||||
String what = path.get_slicec('_', 2);
|
||||
ERR_FAIL_INDEX_V(which, physical_bone_chain.size(), false);
|
||||
|
||||
if (what == "nodepath") {
|
||||
r_ret = get_physical_bone_node(which);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SkeletonModification2DPhysicalBones::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, "fetch_bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
}
|
||||
#endif //TOOLS_ENABLED
|
||||
|
||||
for (int i = 0; i < physical_bone_chain.size(); i++) {
|
||||
String base_string = "joint_" + itos(i) + "_";
|
||||
|
||||
p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicalBone2D", PROPERTY_USAGE_DEFAULT));
|
||||
}
|
||||
}
|
||||
|
||||
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 = ObjectDB::get_instance<PhysicalBone2D>(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 (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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int SkeletonModification2DPhysicalBones::get_physical_bone_chain_length() {
|
||||
return physical_bone_chain.size();
|
||||
}
|
||||
|
||||
void SkeletonModification2DPhysicalBones::set_physical_bone_chain_length(int p_length) {
|
||||
ERR_FAIL_COND(p_length < 0);
|
||||
physical_bone_chain.resize(p_length);
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
void SkeletonModification2DPhysicalBones::fetch_physical_bones() {
|
||||
ERR_FAIL_NULL_MSG(stack, "No modification stack found! Cannot fetch physical bones!");
|
||||
ERR_FAIL_NULL_MSG(stack->skeleton, "No skeleton found! Cannot fetch physical bones!");
|
||||
|
||||
physical_bone_chain.clear();
|
||||
|
||||
List<Node *> node_queue = List<Node *>();
|
||||
node_queue.push_back(stack->skeleton);
|
||||
|
||||
while (node_queue.size() > 0) {
|
||||
Node *node_to_process = node_queue.front()->get();
|
||||
node_queue.pop_front();
|
||||
|
||||
if (node_to_process != nullptr) {
|
||||
PhysicalBone2D *potential_bone = Object::cast_to<PhysicalBone2D>(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_cache = potential_bone->get_instance_id();
|
||||
physical_bone_chain.push_back(new_data);
|
||||
}
|
||||
for (int i = 0; i < node_to_process->get_child_count(); i++) {
|
||||
node_queue.push_back(node_to_process->get_child(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModification2DPhysicalBones::start_simulation(const TypedArray<StringName> &p_bones) {
|
||||
_simulation_state_dirty = true;
|
||||
_simulation_state_dirty_names = p_bones;
|
||||
_simulation_state_dirty_process = true;
|
||||
|
||||
if (is_setup) {
|
||||
_update_simulation_state();
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModification2DPhysicalBones::stop_simulation(const TypedArray<StringName> &p_bones) {
|
||||
_simulation_state_dirty = true;
|
||||
_simulation_state_dirty_names = p_bones;
|
||||
_simulation_state_dirty_process = false;
|
||||
|
||||
if (is_setup) {
|
||||
_update_simulation_state();
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModification2DPhysicalBones::_update_simulation_state() {
|
||||
if (!_simulation_state_dirty) {
|
||||
return;
|
||||
}
|
||||
_simulation_state_dirty = false;
|
||||
|
||||
if (_simulation_state_dirty_names.is_empty()) {
|
||||
for (int i = 0; i < physical_bone_chain.size(); i++) {
|
||||
PhysicalBone2D *physical_bone = Object::cast_to<PhysicalBone2D>(stack->skeleton->get_node(physical_bone_chain[i].physical_bone_node));
|
||||
if (!physical_bone) {
|
||||
continue;
|
||||
}
|
||||
|
||||
physical_bone->set_simulate_physics(_simulation_state_dirty_process);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < physical_bone_chain.size(); i++) {
|
||||
PhysicalBone2D *physical_bone = ObjectDB::get_instance<PhysicalBone2D>(physical_bone_chain[i].physical_bone_node_cache);
|
||||
if (!physical_bone) {
|
||||
continue;
|
||||
}
|
||||
if (_simulation_state_dirty_names.has(physical_bone->get_name())) {
|
||||
physical_bone->set_simulate_physics(_simulation_state_dirty_process);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModification2DPhysicalBones::set_physical_bone_node(int p_joint_idx, const NodePath &p_nodepath) {
|
||||
ERR_FAIL_INDEX_MSG(p_joint_idx, physical_bone_chain.size(), "Joint index out of range!");
|
||||
physical_bone_chain.write[p_joint_idx].physical_bone_node = p_nodepath;
|
||||
_physical_bone_update_cache(p_joint_idx);
|
||||
}
|
||||
|
||||
NodePath SkeletonModification2DPhysicalBones::get_physical_bone_node(int p_joint_idx) const {
|
||||
ERR_FAIL_INDEX_V_MSG(p_joint_idx, physical_bone_chain.size(), NodePath(), "Joint index out of range!");
|
||||
return physical_bone_chain[p_joint_idx].physical_bone_node;
|
||||
}
|
||||
|
||||
void SkeletonModification2DPhysicalBones::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_physical_bone_chain_length", "length"), &SkeletonModification2DPhysicalBones::set_physical_bone_chain_length);
|
||||
ClassDB::bind_method(D_METHOD("get_physical_bone_chain_length"), &SkeletonModification2DPhysicalBones::get_physical_bone_chain_length);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_physical_bone_node", "joint_idx", "physicalbone2d_node"), &SkeletonModification2DPhysicalBones::set_physical_bone_node);
|
||||
ClassDB::bind_method(D_METHOD("get_physical_bone_node", "joint_idx"), &SkeletonModification2DPhysicalBones::get_physical_bone_node);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("fetch_physical_bones"), &SkeletonModification2DPhysicalBones::fetch_physical_bones);
|
||||
ClassDB::bind_method(D_METHOD("start_simulation", "bones"), &SkeletonModification2DPhysicalBones::start_simulation, DEFVAL(Array()));
|
||||
ClassDB::bind_method(D_METHOD("stop_simulation", "bones"), &SkeletonModification2DPhysicalBones::stop_simulation, DEFVAL(Array()));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "physical_bone_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_physical_bone_chain_length", "get_physical_bone_chain_length");
|
||||
}
|
||||
|
||||
SkeletonModification2DPhysicalBones::SkeletonModification2DPhysicalBones() {
|
||||
stack = nullptr;
|
||||
is_setup = false;
|
||||
physical_bone_chain = Vector<PhysicalBone_Data2D>();
|
||||
enabled = true;
|
||||
editor_draw_gizmo = false; // Nothing to really show in a gizmo right now.
|
||||
}
|
||||
|
||||
SkeletonModification2DPhysicalBones::~SkeletonModification2DPhysicalBones() {
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_physicalbones.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
#include "scene/resources/2d/skeleton/skeleton_modification_2d.h"
|
||||
|
||||
///////////////////////////////////////
|
||||
// SkeletonModification2DJIGGLE
|
||||
///////////////////////////////////////
|
||||
|
||||
class SkeletonModification2DPhysicalBones : public SkeletonModification2D {
|
||||
GDCLASS(SkeletonModification2DPhysicalBones, SkeletonModification2D);
|
||||
|
||||
private:
|
||||
struct PhysicalBone_Data2D {
|
||||
NodePath physical_bone_node;
|
||||
ObjectID physical_bone_node_cache;
|
||||
};
|
||||
Vector<PhysicalBone_Data2D> physical_bone_chain;
|
||||
|
||||
void _physical_bone_update_cache(int p_joint_idx);
|
||||
|
||||
bool _simulation_state_dirty = false;
|
||||
TypedArray<StringName> _simulation_state_dirty_names;
|
||||
bool _simulation_state_dirty_process = false;
|
||||
void _update_simulation_state();
|
||||
|
||||
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<PropertyInfo> *p_list) const;
|
||||
|
||||
public:
|
||||
void _execute(float p_delta) override;
|
||||
void _setup_modification(SkeletonModificationStack2D *p_stack) override;
|
||||
|
||||
int get_physical_bone_chain_length();
|
||||
void set_physical_bone_chain_length(int p_new_length);
|
||||
|
||||
void set_physical_bone_node(int p_joint_idx, const NodePath &p_path);
|
||||
NodePath get_physical_bone_node(int p_joint_idx) const;
|
||||
|
||||
void fetch_physical_bones();
|
||||
void start_simulation(const TypedArray<StringName> &p_bones);
|
||||
void stop_simulation(const TypedArray<StringName> &p_bones);
|
||||
|
||||
SkeletonModification2DPhysicalBones();
|
||||
~SkeletonModification2DPhysicalBones();
|
||||
};
|
@@ -0,0 +1,135 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_stackholder.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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
|
||||
else if (path == "editor/draw_gizmo") {
|
||||
set_editor_draw_gizmo(p_value);
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
else if (path == "editor/draw_gizmo") {
|
||||
r_ret = get_editor_draw_gizmo();
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkeletonModification2DStackHolder::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_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<SkeletonModificationStack2D> 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<SkeletonModificationStack2D> 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() {
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_stackholder.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
#include "scene/resources/2d/skeleton/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<PropertyInfo> *p_list) const;
|
||||
|
||||
public:
|
||||
Ref<SkeletonModificationStack2D> 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<SkeletonModificationStack2D> p_held_stack);
|
||||
Ref<SkeletonModificationStack2D> get_held_modification_stack() const;
|
||||
|
||||
SkeletonModification2DStackHolder();
|
||||
~SkeletonModification2DStackHolder();
|
||||
};
|
@@ -0,0 +1,489 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_twoboneik.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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_twoboneik.h"
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/settings/editor_settings.h"
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
bool SkeletonModification2DTwoBoneIK::_set(const StringName &p_path, const Variant &p_value) {
|
||||
String path = p_path;
|
||||
|
||||
if (path == "joint_one_bone_idx") {
|
||||
set_joint_one_bone_idx(p_value);
|
||||
} else if (path == "joint_one_bone2d_node") {
|
||||
set_joint_one_bone2d_node(p_value);
|
||||
} else if (path == "joint_two_bone_idx") {
|
||||
set_joint_two_bone_idx(p_value);
|
||||
} else if (path == "joint_two_bone2d_node") {
|
||||
set_joint_two_bone2d_node(p_value);
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
else if (path.begins_with("editor/draw_gizmo")) {
|
||||
set_editor_draw_gizmo(p_value);
|
||||
} else if (path.begins_with("editor/draw_min_max")) {
|
||||
set_editor_draw_min_max(p_value);
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkeletonModification2DTwoBoneIK::_get(const StringName &p_path, Variant &r_ret) const {
|
||||
String path = p_path;
|
||||
|
||||
if (path == "joint_one_bone_idx") {
|
||||
r_ret = get_joint_one_bone_idx();
|
||||
} else if (path == "joint_one_bone2d_node") {
|
||||
r_ret = get_joint_one_bone2d_node();
|
||||
} else if (path == "joint_two_bone_idx") {
|
||||
r_ret = get_joint_two_bone_idx();
|
||||
} else if (path == "joint_two_bone2d_node") {
|
||||
r_ret = get_joint_two_bone2d_node();
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
else if (path.begins_with("editor/draw_gizmo")) {
|
||||
r_ret = get_editor_draw_gizmo();
|
||||
} else if (path.begins_with("editor/draw_min_max")) {
|
||||
r_ret = get_editor_draw_min_max();
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkeletonModification2DTwoBoneIK::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
p_list->push_back(PropertyInfo(Variant::INT, "joint_one_bone_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::NODE_PATH, "joint_one_bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", PROPERTY_USAGE_DEFAULT));
|
||||
|
||||
p_list->push_back(PropertyInfo(Variant::INT, "joint_two_bone_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
p_list->push_back(PropertyInfo(Variant::NODE_PATH, "joint_two_bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", PROPERTY_USAGE_DEFAULT));
|
||||
|
||||
#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));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_min_max", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
|
||||
}
|
||||
#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 = ObjectDB::get_instance<Node2D>(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 (std::isnan(angle_0) || std::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);
|
||||
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());
|
||||
|
||||
Color bone_ik_color = Color(1.0, 0.65, 0.0, 0.4);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
bone_ik_color = EDITOR_GET("editors/2d/bone_ik_color");
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
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), std::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), std::sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0);
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
if (editor_draw_min_max) {
|
||||
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);
|
||||
Node2D *target = ObjectDB::get_instance<Node2D>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
}
|
||||
|
||||
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,
|
||||
"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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
"Cannot 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 joint one Bone2D cache: node is not in the scene tree!");
|
||||
joint_one_bone2d_node_cache = node->get_instance_id();
|
||||
|
||||
Bone2D *bone = Object::cast_to<Bone2D>(node);
|
||||
if (bone) {
|
||||
joint_one_bone_idx = bone->get_index_in_skeleton();
|
||||
} else {
|
||||
ERR_FAIL_MSG("Update joint one Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
"Cannot 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 joint two Bone2D cache: node is not in scene tree!");
|
||||
joint_two_bone2d_node_cache = node->get_instance_id();
|
||||
|
||||
Bone2D *bone = Object::cast_to<Bone2D>(node);
|
||||
if (bone) {
|
||||
joint_two_bone_idx = bone->get_index_in_skeleton();
|
||||
} else {
|
||||
ERR_FAIL_MSG("Update joint two Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModification2DTwoBoneIK::set_target_node(const NodePath &p_target_node) {
|
||||
target_node = p_target_node;
|
||||
update_target_cache();
|
||||
}
|
||||
|
||||
NodePath SkeletonModification2DTwoBoneIK::get_target_node() const {
|
||||
return target_node;
|
||||
}
|
||||
|
||||
void SkeletonModification2DTwoBoneIK::set_joint_one_bone2d_node(const NodePath &p_target_node) {
|
||||
joint_one_bone2d_node = p_target_node;
|
||||
update_joint_one_bone2d_cache();
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
void SkeletonModification2DTwoBoneIK::set_target_minimum_distance(float p_distance) {
|
||||
ERR_FAIL_COND_MSG(p_distance < 0, "Target minimum distance cannot be less than zero!");
|
||||
target_minimum_distance = p_distance;
|
||||
}
|
||||
|
||||
float SkeletonModification2DTwoBoneIK::get_target_minimum_distance() const {
|
||||
return target_minimum_distance;
|
||||
}
|
||||
|
||||
void SkeletonModification2DTwoBoneIK::set_target_maximum_distance(float p_distance) {
|
||||
ERR_FAIL_COND_MSG(p_distance < 0, "Target maximum distance cannot be less than zero!");
|
||||
target_maximum_distance = p_distance;
|
||||
}
|
||||
|
||||
float SkeletonModification2DTwoBoneIK::get_target_maximum_distance() const {
|
||||
return target_maximum_distance;
|
||||
}
|
||||
|
||||
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 {
|
||||
return flip_bend_direction;
|
||||
}
|
||||
|
||||
NodePath SkeletonModification2DTwoBoneIK::get_joint_one_bone2d_node() const {
|
||||
return joint_one_bone2d_node;
|
||||
}
|
||||
|
||||
void SkeletonModification2DTwoBoneIK::set_joint_two_bone2d_node(const NodePath &p_target_node) {
|
||||
joint_two_bone2d_node = p_target_node;
|
||||
update_joint_two_bone2d_cache();
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
NodePath SkeletonModification2DTwoBoneIK::get_joint_two_bone2d_node() const {
|
||||
return joint_two_bone2d_node;
|
||||
}
|
||||
|
||||
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!");
|
||||
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));
|
||||
} else {
|
||||
WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint one...");
|
||||
joint_one_bone_idx = p_bone_idx;
|
||||
}
|
||||
} else {
|
||||
joint_one_bone_idx = p_bone_idx;
|
||||
}
|
||||
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
int SkeletonModification2DTwoBoneIK::get_joint_one_bone_idx() const {
|
||||
return joint_one_bone_idx;
|
||||
}
|
||||
|
||||
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!");
|
||||
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));
|
||||
} else {
|
||||
WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint two...");
|
||||
joint_two_bone_idx = p_bone_idx;
|
||||
}
|
||||
} else {
|
||||
joint_two_bone_idx = p_bone_idx;
|
||||
}
|
||||
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
int SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx() const {
|
||||
return joint_two_bone_idx;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void SkeletonModification2DTwoBoneIK::set_editor_draw_min_max(bool p_draw) {
|
||||
editor_draw_min_max = p_draw;
|
||||
}
|
||||
|
||||
bool SkeletonModification2DTwoBoneIK::get_editor_draw_min_max() const {
|
||||
return editor_draw_min_max;
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
void SkeletonModification2DTwoBoneIK::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DTwoBoneIK::set_target_node);
|
||||
ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DTwoBoneIK::get_target_node);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_target_minimum_distance", "minimum_distance"), &SkeletonModification2DTwoBoneIK::set_target_minimum_distance);
|
||||
ClassDB::bind_method(D_METHOD("get_target_minimum_distance"), &SkeletonModification2DTwoBoneIK::get_target_minimum_distance);
|
||||
ClassDB::bind_method(D_METHOD("set_target_maximum_distance", "maximum_distance"), &SkeletonModification2DTwoBoneIK::set_target_maximum_distance);
|
||||
ClassDB::bind_method(D_METHOD("get_target_maximum_distance"), &SkeletonModification2DTwoBoneIK::get_target_maximum_distance);
|
||||
ClassDB::bind_method(D_METHOD("set_flip_bend_direction", "flip_direction"), &SkeletonModification2DTwoBoneIK::set_flip_bend_direction);
|
||||
ClassDB::bind_method(D_METHOD("get_flip_bend_direction"), &SkeletonModification2DTwoBoneIK::get_flip_bend_direction);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_joint_one_bone2d_node", "bone2d_node"), &SkeletonModification2DTwoBoneIK::set_joint_one_bone2d_node);
|
||||
ClassDB::bind_method(D_METHOD("get_joint_one_bone2d_node"), &SkeletonModification2DTwoBoneIK::get_joint_one_bone2d_node);
|
||||
ClassDB::bind_method(D_METHOD("set_joint_one_bone_idx", "bone_idx"), &SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx);
|
||||
ClassDB::bind_method(D_METHOD("get_joint_one_bone_idx"), &SkeletonModification2DTwoBoneIK::get_joint_one_bone_idx);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_joint_two_bone2d_node", "bone2d_node"), &SkeletonModification2DTwoBoneIK::set_joint_two_bone2d_node);
|
||||
ClassDB::bind_method(D_METHOD("get_joint_two_bone2d_node"), &SkeletonModification2DTwoBoneIK::get_joint_two_bone2d_node);
|
||||
ClassDB::bind_method(D_METHOD("set_joint_two_bone_idx", "bone_idx"), &SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx);
|
||||
ClassDB::bind_method(D_METHOD("get_joint_two_bone_idx"), &SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_minimum_distance", PROPERTY_HINT_RANGE, "0,100000000,0.01,suffix:px"), "set_target_minimum_distance", "get_target_minimum_distance");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_maximum_distance", PROPERTY_HINT_NONE, "0,100000000,0.01,suffix:px"), "set_target_maximum_distance", "get_target_maximum_distance");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_bend_direction", PROPERTY_HINT_NONE, ""), "set_flip_bend_direction", "get_flip_bend_direction");
|
||||
ADD_GROUP("", "");
|
||||
}
|
||||
|
||||
SkeletonModification2DTwoBoneIK::SkeletonModification2DTwoBoneIK() {
|
||||
stack = nullptr;
|
||||
is_setup = false;
|
||||
enabled = true;
|
||||
editor_draw_gizmo = true;
|
||||
}
|
||||
|
||||
SkeletonModification2DTwoBoneIK::~SkeletonModification2DTwoBoneIK() {
|
||||
}
|
104
scene/resources/2d/skeleton/skeleton_modification_2d_twoboneik.h
Normal file
104
scene/resources/2d/skeleton/skeleton_modification_2d_twoboneik.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_2d_twoboneik.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/2d/skeleton_2d.h"
|
||||
#include "scene/resources/2d/skeleton/skeleton_modification_2d.h"
|
||||
|
||||
///////////////////////////////////////
|
||||
// SkeletonModification2DJIGGLE
|
||||
///////////////////////////////////////
|
||||
|
||||
class SkeletonModification2DTwoBoneIK : public SkeletonModification2D {
|
||||
GDCLASS(SkeletonModification2DTwoBoneIK, SkeletonModification2D);
|
||||
|
||||
private:
|
||||
NodePath target_node;
|
||||
ObjectID target_node_cache;
|
||||
float target_minimum_distance = 0;
|
||||
float target_maximum_distance = 0;
|
||||
bool flip_bend_direction = false;
|
||||
|
||||
NodePath joint_one_bone2d_node;
|
||||
ObjectID joint_one_bone2d_node_cache;
|
||||
int joint_one_bone_idx = -1;
|
||||
|
||||
NodePath joint_two_bone2d_node;
|
||||
ObjectID joint_two_bone2d_node_cache;
|
||||
int joint_two_bone_idx = -1;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool editor_draw_min_max = false;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
void update_target_cache();
|
||||
void update_joint_one_bone2d_cache();
|
||||
void update_joint_two_bone2d_cache();
|
||||
|
||||
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<PropertyInfo> *p_list) const;
|
||||
|
||||
public:
|
||||
void _execute(float p_delta) override;
|
||||
void _setup_modification(SkeletonModificationStack2D *p_stack) override;
|
||||
void _draw_editor_gizmo() override;
|
||||
|
||||
void set_target_node(const NodePath &p_target_node);
|
||||
NodePath get_target_node() const;
|
||||
|
||||
void set_target_minimum_distance(float p_minimum_distance);
|
||||
float get_target_minimum_distance() const;
|
||||
void set_target_maximum_distance(float p_maximum_distance);
|
||||
float get_target_maximum_distance() const;
|
||||
void set_flip_bend_direction(bool p_flip_direction);
|
||||
bool get_flip_bend_direction() const;
|
||||
|
||||
void set_joint_one_bone2d_node(const NodePath &p_node);
|
||||
NodePath get_joint_one_bone2d_node() const;
|
||||
void set_joint_one_bone_idx(int p_bone_idx);
|
||||
int get_joint_one_bone_idx() const;
|
||||
|
||||
void set_joint_two_bone2d_node(const NodePath &p_node);
|
||||
NodePath get_joint_two_bone2d_node() const;
|
||||
void set_joint_two_bone_idx(int p_bone_idx);
|
||||
int get_joint_two_bone_idx() const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void set_editor_draw_min_max(bool p_draw);
|
||||
bool get_editor_draw_min_max() const;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
SkeletonModification2DTwoBoneIK();
|
||||
~SkeletonModification2DTwoBoneIK();
|
||||
};
|
270
scene/resources/2d/skeleton/skeleton_modification_stack_2d.cpp
Normal file
270
scene/resources/2d/skeleton/skeleton_modification_stack_2d.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_stack_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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<PropertyInfo> *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_ALWAYS_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 false;
|
||||
}
|
||||
|
||||
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 false;
|
||||
}
|
||||
|
||||
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_null()) {
|
||||
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_null()) {
|
||||
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_null()) {
|
||||
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_null()) {
|
||||
continue;
|
||||
}
|
||||
modifications.get(i)->set_enabled(p_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<SkeletonModification2D> 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<SkeletonModification2D> p_mod) {
|
||||
ERR_FAIL_COND(p_mod.is_null());
|
||||
|
||||
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<SkeletonModification2D> p_mod) {
|
||||
ERR_FAIL_INDEX(p_mod_idx, modifications.size());
|
||||
|
||||
if (p_mod.is_null()) {
|
||||
modifications.write[p_mod_idx] = Ref<SkeletonModification2D>();
|
||||
} else {
|
||||
modifications.write[p_mod_idx] = p_mod;
|
||||
p_mod->_setup_modification(this);
|
||||
}
|
||||
|
||||
#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() {
|
||||
}
|
95
scene/resources/2d/skeleton/skeleton_modification_stack_2d.h
Normal file
95
scene/resources/2d/skeleton/skeleton_modification_stack_2d.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/**************************************************************************/
|
||||
/* skeleton_modification_stack_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/resource.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<PropertyInfo> *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<Ref<SkeletonModification2D>> modifications;
|
||||
|
||||
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<SkeletonModification2D> get_modification(int p_mod_idx) const;
|
||||
void add_modification(Ref<SkeletonModification2D> p_mod);
|
||||
void delete_modification(int p_mod_idx);
|
||||
void set_modification(int p_mod_idx, Ref<SkeletonModification2D> 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();
|
||||
};
|
Reference in New Issue
Block a user