Merge branch 'blender-v4.4-release'
This commit is contained in:
@@ -816,4 +816,17 @@ bool GPUDevice::is_shared(const void *shared_pointer,
|
||||
|
||||
/* DeviceInfo */
|
||||
|
||||
bool DeviceInfo::contains_device_type(const DeviceType type) const
|
||||
{
|
||||
if (this->type == type) {
|
||||
return true;
|
||||
}
|
||||
for (const DeviceInfo &info : multi_devices) {
|
||||
if (info.contains_device_type(type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
@@ -130,6 +130,8 @@ class DeviceInfo {
|
||||
{
|
||||
return !(*this == info);
|
||||
}
|
||||
|
||||
bool contains_device_type(const DeviceType type) const;
|
||||
};
|
||||
|
||||
/* Device */
|
||||
|
||||
@@ -943,13 +943,23 @@ void GeometryManager::device_update(Device *device,
|
||||
});
|
||||
TaskPool pool;
|
||||
|
||||
/* Work around Embree/oneAPI bug #129596 with BVH updates. */
|
||||
const bool use_multithreaded_build = first_bvh_build ||
|
||||
!device->info.contains_device_type(DEVICE_ONEAPI);
|
||||
first_bvh_build = false;
|
||||
|
||||
size_t i = 0;
|
||||
for (Geometry *geom : scene->geometry) {
|
||||
if (geom->is_modified() || geom->need_update_bvh_for_offset) {
|
||||
need_update_scene_bvh = true;
|
||||
pool.push([geom, device, dscene, scene, &progress, i, num_bvh] {
|
||||
if (use_multithreaded_build) {
|
||||
pool.push([geom, device, dscene, scene, &progress, i, num_bvh] {
|
||||
geom->compute_bvh(device, dscene, &scene->params, &progress, i, num_bvh);
|
||||
});
|
||||
}
|
||||
else {
|
||||
geom->compute_bvh(device, dscene, &scene->params, &progress, i, num_bvh);
|
||||
});
|
||||
}
|
||||
if (geom->need_build_bvh(bvh_layout)) {
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -225,6 +225,7 @@ class GeometryManager {
|
||||
|
||||
/* Update Flags */
|
||||
bool need_flags_update;
|
||||
bool first_bvh_build = true;
|
||||
|
||||
/* Constructor/Destructor */
|
||||
GeometryManager();
|
||||
|
||||
@@ -1625,6 +1625,29 @@ animrig::Channelbag *channelbag_for_action_slot(Action &action, slot_handle_t sl
|
||||
Span<FCurve *> fcurves_for_action_slot(Action &action, slot_handle_t slot_handle);
|
||||
Span<const FCurve *> fcurves_for_action_slot(const Action &action, slot_handle_t slot_handle);
|
||||
|
||||
/**
|
||||
* Find or create an F-Curve on the given action that matches the given fcurve
|
||||
* descriptor.
|
||||
*
|
||||
* \note This function also ensures that dependency graph relationships are
|
||||
* rebuilt. This is necessary when adding a new F-Curve, as a
|
||||
* previously-unanimated depsgraph component may become animated now.
|
||||
*
|
||||
* \param action: MUST already be assigned to the animated ID.
|
||||
*
|
||||
* \param animated_id: The ID that is animated by this Action. It is used to
|
||||
* create and assign an appropriate slot if needed when creating the fcurve, and
|
||||
* set the fcurve color properly
|
||||
*
|
||||
* \param fcurve_descriptor: description of the fcurve to lookup/create. Note
|
||||
* that this is *not* relative to `ptr` (e.g. if `ptr` is not an ID). It should
|
||||
* contain the exact data path of the fcurve to be looked up/created.
|
||||
*/
|
||||
FCurve *action_fcurve_ensure(Main *bmain,
|
||||
bAction &action,
|
||||
ID &animated_id,
|
||||
FCurveDescriptor fcurve_descriptor);
|
||||
|
||||
/**
|
||||
* Find or create an F-Curve on the given action that matches the given fcurve
|
||||
* descriptor.
|
||||
@@ -1646,12 +1669,15 @@ Span<const FCurve *> fcurves_for_action_slot(const Action &action, slot_handle_t
|
||||
* \param fcurve_descriptor: description of the fcurve to lookup/create. Note
|
||||
* that this is *not* relative to `ptr` (e.g. if `ptr` is not an ID). It should
|
||||
* contain the exact data path of the fcurve to be looked up/created.
|
||||
*
|
||||
* \see action_fcurve_ensure for a function that is specific to layered actions,
|
||||
* and is easier to use because it does not depend on an RNA pointer.
|
||||
*/
|
||||
FCurve *action_fcurve_ensure(Main *bmain,
|
||||
bAction *act,
|
||||
const char group[],
|
||||
PointerRNA *ptr,
|
||||
FCurveDescriptor fcurve_descriptor);
|
||||
FCurve *action_fcurve_ensure_ex(Main *bmain,
|
||||
bAction *act,
|
||||
const char group[],
|
||||
PointerRNA *ptr,
|
||||
FCurveDescriptor fcurve_descriptor);
|
||||
|
||||
/**
|
||||
* Same as above, but creates a legacy Action.
|
||||
|
||||
@@ -2596,11 +2596,11 @@ Vector<FCurve *> fcurves_in_listbase_filtered(ListBase /* FCurve * */ fcurves,
|
||||
return found;
|
||||
}
|
||||
|
||||
FCurve *action_fcurve_ensure(Main *bmain,
|
||||
bAction *act,
|
||||
const char group[],
|
||||
PointerRNA *ptr,
|
||||
FCurveDescriptor fcurve_descriptor)
|
||||
FCurve *action_fcurve_ensure_ex(Main *bmain,
|
||||
bAction *act,
|
||||
const char group[],
|
||||
PointerRNA *ptr,
|
||||
FCurveDescriptor fcurve_descriptor)
|
||||
{
|
||||
if (act == nullptr) {
|
||||
return nullptr;
|
||||
@@ -2626,13 +2626,21 @@ FCurve *action_fcurve_ensure(Main *bmain,
|
||||
* hold, or if this is even the best place to handle the layered action
|
||||
* cases at all, was leading to discussion of larger changes than made sense
|
||||
* to tackle at that point. */
|
||||
Action &action = act->wrap();
|
||||
|
||||
BLI_assert(ptr != nullptr);
|
||||
if (ptr == nullptr || ptr->owner_id == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
ID &animated_id = *ptr->owner_id;
|
||||
|
||||
return action_fcurve_ensure(bmain, *act, *ptr->owner_id, fcurve_descriptor);
|
||||
}
|
||||
|
||||
FCurve *action_fcurve_ensure(Main *bmain,
|
||||
bAction &dna_action,
|
||||
ID &animated_id,
|
||||
FCurveDescriptor fcurve_descriptor)
|
||||
{
|
||||
Action &action = dna_action.wrap();
|
||||
|
||||
BLI_assert(get_action(animated_id) == &action);
|
||||
if (get_action(animated_id) != &action) {
|
||||
return nullptr;
|
||||
@@ -2641,7 +2649,9 @@ FCurve *action_fcurve_ensure(Main *bmain,
|
||||
/* Ensure the id has an assigned slot. */
|
||||
Slot *slot = assign_action_ensure_slot_for_keying(action, animated_id);
|
||||
if (!slot) {
|
||||
/* This means the ID type is not animatable. */
|
||||
/* This means the ID type is not animatable. How did an Action get assigned
|
||||
* in the first place? */
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1208,11 +1208,11 @@ TEST_F(ActionLayersTest, action_move_slot)
|
||||
PointerRNA cube_rna_pointer = RNA_id_pointer_create(&cube->id);
|
||||
PointerRNA suzanne_rna_pointer = RNA_id_pointer_create(&suzanne->id);
|
||||
|
||||
action_fcurve_ensure(bmain, action, "Test", &cube_rna_pointer, {"location", 0});
|
||||
action_fcurve_ensure(bmain, action, "Test", &cube_rna_pointer, {"rotation_euler", 1});
|
||||
action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"location", 0});
|
||||
action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"rotation_euler", 1});
|
||||
|
||||
action_fcurve_ensure(bmain, action_2, "Test_2", &suzanne_rna_pointer, {"location", 0});
|
||||
action_fcurve_ensure(bmain, action_2, "Test_2", &suzanne_rna_pointer, {"rotation_euler", 1});
|
||||
action_fcurve_ensure_ex(bmain, action_2, "Test_2", &suzanne_rna_pointer, {"location", 0});
|
||||
action_fcurve_ensure_ex(bmain, action_2, "Test_2", &suzanne_rna_pointer, {"rotation_euler", 1});
|
||||
|
||||
ASSERT_EQ(action->layer_array_num, 1);
|
||||
ASSERT_EQ(action_2->layer_array_num, 1);
|
||||
|
||||
@@ -607,7 +607,7 @@ static SingleKeyingResult insert_keyframe_fcurve_value(Main *bmain,
|
||||
*/
|
||||
|
||||
FCurve *fcu = key_insertion_may_create_fcurve(flag) ?
|
||||
action_fcurve_ensure(bmain, act, group, ptr, {rna_path, array_index}) :
|
||||
action_fcurve_ensure_ex(bmain, act, group, ptr, {rna_path, array_index}) :
|
||||
fcurve_find_in_action(act, {rna_path, array_index});
|
||||
|
||||
/* We may not have a F-Curve when we're replacing only. */
|
||||
|
||||
@@ -2199,7 +2199,16 @@ Mesh *dynamicPaint_Modifier_do(
|
||||
/* Create a surface for uv image sequence format. */
|
||||
#define JITTER_SAMPLES \
|
||||
{ \
|
||||
0.0f, 0.0f, -0.2f, -0.4f, 0.2f, 0.4f, 0.4f, -0.2f, -0.4f, 0.3f, \
|
||||
0.0f, \
|
||||
0.0f, \
|
||||
-0.2f, \
|
||||
-0.4f, \
|
||||
0.2f, \
|
||||
0.4f, \
|
||||
0.4f, \
|
||||
-0.2f, \
|
||||
-0.4f, \
|
||||
0.3f, \
|
||||
}
|
||||
|
||||
struct DynamicPaintCreateUVSurfaceData {
|
||||
@@ -4340,6 +4349,8 @@ static bool dynamicPaint_paintMesh(Depsgraph *depsgraph,
|
||||
}
|
||||
}
|
||||
|
||||
mesh->tag_positions_changed();
|
||||
|
||||
if (brush->flags & MOD_DPAINT_PROX_PROJECT && brush->collision != MOD_DPAINT_COL_VOLUME) {
|
||||
mul_v3_fl(avg_brushNor, 1.0f / float(numOfVerts));
|
||||
/* instead of null vector use positive z */
|
||||
|
||||
@@ -4691,12 +4691,12 @@ const bAnimChannelType *ANIM_channel_get_typeinfo(const bAnimListElem *ale)
|
||||
/* init the typeinfo if not available yet... */
|
||||
ANIM_init_channel_typeinfo_data();
|
||||
|
||||
/* check if type is in bounds... */
|
||||
if ((ale->type >= 0) && (ale->type < ANIMTYPE_NUM_TYPES)) {
|
||||
return animchannelTypeInfo[ale->type];
|
||||
BLI_assert(ale->type < ANIMTYPE_NUM_TYPES);
|
||||
if (ale->type >= ANIMTYPE_NUM_TYPES) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return animchannelTypeInfo[ale->type];
|
||||
}
|
||||
|
||||
/* --------------------------- */
|
||||
|
||||
@@ -111,7 +111,7 @@ class CopyDriversToSelected : public testing::Test {
|
||||
/* Add animation to cube's fourth quaternion element. */
|
||||
PointerRNA cube_ptr = RNA_pointer_create_discrete(&cube->id, &RNA_Object, &cube->id);
|
||||
bAction *act = animrig::id_action_ensure(bmain, &cube->id);
|
||||
FCurve *fcu = animrig::action_fcurve_ensure(
|
||||
FCurve *fcu = animrig::action_fcurve_ensure_ex(
|
||||
bmain, act, "Object Transforms", &cube_ptr, {"rotation_quaternion", 3});
|
||||
animrig::KeyframeSettings keyframe_settings = {BEZT_KEYTYPE_KEYFRAME, HD_AUTO, BEZT_IPO_BEZ};
|
||||
insert_vert_fcurve(fcu, {1.0, 1.0}, keyframe_settings, INSERTKEY_NOFLAGS);
|
||||
|
||||
@@ -1092,7 +1092,7 @@ static int followpath_path_animate_exec(bContext *C, wmOperator *op)
|
||||
/* create F-Curve for path animation */
|
||||
act = animrig::id_action_ensure(bmain, &cu->id);
|
||||
PointerRNA id_ptr = RNA_id_pointer_create(&cu->id);
|
||||
fcu = animrig::action_fcurve_ensure(bmain, act, nullptr, &id_ptr, {"eval_time", 0});
|
||||
fcu = animrig::action_fcurve_ensure_ex(bmain, act, nullptr, &id_ptr, {"eval_time", 0});
|
||||
|
||||
/* standard vertical range - 1:1 = 100 frames */
|
||||
standardRange = 100.0f;
|
||||
@@ -1117,7 +1117,7 @@ static int followpath_path_animate_exec(bContext *C, wmOperator *op)
|
||||
/* create F-Curve for constraint */
|
||||
act = animrig::id_action_ensure(bmain, &ob->id);
|
||||
PointerRNA id_ptr = RNA_id_pointer_create(&ob->id);
|
||||
fcu = animrig::action_fcurve_ensure(bmain, act, nullptr, &id_ptr, {path->c_str(), 0});
|
||||
fcu = animrig::action_fcurve_ensure_ex(bmain, act, nullptr, &id_ptr, {path->c_str(), 0});
|
||||
|
||||
/* standard vertical range - 0.0 to 1.0 */
|
||||
standardRange = 1.0f;
|
||||
|
||||
@@ -546,7 +546,7 @@ bool parent_set(ReportList *reports,
|
||||
/* get or create F-Curve */
|
||||
bAction *act = animrig::id_action_ensure(bmain, &cu->id);
|
||||
PointerRNA id_ptr = RNA_id_pointer_create(&cu->id);
|
||||
FCurve *fcu = animrig::action_fcurve_ensure(
|
||||
FCurve *fcu = animrig::action_fcurve_ensure_ex(
|
||||
bmain, act, nullptr, &id_ptr, {"eval_time", 0});
|
||||
|
||||
/* setup dummy 'generator' modifier here to get 1-1 correspondence still working */
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "BKE_action.hh"
|
||||
#include "BKE_blender.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
@@ -1374,6 +1375,44 @@ static void rna_Action_deselect_keys(bAction *act)
|
||||
WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, nullptr);
|
||||
}
|
||||
|
||||
static FCurve *rna_Action_fcurve_ensure_for_datablock(bAction *_self,
|
||||
Main *bmain,
|
||||
ReportList *reports,
|
||||
ID *datablock,
|
||||
const char *data_path,
|
||||
const int array_index)
|
||||
{
|
||||
/* Precondition checks. */
|
||||
{
|
||||
if (blender::animrig::get_action(*datablock) != _self) {
|
||||
BKE_reportf(reports,
|
||||
RPT_ERROR_INVALID_INPUT,
|
||||
"Assign action \"%s\" to \"%s\" before calling this function",
|
||||
_self->id.name + 2,
|
||||
datablock->name + 2);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLI_assert(data_path != nullptr);
|
||||
if (data_path[0] == '\0') {
|
||||
BKE_report(reports, RPT_ERROR_INVALID_INPUT, "F-Curve data path empty, invalid argument");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
FCurve *fcurve = blender::animrig::action_fcurve_ensure(
|
||||
bmain, *_self, *datablock, {data_path, array_index});
|
||||
|
||||
if (!fcurve) {
|
||||
/* This should never happen, given the precondition check above. */
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, nullptr);
|
||||
return fcurve;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to check if an action (value pointer)
|
||||
* is suitable to be assigned to the ID-block that is ptr.
|
||||
@@ -2695,7 +2734,11 @@ static void rna_def_action_fcurves(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
RNA_def_property_srna(cprop, "ActionFCurves");
|
||||
srna = RNA_def_struct(brna, "ActionFCurves", nullptr);
|
||||
RNA_def_struct_sdna(srna, "bAction");
|
||||
RNA_def_struct_ui_text(srna, "Action F-Curves", "Collection of action F-Curves");
|
||||
RNA_def_struct_ui_text(
|
||||
srna,
|
||||
"Action F-Curves",
|
||||
"Collection of action F-Curves. Note that this is a legacy API that is unaware of action "
|
||||
"slots, and will only consider the F-Curves for this action's first slot");
|
||||
RNA_def_property_collection_funcs(cprop,
|
||||
"rna_iterator_Action_fcurves_begin",
|
||||
"rna_iterator_Action_fcurves_next",
|
||||
@@ -2708,7 +2751,9 @@ static void rna_def_action_fcurves(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
|
||||
/* Action.fcurves.new(...) */
|
||||
func = RNA_def_function(srna, "new", "rna_Action_fcurve_new");
|
||||
RNA_def_function_ui_description(func, "Add an F-Curve to the action");
|
||||
RNA_def_function_ui_description(func,
|
||||
"Add an F-Curve for the first slot of this action, creating the "
|
||||
"necessary layer, strip, and slot if necessary");
|
||||
RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_MAIN);
|
||||
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);
|
||||
@@ -2724,7 +2769,7 @@ static void rna_def_action_fcurves(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
RNA_def_function_ui_description(
|
||||
func,
|
||||
"Find an F-Curve. Note that this function performs a linear scan "
|
||||
"of all F-Curves in the action.");
|
||||
"of all F-Curves for the action's first slot.");
|
||||
RNA_def_function_flag(func, FUNC_USE_REPORTS);
|
||||
parm = RNA_def_string(func, "data_path", nullptr, 0, "Data Path", "F-Curve data path");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
@@ -2735,7 +2780,7 @@ static void rna_def_action_fcurves(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
|
||||
/* Action.fcurves.remove(...) */
|
||||
func = RNA_def_function(srna, "remove", "rna_Action_fcurve_remove");
|
||||
RNA_def_function_ui_description(func, "Remove F-Curve");
|
||||
RNA_def_function_ui_description(func, "Remove the F-Curve from the action's first slot");
|
||||
RNA_def_function_flag(func, FUNC_USE_REPORTS);
|
||||
parm = RNA_def_pointer(func, "fcurve", "FCurve", "", "F-Curve to remove");
|
||||
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
|
||||
@@ -2743,7 +2788,7 @@ static void rna_def_action_fcurves(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
|
||||
/* Action.fcurves.clear() */
|
||||
func = RNA_def_function(srna, "clear", "rna_Action_fcurve_clear");
|
||||
RNA_def_function_ui_description(func, "Remove all F-Curves");
|
||||
RNA_def_function_ui_description(func, "Remove all F-Curves from the action's first slot");
|
||||
}
|
||||
|
||||
static void rna_def_action_pose_markers(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
@@ -2804,13 +2849,21 @@ static void rna_def_action_legacy(BlenderRNA *brna, StructRNA *srna)
|
||||
prop = RNA_def_property(srna, "fcurves", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, nullptr, "curves", nullptr);
|
||||
RNA_def_property_struct_type(prop, "FCurve");
|
||||
RNA_def_property_ui_text(prop, "F-Curves", "The individual F-Curves that make up the action");
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"F-Curves",
|
||||
"Legacy API, for backward compatibility with code that does not handle slotted actions yet. "
|
||||
"This collection contains the F-Curves for the action's first slot");
|
||||
rna_def_action_fcurves(brna, prop);
|
||||
|
||||
prop = RNA_def_property(srna, "groups", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, nullptr, "groups", nullptr);
|
||||
RNA_def_property_struct_type(prop, "ActionGroup");
|
||||
RNA_def_property_ui_text(prop, "Groups", "Convenient groupings of F-Curves");
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Groups",
|
||||
"Legacy API, for backward compatibility with code that does not handle slotted actions yet. "
|
||||
"This collection contains the F-Curve groups for the action's first slot");
|
||||
rna_def_action_groups(brna, prop);
|
||||
|
||||
/* special "type" limiter - should not really be edited in general,
|
||||
@@ -2824,10 +2877,12 @@ static void rna_def_action_legacy(BlenderRNA *brna, StructRNA *srna)
|
||||
"rna_ActionSlot_target_id_type_itemf");
|
||||
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN, "rna_Action_id_root_update");
|
||||
RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"ID Root Type",
|
||||
"Type of ID block that action can be used on - "
|
||||
"DO NOT CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING");
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"ID Root Type",
|
||||
"Legacy API, for backward compatibility with code that does not handle slotted actions yet. "
|
||||
"Type of data-block that the action's first slot can be used on. Do not change unless you "
|
||||
"know what you are doing");
|
||||
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID);
|
||||
}
|
||||
|
||||
@@ -2836,6 +2891,9 @@ static void rna_def_action(BlenderRNA *brna)
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
FunctionRNA *func;
|
||||
PropertyRNA *parm;
|
||||
|
||||
srna = RNA_def_struct(brna, "Action", "ID");
|
||||
RNA_def_struct_sdna(srna, "bAction");
|
||||
RNA_def_struct_ui_text(srna, "Action", "A collection of F-Curves for animation");
|
||||
@@ -2854,7 +2912,9 @@ static void rna_def_action(BlenderRNA *brna)
|
||||
prop,
|
||||
"Is Legacy Action",
|
||||
"Return whether this is a legacy Action. Legacy Actions have no layers or slots. An "
|
||||
"empty Action considered as both a 'legacy' and a 'layered' Action.");
|
||||
"empty Action considered as both a 'legacy' and a 'layered' Action. Since Blender 4.4 "
|
||||
"actions are automatically updated to layered actions, and thus this will only return True "
|
||||
"when the action is empty");
|
||||
RNA_def_property_boolean_funcs(prop, "rna_Action_is_action_legacy_get", nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "is_action_layered", PROP_BOOLEAN, PROP_NONE);
|
||||
@@ -2974,10 +3034,34 @@ static void rna_def_action(BlenderRNA *brna)
|
||||
RNA_def_property_float_funcs(prop, "rna_Action_curve_frame_range_get", nullptr, nullptr);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
FunctionRNA *func = RNA_def_function(srna, "deselect_keys", "rna_Action_deselect_keys");
|
||||
func = RNA_def_function(srna, "deselect_keys", "rna_Action_deselect_keys");
|
||||
RNA_def_function_ui_description(
|
||||
func, "Deselects all keys of the Action. The selection status of F-Curves is unchanged.");
|
||||
|
||||
/* action.fcurve_ensure_for_datablock() */
|
||||
func = RNA_def_function(
|
||||
srna, "fcurve_ensure_for_datablock", "rna_Action_fcurve_ensure_for_datablock");
|
||||
RNA_def_function_ui_description(
|
||||
func,
|
||||
"Ensure that an F-Curve exists, with the given data path and array index, for the given "
|
||||
"data-block. This action must already be assigned to the data-block. This function will "
|
||||
"also create the layer, keyframe strip, and action slot if necessary, and take care of "
|
||||
"assigning the action slot too");
|
||||
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
|
||||
|
||||
parm = RNA_def_pointer(func,
|
||||
"datablock",
|
||||
"ID",
|
||||
"",
|
||||
"The data-block animated by this action, for which to ensure the F-Curve "
|
||||
"exists. This action must already be assigned to the data-block");
|
||||
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
|
||||
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);
|
||||
parm = RNA_def_pointer(func, "fcurve", "FCurve", "", "The found or created F-Curve");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
rna_def_action_legacy(brna, srna);
|
||||
|
||||
/* API calls */
|
||||
|
||||
@@ -913,6 +913,48 @@ class SlotHandleLibraryOverridesTest(unittest.TestCase):
|
||||
-1, "Suzanne should be significantly below Z=0 when animated by the library Action")
|
||||
|
||||
|
||||
class ConvenienceFunctionsTest(unittest.TestCase):
|
||||
|
||||
def setUp(self) -> None:
|
||||
bpy.ops.wm.read_homefile(use_factory_startup=True)
|
||||
|
||||
self.action = bpy.data.actions.new('Action')
|
||||
|
||||
def test_fcurve_ensure_for_datablock(self) -> None:
|
||||
# The function should be None-safe.
|
||||
with self.assertRaises(TypeError):
|
||||
self.action.fcurve_ensure_for_datablock(None, "location")
|
||||
self.assertEqual(0, len(self.action.layers))
|
||||
self.assertEqual(0, len(self.action.slots))
|
||||
|
||||
# The function should not work unless the Action is assigned to its target.
|
||||
ob_cube = bpy.data.objects["Cube"]
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.action.fcurve_ensure_for_datablock(ob_cube, "location")
|
||||
self.assertEqual(0, len(self.action.layers))
|
||||
self.assertEqual(0, len(self.action.slots))
|
||||
|
||||
# The function should not work on empty data paths.
|
||||
adt = ob_cube.animation_data_create()
|
||||
adt.action = self.action
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.action.fcurve_ensure_for_datablock(ob_cube, "")
|
||||
self.assertEqual(0, len(self.action.layers))
|
||||
self.assertEqual(0, len(self.action.slots))
|
||||
|
||||
# And finally the happy flow.
|
||||
fcurve = self.action.fcurve_ensure_for_datablock(ob_cube, "location", index=2)
|
||||
self.assertEqual(1, len(self.action.layers))
|
||||
self.assertEqual(1, len(self.action.layers[0].strips))
|
||||
self.assertEqual('KEYFRAME', self.action.layers[0].strips[0].type)
|
||||
self.assertEqual(1, len(self.action.slots))
|
||||
self.assertEqual("location", fcurve.data_path)
|
||||
self.assertEqual(2, fcurve.array_index)
|
||||
|
||||
channelbag = self.action.layers[0].strips[0].channelbags[0]
|
||||
self.assertEqual(fcurve, channelbag.fcurves[0])
|
||||
|
||||
|
||||
def main():
|
||||
global args
|
||||
import argparse
|
||||
|
||||
Reference in New Issue
Block a user