Workflow improvements for creating Animation Loops #54724

Open
opened 2018-04-19 13:21:18 +02:00 by Alexander Gavrilov · 35 comments

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:

  • NLA: a track can be set up to repeat a certain keyframe range of an action.
This results in a consistent loop that uses the exact same time range of all F-Curves in the action. However, different tracks using the same action may have different settings.
  • F-Curve Modifier: the Cycles modifier makes a single F-Curve repeat.
The period and range of the loop is determined by the keyframes existing in the curve and may be different for different curves of the same action. This is very convenient, because applying the concept of overlap in the animation in some cases requires staggering keys away from nice columns. Absolutely requiring the loop to start and end on one single frame for all curves is thus stifling. However, this freedom can also be a cause of inconsistency in the loop periods, which is a bad thing.
The modifier also has advanced settings that allow e.g. limiting the number of iterations. However, if cycle ends are staggered as described above, the usefulness of this is somewhat doubtful.
  • (Pre-2.5 only) Cyclic Extrapolation: the extrapolation enum of the F-Curve had Cycle and Cycle with Offset settings.
These were abandoned in favor of the modifier approach due to the possibility of extended settings in the modifier case. However, there are some code design arguments in favor of returning to this approach if implementing more comprehensive and integrated support for editing looping animations (e.g. see abandoned D2790).

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.

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: - **NLA:** a track can be set up to repeat a certain keyframe range of an action. ``` This results in a consistent loop that uses the exact same time range of all F-Curves in the action. However, different tracks using the same action may have different settings. ``` - **F-Curve Modifier:** the Cycles modifier makes a single F-Curve repeat. ``` The period and range of the loop is determined by the keyframes existing in the curve and may be different for different curves of the same action. This is very convenient, because applying the concept of overlap in the animation in some cases requires staggering keys away from nice columns. Absolutely requiring the loop to start and end on one single frame for all curves is thus stifling. However, this freedom can also be a cause of inconsistency in the loop periods, which is a bad thing. ``` ``` The modifier also has advanced settings that allow e.g. limiting the number of iterations. However, if cycle ends are staggered as described above, the usefulness of this is somewhat doubtful. ``` - **(Pre-2.5 only) Cyclic Extrapolation:** the extrapolation enum of the F-Curve had Cycle and Cycle with Offset settings. ``` These were abandoned in favor of the modifier approach due to the possibility of extended settings in the modifier case. However, there are some code design arguments in favor of returning to this approach if implementing more comprehensive and integrated support for editing looping animations (e.g. see abandoned D2790). ``` 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](https://archive.blender.org/developer/D2783) and [D2884](https://archive.blender.org/developer/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](https://archive.blender.org/developer/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.
Author
Member

Added subscribers: @angavrilov, @JoshuaLeung, @cessen

Added subscribers: @angavrilov, @JoshuaLeung, @cessen

Added subscriber: @FinbarrORiordan

Added subscriber: @FinbarrORiordan

Added subscriber: @FrankMartin

Added subscriber: @FrankMartin
Author
Member

Added subscriber: @dr.sybren

Added subscriber: @dr.sybren
Author
Member

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.

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](https://archive.blender.org/developer/D2790) and make smooth transition across loop ends a feature of re-introduced built-in extrapolation modes.
Author
Member

So, I've been thinking a bit about this, and came up with some potential small steps to incrementally progress on this issue:

  1. Introduce built-in extrapolation options "Smooth Cycle" and "Smooth Cycle With Offset" to take over the special boundary smoothing behavior from the modifier and thus remove the abstraction violation. Versioning code would convert the 'trivial cycle' cases of the modifier to the new option, while the modifier would be left to handle non-smoothed looping.
  2. As a first step of extending the support of cycles to the action level, add a 'default extrapolation' option to Action (show in the right panel of Action editor?) If a cyclic extrapolated curve is added via this default to an action while Cycle Aware Keying is enabled, it would add a second keyframe to initialize the curve to the longest cycle length found in the action (or maybe most common is best?..) so that the option would immediately work reasonably with the new curve. Since there is no python hook for adding a curve, this behavior must be built-in. This would make it easier to extend an already cyclic action with new channels while keeping the cycle consistent.
  3. Add an option to show the current extrapolation of the curve in Dope Sheet / Action Editor, maybe as another icon to the right of curve names on the left? Currently it's possible to change extrapolation, but it is not shown in any way without going to Graph Editor.

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.

So, I've been thinking a bit about this, and came up with some potential small steps to incrementally progress on this issue: 1. Introduce built-in extrapolation options "Smooth Cycle" and "Smooth Cycle With Offset" to take over the special boundary smoothing behavior from the modifier and thus remove the abstraction violation. Versioning code would convert the 'trivial cycle' cases of the modifier to the new option, while the modifier would be left to handle non-smoothed looping. 2. As a first step of extending the support of cycles to the action level, add a 'default extrapolation' option to Action (show in the right panel of Action editor?) If a cyclic extrapolated curve is added via this default to an action while Cycle Aware Keying is enabled, it would add a second keyframe to initialize the curve to the longest cycle length found in the action (or maybe most common is best?..) so that the option would immediately work reasonably with the new curve. Since there is no python hook for adding a curve, this behavior must be built-in. This would make it easier to extend an already cyclic action with new channels while keeping the cycle consistent. 3. Add an option to show the current extrapolation of the curve in Dope Sheet / Action Editor, maybe as another icon to the right of curve names on the left? Currently it's possible to change extrapolation, but it is not shown in any way without going to Graph Editor. 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.
Member

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:

  1. 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.

  2. 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:

  1. An explicit (optional) frame range for the action.
  2. An explicit boolean specifying whether the action is looping or not.

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.

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: 1. 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. 2. 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: 1. An explicit (optional) frame range for the action. 2. An explicit boolean specifying whether the action is looping or not. 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.
Author
Member

@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:

  1. Enforce all curves in such action are looping.
  2. Enforce all curves in the action have the same time difference between first and last keys (i.e. enforce loop length).
  3. Make the actual position of the curve keys in the timeline not important for animation, i.e. give the illusion that the action is truly an infinite loop for editing.

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?

@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: 1. Enforce all curves in such action are looping. 2. Enforce all curves in the action have the same time difference between first and last keys (i.e. enforce loop length). 3. Make the actual position of the curve keys in the timeline not important for animation, i.e. give the illusion that the action is truly an infinite loop for editing. 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?
Member

@angavrilov

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.

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.

@angavrilov > 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. 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.
Author
Member

@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.

@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.
Member

@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.

@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

Added subscriber: @ErickNyanduKabongo
Author
Member

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?

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?
Member

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:

  • Put it in the panel on the right, as that feels more like part of the action's data somehow.
  • Use some kind of visual brackets to visualize the action range, rather than highlighting/dimming, to keep it visually distinct from the playback frame range.

But doing something different may well turn out to be better.

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: * Put it in the panel on the right, as that feels more like part of the action's data somehow. * Use some kind of visual brackets to visualize the action range, rather than highlighting/dimming, to keep it visually distinct from the playback frame range. But doing something different may well turn out to be better.

Added subscriber: @Dene

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:

  1. Per Action. Some hotkey, hit it and there will be a popup window asking for loop range. After apply all the curves in action will be adjusted to loop.
  2. Per Curve. Select Curve and keypoints in it. Apply. Just selected Curve will be looped in range of selected keypoints.
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: 1. Per Action. Some hotkey, hit it and there will be a popup window asking for loop range. After apply all the curves in action will be adjusted to loop. 2. Per Curve. Select Curve and keypoints in it. Apply. Just selected Curve will be looped in range of selected keypoints.
Member

Added subscriber: @wbmoss_dev

Added subscriber: @wbmoss_dev
Member

(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.

(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: @fred23

Added subscriber: @AndyCuccaro

Added subscriber: @AndyCuccaro

This issue was referenced by 5d59b38605

This issue was referenced by 5d59b38605d61b2aeabfa1cb572b5a0d9158424b
Author
Member

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 custom range bounds are drawn using diagonal hash fill instead of solid, to distinguish from the playback range.
  • If the custom range is specified, it will be used when adding action tracks to NLA.
  • The action with a range can be marked cyclic. The Cycle-Aware Keying option will then automatically make newly-added curves cyclic with the correct period.
  • Optionally, curves that aren't cyclic with the correct period can be considered as having errors for the purpose of the Only Show Errors curve filter button.

The range is technically specified in floats because actual keyframes in the action, and hence the 'default' range used by NLA, can be fractional.

So recently in a branch I implemented the per-action custom frame range idea like this: {[F10145101](https://archive.blender.org/developer/F10145101/Screenshot_20210527_180942.png),size=full} The settings appear only in the Action Editor with the action selected. * The custom range bounds are drawn using diagonal hash fill instead of solid, to distinguish from the playback range. * If the custom range is specified, it will be used when adding action tracks to NLA. * The action with a range can be marked cyclic. The Cycle-Aware Keying option will then automatically make newly-added curves cyclic with the correct period. * Optionally, curves that aren't cyclic with the correct period can be considered as having errors for the purpose of the Only Show Errors curve filter button. The range is technically specified in floats because actual keyframes in the action, and hence the 'default' range used by NLA, can be fractional.
Member

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)?

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)?
Member

Added subscriber: @Mets

Added subscriber: @Mets
Member

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.

Optionally, curves that aren't cyclic with the correct period can be considered as having errors for the purpose of the Only Show Errors curve filter button.

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). 👍

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. > Optionally, curves that aren't cyclic with the correct period can be considered as having errors for the purpose of the Only Show Errors curve filter button. 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). :thumbsup:
Member

Hi @Mets! Glad you like the proposal here. I'm definitely super excited to see something like this in Blender, too. :-)

but consistency when exporting out of Blender (which I agree is important to consider here, since looping animations are mostly used for games)

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.

Hi @Mets! Glad you like the proposal here. I'm definitely super excited to see something like this in Blender, too. :-) > but consistency when exporting out of Blender (which I agree is important to consider here, since looping animations are mostly used for games) 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.
Author
Member

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.

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.
Member

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.

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

Added subscriber: @AquaticNightmare

This issue was referenced by 72acce43bc

This issue was referenced by 72acce43bc34e5e9569606e08ee536b02cab088b
Member

Added subscriber: @BClark

Added subscriber: @BClark
Member

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

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

Added subscriber: @sirwesleybarlow
Philipp Oeser removed the
Interest
Animation & Rigging
label 2023-02-09 14:36:43 +01:00

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.

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.
Member

@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.

@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.
Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
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
EEVEE & Viewport
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
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
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
EEVEE & Viewport
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
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
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
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#54724
No description provided.