Make `action_channelbag_ensure()` and `action_fcurve_ensure()` return a
reference. Earlier it returned pointer that should never be `nullptr`,
but in certain cases (which would indicate a bug, as the function's
preconditions aren't met) could technically still be a `nullptr`.
Instead, the preconditions are checked with `BLI_assert()`, and a
release build will simply assume that they are met. This simplifies the
code of the functions themselves, as well as the callers.
For reference: this was discussed in !134866.
No functional changes.
Pull Request: https://projects.blender.org/blender/blender/pulls/134918
Over time the `FCurveDescriptor` class has grown, and some concern was
raised (!134866) about the performance impact of passing it by value.
It's now passed by const reference instead.
No functional changes. I did find some non-`const` uses that are now
also `const`.
Pull Request: https://projects.blender.org/blender/blender/pulls/134913
Split the majority of `animrig::action_fcurve_ensure()` into a new
function `action_channelbag_ensure()`. This ensures that the Action has
a layer, keyframe strip, action slot, and channelbag for the given ID.
`animrig::action_fcurve_ensure()` now just calls into that function, and
then ensures that there is an F-Curve in that channelbag.
No functional changes.
Pull Request: https://projects.blender.org/blender/blender/pulls/134864
Fix an issue where the versioning of Action & slot assignments did not
use RNA properties to do the slot assignment. This caused certain
on-update callbacks to be missed, which in turn meant that an Action
constraint could remain disabled even though its action slot assignment
had been corrected.
This is now resolved by actually using RNA to set the assigned slot in
the versioning code.
Unfortunately that does mean that any reporting done will be by the
generic RNA code as well, and won't be specific to versioning. This
shouldn't be much of an issue in practice, as any warning was only shown
in the rare case of mis-matched `action.idroot` properties.
Pull Request: https://projects.blender.org/blender/blender/pulls/134759
Expose the convenience function `blender::animrig::action_fcurve_ensure()`
to RNA as `Action.fcurve_ensure_for_datablock(...)`.
The function requires that the Action is already assigned to the
data-block. It then takes care of slot assignment / creation, as well as
the creation of a layer and a keyframe strip.
This function call:
```python
fcurve = action.fcurve_ensure_for_datablock(ob_cube, "location", index=2)
```
effectively performs this logic:
```python
# Ensure the slot exists and is assigned:
slot = ob_cube.animation_data.slot
if not slot:
slot = find_slot_for_keying(action)
if not slot:
slot = action.slots.new(ob_cube.name)
ob_cube.animation_data.slot = slot
# Ensure a layer exists:
if action.layers:
layer = action.layers[0]
else:
layer = action.layers.new("Layer")
# Ensure a keyframe strip exists:
if layer.strips:
strip = layer.strips[0]
else:
strip = layer.strips.new('KEYFRAME')
# Ensure the channelbag exists:
channelbag = strip.channelbag(slot, ensure=True)
# Ensure the F-Curve exists:
fcurve = channelbag.fcurves.find("location", index=1)
if not fcurve:
fcurve = channelbag.fcurves.new("location", index=1)
```
Here `find_slot_for_keying()` represents the logic that's also used when
creating keys via the user interface or the `bpy_struct.keyframe_insert()`
function.
Pull Request: https://projects.blender.org/blender/blender/pulls/134686
Move `Library.runtime` to be a pointer, move the related
`LibraryRuntime` struct to `BKE_library.hh`. Similar to e.g.
Mesh.runtime, that pointer is expected to always be valid, and is
allocated at readtime or when creating a new Library ID.
Related smaller changes:
* Write code now uses standard ID writing codepath for Library IDs too.
* Runtime pointer is reset to nullptr before writing.
* Looking up a library by its absolute path is now handled through a
dedicated utils, `search_filepath_abs`, instead of using
`BLI_findstring`.
Pull Request: https://projects.blender.org/blender/blender/pulls/134188
The previous name was confusingly similar to the new method
`identifier_prefix()` that was introduced in #133983. This new name
better distinguishes them while also reflecting its actual functionality
better and being shorter.
Pull Request: https://projects.blender.org/blender/blender/pulls/134046
Previously it was possible to make the type prefix of a Slot's identifier get
out-of-sync with its actual target ID type, by setting the identifier via
Python.
This PR changes `Slot.identifier` assignment to ensure that the type prefix is
set to match `target_id_type`. This now makes it impossible for the identifier
prefix and `target_id_type` to get out of sync, since this API previously was
the only way to accomplish that.
When the prefix that the user attempts to set doesn't match the `target_id_type`
of the Slot, a warning is issued telling the user about the mismatch and that
the identifier has been set with the correct prefix instead.
Pull Request: https://projects.blender.org/blender/blender/pulls/133983
Most of the old Animato properties on an Action (e.g. FCurve list, Channel
Groups) already act as proxies for the data for the first slot in the first
strip of the first layer. (Say that three times fast!) However, this was not yet
the case for `Action.id_root`.
This PR changes `Action.id_root` to act as a proxy for the first Slot's
`target_id_type` property, both for reading and writing.
If the Action has no Slots, then reading always returns 'UNSPECIFIED', and
writing will create a Slot and set its `target_id_type`.
Note that the ability to write to the first Slot's `target_id_type` via
`Action.id_root` conflicts with `target_id_type` supposedly only being writable
when it's still 'UNSPECIFIED' (#133883). Although that's certainly a little
weird, practically speaking this doesn't break anything for now, and is a
temporary kludge to keep `id_root` working until we can remove it in Blender
5.0. `id_root` will be removed entirely in 5.0, resolving this inconsistency.
Pull Request: https://projects.blender.org/blender/blender/pulls/133823
Similar to how brush assets are created and managed this
PR allows to export pose assets into a different library.
Because of this there is a limitation to this where each
asset is stored in a separate blend file.
This may be lifted in the future as there are planned changes in
the design phase: #122061
### Create Asset
Now available in the 3D viewport in the "Pose" menu: "Create Pose Asset".
The button in the Dope Sheet will now call this new operator as well.
Clicking either of those will open a popup in which you can:
* Choose the name of the asset, which library and catalog it goes into.
* Clicking "Create" will create a pose asset on disk in the given library.
It is possible to create files into an outside library or add it in the current file.
The latter option does a lot less since it basically just creates the
action and tags it as an asset.
If no Asset Shelf **AND** no Asset Browser is visible anywhere in Blender,
the Asset Shelf will be shown on the 3D viewport from which
the operator was called.
### Adjust Pose Asset
Right clicking a pose asset that has been created in the way described
before will have options to overwrite it.
Only the active object will be considered for updating a pose asset
Available Options (the latter 3 under the "Modify Pose Asset" submenu):
* Adjust Pose Asset: From the selected bones, update ONLY channels that
are also present in the asset. This is the default.
* Replace: Will completely replace the data in the Pose Asset from
the current selection
* Add: Adds the current selection to the Pose Asset. Any already existing
channels have their values updated
* Remove: Remove selected bones from the pose asset
Currently this refreshes the thumbnail. In the case of custom
thumbnails it might not be something want
### Deleting an existing Pose Asset
Right click on a Pose Asset and hit "Delete Pose Asset". Works in the shelf
and in the asset library. Doing so will pop up a confirmation dialog,
if confirming, the asset is gone forever. Deleting a local asset is basically the
same as clearing the asset. This is a bit confusing because you get
two options that basically do the same thing sometimes,
but "Delete" works in other cases as well.
I currently don't see a way around that.
Part of design #131840
Pull Request: https://projects.blender.org/blender/blender/pulls/132747
Previously, when use of the legacy Action APIs (specifically the `fcurves` and
`groups` properties) caused a new Slot to be created, it would be named "Slot".
This PR changes things so that Slots created that way are named "Legacy Slot",
just like Slots from upgraded legacy Actions.
The rationale is that Slots created in this way are expected (by the code that
created them) to be used as if the Action were still a legacy Action, and it's
good to reflect that in the name of the data. This also makes it clearer to
users when e.g. scripts and addons they're using may not yet have been updated
to fully work with Slotted Actions.
For consistency, this PR also names Layers created in the same way "Legacy
Layer", the same as Layers from upgraded legacy Actions.
Pull Request: https://projects.blender.org/blender/blender/pulls/133888
When loading old blend files, versioned Actions can end up having a slot with an
'UNSPECIFIED' `target_id_type`. Assigning the slot to an ID will then set the
slot's `target_id_type` to match the type of the ID, but there was previously no
way to directly set it via Python if needed.
This PR changes the writability of `target_id_type` to match the extent of its
mutability when assigning a slot to an IDs. Which is to say, it can be set if
it's 'UNSPECIFIED', but not otherwise.
RNA doesn't have a good way to represent this, so we accomplish this with a
custom set function that simply ignores the write if the slot's `target_id_type`
isn't 'UNSPECIFIED'. This isn't ideal, but is the least-bad solution at the
moment.
Pull Request: https://projects.blender.org/blender/blender/pulls/133883
Add `ensure` boolean parameter to the `ActionKeyframeStrip.channels()` RNA
function. Passing `ensure=True` will ensure the channelbag for the given
action slot handle exists. This makes it more straight-forward to create
F-Curves for a slot regardless of whether there already was a channelbag
for those F-Cuves:
```python
strip = action.layers[0].strips[0]
slot = action.slots[0]
# Old:
channelbag = strip.channels(slot.handle)
if not channelbag:
channelbag = strip.channelbags.new(action_slot)
ob_loc_x = channelbag.fcurves.new('location', index=0)
# New:
channelbag = strip.channels(slot.handle, ensure=True)
ob_loc_x = channelbag.fcurves.new('location', index=0)
```
Pull Request: https://projects.blender.org/blender/blender/pulls/133678
When writing an Action to a blend file, for forward compatability
reasons the animation data in the first slot is also written to the old
Animato Action storage locations. That way, loading the Action in an
older version of Blender will still at least have that animation data.
However, we overlooked `Action.idroot`, which indicates which ID
type the Action is for, and therefore all Actions have an unspecified
`idroot` when loaded in pre-layered-action versions of Blender.
This PR fixes that by also setting the `idroot` to match the target ID
type of the first slot when writing to disk.
Pull Request: https://projects.blender.org/blender/blender/pulls/133819
Refactor the keyframe copy-paste code to prepare for supporting action slots.
This is a non-functional change, as the slots are stored but otherwise completely ignored. This way the refactor to the new data structure can be reviewed & tested before actually changing the behaviour.
Related: #129690
Pull Request: https://projects.blender.org/blender/blender/pulls/133497
There were three methods with succinct but slightly unclear names:
- Action::slot_move()
- Channelbag::channel_group_move()
- Channelbag::fcurve_move()
The ambiguity is due to other functions with similar names that e.g.
move fcurves *between* Channelbags, etc. Whereas these methods only move
items to other positions within the array they're already in.
To make this clearer, this PR adds `_to_index` to the end of these
array-oriented methods.
Pull Request: https://projects.blender.org/blender/blender/pulls/133730
This updates the Move Channels operator in the Action Editor channel list to
also work with Slots, allowing Slots to be reordered within an Action.
Note that the Move Channels operator already had interesting behavior when
channels are selected in multiple levels of the channel hierarchy (e.g. when
both channel groups and f-curve channels are selected). For Slots, I've chosen
a simpler behavior: if any Slot is moved, all non-Slot channels (i.e. those
lower in the hierarchy) are left alone.
In the future we may want to change channel group rearrangement to work the same
way with respect to f-curve channels, but I've left that alone for now.
Pull Request: https://projects.blender.org/blender/blender/pulls/133328
When assigning an Action to an ID, a slot can be automatically
assigned as well. This behaviour is now extended by making untyped
slot identifiers (like `XXSlot`) act as wildcards.
If the last-used slot identifier was 'untyped' (like `XXSlot`), and a
slot with the same name that is specific to the animated ID's type
exists, that slot will be chosen.
Similarly, if the last-used slot identifier was 'typed' (like
`OBSlot`), a slot `OBSlot` does NOT exist, but an untyped slot with
the same name exists (like `XXSlot`), that one will be chosen.
If there is any ambiguity in the matter, the more specific slot is
chosen. In other words, in this case:
- last_slot_identifier = `XXSlot`
- both `XXSlot` and `OBSlot` exist on the Action (where `OB`
represents the ID type of `animated_id`).
the `OBSlot` should be chosen. This means that `XXSlot` NOT being
auto-assigned if there is an alternative. Since untyped slots are
bound on assignment, this design keeps the Action as-is, which means
that the `XXSlot` remains untyped and thus the user is free to assign
this to another ID type if desired.
Pull Request: https://projects.blender.org/blender/blender/pulls/133653
When an action slot does not have an ID type, and it is assigned to some
ID, the slot is bound to that ID's type. This now no longer happens when
the Action is linked, because linked data should not be modified.
Pull Request: https://projects.blender.org/blender/blender/pulls/133670
Deduplicate `assign_action_ensure_slot_for_keying()` and
`generic_slot_for_autoassign()`.
Some of the functionality of the latter function was copied into the
former. This has now been replaced by actually calling the function in
the appropriate spot.
No functional changes.
Pull Request: https://projects.blender.org/blender/blender/pulls/133652
An Action slot usually has a specific ID type (`ActionSlot::idtype`).
When this is set to 0, it is not specifc to any particular ID type. The
latter case was called "unbound", and is now called "untyped".
Note that untyped slots are given an ID type as soon as they are
actually assigned to any ID.
No functional changes.
Pull Request: https://projects.blender.org/blender/blender/pulls/133651
This patch adds support to apply a pose asset to multiple armatures in pose mode at once.
In case there is a suitable slot for the armature, it will be used to read from, otherwise
the system falls back to slot 0.
Part of #131840
Pull Request: https://projects.blender.org/blender/blender/pulls/132601
Just like legacy Actions get a slot "Legacy Slot" when they're
versioned, their layer is now also called "Legacy Layer" to be
consistent with that.
When upgrading a legacy Action to a slotted Action, it also gets a layer
and a keyframe strip on that layer. Those are not shown in the user
interface yet, but will in the future.
Pull Request: https://projects.blender.org/blender/blender/pulls/133009
Internationalize the default Action layer name. Instead of `"Layer"`,
use `DATA_("Layer")`. This is the same for other defaults in Blender.
When upgrading a legacy Action to a slotted Action, it also gets a layer
and a keyframe strip on that layer. Those are not shown in the user
interface yet, but will in the future.
Pull Request: https://projects.blender.org/blender/blender/pulls/133009
A pass at cleaning up / improving the code documentation for the layered action
C++ classes.
There is still more work to do here, but this should be good for an initial
first pass. I've focused on clarifying the behavior of and updating stale docs
of the methods in the action-related C++ classes.
Pull Request: https://projects.blender.org/blender/blender/pulls/131318
The issue was that we were passing a pointer-to-a-pointer to
`BLI_addtail()`, which expects a pointer to something castable to a
`Link`. This in turn led to an invalid memory access when trying to
access the fields of the supposed `Link`.
This fixes the issue by passing a pointer to a zero-initialized `Link`
instead.
This also takes the opportunity to more simply zero-initialize the
`bAction` structs used in the tests as well.
Pull Request: https://projects.blender.org/blender/blender/pulls/132860
Empty legacy Actions were skipped from versioning, because they are
technically valid layered Actions as well. However, this created an
issue where there was no "Legacy Slot" created for those. Furthermore,
their `idroot` could still be set to a non-zero value, which is not
allowed on layered Actions.
`bAction::idroot` is not a very reliable field. It can be zero on legacy
Actions as well, and it can in certain cases be downright wrong (can
happen in certain linking scenarios). Because of this, the field is not
included in any "is this a layered or a legacy Action?" checks.
Pull Request: https://projects.blender.org/blender/blender/pulls/132757
Split the versioning of legacy Actions to slotted ones into two steps:
1. Versioning the Actions themselves, in the regular versioning code
(`do_versions_after_linking_400`). This has to happen in the 'after
linking' stage, and not in the 'before linking' stage, as there's
older 'after linking' code that will break when it gets fed slotted
actions. Any ID that is using a legacy Action will get tagged.
2. Versioning Action assignments, where the correct Action Slot has to
be chosen & assigned for each tagged ID. This has to happen in the
`do_versions_after_setup` stage, as choosing a slot requires that the
actions (both local & linked) have been converted already.
This also includes some necessary changes to the pre-2.50 Action versioning
code.
Note that this change does not handle library overrides. That's dealt
with in !131426.
Pull Request: https://projects.blender.org/blender/blender/pulls/131627
The code for converting pre-Animato actions was not getting run properly
because `chanbase` (where animation data used to be stored) was
erroneously getting cleared before the relevant versioning code was run.
The root cause was that the code checking whether an action was already
valid as a layered action or not was NOT confirming that `chanbase` was
empty as part of that check (as it is a DNA-deprecated field), which in
turn triggered code that defensively clears `chanbase` (among other
things) when an action is identified as layered.
Note that the conversion of IPO curves and other pre-Animato data
happens quite late in the versioning, even _after_ the "versioning after
linking" stage. This is not introduced in this commit, this is just to
illuminate pre-existing design that might not be entirely obvious.
Pull Request: https://projects.blender.org/blender/blender/pulls/131975