From 77014502ffabe5c2f23638e75743bd828f46c957 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Mon, 3 Aug 2020 14:02:24 -0400 Subject: [PATCH 01/34] Initial groundwork (squashed from verbose branch: https://github.com/TwistedTwigleg/godot/tree/GSOC_2020_Working_Branch_2D_IK_VERBOSE_ARCHIVE ) Changes: * Transform2D: Added looking_at function * EditorSettings: Changed editor bone colors so they are transparent by default * CanvasItemEditor: Removed all code relating to drawing the Bone2D gizmo * CanvasItemEditor: Removed the code that made CanvasItemEditor bones and removed IK code that only works on the CanvasItemEditor bones. * Polygon2DEditorPlugin: Removed the use of get_default_length with get_length for Bone2D, as get_default_length is deprecated. * RegisterSceneTypes: Registered SkeletonModificationStack2D and SkeletonModification2D * Bone2D: Added code so the Bone2D node draws its own gizmo * Bone2D: Added properties for bone length and angle. Deprecated default_length. Added functions as required. * Skeleton2D: Added code to hold a SkeletonModificationStack2D. Added functions as required * Skeleton2D: Added a local_pose_override to bones in Skeleton2D and added the necessary functions and functionality to make it work. * SkeletonModificationStack2D: Added full implementation of resource. * SkeletonModification2D: Added full implementation of resource. NOTE: This commit is squashed from the following verbose branch: https://github.com/TwistedTwigleg/godot/tree/GSOC_2020_Working_Branch_2D_IK_VERBOSE_ARCHIVE --- core/math/transform_2d.cpp | 7 + core/math/transform_2d.h | 2 + editor/editor_settings.cpp | 10 +- editor/plugins/canvas_item_editor_plugin.cpp | 617 ++----------------- editor/plugins/canvas_item_editor_plugin.h | 22 +- editor/plugins/polygon_2d_editor_plugin.cpp | 2 +- scene/2d/skeleton_2d.cpp | 476 +++++++++++++- scene/2d/skeleton_2d.h | 52 ++ scene/register_scene_types.cpp | 4 + scene/resources/skeleton_modification_2d.cpp | 315 ++++++++++ scene/resources/skeleton_modification_2d.h | 126 ++++ 11 files changed, 1041 insertions(+), 592 deletions(-) create mode 100644 scene/resources/skeleton_modification_2d.cpp create mode 100644 scene/resources/skeleton_modification_2d.h diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 4a521b96ae1f..000446504e46 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -158,6 +158,13 @@ bool Transform2D::is_equal_approx(const Transform2D &p_transform) const { return elements[0].is_equal_approx(p_transform.elements[0]) && elements[1].is_equal_approx(p_transform.elements[1]) && elements[2].is_equal_approx(p_transform.elements[2]); } +Transform2D Transform2D::looking_at(const Vector2 target) { + Transform2D return_trans = Transform2D(get_rotation(), get_origin()); + Vector2 target_position = affine_inverse().xform(target); + return_trans.set_rotation(return_trans.get_rotation() + (target_position * get_scale()).angle()); + return return_trans; +} + bool Transform2D::operator==(const Transform2D &p_transform) const { for (int i = 0; i < 3; i++) { if (elements[i] != p_transform.elements[i]) { diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index 327d0f244fef..500873718cd0 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -100,6 +100,8 @@ struct Transform2D { Transform2D orthonormalized() const; bool is_equal_approx(const Transform2D &p_transform) const; + Transform2D looking_at(const Vector2 target); + bool operator==(const Transform2D &p_transform) const; bool operator!=(const Transform2D &p_transform) const; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 4ddae7c06296..ccc26b6642b5 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -691,11 +691,11 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { _initial_set("editors/2d/guides_color", Color(0.6, 0.0, 0.8)); _initial_set("editors/2d/smart_snapping_line_color", Color(0.9, 0.1, 0.1)); _initial_set("editors/2d/bone_width", 5); - _initial_set("editors/2d/bone_color1", Color(1.0, 1.0, 1.0, 0.9)); - _initial_set("editors/2d/bone_color2", Color(0.6, 0.6, 0.6, 0.9)); - _initial_set("editors/2d/bone_selected_color", Color(0.9, 0.45, 0.45, 0.9)); - _initial_set("editors/2d/bone_ik_color", Color(0.9, 0.9, 0.45, 0.9)); - _initial_set("editors/2d/bone_outline_color", Color(0.35, 0.35, 0.35)); + _initial_set("editors/2d/bone_color1", Color(1.0, 1.0, 1.0, 0.7)); + _initial_set("editors/2d/bone_color2", Color(0.6, 0.6, 0.6, 0.7)); + _initial_set("editors/2d/bone_selected_color", Color(0.9, 0.45, 0.45, 0.7)); + _initial_set("editors/2d/bone_ik_color", Color(0.9, 0.9, 0.45, 0.7)); + _initial_set("editors/2d/bone_outline_color", Color(0.35, 0.35, 0.35, 0.5)); _initial_set("editors/2d/bone_outline_size", 2); _initial_set("editors/2d/viewport_border_color", Color(0.4, 0.4, 1.0, 0.4)); _initial_set("editors/2d/constrain_editor_view", true); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index d4e06aa9caf9..2974c4a9ef0f 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -664,93 +664,6 @@ void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_Sel } } -void CanvasItemEditor::_get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items) { - Point2 screen_pos = transform.xform(p_pos); - - for (Map::Element *E = bone_list.front(); E; E = E->next()) { - Node2D *from_node = Object::cast_to(ObjectDB::get_instance(E->key().from)); - - Vector bone_shape; - if (!_get_bone_shape(&bone_shape, nullptr, E)) { - continue; - } - - // Check if the point is inside the Polygon2D - if (Geometry2D::is_point_in_polygon(screen_pos, bone_shape)) { - // Check if the item is already in the list - bool duplicate = false; - for (int i = 0; i < r_items.size(); i++) { - if (r_items[i].item == from_node) { - duplicate = true; - break; - } - } - if (duplicate) { - continue; - } - - // Else, add it - _SelectResult res; - res.item = from_node; - res.z_index = from_node ? from_node->get_z_index() : 0; - res.has_z = from_node; - r_items.push_back(res); - } - } -} - -bool CanvasItemEditor::_get_bone_shape(Vector *shape, Vector *outline_shape, Map::Element *bone) { - int bone_width = EditorSettings::get_singleton()->get("editors/2d/bone_width"); - int bone_outline_width = EditorSettings::get_singleton()->get("editors/2d/bone_outline_size"); - - Node2D *from_node = Object::cast_to(ObjectDB::get_instance(bone->key().from)); - Node2D *to_node = Object::cast_to(ObjectDB::get_instance(bone->key().to)); - - if (!from_node) { - return false; - } - if (!from_node->is_inside_tree()) { - return false; //may have been removed - } - - if (!to_node && bone->get().length == 0) { - return false; - } - - Vector2 from = transform.xform(from_node->get_global_position()); - Vector2 to; - - if (to_node) { - to = transform.xform(to_node->get_global_position()); - } else { - to = transform.xform(from_node->get_global_transform().xform(Vector2(bone->get().length, 0))); - } - - Vector2 rel = to - from; - Vector2 relt = rel.orthogonal().normalized() * bone_width; - Vector2 reln = rel.normalized(); - Vector2 reltn = relt.normalized(); - - if (shape) { - shape->clear(); - shape->push_back(from); - shape->push_back(from + rel * 0.2 + relt); - shape->push_back(to); - shape->push_back(from + rel * 0.2 - relt); - } - - if (outline_shape) { - outline_shape->clear(); - outline_shape->push_back(from + (-reln - reltn) * bone_outline_width); - outline_shape->push_back(from + (-reln + reltn) * bone_outline_width); - outline_shape->push_back(from + rel * 0.2 + relt + reltn * bone_outline_width); - outline_shape->push_back(to + (reln + reltn) * bone_outline_width); - outline_shape->push_back(to + (reln - reltn) * bone_outline_width); - outline_shape->push_back(from + rel * 0.2 - relt - reltn * bone_outline_width); - } - return true; -} - void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List *r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { if (!p_node) { return; @@ -880,50 +793,6 @@ Vector2 CanvasItemEditor::_position_to_anchor(const Control *p_control, Vector2 return output; } -void CanvasItemEditor::_save_canvas_item_ik_chain(const CanvasItem *p_canvas_item, List *p_bones_length, List *p_bones_state) { - if (p_bones_length) { - *p_bones_length = List(); - } - if (p_bones_state) { - *p_bones_state = List(); - } - - const Node2D *bone = Object::cast_to(p_canvas_item); - if (bone && bone->has_meta("_edit_bone_")) { - // Check if we have an IK chain - List bone_ik_list; - bool ik_found = false; - bone = Object::cast_to(bone->get_parent()); - while (bone) { - bone_ik_list.push_back(bone); - if (bone->has_meta("_edit_ik_")) { - ik_found = true; - break; - } else if (!bone->has_meta("_edit_bone_")) { - break; - } - bone = Object::cast_to(bone->get_parent()); - } - - //Save the bone state and length if we have an IK chain - if (ik_found) { - bone = Object::cast_to(p_canvas_item); - Transform2D bone_xform = bone->get_global_transform(); - for (List::Element *bone_E = bone_ik_list.front(); bone_E; bone_E = bone_E->next()) { - bone_xform = bone_xform * bone->get_transform().affine_inverse(); - const Node2D *parent_bone = bone_E->get(); - if (p_bones_length) { - p_bones_length->push_back(parent_bone->get_global_transform().get_origin().distance_to(bone->get_global_position())); - } - if (p_bones_state) { - p_bones_state->push_back(parent_bone->_edit_get_state()); - } - bone = parent_bone; - } - } - } -} - void CanvasItemEditor::_save_canvas_item_state(List p_canvas_items, bool save_bones) { for (List::Element *E = p_canvas_items.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); @@ -936,31 +805,15 @@ void CanvasItemEditor::_save_canvas_item_state(List p_canvas_items } else { se->pre_drag_rect = Rect2(); } - - // If we have a bone, save the state of all nodes in the IK chain - _save_canvas_item_ik_chain(canvas_item, &(se->pre_drag_bones_length), &(se->pre_drag_bones_undo_state)); } } } -void CanvasItemEditor::_restore_canvas_item_ik_chain(CanvasItem *p_canvas_item, const List *p_bones_state) { - CanvasItem *canvas_item = p_canvas_item; - for (const List::Element *E = p_bones_state->front(); E; E = E->next()) { - canvas_item = Object::cast_to(canvas_item->get_parent()); - canvas_item->_edit_set_state(E->get()); - } -} - void CanvasItemEditor::_restore_canvas_item_state(List p_canvas_items, bool restore_bones) { for (List::Element *E = drag_selection.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data(canvas_item); - if (se) { - canvas_item->_edit_set_state(se->undo_state); - if (restore_bones) { - _restore_canvas_item_ik_chain(canvas_item, &(se->pre_drag_bones_undo_state)); - } - } + canvas_item->_edit_set_state(se->undo_state); } } @@ -1490,76 +1343,6 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref &p_event) { return false; } -void CanvasItemEditor::_solve_IK(Node2D *leaf_node, Point2 target_position) { - CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data(leaf_node); - if (se) { - int nb_bones = se->pre_drag_bones_undo_state.size(); - if (nb_bones > 0) { - // Build the node list - Point2 leaf_pos = target_position; - - List joints_list; - List joints_pos; - Node2D *joint = leaf_node; - Transform2D joint_transform = leaf_node->get_global_transform_with_canvas(); - for (int i = 0; i < nb_bones + 1; i++) { - joints_list.push_back(joint); - joints_pos.push_back(joint_transform.get_origin()); - joint_transform = joint_transform * joint->get_transform().affine_inverse(); - joint = Object::cast_to(joint->get_parent()); - } - Point2 root_pos = joints_list.back()->get()->get_global_transform_with_canvas().get_origin(); - - // Restraints the node to a maximum distance is necessary - float total_len = 0; - for (List::Element *E = se->pre_drag_bones_length.front(); E; E = E->next()) { - total_len += E->get(); - } - if ((root_pos.distance_to(leaf_pos)) > total_len) { - Vector2 rel = leaf_pos - root_pos; - rel = rel.normalized() * total_len; - leaf_pos = root_pos + rel; - } - joints_pos[0] = leaf_pos; - - // Run the solver - int solver_iterations = 64; - float solver_k = 0.3; - - // Build the position list - for (int i = 0; i < solver_iterations; i++) { - // Handle the leaf joint - int node_id = 0; - for (List::Element *E = se->pre_drag_bones_length.front(); E; E = E->next()) { - Vector2 direction = (joints_pos[node_id + 1] - joints_pos[node_id]).normalized(); - int len = E->get(); - if (E == se->pre_drag_bones_length.front()) { - joints_pos[1] = joints_pos[1].lerp(joints_pos[0] + len * direction, solver_k); - } else if (E == se->pre_drag_bones_length.back()) { - joints_pos[node_id] = joints_pos[node_id].lerp(joints_pos[node_id + 1] - len * direction, solver_k); - } else { - Vector2 center = (joints_pos[node_id + 1] + joints_pos[node_id]) / 2.0; - joints_pos[node_id] = joints_pos[node_id].lerp(center - (direction * len) / 2.0, solver_k); - joints_pos[node_id + 1] = joints_pos[node_id + 1].lerp(center + (direction * len) / 2.0, solver_k); - } - node_id++; - } - } - - // Set the position - for (int node_id = joints_list.size() - 1; node_id > 0; node_id--) { - Point2 current = (joints_list[node_id - 1]->get_global_position() - joints_list[node_id]->get_global_position()).normalized(); - Point2 target = (joints_pos[node_id - 1] - joints_list[node_id]->get_global_position()).normalized(); - float rot = current.angle_to(target); - if (joints_list[node_id]->get_global_transform().basis_determinant() < 0) { - rot = -rot; - } - joints_list[node_id]->rotate(rot); - } - } - } -} - bool CanvasItemEditor::_gui_input_rotate(const Ref &p_event) { Ref b = p_event; Ref m = p_event; @@ -2201,14 +1984,6 @@ bool CanvasItemEditor::_gui_input_move(const Ref &p_event) { if (drag_type == DRAG_MOVE || drag_type == DRAG_MOVE_X || drag_type == DRAG_MOVE_Y) { // Move the nodes if (m.is_valid()) { - // Save the ik chain for reapplying before IK solve - Vector> all_bones_ik_states; - for (List::Element *E = drag_selection.front(); E; E = E->next()) { - List bones_ik_states; - _save_canvas_item_ik_chain(E->get(), nullptr, &bones_ik_states); - all_bones_ik_states.push_back(bones_ik_states); - } - _restore_canvas_item_state(drag_selection, true); drag_to = transform.affine_inverse().xform(m->get_position()); @@ -2237,25 +2012,12 @@ bool CanvasItemEditor::_gui_input_move(const Ref &p_event) { } } - bool force_no_IK = m->get_alt(); int index = 0; for (List::Element *E = drag_selection.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); - CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data(canvas_item); - if (se) { - Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform(); - - Node2D *node2d = Object::cast_to(canvas_item); - if (node2d && se->pre_drag_bones_undo_state.size() > 0 && !force_no_IK) { - real_t initial_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation(); - _restore_canvas_item_ik_chain(node2d, &(all_bones_ik_states[index])); - real_t final_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation(); - node2d->rotate(initial_leaf_node_rotation - final_leaf_node_rotation); - _solve_IK(node2d, new_pos); - } else { - canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos)); - } - } + Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform(); + + canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos)); index++; } return true; @@ -2318,14 +2080,6 @@ bool CanvasItemEditor::_gui_input_move(const Ref &p_event) { } if (drag_selection.size() > 0) { - // Save the ik chain for reapplying before IK solve - Vector> all_bones_ik_states; - for (List::Element *E = drag_selection.front(); E; E = E->next()) { - List bones_ik_states; - _save_canvas_item_ik_chain(E->get(), nullptr, &bones_ik_states); - all_bones_ik_states.push_back(bones_ik_states); - } - _restore_canvas_item_state(drag_selection, true); bool move_local_base = k->get_alt(); @@ -2377,21 +2131,9 @@ bool CanvasItemEditor::_gui_input_move(const Ref &p_event) { int index = 0; for (List::Element *E = drag_selection.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); - CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data(canvas_item); - if (se) { - Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform(); - - Node2D *node2d = Object::cast_to(canvas_item); - if (node2d && se->pre_drag_bones_undo_state.size() > 0) { - real_t initial_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation(); - _restore_canvas_item_ik_chain(node2d, &(all_bones_ik_states[index])); - real_t final_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation(); - node2d->rotate(initial_leaf_node_rotation - final_leaf_node_rotation); - _solve_IK(node2d, new_pos); - } else { - canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos)); - } - } + Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform(); + + canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos)); index++; } } @@ -2517,18 +2259,12 @@ bool CanvasItemEditor::_gui_input_select(const Ref &p_event) { // Find the item to select CanvasItem *canvas_item = nullptr; - // Retrieve the bones Vector<_SelectResult> selection = Vector<_SelectResult>(); - _get_bones_at_pos(click, selection); - if (!selection.is_empty()) { + // Retrieve the canvas items + selection = Vector<_SelectResult>(); + _get_canvas_items_at_pos(click, selection); + if (!selection.empty()) { canvas_item = selection[0].item; - } else { - // Retrieve the canvas items - selection = Vector<_SelectResult>(); - _get_canvas_items_at_pos(click, selection); - if (!selection.is_empty()) { - canvas_item = selection[0].item; - } } if (!canvas_item) { @@ -3724,65 +3460,6 @@ void CanvasItemEditor::_draw_axis() { } } -void CanvasItemEditor::_draw_bones() { - RID ci = viewport->get_canvas_item(); - - if (skeleton_show_bones) { - Color bone_color1 = EditorSettings::get_singleton()->get("editors/2d/bone_color1"); - Color bone_color2 = EditorSettings::get_singleton()->get("editors/2d/bone_color2"); - Color bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color"); - Color bone_outline_color = EditorSettings::get_singleton()->get("editors/2d/bone_outline_color"); - Color bone_selected_color = EditorSettings::get_singleton()->get("editors/2d/bone_selected_color"); - - for (Map::Element *E = bone_list.front(); E; E = E->next()) { - Vector bone_shape; - Vector bone_shape_outline; - if (!_get_bone_shape(&bone_shape, &bone_shape_outline, E)) { - continue; - } - - Node2D *from_node = Object::cast_to(ObjectDB::get_instance(E->key().from)); - if (!from_node->is_visible_in_tree()) { - continue; - } - - Vector colors; - if (from_node->has_meta("_edit_ik_")) { - colors.push_back(bone_ik_color); - colors.push_back(bone_ik_color); - colors.push_back(bone_ik_color); - colors.push_back(bone_ik_color); - } else { - colors.push_back(bone_color1); - colors.push_back(bone_color2); - colors.push_back(bone_color1); - colors.push_back(bone_color2); - } - - Vector outline_colors; - - if (editor_selection->is_selected(from_node)) { - outline_colors.push_back(bone_selected_color); - outline_colors.push_back(bone_selected_color); - outline_colors.push_back(bone_selected_color); - outline_colors.push_back(bone_selected_color); - outline_colors.push_back(bone_selected_color); - outline_colors.push_back(bone_selected_color); - } else { - outline_colors.push_back(bone_outline_color); - outline_colors.push_back(bone_outline_color); - outline_colors.push_back(bone_outline_color); - outline_colors.push_back(bone_outline_color); - outline_colors.push_back(bone_outline_color); - outline_colors.push_back(bone_outline_color); - } - - RenderingServer::get_singleton()->canvas_item_add_polygon(ci, bone_shape_outline, outline_colors); - RenderingServer::get_singleton()->canvas_item_add_primitive(ci, bone_shape, colors, Vector(), RID()); - } - } -} - void CanvasItemEditor::_draw_invisible_nodes_positions(Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { ERR_FAIL_COND(!p_node); @@ -3898,72 +3575,6 @@ void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p } } -bool CanvasItemEditor::_build_bones_list(Node *p_node) { - ERR_FAIL_COND_V(!p_node, false); - - bool has_child_bones = false; - - for (int i = 0; i < p_node->get_child_count(); i++) { - if (_build_bones_list(p_node->get_child(i))) { - has_child_bones = true; - } - } - - CanvasItem *canvas_item = Object::cast_to(p_node); - Node *scene = editor->get_edited_scene(); - if (!canvas_item || !canvas_item->is_visible() || (canvas_item != scene && canvas_item->get_owner() != scene && canvas_item != scene->get_deepest_editable_node(canvas_item))) { - return false; - } - - Node *parent = canvas_item->get_parent(); - - if (Object::cast_to(canvas_item)) { - if (Object::cast_to(parent)) { - // Add as bone->parent relationship - BoneKey bk; - bk.from = parent->get_instance_id(); - bk.to = canvas_item->get_instance_id(); - if (!bone_list.has(bk)) { - BoneList b; - b.length = 0; - bone_list[bk] = b; - } - - bone_list[bk].last_pass = bone_last_frame; - } - - if (!has_child_bones) { - // Add a last bone if the Bone2D has no Bone2D child - BoneKey bk; - bk.from = canvas_item->get_instance_id(); - bk.to = ObjectID(); - if (!bone_list.has(bk)) { - BoneList b; - b.length = 0; - bone_list[bk] = b; - } - bone_list[bk].last_pass = bone_last_frame; - } - - return true; - } - - if (canvas_item->has_meta("_edit_bone_")) { - // Add a "custom bone" - BoneKey bk; - bk.from = parent->get_instance_id(); - bk.to = canvas_item->get_instance_id(); - if (!bone_list.has(bk)) { - BoneList b; - b.length = 0; - bone_list[bk] = b; - } - bone_list[bk].last_pass = bone_last_frame; - } - - return false; -} - void CanvasItemEditor::_draw_viewport() { // Update the transform transform = Transform2D(); @@ -4023,7 +3634,6 @@ void CanvasItemEditor::_draw_viewport() { force_over_plugin_list->forward_canvas_force_draw_over_viewport(viewport); } - _draw_bones(); if (show_rulers) { _draw_rulers(); } @@ -4149,8 +3759,8 @@ void CanvasItemEditor::_notification(int p_what) { } Bone2D *bone = Object::cast_to(b); - if (bone && bone->get_default_length() != E->get().length) { - E->get().length = bone->get_default_length(); + if (bone && bone->get_length() != E->get().length) { + E->get().length = bone->get_length(); viewport->update(); } } @@ -4165,18 +3775,11 @@ void CanvasItemEditor::_notification(int p_what) { AnimationPlayerEditor::singleton->get_track_editor()->connect("visibility_changed", callable_mp(this, &CanvasItemEditor::_keying_changed)); _keying_changed(); - get_tree()->connect("node_added", callable_mp(this, &CanvasItemEditor::_tree_changed), varray()); - get_tree()->connect("node_removed", callable_mp(this, &CanvasItemEditor::_tree_changed), varray()); } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { select_sb->set_texture(get_theme_icon("EditorRect2D", "EditorIcons")); } - if (p_what == NOTIFICATION_EXIT_TREE) { - get_tree()->disconnect("node_added", callable_mp(this, &CanvasItemEditor::_tree_changed)); - get_tree()->disconnect("node_removed", callable_mp(this, &CanvasItemEditor::_tree_changed)); - } - if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { select_button->set_icon(get_theme_icon("ToolSelect", "EditorIcons")); list_select_button->set_icon(get_theme_icon("ListSelect", "EditorIcons")); @@ -4314,46 +3917,6 @@ void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { } } -void CanvasItemEditor::_queue_update_bone_list() { - if (bone_list_dirty) { - return; - } - - call_deferred("_update_bone_list"); - bone_list_dirty = true; -} - -void CanvasItemEditor::_update_bone_list() { - bone_last_frame++; - - if (editor->get_edited_scene()) { - _build_bones_list(editor->get_edited_scene()); - } - - List::Element *> bone_to_erase; - for (Map::Element *E = bone_list.front(); E; E = E->next()) { - if (E->get().last_pass != bone_last_frame) { - bone_to_erase.push_back(E); - continue; - } - - Node *node = Object::cast_to(ObjectDB::get_instance(E->key().from)); - if (!node || !node->is_inside_tree() || (node != get_tree()->get_edited_scene_root() && !get_tree()->get_edited_scene_root()->is_a_parent_of(node))) { - bone_to_erase.push_back(E); - continue; - } - } - while (bone_to_erase.size()) { - bone_list.erase(bone_to_erase.front()->get()); - bone_to_erase.pop_front(); - } - bone_list_dirty = false; -} - -void CanvasItemEditor::_tree_changed(Node *) { - _queue_update_bone_list(); -} - void CanvasItemEditor::_update_scrollbars() { updating_scroll = true; @@ -4369,8 +3932,6 @@ void CanvasItemEditor::_update_scrollbars() { Size2 screen_rect = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); Rect2 local_rect = Rect2(Point2(), viewport->get_size() - Size2(vmin.width, hmin.height)); - _queue_update_bone_list(); - // Calculate scrollable area. Rect2 canvas_item_rect = Rect2(Point2(), screen_rect); if (editor->get_edited_scene()) { @@ -4881,10 +4442,19 @@ void CanvasItemEditor::_popup_callback(int p_op) { snap_dialog->popup_centered(Size2(220, 160) * EDSCALE); } break; case SKELETON_SHOW_BONES: { - skeleton_show_bones = !skeleton_show_bones; - int idx = skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES); - skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones); - viewport->update(); + List selection = editor_selection->get_selected_node_list(); + for (List::Element *E = selection.front(); E; E = E->next()) { + // Add children nodes so they are processed + for (int child = 0; child < E->get()->get_child_count(); child++) { + selection.push_back(E->get()->get_child(child)); + } + + Bone2D *bone_2d = Object::cast_to(E->get()); + if (!bone_2d || !bone_2d->is_inside_tree()) { + continue; + } + bone_2d->_editor_set_show_bone_gizmo(!bone_2d->_editor_get_show_bone_gizmo()); + } } break; case SHOW_HELPERS: { show_helpers = !show_helpers; @@ -5233,107 +4803,45 @@ void CanvasItemEditor::_popup_callback(int p_op) { } break; case SKELETON_MAKE_BONES: { Map &selection = editor_selection->get_selection(); + Node *editor_root = EditorNode::get_singleton()->get_edited_scene()->get_tree()->get_edited_scene_root(); - undo_redo->create_action(TTR("Create Custom Bone(s) from Node(s)")); + undo_redo->create_action(TTR("Create Custom Bone2D(s) from Node(s)")); for (Map::Element *E = selection.front(); E; E = E->next()) { Node2D *n2d = Object::cast_to(E->key()); - if (!n2d) { - continue; - } - if (!n2d->is_visible_in_tree()) { - continue; - } - if (!n2d->get_parent_item()) { - continue; - } - if (n2d->has_meta("_edit_bone_") && n2d->get_meta("_edit_bone_")) { - continue; - } - undo_redo->add_do_method(n2d, "set_meta", "_edit_bone_", true); - undo_redo->add_undo_method(n2d, "remove_meta", "_edit_bone_"); - } - undo_redo->add_do_method(this, "_queue_update_bone_list"); - undo_redo->add_undo_method(this, "_queue_update_bone_list"); - undo_redo->add_do_method(viewport, "update"); - undo_redo->add_undo_method(viewport, "update"); - undo_redo->commit_action(); + Bone2D *new_bone = memnew(Bone2D); + String new_bone_name = n2d->get_name(); + new_bone_name += "Bone2D"; + new_bone->set_name(new_bone_name); + new_bone->set_transform(n2d->get_transform()); - } break; - case SKELETON_CLEAR_BONES: { - Map &selection = editor_selection->get_selection(); - - undo_redo->create_action(TTR("Clear Bones")); - for (Map::Element *E = selection.front(); E; E = E->next()) { - Node2D *n2d = Object::cast_to(E->key()); - if (!n2d) { - continue; - } - if (!n2d->is_visible_in_tree()) { + Node *n2d_parent = n2d->get_parent(); + if (!n2d_parent) { continue; } - if (!n2d->has_meta("_edit_bone_")) { - continue; - } - - undo_redo->add_do_method(n2d, "remove_meta", "_edit_bone_"); - undo_redo->add_undo_method(n2d, "set_meta", "_edit_bone_", n2d->get_meta("_edit_bone_")); - } - undo_redo->add_do_method(this, "_queue_update_bone_list"); - undo_redo->add_undo_method(this, "_queue_update_bone_list"); - undo_redo->add_do_method(viewport, "update"); - undo_redo->add_undo_method(viewport, "update"); - undo_redo->commit_action(); - - } break; - case SKELETON_SET_IK_CHAIN: { - List selection = editor_selection->get_selected_node_list(); - undo_redo->create_action(TTR("Make IK Chain")); - for (List::Element *E = selection.front(); E; E = E->next()) { - CanvasItem *canvas_item = Object::cast_to(E->get()); - if (!canvas_item || !canvas_item->is_visible_in_tree()) { - continue; - } - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) { - continue; - } - if (canvas_item->has_meta("_edit_ik_") && canvas_item->get_meta("_edit_ik_")) { - continue; - } + undo_redo->add_do_method(n2d_parent, "add_child", new_bone); + undo_redo->add_do_method(n2d_parent, "remove_child", n2d); + undo_redo->add_do_method(new_bone, "add_child", n2d); + undo_redo->add_do_method(n2d, "set_transform", Transform2D()); + undo_redo->add_do_method(this, "_set_owner_for_node_and_children", new_bone, editor_root); - undo_redo->add_do_method(canvas_item, "set_meta", "_edit_ik_", true); - undo_redo->add_undo_method(canvas_item, "remove_meta", "_edit_ik_"); + undo_redo->add_undo_method(new_bone, "remove_child", n2d); + undo_redo->add_undo_method(n2d_parent, "add_child", n2d); + undo_redo->add_undo_method(n2d, "set_transform", new_bone->get_transform()); + undo_redo->add_undo_method(new_bone, "queue_free"); + undo_redo->add_undo_method(this, "_set_owner_for_node_and_children", n2d, editor_root); } - undo_redo->add_do_method(viewport, "update"); - undo_redo->add_undo_method(viewport, "update"); undo_redo->commit_action(); } break; - case SKELETON_CLEAR_IK_CHAIN: { - Map &selection = editor_selection->get_selection(); - - undo_redo->create_action(TTR("Clear IK Chain")); - for (Map::Element *E = selection.front(); E; E = E->next()) { - CanvasItem *n2d = Object::cast_to(E->key()); - if (!n2d) { - continue; - } - if (!n2d->is_visible_in_tree()) { - continue; - } - if (!n2d->has_meta("_edit_ik_")) { - continue; - } - - undo_redo->add_do_method(n2d, "remove_meta", "_edit_ik_"); - undo_redo->add_undo_method(n2d, "set_meta", "_edit_ik_", n2d->get_meta("_edit_ik_")); - } - undo_redo->add_do_method(viewport, "update"); - undo_redo->add_undo_method(viewport, "update"); - undo_redo->commit_action(); + } +} - } break; +void CanvasItemEditor::_set_owner_for_node_and_children(Node *node, Node *owner) { + node->set_owner(owner); + for (int i = 0; i < node->get_child_count(); i++) { + _set_owner_for_node_and_children(node->get_child(i), owner); } } @@ -5405,12 +4913,11 @@ void CanvasItemEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_override_camera_button", "game_running"), &CanvasItemEditor::_update_override_camera_button); ClassDB::bind_method("_get_editor_data", &CanvasItemEditor::_get_editor_data); ClassDB::bind_method("_unhandled_key_input", &CanvasItemEditor::_unhandled_key_input); - ClassDB::bind_method("_queue_update_bone_list", &CanvasItemEditor::_update_bone_list); - ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list); - ClassDB::bind_method("_reset_create_position", &CanvasItemEditor::_reset_create_position); ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state); ClassDB::bind_method(D_METHOD("update_viewport"), &CanvasItemEditor::update_viewport); + ClassDB::bind_method("_set_owner_for_node_and_children", &CanvasItemEditor::_set_owner_for_node_and_children); + ADD_SIGNAL(MethodInfo("item_lock_status_changed")); ADD_SIGNAL(MethodInfo("item_group_status_changed")); } @@ -5447,7 +4954,6 @@ Dictionary CanvasItemEditor::get_state() const { state["snap_scale"] = snap_scale; state["snap_relative"] = snap_relative; state["snap_pixel"] = snap_pixel; - state["skeleton_show_bones"] = skeleton_show_bones; return state; } @@ -5615,12 +5121,6 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { snap_config_menu->get_popup()->set_item_checked(idx, snap_pixel); } - if (state.has("skeleton_show_bones")) { - skeleton_show_bones = state["skeleton_show_bones"]; - int idx = skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES); - skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones); - } - if (update_scrollbars) { _update_scrollbars(); } @@ -5703,8 +5203,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { selected_from_canvas = false; anchors_mode = false; - skeleton_show_bones = true; - drag_type = DRAG_NONE; drag_from = Vector2(); drag_to = Vector2(); @@ -5720,7 +5218,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { bone_last_frame = 0; - bone_list_dirty = false; tool = TOOL_SELECT; undo_redo = p_editor->get_undo_redo(); editor = p_editor; @@ -6008,13 +5505,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p = skeleton_menu->get_popup(); p->set_hide_on_checkable_item_selection(false); - p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_show_bones", TTR("Show Bones")), SKELETON_SHOW_BONES); - p->add_separator(); - p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_set_ik_chain", TTR("Make IK Chain")), SKELETON_SET_IK_CHAIN); - p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_ik_chain", TTR("Clear IK Chain")), SKELETON_CLEAR_IK_CHAIN); + p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_show_bones", TTR("Show Bones")), SKELETON_SHOW_BONES); p->add_separator(); - p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Custom Bone(s) from Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B), SKELETON_MAKE_BONES); - p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_bones", TTR("Clear Custom Bones")), SKELETON_CLEAR_BONES); + p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Bone2D Node(s) from Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B), SKELETON_MAKE_BONES); p->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_popup_callback)); hb->add_child(memnew(VSeparator)); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 62a9b1e1629c..bb648c681b2c 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -186,10 +186,7 @@ class CanvasItemEditor : public VBoxContainer { VIEW_FRAME_TO_SELECTION, PREVIEW_CANVAS_SCALE, SKELETON_MAKE_BONES, - SKELETON_CLEAR_BONES, - SKELETON_SHOW_BONES, - SKELETON_SET_IK_CHAIN, - SKELETON_CLEAR_IK_CHAIN + SKELETON_SHOW_BONES }; enum DragType { @@ -222,7 +219,6 @@ class CanvasItemEditor : public VBoxContainer { DRAG_KEY_MOVE }; - EditorSelection *editor_selection; bool selection_menu_additive_selection; Tool tool; @@ -280,7 +276,6 @@ class CanvasItemEditor : public VBoxContainer { bool snap_scale; bool snap_relative; bool snap_pixel; - bool skeleton_show_bones; bool key_pos; bool key_rot; bool key_scale; @@ -415,7 +410,6 @@ class CanvasItemEditor : public VBoxContainer { bool _is_node_movable(const Node *p_node, bool p_popup_warning = false); void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, bool p_allow_locked = false); - void _get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items); void _find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); bool _select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append); @@ -426,9 +420,7 @@ class CanvasItemEditor : public VBoxContainer { void _add_canvas_item(CanvasItem *p_canvas_item); - void _save_canvas_item_ik_chain(const CanvasItem *p_canvas_item, List *p_bones_length, List *p_bones_state); void _save_canvas_item_state(List p_canvas_items, bool save_bones = false); - void _restore_canvas_item_ik_chain(CanvasItem *p_canvas_item, const List *p_bones_state); void _restore_canvas_item_state(List p_canvas_items, bool restore_bones = false); void _commit_canvas_item_state(List p_canvas_items, String action_name, bool commit_bones = false); @@ -448,8 +440,6 @@ class CanvasItemEditor : public VBoxContainer { void _reset_create_position(); UndoRedo *undo_redo; - bool _build_bones_list(Node *p_node); - bool _get_bone_shape(Vector *shape, Vector *outline_shape, Map::Element *bone); List _get_edited_canvas_items(bool retreive_locked = false, bool remove_canvas_item_if_parent_in_selection = true); Rect2 _get_encompassing_rect_from_list(List p_list); @@ -479,7 +469,6 @@ class CanvasItemEditor : public VBoxContainer { void _draw_control_helpers(Control *control); void _draw_selection(); void _draw_axis(); - void _draw_bones(); void _draw_invisible_nodes_positions(Node *p_node, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); void _draw_locks_and_groups(Node *p_node, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); void _draw_hover(); @@ -506,8 +495,6 @@ class CanvasItemEditor : public VBoxContainer { void _focus_selection(int p_op); - void _solve_IK(Node2D *leaf_node, Point2 target_position); - SnapTarget snap_target[2]; Transform2D snap_transform; void _snap_if_closer_float( @@ -553,14 +540,11 @@ class CanvasItemEditor : public VBoxContainer { HSplitContainer *palette_split; VSplitContainer *bottom_split; - bool bone_list_dirty; - void _queue_update_bone_list(); - void _update_bone_list(); - void _tree_changed(Node *); - void _popup_warning_temporarily(Control *p_control, const float p_duration); void _popup_warning_depop(Control *p_control); + void _set_owner_for_node_and_children(Node *node, Node *owner); + friend class CanvasItemEditorPlugin; protected: diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 470d897dccb9..dd34ef2ba1c1 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -1144,7 +1144,7 @@ void Polygon2DEditor::_uv_draw() { if (!found_child) { //draw normally Transform2D bone_xform = node->get_global_transform().affine_inverse() * (skeleton->get_global_transform() * bone->get_skeleton_rest()); - Transform2D endpoint_xform = bone_xform * Transform2D(0, Vector2(bone->get_default_length(), 0)); + Transform2D endpoint_xform = bone_xform * Transform2D(0, Vector2(bone->get_length(), 0)); Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5); uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE)); diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 2d19d254b155..169e32eba8ce 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -29,6 +29,64 @@ /*************************************************************************/ #include "skeleton_2d.h" +#include "scene/resources/skeleton_modification_2d.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#include "editor/plugins/canvas_item_editor_plugin.h" +#endif //TOOLS_ENABLED + +bool Bone2D::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + + if (path.begins_with("auto_calculate_length_and_angle")) { + set_autocalculate_length_and_angle(p_value); + } else if (path.begins_with("length")) { + set_length(p_value); + } else if (path.begins_with("bone_angle")) { + set_bone_angle(Math::deg2rad(float(p_value))); + } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor_settings/show_bone_gizmo")) { + _editor_set_show_bone_gizmo(p_value); + } +#endif // TOOLS_ENABLED + + return true; +} + +bool Bone2D::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + + if (path.begins_with("auto_calculate_length_and_angle")) { + r_ret = get_autocalculate_length_and_angle(); + } else if (path.begins_with("length")) { + r_ret = get_length(); + } else if (path.begins_with("bone_angle")) { + r_ret = Math::rad2deg(get_bone_angle()); + } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor_settings/show_bone_gizmo")) { + r_ret = _editor_get_show_bone_gizmo(); + } +#endif // TOOLS_ENABLED + + return true; +} + +void Bone2D::_get_property_list(List *p_list) const { + p_list->push_back(PropertyInfo(Variant::BOOL, "auto_calculate_length_and_angle", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + if (!autocalculate_length_and_angle) { + p_list->push_back(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "1, 1024, 1", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::FLOAT, "bone_angle", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT)); + } + +#ifdef TOOLS_ENABLED + p_list->push_back(PropertyInfo(Variant::BOOL, "editor_settings/show_bone_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); +#endif // TOOLS_ENABLED +} void Bone2D::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { @@ -53,11 +111,35 @@ void Bone2D::_notification(int p_what) { skeleton->bones.push_back(bone); skeleton->_make_bone_setup_dirty(); } + +#ifdef TOOLS_ENABLED + // Only draw the gizmo in the editor! + if (Engine::get_singleton()->is_editor_hint() == false) { + return; + } + + update(); +#endif // TOOLS_ENABLED } if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { if (skeleton) { skeleton->_make_transform_dirty(); } +#ifdef TOOLS_ENABLED + // Only draw the gizmo in the editor! + if (Engine::get_singleton()->is_editor_hint() == false) { + return; + } + + update(); + + if (get_parent()) { + Bone2D *parent_bone = Object::cast_to(get_parent()); + if (parent_bone) { + parent_bone->update(); + } + } +#endif // TOOLS_ENABLED } if (p_what == NOTIFICATION_MOVED_IN_PARENT) { if (skeleton) { @@ -78,8 +160,191 @@ void Bone2D::_notification(int p_what) { } parent_bone = nullptr; } + + if (p_what == NOTIFICATION_READY) { + if (autocalculate_length_and_angle) { + calculate_length_and_rotation(); + } + } + +#ifdef TOOLS_ENABLED + // Bone2D Editor gizmo drawing: + if (p_what == NOTIFICATION_DRAW) { + // Only draw the gizmo in the editor! + if (Engine::get_singleton()->is_editor_hint() == false) { + return; + } + + if (editor_gizmo_rid.is_null()) { + editor_gizmo_rid = RenderingServer::get_singleton()->canvas_item_create(); + RenderingServer::get_singleton()->canvas_item_set_parent(editor_gizmo_rid, get_canvas_item()); + RenderingServer::get_singleton()->canvas_item_set_z_as_relative_to_parent(editor_gizmo_rid, true); + RenderingServer::get_singleton()->canvas_item_set_z_index(editor_gizmo_rid, 10); + } + RenderingServer::get_singleton()->canvas_item_clear(editor_gizmo_rid); + + if (!_editor_show_bone_gizmo) { + return; + } + + // Undo scaling + Transform2D editor_gizmo_trans = Transform2D(); + editor_gizmo_trans.set_scale(Vector2(1, 1) / get_global_scale()); + RenderingServer::get_singleton()->canvas_item_set_transform(editor_gizmo_rid, editor_gizmo_trans); + + Color bone_color1 = EditorSettings::get_singleton()->get("editors/2d/bone_color1"); + Color bone_color2 = EditorSettings::get_singleton()->get("editors/2d/bone_color2"); + Color bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color"); + Color bone_outline_color = EditorSettings::get_singleton()->get("editors/2d/bone_outline_color"); + Color bone_selected_color = EditorSettings::get_singleton()->get("editors/2d/bone_selected_color"); + + bool Bone2D_found = false; + for (int i = 0; i < get_child_count(); i++) { + Bone2D *child_node = nullptr; + child_node = Object::cast_to(get_child(i)); + if (!child_node) { + continue; + } + Bone2D_found = true; + + Vector bone_shape; + Vector bone_shape_outline; + + _editor_get_bone_shape(&bone_shape, &bone_shape_outline, child_node); + + Vector colors; + if (has_meta("_local_pose_override_enabled_")) { + colors.push_back(bone_ik_color); + colors.push_back(bone_ik_color); + colors.push_back(bone_ik_color); + colors.push_back(bone_ik_color); + } else { + colors.push_back(bone_color1); + colors.push_back(bone_color2); + colors.push_back(bone_color1); + colors.push_back(bone_color2); + } + + Vector outline_colors; + if (CanvasItemEditor::get_singleton()->editor_selection->is_selected(this)) { + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + } else { + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + } + + RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape_outline, outline_colors); + RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape, colors); + } + + if (!Bone2D_found) { + Vector bone_shape; + Vector bone_shape_outline; + + _editor_get_bone_shape(&bone_shape, &bone_shape_outline, nullptr); + + Vector colors; + if (has_meta("_local_pose_override_enabled_")) { + colors.push_back(bone_ik_color); + colors.push_back(bone_ik_color); + colors.push_back(bone_ik_color); + colors.push_back(bone_ik_color); + } else { + colors.push_back(bone_color1); + colors.push_back(bone_color2); + colors.push_back(bone_color1); + colors.push_back(bone_color2); + } + + Vector outline_colors; + if (CanvasItemEditor::get_singleton()->editor_selection->is_selected(this)) { + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + } else { + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + } + + RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape_outline, outline_colors); + RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape, colors); + } + } +#endif // TOOLS_ENALBED } +#ifdef TOOLS_ENABLED +bool Bone2D::_editor_get_bone_shape(Vector *shape, Vector *outline_shape, Bone2D *other_bone) { + int bone_width = EditorSettings::get_singleton()->get("editors/2d/bone_width"); + int bone_outline_width = EditorSettings::get_singleton()->get("editors/2d/bone_outline_size"); + + if (!is_inside_tree()) { + return false; //may have been removed + } + if (!other_bone && length <= 0) { + return false; + } + + Vector2 rel; + if (other_bone) { + rel = (other_bone->get_global_transform().get_origin() - get_global_transform().get_origin()); + rel = rel.rotated(-get_global_rotation()); // Undo Bone2D node's rotation so its drawn correctly regardless of the node's rotation + } else { + float angle_to_use = get_rotation() + bone_angle; + rel = Vector2(cos(angle_to_use), sin(angle_to_use)) * (length * MIN(get_global_scale().x, get_global_scale().y)); + rel = rel.rotated(-get_rotation()); // Undo Bone2D node's rotation so its drawn correctly regardless of the node's rotation + } + + Vector2 relt = relt.rotated(Math_PI).normalized() * bone_width; + Vector2 reln = rel.normalized(); + Vector2 reltn = relt.normalized(); + + if (shape) { + shape->clear(); + shape->push_back(Vector2(0, 0)); + shape->push_back(rel * 0.2 + relt); + shape->push_back(rel); + shape->push_back(rel * 0.2 - relt); + } + + if (outline_shape) { + outline_shape->clear(); + outline_shape->push_back((-reln - reltn) * bone_outline_width); + outline_shape->push_back((-reln + reltn) * bone_outline_width); + outline_shape->push_back(rel * 0.2 + relt + reltn * bone_outline_width); + outline_shape->push_back(rel + (reln + reltn) * bone_outline_width); + outline_shape->push_back(rel + (reln - reltn) * bone_outline_width); + outline_shape->push_back(rel * 0.2 - relt - reltn * bone_outline_width); + } + return true; +} + +void Bone2D::_editor_set_show_bone_gizmo(bool p_show_gizmo) { + _editor_show_bone_gizmo = p_show_gizmo; + update(); +} + +bool Bone2D::_editor_get_show_bone_gizmo() const { + return _editor_show_bone_gizmo; +} +#endif // TOOLS_ENABLED + void Bone2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rest", "rest"), &Bone2D::set_rest); ClassDB::bind_method(D_METHOD("get_rest"), &Bone2D::get_rest); @@ -90,8 +355,14 @@ void Bone2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_default_length", "default_length"), &Bone2D::set_default_length); ClassDB::bind_method(D_METHOD("get_default_length"), &Bone2D::get_default_length); + ClassDB::bind_method(D_METHOD("set_autocalculate_length_and_angle", "auto_calculate"), &Bone2D::set_autocalculate_length_and_angle); + ClassDB::bind_method(D_METHOD("get_autocalculate_length_and_angle"), &Bone2D::get_autocalculate_length_and_angle); + ClassDB::bind_method(D_METHOD("set_length", "length"), &Bone2D::set_length); + ClassDB::bind_method(D_METHOD("get_length"), &Bone2D::get_length); + ClassDB::bind_method(D_METHOD("set_bone_angle", "angle"), &Bone2D::set_bone_angle); + ClassDB::bind_method(D_METHOD("get_bone_angle"), &Bone2D::get_bone_angle); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "rest"), "set_rest", "get_rest"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "default_length", PROPERTY_HINT_RANGE, "1,1024,1"), "set_default_length", "get_default_length"); } void Bone2D::set_rest(const Transform2D &p_rest) { @@ -119,12 +390,14 @@ void Bone2D::apply_rest() { set_transform(rest); } -void Bone2D::set_default_length(real_t p_length) { - default_length = p_length; +void Bone2D::set_default_length(float p_length) { + WARN_DEPRECATED_MSG("set_default_length is deprecated. Please use set_length instead!"); + set_length(p_length); } -real_t Bone2D::get_default_length() const { - return default_length; +float Bone2D::get_default_length() const { + WARN_DEPRECATED_MSG("get_default_length is deprecated. Please use get_length instead!"); + return get_length(); } int Bone2D::get_index_in_skeleton() const { @@ -156,7 +429,75 @@ String Bone2D::get_configuration_warning() const { return warning; } +void Bone2D::calculate_length_and_rotation() { + // if there is at least a single child Bone2D node, we can calculate + // the length and direction. We will always just use the first Bone2D for this. + bool calculated = false; + int child_count = get_child_count(); + if (child_count > 0) { + for (int i = 0; i < child_count; i++) { + Bone2D *child = Object::cast_to(get_child(i)); + if (child) { + Vector2 child_local_pos = to_local(child->get_global_transform().get_origin()); + length = child_local_pos.length(); + bone_angle = Math::atan2(child_local_pos.normalized().y, child_local_pos.normalized().x); + calculated = true; + break; + } + } + } + if (calculated) { + return; // Finished! + } else { + WARN_PRINT("No Bone2D children of node " + get_name() + ". Cannot calculate bone length or angle reliably.\nUsing transform rotation for bone angle"); + bone_angle = get_transform().get_rotation(); + return; + } +} + +void Bone2D::set_autocalculate_length_and_angle(bool p_autocalculate) { + autocalculate_length_and_angle = p_autocalculate; + if (autocalculate_length_and_angle) { + calculate_length_and_rotation(); + } + notify_property_list_changed(); +} + +bool Bone2D::get_autocalculate_length_and_angle() const { + return autocalculate_length_and_angle; +} + +void Bone2D::set_length(float p_length) { + length = p_length; + +#ifdef TOOLS_ENABLED + update(); +#endif // TOOLS_ENABLED +} + +float Bone2D::get_length() const { + return length; +} + +void Bone2D::set_bone_angle(float p_angle) { + bone_angle = p_angle; + +#ifdef TOOLS_ENABLED + update(); +#endif // TOOLS_ENABLED +} + +float Bone2D::get_bone_angle() const { + return bone_angle; +} + Bone2D::Bone2D() { + skeleton = nullptr; + parent_bone = nullptr; + skeleton_index = -1; + length = 16; + bone_angle = 0; + autocalculate_length_and_angle = true; set_notify_local_transform(true); //this is a clever hack so the bone knows no rest has been set yet, allowing to show an error. for (int i = 0; i < 3; i++) { @@ -164,8 +505,44 @@ Bone2D::Bone2D() { } } +Bone2D::~Bone2D() { +#ifdef TOOLS_ENABLED + if (!editor_gizmo_rid.is_null()) { + RenderingServer::get_singleton()->free(editor_gizmo_rid); + } +#endif // TOOLS_ENABLED +} + ////////////////////////////////////// +bool Skeleton2D::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + + if (path.begins_with("modification_stack")) { + set_modification_stack(p_value); + return true; + } + return true; +} + +bool Skeleton2D::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + + if (path.begins_with("modification_stack")) { + r_ret = get_modification_stack(); + return true; + } + return true; +} + +void Skeleton2D::_get_property_list(List *p_list) const { + p_list->push_back( + PropertyInfo(Variant::OBJECT, "modification_stack", + PROPERTY_HINT_RESOURCE_TYPE, + "SkeletonModificationStack2D", + PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); +} + void Skeleton2D::_make_bone_setup_dirty() { if (bone_setup_dirty) { return; @@ -195,6 +572,8 @@ void Skeleton2D::_update_bone_setup() { } else { bones.write[i].parent_index = -1; } + + bones.write[i].local_pose_override = bones[i].bone->get_skeleton_rest(); } transform_dirty = true; @@ -264,18 +643,98 @@ void Skeleton2D::_notification(int p_what) { _update_transform(); } + set_process(true); + set_physics_process(true); request_ready(); } if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { RS::get_singleton()->skeleton_set_base_transform_2d(skeleton, get_global_transform()); } + if (p_what == NOTIFICATION_PROCESS) { + if (modification_stack.is_valid()) { + if (modification_stack->execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process) { + execute_modification(get_process_delta_time()); + } + } + } + if (p_what == NOTIFICATION_PHYSICS_PROCESS) { + if (modification_stack.is_valid()) { + if (modification_stack->execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process) { + execute_modification(get_physics_process_delta_time()); + } + } + } } RID Skeleton2D::get_skeleton() const { return skeleton; } +void Skeleton2D::set_bone_local_pose_override(int bone_idx, Transform2D p_override, float amount, bool persistent) { + ERR_FAIL_INDEX_MSG(bone_idx, bones.size(), "Bone index is out of range!"); + bones.write[bone_idx].local_pose_override = p_override; + bones.write[bone_idx].local_pose_override_amount = amount; + bones.write[bone_idx].local_pose_override_persistent = persistent; +} + +Transform2D Skeleton2D::get_bone_local_pose_override(int bone_idx) { + ERR_FAIL_INDEX_V_MSG(bone_idx, bones.size(), Transform2D(), "Bone index is out of range!"); + return bones[bone_idx].local_pose_override; +} + +void Skeleton2D::set_modification_stack(Ref p_stack) { + if (modification_stack.is_valid()) { + modification_stack->is_setup = false; + modification_stack->set_skeleton(nullptr); + } + modification_stack = p_stack; + if (modification_stack.is_valid()) { + modification_stack->set_skeleton(this); + modification_stack->setup(); + } +} + +Ref Skeleton2D::get_modification_stack() const { + return modification_stack; +} + +void Skeleton2D::execute_modification(float delta) { + if (!modification_stack.is_valid()) { + return; + } + + // Cache the transform of the Bone2D before we apply any modifications to it. + for (int i = 0; i < bones.size(); i++) { + bones.write[i].local_pose_cache = bones[i].bone->get_transform(); + bones[i].bone->set_transform(bones.write[i].local_pose_cache); + } + + modification_stack->execute(delta); + + // A hack: Override the CanvasItem transform using the RenderingServer so the local pose override is taken into account. + for (int i = 0; i < bones.size(); i++) { + if (bones[i].local_pose_override_amount > 0) { + bones[i].bone->set_meta("_local_pose_override_enabled_", true); + bones[i].bone->set_transform(bones[i].local_pose_cache); + + Transform2D final_trans = bones[i].local_pose_cache; + final_trans = final_trans.interpolate_with(bones[i].local_pose_override, bones[i].local_pose_override_amount); + + RenderingServer::get_singleton()->canvas_item_set_transform(bones[i].bone->get_canvas_item(), final_trans); + + if (bones[i].local_pose_override_persistent) { + bones.write[i].local_pose_override_amount = 0.0; + } + } else { + // TODO: see if there is a way to undo the override without having to resort to setting every bone's transform. + bones[i].bone->remove_meta("_local_pose_override_enabled_"); + bones[i].bone->set_transform(bones.write[i].local_pose_cache); + RenderingServer::get_singleton()->canvas_item_set_transform(bones[i].bone->get_canvas_item(), bones[i].local_pose_cache); + } + } +} + void Skeleton2D::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_bone_setup"), &Skeleton2D::_update_bone_setup); ClassDB::bind_method(D_METHOD("_update_transform"), &Skeleton2D::_update_transform); @@ -285,6 +744,13 @@ void Skeleton2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_skeleton"), &Skeleton2D::get_skeleton); + ClassDB::bind_method(D_METHOD("set_modification_stack", "modification_stack"), &Skeleton2D::set_modification_stack); + ClassDB::bind_method(D_METHOD("get_modification_stack"), &Skeleton2D::get_modification_stack); + ClassDB::bind_method(D_METHOD("execute_modification"), &Skeleton2D::execute_modification); + + ClassDB::bind_method(D_METHOD("set_bone_local_pose_override", "bone_idx", "override_pose", "strength", "persistent"), &Skeleton2D::set_bone_local_pose_override); + ClassDB::bind_method(D_METHOD("get_bone_local_pose_override", "bone_idx"), &Skeleton2D::get_bone_local_pose_override); + ADD_SIGNAL(MethodInfo("bone_setup_changed")); } diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index 1f43ea742ba7..21e38945589d 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -32,6 +32,7 @@ #define SKELETON_2D_H #include "scene/2d/node_2d.h" +#include "scene/resources/skeleton_modification_2d.h" class Skeleton2D; @@ -46,13 +47,31 @@ class Bone2D : public Node2D { Bone2D *parent_bone = nullptr; Skeleton2D *skeleton = nullptr; Transform2D rest; +<<<<<<< HEAD real_t default_length = 16.0; +======= + + bool autocalculate_length_and_angle = true; + float length = 16; + float bone_angle = 0; +>>>>>>> Initial groundwork (squashed from verbose branch: https://github.com/TwistedTwigleg/godot/tree/GSOC_2020_Working_Branch_2D_IK_VERBOSE_ARCHIVE ) int skeleton_index = -1; + void calculate_length_and_rotation(); + +#ifdef TOOLS_ENABLED + RID editor_gizmo_rid; + bool _editor_get_bone_shape(Vector *shape, Vector *outline_shape, Bone2D *other_bone); + bool _editor_show_bone_gizmo = true; +#endif // TOOLS ENABLED + protected: void _notification(int p_what); 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 *p_list) const; public: void set_rest(const Transform2D &p_rest); @@ -65,11 +84,26 @@ class Bone2D : public Node2D { void set_default_length(real_t p_length); real_t get_default_length() const; + void set_autocalculate_length_and_angle(bool p_autocalculate); + bool get_autocalculate_length_and_angle() const; + void set_length(float p_length); + float get_length() const; + void set_bone_angle(float p_angle); + float get_bone_angle() const; + int get_index_in_skeleton() const; +#ifdef TOOLS_ENABLED + void _editor_set_show_bone_gizmo(bool p_show_gizmo); + bool _editor_get_show_bone_gizmo() const; +#endif // TOOLS_ENABLED + Bone2D(); + ~Bone2D(); }; +class SkeletonModificationStack2D; + class Skeleton2D : public Node2D { GDCLASS(Skeleton2D, Node2D); @@ -86,6 +120,11 @@ class Skeleton2D : public Node2D { int parent_index = 0; Transform2D accum_transform; Transform2D rest_inverse; + + Transform2D local_pose_cache; + Transform2D local_pose_override; + float local_pose_override_amount = 0; + bool local_pose_override_persistent = false; }; Vector bones; @@ -100,15 +139,28 @@ class Skeleton2D : public Node2D { RID skeleton; + Ref modification_stack; + protected: void _notification(int p_what); 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 *p_list) const; public: int get_bone_count() const; Bone2D *get_bone(int p_idx); RID get_skeleton() const; + + void set_bone_local_pose_override(int bone_idx, Transform2D p_override, float amount, bool persistent = true); + Transform2D get_bone_local_pose_override(int bone_idx); + + Ref get_modification_stack() const; + void set_modification_stack(Ref p_stack); + void execute_modification(float delta); + Skeleton2D(); ~Skeleton2D(); }; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index fe8591e3d93a..1251e61c603f 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -161,6 +161,7 @@ #include "scene/resources/rectangle_shape_2d.h" #include "scene/resources/resource_format_text.h" #include "scene/resources/segment_shape_2d.h" +#include "scene/resources/skeleton_modification_2d.h" #include "scene/resources/sky.h" #include "scene/resources/sky_material.h" #include "scene/resources/sphere_shape_3d.h" @@ -657,6 +658,9 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_virtual_class(); + OS::get_singleton()->yield(); //may take time to init /* REGISTER RESOURCES */ diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp new file mode 100644 index 000000000000..2d2782e2eb53 --- /dev/null +++ b/scene/resources/skeleton_modification_2d.cpp @@ -0,0 +1,315 @@ +/*************************************************************************/ +/* skeleton_modification_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_modification_2d.h" +#include "scene/2d/skeleton_2d.h" + +/////////////////////////////////////// +// ModificationStack2D +/////////////////////////////////////// + +void SkeletonModificationStack2D::_get_property_list(List *p_list) const { + for (int i = 0; i < modifications.size(); i++) { + p_list->push_back( + PropertyInfo(Variant::OBJECT, "modifications/" + itos(i), + PROPERTY_HINT_RESOURCE_TYPE, + "SkeletonModification2D", + PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); + } +} + +bool SkeletonModificationStack2D::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + + if (path.begins_with("modifications/")) { + int mod_idx = path.get_slicec('/', 1).to_int(); + set_modification(mod_idx, p_value); + return true; + } + return true; +} + +bool SkeletonModificationStack2D::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + + if (path.begins_with("modifications/")) { + int mod_idx = path.get_slicec('/', 1).to_int(); + r_ret = get_modification(mod_idx); + return true; + } + return true; +} + +void SkeletonModificationStack2D::setup() { + if (is_setup) { + return; + } + + if (skeleton != nullptr) { + is_setup = true; + for (int i = 0; i < modifications.size(); i++) { + if (!modifications[i].is_valid()) { + continue; + } + modifications.get(i)->setup_modification(this); + } + } else { + WARN_PRINT("Cannot setup SkeletonModificationStack2D: no Skeleton2D set!"); + } +} + +void SkeletonModificationStack2D::execute(float delta) { + ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr || is_queued_for_deletion(), + "Modification stack is not properly setup and therefore cannot execute!"); + + if (!skeleton->is_inside_tree()) { + ERR_PRINT_ONCE("Skeleton is not inside SceneTree! Cannot execute modification!"); + return; + } + + if (!enabled) { + return; + } + + for (int i = 0; i < modifications.size(); i++) { + if (!modifications[i].is_valid()) { + continue; + } + modifications.get(i)->execute(delta); + } +} + +void SkeletonModificationStack2D::enable_all_modifications(bool p_enabled) { + for (int i = 0; i < modifications.size(); i++) { + if (!modifications[i].is_valid()) { + continue; + } + modifications.get(i)->set_enabled(p_enabled); + } +} + +Ref SkeletonModificationStack2D::get_modification(int p_mod_idx) const { + ERR_FAIL_INDEX_V(p_mod_idx, modifications.size(), nullptr); + return modifications[p_mod_idx]; +} + +void SkeletonModificationStack2D::add_modification(Ref p_mod) { + p_mod->setup_modification(this); + modifications.push_back(p_mod); +} + +void SkeletonModificationStack2D::delete_modification(int p_mod_idx) { + ERR_FAIL_INDEX(p_mod_idx, modifications.size()); + modifications.remove(p_mod_idx); +} + +void SkeletonModificationStack2D::set_modification(int p_mod_idx, Ref p_mod) { + ERR_FAIL_INDEX(p_mod_idx, modifications.size()); + + if (p_mod == nullptr) { + modifications.set(p_mod_idx, nullptr); + } else { + p_mod->setup_modification(this); + modifications.set(p_mod_idx, p_mod); + } +} + +void SkeletonModificationStack2D::set_modification_count(int p_count) { + modifications.resize(p_count); + _change_notify(); +} + +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::set_execution_mode(int p_new_mode) { + execution_mode = p_new_mode; +} + +int SkeletonModificationStack2D::get_execution_mode() { + return execution_mode; +} + +void SkeletonModificationStack2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("setup"), &SkeletonModificationStack2D::setup); + ClassDB::bind_method(D_METHOD("execute", "delta"), &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"), &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("set_execution_mode", "execution_mode"), &SkeletonModificationStack2D::set_execution_mode); + ClassDB::bind_method(D_METHOD("get_execution_mode"), &SkeletonModificationStack2D::get_execution_mode); + + 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, "execution_mode", PROPERTY_HINT_ENUM, "process, physics_process"), "set_execution_mode", "get_execution_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_modification_count", "get_modification_count"); +} + +SkeletonModificationStack2D::SkeletonModificationStack2D() { + skeleton = nullptr; + modifications = Vector>(); + is_setup = false; + enabled = false; + modifications_count = 0; + strength = 1; +} + +/////////////////////////////////////// +// Modification2D +/////////////////////////////////////// + +void SkeletonModification2D::execute(float delta) { + if (!enabled) + return; +} + +void SkeletonModification2D::setup_modification(SkeletonModificationStack2D *p_stack) { + stack = p_stack; + if (stack) { + is_setup = true; + } +} + +void SkeletonModification2D::set_enabled(bool p_enabled) { + enabled = p_enabled; +} + +bool SkeletonModification2D::get_enabled() { + return enabled; +} + +float SkeletonModification2D::clamp_angle(float angle, float min_bound, float max_bound, bool invert) { + // Map to the 0 to 360 range (in radians though) instead of the -180 to 180 range. + if (angle < 0) { + angle = (Math_PI * 2) + angle; + } + + // Make min and max in the range of 0 to 360 (in radians), and make sure they are in the right order + if (min_bound < 0) { + min_bound = (Math_PI * 2) + min_bound; + } + if (max_bound < 0) { + max_bound = (Math_PI * 2) + max_bound; + } + if (min_bound > max_bound) { + float tmp = min_bound; + min_bound = max_bound; + max_bound = tmp; + } + + // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle. + if (invert == false) { + if (angle < min_bound || angle > max_bound) { + Vector2 min_bound_vec = Vector2(Math::cos(min_bound), Math::sin(min_bound)); + Vector2 max_bound_vec = Vector2(Math::cos(max_bound), Math::sin(max_bound)); + Vector2 angle_vec = Vector2(Math::cos(angle), Math::sin(angle)); + + if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { + angle = min_bound; + } else { + angle = max_bound; + } + } + } else { + if (angle > min_bound && angle < max_bound) { + Vector2 min_bound_vec = Vector2(Math::cos(min_bound), Math::sin(min_bound)); + Vector2 max_bound_vec = Vector2(Math::cos(max_bound), Math::sin(max_bound)); + Vector2 angle_vec = Vector2(Math::cos(angle), Math::sin(angle)); + + if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { + angle = min_bound; + } else { + angle = max_bound; + } + } + } + return angle; +} + +void SkeletonModification2D::_bind_methods() { + BIND_VMETHOD(MethodInfo("execute")); + BIND_VMETHOD(MethodInfo("setup_modification")); + + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification2D::set_enabled); + ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification2D::get_enabled); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled"); +} + +SkeletonModification2D::SkeletonModification2D() { + stack = nullptr; + is_setup = false; +} diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h new file mode 100644 index 000000000000..69916027c98f --- /dev/null +++ b/scene/resources/skeleton_modification_2d.h @@ -0,0 +1,126 @@ +/*************************************************************************/ +/* skeleton_modification_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETONMODIFICATION2D_H +#define SKELETONMODIFICATION2D_H + +#include "scene/2d/skeleton_2d.h" + +/////////////////////////////////////// +// SkeletonModificationStack2D +/////////////////////////////////////// + +class Skeleton2D; +class SkeletonModification2D; + +class SkeletonModificationStack2D : public Resource { + GDCLASS(SkeletonModificationStack2D, Resource); + friend class Skeleton2D; + friend class SkeletonModification2D; + +protected: + static void _bind_methods(); + void _get_property_list(List *p_list) const; + bool _set(const StringName &p_path, const Variant &p_value); + bool _get(const StringName &p_path, Variant &r_ret) const; + +public: + Skeleton2D *skeleton; + bool is_setup = false; + bool enabled = true; + float strength = 0.0; + + enum EXECUTION_MODE { + execution_mode_process, + execution_mode_physics_process + }; + int execution_mode = execution_mode_process; + + void set_execution_mode(int p_mode); + int get_execution_mode(); + + Vector> modifications; + int modifications_count = 0; + + void setup(); + void execute(float delta); + + void enable_all_modifications(bool p_enable); + Ref get_modification(int p_mod_idx) const; + void add_modification(Ref p_mod); + void delete_modification(int p_mod_idx); + void set_modification(int p_mod_idx, Ref p_mod); + + void set_modification_count(int p_count); + int get_modification_count() const; + + void set_skeleton(Skeleton2D *p_skeleton); + Skeleton2D *get_skeleton() const; + + bool get_is_setup() const; + + void set_enabled(bool p_enabled); + bool get_enabled() const; + + void set_strength(float p_strength); + float get_strength() const; + + SkeletonModificationStack2D(); +}; + +/////////////////////////////////////// +// SkeletonModification2D +/////////////////////////////////////// + +class SkeletonModification2D : public Resource { + GDCLASS(SkeletonModification2D, Resource); + friend class Skeleton2D; + +protected: + static void _bind_methods(); + + SkeletonModificationStack2D *stack; + + bool enabled = false; + bool is_setup = false; + +public: + virtual void execute(float delta); + virtual void setup_modification(SkeletonModificationStack2D *p_stack); + + void set_enabled(bool p_enabled); + bool get_enabled(); + + float clamp_angle(float angle, float min_bound, float max_bound, bool invert_clamp = false); + + SkeletonModification2D(); +}; + +#endif // SKELETONMODIFICATION2D_H From f222cacd19c8c31ed0efd8090293b4845b348eca Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Mon, 3 Aug 2020 14:24:12 -0400 Subject: [PATCH 02/34] Changes: * SkeletonModification2DLookAt: Added full implementation of the entire modification. * RegisterSceneTypes: Registered SkeletonModification2DLookAt NOTE: This commit is squashed from the following verbose branch: https://github.com/TwistedTwigleg/godot/tree/GSOC_2020_Working_Branch_2D_IK_VERBOSE_ARCHIVE --- scene/register_scene_types.cpp | 1 + scene/resources/skeleton_modification_2d.cpp | 306 +++++++++++++++++++ scene/resources/skeleton_modification_2d.h | 61 ++++ 3 files changed, 368 insertions(+) diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 1251e61c603f..67c744af7c61 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -660,6 +660,7 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_virtual_class(); + ClassDB::register_class(); OS::get_singleton()->yield(); //may take time to init diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 2d2782e2eb53..ce40faa87c55 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -313,3 +313,309 @@ SkeletonModification2D::SkeletonModification2D() { stack = nullptr; is_setup = false; } + +/////////////////////////////////////// +// LookAt +/////////////////////////////////////// + +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::deg2rad(float(p_value))); + } else if (path.begins_with("constraint_angle_max")) { + set_constraint_angle_max(Math::deg2rad(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::deg2rad(float(p_value))); + } + 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::rad2deg(get_constraint_angle_min()); + } else if (path.begins_with("constraint_angle_max")) { + r_ret = Math::rad2deg(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::rad2deg(get_additional_rotation()); + } + return true; +} + +void SkeletonModification2DLookAt::_get_property_list(List *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)); +} + +void SkeletonModification2DLookAt::execute(float 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()) { + update_target_cache(); + WARN_PRINT("Target cache is out of date. Updating..."); + return; + } + + if (bone2d_node_cache.is_null() && !bone2d_node.is_empty()) { + update_bone2d_cache(); + WARN_PRINT("Bone2D node cache is out of date. Updating..."); + } + + Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + ERR_FAIL_COND_MSG(!target, "Target node is not a Node2D-based node. Cannot execute modification!"); + ERR_FAIL_COND_MSG(!target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!"); + ERR_FAIL_COND_MSG(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!"); + + Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx); + ERR_FAIL_COND_MSG(operation_bone == nullptr, "bone_idx for modification does not point to a valid bone! Cannot execute modification"); + Transform2D operation_transform = operation_bone->get_global_transform(); + Transform2D target_trans = target->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_transform().get_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::update_bone2d_cache() { + if (!is_setup || !stack) { + WARN_PRINT("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!"); + bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to(node); + if (bone) { + bone_idx = bone->get_index_in_skeleton(); + } else { + ERR_FAIL_MSG("Error Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); + } + } + } + } +} + +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) { + 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 { + WARN_PRINT("Cannot verify the bone index for this modification..."); + bone_idx = p_bone_idx; + } + + _change_notify(); +} + +void SkeletonModification2DLookAt::update_target_cache() { + if (!is_setup || !stack) { + WARN_PRINT("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!"); + 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; + _change_notify(); +} + +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; +} + +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; +} + +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; +} + +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; +} + +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"); + + // TODO: make the additional_rotation, angle_min, and angle_max properties to use degrees in the editor! + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "additional_rotation"), "set_additional_rotation", "get_additional_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_constraint"), "set_enable_constraint", "get_enable_constraint"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constraint_angle_min"), "set_constraint_angle_min", "get_constraint_angle_min"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constraint_angle_max"), "set_constraint_angle_max", "get_constraint_angle_max"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constraint_angle_invert"), "set_constraint_angle_invert", "get_constraint_angle_invert"); +} + +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; +} + +SkeletonModification2DLookAt::~SkeletonModification2DLookAt() { +} diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index 69916027c98f..6ed8f2db2893 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -123,4 +123,65 @@ class SkeletonModification2D : public Resource { SkeletonModification2D(); }; +/////////////////////////////////////// +// 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; + + 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 *p_list) const; + +public: + void execute(float delta) override; + void setup_modification(SkeletonModificationStack2D *p_stack) 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(); +}; + #endif // SKELETONMODIFICATION2D_H From 2a3de40cd3e36a4139e3b31cb57f3355da08b51c Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Mon, 3 Aug 2020 14:33:55 -0400 Subject: [PATCH 03/34] Changes: * SkeletonModification2DCCDIK: Added full implementation * RegisterSceneTypes: Registered 2D CCCDIK NOTE This commit is squashed changes from a verbose PR: https://github.com/TwistedTwigleg/godot/tree/GSOC_2020_Working_Branch_2D_IK_VERBOSE_ARCHIVE --- scene/register_scene_types.cpp | 1 + scene/resources/skeleton_modification_2d.cpp | 397 +++++++++++++++++++ scene/resources/skeleton_modification_2d.h | 74 ++++ 3 files changed, 472 insertions(+) diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 67c744af7c61..543a7095ab66 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -661,6 +661,7 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_virtual_class(); ClassDB::register_class(); + ClassDB::register_class(); OS::get_singleton()->yield(); //may take time to init diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index ce40faa87c55..e1004bf4d03f 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -619,3 +619,400 @@ SkeletonModification2DLookAt::SkeletonModification2DLookAt() { SkeletonModification2DLookAt::~SkeletonModification2DLookAt() { } + +/////////////////////////////////////// +// CCDIK +/////////////////////////////////////// + +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") { + ccdik_joint_set_bone2d_node(which, p_value); + } else if (what == "bone_index") { + ccdik_joint_set_bone_index(which, p_value); + } else if (what == "rotate_from_joint") { + ccdik_joint_set_rotate_from_joint(which, p_value); + } else if (what == "enable_constraint") { + ccdik_joint_set_enable_constraint(which, p_value); + } else if (what == "constraint_angle_min") { + ccdik_joint_set_constraint_angle_min(which, Math::deg2rad(float(p_value))); + } else if (what == "constraint_angle_max") { + ccdik_joint_set_constraint_angle_max(which, Math::deg2rad(float(p_value))); + } else if (what == "constraint_angle_invert") { + ccdik_joint_set_constraint_angle_invert(which, p_value); + } else if (what == "constraint_in_localspace") { + ccdik_joint_set_constraint_in_localspace(which, p_value); + } + return true; + } + 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 = ccdik_joint_get_bone2d_node(which); + } else if (what == "bone_index") { + r_ret = ccdik_joint_get_bone_index(which); + } else if (what == "rotate_from_joint") { + r_ret = ccdik_joint_get_rotate_from_joint(which); + } else if (what == "enable_constraint") { + r_ret = ccdik_joint_get_enable_constraint(which); + } else if (what == "constraint_angle_min") { + r_ret = Math::rad2deg(ccdik_joint_get_constraint_angle_min(which)); + } else if (what == "constraint_angle_max") { + r_ret = Math::rad2deg(ccdik_joint_get_constraint_angle_max(which)); + } else if (what == "constraint_angle_invert") { + r_ret = ccdik_joint_get_constraint_angle_invert(which); + } else if (what == "constraint_in_localspace") { + r_ret = ccdik_joint_get_constraint_in_localspace(which); + } + return true; + } + return true; +} + +void SkeletonModification2DCCDIK::_get_property_list(List *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)); + } + } +} + +void SkeletonModification2DCCDIK::execute(float 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()) { + update_target_cache(); + WARN_PRINT("Target cache is out of date. Updating..."); + return; + } + if (tip_node_cache.is_null()) { + update_tip_cache(); + WARN_PRINT("Tip cache is out of date. Updating..."); + return; + } + + Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + ERR_FAIL_COND_MSG(!target, "Target node is not a Node2D-based node. Cannot execute modification!"); + ERR_FAIL_COND_MSG(!target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!"); + + Node2D *tip = Object::cast_to(ObjectDB::get_instance(tip_node_cache)); + ERR_FAIL_COND_MSG(!tip, "Tip node is not a Node2D-based node. Cannot execute modification!"); + ERR_FAIL_COND_MSG(!tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!"); + + 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 *target, Node2D *tip) { + CCDIK_Joint_Data2D ccdik_data = ccdik_data_chain[p_joint_idx]; + ERR_FAIL_INDEX_MSG(ccdik_data.bone_idx, stack->skeleton->get_bone_count(), "2D CCDIK joint: bone index not found!"); + + 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(target->get_global_transform().get_origin()).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 = operation_transform.get_origin().angle_to_point(tip->get_global_transform().get_origin()); + float joint_to_target = operation_transform.get_origin().angle_to_point(target->get_global_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_transform().get_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::update_target_cache() { + if (!is_setup || !stack) { + WARN_PRINT("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!"); + target_node_cache = node->get_instance_id(); + } + } + } +} + +void SkeletonModification2DCCDIK::update_tip_cache() { + if (!is_setup || !stack) { + WARN_PRINT("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!"); + 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) { + WARN_PRINT("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 Bone2D cache: node is this modification's skeleton or cannot be found!"); + ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to(node); + if (bone) { + ccdik_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton(); + } else { + ERR_FAIL_MSG("CCDIK 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); + _change_notify(); +} + +int SkeletonModification2DCCDIK::get_ccdik_data_chain_length() { + return ccdik_data_chain.size(); +} + +void SkeletonModification2DCCDIK::ccdik_joint_set_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); + + _change_notify(); +} + +NodePath SkeletonModification2DCCDIK::ccdik_joint_get_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::ccdik_joint_set_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 bone index for this modification..."); + ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + } + } else { + WARN_PRINT("Cannot verify the CCDIK joint bone index for this modification..."); + ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + } + + _change_notify(); +} + +int SkeletonModification2DCCDIK::ccdik_joint_get_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::ccdik_joint_set_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::ccdik_joint_get_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::ccdik_joint_set_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; + _change_notify(); +} + +bool SkeletonModification2DCCDIK::ccdik_joint_get_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::ccdik_joint_set_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; +} + +float SkeletonModification2DCCDIK::ccdik_joint_get_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::ccdik_joint_set_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; +} + +float SkeletonModification2DCCDIK::ccdik_joint_get_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::ccdik_joint_set_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; +} + +bool SkeletonModification2DCCDIK::ccdik_joint_get_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::ccdik_joint_set_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; +} + +bool SkeletonModification2DCCDIK::ccdik_joint_get_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::_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("ccdik_joint_set_bone2d_node", "joint_idx", "bone2d_nodepath"), &SkeletonModification2DCCDIK::ccdik_joint_set_bone2d_node); + ClassDB::bind_method(D_METHOD("ccdik_joint_get_bone2d_node", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_bone2d_node); + ClassDB::bind_method(D_METHOD("ccdik_joint_set_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DCCDIK::ccdik_joint_set_bone_index); + ClassDB::bind_method(D_METHOD("ccdik_joint_get_bone_index", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_bone_index); + ClassDB::bind_method(D_METHOD("ccdik_joint_set_rotate_from_joint", "joint_idx", "rotate_from_joint"), &SkeletonModification2DCCDIK::ccdik_joint_set_rotate_from_joint); + ClassDB::bind_method(D_METHOD("ccdik_joint_get_rotate_from_joint", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_rotate_from_joint); + ClassDB::bind_method(D_METHOD("ccdik_joint_set_enable_constraint", "joint_idx", "enable_constraint"), &SkeletonModification2DCCDIK::ccdik_joint_set_enable_constraint); + ClassDB::bind_method(D_METHOD("ccdik_joint_get_enable_constraint", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_enable_constraint); + ClassDB::bind_method(D_METHOD("ccdik_joint_set_constraint_angle_min", "joint_idx", "angle_min"), &SkeletonModification2DCCDIK::ccdik_joint_set_constraint_angle_min); + ClassDB::bind_method(D_METHOD("ccdik_joint_get_constraint_angle_min", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_constraint_angle_min); + ClassDB::bind_method(D_METHOD("ccdik_joint_set_constraint_angle_max", "joint_idx", "angle_max"), &SkeletonModification2DCCDIK::ccdik_joint_set_constraint_angle_max); + ClassDB::bind_method(D_METHOD("ccdik_joint_get_constraint_angle_max", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_constraint_angle_max); + ClassDB::bind_method(D_METHOD("ccdik_joint_set_constraint_angle_invert", "joint_idx", "invert"), &SkeletonModification2DCCDIK::ccdik_joint_set_constraint_angle_invert); + ClassDB::bind_method(D_METHOD("ccdik_joint_get_constraint_angle_invert", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_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; +} + +SkeletonModification2DCCDIK::~SkeletonModification2DCCDIK() { +} diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index 6ed8f2db2893..c5ce1cdac50d 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -184,4 +184,78 @@ class SkeletonModification2DLookAt : public SkeletonModification2D { ~SkeletonModification2DLookAt(); }; +/////////////////////////////////////// +// 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; + }; + + Vector 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 *target, Node2D *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 *p_list) const; + +public: + void execute(float 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_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 ccdik_joint_set_bone2d_node(int p_joint_idx, const NodePath &p_target_node); + NodePath ccdik_joint_get_bone2d_node(int p_joint_idx) const; + void ccdik_joint_set_bone_index(int p_joint_idx, int p_bone_idx); + int ccdik_joint_get_bone_index(int p_joint_idx) const; + + void ccdik_joint_set_rotate_from_joint(int p_joint_idx, bool p_rotate_from_joint); + bool ccdik_joint_get_rotate_from_joint(int p_joint_idx) const; + void ccdik_joint_set_enable_constraint(int p_joint_idx, bool p_constraint); + bool ccdik_joint_get_enable_constraint(int p_joint_idx) const; + void ccdik_joint_set_constraint_angle_min(int p_joint_idx, float p_angle_min); + float ccdik_joint_get_constraint_angle_min(int p_joint_idx) const; + void ccdik_joint_set_constraint_angle_max(int p_joint_idx, float p_angle_max); + float ccdik_joint_get_constraint_angle_max(int p_joint_idx) const; + void ccdik_joint_set_constraint_angle_invert(int p_joint_idx, bool p_invert); + bool ccdik_joint_get_constraint_angle_invert(int p_joint_idx) const; + void ccdik_joint_set_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace); + bool ccdik_joint_get_constraint_in_localspace(int p_joint_idx) const; + + SkeletonModification2DCCDIK(); + ~SkeletonModification2DCCDIK(); +}; + #endif // SKELETONMODIFICATION2D_H From a5847a233b05b6c121ab4ee64e06ce24fb2bf303 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Mon, 3 Aug 2020 14:42:31 -0400 Subject: [PATCH 04/34] Changes: * SkeletonModification2DFABRIK: Added full implementation of FABRIK * RegisterSceneTypes: Registered 2D FABRIK NOTE: this commit is changes squashed from the following verbose branch: https://github.com/TwistedTwigleg/godot/tree/GSOC_2020_Working_Branch_2D_IK_VERBOSE_ARCHIVE --- scene/register_scene_types.cpp | 1 + scene/resources/skeleton_modification_2d.cpp | 518 +++++++++++++++++++ scene/resources/skeleton_modification_2d.h | 85 +++ 3 files changed, 604 insertions(+) diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 543a7095ab66..45b1ce6a1dc6 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -662,6 +662,7 @@ void register_scene_types() { ClassDB::register_virtual_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); OS::get_singleton()->yield(); //may take time to init diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index e1004bf4d03f..44787ad800c4 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -1016,3 +1016,521 @@ SkeletonModification2DCCDIK::SkeletonModification2DCCDIK() { SkeletonModification2DCCDIK::~SkeletonModification2DCCDIK() { } + +/////////////////////////////////////// +// FABRIK +/////////////////////////////////////// + +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") { + fabrik_joint_set_bone2d_node(which, p_value); + } else if (what == "bone_index") { + fabrik_joint_set_bone_index(which, p_value); + } else if (what == "magnet_position") { + fabrik_joint_set_magnet_position(which, p_value); + } else if (what == "use_target_rotation") { + fabrik_joint_set_use_target_rotation(which, p_value); + } else if (what == "enable_constraint") { + fabrik_joint_set_enable_constraint(which, p_value); + } else if (what == "constraint_angle_min") { + fabrik_joint_set_constraint_angle_min(which, Math::deg2rad(float(p_value))); + } else if (what == "constraint_angle_max") { + fabrik_joint_set_constraint_angle_max(which, Math::deg2rad(float(p_value))); + } else if (what == "constraint_angle_invert") { + fabrik_joint_set_constraint_angle_invert(which, p_value); + } else if (what == "constraint_in_localspace") { + fabrik_joint_set_constraint_in_localspace(which, p_value); + } + return true; + } + 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 = fabrik_joint_get_bone2d_node(which); + } else if (what == "bone_index") { + r_ret = fabrik_joint_get_bone_index(which); + } else if (what == "magnet_position") { + r_ret = fabrik_joint_get_magnet_position(which); + } else if (what == "use_target_rotation") { + r_ret = fabrik_joint_get_use_target_rotation(which); + } else if (what == "enable_constraint") { + r_ret = fabrik_joint_get_enable_constraint(which); + } else if (what == "constraint_angle_min") { + r_ret = Math::rad2deg(fabrik_joint_get_constraint_angle_min(which)); + } else if (what == "constraint_angle_max") { + r_ret = Math::rad2deg(fabrik_joint_get_constraint_angle_max(which)); + } else if (what == "constraint_angle_invert") { + r_ret = fabrik_joint_get_constraint_angle_invert(which); + } else if (what == "constraint_in_localspace") { + r_ret = fabrik_joint_get_constraint_in_localspace(which); + } + return true; + } + return true; +} + +void SkeletonModification2DFABRIK::_get_property_list(List *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)); + } + + p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "enable_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + if (fabrik_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)); + } + } +} + +void SkeletonModification2DFABRIK::execute(float 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()) { + update_target_cache(); + WARN_PRINT("Target cache is out of date. Updating..."); + return; + } + + Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + ERR_FAIL_COND_MSG(!target, "Target node is not a Node2D-based node. Cannot execute modification!"); + ERR_FAIL_COND_MSG(!target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!"); + ERR_FAIL_COND_MSG(fabrik_data_chain.size() <= 1, "FABRIK requires at least two nodes to opperate! Cannot execute modification!"); + target_global_pose = target->get_global_transform(); + + if (fabrik_data_chain[0].bone2d_node_cache.is_null() && !fabrik_data_chain[0].bone2d_node.is_empty()) { + fabrik_joint_update_bone2d_cache(0); + WARN_PRINT("Bone2D cache for origin joint is out of date. Updating..."); + } + + Bone2D *origin_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[0].bone2d_node_cache)); + ERR_FAIL_COND_MSG(!origin_bone2d_node, "Origin joint's Bone2D node not found! Cannot execute modification!"); + 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()) { + fabrik_joint_update_bone2d_cache(i); + WARN_PRINT("Bone2D cache for joint " + itos(i) + " is out of date. Updating..."); + } + Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); + ERR_FAIL_COND_MSG(!joint_bone2d_node, "Joint " + itos(i) + " does not have a Bone2D node set! Cannot execute modification!"); + fabrik_transform_chain.write[i] = joint_bone2d_node->get_global_transform(); + + // Apply magnet positions + if (i == 0) { + continue; // The origin cannot use a magnet position! + } else { + Transform2D joint_trans = fabrik_transform_chain[i]; + joint_trans.set_origin(joint_trans.get_origin() + fabrik_data_chain[i].magnet_position); + fabrik_transform_chain.write[i] = joint_trans; + } + } + + Bone2D *final_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[fabrik_data_chain.size() - 1].bone2d_node_cache)); + float final_bone2d_angle = final_bone2d_node->get_global_transform().get_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_transform().get_origin() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_transform().get_origin()); + chain_iterations = 0; + + while (target_distance > chain_tolarance) { + chain_backwards(); + chain_forwards(); + + final_bone2d_angle = final_bone2d_node->get_global_transform().get_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_transform().get_origin() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_transform().get_origin()); + + chain_iterations += 1; + if (chain_iterations >= chain_max_iterations) { + break; + } + } + + // Apply all of the saved transforms to the Bone2D nodes + for (int i = 0; i < fabrik_data_chain.size(); i++) { + Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); + ERR_CONTINUE_MSG(!joint_bone2d_node, "Joint " + itos(i) + " does not have a Bone2D node set!"); + 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()); + } + + // Account for constraints + if (fabrik_data_chain[i].enable_constraint) { + chain_trans.set_rotation(clamp_angle(chain_trans.get_rotation(), fabrik_data_chain[i].constraint_angle_min, + fabrik_data_chain[i].constraint_angle_max, fabrik_data_chain[i].constraint_angle_invert)); + } + } + // 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_transform().get_scale()); + + // Apply to the bone, and to the override + joint_bone2d_node->set_global_transform(chain_trans); + stack->skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, joint_bone2d_node->get_transform(), stack->strength, true); + } +} + +void SkeletonModification2DFABRIK::chain_backwards() { + int final_joint_index = fabrik_data_chain.size() - 1; + Bone2D *final_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[final_joint_index].bone2d_node_cache)); + Transform2D final_bone2d_trans = fabrik_transform_chain[final_joint_index]; + + // 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(); + } + + if (fabrik_data_chain[final_joint_index].enable_constraint) { + final_bone2d_angle = clamp_angle(final_bone2d_angle, fabrik_data_chain[final_joint_index].constraint_angle_min, + fabrik_data_chain[final_joint_index].constraint_angle_max, fabrik_data_chain[final_joint_index].constraint_angle_invert); + } + 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 = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); + Transform2D current_pose = fabrik_transform_chain[i]; + + // Commenting out this chunk of code makes the solves look better, but then it doesn't always find a solution even when one is possible... + if (fabrik_data_chain[i].enable_constraint) { + float previous_to_current_angle = 0; + float previous_to_current_length = previous_pose.get_origin().distance_to(current_pose.get_origin()); + + // Calculate the current angle + Vector2 previous_to_current_dir = previous_pose.get_origin().direction_to(current_pose.get_origin()); + previous_to_current_angle = Math::atan2(previous_to_current_dir.y, previous_to_current_dir.x); + + // Clamp the angle + previous_to_current_angle = clamp_angle(previous_to_current_angle, fabrik_data_chain[i].constraint_angle_min, fabrik_data_chain[i].constraint_angle_max, fabrik_data_chain[i].constraint_angle_invert); + + current_pose.set_origin(previous_pose.get_origin() + (Vector2(Math::cos(previous_to_current_angle), Math::sin(previous_to_current_angle)) * previous_to_current_length)); + } + + 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 / (previous_pose.get_origin() - current_pose.get_origin()).length(); + 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 = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); + Transform2D current_pose = fabrik_transform_chain[i]; + Transform2D next_pose = fabrik_transform_chain[i + 1]; + + if (fabrik_data_chain[i].enable_constraint) { + float next_to_current_angle = 0; + float next_to_current_length = next_pose.get_origin().distance_to(current_pose.get_origin()); + + // Calculate the current angle + Vector2 next_to_current_dir = current_pose.get_origin().direction_to(next_pose.get_origin()); + next_to_current_angle = Math::atan2(next_to_current_dir.y, next_to_current_dir.x); + + // Clamp the angle + next_to_current_angle = clamp_angle(next_to_current_angle, fabrik_data_chain[i].constraint_angle_min, fabrik_data_chain[i].constraint_angle_max, fabrik_data_chain[i].constraint_angle_invert); + + next_pose.set_origin(current_pose.get_origin() + (Vector2(Math::cos(next_to_current_angle), Math::sin(next_to_current_angle)) * next_to_current_length)); + } + + 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() - next_pose.get_origin()).length(); + 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; + update_target_cache(); + } +} + +void SkeletonModification2DFABRIK::update_target_cache() { + if (!is_setup || !stack) { + WARN_PRINT("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!"); + 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) { + WARN_PRINT("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 Bone2D cache: node is this modification's skeleton or cannot be found!"); + fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to(node); + if (bone) { + fabrik_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton(); + } else { + ERR_FAIL_MSG("FABRIK 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); + _change_notify(); +} + +int SkeletonModification2DFABRIK::get_fabrik_data_chain_length() { + return fabrik_data_chain.size(); +} + +void SkeletonModification2DFABRIK::fabrik_joint_set_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); + + _change_notify(); +} + +NodePath SkeletonModification2DFABRIK::fabrik_joint_get_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::fabrik_joint_set_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 bone index for this modification..."); + fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + } + } else { + WARN_PRINT("Cannot verify the FABRIK joint bone index for this modification..."); + fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + } + + _change_notify(); +} + +int SkeletonModification2DFABRIK::fabrik_joint_get_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::fabrik_joint_set_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::fabrik_joint_get_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::fabrik_joint_set_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::fabrik_joint_get_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::fabrik_joint_set_enable_constraint(int p_joint_idx, bool p_constraint) { + ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); + fabrik_data_chain.write[p_joint_idx].enable_constraint = p_constraint; + _change_notify(); +} + +bool SkeletonModification2DFABRIK::fabrik_joint_get_enable_constraint(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].enable_constraint; +} + +void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_min(int p_joint_idx, float p_angle_min) { + ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); + fabrik_data_chain.write[p_joint_idx].constraint_angle_min = p_angle_min; +} + +float SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_min(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), 0.0, "FABRIK joint out of range!"); + return fabrik_data_chain[p_joint_idx].constraint_angle_min; +} + +void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_max(int p_joint_idx, float p_angle_max) { + ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); + fabrik_data_chain.write[p_joint_idx].constraint_angle_max = p_angle_max; +} + +float SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_max(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), 0.0, "FABRIK joint out of range!"); + return fabrik_data_chain[p_joint_idx].constraint_angle_max; +} + +void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_invert(int p_joint_idx, bool p_invert) { + ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); + fabrik_data_chain.write[p_joint_idx].constraint_angle_invert = p_invert; +} + +bool SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_invert(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].constraint_angle_invert; +} + +void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace) { + ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); + fabrik_data_chain.write[p_joint_idx].constraint_in_localspace = p_constraint_in_localspace; +} + +bool SkeletonModification2DFABRIK::fabrik_joint_get_constraint_in_localspace(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].constraint_in_localspace; +} + +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("fabrik_joint_set_bone2d_node", "joint_idx", "bone2d_nodepath"), &SkeletonModification2DFABRIK::fabrik_joint_set_bone2d_node); + ClassDB::bind_method(D_METHOD("fabrik_joint_get_bone2d_node", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_bone2d_node); + ClassDB::bind_method(D_METHOD("fabrik_joint_set_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DFABRIK::fabrik_joint_set_bone_index); + ClassDB::bind_method(D_METHOD("fabrik_joint_get_bone_index", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_bone_index); + ClassDB::bind_method(D_METHOD("fabrik_joint_set_magnet_position", "joint_idx", "magnet_position"), &SkeletonModification2DFABRIK::fabrik_joint_set_magnet_position); + ClassDB::bind_method(D_METHOD("fabrik_joint_get_magnet_position", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_magnet_position); + ClassDB::bind_method(D_METHOD("fabrik_joint_set_use_target_rotation", "joint_idx", "use_target_rotation"), &SkeletonModification2DFABRIK::fabrik_joint_set_use_target_rotation); + ClassDB::bind_method(D_METHOD("fabrik_joint_get_use_target_rotation", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_use_target_rotation); + ClassDB::bind_method(D_METHOD("fabrik_joint_set_enable_constraint", "joint_idx", "enable_constraint"), &SkeletonModification2DFABRIK::fabrik_joint_set_enable_constraint); + ClassDB::bind_method(D_METHOD("fabrik_joint_get_enable_constraint", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_enable_constraint); + ClassDB::bind_method(D_METHOD("fabrik_joint_set_constraint_angle_min", "joint_idx", "angle_min"), &SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_min); + ClassDB::bind_method(D_METHOD("fabrik_joint_get_constraint_angle_min", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_min); + ClassDB::bind_method(D_METHOD("fabrik_joint_set_constraint_angle_max", "joint_idx", "angle_max"), &SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_max); + ClassDB::bind_method(D_METHOD("fabrik_joint_get_constraint_angle_max", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_max); + ClassDB::bind_method(D_METHOD("fabrik_joint_set_constraint_angle_invert", "joint_idx", "invert"), &SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_invert); + ClassDB::bind_method(D_METHOD("fabrik_joint_get_constraint_angle_invert", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_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::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; +} + +SkeletonModification2DFABRIK::~SkeletonModification2DFABRIK() { +} diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index c5ce1cdac50d..1ba69061f3a4 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -258,4 +258,89 @@ class SkeletonModification2DCCDIK : public SkeletonModification2D { ~SkeletonModification2DCCDIK(); }; +/////////////////////////////////////// +// 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 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; + }; + + Vector 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 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(); + Transform2D origin_global_pose = Transform2D(); + + 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 *p_list) const; + +public: + void execute(float 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 fabrik_joint_set_bone2d_node(int p_joint_idx, const NodePath &p_target_node); + NodePath fabrik_joint_get_bone2d_node(int p_joint_idx) const; + void fabrik_joint_set_bone_index(int p_joint_idx, int p_bone_idx); + int fabrik_joint_get_bone_index(int p_joint_idx) const; + + void fabrik_joint_set_magnet_position(int p_joint_idx, Vector2 p_magnet_position); + Vector2 fabrik_joint_get_magnet_position(int p_joint_idx) const; + void fabrik_joint_set_use_target_rotation(int p_joint_idx, bool p_use_target_rotation); + bool fabrik_joint_get_use_target_rotation(int p_joint_idx) const; + void fabrik_joint_set_enable_constraint(int p_joint_idx, bool p_constraint); + bool fabrik_joint_get_enable_constraint(int p_joint_idx) const; + void fabrik_joint_set_constraint_angle_min(int p_joint_idx, float p_angle_min); + float fabrik_joint_get_constraint_angle_min(int p_joint_idx) const; + void fabrik_joint_set_constraint_angle_max(int p_joint_idx, float p_angle_max); + float fabrik_joint_get_constraint_angle_max(int p_joint_idx) const; + void fabrik_joint_set_constraint_angle_invert(int p_joint_idx, bool p_invert); + bool fabrik_joint_get_constraint_angle_invert(int p_joint_idx) const; + void fabrik_joint_set_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace); + bool fabrik_joint_get_constraint_in_localspace(int p_joint_idx) const; + + SkeletonModification2DFABRIK(); + ~SkeletonModification2DFABRIK(); +}; + #endif // SKELETONMODIFICATION2D_H From e614ca15a0685de45f92e593da659fb4c1a9d2b9 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Mon, 3 Aug 2020 14:53:10 -0400 Subject: [PATCH 05/34] Changes: * SkeletonModification2DJiggle: Added full implementation * RegisterSceneTypes: Registered the 2D Jiggle modification. NOTE: Commit is squashed changes from the following verbose branch: https://github.com/TwistedTwigleg/godot/tree/GSOC_2020_Working_Branch_2D_IK_VERBOSE_ARCHIVE --- scene/register_scene_types.cpp | 1 + scene/resources/skeleton_modification_2d.cpp | 522 +++++++++++++++++++ scene/resources/skeleton_modification_2d.h | 102 ++++ 3 files changed, 625 insertions(+) diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 45b1ce6a1dc6..2266f02c0afe 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -663,6 +663,7 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); OS::get_singleton()->yield(); //may take time to init diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 44787ad800c4..ed623bc4db6e 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -1534,3 +1534,525 @@ SkeletonModification2DFABRIK::SkeletonModification2DFABRIK() { SkeletonModification2DFABRIK::~SkeletonModification2DFABRIK() { } + +/////////////////////////////////////// +// Jiggle +/////////////////////////////////////// + +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") { + jiggle_joint_set_bone2d_node(which, p_value); + } else if (what == "bone_index") { + jiggle_joint_set_bone_index(which, p_value); + } else if (what == "override_defaults") { + jiggle_joint_set_override(which, p_value); + } else if (what == "stiffness") { + jiggle_joint_set_stiffness(which, p_value); + } else if (what == "mass") { + jiggle_joint_set_mass(which, p_value); + } else if (what == "damping") { + jiggle_joint_set_damping(which, p_value); + } else if (what == "use_gravity") { + jiggle_joint_set_use_gravity(which, p_value); + } else if (what == "gravity") { + jiggle_joint_set_gravity(which, p_value); + } + return true; + } else { + if (path == "use_colliders") { + set_use_colliders(p_value); + } else if (path == "collision_mask") { + set_collision_mask(p_value); + } + } + 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 = jiggle_joint_get_bone2d_node(which); + } else if (what == "bone_index") { + r_ret = jiggle_joint_get_bone_index(which); + } else if (what == "override_defaults") { + r_ret = jiggle_joint_get_override(which); + } else if (what == "stiffness") { + r_ret = jiggle_joint_get_stiffness(which); + } else if (what == "mass") { + r_ret = jiggle_joint_get_mass(which); + } else if (what == "damping") { + r_ret = jiggle_joint_get_damping(which); + } else if (what == "use_gravity") { + r_ret = jiggle_joint_get_use_gravity(which); + } else if (what == "gravity") { + r_ret = jiggle_joint_get_gravity(which); + } + return true; + } else { + if (path == "use_colliders") { + r_ret = get_use_colliders(); + } else if (path == "collision_mask") { + r_ret = get_collision_mask(); + } + } + return true; +} + +void SkeletonModification2DJiggle::_get_property_list(List *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 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()) { + update_target_cache(); + WARN_PRINT("Target cache is out of date. Updating..."); + return; + } + Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + ERR_FAIL_COND_MSG(!target, "Target node is not a Node2D-based node. Cannot execute modification!"); + ERR_FAIL_COND_MSG(!target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!"); + + for (int i = 0; i < jiggle_data_chain.size(); i++) { + _execute_jiggle_joint(i, target, delta); + } +} + +void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D *target, float delta) { + // Adopted from: https://wiki.unity3d.com/index.php/JiggleBone + // With modifications by TwistedTwigleg. + + ERR_FAIL_COND_MSG(jiggle_data_chain[p_joint_idx].bone_idx <= -1, "Jiggle joint " + itos(p_joint_idx) + " bone index is invalid. Cannot execute modification on joint..."); + + if (jiggle_data_chain[p_joint_idx].bone2d_node_cache.is_null() && !jiggle_data_chain[p_joint_idx].bone2d_node.is_empty()) { + jiggle_joint_update_bone2d_cache(p_joint_idx); + WARN_PRINT("Bone2D cache for joint " + itos(p_joint_idx) + " is out of date. Updating..."); + } + Bone2D *operation_bone = stack->skeleton->get_bone(jiggle_data_chain[p_joint_idx].bone_idx); + ERR_FAIL_COND_MSG(!operation_bone, "Jiggle joint " + itos(p_joint_idx) + " does not have a Bone2D node or it cannot be found!"); + + Transform2D operation_bone_trans = operation_bone->get_global_transform(); + Vector2 target_position = target->get_global_transform().get_origin(); + + 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 * 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 * 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 (stack->execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process) { + Ref 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; + + // Add exception support? + bool ray_hit = space_state->intersect_ray(operation_bone_trans.get_origin(), jiggle_data_chain[p_joint_idx].dynamic_position, + ray_result, Set(), collision_mask); + + 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("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_transform().get_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) { + jiggle_joint_set_stiffness(i, stiffness); + jiggle_joint_set_mass(i, mass); + jiggle_joint_set_damping(i, damping); + jiggle_joint_set_use_gravity(i, use_gravity); + jiggle_joint_set_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_transform().get_origin(); + } + } + } + + update_target_cache(); + } +} + +void SkeletonModification2DJiggle::update_target_cache() { + if (!is_setup || !stack) { + WARN_PRINT("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 cache: Target node is this modification's skeleton or cannot be found!"); + 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) { + WARN_PRINT("Cannot update Jiggle 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 Bone2D cache: node is this modification's skeleton or cannot be found!"); + jiggle_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to(node); + if (bone) { + jiggle_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton(); + } else { + ERR_FAIL_MSG("Jiggle 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; + _change_notify(); +} + +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); + _change_notify(); +} + +void SkeletonModification2DJiggle::jiggle_joint_set_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); + + _change_notify(); +} + +NodePath SkeletonModification2DJiggle::jiggle_joint_get_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::jiggle_joint_set_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 bone index for this modification..."); + jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + } + } else { + WARN_PRINT("Cannot verify the Jiggle joint bone index for this modification..."); + jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + } + + _change_notify(); +} + +int SkeletonModification2DJiggle::jiggle_joint_get_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::jiggle_joint_set_override(int joint_idx, bool p_override) { + ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[joint_idx].override_defaults = p_override; + _update_jiggle_joint_data(); + _change_notify(); +} + +bool SkeletonModification2DJiggle::jiggle_joint_get_override(int joint_idx) const { + ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), false); + return jiggle_data_chain[joint_idx].override_defaults; +} + +void SkeletonModification2DJiggle::jiggle_joint_set_stiffness(int joint_idx, float p_stiffness) { + ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!"); + ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[joint_idx].stiffness = p_stiffness; +} + +float SkeletonModification2DJiggle::jiggle_joint_get_stiffness(int joint_idx) const { + ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); + return jiggle_data_chain[joint_idx].stiffness; +} + +void SkeletonModification2DJiggle::jiggle_joint_set_mass(int joint_idx, float p_mass) { + ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!"); + ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[joint_idx].mass = p_mass; +} + +float SkeletonModification2DJiggle::jiggle_joint_get_mass(int joint_idx) const { + ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); + return jiggle_data_chain[joint_idx].mass; +} + +void SkeletonModification2DJiggle::jiggle_joint_set_damping(int joint_idx, float p_damping) { + ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!"); + ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[joint_idx].damping = p_damping; +} + +float SkeletonModification2DJiggle::jiggle_joint_get_damping(int joint_idx) const { + ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); + return jiggle_data_chain[joint_idx].damping; +} + +void SkeletonModification2DJiggle::jiggle_joint_set_use_gravity(int joint_idx, bool p_use_gravity) { + ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[joint_idx].use_gravity = p_use_gravity; + _change_notify(); +} + +bool SkeletonModification2DJiggle::jiggle_joint_get_use_gravity(int joint_idx) const { + ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), false); + return jiggle_data_chain[joint_idx].use_gravity; +} + +void SkeletonModification2DJiggle::jiggle_joint_set_gravity(int joint_idx, Vector2 p_gravity) { + ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[joint_idx].gravity = p_gravity; +} + +Vector2 SkeletonModification2DJiggle::jiggle_joint_get_gravity(int joint_idx) const { + ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), Vector2(0, 0)); + return jiggle_data_chain[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("jiggle_joint_set_bone2d_node", "joint_idx", "bone2d_node"), &SkeletonModification2DJiggle::jiggle_joint_set_bone2d_node); + ClassDB::bind_method(D_METHOD("jiggle_joint_get_bone2d_node", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_bone2d_node); + ClassDB::bind_method(D_METHOD("jiggle_joint_set_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DJiggle::jiggle_joint_set_bone_index); + ClassDB::bind_method(D_METHOD("jiggle_joint_get_bone_index", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_bone_index); + ClassDB::bind_method(D_METHOD("jiggle_joint_set_override", "joint_idx", "override"), &SkeletonModification2DJiggle::jiggle_joint_set_override); + ClassDB::bind_method(D_METHOD("jiggle_joint_get_override", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_override); + ClassDB::bind_method(D_METHOD("jiggle_joint_set_stiffness", "joint_idx", "stiffness"), &SkeletonModification2DJiggle::jiggle_joint_set_stiffness); + ClassDB::bind_method(D_METHOD("jiggle_joint_get_stiffness", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_stiffness); + ClassDB::bind_method(D_METHOD("jiggle_joint_set_mass", "joint_idx", "mass"), &SkeletonModification2DJiggle::jiggle_joint_set_mass); + ClassDB::bind_method(D_METHOD("jiggle_joint_get_mass", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_mass); + ClassDB::bind_method(D_METHOD("jiggle_joint_set_damping", "joint_idx", "damping"), &SkeletonModification2DJiggle::jiggle_joint_set_damping); + ClassDB::bind_method(D_METHOD("jiggle_joint_get_damping", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_damping); + ClassDB::bind_method(D_METHOD("jiggle_joint_set_use_gravity", "joint_idx", "use_gravity"), &SkeletonModification2DJiggle::jiggle_joint_set_use_gravity); + ClassDB::bind_method(D_METHOD("jiggle_joint_get_use_gravity", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_use_gravity); + ClassDB::bind_method(D_METHOD("jiggle_joint_set_gravity", "joint_idx", "gravity"), &SkeletonModification2DJiggle::jiggle_joint_set_gravity); + ClassDB::bind_method(D_METHOD("jiggle_joint_get_gravity", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_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(); + stiffness = 3; + mass = 0.75; + damping = 0.75; + use_gravity = false; + gravity = Vector2(0, 6.0); + enabled = true; +} + +SkeletonModification2DJiggle::~SkeletonModification2DJiggle() { +} diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index 1ba69061f3a4..f77dabccc550 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -343,4 +343,106 @@ class SkeletonModification2DFABRIK : public SkeletonModification2D { ~SkeletonModification2DFABRIK(); }; +/////////////////////////////////////// +// 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_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 *target, float 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 *p_list) const; + +public: + void execute(float 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 jiggle_joint_set_bone2d_node(int p_joint_idx, const NodePath &p_target_node); + NodePath jiggle_joint_get_bone2d_node(int p_joint_idx) const; + void jiggle_joint_set_bone_index(int p_joint_idx, int p_bone_idx); + int jiggle_joint_get_bone_index(int p_joint_idx) const; + + void jiggle_joint_set_override(int p_joint_idx, bool p_override); + bool jiggle_joint_get_override(int p_joint_idx) const; + void jiggle_joint_set_stiffness(int p_joint_idx, float p_stiffness); + float jiggle_joint_get_stiffness(int p_joint_idx) const; + void jiggle_joint_set_mass(int p_joint_idx, float p_mass); + float jiggle_joint_get_mass(int p_joint_idx) const; + void jiggle_joint_set_damping(int p_joint_idx, float p_damping); + float jiggle_joint_get_damping(int p_joint_idx) const; + void jiggle_joint_set_use_gravity(int p_joint_idx, bool p_use_gravity); + bool jiggle_joint_get_use_gravity(int p_joint_idx) const; + void jiggle_joint_set_gravity(int p_joint_idx, Vector2 p_gravity); + Vector2 jiggle_joint_get_gravity(int p_joint_idx) const; + + SkeletonModification2DJiggle(); + ~SkeletonModification2DJiggle(); +}; + #endif // SKELETONMODIFICATION2D_H From 283d16d6954228f61f08618fe5235b49de189788 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Mon, 3 Aug 2020 15:01:48 -0400 Subject: [PATCH 06/34] Changes: * SkeletonModification2DTwoBoneIK: Added full implementation * RegisterSceneTypes: Registered the TwoBoneIK in 2D NOTE: This commits contains squished changes from the following verbose branch: https://github.com/TwistedTwigleg/godot/tree/GSOC_2020_Working_Branch_2D_IK_VERBOSE_ARCHIVE --- scene/register_scene_types.cpp | 1 + scene/resources/skeleton_modification_2d.cpp | 499 +++++++++++++++++++ scene/resources/skeleton_modification_2d.h | 87 ++++ 3 files changed, 587 insertions(+) diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 2266f02c0afe..e762b2bca4da 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -664,6 +664,7 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); OS::get_singleton()->yield(); //may take time to init diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index ed623bc4db6e..5d61626e05e4 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -2056,3 +2056,502 @@ SkeletonModification2DJiggle::SkeletonModification2DJiggle() { SkeletonModification2DJiggle::~SkeletonModification2DJiggle() { } + +/////////////////////////////////////// +// TwoBoneIK +/////////////////////////////////////// + +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_one_enable_constraint") { + set_joint_one_enable_constraint(p_value); + } else if (path == "joint_one_constraint_angle_min") { + set_joint_one_constraint_angle_min(Math::deg2rad(float(p_value))); + } else if (path == "joint_one_constraint_angle_max") { + set_joint_one_constraint_angle_max(Math::deg2rad(float(p_value))); + } else if (path == "joint_one_constraint_angle_invert") { + set_joint_one_constraint_angle_invert(p_value); + } else if (path == "joint_one_constraint_in_localspace") { + set_joint_one_constraint_in_localspace(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); + } else if (path == "joint_two_enable_constraint") { + set_joint_two_enable_constraint(p_value); + } else if (path == "joint_two_constraint_angle_min") { + set_joint_two_constraint_angle_min(Math::deg2rad(float(p_value))); + } else if (path == "joint_two_constraint_angle_max") { + set_joint_two_constraint_angle_max(Math::deg2rad(float(p_value))); + } else if (path == "joint_two_constraint_angle_invert") { + set_joint_two_constraint_angle_invert(p_value); + } else if (path == "joint_two_constraint_in_localspace") { + set_joint_two_constraint_in_localspace(p_value); + } + + 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_one_enable_constraint") { + r_ret = get_joint_one_enable_constraint(); + } else if (path == "joint_one_constraint_angle_min") { + r_ret = Math::rad2deg(get_joint_one_constraint_angle_min()); + } else if (path == "joint_one_constraint_angle_max") { + r_ret = Math::rad2deg(get_joint_one_constraint_angle_max()); + } else if (path == "joint_one_constraint_angle_invert") { + r_ret = get_joint_one_constraint_angle_invert(); + } else if (path == "joint_one_constraint_in_localspace") { + r_ret = get_joint_one_constraint_in_localspace(); + } 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(); + } else if (path == "joint_two_enable_constraint") { + r_ret = get_joint_two_enable_constraint(); + } else if (path == "joint_two_constraint_angle_min") { + r_ret = Math::rad2deg(get_joint_two_constraint_angle_min()); + } else if (path == "joint_two_constraint_angle_max") { + r_ret = Math::rad2deg(get_joint_two_constraint_angle_max()); + } else if (path == "joint_two_constraint_angle_invert") { + r_ret = get_joint_two_constraint_angle_invert(); + } else if (path == "joint_two_constraint_in_localspace") { + r_ret = get_joint_two_constraint_in_localspace(); + } + + return true; +} + +void SkeletonModification2DTwoBoneIK::_get_property_list(List *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::BOOL, "joint_one_enable_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + if (joint_one_enable_constraint) { + p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_one_constraint_angle_min", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_one_constraint_angle_max", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::BOOL, "joint_one_constraint_angle_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::BOOL, "joint_one_constraint_in_localspace", PROPERTY_HINT_NONE, "", 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)); + p_list->push_back(PropertyInfo(Variant::BOOL, "joint_two_enable_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + if (joint_two_enable_constraint) { + p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_two_constraint_angle_min", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_two_constraint_angle_max", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::BOOL, "joint_two_constraint_angle_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::BOOL, "joint_two_constraint_in_localspace", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } +} + +void SkeletonModification2DTwoBoneIK::execute(float 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()) { + update_target_cache(); + WARN_PRINT("Target cache is out of date. Updating..."); + return; + } + + if (joint_one_bone2d_node_cache.is_null() && !joint_one_bone2d_node.is_empty()) { + update_joint_one_bone2d_cache(); + WARN_PRINT("Joint One Bone2D node cache is out of date. Updating..."); + } + if (joint_two_bone2d_node_cache.is_null() && !joint_two_bone2d_node.is_empty()) { + update_joint_two_bone2d_cache(); + WARN_PRINT("Joint Two Bone2D node cache is out of date. Updating..."); + } + + Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + ERR_FAIL_COND_MSG(!target, "Target node is not a Node2D-based node. Cannot execute modification!"); + ERR_FAIL_COND_MSG(!target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!"); + + Bone2D *joint_one_bone = stack->skeleton->get_bone(joint_one_bone_idx); + ERR_FAIL_COND_MSG(joint_one_bone == nullptr, "Joint one bone_idx does not point to a valid bone! Cannot execute modification!"); + Bone2D *joint_two_bone = stack->skeleton->get_bone(joint_two_bone_idx); + ERR_FAIL_COND_MSG(joint_one_bone == nullptr, "Joint two bone_idx does not point to a valid bone! Cannot execute modification!"); + + // 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_transform().get_origin() - joint_one_bone->get_global_transform().get_origin(); + float joint_one_to_target = target_difference.length(); + float angle_atan = Math::atan2(target_difference.y, target_difference.x); + + 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 (bone_one_length + bone_two_length < joint_one_to_target) { + override_angles_due_to_out_of_range = true; + } else if (joint_one_to_target < target_minimum_distance) { + joint_one_to_target = target_minimum_distance; + } + + 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; + } + + 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()); + } + + // global constrains + if (joint_one_enable_constraint && !joint_one_constraint_in_localspace) { + joint_one_bone->set_global_rotation(clamp_angle(joint_one_bone->get_global_rotation(), joint_one_constraint_angle_min, joint_one_constraint_angle_max, joint_one_constraint_angle_invert)); + } + if (joint_two_enable_constraint && !joint_two_constraint_in_localspace) { + joint_two_bone->set_global_rotation(clamp_angle(joint_two_bone->get_global_rotation(), joint_two_constraint_angle_min, joint_two_constraint_angle_max, joint_two_constraint_angle_invert)); + } + + // local constrains + if (joint_one_enable_constraint && joint_one_constraint_in_localspace) { + joint_one_bone->set_rotation(clamp_angle(joint_one_bone->get_rotation(), joint_one_constraint_angle_min, joint_one_constraint_angle_max, joint_one_constraint_angle_invert)); + } + if (joint_two_enable_constraint && joint_two_constraint_in_localspace) { + joint_two_bone->set_rotation(clamp_angle(joint_two_bone->get_rotation(), joint_two_constraint_angle_min, joint_two_constraint_angle_max, joint_two_constraint_angle_invert)); + } + + 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::update_target_cache() { + if (!is_setup || !stack) { + WARN_PRINT("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 cache: Target node is this modification's skeleton or cannot be found!"); + target_node_cache = node->get_instance_id(); + } + } + } +} + +void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { + if (!is_setup || !stack) { + WARN_PRINT("Cannot update 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 update joint one Bone2D cache: node is this modification's skeleton or cannot be found!"); + joint_one_bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to(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) { + WARN_PRINT("Cannot update 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 update joint two Bone2D cache: node is this modification's skeleton or cannot be found!"); + joint_two_bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to(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(); + _change_notify(); +} + +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_flip_bend_direction(bool p_flip_direction) { + flip_bend_direction = p_flip_direction; +} + +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(); + _change_notify(); +} + +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 { + WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint one..."); + joint_one_bone_idx = p_bone_idx; + } + + _change_notify(); +} + +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 { + WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint two..."); + joint_two_bone_idx = p_bone_idx; + } + + _change_notify(); +} + +int SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx() const { + return joint_two_bone_idx; +} + +// TwoBoneIK property functions + +void SkeletonModification2DTwoBoneIK::set_joint_one_enable_constraint(bool p_constraint) { + joint_one_enable_constraint = p_constraint; + _change_notify(); +} + +bool SkeletonModification2DTwoBoneIK::get_joint_one_enable_constraint() const { + return joint_one_enable_constraint; +} + +void SkeletonModification2DTwoBoneIK::set_joint_one_constraint_angle_min(float p_angle) { + joint_one_constraint_angle_min = p_angle; +} + +float SkeletonModification2DTwoBoneIK::get_joint_one_constraint_angle_min() const { + return joint_one_constraint_angle_min; +} + +void SkeletonModification2DTwoBoneIK::set_joint_one_constraint_angle_max(float p_angle) { + joint_one_constraint_angle_max = p_angle; +} + +float SkeletonModification2DTwoBoneIK::get_joint_one_constraint_angle_max() const { + return joint_one_constraint_angle_max; +} + +void SkeletonModification2DTwoBoneIK::set_joint_one_constraint_angle_invert(bool p_invert) { + joint_one_constraint_angle_invert = p_invert; +} + +bool SkeletonModification2DTwoBoneIK::get_joint_one_constraint_angle_invert() const { + return joint_one_constraint_angle_invert; +} + +void SkeletonModification2DTwoBoneIK::set_joint_one_constraint_in_localspace(bool p_in_localspace) { + joint_one_constraint_in_localspace = p_in_localspace; +} + +bool SkeletonModification2DTwoBoneIK::get_joint_one_constraint_in_localspace() const { + return joint_one_constraint_in_localspace; +} + +void SkeletonModification2DTwoBoneIK::set_joint_two_enable_constraint(bool p_constraint) { + joint_two_enable_constraint = p_constraint; + _change_notify(); +} + +bool SkeletonModification2DTwoBoneIK::get_joint_two_enable_constraint() const { + return joint_two_enable_constraint; +} + +void SkeletonModification2DTwoBoneIK::set_joint_two_constraint_angle_min(float p_angle) { + joint_two_constraint_angle_min = p_angle; +} + +float SkeletonModification2DTwoBoneIK::get_joint_two_constraint_angle_min() const { + return joint_two_constraint_angle_min; +} + +void SkeletonModification2DTwoBoneIK::set_joint_two_constraint_angle_max(float p_angle) { + joint_two_constraint_angle_max = p_angle; +} + +float SkeletonModification2DTwoBoneIK::get_joint_two_constraint_angle_max() const { + return joint_two_constraint_angle_max; +} + +void SkeletonModification2DTwoBoneIK::set_joint_two_constraint_angle_invert(bool p_invert) { + joint_two_constraint_angle_invert = p_invert; +} + +bool SkeletonModification2DTwoBoneIK::get_joint_two_constraint_angle_invert() const { + return joint_two_constraint_angle_invert; +} + +void SkeletonModification2DTwoBoneIK::set_joint_two_constraint_in_localspace(bool p_in_localspace) { + joint_two_constraint_in_localspace = p_in_localspace; +} + +bool SkeletonModification2DTwoBoneIK::get_joint_two_constraint_in_localspace() const { + return joint_two_constraint_in_localspace; +} + +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_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_one_enable_constraint", "enable_constraint"), &SkeletonModification2DTwoBoneIK::set_joint_one_enable_constraint); + ClassDB::bind_method(D_METHOD("get_joint_one_enable_constraint"), &SkeletonModification2DTwoBoneIK::get_joint_one_enable_constraint); + ClassDB::bind_method(D_METHOD("set_joint_one_constraint_angle_min", "angle"), &SkeletonModification2DTwoBoneIK::set_joint_one_constraint_angle_min); + ClassDB::bind_method(D_METHOD("get_joint_one_constraint_angle_min"), &SkeletonModification2DTwoBoneIK::get_joint_one_constraint_angle_min); + ClassDB::bind_method(D_METHOD("set_joint_one_constraint_angle_max", "angle"), &SkeletonModification2DTwoBoneIK::set_joint_one_constraint_angle_max); + ClassDB::bind_method(D_METHOD("get_joint_one_constraint_angle_max"), &SkeletonModification2DTwoBoneIK::get_joint_one_constraint_angle_max); + ClassDB::bind_method(D_METHOD("set_joint_one_constraint_angle_invert", "invert"), &SkeletonModification2DTwoBoneIK::set_joint_one_constraint_angle_invert); + ClassDB::bind_method(D_METHOD("get_joint_one_constraint_angle_invert"), &SkeletonModification2DTwoBoneIK::get_joint_one_constraint_angle_invert); + ClassDB::bind_method(D_METHOD("set_joint_one_constraint_in_localspace", "in_localspace"), &SkeletonModification2DTwoBoneIK::set_joint_one_constraint_in_localspace); + ClassDB::bind_method(D_METHOD("get_joint_one_constraint_in_localspace"), &SkeletonModification2DTwoBoneIK::get_joint_one_constraint_in_localspace); + + 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); + ClassDB::bind_method(D_METHOD("set_joint_two_enable_constraint", "enable_constraint"), &SkeletonModification2DTwoBoneIK::set_joint_two_enable_constraint); + ClassDB::bind_method(D_METHOD("get_joint_two_enable_constraint"), &SkeletonModification2DTwoBoneIK::get_joint_two_enable_constraint); + ClassDB::bind_method(D_METHOD("set_joint_two_constraint_angle_min", "angle"), &SkeletonModification2DTwoBoneIK::set_joint_two_constraint_angle_min); + ClassDB::bind_method(D_METHOD("get_joint_two_constraint_angle_min"), &SkeletonModification2DTwoBoneIK::get_joint_two_constraint_angle_min); + ClassDB::bind_method(D_METHOD("set_joint_two_constraint_angle_max", "angle"), &SkeletonModification2DTwoBoneIK::set_joint_two_constraint_angle_max); + ClassDB::bind_method(D_METHOD("get_joint_two_constraint_angle_max"), &SkeletonModification2DTwoBoneIK::get_joint_two_constraint_angle_max); + ClassDB::bind_method(D_METHOD("set_joint_two_constraint_angle_invert", "invert"), &SkeletonModification2DTwoBoneIK::set_joint_two_constraint_angle_invert); + ClassDB::bind_method(D_METHOD("get_joint_two_constraint_angle_invert"), &SkeletonModification2DTwoBoneIK::get_joint_two_constraint_angle_invert); + ClassDB::bind_method(D_METHOD("set_joint_two_constraint_in_localspace", "in_localspace"), &SkeletonModification2DTwoBoneIK::set_joint_two_constraint_in_localspace); + ClassDB::bind_method(D_METHOD("get_joint_two_constraint_in_localspace"), &SkeletonModification2DTwoBoneIK::get_joint_two_constraint_in_localspace); + + 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_NONE, ""), "set_target_minimum_distance", "get_target_minimum_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; +} + +SkeletonModification2DTwoBoneIK::~SkeletonModification2DTwoBoneIK() { +} diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index f77dabccc550..6541c60fd78f 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -445,4 +445,91 @@ class SkeletonModification2DJiggle : public SkeletonModification2D { ~SkeletonModification2DJiggle(); }; +/////////////////////////////////////// +// SkeletonModification2DTwoBoneIK +/////////////////////////////////////// + +class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { + GDCLASS(SkeletonModification2DTwoBoneIK, SkeletonModification2D); + +private: + NodePath target_node; + ObjectID target_node_cache; + float target_minimum_distance = 6; + bool flip_bend_direction = false; + + NodePath joint_one_bone2d_node; + ObjectID joint_one_bone2d_node_cache; + int joint_one_bone_idx = -1; + bool joint_one_enable_constraint = false; + float joint_one_constraint_angle_min = 0; + float joint_one_constraint_angle_max = (2.0 * Math_PI); + bool joint_one_constraint_angle_invert = false; + bool joint_one_constraint_in_localspace = true; + + NodePath joint_two_bone2d_node; + ObjectID joint_two_bone2d_node_cache; + int joint_two_bone_idx = -1; + bool joint_two_enable_constraint = false; + float joint_two_constraint_angle_min = 0; + float joint_two_constraint_angle_max = (2.0 * Math_PI); + bool joint_two_constraint_angle_invert = false; + bool joint_two_constraint_in_localspace = true; + + 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 *p_list) const; + +public: + void execute(float 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_target_minimum_distance(float p_minimum_distance); + float get_target_minimum_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_one_enable_constraint(bool p_constraint); + bool get_joint_one_enable_constraint() const; + void set_joint_one_constraint_angle_min(float p_angle); + float get_joint_one_constraint_angle_min() const; + void set_joint_one_constraint_angle_max(float p_angle); + float get_joint_one_constraint_angle_max() const; + void set_joint_one_constraint_angle_invert(bool p_invert); + bool get_joint_one_constraint_angle_invert() const; + void set_joint_one_constraint_in_localspace(bool p_in_localspace); + bool get_joint_one_constraint_in_localspace() 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; + void set_joint_two_enable_constraint(bool p_constraint); + bool get_joint_two_enable_constraint() const; + void set_joint_two_constraint_angle_min(float p_angle); + float get_joint_two_constraint_angle_min() const; + void set_joint_two_constraint_angle_max(float p_angle); + float get_joint_two_constraint_angle_max() const; + void set_joint_two_constraint_angle_invert(bool p_invert); + bool get_joint_two_constraint_angle_invert() const; + void set_joint_two_constraint_in_localspace(bool p_in_localspace); + bool get_joint_two_constraint_in_localspace() const; + + SkeletonModification2DTwoBoneIK(); + ~SkeletonModification2DTwoBoneIK(); +}; + #endif // SKELETONMODIFICATION2D_H From c9c4d52b6b5ad86c92554053f3d243ffc9bbbcd9 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Mon, 3 Aug 2020 15:14:54 -0400 Subject: [PATCH 07/34] Changes: * PhysicalBone2D: Added full node implementation * SkeletonModification2dPhysicalBones: Added full implementation * RegisterSceneTypes: Registered both the PhysicalBone2D node and the PhysicalBone2D node modification. NOTE: These changes are sqashed from the following verbose branch: https://github.com/TwistedTwigleg/godot/tree/GSOC_2020_Working_Branch_2D_IK_VERBOSE_ARCHIVE --- scene/2d/physical_bone_2d.cpp | 300 +++++++++++++++++++ scene/2d/physical_bone_2d.h | 86 ++++++ scene/register_scene_types.cpp | 4 + scene/resources/skeleton_modification_2d.cpp | 258 ++++++++++++++++ scene/resources/skeleton_modification_2d.h | 45 +++ 5 files changed, 693 insertions(+) create mode 100644 scene/2d/physical_bone_2d.cpp create mode 100644 scene/2d/physical_bone_2d.h diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp new file mode 100644 index 000000000000..ede2355496bf --- /dev/null +++ b/scene/2d/physical_bone_2d.cpp @@ -0,0 +1,300 @@ +/*************************************************************************/ +/* physical_bone_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "physical_bone_2d.h" + +void PhysicalBone2D::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + _find_skeleton_parent(); + _find_joint_child(); + + // Configure joint + if (child_joint && auto_configure_joint) { + _auto_configure_joint(); + } + + // Simulate physics if set + if (simulate_physics) { + _start_physics_simulation(); + } else { + _stop_physics_simulation(); + } + } + + if (p_what == NOTIFICATION_READY) { + _position_at_bone2d(); + set_physics_process_internal(true); + } + + // Keep the child joint in the correct position. + if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { + if (child_joint && auto_configure_joint) { + CanvasItem *node_a = Object::cast_to(child_joint->get_node(child_joint->get_node_a())); + if (node_a) { + child_joint->set_global_position(node_a->get_global_transform().get_origin()); + } + } + + // Remove any collision layers and masks if we're disabled. + // We do this so the RigidBody can still have it's layers and masks set like normal, but they will not be applied + // unless physics are told to simulate, only making them effective when we want them to be. + if (!_internal_simulate_physics) { + PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0); + PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0); + } + } + +// Only in the editor: keep the PhysicalBone2D at the Bone2D position (if there is one) at all times. +#ifdef TOOLS_ENABLED + if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { + if (Engine::get_singleton()->is_editor_hint()) { + _position_at_bone2d(); + } + } +#endif //TOOLS_ENABLED +} + +void PhysicalBone2D::_position_at_bone2d() { + // Reset to Bone2D position + if (parent_skeleton && bone2d_index > -1) { + if (bone2d_index <= parent_skeleton->get_bone_count()) { + Bone2D *bone_to_use = parent_skeleton->get_bone(bone2d_index); + set_global_transform(bone_to_use->get_global_transform()); + } + } +} + +void PhysicalBone2D::_find_skeleton_parent() { + Node *current_parent = get_parent(); + + while (current_parent != nullptr) { + Skeleton2D *potential_skeleton = Object::cast_to(current_parent); + if (potential_skeleton) { + parent_skeleton = potential_skeleton; + break; + } else { + PhysicalBone2D *potential_parent_bone = Object::cast_to(current_parent); + if (potential_parent_bone) { + current_parent = potential_parent_bone->get_parent(); + } else { + current_parent = nullptr; + } + } + } +} + +void PhysicalBone2D::_find_joint_child() { + for (int i = 0; i < get_child_count(); i++) { + Node *child_node = get_child(i); + Joint2D *potential_joint = Object::cast_to(child_node); + if (potential_joint) { + child_joint = potential_joint; + break; + } + } +} + +String PhysicalBone2D::get_configuration_warning() const { + String warning = Node2D::get_configuration_warning(); + + if (!parent_skeleton) { + if (warning != String()) { + warning += "\n\n"; + } + warning += TTR("A PhysicalBone2D only works with a Skeleton2D or another PhysicalBone2D as a parent node!"); + } + + if (parent_skeleton && bone2d_index <= -1) { + if (warning != String()) { + warning += "\n\n"; + } + warning += TTR("A PhysicalBone2D needs to be assigned to a Bone2D node in order to function! Please set a Bone2D node in the inspector."); + } + + if (!child_joint) { + PhysicalBone2D *parent_bone = Object::cast_to(get_parent()); + if (parent_bone) { + if (warning != String()) { + warning += "\n\n"; + } + warning += TTR("A PhysicalBone2D node should have a Joint2D-based child node to keep bones connected! Please add a Joint2D-based node as a child to this node!"); + } + } + + return warning; +} + +void PhysicalBone2D::_auto_configure_joint() { + if (!auto_configure_joint) { + return; + } + + if (child_joint) { + // Node A = parent | Node B = this node + Node *parent_node = get_parent(); + PhysicalBone2D *potential_parent_bone = Object::cast_to(parent_node); + + if (potential_parent_bone) { + child_joint->set_node_a(child_joint->get_path_to(potential_parent_bone)); + child_joint->set_node_b(child_joint->get_path_to(this)); + + // Place the child joint at the parent's position. + child_joint->set_global_position(potential_parent_bone->get_global_position()); + + } else { + WARN_PRINT("Cannot setup joint without a parent PhysicalBone2D node."); + } + } +} + +void PhysicalBone2D::_start_physics_simulation() { + if (_internal_simulate_physics) { + return; + } + + // Reset to Bone2D position + _position_at_bone2d(); + + // Let the RigidBody executing its force integration. + PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + + // Apply the layers and masks + PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); + PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); + + _internal_simulate_physics = true; +} + +void PhysicalBone2D::_stop_physics_simulation() { + if (_internal_simulate_physics) { + // Stop the RigidBody from executing its force integration. + PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), nullptr, ""); + _internal_simulate_physics = false; + + // Reset to Bone2D position + _position_at_bone2d(); + } +} + +Joint2D *PhysicalBone2D::get_joint() const { + return child_joint; +} + +bool PhysicalBone2D::get_auto_configure_joint() const { + return auto_configure_joint; +} + +void PhysicalBone2D::set_auto_configure_joint(bool p_auto_configure) { + auto_configure_joint = p_auto_configure; + _auto_configure_joint(); +} + +void PhysicalBone2D::set_simulate_physics(bool p_simulate) { + if (p_simulate == simulate_physics) { + return; + } + simulate_physics = p_simulate; + + if (simulate_physics) { + _start_physics_simulation(); + } else { + _stop_physics_simulation(); + } +} + +bool PhysicalBone2D::get_simulate_physics() const { + return simulate_physics; +} + +bool PhysicalBone2D::is_simulating_physics() const { + return _internal_simulate_physics; +} + +void PhysicalBone2D::set_bone2d_nodepath(const NodePath &p_nodepath) { + bone2d_nodepath = p_nodepath; + _change_notify(); +} + +NodePath PhysicalBone2D::get_bone2d_nodepath() const { + return bone2d_nodepath; +} + +void PhysicalBone2D::set_bone2d_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_inside_tree()) { + bone2d_index = p_bone_idx; + return; + } + + if (parent_skeleton) { + ERR_FAIL_INDEX_MSG(p_bone_idx, parent_skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); + bone2d_index = p_bone_idx; + + bone2d_nodepath = get_path_to(parent_skeleton->get_bone(bone2d_index)); + } else { + WARN_PRINT("Cannot verify bone index..."); + bone2d_index = p_bone_idx; + } + + _change_notify(); +} + +int PhysicalBone2D::get_bone2d_index() const { + return bone2d_index; +} + +void PhysicalBone2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_joint"), &PhysicalBone2D::get_joint); + ClassDB::bind_method(D_METHOD("get_auto_configure_joint"), &PhysicalBone2D::get_auto_configure_joint); + ClassDB::bind_method(D_METHOD("set_auto_configure_joint", "auto_configure_joint"), &PhysicalBone2D::set_auto_configure_joint); + + ClassDB::bind_method(D_METHOD("set_simulate_physics", "simulate_physics"), &PhysicalBone2D::set_simulate_physics); + ClassDB::bind_method(D_METHOD("get_simulate_physics"), &PhysicalBone2D::get_simulate_physics); + ClassDB::bind_method(D_METHOD("is_simulating_physics"), &PhysicalBone2D::is_simulating_physics); + + ClassDB::bind_method(D_METHOD("set_bone2d_nodepath", "nodepath"), &PhysicalBone2D::set_bone2d_nodepath); + ClassDB::bind_method(D_METHOD("get_bone2d_nodepath"), &PhysicalBone2D::get_bone2d_nodepath); + ClassDB::bind_method(D_METHOD("set_bone2d_index", "bone_index"), &PhysicalBone2D::set_bone2d_index); + ClassDB::bind_method(D_METHOD("get_bone2d_index"), &PhysicalBone2D::get_bone2d_index); + + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "bone2d_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D"), "set_bone2d_nodepath", "get_bone2d_nodepath"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "bone2d_index", PROPERTY_HINT_RANGE, "-1, 1000, 1"), "set_bone2d_index", "get_bone2d_index"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_configure_joint"), "set_auto_configure_joint", "get_auto_configure_joint"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simulate_physics"), "set_simulate_physics", "get_simulate_physics"); +} + +PhysicalBone2D::PhysicalBone2D() { + // Stop the RigidBody from executing its force integration. + PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), nullptr, ""); +} + +PhysicalBone2D::~PhysicalBone2D() { +} diff --git a/scene/2d/physical_bone_2d.h b/scene/2d/physical_bone_2d.h new file mode 100644 index 000000000000..e00c5fa081db --- /dev/null +++ b/scene/2d/physical_bone_2d.h @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* physical_bone_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef PHYSICAL_BONE_2D_H +#define PHYSICAL_BONE_2D_H + +#include "scene/2d/joints_2d.h" +#include "scene/2d/physics_body_2d.h" + +#include "scene/2d/skeleton_2d.h" + +class PhysicalBone2D : public RigidBody2D { + GDCLASS(PhysicalBone2D, RigidBody2D); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +private: + Skeleton2D *parent_skeleton; + int bone2d_index = -1; + NodePath bone2d_nodepath; + + // TODO: make child joint only required if there is a PhysicalBone2D child. Root PhysicalBone2D nodes shouldn't need joints. + Joint2D *child_joint; + bool auto_configure_joint = true; + + bool simulate_physics = false; + bool _internal_simulate_physics = false; + + void _find_skeleton_parent(); + void _find_joint_child(); + void _auto_configure_joint(); + + void _start_physics_simulation(); + void _stop_physics_simulation(); + void _position_at_bone2d(); + +public: + Joint2D *get_joint() const; + bool get_auto_configure_joint() const; + void set_auto_configure_joint(bool p_auto_configure); + + void set_simulate_physics(bool p_simulate); + bool get_simulate_physics() const; + bool is_simulating_physics() const; + + void set_bone2d_nodepath(const NodePath &p_nodepath); + NodePath get_bone2d_nodepath() const; + void set_bone2d_index(int p_bone_idx); + int get_bone2d_index() const; + + String get_configuration_warning() const override; + + PhysicalBone2D(); + ~PhysicalBone2D(); +}; + +#endif // PHYSICAL_BONE_2D_H diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index e762b2bca4da..022b381210a2 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -55,6 +55,7 @@ #include "scene/2d/parallax_background.h" #include "scene/2d/parallax_layer.h" #include "scene/2d/path_2d.h" +#include "scene/2d/physical_bone_2d.h" #include "scene/2d/physics_body_2d.h" #include "scene/2d/polygon_2d.h" #include "scene/2d/position_2d.h" @@ -666,6 +667,9 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); + OS::get_singleton()->yield(); //may take time to init /* REGISTER RESOURCES */ diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 5d61626e05e4..84dcab8ba260 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -31,6 +31,10 @@ #include "skeleton_modification_2d.h" #include "scene/2d/skeleton_2d.h" +#include "scene/2d/collision_object_2d.h" +#include "scene/2d/collision_shape_2d.h" +#include "scene/2d/physical_bone_2d.h" + /////////////////////////////////////// // ModificationStack2D /////////////////////////////////////// @@ -2555,3 +2559,257 @@ SkeletonModification2DTwoBoneIK::SkeletonModification2DTwoBoneIK() { SkeletonModification2DTwoBoneIK::~SkeletonModification2DTwoBoneIK() { } + +/////////////////////////////////////// +// PhysicalBones +/////////////////////////////////////// + +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(); + _change_notify(); + 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") { + physical_bone_set_node(which, p_value); + } + return true; + } + return true; +} + +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")) { + return true; // Do nothing! + } + } +#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 = physical_bone_get_node(which); + } + return true; + } + return true; +} + +void SkeletonModification2DPhysicalBones::_get_property_list(List *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 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("PhysicalBone2D cache " + itos(i) + " is out of date. Updating..."); + _physical_bone_update_cache(i); + continue; + } + + PhysicalBone2D *physical_bone = Object::cast_to(ObjectDB::get_instance(bone_data.physical_bone_node_cache)); + ERR_CONTINUE_MSG(!physical_bone, "PhysicalBone2D not found at index " + itos(i) + "!"); + ERR_FAIL_INDEX_MSG(physical_bone->get_bone2d_index(), stack->skeleton->get_bone_count(), "PhysicalBone2D at index " + itos(i) + " has invalid Bone2D!"); + Bone2D *bone_2d = stack->skeleton->get_bone(physical_bone->get_bone2d_index()); + + if (physical_bone->get_simulate_physics()) { + 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) { + WARN_PRINT("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 Jiggle Bone2D cache: node is this modification's skeleton or cannot be found!"); + 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); + _change_notify(); +} + +void SkeletonModification2DPhysicalBones::fetch_physical_bones() { + ERR_FAIL_COND_MSG(!stack->skeleton, "No skeleton found! Cannot fetch physical bones!"); + + physical_bone_chain.clear(); + + List node_queue = List(); + node_queue.push_back(stack->skeleton); + + while (node_queue.size() > 0) { + Node *node_to_process = node_queue[0]; + node_queue.pop_front(); + + PhysicalBone2D *potential_bone = Object::cast_to(node_to_process); + if (potential_bone) { + PhysicalBone_Data2D new_data = PhysicalBone_Data2D(); + new_data.physical_bone_node = stack->skeleton->get_path_to(potential_bone); + new_data.physical_bone_node_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 &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 &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.size() <= 0) { + for (int i = 0; i < physical_bone_chain.size(); i++) { + PhysicalBone2D *physical_bone = Object::cast_to(stack->skeleton->get_node(physical_bone_chain[i].physical_bone_node)); + 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 = Object::cast_to(ObjectDB::get_instance(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::physical_bone_set_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::physical_bone_get_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("physical_bone_set_node", "joint_idx", "physicalbone2d_node"), &SkeletonModification2DPhysicalBones::physical_bone_set_node); + ClassDB::bind_method(D_METHOD("physical_bone_get_node", "joint_idx"), &SkeletonModification2DPhysicalBones::physical_bone_get_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(); + enabled = true; +} + +SkeletonModification2DPhysicalBones::~SkeletonModification2DPhysicalBones() { +} diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index 6541c60fd78f..86aa41118a33 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -532,4 +532,49 @@ class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { ~SkeletonModification2DTwoBoneIK(); }; +/////////////////////////////////////// +// SkeletonModification2DPhysicalBones +/////////////////////////////////////// + +class SkeletonModification2DPhysicalBones : public SkeletonModification2D { + GDCLASS(SkeletonModification2DPhysicalBones, SkeletonModification2D); + +private: + struct PhysicalBone_Data2D { + NodePath physical_bone_node; + ObjectID physical_bone_node_cache; + }; + Vector physical_bone_chain; + + void _physical_bone_update_cache(int p_joint_idx); + + bool _simulation_state_dirty = false; + TypedArray _simulation_state_dirty_names; + bool _simulation_state_dirty_process; + 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 *p_list) const; + +public: + void execute(float 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 physical_bone_set_node(int p_joint_idx, const NodePath &p_path); + NodePath physical_bone_get_node(int p_joint_idx) const; + + void fetch_physical_bones(); + void start_simulation(const TypedArray &p_bones); + void stop_simulation(const TypedArray &p_bones); + + SkeletonModification2DPhysicalBones(); + ~SkeletonModification2DPhysicalBones(); +}; + #endif // SKELETONMODIFICATION2D_H From 1c44aa0bace9b30648c69f1e17312c90ee2b979f Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Mon, 3 Aug 2020 15:17:51 -0400 Subject: [PATCH 08/34] Changes: * Added documentation for the changes to Bone2D * Added documentation for the changes to Skeleton2D * Added documentation for the new SkeletonModifications and related classes * Added documentation for the PhysicalBone2D node NOTE: The following commit is squashed changes from the following verbose branch: https://github.com/TwistedTwigleg/godot/tree/GSOC_2020_Working_Branch_2D_IK_VERBOSE_ARCHIVE --- doc/classes/Bone2D.xml | 69 ++++- doc/classes/PhysicalBone2D.xml | 46 ++++ doc/classes/Skeleton2D.xml | 52 ++++ doc/classes/SkeletonModification2D.xml | 36 +++ doc/classes/SkeletonModification2DCCDIK.xml | 170 ++++++++++++ doc/classes/SkeletonModification2DFABRIK.xml | 189 +++++++++++++ doc/classes/SkeletonModification2DJiggle.xml | 232 ++++++++++++++++ doc/classes/SkeletonModification2DLookAt.xml | 42 +++ .../SkeletonModification2DPhysicalBones.xml | 68 +++++ .../SkeletonModification2DTwoBoneIK.xml | 257 ++++++++++++++++++ doc/classes/SkeletonModificationStack2D.xml | 102 +++++++ 11 files changed, 1260 insertions(+), 3 deletions(-) create mode 100644 doc/classes/PhysicalBone2D.xml create mode 100644 doc/classes/SkeletonModification2D.xml create mode 100644 doc/classes/SkeletonModification2DCCDIK.xml create mode 100644 doc/classes/SkeletonModification2DFABRIK.xml create mode 100644 doc/classes/SkeletonModification2DJiggle.xml create mode 100644 doc/classes/SkeletonModification2DLookAt.xml create mode 100644 doc/classes/SkeletonModification2DPhysicalBones.xml create mode 100644 doc/classes/SkeletonModification2DTwoBoneIK.xml create mode 100644 doc/classes/SkeletonModificationStack2D.xml diff --git a/doc/classes/Bone2D.xml b/doc/classes/Bone2D.xml index 910d488dfd9e..bc393f5ad0e9 100644 --- a/doc/classes/Bone2D.xml +++ b/doc/classes/Bone2D.xml @@ -19,6 +19,28 @@ Stores the node's current transforms in [member rest]. + + + + + Returns whether this [code]Bone2D[/code] node is going to autocalculate it's length and bone angle using its first [code]Bone2D[/code] child node, if one exists. If there are no [code]Bone2D[/code] children, then it cannot autocalculate these values and will print a warning. + + + + + + + Returns the angle of the bone in the [code]Bone2D[/code] node. + [b]Note[/b]: This is different from the [code]Bone2D[/code]'s rotation. The bone angle is the rotation of the bone shown by the [code]Bone2D[/code] gizmo, and because [code]Bone2D[/code] bones are based on positions, this can vary from the actual rotation of the [code]Bone2D[/code] node. + + + + + + + [i]Deprecated soon.[/i] Please use [code]get_length[/code] instead. + + @@ -26,6 +48,13 @@ Returns the node's index as part of the entire skeleton. See [Skeleton2D]. + + + + + Returns the length of the bone in the [code]Bone2D[/code] node. + + @@ -33,11 +62,45 @@ Returns the node's [member rest] [code]Transform2D[/code] if it doesn't have a parent, or its rest pose relative to its parent. + + + + + + + When set to [code]true[/code], the [code]Bone2D[/code] node will attempt to automatically calculate the bone angle and length using the first child [code]Bone2D[/code] node, if one exists. If none exist, the [code]Bone2D[/code] cannot automatically calculate these values and will print a warning. + + + + + + + + + Sets the bone angle for the [code]Bone2D[/code] node. This is typically set to the rotation from the [code]Bone2D[/code] node to a child [code]Bone2D[/code] node. + [b]Note[/b]: This is different from the [code]Bone2D[/code]'s rotation. The bone angle is the rotation of the bone shown by the [code]Bone2D[/code] gizmo, and because [code]Bone2D[/code] bones are based on positions, this can vary from the actual rotation of the [code]Bone2D[/code] node. + + + + + + + + + [i]Deprecated soon.[/i] Please use [code]set_length[/code] instead. + + + + + + + + + Sets the length of the bone in the [code]Bone2D[/code] node. + + - - Length of the bone's representation drawn in the editor's viewport in pixels. - Rest transform of the bone. You can reset the node's transforms to this value using [method apply_rest]. diff --git a/doc/classes/PhysicalBone2D.xml b/doc/classes/PhysicalBone2D.xml new file mode 100644 index 000000000000..3e1c9a7bd28c --- /dev/null +++ b/doc/classes/PhysicalBone2D.xml @@ -0,0 +1,46 @@ + + + + A 2D node that can be used for physically aware bones in 2D. + + + The [code]PhysicalBone2D[/code] node is a [RigidBody2D]-based node that can be used to make [Bone2D] nodes in a [Skeleton2D] react to physics. This node is very similar to the [PhysicalBone3D] node, just for 2D instead of 3D. + [b]Note[/b]: To have the Bone2D nodes visually follow the [code]PhysicalBone2D[/code] node, use a [SkeletonModification2DPhysicalBones] modification on the [Skeleton2D] node with the [Bone2D] nodes. + [b]Note[/b]: The PhysicalBone2D node does not automatically create a [Joint2D] node to keep [code]PhysicalBone2D[/code] nodes together. You will need to create these manually. For most cases, you want to use a [PinJoint2D] node. The [code]PhysicalBone2D[/code] node can automatically configure the [Joint2D] node once it's been created as a child node. + + + + + + + + + Returns the first [Joint2D] child node, if one exists. This is mainly a helper function to make it easier to get the [Joint2D] that the [code]PhysicalBone2D[/code] is autoconfiguring. + + + + + + + Returns a boolean that indicates whether the [code]PhysicalBone2D[/code] node is running and simulating using the Godot 2D physics engine. When [code]true[/code], the PhysicalBone2D node is using physics. + + + + + + When true, the [code]PhysicalBone2D[/code] node will automatically configure the first [Joint2D] child node. The automatic configuration is limited to setting up the node properties and positioning the [Joint2D]. + + + The index of the [Bone2D] node that this [code]PhysicalBone2D[/code] node is supposed to be simulating. + + + The [NodePath] to the [Bone2D] node that this [code]PhysicalBone2D[/code] node is supposed to be simulating. + + + When true, the [code]PhysicalBone2D[/code] will start simulating using physics. When false, the [code]PhysicalBone2D[/code] will follow the transform of the [Bone2D] node. + [b]Note[/b]: To have the Bone2D nodes visually follow the [code]PhysicalBone2D[/code] node, use a [SkeletonModification2DPhysicalBones] modification on the [Skeleton2D] node with the [Bone2D] nodes. + + + + + diff --git a/doc/classes/Skeleton2D.xml b/doc/classes/Skeleton2D.xml index 0ddbac9ba48e..445cc2e0e607 100644 --- a/doc/classes/Skeleton2D.xml +++ b/doc/classes/Skeleton2D.xml @@ -10,6 +10,15 @@ https://docs.godotengine.org/en/latest/tutorials/animation/2d_skeletons.html + + + + + + + Executes all the modifications on the [SkeletonModificationStack2D], if the skeleton has one. + + @@ -26,6 +35,22 @@ Returns the number of [Bone2D] nodes in the node hierarchy parented by Skeleton2D. + + + + + + + Returns the local pose override transform for [code]bone_idx[/code]. + + + + + + + Returns the [SkeletonModificationStack2D] attached to this skeleton, if one exists. + + @@ -33,10 +58,37 @@ Returns the [RID] of a Skeleton2D instance. + + + + + + + + + + + + + Sets the local pose transform, [code]pose[/code], for the bone at [code]bone_idx[/code]. + [code]amount[/code] is the interpolation strengh that will be used when applying the pose, and [code]persistent[/code] determines if the applied pose will remain. + [b]Note[/b]: The pose transform needs to be a local transform relative to the [Bone2D] node at [code]bone_idx[/code]! + + + + + + + + + Sets the [SkeletonModificationStack2D] attached to this skeleton. + + + Emitted when the [Bone2D] setup attached to this skeletons changes. This is primarily used internally within the skeleton. diff --git a/doc/classes/SkeletonModification2D.xml b/doc/classes/SkeletonModification2D.xml new file mode 100644 index 000000000000..877e51315b2c --- /dev/null +++ b/doc/classes/SkeletonModification2D.xml @@ -0,0 +1,36 @@ + + + + A resource that operates on [Bone2D] nodes in a [Skeleton2D]. + + + This resource provides an interface that can be expanded so code that operates on [Bone2D] nodes in a [Skeleton2D] can be mixed and matched together to create complex interactions. + This is used to provide Godot with a flexible and powerful Inverse Kinematics solution that can be adapted for many different uses. + + + + + + + + + Executes the given modification. This is where the modification performs whatever function it is designed to do. + + + + + + + Sets up the modification so it can be executed. This function should be called automatically by the [SkeletonModificationStack2D] containing this modification. + If you need to initialize a modification before use, this is the place to do it! + + + + + + When true, the modification's [method execute] function will be called by the [SkeletonModificationStack2D]. + + + + + diff --git a/doc/classes/SkeletonModification2DCCDIK.xml b/doc/classes/SkeletonModification2DCCDIK.xml new file mode 100644 index 000000000000..39637d78d250 --- /dev/null +++ b/doc/classes/SkeletonModification2DCCDIK.xml @@ -0,0 +1,170 @@ + + + + A modification that uses CCDIK to manipulate a series of bones to reach a target in 2D. + + + This [SkeletonModification2D] uses an algorithm called [b]C[/b]yclic [b]C[/b]oordinate [b]D[/b]escent [b]I[/b]nverse [b]K[/b]inematics, or CCDIK, to maniuplate a chain of bones in a [Skeleton2D] so it reaches a defined target. + CCDIK works by rotating a set of bones, typically called a "bone chain", on a single axis. Each bone is rotated to face the target from the tip (by default), which over a chain of bones allow it to rotate properly to reach the target. Because the bones only rotate on a single axis, CCDIK [i]can[/i] look more robotic than other IK solvers. + [b]Note:[/b] The CCDIK modifier has [code]ccdik_joints[/code], which are the data objects that hold the data for each joint in the CCDIK chain. This is different from a bone! CCDIK joints hold the data needed for each bone in the bone chain used by CCDIK. + CCDIK also fully supports angle constraints, allowing for more control over how a solution is met. + + + + + + + + + + + Returns the [Bone2D] node assigned to the CCDIK joint at [code]joint_idx[/code]. + + + + + + + + + Returns the index of the [Bone2D] node assigned to the CCDIK joint at [code]joint_idx[/code]. + + + + + + + + + Returns whether the CCDIK joint at [code]joint_idx[/code] uses an inverted joint constraint. See [method ccdik_joint_set_joint_constraint_invert] for details. + + + + + + + + + Returns the maximum angle constraint for the joint at [code]joint_idx[/code]. + + + + + + + + + Returns the minimum angle constraint for the joint at [code]joint_idx[/code]. + + + + + + + + + Returns whether angle constraints on the CCDIK joint at [code]joint_idx[/code] are enabled. + + + + + + + + + Returns whether the joint at [code]joint_idx[/code] is set to rotate from the joint, [code]true[/code], or to rotate from the tip, [code]false[/code]. The default is to rotate from the tip. + + + + + + + + + + + Sets the [Bone2D] node assigned to the CCDIK joint at [code]joint_idx[/code]. + + + + + + + + + + + Sets the bone index, [code]bone_index[/code], of the CCDIK joint at [code]joint_idx[/code]. When possible, this will also update the [code]bone2d_node[/code] of the CCDIK joint based on data provided by the linked skeleton. + + + + + + + + + + + Sets whether the CCDIK joint at [code]joint_idx[/code] uses an inverted joint constraint. + An inverted joint constraint only constraints the CCDIK joint to the angles [i]outside of[/i] the inputted minimum and maximum angles. For this reason, it is referred to as an inverted joint constraint, as it constraints the joint to the outside of the inputted values. + + + + + + + + + + + Sets the maximum angle constraint for the joint at [code]joint_idx[/code]. + + + + + + + + + + + Sets the minimum angle constraint for the joint at [code]joint_idx[/code]. + + + + + + + + + + + Determines whether angle constraints on the CCDIK joint at [code]joint_idx[/code] are enabled. When [code]true[/code], constraints will be enabled and taken into account when solving. + + + + + + + + + + + Sets whether the joint at [code]joint_idx[/code] is set to rotate from the joint, [code]true[/code], or to rotate from the tip, [code]false[/code]. + + + + + + The amount of CCDIK joints in the CCDIK modification. + + + The NodePath to the node that is the target for the CCDIK modification. This node is what the CCDIK chain will attempt to rotate the bone chain to. + + + The end position of the CCDIK chain. Typically, this should be a child of a [Bone2D] node attached to the final [Bone2D] in the CCDIK chain. + + + + + diff --git a/doc/classes/SkeletonModification2DFABRIK.xml b/doc/classes/SkeletonModification2DFABRIK.xml new file mode 100644 index 000000000000..38703aae5b7f --- /dev/null +++ b/doc/classes/SkeletonModification2DFABRIK.xml @@ -0,0 +1,189 @@ + + + + A modification that uses FABRIK to manipulate a series of [Bone2D] nodes to reach a target. + + + This [SkeletonModification2D] uses an algorithm called [b]F[/b]orward [b]A[/b]nd [b]B[/b]ackward [b]R[/b]eaching [b]I[/b]nverse [b]K[/b]inematics, or FABRIK, to rotate a bone chain so that it reaches a target. + FABRIK works by knowing the positions and lengths of a series of bones, typically called a "bone chain". It first starts by running a forward pass, which places the final bone at the target's position. Then all other bones are moved towards the tip bone, so they stay at the defined bone length away. Then a backwards pass is performed, where the root/first bone in the FABRIK chain is placed back at the origin. then all other bones are moved so they stay at the defined bone length away. This positions the bone chain so that it reaches the target when possible, but all of the bones stay the correct length away from each other. + Because of how FABRIK works, it often gives more natural results than those seen in [SkeletonModification2DCCDIK]. FABRIK also supports angle constraints, which are fully taken into account when solving. + [b]Note:[/b] The FABRIK modifier has [code]fabrik_joints[/code], which are the data objects that hold the data for each joint in the FABRIK chain. This is different from [Bone2D] nodes! FABRIK joints hold the data needed for each [Bone2D] in the bone chain used by FABRIK. + To help control how the FABRIK joints move, a magnet vector can be passed, which can nudge the bones in a certain direction prior to solving, giving a level of control over the final result. + + + + + + + + + + + Returns the [Bone2D] node assigned to the FABRIK joint at [code]joint_idx[/code]. + + + + + + + + + Returns the index of the [Bone2D] node assigned to the FABRIK joint at [code]joint_idx[/code]. + + + + + + + + + Returns whether the FABRIK joint at [code]joint_idx[/code] uses an inverted joint constraint. See [method fabrik_joint_set_joint_constraint_invert] for details. + + + + + + + + + Returns the maximum angle constraint for the joint at [code]joint_idx[/code]. + + + + + + + + + Returns the minimum angle constraint for the joint at [code]joint_idx[/code]. + + + + + + + + + Returns whether angle constraints on the FABRIK joint at [code]joint_idx[/code] are enabled. + + + + + + + + + Returns the magnet position vector for the joint at [code]joint_idx[/code]. + + + + + + + + + Returns whether the joint is using the target's rotation rather than allowing FABRIK to rotate the joint. This option only applies to the tip/final joint in the chain. + + + + + + + + + + + Sets the [Bone2D] node assigned to the FABRIK joint at [code]joint_idx[/code]. + + + + + + + + + + + Sets the bone index, [code]bone_index[/code], of the FABRIK joint at [code]joint_idx[/code]. When possible, this will also update the [code]bone2d_node[/code] of the FABRIK joint based on data provided by the linked skeleton. + + + + + + + + + + + Sets whether the FABRIK joint at [code]joint_idx[/code] uses an inverted joint constraint. + An inverted joint constraint only constraints the FABRIK joint to the angles [i]outside of[/i] the inputted minimum and maximum angles. For this reason, it is referred to as an inverted joint constraint, as it constraints the joint to the outside of the inputted values. + + + + + + + + + + + Sets the maximum angle constraint for the joint at [code]joint_idx[/code]. + + + + + + + + + + + Sets the minimum angle constraint for the joint at [code]joint_idx[/code]. + + + + + + + + + + + Determines whether angle constraints on the FABRIK joint at [code]joint_idx[/code] are enabled. When [code]true[/code], constraints will be enabled and taken into account when solving. + + + + + + + + + + + Sets the magnet position vector for the joint at [code]joint_idx[/code]. + + + + + + + + + + + Sets whether the joint at [code]joint_idx[/code] will use the target node's rotation rather than letting FABRIK rotate the node. + [b]Note[/b]: This option only works for the tip/final joint in the chain. For all other nodes, this option will be ignored. + + + + + + The amount of FABRIK joints in the FABRIK modification. + + + The NodePath to the node that is the target for the FABRIK modification. This node is what the FABRIK chain will attempt to rotate the bone chain to. + + + + + diff --git a/doc/classes/SkeletonModification2DJiggle.xml b/doc/classes/SkeletonModification2DJiggle.xml new file mode 100644 index 000000000000..eef41cdca186 --- /dev/null +++ b/doc/classes/SkeletonModification2DJiggle.xml @@ -0,0 +1,232 @@ + + + + A modification that jiggles [Bone2D] nodes as they move towards a target. + + + This modification moves a series of bones, typically called a bone chain, towards a target. What makes this modification special is that it calculates the velocity and acceleration for each bone in the bone chain, and runs a very light physics-like calculation using the inputted values. This allows the bones to overshoot the target and "jiggle" around. It can be configured to act more like a spring, or sway around like cloth might. + This modification is useful for adding additional motion to things like hair, the edges of clothing, and more. It has several settings to that allow control over how the joint moves when the target moves. + [b]Note:[/b] The Jiggle modifier has [code]jiggle_joints[/code], which are the data objects that hold the data for each joint in the Jiggle chain. This is different from than [Bone2D] nodes! Jiggle joints hold the data needed for each [Bone2D] in the bone chain used by the Jiggle modification. + + + + + + + + + Returns the collision mask used by the Jiggle modifier when collisions are enabled. + + + + + + + Returns whether the jiggle modifier is taking physics colliders into account when solving. + + + + + + + + + Returns the [Bone2D] node assigned to the Jiggle joint at [code]joint_idx[/code]. + + + + + + + + + Returns the index of the [Bone2D] node assigned to the Jiggle joint at [code]joint_idx[/code]. + + + + + + + + + Returns the amount of damping of the Jiggle joint at [code]joint_idx[/code]. + + + + + + + + + Returns a [Vector2] representing the amount of gravity the Jiggle joint at [code]joint_idx[/code] is influenced by. + + + + + + + + + Returns the amount of mass of the jiggle joint at [code]joint_idx[/code]. + + + + + + + + + Returns a boolean that indiciates whether the joint at [code]joint_idx[/code] is overriding the default Jiggle joint data defined in the modification. + + + + + + + + + Returns the stiffness of the Jiggle joint at [code]joint_idx[/code]. + + + + + + + + + Returns a boolean that indiciates whether the joint at [code]joint_idx[/code] is using gravity or not. + + + + + + + + + + + Sets the [Bone2D] node assigned to the Jiggle joint at [code]joint_idx[/code]. + + + + + + + + + + + Sets the bone index, [code]bone_index[/code], of the Jiggle joint at [code]joint_idx[/code]. When possible, this will also update the [code]bone2d_node[/code] of the Jiggle joint based on data provided by the linked skeleton. + + + + + + + + + + + Sets the amount of dampening of the Jiggle joint at [code]joint_idx[/code]. + + + + + + + + + + + Sets the gravity vector of the Jiggle joint at [code]joint_idx[/code]. + + + + + + + + + + + Sets the of mass of the Jiggle joint at [code]joint_idx[/code]. + + + + + + + + + + + Sets whether the Jiggle joint at [code]joint_idx[/code] should override the default Jiggle joint settings. Setting this to [code]true[/code] will make the joint use its own settings rather than the default ones attached to the modification. + + + + + + + + + + + Sets the of stiffness of the Jiggle joint at [code]joint_idx[/code]. + + + + + + + + + + + Sets whether the Jiggle joint at [code]joint_idx[/code] should use gravity. + + + + + + + + + Sets the collision mask that the Jiggle modifier will use when reacting to colliders, if the Jiggle modifier is set to take colliders into account. + + + + + + + + + When [code]true[/code], the Jiggle modifier will take colliders into account, keeping them from entering into these collision objects. + + + + + + The default amount of dampening applied to the Jiggle joints, if they are not overriden. Higher values lead to more of the calculated velocity being applied. + + + The default amount of gravity applied to the Jiggle joints, if they are not overriden. + + + The amount of Jiggle joints in the Jiggle modification. + + + The default amount of mass assigned to the Jiggle joints, if they are not overriden. Higher values lead to faster movements and more overshooting. + + + The default amount of stiffness assigned to the Jiggle joints, if they are not overriden. Higher values act more like springs, quickly moving into the correct position. + + + The NodePath to the node that is the target for the Jiggle modification. This node is what the Jiggle chain will attempt to rotate the bone chain to. + + + Whether the gravity vector, [member gravity], should be applied to the Jiggle joints, assuming they are not overriding the default settings. + + + + + diff --git a/doc/classes/SkeletonModification2DLookAt.xml b/doc/classes/SkeletonModification2DLookAt.xml new file mode 100644 index 000000000000..3fd8540c8d5f --- /dev/null +++ b/doc/classes/SkeletonModification2DLookAt.xml @@ -0,0 +1,42 @@ + + + + A modification that rotates a [Bone2D] node to look at a target. + + + This [SkeletonModification2D] rotates a bone to look a target. This is extremely helpful for moving character's head to look at the player, rotating a turret to look at a target, or any other case where you want to make a bone rotate towards something quickly and easily. + + + + + + + + The amount of additional rotation to be applied after performing the modification. This allows you to adjust the results. + + + The [Bone2D] node that the modification will operate on. + + + The index of the [Bone2D] node that the modification will oeprate on. + + + Sets whether the modification uses an inverted joint constraint. + An inverted joint constraint only constraints the [Bone2D] to the angles [i]outside of[/i] the inputted minimum and maximum angles. For this reason, it is referred to as an inverted joint constraint, as it constraints the joint to the outside of the inputted values. + + + The LookAt modifcation constraint's maximum allowed angle. + + + The LookAt modifcation constraint's minimum allowed angle. + + + If [code]true[/code], then constraints will be applied when solving. + + + The NodePath to the node that is the target for the LookAt modification. This node is what the modification will rotate the [Bone2D] to. + + + + + diff --git a/doc/classes/SkeletonModification2DPhysicalBones.xml b/doc/classes/SkeletonModification2DPhysicalBones.xml new file mode 100644 index 000000000000..76307bfcf353 --- /dev/null +++ b/doc/classes/SkeletonModification2DPhysicalBones.xml @@ -0,0 +1,68 @@ + + + + A modification that applies the transforms of [PhysicalBone2D] nodes to [Bone2D] nodes. + + + This modification takes the transforms of [PhysicalBone2D] nodes and applies them to [Bone2D] nodes. This allows the [Bone2D] nodes to react to physics thanks to the linked [PhysicalBone2D] nodes. + + + + + + + + + Empties the list of [PhysicalBone2D] nodes and populates it will all [PhysicalBone2D] nodes that are children of the [Skeleton2D]. + + + + + + + + + Returns the [PhysicalBone2D] node at [joint_idx]. + + + + + + + + + + + Sets the [PhysicalBone2D] node at [joint_idx]. + [b]Note[/b]: This is just the index used for this modification, not the bone index used in the [Skeleton2D]. + + + + + + + + + Tell the [PhysicalBone2D] nodes to start simulating and interacting with the physics world. + Optionally, an array of bone names can be passed to this function, and that will cause only [PhysicalBone2D] nodes with those names to start simulating. + + + + + + + + + Tell the [PhysicalBone2D] nodes to stop simulating and interacting with the physics world. + Optionally, an array of bone names can be passed to this function, and that will cause only [PhysicalBone2D] nodes with those names to stop simulating. + + + + + + The amount of [PhysicalBone2D] nodes linked in this modification. + + + + + diff --git a/doc/classes/SkeletonModification2DTwoBoneIK.xml b/doc/classes/SkeletonModification2DTwoBoneIK.xml new file mode 100644 index 000000000000..09eb36c4dbc7 --- /dev/null +++ b/doc/classes/SkeletonModification2DTwoBoneIK.xml @@ -0,0 +1,257 @@ + + + + A modification that rotates two bones using the law of cosigns to reach the target. + + + This [SkeletonModification2D] uses an algorithm typically called TwoBoneIK. This algorithm works by leveraging the law of cosigns and the lengths of the bones to figure out what rotation the bones currently have, and what rotation they need to make a complete triangle, where the first bone, the second bone, and the target form the three verticies of the triangle. Because the algorithm works by making a triangle, it can only opperate on two bones. + TwoBoneIK is great for arms, legs, and really any joints that can be represented by just two bones that bend to reach a target. This solver is more lightweight than [SkeletonModification2DFABRIK], but gives similar, natural looking results. + + + + + + + + + Returns the [Bone2D] node that is being used as the first bone in the TwoBoneIK modification. + + + + + + + Returns the index of the [Bone2D] node that is being used as the first bone in the TwoBoneIK modification. + + + + + + + Returns whether the constraints applied to the first bone in the TwoBoneIK modification is inverted. + + + + + + + Returns the maximum angle the first bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. + + + + + + + Returns the minimum angle the first bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. + + + + + + + Returns whether the constraints applied to the first bone in the TwoBoneIK modification are calculated in localspace. + + + + + + + Returns whether the constraints applied to the first bone in the TwoBoneIK modification are enabled. + [b]Note[/b]: Constraints are purely visual and not taken into account by the modification. This means that the modification may not find a solution when constraints are enabled, even if a solution may be possible. + + + + + + + Returns the [Bone2D] node that is being used as the second bone in the TwoBoneIK modification. + + + + + + + Returns the index of the [Bone2D] node that is being used as the second bone in the TwoBoneIK modification. + + + + + + + Returns whether the constraints applied to the second bone in the TwoBoneIK modification is inverted. + + + + + + + Returns the maximum angle the second bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. + + + + + + + Returns the minimum angle the second bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. + + + + + + + Returns whether the constraints applied to the second bone in the TwoBoneIK modification are calculated in localspace. + + + + + + + Returns whether the constraints applied to the second bone in the TwoBoneIK modification are enabled. + [b]Note[/b]: Constraints are purely visual and not taken into account by the modification. This means that the modification may not find a solution when constraints are enabled, even if a solution may be possible. + + + + + + + + + Sets the [Bone2D] node that is being used as the first bone in the TwoBoneIK modification. + + + + + + + + + Sets the index of the [Bone2D] node that is being used as the first bone in the TwoBoneIK modification. + + + + + + + + + Sets whether the constraints applied to the first bone in the TwoBoneIK modification is inverted. + An inverted joint constraint only constraints the joint to the angles [i]outside of[/i] the inputted minimum and maximum angles. For this reason, it is referred to as an inverted joint constraint, as it constraints the joint to the outside of the inputted values. + + + + + + + + + Sets the maximum angle the first bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. + + + + + + + + + Sets the minimum angle the first bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. + + + + + + + + + Sets whether the constraints applied to the first bone in the TwoBoneIK modification are calculated in localspace. + + + + + + + + + Sets whether the constraints applied to the first bone in the TwoBoneIK modification are enabled. + [b]Note[/b]: Constraints are purely visual and not taken into account by the modification. This means that the modification may not find a solution when constraints are enabled, even if a solution may be possible. + + + + + + + + + Sets the [Bone2D] node that is being used as the second bone in the TwoBoneIK modification. + + + + + + + + + Sets the index of the [Bone2D] node that is being used as the second bone in the TwoBoneIK modification. + + + + + + + + + Sets whether the constraints applied to the second bone in the TwoBoneIK modification is inverted. + An inverted joint constraint only constraints the joint to the angles [i]outside of[/i] the inputted minimum and maximum angles. For this reason, it is referred to as an inverted joint constraint, as it constraints the joint to the outside of the inputted values. + + + + + + + + + Sets the maximum angle the second bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. + + + + + + + + + Sets the minimum angle the second bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. + + + + + + + + + Sets whether the constraints applied to the second bone in the TwoBoneIK modification are calculated in localspace. + + + + + + + + + Sets whether the constraints applied to the second bone in the TwoBoneIK modification are enabled. + [b]Note[/b]: Constraints are purely visual and not taken into account by the modification. This means that the modification may not find a solution when constraints are enabled, even if a solution may be possible. + + + + + + When [code]true[/code], the bones in the modification will blend outward as opposed to inwards when contracting. When [code]false[/code], the bones will bend inwards when contracting. + + + The minimum distance the target can be at. If the target is closer than this distance, the modification will solve as if it's at this minimum distance. + + + The NodePath to the node that is the target for the TwoBoneIK modification. This node is what the modification will use when bending the [Bone2D] nodes. + + + + + diff --git a/doc/classes/SkeletonModificationStack2D.xml b/doc/classes/SkeletonModificationStack2D.xml new file mode 100644 index 000000000000..f42165ce4825 --- /dev/null +++ b/doc/classes/SkeletonModificationStack2D.xml @@ -0,0 +1,102 @@ + + + + A resource that holds a stack of [SkeletonModification2D]s. + + + This resource is used by the Skeleton and holds a stack of [SkeletonModification2D]s. + This controls the order of the modifications and how they are applied. Modification order is especially important for full-body IK setups, as you need to execute the modifications in the correct order to get the desired results. For example, you want to execute a modification on the spine [i]before[/i] the arms on a humanoid skeleton. + This resource also controls how strongly all of the modifications are applied to the [Skeleton2D]. + + + + + + + + + + + Adds the passed-in [SkeletonModification2D] to the stack. + + + + + + + + + Deletes the [SkeletonModification2D] at the index position [code]mod_idx[/code], if it exists. + + + + + + + + + Enables all [SkeletonModification2D]s in the stack. + + + + + + + + + Executes all of the [SkeletonModification2D]s in the stack, starting from index [code]0[/code] to [member modification_count]. + [b]Note:[/b] The order of the modifications can matter depending on the modifications. For example, modifications on a spine should operate before modifications on the arms in order to get proper results. + + + + + + + Returns a boolean that indiciates whether the modification stack is setup and can execute. + + + + + + + + + Returns the [SkeletonModification2D] at the passed-in index, [code]mod_idx[/code]. + + + + + + + + + + + Sets the modification at [code]mod_idx[/code] to the passed-in modification, [code]modification[/code]. + + + + + + + Sets up the modification stack so it can execute. This function should be called by [Skeleton2D] and shouldn't be manually called unless you know what you are doing. + + + + + + When [code]true[/code], the modification's in the stack will be called. This is handled automatically through the [Skeleton2D] node. + + + When the modification will be executed. Can be set to either [code]_process[/code] or [code]_physics_process[/code]. For physics-related or physics-aware modifications, using [code]_physics_process[/code] may be required. + + + The number of modifications in the stack. + + + The interpolation strength of the modifications in stack. A value of [code]0[/code] will make it where the modifications are not applied, a strength of [code]0.5[/code] will be half applied, and a strength of [code]1[/code] will allow the modifications to be fully applied and override the [Skeleton2D] [Bone2D] poses. + + + + + From 79f0b745bf66ebc781d8b5d2b675401e13efb668 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Mon, 3 Aug 2020 15:36:47 -0400 Subject: [PATCH 09/34] Changes: * SkeletonModification2DLookAt: Fixed issue where I accidentally added properties that were added through the property list, causing them to be added twice and messing things up. This is fixed now. * Updated the documentation for SkeletonModification2DLookAt and filled out the documentation for the property changing functions. * Fixed documentation function and property typos in the documentation for the 2D CCDIK, FABRIK, and PhysicalBones modifications --- doc/classes/SkeletonModification2DCCDIK.xml | 2 +- doc/classes/SkeletonModification2DFABRIK.xml | 2 +- doc/classes/SkeletonModification2DLookAt.xml | 98 ++++++++++++++++--- .../SkeletonModification2DPhysicalBones.xml | 4 +- scene/resources/skeleton_modification_2d.cpp | 7 -- 5 files changed, 86 insertions(+), 27 deletions(-) diff --git a/doc/classes/SkeletonModification2DCCDIK.xml b/doc/classes/SkeletonModification2DCCDIK.xml index 39637d78d250..617d50bb0f65 100644 --- a/doc/classes/SkeletonModification2DCCDIK.xml +++ b/doc/classes/SkeletonModification2DCCDIK.xml @@ -36,7 +36,7 @@ - Returns whether the CCDIK joint at [code]joint_idx[/code] uses an inverted joint constraint. See [method ccdik_joint_set_joint_constraint_invert] for details. + Returns whether the CCDIK joint at [code]joint_idx[/code] uses an inverted joint constraint. See [method ccdik_joint_set_constraint_angle_invert] for details. diff --git a/doc/classes/SkeletonModification2DFABRIK.xml b/doc/classes/SkeletonModification2DFABRIK.xml index 38703aae5b7f..48b0c8fd0ca5 100644 --- a/doc/classes/SkeletonModification2DFABRIK.xml +++ b/doc/classes/SkeletonModification2DFABRIK.xml @@ -37,7 +37,7 @@ - Returns whether the FABRIK joint at [code]joint_idx[/code] uses an inverted joint constraint. See [method fabrik_joint_set_joint_constraint_invert] for details. + Returns whether the FABRIK joint at [code]joint_idx[/code] uses an inverted joint constraint. See [method fabrik_joint_set_constraint_angle_invert] for details. diff --git a/doc/classes/SkeletonModification2DLookAt.xml b/doc/classes/SkeletonModification2DLookAt.xml index 3fd8540c8d5f..65f2d21021c9 100644 --- a/doc/classes/SkeletonModification2DLookAt.xml +++ b/doc/classes/SkeletonModification2DLookAt.xml @@ -9,30 +9,96 @@ + + + + + Returns the amount of additional rotation that is applied after the LookAt modification executes. + + + + + + + Returns whether the constraints to this modification are inverted or not. + + + + + + + + Returns the constraint's maximum allowed angle. + + + + + + + Returns the constraint's minimum allowed angle. + + + + + + + Returns [code]true[/code] if the LookAt modification is using constraints. + + + + + + + + + Sets the amount of additional rotation that is to be applied after executing the modification. This allows for offsetting the results by the inputted rotation amount. + + + + + + + + + When [code]true[/code], the modification will use an inverted joint constraint. + An inverted joint constraint only constraints the [Bone2D] to the angles [i]outside of[/i] the inputted minimum and maximum angles. For this reason, it is referred to as an inverted joint constraint, as it constraints the joint to the outside of the inputted values. + + + + + + + + + Sets the constraint's maximum allowed angle. + + + + + + + + + Sets the constraint's minimum allowed angle. + + + + + + + + + Sets whether this modification will use constraints or not. When [code]true[/code], constraints will be applied when solving the LookAt modification. + + - - The amount of additional rotation to be applied after performing the modification. This allows you to adjust the results. - The [Bone2D] node that the modification will operate on. The index of the [Bone2D] node that the modification will oeprate on. - - Sets whether the modification uses an inverted joint constraint. - An inverted joint constraint only constraints the [Bone2D] to the angles [i]outside of[/i] the inputted minimum and maximum angles. For this reason, it is referred to as an inverted joint constraint, as it constraints the joint to the outside of the inputted values. - - - The LookAt modifcation constraint's maximum allowed angle. - - - The LookAt modifcation constraint's minimum allowed angle. - - - If [code]true[/code], then constraints will be applied when solving. - The NodePath to the node that is the target for the LookAt modification. This node is what the modification will rotate the [Bone2D] to. diff --git a/doc/classes/SkeletonModification2DPhysicalBones.xml b/doc/classes/SkeletonModification2DPhysicalBones.xml index 76307bfcf353..d2f1cd696b9d 100644 --- a/doc/classes/SkeletonModification2DPhysicalBones.xml +++ b/doc/classes/SkeletonModification2DPhysicalBones.xml @@ -22,7 +22,7 @@ - Returns the [PhysicalBone2D] node at [joint_idx]. + Returns the [PhysicalBone2D] node at [code]joint_idx[/code]. @@ -33,7 +33,7 @@ - Sets the [PhysicalBone2D] node at [joint_idx]. + Sets the [PhysicalBone2D] node at [code]joint_idx[/code]. [b]Note[/b]: This is just the index used for this modification, not the bone index used in the [Skeleton2D]. diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 84dcab8ba260..dac5a14a6147 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -600,13 +600,6 @@ void SkeletonModification2DLookAt::_bind_methods() { 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"); - - // TODO: make the additional_rotation, angle_min, and angle_max properties to use degrees in the editor! - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "additional_rotation"), "set_additional_rotation", "get_additional_rotation"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_constraint"), "set_enable_constraint", "get_enable_constraint"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constraint_angle_min"), "set_constraint_angle_min", "get_constraint_angle_min"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constraint_angle_max"), "set_constraint_angle_max", "get_constraint_angle_max"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constraint_angle_invert"), "set_constraint_angle_invert", "get_constraint_angle_invert"); } SkeletonModification2DLookAt::SkeletonModification2DLookAt() { From e9d6a643de279d6aba90ce8656354903000daaa7 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Tue, 11 Aug 2020 16:51:04 -0400 Subject: [PATCH 10/34] Changes: * Skeleton2D: Fixed the bug that caused the SkeletonModificationStack2D to no longer work once the scene is saved in the editor. Fix is ported from the 3D IK PR. Now scenes with running IK can be saved without any issues. --- scene/2d/skeleton_2d.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 169e32eba8ce..d70e9645b668 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -710,6 +710,10 @@ void Skeleton2D::execute_modification(float delta) { bones[i].bone->set_transform(bones.write[i].local_pose_cache); } + if (modification_stack->skeleton != this) { + modification_stack->set_skeleton(this); + } + modification_stack->execute(delta); // A hack: Override the CanvasItem transform using the RenderingServer so the local pose override is taken into account. From a9748cf9515a1aeb27f8951fd7d097083717e581 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Fri, 21 Aug 2020 15:00:38 -0400 Subject: [PATCH 11/34] Changes: * Skeleton2D: SkeletonModificationStack2D now has PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE so modifications can be used properly on instanced/duplicated scenes. * Skeleton2D: Changed how the modifications are executed so per-modification execution modes are possible. Now local_pose_override is only applied on the _process call. * SkeletonModificationStack2D: Modifications in the stack now have PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE so modifications can be used properly on instanced/duplicated scenes. * SkeletonModificationStack2D: Made changes to allow for per-modification execution modes, rather than the modification mode being set by the stack. * SkeletonModificationStack2D: Added and registered new functions so GDScript-powered modifications can use them, allowing for the creation of modifications similar to those in C++. * SkeletonModification2D: Made changes so GDScript-powered modifications are actually possible. * SkeletonModification2D: Added and registered new functions so GDScript-powered modifications can use them. * SkeletonModification2D: Added a execution mode property. Execution modes are now a per-modification setting, allowing for mixing-and-matching modifications and modes. * SkeletonModification2DJiggle: Fixed code to work with new execution mode setting. * SkeletonModification2DStackHolder: Added a 2D stack holder modification, so users can use more than one modification stack on a Skeleton2D. * RegisterSceneTypes: Changed SkeletonModification2D from a vitrual class to a normal class so it can be extended from GDScript. * RegisterSceneTypes: Registered SkeletonModification2DStackHolder. --- scene/2d/skeleton_2d.cpp | 53 +++---- scene/2d/skeleton_2d.h | 2 +- scene/register_scene_types.cpp | 3 +- scene/resources/skeleton_modification_2d.cpp | 144 +++++++++++++++++-- scene/resources/skeleton_modification_2d.h | 43 +++++- 5 files changed, 197 insertions(+), 48 deletions(-) diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index d70e9645b668..8f1a1fcaec8a 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -540,7 +540,7 @@ void Skeleton2D::_get_property_list(List *p_list) const { PropertyInfo(Variant::OBJECT, "modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D", - PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); + PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); } void Skeleton2D::_make_bone_setup_dirty() { @@ -653,16 +653,12 @@ void Skeleton2D::_notification(int p_what) { } if (p_what == NOTIFICATION_PROCESS) { if (modification_stack.is_valid()) { - if (modification_stack->execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process) { - execute_modification(get_process_delta_time()); - } + execute_modification(get_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process); } } if (p_what == NOTIFICATION_PHYSICS_PROCESS) { if (modification_stack.is_valid()) { - if (modification_stack->execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process) { - execute_modification(get_physics_process_delta_time()); - } + execute_modification(get_physics_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process); } } } @@ -699,7 +695,7 @@ Ref Skeleton2D::get_modification_stack() const { return modification_stack; } -void Skeleton2D::execute_modification(float delta) { +void Skeleton2D::execute_modification(float delta, int p_execution_mode) { if (!modification_stack.is_valid()) { return; } @@ -714,27 +710,34 @@ void Skeleton2D::execute_modification(float delta) { modification_stack->set_skeleton(this); } - modification_stack->execute(delta); + modification_stack->execute(delta, p_execution_mode); - // A hack: Override the CanvasItem transform using the RenderingServer so the local pose override is taken into account. - for (int i = 0; i < bones.size(); i++) { - if (bones[i].local_pose_override_amount > 0) { - bones[i].bone->set_meta("_local_pose_override_enabled_", true); - bones[i].bone->set_transform(bones[i].local_pose_cache); + // Only apply the local pose override on _process. Otherwise, just calculate the local_pose_override and reset the transform. + if (p_execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process) { + // A hack: Override the CanvasItem transform using the RenderingServer so the local pose override is taken into account. + for (int i = 0; i < bones.size(); i++) { + if (bones[i].local_pose_override_amount > 0) { + bones[i].bone->set_meta("_local_pose_override_enabled_", true); + bones[i].bone->set_transform(bones[i].local_pose_cache); - Transform2D final_trans = bones[i].local_pose_cache; - final_trans = final_trans.interpolate_with(bones[i].local_pose_override, bones[i].local_pose_override_amount); + Transform2D final_trans = bones[i].local_pose_cache; + final_trans = final_trans.interpolate_with(bones[i].local_pose_override, bones[i].local_pose_override_amount); - RenderingServer::get_singleton()->canvas_item_set_transform(bones[i].bone->get_canvas_item(), final_trans); + RenderingServer::get_singleton()->canvas_item_set_transform(bones[i].bone->get_canvas_item(), final_trans); - if (bones[i].local_pose_override_persistent) { - bones.write[i].local_pose_override_amount = 0.0; + if (bones[i].local_pose_override_persistent) { + bones.write[i].local_pose_override_amount = 0.0; + } + } else { + // TODO: see if there is a way to undo the override without having to resort to setting every bone's transform. + bones[i].bone->remove_meta("_local_pose_override_enabled_"); + bones[i].bone->set_transform(bones.write[i].local_pose_cache); + RenderingServer::get_singleton()->canvas_item_set_transform(bones[i].bone->get_canvas_item(), bones[i].local_pose_cache); } - } else { - // TODO: see if there is a way to undo the override without having to resort to setting every bone's transform. - bones[i].bone->remove_meta("_local_pose_override_enabled_"); - bones[i].bone->set_transform(bones.write[i].local_pose_cache); - RenderingServer::get_singleton()->canvas_item_set_transform(bones[i].bone->get_canvas_item(), bones[i].local_pose_cache); + } + } else { + for (int i = 0; i < bones.size(); i++) { + bones[i].bone->set_transform(bones[i].local_pose_cache); } } } @@ -750,7 +753,7 @@ void Skeleton2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_modification_stack", "modification_stack"), &Skeleton2D::set_modification_stack); ClassDB::bind_method(D_METHOD("get_modification_stack"), &Skeleton2D::get_modification_stack); - ClassDB::bind_method(D_METHOD("execute_modification"), &Skeleton2D::execute_modification); + ClassDB::bind_method(D_METHOD("execute_modification", "execution_mode"), &Skeleton2D::execute_modification); ClassDB::bind_method(D_METHOD("set_bone_local_pose_override", "bone_idx", "override_pose", "strength", "persistent"), &Skeleton2D::set_bone_local_pose_override); ClassDB::bind_method(D_METHOD("get_bone_local_pose_override", "bone_idx"), &Skeleton2D::get_bone_local_pose_override); diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index 21e38945589d..f3ad37e3bac0 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -159,7 +159,7 @@ class Skeleton2D : public Node2D { Ref get_modification_stack() const; void set_modification_stack(Ref p_stack); - void execute_modification(float delta); + void execute_modification(float delta, int p_execution_mode); Skeleton2D(); ~Skeleton2D(); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 022b381210a2..6118dcb3171a 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -660,12 +660,13 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); - ClassDB::register_virtual_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index dac5a14a6147..f9afcaa49d90 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -45,7 +45,7 @@ void SkeletonModificationStack2D::_get_property_list(List *p_list) PropertyInfo(Variant::OBJECT, "modifications/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModification2D", - PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); + PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); } } @@ -89,7 +89,7 @@ void SkeletonModificationStack2D::setup() { } } -void SkeletonModificationStack2D::execute(float delta) { +void SkeletonModificationStack2D::execute(float 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!"); @@ -106,7 +106,10 @@ void SkeletonModificationStack2D::execute(float delta) { if (!modifications[i].is_valid()) { continue; } - modifications.get(i)->execute(delta); + + if (modifications[i]->get_execution_mode() == p_execution_mode) { + modifications.get(i)->execute(delta); + } } } @@ -162,6 +165,10 @@ Skeleton2D *SkeletonModificationStack2D::get_skeleton() const { return skeleton; } +void SkeletonModificationStack2D::set_is_setup(bool p_setup) { + is_setup = p_setup; +} + bool SkeletonModificationStack2D::get_is_setup() const { return is_setup; } @@ -184,14 +191,6 @@ float SkeletonModificationStack2D::get_strength() const { return strength; } -void SkeletonModificationStack2D::set_execution_mode(int p_new_mode) { - execution_mode = p_new_mode; -} - -int SkeletonModificationStack2D::get_execution_mode() { - return execution_mode; -} - void SkeletonModificationStack2D::_bind_methods() { ClassDB::bind_method(D_METHOD("setup"), &SkeletonModificationStack2D::setup); ClassDB::bind_method(D_METHOD("execute", "delta"), &SkeletonModificationStack2D::execute); @@ -205,6 +204,7 @@ void SkeletonModificationStack2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack2D::set_modification_count); ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack2D::get_modification_count); + ClassDB::bind_method(D_METHOD("set_is_setup", "setup"), &SkeletonModificationStack2D::set_is_setup); ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack2D::get_is_setup); ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModificationStack2D::set_enabled); @@ -213,12 +213,10 @@ void SkeletonModificationStack2D::_bind_methods() { 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("set_execution_mode", "execution_mode"), &SkeletonModificationStack2D::set_execution_mode); - ClassDB::bind_method(D_METHOD("get_execution_mode"), &SkeletonModificationStack2D::get_execution_mode); + 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, "execution_mode", PROPERTY_HINT_ENUM, "process, physics_process"), "set_execution_mode", "get_execution_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_modification_count", "get_modification_count"); } @@ -236,6 +234,12 @@ SkeletonModificationStack2D::SkeletonModificationStack2D() { /////////////////////////////////////// void SkeletonModification2D::execute(float delta) { + if (get_script_instance()) { + if (get_script_instance()->has_method("execute")) { + get_script_instance()->call("execute", delta); + } + } + if (!enabled) return; } @@ -244,6 +248,14 @@ void SkeletonModification2D::setup_modification(SkeletonModificationStack2D *p_s stack = p_stack; if (stack) { is_setup = true; + } else { + WARN_PRINT("Could not setup modification with name " + get_name()); + } + + if (get_script_instance()) { + if (get_script_instance()->has_method("execute")) { + get_script_instance()->call("setup_modification", p_stack); + } } } @@ -303,14 +315,41 @@ float SkeletonModification2D::clamp_angle(float angle, float min_bound, float ma return angle; } +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::_bind_methods() { BIND_VMETHOD(MethodInfo("execute")); BIND_VMETHOD(MethodInfo("setup_modification")); 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); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "execution_mode", PROPERTY_HINT_ENUM, "process, physics_process"), "set_execution_mode", "get_execution_mode"); } SkeletonModification2D::SkeletonModification2D() { @@ -1684,7 +1723,7 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D // Collision detection/response if (use_colliders) { - if (stack->execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process) { + if (execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process) { Ref 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()); @@ -2806,3 +2845,78 @@ SkeletonModification2DPhysicalBones::SkeletonModification2DPhysicalBones() { SkeletonModification2DPhysicalBones::~SkeletonModification2DPhysicalBones() { } + +/////////////////////////////////////// +// StackHolder +/////////////////////////////////////// + +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); + } + 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(); + } + return true; +} + +void SkeletonModification2DStackHolder::_get_property_list(List *p_list) const { + p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); +} + +void SkeletonModification2DStackHolder::execute(float 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(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::set_held_modification_stack(Ref p_held_stack) { + held_modification_stack = p_held_stack; + + if (is_setup && held_modification_stack.is_valid()) { + held_modification_stack->set_skeleton(stack->get_skeleton()); + held_modification_stack->setup(); + } +} + +Ref SkeletonModification2DStackHolder::get_held_modification_stack() const { + return held_modification_stack; +} + +void SkeletonModification2DStackHolder::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_held_modification_stack", "held_modification_stack"), &SkeletonModification2DStackHolder::set_held_modification_stack); + ClassDB::bind_method(D_METHOD("get_held_modification_stack"), &SkeletonModification2DStackHolder::get_held_modification_stack); +} + +SkeletonModification2DStackHolder::SkeletonModification2DStackHolder() { + stack = nullptr; + is_setup = false; + enabled = true; +} + +SkeletonModification2DStackHolder::~SkeletonModification2DStackHolder() { +} diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index 86aa41118a33..701d22cb07b5 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -61,16 +61,12 @@ class SkeletonModificationStack2D : public Resource { execution_mode_process, execution_mode_physics_process }; - int execution_mode = execution_mode_process; - - void set_execution_mode(int p_mode); - int get_execution_mode(); Vector> modifications; int modifications_count = 0; void setup(); - void execute(float delta); + void execute(float delta, int p_execution_mode); void enable_all_modifications(bool p_enable); Ref get_modification(int p_mod_idx) const; @@ -85,6 +81,7 @@ class SkeletonModificationStack2D : public Resource { Skeleton2D *get_skeleton() const; bool get_is_setup() const; + void set_is_setup(bool p_setup); void set_enabled(bool p_enabled); bool get_enabled() const; @@ -107,8 +104,9 @@ class SkeletonModification2D : public Resource { static void _bind_methods(); SkeletonModificationStack2D *stack; + int execution_mode = SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process; - bool enabled = false; + bool enabled = true; bool is_setup = false; public: @@ -118,6 +116,13 @@ class SkeletonModification2D : public Resource { void set_enabled(bool p_enabled); bool get_enabled(); + 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 angle, float min_bound, float max_bound, bool invert_clamp = false); SkeletonModification2D(); @@ -577,4 +582,30 @@ class SkeletonModification2DPhysicalBones : public SkeletonModification2D { ~SkeletonModification2DPhysicalBones(); }; +/////////////////////////////////////// +// SkeletonModification2DStackHolder +/////////////////////////////////////// + +class SkeletonModification2DStackHolder : public SkeletonModification2D { + GDCLASS(SkeletonModification2DStackHolder, SkeletonModification2D); + +protected: + static void _bind_methods(); + bool _get(const StringName &p_path, Variant &r_ret) const; + bool _set(const StringName &p_path, const Variant &p_value); + void _get_property_list(List *p_list) const; + +public: + Ref held_modification_stack; + + void execute(float delta) override; + void setup_modification(SkeletonModificationStack2D *p_stack) override; + + void set_held_modification_stack(Ref p_held_stack); + Ref get_held_modification_stack() const; + + SkeletonModification2DStackHolder(); + ~SkeletonModification2DStackHolder(); +}; + #endif // SKELETONMODIFICATION2D_H From d440335bc6803c5054fc61d8484e07917a26a9c9 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Mon, 24 Aug 2020 15:58:47 -0400 Subject: [PATCH 12/34] Changes: * Updated the class reference for all Skeleton2D, SkeletonModification2D, and SkeletonModificationStack2D changes. * Skeleton2D: Changed the name of the "execute_modification" function to "execute_modifications", which is what 3D uses and is more reflective of what the function does. * SkeletonModificationStack2D: Removed the set_is_setup function, since it does not exist in 3D and shouldn't be needed. Modifications should be setup by the modification stack. * Skeleton2D and SkeletonModificationStack2D: Adjusted function bindings so they work correctly with the documentation. --- doc/classes/Skeleton2D.xml | 8 ++-- doc/classes/SkeletonModification2D.xml | 41 +++++++++++++++++++ doc/classes/SkeletonModification2DLookAt.xml | 1 - .../SkeletonModification2DStackHolder.xml | 32 +++++++++++++++ doc/classes/SkeletonModificationStack2D.xml | 14 +++++-- scene/2d/skeleton_2d.cpp | 8 ++-- scene/2d/skeleton_2d.h | 2 +- scene/resources/skeleton_modification_2d.cpp | 7 +--- scene/resources/skeleton_modification_2d.h | 1 - 9 files changed, 94 insertions(+), 20 deletions(-) create mode 100644 doc/classes/SkeletonModification2DStackHolder.xml diff --git a/doc/classes/Skeleton2D.xml b/doc/classes/Skeleton2D.xml index 445cc2e0e607..c3e36d68e2a9 100644 --- a/doc/classes/Skeleton2D.xml +++ b/doc/classes/Skeleton2D.xml @@ -10,13 +10,15 @@ https://docs.godotengine.org/en/latest/tutorials/animation/2d_skeletons.html - + - + + + - Executes all the modifications on the [SkeletonModificationStack2D], if the skeleton has one. + Executes all the modifications on the [SkeletonModificationStack2D], if the Skeleton3D has one assigned. diff --git a/doc/classes/SkeletonModification2D.xml b/doc/classes/SkeletonModification2D.xml index 877e51315b2c..0282e4bab945 100644 --- a/doc/classes/SkeletonModification2D.xml +++ b/doc/classes/SkeletonModification2D.xml @@ -10,6 +10,21 @@ + + + + + + + + + + + + + Takes a angle and clamps it so it is within the passed-in [code]min[/code] and [code]max[/code] range. [code]invert[/code] will inversely clamp the angle, clamping it to the range outside of the given bounds. + + @@ -17,6 +32,29 @@ Executes the given modification. This is where the modification performs whatever function it is designed to do. + + + + + Returns whether this modification has been successfully setup or not. + + + + + + + Returns the [SkeletonModificationStack2D] that this modification is bound to. Through the modification stack, you can access the Skeleton3D the modification is operating on. + + + + + + + + + Manually allows you to set the setup state of the modification. This function should only rarely be used, as the [SkeletonModificationStack2D] the modification is bound to should handle setting the modification up. + + @@ -30,6 +68,9 @@ When true, the modification's [method execute] function will be called by the [SkeletonModificationStack2D]. + + The execution mode for the modification. This tells the modification stack when to execute the modification. Some modifications have settings that are only availible in certain execution modes. + diff --git a/doc/classes/SkeletonModification2DLookAt.xml b/doc/classes/SkeletonModification2DLookAt.xml index 65f2d21021c9..b0fa0e5a01d2 100644 --- a/doc/classes/SkeletonModification2DLookAt.xml +++ b/doc/classes/SkeletonModification2DLookAt.xml @@ -21,7 +21,6 @@ Returns whether the constraints to this modification are inverted or not. - diff --git a/doc/classes/SkeletonModification2DStackHolder.xml b/doc/classes/SkeletonModification2DStackHolder.xml new file mode 100644 index 000000000000..acaf78f53d63 --- /dev/null +++ b/doc/classes/SkeletonModification2DStackHolder.xml @@ -0,0 +1,32 @@ + + + + A modification that holds and executes a [SkeletonModificationStack2D]. + + + This [SkeletonModification2D] holds a reference to a [SkeletonModificationStack2D], allowing you to use multiple modification stacks on a single [Skeleton2D]. + [b]Note[/b]: The modifications in the held [SkeletonModificationStack2D] will only be executed if their execution mode matches the execution mode of the SkeletonModification2DStackHolder. + + + + + + + + + Returns the [SkeletonModificationStack2D] that this modification is holding. + + + + + + + + + Sets the [SkeletonModificationStack2D] that this modification is holding. This modification stack will then be executed when this modification is executed. + + + + + + diff --git a/doc/classes/SkeletonModificationStack2D.xml b/doc/classes/SkeletonModificationStack2D.xml index f42165ce4825..55822a84e7f6 100644 --- a/doc/classes/SkeletonModificationStack2D.xml +++ b/doc/classes/SkeletonModificationStack2D.xml @@ -43,8 +43,10 @@ + + - Executes all of the [SkeletonModification2D]s in the stack, starting from index [code]0[/code] to [member modification_count]. + Executes all of the [SkeletonModification2D]s in the stack that use the same execution mode as the passed-in [code]execution_mode[/code], starting from index [code]0[/code] to [member modification_count]. [b]Note:[/b] The order of the modifications can matter depending on the modifications. For example, modifications on a spine should operate before modifications on the arms in order to get proper results. @@ -64,6 +66,13 @@ Returns the [SkeletonModification2D] at the passed-in index, [code]mod_idx[/code]. + + + + + Returns the [Skeleton2D] node that the SkeletonModificationStack2D is bound to. + + @@ -87,9 +96,6 @@ When [code]true[/code], the modification's in the stack will be called. This is handled automatically through the [Skeleton2D] node. - - When the modification will be executed. Can be set to either [code]_process[/code] or [code]_physics_process[/code]. For physics-related or physics-aware modifications, using [code]_physics_process[/code] may be required. - The number of modifications in the stack. diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 8f1a1fcaec8a..124c225f3398 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -653,12 +653,12 @@ void Skeleton2D::_notification(int p_what) { } if (p_what == NOTIFICATION_PROCESS) { if (modification_stack.is_valid()) { - execute_modification(get_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process); + execute_modifications(get_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process); } } if (p_what == NOTIFICATION_PHYSICS_PROCESS) { if (modification_stack.is_valid()) { - execute_modification(get_physics_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process); + execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process); } } } @@ -695,7 +695,7 @@ Ref Skeleton2D::get_modification_stack() const { return modification_stack; } -void Skeleton2D::execute_modification(float delta, int p_execution_mode) { +void Skeleton2D::execute_modifications(float delta, int p_execution_mode) { if (!modification_stack.is_valid()) { return; } @@ -753,7 +753,7 @@ void Skeleton2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_modification_stack", "modification_stack"), &Skeleton2D::set_modification_stack); ClassDB::bind_method(D_METHOD("get_modification_stack"), &Skeleton2D::get_modification_stack); - ClassDB::bind_method(D_METHOD("execute_modification", "execution_mode"), &Skeleton2D::execute_modification); + ClassDB::bind_method(D_METHOD("execute_modifications", "execution_mode", "execution_mode"), &Skeleton2D::execute_modifications); ClassDB::bind_method(D_METHOD("set_bone_local_pose_override", "bone_idx", "override_pose", "strength", "persistent"), &Skeleton2D::set_bone_local_pose_override); ClassDB::bind_method(D_METHOD("get_bone_local_pose_override", "bone_idx"), &Skeleton2D::get_bone_local_pose_override); diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index f3ad37e3bac0..c16f6e133750 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -159,7 +159,7 @@ class Skeleton2D : public Node2D { Ref get_modification_stack() const; void set_modification_stack(Ref p_stack); - void execute_modification(float delta, int p_execution_mode); + void execute_modifications(float delta, int p_execution_mode); Skeleton2D(); ~Skeleton2D(); diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index f9afcaa49d90..13d806fcfbc6 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -165,10 +165,6 @@ Skeleton2D *SkeletonModificationStack2D::get_skeleton() const { return skeleton; } -void SkeletonModificationStack2D::set_is_setup(bool p_setup) { - is_setup = p_setup; -} - bool SkeletonModificationStack2D::get_is_setup() const { return is_setup; } @@ -193,7 +189,7 @@ float SkeletonModificationStack2D::get_strength() const { void SkeletonModificationStack2D::_bind_methods() { ClassDB::bind_method(D_METHOD("setup"), &SkeletonModificationStack2D::setup); - ClassDB::bind_method(D_METHOD("execute", "delta"), &SkeletonModificationStack2D::execute); + 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); @@ -204,7 +200,6 @@ void SkeletonModificationStack2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack2D::set_modification_count); ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack2D::get_modification_count); - ClassDB::bind_method(D_METHOD("set_is_setup", "setup"), &SkeletonModificationStack2D::set_is_setup); ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack2D::get_is_setup); ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModificationStack2D::set_enabled); diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index 701d22cb07b5..be76e37ae34d 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -81,7 +81,6 @@ class SkeletonModificationStack2D : public Resource { Skeleton2D *get_skeleton() const; bool get_is_setup() const; - void set_is_setup(bool p_setup); void set_enabled(bool p_enabled); bool get_enabled() const; From 9c62f26c276ebf20a6988b868843a6bffc5aafd7 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Tue, 25 Aug 2020 12:45:08 -0400 Subject: [PATCH 13/34] Changes: * VariantCall.cpp: Exposed looking_at and set_rotation in Transform2D to GDScript. * Transform2D: Made looking_at a constant function * PhysicalBone2D: Added a property to tell the physical bone to follow the Bone2D its bound to. * Without this, moving the root bone in the chain will visually not appear to do anything. Now you can move root bones and the physics chain will be updated accordingly when the root bone has the new property enabled. * PhysicalBone2D: Changed where the autoconfigure joint option places the joint. Now the joint is placed at the position of the node, rather than it's parent. This is more in line with what you would expect when using physics for a bunch of chains. * SkeletonModification2D: Adjusted the virual function bindings given to GDScript, so GDScript knows of the arguments passed. * Updated class reference to account for the changes in this commit. --- core/math/transform_2d.cpp | 2 +- core/math/transform_2d.h | 2 +- doc/classes/PhysicalBone2D.xml | 5 ++- doc/classes/SkeletonModification2D.xml | 6 ++- doc/classes/Transform2D.xml | 17 +++++++++ scene/2d/physical_bone_2d.cpp | 40 +++++++++++++++----- scene/2d/physical_bone_2d.h | 4 +- scene/resources/skeleton_modification_2d.cpp | 4 +- 8 files changed, 62 insertions(+), 18 deletions(-) diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 000446504e46..5f5d8a766a96 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -158,7 +158,7 @@ bool Transform2D::is_equal_approx(const Transform2D &p_transform) const { return elements[0].is_equal_approx(p_transform.elements[0]) && elements[1].is_equal_approx(p_transform.elements[1]) && elements[2].is_equal_approx(p_transform.elements[2]); } -Transform2D Transform2D::looking_at(const Vector2 target) { +Transform2D Transform2D::looking_at(const Vector2 target) const { Transform2D return_trans = Transform2D(get_rotation(), get_origin()); Vector2 target_position = affine_inverse().xform(target); return_trans.set_rotation(return_trans.get_rotation() + (target_position * get_scale()).angle()); diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index 500873718cd0..cf803b0e9d5f 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -100,7 +100,7 @@ struct Transform2D { Transform2D orthonormalized() const; bool is_equal_approx(const Transform2D &p_transform) const; - Transform2D looking_at(const Vector2 target); + Transform2D looking_at(const Vector2 target) const; bool operator==(const Transform2D &p_transform) const; bool operator!=(const Transform2D &p_transform) const; diff --git a/doc/classes/PhysicalBone2D.xml b/doc/classes/PhysicalBone2D.xml index 3e1c9a7bd28c..f3f32ac4f00e 100644 --- a/doc/classes/PhysicalBone2D.xml +++ b/doc/classes/PhysicalBone2D.xml @@ -36,8 +36,11 @@ The [NodePath] to the [Bone2D] node that this [code]PhysicalBone2D[/code] node is supposed to be simulating. + + When [code]true[/code], the [code]PhysicalBone2D[/code] will keep the transform of the bone it is bound to when simulating physics. + - When true, the [code]PhysicalBone2D[/code] will start simulating using physics. When false, the [code]PhysicalBone2D[/code] will follow the transform of the [Bone2D] node. + When [code]true[/code], the [code]PhysicalBone2D[/code] will start simulating using physics. When false, the [code]PhysicalBone2D[/code] will follow the transform of the [Bone2D] node. [b]Note[/b]: To have the Bone2D nodes visually follow the [code]PhysicalBone2D[/code] node, use a [SkeletonModification2DPhysicalBones] modification on the [Skeleton2D] node with the [Bone2D] nodes. diff --git a/doc/classes/SkeletonModification2D.xml b/doc/classes/SkeletonModification2D.xml index 0282e4bab945..96bf762fd7f3 100644 --- a/doc/classes/SkeletonModification2D.xml +++ b/doc/classes/SkeletonModification2D.xml @@ -28,6 +28,8 @@ + + Executes the given modification. This is where the modification performs whatever function it is designed to do. @@ -58,9 +60,9 @@ + + - Sets up the modification so it can be executed. This function should be called automatically by the [SkeletonModificationStack2D] containing this modification. - If you need to initialize a modification before use, this is the place to do it! diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index 6ae7fbcf799b..5f7e3a5951d4 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -183,6 +183,14 @@ + + + + + + + Returns a copy of the transform rotated such that it's rotation on the X-axis points towards the [code]target[/code] position. + Operations take place in global space. @@ -210,6 +218,15 @@ Scales the transform by the given scale factor, using matrix multiplication. + + + + + + + Sets the transform's rotation (in radians). + + diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index ede2355496bf..14692ef08db8 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -53,13 +53,15 @@ void PhysicalBone2D::_notification(int p_what) { set_physics_process_internal(true); } - // Keep the child joint in the correct position. if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { + // Position the RigidBody in the correct position + if (follow_bone_when_simulating) { + _position_at_bone2d(); + } + + // Keep the child joint in the correct position. if (child_joint && auto_configure_joint) { - CanvasItem *node_a = Object::cast_to(child_joint->get_node(child_joint->get_node_a())); - if (node_a) { - child_joint->set_global_position(node_a->get_global_transform().get_origin()); - } + child_joint->set_global_position(get_global_position()); } // Remove any collision layers and masks if we're disabled. @@ -164,13 +166,12 @@ void PhysicalBone2D::_auto_configure_joint() { if (potential_parent_bone) { child_joint->set_node_a(child_joint->get_path_to(potential_parent_bone)); child_joint->set_node_b(child_joint->get_path_to(this)); - - // Place the child joint at the parent's position. - child_joint->set_global_position(potential_parent_bone->get_global_position()); - } else { WARN_PRINT("Cannot setup joint without a parent PhysicalBone2D node."); } + + // Place the child joint at this node's position. + child_joint->set_global_position(get_global_position()); } } @@ -183,7 +184,9 @@ void PhysicalBone2D::_start_physics_simulation() { _position_at_bone2d(); // Let the RigidBody executing its force integration. - PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + if (!follow_bone_when_simulating) { + PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + } // Apply the layers and masks PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); @@ -271,6 +274,19 @@ int PhysicalBone2D::get_bone2d_index() const { return bone2d_index; } +void PhysicalBone2D::set_follow_bone_when_simulating(bool p_follow_bone) { + follow_bone_when_simulating = p_follow_bone; + + if (_internal_simulate_physics) { + _stop_physics_simulation(); + _start_physics_simulation(); + } +} + +bool PhysicalBone2D::get_follow_bone_when_simulating() const { + return follow_bone_when_simulating; +} + void PhysicalBone2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_joint"), &PhysicalBone2D::get_joint); ClassDB::bind_method(D_METHOD("get_auto_configure_joint"), &PhysicalBone2D::get_auto_configure_joint); @@ -284,16 +300,20 @@ void PhysicalBone2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone2d_nodepath"), &PhysicalBone2D::get_bone2d_nodepath); ClassDB::bind_method(D_METHOD("set_bone2d_index", "bone_index"), &PhysicalBone2D::set_bone2d_index); ClassDB::bind_method(D_METHOD("get_bone2d_index"), &PhysicalBone2D::get_bone2d_index); + ClassDB::bind_method(D_METHOD("set_follow_bone_when_simulating", "follow_bone"), &PhysicalBone2D::set_follow_bone_when_simulating); + ClassDB::bind_method(D_METHOD("get_follow_bone_when_simulating"), &PhysicalBone2D::get_follow_bone_when_simulating); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "bone2d_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D"), "set_bone2d_nodepath", "get_bone2d_nodepath"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bone2d_index", PROPERTY_HINT_RANGE, "-1, 1000, 1"), "set_bone2d_index", "get_bone2d_index"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_configure_joint"), "set_auto_configure_joint", "get_auto_configure_joint"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simulate_physics"), "set_simulate_physics", "get_simulate_physics"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_bone_when_simulating"), "set_follow_bone_when_simulating", "get_follow_bone_when_simulating"); } PhysicalBone2D::PhysicalBone2D() { // Stop the RigidBody from executing its force integration. PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), nullptr, ""); + child_joint = nullptr; } PhysicalBone2D::~PhysicalBone2D() { diff --git a/scene/2d/physical_bone_2d.h b/scene/2d/physical_bone_2d.h index e00c5fa081db..7f30cbcfacbb 100644 --- a/scene/2d/physical_bone_2d.h +++ b/scene/2d/physical_bone_2d.h @@ -47,8 +47,8 @@ class PhysicalBone2D : public RigidBody2D { Skeleton2D *parent_skeleton; int bone2d_index = -1; NodePath bone2d_nodepath; + bool follow_bone_when_simulating = false; - // TODO: make child joint only required if there is a PhysicalBone2D child. Root PhysicalBone2D nodes shouldn't need joints. Joint2D *child_joint; bool auto_configure_joint = true; @@ -76,6 +76,8 @@ class PhysicalBone2D : public RigidBody2D { NodePath get_bone2d_nodepath() const; void set_bone2d_index(int p_bone_idx); int get_bone2d_index() const; + void set_follow_bone_when_simulating(bool p_follow); + bool get_follow_bone_when_simulating() const; String get_configuration_warning() const override; diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 13d806fcfbc6..3d79cb0175c5 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -331,8 +331,8 @@ int SkeletonModification2D::get_execution_mode() const { } void SkeletonModification2D::_bind_methods() { - BIND_VMETHOD(MethodInfo("execute")); - BIND_VMETHOD(MethodInfo("setup_modification")); + BIND_VMETHOD(MethodInfo("execute", PropertyInfo(Variant::FLOAT, "delta"))); + BIND_VMETHOD(MethodInfo("setup_modification", PropertyInfo(Variant::OBJECT, "modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D"))); ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification2D::set_enabled); ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification2D::get_enabled); From d564845f18818dc8fc914b5dba3f912678273ff3 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Wed, 26 Aug 2020 15:31:09 -0400 Subject: [PATCH 14/34] Changes: * Skeleton2D and Bone2D: Fixed issue where children of Bone2D nodes were not properly updated. Now children bones should update as expected. * Doesn't require any hacks or tricks! Now the Bone2D node's position is directly modified by the IK, making it much more straightforward. * All IK modifications still work as expected. CCDIK seems to be improved actually. * A minor downside of this change: currently, if you save a scene with IK modifications enabled, the IK position will be saved rather than the Bone2D cache. * All SkeletonModification2D resources: Changed how errors and warnings are reported. * With the new way the code is written, errors and warnings should not spam the console when there is an issue. * Added an extra check to getting external nodes. * For modifications that have multiple joints/bones, the errors now report which joint is causing the error in the log. * I'm not 100% happy with how the new error and warning reporting code works, but it should be MUCH better than the previous solution. --- scene/2d/skeleton_2d.cpp | 36 +-- scene/2d/skeleton_2d.h | 5 +- scene/resources/skeleton_modification_2d.cpp | 240 ++++++++++++++----- scene/resources/skeleton_modification_2d.h | 3 + 4 files changed, 214 insertions(+), 70 deletions(-) diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 124c225f3398..3dcaaacccf80 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -112,6 +112,9 @@ void Bone2D::_notification(int p_what) { skeleton->_make_bone_setup_dirty(); } + cache_transform = get_transform(); + copy_transform_to_cache = true; + #ifdef TOOLS_ENABLED // Only draw the gizmo in the editor! if (Engine::get_singleton()->is_editor_hint() == false) { @@ -125,6 +128,9 @@ void Bone2D::_notification(int p_what) { if (skeleton) { skeleton->_make_transform_dirty(); } + if (copy_transform_to_cache) { + cache_transform = get_transform(); + } #ifdef TOOLS_ENABLED // Only draw the gizmo in the editor! if (Engine::get_singleton()->is_editor_hint() == false) { @@ -145,6 +151,9 @@ void Bone2D::_notification(int p_what) { if (skeleton) { skeleton->_make_bone_setup_dirty(); } + if (copy_transform_to_cache) { + cache_transform = get_transform(); + } } if (p_what == NOTIFICATION_EXIT_TREE) { @@ -159,6 +168,7 @@ void Bone2D::_notification(int p_what) { skeleton = nullptr; } parent_bone = nullptr; + set_transform(cache_transform); } if (p_what == NOTIFICATION_READY) { @@ -503,6 +513,7 @@ Bone2D::Bone2D() { for (int i = 0; i < 3; i++) { rest[i] = Vector2(0, 0); } + copy_transform_to_cache = true; } Bone2D::~Bone2D() { @@ -700,10 +711,9 @@ void Skeleton2D::execute_modifications(float delta, int p_execution_mode) { return; } - // Cache the transform of the Bone2D before we apply any modifications to it. + // Do not cache the transform changes caused by the modifications! for (int i = 0; i < bones.size(); i++) { - bones.write[i].local_pose_cache = bones[i].bone->get_transform(); - bones[i].bone->set_transform(bones.write[i].local_pose_cache); + bones[i].bone->copy_transform_to_cache = false; } if (modification_stack->skeleton != this) { @@ -714,16 +724,14 @@ void Skeleton2D::execute_modifications(float delta, int p_execution_mode) { // Only apply the local pose override on _process. Otherwise, just calculate the local_pose_override and reset the transform. if (p_execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process) { - // A hack: Override the CanvasItem transform using the RenderingServer so the local pose override is taken into account. for (int i = 0; i < bones.size(); i++) { if (bones[i].local_pose_override_amount > 0) { bones[i].bone->set_meta("_local_pose_override_enabled_", true); - bones[i].bone->set_transform(bones[i].local_pose_cache); - Transform2D final_trans = bones[i].local_pose_cache; + Transform2D final_trans = bones[i].bone->cache_transform; final_trans = final_trans.interpolate_with(bones[i].local_pose_override, bones[i].local_pose_override_amount); - - RenderingServer::get_singleton()->canvas_item_set_transform(bones[i].bone->get_canvas_item(), final_trans); + bones[i].bone->set_transform(final_trans); + bones[i].bone->propagate_call("force_update_transform"); if (bones[i].local_pose_override_persistent) { bones.write[i].local_pose_override_amount = 0.0; @@ -731,14 +739,14 @@ void Skeleton2D::execute_modifications(float delta, int p_execution_mode) { } else { // TODO: see if there is a way to undo the override without having to resort to setting every bone's transform. bones[i].bone->remove_meta("_local_pose_override_enabled_"); - bones[i].bone->set_transform(bones.write[i].local_pose_cache); - RenderingServer::get_singleton()->canvas_item_set_transform(bones[i].bone->get_canvas_item(), bones[i].local_pose_cache); + bones[i].bone->set_transform(bones[i].bone->cache_transform); } } - } else { - for (int i = 0; i < bones.size(); i++) { - bones[i].bone->set_transform(bones[i].local_pose_cache); - } + } + + // Cache any future transform changes + for (int i = 0; i < bones.size(); i++) { + bones[i].bone->copy_transform_to_cache = true; } } diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index c16f6e133750..fc0631be3601 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -74,6 +74,9 @@ class Bone2D : public Node2D { void _get_property_list(List *p_list) const; public: + Transform2D cache_transform; + bool copy_transform_to_cache = true; + void set_rest(const Transform2D &p_rest); Transform2D get_rest() const; void apply_rest(); @@ -121,7 +124,7 @@ class Skeleton2D : public Node2D { Transform2D accum_transform; Transform2D rest_inverse; - Transform2D local_pose_cache; + //Transform2D local_pose_cache; Transform2D local_pose_override; float local_pose_override_amount = 0; bool local_pose_override_persistent = false; diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 3d79cb0175c5..98c1a5def479 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -254,6 +254,14 @@ void SkeletonModification2D::setup_modification(SkeletonModificationStack2D *p_s } } +bool SkeletonModification2D::_print_execution_error(bool p_condition, String p_message) { + if (p_condition && !execution_error_found) { + ERR_PRINT(p_message); + execution_error_found = true; + } + return p_condition; +} + void SkeletonModification2D::set_enabled(bool p_enabled) { enabled = p_enabled; } @@ -413,23 +421,30 @@ void SkeletonModification2DLookAt::execute(float delta) { } if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); update_target_cache(); - WARN_PRINT("Target cache is out of date. Updating..."); return; } if (bone2d_node_cache.is_null() && !bone2d_node.is_empty()) { update_bone2d_cache(); - WARN_PRINT("Bone2D node cache is out of date. Updating..."); + _print_execution_error(true, "Bone2D node cache is out of date. Attempting to update..."); + return; } Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - ERR_FAIL_COND_MSG(!target, "Target node is not a Node2D-based node. Cannot execute modification!"); - ERR_FAIL_COND_MSG(!target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!"); - ERR_FAIL_COND_MSG(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!"); + if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + return; + } + if (_print_execution_error(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!")) { + return; + } Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx); - ERR_FAIL_COND_MSG(operation_bone == nullptr, "bone_idx for modification does not point to a valid bone! Cannot execute modification"); + if (_print_execution_error(operation_bone == nullptr, "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->get_global_transform(); @@ -461,6 +476,9 @@ void SkeletonModification2DLookAt::execute(float delta) { // 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); + + // If we completed it successfully, then we can set execution_error_found to false. + execution_error_found = false; } void SkeletonModification2DLookAt::setup_modification(SkeletonModificationStack2D *p_stack) { @@ -468,6 +486,7 @@ void SkeletonModification2DLookAt::setup_modification(SkeletonModificationStack2 if (stack != nullptr) { is_setup = true; + execution_error_found = false; update_target_cache(); update_bone2d_cache(); } @@ -486,6 +505,8 @@ void SkeletonModification2DLookAt::update_bone2d_cache() { Node *node = stack->skeleton->get_node(bone2d_node); ERR_FAIL_COND_MSG(!node || stack->skeleton == node, "Cannot update Bone2D cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update Bone2D cache: node is not in the scene tree!"); bone2d_node_cache = node->get_instance_id(); Bone2D *bone = Object::cast_to(node); @@ -494,6 +515,8 @@ void SkeletonModification2DLookAt::update_bone2d_cache() { } else { ERR_FAIL_MSG("Error Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } + + execution_error_found = false; } } } @@ -530,6 +553,7 @@ void SkeletonModification2DLookAt::set_bone_index(int p_bone_idx) { bone_idx = p_bone_idx; } + execution_error_found = false; _change_notify(); } @@ -546,7 +570,11 @@ void SkeletonModification2DLookAt::update_target_cache() { Node *node = stack->skeleton->get_node(target_node); ERR_FAIL_COND_MSG(!node || stack->skeleton == node, "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in the scene tree!"); target_node_cache = node->get_instance_id(); + + execution_error_found = false; } } } @@ -741,32 +769,38 @@ void SkeletonModification2DCCDIK::execute(float delta) { } if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); update_target_cache(); - WARN_PRINT("Target cache is out of date. Updating..."); return; } if (tip_node_cache.is_null()) { + _print_execution_error(true, "Tip cache is out of date. Attempting to update..."); update_tip_cache(); - WARN_PRINT("Tip cache is out of date. Updating..."); return; } Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - ERR_FAIL_COND_MSG(!target, "Target node is not a Node2D-based node. Cannot execute modification!"); - ERR_FAIL_COND_MSG(!target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!"); + if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + return; + } Node2D *tip = Object::cast_to(ObjectDB::get_instance(tip_node_cache)); - ERR_FAIL_COND_MSG(!tip, "Tip node is not a Node2D-based node. Cannot execute modification!"); - ERR_FAIL_COND_MSG(!tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!"); + if (_print_execution_error(!tip || !tip->is_inside_tree(), "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); } + + execution_error_found = false; } void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D *target, Node2D *tip) { CCDIK_Joint_Data2D ccdik_data = ccdik_data_chain[p_joint_idx]; - ERR_FAIL_INDEX_MSG(ccdik_data.bone_idx, stack->skeleton->get_bone_count(), "2D CCDIK joint: bone index not found!"); + if (_print_execution_error(ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > stack->skeleton->get_bone_count(), "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(); @@ -812,6 +846,7 @@ void SkeletonModification2DCCDIK::setup_modification(SkeletonModificationStack2D if (stack != nullptr) { is_setup = true; + execution_error_found = false; update_target_cache(); update_tip_cache(); } @@ -830,7 +865,11 @@ void SkeletonModification2DCCDIK::update_target_cache() { Node *node = stack->skeleton->get_node(target_node); ERR_FAIL_COND_MSG(!node || stack->skeleton == node, "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in the scene tree!"); target_node_cache = node->get_instance_id(); + + execution_error_found = false; } } } @@ -849,7 +888,11 @@ void SkeletonModification2DCCDIK::update_tip_cache() { 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(); + + execution_error_found = false; } } } @@ -868,15 +911,19 @@ void SkeletonModification2DCCDIK::ccdik_joint_update_bone2d_cache(int p_joint_id 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 Bone2D cache: node is this modification's skeleton or cannot be found!"); + "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(node); if (bone) { ccdik_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton(); } else { - ERR_FAIL_MSG("CCDIK Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); + ERR_FAIL_MSG("CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } + + execution_error_found = false; } } } @@ -902,6 +949,7 @@ NodePath SkeletonModification2DCCDIK::get_tip_node() const { void SkeletonModification2DCCDIK::set_ccdik_data_chain_length(int p_length) { ccdik_data_chain.resize(p_length); + execution_error_found = false; _change_notify(); } @@ -933,14 +981,15 @@ void SkeletonModification2DCCDIK::ccdik_joint_set_bone_index(int p_joint_idx, in 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 bone index for this modification..."); + 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 { - WARN_PRINT("Cannot verify the CCDIK joint bone index for this modification..."); + 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; } + execution_error_found = false; _change_notify(); } @@ -1148,15 +1197,19 @@ void SkeletonModification2DFABRIK::execute(float delta) { } if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); update_target_cache(); - WARN_PRINT("Target cache is out of date. Updating..."); + return; + } + + if (_print_execution_error(fabrik_data_chain.size() <= 1, "FABRIK requires at least two joints to operate! Cannot execute modification!")) { return; } Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - ERR_FAIL_COND_MSG(!target, "Target node is not a Node2D-based node. Cannot execute modification!"); - ERR_FAIL_COND_MSG(!target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!"); - ERR_FAIL_COND_MSG(fabrik_data_chain.size() <= 1, "FABRIK requires at least two nodes to opperate! Cannot execute modification!"); + if (_print_execution_error(!target || !target->is_inside_tree(), "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()) { @@ -1165,7 +1218,10 @@ void SkeletonModification2DFABRIK::execute(float delta) { } Bone2D *origin_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[0].bone2d_node_cache)); - ERR_FAIL_COND_MSG(!origin_bone2d_node, "Origin joint's Bone2D node not found! Cannot execute modification!"); + if (_print_execution_error(!origin_bone2d_node || !origin_bone2d_node->is_inside_tree(), "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()) { @@ -1175,11 +1231,13 @@ void SkeletonModification2DFABRIK::execute(float delta) { 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()) { + _print_execution_error(true, "Bone2D cache for joint " + itos(i) + " is out of date.. Attempting to update..."); fabrik_joint_update_bone2d_cache(i); - WARN_PRINT("Bone2D cache for joint " + itos(i) + " is out of date. Updating..."); } Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); - ERR_FAIL_COND_MSG(!joint_bone2d_node, "Joint " + itos(i) + " does not have a Bone2D node set! Cannot execute modification!"); + if (_print_execution_error(!joint_bone2d_node, "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(); // Apply magnet positions @@ -1222,7 +1280,9 @@ void SkeletonModification2DFABRIK::execute(float delta) { // Apply all of the saved transforms to the Bone2D nodes for (int i = 0; i < fabrik_data_chain.size(); i++) { Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); - ERR_CONTINUE_MSG(!joint_bone2d_node, "Joint " + itos(i) + " does not have a Bone2D node set!"); + if (_print_execution_error(!joint_bone2d_node, "FABRIK Joint " + itos(i) + " does not have a Bone2D node set!")) { + continue; + } Transform2D chain_trans = fabrik_transform_chain[i]; // Apply rotation @@ -1251,6 +1311,8 @@ void SkeletonModification2DFABRIK::execute(float delta) { 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); } + + execution_error_found = false; } void SkeletonModification2DFABRIK::chain_backwards() { @@ -1350,6 +1412,7 @@ void SkeletonModification2DFABRIK::setup_modification(SkeletonModificationStack2 if (stack != nullptr) { is_setup = true; + execution_error_found = false; update_target_cache(); } } @@ -1367,7 +1430,11 @@ void SkeletonModification2DFABRIK::update_target_cache() { Node *node = stack->skeleton->get_node(target_node); ERR_FAIL_COND_MSG(!node || stack->skeleton == node, "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in scene tree!"); target_node_cache = node->get_instance_id(); + + execution_error_found = false; } } } @@ -1386,15 +1453,19 @@ void SkeletonModification2DFABRIK::fabrik_joint_update_bone2d_cache(int p_joint_ 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 Bone2D cache: node is this modification's skeleton or cannot be found!"); + "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(node); if (bone) { fabrik_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton(); } else { - ERR_FAIL_MSG("FABRIK Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); + ERR_FAIL_MSG("FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } + + execution_error_found = false; } } } @@ -1411,6 +1482,7 @@ NodePath SkeletonModification2DFABRIK::get_target_node() const { void SkeletonModification2DFABRIK::set_fabrik_data_chain_length(int p_length) { fabrik_data_chain.resize(p_length); + execution_error_found = false; _change_notify(); } @@ -1442,14 +1514,15 @@ void SkeletonModification2DFABRIK::fabrik_joint_set_bone_index(int p_joint_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 bone index for this modification..."); + 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 { - WARN_PRINT("Cannot verify the FABRIK joint bone index for this modification..."); + 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; } + execution_error_found = false; _change_notify(); } @@ -1674,31 +1747,41 @@ void SkeletonModification2DJiggle::execute(float delta) { return; } if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); update_target_cache(); - WARN_PRINT("Target cache is out of date. Updating..."); return; } Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - ERR_FAIL_COND_MSG(!target, "Target node is not a Node2D-based node. Cannot execute modification!"); - ERR_FAIL_COND_MSG(!target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!"); + if (_print_execution_error(!target || !target->is_inside_tree(), "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, delta); } + + execution_error_found = false; } void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D *target, float delta) { // Adopted from: https://wiki.unity3d.com/index.php/JiggleBone // With modifications by TwistedTwigleg. - ERR_FAIL_COND_MSG(jiggle_data_chain[p_joint_idx].bone_idx <= -1, "Jiggle joint " + itos(p_joint_idx) + " bone index is invalid. Cannot execute modification on joint..."); + if (_print_execution_error( + jiggle_data_chain[p_joint_idx].bone_idx <= -1 || jiggle_data_chain[p_joint_idx].bone_idx > stack->skeleton->get_bone_count(), + "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()) { + _print_execution_error(true, "Bone2D cache for joint " + itos(p_joint_idx) + " is out of date. Updating..."); jiggle_joint_update_bone2d_cache(p_joint_idx); - WARN_PRINT("Bone2D cache for joint " + itos(p_joint_idx) + " is out of date. Updating..."); } + Bone2D *operation_bone = stack->skeleton->get_bone(jiggle_data_chain[p_joint_idx].bone_idx); - ERR_FAIL_COND_MSG(!operation_bone, "Jiggle joint " + itos(p_joint_idx) + " does not have a Bone2D node or it cannot be found!"); + if (_print_execution_error(!operation_bone, "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 = target->get_global_transform().get_origin(); @@ -1736,7 +1819,7 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D jiggle_data_chain.write[p_joint_idx].last_noncollision_position = jiggle_data_chain[p_joint_idx].dynamic_position; } } else { - WARN_PRINT("Jiggle 2D modifier: You cannot detect colliders without the stack mode being set to _physics_process!"); + WARN_PRINT_ONCE("Jiggle 2D modifier: You cannot detect colliders without the stack mode being set to _physics_process!"); } } @@ -1768,6 +1851,7 @@ void SkeletonModification2DJiggle::setup_modification(SkeletonModificationStack2 if (stack) { is_setup = true; + execution_error_found = false; if (stack->skeleton) { for (int i = 0; i < jiggle_data_chain.size(); i++) { @@ -1795,8 +1879,12 @@ void SkeletonModification2DJiggle::update_target_cache() { 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 cache: Target node is this modification's skeleton or cannot be found!"); + "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in scene tree!"); target_node_cache = node->get_instance_id(); + + execution_error_found = false; } } } @@ -1805,7 +1893,7 @@ void SkeletonModification2DJiggle::update_target_cache() { 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) { - WARN_PRINT("Cannot update Jiggle Bone2D cache: modification is not properly setup!"); + WARN_PRINT("Cannot update Jiggle " + itos(p_joint_idx) + " Bone2D cache: modification is not properly setup!"); return; } @@ -1815,15 +1903,19 @@ void SkeletonModification2DJiggle::jiggle_joint_update_bone2d_cache(int p_joint_ 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 Bone2D cache: node is this modification's skeleton or cannot be found!"); + "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(node); if (bone) { jiggle_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton(); } else { - ERR_FAIL_MSG("Jiggle Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); + ERR_FAIL_MSG("Jiggle joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } + + execution_error_found = false; } } } @@ -1912,6 +2004,7 @@ int SkeletonModification2DJiggle::get_jiggle_data_chain_length() { void SkeletonModification2DJiggle::set_jiggle_data_chain_length(int p_length) { ERR_FAIL_COND(p_length < 0); jiggle_data_chain.resize(p_length); + execution_error_found = false; _change_notify(); } @@ -1939,14 +2032,15 @@ void SkeletonModification2DJiggle::jiggle_joint_set_bone_index(int p_joint_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 bone index for this modification..."); + 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 { - WARN_PRINT("Cannot verify the Jiggle joint bone index for this modification..."); + 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; } + execution_error_found = false; _change_notify(); } @@ -2194,28 +2288,34 @@ void SkeletonModification2DTwoBoneIK::execute(float delta) { } if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); update_target_cache(); - WARN_PRINT("Target cache is out of date. Updating..."); return; } if (joint_one_bone2d_node_cache.is_null() && !joint_one_bone2d_node.is_empty()) { + _print_execution_error(true, "Joint one Bone2D node cache is out of date. Attempting to update..."); update_joint_one_bone2d_cache(); - WARN_PRINT("Joint One Bone2D node cache is out of date. Updating..."); } if (joint_two_bone2d_node_cache.is_null() && !joint_two_bone2d_node.is_empty()) { + _print_execution_error(true, "Joint two Bone2D node cache is out of date. Attempting to update..."); update_joint_two_bone2d_cache(); - WARN_PRINT("Joint Two Bone2D node cache is out of date. Updating..."); } Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - ERR_FAIL_COND_MSG(!target, "Target node is not a Node2D-based node. Cannot execute modification!"); - ERR_FAIL_COND_MSG(!target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!"); + if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + return; + } Bone2D *joint_one_bone = stack->skeleton->get_bone(joint_one_bone_idx); - ERR_FAIL_COND_MSG(joint_one_bone == nullptr, "Joint one bone_idx does not point to a valid bone! Cannot execute modification!"); + if (_print_execution_error(joint_one_bone == nullptr, "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); - ERR_FAIL_COND_MSG(joint_one_bone == nullptr, "Joint two bone_idx does not point to a valid bone! Cannot execute modification!"); + if (_print_execution_error(joint_two_bone == nullptr, "Joint one 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 @@ -2269,6 +2369,8 @@ void SkeletonModification2DTwoBoneIK::execute(float delta) { 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); + + execution_error_found = false; } void SkeletonModification2DTwoBoneIK::setup_modification(SkeletonModificationStack2D *p_stack) { @@ -2276,6 +2378,7 @@ void SkeletonModification2DTwoBoneIK::setup_modification(SkeletonModificationSta if (stack) { is_setup = true; + execution_error_found = false; update_target_cache(); update_joint_one_bone2d_cache(); update_joint_two_bone2d_cache(); @@ -2294,8 +2397,12 @@ void SkeletonModification2DTwoBoneIK::update_target_cache() { 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 cache: Target node is this modification's skeleton or cannot be found!"); + "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in the scene tree!"); target_node_cache = node->get_instance_id(); + + execution_error_found = false; } } } @@ -2314,6 +2421,8 @@ void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { Node *node = stack->skeleton->get_node(joint_one_bone2d_node); ERR_FAIL_COND_MSG(!node || stack->skeleton == node, "Cannot update update joint one Bone2D cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update update joint one Bone2D cache: node is not in the scene tree!"); joint_one_bone2d_node_cache = node->get_instance_id(); Bone2D *bone = Object::cast_to(node); @@ -2322,6 +2431,8 @@ void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { } else { ERR_FAIL_MSG("update joint one Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } + + execution_error_found = false; } } } @@ -2340,6 +2451,8 @@ void SkeletonModification2DTwoBoneIK::update_joint_two_bone2d_cache() { Node *node = stack->skeleton->get_node(joint_two_bone2d_node); ERR_FAIL_COND_MSG(!node || stack->skeleton == node, "Cannot update update joint two Bone2D cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update update joint two Bone2D cache: node is not in scene tree!"); joint_two_bone2d_node_cache = node->get_instance_id(); Bone2D *bone = Object::cast_to(node); @@ -2348,6 +2461,8 @@ void SkeletonModification2DTwoBoneIK::update_joint_two_bone2d_cache() { } else { ERR_FAIL_MSG("update joint two Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } + + execution_error_found = false; } } } @@ -2417,6 +2532,7 @@ void SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) { joint_one_bone_idx = p_bone_idx; } + execution_error_found = false; _change_notify(); } @@ -2442,6 +2558,7 @@ void SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) { joint_two_bone_idx = p_bone_idx; } + execution_error_found = false; _change_notify(); } @@ -2672,21 +2789,28 @@ void SkeletonModification2DPhysicalBones::execute(float delta) { 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("PhysicalBone2D cache " + itos(i) + " is out of date. Updating..."); + _print_execution_error(true, "PhysicalBone2D cache " + itos(i) + " is out of date. Attempting to update..."); _physical_bone_update_cache(i); continue; } PhysicalBone2D *physical_bone = Object::cast_to(ObjectDB::get_instance(bone_data.physical_bone_node_cache)); - ERR_CONTINUE_MSG(!physical_bone, "PhysicalBone2D not found at index " + itos(i) + "!"); - ERR_FAIL_INDEX_MSG(physical_bone->get_bone2d_index(), stack->skeleton->get_bone_count(), "PhysicalBone2D at index " + itos(i) + " has invalid Bone2D!"); + if (_print_execution_error(!physical_bone, "PhysicalBone2D not found at index " + itos(i) + "!")) { + return; + } + if (_print_execution_error(physical_bone->get_bone2d_index() < 0 || physical_bone->get_bone2d_index() > stack->skeleton->get_bone_count(), + "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()) { + 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); } } + + execution_error_found = false; } void SkeletonModification2DPhysicalBones::setup_modification(SkeletonModificationStack2D *p_stack) { @@ -2694,6 +2818,7 @@ void SkeletonModification2DPhysicalBones::setup_modification(SkeletonModificatio if (stack) { is_setup = true; + execution_error_found = false; if (stack->skeleton) { for (int i = 0; i < physical_bone_chain.size(); i++) { @@ -2716,8 +2841,12 @@ void SkeletonModification2DPhysicalBones::_physical_bone_update_cache(int p_join 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 Jiggle Bone2D cache: node is this modification's skeleton or cannot be found!"); + "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(); + + execution_error_found = false; } } } @@ -2730,6 +2859,7 @@ int SkeletonModification2DPhysicalBones::get_physical_bone_chain_length() { void SkeletonModification2DPhysicalBones::set_physical_bone_chain_length(int p_length) { ERR_FAIL_COND(p_length < 0); physical_bone_chain.resize(p_length); + execution_error_found = false; _change_notify(); } diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index be76e37ae34d..9b7f8f2a4e15 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -107,6 +107,9 @@ class SkeletonModification2D : public Resource { bool enabled = true; bool is_setup = false; + bool execution_error_found = false; + + bool _print_execution_error(bool p_condition, String p_message); public: virtual void execute(float delta); From a31dcc306974e75dc925706cbb65b06f7891d63c Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Thu, 27 Aug 2020 10:57:36 -0400 Subject: [PATCH 15/34] Changes: * Node.cpp and Node.h: Added two new notifications: NOTIFICATION_EDITOR_PRE_SAVE and NOTIFICATION_EDITOR_POST_SAVE * These notifications are sent to all nodes in the scene right before (pre) and right after (post) saving the scene in the editor. * This is needed for the Bone2D node to properly set itself to the cache position before saving, so scenes with IK running are still saved properly. * These nodifications can also be invaluable for Godot users writing plugins, as now they can detect scene saves easily, something I have personally encountered when making plugins and have seen as a common question for plugin makers in the Godot community. * Editor_Node.cpp: Now calls the new pre and post editor save notifications. * Bone2D: Now correctly positions itself before and after saving the scene. Now scenes with modifications/IK running can be saved without loosing the initial Bone2D position. * Node documentation: Updated to include descriptions of the two new signals. --- doc/classes/Node.xml | 6 ++++++ editor/editor_node.cpp | 4 ++++ scene/2d/skeleton_2d.cpp | 8 ++++++++ scene/main/node.cpp | 3 +++ scene/main/node.h | 5 +++++ 5 files changed, 26 insertions(+) diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 7750d45226ac..f204dbb45f1a 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -969,6 +969,12 @@ Notification received when the node is ready, just before [constant NOTIFICATION_READY] is received. Unlike the latter, it's sent every time the node enters tree, instead of only once. + + Notification received right before the scene with the node is saved in the editor. This notification is only sent in the Godot editor and will not occur in exported projects. + + + Notification received right after the scene with the node is saved in the editor. This notification is only sent in the Godot editor and will not occur in exported projects. + Notification received from the OS when the mouse enters the game window. Implemented on desktop and web platforms. diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 156cb6694aec..ec4fe64bfee6 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1546,6 +1546,8 @@ void EditorNode::_save_scene(String p_file, int idx) { return; } + scene->propagate_notification(NOTIFICATION_EDITOR_PRE_SAVE); + editor_data.apply_changes_in_editors(); List> anim_backups; _reset_animation_players(scene, &anim_backups); @@ -1617,6 +1619,8 @@ void EditorNode::_save_scene(String p_file, int idx) { } else { _dialog_display_save_error(p_file, err); } + + scene->propagate_notification(NOTIFICATION_EDITOR_POST_SAVE); } void EditorNode::save_all_scenes() { diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 3dcaaacccf80..aa68c2e699d5 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -124,6 +124,7 @@ void Bone2D::_notification(int p_what) { update(); #endif // TOOLS_ENABLED } + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { if (skeleton) { skeleton->_make_transform_dirty(); @@ -147,6 +148,7 @@ void Bone2D::_notification(int p_what) { } #endif // TOOLS_ENABLED } + if (p_what == NOTIFICATION_MOVED_IN_PARENT) { if (skeleton) { skeleton->_make_bone_setup_dirty(); @@ -178,6 +180,12 @@ void Bone2D::_notification(int p_what) { } #ifdef TOOLS_ENABLED + if (p_what == NOTIFICATION_EDITOR_PRE_SAVE || p_what == NOTIFICATION_EDITOR_POST_SAVE) { + Transform2D tmp_trans = get_transform(); + set_transform(cache_transform); + cache_transform = tmp_trans; + } + // Bone2D Editor gizmo drawing: if (p_what == NOTIFICATION_DRAW) { // Only draw the gizmo in the editor! diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 933f67db68dd..42acf0947728 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2817,6 +2817,9 @@ void Node::_bind_methods() { BIND_CONSTANT(NOTIFICATION_INTERNAL_PHYSICS_PROCESS); BIND_CONSTANT(NOTIFICATION_POST_ENTER_TREE); + BIND_CONSTANT(NOTIFICATION_EDITOR_PRE_SAVE); + BIND_CONSTANT(NOTIFICATION_EDITOR_POST_SAVE); + BIND_CONSTANT(NOTIFICATION_WM_MOUSE_ENTER); BIND_CONSTANT(NOTIFICATION_WM_MOUSE_EXIT); BIND_CONSTANT(NOTIFICATION_WM_WINDOW_FOCUS_IN); diff --git a/scene/main/node.h b/scene/main/node.h index b1e51d2aee7e..abe364c601df 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -253,6 +253,11 @@ class Node : public Object { NOTIFICATION_APPLICATION_FOCUS_IN = MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN, NOTIFICATION_APPLICATION_FOCUS_OUT = MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT, NOTIFICATION_TEXT_SERVER_CHANGED = MainLoop::NOTIFICATION_TEXT_SERVER_CHANGED, + + // Editor specific node notifications + NOTIFICATION_EDITOR_PRE_SAVE = 9001, + NOTIFICATION_EDITOR_POST_SAVE = 9002 + }; /* NODE/TREE */ From a0cf8b433566d5eb5ea41761390b1b2f3bd5683d Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Thu, 10 Sep 2020 17:54:19 -0400 Subject: [PATCH 16/34] Changes: * SkeletonModificationStack2D: Added code so 2D modifications can draw custom, editor visible gizmos! * This should make it MUCH easier to setup things like angle constraints. * The code is only called in the editor with TOOLS_ENABLED, so exported projects shouldn't see any impact. * SkeletonModification2D: Added virtual function for drawing gizmos, added property to show/hide the gizmo (only in the Godot editor) * SkeletonModification2DLookAt: Added a editor gizmo to show angle constraints! Still WIP, but the results are looking nice! * Shows the angle constraints in the editor, positioned correctly at each bone. * When there are no constraints, a full circle is show instead. * The gizmo radius is equal to the bone length, and the color is the IK color in the Godot project settings. * Still a work in progress! There are a few bugs here and there that need hammering out. * Skeleton2D: Added code to tell the ModificationStack to draw the modification gizmos. First commit after Google Summer of Code 2020! --- scene/2d/skeleton_2d.cpp | 18 ++ scene/resources/skeleton_modification_2d.cpp | 189 +++++++++++++++++++ scene/resources/skeleton_modification_2d.h | 10 + 3 files changed, 217 insertions(+) diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index aa68c2e699d5..67eaa7ef2d5d 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -680,6 +680,16 @@ void Skeleton2D::_notification(int p_what) { execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process); } } + +#ifdef TOOLS_ENABLED + if (p_what == NOTIFICATION_DRAW) { + if (Engine::get_singleton()->is_editor_hint()) { + if (modification_stack.is_valid()) { + modification_stack->draw_editor_gizmos(); + } + } + } +#endif // TOOLS_ENABLED } RID Skeleton2D::get_skeleton() const { @@ -707,6 +717,10 @@ void Skeleton2D::set_modification_stack(Ref p_stack if (modification_stack.is_valid()) { modification_stack->set_skeleton(this); modification_stack->setup(); + +#ifdef TOOLS_ENABLED + modification_stack->set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED } } @@ -756,6 +770,10 @@ void Skeleton2D::execute_modifications(float delta, int p_execution_mode) { for (int i = 0; i < bones.size(); i++) { bones[i].bone->copy_transform_to_cache = true; } + +#ifdef TOOLS_ENABLED + modification_stack->set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED } void Skeleton2D::_bind_methods() { diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 98c1a5def479..0c55c518f75a 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -35,6 +35,10 @@ #include "scene/2d/collision_shape_2d.h" #include "scene/2d/physical_bone_2d.h" +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif // TOOLS_ENABLED + /////////////////////////////////////// // ModificationStack2D /////////////////////////////////////// @@ -84,6 +88,11 @@ void SkeletonModificationStack2D::setup() { } 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!"); } @@ -113,6 +122,41 @@ void SkeletonModificationStack2D::execute(float delta, int p_execution_mode) { } } +void SkeletonModificationStack2D::draw_editor_gizmos() { + if (!is_setup) { + return; + } + + if (editor_gizmo_dirty) { + for (int i = 0; i < modifications.size(); i++) { + if (!modifications[i].is_valid()) { + continue; + } + + if (modifications[i]->editor_draw_gizmo) { + modifications.get(i)->draw_editor_gizmo(); + } + } + skeleton->draw_set_transform(Vector2(0, 0)); + editor_gizmo_dirty = false; + } +} + +void SkeletonModificationStack2D::set_editor_gizmos_dirty(bool p_dirty) { + if (!is_setup) { + return; + } + + if (!editor_gizmo_dirty && p_dirty) { + editor_gizmo_dirty = p_dirty; + if (skeleton) { + skeleton->update(); + } + } else { + editor_gizmo_dirty = p_dirty; + } +} + void SkeletonModificationStack2D::enable_all_modifications(bool p_enabled) { for (int i = 0; i < modifications.size(); i++) { if (!modifications[i].is_valid()) { @@ -130,11 +174,19 @@ Ref SkeletonModificationStack2D::get_modification(int p_ void SkeletonModificationStack2D::add_modification(Ref p_mod) { 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(p_mod_idx); + +#ifdef TOOLS_ENABLED + set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED } void SkeletonModificationStack2D::set_modification(int p_mod_idx, Ref p_mod) { @@ -146,11 +198,19 @@ void SkeletonModificationStack2D::set_modification(int p_mod_idx, Refsetup_modification(this); modifications.set(p_mod_idx, p_mod); } + +#ifdef TOOLS_ENABLED + set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED } void SkeletonModificationStack2D::set_modification_count(int p_count) { modifications.resize(p_count); _change_notify(); + +#ifdef TOOLS_ENABLED + set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED } int SkeletonModificationStack2D::get_modification_count() const { @@ -254,6 +314,9 @@ void SkeletonModification2D::setup_modification(SkeletonModificationStack2D *p_s } } +void SkeletonModification2D::draw_editor_gizmo() { +} + bool SkeletonModification2D::_print_execution_error(bool p_condition, String p_message) { if (p_condition && !execution_error_found) { ERR_PRINT(p_message); @@ -264,6 +327,14 @@ bool SkeletonModification2D::_print_execution_error(bool p_condition, String p_m 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() { @@ -338,6 +409,17 @@ 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 + stack->set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED +} + +bool SkeletonModification2D::get_editor_draw_gizmo() const { + return editor_draw_gizmo; +} + void SkeletonModification2D::_bind_methods() { BIND_VMETHOD(MethodInfo("execute", PropertyInfo(Variant::FLOAT, "delta"))); BIND_VMETHOD(MethodInfo("setup_modification", PropertyInfo(Variant::OBJECT, "modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D"))); @@ -380,6 +462,13 @@ bool SkeletonModification2DLookAt::_set(const StringName &p_path, const Variant } else if (path.begins_with("additional_rotation")) { set_additional_rotation(Math::deg2rad(float(p_value))); } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + set_editor_draw_gizmo(p_value); + } +#endif // TOOLS_ENABLED + return true; } @@ -399,6 +488,13 @@ bool SkeletonModification2DLookAt::_get(const StringName &p_path, Variant &r_ret } else if (path.begins_with("additional_rotation")) { r_ret = Math::rad2deg(get_additional_rotation()); } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + r_ret = get_editor_draw_gizmo(); + } +#endif // TOOLS_ENABLED + return true; } @@ -411,6 +507,12 @@ void SkeletonModification2DLookAt::_get_property_list(List *p_list 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 delta) { @@ -479,6 +581,10 @@ void SkeletonModification2DLookAt::execute(float delta) { // If we completed it successfully, then we can set execution_error_found to false. execution_error_found = false; + // Draw the editor gizmo in case something changed +#ifdef TOOLS_ENABLED + //stack->set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED } void SkeletonModification2DLookAt::setup_modification(SkeletonModificationStack2D *p_stack) { @@ -492,6 +598,62 @@ void SkeletonModification2DLookAt::setup_modification(SkeletonModificationStack2 } } +void SkeletonModification2DLookAt::draw_editor_gizmo() { + if (!enabled || !is_setup) { + return; + } + + Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx); + if (!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 = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color"); + } +#endif // TOOLS_ENABLED + + float arc_angle_min = constraint_angle_min + operation_bone->get_bone_angle(); + float arc_angle_max = constraint_angle_max + operation_bone->get_bone_angle(); + + if (arc_angle_min > arc_angle_max) { + float tmp = arc_angle_min; + arc_angle_min = arc_angle_max; + arc_angle_max = tmp; + } + + if (enable_constraint) { + if (constraint_in_localspace) { + Node *operation_bone_parent = operation_bone->get_parent(); + Bone2D *operation_bone_parent_bone = Object::cast_to(operation_bone_parent); + + if (operation_bone_parent_bone) { + stack->skeleton->draw_set_transform( + stack->skeleton->get_global_transform().affine_inverse().xform(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->get_global_transform().affine_inverse().xform(operation_bone->get_global_position())); + } + } else { + stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(operation_bone->get_global_position())); + } + + if (constraint_angle_invert) { + stack->skeleton->draw_arc(Vector2(0, 0), operation_bone->get_length(), + (Math_PI * 2) - arc_angle_max, arc_angle_min, 32, bone_ik_color, 4.0); + } else { + stack->skeleton->draw_arc(Vector2(0, 0), operation_bone->get_length(), + arc_angle_min, arc_angle_max, 32, bone_ik_color, 4.0); + } + } else { + stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(operation_bone->get_global_position())); + stack->skeleton->draw_arc(Vector2(0, 0), operation_bone->get_length(), 0, Math_PI * 2, 32, bone_ik_color, 4.0); + } +} + void SkeletonModification2DLookAt::update_bone2d_cache() { if (!is_setup || !stack) { WARN_PRINT("Cannot update Bone2D cache: modification is not properly setup!"); @@ -600,6 +762,11 @@ void SkeletonModification2DLookAt::set_additional_rotation(float p_rotation) { void SkeletonModification2DLookAt::set_enable_constraint(bool p_constraint) { enable_constraint = p_constraint; _change_notify(); +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED } bool SkeletonModification2DLookAt::get_enable_constraint() const { @@ -608,6 +775,11 @@ bool SkeletonModification2DLookAt::get_enable_constraint() const { void SkeletonModification2DLookAt::set_constraint_angle_min(float p_angle_min) { constraint_angle_min = p_angle_min; +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED } float SkeletonModification2DLookAt::get_constraint_angle_min() const { @@ -616,6 +788,11 @@ float SkeletonModification2DLookAt::get_constraint_angle_min() const { void SkeletonModification2DLookAt::set_constraint_angle_max(float p_angle_max) { constraint_angle_max = p_angle_max; +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED } float SkeletonModification2DLookAt::get_constraint_angle_max() const { @@ -624,6 +801,11 @@ float SkeletonModification2DLookAt::get_constraint_angle_max() const { void SkeletonModification2DLookAt::set_constraint_angle_invert(bool p_invert) { constraint_angle_invert = p_invert; +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED } bool SkeletonModification2DLookAt::get_constraint_angle_invert() const { @@ -632,6 +814,11 @@ bool SkeletonModification2DLookAt::get_constraint_angle_invert() const { void SkeletonModification2DLookAt::set_constraint_in_localspace(bool p_constraint_in_localspace) { constraint_in_localspace = p_constraint_in_localspace; +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED } bool SkeletonModification2DLookAt::get_constraint_in_localspace() const { @@ -674,6 +861,8 @@ SkeletonModification2DLookAt::SkeletonModification2DLookAt() { constraint_angle_max = Math_PI * 2; constraint_angle_invert = false; enabled = true; + + editor_draw_gizmo = true; } SkeletonModification2DLookAt::~SkeletonModification2DLookAt() { diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index 9b7f8f2a4e15..97c5ed2d37f0 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -68,6 +68,10 @@ class SkeletonModificationStack2D : public Resource { void setup(); void execute(float delta, int p_execution_mode); + bool editor_gizmo_dirty = false; + void draw_editor_gizmos(); + void set_editor_gizmos_dirty(bool p_dirty); + void enable_all_modifications(bool p_enable); Ref get_modification(int p_mod_idx) const; void add_modification(Ref p_mod); @@ -114,6 +118,11 @@ class SkeletonModification2D : public Resource { 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(); @@ -164,6 +173,7 @@ class SkeletonModification2DLookAt : public SkeletonModification2D { public: void execute(float 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; From 3695a89178dbd3712ccaaa03878cedf6aa4d572c Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Fri, 11 Sep 2020 15:59:57 -0400 Subject: [PATCH 17/34] Changes: * SkeletonModification2D: Moved the code for drawing the angle constraints for a Bone2D to SkeletonModification2D, so all the modifications that need to draw angle constraints can use it. * The code is now much more accurate with drawing the angle constraints, as it performs the same angle conversions as the clamp_angle function prior to drawing. * The arc drawn is now a wedge! Two lines are added to connect the arc if the arc is not a circle, and if the arc is a circle only a single line is drawn. * SkeletonModification2DLookAt: Adjusted the editor gizmo drawing code so it uses the new function in SkeletonModification2D * SkeletonModification2DCCDIK: Added editor gizmo drawing code. Now angle constraints are drawn in the editor. The gizmo can be disabled for the whole modification and on a per CCDIK joint basis. * SkeletonModification2DFABRIK: Added editor gizmo drawing code. Now angle constraints are drawn in the editor. The gizmo can be disabled for the whole modification and on a per FABRIK joint basis. * SkeletonModification2DJiggle: Turned editor gizmo drawing off, so the virual function is not called. No editor gizmo drawing code added because I couldn't think of anything that would need to be drawn. * SkeletonModification2DTwoBoneIK: Added editor gizmo drawing code. Now a single line is drawn to show the bend direction of the TwoBoneIK joint. * SkeletonModification2DTwoBoneIK: Removed angle constraint code, as it doesn't work like the other solvers since it is not taken into account by the algorithm. * This should be less confusing for users and since it was visual only, I'm not really sure it had a good usecase anyway. * SkeletonModification2DPhysicalBones: Turned editor gizmo drawing off, so the virtual function is not called. No editor gizmo drawing code added because I couldn't think of anything that would need to be drawn. * SkeletonModification2DStackHolder: Added editor gizmo drawing code. Now the stack that the modification holds will draw its editor gizmo(s) as well. --- scene/resources/skeleton_modification_2d.cpp | 578 +++++++++++-------- scene/resources/skeleton_modification_2d.h | 45 +- 2 files changed, 368 insertions(+), 255 deletions(-) diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 0c55c518f75a..d0dc9d469fba 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -389,6 +389,68 @@ float SkeletonModification2D::clamp_angle(float angle, float min_bound, float ma return angle; } +void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *operation_bone, float min_bound, float max_bound, + bool constraint_enabled, bool constraint_in_localspace, bool constraint_inverted) { + if (!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 = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color"); + } +#endif // TOOLS_ENABLED + + float arc_angle_min = min_bound; + float arc_angle_max = 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) { + float tmp = arc_angle_min; + arc_angle_min = arc_angle_max; + arc_angle_max = tmp; + } + arc_angle_min += operation_bone->get_bone_angle(); + arc_angle_max += operation_bone->get_bone_angle(); + + if (constraint_enabled) { + if (constraint_in_localspace) { + Node *operation_bone_parent = operation_bone->get_parent(); + Bone2D *operation_bone_parent_bone = Object::cast_to(operation_bone_parent); + + if (operation_bone_parent_bone) { + stack->skeleton->draw_set_transform( + stack->skeleton->get_global_transform().affine_inverse().xform(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->get_global_transform().affine_inverse().xform(operation_bone->get_global_position())); + } + } else { + stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(operation_bone->get_global_position())); + } + + if (constraint_inverted) { + stack->skeleton->draw_arc(Vector2(0, 0), operation_bone->get_length(), + (Math_PI * 2) - arc_angle_max, arc_angle_min, 32, bone_ik_color, 1.0); + } else { + stack->skeleton->draw_arc(Vector2(0, 0), 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)) * 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)) * operation_bone->get_length(), bone_ik_color, 1.0); + + } else { + stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(operation_bone->get_global_position())); + stack->skeleton->draw_arc(Vector2(0, 0), operation_bone->get_length(), 0, Math_PI * 2, 32, bone_ik_color, 1.0); + stack->skeleton->draw_line(Vector2(0, 0), Vector2(1, 0) * operation_bone->get_length(), bone_ik_color, 1.0); + } +} + SkeletonModificationStack2D *SkeletonModification2D::get_modification_stack() { return stack; } @@ -581,10 +643,6 @@ void SkeletonModification2DLookAt::execute(float delta) { // If we completed it successfully, then we can set execution_error_found to false. execution_error_found = false; - // Draw the editor gizmo in case something changed -#ifdef TOOLS_ENABLED - //stack->set_editor_gizmos_dirty(true); -#endif // TOOLS_ENABLED } void SkeletonModification2DLookAt::setup_modification(SkeletonModificationStack2D *p_stack) { @@ -604,54 +662,8 @@ void SkeletonModification2DLookAt::draw_editor_gizmo() { } Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx); - if (!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 = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color"); - } -#endif // TOOLS_ENABLED - - float arc_angle_min = constraint_angle_min + operation_bone->get_bone_angle(); - float arc_angle_max = constraint_angle_max + operation_bone->get_bone_angle(); - - if (arc_angle_min > arc_angle_max) { - float tmp = arc_angle_min; - arc_angle_min = arc_angle_max; - arc_angle_max = tmp; - } - - if (enable_constraint) { - if (constraint_in_localspace) { - Node *operation_bone_parent = operation_bone->get_parent(); - Bone2D *operation_bone_parent_bone = Object::cast_to(operation_bone_parent); - - if (operation_bone_parent_bone) { - stack->skeleton->draw_set_transform( - stack->skeleton->get_global_transform().affine_inverse().xform(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->get_global_transform().affine_inverse().xform(operation_bone->get_global_position())); - } - } else { - stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(operation_bone->get_global_position())); - } - - if (constraint_angle_invert) { - stack->skeleton->draw_arc(Vector2(0, 0), operation_bone->get_length(), - (Math_PI * 2) - arc_angle_max, arc_angle_min, 32, bone_ik_color, 4.0); - } else { - stack->skeleton->draw_arc(Vector2(0, 0), operation_bone->get_length(), - arc_angle_min, arc_angle_max, 32, bone_ik_color, 4.0); - } - } else { - stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(operation_bone->get_global_position())); - stack->skeleton->draw_arc(Vector2(0, 0), operation_bone->get_length(), 0, Math_PI * 2, 32, bone_ik_color, 4.0); - } + 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() { @@ -897,8 +909,22 @@ bool SkeletonModification2DCCDIK::_set(const StringName &p_path, const Variant & } else if (what == "constraint_in_localspace") { ccdik_joint_set_constraint_in_localspace(which, p_value); } + +#ifdef TOOLS_ENABLED + if (what.begins_with("editor_draw_gizmo")) { + ccdik_joint_set_editor_draw_gizmo(which, p_value); + } +#endif // TOOLS_ENABLED + return true; } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + set_editor_draw_gizmo(p_value); + } +#endif // TOOLS_ENABLED + return true; } @@ -927,8 +953,22 @@ bool SkeletonModification2DCCDIK::_get(const StringName &p_path, Variant &r_ret) } else if (what == "constraint_in_localspace") { r_ret = ccdik_joint_get_constraint_in_localspace(which); } + +#ifdef TOOLS_ENABLED + if (what.begins_with("editor_draw_gizmo")) { + r_ret = ccdik_joint_get_editor_draw_gizmo(which); + } +#endif // TOOLS_ENABLED + return true; } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + r_ret = get_editor_draw_gizmo(); + } +#endif // TOOLS_ENABLED + return true; } @@ -947,7 +987,19 @@ void SkeletonModification2DCCDIK::_get_property_list(List *p_list) 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 delta) { @@ -1041,6 +1093,22 @@ void SkeletonModification2DCCDIK::setup_modification(SkeletonModificationStack2D } } +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) { WARN_PRINT("Cannot update target cache: modification is not properly setup!"); @@ -1201,6 +1269,12 @@ void SkeletonModification2DCCDIK::ccdik_joint_set_enable_constraint(int p_joint_ ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); ccdik_data_chain.write[p_joint_idx].enable_constraint = p_constraint; _change_notify(); + +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED } bool SkeletonModification2DCCDIK::ccdik_joint_get_enable_constraint(int p_joint_idx) const { @@ -1211,6 +1285,12 @@ bool SkeletonModification2DCCDIK::ccdik_joint_get_enable_constraint(int p_joint_ void SkeletonModification2DCCDIK::ccdik_joint_set_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::ccdik_joint_get_constraint_angle_min(int p_joint_idx) const { @@ -1221,6 +1301,12 @@ float SkeletonModification2DCCDIK::ccdik_joint_get_constraint_angle_min(int p_jo void SkeletonModification2DCCDIK::ccdik_joint_set_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::ccdik_joint_get_constraint_angle_max(int p_joint_idx) const { @@ -1231,6 +1317,12 @@ float SkeletonModification2DCCDIK::ccdik_joint_get_constraint_angle_max(int p_jo void SkeletonModification2DCCDIK::ccdik_joint_set_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::ccdik_joint_get_constraint_angle_invert(int p_joint_idx) const { @@ -1241,6 +1333,12 @@ bool SkeletonModification2DCCDIK::ccdik_joint_get_constraint_angle_invert(int p_ void SkeletonModification2DCCDIK::ccdik_joint_set_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::ccdik_joint_get_constraint_in_localspace(int p_joint_idx) const { @@ -1248,6 +1346,22 @@ bool SkeletonModification2DCCDIK::ccdik_joint_get_constraint_in_localspace(int p return ccdik_data_chain[p_joint_idx].constraint_in_localspace; } +void SkeletonModification2DCCDIK::ccdik_joint_set_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::ccdik_joint_get_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); @@ -1281,6 +1395,7 @@ SkeletonModification2DCCDIK::SkeletonModification2DCCDIK() { stack = nullptr; is_setup = false; enabled = true; + editor_draw_gizmo = true; } SkeletonModification2DCCDIK::~SkeletonModification2DCCDIK() { @@ -1317,8 +1432,22 @@ bool SkeletonModification2DFABRIK::_set(const StringName &p_path, const Variant } else if (what == "constraint_in_localspace") { fabrik_joint_set_constraint_in_localspace(which, p_value); } + +#ifdef TOOLS_ENABLED + if (what.begins_with("editor_draw_gizmo")) { + fabrik_joint_set_editor_draw_gizmo(which, p_value); + } +#endif // TOOLS_ENABLED + return true; } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + set_editor_draw_gizmo(p_value); + } +#endif // TOOLS_ENABLED + return true; } @@ -1349,8 +1478,22 @@ bool SkeletonModification2DFABRIK::_get(const StringName &p_path, Variant &r_ret } else if (what == "constraint_in_localspace") { r_ret = fabrik_joint_get_constraint_in_localspace(which); } + +#ifdef TOOLS_ENABLED + if (what.begins_with("editor_draw_gizmo")) { + r_ret = fabrik_joint_get_editor_draw_gizmo(which); + } +#endif // TOOLS_ENABLED + return true; } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + r_ret = get_editor_draw_gizmo(); + } +#endif // TOOLS_ENABLED + return true; } @@ -1375,7 +1518,19 @@ void SkeletonModification2DFABRIK::_get_property_list(List *p_list 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 SkeletonModification2DFABRIK::execute(float delta) { @@ -1629,6 +1784,22 @@ void SkeletonModification2DFABRIK::update_target_cache() { } } +void SkeletonModification2DFABRIK::draw_editor_gizmo() { + if (!enabled || !is_setup) { + return; + } + + for (int i = 0; i < fabrik_data_chain.size(); i++) { + if (!fabrik_data_chain[i].editor_draw_gizmo) { + continue; + } + + Bone2D *operation_bone = stack->skeleton->get_bone(fabrik_data_chain[i].bone_idx); + editor_draw_angle_constraints(operation_bone, fabrik_data_chain[i].constraint_angle_min, fabrik_data_chain[i].constraint_angle_max, + fabrik_data_chain[i].enable_constraint, fabrik_data_chain[i].constraint_in_localspace, fabrik_data_chain[i].constraint_angle_invert); + } +} + 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) { @@ -1744,6 +1915,12 @@ void SkeletonModification2DFABRIK::fabrik_joint_set_enable_constraint(int p_join ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); fabrik_data_chain.write[p_joint_idx].enable_constraint = p_constraint; _change_notify(); + +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED } bool SkeletonModification2DFABRIK::fabrik_joint_get_enable_constraint(int p_joint_idx) const { @@ -1754,6 +1931,12 @@ bool SkeletonModification2DFABRIK::fabrik_joint_get_enable_constraint(int p_join void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_min(int p_joint_idx, float p_angle_min) { ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); fabrik_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 SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_min(int p_joint_idx) const { @@ -1764,6 +1947,12 @@ float SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_min(int p_ void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_max(int p_joint_idx, float p_angle_max) { ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); fabrik_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 SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_max(int p_joint_idx) const { @@ -1774,6 +1963,12 @@ float SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_max(int p_ void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_invert(int p_joint_idx, bool p_invert) { ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); fabrik_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 SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_invert(int p_joint_idx) const { @@ -1784,6 +1979,12 @@ bool SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_invert(int void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace) { ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); fabrik_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 SkeletonModification2DFABRIK::fabrik_joint_get_constraint_in_localspace(int p_joint_idx) const { @@ -1791,6 +1992,22 @@ bool SkeletonModification2DFABRIK::fabrik_joint_get_constraint_in_localspace(int return fabrik_data_chain[p_joint_idx].constraint_in_localspace; } +void SkeletonModification2DFABRIK::fabrik_joint_set_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo) { + ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); + fabrik_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 SkeletonModification2DFABRIK::fabrik_joint_get_editor_draw_gizmo(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].editor_draw_gizmo; +} + 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); @@ -1823,6 +2040,7 @@ SkeletonModification2DFABRIK::SkeletonModification2DFABRIK() { stack = nullptr; is_setup = false; enabled = true; + editor_draw_gizmo = true; } SkeletonModification2DFABRIK::~SkeletonModification2DFABRIK() { @@ -2366,6 +2584,7 @@ SkeletonModification2DJiggle::SkeletonModification2DJiggle() { 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() { @@ -2382,32 +2601,18 @@ bool SkeletonModification2DTwoBoneIK::_set(const StringName &p_path, const Varia 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_one_enable_constraint") { - set_joint_one_enable_constraint(p_value); - } else if (path == "joint_one_constraint_angle_min") { - set_joint_one_constraint_angle_min(Math::deg2rad(float(p_value))); - } else if (path == "joint_one_constraint_angle_max") { - set_joint_one_constraint_angle_max(Math::deg2rad(float(p_value))); - } else if (path == "joint_one_constraint_angle_invert") { - set_joint_one_constraint_angle_invert(p_value); - } else if (path == "joint_one_constraint_in_localspace") { - set_joint_one_constraint_in_localspace(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); - } else if (path == "joint_two_enable_constraint") { - set_joint_two_enable_constraint(p_value); - } else if (path == "joint_two_constraint_angle_min") { - set_joint_two_constraint_angle_min(Math::deg2rad(float(p_value))); - } else if (path == "joint_two_constraint_angle_max") { - set_joint_two_constraint_angle_max(Math::deg2rad(float(p_value))); - } else if (path == "joint_two_constraint_angle_invert") { - set_joint_two_constraint_angle_invert(p_value); - } else if (path == "joint_two_constraint_in_localspace") { - set_joint_two_constraint_in_localspace(p_value); } +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + set_editor_draw_gizmo(p_value); + } +#endif // TOOLS_ENABLED + return true; } @@ -2418,55 +2623,33 @@ bool SkeletonModification2DTwoBoneIK::_get(const StringName &p_path, Variant &r_ 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_one_enable_constraint") { - r_ret = get_joint_one_enable_constraint(); - } else if (path == "joint_one_constraint_angle_min") { - r_ret = Math::rad2deg(get_joint_one_constraint_angle_min()); - } else if (path == "joint_one_constraint_angle_max") { - r_ret = Math::rad2deg(get_joint_one_constraint_angle_max()); - } else if (path == "joint_one_constraint_angle_invert") { - r_ret = get_joint_one_constraint_angle_invert(); - } else if (path == "joint_one_constraint_in_localspace") { - r_ret = get_joint_one_constraint_in_localspace(); } 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(); - } else if (path == "joint_two_enable_constraint") { - r_ret = get_joint_two_enable_constraint(); - } else if (path == "joint_two_constraint_angle_min") { - r_ret = Math::rad2deg(get_joint_two_constraint_angle_min()); - } else if (path == "joint_two_constraint_angle_max") { - r_ret = Math::rad2deg(get_joint_two_constraint_angle_max()); - } else if (path == "joint_two_constraint_angle_invert") { - r_ret = get_joint_two_constraint_angle_invert(); - } else if (path == "joint_two_constraint_in_localspace") { - r_ret = get_joint_two_constraint_in_localspace(); } +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + r_ret = get_editor_draw_gizmo(); + } +#endif // TOOLS_ENABLED + return true; } void SkeletonModification2DTwoBoneIK::_get_property_list(List *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::BOOL, "joint_one_enable_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); - if (joint_one_enable_constraint) { - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_one_constraint_angle_min", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_one_constraint_angle_max", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_one_constraint_angle_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_one_constraint_in_localspace", PROPERTY_HINT_NONE, "", 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)); - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_two_enable_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); - if (joint_two_enable_constraint) { - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_two_constraint_angle_min", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_two_constraint_angle_max", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_two_constraint_angle_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_two_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, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); } +#endif // TOOLS_ENABLED } void SkeletonModification2DTwoBoneIK::execute(float delta) { @@ -2540,22 +2723,6 @@ void SkeletonModification2DTwoBoneIK::execute(float delta) { joint_two_bone->set_global_rotation(angle_atan - joint_two_bone->get_bone_angle()); } - // global constrains - if (joint_one_enable_constraint && !joint_one_constraint_in_localspace) { - joint_one_bone->set_global_rotation(clamp_angle(joint_one_bone->get_global_rotation(), joint_one_constraint_angle_min, joint_one_constraint_angle_max, joint_one_constraint_angle_invert)); - } - if (joint_two_enable_constraint && !joint_two_constraint_in_localspace) { - joint_two_bone->set_global_rotation(clamp_angle(joint_two_bone->get_global_rotation(), joint_two_constraint_angle_min, joint_two_constraint_angle_max, joint_two_constraint_angle_invert)); - } - - // local constrains - if (joint_one_enable_constraint && joint_one_constraint_in_localspace) { - joint_one_bone->set_rotation(clamp_angle(joint_one_bone->get_rotation(), joint_one_constraint_angle_min, joint_one_constraint_angle_max, joint_one_constraint_angle_invert)); - } - if (joint_two_enable_constraint && joint_two_constraint_in_localspace) { - joint_two_bone->set_rotation(clamp_angle(joint_two_bone->get_rotation(), joint_two_constraint_angle_min, joint_two_constraint_angle_max, joint_two_constraint_angle_invert)); - } - 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); @@ -2574,6 +2741,35 @@ void SkeletonModification2DTwoBoneIK::setup_modification(SkeletonModificationSta } } +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->get_global_transform().affine_inverse().xform(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 = EditorSettings::get_singleton()->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), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 1.0); + } else { + float angle = (Math_PI * 0.5) + operation_bone_one->get_bone_angle(); + stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 1.0); + } +} + void SkeletonModification2DTwoBoneIK::update_target_cache() { if (!is_setup || !stack) { WARN_PRINT("Cannot update target cache: modification is not properly setup!"); @@ -2683,6 +2879,12 @@ float SkeletonModification2DTwoBoneIK::get_target_minimum_distance() const { void SkeletonModification2DTwoBoneIK::set_flip_bend_direction(bool p_flip_direction) { flip_bend_direction = p_flip_direction; + +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED } bool SkeletonModification2DTwoBoneIK::get_flip_bend_direction() const { @@ -2755,90 +2957,6 @@ int SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx() const { return joint_two_bone_idx; } -// TwoBoneIK property functions - -void SkeletonModification2DTwoBoneIK::set_joint_one_enable_constraint(bool p_constraint) { - joint_one_enable_constraint = p_constraint; - _change_notify(); -} - -bool SkeletonModification2DTwoBoneIK::get_joint_one_enable_constraint() const { - return joint_one_enable_constraint; -} - -void SkeletonModification2DTwoBoneIK::set_joint_one_constraint_angle_min(float p_angle) { - joint_one_constraint_angle_min = p_angle; -} - -float SkeletonModification2DTwoBoneIK::get_joint_one_constraint_angle_min() const { - return joint_one_constraint_angle_min; -} - -void SkeletonModification2DTwoBoneIK::set_joint_one_constraint_angle_max(float p_angle) { - joint_one_constraint_angle_max = p_angle; -} - -float SkeletonModification2DTwoBoneIK::get_joint_one_constraint_angle_max() const { - return joint_one_constraint_angle_max; -} - -void SkeletonModification2DTwoBoneIK::set_joint_one_constraint_angle_invert(bool p_invert) { - joint_one_constraint_angle_invert = p_invert; -} - -bool SkeletonModification2DTwoBoneIK::get_joint_one_constraint_angle_invert() const { - return joint_one_constraint_angle_invert; -} - -void SkeletonModification2DTwoBoneIK::set_joint_one_constraint_in_localspace(bool p_in_localspace) { - joint_one_constraint_in_localspace = p_in_localspace; -} - -bool SkeletonModification2DTwoBoneIK::get_joint_one_constraint_in_localspace() const { - return joint_one_constraint_in_localspace; -} - -void SkeletonModification2DTwoBoneIK::set_joint_two_enable_constraint(bool p_constraint) { - joint_two_enable_constraint = p_constraint; - _change_notify(); -} - -bool SkeletonModification2DTwoBoneIK::get_joint_two_enable_constraint() const { - return joint_two_enable_constraint; -} - -void SkeletonModification2DTwoBoneIK::set_joint_two_constraint_angle_min(float p_angle) { - joint_two_constraint_angle_min = p_angle; -} - -float SkeletonModification2DTwoBoneIK::get_joint_two_constraint_angle_min() const { - return joint_two_constraint_angle_min; -} - -void SkeletonModification2DTwoBoneIK::set_joint_two_constraint_angle_max(float p_angle) { - joint_two_constraint_angle_max = p_angle; -} - -float SkeletonModification2DTwoBoneIK::get_joint_two_constraint_angle_max() const { - return joint_two_constraint_angle_max; -} - -void SkeletonModification2DTwoBoneIK::set_joint_two_constraint_angle_invert(bool p_invert) { - joint_two_constraint_angle_invert = p_invert; -} - -bool SkeletonModification2DTwoBoneIK::get_joint_two_constraint_angle_invert() const { - return joint_two_constraint_angle_invert; -} - -void SkeletonModification2DTwoBoneIK::set_joint_two_constraint_in_localspace(bool p_in_localspace) { - joint_two_constraint_in_localspace = p_in_localspace; -} - -bool SkeletonModification2DTwoBoneIK::get_joint_two_constraint_in_localspace() const { - return joint_two_constraint_in_localspace; -} - 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); @@ -2852,31 +2970,11 @@ void SkeletonModification2DTwoBoneIK::_bind_methods() { 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_one_enable_constraint", "enable_constraint"), &SkeletonModification2DTwoBoneIK::set_joint_one_enable_constraint); - ClassDB::bind_method(D_METHOD("get_joint_one_enable_constraint"), &SkeletonModification2DTwoBoneIK::get_joint_one_enable_constraint); - ClassDB::bind_method(D_METHOD("set_joint_one_constraint_angle_min", "angle"), &SkeletonModification2DTwoBoneIK::set_joint_one_constraint_angle_min); - ClassDB::bind_method(D_METHOD("get_joint_one_constraint_angle_min"), &SkeletonModification2DTwoBoneIK::get_joint_one_constraint_angle_min); - ClassDB::bind_method(D_METHOD("set_joint_one_constraint_angle_max", "angle"), &SkeletonModification2DTwoBoneIK::set_joint_one_constraint_angle_max); - ClassDB::bind_method(D_METHOD("get_joint_one_constraint_angle_max"), &SkeletonModification2DTwoBoneIK::get_joint_one_constraint_angle_max); - ClassDB::bind_method(D_METHOD("set_joint_one_constraint_angle_invert", "invert"), &SkeletonModification2DTwoBoneIK::set_joint_one_constraint_angle_invert); - ClassDB::bind_method(D_METHOD("get_joint_one_constraint_angle_invert"), &SkeletonModification2DTwoBoneIK::get_joint_one_constraint_angle_invert); - ClassDB::bind_method(D_METHOD("set_joint_one_constraint_in_localspace", "in_localspace"), &SkeletonModification2DTwoBoneIK::set_joint_one_constraint_in_localspace); - ClassDB::bind_method(D_METHOD("get_joint_one_constraint_in_localspace"), &SkeletonModification2DTwoBoneIK::get_joint_one_constraint_in_localspace); 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); - ClassDB::bind_method(D_METHOD("set_joint_two_enable_constraint", "enable_constraint"), &SkeletonModification2DTwoBoneIK::set_joint_two_enable_constraint); - ClassDB::bind_method(D_METHOD("get_joint_two_enable_constraint"), &SkeletonModification2DTwoBoneIK::get_joint_two_enable_constraint); - ClassDB::bind_method(D_METHOD("set_joint_two_constraint_angle_min", "angle"), &SkeletonModification2DTwoBoneIK::set_joint_two_constraint_angle_min); - ClassDB::bind_method(D_METHOD("get_joint_two_constraint_angle_min"), &SkeletonModification2DTwoBoneIK::get_joint_two_constraint_angle_min); - ClassDB::bind_method(D_METHOD("set_joint_two_constraint_angle_max", "angle"), &SkeletonModification2DTwoBoneIK::set_joint_two_constraint_angle_max); - ClassDB::bind_method(D_METHOD("get_joint_two_constraint_angle_max"), &SkeletonModification2DTwoBoneIK::get_joint_two_constraint_angle_max); - ClassDB::bind_method(D_METHOD("set_joint_two_constraint_angle_invert", "invert"), &SkeletonModification2DTwoBoneIK::set_joint_two_constraint_angle_invert); - ClassDB::bind_method(D_METHOD("get_joint_two_constraint_angle_invert"), &SkeletonModification2DTwoBoneIK::get_joint_two_constraint_angle_invert); - ClassDB::bind_method(D_METHOD("set_joint_two_constraint_in_localspace", "in_localspace"), &SkeletonModification2DTwoBoneIK::set_joint_two_constraint_in_localspace); - ClassDB::bind_method(D_METHOD("get_joint_two_constraint_in_localspace"), &SkeletonModification2DTwoBoneIK::get_joint_two_constraint_in_localspace); 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_NONE, ""), "set_target_minimum_distance", "get_target_minimum_distance"); @@ -2888,6 +2986,7 @@ SkeletonModification2DTwoBoneIK::SkeletonModification2DTwoBoneIK() { stack = nullptr; is_setup = false; enabled = true; + editor_draw_gizmo = true; } SkeletonModification2DTwoBoneIK::~SkeletonModification2DTwoBoneIK() { @@ -3155,6 +3254,7 @@ SkeletonModification2DPhysicalBones::SkeletonModification2DPhysicalBones() { is_setup = false; physical_bone_chain = Vector(); enabled = true; + editor_draw_gizmo = false; // Nothing to really show in a gizmo right now. } SkeletonModification2DPhysicalBones::~SkeletonModification2DPhysicalBones() { @@ -3170,6 +3270,13 @@ bool SkeletonModification2DStackHolder::_set(const StringName &p_path, const Var if (path == "held_modification_stack") { set_held_modification_stack(p_value); } + +#ifdef TOOLS_ENABLED + if (path == "editor/draw_gizmo") { + set_editor_draw_gizmo(p_value); + } +#endif // TOOLS_ENABLED + return true; } @@ -3179,11 +3286,24 @@ bool SkeletonModification2DStackHolder::_get(const StringName &p_path, Variant & if (path == "held_modification_stack") { r_ret = get_held_modification_stack(); } + +#ifdef TOOLS_ENABLED + if (path == "editor/draw_gizmo") { + r_ret = get_editor_draw_gizmo(); + } +#endif // TOOLS_ENABLED + return true; } void SkeletonModification2DStackHolder::_get_property_list(List *p_list) const { p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } +#endif // TOOLS_ENABLED } void SkeletonModification2DStackHolder::execute(float delta) { @@ -3208,6 +3328,14 @@ void SkeletonModification2DStackHolder::setup_modification(SkeletonModificationS } } +void SkeletonModification2DStackHolder::draw_editor_gizmo() { + if (stack) { + if (held_modification_stack.is_valid()) { + held_modification_stack->draw_editor_gizmos(); + } + } +} + void SkeletonModification2DStackHolder::set_held_modification_stack(Ref p_held_stack) { held_modification_stack = p_held_stack; diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index 97c5ed2d37f0..75eed38391d1 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -39,6 +39,7 @@ class Skeleton2D; class SkeletonModification2D; +class Bone2D; class SkeletonModificationStack2D : public Resource { GDCLASS(SkeletonModificationStack2D, Resource); @@ -102,6 +103,7 @@ class SkeletonModificationStack2D : public Resource { class SkeletonModification2D : public Resource { GDCLASS(SkeletonModification2D, Resource); friend class Skeleton2D; + friend class Bone2D; protected: static void _bind_methods(); @@ -135,6 +137,7 @@ class SkeletonModification2D : public Resource { int get_execution_mode() const; float clamp_angle(float angle, float min_bound, float max_bound, bool invert_clamp = false); + void editor_draw_angle_constraints(Bone2D *operation_bone, float min_bound, float max_bound, bool constraint_enabled, bool constraint_in_localspace, bool constraint_inverted); SkeletonModification2D(); }; @@ -220,6 +223,8 @@ class SkeletonModification2DCCDIK : public SkeletonModification2D { 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_data_chain; @@ -244,6 +249,7 @@ class SkeletonModification2DCCDIK : public SkeletonModification2D { public: void execute(float 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; @@ -270,6 +276,8 @@ class SkeletonModification2DCCDIK : public SkeletonModification2D { bool ccdik_joint_get_constraint_angle_invert(int p_joint_idx) const; void ccdik_joint_set_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace); bool ccdik_joint_get_constraint_in_localspace(int p_joint_idx) const; + void ccdik_joint_set_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo); + bool ccdik_joint_get_editor_draw_gizmo(int p_joint_idx) const; SkeletonModification2DCCDIK(); ~SkeletonModification2DCCDIK(); @@ -296,6 +304,8 @@ class SkeletonModification2DFABRIK : public SkeletonModification2D { float constraint_angle_max = (2.0 * Math_PI); bool constraint_angle_invert = false; bool constraint_in_localspace = true; + + bool editor_draw_gizmo = true; }; Vector fabrik_data_chain; @@ -329,6 +339,7 @@ class SkeletonModification2DFABRIK : public SkeletonModification2D { public: void execute(float 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; @@ -355,6 +366,8 @@ class SkeletonModification2DFABRIK : public SkeletonModification2D { bool fabrik_joint_get_constraint_angle_invert(int p_joint_idx) const; void fabrik_joint_set_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace); bool fabrik_joint_get_constraint_in_localspace(int p_joint_idx) const; + void fabrik_joint_set_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo); + bool fabrik_joint_get_editor_draw_gizmo(int p_joint_idx) const; SkeletonModification2DFABRIK(); ~SkeletonModification2DFABRIK(); @@ -478,20 +491,10 @@ class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { NodePath joint_one_bone2d_node; ObjectID joint_one_bone2d_node_cache; int joint_one_bone_idx = -1; - bool joint_one_enable_constraint = false; - float joint_one_constraint_angle_min = 0; - float joint_one_constraint_angle_max = (2.0 * Math_PI); - bool joint_one_constraint_angle_invert = false; - bool joint_one_constraint_in_localspace = true; NodePath joint_two_bone2d_node; ObjectID joint_two_bone2d_node_cache; int joint_two_bone_idx = -1; - bool joint_two_enable_constraint = false; - float joint_two_constraint_angle_min = 0; - float joint_two_constraint_angle_max = (2.0 * Math_PI); - bool joint_two_constraint_angle_invert = false; - bool joint_two_constraint_in_localspace = true; void update_target_cache(); void update_joint_one_bone2d_cache(); @@ -506,6 +509,7 @@ class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { public: void execute(float 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; @@ -519,31 +523,11 @@ class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { 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_one_enable_constraint(bool p_constraint); - bool get_joint_one_enable_constraint() const; - void set_joint_one_constraint_angle_min(float p_angle); - float get_joint_one_constraint_angle_min() const; - void set_joint_one_constraint_angle_max(float p_angle); - float get_joint_one_constraint_angle_max() const; - void set_joint_one_constraint_angle_invert(bool p_invert); - bool get_joint_one_constraint_angle_invert() const; - void set_joint_one_constraint_in_localspace(bool p_in_localspace); - bool get_joint_one_constraint_in_localspace() 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; - void set_joint_two_enable_constraint(bool p_constraint); - bool get_joint_two_enable_constraint() const; - void set_joint_two_constraint_angle_min(float p_angle); - float get_joint_two_constraint_angle_min() const; - void set_joint_two_constraint_angle_max(float p_angle); - float get_joint_two_constraint_angle_max() const; - void set_joint_two_constraint_angle_invert(bool p_invert); - bool get_joint_two_constraint_angle_invert() const; - void set_joint_two_constraint_in_localspace(bool p_in_localspace); - bool get_joint_two_constraint_in_localspace() const; SkeletonModification2DTwoBoneIK(); ~SkeletonModification2DTwoBoneIK(); @@ -612,6 +596,7 @@ class SkeletonModification2DStackHolder : public SkeletonModification2D { void execute(float delta) override; void setup_modification(SkeletonModificationStack2D *p_stack) override; + void draw_editor_gizmo() override; void set_held_modification_stack(Ref p_held_stack); Ref get_held_modification_stack() const; From 9e2e8a8a6e954fd30ec132d24303a7055b853d67 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Thu, 17 Sep 2020 11:21:08 -0400 Subject: [PATCH 18/34] Changes: * SkeletonModification2DTwoBoneIK: Fixed issue where the joints would disappear if the target was too close to the origin. The issue turned out to be a NaN result from the acos function call. This is now properly handled and the issue should not occur anymore. * SkeletonModification2DTwoBoneIK: Changed the default minimum distance to 0 instead of 6, since the target being directly at the root does not seem to cause an issue anymore. * SkeletonModification2DTwoBoneIK: Added a maximum distance property. When at 0, the IK algorithm doesn't take the maximum distance into account. * Thanks to pwab on GitHub for the suggestion to add this feature! * SkeletonModification2D: Fixed crash that occured rarely due to the gizmo drawing code. * SkeletonModification2D: Changed how the angle constraint code is drawn, especially for inverted angles. * Now inverted angles should be drawn correctly regardless of the inputted angles. * Angle constraint gizmos should, hopefully, draw the correct range 100% of the time. * SkeletonModification2D: Exposed the draw_editor_gizmo function to GDScript. * Now GDScript-powered modifications can optionally draw their gizmos in the Godot editor using the same API * SkeletonModification2D: Exposed the get_editor_draw_gizmo and set_editor_draw_gizmo functions to GDScript * This was needed for GDScript-powered modifications * Added documentation for the newly exposed functions * Added documentation for the maximum distance property in TwoBoneIK. * Also, now all of the old angle constraint documentation in TwoBoneIK has been removed. * Changed how get_modification_stack is defined. Now it returns a Ref and that allows the CI tests to run successfully. --- doc/classes/SkeletonModification2D.xml | 24 +++ .../SkeletonModification2DTwoBoneIK.xml | 173 +----------------- scene/resources/skeleton_modification_2d.cpp | 47 ++++- scene/resources/skeleton_modification_2d.h | 7 +- 4 files changed, 73 insertions(+), 178 deletions(-) diff --git a/doc/classes/SkeletonModification2D.xml b/doc/classes/SkeletonModification2D.xml index 96bf762fd7f3..a27662846d78 100644 --- a/doc/classes/SkeletonModification2D.xml +++ b/doc/classes/SkeletonModification2D.xml @@ -25,6 +25,14 @@ Takes a angle and clamps it so it is within the passed-in [code]min[/code] and [code]max[/code] range. [code]invert[/code] will inversely clamp the angle, clamping it to the range outside of the given bounds. + + + + + Used for drawing [b]editor-only[/b] modification gizmos. This function will only be called in the Godot editor and can be overriden to draw custom gizmos. + [b]Note[/b]: You will need to use the Skeleton2D from [method SkeletonModificationStack2D.get_skeleton] and it's draw functions, as the [SkeletonModification2D] resource cannot draw on its own. + + @@ -34,6 +42,13 @@ Executes the given modification. This is where the modification performs whatever function it is designed to do. + + + + + Returns whether this modification will call [method draw_editor_gizmo] in the Godot editor to draw modification-specific gizmos. + + @@ -48,6 +63,15 @@ Returns the [SkeletonModificationStack2D] that this modification is bound to. Through the modification stack, you can access the Skeleton3D the modification is operating on. + + + + + + + Sets whether this modification will call [method draw_editor_gizmo] in the Godot editor to draw modification-specific gizmos. + + diff --git a/doc/classes/SkeletonModification2DTwoBoneIK.xml b/doc/classes/SkeletonModification2DTwoBoneIK.xml index 09eb36c4dbc7..10a122e9b4e9 100644 --- a/doc/classes/SkeletonModification2DTwoBoneIK.xml +++ b/doc/classes/SkeletonModification2DTwoBoneIK.xml @@ -24,42 +24,6 @@ Returns the index of the [Bone2D] node that is being used as the first bone in the TwoBoneIK modification. - - - - - Returns whether the constraints applied to the first bone in the TwoBoneIK modification is inverted. - - - - - - - Returns the maximum angle the first bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. - - - - - - - Returns the minimum angle the first bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. - - - - - - - Returns whether the constraints applied to the first bone in the TwoBoneIK modification are calculated in localspace. - - - - - - - Returns whether the constraints applied to the first bone in the TwoBoneIK modification are enabled. - [b]Note[/b]: Constraints are purely visual and not taken into account by the modification. This means that the modification may not find a solution when constraints are enabled, even if a solution may be possible. - - @@ -74,42 +38,6 @@ Returns the index of the [Bone2D] node that is being used as the second bone in the TwoBoneIK modification. - - - - - Returns whether the constraints applied to the second bone in the TwoBoneIK modification is inverted. - - - - - - - Returns the maximum angle the second bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. - - - - - - - Returns the minimum angle the second bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. - - - - - - - Returns whether the constraints applied to the second bone in the TwoBoneIK modification are calculated in localspace. - - - - - - - Returns whether the constraints applied to the second bone in the TwoBoneIK modification are enabled. - [b]Note[/b]: Constraints are purely visual and not taken into account by the modification. This means that the modification may not find a solution when constraints are enabled, even if a solution may be possible. - - @@ -128,53 +56,6 @@ Sets the index of the [Bone2D] node that is being used as the first bone in the TwoBoneIK modification. - - - - - - - Sets whether the constraints applied to the first bone in the TwoBoneIK modification is inverted. - An inverted joint constraint only constraints the joint to the angles [i]outside of[/i] the inputted minimum and maximum angles. For this reason, it is referred to as an inverted joint constraint, as it constraints the joint to the outside of the inputted values. - - - - - - - - - Sets the maximum angle the first bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. - - - - - - - - - Sets the minimum angle the first bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. - - - - - - - - - Sets whether the constraints applied to the first bone in the TwoBoneIK modification are calculated in localspace. - - - - - - - - - Sets whether the constraints applied to the first bone in the TwoBoneIK modification are enabled. - [b]Note[/b]: Constraints are purely visual and not taken into account by the modification. This means that the modification may not find a solution when constraints are enabled, even if a solution may be possible. - - @@ -193,60 +74,16 @@ Sets the index of the [Bone2D] node that is being used as the second bone in the TwoBoneIK modification. - - - - - - - Sets whether the constraints applied to the second bone in the TwoBoneIK modification is inverted. - An inverted joint constraint only constraints the joint to the angles [i]outside of[/i] the inputted minimum and maximum angles. For this reason, it is referred to as an inverted joint constraint, as it constraints the joint to the outside of the inputted values. - - - - - - - - - Sets the maximum angle the second bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. - - - - - - - - - Sets the minimum angle the second bone in the TwoBoneIK modification take rotate to, if constraints are enabled for this bone. - - - - - - - - - Sets whether the constraints applied to the second bone in the TwoBoneIK modification are calculated in localspace. - - - - - - - - - Sets whether the constraints applied to the second bone in the TwoBoneIK modification are enabled. - [b]Note[/b]: Constraints are purely visual and not taken into account by the modification. This means that the modification may not find a solution when constraints are enabled, even if a solution may be possible. - - When [code]true[/code], the bones in the modification will blend outward as opposed to inwards when contracting. When [code]false[/code], the bones will bend inwards when contracting. - - The minimum distance the target can be at. If the target is closer than this distance, the modification will solve as if it's at this minimum distance. + + The maximum distance the target can be at. If the target is farther than this distance, the modification will solve as if it's at this maximum distance. When set to [code]0[/code], the modification will solve without distance constraints. + + + The minimum distance the target can be at. If the target is closer than this distance, the modification will solve as if it's at this minimum distance. When set to [code]0[/code], the modification will solve without distance constraints. The NodePath to the node that is the target for the TwoBoneIK modification. This node is what the modification will use when bending the [Bone2D] nodes. diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index d0dc9d469fba..86bd28b29bff 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -315,6 +315,11 @@ void SkeletonModification2D::setup_modification(SkeletonModificationStack2D *p_s } void SkeletonModification2D::draw_editor_gizmo() { + if (get_script_instance()) { + if (get_script_instance()->has_method("draw_editor_gizmo")) { + get_script_instance()->call("draw_editor_gizmo"); + } + } } bool SkeletonModification2D::_print_execution_error(bool p_condition, String p_message) { @@ -436,7 +441,7 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *operation_bon if (constraint_inverted) { stack->skeleton->draw_arc(Vector2(0, 0), operation_bone->get_length(), - (Math_PI * 2) - arc_angle_max, arc_angle_min, 32, bone_ik_color, 1.0); + arc_angle_min + (Math_PI * 2), arc_angle_max, 32, bone_ik_color, 1.0); } else { stack->skeleton->draw_arc(Vector2(0, 0), operation_bone->get_length(), arc_angle_min, arc_angle_max, 32, bone_ik_color, 1.0); @@ -451,7 +456,7 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *operation_bon } } -SkeletonModificationStack2D *SkeletonModification2D::get_modification_stack() { +Ref SkeletonModification2D::get_modification_stack() { return stack; } @@ -474,7 +479,9 @@ int SkeletonModification2D::get_execution_mode() const { void SkeletonModification2D::set_editor_draw_gizmo(bool p_draw_gizmo) { editor_draw_gizmo = p_draw_gizmo; #ifdef TOOLS_ENABLED - stack->set_editor_gizmos_dirty(true); + if (is_setup) { + stack->set_editor_gizmos_dirty(true); + } #endif // TOOLS_ENABLED } @@ -485,6 +492,7 @@ bool SkeletonModification2D::get_editor_draw_gizmo() const { void SkeletonModification2D::_bind_methods() { BIND_VMETHOD(MethodInfo("execute", PropertyInfo(Variant::FLOAT, "delta"))); BIND_VMETHOD(MethodInfo("setup_modification", PropertyInfo(Variant::OBJECT, "modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D"))); + BIND_VMETHOD(MethodInfo("draw_editor_gizmo")); ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification2D::set_enabled); ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification2D::get_enabled); @@ -494,6 +502,8 @@ void SkeletonModification2D::_bind_methods() { 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"); @@ -2701,10 +2711,15 @@ void SkeletonModification2DTwoBoneIK::execute(float delta) { 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; - } else if (joint_one_to_target < target_minimum_distance) { - joint_one_to_target = target_minimum_distance; } if (!override_angles_due_to_out_of_range) { @@ -2716,8 +2731,12 @@ void SkeletonModification2DTwoBoneIK::execute(float delta) { angle_1 = -angle_1; } - 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()); + if (isnan(angle_0) || isnan(angle_1)) { + // We cannot solve for this angle! Do nothing to avoid setting the rotation (and scale) to NaN. + } else { + joint_one_bone->set_global_rotation(angle_atan - angle_0 - joint_one_bone->get_bone_angle()); + joint_two_bone->set_rotation(-Math_PI - angle_1 - joint_two_bone->get_bone_angle() + joint_one_bone->get_bone_angle()); + } } else { joint_one_bone->set_global_rotation(angle_atan - joint_one_bone->get_bone_angle()); joint_two_bone->set_global_rotation(angle_atan - joint_two_bone->get_bone_angle()); @@ -2877,6 +2896,15 @@ 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; @@ -2963,6 +2991,8 @@ void SkeletonModification2DTwoBoneIK::_bind_methods() { 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); @@ -2977,7 +3007,8 @@ void SkeletonModification2DTwoBoneIK::_bind_methods() { 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_NONE, ""), "set_target_minimum_distance", "get_target_minimum_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_minimum_distance", PROPERTY_HINT_RANGE, "0, 100000000, 0.01"), "set_target_minimum_distance", "get_target_minimum_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_maximum_distance", PROPERTY_HINT_NONE, "0, 100000000, 0.01"), "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("", ""); } diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index 75eed38391d1..bb6b1aa88486 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -129,7 +129,7 @@ class SkeletonModification2D : public Resource { void set_enabled(bool p_enabled); bool get_enabled(); - SkeletonModificationStack2D *get_modification_stack(); + Ref get_modification_stack(); void set_is_setup(bool p_setup); bool get_is_setup() const; @@ -485,7 +485,8 @@ class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { private: NodePath target_node; ObjectID target_node_cache; - float target_minimum_distance = 6; + float target_minimum_distance = 0; + float target_maximum_distance = 0; bool flip_bend_direction = false; NodePath joint_one_bone2d_node; @@ -516,6 +517,8 @@ class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { 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; From 677290c4ec14e4572e989db3b9b0b30b03b9a49b Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Wed, 23 Sep 2020 15:58:41 -0400 Subject: [PATCH 19/34] Changes: * SkeletonModification2D: _print_execution_error now does not print a message when the modification is not setup. * All 2D SkeletonModification resources: Now uses _print_execution_error when checking if setup, so there are not a ton of "errors" when loading just because the resource has not been setup by the Skeleton2D node at start. * SkeletonModification2DCCDIK, SkeletonModification2DFABRIK, SkeletonModification2DJiggle and SkeletonModification2DPhysicalBones: Joint functions now use "get_joint_property" naming convention in getters and setters, instead of "joint_get_property". * Class reference has been updated to reflect these changes. --- doc/classes/SkeletonModification2DCCDIK.xml | 30 +- doc/classes/SkeletonModification2DFABRIK.xml | 34 +- doc/classes/SkeletonModification2DJiggle.xml | 32 +- .../SkeletonModification2DPhysicalBones.xml | 4 +- scene/resources/skeleton_modification_2d.cpp | 360 +++++++++--------- scene/resources/skeleton_modification_2d.h | 118 +++--- 6 files changed, 291 insertions(+), 287 deletions(-) diff --git a/doc/classes/SkeletonModification2DCCDIK.xml b/doc/classes/SkeletonModification2DCCDIK.xml index 617d50bb0f65..014d366a429e 100644 --- a/doc/classes/SkeletonModification2DCCDIK.xml +++ b/doc/classes/SkeletonModification2DCCDIK.xml @@ -12,7 +12,7 @@ - + @@ -21,7 +21,7 @@ Returns the [Bone2D] node assigned to the CCDIK joint at [code]joint_idx[/code]. - + @@ -30,16 +30,16 @@ Returns the index of the [Bone2D] node assigned to the CCDIK joint at [code]joint_idx[/code]. - + - Returns whether the CCDIK joint at [code]joint_idx[/code] uses an inverted joint constraint. See [method ccdik_joint_set_constraint_angle_invert] for details. + Returns whether the CCDIK joint at [code]joint_idx[/code] uses an inverted joint constraint. See [method set_ccdik_joint_constraint_angle_invert] for details. - + @@ -48,7 +48,7 @@ Returns the maximum angle constraint for the joint at [code]joint_idx[/code]. - + @@ -57,7 +57,7 @@ Returns the minimum angle constraint for the joint at [code]joint_idx[/code]. - + @@ -66,7 +66,7 @@ Returns whether angle constraints on the CCDIK joint at [code]joint_idx[/code] are enabled. - + @@ -75,7 +75,7 @@ Returns whether the joint at [code]joint_idx[/code] is set to rotate from the joint, [code]true[/code], or to rotate from the tip, [code]false[/code]. The default is to rotate from the tip. - + @@ -86,7 +86,7 @@ Sets the [Bone2D] node assigned to the CCDIK joint at [code]joint_idx[/code]. - + @@ -97,7 +97,7 @@ Sets the bone index, [code]bone_index[/code], of the CCDIK joint at [code]joint_idx[/code]. When possible, this will also update the [code]bone2d_node[/code] of the CCDIK joint based on data provided by the linked skeleton. - + @@ -109,7 +109,7 @@ An inverted joint constraint only constraints the CCDIK joint to the angles [i]outside of[/i] the inputted minimum and maximum angles. For this reason, it is referred to as an inverted joint constraint, as it constraints the joint to the outside of the inputted values. - + @@ -120,7 +120,7 @@ Sets the maximum angle constraint for the joint at [code]joint_idx[/code]. - + @@ -131,7 +131,7 @@ Sets the minimum angle constraint for the joint at [code]joint_idx[/code]. - + @@ -142,7 +142,7 @@ Determines whether angle constraints on the CCDIK joint at [code]joint_idx[/code] are enabled. When [code]true[/code], constraints will be enabled and taken into account when solving. - + diff --git a/doc/classes/SkeletonModification2DFABRIK.xml b/doc/classes/SkeletonModification2DFABRIK.xml index 48b0c8fd0ca5..23f85a373a5d 100644 --- a/doc/classes/SkeletonModification2DFABRIK.xml +++ b/doc/classes/SkeletonModification2DFABRIK.xml @@ -13,7 +13,7 @@ - + @@ -22,7 +22,7 @@ Returns the [Bone2D] node assigned to the FABRIK joint at [code]joint_idx[/code]. - + @@ -31,16 +31,16 @@ Returns the index of the [Bone2D] node assigned to the FABRIK joint at [code]joint_idx[/code]. - + - Returns whether the FABRIK joint at [code]joint_idx[/code] uses an inverted joint constraint. See [method fabrik_joint_set_constraint_angle_invert] for details. + Returns whether the FABRIK joint at [code]joint_idx[/code] uses an inverted joint constraint. See [method set_fabrik_joint_constraint_angle_invert] for details. - + @@ -49,7 +49,7 @@ Returns the maximum angle constraint for the joint at [code]joint_idx[/code]. - + @@ -58,7 +58,7 @@ Returns the minimum angle constraint for the joint at [code]joint_idx[/code]. - + @@ -67,7 +67,7 @@ Returns whether angle constraints on the FABRIK joint at [code]joint_idx[/code] are enabled. - + @@ -76,7 +76,7 @@ Returns the magnet position vector for the joint at [code]joint_idx[/code]. - + @@ -85,7 +85,7 @@ Returns whether the joint is using the target's rotation rather than allowing FABRIK to rotate the joint. This option only applies to the tip/final joint in the chain. - + @@ -96,7 +96,7 @@ Sets the [Bone2D] node assigned to the FABRIK joint at [code]joint_idx[/code]. - + @@ -107,7 +107,7 @@ Sets the bone index, [code]bone_index[/code], of the FABRIK joint at [code]joint_idx[/code]. When possible, this will also update the [code]bone2d_node[/code] of the FABRIK joint based on data provided by the linked skeleton. - + @@ -119,7 +119,7 @@ An inverted joint constraint only constraints the FABRIK joint to the angles [i]outside of[/i] the inputted minimum and maximum angles. For this reason, it is referred to as an inverted joint constraint, as it constraints the joint to the outside of the inputted values. - + @@ -130,7 +130,7 @@ Sets the maximum angle constraint for the joint at [code]joint_idx[/code]. - + @@ -141,7 +141,7 @@ Sets the minimum angle constraint for the joint at [code]joint_idx[/code]. - + @@ -152,7 +152,7 @@ Determines whether angle constraints on the FABRIK joint at [code]joint_idx[/code] are enabled. When [code]true[/code], constraints will be enabled and taken into account when solving. - + @@ -163,7 +163,7 @@ Sets the magnet position vector for the joint at [code]joint_idx[/code]. - + diff --git a/doc/classes/SkeletonModification2DJiggle.xml b/doc/classes/SkeletonModification2DJiggle.xml index eef41cdca186..15ba2e4b8e0a 100644 --- a/doc/classes/SkeletonModification2DJiggle.xml +++ b/doc/classes/SkeletonModification2DJiggle.xml @@ -25,7 +25,7 @@ Returns whether the jiggle modifier is taking physics colliders into account when solving. - + @@ -34,7 +34,7 @@ Returns the [Bone2D] node assigned to the Jiggle joint at [code]joint_idx[/code]. - + @@ -43,7 +43,7 @@ Returns the index of the [Bone2D] node assigned to the Jiggle joint at [code]joint_idx[/code]. - + @@ -52,7 +52,7 @@ Returns the amount of damping of the Jiggle joint at [code]joint_idx[/code]. - + @@ -61,7 +61,7 @@ Returns a [Vector2] representing the amount of gravity the Jiggle joint at [code]joint_idx[/code] is influenced by. - + @@ -70,7 +70,7 @@ Returns the amount of mass of the jiggle joint at [code]joint_idx[/code]. - + @@ -79,7 +79,7 @@ Returns a boolean that indiciates whether the joint at [code]joint_idx[/code] is overriding the default Jiggle joint data defined in the modification. - + @@ -88,7 +88,7 @@ Returns the stiffness of the Jiggle joint at [code]joint_idx[/code]. - + @@ -97,7 +97,7 @@ Returns a boolean that indiciates whether the joint at [code]joint_idx[/code] is using gravity or not. - + @@ -108,7 +108,7 @@ Sets the [Bone2D] node assigned to the Jiggle joint at [code]joint_idx[/code]. - + @@ -119,7 +119,7 @@ Sets the bone index, [code]bone_index[/code], of the Jiggle joint at [code]joint_idx[/code]. When possible, this will also update the [code]bone2d_node[/code] of the Jiggle joint based on data provided by the linked skeleton. - + @@ -130,7 +130,7 @@ Sets the amount of dampening of the Jiggle joint at [code]joint_idx[/code]. - + @@ -141,7 +141,7 @@ Sets the gravity vector of the Jiggle joint at [code]joint_idx[/code]. - + @@ -152,7 +152,7 @@ Sets the of mass of the Jiggle joint at [code]joint_idx[/code]. - + @@ -163,7 +163,7 @@ Sets whether the Jiggle joint at [code]joint_idx[/code] should override the default Jiggle joint settings. Setting this to [code]true[/code] will make the joint use its own settings rather than the default ones attached to the modification. - + @@ -174,7 +174,7 @@ Sets the of stiffness of the Jiggle joint at [code]joint_idx[/code]. - + diff --git a/doc/classes/SkeletonModification2DPhysicalBones.xml b/doc/classes/SkeletonModification2DPhysicalBones.xml index d2f1cd696b9d..8ccf10e49776 100644 --- a/doc/classes/SkeletonModification2DPhysicalBones.xml +++ b/doc/classes/SkeletonModification2DPhysicalBones.xml @@ -16,7 +16,7 @@ Empties the list of [PhysicalBone2D] nodes and populates it will all [PhysicalBone2D] nodes that are children of the [Skeleton2D]. - + @@ -25,7 +25,7 @@ Returns the [PhysicalBone2D] node at [code]joint_idx[/code]. - + diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 86bd28b29bff..b0dbda2bcd3a 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -323,6 +323,10 @@ void SkeletonModification2D::draw_editor_gizmo() { } bool SkeletonModification2D::_print_execution_error(bool p_condition, String p_message) { + if (!is_setup) { + return p_condition; + } + if (p_condition && !execution_error_found) { ERR_PRINT(p_message); execution_error_found = true; @@ -678,7 +682,7 @@ void SkeletonModification2DLookAt::draw_editor_gizmo() { void SkeletonModification2DLookAt::update_bone2d_cache() { if (!is_setup || !stack) { - WARN_PRINT("Cannot update Bone2D cache: modification is not properly setup!"); + _print_execution_error(true, "Cannot update Bone2D cache: modification is not properly setup!"); return; } @@ -743,7 +747,7 @@ void SkeletonModification2DLookAt::set_bone_index(int p_bone_idx) { void SkeletonModification2DLookAt::update_target_cache() { if (!is_setup || !stack) { - WARN_PRINT("Cannot update target cache: modification is not properly setup!"); + _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); return; } @@ -903,26 +907,26 @@ bool SkeletonModification2DCCDIK::_set(const StringName &p_path, const Variant & ERR_FAIL_INDEX_V(which, ccdik_data_chain.size(), false); if (what == "bone2d_node") { - ccdik_joint_set_bone2d_node(which, p_value); + set_ccdik_joint_bone2d_node(which, p_value); } else if (what == "bone_index") { - ccdik_joint_set_bone_index(which, p_value); + set_ccdik_joint_bone_index(which, p_value); } else if (what == "rotate_from_joint") { - ccdik_joint_set_rotate_from_joint(which, p_value); + set_ccdik_joint_rotate_from_joint(which, p_value); } else if (what == "enable_constraint") { - ccdik_joint_set_enable_constraint(which, p_value); + set_ccdik_joint_enable_constraint(which, p_value); } else if (what == "constraint_angle_min") { - ccdik_joint_set_constraint_angle_min(which, Math::deg2rad(float(p_value))); + set_ccdik_joint_constraint_angle_min(which, Math::deg2rad(float(p_value))); } else if (what == "constraint_angle_max") { - ccdik_joint_set_constraint_angle_max(which, Math::deg2rad(float(p_value))); + set_ccdik_joint_constraint_angle_max(which, Math::deg2rad(float(p_value))); } else if (what == "constraint_angle_invert") { - ccdik_joint_set_constraint_angle_invert(which, p_value); + set_ccdik_joint_constraint_angle_invert(which, p_value); } else if (what == "constraint_in_localspace") { - ccdik_joint_set_constraint_in_localspace(which, p_value); + set_ccdik_joint_constraint_in_localspace(which, p_value); } #ifdef TOOLS_ENABLED if (what.begins_with("editor_draw_gizmo")) { - ccdik_joint_set_editor_draw_gizmo(which, p_value); + set_ccdik_joint_editor_draw_gizmo(which, p_value); } #endif // TOOLS_ENABLED @@ -947,26 +951,26 @@ bool SkeletonModification2DCCDIK::_get(const StringName &p_path, Variant &r_ret) ERR_FAIL_INDEX_V(which, ccdik_data_chain.size(), false); if (what == "bone2d_node") { - r_ret = ccdik_joint_get_bone2d_node(which); + r_ret = get_ccdik_joint_bone2d_node(which); } else if (what == "bone_index") { - r_ret = ccdik_joint_get_bone_index(which); + r_ret = get_ccdik_joint_bone_index(which); } else if (what == "rotate_from_joint") { - r_ret = ccdik_joint_get_rotate_from_joint(which); + r_ret = get_ccdik_joint_rotate_from_joint(which); } else if (what == "enable_constraint") { - r_ret = ccdik_joint_get_enable_constraint(which); + r_ret = get_ccdik_joint_enable_constraint(which); } else if (what == "constraint_angle_min") { - r_ret = Math::rad2deg(ccdik_joint_get_constraint_angle_min(which)); + r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_min(which)); } else if (what == "constraint_angle_max") { - r_ret = Math::rad2deg(ccdik_joint_get_constraint_angle_max(which)); + r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_max(which)); } else if (what == "constraint_angle_invert") { - r_ret = ccdik_joint_get_constraint_angle_invert(which); + r_ret = get_ccdik_joint_constraint_angle_invert(which); } else if (what == "constraint_in_localspace") { - r_ret = ccdik_joint_get_constraint_in_localspace(which); + r_ret = get_ccdik_joint_constraint_in_localspace(which); } #ifdef TOOLS_ENABLED if (what.begins_with("editor_draw_gizmo")) { - r_ret = ccdik_joint_get_editor_draw_gizmo(which); + r_ret = get_ccdik_joint_editor_draw_gizmo(which); } #endif // TOOLS_ENABLED @@ -1121,7 +1125,7 @@ void SkeletonModification2DCCDIK::draw_editor_gizmo() { void SkeletonModification2DCCDIK::update_target_cache() { if (!is_setup || !stack) { - WARN_PRINT("Cannot update target cache: modification is not properly setup!"); + _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); return; } @@ -1144,7 +1148,7 @@ void SkeletonModification2DCCDIK::update_target_cache() { void SkeletonModification2DCCDIK::update_tip_cache() { if (!is_setup || !stack) { - WARN_PRINT("Cannot update tip cache: modification is not properly setup!"); + _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!"); return; } @@ -1168,7 +1172,7 @@ void SkeletonModification2DCCDIK::update_tip_cache() { void SkeletonModification2DCCDIK::ccdik_joint_update_bone2d_cache(int p_joint_idx) { ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "Cannot update bone2d cache: joint index out of range!"); if (!is_setup || !stack) { - WARN_PRINT("Cannot update CCDIK Bone2D cache: modification is not properly setup!"); + _print_execution_error(true, "Cannot update CCDIK Bone2D cache: modification is not properly setup!"); return; } @@ -1224,7 +1228,7 @@ int SkeletonModification2DCCDIK::get_ccdik_data_chain_length() { return ccdik_data_chain.size(); } -void SkeletonModification2DCCDIK::ccdik_joint_set_bone2d_node(int p_joint_idx, const NodePath &p_target_node) { +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); @@ -1232,12 +1236,12 @@ void SkeletonModification2DCCDIK::ccdik_joint_set_bone2d_node(int p_joint_idx, c _change_notify(); } -NodePath SkeletonModification2DCCDIK::ccdik_joint_get_bone2d_node(int p_joint_idx) const { +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::ccdik_joint_set_bone_index(int p_joint_idx, int p_bone_idx) { +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!"); @@ -1260,22 +1264,22 @@ void SkeletonModification2DCCDIK::ccdik_joint_set_bone_index(int p_joint_idx, in _change_notify(); } -int SkeletonModification2DCCDIK::ccdik_joint_get_bone_index(int p_joint_idx) const { +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::ccdik_joint_set_rotate_from_joint(int p_joint_idx, bool p_rotate_from_joint) { +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::ccdik_joint_get_rotate_from_joint(int p_joint_idx) const { +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::ccdik_joint_set_enable_constraint(int p_joint_idx, bool p_constraint) { +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; _change_notify(); @@ -1287,12 +1291,12 @@ void SkeletonModification2DCCDIK::ccdik_joint_set_enable_constraint(int p_joint_ #endif // TOOLS_ENABLED } -bool SkeletonModification2DCCDIK::ccdik_joint_get_enable_constraint(int p_joint_idx) const { +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::ccdik_joint_set_constraint_angle_min(int p_joint_idx, float p_angle_min) { +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; @@ -1303,12 +1307,12 @@ void SkeletonModification2DCCDIK::ccdik_joint_set_constraint_angle_min(int p_joi #endif // TOOLS_ENABLED } -float SkeletonModification2DCCDIK::ccdik_joint_get_constraint_angle_min(int p_joint_idx) const { +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::ccdik_joint_set_constraint_angle_max(int p_joint_idx, float p_angle_max) { +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; @@ -1319,12 +1323,12 @@ void SkeletonModification2DCCDIK::ccdik_joint_set_constraint_angle_max(int p_joi #endif // TOOLS_ENABLED } -float SkeletonModification2DCCDIK::ccdik_joint_get_constraint_angle_max(int p_joint_idx) const { +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::ccdik_joint_set_constraint_angle_invert(int p_joint_idx, bool p_invert) { +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; @@ -1335,12 +1339,12 @@ void SkeletonModification2DCCDIK::ccdik_joint_set_constraint_angle_invert(int p_ #endif // TOOLS_ENABLED } -bool SkeletonModification2DCCDIK::ccdik_joint_get_constraint_angle_invert(int p_joint_idx) const { +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::ccdik_joint_set_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace) { +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; @@ -1351,12 +1355,12 @@ void SkeletonModification2DCCDIK::ccdik_joint_set_constraint_in_localspace(int p #endif // TOOLS_ENABLED } -bool SkeletonModification2DCCDIK::ccdik_joint_get_constraint_in_localspace(int p_joint_idx) const { +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::ccdik_joint_set_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo) { +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; @@ -1367,7 +1371,7 @@ void SkeletonModification2DCCDIK::ccdik_joint_set_editor_draw_gizmo(int p_joint_ #endif // TOOLS_ENABLED } -bool SkeletonModification2DCCDIK::ccdik_joint_get_editor_draw_gizmo(int p_joint_idx) const { +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; } @@ -1381,20 +1385,20 @@ void SkeletonModification2DCCDIK::_bind_methods() { 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("ccdik_joint_set_bone2d_node", "joint_idx", "bone2d_nodepath"), &SkeletonModification2DCCDIK::ccdik_joint_set_bone2d_node); - ClassDB::bind_method(D_METHOD("ccdik_joint_get_bone2d_node", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_bone2d_node); - ClassDB::bind_method(D_METHOD("ccdik_joint_set_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DCCDIK::ccdik_joint_set_bone_index); - ClassDB::bind_method(D_METHOD("ccdik_joint_get_bone_index", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_bone_index); - ClassDB::bind_method(D_METHOD("ccdik_joint_set_rotate_from_joint", "joint_idx", "rotate_from_joint"), &SkeletonModification2DCCDIK::ccdik_joint_set_rotate_from_joint); - ClassDB::bind_method(D_METHOD("ccdik_joint_get_rotate_from_joint", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_rotate_from_joint); - ClassDB::bind_method(D_METHOD("ccdik_joint_set_enable_constraint", "joint_idx", "enable_constraint"), &SkeletonModification2DCCDIK::ccdik_joint_set_enable_constraint); - ClassDB::bind_method(D_METHOD("ccdik_joint_get_enable_constraint", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_enable_constraint); - ClassDB::bind_method(D_METHOD("ccdik_joint_set_constraint_angle_min", "joint_idx", "angle_min"), &SkeletonModification2DCCDIK::ccdik_joint_set_constraint_angle_min); - ClassDB::bind_method(D_METHOD("ccdik_joint_get_constraint_angle_min", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_constraint_angle_min); - ClassDB::bind_method(D_METHOD("ccdik_joint_set_constraint_angle_max", "joint_idx", "angle_max"), &SkeletonModification2DCCDIK::ccdik_joint_set_constraint_angle_max); - ClassDB::bind_method(D_METHOD("ccdik_joint_get_constraint_angle_max", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_constraint_angle_max); - ClassDB::bind_method(D_METHOD("ccdik_joint_set_constraint_angle_invert", "joint_idx", "invert"), &SkeletonModification2DCCDIK::ccdik_joint_set_constraint_angle_invert); - ClassDB::bind_method(D_METHOD("ccdik_joint_get_constraint_angle_invert", "joint_idx"), &SkeletonModification2DCCDIK::ccdik_joint_get_constraint_angle_invert); + 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"); @@ -1424,28 +1428,28 @@ bool SkeletonModification2DFABRIK::_set(const StringName &p_path, const Variant ERR_FAIL_INDEX_V(which, fabrik_data_chain.size(), false); if (what == "bone2d_node") { - fabrik_joint_set_bone2d_node(which, p_value); + set_fabrik_joint_bone2d_node(which, p_value); } else if (what == "bone_index") { - fabrik_joint_set_bone_index(which, p_value); + set_fabrik_joint_bone_index(which, p_value); } else if (what == "magnet_position") { - fabrik_joint_set_magnet_position(which, p_value); + set_fabrik_joint_magnet_position(which, p_value); } else if (what == "use_target_rotation") { - fabrik_joint_set_use_target_rotation(which, p_value); + set_fabrik_joint_use_target_rotation(which, p_value); } else if (what == "enable_constraint") { - fabrik_joint_set_enable_constraint(which, p_value); + set_fabrik_joint_enable_constraint(which, p_value); } else if (what == "constraint_angle_min") { - fabrik_joint_set_constraint_angle_min(which, Math::deg2rad(float(p_value))); + set_fabrik_joint_constraint_angle_min(which, Math::deg2rad(float(p_value))); } else if (what == "constraint_angle_max") { - fabrik_joint_set_constraint_angle_max(which, Math::deg2rad(float(p_value))); + set_fabrik_joint_constraint_angle_max(which, Math::deg2rad(float(p_value))); } else if (what == "constraint_angle_invert") { - fabrik_joint_set_constraint_angle_invert(which, p_value); + set_fabrik_joint_constraint_angle_invert(which, p_value); } else if (what == "constraint_in_localspace") { - fabrik_joint_set_constraint_in_localspace(which, p_value); + set_fabrik_joint_constraint_in_localspace(which, p_value); } #ifdef TOOLS_ENABLED if (what.begins_with("editor_draw_gizmo")) { - fabrik_joint_set_editor_draw_gizmo(which, p_value); + set_fabrik_joint_editor_draw_gizmo(which, p_value); } #endif // TOOLS_ENABLED @@ -1470,28 +1474,28 @@ bool SkeletonModification2DFABRIK::_get(const StringName &p_path, Variant &r_ret ERR_FAIL_INDEX_V(which, fabrik_data_chain.size(), false); if (what == "bone2d_node") { - r_ret = fabrik_joint_get_bone2d_node(which); + r_ret = get_fabrik_joint_bone2d_node(which); } else if (what == "bone_index") { - r_ret = fabrik_joint_get_bone_index(which); + r_ret = get_fabrik_joint_bone_index(which); } else if (what == "magnet_position") { - r_ret = fabrik_joint_get_magnet_position(which); + r_ret = get_fabrik_joint_magnet_position(which); } else if (what == "use_target_rotation") { - r_ret = fabrik_joint_get_use_target_rotation(which); + r_ret = get_fabrik_joint_use_target_rotation(which); } else if (what == "enable_constraint") { - r_ret = fabrik_joint_get_enable_constraint(which); + r_ret = get_fabrik_joint_enable_constraint(which); } else if (what == "constraint_angle_min") { - r_ret = Math::rad2deg(fabrik_joint_get_constraint_angle_min(which)); + r_ret = Math::rad2deg(get_fabrik_joint_constraint_angle_min(which)); } else if (what == "constraint_angle_max") { - r_ret = Math::rad2deg(fabrik_joint_get_constraint_angle_max(which)); + r_ret = Math::rad2deg(get_fabrik_joint_constraint_angle_max(which)); } else if (what == "constraint_angle_invert") { - r_ret = fabrik_joint_get_constraint_angle_invert(which); + r_ret = get_fabrik_joint_constraint_angle_invert(which); } else if (what == "constraint_in_localspace") { - r_ret = fabrik_joint_get_constraint_in_localspace(which); + r_ret = get_fabrik_joint_constraint_in_localspace(which); } #ifdef TOOLS_ENABLED if (what.begins_with("editor_draw_gizmo")) { - r_ret = fabrik_joint_get_editor_draw_gizmo(which); + r_ret = get_fabrik_joint_editor_draw_gizmo(which); } #endif // TOOLS_ENABLED @@ -1773,7 +1777,7 @@ void SkeletonModification2DFABRIK::setup_modification(SkeletonModificationStack2 void SkeletonModification2DFABRIK::update_target_cache() { if (!is_setup || !stack) { - WARN_PRINT("Cannot update target cache: modification is not properly setup!"); + _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); return; } @@ -1813,7 +1817,7 @@ void SkeletonModification2DFABRIK::draw_editor_gizmo() { 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) { - WARN_PRINT("Cannot update FABRIK Bone2D cache: modification is not properly setup!"); + _print_execution_error(true, "Cannot update FABRIK Bone2D cache: modification is not properly setup!"); return; } @@ -1860,7 +1864,7 @@ int SkeletonModification2DFABRIK::get_fabrik_data_chain_length() { return fabrik_data_chain.size(); } -void SkeletonModification2DFABRIK::fabrik_joint_set_bone2d_node(int p_joint_idx, const NodePath &p_target_node) { +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); @@ -1868,12 +1872,12 @@ void SkeletonModification2DFABRIK::fabrik_joint_set_bone2d_node(int p_joint_idx, _change_notify(); } -NodePath SkeletonModification2DFABRIK::fabrik_joint_get_bone2d_node(int p_joint_idx) const { +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::fabrik_joint_set_bone_index(int p_joint_idx, int p_bone_idx) { +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!"); @@ -1896,32 +1900,32 @@ void SkeletonModification2DFABRIK::fabrik_joint_set_bone_index(int p_joint_idx, _change_notify(); } -int SkeletonModification2DFABRIK::fabrik_joint_get_bone_index(int p_joint_idx) const { +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::fabrik_joint_set_magnet_position(int p_joint_idx, Vector2 p_magnet_position) { +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::fabrik_joint_get_magnet_position(int p_joint_idx) const { +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::fabrik_joint_set_use_target_rotation(int p_joint_idx, bool p_use_target_rotation) { +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::fabrik_joint_get_use_target_rotation(int p_joint_idx) const { +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::fabrik_joint_set_enable_constraint(int p_joint_idx, bool p_constraint) { +void SkeletonModification2DFABRIK::set_fabrik_joint_enable_constraint(int p_joint_idx, bool p_constraint) { ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); fabrik_data_chain.write[p_joint_idx].enable_constraint = p_constraint; _change_notify(); @@ -1933,12 +1937,12 @@ void SkeletonModification2DFABRIK::fabrik_joint_set_enable_constraint(int p_join #endif // TOOLS_ENABLED } -bool SkeletonModification2DFABRIK::fabrik_joint_get_enable_constraint(int p_joint_idx) const { +bool SkeletonModification2DFABRIK::get_fabrik_joint_enable_constraint(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].enable_constraint; } -void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_min(int p_joint_idx, float p_angle_min) { +void SkeletonModification2DFABRIK::set_fabrik_joint_constraint_angle_min(int p_joint_idx, float p_angle_min) { ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); fabrik_data_chain.write[p_joint_idx].constraint_angle_min = p_angle_min; @@ -1949,12 +1953,12 @@ void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_min(int p_j #endif // TOOLS_ENABLED } -float SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_min(int p_joint_idx) const { +float SkeletonModification2DFABRIK::get_fabrik_joint_constraint_angle_min(int p_joint_idx) const { ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), 0.0, "FABRIK joint out of range!"); return fabrik_data_chain[p_joint_idx].constraint_angle_min; } -void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_max(int p_joint_idx, float p_angle_max) { +void SkeletonModification2DFABRIK::set_fabrik_joint_constraint_angle_max(int p_joint_idx, float p_angle_max) { ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); fabrik_data_chain.write[p_joint_idx].constraint_angle_max = p_angle_max; @@ -1965,12 +1969,12 @@ void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_max(int p_j #endif // TOOLS_ENABLED } -float SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_max(int p_joint_idx) const { +float SkeletonModification2DFABRIK::get_fabrik_joint_constraint_angle_max(int p_joint_idx) const { ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), 0.0, "FABRIK joint out of range!"); return fabrik_data_chain[p_joint_idx].constraint_angle_max; } -void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_invert(int p_joint_idx, bool p_invert) { +void SkeletonModification2DFABRIK::set_fabrik_joint_constraint_angle_invert(int p_joint_idx, bool p_invert) { ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); fabrik_data_chain.write[p_joint_idx].constraint_angle_invert = p_invert; @@ -1981,12 +1985,12 @@ void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_invert(int #endif // TOOLS_ENABLED } -bool SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_invert(int p_joint_idx) const { +bool SkeletonModification2DFABRIK::get_fabrik_joint_constraint_angle_invert(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].constraint_angle_invert; } -void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace) { +void SkeletonModification2DFABRIK::set_fabrik_joint_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace) { ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); fabrik_data_chain.write[p_joint_idx].constraint_in_localspace = p_constraint_in_localspace; @@ -1997,12 +2001,12 @@ void SkeletonModification2DFABRIK::fabrik_joint_set_constraint_in_localspace(int #endif // TOOLS_ENABLED } -bool SkeletonModification2DFABRIK::fabrik_joint_get_constraint_in_localspace(int p_joint_idx) const { +bool SkeletonModification2DFABRIK::get_fabrik_joint_constraint_in_localspace(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].constraint_in_localspace; } -void SkeletonModification2DFABRIK::fabrik_joint_set_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo) { +void SkeletonModification2DFABRIK::set_fabrik_joint_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo) { ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); fabrik_data_chain.write[p_joint_idx].editor_draw_gizmo = p_draw_gizmo; @@ -2013,7 +2017,7 @@ void SkeletonModification2DFABRIK::fabrik_joint_set_editor_draw_gizmo(int p_join #endif // TOOLS_ENABLED } -bool SkeletonModification2DFABRIK::fabrik_joint_get_editor_draw_gizmo(int p_joint_idx) const { +bool SkeletonModification2DFABRIK::get_fabrik_joint_editor_draw_gizmo(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].editor_draw_gizmo; } @@ -2025,22 +2029,22 @@ void SkeletonModification2DFABRIK::_bind_methods() { 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("fabrik_joint_set_bone2d_node", "joint_idx", "bone2d_nodepath"), &SkeletonModification2DFABRIK::fabrik_joint_set_bone2d_node); - ClassDB::bind_method(D_METHOD("fabrik_joint_get_bone2d_node", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_bone2d_node); - ClassDB::bind_method(D_METHOD("fabrik_joint_set_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DFABRIK::fabrik_joint_set_bone_index); - ClassDB::bind_method(D_METHOD("fabrik_joint_get_bone_index", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_bone_index); - ClassDB::bind_method(D_METHOD("fabrik_joint_set_magnet_position", "joint_idx", "magnet_position"), &SkeletonModification2DFABRIK::fabrik_joint_set_magnet_position); - ClassDB::bind_method(D_METHOD("fabrik_joint_get_magnet_position", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_magnet_position); - ClassDB::bind_method(D_METHOD("fabrik_joint_set_use_target_rotation", "joint_idx", "use_target_rotation"), &SkeletonModification2DFABRIK::fabrik_joint_set_use_target_rotation); - ClassDB::bind_method(D_METHOD("fabrik_joint_get_use_target_rotation", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_use_target_rotation); - ClassDB::bind_method(D_METHOD("fabrik_joint_set_enable_constraint", "joint_idx", "enable_constraint"), &SkeletonModification2DFABRIK::fabrik_joint_set_enable_constraint); - ClassDB::bind_method(D_METHOD("fabrik_joint_get_enable_constraint", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_enable_constraint); - ClassDB::bind_method(D_METHOD("fabrik_joint_set_constraint_angle_min", "joint_idx", "angle_min"), &SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_min); - ClassDB::bind_method(D_METHOD("fabrik_joint_get_constraint_angle_min", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_min); - ClassDB::bind_method(D_METHOD("fabrik_joint_set_constraint_angle_max", "joint_idx", "angle_max"), &SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_max); - ClassDB::bind_method(D_METHOD("fabrik_joint_get_constraint_angle_max", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_max); - ClassDB::bind_method(D_METHOD("fabrik_joint_set_constraint_angle_invert", "joint_idx", "invert"), &SkeletonModification2DFABRIK::fabrik_joint_set_constraint_angle_invert); - ClassDB::bind_method(D_METHOD("fabrik_joint_get_constraint_angle_invert", "joint_idx"), &SkeletonModification2DFABRIK::fabrik_joint_get_constraint_angle_invert); + 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); + ClassDB::bind_method(D_METHOD("set_fabrik_joint_enable_constraint", "joint_idx", "enable_constraint"), &SkeletonModification2DFABRIK::set_fabrik_joint_enable_constraint); + ClassDB::bind_method(D_METHOD("get_fabrik_joint_enable_constraint", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_enable_constraint); + ClassDB::bind_method(D_METHOD("set_fabrik_joint_constraint_angle_min", "joint_idx", "angle_min"), &SkeletonModification2DFABRIK::set_fabrik_joint_constraint_angle_min); + ClassDB::bind_method(D_METHOD("get_fabrik_joint_constraint_angle_min", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_constraint_angle_min); + ClassDB::bind_method(D_METHOD("set_fabrik_joint_constraint_angle_max", "joint_idx", "angle_max"), &SkeletonModification2DFABRIK::set_fabrik_joint_constraint_angle_max); + ClassDB::bind_method(D_METHOD("get_fabrik_joint_constraint_angle_max", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_constraint_angle_max); + ClassDB::bind_method(D_METHOD("set_fabrik_joint_constraint_angle_invert", "joint_idx", "invert"), &SkeletonModification2DFABRIK::set_fabrik_joint_constraint_angle_invert); + ClassDB::bind_method(D_METHOD("get_fabrik_joint_constraint_angle_invert", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_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::INT, "fabrik_data_chain_length", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_fabrik_data_chain_length", "get_fabrik_data_chain_length"); @@ -2069,21 +2073,21 @@ bool SkeletonModification2DJiggle::_set(const StringName &p_path, const Variant ERR_FAIL_INDEX_V(which, jiggle_data_chain.size(), false); if (what == "bone2d_node") { - jiggle_joint_set_bone2d_node(which, p_value); + set_jiggle_joint_bone2d_node(which, p_value); } else if (what == "bone_index") { - jiggle_joint_set_bone_index(which, p_value); + set_jiggle_joint_bone_index(which, p_value); } else if (what == "override_defaults") { - jiggle_joint_set_override(which, p_value); + set_jiggle_joint_override(which, p_value); } else if (what == "stiffness") { - jiggle_joint_set_stiffness(which, p_value); + set_jiggle_joint_stiffness(which, p_value); } else if (what == "mass") { - jiggle_joint_set_mass(which, p_value); + set_jiggle_joint_mass(which, p_value); } else if (what == "damping") { - jiggle_joint_set_damping(which, p_value); + set_jiggle_joint_damping(which, p_value); } else if (what == "use_gravity") { - jiggle_joint_set_use_gravity(which, p_value); + set_jiggle_joint_use_gravity(which, p_value); } else if (what == "gravity") { - jiggle_joint_set_gravity(which, p_value); + set_jiggle_joint_gravity(which, p_value); } return true; } else { @@ -2105,21 +2109,21 @@ bool SkeletonModification2DJiggle::_get(const StringName &p_path, Variant &r_ret ERR_FAIL_INDEX_V(which, jiggle_data_chain.size(), false); if (what == "bone2d_node") { - r_ret = jiggle_joint_get_bone2d_node(which); + r_ret = get_jiggle_joint_bone2d_node(which); } else if (what == "bone_index") { - r_ret = jiggle_joint_get_bone_index(which); + r_ret = get_jiggle_joint_bone_index(which); } else if (what == "override_defaults") { - r_ret = jiggle_joint_get_override(which); + r_ret = get_jiggle_joint_override(which); } else if (what == "stiffness") { - r_ret = jiggle_joint_get_stiffness(which); + r_ret = get_jiggle_joint_stiffness(which); } else if (what == "mass") { - r_ret = jiggle_joint_get_mass(which); + r_ret = get_jiggle_joint_mass(which); } else if (what == "damping") { - r_ret = jiggle_joint_get_damping(which); + r_ret = get_jiggle_joint_damping(which); } else if (what == "use_gravity") { - r_ret = jiggle_joint_get_use_gravity(which); + r_ret = get_jiggle_joint_use_gravity(which); } else if (what == "gravity") { - r_ret = jiggle_joint_get_gravity(which); + r_ret = get_jiggle_joint_gravity(which); } return true; } else { @@ -2254,11 +2258,11 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D void SkeletonModification2DJiggle::_update_jiggle_joint_data() { for (int i = 0; i < jiggle_data_chain.size(); i++) { if (!jiggle_data_chain[i].override_defaults) { - jiggle_joint_set_stiffness(i, stiffness); - jiggle_joint_set_mass(i, mass); - jiggle_joint_set_damping(i, damping); - jiggle_joint_set_use_gravity(i, use_gravity); - jiggle_joint_set_gravity(i, gravity); + 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); } } } @@ -2286,7 +2290,7 @@ void SkeletonModification2DJiggle::setup_modification(SkeletonModificationStack2 void SkeletonModification2DJiggle::update_target_cache() { if (!is_setup || !stack) { - WARN_PRINT("Cannot update target cache: modification is not properly setup!"); + _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); return; } @@ -2310,7 +2314,7 @@ void SkeletonModification2DJiggle::update_target_cache() { 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) { - WARN_PRINT("Cannot update Jiggle " + itos(p_joint_idx) + " Bone2D cache: modification is not properly setup!"); + _print_execution_error(true, "Cannot update Jiggle " + itos(p_joint_idx) + " Bone2D cache: modification is not properly setup!"); return; } @@ -2425,7 +2429,7 @@ void SkeletonModification2DJiggle::set_jiggle_data_chain_length(int p_length) { _change_notify(); } -void SkeletonModification2DJiggle::jiggle_joint_set_bone2d_node(int p_joint_idx, const NodePath &p_target_node) { +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); @@ -2433,12 +2437,12 @@ void SkeletonModification2DJiggle::jiggle_joint_set_bone2d_node(int p_joint_idx, _change_notify(); } -NodePath SkeletonModification2DJiggle::jiggle_joint_get_bone2d_node(int p_joint_idx) const { +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::jiggle_joint_set_bone_index(int p_joint_idx, int p_bone_idx) { +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!"); @@ -2461,73 +2465,73 @@ void SkeletonModification2DJiggle::jiggle_joint_set_bone_index(int p_joint_idx, _change_notify(); } -int SkeletonModification2DJiggle::jiggle_joint_get_bone_index(int p_joint_idx) const { +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::jiggle_joint_set_override(int joint_idx, bool p_override) { +void SkeletonModification2DJiggle::set_jiggle_joint_override(int joint_idx, bool p_override) { ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); jiggle_data_chain.write[joint_idx].override_defaults = p_override; _update_jiggle_joint_data(); _change_notify(); } -bool SkeletonModification2DJiggle::jiggle_joint_get_override(int joint_idx) const { +bool SkeletonModification2DJiggle::get_jiggle_joint_override(int joint_idx) const { ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), false); return jiggle_data_chain[joint_idx].override_defaults; } -void SkeletonModification2DJiggle::jiggle_joint_set_stiffness(int joint_idx, float p_stiffness) { +void SkeletonModification2DJiggle::set_jiggle_joint_stiffness(int joint_idx, float p_stiffness) { ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!"); ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); jiggle_data_chain.write[joint_idx].stiffness = p_stiffness; } -float SkeletonModification2DJiggle::jiggle_joint_get_stiffness(int joint_idx) const { +float SkeletonModification2DJiggle::get_jiggle_joint_stiffness(int joint_idx) const { ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); return jiggle_data_chain[joint_idx].stiffness; } -void SkeletonModification2DJiggle::jiggle_joint_set_mass(int joint_idx, float p_mass) { +void SkeletonModification2DJiggle::set_jiggle_joint_mass(int joint_idx, float p_mass) { ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!"); ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); jiggle_data_chain.write[joint_idx].mass = p_mass; } -float SkeletonModification2DJiggle::jiggle_joint_get_mass(int joint_idx) const { +float SkeletonModification2DJiggle::get_jiggle_joint_mass(int joint_idx) const { ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); return jiggle_data_chain[joint_idx].mass; } -void SkeletonModification2DJiggle::jiggle_joint_set_damping(int joint_idx, float p_damping) { +void SkeletonModification2DJiggle::set_jiggle_joint_damping(int joint_idx, float p_damping) { ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!"); ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); jiggle_data_chain.write[joint_idx].damping = p_damping; } -float SkeletonModification2DJiggle::jiggle_joint_get_damping(int joint_idx) const { +float SkeletonModification2DJiggle::get_jiggle_joint_damping(int joint_idx) const { ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); return jiggle_data_chain[joint_idx].damping; } -void SkeletonModification2DJiggle::jiggle_joint_set_use_gravity(int joint_idx, bool p_use_gravity) { +void SkeletonModification2DJiggle::set_jiggle_joint_use_gravity(int joint_idx, bool p_use_gravity) { ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); jiggle_data_chain.write[joint_idx].use_gravity = p_use_gravity; _change_notify(); } -bool SkeletonModification2DJiggle::jiggle_joint_get_use_gravity(int joint_idx) const { +bool SkeletonModification2DJiggle::get_jiggle_joint_use_gravity(int joint_idx) const { ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), false); return jiggle_data_chain[joint_idx].use_gravity; } -void SkeletonModification2DJiggle::jiggle_joint_set_gravity(int joint_idx, Vector2 p_gravity) { +void SkeletonModification2DJiggle::set_jiggle_joint_gravity(int joint_idx, Vector2 p_gravity) { ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); jiggle_data_chain.write[joint_idx].gravity = p_gravity; } -Vector2 SkeletonModification2DJiggle::jiggle_joint_get_gravity(int joint_idx) const { +Vector2 SkeletonModification2DJiggle::get_jiggle_joint_gravity(int joint_idx) const { ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), Vector2(0, 0)); return jiggle_data_chain[joint_idx].gravity; } @@ -2556,22 +2560,22 @@ void SkeletonModification2DJiggle::_bind_methods() { ClassDB::bind_method(D_METHOD("get_collision_mask"), &SkeletonModification2DJiggle::get_collision_mask); // Jiggle joint data functions - ClassDB::bind_method(D_METHOD("jiggle_joint_set_bone2d_node", "joint_idx", "bone2d_node"), &SkeletonModification2DJiggle::jiggle_joint_set_bone2d_node); - ClassDB::bind_method(D_METHOD("jiggle_joint_get_bone2d_node", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_bone2d_node); - ClassDB::bind_method(D_METHOD("jiggle_joint_set_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DJiggle::jiggle_joint_set_bone_index); - ClassDB::bind_method(D_METHOD("jiggle_joint_get_bone_index", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_bone_index); - ClassDB::bind_method(D_METHOD("jiggle_joint_set_override", "joint_idx", "override"), &SkeletonModification2DJiggle::jiggle_joint_set_override); - ClassDB::bind_method(D_METHOD("jiggle_joint_get_override", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_override); - ClassDB::bind_method(D_METHOD("jiggle_joint_set_stiffness", "joint_idx", "stiffness"), &SkeletonModification2DJiggle::jiggle_joint_set_stiffness); - ClassDB::bind_method(D_METHOD("jiggle_joint_get_stiffness", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_stiffness); - ClassDB::bind_method(D_METHOD("jiggle_joint_set_mass", "joint_idx", "mass"), &SkeletonModification2DJiggle::jiggle_joint_set_mass); - ClassDB::bind_method(D_METHOD("jiggle_joint_get_mass", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_mass); - ClassDB::bind_method(D_METHOD("jiggle_joint_set_damping", "joint_idx", "damping"), &SkeletonModification2DJiggle::jiggle_joint_set_damping); - ClassDB::bind_method(D_METHOD("jiggle_joint_get_damping", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_damping); - ClassDB::bind_method(D_METHOD("jiggle_joint_set_use_gravity", "joint_idx", "use_gravity"), &SkeletonModification2DJiggle::jiggle_joint_set_use_gravity); - ClassDB::bind_method(D_METHOD("jiggle_joint_get_use_gravity", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_use_gravity); - ClassDB::bind_method(D_METHOD("jiggle_joint_set_gravity", "joint_idx", "gravity"), &SkeletonModification2DJiggle::jiggle_joint_set_gravity); - ClassDB::bind_method(D_METHOD("jiggle_joint_get_gravity", "joint_idx"), &SkeletonModification2DJiggle::jiggle_joint_get_gravity); + 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"); @@ -2791,7 +2795,7 @@ void SkeletonModification2DTwoBoneIK::draw_editor_gizmo() { void SkeletonModification2DTwoBoneIK::update_target_cache() { if (!is_setup || !stack) { - WARN_PRINT("Cannot update target cache: modification is not properly setup!"); + _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); return; } @@ -2814,7 +2818,7 @@ void SkeletonModification2DTwoBoneIK::update_target_cache() { void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { if (!is_setup || !stack) { - WARN_PRINT("Cannot update update joint one Bone2D cache: modification is not properly setup!"); + _print_execution_error(true, "Cannot update joint one Bone2D cache: modification is not properly setup!"); return; } @@ -2844,7 +2848,7 @@ void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { void SkeletonModification2DTwoBoneIK::update_joint_two_bone2d_cache() { if (!is_setup || !stack) { - WARN_PRINT("Cannot update update joint two Bone2D cache: modification is not properly setup!"); + _print_execution_error(true, "Cannot update joint two Bone2D cache: modification is not properly setup!"); return; } @@ -3049,7 +3053,7 @@ bool SkeletonModification2DPhysicalBones::_set(const StringName &p_path, const V ERR_FAIL_INDEX_V(which, physical_bone_chain.size(), false); if (what == "nodepath") { - physical_bone_set_node(which, p_value); + set_physical_bone_node(which, p_value); } return true; } @@ -3073,7 +3077,7 @@ bool SkeletonModification2DPhysicalBones::_get(const StringName &p_path, Variant ERR_FAIL_INDEX_V(which, physical_bone_chain.size(), false); if (what == "nodepath") { - r_ret = physical_bone_get_node(which); + r_ret = get_physical_bone_node(which); } return true; } @@ -3150,7 +3154,7 @@ void SkeletonModification2DPhysicalBones::setup_modification(SkeletonModificatio 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) { - WARN_PRINT("Cannot update PhysicalBone2D cache: modification is not properly setup!"); + _print_execution_error(true, "Cannot update PhysicalBone2D cache: modification is not properly setup!"); return; } @@ -3255,13 +3259,13 @@ void SkeletonModification2DPhysicalBones::_update_simulation_state() { } } -void SkeletonModification2DPhysicalBones::physical_bone_set_node(int p_joint_idx, const NodePath &p_nodepath) { +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::physical_bone_get_node(int p_joint_idx) const { +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; } @@ -3270,8 +3274,8 @@ 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("physical_bone_set_node", "joint_idx", "physicalbone2d_node"), &SkeletonModification2DPhysicalBones::physical_bone_set_node); - ClassDB::bind_method(D_METHOD("physical_bone_get_node", "joint_idx"), &SkeletonModification2DPhysicalBones::physical_bone_get_node); + 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())); diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index bb6b1aa88486..d606d4f8288a 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -259,25 +259,25 @@ class SkeletonModification2DCCDIK : public SkeletonModification2D { int get_ccdik_data_chain_length(); void set_ccdik_data_chain_length(int p_new_length); - void ccdik_joint_set_bone2d_node(int p_joint_idx, const NodePath &p_target_node); - NodePath ccdik_joint_get_bone2d_node(int p_joint_idx) const; - void ccdik_joint_set_bone_index(int p_joint_idx, int p_bone_idx); - int ccdik_joint_get_bone_index(int p_joint_idx) const; - - void ccdik_joint_set_rotate_from_joint(int p_joint_idx, bool p_rotate_from_joint); - bool ccdik_joint_get_rotate_from_joint(int p_joint_idx) const; - void ccdik_joint_set_enable_constraint(int p_joint_idx, bool p_constraint); - bool ccdik_joint_get_enable_constraint(int p_joint_idx) const; - void ccdik_joint_set_constraint_angle_min(int p_joint_idx, float p_angle_min); - float ccdik_joint_get_constraint_angle_min(int p_joint_idx) const; - void ccdik_joint_set_constraint_angle_max(int p_joint_idx, float p_angle_max); - float ccdik_joint_get_constraint_angle_max(int p_joint_idx) const; - void ccdik_joint_set_constraint_angle_invert(int p_joint_idx, bool p_invert); - bool ccdik_joint_get_constraint_angle_invert(int p_joint_idx) const; - void ccdik_joint_set_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace); - bool ccdik_joint_get_constraint_in_localspace(int p_joint_idx) const; - void ccdik_joint_set_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo); - bool ccdik_joint_get_editor_draw_gizmo(int p_joint_idx) const; + 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(); @@ -347,27 +347,27 @@ class SkeletonModification2DFABRIK : public SkeletonModification2D { int get_fabrik_data_chain_length(); void set_fabrik_data_chain_length(int p_new_length); - void fabrik_joint_set_bone2d_node(int p_joint_idx, const NodePath &p_target_node); - NodePath fabrik_joint_get_bone2d_node(int p_joint_idx) const; - void fabrik_joint_set_bone_index(int p_joint_idx, int p_bone_idx); - int fabrik_joint_get_bone_index(int p_joint_idx) const; - - void fabrik_joint_set_magnet_position(int p_joint_idx, Vector2 p_magnet_position); - Vector2 fabrik_joint_get_magnet_position(int p_joint_idx) const; - void fabrik_joint_set_use_target_rotation(int p_joint_idx, bool p_use_target_rotation); - bool fabrik_joint_get_use_target_rotation(int p_joint_idx) const; - void fabrik_joint_set_enable_constraint(int p_joint_idx, bool p_constraint); - bool fabrik_joint_get_enable_constraint(int p_joint_idx) const; - void fabrik_joint_set_constraint_angle_min(int p_joint_idx, float p_angle_min); - float fabrik_joint_get_constraint_angle_min(int p_joint_idx) const; - void fabrik_joint_set_constraint_angle_max(int p_joint_idx, float p_angle_max); - float fabrik_joint_get_constraint_angle_max(int p_joint_idx) const; - void fabrik_joint_set_constraint_angle_invert(int p_joint_idx, bool p_invert); - bool fabrik_joint_get_constraint_angle_invert(int p_joint_idx) const; - void fabrik_joint_set_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace); - bool fabrik_joint_get_constraint_in_localspace(int p_joint_idx) const; - void fabrik_joint_set_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo); - bool fabrik_joint_get_editor_draw_gizmo(int p_joint_idx) const; + 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; + void set_fabrik_joint_enable_constraint(int p_joint_idx, bool p_constraint); + bool get_fabrik_joint_enable_constraint(int p_joint_idx) const; + void set_fabrik_joint_constraint_angle_min(int p_joint_idx, float p_angle_min); + float get_fabrik_joint_constraint_angle_min(int p_joint_idx) const; + void set_fabrik_joint_constraint_angle_max(int p_joint_idx, float p_angle_max); + float get_fabrik_joint_constraint_angle_max(int p_joint_idx) const; + void set_fabrik_joint_constraint_angle_invert(int p_joint_idx, bool p_invert); + bool get_fabrik_joint_constraint_angle_invert(int p_joint_idx) const; + void set_fabrik_joint_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace); + bool get_fabrik_joint_constraint_in_localspace(int p_joint_idx) const; + void set_fabrik_joint_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo); + bool get_fabrik_joint_editor_draw_gizmo(int p_joint_idx) const; SkeletonModification2DFABRIK(); ~SkeletonModification2DFABRIK(); @@ -453,23 +453,23 @@ class SkeletonModification2DJiggle : public SkeletonModification2D { int get_jiggle_data_chain_length(); void set_jiggle_data_chain_length(int p_new_length); - void jiggle_joint_set_bone2d_node(int p_joint_idx, const NodePath &p_target_node); - NodePath jiggle_joint_get_bone2d_node(int p_joint_idx) const; - void jiggle_joint_set_bone_index(int p_joint_idx, int p_bone_idx); - int jiggle_joint_get_bone_index(int p_joint_idx) const; - - void jiggle_joint_set_override(int p_joint_idx, bool p_override); - bool jiggle_joint_get_override(int p_joint_idx) const; - void jiggle_joint_set_stiffness(int p_joint_idx, float p_stiffness); - float jiggle_joint_get_stiffness(int p_joint_idx) const; - void jiggle_joint_set_mass(int p_joint_idx, float p_mass); - float jiggle_joint_get_mass(int p_joint_idx) const; - void jiggle_joint_set_damping(int p_joint_idx, float p_damping); - float jiggle_joint_get_damping(int p_joint_idx) const; - void jiggle_joint_set_use_gravity(int p_joint_idx, bool p_use_gravity); - bool jiggle_joint_get_use_gravity(int p_joint_idx) const; - void jiggle_joint_set_gravity(int p_joint_idx, Vector2 p_gravity); - Vector2 jiggle_joint_get_gravity(int p_joint_idx) const; + 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(); @@ -570,8 +570,8 @@ class SkeletonModification2DPhysicalBones : public SkeletonModification2D { int get_physical_bone_chain_length(); void set_physical_bone_chain_length(int p_new_length); - void physical_bone_set_node(int p_joint_idx, const NodePath &p_path); - NodePath physical_bone_get_node(int p_joint_idx) const; + 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 &p_bones); From 970ccb473d3e50660b91df7804655ba4b4921068 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Sun, 11 Oct 2020 11:56:12 -0400 Subject: [PATCH 20/34] Changes: * TwoBoneIK: Added a gizmo drawing option for showing the minimum and maximum distances visually. --- scene/resources/skeleton_modification_2d.cpp | 38 ++++++++++++++++++-- scene/resources/skeleton_modification_2d.h | 9 +++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index b0dbda2bcd3a..243b8776babd 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -2624,6 +2624,8 @@ bool SkeletonModification2DTwoBoneIK::_set(const StringName &p_path, const Varia #ifdef TOOLS_ENABLED 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 @@ -2646,6 +2648,8 @@ bool SkeletonModification2DTwoBoneIK::_get(const StringName &p_path, Variant &r_ #ifdef TOOLS_ENABLED 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 @@ -2662,6 +2666,7 @@ void SkeletonModification2DTwoBoneIK::_get_property_list(List *p_l #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 } @@ -2786,11 +2791,30 @@ void SkeletonModification2DTwoBoneIK::draw_editor_gizmo() { if (flip_bend_direction) { float angle = -(Math_PI * 0.5) + operation_bone_one->get_bone_angle(); - stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 1.0); + stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); } else { float angle = (Math_PI * 0.5) + operation_bone_one->get_bone_angle(); - stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 1.0); + stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); } + +#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 = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + target_direction = operation_bone_one->get_global_position().direction_to(target->get_global_position()); + } + + stack->skeleton->draw_circle(target_direction * target_minimum_distance, 8, bone_ik_color); + stack->skeleton->draw_circle(target_direction * target_maximum_distance, 8, bone_ik_color); + stack->skeleton->draw_line(target_direction * target_minimum_distance, target_direction * target_maximum_distance, bone_ik_color, 2.0); + } + } + } +#endif // TOOLS_ENABLED } void SkeletonModification2DTwoBoneIK::update_target_cache() { @@ -2989,6 +3013,16 @@ 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); diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index d606d4f8288a..80fc26e78958 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -497,6 +497,10 @@ class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { 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(); @@ -532,6 +536,11 @@ class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { 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(); }; From 93e1648e12858ebffd73294fad5eea1fc70689b8 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Mon, 4 Jan 2021 18:32:52 -0500 Subject: [PATCH 21/34] Changes after code review! (Huge thanks to AndreaCatania for the review!) Changes: * The 2D physical bone notification function has been optimized. * Physics turn on and off more predictably * The internal physics process only runs when needed, saving performance. * Adjusted the _position_at_bone2d function based on feedback. * The Bone2D node notification function has been optimized. --- scene/2d/physical_bone_2d.cpp | 64 ++++++++++++++++------------------- scene/2d/skeleton_2d.cpp | 12 +++---- 2 files changed, 35 insertions(+), 41 deletions(-) diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index 14692ef08db8..6c287d08a328 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -31,27 +31,6 @@ #include "physical_bone_2d.h" void PhysicalBone2D::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - _find_skeleton_parent(); - _find_joint_child(); - - // Configure joint - if (child_joint && auto_configure_joint) { - _auto_configure_joint(); - } - - // Simulate physics if set - if (simulate_physics) { - _start_physics_simulation(); - } else { - _stop_physics_simulation(); - } - } - - if (p_what == NOTIFICATION_READY) { - _position_at_bone2d(); - set_physics_process_internal(true); - } if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { // Position the RigidBody in the correct position @@ -63,33 +42,43 @@ void PhysicalBone2D::_notification(int p_what) { if (child_joint && auto_configure_joint) { child_joint->set_global_position(get_global_position()); } - - // Remove any collision layers and masks if we're disabled. - // We do this so the RigidBody can still have it's layers and masks set like normal, but they will not be applied - // unless physics are told to simulate, only making them effective when we want them to be. - if (!_internal_simulate_physics) { - PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0); - PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0); - } } // Only in the editor: keep the PhysicalBone2D at the Bone2D position (if there is one) at all times. #ifdef TOOLS_ENABLED - if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { + else if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { if (Engine::get_singleton()->is_editor_hint()) { _position_at_bone2d(); } } #endif //TOOLS_ENABLED + + else if (p_what == NOTIFICATION_READY) { + _find_skeleton_parent(); + _find_joint_child(); + + // Configure joint + if (child_joint && auto_configure_joint) { + _auto_configure_joint(); + } + + // Simulate physics if set + if (simulate_physics) { + _start_physics_simulation(); + } else { + _stop_physics_simulation(); + } + + set_physics_process_internal(true); + } } void PhysicalBone2D::_position_at_bone2d() { // Reset to Bone2D position - if (parent_skeleton && bone2d_index > -1) { - if (bone2d_index <= parent_skeleton->get_bone_count()) { - Bone2D *bone_to_use = parent_skeleton->get_bone(bone2d_index); - set_global_transform(bone_to_use->get_global_transform()); - } + if (parent_skeleton) { + Bone2D *bone_to_use = parent_skeleton->get_bone(bone2d_index); + ERR_FAIL_COND_MSG(bone_to_use==nullptr, "It's not possible to position the bone with ID: " + itos(bone2d_index)); + set_global_transform(bone_to_use->get_global_transform()); } } @@ -193,6 +182,7 @@ void PhysicalBone2D::_start_physics_simulation() { PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); _internal_simulate_physics = true; + set_physics_process_internal(true); } void PhysicalBone2D::_stop_physics_simulation() { @@ -203,6 +193,10 @@ void PhysicalBone2D::_stop_physics_simulation() { // Reset to Bone2D position _position_at_bone2d(); + + set_physics_process_internal(false); + PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0); + PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0); } } diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 67eaa7ef2d5d..44dc56f754ac 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -125,7 +125,7 @@ void Bone2D::_notification(int p_what) { #endif // TOOLS_ENABLED } - if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { + else if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { if (skeleton) { skeleton->_make_transform_dirty(); } @@ -149,7 +149,7 @@ void Bone2D::_notification(int p_what) { #endif // TOOLS_ENABLED } - if (p_what == NOTIFICATION_MOVED_IN_PARENT) { + else if (p_what == NOTIFICATION_MOVED_IN_PARENT) { if (skeleton) { skeleton->_make_bone_setup_dirty(); } @@ -158,7 +158,7 @@ void Bone2D::_notification(int p_what) { } } - if (p_what == NOTIFICATION_EXIT_TREE) { + else if (p_what == NOTIFICATION_EXIT_TREE) { if (skeleton) { for (int i = 0; i < skeleton->bones.size(); i++) { if (skeleton->bones[i].bone == this) { @@ -173,21 +173,21 @@ void Bone2D::_notification(int p_what) { set_transform(cache_transform); } - if (p_what == NOTIFICATION_READY) { + else if (p_what == NOTIFICATION_READY) { if (autocalculate_length_and_angle) { calculate_length_and_rotation(); } } #ifdef TOOLS_ENABLED - if (p_what == NOTIFICATION_EDITOR_PRE_SAVE || p_what == NOTIFICATION_EDITOR_POST_SAVE) { + else if (p_what == NOTIFICATION_EDITOR_PRE_SAVE || p_what == NOTIFICATION_EDITOR_POST_SAVE) { Transform2D tmp_trans = get_transform(); set_transform(cache_transform); cache_transform = tmp_trans; } // Bone2D Editor gizmo drawing: - if (p_what == NOTIFICATION_DRAW) { + else if (p_what == NOTIFICATION_DRAW) { // Only draw the gizmo in the editor! if (Engine::get_singleton()->is_editor_hint() == false) { return; From ec24bdb4841dd0a1a36fdc95880eb3c157e93962 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Thu, 7 Jan 2021 12:14:05 -0500 Subject: [PATCH 22/34] Changes to fix the issues that came up after the rebase and fixed formatting issues --- editor/plugins/canvas_item_editor_plugin.cpp | 2 +- scene/2d/physical_bone_2d.cpp | 8 +++----- scene/2d/skeleton_2d.cpp | 3 +-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 2974c4a9ef0f..290de6cad053 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -2263,7 +2263,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref &p_event) { // Retrieve the canvas items selection = Vector<_SelectResult>(); _get_canvas_items_at_pos(click, selection); - if (!selection.empty()) { + if (!selection.is_empty()) { canvas_item = selection[0].item; } diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index 6c287d08a328..61982136a4e9 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -31,7 +31,6 @@ #include "physical_bone_2d.h" void PhysicalBone2D::_notification(int p_what) { - if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { // Position the RigidBody in the correct position if (follow_bone_when_simulating) { @@ -43,7 +42,6 @@ void PhysicalBone2D::_notification(int p_what) { child_joint->set_global_position(get_global_position()); } } - // Only in the editor: keep the PhysicalBone2D at the Bone2D position (if there is one) at all times. #ifdef TOOLS_ENABLED else if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { @@ -52,7 +50,7 @@ void PhysicalBone2D::_notification(int p_what) { } } #endif //TOOLS_ENABLED - + else if (p_what == NOTIFICATION_READY) { _find_skeleton_parent(); _find_joint_child(); @@ -77,7 +75,7 @@ void PhysicalBone2D::_position_at_bone2d() { // Reset to Bone2D position if (parent_skeleton) { Bone2D *bone_to_use = parent_skeleton->get_bone(bone2d_index); - ERR_FAIL_COND_MSG(bone_to_use==nullptr, "It's not possible to position the bone with ID: " + itos(bone2d_index)); + ERR_FAIL_COND_MSG(bone_to_use == nullptr, "It's not possible to position the bone with ID: " + itos(bone2d_index)); set_global_transform(bone_to_use->get_global_transform()); } } @@ -193,7 +191,7 @@ void PhysicalBone2D::_stop_physics_simulation() { // Reset to Bone2D position _position_at_bone2d(); - + set_physics_process_internal(false); PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0); PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0); diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 44dc56f754ac..bbffa9d96694 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -178,7 +178,6 @@ void Bone2D::_notification(int p_what) { calculate_length_and_rotation(); } } - #ifdef TOOLS_ENABLED else if (p_what == NOTIFICATION_EDITOR_PRE_SAVE || p_what == NOTIFICATION_EDITOR_POST_SAVE) { Transform2D tmp_trans = get_transform(); @@ -329,7 +328,7 @@ bool Bone2D::_editor_get_bone_shape(Vector *shape, Vector *out rel = rel.rotated(-get_rotation()); // Undo Bone2D node's rotation so its drawn correctly regardless of the node's rotation } - Vector2 relt = relt.rotated(Math_PI).normalized() * bone_width; + Vector2 relt = rel.rotated(Math_PI * 0.5).normalized() * bone_width; Vector2 reln = rel.normalized(); Vector2 reltn = relt.normalized(); From 4456bf02ea7b74812e4b40a99bfcd2d5454357af Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Sat, 30 Jan 2021 16:37:55 -0500 Subject: [PATCH 23/34] More changes based on review feedback! Huge thanks to AndreaCatania for the review! Changes: * Changed how the code for enabling and disabling physics joints in 2D works so it fully doesn't run the physics simulation when not in use * Format changes to Skeleton2D * Skeleton2D no longer processes _process and _physics_process when IK is not used * Skeleton2D uses internal versions of _process and _physics_process for IK * Skeleton_Modification2D class initalization is done in the header file * execute, setup_modification, and draw_gizmo now all have an underscore (so execute becomes _execute) * Virtual functions are now propocated using the call function * The error print function has been simplified * The target node in LookAt Modification2D is cached * Math_TAU is used instead of Math_PI multiplied by 2 * Modifications in a modification stack are now stored in a LocalVector rather than a Vector. Modifications_count removed internally. --- scene/2d/physical_bone_2d.cpp | 21 ++- scene/2d/skeleton_2d.cpp | 14 +- scene/resources/skeleton_modification_2d.cpp | 148 ++++++------------- scene/resources/skeleton_modification_2d.h | 56 +++---- 4 files changed, 105 insertions(+), 134 deletions(-) diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index 61982136a4e9..8be87f62eb1f 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -179,6 +179,21 @@ void PhysicalBone2D::_start_physics_simulation() { PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); + // Apply the correct mode + RigidBody2D::Mode rigid_mode = get_mode(); + if (rigid_mode == RigidBody2D::MODE_STATIC) { + PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC); + } else if (rigid_mode == RigidBody2D::MODE_RIGID) { + PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_RIGID); + } else if (rigid_mode == RigidBody2D::MODE_KINEMATIC) { + PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_KINEMATIC); + } else if (rigid_mode == RigidBody2D::MODE_CHARACTER) { + PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_CHARACTER); + } else { + // Default to Rigid + PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_RIGID); + } + _internal_simulate_physics = true; set_physics_process_internal(true); } @@ -195,6 +210,7 @@ void PhysicalBone2D::_stop_physics_simulation() { set_physics_process_internal(false); PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0); PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0); + PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC); } } @@ -304,7 +320,10 @@ void PhysicalBone2D::_bind_methods() { PhysicalBone2D::PhysicalBone2D() { // Stop the RigidBody from executing its force integration. - PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), nullptr, ""); + PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0); + PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0); + PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC); + child_joint = nullptr; } diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index bbffa9d96694..23bd8d7b3814 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "skeleton_2d.h" + #include "scene/resources/skeleton_modification_2d.h" #ifdef TOOLS_ENABLED @@ -660,21 +661,18 @@ void Skeleton2D::_notification(int p_what) { if (transform_dirty) { _update_transform(); } - - set_process(true); - set_physics_process(true); request_ready(); } if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { RS::get_singleton()->skeleton_set_base_transform_2d(skeleton, get_global_transform()); } - if (p_what == NOTIFICATION_PROCESS) { + else if (p_what == NOTIFICATION_INTERNAL_PROCESS) { if (modification_stack.is_valid()) { execute_modifications(get_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process); } } - if (p_what == NOTIFICATION_PHYSICS_PROCESS) { + else if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { if (modification_stack.is_valid()) { execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process); } @@ -711,12 +709,18 @@ void Skeleton2D::set_modification_stack(Ref p_stack if (modification_stack.is_valid()) { modification_stack->is_setup = false; modification_stack->set_skeleton(nullptr); + + set_process_internal(false); + set_physics_process_internal(false); } modification_stack = p_stack; if (modification_stack.is_valid()) { modification_stack->set_skeleton(this); modification_stack->setup(); + set_process_internal(true); + set_physics_process_internal(true); + #ifdef TOOLS_ENABLED modification_stack->set_editor_gizmos_dirty(true); #endif // TOOLS_ENABLED diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 243b8776babd..7fe206f589fd 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -86,7 +86,7 @@ void SkeletonModificationStack2D::setup() { if (!modifications[i].is_valid()) { continue; } - modifications.get(i)->setup_modification(this); + modifications[i]->_setup_modification(this); } #ifdef TOOLS_ENABLED @@ -117,7 +117,7 @@ void SkeletonModificationStack2D::execute(float delta, int p_execution_mode) { } if (modifications[i]->get_execution_mode() == p_execution_mode) { - modifications.get(i)->execute(delta); + modifications[i]->_execute(delta); } } } @@ -134,7 +134,7 @@ void SkeletonModificationStack2D::draw_editor_gizmos() { } if (modifications[i]->editor_draw_gizmo) { - modifications.get(i)->draw_editor_gizmo(); + modifications[i]->_draw_editor_gizmo(); } } skeleton->draw_set_transform(Vector2(0, 0)); @@ -162,7 +162,7 @@ void SkeletonModificationStack2D::enable_all_modifications(bool p_enabled) { if (!modifications[i].is_valid()) { continue; } - modifications.get(i)->set_enabled(p_enabled); + modifications[i]->set_enabled(p_enabled); } } @@ -172,7 +172,7 @@ Ref SkeletonModificationStack2D::get_modification(int p_ } void SkeletonModificationStack2D::add_modification(Ref p_mod) { - p_mod->setup_modification(this); + p_mod->_setup_modification(this); modifications.push_back(p_mod); #ifdef TOOLS_ENABLED @@ -193,10 +193,10 @@ void SkeletonModificationStack2D::set_modification(int p_mod_idx, Ref(); } else { - p_mod->setup_modification(this); - modifications.set(p_mod_idx, p_mod); + p_mod->_setup_modification(this); + modifications[p_mod_idx] = p_mod; } #ifdef TOOLS_ENABLED @@ -276,30 +276,20 @@ void SkeletonModificationStack2D::_bind_methods() { } SkeletonModificationStack2D::SkeletonModificationStack2D() { - skeleton = nullptr; - modifications = Vector>(); - is_setup = false; - enabled = false; - modifications_count = 0; - strength = 1; } /////////////////////////////////////// // Modification2D /////////////////////////////////////// -void SkeletonModification2D::execute(float delta) { - if (get_script_instance()) { - if (get_script_instance()->has_method("execute")) { - get_script_instance()->call("execute", delta); - } - } +void SkeletonModification2D::_execute(float delta) { + call("_execute", delta); if (!enabled) return; } -void SkeletonModification2D::setup_modification(SkeletonModificationStack2D *p_stack) { +void SkeletonModification2D::_setup_modification(SkeletonModificationStack2D *p_stack) { stack = p_stack; if (stack) { is_setup = true; @@ -307,19 +297,11 @@ void SkeletonModification2D::setup_modification(SkeletonModificationStack2D *p_s WARN_PRINT("Could not setup modification with name " + get_name()); } - if (get_script_instance()) { - if (get_script_instance()->has_method("execute")) { - get_script_instance()->call("setup_modification", p_stack); - } - } + call("_setup_modification", p_stack); } -void SkeletonModification2D::draw_editor_gizmo() { - if (get_script_instance()) { - if (get_script_instance()->has_method("draw_editor_gizmo")) { - get_script_instance()->call("draw_editor_gizmo"); - } - } +void SkeletonModification2D::_draw_editor_gizmo() { + call("_draw_editor_gizmo"); } bool SkeletonModification2D::_print_execution_error(bool p_condition, String p_message) { @@ -327,9 +309,8 @@ bool SkeletonModification2D::_print_execution_error(bool p_condition, String p_m return p_condition; } - if (p_condition && !execution_error_found) { - ERR_PRINT(p_message); - execution_error_found = true; + if (p_condition) { + ERR_PRINT_ONCE(p_message); } return p_condition; } @@ -353,15 +334,15 @@ bool SkeletonModification2D::get_enabled() { float SkeletonModification2D::clamp_angle(float angle, float min_bound, float max_bound, bool invert) { // Map to the 0 to 360 range (in radians though) instead of the -180 to 180 range. if (angle < 0) { - angle = (Math_PI * 2) + angle; + angle = Math_TAU + angle; } // Make min and max in the range of 0 to 360 (in radians), and make sure they are in the right order if (min_bound < 0) { - min_bound = (Math_PI * 2) + min_bound; + min_bound = Math_TAU + min_bound; } if (max_bound < 0) { - max_bound = (Math_PI * 2) + max_bound; + max_bound = Math_TAU + max_bound; } if (min_bound > max_bound) { float tmp = min_bound; @@ -494,9 +475,9 @@ bool SkeletonModification2D::get_editor_draw_gizmo() const { } void SkeletonModification2D::_bind_methods() { - BIND_VMETHOD(MethodInfo("execute", PropertyInfo(Variant::FLOAT, "delta"))); - BIND_VMETHOD(MethodInfo("setup_modification", PropertyInfo(Variant::OBJECT, "modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D"))); - BIND_VMETHOD(MethodInfo("draw_editor_gizmo")); + BIND_VMETHOD(MethodInfo("_execute", PropertyInfo(Variant::FLOAT, "delta"))); + BIND_VMETHOD(MethodInfo("_setup_modification", PropertyInfo(Variant::OBJECT, "modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D"))); + BIND_VMETHOD(MethodInfo("_draw_editor_gizmo")); ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification2D::set_enabled); ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification2D::get_enabled); @@ -591,7 +572,7 @@ void SkeletonModification2DLookAt::_get_property_list(List *p_list #endif // TOOLS_ENABLED } -void SkeletonModification2DLookAt::execute(float delta) { +void SkeletonModification2DLookAt::_execute(float delta) { ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, "Modification is not setup and therefore cannot execute!"); if (!enabled) { @@ -610,8 +591,10 @@ void SkeletonModification2DLookAt::execute(float delta) { return; } - Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + if (target_node_reference == nullptr) { + target_node_reference = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + } + if (_print_execution_error(!target_node_reference || !target_node_reference->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { return; } if (_print_execution_error(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!")) { @@ -624,7 +607,7 @@ void SkeletonModification2DLookAt::execute(float delta) { } Transform2D operation_transform = operation_bone->get_global_transform(); - Transform2D target_trans = target->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()); @@ -654,23 +637,19 @@ void SkeletonModification2DLookAt::execute(float delta) { // 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); - - // If we completed it successfully, then we can set execution_error_found to false. - execution_error_found = false; } -void SkeletonModification2DLookAt::setup_modification(SkeletonModificationStack2D *p_stack) { +void SkeletonModification2DLookAt::_setup_modification(SkeletonModificationStack2D *p_stack) { stack = p_stack; if (stack != nullptr) { is_setup = true; - execution_error_found = false; update_target_cache(); update_bone2d_cache(); } } -void SkeletonModification2DLookAt::draw_editor_gizmo() { +void SkeletonModification2DLookAt::_draw_editor_gizmo() { if (!enabled || !is_setup) { return; } @@ -704,7 +683,8 @@ void SkeletonModification2DLookAt::update_bone2d_cache() { ERR_FAIL_MSG("Error Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } - execution_error_found = false; + // Set this to null so we update it + target_node_reference = nullptr; } } } @@ -741,7 +721,6 @@ void SkeletonModification2DLookAt::set_bone_index(int p_bone_idx) { bone_idx = p_bone_idx; } - execution_error_found = false; _change_notify(); } @@ -762,7 +741,6 @@ void SkeletonModification2DLookAt::update_target_cache() { "Cannot update target cache: node is not in the scene tree!"); target_node_cache = node->get_instance_id(); - execution_error_found = false; } } } @@ -1016,7 +994,7 @@ void SkeletonModification2DCCDIK::_get_property_list(List *p_list) #endif // TOOLS_ENABLED } -void SkeletonModification2DCCDIK::execute(float delta) { +void SkeletonModification2DCCDIK::_execute(float delta) { ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, "Modification is not setup and therefore cannot execute!"); if (!enabled) { @@ -1048,7 +1026,6 @@ void SkeletonModification2DCCDIK::execute(float delta) { _execute_ccdik_joint(i, target, tip); } - execution_error_found = false; } void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D *target, Node2D *tip) { @@ -1096,18 +1073,17 @@ void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D * operation_bone->notification(operation_bone->NOTIFICATION_TRANSFORM_CHANGED); } -void SkeletonModification2DCCDIK::setup_modification(SkeletonModificationStack2D *p_stack) { +void SkeletonModification2DCCDIK::_setup_modification(SkeletonModificationStack2D *p_stack) { stack = p_stack; if (stack != nullptr) { is_setup = true; - execution_error_found = false; update_target_cache(); update_tip_cache(); } } -void SkeletonModification2DCCDIK::draw_editor_gizmo() { +void SkeletonModification2DCCDIK::_draw_editor_gizmo() { if (!enabled || !is_setup) { return; } @@ -1140,7 +1116,6 @@ void SkeletonModification2DCCDIK::update_target_cache() { "Cannot update target cache: node is not in the scene tree!"); target_node_cache = node->get_instance_id(); - execution_error_found = false; } } } @@ -1163,7 +1138,6 @@ void SkeletonModification2DCCDIK::update_tip_cache() { "Cannot update tip cache: node is not in the scene tree!"); tip_node_cache = node->get_instance_id(); - execution_error_found = false; } } } @@ -1194,7 +1168,6 @@ void SkeletonModification2DCCDIK::ccdik_joint_update_bone2d_cache(int p_joint_id ERR_FAIL_MSG("CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } - execution_error_found = false; } } } @@ -1220,7 +1193,6 @@ NodePath SkeletonModification2DCCDIK::get_tip_node() const { void SkeletonModification2DCCDIK::set_ccdik_data_chain_length(int p_length) { ccdik_data_chain.resize(p_length); - execution_error_found = false; _change_notify(); } @@ -1260,7 +1232,6 @@ void SkeletonModification2DCCDIK::set_ccdik_joint_bone_index(int p_joint_idx, in ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; } - execution_error_found = false; _change_notify(); } @@ -1547,7 +1518,7 @@ void SkeletonModification2DFABRIK::_get_property_list(List *p_list #endif // TOOLS_ENABLED } -void SkeletonModification2DFABRIK::execute(float delta) { +void SkeletonModification2DFABRIK::_execute(float delta) { ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, "Modification is not setup and therefore cannot execute!"); if (!enabled) { @@ -1670,7 +1641,6 @@ void SkeletonModification2DFABRIK::execute(float delta) { stack->skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, joint_bone2d_node->get_transform(), stack->strength, true); } - execution_error_found = false; } void SkeletonModification2DFABRIK::chain_backwards() { @@ -1765,12 +1735,11 @@ void SkeletonModification2DFABRIK::chain_forwards() { } } -void SkeletonModification2DFABRIK::setup_modification(SkeletonModificationStack2D *p_stack) { +void SkeletonModification2DFABRIK::_setup_modification(SkeletonModificationStack2D *p_stack) { stack = p_stack; if (stack != nullptr) { is_setup = true; - execution_error_found = false; update_target_cache(); } } @@ -1792,13 +1761,12 @@ void SkeletonModification2DFABRIK::update_target_cache() { "Cannot update target cache: node is not in scene tree!"); target_node_cache = node->get_instance_id(); - execution_error_found = false; } } } } -void SkeletonModification2DFABRIK::draw_editor_gizmo() { +void SkeletonModification2DFABRIK::_draw_editor_gizmo() { if (!enabled || !is_setup) { return; } @@ -1839,7 +1807,6 @@ void SkeletonModification2DFABRIK::fabrik_joint_update_bone2d_cache(int p_joint_ ERR_FAIL_MSG("FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } - execution_error_found = false; } } } @@ -1856,7 +1823,6 @@ NodePath SkeletonModification2DFABRIK::get_target_node() const { void SkeletonModification2DFABRIK::set_fabrik_data_chain_length(int p_length) { fabrik_data_chain.resize(p_length); - execution_error_found = false; _change_notify(); } @@ -1896,7 +1862,6 @@ void SkeletonModification2DFABRIK::set_fabrik_joint_bone_index(int p_joint_idx, fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; } - execution_error_found = false; _change_notify(); } @@ -2161,7 +2126,7 @@ void SkeletonModification2DJiggle::_get_property_list(List *p_list } } -void SkeletonModification2DJiggle::execute(float delta) { +void SkeletonModification2DJiggle::_execute(float delta) { ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, "Modification is not setup and therefore cannot execute!"); if (!enabled) { @@ -2181,7 +2146,6 @@ void SkeletonModification2DJiggle::execute(float delta) { _execute_jiggle_joint(i, target, delta); } - execution_error_found = false; } void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D *target, float delta) { @@ -2267,12 +2231,11 @@ void SkeletonModification2DJiggle::_update_jiggle_joint_data() { } } -void SkeletonModification2DJiggle::setup_modification(SkeletonModificationStack2D *p_stack) { +void SkeletonModification2DJiggle::_setup_modification(SkeletonModificationStack2D *p_stack) { stack = p_stack; if (stack) { is_setup = true; - execution_error_found = false; if (stack->skeleton) { for (int i = 0; i < jiggle_data_chain.size(); i++) { @@ -2305,7 +2268,6 @@ void SkeletonModification2DJiggle::update_target_cache() { "Cannot update target cache: node is not in scene tree!"); target_node_cache = node->get_instance_id(); - execution_error_found = false; } } } @@ -2336,7 +2298,6 @@ void SkeletonModification2DJiggle::jiggle_joint_update_bone2d_cache(int p_joint_ ERR_FAIL_MSG("Jiggle joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } - execution_error_found = false; } } } @@ -2425,7 +2386,6 @@ int SkeletonModification2DJiggle::get_jiggle_data_chain_length() { void SkeletonModification2DJiggle::set_jiggle_data_chain_length(int p_length) { ERR_FAIL_COND(p_length < 0); jiggle_data_chain.resize(p_length); - execution_error_found = false; _change_notify(); } @@ -2461,7 +2421,6 @@ void SkeletonModification2DJiggle::set_jiggle_joint_bone_index(int p_joint_idx, jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; } - execution_error_found = false; _change_notify(); } @@ -2671,7 +2630,7 @@ void SkeletonModification2DTwoBoneIK::_get_property_list(List *p_l #endif // TOOLS_ENABLED } -void SkeletonModification2DTwoBoneIK::execute(float delta) { +void SkeletonModification2DTwoBoneIK::_execute(float delta) { ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, "Modification is not setup and therefore cannot execute!"); if (!enabled) { @@ -2754,22 +2713,20 @@ void SkeletonModification2DTwoBoneIK::execute(float delta) { 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); - execution_error_found = false; } -void SkeletonModification2DTwoBoneIK::setup_modification(SkeletonModificationStack2D *p_stack) { +void SkeletonModification2DTwoBoneIK::_setup_modification(SkeletonModificationStack2D *p_stack) { stack = p_stack; if (stack) { is_setup = true; - execution_error_found = false; update_target_cache(); update_joint_one_bone2d_cache(); update_joint_two_bone2d_cache(); } } -void SkeletonModification2DTwoBoneIK::draw_editor_gizmo() { +void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() { if (!enabled || !is_setup) { return; } @@ -2834,7 +2791,6 @@ void SkeletonModification2DTwoBoneIK::update_target_cache() { "Cannot update target cache: node is not in the scene tree!"); target_node_cache = node->get_instance_id(); - execution_error_found = false; } } } @@ -2864,7 +2820,6 @@ void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { ERR_FAIL_MSG("update joint one Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } - execution_error_found = false; } } } @@ -2894,7 +2849,6 @@ void SkeletonModification2DTwoBoneIK::update_joint_two_bone2d_cache() { ERR_FAIL_MSG("update joint two Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } - execution_error_found = false; } } } @@ -2979,7 +2933,6 @@ void SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) { joint_one_bone_idx = p_bone_idx; } - execution_error_found = false; _change_notify(); } @@ -3005,7 +2958,6 @@ void SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) { joint_two_bone_idx = p_bone_idx; } - execution_error_found = false; _change_notify(); } @@ -3132,7 +3084,7 @@ void SkeletonModification2DPhysicalBones::_get_property_list(List } } -void SkeletonModification2DPhysicalBones::execute(float delta) { +void SkeletonModification2DPhysicalBones::_execute(float delta) { ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, "Modification is not setup and therefore cannot execute!"); if (!enabled) { @@ -3167,15 +3119,13 @@ void SkeletonModification2DPhysicalBones::execute(float delta) { } } - execution_error_found = false; } -void SkeletonModification2DPhysicalBones::setup_modification(SkeletonModificationStack2D *p_stack) { +void SkeletonModification2DPhysicalBones::_setup_modification(SkeletonModificationStack2D *p_stack) { stack = p_stack; if (stack) { is_setup = true; - execution_error_found = false; if (stack->skeleton) { for (int i = 0; i < physical_bone_chain.size(); i++) { @@ -3203,7 +3153,6 @@ void SkeletonModification2DPhysicalBones::_physical_bone_update_cache(int p_join "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(); - execution_error_found = false; } } } @@ -3216,7 +3165,6 @@ int SkeletonModification2DPhysicalBones::get_physical_bone_chain_length() { void SkeletonModification2DPhysicalBones::set_physical_bone_chain_length(int p_length) { ERR_FAIL_COND(p_length < 0); physical_bone_chain.resize(p_length); - execution_error_found = false; _change_notify(); } @@ -3375,7 +3323,7 @@ void SkeletonModification2DStackHolder::_get_property_list(List *p #endif // TOOLS_ENABLED } -void SkeletonModification2DStackHolder::execute(float delta) { +void SkeletonModification2DStackHolder::_execute(float delta) { ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, "Modification is not setup and therefore cannot execute!"); @@ -3384,7 +3332,7 @@ void SkeletonModification2DStackHolder::execute(float delta) { } } -void SkeletonModification2DStackHolder::setup_modification(SkeletonModificationStack2D *p_stack) { +void SkeletonModification2DStackHolder::_setup_modification(SkeletonModificationStack2D *p_stack) { stack = p_stack; if (stack != nullptr) { @@ -3397,7 +3345,7 @@ void SkeletonModification2DStackHolder::setup_modification(SkeletonModificationS } } -void SkeletonModification2DStackHolder::draw_editor_gizmo() { +void SkeletonModification2DStackHolder::_draw_editor_gizmo() { if (stack) { if (held_modification_stack.is_valid()) { held_modification_stack->draw_editor_gizmos(); diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index 80fc26e78958..f4494a9a72c7 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -32,6 +32,7 @@ #define SKELETONMODIFICATION2D_H #include "scene/2d/skeleton_2d.h" +#include "core/templates/local_vector.h" /////////////////////////////////////// // SkeletonModificationStack2D @@ -53,18 +54,17 @@ class SkeletonModificationStack2D : public Resource { bool _get(const StringName &p_path, Variant &r_ret) const; public: - Skeleton2D *skeleton; + Skeleton2D *skeleton = nullptr; bool is_setup = false; - bool enabled = true; - float strength = 0.0; + bool enabled = false; + float strength = 1.0; enum EXECUTION_MODE { execution_mode_process, execution_mode_physics_process }; - Vector> modifications; - int modifications_count = 0; + LocalVector> modifications = LocalVector>(); void setup(); void execute(float delta, int p_execution_mode); @@ -113,14 +113,13 @@ class SkeletonModification2D : public Resource { bool enabled = true; bool is_setup = false; - bool execution_error_found = false; bool _print_execution_error(bool p_condition, String p_message); public: - virtual void execute(float delta); - virtual void setup_modification(SkeletonModificationStack2D *p_stack); - virtual void draw_editor_gizmo(); + 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); @@ -156,6 +155,7 @@ class SkeletonModification2DLookAt : public SkeletonModification2D { NodePath target_node; ObjectID target_node_cache; + Node2D* target_node_reference = nullptr; float additional_rotation = 0; bool enable_constraint = false; @@ -174,9 +174,9 @@ class SkeletonModification2DLookAt : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void execute(float delta) override; - void setup_modification(SkeletonModificationStack2D *p_stack) override; - void draw_editor_gizmo() override; + void _execute(float 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; @@ -247,9 +247,9 @@ class SkeletonModification2DCCDIK : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void execute(float delta) override; - void setup_modification(SkeletonModificationStack2D *p_stack) override; - void draw_editor_gizmo() override; + void _execute(float 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; @@ -337,9 +337,9 @@ class SkeletonModification2DFABRIK : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void execute(float delta) override; - void setup_modification(SkeletonModificationStack2D *p_stack) override; - void draw_editor_gizmo() override; + void _execute(float 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; @@ -428,8 +428,8 @@ class SkeletonModification2DJiggle : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void execute(float delta) override; - void setup_modification(SkeletonModificationStack2D *p_stack) override; + void _execute(float delta) override; + void _setup_modification(SkeletonModificationStack2D *p_stack) override; void set_target_node(const NodePath &p_target_node); NodePath get_target_node() const; @@ -512,9 +512,9 @@ class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void execute(float delta) override; - void setup_modification(SkeletonModificationStack2D *p_stack) override; - void draw_editor_gizmo() override; + void _execute(float 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; @@ -573,8 +573,8 @@ class SkeletonModification2DPhysicalBones : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void execute(float delta) override; - void setup_modification(SkeletonModificationStack2D *p_stack) override; + void _execute(float 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); @@ -606,9 +606,9 @@ class SkeletonModification2DStackHolder : public SkeletonModification2D { public: Ref held_modification_stack; - void execute(float delta) override; - void setup_modification(SkeletonModificationStack2D *p_stack) override; - void draw_editor_gizmo() override; + void _execute(float delta) override; + void _setup_modification(SkeletonModificationStack2D *p_stack) override; + void _draw_editor_gizmo() override; void set_held_modification_stack(Ref p_held_stack); Ref get_held_modification_stack() const; From cc76c91566c5c36214ff75d96642f062594cc42d Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Sun, 7 Feb 2021 14:52:33 -0500 Subject: [PATCH 24/34] Changed the use of LocalVector to just Vector, so the CI on GitHub works. Edit: Fixed formatting issues with the static formatting checks Edit 2: Even more formatting fixes to hopeful solve static formatting check issues. --- scene/2d/physical_bone_2d.cpp | 6 ++-- scene/2d/skeleton_2d.cpp | 6 ++-- scene/resources/skeleton_modification_2d.cpp | 33 +++++--------------- scene/resources/skeleton_modification_2d.h | 9 +++--- 4 files changed, 17 insertions(+), 37 deletions(-) diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index 8be87f62eb1f..c0ddbfdc16b1 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -323,7 +323,7 @@ PhysicalBone2D::PhysicalBone2D() { PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0); PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0); PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC); - + child_joint = nullptr; } diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 23bd8d7b3814..67708df9dd3a 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -666,13 +666,11 @@ void Skeleton2D::_notification(int p_what) { if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { RS::get_singleton()->skeleton_set_base_transform_2d(skeleton, get_global_transform()); - } - else if (p_what == NOTIFICATION_INTERNAL_PROCESS) { + } else if (p_what == NOTIFICATION_INTERNAL_PROCESS) { if (modification_stack.is_valid()) { execute_modifications(get_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process); } - } - else if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { + } else if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { if (modification_stack.is_valid()) { execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process); } diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 7fe206f589fd..2979c8e5c60a 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -86,7 +86,7 @@ void SkeletonModificationStack2D::setup() { if (!modifications[i].is_valid()) { continue; } - modifications[i]->_setup_modification(this); + modifications.get(i)->_setup_modification(this); } #ifdef TOOLS_ENABLED @@ -117,7 +117,7 @@ void SkeletonModificationStack2D::execute(float delta, int p_execution_mode) { } if (modifications[i]->get_execution_mode() == p_execution_mode) { - modifications[i]->_execute(delta); + modifications.get(i)->_execute(delta); } } } @@ -134,7 +134,7 @@ void SkeletonModificationStack2D::draw_editor_gizmos() { } if (modifications[i]->editor_draw_gizmo) { - modifications[i]->_draw_editor_gizmo(); + modifications.get(i)->_draw_editor_gizmo(); } } skeleton->draw_set_transform(Vector2(0, 0)); @@ -162,7 +162,7 @@ void SkeletonModificationStack2D::enable_all_modifications(bool p_enabled) { if (!modifications[i].is_valid()) { continue; } - modifications[i]->set_enabled(p_enabled); + modifications.get(i)->set_enabled(p_enabled); } } @@ -193,10 +193,10 @@ void SkeletonModificationStack2D::set_modification(int p_mod_idx, Ref(); + modifications.insert(p_mod_idx, nullptr); } else { p_mod->_setup_modification(this); - modifications[p_mod_idx] = p_mod; + modifications.insert(p_mod_idx, p_mod); } #ifdef TOOLS_ENABLED @@ -740,7 +740,6 @@ void SkeletonModification2DLookAt::update_target_cache() { 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(); - } } } @@ -1025,7 +1024,6 @@ void SkeletonModification2DCCDIK::_execute(float delta) { 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 *target, Node2D *tip) { @@ -1115,7 +1113,6 @@ void SkeletonModification2DCCDIK::update_target_cache() { 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(); - } } } @@ -1137,7 +1134,6 @@ void SkeletonModification2DCCDIK::update_tip_cache() { 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(); - } } } @@ -1167,7 +1163,6 @@ void SkeletonModification2DCCDIK::ccdik_joint_update_bone2d_cache(int p_joint_id } else { ERR_FAIL_MSG("CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } - } } } @@ -1640,7 +1635,6 @@ void SkeletonModification2DFABRIK::_execute(float delta) { 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() { @@ -1760,7 +1754,6 @@ void SkeletonModification2DFABRIK::update_target_cache() { 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(); - } } } @@ -1806,7 +1799,6 @@ void SkeletonModification2DFABRIK::fabrik_joint_update_bone2d_cache(int p_joint_ } else { ERR_FAIL_MSG("FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } - } } } @@ -2145,7 +2137,6 @@ void SkeletonModification2DJiggle::_execute(float delta) { for (int i = 0; i < jiggle_data_chain.size(); i++) { _execute_jiggle_joint(i, target, delta); } - } void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D *target, float delta) { @@ -2267,7 +2258,6 @@ void SkeletonModification2DJiggle::update_target_cache() { 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(); - } } } @@ -2297,7 +2287,6 @@ void SkeletonModification2DJiggle::jiggle_joint_update_bone2d_cache(int p_joint_ } else { ERR_FAIL_MSG("Jiggle joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } - } } } @@ -2712,7 +2701,6 @@ void SkeletonModification2DTwoBoneIK::_execute(float delta) { 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) { @@ -2790,7 +2778,6 @@ void SkeletonModification2DTwoBoneIK::update_target_cache() { 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(); - } } } @@ -2819,7 +2806,6 @@ void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { } else { ERR_FAIL_MSG("update joint one Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } - } } } @@ -2848,7 +2834,6 @@ void SkeletonModification2DTwoBoneIK::update_joint_two_bone2d_cache() { } else { ERR_FAIL_MSG("update joint two Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); } - } } } @@ -3118,7 +3103,6 @@ void SkeletonModification2DPhysicalBones::_execute(float delta) { 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) { @@ -3152,7 +3136,6 @@ void SkeletonModification2DPhysicalBones::_physical_bone_update_cache(int p_join 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(); - } } } diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index f4494a9a72c7..c4af2f671a71 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,7 +32,6 @@ #define SKELETONMODIFICATION2D_H #include "scene/2d/skeleton_2d.h" -#include "core/templates/local_vector.h" /////////////////////////////////////// // SkeletonModificationStack2D @@ -64,7 +63,7 @@ class SkeletonModificationStack2D : public Resource { execution_mode_physics_process }; - LocalVector> modifications = LocalVector>(); + Vector> modifications = Vector>(); void setup(); void execute(float delta, int p_execution_mode); @@ -155,7 +154,7 @@ class SkeletonModification2DLookAt : public SkeletonModification2D { NodePath target_node; ObjectID target_node_cache; - Node2D* target_node_reference = nullptr; + Node2D *target_node_reference = nullptr; float additional_rotation = 0; bool enable_constraint = false; From 27f802290325399e7e174c636f4d78bf626abc17 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Sat, 13 Feb 2021 11:19:24 -0500 Subject: [PATCH 25/34] Removed angle constraints from 2D FABRIK, as they were not working. This was my fault, I didn't test the code very much and assumed it was working. The code works, but only on the very first joint and only if that first joint is never rotated by other IK modifiers or any other means. All other joins beyond the first joint do not work. I only discovered this while trying to write constrained FABRIK for Twisted IK 2 and found that the GSoC code was not working in the plugin, so I tested here with this code and confirmed it does not work. So, FABRIK will just be unconstrained now, unfortunately. If I find a solution to constrained IK for 2D FABRIK, I will document it so it can be potentially implemented into this system as well. Also: Fixed the header in physical_bone_2d.h not using the correct year. --- scene/2d/physical_bone_2d.h | 4 +- scene/resources/skeleton_modification_2d.cpp | 231 +------------------ scene/resources/skeleton_modification_2d.h | 19 -- 3 files changed, 3 insertions(+), 251 deletions(-) diff --git a/scene/2d/physical_bone_2d.h b/scene/2d/physical_bone_2d.h index 7f30cbcfacbb..8bfd650a57d3 100644 --- a/scene/2d/physical_bone_2d.h +++ b/scene/2d/physical_bone_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 2979c8e5c60a..05ba2da77dc9 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -1401,33 +1401,9 @@ bool SkeletonModification2DFABRIK::_set(const StringName &p_path, const Variant set_fabrik_joint_magnet_position(which, p_value); } else if (what == "use_target_rotation") { set_fabrik_joint_use_target_rotation(which, p_value); - } else if (what == "enable_constraint") { - set_fabrik_joint_enable_constraint(which, p_value); - } else if (what == "constraint_angle_min") { - set_fabrik_joint_constraint_angle_min(which, Math::deg2rad(float(p_value))); - } else if (what == "constraint_angle_max") { - set_fabrik_joint_constraint_angle_max(which, Math::deg2rad(float(p_value))); - } else if (what == "constraint_angle_invert") { - set_fabrik_joint_constraint_angle_invert(which, p_value); - } else if (what == "constraint_in_localspace") { - set_fabrik_joint_constraint_in_localspace(which, p_value); } - -#ifdef TOOLS_ENABLED - if (what.begins_with("editor_draw_gizmo")) { - set_fabrik_joint_editor_draw_gizmo(which, p_value); - } -#endif // TOOLS_ENABLED - - return true; } -#ifdef TOOLS_ENABLED - if (path.begins_with("editor/draw_gizmo")) { - set_editor_draw_gizmo(p_value); - } -#endif // TOOLS_ENABLED - return true; } @@ -1447,33 +1423,9 @@ bool SkeletonModification2DFABRIK::_get(const StringName &p_path, Variant &r_ret r_ret = get_fabrik_joint_magnet_position(which); } else if (what == "use_target_rotation") { r_ret = get_fabrik_joint_use_target_rotation(which); - } else if (what == "enable_constraint") { - r_ret = get_fabrik_joint_enable_constraint(which); - } else if (what == "constraint_angle_min") { - r_ret = Math::rad2deg(get_fabrik_joint_constraint_angle_min(which)); - } else if (what == "constraint_angle_max") { - r_ret = Math::rad2deg(get_fabrik_joint_constraint_angle_max(which)); - } else if (what == "constraint_angle_invert") { - r_ret = get_fabrik_joint_constraint_angle_invert(which); - } else if (what == "constraint_in_localspace") { - r_ret = get_fabrik_joint_constraint_in_localspace(which); - } - -#ifdef TOOLS_ENABLED - if (what.begins_with("editor_draw_gizmo")) { - r_ret = get_fabrik_joint_editor_draw_gizmo(which); } -#endif // TOOLS_ENABLED - return true; } - -#ifdef TOOLS_ENABLED - if (path.begins_with("editor/draw_gizmo")) { - r_ret = get_editor_draw_gizmo(); - } -#endif // TOOLS_ENABLED - return true; } @@ -1490,27 +1442,7 @@ void SkeletonModification2DFABRIK::_get_property_list(List *p_list 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)); } - - p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "enable_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); - if (fabrik_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 SkeletonModification2DFABRIK::_execute(float delta) { @@ -1618,12 +1550,6 @@ void SkeletonModification2DFABRIK::_execute(float delta) { } else { chain_trans = chain_trans.looking_at(target_global_pose.get_origin()); } - - // Account for constraints - if (fabrik_data_chain[i].enable_constraint) { - chain_trans.set_rotation(clamp_angle(chain_trans.get_rotation(), fabrik_data_chain[i].constraint_angle_min, - fabrik_data_chain[i].constraint_angle_max, fabrik_data_chain[i].constraint_angle_invert)); - } } // Adjust for the bone angle chain_trans.set_rotation(chain_trans.get_rotation() - joint_bone2d_node->get_bone_angle()); @@ -1650,11 +1576,6 @@ void SkeletonModification2DFABRIK::chain_backwards() { if (fabrik_data_chain[final_joint_index].use_target_rotation) { final_bone2d_angle = target_global_pose.get_rotation(); } - - if (fabrik_data_chain[final_joint_index].enable_constraint) { - final_bone2d_angle = clamp_angle(final_bone2d_angle, fabrik_data_chain[final_joint_index].constraint_angle_min, - fabrik_data_chain[final_joint_index].constraint_angle_max, fabrik_data_chain[final_joint_index].constraint_angle_invert); - } 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)); @@ -1669,21 +1590,6 @@ void SkeletonModification2DFABRIK::chain_backwards() { Bone2D *current_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); Transform2D current_pose = fabrik_transform_chain[i]; - // Commenting out this chunk of code makes the solves look better, but then it doesn't always find a solution even when one is possible... - if (fabrik_data_chain[i].enable_constraint) { - float previous_to_current_angle = 0; - float previous_to_current_length = previous_pose.get_origin().distance_to(current_pose.get_origin()); - - // Calculate the current angle - Vector2 previous_to_current_dir = previous_pose.get_origin().direction_to(current_pose.get_origin()); - previous_to_current_angle = Math::atan2(previous_to_current_dir.y, previous_to_current_dir.x); - - // Clamp the angle - previous_to_current_angle = clamp_angle(previous_to_current_angle, fabrik_data_chain[i].constraint_angle_min, fabrik_data_chain[i].constraint_angle_max, fabrik_data_chain[i].constraint_angle_invert); - - current_pose.set_origin(previous_pose.get_origin() + (Vector2(Math::cos(previous_to_current_angle), Math::sin(previous_to_current_angle)) * previous_to_current_length)); - } - 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 / (previous_pose.get_origin() - current_pose.get_origin()).length(); Vector2 finish_position = previous_pose.get_origin().lerp(current_pose.get_origin(), length); @@ -1705,20 +1611,6 @@ void SkeletonModification2DFABRIK::chain_forwards() { Transform2D current_pose = fabrik_transform_chain[i]; Transform2D next_pose = fabrik_transform_chain[i + 1]; - if (fabrik_data_chain[i].enable_constraint) { - float next_to_current_angle = 0; - float next_to_current_length = next_pose.get_origin().distance_to(current_pose.get_origin()); - - // Calculate the current angle - Vector2 next_to_current_dir = current_pose.get_origin().direction_to(next_pose.get_origin()); - next_to_current_angle = Math::atan2(next_to_current_dir.y, next_to_current_dir.x); - - // Clamp the angle - next_to_current_angle = clamp_angle(next_to_current_angle, fabrik_data_chain[i].constraint_angle_min, fabrik_data_chain[i].constraint_angle_max, fabrik_data_chain[i].constraint_angle_invert); - - next_pose.set_origin(current_pose.get_origin() + (Vector2(Math::cos(next_to_current_angle), Math::sin(next_to_current_angle)) * next_to_current_length)); - } - 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() - next_pose.get_origin()).length(); Vector2 finish_position = current_pose.get_origin().lerp(next_pose.get_origin(), length); @@ -1759,22 +1651,6 @@ void SkeletonModification2DFABRIK::update_target_cache() { } } -void SkeletonModification2DFABRIK::_draw_editor_gizmo() { - if (!enabled || !is_setup) { - return; - } - - for (int i = 0; i < fabrik_data_chain.size(); i++) { - if (!fabrik_data_chain[i].editor_draw_gizmo) { - continue; - } - - Bone2D *operation_bone = stack->skeleton->get_bone(fabrik_data_chain[i].bone_idx); - editor_draw_angle_constraints(operation_bone, fabrik_data_chain[i].constraint_angle_min, fabrik_data_chain[i].constraint_angle_max, - fabrik_data_chain[i].enable_constraint, fabrik_data_chain[i].constraint_in_localspace, fabrik_data_chain[i].constraint_angle_invert); - } -} - 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) { @@ -1882,103 +1758,6 @@ bool SkeletonModification2DFABRIK::get_fabrik_joint_use_target_rotation(int p_jo return fabrik_data_chain[p_joint_idx].use_target_rotation; } -void SkeletonModification2DFABRIK::set_fabrik_joint_enable_constraint(int p_joint_idx, bool p_constraint) { - ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); - fabrik_data_chain.write[p_joint_idx].enable_constraint = p_constraint; - _change_notify(); - -#ifdef TOOLS_ENABLED - if (stack && is_setup) { - stack->set_editor_gizmos_dirty(true); - } -#endif // TOOLS_ENABLED -} - -bool SkeletonModification2DFABRIK::get_fabrik_joint_enable_constraint(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].enable_constraint; -} - -void SkeletonModification2DFABRIK::set_fabrik_joint_constraint_angle_min(int p_joint_idx, float p_angle_min) { - ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); - fabrik_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 SkeletonModification2DFABRIK::get_fabrik_joint_constraint_angle_min(int p_joint_idx) const { - ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), 0.0, "FABRIK joint out of range!"); - return fabrik_data_chain[p_joint_idx].constraint_angle_min; -} - -void SkeletonModification2DFABRIK::set_fabrik_joint_constraint_angle_max(int p_joint_idx, float p_angle_max) { - ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); - fabrik_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 SkeletonModification2DFABRIK::get_fabrik_joint_constraint_angle_max(int p_joint_idx) const { - ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), 0.0, "FABRIK joint out of range!"); - return fabrik_data_chain[p_joint_idx].constraint_angle_max; -} - -void SkeletonModification2DFABRIK::set_fabrik_joint_constraint_angle_invert(int p_joint_idx, bool p_invert) { - ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); - fabrik_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 SkeletonModification2DFABRIK::get_fabrik_joint_constraint_angle_invert(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].constraint_angle_invert; -} - -void SkeletonModification2DFABRIK::set_fabrik_joint_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace) { - ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); - fabrik_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 SkeletonModification2DFABRIK::get_fabrik_joint_constraint_in_localspace(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].constraint_in_localspace; -} - -void SkeletonModification2DFABRIK::set_fabrik_joint_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo) { - ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); - fabrik_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 SkeletonModification2DFABRIK::get_fabrik_joint_editor_draw_gizmo(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].editor_draw_gizmo; -} - 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); @@ -1994,14 +1773,6 @@ void SkeletonModification2DFABRIK::_bind_methods() { 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); - ClassDB::bind_method(D_METHOD("set_fabrik_joint_enable_constraint", "joint_idx", "enable_constraint"), &SkeletonModification2DFABRIK::set_fabrik_joint_enable_constraint); - ClassDB::bind_method(D_METHOD("get_fabrik_joint_enable_constraint", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_enable_constraint); - ClassDB::bind_method(D_METHOD("set_fabrik_joint_constraint_angle_min", "joint_idx", "angle_min"), &SkeletonModification2DFABRIK::set_fabrik_joint_constraint_angle_min); - ClassDB::bind_method(D_METHOD("get_fabrik_joint_constraint_angle_min", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_constraint_angle_min); - ClassDB::bind_method(D_METHOD("set_fabrik_joint_constraint_angle_max", "joint_idx", "angle_max"), &SkeletonModification2DFABRIK::set_fabrik_joint_constraint_angle_max); - ClassDB::bind_method(D_METHOD("get_fabrik_joint_constraint_angle_max", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_constraint_angle_max); - ClassDB::bind_method(D_METHOD("set_fabrik_joint_constraint_angle_invert", "joint_idx", "invert"), &SkeletonModification2DFABRIK::set_fabrik_joint_constraint_angle_invert); - ClassDB::bind_method(D_METHOD("get_fabrik_joint_constraint_angle_invert", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_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::INT, "fabrik_data_chain_length", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_fabrik_data_chain_length", "get_fabrik_data_chain_length"); @@ -2011,7 +1782,7 @@ SkeletonModification2DFABRIK::SkeletonModification2DFABRIK() { stack = nullptr; is_setup = false; enabled = true; - editor_draw_gizmo = true; + editor_draw_gizmo = false; } SkeletonModification2DFABRIK::~SkeletonModification2DFABRIK() { diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index c4af2f671a71..aa92e5a05dbc 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -298,12 +298,6 @@ class SkeletonModification2DFABRIK : public SkeletonModification2D { Vector2 magnet_position = Vector2(0, 0); bool use_target_rotation = 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; }; @@ -338,7 +332,6 @@ class SkeletonModification2DFABRIK : public SkeletonModification2D { public: void _execute(float 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; @@ -355,18 +348,6 @@ class SkeletonModification2DFABRIK : public SkeletonModification2D { 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; - void set_fabrik_joint_enable_constraint(int p_joint_idx, bool p_constraint); - bool get_fabrik_joint_enable_constraint(int p_joint_idx) const; - void set_fabrik_joint_constraint_angle_min(int p_joint_idx, float p_angle_min); - float get_fabrik_joint_constraint_angle_min(int p_joint_idx) const; - void set_fabrik_joint_constraint_angle_max(int p_joint_idx, float p_angle_max); - float get_fabrik_joint_constraint_angle_max(int p_joint_idx) const; - void set_fabrik_joint_constraint_angle_invert(int p_joint_idx, bool p_invert); - bool get_fabrik_joint_constraint_angle_invert(int p_joint_idx) const; - void set_fabrik_joint_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace); - bool get_fabrik_joint_constraint_in_localspace(int p_joint_idx) const; - void set_fabrik_joint_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo); - bool get_fabrik_joint_editor_draw_gizmo(int p_joint_idx) const; SkeletonModification2DFABRIK(); ~SkeletonModification2DFABRIK(); From c6c3e6c77c124a71f3156936643204e1eebcfa97 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Sat, 13 Feb 2021 12:15:06 -0500 Subject: [PATCH 26/34] Rebased with master and fixed issues preventing the PR from running on the latest version of master --- scene/2d/physical_bone_2d.cpp | 4 +- scene/resources/skeleton_modification_2d.cpp | 44 ++++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index c0ddbfdc16b1..e7c274351851 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -250,7 +250,7 @@ bool PhysicalBone2D::is_simulating_physics() const { void PhysicalBone2D::set_bone2d_nodepath(const NodePath &p_nodepath) { bone2d_nodepath = p_nodepath; - _change_notify(); + notify_property_list_changed(); } NodePath PhysicalBone2D::get_bone2d_nodepath() const { @@ -275,7 +275,7 @@ void PhysicalBone2D::set_bone2d_index(int p_bone_idx) { bone2d_index = p_bone_idx; } - _change_notify(); + notify_property_list_changed(); } int PhysicalBone2D::get_bone2d_index() const { diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 05ba2da77dc9..9fb46063f5f8 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -206,7 +206,7 @@ void SkeletonModificationStack2D::set_modification(int p_mod_idx, Refset_editor_gizmos_dirty(true); @@ -1188,7 +1188,7 @@ NodePath SkeletonModification2DCCDIK::get_tip_node() const { void SkeletonModification2DCCDIK::set_ccdik_data_chain_length(int p_length) { ccdik_data_chain.resize(p_length); - _change_notify(); + notify_property_list_changed(); } int SkeletonModification2DCCDIK::get_ccdik_data_chain_length() { @@ -1200,7 +1200,7 @@ void SkeletonModification2DCCDIK::set_ccdik_joint_bone2d_node(int p_joint_idx, c ccdik_data_chain.write[p_joint_idx].bone2d_node = p_target_node; ccdik_joint_update_bone2d_cache(p_joint_idx); - _change_notify(); + notify_property_list_changed(); } NodePath SkeletonModification2DCCDIK::get_ccdik_joint_bone2d_node(int p_joint_idx) const { @@ -1227,7 +1227,7 @@ void SkeletonModification2DCCDIK::set_ccdik_joint_bone_index(int p_joint_idx, in ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; } - _change_notify(); + notify_property_list_changed(); } int SkeletonModification2DCCDIK::get_ccdik_joint_bone_index(int p_joint_idx) const { @@ -1248,7 +1248,7 @@ bool SkeletonModification2DCCDIK::get_ccdik_joint_rotate_from_joint(int p_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; - _change_notify(); + notify_property_list_changed(); #ifdef TOOLS_ENABLED if (stack && is_setup) { @@ -1691,7 +1691,7 @@ NodePath SkeletonModification2DFABRIK::get_target_node() const { void SkeletonModification2DFABRIK::set_fabrik_data_chain_length(int p_length) { fabrik_data_chain.resize(p_length); - _change_notify(); + notify_property_list_changed(); } int SkeletonModification2DFABRIK::get_fabrik_data_chain_length() { @@ -1703,7 +1703,7 @@ void SkeletonModification2DFABRIK::set_fabrik_joint_bone2d_node(int p_joint_idx, fabrik_data_chain.write[p_joint_idx].bone2d_node = p_target_node; fabrik_joint_update_bone2d_cache(p_joint_idx); - _change_notify(); + notify_property_list_changed(); } NodePath SkeletonModification2DFABRIK::get_fabrik_joint_bone2d_node(int p_joint_idx) const { @@ -1730,7 +1730,7 @@ void SkeletonModification2DFABRIK::set_fabrik_joint_bone_index(int p_joint_idx, fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; } - _change_notify(); + notify_property_list_changed(); } int SkeletonModification2DFABRIK::get_fabrik_joint_bone_index(int p_joint_idx) const { @@ -2123,7 +2123,7 @@ Vector2 SkeletonModification2DJiggle::get_gravity() const { void SkeletonModification2DJiggle::set_use_colliders(bool p_use_colliders) { use_colliders = p_use_colliders; - _change_notify(); + notify_property_list_changed(); } bool SkeletonModification2DJiggle::get_use_colliders() const { @@ -2146,7 +2146,7 @@ int SkeletonModification2DJiggle::get_jiggle_data_chain_length() { void SkeletonModification2DJiggle::set_jiggle_data_chain_length(int p_length) { ERR_FAIL_COND(p_length < 0); jiggle_data_chain.resize(p_length); - _change_notify(); + notify_property_list_changed(); } void SkeletonModification2DJiggle::set_jiggle_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node) { @@ -2154,7 +2154,7 @@ void SkeletonModification2DJiggle::set_jiggle_joint_bone2d_node(int p_joint_idx, jiggle_data_chain.write[p_joint_idx].bone2d_node = p_target_node; jiggle_joint_update_bone2d_cache(p_joint_idx); - _change_notify(); + notify_property_list_changed(); } NodePath SkeletonModification2DJiggle::get_jiggle_joint_bone2d_node(int p_joint_idx) const { @@ -2181,7 +2181,7 @@ void SkeletonModification2DJiggle::set_jiggle_joint_bone_index(int p_joint_idx, jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; } - _change_notify(); + notify_property_list_changed(); } int SkeletonModification2DJiggle::get_jiggle_joint_bone_index(int p_joint_idx) const { @@ -2193,7 +2193,7 @@ void SkeletonModification2DJiggle::set_jiggle_joint_override(int joint_idx, bool ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); jiggle_data_chain.write[joint_idx].override_defaults = p_override; _update_jiggle_joint_data(); - _change_notify(); + notify_property_list_changed(); } bool SkeletonModification2DJiggle::get_jiggle_joint_override(int joint_idx) const { @@ -2237,7 +2237,7 @@ float SkeletonModification2DJiggle::get_jiggle_joint_damping(int joint_idx) cons void SkeletonModification2DJiggle::set_jiggle_joint_use_gravity(int joint_idx, bool p_use_gravity) { ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); jiggle_data_chain.write[joint_idx].use_gravity = p_use_gravity; - _change_notify(); + notify_property_list_changed(); } bool SkeletonModification2DJiggle::get_jiggle_joint_use_gravity(int joint_idx) const { @@ -2622,7 +2622,7 @@ NodePath SkeletonModification2DTwoBoneIK::get_target_node() const { void SkeletonModification2DTwoBoneIK::set_joint_one_bone2d_node(const NodePath &p_target_node) { joint_one_bone2d_node = p_target_node; update_joint_one_bone2d_cache(); - _change_notify(); + notify_property_list_changed(); } void SkeletonModification2DTwoBoneIK::set_target_minimum_distance(float p_distance) { @@ -2664,7 +2664,7 @@ NodePath SkeletonModification2DTwoBoneIK::get_joint_one_bone2d_node() const { void SkeletonModification2DTwoBoneIK::set_joint_two_bone2d_node(const NodePath &p_target_node) { joint_two_bone2d_node = p_target_node; update_joint_two_bone2d_cache(); - _change_notify(); + notify_property_list_changed(); } NodePath SkeletonModification2DTwoBoneIK::get_joint_two_bone2d_node() const { @@ -2689,7 +2689,7 @@ void SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) { joint_one_bone_idx = p_bone_idx; } - _change_notify(); + notify_property_list_changed(); } int SkeletonModification2DTwoBoneIK::get_joint_one_bone_idx() const { @@ -2714,7 +2714,7 @@ void SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) { joint_two_bone_idx = p_bone_idx; } - _change_notify(); + notify_property_list_changed(); } int SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx() const { @@ -2782,7 +2782,7 @@ bool SkeletonModification2DPhysicalBones::_set(const StringName &p_path, const V if (Engine::get_singleton()->is_editor_hint()) { if (path.begins_with("fetch_bones")) { fetch_physical_bones(); - _change_notify(); + notify_property_list_changed(); return true; } } @@ -2919,7 +2919,7 @@ int SkeletonModification2DPhysicalBones::get_physical_bone_chain_length() { void SkeletonModification2DPhysicalBones::set_physical_bone_chain_length(int p_length) { ERR_FAIL_COND(p_length < 0); physical_bone_chain.resize(p_length); - _change_notify(); + notify_property_list_changed(); } void SkeletonModification2DPhysicalBones::fetch_physical_bones() { From d26d755024fa116f14db19e9e42adf0c7a9722c6 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Tue, 16 Mar 2021 17:34:30 -0400 Subject: [PATCH 27/34] Took the 2D IK modifications and put them into their own files. --- scene/register_scene_types.cpp | 8 + scene/resources/skeleton_modification_2d.cpp | 2874 ----------------- scene/resources/skeleton_modification_2d.h | 523 +-- .../skeleton_modification_2d_ccdik.cpp | 542 ++++ .../skeleton_modification_2d_ccdik.h | 116 + .../skeleton_modification_2d_fabrik.cpp | 439 +++ .../skeleton_modification_2d_fabrik.h | 108 + .../skeleton_modification_2d_jiggle.cpp | 563 ++++ .../skeleton_modification_2d_jiggle.h | 139 + .../skeleton_modification_2d_lookat.cpp | 404 +++ .../skeleton_modification_2d_lookat.h | 100 + ...skeleton_modification_2d_physicalbones.cpp | 291 ++ .../skeleton_modification_2d_physicalbones.h | 82 + .../skeleton_modification_2d_stackholder.cpp | 131 + .../skeleton_modification_2d_stackholder.h | 64 + .../skeleton_modification_2d_twoboneik.cpp | 478 +++ .../skeleton_modification_2d_twoboneik.h | 107 + .../skeleton_modification_stack_2d.cpp | 267 ++ .../skeleton_modification_stack_2d.h | 99 + 19 files changed, 3942 insertions(+), 3393 deletions(-) create mode 100644 scene/resources/skeleton_modification_2d_ccdik.cpp create mode 100644 scene/resources/skeleton_modification_2d_ccdik.h create mode 100644 scene/resources/skeleton_modification_2d_fabrik.cpp create mode 100644 scene/resources/skeleton_modification_2d_fabrik.h create mode 100644 scene/resources/skeleton_modification_2d_jiggle.cpp create mode 100644 scene/resources/skeleton_modification_2d_jiggle.h create mode 100644 scene/resources/skeleton_modification_2d_lookat.cpp create mode 100644 scene/resources/skeleton_modification_2d_lookat.h create mode 100644 scene/resources/skeleton_modification_2d_physicalbones.cpp create mode 100644 scene/resources/skeleton_modification_2d_physicalbones.h create mode 100644 scene/resources/skeleton_modification_2d_stackholder.cpp create mode 100644 scene/resources/skeleton_modification_2d_stackholder.h create mode 100644 scene/resources/skeleton_modification_2d_twoboneik.cpp create mode 100644 scene/resources/skeleton_modification_2d_twoboneik.h create mode 100644 scene/resources/skeleton_modification_stack_2d.cpp create mode 100644 scene/resources/skeleton_modification_stack_2d.h diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 6118dcb3171a..41b474d19e6b 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -163,6 +163,14 @@ #include "scene/resources/resource_format_text.h" #include "scene/resources/segment_shape_2d.h" #include "scene/resources/skeleton_modification_2d.h" +#include "scene/resources/skeleton_modification_stack_2d.h" +#include "scene/resources/skeleton_modification_2d_lookat.h" +#include "scene/resources/skeleton_modification_2d_ccdik.h" +#include "scene/resources/skeleton_modification_2d_fabrik.h" +#include "scene/resources/skeleton_modification_2d_jiggle.h" +#include "scene/resources/skeleton_modification_2d_twoboneik.h" +#include "scene/resources/skeleton_modification_2d_physicalbones.h" +#include "scene/resources/skeleton_modification_2d_stackholder.h" #include "scene/resources/sky.h" #include "scene/resources/sky_material.h" #include "scene/resources/sphere_shape_3d.h" diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 9fb46063f5f8..e09e845b2b37 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -39,245 +39,6 @@ #include "editor/editor_settings.h" #endif // TOOLS_ENABLED -/////////////////////////////////////// -// ModificationStack2D -/////////////////////////////////////// - -void SkeletonModificationStack2D::_get_property_list(List *p_list) const { - for (int i = 0; i < modifications.size(); i++) { - p_list->push_back( - PropertyInfo(Variant::OBJECT, "modifications/" + itos(i), - PROPERTY_HINT_RESOURCE_TYPE, - "SkeletonModification2D", - PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); - } -} - -bool SkeletonModificationStack2D::_set(const StringName &p_path, const Variant &p_value) { - String path = p_path; - - if (path.begins_with("modifications/")) { - int mod_idx = path.get_slicec('/', 1).to_int(); - set_modification(mod_idx, p_value); - return true; - } - return true; -} - -bool SkeletonModificationStack2D::_get(const StringName &p_path, Variant &r_ret) const { - String path = p_path; - - if (path.begins_with("modifications/")) { - int mod_idx = path.get_slicec('/', 1).to_int(); - r_ret = get_modification(mod_idx); - return true; - } - return true; -} - -void SkeletonModificationStack2D::setup() { - if (is_setup) { - return; - } - - if (skeleton != nullptr) { - is_setup = true; - for (int i = 0; i < modifications.size(); i++) { - if (!modifications[i].is_valid()) { - continue; - } - modifications.get(i)->_setup_modification(this); - } - -#ifdef TOOLS_ENABLED - set_editor_gizmos_dirty(true); -#endif // TOOLS_ENABLED - - } else { - WARN_PRINT("Cannot setup SkeletonModificationStack2D: no Skeleton2D set!"); - } -} - -void SkeletonModificationStack2D::execute(float delta, int p_execution_mode) { - ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr || is_queued_for_deletion(), - "Modification stack is not properly setup and therefore cannot execute!"); - - if (!skeleton->is_inside_tree()) { - ERR_PRINT_ONCE("Skeleton is not inside SceneTree! Cannot execute modification!"); - return; - } - - if (!enabled) { - return; - } - - for (int i = 0; i < modifications.size(); i++) { - if (!modifications[i].is_valid()) { - continue; - } - - if (modifications[i]->get_execution_mode() == p_execution_mode) { - modifications.get(i)->_execute(delta); - } - } -} - -void SkeletonModificationStack2D::draw_editor_gizmos() { - if (!is_setup) { - return; - } - - if (editor_gizmo_dirty) { - for (int i = 0; i < modifications.size(); i++) { - if (!modifications[i].is_valid()) { - continue; - } - - if (modifications[i]->editor_draw_gizmo) { - modifications.get(i)->_draw_editor_gizmo(); - } - } - skeleton->draw_set_transform(Vector2(0, 0)); - editor_gizmo_dirty = false; - } -} - -void SkeletonModificationStack2D::set_editor_gizmos_dirty(bool p_dirty) { - if (!is_setup) { - return; - } - - if (!editor_gizmo_dirty && p_dirty) { - editor_gizmo_dirty = p_dirty; - if (skeleton) { - skeleton->update(); - } - } else { - editor_gizmo_dirty = p_dirty; - } -} - -void SkeletonModificationStack2D::enable_all_modifications(bool p_enabled) { - for (int i = 0; i < modifications.size(); i++) { - if (!modifications[i].is_valid()) { - continue; - } - modifications.get(i)->set_enabled(p_enabled); - } -} - -Ref SkeletonModificationStack2D::get_modification(int p_mod_idx) const { - ERR_FAIL_INDEX_V(p_mod_idx, modifications.size(), nullptr); - return modifications[p_mod_idx]; -} - -void SkeletonModificationStack2D::add_modification(Ref p_mod) { - 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(p_mod_idx); - -#ifdef TOOLS_ENABLED - set_editor_gizmos_dirty(true); -#endif // TOOLS_ENABLED -} - -void SkeletonModificationStack2D::set_modification(int p_mod_idx, Ref p_mod) { - ERR_FAIL_INDEX(p_mod_idx, modifications.size()); - - if (p_mod == nullptr) { - modifications.insert(p_mod_idx, nullptr); - } else { - p_mod->_setup_modification(this); - modifications.insert(p_mod_idx, p_mod); - } - -#ifdef TOOLS_ENABLED - set_editor_gizmos_dirty(true); -#endif // TOOLS_ENABLED -} - -void SkeletonModificationStack2D::set_modification_count(int p_count) { - 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"), &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"), "set_modification_count", "get_modification_count"); -} - -SkeletonModificationStack2D::SkeletonModificationStack2D() { -} - /////////////////////////////////////// // Modification2D /////////////////////////////////////// @@ -498,2638 +259,3 @@ SkeletonModification2D::SkeletonModification2D() { stack = nullptr; is_setup = false; } - -/////////////////////////////////////// -// LookAt -/////////////////////////////////////// - -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::deg2rad(float(p_value))); - } else if (path.begins_with("constraint_angle_max")) { - set_constraint_angle_max(Math::deg2rad(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::deg2rad(float(p_value))); - } - -#ifdef TOOLS_ENABLED - if (path.begins_with("editor/draw_gizmo")) { - set_editor_draw_gizmo(p_value); - } -#endif // TOOLS_ENABLED - - 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::rad2deg(get_constraint_angle_min()); - } else if (path.begins_with("constraint_angle_max")) { - r_ret = Math::rad2deg(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::rad2deg(get_additional_rotation()); - } - -#ifdef TOOLS_ENABLED - if (path.begins_with("editor/draw_gizmo")) { - r_ret = get_editor_draw_gizmo(); - } -#endif // TOOLS_ENABLED - - return true; -} - -void SkeletonModification2DLookAt::_get_property_list(List *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 delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - - if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); - update_target_cache(); - return; - } - - if (bone2d_node_cache.is_null() && !bone2d_node.is_empty()) { - update_bone2d_cache(); - _print_execution_error(true, "Bone2D node cache is out of date. Attempting to update..."); - return; - } - - if (target_node_reference == nullptr) { - target_node_reference = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - } - if (_print_execution_error(!target_node_reference || !target_node_reference->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { - return; - } - if (_print_execution_error(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!")) { - return; - } - - Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx); - if (_print_execution_error(operation_bone == nullptr, "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_transform().get_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) { - _print_execution_error(true, "Cannot update Bone2D cache: modification is not properly setup!"); - return; - } - - bone2d_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(bone2d_node)) { - Node *node = stack->skeleton->get_node(bone2d_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update Bone2D cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update Bone2D cache: node is not in the scene tree!"); - bone2d_node_cache = node->get_instance_id(); - - Bone2D *bone = Object::cast_to(node); - if (bone) { - bone_idx = bone->get_index_in_skeleton(); - } else { - ERR_FAIL_MSG("Error Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); - } - - // Set this to null so we update it - target_node_reference = nullptr; - } - } - } -} - -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) { - 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 { - WARN_PRINT("Cannot verify the bone index for this modification..."); - bone_idx = p_bone_idx; - } - - notify_property_list_changed(); -} - -void SkeletonModification2DLookAt::update_target_cache() { - if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); - return; - } - - target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update target cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update target cache: node is not in the scene tree!"); - target_node_cache = node->get_instance_id(); - } - } - } -} - -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() { -} - -/////////////////////////////////////// -// CCDIK -/////////////////////////////////////// - -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::deg2rad(float(p_value))); - } else if (what == "constraint_angle_max") { - set_ccdik_joint_constraint_angle_max(which, Math::deg2rad(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 - if (what.begins_with("editor_draw_gizmo")) { - set_ccdik_joint_editor_draw_gizmo(which, p_value); - } -#endif // TOOLS_ENABLED - - return true; - } - -#ifdef TOOLS_ENABLED - if (path.begins_with("editor/draw_gizmo")) { - set_editor_draw_gizmo(p_value); - } -#endif // TOOLS_ENABLED - - 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::rad2deg(get_ccdik_joint_constraint_angle_min(which)); - } else if (what == "constraint_angle_max") { - r_ret = Math::rad2deg(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 - if (what.begins_with("editor_draw_gizmo")) { - r_ret = get_ccdik_joint_editor_draw_gizmo(which); - } -#endif // TOOLS_ENABLED - - return true; - } - -#ifdef TOOLS_ENABLED - if (path.begins_with("editor/draw_gizmo")) { - r_ret = get_editor_draw_gizmo(); - } -#endif // TOOLS_ENABLED - - return true; -} - -void SkeletonModification2DCCDIK::_get_property_list(List *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 delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - - if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); - update_target_cache(); - return; - } - if (tip_node_cache.is_null()) { - _print_execution_error(true, "Tip cache is out of date. Attempting to update..."); - update_tip_cache(); - return; - } - - Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { - return; - } - - Node2D *tip = Object::cast_to(ObjectDB::get_instance(tip_node_cache)); - if (_print_execution_error(!tip || !tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) { - return; - } - - 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 *target, Node2D *tip) { - CCDIK_Joint_Data2D ccdik_data = ccdik_data_chain[p_joint_idx]; - if (_print_execution_error(ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > stack->skeleton->get_bone_count(), "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(target->get_global_transform().get_origin()).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 = operation_transform.get_origin().angle_to_point(tip->get_global_transform().get_origin()); - float joint_to_target = operation_transform.get_origin().angle_to_point(target->get_global_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_transform().get_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) { - _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); - return; - } - - target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update target cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update target cache: node is not in the scene tree!"); - target_node_cache = node->get_instance_id(); - } - } - } -} - -void SkeletonModification2DCCDIK::update_tip_cache() { - if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!"); - return; - } - - tip_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(tip_node)) { - Node *node = stack->skeleton->get_node(tip_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update tip cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update tip cache: node is not in 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) { - _print_execution_error(true, "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(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 { - 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; - } - - 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() { -} - -/////////////////////////////////////// -// FABRIK -/////////////////////////////////////// - -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); - } - } - - 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); - } - return true; - } - return true; -} - -void SkeletonModification2DFABRIK::_get_property_list(List *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 delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - - if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); - update_target_cache(); - return; - } - - if (_print_execution_error(fabrik_data_chain.size() <= 1, "FABRIK requires at least two joints to operate! Cannot execute modification!")) { - return; - } - - Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { - return; - } - target_global_pose = target->get_global_transform(); - - if (fabrik_data_chain[0].bone2d_node_cache.is_null() && !fabrik_data_chain[0].bone2d_node.is_empty()) { - fabrik_joint_update_bone2d_cache(0); - WARN_PRINT("Bone2D cache for origin joint is out of date. Updating..."); - } - - Bone2D *origin_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[0].bone2d_node_cache)); - if (_print_execution_error(!origin_bone2d_node || !origin_bone2d_node->is_inside_tree(), "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()) { - _print_execution_error(true, "Bone2D cache for joint " + itos(i) + " is out of date.. Attempting to update..."); - fabrik_joint_update_bone2d_cache(i); - } - Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); - if (_print_execution_error(!joint_bone2d_node, "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(); - - // Apply magnet positions - if (i == 0) { - continue; // The origin cannot use a magnet position! - } else { - Transform2D joint_trans = fabrik_transform_chain[i]; - joint_trans.set_origin(joint_trans.get_origin() + fabrik_data_chain[i].magnet_position); - fabrik_transform_chain.write[i] = joint_trans; - } - } - - Bone2D *final_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[fabrik_data_chain.size() - 1].bone2d_node_cache)); - float final_bone2d_angle = final_bone2d_node->get_global_transform().get_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_transform().get_origin() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_transform().get_origin()); - chain_iterations = 0; - - while (target_distance > chain_tolarance) { - chain_backwards(); - chain_forwards(); - - final_bone2d_angle = final_bone2d_node->get_global_transform().get_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_transform().get_origin() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_transform().get_origin()); - - chain_iterations += 1; - if (chain_iterations >= chain_max_iterations) { - break; - } - } - - // Apply all of the saved transforms to the Bone2D nodes - for (int i = 0; i < fabrik_data_chain.size(); i++) { - Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); - if (_print_execution_error(!joint_bone2d_node, "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_transform().get_scale()); - - // Apply to the bone, and to the override - joint_bone2d_node->set_global_transform(chain_trans); - stack->skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, joint_bone2d_node->get_transform(), stack->strength, true); - } -} - -void SkeletonModification2DFABRIK::chain_backwards() { - int final_joint_index = fabrik_data_chain.size() - 1; - Bone2D *final_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[final_joint_index].bone2d_node_cache)); - Transform2D final_bone2d_trans = fabrik_transform_chain[final_joint_index]; - - // 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 = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); - Transform2D current_pose = fabrik_transform_chain[i]; - - 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 / (previous_pose.get_origin() - current_pose.get_origin()).length(); - 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 = Object::cast_to(ObjectDB::get_instance(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 / (current_pose.get_origin() - next_pose.get_origin()).length(); - 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; - update_target_cache(); - } -} - -void SkeletonModification2DFABRIK::update_target_cache() { - if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); - return; - } - - target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update target cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update target cache: node is not in scene tree!"); - target_node_cache = node->get_instance_id(); - } - } - } -} - -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) { - _print_execution_error(true, "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(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 { - 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; - } - - 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() { -} - -/////////////////////////////////////// -// Jiggle -/////////////////////////////////////// - -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); - } - return true; - } else { - if (path == "use_colliders") { - set_use_colliders(p_value); - } else if (path == "collision_mask") { - set_collision_mask(p_value); - } - } - 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); - } - return true; - } else { - if (path == "use_colliders") { - r_ret = get_use_colliders(); - } else if (path == "collision_mask") { - r_ret = get_collision_mask(); - } - } - return true; -} - -void SkeletonModification2DJiggle::_get_property_list(List *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 delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); - update_target_cache(); - return; - } - Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { - return; - } - - for (int i = 0; i < jiggle_data_chain.size(); i++) { - _execute_jiggle_joint(i, target, delta); - } -} - -void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D *target, float delta) { - // Adopted from: https://wiki.unity3d.com/index.php/JiggleBone - // With modifications by TwistedTwigleg. - - if (_print_execution_error( - jiggle_data_chain[p_joint_idx].bone_idx <= -1 || jiggle_data_chain[p_joint_idx].bone_idx > stack->skeleton->get_bone_count(), - "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()) { - _print_execution_error(true, "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 (_print_execution_error(!operation_bone, "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 = target->get_global_transform().get_origin(); - - 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 * 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 * 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 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; - - // Add exception support? - bool ray_hit = space_state->intersect_ray(operation_bone_trans.get_origin(), jiggle_data_chain[p_joint_idx].dynamic_position, - ray_result, Set(), collision_mask); - - 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_transform().get_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_transform().get_origin(); - } - } - } - - update_target_cache(); - } -} - -void SkeletonModification2DJiggle::update_target_cache() { - if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); - return; - } - - target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update target cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update target cache: node is not in scene tree!"); - target_node_cache = node->get_instance_id(); - } - } - } -} - -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) { - _print_execution_error(true, "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(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 { - 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; - } - - 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 joint_idx, bool p_override) { - ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); - jiggle_data_chain.write[joint_idx].override_defaults = p_override; - _update_jiggle_joint_data(); - notify_property_list_changed(); -} - -bool SkeletonModification2DJiggle::get_jiggle_joint_override(int joint_idx) const { - ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), false); - return jiggle_data_chain[joint_idx].override_defaults; -} - -void SkeletonModification2DJiggle::set_jiggle_joint_stiffness(int joint_idx, float p_stiffness) { - ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!"); - ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); - jiggle_data_chain.write[joint_idx].stiffness = p_stiffness; -} - -float SkeletonModification2DJiggle::get_jiggle_joint_stiffness(int joint_idx) const { - ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); - return jiggle_data_chain[joint_idx].stiffness; -} - -void SkeletonModification2DJiggle::set_jiggle_joint_mass(int joint_idx, float p_mass) { - ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!"); - ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); - jiggle_data_chain.write[joint_idx].mass = p_mass; -} - -float SkeletonModification2DJiggle::get_jiggle_joint_mass(int joint_idx) const { - ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); - return jiggle_data_chain[joint_idx].mass; -} - -void SkeletonModification2DJiggle::set_jiggle_joint_damping(int joint_idx, float p_damping) { - ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!"); - ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); - jiggle_data_chain.write[joint_idx].damping = p_damping; -} - -float SkeletonModification2DJiggle::get_jiggle_joint_damping(int joint_idx) const { - ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); - return jiggle_data_chain[joint_idx].damping; -} - -void SkeletonModification2DJiggle::set_jiggle_joint_use_gravity(int joint_idx, bool p_use_gravity) { - ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); - jiggle_data_chain.write[joint_idx].use_gravity = p_use_gravity; - notify_property_list_changed(); -} - -bool SkeletonModification2DJiggle::get_jiggle_joint_use_gravity(int joint_idx) const { - ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), false); - return jiggle_data_chain[joint_idx].use_gravity; -} - -void SkeletonModification2DJiggle::set_jiggle_joint_gravity(int joint_idx, Vector2 p_gravity) { - ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); - jiggle_data_chain.write[joint_idx].gravity = p_gravity; -} - -Vector2 SkeletonModification2DJiggle::get_jiggle_joint_gravity(int joint_idx) const { - ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), Vector2(0, 0)); - return jiggle_data_chain[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(); - 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() { -} - -/////////////////////////////////////// -// TwoBoneIK -/////////////////////////////////////// - -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 - 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 - - 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 - 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 - - return true; -} - -void SkeletonModification2DTwoBoneIK::_get_property_list(List *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 delta) { - ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, - "Modification is not setup and therefore cannot execute!"); - if (!enabled) { - return; - } - - if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); - update_target_cache(); - return; - } - - if (joint_one_bone2d_node_cache.is_null() && !joint_one_bone2d_node.is_empty()) { - _print_execution_error(true, "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()) { - _print_execution_error(true, "Joint two Bone2D node cache is out of date. Attempting to update..."); - update_joint_two_bone2d_cache(); - } - - Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (_print_execution_error(!target || !target->is_inside_tree(), "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 (_print_execution_error(joint_one_bone == nullptr, "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 (_print_execution_error(joint_two_bone == nullptr, "Joint one 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_transform().get_origin() - joint_one_bone->get_global_transform().get_origin(); - float joint_one_to_target = target_difference.length(); - float angle_atan = Math::atan2(target_difference.y, target_difference.x); - - float bone_one_length = joint_one_bone->get_length() * MIN(joint_one_bone->get_global_scale().x, joint_one_bone->get_global_scale().y); - float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().x, joint_two_bone->get_global_scale().y); - bool override_angles_due_to_out_of_range = false; - - if (joint_one_to_target < target_minimum_distance) { - joint_one_to_target = target_minimum_distance; - } - if (joint_one_to_target > target_maximum_distance && target_maximum_distance > 0.0) { - joint_one_to_target = target_maximum_distance; - } - - if (bone_one_length + bone_two_length < joint_one_to_target) { - override_angles_due_to_out_of_range = true; - } - - if (!override_angles_due_to_out_of_range) { - float angle_0 = Math::acos(((joint_one_to_target * joint_one_to_target) + (bone_one_length * bone_one_length) - (bone_two_length * bone_two_length)) / (2.0 * joint_one_to_target * bone_one_length)); - float angle_1 = Math::acos(((bone_two_length * bone_two_length) + (bone_one_length * bone_one_length) - (joint_one_to_target * joint_one_to_target)) / (2.0 * bone_two_length * bone_one_length)); - - if (flip_bend_direction) { - angle_0 = -angle_0; - angle_1 = -angle_1; - } - - if (isnan(angle_0) || isnan(angle_1)) { - // We cannot solve for this angle! Do nothing to avoid setting the rotation (and scale) to NaN. - } else { - joint_one_bone->set_global_rotation(angle_atan - angle_0 - joint_one_bone->get_bone_angle()); - joint_two_bone->set_rotation(-Math_PI - angle_1 - joint_two_bone->get_bone_angle() + joint_one_bone->get_bone_angle()); - } - } else { - joint_one_bone->set_global_rotation(angle_atan - joint_one_bone->get_bone_angle()); - joint_two_bone->set_global_rotation(angle_atan - joint_two_bone->get_bone_angle()); - } - - stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, joint_one_bone->get_transform(), stack->strength, true); - stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, joint_two_bone->get_transform(), stack->strength, true); -} - -void SkeletonModification2DTwoBoneIK::_setup_modification(SkeletonModificationStack2D *p_stack) { - stack = p_stack; - - if (stack) { - is_setup = true; - update_target_cache(); - update_joint_one_bone2d_cache(); - update_joint_two_bone2d_cache(); - } -} - -void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() { - if (!enabled || !is_setup) { - return; - } - - Bone2D *operation_bone_one = stack->skeleton->get_bone(joint_one_bone_idx); - if (!operation_bone_one) { - return; - } - stack->skeleton->draw_set_transform( - stack->skeleton->get_global_transform().affine_inverse().xform(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 = EditorSettings::get_singleton()->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), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); - } else { - float angle = (Math_PI * 0.5) + operation_bone_one->get_bone_angle(); - stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); - } - -#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 = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - target_direction = operation_bone_one->get_global_position().direction_to(target->get_global_position()); - } - - stack->skeleton->draw_circle(target_direction * target_minimum_distance, 8, bone_ik_color); - stack->skeleton->draw_circle(target_direction * target_maximum_distance, 8, bone_ik_color); - stack->skeleton->draw_line(target_direction * target_minimum_distance, target_direction * target_maximum_distance, bone_ik_color, 2.0); - } - } - } -#endif // TOOLS_ENABLED -} - -void SkeletonModification2DTwoBoneIK::update_target_cache() { - if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); - return; - } - - target_node_cache = ObjectID(); - if (stack->skeleton) { - if (stack->skeleton->is_inside_tree()) { - if (stack->skeleton->has_node(target_node)) { - Node *node = stack->skeleton->get_node(target_node); - ERR_FAIL_COND_MSG(!node || stack->skeleton == node, - "Cannot update target cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update target cache: node is not in the scene tree!"); - target_node_cache = node->get_instance_id(); - } - } - } -} - -void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { - if (!is_setup || !stack) { - _print_execution_error(true, "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 update joint one Bone2D cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update update joint one Bone2D cache: node is not in the scene tree!"); - joint_one_bone2d_node_cache = node->get_instance_id(); - - Bone2D *bone = Object::cast_to(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) { - _print_execution_error(true, "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 update joint two Bone2D cache: node is this modification's skeleton or cannot be found!"); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), - "Cannot update update joint two Bone2D cache: node is not in scene tree!"); - joint_two_bone2d_node_cache = node->get_instance_id(); - - Bone2D *bone = Object::cast_to(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 { - WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint one..."); - 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 { - WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint two..."); - 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"), "set_target_minimum_distance", "get_target_minimum_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_maximum_distance", PROPERTY_HINT_NONE, "0, 100000000, 0.01"), "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() { -} - -/////////////////////////////////////// -// PhysicalBones -/////////////////////////////////////// - -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 true; -} - -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")) { - return true; // Do nothing! - } - } -#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 true; -} - -void SkeletonModification2DPhysicalBones::_get_property_list(List *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 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()) { - _print_execution_error(true, "PhysicalBone2D cache " + itos(i) + " is out of date. Attempting to update..."); - _physical_bone_update_cache(i); - continue; - } - - PhysicalBone2D *physical_bone = Object::cast_to(ObjectDB::get_instance(bone_data.physical_bone_node_cache)); - if (_print_execution_error(!physical_bone, "PhysicalBone2D not found at index " + itos(i) + "!")) { - return; - } - if (_print_execution_error(physical_bone->get_bone2d_index() < 0 || physical_bone->get_bone2d_index() > stack->skeleton->get_bone_count(), - "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) { - _print_execution_error(true, "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_COND_MSG(!stack->skeleton, "No skeleton found! Cannot fetch physical bones!"); - - physical_bone_chain.clear(); - - List node_queue = List(); - node_queue.push_back(stack->skeleton); - - while (node_queue.size() > 0) { - Node *node_to_process = node_queue[0]; - node_queue.pop_front(); - - PhysicalBone2D *potential_bone = Object::cast_to(node_to_process); - if (potential_bone) { - PhysicalBone_Data2D new_data = PhysicalBone_Data2D(); - new_data.physical_bone_node = stack->skeleton->get_path_to(potential_bone); - new_data.physical_bone_node_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 &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 &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.size() <= 0) { - for (int i = 0; i < physical_bone_chain.size(); i++) { - PhysicalBone2D *physical_bone = Object::cast_to(stack->skeleton->get_node(physical_bone_chain[i].physical_bone_node)); - 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 = Object::cast_to(ObjectDB::get_instance(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(); - enabled = true; - editor_draw_gizmo = false; // Nothing to really show in a gizmo right now. -} - -SkeletonModification2DPhysicalBones::~SkeletonModification2DPhysicalBones() { -} - -/////////////////////////////////////// -// StackHolder -/////////////////////////////////////// - -bool SkeletonModification2DStackHolder::_set(const StringName &p_path, const Variant &p_value) { - String path = p_path; - - if (path == "held_modification_stack") { - set_held_modification_stack(p_value); - } - -#ifdef TOOLS_ENABLED - if (path == "editor/draw_gizmo") { - set_editor_draw_gizmo(p_value); - } -#endif // TOOLS_ENABLED - - return true; -} - -bool SkeletonModification2DStackHolder::_get(const StringName &p_path, Variant &r_ret) const { - String path = p_path; - - if (path == "held_modification_stack") { - r_ret = get_held_modification_stack(); - } - -#ifdef TOOLS_ENABLED - if (path == "editor/draw_gizmo") { - r_ret = get_editor_draw_gizmo(); - } -#endif // TOOLS_ENABLED - - return true; -} - -void SkeletonModification2DStackHolder::_get_property_list(List *p_list) const { - p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); - -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); - } -#endif // TOOLS_ENABLED -} - -void SkeletonModification2DStackHolder::_execute(float 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(delta, execution_mode); - } -} - -void SkeletonModification2DStackHolder::_setup_modification(SkeletonModificationStack2D *p_stack) { - stack = p_stack; - - if (stack != nullptr) { - is_setup = true; - - if (held_modification_stack.is_valid()) { - held_modification_stack->set_skeleton(stack->get_skeleton()); - held_modification_stack->setup(); - } - } -} - -void SkeletonModification2DStackHolder::_draw_editor_gizmo() { - if (stack) { - if (held_modification_stack.is_valid()) { - held_modification_stack->draw_editor_gizmos(); - } - } -} - -void SkeletonModification2DStackHolder::set_held_modification_stack(Ref p_held_stack) { - held_modification_stack = p_held_stack; - - if (is_setup && held_modification_stack.is_valid()) { - held_modification_stack->set_skeleton(stack->get_skeleton()); - held_modification_stack->setup(); - } -} - -Ref SkeletonModification2DStackHolder::get_held_modification_stack() const { - return held_modification_stack; -} - -void SkeletonModification2DStackHolder::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_held_modification_stack", "held_modification_stack"), &SkeletonModification2DStackHolder::set_held_modification_stack); - ClassDB::bind_method(D_METHOD("get_held_modification_stack"), &SkeletonModification2DStackHolder::get_held_modification_stack); -} - -SkeletonModification2DStackHolder::SkeletonModification2DStackHolder() { - stack = nullptr; - is_setup = false; - enabled = true; -} - -SkeletonModification2DStackHolder::~SkeletonModification2DStackHolder() { -} diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index aa92e5a05dbc..7fcf7576936f 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -32,73 +32,15 @@ #define SKELETONMODIFICATION2D_H #include "scene/2d/skeleton_2d.h" +#include "scene/resources/skeleton_modification_stack_2d.h" /////////////////////////////////////// -// SkeletonModificationStack2D +// SkeletonModification2D /////////////////////////////////////// -class Skeleton2D; -class SkeletonModification2D; +class SkeletonModificationStack2D; class Bone2D; -class SkeletonModificationStack2D : public Resource { - GDCLASS(SkeletonModificationStack2D, Resource); - friend class Skeleton2D; - friend class SkeletonModification2D; - -protected: - static void _bind_methods(); - void _get_property_list(List *p_list) const; - bool _set(const StringName &p_path, const Variant &p_value); - bool _get(const StringName &p_path, Variant &r_ret) const; - -public: - Skeleton2D *skeleton = nullptr; - bool is_setup = false; - bool enabled = false; - float strength = 1.0; - - enum EXECUTION_MODE { - execution_mode_process, - execution_mode_physics_process - }; - - Vector> modifications = Vector>(); - - void setup(); - void execute(float delta, int p_execution_mode); - - bool editor_gizmo_dirty = false; - void draw_editor_gizmos(); - void set_editor_gizmos_dirty(bool p_dirty); - - void enable_all_modifications(bool p_enable); - Ref get_modification(int p_mod_idx) const; - void add_modification(Ref p_mod); - void delete_modification(int p_mod_idx); - void set_modification(int p_mod_idx, Ref p_mod); - - void set_modification_count(int p_count); - int get_modification_count() const; - - void set_skeleton(Skeleton2D *p_skeleton); - Skeleton2D *get_skeleton() const; - - bool get_is_setup() const; - - void set_enabled(bool p_enabled); - bool get_enabled() const; - - void set_strength(float p_strength); - float get_strength() const; - - SkeletonModificationStack2D(); -}; - -/////////////////////////////////////// -// SkeletonModification2D -/////////////////////////////////////// - class SkeletonModification2D : public Resource { GDCLASS(SkeletonModification2D, Resource); friend class Skeleton2D; @@ -108,7 +50,7 @@ class SkeletonModification2D : public Resource { static void _bind_methods(); SkeletonModificationStack2D *stack; - int execution_mode = SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process; + int execution_mode = 0; // 0 = process bool enabled = true; bool is_setup = false; @@ -140,461 +82,4 @@ class SkeletonModification2D : public Resource { SkeletonModification2D(); }; -/////////////////////////////////////// -// 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 *p_list) const; - -public: - void _execute(float 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(); -}; - -/////////////////////////////////////// -// 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_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 *target, Node2D *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 *p_list) const; - -public: - void _execute(float 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(); -}; - -/////////////////////////////////////// -// 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_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 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(); - Transform2D origin_global_pose = Transform2D(); - - 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 *p_list) const; - -public: - void _execute(float 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(); -}; - -/////////////////////////////////////// -// 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_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 *target, float 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 *p_list) const; - -public: - void _execute(float 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(); -}; - -/////////////////////////////////////// -// SkeletonModification2DTwoBoneIK -/////////////////////////////////////// - -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 *p_list) const; - -public: - void _execute(float 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(); -}; - -/////////////////////////////////////// -// SkeletonModification2DPhysicalBones -/////////////////////////////////////// - -class SkeletonModification2DPhysicalBones : public SkeletonModification2D { - GDCLASS(SkeletonModification2DPhysicalBones, SkeletonModification2D); - -private: - struct PhysicalBone_Data2D { - NodePath physical_bone_node; - ObjectID physical_bone_node_cache; - }; - Vector physical_bone_chain; - - void _physical_bone_update_cache(int p_joint_idx); - - bool _simulation_state_dirty = false; - TypedArray _simulation_state_dirty_names; - bool _simulation_state_dirty_process; - 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 *p_list) const; - -public: - void _execute(float 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 &p_bones); - void stop_simulation(const TypedArray &p_bones); - - SkeletonModification2DPhysicalBones(); - ~SkeletonModification2DPhysicalBones(); -}; - -/////////////////////////////////////// -// SkeletonModification2DStackHolder -/////////////////////////////////////// - -class SkeletonModification2DStackHolder : public SkeletonModification2D { - GDCLASS(SkeletonModification2DStackHolder, SkeletonModification2D); - -protected: - static void _bind_methods(); - bool _get(const StringName &p_path, Variant &r_ret) const; - bool _set(const StringName &p_path, const Variant &p_value); - void _get_property_list(List *p_list) const; - -public: - Ref held_modification_stack; - - void _execute(float delta) override; - void _setup_modification(SkeletonModificationStack2D *p_stack) override; - void _draw_editor_gizmo() override; - - void set_held_modification_stack(Ref p_held_stack); - Ref get_held_modification_stack() const; - - SkeletonModification2DStackHolder(); - ~SkeletonModification2DStackHolder(); -}; - #endif // SKELETONMODIFICATION2D_H diff --git a/scene/resources/skeleton_modification_2d_ccdik.cpp b/scene/resources/skeleton_modification_2d_ccdik.cpp new file mode 100644 index 000000000000..d075239edf0e --- /dev/null +++ b/scene/resources/skeleton_modification_2d_ccdik.cpp @@ -0,0 +1,542 @@ +/*************************************************************************/ +/* skeleton_modification_2d_ccdik.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_modification_2d_ccdik.h" +#include "scene/2d/skeleton_2d.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif // TOOLS_ENABLED + +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::deg2rad(float(p_value))); + } else if (what == "constraint_angle_max") { + set_ccdik_joint_constraint_angle_max(which, Math::deg2rad(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 + if (what.begins_with("editor_draw_gizmo")) { + set_ccdik_joint_editor_draw_gizmo(which, p_value); + } +#endif // TOOLS_ENABLED + + return true; + } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + set_editor_draw_gizmo(p_value); + } +#endif // TOOLS_ENABLED + + 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::rad2deg(get_ccdik_joint_constraint_angle_min(which)); + } else if (what == "constraint_angle_max") { + r_ret = Math::rad2deg(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 + if (what.begins_with("editor_draw_gizmo")) { + r_ret = get_ccdik_joint_editor_draw_gizmo(which); + } +#endif // TOOLS_ENABLED + + return true; + } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + r_ret = get_editor_draw_gizmo(); + } +#endif // TOOLS_ENABLED + + return true; +} + +void SkeletonModification2DCCDIK::_get_property_list(List *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 delta) { + ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, + "Modification is not setup and therefore cannot execute!"); + if (!enabled) { + return; + } + + if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + if (tip_node_cache.is_null()) { + _print_execution_error(true, "Tip cache is out of date. Attempting to update..."); + update_tip_cache(); + return; + } + + Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + return; + } + + Node2D *tip = Object::cast_to(ObjectDB::get_instance(tip_node_cache)); + if (_print_execution_error(!tip || !tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) { + return; + } + + 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 *target, Node2D *tip) { + CCDIK_Joint_Data2D ccdik_data = ccdik_data_chain[p_joint_idx]; + if (_print_execution_error(ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > stack->skeleton->get_bone_count(), "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(target->get_global_transform().get_origin()).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 = operation_transform.get_origin().angle_to_point(tip->get_global_transform().get_origin()); + float joint_to_target = operation_transform.get_origin().angle_to_point(target->get_global_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_transform().get_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) { + _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); + return; + } + + target_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(target_node)) { + Node *node = stack->skeleton->get_node(target_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in the scene tree!"); + target_node_cache = node->get_instance_id(); + } + } + } +} + +void SkeletonModification2DCCDIK::update_tip_cache() { + if (!is_setup || !stack) { + _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!"); + return; + } + + tip_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(tip_node)) { + Node *node = stack->skeleton->get_node(tip_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update tip cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update tip cache: node is not in 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) { + _print_execution_error(true, "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(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 { + 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; + } + + 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() { +} diff --git a/scene/resources/skeleton_modification_2d_ccdik.h b/scene/resources/skeleton_modification_2d_ccdik.h new file mode 100644 index 000000000000..aba6773bf761 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_ccdik.h @@ -0,0 +1,116 @@ +/*************************************************************************/ +/* skeleton_modification_2d_ccdik.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETONMODIFICATION2DCCDIK_H +#define SKELETONMODIFICATION2DCCDIK_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/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_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 *target, Node2D *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 *p_list) const; + +public: + void _execute(float 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(); +}; + +#endif // SKELETONMODIFICATION2DCCDIK_H diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp new file mode 100644 index 000000000000..36b78b683831 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_fabrik.cpp @@ -0,0 +1,439 @@ +/*************************************************************************/ +/* skeleton_modification_2d_fabrik.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_modification_2d_fabrik.h" +#include "scene/2d/skeleton_2d.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif // TOOLS_ENABLED + +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); + } + } + + 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); + } + return true; + } + return true; +} + +void SkeletonModification2DFABRIK::_get_property_list(List *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 delta) { + ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, + "Modification is not setup and therefore cannot execute!"); + if (!enabled) { + return; + } + + if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + + if (_print_execution_error(fabrik_data_chain.size() <= 1, "FABRIK requires at least two joints to operate! Cannot execute modification!")) { + return; + } + + Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + return; + } + target_global_pose = target->get_global_transform(); + + if (fabrik_data_chain[0].bone2d_node_cache.is_null() && !fabrik_data_chain[0].bone2d_node.is_empty()) { + fabrik_joint_update_bone2d_cache(0); + WARN_PRINT("Bone2D cache for origin joint is out of date. Updating..."); + } + + Bone2D *origin_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[0].bone2d_node_cache)); + if (_print_execution_error(!origin_bone2d_node || !origin_bone2d_node->is_inside_tree(), "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()) { + _print_execution_error(true, "Bone2D cache for joint " + itos(i) + " is out of date.. Attempting to update..."); + fabrik_joint_update_bone2d_cache(i); + } + Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); + if (_print_execution_error(!joint_bone2d_node, "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(); + + // Apply magnet positions + if (i == 0) { + continue; // The origin cannot use a magnet position! + } else { + Transform2D joint_trans = fabrik_transform_chain[i]; + joint_trans.set_origin(joint_trans.get_origin() + fabrik_data_chain[i].magnet_position); + fabrik_transform_chain.write[i] = joint_trans; + } + } + + Bone2D *final_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[fabrik_data_chain.size() - 1].bone2d_node_cache)); + float final_bone2d_angle = final_bone2d_node->get_global_transform().get_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_transform().get_origin() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_transform().get_origin()); + chain_iterations = 0; + + while (target_distance > chain_tolarance) { + chain_backwards(); + chain_forwards(); + + final_bone2d_angle = final_bone2d_node->get_global_transform().get_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_transform().get_origin() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_transform().get_origin()); + + chain_iterations += 1; + if (chain_iterations >= chain_max_iterations) { + break; + } + } + + // Apply all of the saved transforms to the Bone2D nodes + for (int i = 0; i < fabrik_data_chain.size(); i++) { + Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); + if (_print_execution_error(!joint_bone2d_node, "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_transform().get_scale()); + + // Apply to the bone, and to the override + joint_bone2d_node->set_global_transform(chain_trans); + stack->skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, joint_bone2d_node->get_transform(), stack->strength, true); + } +} + +void SkeletonModification2DFABRIK::chain_backwards() { + int final_joint_index = fabrik_data_chain.size() - 1; + Bone2D *final_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[final_joint_index].bone2d_node_cache)); + Transform2D final_bone2d_trans = fabrik_transform_chain[final_joint_index]; + + // 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 = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); + Transform2D current_pose = fabrik_transform_chain[i]; + + 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 / (previous_pose.get_origin() - current_pose.get_origin()).length(); + 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 = Object::cast_to(ObjectDB::get_instance(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 / (current_pose.get_origin() - next_pose.get_origin()).length(); + 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; + update_target_cache(); + } +} + +void SkeletonModification2DFABRIK::update_target_cache() { + if (!is_setup || !stack) { + _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); + return; + } + + target_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(target_node)) { + Node *node = stack->skeleton->get_node(target_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in scene tree!"); + target_node_cache = node->get_instance_id(); + } + } + } +} + +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) { + _print_execution_error(true, "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(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 { + 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; + } + + 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() { +} diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/resources/skeleton_modification_2d_fabrik.h new file mode 100644 index 000000000000..b566322edf2f --- /dev/null +++ b/scene/resources/skeleton_modification_2d_fabrik.h @@ -0,0 +1,108 @@ +/*************************************************************************/ +/* skeleton_modification_2d_fabrik.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETONMODIFICATION2DFABRIK_H +#define SKELETONMODIFICATION2DFABRIK_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/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_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 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(); + Transform2D origin_global_pose = Transform2D(); + + 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 *p_list) const; + +public: + void _execute(float 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(); +}; + +#endif // SKELETONMODIFICATION2DFABRIK_H \ No newline at end of file diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp new file mode 100644 index 000000000000..4b63709ba787 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_jiggle.cpp @@ -0,0 +1,563 @@ +/*************************************************************************/ +/* skeleton_modification_2d_jiggle.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_modification_2d_jiggle.h" +#include "scene/2d/skeleton_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); + } + return true; + } else { + if (path == "use_colliders") { + set_use_colliders(p_value); + } else if (path == "collision_mask") { + set_collision_mask(p_value); + } + } + 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); + } + return true; + } else { + if (path == "use_colliders") { + r_ret = get_use_colliders(); + } else if (path == "collision_mask") { + r_ret = get_collision_mask(); + } + } + return true; +} + +void SkeletonModification2DJiggle::_get_property_list(List *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 delta) { + ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, + "Modification is not setup and therefore cannot execute!"); + if (!enabled) { + return; + } + if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + return; + } + + for (int i = 0; i < jiggle_data_chain.size(); i++) { + _execute_jiggle_joint(i, target, delta); + } +} + +void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D *target, float delta) { + // Adopted from: https://wiki.unity3d.com/index.php/JiggleBone + // With modifications by TwistedTwigleg. + + if (_print_execution_error( + jiggle_data_chain[p_joint_idx].bone_idx <= -1 || jiggle_data_chain[p_joint_idx].bone_idx > stack->skeleton->get_bone_count(), + "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()) { + _print_execution_error(true, "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 (_print_execution_error(!operation_bone, "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 = target->get_global_transform().get_origin(); + + 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 * 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 * 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 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; + + // Add exception support? + bool ray_hit = space_state->intersect_ray(operation_bone_trans.get_origin(), jiggle_data_chain[p_joint_idx].dynamic_position, + ray_result, Set(), collision_mask); + + 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_transform().get_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_transform().get_origin(); + } + } + } + + update_target_cache(); + } +} + +void SkeletonModification2DJiggle::update_target_cache() { + if (!is_setup || !stack) { + _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); + return; + } + + target_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(target_node)) { + Node *node = stack->skeleton->get_node(target_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in scene tree!"); + target_node_cache = node->get_instance_id(); + } + } + } +} + +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) { + _print_execution_error(true, "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(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 { + 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; + } + + 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 joint_idx, bool p_override) { + ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[joint_idx].override_defaults = p_override; + _update_jiggle_joint_data(); + notify_property_list_changed(); +} + +bool SkeletonModification2DJiggle::get_jiggle_joint_override(int joint_idx) const { + ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), false); + return jiggle_data_chain[joint_idx].override_defaults; +} + +void SkeletonModification2DJiggle::set_jiggle_joint_stiffness(int joint_idx, float p_stiffness) { + ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!"); + ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[joint_idx].stiffness = p_stiffness; +} + +float SkeletonModification2DJiggle::get_jiggle_joint_stiffness(int joint_idx) const { + ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); + return jiggle_data_chain[joint_idx].stiffness; +} + +void SkeletonModification2DJiggle::set_jiggle_joint_mass(int joint_idx, float p_mass) { + ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!"); + ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[joint_idx].mass = p_mass; +} + +float SkeletonModification2DJiggle::get_jiggle_joint_mass(int joint_idx) const { + ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); + return jiggle_data_chain[joint_idx].mass; +} + +void SkeletonModification2DJiggle::set_jiggle_joint_damping(int joint_idx, float p_damping) { + ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!"); + ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[joint_idx].damping = p_damping; +} + +float SkeletonModification2DJiggle::get_jiggle_joint_damping(int joint_idx) const { + ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); + return jiggle_data_chain[joint_idx].damping; +} + +void SkeletonModification2DJiggle::set_jiggle_joint_use_gravity(int joint_idx, bool p_use_gravity) { + ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[joint_idx].use_gravity = p_use_gravity; + notify_property_list_changed(); +} + +bool SkeletonModification2DJiggle::get_jiggle_joint_use_gravity(int joint_idx) const { + ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), false); + return jiggle_data_chain[joint_idx].use_gravity; +} + +void SkeletonModification2DJiggle::set_jiggle_joint_gravity(int joint_idx, Vector2 p_gravity) { + ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[joint_idx].gravity = p_gravity; +} + +Vector2 SkeletonModification2DJiggle::get_jiggle_joint_gravity(int joint_idx) const { + ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), Vector2(0, 0)); + return jiggle_data_chain[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(); + 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() { +} diff --git a/scene/resources/skeleton_modification_2d_jiggle.h b/scene/resources/skeleton_modification_2d_jiggle.h new file mode 100644 index 000000000000..81dfd99a5dca --- /dev/null +++ b/scene/resources/skeleton_modification_2d_jiggle.h @@ -0,0 +1,139 @@ +/*************************************************************************/ +/* skeleton_modification_2d_jiggle.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETONMODIFICATION2DJIGGLE_H +#define SKELETONMODIFICATION2DJIGGLE_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/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_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 *target, float 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 *p_list) const; + +public: + void _execute(float 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(); +}; + +#endif // SKELETONMODIFICATION2DJIGGLE_H \ No newline at end of file diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/resources/skeleton_modification_2d_lookat.cpp new file mode 100644 index 000000000000..1ffd05a882b4 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_lookat.cpp @@ -0,0 +1,404 @@ +/*************************************************************************/ +/* skeleton_modification_2d_lookat.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_modification_2d_lookat.h" +#include "scene/2d/skeleton_2d.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif // TOOLS_ENABLED + +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::deg2rad(float(p_value))); + } else if (path.begins_with("constraint_angle_max")) { + set_constraint_angle_max(Math::deg2rad(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::deg2rad(float(p_value))); + } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + set_editor_draw_gizmo(p_value); + } +#endif // TOOLS_ENABLED + + 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::rad2deg(get_constraint_angle_min()); + } else if (path.begins_with("constraint_angle_max")) { + r_ret = Math::rad2deg(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::rad2deg(get_additional_rotation()); + } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + r_ret = get_editor_draw_gizmo(); + } +#endif // TOOLS_ENABLED + + return true; +} + +void SkeletonModification2DLookAt::_get_property_list(List *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 delta) { + ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, + "Modification is not setup and therefore cannot execute!"); + if (!enabled) { + return; + } + + if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + + if (bone2d_node_cache.is_null() && !bone2d_node.is_empty()) { + update_bone2d_cache(); + _print_execution_error(true, "Bone2D node cache is out of date. Attempting to update..."); + return; + } + + if (target_node_reference == nullptr) { + target_node_reference = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + } + if (_print_execution_error(!target_node_reference || !target_node_reference->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + return; + } + if (_print_execution_error(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!")) { + return; + } + + Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx); + if (_print_execution_error(operation_bone == nullptr, "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_transform().get_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) { + _print_execution_error(true, "Cannot update Bone2D cache: modification is not properly setup!"); + return; + } + + bone2d_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(bone2d_node)) { + Node *node = stack->skeleton->get_node(bone2d_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update Bone2D cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update Bone2D cache: node is not in the scene tree!"); + bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to(node); + if (bone) { + bone_idx = bone->get_index_in_skeleton(); + } else { + ERR_FAIL_MSG("Error Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); + } + + // Set this to null so we update it + target_node_reference = nullptr; + } + } + } +} + +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) { + 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 { + WARN_PRINT("Cannot verify the bone index for this modification..."); + bone_idx = p_bone_idx; + } + + notify_property_list_changed(); +} + +void SkeletonModification2DLookAt::update_target_cache() { + if (!is_setup || !stack) { + _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); + return; + } + + target_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(target_node)) { + Node *node = stack->skeleton->get_node(target_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in the scene tree!"); + target_node_cache = node->get_instance_id(); + } + } + } +} + +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() { +} diff --git a/scene/resources/skeleton_modification_2d_lookat.h b/scene/resources/skeleton_modification_2d_lookat.h new file mode 100644 index 000000000000..37c3c40f2140 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_lookat.h @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* skeleton_modification_2d_lookat.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETONMODIFICATION2DLOOKAT_H +#define SKELETONMODIFICATION2DLOOKAT_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/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 *p_list) const; + +public: + void _execute(float 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(); +}; + +#endif // SKELETONMODIFICATION2DLOOKAT_H \ No newline at end of file diff --git a/scene/resources/skeleton_modification_2d_physicalbones.cpp b/scene/resources/skeleton_modification_2d_physicalbones.cpp new file mode 100644 index 000000000000..783d23c0c6d9 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_physicalbones.cpp @@ -0,0 +1,291 @@ +/*************************************************************************/ +/* skeleton_modification_2d_physicalbones.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_modification_2d_physicalbones.h" +#include "scene/2d/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 true; +} + +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")) { + return true; // Do nothing! + } + } +#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 true; +} + +void SkeletonModification2DPhysicalBones::_get_property_list(List *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 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()) { + _print_execution_error(true, "PhysicalBone2D cache " + itos(i) + " is out of date. Attempting to update..."); + _physical_bone_update_cache(i); + continue; + } + + PhysicalBone2D *physical_bone = Object::cast_to(ObjectDB::get_instance(bone_data.physical_bone_node_cache)); + if (_print_execution_error(!physical_bone, "PhysicalBone2D not found at index " + itos(i) + "!")) { + return; + } + if (_print_execution_error(physical_bone->get_bone2d_index() < 0 || physical_bone->get_bone2d_index() > stack->skeleton->get_bone_count(), + "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) { + _print_execution_error(true, "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_COND_MSG(!stack->skeleton, "No skeleton found! Cannot fetch physical bones!"); + + physical_bone_chain.clear(); + + List node_queue = List(); + node_queue.push_back(stack->skeleton); + + while (node_queue.size() > 0) { + Node *node_to_process = node_queue[0]; + node_queue.pop_front(); + + PhysicalBone2D *potential_bone = Object::cast_to(node_to_process); + if (potential_bone) { + PhysicalBone_Data2D new_data = PhysicalBone_Data2D(); + new_data.physical_bone_node = stack->skeleton->get_path_to(potential_bone); + new_data.physical_bone_node_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 &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 &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.size() <= 0) { + for (int i = 0; i < physical_bone_chain.size(); i++) { + PhysicalBone2D *physical_bone = Object::cast_to(stack->skeleton->get_node(physical_bone_chain[i].physical_bone_node)); + 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 = Object::cast_to(ObjectDB::get_instance(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(); + enabled = true; + editor_draw_gizmo = false; // Nothing to really show in a gizmo right now. +} + +SkeletonModification2DPhysicalBones::~SkeletonModification2DPhysicalBones() { +} diff --git a/scene/resources/skeleton_modification_2d_physicalbones.h b/scene/resources/skeleton_modification_2d_physicalbones.h new file mode 100644 index 000000000000..ff276b23ee1b --- /dev/null +++ b/scene/resources/skeleton_modification_2d_physicalbones.h @@ -0,0 +1,82 @@ +/*************************************************************************/ +/* skeleton_modification_2d_physicalbones.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETONMODIFICATION2DPHYSICALBONES_H +#define SKELETONMODIFICATION2DPHYSICALBONES_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/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 physical_bone_chain; + + void _physical_bone_update_cache(int p_joint_idx); + + bool _simulation_state_dirty = false; + TypedArray _simulation_state_dirty_names; + bool _simulation_state_dirty_process; + 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 *p_list) const; + +public: + void _execute(float 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 &p_bones); + void stop_simulation(const TypedArray &p_bones); + + SkeletonModification2DPhysicalBones(); + ~SkeletonModification2DPhysicalBones(); +}; + +#endif // SKELETONMODIFICATION2DPHYSICALBONES_H \ No newline at end of file diff --git a/scene/resources/skeleton_modification_2d_stackholder.cpp b/scene/resources/skeleton_modification_2d_stackholder.cpp new file mode 100644 index 000000000000..940d8b225175 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_stackholder.cpp @@ -0,0 +1,131 @@ +/*************************************************************************/ +/* skeleton_modification_2d_stackholder.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_modification_2d_stackholder.h" +#include "scene/2d/skeleton_2d.h" + +bool SkeletonModification2DStackHolder::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + + if (path == "held_modification_stack") { + set_held_modification_stack(p_value); + } + +#ifdef TOOLS_ENABLED + if (path == "editor/draw_gizmo") { + set_editor_draw_gizmo(p_value); + } +#endif // TOOLS_ENABLED + + return true; +} + +bool SkeletonModification2DStackHolder::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + + if (path == "held_modification_stack") { + r_ret = get_held_modification_stack(); + } + +#ifdef TOOLS_ENABLED + if (path == "editor/draw_gizmo") { + r_ret = get_editor_draw_gizmo(); + } +#endif // TOOLS_ENABLED + + return true; +} + +void SkeletonModification2DStackHolder::_get_property_list(List *p_list) const { + p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } +#endif // TOOLS_ENABLED +} + +void SkeletonModification2DStackHolder::_execute(float 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(delta, execution_mode); + } +} + +void SkeletonModification2DStackHolder::_setup_modification(SkeletonModificationStack2D *p_stack) { + stack = p_stack; + + if (stack != nullptr) { + is_setup = true; + + if (held_modification_stack.is_valid()) { + held_modification_stack->set_skeleton(stack->get_skeleton()); + held_modification_stack->setup(); + } + } +} + +void SkeletonModification2DStackHolder::_draw_editor_gizmo() { + if (stack) { + if (held_modification_stack.is_valid()) { + held_modification_stack->draw_editor_gizmos(); + } + } +} + +void SkeletonModification2DStackHolder::set_held_modification_stack(Ref p_held_stack) { + held_modification_stack = p_held_stack; + + if (is_setup && held_modification_stack.is_valid()) { + held_modification_stack->set_skeleton(stack->get_skeleton()); + held_modification_stack->setup(); + } +} + +Ref SkeletonModification2DStackHolder::get_held_modification_stack() const { + return held_modification_stack; +} + +void SkeletonModification2DStackHolder::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_held_modification_stack", "held_modification_stack"), &SkeletonModification2DStackHolder::set_held_modification_stack); + ClassDB::bind_method(D_METHOD("get_held_modification_stack"), &SkeletonModification2DStackHolder::get_held_modification_stack); +} + +SkeletonModification2DStackHolder::SkeletonModification2DStackHolder() { + stack = nullptr; + is_setup = false; + enabled = true; +} + +SkeletonModification2DStackHolder::~SkeletonModification2DStackHolder() { +} diff --git a/scene/resources/skeleton_modification_2d_stackholder.h b/scene/resources/skeleton_modification_2d_stackholder.h new file mode 100644 index 000000000000..ec157ad8fa79 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_stackholder.h @@ -0,0 +1,64 @@ +/*************************************************************************/ +/* skeleton_modification_2d_stackholder.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETONMODIFICATION2DSTACKHOLDER_H +#define SKELETONMODIFICATION2DSTACKHOLDER_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/skeleton_modification_2d.h" + +/////////////////////////////////////// +// SkeletonModification2DJIGGLE +/////////////////////////////////////// + +class SkeletonModification2DStackHolder : public SkeletonModification2D { + GDCLASS(SkeletonModification2DStackHolder, SkeletonModification2D); + +protected: + static void _bind_methods(); + bool _get(const StringName &p_path, Variant &r_ret) const; + bool _set(const StringName &p_path, const Variant &p_value); + void _get_property_list(List *p_list) const; + +public: + Ref held_modification_stack; + + void _execute(float delta) override; + void _setup_modification(SkeletonModificationStack2D *p_stack) override; + void _draw_editor_gizmo() override; + + void set_held_modification_stack(Ref p_held_stack); + Ref get_held_modification_stack() const; + + SkeletonModification2DStackHolder(); + ~SkeletonModification2DStackHolder(); +}; + +#endif // SKELETONMODIFICATION2DSTACKHOLDER_H \ No newline at end of file diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp new file mode 100644 index 000000000000..c446f20e05b0 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp @@ -0,0 +1,478 @@ +/*************************************************************************/ +/* skeleton_modification_2d_twoboneik.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_modification_2d_twoboneik.h" +#include "scene/2d/skeleton_2d.h" + +#ifdef TOOLS_ENABLED +#include "editor/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 + 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 + + 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 + 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 + + return true; +} + +void SkeletonModification2DTwoBoneIK::_get_property_list(List *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 delta) { + ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, + "Modification is not setup and therefore cannot execute!"); + if (!enabled) { + return; + } + + if (target_node_cache.is_null()) { + _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + + if (joint_one_bone2d_node_cache.is_null() && !joint_one_bone2d_node.is_empty()) { + _print_execution_error(true, "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()) { + _print_execution_error(true, "Joint two Bone2D node cache is out of date. Attempting to update..."); + update_joint_two_bone2d_cache(); + } + + Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + if (_print_execution_error(!target || !target->is_inside_tree(), "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 (_print_execution_error(joint_one_bone == nullptr, "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 (_print_execution_error(joint_two_bone == nullptr, "Joint one 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_transform().get_origin() - joint_one_bone->get_global_transform().get_origin(); + float joint_one_to_target = target_difference.length(); + float angle_atan = Math::atan2(target_difference.y, target_difference.x); + + float bone_one_length = joint_one_bone->get_length() * MIN(joint_one_bone->get_global_scale().x, joint_one_bone->get_global_scale().y); + float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().x, joint_two_bone->get_global_scale().y); + bool override_angles_due_to_out_of_range = false; + + if (joint_one_to_target < target_minimum_distance) { + joint_one_to_target = target_minimum_distance; + } + if (joint_one_to_target > target_maximum_distance && target_maximum_distance > 0.0) { + joint_one_to_target = target_maximum_distance; + } + + if (bone_one_length + bone_two_length < joint_one_to_target) { + override_angles_due_to_out_of_range = true; + } + + if (!override_angles_due_to_out_of_range) { + float angle_0 = Math::acos(((joint_one_to_target * joint_one_to_target) + (bone_one_length * bone_one_length) - (bone_two_length * bone_two_length)) / (2.0 * joint_one_to_target * bone_one_length)); + float angle_1 = Math::acos(((bone_two_length * bone_two_length) + (bone_one_length * bone_one_length) - (joint_one_to_target * joint_one_to_target)) / (2.0 * bone_two_length * bone_one_length)); + + if (flip_bend_direction) { + angle_0 = -angle_0; + angle_1 = -angle_1; + } + + if (isnan(angle_0) || isnan(angle_1)) { + // We cannot solve for this angle! Do nothing to avoid setting the rotation (and scale) to NaN. + } else { + joint_one_bone->set_global_rotation(angle_atan - angle_0 - joint_one_bone->get_bone_angle()); + joint_two_bone->set_rotation(-Math_PI - angle_1 - joint_two_bone->get_bone_angle() + joint_one_bone->get_bone_angle()); + } + } else { + joint_one_bone->set_global_rotation(angle_atan - joint_one_bone->get_bone_angle()); + joint_two_bone->set_global_rotation(angle_atan - joint_two_bone->get_bone_angle()); + } + + stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, joint_one_bone->get_transform(), stack->strength, true); + stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, joint_two_bone->get_transform(), stack->strength, true); +} + +void SkeletonModification2DTwoBoneIK::_setup_modification(SkeletonModificationStack2D *p_stack) { + stack = p_stack; + + if (stack) { + is_setup = true; + update_target_cache(); + update_joint_one_bone2d_cache(); + update_joint_two_bone2d_cache(); + } +} + +void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() { + if (!enabled || !is_setup) { + return; + } + + Bone2D *operation_bone_one = stack->skeleton->get_bone(joint_one_bone_idx); + if (!operation_bone_one) { + return; + } + stack->skeleton->draw_set_transform( + stack->skeleton->get_global_transform().affine_inverse().xform(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 = EditorSettings::get_singleton()->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), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); + } else { + float angle = (Math_PI * 0.5) + operation_bone_one->get_bone_angle(); + stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); + } + +#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 = Object::cast_to(ObjectDB::get_instance(target_node_cache)); + target_direction = operation_bone_one->get_global_position().direction_to(target->get_global_position()); + } + + stack->skeleton->draw_circle(target_direction * target_minimum_distance, 8, bone_ik_color); + stack->skeleton->draw_circle(target_direction * target_maximum_distance, 8, bone_ik_color); + stack->skeleton->draw_line(target_direction * target_minimum_distance, target_direction * target_maximum_distance, bone_ik_color, 2.0); + } + } + } +#endif // TOOLS_ENABLED +} + +void SkeletonModification2DTwoBoneIK::update_target_cache() { + if (!is_setup || !stack) { + _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); + return; + } + + target_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(target_node)) { + Node *node = stack->skeleton->get_node(target_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update target cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update target cache: node is not in the scene tree!"); + target_node_cache = node->get_instance_id(); + } + } + } +} + +void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { + if (!is_setup || !stack) { + _print_execution_error(true, "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 update joint one Bone2D cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update update joint one Bone2D cache: node is not in the scene tree!"); + joint_one_bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to(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) { + _print_execution_error(true, "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 update joint two Bone2D cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update update joint two Bone2D cache: node is not in scene tree!"); + joint_two_bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to(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 { + WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint one..."); + 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 { + WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint two..."); + 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"), "set_target_minimum_distance", "get_target_minimum_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_maximum_distance", PROPERTY_HINT_NONE, "0, 100000000, 0.01"), "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() { +} diff --git a/scene/resources/skeleton_modification_2d_twoboneik.h b/scene/resources/skeleton_modification_2d_twoboneik.h new file mode 100644 index 000000000000..2e8574387dc5 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_twoboneik.h @@ -0,0 +1,107 @@ +/*************************************************************************/ +/* skeleton_modification_2d_twoboneik.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETONMODIFICATION2DTWOBONEIK_H +#define SKELETONMODIFICATION2DTWOBONEIK_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/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 *p_list) const; + +public: + void _execute(float 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(); +}; + +#endif // SKELETONMODIFICATION2DTWOBONEIK_H \ No newline at end of file diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp new file mode 100644 index 000000000000..07787ea226b6 --- /dev/null +++ b/scene/resources/skeleton_modification_stack_2d.cpp @@ -0,0 +1,267 @@ +/*************************************************************************/ +/* skeleton_modification_stack_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_modification_stack_2d.h" +#include "scene/2d/skeleton_2d.h" + +void SkeletonModificationStack2D::_get_property_list(List *p_list) const { + for (int i = 0; i < modifications.size(); i++) { + p_list->push_back( + PropertyInfo(Variant::OBJECT, "modifications/" + itos(i), + PROPERTY_HINT_RESOURCE_TYPE, + "SkeletonModification2D", + PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + } +} + +bool SkeletonModificationStack2D::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + + if (path.begins_with("modifications/")) { + int mod_idx = path.get_slicec('/', 1).to_int(); + set_modification(mod_idx, p_value); + return true; + } + return true; +} + +bool SkeletonModificationStack2D::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + + if (path.begins_with("modifications/")) { + int mod_idx = path.get_slicec('/', 1).to_int(); + r_ret = get_modification(mod_idx); + return true; + } + return true; +} + +void SkeletonModificationStack2D::setup() { + if (is_setup) { + return; + } + + if (skeleton != nullptr) { + is_setup = true; + for (int i = 0; i < modifications.size(); i++) { + if (!modifications[i].is_valid()) { + continue; + } + modifications.get(i)->_setup_modification(this); + } + +#ifdef TOOLS_ENABLED + set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED + + } else { + WARN_PRINT("Cannot setup SkeletonModificationStack2D: no Skeleton2D set!"); + } +} + +void SkeletonModificationStack2D::execute(float delta, int p_execution_mode) { + ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr || is_queued_for_deletion(), + "Modification stack is not properly setup and therefore cannot execute!"); + + if (!skeleton->is_inside_tree()) { + ERR_PRINT_ONCE("Skeleton is not inside SceneTree! Cannot execute modification!"); + return; + } + + if (!enabled) { + return; + } + + for (int i = 0; i < modifications.size(); i++) { + if (!modifications[i].is_valid()) { + continue; + } + + if (modifications[i]->get_execution_mode() == p_execution_mode) { + modifications.get(i)->_execute(delta); + } + } +} + +void SkeletonModificationStack2D::draw_editor_gizmos() { + if (!is_setup) { + return; + } + + if (editor_gizmo_dirty) { + for (int i = 0; i < modifications.size(); i++) { + if (!modifications[i].is_valid()) { + continue; + } + + if (modifications[i]->editor_draw_gizmo) { + modifications.get(i)->_draw_editor_gizmo(); + } + } + skeleton->draw_set_transform(Vector2(0, 0)); + editor_gizmo_dirty = false; + } +} + +void SkeletonModificationStack2D::set_editor_gizmos_dirty(bool p_dirty) { + if (!is_setup) { + return; + } + + if (!editor_gizmo_dirty && p_dirty) { + editor_gizmo_dirty = p_dirty; + if (skeleton) { + skeleton->update(); + } + } else { + editor_gizmo_dirty = p_dirty; + } +} + +void SkeletonModificationStack2D::enable_all_modifications(bool p_enabled) { + for (int i = 0; i < modifications.size(); i++) { + if (!modifications[i].is_valid()) { + continue; + } + modifications.get(i)->set_enabled(p_enabled); + } +} + +Ref SkeletonModificationStack2D::get_modification(int p_mod_idx) const { + ERR_FAIL_INDEX_V(p_mod_idx, modifications.size(), nullptr); + return modifications[p_mod_idx]; +} + +void SkeletonModificationStack2D::add_modification(Ref p_mod) { + 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(p_mod_idx); + +#ifdef TOOLS_ENABLED + set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED +} + +void SkeletonModificationStack2D::set_modification(int p_mod_idx, Ref p_mod) { + ERR_FAIL_INDEX(p_mod_idx, modifications.size()); + + if (p_mod == nullptr) { + modifications.insert(p_mod_idx, nullptr); + } else { + p_mod->_setup_modification(this); + modifications.insert(p_mod_idx, p_mod); + } + +#ifdef TOOLS_ENABLED + set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED +} + +void SkeletonModificationStack2D::set_modification_count(int p_count) { + 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"), &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"), "set_modification_count", "get_modification_count"); +} + +SkeletonModificationStack2D::SkeletonModificationStack2D() { +} diff --git a/scene/resources/skeleton_modification_stack_2d.h b/scene/resources/skeleton_modification_stack_2d.h new file mode 100644 index 000000000000..9c12ac00f99f --- /dev/null +++ b/scene/resources/skeleton_modification_stack_2d.h @@ -0,0 +1,99 @@ +/*************************************************************************/ +/* skeleton_modification_stack_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETONMODIFICATIONSTACK2D_H +#define SKELETONMODIFICATIONSTACK2D_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/skeleton_modification_2d.h" + +/////////////////////////////////////// +// SkeletonModificationStack2D +/////////////////////////////////////// + +class Skeleton2D; +class SkeletonModification2D; +class Bone2D; + +class SkeletonModificationStack2D : public Resource { + GDCLASS(SkeletonModificationStack2D, Resource); + friend class Skeleton2D; + friend class SkeletonModification2D; + +protected: + static void _bind_methods(); + void _get_property_list(List *p_list) const; + bool _set(const StringName &p_path, const Variant &p_value); + bool _get(const StringName &p_path, Variant &r_ret) const; + +public: + Skeleton2D *skeleton = nullptr; + bool is_setup = false; + bool enabled = false; + float strength = 1.0; + + enum EXECUTION_MODE { + execution_mode_process, + execution_mode_physics_process + }; + + Vector> modifications = Vector>(); + + void setup(); + void execute(float delta, int p_execution_mode); + + bool editor_gizmo_dirty = false; + void draw_editor_gizmos(); + void set_editor_gizmos_dirty(bool p_dirty); + + void enable_all_modifications(bool p_enable); + Ref get_modification(int p_mod_idx) const; + void add_modification(Ref p_mod); + void delete_modification(int p_mod_idx); + void set_modification(int p_mod_idx, Ref p_mod); + + void set_modification_count(int p_count); + int get_modification_count() const; + + void set_skeleton(Skeleton2D *p_skeleton); + Skeleton2D *get_skeleton() const; + + bool get_is_setup() const; + + void set_enabled(bool p_enabled); + bool get_enabled() const; + + void set_strength(float p_strength); + float get_strength() const; + + SkeletonModificationStack2D(); +}; + +#endif // SKELETONMODIFICATION2D_H \ No newline at end of file From 29c8e8107a80fa65826f717235c5680b7aa4f7cb Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Tue, 16 Mar 2021 18:17:36 -0400 Subject: [PATCH 28/34] Removed _print_execution_error and instead use ERR_PRINT_ONCE with normal conditions for maximum compatability with how the code was written before. Changed errors about updating the cache to warnings. Fixed a typo. Added a compile warning about the Bone2D gizmo drawing code Added missing newlines on the header files for 2D IK modifications --- scene/2d/skeleton_2d.cpp | 3 +++ scene/resources/skeleton_modification_2d.cpp | 11 --------- .../skeleton_modification_2d_ccdik.cpp | 19 ++++++++------- .../skeleton_modification_2d_fabrik.cpp | 23 +++++++++++-------- .../skeleton_modification_2d_fabrik.h | 2 +- .../skeleton_modification_2d_jiggle.cpp | 19 +++++++-------- .../skeleton_modification_2d_jiggle.h | 2 +- .../skeleton_modification_2d_lookat.cpp | 17 ++++++++------ .../skeleton_modification_2d_lookat.h | 2 +- ...skeleton_modification_2d_physicalbones.cpp | 11 +++++---- .../skeleton_modification_2d_physicalbones.h | 2 +- .../skeleton_modification_2d_stackholder.h | 2 +- .../skeleton_modification_2d_twoboneik.cpp | 21 +++++++++-------- .../skeleton_modification_2d_twoboneik.h | 2 +- .../skeleton_modification_stack_2d.h | 2 +- 15 files changed, 73 insertions(+), 65 deletions(-) diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 67708df9dd3a..68c6521997fe 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -187,6 +187,9 @@ void Bone2D::_notification(int p_what) { } // Bone2D Editor gizmo drawing: +#ifdef _MSC_VER +#warning TODO Bone2D gizmo drawing needs to be moved to an editor plugin +#endif else if (p_what == NOTIFICATION_DRAW) { // Only draw the gizmo in the editor! if (Engine::get_singleton()->is_editor_hint() == false) { diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index e09e845b2b37..cdc2ddc6b119 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -65,17 +65,6 @@ void SkeletonModification2D::_draw_editor_gizmo() { call("_draw_editor_gizmo"); } -bool SkeletonModification2D::_print_execution_error(bool p_condition, String p_message) { - if (!is_setup) { - return p_condition; - } - - if (p_condition) { - ERR_PRINT_ONCE(p_message); - } - return p_condition; -} - void SkeletonModification2D::set_enabled(bool p_enabled) { enabled = p_enabled; diff --git a/scene/resources/skeleton_modification_2d_ccdik.cpp b/scene/resources/skeleton_modification_2d_ccdik.cpp index d075239edf0e..16501ea298f2 100644 --- a/scene/resources/skeleton_modification_2d_ccdik.cpp +++ b/scene/resources/skeleton_modification_2d_ccdik.cpp @@ -161,23 +161,25 @@ void SkeletonModification2DCCDIK::_execute(float delta) { } if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); update_target_cache(); return; } if (tip_node_cache.is_null()) { - _print_execution_error(true, "Tip cache is out of date. Attempting to update..."); + WARN_PRINT_ONCE("Tip cache is out of date. Attempting to update..."); update_tip_cache(); return; } Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + if (!target || !target->is_inside_tree()) { + ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); return; } Node2D *tip = Object::cast_to(ObjectDB::get_instance(tip_node_cache)); - if (_print_execution_error(!tip || !tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) { + if (!tip || !tip->is_inside_tree()) { + ERR_PRINT_ONCE("Tip node is not in the scene tree. Cannot execute modification!"); return; } @@ -188,7 +190,8 @@ void SkeletonModification2DCCDIK::_execute(float delta) { void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D *target, Node2D *tip) { CCDIK_Joint_Data2D ccdik_data = ccdik_data_chain[p_joint_idx]; - if (_print_execution_error(ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > stack->skeleton->get_bone_count(), "2D CCDIK joint: bone index not found!")) { + 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; } @@ -259,7 +262,7 @@ void SkeletonModification2DCCDIK::_draw_editor_gizmo() { void SkeletonModification2DCCDIK::update_target_cache() { if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); + ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); return; } @@ -280,7 +283,7 @@ void SkeletonModification2DCCDIK::update_target_cache() { void SkeletonModification2DCCDIK::update_tip_cache() { if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!"); + ERR_PRINT_ONCE("Cannot update tip cache: modification is not properly setup!"); return; } @@ -302,7 +305,7 @@ void SkeletonModification2DCCDIK::update_tip_cache() { void SkeletonModification2DCCDIK::ccdik_joint_update_bone2d_cache(int p_joint_idx) { ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "Cannot update bone2d cache: joint index out of range!"); if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update CCDIK Bone2D cache: modification is not properly setup!"); + ERR_PRINT_ONCE("Cannot update CCDIK Bone2D cache: modification is not properly setup!"); return; } diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp index 36b78b683831..73ee63ae2f9e 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.cpp +++ b/scene/resources/skeleton_modification_2d_fabrik.cpp @@ -103,17 +103,19 @@ void SkeletonModification2DFABRIK::_execute(float delta) { } if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); update_target_cache(); return; } - if (_print_execution_error(fabrik_data_chain.size() <= 1, "FABRIK requires at least two joints to operate! Cannot execute modification!")) { + if (fabrik_data_chain.size() <= 1) { + ERR_PRINT_ONCE("FABRIK requires at least two joints to operate! Cannot execute modification!"); return; } Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + 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(); @@ -124,7 +126,8 @@ void SkeletonModification2DFABRIK::_execute(float delta) { } Bone2D *origin_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[0].bone2d_node_cache)); - if (_print_execution_error(!origin_bone2d_node || !origin_bone2d_node->is_inside_tree(), "Origin joint's Bone2D node is not in the scene tree. Cannot execute modification!")) { + 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; } @@ -137,11 +140,12 @@ void SkeletonModification2DFABRIK::_execute(float delta) { 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()) { - _print_execution_error(true, "Bone2D cache for joint " + itos(i) + " is out of date.. Attempting to update..."); + WARN_PRINT_ONCE("Bone2D cache for joint " + itos(i) + " is out of date.. Attempting to update..."); fabrik_joint_update_bone2d_cache(i); } Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); - if (_print_execution_error(!joint_bone2d_node, "FABRIK Joint " + itos(i) + " does not have a Bone2D node set! Cannot execute modification!")) { + 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(); @@ -186,7 +190,8 @@ void SkeletonModification2DFABRIK::_execute(float delta) { // Apply all of the saved transforms to the Bone2D nodes for (int i = 0; i < fabrik_data_chain.size(); i++) { Bone2D *joint_bone2d_node = Object::cast_to(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); - if (_print_execution_error(!joint_bone2d_node, "FABRIK Joint " + itos(i) + " does not have a Bone2D node set!")) { + 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]; @@ -282,7 +287,7 @@ void SkeletonModification2DFABRIK::_setup_modification(SkeletonModificationStack void SkeletonModification2DFABRIK::update_target_cache() { if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); + ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); return; } @@ -304,7 +309,7 @@ void SkeletonModification2DFABRIK::update_target_cache() { void SkeletonModification2DFABRIK::fabrik_joint_update_bone2d_cache(int p_joint_idx) { ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "Cannot update bone2d cache: joint index out of range!"); if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update FABRIK Bone2D cache: modification is not properly setup!"); + ERR_PRINT_ONCE("Cannot update FABRIK Bone2D cache: modification is not properly setup!"); return; } diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/resources/skeleton_modification_2d_fabrik.h index b566322edf2f..1d4514b432c0 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.h +++ b/scene/resources/skeleton_modification_2d_fabrik.h @@ -105,4 +105,4 @@ class SkeletonModification2DFABRIK : public SkeletonModification2D { ~SkeletonModification2DFABRIK(); }; -#endif // SKELETONMODIFICATION2DFABRIK_H \ No newline at end of file +#endif // SKELETONMODIFICATION2DFABRIK_H diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp index 4b63709ba787..47487ef5b06c 100644 --- a/scene/resources/skeleton_modification_2d_jiggle.cpp +++ b/scene/resources/skeleton_modification_2d_jiggle.cpp @@ -135,12 +135,13 @@ void SkeletonModification2DJiggle::_execute(float delta) { return; } if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); update_target_cache(); return; } Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + if (!target || !target->is_inside_tree()) { + ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); return; } @@ -153,19 +154,19 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D // Adopted from: https://wiki.unity3d.com/index.php/JiggleBone // With modifications by TwistedTwigleg. - if (_print_execution_error( - jiggle_data_chain[p_joint_idx].bone_idx <= -1 || jiggle_data_chain[p_joint_idx].bone_idx > stack->skeleton->get_bone_count(), - "Jiggle joint " + itos(p_joint_idx) + " bone index is invalid. Cannot execute modification on joint...")) { + 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()) { - _print_execution_error(true, "Bone2D cache for joint " + itos(p_joint_idx) + " is out of date. Updating..."); + 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 (_print_execution_error(!operation_bone, "Jiggle joint " + itos(p_joint_idx) + " does not have a Bone2D node or it cannot be found!")) { + if (!operation_bone) { + ERR_PRINT_ONCE("Jiggle joint " + itos(p_joint_idx) + " does not have a Bone2D node or it cannot be found!"); return; } @@ -254,7 +255,7 @@ void SkeletonModification2DJiggle::_setup_modification(SkeletonModificationStack void SkeletonModification2DJiggle::update_target_cache() { if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); + ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); return; } @@ -276,7 +277,7 @@ void SkeletonModification2DJiggle::update_target_cache() { 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) { - _print_execution_error(true, "Cannot update Jiggle " + itos(p_joint_idx) + " Bone2D cache: modification is not properly setup!"); + ERR_PRINT_ONCE("Cannot update Jiggle " + itos(p_joint_idx) + " Bone2D cache: modification is not properly setup!"); return; } diff --git a/scene/resources/skeleton_modification_2d_jiggle.h b/scene/resources/skeleton_modification_2d_jiggle.h index 81dfd99a5dca..247e0a147e6a 100644 --- a/scene/resources/skeleton_modification_2d_jiggle.h +++ b/scene/resources/skeleton_modification_2d_jiggle.h @@ -136,4 +136,4 @@ class SkeletonModification2DJiggle : public SkeletonModification2D { ~SkeletonModification2DJiggle(); }; -#endif // SKELETONMODIFICATION2DJIGGLE_H \ No newline at end of file +#endif // SKELETONMODIFICATION2DJIGGLE_H diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/resources/skeleton_modification_2d_lookat.cpp index 1ffd05a882b4..4fc429a7c45d 100644 --- a/scene/resources/skeleton_modification_2d_lookat.cpp +++ b/scene/resources/skeleton_modification_2d_lookat.cpp @@ -112,29 +112,32 @@ void SkeletonModification2DLookAt::_execute(float delta) { } if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + 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(); - _print_execution_error(true, "Bone2D node cache is out of date. Attempting to update..."); + WARN_PRINT_ONCE("Bone2D node cache is out of date. Attempting to update..."); return; } if (target_node_reference == nullptr) { target_node_reference = Object::cast_to(ObjectDB::get_instance(target_node_cache)); } - if (_print_execution_error(!target_node_reference || !target_node_reference->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + 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 (_print_execution_error(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!")) { + 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 (_print_execution_error(operation_bone == nullptr, "bone_idx for modification does not point to a valid bone! Cannot execute modification")) { + if (operation_bone == nullptr) { + ERR_PRINT_ONCE("bone_idx for modification does not point to a valid bone! Cannot execute modification"); return; } @@ -193,7 +196,7 @@ void SkeletonModification2DLookAt::_draw_editor_gizmo() { void SkeletonModification2DLookAt::update_bone2d_cache() { if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update Bone2D cache: modification is not properly setup!"); + ERR_PRINT_ONCE("Cannot update Bone2D cache: modification is not properly setup!"); return; } @@ -258,7 +261,7 @@ void SkeletonModification2DLookAt::set_bone_index(int p_bone_idx) { void SkeletonModification2DLookAt::update_target_cache() { if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); + ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); return; } diff --git a/scene/resources/skeleton_modification_2d_lookat.h b/scene/resources/skeleton_modification_2d_lookat.h index 37c3c40f2140..0576d6db8989 100644 --- a/scene/resources/skeleton_modification_2d_lookat.h +++ b/scene/resources/skeleton_modification_2d_lookat.h @@ -97,4 +97,4 @@ class SkeletonModification2DLookAt : public SkeletonModification2D { ~SkeletonModification2DLookAt(); }; -#endif // SKELETONMODIFICATION2DLOOKAT_H \ No newline at end of file +#endif // SKELETONMODIFICATION2DLOOKAT_H diff --git a/scene/resources/skeleton_modification_2d_physicalbones.cpp b/scene/resources/skeleton_modification_2d_physicalbones.cpp index 783d23c0c6d9..ced2ed124b56 100644 --- a/scene/resources/skeleton_modification_2d_physicalbones.cpp +++ b/scene/resources/skeleton_modification_2d_physicalbones.cpp @@ -113,17 +113,18 @@ void SkeletonModification2DPhysicalBones::_execute(float delta) { 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()) { - _print_execution_error(true, "PhysicalBone2D cache " + itos(i) + " is out of date. Attempting to update..."); + WARN_PRINT_ONCE("PhysicalBone2D cache " + itos(i) + " is out of date. Attempting to update..."); _physical_bone_update_cache(i); continue; } PhysicalBone2D *physical_bone = Object::cast_to(ObjectDB::get_instance(bone_data.physical_bone_node_cache)); - if (_print_execution_error(!physical_bone, "PhysicalBone2D not found at index " + itos(i) + "!")) { + if (!physical_bone) { + ERR_PRINT_ONCE("PhysicalBone2D not found at index " + itos(i) + "!"); return; } - if (_print_execution_error(physical_bone->get_bone2d_index() < 0 || physical_bone->get_bone2d_index() > stack->skeleton->get_bone_count(), - "PhysicalBone2D at index " + itos(i) + " has invalid Bone2D!")) { + 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()); @@ -152,7 +153,7 @@ void SkeletonModification2DPhysicalBones::_setup_modification(SkeletonModificati 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) { - _print_execution_error(true, "Cannot update PhysicalBone2D cache: modification is not properly setup!"); + ERR_PRINT_ONCE("Cannot update PhysicalBone2D cache: modification is not properly setup!"); return; } diff --git a/scene/resources/skeleton_modification_2d_physicalbones.h b/scene/resources/skeleton_modification_2d_physicalbones.h index ff276b23ee1b..5d3d9c73b660 100644 --- a/scene/resources/skeleton_modification_2d_physicalbones.h +++ b/scene/resources/skeleton_modification_2d_physicalbones.h @@ -79,4 +79,4 @@ class SkeletonModification2DPhysicalBones : public SkeletonModification2D { ~SkeletonModification2DPhysicalBones(); }; -#endif // SKELETONMODIFICATION2DPHYSICALBONES_H \ No newline at end of file +#endif // SKELETONMODIFICATION2DPHYSICALBONES_H diff --git a/scene/resources/skeleton_modification_2d_stackholder.h b/scene/resources/skeleton_modification_2d_stackholder.h index ec157ad8fa79..44a9be1387af 100644 --- a/scene/resources/skeleton_modification_2d_stackholder.h +++ b/scene/resources/skeleton_modification_2d_stackholder.h @@ -61,4 +61,4 @@ class SkeletonModification2DStackHolder : public SkeletonModification2D { ~SkeletonModification2DStackHolder(); }; -#endif // SKELETONMODIFICATION2DSTACKHOLDER_H \ No newline at end of file +#endif // SKELETONMODIFICATION2DSTACKHOLDER_H diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp index c446f20e05b0..c4ea77ff6272 100644 --- a/scene/resources/skeleton_modification_2d_twoboneik.cpp +++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp @@ -106,32 +106,35 @@ void SkeletonModification2DTwoBoneIK::_execute(float delta) { } if (target_node_cache.is_null()) { - _print_execution_error(true, "Target cache is out of date. Attempting to update..."); + 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()) { - _print_execution_error(true, "Joint one Bone2D node cache is out of date. Attempting to update..."); + 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()) { - _print_execution_error(true, "Joint two Bone2D node cache is out of date. Attempting to update..."); + WARN_PRINT_ONCE("Joint two Bone2D node cache is out of date. Attempting to update..."); update_joint_two_bone2d_cache(); } Node2D *target = Object::cast_to(ObjectDB::get_instance(target_node_cache)); - if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) { + 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 (_print_execution_error(joint_one_bone == nullptr, "Joint one bone_idx does not point to a valid bone! Cannot execute modification!")) { + 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 (_print_execution_error(joint_two_bone == nullptr, "Joint one bone_idx does not point to a valid bone! Cannot execute modification!")) { + if (joint_two_bone == nullptr) { + ERR_PRINT_ONCE("Joint two bone_idx does not point to a valid bone! Cannot execute modification!"); return; } @@ -243,7 +246,7 @@ void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() { void SkeletonModification2DTwoBoneIK::update_target_cache() { if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update target cache: modification is not properly setup!"); + ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!"); return; } @@ -264,7 +267,7 @@ void SkeletonModification2DTwoBoneIK::update_target_cache() { void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update joint one Bone2D cache: modification is not properly setup!"); + ERR_PRINT_ONCE("Cannot update joint one Bone2D cache: modification is not properly setup!"); return; } @@ -292,7 +295,7 @@ void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { void SkeletonModification2DTwoBoneIK::update_joint_two_bone2d_cache() { if (!is_setup || !stack) { - _print_execution_error(true, "Cannot update joint two Bone2D cache: modification is not properly setup!"); + ERR_PRINT_ONCE("Cannot update joint two Bone2D cache: modification is not properly setup!"); return; } diff --git a/scene/resources/skeleton_modification_2d_twoboneik.h b/scene/resources/skeleton_modification_2d_twoboneik.h index 2e8574387dc5..8b5f876a1412 100644 --- a/scene/resources/skeleton_modification_2d_twoboneik.h +++ b/scene/resources/skeleton_modification_2d_twoboneik.h @@ -104,4 +104,4 @@ class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { ~SkeletonModification2DTwoBoneIK(); }; -#endif // SKELETONMODIFICATION2DTWOBONEIK_H \ No newline at end of file +#endif // SKELETONMODIFICATION2DTWOBONEIK_H diff --git a/scene/resources/skeleton_modification_stack_2d.h b/scene/resources/skeleton_modification_stack_2d.h index 9c12ac00f99f..59d9f43a046e 100644 --- a/scene/resources/skeleton_modification_stack_2d.h +++ b/scene/resources/skeleton_modification_stack_2d.h @@ -96,4 +96,4 @@ class SkeletonModificationStack2D : public Resource { SkeletonModificationStack2D(); }; -#endif // SKELETONMODIFICATION2D_H \ No newline at end of file +#endif // SKELETONMODIFICATION2D_H From b3bda2d2e1a3d912e774ba379ae2423b582d1cf5 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Tue, 16 Mar 2021 19:39:43 -0400 Subject: [PATCH 29/34] Fixing small issue that occured after rebase. Not sure what happened, but it may be I accidentally deleted the line when fixing the merge conflicts Edit: Fixed code format issue in Skeleton_2d.cpp Edit 2: Fixed code format issue in register_scene_types.cpp --- editor/plugins/canvas_item_editor_plugin.h | 2 ++ scene/2d/skeleton_2d.cpp | 1 - scene/register_scene_types.cpp | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index bb648c681b2c..c6ce03ae134b 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -632,6 +632,8 @@ class CanvasItemEditor : public VBoxContainer { bool is_anchors_mode_enabled() { return anchors_mode; }; + EditorSelection *editor_selection; + CanvasItemEditor(EditorNode *p_editor); }; diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 68c6521997fe..8ad3533813c9 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -185,7 +185,6 @@ void Bone2D::_notification(int p_what) { set_transform(cache_transform); cache_transform = tmp_trans; } - // Bone2D Editor gizmo drawing: #ifdef _MSC_VER #warning TODO Bone2D gizmo drawing needs to be moved to an editor plugin diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 41b474d19e6b..409ad066e5fe 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -163,14 +163,14 @@ #include "scene/resources/resource_format_text.h" #include "scene/resources/segment_shape_2d.h" #include "scene/resources/skeleton_modification_2d.h" -#include "scene/resources/skeleton_modification_stack_2d.h" -#include "scene/resources/skeleton_modification_2d_lookat.h" #include "scene/resources/skeleton_modification_2d_ccdik.h" #include "scene/resources/skeleton_modification_2d_fabrik.h" #include "scene/resources/skeleton_modification_2d_jiggle.h" -#include "scene/resources/skeleton_modification_2d_twoboneik.h" +#include "scene/resources/skeleton_modification_2d_lookat.h" #include "scene/resources/skeleton_modification_2d_physicalbones.h" #include "scene/resources/skeleton_modification_2d_stackholder.h" +#include "scene/resources/skeleton_modification_2d_twoboneik.h" +#include "scene/resources/skeleton_modification_stack_2d.h" #include "scene/resources/sky.h" #include "scene/resources/sky_material.h" #include "scene/resources/sphere_shape_3d.h" From aea42d0f148842b0ed9cf6be56f5da9af09d159c Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Sun, 21 Mar 2021 15:03:47 -0400 Subject: [PATCH 30/34] Expose looking_at and set_rotation in Transform2D to GDScript, fix the code in Skeleton2D, fix documentation issues --- core/variant/variant_call.cpp | 2 ++ doc/classes/Transform2D.xml | 2 ++ scene/2d/skeleton_2d.cpp | 5 ++--- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 8c1d8066d6e0..df1656356ad4 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1361,6 +1361,8 @@ static void _register_variant_builtin_methods() { bind_method(Transform2D, basis_xform_inv, sarray("v"), varray()); bind_method(Transform2D, interpolate_with, sarray("xform", "weight"), varray()); bind_method(Transform2D, is_equal_approx, sarray("xform"), varray()); + bind_method(Transform2D, set_rotation, sarray("rotation"), varray()); + bind_method(Transform2D, looking_at, sarray("target"), varray(Transform2D())); /* Basis */ diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index 5f7e3a5951d4..1405fc615e49 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -183,6 +183,8 @@ + + diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 8ad3533813c9..dd52fe3c1633 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -186,7 +186,7 @@ void Bone2D::_notification(int p_what) { cache_transform = tmp_trans; } // Bone2D Editor gizmo drawing: -#ifdef _MSC_VER +#ifndef _MSC_VER #warning TODO Bone2D gizmo drawing needs to be moved to an editor plugin #endif else if (p_what == NOTIFICATION_DRAW) { @@ -677,9 +677,8 @@ void Skeleton2D::_notification(int p_what) { execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process); } } - #ifdef TOOLS_ENABLED - if (p_what == NOTIFICATION_DRAW) { + else if (p_what == NOTIFICATION_DRAW) { if (Engine::get_singleton()->is_editor_hint()) { if (modification_stack.is_valid()) { modification_stack->draw_editor_gizmos(); From 022d1a3559ff0ac626bbf5453cc592db951004e6 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Fri, 26 Mar 2021 17:18:11 -0400 Subject: [PATCH 31/34] Missed a merge conflict on the rebase. --- scene/2d/skeleton_2d.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index fc0631be3601..dbe696e9d2aa 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -47,14 +47,10 @@ class Bone2D : public Node2D { Bone2D *parent_bone = nullptr; Skeleton2D *skeleton = nullptr; Transform2D rest; -<<<<<<< HEAD - real_t default_length = 16.0; -======= bool autocalculate_length_and_angle = true; float length = 16; float bone_angle = 0; ->>>>>>> Initial groundwork (squashed from verbose branch: https://github.com/TwistedTwigleg/godot/tree/GSOC_2020_Working_Branch_2D_IK_VERBOSE_ARCHIVE ) int skeleton_index = -1; From 6db374199501be48c959bc1bd6e909e2d37943f5 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Fri, 26 Mar 2021 17:55:03 -0400 Subject: [PATCH 32/34] Changes based on review feedback: * Changed documentation wording * Removed the deprecated length functions from Bone2D and added compatibility through _set and _get * Changed parameters in functions to use p_parameter * (Maybe other changes? I don't remember. All changes are based on review feedback) --- core/math/transform_2d.cpp | 4 +- core/math/transform_2d.h | 2 +- doc/classes/Bone2D.xml | 22 +------ doc/classes/PhysicalBone2D.xml | 12 ++-- doc/classes/SkeletonModification2D.xml | 4 +- doc/classes/SkeletonModification2DJiggle.xml | 2 +- .../SkeletonModification2DTwoBoneIK.xml | 2 +- doc/classes/SkeletonModificationStack2D.xml | 2 +- scene/2d/skeleton_2d.cpp | 4 ++ scene/main/node.h | 3 +- scene/resources/skeleton_modification_2d.cpp | 57 ++++++++++--------- scene/resources/skeleton_modification_2d.h | 6 +- .../skeleton_modification_2d_ccdik.cpp | 10 ++-- .../skeleton_modification_2d_ccdik.h | 4 +- .../skeleton_modification_2d_fabrik.cpp | 2 +- .../skeleton_modification_2d_fabrik.h | 2 +- .../skeleton_modification_2d_jiggle.cpp | 12 ++-- .../skeleton_modification_2d_jiggle.h | 4 +- .../skeleton_modification_2d_lookat.cpp | 2 +- .../skeleton_modification_2d_lookat.h | 2 +- ...skeleton_modification_2d_physicalbones.cpp | 2 +- .../skeleton_modification_2d_physicalbones.h | 2 +- .../skeleton_modification_2d_stackholder.cpp | 4 +- .../skeleton_modification_2d_stackholder.h | 2 +- .../skeleton_modification_2d_twoboneik.cpp | 2 +- .../skeleton_modification_2d_twoboneik.h | 2 +- .../skeleton_modification_stack_2d.cpp | 4 +- .../skeleton_modification_stack_2d.h | 2 +- 28 files changed, 83 insertions(+), 95 deletions(-) diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 5f5d8a766a96..d1e3d2f6ea3f 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -158,9 +158,9 @@ bool Transform2D::is_equal_approx(const Transform2D &p_transform) const { return elements[0].is_equal_approx(p_transform.elements[0]) && elements[1].is_equal_approx(p_transform.elements[1]) && elements[2].is_equal_approx(p_transform.elements[2]); } -Transform2D Transform2D::looking_at(const Vector2 target) const { +Transform2D Transform2D::looking_at(const Vector2 p_target) const { Transform2D return_trans = Transform2D(get_rotation(), get_origin()); - Vector2 target_position = affine_inverse().xform(target); + Vector2 target_position = affine_inverse().xform(p_target); return_trans.set_rotation(return_trans.get_rotation() + (target_position * get_scale()).angle()); return return_trans; } diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index cf803b0e9d5f..8663c30db5ce 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -100,7 +100,7 @@ struct Transform2D { Transform2D orthonormalized() const; bool is_equal_approx(const Transform2D &p_transform) const; - Transform2D looking_at(const Vector2 target) const; + Transform2D looking_at(const Vector2 p_target) const; bool operator==(const Transform2D &p_transform) const; bool operator!=(const Transform2D &p_transform) const; diff --git a/doc/classes/Bone2D.xml b/doc/classes/Bone2D.xml index bc393f5ad0e9..88f7c55e614f 100644 --- a/doc/classes/Bone2D.xml +++ b/doc/classes/Bone2D.xml @@ -23,7 +23,7 @@ - Returns whether this [code]Bone2D[/code] node is going to autocalculate it's length and bone angle using its first [code]Bone2D[/code] child node, if one exists. If there are no [code]Bone2D[/code] children, then it cannot autocalculate these values and will print a warning. + Returns whether this [code]Bone2D[/code] node is going to autocalculate its length and bone angle using its first [code]Bone2D[/code] child node, if one exists. If there are no [code]Bone2D[/code] children, then it cannot autocalculate these values and will print a warning. @@ -31,14 +31,7 @@ Returns the angle of the bone in the [code]Bone2D[/code] node. - [b]Note[/b]: This is different from the [code]Bone2D[/code]'s rotation. The bone angle is the rotation of the bone shown by the [code]Bone2D[/code] gizmo, and because [code]Bone2D[/code] bones are based on positions, this can vary from the actual rotation of the [code]Bone2D[/code] node. - - - - - - - [i]Deprecated soon.[/i] Please use [code]get_length[/code] instead. + [b]Note:[/b] This is different from the [code]Bone2D[/code]'s rotation. The bone angle is the rotation of the bone shown by the [code]Bone2D[/code] gizmo, and because [code]Bone2D[/code] bones are based on positions, this can vary from the actual rotation of the [code]Bone2D[/code] node. @@ -78,16 +71,7 @@ Sets the bone angle for the [code]Bone2D[/code] node. This is typically set to the rotation from the [code]Bone2D[/code] node to a child [code]Bone2D[/code] node. - [b]Note[/b]: This is different from the [code]Bone2D[/code]'s rotation. The bone angle is the rotation of the bone shown by the [code]Bone2D[/code] gizmo, and because [code]Bone2D[/code] bones are based on positions, this can vary from the actual rotation of the [code]Bone2D[/code] node. - - - - - - - - - [i]Deprecated soon.[/i] Please use [code]set_length[/code] instead. + [b]Note:[/b] This is different from the [code]Bone2D[/code]'s rotation. The bone angle is the rotation of the bone shown by the [code]Bone2D[/code] gizmo, and because [code]Bone2D[/code] bones are based on positions, this can vary from the actual rotation of the [code]Bone2D[/code] node. diff --git a/doc/classes/PhysicalBone2D.xml b/doc/classes/PhysicalBone2D.xml index f3f32ac4f00e..cea75bad52e7 100644 --- a/doc/classes/PhysicalBone2D.xml +++ b/doc/classes/PhysicalBone2D.xml @@ -5,8 +5,8 @@ The [code]PhysicalBone2D[/code] node is a [RigidBody2D]-based node that can be used to make [Bone2D] nodes in a [Skeleton2D] react to physics. This node is very similar to the [PhysicalBone3D] node, just for 2D instead of 3D. - [b]Note[/b]: To have the Bone2D nodes visually follow the [code]PhysicalBone2D[/code] node, use a [SkeletonModification2DPhysicalBones] modification on the [Skeleton2D] node with the [Bone2D] nodes. - [b]Note[/b]: The PhysicalBone2D node does not automatically create a [Joint2D] node to keep [code]PhysicalBone2D[/code] nodes together. You will need to create these manually. For most cases, you want to use a [PinJoint2D] node. The [code]PhysicalBone2D[/code] node can automatically configure the [Joint2D] node once it's been created as a child node. + [b]Note:[/b] To have the Bone2D nodes visually follow the [code]PhysicalBone2D[/code] node, use a [SkeletonModification2DPhysicalBones] modification on the [Skeleton2D] node with the [Bone2D] nodes. + [b]Note:[/b] The PhysicalBone2D node does not automatically create a [Joint2D] node to keep [code]PhysicalBone2D[/code] nodes together. You will need to create these manually. For most cases, you want to use a [PinJoint2D] node. The [code]PhysicalBone2D[/code] node can automatically configure the [Joint2D] node once it's been created as a child node. @@ -28,7 +28,7 @@ - When true, the [code]PhysicalBone2D[/code] node will automatically configure the first [Joint2D] child node. The automatic configuration is limited to setting up the node properties and positioning the [Joint2D]. + If [code]true[/code], the [code]PhysicalBone2D[/code] node will automatically configure the first [Joint2D] child node. The automatic configuration is limited to setting up the node properties and positioning the [Joint2D]. The index of the [Bone2D] node that this [code]PhysicalBone2D[/code] node is supposed to be simulating. @@ -37,11 +37,11 @@ The [NodePath] to the [Bone2D] node that this [code]PhysicalBone2D[/code] node is supposed to be simulating. - When [code]true[/code], the [code]PhysicalBone2D[/code] will keep the transform of the bone it is bound to when simulating physics. + If [code]true[/code], the [code]PhysicalBone2D[/code] will keep the transform of the bone it is bound to when simulating physics. - When [code]true[/code], the [code]PhysicalBone2D[/code] will start simulating using physics. When false, the [code]PhysicalBone2D[/code] will follow the transform of the [Bone2D] node. - [b]Note[/b]: To have the Bone2D nodes visually follow the [code]PhysicalBone2D[/code] node, use a [SkeletonModification2DPhysicalBones] modification on the [Skeleton2D] node with the [Bone2D] nodes. + If [code]true[/code], the [code]PhysicalBone2D[/code] will start simulating using physics. If [code]false[/code], the [code]PhysicalBone2D[/code] will follow the transform of the [Bone2D] node. + [b]Note:[/b] To have the Bone2D nodes visually follow the [code]PhysicalBone2D[/code] node, use a [SkeletonModification2DPhysicalBones] modification on the [Skeleton2D] node with the [Bone2D] nodes. diff --git a/doc/classes/SkeletonModification2D.xml b/doc/classes/SkeletonModification2D.xml index a27662846d78..cfbc2414ea3b 100644 --- a/doc/classes/SkeletonModification2D.xml +++ b/doc/classes/SkeletonModification2D.xml @@ -30,7 +30,7 @@ Used for drawing [b]editor-only[/b] modification gizmos. This function will only be called in the Godot editor and can be overriden to draw custom gizmos. - [b]Note[/b]: You will need to use the Skeleton2D from [method SkeletonModificationStack2D.get_skeleton] and it's draw functions, as the [SkeletonModification2D] resource cannot draw on its own. + [b]Note:[/b] You will need to use the Skeleton2D from [method SkeletonModificationStack2D.get_skeleton] and it's draw functions, as the [SkeletonModification2D] resource cannot draw on its own. @@ -92,7 +92,7 @@ - When true, the modification's [method execute] function will be called by the [SkeletonModificationStack2D]. + If [code]true[/code], the modification's [method execute] function will be called by the [SkeletonModificationStack2D]. The execution mode for the modification. This tells the modification stack when to execute the modification. Some modifications have settings that are only availible in certain execution modes. diff --git a/doc/classes/SkeletonModification2DJiggle.xml b/doc/classes/SkeletonModification2DJiggle.xml index 15ba2e4b8e0a..2233fbff53d4 100644 --- a/doc/classes/SkeletonModification2DJiggle.xml +++ b/doc/classes/SkeletonModification2DJiggle.xml @@ -200,7 +200,7 @@ - When [code]true[/code], the Jiggle modifier will take colliders into account, keeping them from entering into these collision objects. + If [code]true[/code], the Jiggle modifier will take colliders into account, keeping them from entering into these collision objects. diff --git a/doc/classes/SkeletonModification2DTwoBoneIK.xml b/doc/classes/SkeletonModification2DTwoBoneIK.xml index 10a122e9b4e9..554515556b1c 100644 --- a/doc/classes/SkeletonModification2DTwoBoneIK.xml +++ b/doc/classes/SkeletonModification2DTwoBoneIK.xml @@ -77,7 +77,7 @@ - When [code]true[/code], the bones in the modification will blend outward as opposed to inwards when contracting. When [code]false[/code], the bones will bend inwards when contracting. + If [code]true[/code], the bones in the modification will blend outward as opposed to inwards when contracting. If [code]false[/code], the bones will bend inwards when contracting. The maximum distance the target can be at. If the target is farther than this distance, the modification will solve as if it's at this maximum distance. When set to [code]0[/code], the modification will solve without distance constraints. diff --git a/doc/classes/SkeletonModificationStack2D.xml b/doc/classes/SkeletonModificationStack2D.xml index 55822a84e7f6..35b899fe08a9 100644 --- a/doc/classes/SkeletonModificationStack2D.xml +++ b/doc/classes/SkeletonModificationStack2D.xml @@ -94,7 +94,7 @@ - When [code]true[/code], the modification's in the stack will be called. This is handled automatically through the [Skeleton2D] node. + If [code]true[/code], the modification's in the stack will be called. This is handled automatically through the [Skeleton2D] node. The number of modifications in the stack. diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index dd52fe3c1633..ecbb6654c839 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -46,6 +46,8 @@ bool Bone2D::_set(const StringName &p_path, const Variant &p_value) { set_length(p_value); } else if (path.begins_with("bone_angle")) { set_bone_angle(Math::deg2rad(float(p_value))); + } else if (path.begins_with("default_length")) { + set_length(p_value); } #ifdef TOOLS_ENABLED @@ -66,6 +68,8 @@ bool Bone2D::_get(const StringName &p_path, Variant &r_ret) const { r_ret = get_length(); } else if (path.begins_with("bone_angle")) { r_ret = Math::rad2deg(get_bone_angle()); + } else if (path.begins_with("default_length")) { + r_ret = get_length(); } #ifdef TOOLS_ENABLED diff --git a/scene/main/node.h b/scene/main/node.h index abe364c601df..de7953e4bc5b 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -256,8 +256,7 @@ class Node : public Object { // Editor specific node notifications NOTIFICATION_EDITOR_PRE_SAVE = 9001, - NOTIFICATION_EDITOR_POST_SAVE = 9002 - + NOTIFICATION_EDITOR_POST_SAVE = 9002, }; /* NODE/TREE */ diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index cdc2ddc6b119..d2dabf4e6055 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -43,11 +43,12 @@ // Modification2D /////////////////////////////////////// -void SkeletonModification2D::_execute(float delta) { - call("_execute", delta); +void SkeletonModification2D::_execute(float p_delta) { + call("_execute", p_delta); - if (!enabled) + if (!enabled) { return; + } } void SkeletonModification2D::_setup_modification(SkeletonModificationStack2D *p_stack) { @@ -81,52 +82,52 @@ bool SkeletonModification2D::get_enabled() { return enabled; } -float SkeletonModification2D::clamp_angle(float angle, float min_bound, float max_bound, bool invert) { +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 (angle < 0) { - angle = Math_TAU + angle; + 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 (min_bound < 0) { - min_bound = Math_TAU + min_bound; + if (p_min_bound < 0) { + p_min_bound = Math_TAU + p_min_bound; } - if (max_bound < 0) { - max_bound = Math_TAU + max_bound; + if (p_max_bound < 0) { + p_max_bound = Math_TAU + p_max_bound; } - if (min_bound > max_bound) { - float tmp = min_bound; - min_bound = max_bound; - max_bound = tmp; + if (p_min_bound > p_max_bound) { + float tmp = p_min_bound; + p_min_bound = p_max_bound; + p_max_bound = tmp; } // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle. - if (invert == false) { - if (angle < min_bound || angle > max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(min_bound), Math::sin(min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(max_bound), Math::sin(max_bound)); - Vector2 angle_vec = Vector2(Math::cos(angle), Math::sin(angle)); + if (p_invert == false) { + if (p_angle < p_min_bound || p_angle > p_max_bound) { + 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)) { - angle = min_bound; + p_angle = p_min_bound; } else { - angle = max_bound; + p_angle = p_max_bound; } } } else { - if (angle > min_bound && angle < max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(min_bound), Math::sin(min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(max_bound), Math::sin(max_bound)); - Vector2 angle_vec = Vector2(Math::cos(angle), Math::sin(angle)); + if (p_angle > p_min_bound && p_angle < p_max_bound) { + 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)) { - angle = min_bound; + p_angle = p_min_bound; } else { - angle = max_bound; + p_angle = p_max_bound; } } } - return angle; + return p_angle; } void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *operation_bone, float min_bound, float max_bound, diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index 7fcf7576936f..18633e55cba9 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -58,7 +58,7 @@ class SkeletonModification2D : public Resource { bool _print_execution_error(bool p_condition, String p_message); public: - virtual void _execute(float delta); + virtual void _execute(float _delta); virtual void _setup_modification(SkeletonModificationStack2D *p_stack); virtual void _draw_editor_gizmo(); @@ -76,8 +76,8 @@ class SkeletonModification2D : public Resource { void set_execution_mode(int p_mode); int get_execution_mode() const; - float clamp_angle(float angle, float min_bound, float max_bound, bool invert_clamp = false); - void editor_draw_angle_constraints(Bone2D *operation_bone, float min_bound, float max_bound, bool constraint_enabled, bool constraint_in_localspace, bool constraint_inverted); + 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(); }; diff --git a/scene/resources/skeleton_modification_2d_ccdik.cpp b/scene/resources/skeleton_modification_2d_ccdik.cpp index 16501ea298f2..7ea60e584ea3 100644 --- a/scene/resources/skeleton_modification_2d_ccdik.cpp +++ b/scene/resources/skeleton_modification_2d_ccdik.cpp @@ -153,7 +153,7 @@ void SkeletonModification2DCCDIK::_get_property_list(List *p_list) #endif // TOOLS_ENABLED } -void SkeletonModification2DCCDIK::_execute(float delta) { +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) { @@ -188,7 +188,7 @@ void SkeletonModification2DCCDIK::_execute(float delta) { } } -void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D *target, Node2D *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!"); @@ -201,12 +201,12 @@ void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D * if (ccdik_data.rotate_from_joint) { // To rotate from the joint, simply look at the target! operation_transform.set_rotation( - operation_transform.looking_at(target->get_global_transform().get_origin()).get_rotation() - operation_bone->get_bone_angle()); + operation_transform.looking_at(p_target->get_global_transform().get_origin()).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 = operation_transform.get_origin().angle_to_point(tip->get_global_transform().get_origin()); - float joint_to_target = operation_transform.get_origin().angle_to_point(target->get_global_transform().get_origin()); + float joint_to_tip = operation_transform.get_origin().angle_to_point(p_tip->get_global_transform().get_origin()); + float joint_to_target = operation_transform.get_origin().angle_to_point(p_target->get_global_transform().get_origin()); operation_transform.set_rotation( operation_transform.get_rotation() + (joint_to_target - joint_to_tip)); } diff --git a/scene/resources/skeleton_modification_2d_ccdik.h b/scene/resources/skeleton_modification_2d_ccdik.h index aba6773bf761..dc48291f6254 100644 --- a/scene/resources/skeleton_modification_2d_ccdik.h +++ b/scene/resources/skeleton_modification_2d_ccdik.h @@ -68,7 +68,7 @@ class SkeletonModification2DCCDIK : public SkeletonModification2D { void update_tip_cache(); void ccdik_joint_update_bone2d_cache(int p_joint_idx); - void _execute_ccdik_joint(int p_joint_idx, Node2D *target, Node2D *tip); + void _execute_ccdik_joint(int p_joint_idx, Node2D *p_target, Node2D *p_tip); protected: static void _bind_methods(); @@ -77,7 +77,7 @@ class SkeletonModification2DCCDIK : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void _execute(float delta) override; + void _execute(float p_delta) override; void _setup_modification(SkeletonModificationStack2D *p_stack) override; void _draw_editor_gizmo() override; diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp index 73ee63ae2f9e..aef852f7e48a 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.cpp +++ b/scene/resources/skeleton_modification_2d_fabrik.cpp @@ -95,7 +95,7 @@ void SkeletonModification2DFABRIK::_get_property_list(List *p_list } } -void SkeletonModification2DFABRIK::_execute(float delta) { +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) { diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/resources/skeleton_modification_2d_fabrik.h index 1d4514b432c0..79e0106e2665 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.h +++ b/scene/resources/skeleton_modification_2d_fabrik.h @@ -82,7 +82,7 @@ class SkeletonModification2DFABRIK : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void _execute(float delta) override; + void _execute(float p_delta) override; void _setup_modification(SkeletonModificationStack2D *p_stack) override; void set_target_node(const NodePath &p_target_node); diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp index 47487ef5b06c..d441e3346b96 100644 --- a/scene/resources/skeleton_modification_2d_jiggle.cpp +++ b/scene/resources/skeleton_modification_2d_jiggle.cpp @@ -128,7 +128,7 @@ void SkeletonModification2DJiggle::_get_property_list(List *p_list } } -void SkeletonModification2DJiggle::_execute(float delta) { +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) { @@ -146,11 +146,11 @@ void SkeletonModification2DJiggle::_execute(float delta) { } for (int i = 0; i < jiggle_data_chain.size(); i++) { - _execute_jiggle_joint(i, target, delta); + _execute_jiggle_joint(i, target, p_delta); } } -void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D *target, float 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. @@ -171,12 +171,12 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D } Transform2D operation_bone_trans = operation_bone->get_global_transform(); - Vector2 target_position = target->get_global_transform().get_origin(); + Vector2 target_position = p_target->get_global_transform().get_origin(); - 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 * delta; + 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 * delta; + 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; diff --git a/scene/resources/skeleton_modification_2d_jiggle.h b/scene/resources/skeleton_modification_2d_jiggle.h index 247e0a147e6a..e24038a1db51 100644 --- a/scene/resources/skeleton_modification_2d_jiggle.h +++ b/scene/resources/skeleton_modification_2d_jiggle.h @@ -79,7 +79,7 @@ class SkeletonModification2DJiggle : public SkeletonModification2D { uint32_t collision_mask = 1; void jiggle_joint_update_bone2d_cache(int p_joint_idx); - void _execute_jiggle_joint(int p_joint_idx, Node2D *target, float delta); + void _execute_jiggle_joint(int p_joint_idx, Node2D *p_target, float p_delta); void _update_jiggle_joint_data(); protected: @@ -89,7 +89,7 @@ class SkeletonModification2DJiggle : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void _execute(float delta) override; + void _execute(float p_delta) override; void _setup_modification(SkeletonModificationStack2D *p_stack) override; void set_target_node(const NodePath &p_target_node); diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/resources/skeleton_modification_2d_lookat.cpp index 4fc429a7c45d..fd5c8c7cc229 100644 --- a/scene/resources/skeleton_modification_2d_lookat.cpp +++ b/scene/resources/skeleton_modification_2d_lookat.cpp @@ -104,7 +104,7 @@ void SkeletonModification2DLookAt::_get_property_list(List *p_list #endif // TOOLS_ENABLED } -void SkeletonModification2DLookAt::_execute(float delta) { +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) { diff --git a/scene/resources/skeleton_modification_2d_lookat.h b/scene/resources/skeleton_modification_2d_lookat.h index 0576d6db8989..6aff30b826e2 100644 --- a/scene/resources/skeleton_modification_2d_lookat.h +++ b/scene/resources/skeleton_modification_2d_lookat.h @@ -67,7 +67,7 @@ class SkeletonModification2DLookAt : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void _execute(float delta) override; + void _execute(float p_delta) override; void _setup_modification(SkeletonModificationStack2D *p_stack) override; void _draw_editor_gizmo() override; diff --git a/scene/resources/skeleton_modification_2d_physicalbones.cpp b/scene/resources/skeleton_modification_2d_physicalbones.cpp index ced2ed124b56..f2ed169bb006 100644 --- a/scene/resources/skeleton_modification_2d_physicalbones.cpp +++ b/scene/resources/skeleton_modification_2d_physicalbones.cpp @@ -99,7 +99,7 @@ void SkeletonModification2DPhysicalBones::_get_property_list(List } } -void SkeletonModification2DPhysicalBones::_execute(float delta) { +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) { diff --git a/scene/resources/skeleton_modification_2d_physicalbones.h b/scene/resources/skeleton_modification_2d_physicalbones.h index 5d3d9c73b660..cdf6a5f570d4 100644 --- a/scene/resources/skeleton_modification_2d_physicalbones.h +++ b/scene/resources/skeleton_modification_2d_physicalbones.h @@ -62,7 +62,7 @@ class SkeletonModification2DPhysicalBones : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void _execute(float delta) override; + void _execute(float p_delta) override; void _setup_modification(SkeletonModificationStack2D *p_stack) override; int get_physical_bone_chain_length(); diff --git a/scene/resources/skeleton_modification_2d_stackholder.cpp b/scene/resources/skeleton_modification_2d_stackholder.cpp index 940d8b225175..9436092cd9e0 100644 --- a/scene/resources/skeleton_modification_2d_stackholder.cpp +++ b/scene/resources/skeleton_modification_2d_stackholder.cpp @@ -73,12 +73,12 @@ void SkeletonModification2DStackHolder::_get_property_list(List *p #endif // TOOLS_ENABLED } -void SkeletonModification2DStackHolder::_execute(float delta) { +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(delta, execution_mode); + held_modification_stack->execute(p_delta, execution_mode); } } diff --git a/scene/resources/skeleton_modification_2d_stackholder.h b/scene/resources/skeleton_modification_2d_stackholder.h index 44a9be1387af..9cc38e39425c 100644 --- a/scene/resources/skeleton_modification_2d_stackholder.h +++ b/scene/resources/skeleton_modification_2d_stackholder.h @@ -50,7 +50,7 @@ class SkeletonModification2DStackHolder : public SkeletonModification2D { public: Ref held_modification_stack; - void _execute(float delta) override; + void _execute(float p_delta) override; void _setup_modification(SkeletonModificationStack2D *p_stack) override; void _draw_editor_gizmo() override; diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp index c4ea77ff6272..0a912900152f 100644 --- a/scene/resources/skeleton_modification_2d_twoboneik.cpp +++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp @@ -98,7 +98,7 @@ void SkeletonModification2DTwoBoneIK::_get_property_list(List *p_l #endif // TOOLS_ENABLED } -void SkeletonModification2DTwoBoneIK::_execute(float delta) { +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) { diff --git a/scene/resources/skeleton_modification_2d_twoboneik.h b/scene/resources/skeleton_modification_2d_twoboneik.h index 8b5f876a1412..c7e545a48894 100644 --- a/scene/resources/skeleton_modification_2d_twoboneik.h +++ b/scene/resources/skeleton_modification_2d_twoboneik.h @@ -71,7 +71,7 @@ class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { void _get_property_list(List *p_list) const; public: - void _execute(float delta) override; + void _execute(float p_delta) override; void _setup_modification(SkeletonModificationStack2D *p_stack) override; void _draw_editor_gizmo() override; diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp index 07787ea226b6..d12ec4add30a 100644 --- a/scene/resources/skeleton_modification_stack_2d.cpp +++ b/scene/resources/skeleton_modification_stack_2d.cpp @@ -86,7 +86,7 @@ void SkeletonModificationStack2D::setup() { } } -void SkeletonModificationStack2D::execute(float delta, int p_execution_mode) { +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!"); @@ -105,7 +105,7 @@ void SkeletonModificationStack2D::execute(float delta, int p_execution_mode) { } if (modifications[i]->get_execution_mode() == p_execution_mode) { - modifications.get(i)->_execute(delta); + modifications.get(i)->_execute(p_delta); } } } diff --git a/scene/resources/skeleton_modification_stack_2d.h b/scene/resources/skeleton_modification_stack_2d.h index 59d9f43a046e..58855701a193 100644 --- a/scene/resources/skeleton_modification_stack_2d.h +++ b/scene/resources/skeleton_modification_stack_2d.h @@ -67,7 +67,7 @@ class SkeletonModificationStack2D : public Resource { Vector> modifications = Vector>(); void setup(); - void execute(float delta, int p_execution_mode); + void execute(float p_delta, int p_execution_mode); bool editor_gizmo_dirty = false; void draw_editor_gizmos(); From e9552be02d42bfa9ab6c376fc666a574f8914b89 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Sat, 10 Apr 2021 14:34:40 -0400 Subject: [PATCH 33/34] Syntax changes to the documentation for Skeleton2D and several of the SkeletonModification2D pages. Syntax changes to canvas_item_editor_plugin, skeleton_2d, skeleton_modification_2d, and skeleton_modification_2d_jiggle --- doc/classes/Skeleton2D.xml | 2 +- doc/classes/SkeletonModification2DFABRIK.xml | 2 +- .../SkeletonModification2DPhysicalBones.xml | 2 +- .../SkeletonModification2DStackHolder.xml | 2 +- editor/plugins/canvas_item_editor_plugin.cpp | 8 +-- editor/plugins/canvas_item_editor_plugin.h | 2 +- scene/2d/skeleton_2d.cpp | 56 +++++++-------- scene/2d/skeleton_2d.h | 8 +-- scene/resources/skeleton_modification_2d.cpp | 42 +++++------ .../skeleton_modification_2d_jiggle.cpp | 72 +++++++++---------- 10 files changed, 98 insertions(+), 98 deletions(-) diff --git a/doc/classes/Skeleton2D.xml b/doc/classes/Skeleton2D.xml index c3e36d68e2a9..80db57d7d063 100644 --- a/doc/classes/Skeleton2D.xml +++ b/doc/classes/Skeleton2D.xml @@ -74,7 +74,7 @@ Sets the local pose transform, [code]pose[/code], for the bone at [code]bone_idx[/code]. [code]amount[/code] is the interpolation strengh that will be used when applying the pose, and [code]persistent[/code] determines if the applied pose will remain. - [b]Note[/b]: The pose transform needs to be a local transform relative to the [Bone2D] node at [code]bone_idx[/code]! + [b]Note:[/b] The pose transform needs to be a local transform relative to the [Bone2D] node at [code]bone_idx[/code]! diff --git a/doc/classes/SkeletonModification2DFABRIK.xml b/doc/classes/SkeletonModification2DFABRIK.xml index 23f85a373a5d..a875e6e9082f 100644 --- a/doc/classes/SkeletonModification2DFABRIK.xml +++ b/doc/classes/SkeletonModification2DFABRIK.xml @@ -172,7 +172,7 @@ Sets whether the joint at [code]joint_idx[/code] will use the target node's rotation rather than letting FABRIK rotate the node. - [b]Note[/b]: This option only works for the tip/final joint in the chain. For all other nodes, this option will be ignored. + [b]Note:[/b] This option only works for the tip/final joint in the chain. For all other nodes, this option will be ignored. diff --git a/doc/classes/SkeletonModification2DPhysicalBones.xml b/doc/classes/SkeletonModification2DPhysicalBones.xml index 8ccf10e49776..d8aaf09a8e34 100644 --- a/doc/classes/SkeletonModification2DPhysicalBones.xml +++ b/doc/classes/SkeletonModification2DPhysicalBones.xml @@ -34,7 +34,7 @@ Sets the [PhysicalBone2D] node at [code]joint_idx[/code]. - [b]Note[/b]: This is just the index used for this modification, not the bone index used in the [Skeleton2D]. + [b]Note:[/b] This is just the index used for this modification, not the bone index used in the [Skeleton2D]. diff --git a/doc/classes/SkeletonModification2DStackHolder.xml b/doc/classes/SkeletonModification2DStackHolder.xml index acaf78f53d63..313cf8148283 100644 --- a/doc/classes/SkeletonModification2DStackHolder.xml +++ b/doc/classes/SkeletonModification2DStackHolder.xml @@ -5,7 +5,7 @@ This [SkeletonModification2D] holds a reference to a [SkeletonModificationStack2D], allowing you to use multiple modification stacks on a single [Skeleton2D]. - [b]Note[/b]: The modifications in the held [SkeletonModificationStack2D] will only be executed if their execution mode matches the execution mode of the SkeletonModification2DStackHolder. + [b]Note:[/b] The modifications in the held [SkeletonModificationStack2D] will only be executed if their execution mode matches the execution mode of the SkeletonModification2DStackHolder. diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 290de6cad053..647223fa08d7 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -4838,10 +4838,10 @@ void CanvasItemEditor::_popup_callback(int p_op) { } } -void CanvasItemEditor::_set_owner_for_node_and_children(Node *node, Node *owner) { - node->set_owner(owner); - for (int i = 0; i < node->get_child_count(); i++) { - _set_owner_for_node_and_children(node->get_child(i), owner); +void CanvasItemEditor::_set_owner_for_node_and_children(Node *p_node, Node *p_owner) { + p_node->set_owner(p_owner); + for (int i = 0; i < p_node->get_child_count(); i++) { + _set_owner_for_node_and_children(p_node->get_child(i), p_owner); } } diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index c6ce03ae134b..e77a34509c64 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -543,7 +543,7 @@ class CanvasItemEditor : public VBoxContainer { void _popup_warning_temporarily(Control *p_control, const float p_duration); void _popup_warning_depop(Control *p_control); - void _set_owner_for_node_and_children(Node *node, Node *owner); + void _set_owner_for_node_and_children(Node *p_node, Node *p_owner); friend class CanvasItemEditorPlugin; diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index ecbb6654c839..6ddb8bd81caf 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -314,20 +314,20 @@ void Bone2D::_notification(int p_what) { } #ifdef TOOLS_ENABLED -bool Bone2D::_editor_get_bone_shape(Vector *shape, Vector *outline_shape, Bone2D *other_bone) { +bool Bone2D::_editor_get_bone_shape(Vector *p_shape, Vector *p_outline_shape, Bone2D *p_other_bone) { int bone_width = EditorSettings::get_singleton()->get("editors/2d/bone_width"); int bone_outline_width = EditorSettings::get_singleton()->get("editors/2d/bone_outline_size"); if (!is_inside_tree()) { return false; //may have been removed } - if (!other_bone && length <= 0) { + if (!p_other_bone && length <= 0) { return false; } Vector2 rel; - if (other_bone) { - rel = (other_bone->get_global_transform().get_origin() - get_global_transform().get_origin()); + if (p_other_bone) { + rel = (p_other_bone->get_global_transform().get_origin() - get_global_transform().get_origin()); rel = rel.rotated(-get_global_rotation()); // Undo Bone2D node's rotation so its drawn correctly regardless of the node's rotation } else { float angle_to_use = get_rotation() + bone_angle; @@ -339,22 +339,22 @@ bool Bone2D::_editor_get_bone_shape(Vector *shape, Vector *out Vector2 reln = rel.normalized(); Vector2 reltn = relt.normalized(); - if (shape) { - shape->clear(); - shape->push_back(Vector2(0, 0)); - shape->push_back(rel * 0.2 + relt); - shape->push_back(rel); - shape->push_back(rel * 0.2 - relt); + if (p_shape) { + p_shape->clear(); + p_shape->push_back(Vector2(0, 0)); + p_shape->push_back(rel * 0.2 + relt); + p_shape->push_back(rel); + p_shape->push_back(rel * 0.2 - relt); } - if (outline_shape) { - outline_shape->clear(); - outline_shape->push_back((-reln - reltn) * bone_outline_width); - outline_shape->push_back((-reln + reltn) * bone_outline_width); - outline_shape->push_back(rel * 0.2 + relt + reltn * bone_outline_width); - outline_shape->push_back(rel + (reln + reltn) * bone_outline_width); - outline_shape->push_back(rel + (reln - reltn) * bone_outline_width); - outline_shape->push_back(rel * 0.2 - relt - reltn * bone_outline_width); + if (p_outline_shape) { + p_outline_shape->clear(); + p_outline_shape->push_back((-reln - reltn) * bone_outline_width); + p_outline_shape->push_back((-reln + reltn) * bone_outline_width); + p_outline_shape->push_back(rel * 0.2 + relt + reltn * bone_outline_width); + p_outline_shape->push_back(rel + (reln + reltn) * bone_outline_width); + p_outline_shape->push_back(rel + (reln - reltn) * bone_outline_width); + p_outline_shape->push_back(rel * 0.2 - relt - reltn * bone_outline_width); } return true; } @@ -696,16 +696,16 @@ RID Skeleton2D::get_skeleton() const { return skeleton; } -void Skeleton2D::set_bone_local_pose_override(int bone_idx, Transform2D p_override, float amount, bool persistent) { - ERR_FAIL_INDEX_MSG(bone_idx, bones.size(), "Bone index is out of range!"); - bones.write[bone_idx].local_pose_override = p_override; - bones.write[bone_idx].local_pose_override_amount = amount; - bones.write[bone_idx].local_pose_override_persistent = persistent; +void Skeleton2D::set_bone_local_pose_override(int p_bone_idx, Transform2D p_override, float p_amount, bool p_persistent) { + ERR_FAIL_INDEX_MSG(p_bone_idx, bones.size(), "Bone index is out of range!"); + bones.write[p_bone_idx].local_pose_override = p_override; + bones.write[p_bone_idx].local_pose_override_amount = p_amount; + bones.write[p_bone_idx].local_pose_override_persistent = p_persistent; } -Transform2D Skeleton2D::get_bone_local_pose_override(int bone_idx) { - ERR_FAIL_INDEX_V_MSG(bone_idx, bones.size(), Transform2D(), "Bone index is out of range!"); - return bones[bone_idx].local_pose_override; +Transform2D Skeleton2D::get_bone_local_pose_override(int p_bone_idx) { + ERR_FAIL_INDEX_V_MSG(p_bone_idx, bones.size(), Transform2D(), "Bone index is out of range!"); + return bones[p_bone_idx].local_pose_override; } void Skeleton2D::set_modification_stack(Ref p_stack) { @@ -734,7 +734,7 @@ Ref Skeleton2D::get_modification_stack() const { return modification_stack; } -void Skeleton2D::execute_modifications(float delta, int p_execution_mode) { +void Skeleton2D::execute_modifications(float p_delta, int p_execution_mode) { if (!modification_stack.is_valid()) { return; } @@ -748,7 +748,7 @@ void Skeleton2D::execute_modifications(float delta, int p_execution_mode) { modification_stack->set_skeleton(this); } - modification_stack->execute(delta, p_execution_mode); + modification_stack->execute(p_delta, p_execution_mode); // Only apply the local pose override on _process. Otherwise, just calculate the local_pose_override and reset the transform. if (p_execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process) { diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index dbe696e9d2aa..361907938e92 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -58,7 +58,7 @@ class Bone2D : public Node2D { #ifdef TOOLS_ENABLED RID editor_gizmo_rid; - bool _editor_get_bone_shape(Vector *shape, Vector *outline_shape, Bone2D *other_bone); + bool _editor_get_bone_shape(Vector *p_shape, Vector *p_outline_shape, Bone2D *p_other_bone); bool _editor_show_bone_gizmo = true; #endif // TOOLS ENABLED @@ -153,12 +153,12 @@ class Skeleton2D : public Node2D { RID get_skeleton() const; - void set_bone_local_pose_override(int bone_idx, Transform2D p_override, float amount, bool persistent = true); - Transform2D get_bone_local_pose_override(int bone_idx); + void set_bone_local_pose_override(int p_bone_idx, Transform2D p_override, float p_amount, bool p_persistent = true); + Transform2D get_bone_local_pose_override(int p_bone_idx); Ref get_modification_stack() const; void set_modification_stack(Ref p_stack); - void execute_modifications(float delta, int p_execution_mode); + void execute_modifications(float p_delta, int p_execution_mode); Skeleton2D(); ~Skeleton2D(); diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index d2dabf4e6055..9b072939652e 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -130,9 +130,9 @@ float SkeletonModification2D::clamp_angle(float p_angle, float p_min_bound, floa return p_angle; } -void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *operation_bone, float min_bound, float max_bound, - bool constraint_enabled, bool constraint_in_localspace, bool constraint_inverted) { - if (!operation_bone) { +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; } @@ -143,8 +143,8 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *operation_bon } #endif // TOOLS_ENABLED - float arc_angle_min = min_bound; - float arc_angle_max = max_bound; + 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; } @@ -156,39 +156,39 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *operation_bon arc_angle_min = arc_angle_max; arc_angle_max = tmp; } - arc_angle_min += operation_bone->get_bone_angle(); - arc_angle_max += operation_bone->get_bone_angle(); + arc_angle_min += p_operation_bone->get_bone_angle(); + arc_angle_max += p_operation_bone->get_bone_angle(); - if (constraint_enabled) { - if (constraint_in_localspace) { - Node *operation_bone_parent = operation_bone->get_parent(); + 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(operation_bone_parent); if (operation_bone_parent_bone) { stack->skeleton->draw_set_transform( - stack->skeleton->get_global_transform().affine_inverse().xform(operation_bone->get_global_position()), + stack->skeleton->get_global_transform().affine_inverse().xform(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->get_global_transform().affine_inverse().xform(operation_bone->get_global_position())); + stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position())); } } else { - stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(operation_bone->get_global_position())); + stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position())); } - if (constraint_inverted) { - stack->skeleton->draw_arc(Vector2(0, 0), operation_bone->get_length(), + 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), operation_bone->get_length(), + 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)) * 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)) * operation_bone->get_length(), 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->get_global_transform().affine_inverse().xform(operation_bone->get_global_position())); - stack->skeleton->draw_arc(Vector2(0, 0), operation_bone->get_length(), 0, Math_PI * 2, 32, bone_ik_color, 1.0); - stack->skeleton->draw_line(Vector2(0, 0), Vector2(1, 0) * operation_bone->get_length(), bone_ik_color, 1.0); + stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(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); } } diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp index d441e3346b96..254708333618 100644 --- a/scene/resources/skeleton_modification_2d_jiggle.cpp +++ b/scene/resources/skeleton_modification_2d_jiggle.cpp @@ -429,70 +429,70 @@ int SkeletonModification2DJiggle::get_jiggle_joint_bone_index(int p_joint_idx) c return jiggle_data_chain[p_joint_idx].bone_idx; } -void SkeletonModification2DJiggle::set_jiggle_joint_override(int joint_idx, bool p_override) { - ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); - jiggle_data_chain.write[joint_idx].override_defaults = p_override; +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 joint_idx) const { - ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), false); - return jiggle_data_chain[joint_idx].override_defaults; +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 joint_idx, float p_stiffness) { +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(joint_idx, jiggle_data_chain.size()); - jiggle_data_chain.write[joint_idx].stiffness = p_stiffness; + 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 joint_idx) const { - ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); - return jiggle_data_chain[joint_idx].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 joint_idx, float p_mass) { +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(joint_idx, jiggle_data_chain.size()); - jiggle_data_chain.write[joint_idx].mass = p_mass; + 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 joint_idx) const { - ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); - return jiggle_data_chain[joint_idx].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 joint_idx, float p_damping) { +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(joint_idx, jiggle_data_chain.size()); - jiggle_data_chain.write[joint_idx].damping = p_damping; + 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 joint_idx) const { - ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), -1); - return jiggle_data_chain[joint_idx].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 joint_idx, bool p_use_gravity) { - ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); - jiggle_data_chain.write[joint_idx].use_gravity = p_use_gravity; +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 joint_idx) const { - ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), false); - return jiggle_data_chain[joint_idx].use_gravity; +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 joint_idx, Vector2 p_gravity) { - ERR_FAIL_INDEX(joint_idx, jiggle_data_chain.size()); - jiggle_data_chain.write[joint_idx].gravity = p_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 joint_idx) const { - ERR_FAIL_INDEX_V(joint_idx, jiggle_data_chain.size(), Vector2(0, 0)); - return jiggle_data_chain[joint_idx].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() { From 19f1b1ee3693fa9ae5827181d3ca69b9a998bed9 Mon Sep 17 00:00:00 2001 From: TwistedTwigleg Date: Tue, 13 Apr 2021 17:23:45 -0400 Subject: [PATCH 34/34] Slight adjustment to the looking_at function in Transfrom2D --- core/math/transform_2d.cpp | 2 +- core/math/transform_2d.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index d1e3d2f6ea3f..9189234d0499 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -158,7 +158,7 @@ bool Transform2D::is_equal_approx(const Transform2D &p_transform) const { return elements[0].is_equal_approx(p_transform.elements[0]) && elements[1].is_equal_approx(p_transform.elements[1]) && elements[2].is_equal_approx(p_transform.elements[2]); } -Transform2D Transform2D::looking_at(const Vector2 p_target) const { +Transform2D Transform2D::looking_at(const Vector2 &p_target) const { Transform2D return_trans = Transform2D(get_rotation(), get_origin()); Vector2 target_position = affine_inverse().xform(p_target); return_trans.set_rotation(return_trans.get_rotation() + (target_position * get_scale()).angle()); diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index 8663c30db5ce..715f013701e9 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -100,7 +100,7 @@ struct Transform2D { Transform2D orthonormalized() const; bool is_equal_approx(const Transform2D &p_transform) const; - Transform2D looking_at(const Vector2 p_target) const; + Transform2D looking_at(const Vector2 &p_target) const; bool operator==(const Transform2D &p_transform) const; bool operator!=(const Transform2D &p_transform) const;