godot
godot copied to clipboard
Support converting Animation Track to Bezier Track
This would close https://github.com/godotengine/godot-proposals/issues/9597
TODO
- [x] Add edit option to convert to bezier curves
- [x] Add popup to select tracks with
- [x] Add functionality to generate the bezier tracks and delete the old tracks
~~Ensure angle rotations work correctly~~ as far as I can tell they actually work as expected already - [x] Add support for colors and planes
- [x] Add functionality to autogenerate balanced handles based on surrounding keys
- [ ] Add Undo/Redo support
https://github.com/godotengine/godot/assets/4955051/8287ed20-b04b-40af-a374-7173e213d037
Small sample project to test on. just a simple 2d animation you can convert tracks on. convert_to_bezier_examples.zip
I worked on this a bit and got stuck, but I noticed you converted the tracks directly which seems to work right?
I worked on this a bit and got stuck, but I noticed you converted the tracks directly which seems to work right?
Yeah it seems to work well.
And by the way it's still a bit rough so you probably don't need to review it until a work through the remaining issues and mark it as ready for review.
I also found out that the _get_bezier_subindices_for_type function does a bunch of work I can reuse rather than doing it myself, so I'm adding that now.
I think I got lost when doing euler rotation tracker -> bezier quaternion/basis but curve reduction wasn't required for this problem. So I appreciate!
https://github.com/V-Sekai/godot/tree/keyframe-reduce-3.5
https://github.com/godotengine/godot-proposals/issues/392
Yeah I need to deal with angle rotations correctly. I'll add a todo for that as well.
I might be able to get angle rotations correct for you. Poke me when you're at that point.
Edited:
I was able to extract the bezier key_frame reducer independent of the transforms for my Godot Engine proposal for 3.5. Now to make sure it works for Godot Engine 4.
Edited:
https://github.com/V-Sekai/godot/tree/vsk-keyframe-reduce-4.3
class Vector2Bezier : public Vector2 {
public:
// Distance between two points
// @return real_t distance
real_t distance_between(Vector2Bezier p_other) const;
// Signed angle between points
// @return real_t
real_t signed_angle(Vector2 p_this, Vector2 p_other);
Vector2Bezier();
Vector2Bezier(real_t p_x, real_t p_y);
Vector2Bezier(Vector2 p_vector2);
};
struct KeyframeReductionSetting {
// Maximum allowed error when reducing the animation curves.
real_t max_error = 0.1f;
// Step size at which to sample the animation curves."
real_t step_size = 0.5f;
// The threshold to split tangents.
real_t tangent_split_angle_thresholdValue = 5.0f;
};
struct Bezier {
Vector2Bezier in_handle; //relative (x always <0)
Vector2Bezier out_handle; //relative (x always >0)
Vector2Bezier time_value;
bool weighted_tangents = true;
Bezier normalized() const;
Bezier operator-(Bezier p_other) const;
Bezier();
Bezier(Vector2Bezier p_time_value, Vector2Bezier p_in_handle, Vector2Bezier p_out_handle);
};
// Reduce the number of keyframes on the animation curve. Useful when
// you are working with baked curves.
// @param Vector<Bezier> p_points
// @param Vector<Bezier> r_keyframes
// @param KeyframeReductionSetting p_settings
// @return float Reduction rate
real_t reduce(const Vector<Bezier> &p_points, Vector<Bezier> &r_keyframes, KeyframeReductionSetting p_settings);
Edited:
Vector to LocalVector ...
Wait wouldn’t this be related to https://github.com/godotengine/godot-proposals/issues/392 ?
Also there could be a feature where we can select tracks without selecting the check button, as it would make it a better experience when using this for a lot more bones.
Hmm I think I completely bonked the rebase of this. I will probably close it and make a new PR.
I just did a bunch of cleanup on this and it now works to auto balance / mirror all the keys with the drop down as well.
I have tried several times now to get undo / redo set up correctly and I just cannot crack it. I think the issue is that I use functions such as _animation_track_remove_request to clean up the keys after because it clears the reset tracks correctly. But that function already has Undo / Redo set up, so I'm not sure how to do it correctly on top.
https://github.com/Arnklit/godot/blob/512c19095bc1035cefd3cd3e7a686626876cd745/editor/animation_track_editor.cpp#L6824-L6833
Looking at the documentation from Undo / Redo I don't really understand if you are allowed to nest undo redo stuff on top of each other or not.
If anyone wants to give it a crack or point me in the right I would very much appreciate it. I was told @KoBeWi is the Undo / Redo master, so maybe they can give me a hint if they have time :).
Nesting undo/redo actions is allowed and encouraged if an action consists of several already-existing undo/redo actions. Only the top-most action will be "visible" in the history, nested ones just execute silently.
This is now ready for a review if you wanna have a look @fire . Huge thanks to @mihe (set as co-author) who ended up adding undo redo for this and cleaned up the code immensely.
I have looked at the code and the most major concern is that there is no conversion taken into account at all with respect to interpolation.
ValueTrack is allowed to perform nonlinear interpolation in several ways:
UpdateMode
- Continuous
- In theory, it can be migrated to Bezier tracks, but InterpolationType must be taken into account
- Discrete
- Similar to Nearest Interpolation, but due to the difference in update timing, it is almost never possible to migrate to Bezier Track
- Capture
- Currently un-migratable: Need to make the Bezier track available for Capture
InterpolationType
- Nearest
- Currently un-migratable: Need to implement a handle mode like Blender's NLA track for Nearest interpolation in the Bezier track
- Linear
- If Easing is not set, can be migrated with
HandleMode = None - If Easing is set, should be calculate In-Out handle value to reproduces the Easing
- If Easing is not set, can be migrated with
- Cubic
- Can be migrated, but should be calculate In-Out handle value to reproduce Cubic interpolation, also key positions may be shifted from ValueTrack
- If Easing is set, it is still possible to migrate, but the calculation In-Out handle value is more complicated due to the double interpolation (Cubic + Easing)
As I wrote above, it is somewhat possible to calculate the In-Out handle value directly from the existing ValueTrack properties to reproduce ValueTrack easing. However, if that is too difficult, we can think the approach that all interpolations except Discrete and Nearest are once baked as Linear and reduce the curve points by approximation. If it does not, then at the very least there should be a check to handle only Continuous + Linear + Easing 1.0 cases.
In the end, there are many cases where you cannot migrate from ValueTrack to BezierTrack. While BezierTrack appears to perform more advanced interpolation, in reality ValueTrack has more options for interpolation, so it is my opinion that it can only provide a half-assed conversion.
As I have said before somewhere, my opinion is that the right way to go in core is to discontinue BezierTrack and have the Bezier option in ValueTrack's InterpolationType; However, this would require a lot of work and may not be possible until Godot 5.
So I think the better solution is to provide this as an add-on and distribute it in the asset library. Then the only thing we can do with Godot 4 is to expose the missing API to make it an add-on.
Is there anything we can do to beizer to implement Nearest Interpolation support?
How does Blender's NLA track for Nearest interpolation work?
Would you approve creating a new InterpolationType for ValueTrack? and deprecating BezierTrack for Godot 4?
How does Blender's NLA track for Nearest interpolation work?
This is a bit off-topic, but to implement Blender-like NLA animation, we would need to increase HandleMode, and do HandleMode-dependent calculations where the final BezierTrack interpolation results are calculated. In any case, it would be difficult to support that as of Godot 4.
Would you approve creating a new InterpolationType for ValueTrack? and deprecating BezierTrack for Godot 4?
I believe it's worth doing this - we can't remove BezierTrack during Godot 4, but we can move forward in preparing for Godot 5.
@TokageItLab Could I just get a verdict on whether it makes sense to keep working on this. Is your feeling it only makes sense to add this if it perfectly translates the regular keys into bezier keys?
The thought behind the tool was for it to help people who start out making regular keys, but change their minds along the way, so they don't have to start our from scratch. Since we are stuck with two different key types for now, it felt like a nice option to give people, but I'm not sure it will ever be a perfect conversion tool.
I'll move over to make this some kind of addon instead if you feel it does not make sense to include it unless it covers every behavior of regular keys.
@TokageItLab can you reply?
I'm in favour of adding this because it also leads into my code for simplifying the beziers.
I believe it should not be supported in core because it would be an incomplete feature in any way.
As commented above, we will eventually need to do the following:
Would you approve creating a new InterpolationType for ValueTrack? and deprecating BezierTrack for Godot 4?
I believe it's worth doing this - we can't remove BezierTrack during Godot 4, but we can move forward in preparing for Godot 5.
If there are missing APIs to make this PR an add-on, adding them would be acceptable.
Thanks guys. I'll close this. And get started on the addon.
EDIT: I have now implemented this as an addon: https://github.com/Arnklit/godot-anim-track-converter