Workflow improvements for creating Animation Loops #54724
Labels
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset System
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Viewport & EEVEE
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Asset Browser Project
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Module
Viewport & EEVEE
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Severity
High
Severity
Low
Severity
Normal
Severity
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
15 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: blender/blender#54724
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Currently blender UI provides rather limited workflow aids for creating smoothly looping interpolated (not keyed on every frame) animation, like walk cycles. It would be good to improve the situation.
These are some objectives involved in creating loops, which I can think of at the moment, and their current status:
Looping the Animation (in release)
Simply creating a repeating animation has been possible for ages in a number of ways:
All of these methods have a common property: in order to represent the cyclic animation, which can be conceptually imagined to have its keys be located on a literal cycle of time, as a linear F-Curve, one of the conceptual keys has to be arbitrarily selected, split into two copies, and set to represent the 'ends' of the cycle. This specific fact causes major workflow problems when editing the animation, which need to be solved.
Smooth Auto Interpolation Across Loop Transition (addressed in master)
A very important feature for convenient animation workflow is Auto bezier handle placement, which tries to create a smooth bezier interpolation curve by computing handles purely from the created key positions. This allows animating just by inserting keys, without mandatory use of the Graph Editor.
In order to create a smooth loop transition, this computation must be aware of the fact that the animation loops, and be able to look 'through' the loop ends to take the full flow of the curve into account.
This raises a technical design problem with code abstraction layers with both of the currently existing methods for looping the animation. Specifically, handle computation happens at the level of the base F-Curve, while both modifiers and especially NLA are supposed to be layers that simply change how the curve is evaluated. A 2.49 style Cyclic extrapolation setting won't have this abstraction problem.
For now this objective has been addressed in master by D2783 and D2884, via detecting and specially handling the F-Curve modifier with settings completely equivalent to those legacy extrapolation options (henceforth trivially cyclic curves); however this is an obvious hack from code design standpoint.
Addressing this for NLA seems to be completely impractical, unless actions are instanced in some COW fashion for each track, due to there being even more layers between the curve and the NLA stack, and the fact that settings can differ between tracks. This means that NLA is currently disqualified as a way to create smooth loops, and this workflow is more suited to be implemented at F-Curve/Action level.
Keeping Ends in Sync
As mentioned above, all of the existing ways of looping an animation require duplicating one key to serve as both ends of the linear curve. These keys must be kept in sync to avoid a jump in the action. However, Blender does not aid this in any way, so the burden falls completely on the user. Addressing this issue is likely the most important improvement to be done next.
The curve keys may be edited in a number of ways:
Insert Keyframe via 3D View (manual or auto)
This situation is quite simple to handle: it is simply necessary to duplicate the insertion to the other end of the curve if an end key is modified. In order to facilitate working with staggered curves (i.e. overlap etc) it is also convenient to remap the insertion in time if an attempt is made to insert a key outside of the existing range.
D3140 adds a toggle button that enables this specific behavior when modifying trivially cyclic curves. The behavior isn't always on because it may be inconvenient or confusing during blockout phase, when the loop isn't properly established yet.
Dopesheet
Dopesheet and action editor allow inserting keys, and also moving keys in time. Since the user sees the actual range of the curves in these editors, it is a question to be discussed whether it is more useful or confusing to retime key insertion. Regarding retiming keys, moving an end key may mean an intent of changing the loop period, or retiming the end key within the same loop period; this changes whether the modification should be duplicated to the other end.
Graph Editor
In addition to having similar concerns to the Dopesheet, this editor allows modifying bezier handles. This means considering how and when these changes should be duplicated to the other end, including a technical problem of what to do if the user actually has both end keys selected when performing an action.
Maintaining Loop Period Consistency
While staggering the loop range for different curves is a desired property, having differing loop periods for different curves in an action is likely undesirable. Thus an ability to specify a desired loop period at Action level, with some automated support for enforcing it may be useful. This may include things like automatically configuring F-Curves that are added within the action to be cyclic and setting up the correct period, or highlighting curves with the wrong period in the UI.
On occasion it may be actually useful to have different loop periods in an action, especially of lengths that are a divisor of the main period.
This objective requires discussion, and possibly experimentation with python addons to find what is useful and how to design it best.
Miscellaneous
Rotate Loop Operator
As described above, the location within the loop that is split to create the technically required loop ends is arbitrary. Thus sometimes it is useful to be able to change this without actually changing the resulting animation by appropriately moving keys around. This can be done manually in dopesheet, but a single operator would obviously be more convenient.
Cycles That Repeat Mirrored
Some cycles, like perfectly symmetrical walk cycles, have two halves that are mirrors of each other. Editing these involves copying any changes to the mirrored section, so implementing at least an operator (possibly in python) to do that with one button press could be useful.
Added subscribers: @angavrilov, @JoshuaLeung, @cessen
Added subscriber: @FinbarrORiordan
Added subscriber: @FrankMartin
Added subscriber: @dr.sybren
To start some discussion: As shown by #83023, and further argued in chat by @dr.sybren it is confusing to users when 'modifiers' affect the shape of the curve. Therefore maybe it is better to return to the approach suggested in D2790 and make smooth transition across loop ends a feature of re-introduced built-in extrapolation modes.
So, I've been thinking a bit about this, and came up with some potential small steps to incrementally progress on this issue:
Tools that don't require hooks into built-in behavior like initializing the cyclic curve when it's added due to key insertion could be prototyped in a Python addon.
I think being able to specify an explicit action-level frame range is a core feature that should be supported. It has a number of useful applications:
For looping animations, as mentioned under the "Maintaining Loop Period Consistency" heading in this ticket, it allows Blender to actually help make the animation loop properly, freeing up the animator to focus on the animation itself. The current infer-action-length-from-keyframes approach fundamentally doesn't cut it for this.
Even outside of looping animations, it's often useful to specify the intended extent of the action. For example, when building an animation library for a game character, you want to make sure all of those animations are exported with the intended length, whether they're looping or not.
Blender's current approach of trying to infer action frame range/length from key frames is assuming that keyframes themselves are a useful measure of animation length. But that's simply not the case. It's frequently (even usually?) useful to set keyframes outside the intended playback range.
So although this goes a bit beyond just the looping animation use-case of this ticket, I'd like to suggest that any solution be built on the following action-level data:
The frame range allows the animator to specify the intended length (which is often useful even for non-looping actions), and the loop boolean will let Blender help (or enforce) proper looping when it's desired.
@cessen I think 'frame range' is not useful - the important thing is 'loop length'. A loop is by definition infinite, so you can cut out any range of the period length, and have a consistent loop. As long as Blender animation curves require 'ends' with keyframes there to work (a rather core assumption in the system), enforcing a 'range' is counterproductive as that wouldn't allow shifting individual curves by a few frames to do overlap.
Thus, given that that core design feature of curves isn't changed any time soon, to support looping actions it's necessary to:
Point 3 here is already partially addressed by the Cycle-Aware Keying option - when it is enabled, inserting keys into cyclic curves is automatically remapped to be within the actual keyed part of the range, so you can in fact insert keys outside the 'technical' loop ends and they should be adjusted to give the desired effect of tweaking the loop curve rather than extending its period.
However, to use the option you need to first manually enforce points 1 and 2 (and it only affects key insertion, i.e. 'I' and auto keying), so the immediate area for gradual progress is there.
P.S. Also, regarding loop length: do you think having multiple different length sub-loops in the same action is useful/important? I.e. sometimes some curves might loop on a 1/2 or 1/3 or whatever of the main period. Or should it always enforce identical period on all curves in the action?
P.P.S. Another thing is, if the loop length is fixed (at action level or curve level), how do you change it in the UI?
@angavrilov
I understand this point of view, and for the specific application you're considering you're right. But what I was trying to communicate in my comment above is that this feature applies to things beyond just using animation loops in the NLA system. For game animation, for example, you definitely want to be sure that the same frame range is exported every time. And for non-looping animations, you still want to be able to specify a frame range for various applications.
So, in a nut shell, using an explicit frame range covers not only this use-case but also a host of other ones, so I think it's the way to go.
@cessen Actually, I didn't think about NLA at all. Since this all started with an idea of auto handles taking cycles into account to produce a smooth transition, I'm focused on the fcurve/action level - NLA is too far from handle interpolation to matter.
I agree that for NLA an option to specify a default range in the action would be useful, even for non-cyclic actions - provided it is not interpreted to restrict keyframe placement when editing the action itself, except to maintain the length of the loop, so that you can shift the individual cyclic curves in time a bit. Then the Cycle-Aware keyframing option could use just the length of the range for an immediate improvement in features.
@angavrilov Ah, got it!
And yeah, that's how I envision it working: it wouldn't limit the action's use. You could always go outside its frame range if you want. But it would be used by various features (export, animation loop management, default initial clip size in the NLA, default initial range in action constraints, etc.) to make them work more smoothly.
Basically, it would allow animators to directly specify their intentions rather than Blender trying to infer them (usually incorrectly) from the keyframes.
Added subscriber: @ErickNyanduKabongo
So where would you put the range in the ui? Action Editor header, or the panel on the right? Also, the range probably should be highlighted in the background of the action editor, so how should it be distinct from the highlighting of the playback range?
I think figuring out the UI will require some experimentation, to figure out what works in practice.
My initial thoughts for what to try first are:
But doing something different may well turn out to be better.
Added subscriber: @Dene
I had to create looping animations for games in the past. And for non-skilled animator (I've been at that time) that was really hard to figure it out - why the animations are not looping. Even if one just copy first keyframe and past it as a last keyframe that will not solve the problem. The key here are the handles that animator needs to adjust to loop animation perfectly. I think that's pretty easy to automate it when you know the first and last frame number of the loop. Usually that's first and last keyframes of the action but sometimes that's not the case. I'd say user should set loop frame range manually so the curve will be adjusted. As a animator I'd prefer destructive fix of the curve, not a modificator or NLA related stuff.
I see two approaches here:
Added subscriber: @wbmoss_dev
(haven't read through everything)
Is the special smoothing behavior the reason cyclic modifiers must be the first modifier in the list? If so, then yeah I'd rather move the special behavior elsewhere instead. It's weird that we limit cyclic modifier placement for that behavior.
Added subscriber: @fred23
Added subscriber: @AndyCuccaro
This issue was referenced by
5d59b38605
So recently in a branch I implemented the per-action custom frame range idea like this:
{F10145101,size=full}
The settings appear only in the Action Editor with the action selected.
The range is technically specified in floats because actual keyframes in the action, and hence the 'default' range used by NLA, can be fractional.
This looks great to me! Is your branch published somewhere so I could play around with it (mainly just because I'm excited, ha ha)?
Added subscriber: @Mets
Sybren has asked me to go through this and try to form an opinion, so here goes! I do have some experience with animating loops, although it was a few years ago, so the issues discussed here definitely ring a bell and I'm sure many animators would welcome improvements to them. TL;DR: I love what you guys have come up with so far, would love to test!
After reading the task but before even reading the comments, my first gut reaction was "Add some new properties at the Action level that forces certain behaviours" which is exactly what you guys have also come up with.
The 3 points listed in #54724#1069292 I completely agree would be great behaviours to simplify this workflow.
I agree with this also.
Sign me up for trying out that branch! I still wonder what would be the best way to have as few "range" definitions as possible, while still allowing flexibility while in Blender, but consistency when exporting out of Blender (which I agree is important to consider here, since looping animations are mostly used for games). 👍
Hi @Mets! Glad you like the proposal here. I'm definitely super excited to see something like this in Blender, too. :-)
A probably unnecessary and overly pedantic note, but just want to add again that non-looping animations (e.g. think of death animations for enemies) are also important for games, and usually need to have a consistent animation length on export as well. Just want to make sure we don't lose sight of that. This feature is definitely critical for good looping animation workflow, but it also needs to accommodate the non-looping case.
Ultimately, it just comes down to it being fundamentally broken (IMO) to infer animation length from key frames at all. It would be broken if we did it for scene-level animation length, and it's broken for action-level animation length as well. It's a long-standing issue with Blender.
About the branch, the changes used to make the screenshot above should be available (along with other stuff) in the 'temp-angavrilov-constraints' builds here: https://builder.blender.org/download/experimental/ I'm waiting on the patches needed for new Rigify face before submitting this properly.
One big limitation currently is that not all actions can actually be viewed in the action editor - iirc it only covers Object and Shape Key actions. That makes these options inaccessible in the UI for more obscure actions, e.g. if you keyframe material properties etc.
How about placing the properties in the (N) panel under the tab "Action"? It could be shown for the active channel's action. That way it can be used in all* other editors, including the Graph Editor.
*except the Dopesheet Mask Editor since there's no active channel support.
Added subscriber: @AquaticNightmare
This issue was referenced by
72acce43bc
Added subscriber: @BClark
Just a heads up, been a while since this has been updated it looks like but I am working on and have someone exploring improving the NLA repeat have the ability to also do offset like the cycle modifier repeat with offset.
This would save some hacks and workarounds and allow the repeat tool to be much more usable for common production tasks.
https://docs.google.com/document/d/1mRMN4pv28YUHd6w3_vW3ZHzchKe8fuKZ6lwOPcawk74/edit#heading=h.l6jkqw4kjgn7
Added subscriber: @sirwesleybarlow
From #111322 but I think this is more relevant here so I'm commenting here (maybe some of the UX could be reevaluated in light of this user story?):
The whole interaction between animations and looping is horribly complicated and undiscoverable at the moment. To loop an animation you need to 1. turn on manual frame range (or at least, the checkbox is grey unless you do that) in the action editor or the nla editor action side panel, 2. turn on cycle-aware keying (documentation for cyclic animation implies this is necessary) in the timeline, 3. add a cycle f-mod to each channel (100 bones, 10 dimensions - loc xyz, scale xyz, rot xyzw = need to add 1000 modifiers, see further notes about that) - however, depending on how you went about this this may be done automatically? in the graph editor, 4. create a new frame (not the end frame) in your animation, and the end frame will be automatically created
If any of these are missing looping doesn't happen. It's not clear what each of these do on their own, or what their scopes are (ex: cycle-aware keying appears to be global, but it supposedly affects playback of individual actions per documentation - if action cyclic animation is useful on its own then it's possible you'd want cycle-aware keying for some actions but not others, but this isn't achievable given current actions).
Here's what I tried to add the cycle f-mod since it wasn't created for me in one instance. Opening the graph editor, the object and action are selected in the tree and the modifier sidepanel is missing. After clicking the action or "object transform" items in the tree one by one it's still missing. Holding your cursor over the graph area and pressing "a" selects all the channels but the mod panel doesn't appear. Holding ctrl and clicking the object makes everything disappear (requiring you to switch editors and re-select the object then switch back), but it doesn't disappear when you toggle selection with "a". I expanded the "object transform" item to view the individual channels. With the object and action items in the tree selected, pressing "a" in the graph editor to select all channels doesn't result in the mod panel appearing. Toggling "a" with my mouse over the tree, first everything except the object is deselected, then everything is selected (including the channels) but the modifier sidepanel doesn't appear. Holding ctrl and deselecting the action and "object transform" items (careful not to deselect the object) the modifier sidepanel still doesn't appear. In the end I had to expand everything, with my mouse over the tree area deselect everything with "a", then move my mouse over the graph area and press "a" to select just the channels before the modifier sidepanel appeared. (These are all the things I tried before finding the right way to do it, including a significant amount of googling and watching youtube videos, all of which suggested copying the modifier to each channel individually).
Per #111322 depending on the order of adding keyframes and enabling cycle-aware keying the end frame may or may not be automatically created. It appears not to be recreated if removed. Depending on the order you do things you may need/be able to manually create the end frame, or it may be impossible to manually create the end frame. This may also depend on or change based on the cycle f-mod being set up. I'm not sure what happens if you reorder the end/start frames within the action, but I can't predict what would happen either.
@tj2n497vxdq1ui
I agree that in practice this has turned into what essentially feels like a series of magical incantations that have to be done just right to get things to work properly.
What I originally envisioned was that an action could either be looping or non-looping, and that this would be a real distinction (e.g. all f-curves in a looping action would always be interpreted as looping with the specified period, no extra modifiers required). Instead, what we have now tries to reusing existing machinery (which often makes sense, but IMO didn't work well in this case), and whether an action is looping or not is little more than an informative checkbox and doesn't change anything about how the action's animation data is actually interpreted by Blender.
I do think that things can still be improved even with the current semi-ad-hoc system. But as a note to myself: this is important to keep in mind and get right for Animation 2025. The looping animation use case is common and fundamental, and IMO should be supported as a fundamental feature, not as an ad-hoc collection of features that require careful management by the user.