Anim: make it easier to convert from legacy to current Action API
The changes:
1. Add `group_name` to the `channelbag.fcurves.new()` and
`action.fcurve_ensure_for_datablock()` RNA functions.
2. Add `anim_utils.action_ensure_channelbag_for_slot(action, slot)`.
3. Add `channelbag.fcurves.ensure()` RNA function.
This makes it possible to replace this legacy code:
```py
fcurve = action.fcurves.new("location", index=2, action_group="Name")
```
with this code:
```py
channelbag = action_ensure_channelbag_for_slot(action, action_slot)
fcurve = channelbag.fcurves.new("location", index=2, group_name="Name")
```
or replace this legacy code:
```py
fcurve = action.fcurves.find("location", index=2, action_group="Name")
if not fcurve:
fcurve = action.fcurves.new("location", index=2, action_group="Name")
```
with this code:
```py
channelbag = action_ensure_channelbag_for_slot(action, action_slot)
fcurve = channelbag.fcurves.ensure("location", index=2, group_name="Name")
```
Note that the parameter name is different (`action_group` became
`group_name`). This clarifies that this is the name of the group, and
not a reference to the group itself.
This is part of #146586
Pull Request: https://projects.blender.org/blender/blender/pulls/146977
This commit is contained in:
@@ -96,7 +96,9 @@ def action_get_channelbag_for_slot(action: Action | None, slot: ActionSlot | Non
|
||||
return None
|
||||
|
||||
|
||||
def _ensure_channelbag_exists(action: Action, slot: ActionSlot) -> ActionChannelbag:
|
||||
def action_ensure_channelbag_for_slot(action: Action, slot: ActionSlot) -> ActionChannelbag:
|
||||
"""Ensure a layer and a keyframe strip exists, then ensure that that strip has a channelbag for the slot."""
|
||||
|
||||
try:
|
||||
layer = action.layers[0]
|
||||
except IndexError:
|
||||
@@ -732,7 +734,7 @@ class KeyframesCo:
|
||||
if fcurve is None:
|
||||
data_path, array_index = fc_key
|
||||
assert action.is_action_layered
|
||||
channelbag = _ensure_channelbag_exists(action, action_slot)
|
||||
channelbag = action_ensure_channelbag_for_slot(action, action_slot)
|
||||
fcurve = channelbag.fcurves.new(data_path, index=array_index)
|
||||
|
||||
keyframe_points = fcurve.keyframe_points
|
||||
|
||||
@@ -106,6 +106,7 @@ const EnumPropertyItem default_ActionSlot_target_id_type_items[] = {
|
||||
# include "UI_interface_icons.hh"
|
||||
|
||||
# include "ANIM_action_legacy.hh"
|
||||
# include "ANIM_fcurve.hh"
|
||||
# include "ANIM_keyframing.hh"
|
||||
|
||||
# include <fmt/format.h>
|
||||
@@ -641,7 +642,8 @@ static FCurve *rna_Channelbag_fcurve_new(ActionChannelbag *dna_channelbag,
|
||||
Main *bmain,
|
||||
ReportList *reports,
|
||||
const char *data_path,
|
||||
const int index)
|
||||
const int index,
|
||||
const char *group_name)
|
||||
{
|
||||
BLI_assert(data_path != nullptr);
|
||||
if (data_path[0] == '\0') {
|
||||
@@ -649,8 +651,13 @@ static FCurve *rna_Channelbag_fcurve_new(ActionChannelbag *dna_channelbag,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
blender::animrig::FCurveDescriptor descr = {data_path, index};
|
||||
if (group_name && group_name[0]) {
|
||||
descr.channel_group = {group_name};
|
||||
}
|
||||
|
||||
animrig::Channelbag &self = dna_channelbag->wrap();
|
||||
FCurve *fcurve = self.fcurve_create_unique(bmain, {data_path, index});
|
||||
FCurve *fcurve = self.fcurve_create_unique(bmain, descr);
|
||||
if (!fcurve) {
|
||||
BKE_reportf(reports,
|
||||
RPT_ERROR,
|
||||
@@ -662,6 +669,29 @@ static FCurve *rna_Channelbag_fcurve_new(ActionChannelbag *dna_channelbag,
|
||||
return fcurve;
|
||||
}
|
||||
|
||||
static FCurve *rna_Channelbag_fcurve_ensure(ActionChannelbag *dna_channelbag,
|
||||
Main *bmain,
|
||||
ReportList *reports,
|
||||
const char *data_path,
|
||||
const int index,
|
||||
const char *group_name)
|
||||
{
|
||||
BLI_assert(data_path != nullptr);
|
||||
if (data_path[0] == '\0') {
|
||||
BKE_report(reports, RPT_ERROR, "F-Curve data path empty, invalid argument");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
blender::animrig::FCurveDescriptor descr = {data_path, index};
|
||||
if (group_name && group_name[0]) {
|
||||
descr.channel_group = {group_name};
|
||||
}
|
||||
|
||||
animrig::Channelbag &self = dna_channelbag->wrap();
|
||||
FCurve &fcurve = self.fcurve_ensure(bmain, descr);
|
||||
return &fcurve;
|
||||
}
|
||||
|
||||
static FCurve *rna_Channelbag_fcurve_find(ActionChannelbag *dna_channelbag,
|
||||
ReportList *reports,
|
||||
const char *data_path,
|
||||
@@ -1387,7 +1417,8 @@ static FCurve *rna_Action_fcurve_ensure_for_datablock(bAction *_self,
|
||||
ReportList *reports,
|
||||
ID *datablock,
|
||||
const char *data_path,
|
||||
const int array_index)
|
||||
const int array_index,
|
||||
const char *group_name)
|
||||
{
|
||||
/* Precondition checks. */
|
||||
{
|
||||
@@ -1407,8 +1438,12 @@ static FCurve *rna_Action_fcurve_ensure_for_datablock(bAction *_self,
|
||||
}
|
||||
}
|
||||
|
||||
FCurve &fcurve = blender::animrig::action_fcurve_ensure(
|
||||
bmain, *_self, *datablock, {data_path, array_index});
|
||||
blender::animrig::FCurveDescriptor descriptor = {data_path, array_index};
|
||||
if (group_name && group_name[0]) {
|
||||
descriptor.channel_group = std::string(group_name);
|
||||
}
|
||||
|
||||
FCurve &fcurve = blender::animrig::action_fcurve_ensure(bmain, *_self, *datablock, descriptor);
|
||||
|
||||
WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, nullptr);
|
||||
return &fcurve;
|
||||
@@ -2501,23 +2536,40 @@ static void rna_def_channelbag_fcurves(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
srna, "F-Curves", "Collection of F-Curves for a specific action slot, on a specific strip");
|
||||
|
||||
/* Channelbag.fcurves.new(...) */
|
||||
extern FCurve *ActionChannelbagFCurves_new_func(ID * _selfid,
|
||||
ActionChannelbag * _self,
|
||||
Main * bmain,
|
||||
ReportList * reports,
|
||||
const char *data_path,
|
||||
int index);
|
||||
|
||||
func = RNA_def_function(srna, "new", "rna_Channelbag_fcurve_new");
|
||||
RNA_def_function_ui_description(func, "Add an F-Curve to the channelbag");
|
||||
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
|
||||
parm = RNA_def_string(func, "data_path", nullptr, 0, "Data Path", "F-Curve data path to use");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
RNA_def_int(func, "index", 0, 0, INT_MAX, "Index", "Array index", 0, INT_MAX);
|
||||
|
||||
parm = RNA_def_string(
|
||||
func,
|
||||
"group_name",
|
||||
nullptr,
|
||||
sizeof(bActionGroup::name),
|
||||
"Group Name",
|
||||
"Name of the Group for this F-Curve, will be created if it does not exist yet");
|
||||
parm = RNA_def_pointer(func, "fcurve", "FCurve", "", "Newly created F-Curve");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
/* Channelbag.fcurves.ensure(...) */
|
||||
func = RNA_def_function(srna, "ensure", "rna_Channelbag_fcurve_ensure");
|
||||
RNA_def_function_ui_description(
|
||||
func, "Returns the F-Curve if it already exists, and creates it if necessary");
|
||||
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
|
||||
parm = RNA_def_string(func, "data_path", nullptr, 0, "Data Path", "F-Curve data path to use");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
RNA_def_int(func, "index", 0, 0, INT_MAX, "Index", "Array index", 0, INT_MAX);
|
||||
parm = RNA_def_string(func,
|
||||
"group_name",
|
||||
nullptr,
|
||||
sizeof(bActionGroup::name),
|
||||
"Group Name",
|
||||
"Name of the Group for this F-Curve, will be created if it does not exist "
|
||||
"yet. This parameter is ignored if the F-Curve already exists");
|
||||
parm = RNA_def_pointer(func, "fcurve", "FCurve", "", "Found or newly created F-Curve");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
/* Channelbag.fcurves.find(...) */
|
||||
func = RNA_def_function(srna, "find", "rna_Channelbag_fcurve_find");
|
||||
RNA_def_function_ui_description(
|
||||
@@ -3080,6 +3132,13 @@ static void rna_def_action(BlenderRNA *brna)
|
||||
parm = RNA_def_string(func, "data_path", nullptr, 0, "Data Path", "F-Curve data path");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
RNA_def_int(func, "index", 0, 0, INT_MAX, "Index", "Array index", 0, INT_MAX);
|
||||
RNA_def_string(func,
|
||||
"group_name",
|
||||
nullptr,
|
||||
0,
|
||||
"Group Name",
|
||||
"Name of the group for this F-Curve, if any. If the F-Curve already exists, this "
|
||||
"parameter is ignored");
|
||||
parm = RNA_def_pointer(func, "fcurve", "FCurve", "", "The found or created F-Curve");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
|
||||
@@ -607,6 +607,21 @@ class ChannelbagsTest(unittest.TestCase):
|
||||
self.assertEqual([channelbag], list(self.strip.channelbags))
|
||||
self.assertEqual(self.slot, channelbag.slot)
|
||||
|
||||
def test_ensure_fcurve(self):
|
||||
channelbag = self.strip.channelbag(self.slot, ensure=True)
|
||||
self.assertEqual([], channelbag.fcurves[:])
|
||||
|
||||
fcurve_1 = channelbag.fcurves.ensure("location", index=2, group_name="Name")
|
||||
self.assertEqual("location", fcurve_1.data_path)
|
||||
self.assertEqual(2, fcurve_1.array_index)
|
||||
self.assertEqual("Name", fcurve_1.group.name)
|
||||
self.assertIn("Name", channelbag.groups)
|
||||
self.assertEqual([fcurve_1], channelbag.fcurves[:])
|
||||
|
||||
fcurve_2 = channelbag.fcurves.ensure("location", index=2, group_name="Name")
|
||||
self.assertEqual(fcurve_1, fcurve_2)
|
||||
self.assertEqual([fcurve_1], channelbag.fcurves[:])
|
||||
|
||||
def test_create_remove_fcurves(self):
|
||||
channelbag = self.strip.channelbags.new(self.slot)
|
||||
|
||||
@@ -988,6 +1003,24 @@ class ConvenienceFunctionsTest(unittest.TestCase):
|
||||
channelbag = self.action.layers[0].strips[0].channelbags[0]
|
||||
self.assertEqual(fcurve, channelbag.fcurves[0])
|
||||
|
||||
def test_fcurve_ensure_for_datablock_group_name(self) -> None:
|
||||
# Assign the Action to the Cube.
|
||||
ob_cube = bpy.data.objects["Cube"]
|
||||
adt = ob_cube.animation_data_create()
|
||||
adt.action = self.action
|
||||
|
||||
with self.assertRaises(IndexError):
|
||||
self.action.layers[0].strips[0].channelbags[0]
|
||||
|
||||
fcurve = self.action.fcurve_ensure_for_datablock(ob_cube, "location", index=2, group_name="grúpa")
|
||||
|
||||
channelbag = self.action.layers[0].strips[0].channelbags[0]
|
||||
self.assertEqual(fcurve, channelbag.fcurves[0])
|
||||
|
||||
# Check that the group was also created correctly.
|
||||
self.assertIn("grúpa", channelbag.groups)
|
||||
self.assertIn(fcurve, channelbag.groups["grúpa"].channels[:])
|
||||
|
||||
|
||||
def main():
|
||||
global args
|
||||
|
||||
Reference in New Issue
Block a user