Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New and improved IK in Skeleton2D #40347

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
7701450
Initial groundwork (squashed from verbose branch: https://github.com/…
TwistedTwigleg Aug 3, 2020
f222cac
Changes:
TwistedTwigleg Aug 3, 2020
2a3de40
Changes:
TwistedTwigleg Aug 3, 2020
a5847a2
Changes:
TwistedTwigleg Aug 3, 2020
e614ca1
Changes:
TwistedTwigleg Aug 3, 2020
283d16d
Changes:
TwistedTwigleg Aug 3, 2020
c9c4d52
Changes:
TwistedTwigleg Aug 3, 2020
1c44aa0
Changes:
TwistedTwigleg Aug 3, 2020
79f0b74
Changes:
TwistedTwigleg Aug 3, 2020
e9d6a64
Changes:
TwistedTwigleg Aug 11, 2020
a9748cf
Changes:
TwistedTwigleg Aug 21, 2020
d440335
Changes:
TwistedTwigleg Aug 24, 2020
9c62f26
Changes:
TwistedTwigleg Aug 25, 2020
d564845
Changes:
TwistedTwigleg Aug 26, 2020
a31dcc3
Changes:
TwistedTwigleg Aug 27, 2020
a0cf8b4
Changes:
TwistedTwigleg Sep 10, 2020
3695a89
Changes:
TwistedTwigleg Sep 11, 2020
9e2e8a8
Changes:
TwistedTwigleg Sep 17, 2020
677290c
Changes:
TwistedTwigleg Sep 23, 2020
970ccb4
Changes:
TwistedTwigleg Oct 11, 2020
93e1648
Changes after code review! (Huge thanks to AndreaCatania for the revi…
TwistedTwigleg Jan 4, 2021
ec24bdb
Changes to fix the issues that came up after the rebase and fixed for…
TwistedTwigleg Jan 7, 2021
4456bf0
More changes based on review feedback! Huge thanks to AndreaCatania f…
TwistedTwigleg Jan 30, 2021
cc76c91
Changed the use of LocalVector to just Vector, so the CI on GitHub wo…
TwistedTwigleg Feb 7, 2021
27f8022
Removed angle constraints from 2D FABRIK, as they were not working.
TwistedTwigleg Feb 13, 2021
c6c3e6c
Rebased with master and fixed issues preventing the PR from running o…
TwistedTwigleg Feb 13, 2021
d26d755
Took the 2D IK modifications and put them into their own files.
TwistedTwigleg Mar 16, 2021
29c8e81
Removed _print_execution_error and instead use ERR_PRINT_ONCE with no…
TwistedTwigleg Mar 16, 2021
b3bda2d
Fixing small issue that occured after rebase. Not sure what happened,…
TwistedTwigleg Mar 16, 2021
aea42d0
Expose looking_at and set_rotation in Transform2D to GDScript, fix th…
TwistedTwigleg Mar 21, 2021
022d1a3
Missed a merge conflict on the rebase.
TwistedTwigleg Mar 26, 2021
6db3741
Changes based on review feedback:
TwistedTwigleg Mar 26, 2021
e9552be
Syntax changes to the documentation for Skeleton2D and several of the…
TwistedTwigleg Apr 10, 2021
19f1b1e
Slight adjustment to the looking_at function in Transfrom2D
TwistedTwigleg Apr 13, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions core/math/transform_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 &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());
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]) {
Expand Down
2 changes: 2 additions & 0 deletions core/math/transform_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ struct Transform2D {
Transform2D orthonormalized() const;
bool is_equal_approx(const Transform2D &p_transform) const;

Transform2D looking_at(const Vector2 &p_target) const;

bool operator==(const Transform2D &p_transform) const;
bool operator!=(const Transform2D &p_transform) const;

Expand Down
2 changes: 2 additions & 0 deletions core/variant/variant_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down
53 changes: 50 additions & 3 deletions doc/classes/Bone2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,72 @@
Stores the node's current transforms in [member rest].
</description>
</method>
<method name="get_autocalculate_length_and_angle" qualifiers="const">
<return type="bool">
</return>
<description>
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.
</description>
</method>
<method name="get_bone_angle" qualifiers="const">
<return type="float">
</return>
<description>
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.
</description>
</method>
<method name="get_index_in_skeleton" qualifiers="const">
<return type="int">
</return>
<description>
Returns the node's index as part of the entire skeleton. See [Skeleton2D].
</description>
</method>
<method name="get_length" qualifiers="const">
<return type="float">
</return>
<description>
Returns the length of the bone in the [code]Bone2D[/code] node.
</description>
</method>
<method name="get_skeleton_rest" qualifiers="const">
<return type="Transform2D">
</return>
<description>
Returns the node's [member rest] [code]Transform2D[/code] if it doesn't have a parent, or its rest pose relative to its parent.
</description>
</method>
<method name="set_autocalculate_length_and_angle">
<return type="void">
</return>
<argument index="0" name="auto_calculate" type="bool">
</argument>
<description>
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.
</description>
</method>
<method name="set_bone_angle">
<return type="void">
</return>
<argument index="0" name="angle" type="float">
</argument>
<description>
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.
</description>
</method>
<method name="set_length">
<return type="void">
</return>
<argument index="0" name="length" type="float">
</argument>
<description>
Sets the length of the bone in the [code]Bone2D[/code] node.
</description>
</method>
</methods>
<members>
<member name="default_length" type="float" setter="set_default_length" getter="get_default_length" default="16.0">
Length of the bone's representation drawn in the editor's viewport in pixels.
</member>
<member name="rest" type="Transform2D" setter="set_rest" getter="get_rest" default="Transform2D( 0, 0, 0, 0, 0, 0 )">
Rest transform of the bone. You can reset the node's transforms to this value using [method apply_rest].
</member>
Expand Down
6 changes: 6 additions & 0 deletions doc/classes/Node.xml
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,12 @@
<constant name="NOTIFICATION_POST_ENTER_TREE" value="27">
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.
</constant>
<constant name="NOTIFICATION_EDITOR_PRE_SAVE" value="9001">
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.
</constant>
<constant name="NOTIFICATION_EDITOR_POST_SAVE" value="9002">
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.
</constant>
<constant name="NOTIFICATION_WM_MOUSE_ENTER" value="1002">
Notification received from the OS when the mouse enters the game window.
Implemented on desktop and web platforms.
Expand Down
49 changes: 49 additions & 0 deletions doc/classes/PhysicalBone2D.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PhysicalBone2D" inherits="RigidBody2D" version="4.0">
<brief_description>
A 2D node that can be used for physically aware bones in 2D.
</brief_description>
<description>
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.
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_joint" qualifiers="const">
<return type="Joint2D">
</return>
<description>
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.
</description>
</method>
<method name="is_simulating_physics" qualifiers="const">
<return type="bool">
</return>
<description>
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.
</description>
</method>
</methods>
<members>
<member name="auto_configure_joint" type="bool" setter="set_auto_configure_joint" getter="get_auto_configure_joint" default="true">
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].
</member>
<member name="bone2d_index" type="int" setter="set_bone2d_index" getter="get_bone2d_index" default="-1">
The index of the [Bone2D] node that this [code]PhysicalBone2D[/code] node is supposed to be simulating.
</member>
<member name="bone2d_nodepath" type="NodePath" setter="set_bone2d_nodepath" getter="get_bone2d_nodepath" default="NodePath(&quot;&quot;)">
The [NodePath] to the [Bone2D] node that this [code]PhysicalBone2D[/code] node is supposed to be simulating.
</member>
<member name="follow_bone_when_simulating" type="bool" setter="set_follow_bone_when_simulating" getter="get_follow_bone_when_simulating" default="false">
If [code]true[/code], the [code]PhysicalBone2D[/code] will keep the transform of the bone it is bound to when simulating physics.
</member>
<member name="simulate_physics" type="bool" setter="set_simulate_physics" getter="get_simulate_physics" default="false">
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.
</member>
</members>
<constants>
</constants>
</class>
54 changes: 54 additions & 0 deletions doc/classes/Skeleton2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@
<link title="2D skeletons">https://docs.godotengine.org/en/latest/tutorials/animation/2d_skeletons.html</link>
</tutorials>
<methods>
<method name="execute_modifications">
<return type="void">
</return>
<argument index="0" name="execution_mode" type="float">
</argument>
<argument index="1" name="execution_mode" type="int">
</argument>
<description>
Executes all the modifications on the [SkeletonModificationStack2D], if the Skeleton3D has one assigned.
</description>
</method>
<method name="get_bone">
<return type="Bone2D">
</return>
Expand All @@ -26,17 +37,60 @@
Returns the number of [Bone2D] nodes in the node hierarchy parented by Skeleton2D.
</description>
</method>
<method name="get_bone_local_pose_override">
<return type="Transform2D">
</return>
<argument index="0" name="bone_idx" type="int">
</argument>
<description>
Returns the local pose override transform for [code]bone_idx[/code].
</description>
</method>
<method name="get_modification_stack" qualifiers="const">
<return type="SkeletonModificationStack2D">
</return>
<description>
Returns the [SkeletonModificationStack2D] attached to this skeleton, if one exists.
</description>
</method>
<method name="get_skeleton" qualifiers="const">
<return type="RID">
</return>
<description>
Returns the [RID] of a Skeleton2D instance.
</description>
</method>
<method name="set_bone_local_pose_override">
<return type="void">
</return>
<argument index="0" name="bone_idx" type="int">
</argument>
<argument index="1" name="override_pose" type="Transform2D">
</argument>
<argument index="2" name="strength" type="float">
</argument>
<argument index="3" name="persistent" type="bool">
</argument>
<description>
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]!
</description>
</method>
<method name="set_modification_stack">
<return type="void">
</return>
<argument index="0" name="modification_stack" type="SkeletonModificationStack2D">
</argument>
<description>
Sets the [SkeletonModificationStack2D] attached to this skeleton.
</description>
</method>
</methods>
<signals>
<signal name="bone_setup_changed">
<description>
Emitted when the [Bone2D] setup attached to this skeletons changes. This is primarily used internally within the skeleton.
</description>
</signal>
</signals>
Expand Down
103 changes: 103 additions & 0 deletions doc/classes/SkeletonModification2D.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="SkeletonModification2D" inherits="Resource" version="4.0">
<brief_description>
A resource that operates on [Bone2D] nodes in a [Skeleton2D].
</brief_description>
<description>
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.
</description>
<tutorials>
</tutorials>
<methods>
<method name="clamp_angle">
<return type="float">
</return>
<argument index="0" name="angle" type="float">
</argument>
<argument index="1" name="min" type="float">
</argument>
<argument index="2" name="max" type="float">
</argument>
<argument index="3" name="invert" type="bool">
</argument>
<description>
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.
</description>
</method>
<method name="draw_editor_gizmo" qualifiers="virtual">
<return type="void">
</return>
<description>
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.
</description>
</method>
<method name="execute" qualifiers="virtual">
<return type="void">
</return>
<argument index="0" name="delta" type="float">
</argument>
<description>
Executes the given modification. This is where the modification performs whatever function it is designed to do.
</description>
</method>
<method name="get_editor_draw_gizmo" qualifiers="const">
<return type="bool">
</return>
<description>
Returns whether this modification will call [method draw_editor_gizmo] in the Godot editor to draw modification-specific gizmos.
</description>
</method>
<method name="get_is_setup" qualifiers="const">
<return type="bool">
</return>
<description>
Returns whether this modification has been successfully setup or not.
</description>
</method>
<method name="get_modification_stack">
<return type="SkeletonModificationStack2D">
</return>
<description>
Returns the [SkeletonModificationStack2D] that this modification is bound to. Through the modification stack, you can access the Skeleton3D the modification is operating on.
</description>
</method>
<method name="set_editor_draw_gizmo">
<return type="void">
</return>
<argument index="0" name="draw_gizmo" type="bool">
</argument>
<description>
Sets whether this modification will call [method draw_editor_gizmo] in the Godot editor to draw modification-specific gizmos.
</description>
</method>
<method name="set_is_setup">
<return type="void">
</return>
<argument index="0" name="is_setup" type="bool">
</argument>
<description>
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.
</description>
</method>
<method name="setup_modification" qualifiers="virtual">
<return type="void">
</return>
<argument index="0" name="modification_stack" type="SkeletonModificationStack2D">
</argument>
<description>
</description>
</method>
</methods>
<members>
<member name="enabled" type="bool" setter="set_enabled" getter="get_enabled" default="true">
If [code]true[/code], the modification's [method execute] function will be called by the [SkeletonModificationStack2D].
</member>
<member name="execution_mode" type="int" setter="set_execution_mode" getter="get_execution_mode" default="0">
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.
</member>
</members>
<constants>
</constants>
</class>
Loading