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

Conversation

TwistedTwigleg
Copy link
Contributor

@TwistedTwigleg TwistedTwigleg commented Jul 13, 2020

This pull request is the 2D work for the new and improved IK in Godot, as part of my Google Summer of Code proposal. This PR includes behind the scenes changes, like changes to Skeleton2D and Bone2D, as well as the IK code itself. You can find the 3D equivalent for this PR here: #39353

Below is a list of everything that has been added, removed, or otherwise changed. The list is in no particular order.


Changes

  • Added a SkeletonModifier2D class
    • Works just like the SkeletonModification3D class, just for 2D. It extends Resource, can be saved to a file, will be used by IK, is extendable via GDScript, and all modifications should be fully compatible with each other for mix-and-matching.
    • Has a function for clamping angles, as it is needed by almost every (if not every) SkeletonModification2D that extends it.
    • Has a property that determines whether the modification will run in either _process or _physics_process! This is a per modification property.
      • You can now mix-and-match physics aware modifications (only Jiggle right now) with non-physics aware modifications!
    • Has an editor-only function for drawing gizmos in the Godot editor using the Skeleton2D node's draw functions!
      • Extremely helpful for showing things like angle constraints!
      • The gizmo can be turned off via the modification settings and modifications that are not enabled will not be drawn. Modifications are drawn in the editor regardless if they are executing so things like angle constraints can be adjusted without IK running.
      • The gizmo drawing system for SkeletonModification2D resources is also exposed to GDScript, so users can make custom drawn gizmos if desired. See test 10 in the 2D test project for a simple working implementation.

|

  • Added a SkeletonModifierStack2D class
    • Works just like the 3D version: holds all of the SkeletonModification2D resources that are going to be applied to a Skeleton2D. Has settings for enabling/disabling all modifications, can set the interpolation strength, and can be saved to a file.
    • All modifications work with animations via the AnimationPlayer node, at least in my testing.
    • Modifications are now unique when duplicated, so they work with instanced/duplicated nodes/scenes.

|

  • Added SkeletonModification2DLookAt
    • Rotates a Bone2D in the Skeleton to look at the target. Simple, but can be very helpful.
    • Has an option to apply additional rotation, allowing for applying an offset.
    • Has fully working angle constraints that can be applied in both local-space or global-space. Angle constraints can also be inverted.
    • Has a fully functional editor-only gizmo that is drawn in the editor to show angle constraints. Can be disabled via a property.

|

  • Added SkeletonModification2DCCDIK
    • Uses CCDIK to solve a chain of bones, just like the algorithm in 3D.
    • Has full angle constraint support, including with inverted constraints. Like with LookAt, angle constraints can be enforced in either global-space or local-space.
    • Can rotate from the tip like normal CCDIK, or from the joint. This option is configurable on a per-joint basis.
    • Has a fully functional editor-only gizmo that is drawn in the editor to show angle constraints. Can be disabled on the entire modification, or on a per-joint basis.

|

  • Added SkeletonModification2DFABRIK
    • Uses FABRIK in 2D to solve the bone chain. Should work just like the current 2D IK solver for Skeleton2D, but as a SkeletonModification2D. Works in both the editor and in Godot projects.
    • Supports magnet positions, allowing for a measure of control over how the joints bend.
    • Has full angle constraint support that is taken into account when solving. Right now constraints only work in local-space (global-space support has not been added yet) Edit: angle constraints were not working. See comments below!
    • The final bone in the chain supports using the target's rotation, similar to the Basis override in SkeletonIK. It even works with angle constraints!
    • Has a fully functional editor-only gizmo that is drawn in the editor to show angle constraints. Can be disabled on the entire modification, or on a per-joint basis.

|

  • Added SkeletonModification2DJiggle
    • Rotates bones in a chain using jiggle physics. Great for hair, rope, edges of clothing, etc. Works just like the 3D version, but in 2D.
    • Has all the same options as the 3D version: stiffness, mass, damping, gravity, etc.
    • Has optional collider detection support! This is possible by running the modification stack in _physics_process mode. Uses raycasts to detect colliders.
      • It only applies to the motion created by the jiggle modifier. The jiggle modifier will not notice if a collider moves into it, but it will no longer move itself into colliders.
      • Options have been added to configure this functionality.
      • It actually works really well! It did require some modifications, but it also gives me a better idea of how to handle physics with modifications. This also should be doable in 3D.

|

  • Added SkeletonModification2DTwoBoneIK
    • Works just like the 3D version, but in 2D. Uses the Bone2D node to get the lengths required, making it easier to get setup and running.
    • Has support for angle constraints, though these constraints are not taken into account by the algorithm. Removed!
      • I'm not sure if there is a way to make the algorithm take the angle constraints into account when solving, making me unsure if angle constraints should be kept or not. The code is relatively easy to add or remove, so I left it in for now.
      • Removed because the constraints are not taken into account by the solver, which would be confusing to users as why it doesn't seem to "work". It also makes the code for TwoBoneIK smaller, as all of the properties can be removed. Maybe in the future I'll take another look at implementing angle constraints, but for now, I'm just going to leave it without.
    • Has a boolean, called "flip bend direction", that changes whether the TwoBoneIK modification will push the bones inward or outward when solving.
    • Has properties for defining the minimum and maximum distances the TwoBoneIK modification can take into account, allowing the user to control how far the solver can extend and contract when solving.
    • Has a fully functional editor-only gizmo that is drawn in the editor to show the bend direction. Can be disabled via the modification properties.

|

  • Added PhysicalBone2D
    • The new node extends RigidBody2D, allowing for full physics support and use of the RigidBody2D API. You can apply forces, set it to sleep, change the mode, etc.
      • Because it extends RigidBody2D, anything added, fixed, or modified in RigidBody2D will also benefit the PhysicalBone2D node automatically!
    • PhysicalBone2D has to be a child of a Skeleton2D node or another PhysicalBone2D node to function. This is similar to PhysicalBone3D in that respect.
    • PhysicalBone2D takes a Bone2D index (or NodePath to a Bone2D) and will automatically position itself accordingly.
    • Currently, PhysicalBone2D nodes that are children to PhysicalBone2D nodes recommend having a Joint2D node as a child to keep the joint chain intact.
      • The PhysicalBone2D has settings to auto configure the Joint2D settings. Right now this is limited to setting Node A and Node B in the joint, as well as setting the joint's position.
      • This may change in the future though!
    • Informs the user of any configuration issues via the get_configuration_warning function.
    • Can be applied to Bone2D nodes via a modification.
    • Supports Joint2D nodes and any constraints these nodes apply.
    • The creation of this node is to prototype how I may adjust PhysicalBone3D, making it more independent and usable with the new skeleton modification system.
    • Has a property to follow the Bone2D node when simulating, which is necessary for a PhysicalBone2D to be used as a root in a physics chain.

|

  • Added a SkeletonModification2DPhysicalBones
    • This modifier handles applying the transform from the PhysicalBone2D nodes to the bones and the skeleton.
    • Has functions that mirror the PhysicalBone2D API seen in Skeleton3D, but instead these functions are for PhysicalBone2D.
    • Exposes a function (and editor-only-property) that allows for fetching all PhysicalBone2D nodes in the Skeleton.
    • (Modification names are kinda long. Maybe I should shorten them? Hmm...)

|

  • Added a SkeletonModification2DStackHolder
    • This is a simple modification that allows users to use more than one modification stack with a single Skeleton2D node. Can be helpful for organization and modification reuse.
    • Known limitation: A stack holder modification can only execution modifications in the stack it's holding if the modification execution mode lines up with the stack holder modification's mode.

|

  • Made various changes to Skeleton2D:
    • Skeleton2D now holds a SkeletonModificationStack2D.
    • Added functions for setting a local_pose_override to Skeleton2D. The local_pose_override works just like it does for Skeleton3D, and can even be interpolated! It does require using RenderingServer to apply the override, but it works pretty well with only minimal changes required.
    • No longer uses a RenderingServer trick! Now it directly sets the transform of the Bone2D node.
    • Supports executing the modification stack in either _process or _physics_process, based on the information in the SkeletonModificationStack2D.
    • When modifications are running, the Bone2D nodes being modified change to the editor IK color.

|

  • Made various changes to Bone2D:
    • Bone2D now has a property called length, which is the bone's current length. default_length has been removed and its functions deprecated. length can be set in the editor like normal, so there's no loss of functionality.
    • Bone2D now has a property called bone_angle. bone_angle stores the angle that the bone has, which can be different than the transform's rotation.
      • For example, if you have a Bone2D making a right triangle. Since the Bone2D nodes are not rotated, but the bones themselves are (since bone rotation is determined by child Bone2D nodes), the rotation of the transform is not accurate enough for IK. bone_angle solves this issue by storing the angle of the bone. It is kinda hard to explain, but without it, all bone chains would have to be straight.
    • Bone2D now has a boolean for auto calculating the length and angle of the Bone2D, which is enabled by default. The autocalculation will use the first Bone2D child node it finds to calculate the length and angle. If there are no Bone2D children, it will just take the Bone2D's transform rotation instead and will print a warning.
    • Functions have been added for the various additions and properties exposed as required.
    • Bone2D internally caches it's transform when it is not being modified by a Skeleton modification. This is needed for proper pose interpolation between the bone's transform and the modification result.
    • Bone2D now handles its own editor gizmo via the _draw function.
      • The gizmo works exactly like before: It uses the same shape, color, etc. The only things that have changed is that it is not anti-aliased by default (due to project settings not having AA by default) and it no longer scales with the zoom level of the editor.
      • This makes the Bone2D gizmo in the proper position when overridden by modifications. This theoretically is possible in CanvasItemEditor, but I couldn't get it working and I even if I could, I fear the code would be rely on hacks and difficult to manage.
      • The gizmo now takes the bone angle into account, removing the need for using a Bone2D node just to have the Bone2D gizmo drawing the bone with the correct rotation.
      • The gizmo code only executes in the editor and uses #ifdef TOOLS_ENABLED, so no performance is lost on exported projects.
      • In the editor, a boolean property is exposed to allow for showing/hiding the editor gizmo. This option only is available in the Godot editor.
      • The editor colors for Bone2D gizmos have been adjusted so they are more transparent by default, making them easier to use with Sprite2D and Polygon2D nodes.

|

  • CanvasItemEditor changes:
    • The code for drawing Bone2D gizmos have been removed from CanvasItemEditor.
    • The code for 2D IK in CanvasItemEditor has been removed. Now Bone2D is the only bone (or bone-like) thing in 2D.
    • A few of the options in the Bone dropdown have been removed, like Clear Bones and the IK related options, as they are no longer needed or usable.
    • The Show Bones option now toggles the editor gizmo visibility of any selected Bone2D nodes and any child Bone2D nodes for those selected.
    • The Create Custom Bone2D(s) from Node(s) option will make Bone2D nodes using the selected Node2D nodes, and will re-parent the selected nodes to the created Bone2D nodes, making the creation process much easier and similar to how the Create Custom Bone(s) from Node(s) option worked.
    • The editor_selection variable has been changed to a public variable in CanvasItemEditor, as otherwise there is no great way for Bone2D nodes to know if they have been selected.
    • Its possible these changes are comparability breaking with Godot 3 projects that heavily rely on the old 2D IK system. I don't think there will be any issues, since old 2D IK saves the positions and doesn't work during runtime, but I have not tested. I need to find a project that uses the old IK system to double check.

|

  • Added a looking_at function to Transform2D.
    • This function is now exposed to GDScript and documented.

|

  • Added two new notifications to the Node class: 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 Godot editor.
    • This was needed to correctly reset the Bone2D nodes to their cache positions when saving the scene, so scenes saved with modifications running do not save the result of the modification over the position of the Bone2D node prior to the modification running.
    • These notifications will be extremely helpful for Godot users writing plugins, as now there is a way to detect when the editor is going to save and has saved, so now any preparations prior to saving can be properly performed. This was something that I found missing when I was writing Godot plugins, and something I have seen as a common question by those making plugins for Godot. (To be fair though, it does have a limited appeal.)
    • The new notifications are only called in the Godot editor, and the class reference has been updated to include the new notifications.

|

  • Added documentation for all new additions and any changes made.

|

  • Probably other little changes I have forgot! This will be a rather big PR with lots of changes, making it difficult to manage. I will try to keep this list as accurate as possible though.

Todo:

Nothing for now!

Known bugs/errors/problems:

  • None of the IK modifiers work when the Skeleton2D is scaled to a negative number on the X or Y axis.
    • Edit: Maybe this is not a bug? I did some looking around, and it appears a lot of Godot nodes don't work with negative scales (based on my Google searches). It seems to flip a character, the visual nodes should be flipped rather than using a negative scale. This may mean that the modifications not working with negative scales is normal/okay?
  • Known limitation: FABRIK and TwoBoneIK only work with uniform scales.
    • This is due to their reliance on bone length and how they handle scaled Bone2D nodes. Ideally this should be fixed by using a more universal solution, but I have yet to find one (as of when this was written).
  • Setting bones before setting the target causes some modifications not to run. Can not reproduce anymore, so I'm going to assume this bug is fixed.
  • Saving a scene with SkeletonModification2D resources attached to a Skeleton2D cause them to no longer work in the editor, though functionality returns upon reloading the scene. This is a known issue that also occurs in the 3D PR. Fixed! Used the same fix that I found in the 3D IK PR.

The test project I am using can be found here.

Disclaimer: Some of the work done in this PR was part of the Google Summer of Code 2020 program!

@Calinou Calinou added this to the 4.0 milestone Jul 13, 2020
@TwistedTwigleg TwistedTwigleg force-pushed the GSOC_2020_Working_Branch_2D_IK branch 7 times, most recently from cd1736c to 195ef55 Compare July 20, 2020 22:10
@TwistedTwigleg TwistedTwigleg force-pushed the GSOC_2020_Working_Branch_2D_IK branch 3 times, most recently from 054bd07 to 78fcf91 Compare July 24, 2020 15:23
@TwistedTwigleg TwistedTwigleg force-pushed the GSOC_2020_Working_Branch_2D_IK branch 4 times, most recently from 2d7d309 to 3edb4f2 Compare July 31, 2020 15:42
@TwistedTwigleg TwistedTwigleg marked this pull request as ready for review August 3, 2020 16:51
@TwistedTwigleg TwistedTwigleg requested a review from a team as a code owner August 3, 2020 16:51
@TwistedTwigleg
Copy link
Contributor Author

Alright, I think this PR is ready for review now! 🎉

@TwistedTwigleg TwistedTwigleg force-pushed the GSOC_2020_Working_Branch_2D_IK branch 2 times, most recently from 6fa802e to da0f120 Compare August 3, 2020 19:20
@TwistedTwigleg TwistedTwigleg force-pushed the GSOC_2020_Working_Branch_2D_IK branch 5 times, most recently from 7123dcf to 8cb4e88 Compare August 26, 2020 21:42
@TwistedTwigleg
Copy link
Contributor Author

TwistedTwigleg commented Aug 27, 2020

Just an FYI to anyone looking - I need to update the 2D IK PR to the latest version of master to fix a merge conflict that has arisen. I was going to wait until the end of the GSOC, but because of the merge conflict, the CI tests are not running. Thankfully it appears everything is compatible going forward, with only PhysicalBone2D having some issues in the latest version of master. I'll rebase this PR soon and will update this comment once the PR is fully rebased.

Edit: Rebased with the latest master build! It is a little unstable for PhysicalBone2D still, but the rest seems okay. I'm occasionally getting crashes, but it seems to be unrelated (and instead is input related?).

Edit 2: Nevermind, it's just because 2D physics in Godot are broken right now. It is not related to the code in this PR!

Edit 3: 2D physics are now fixed in Godot master, so all that's left is to rebase. I'll rebase this PR to master soon 👍

…rks.

Edit: Fixed formatting issues with the static formatting checks
Edit 2: Even more formatting fixes to hopeful solve static formatting check issues.
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.
…rmal 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
… 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
…e code in Skeleton2D, fix documentation issues
* 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)
@TwistedTwigleg
Copy link
Contributor Author

Alright, everything has been rebased and the review feedback taken into account. I messed up one part of the rebase a little bit, but since this PR isn't going to be merged and a new, single commit PR will be instead, I figured it was fine to leave it.
Once everyone is ready and everything here is good to go, I'll make a single commit PR with these changes.

Thanks again for the reviews everyone! Also, thanks for the link and example on the commit message @akien-mga, I will structure the commit on the new PR accordingly.

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are still lots of parameters that are missing the _p prefix in several headers (coding style).

@@ -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) const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TwistedTwigleg There are still lots of parameters that are missing the _p prefix in several headers.

Copy link
Contributor

@pouleyKetchoupp pouleyKetchoupp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's still some coding style feedback not addressed from @akien-mga's review. Once it's done you can create a new PR with squashed commits.

Here's the list remaining issues that we need to keep track of:

  • Editor code needs to be removed from scene node (might need proper support for 2D gizmos plugins).
  • Use property binding macros instead of _set / _get override
  • Review NOTIFICATION_EDITOR_PRE_SAVE & NOTIFICATION_EDITOR_POST_SAVE notifications used to reset the skeleton while saving the scene.

@akien-mga
Copy link
Member

Here's the list remaining issues that we need to keep track of:

Just to clarify, that's the list of issues we want to keep track of once this has been merged. They don't need to be addressed in this PR, as discussed previously.

@TwistedTwigleg
Copy link
Contributor Author

Thanks for the review, I will try to address the remaining code style feedback issues this weekend.

… SkeletonModification2D pages. Syntax changes to canvas_item_editor_plugin, skeleton_2d, skeleton_modification_2d, and skeleton_modification_2d_jiggle
@TwistedTwigleg
Copy link
Contributor Author

Alright, the syntax changes have been made and now every file should be good to go. If everyone is good with how everything in this PR, I'll make a new PR with the commits squashed into one.

Copy link
Contributor

@pouleyKetchoupp pouleyKetchoupp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! It should be good to go with this one last thing I missed before. I guess you can open the new PR at this point.

@@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Transform2D looking_at(const Vector2 p_target) const;
Transform2D looking_at(const Vector2 &p_target) const;

@TwistedTwigleg
Copy link
Contributor Author

I'm not sure why the CI is failing, all I did was make the change suggested and it compiles fine on my machine. Regardless, I made the squished PR here: #47872

@YeldhamDev
Copy link
Member

@TwistedTwigleg May I close this then?

@pouleyKetchoupp
Copy link
Contributor

Let's close this PR since #47872 was opened instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants