Merge branch 'blender-v4.4-release'

This commit is contained in:
Hans Goudey
2025-02-18 10:55:49 -05:00
15 changed files with 242 additions and 43 deletions

View File

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

View File

@@ -130,6 +130,8 @@ class DeviceInfo {
{
return !(*this == info);
}
bool contains_device_type(const DeviceType type) const;
};
/* Device */

View File

@@ -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++;
}

View File

@@ -225,6 +225,7 @@ class GeometryManager {
/* Update Flags */
bool need_flags_update;
bool first_bvh_build = true;
/* Constructor/Destructor */
GeometryManager();

View File

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

View File

@@ -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;
}

View File

@@ -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);

View File

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

View File

@@ -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 */

View File

@@ -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];
}
/* --------------------------- */

View File

@@ -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);

View File

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

View File

@@ -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 */

View File

@@ -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 */

View File

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