Depsgraph: handle camera switching via markers in context drivers.

Blender allows animating the active camera selection (i.e. scene.camera)
by binding cameras to markers in the timeline. The dependency graph was
completely ignoring this by not building nodes for these cameras (it is
possible to reference a camera not directly included in the scene), and
not taking this into account in driver relations.

This change ensures that all cameras are included in the dependency
graph, and any drivers referencing scene.camera get dependencies on
all cameras of the timeline, and also time itself to ensure switches
are processed.

Pull Request #110139
This commit is contained in:
Alexander Gavrilov 2023-07-15 18:38:24 +03:00
parent 406f601c4b
commit b248295530
8 changed files with 166 additions and 51 deletions

View File

@ -148,6 +148,27 @@ bool DepsgraphBuilder::check_pchan_has_bbone_segments(const Object *object, cons
return check_pchan_has_bbone_segments(object, pchan);
}
const char *DepsgraphBuilder::get_rna_path_relative_to_scene_camera(const Scene *scene,
const PointerRNA &target_prop,
const char *rna_path)
{
if (rna_path == nullptr || target_prop.data != scene || target_prop.type != &RNA_Scene ||
!BLI_str_startswith(rna_path, "camera"))
{
return nullptr;
}
/* Return the part of the path relative to the camera. */
switch (rna_path[6]) {
case '.':
return rna_path + 7;
case '[':
return rna_path + 6;
default:
return nullptr;
}
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -13,6 +13,8 @@ struct ID;
struct Main;
struct ModifierData;
struct Object;
struct PointerRNA;
struct Scene;
struct bPoseChannel;
namespace blender::deg {
@ -33,6 +35,12 @@ class DepsgraphBuilder {
virtual bool check_pchan_has_bbone_segments(const Object *object, const bPoseChannel *pchan);
virtual bool check_pchan_has_bbone_segments(const Object *object, const char *bone_name);
/** If `target_prop` + `rna_path` uses indirection via the `scene.camera` pointer, returns
* the substring of `rna_path` relative to the camera; otherwise returns nullptr. */
static const char *get_rna_path_relative_to_scene_camera(const Scene *scene,
const PointerRNA &target_prop,
const char *rna_path);
protected:
/* NOTE: The builder does NOT take ownership over any of those resources. */
DepsgraphBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache);

View File

@ -1300,11 +1300,34 @@ void DepsgraphNodeBuilder::build_driver_variables(ID *id, FCurve *fcurve)
build_id(target_id);
build_driver_id_property(target_prop, dtar->rna_path);
/* For rna_path based variables: */
if ((dtar->flag & DTAR_FLAG_STRUCT_REF) == 0) {
/* Handle all other cameras used by the scene timeline if applicable. */
if (const char *camera_path = get_rna_path_relative_to_scene_camera(
scene_, target_prop, dtar->rna_path))
{
build_driver_scene_camera_variable(scene_, camera_path);
}
}
}
DRIVER_TARGETS_LOOPER_END;
}
}
void DepsgraphNodeBuilder::build_driver_scene_camera_variable(Scene *scene,
const char *camera_path)
{
/* This skips scene->camera, which was already handled by the caller. */
LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) {
if (!ELEM(marker->camera, nullptr, scene->camera)) {
PointerRNA camera_ptr;
RNA_id_pointer_create(&marker->camera->id, &camera_ptr);
build_driver_id_property(camera_ptr, camera_path);
}
}
}
void DepsgraphNodeBuilder::build_driver_id_property(const PointerRNA &target_prop,
const char *rna_path_from_target_prop)
{

View File

@ -164,6 +164,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void build_idproperties(IDProperty *id_property);
virtual void build_scene_render(Scene *scene, ViewLayer *view_layer);
virtual void build_scene_camera(Scene *scene);
virtual void build_scene_parameters(Scene *scene);
virtual void build_scene_compositor(Scene *scene);
@ -223,6 +224,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void build_driver(ID *id, FCurve *fcurve, int driver_index);
virtual void build_driver_variables(ID *id, FCurve *fcurve);
virtual void build_driver_scene_camera_variable(Scene *scene, const char *camera_path);
/* Build operations of a property value from which is read by a driver target.
*

View File

@ -31,8 +31,18 @@ void DepsgraphNodeBuilder::build_scene_render(Scene *scene, ViewLayer *view_laye
build_scene_sequencer(scene);
build_scene_speakers(scene, view_layer);
}
build_scene_camera(scene);
}
void DepsgraphNodeBuilder::build_scene_camera(Scene *scene)
{
if (scene->camera != nullptr) {
build_object(-1, scene->camera, DEG_ID_LINKED_DIRECTLY, true);
build_object(-1, scene->camera, DEG_ID_LINKED_INDIRECTLY, true);
}
LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) {
if (!ELEM(marker->camera, nullptr, scene->camera)) {
build_object(-1, marker->camera, DEG_ID_LINKED_INDIRECTLY, true);
}
}
}

View File

@ -114,9 +114,7 @@ void DepsgraphNodeBuilder::build_view_layer(Scene *scene,
}
}
build_layer_collections(&view_layer->layer_collections);
if (scene->camera != nullptr) {
build_object(-1, scene->camera, DEG_ID_LINKED_INDIRECTLY, true);
}
build_scene_camera(scene);
/* Rigidbody. */
if (scene->rigidbody_world != nullptr) {
build_rigidbody(scene);

View File

@ -1930,14 +1930,81 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu)
add_relation(target_key, driver_key, "Target -> Driver");
}
else if (dtar->rna_path != nullptr && dtar->rna_path[0] != '\0') {
RNAPathKey variable_exit_key(target_prop, dtar->rna_path, RNAPointerSource::EXIT);
build_driver_rna_path_variable(
driver_key, self_key, target_id, target_prop, dtar->rna_path);
/* Add relations to all other cameras used by the scene timeline if applicable. */
if (const char *camera_path = get_rna_path_relative_to_scene_camera(
scene_, target_prop, dtar->rna_path))
{
build_driver_scene_camera_variable(driver_key, self_key, scene_, camera_path);
}
/* The RNA getter for `object.data` can write to the mesh datablock due
* to the call to `BKE_mesh_wrapper_ensure_subdivision()`. This relation
* ensures it is safe to call when the driver is evaluated.
*
* For the sake of making the code more generic/defensive, the relation
* is added for any geometry type.
*
* See #96289 for more info. */
if (object != nullptr && OB_TYPE_IS_GEOMETRY(object->type)) {
StringRef rna_path(dtar->rna_path);
if (rna_path == "data" || rna_path.startswith("data.")) {
ComponentKey ob_key(target_id, NodeType::GEOMETRY);
add_relation(ob_key, driver_key, "ID -> Driver");
}
}
}
else {
/* If rna_path is nullptr, and DTAR_FLAG_STRUCT_REF isn't set, this
* is an incomplete target reference, so nothing to do here. */
}
}
DRIVER_TARGETS_LOOPER_END;
}
}
void DepsgraphRelationBuilder::build_driver_scene_camera_variable(const OperationKey &driver_key,
const RNAPathKey &self_key,
Scene *scene,
const char *rna_path)
{
/* First, add relations to all cameras used in the timeline,
* excluding scene->camera which was already handled by the caller. */
bool animated = false;
LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) {
if (!ELEM(marker->camera, nullptr, scene->camera)) {
PointerRNA camera_ptr;
RNA_id_pointer_create(&marker->camera->id, &camera_ptr);
build_driver_id_property(camera_ptr, rna_path);
build_driver_rna_path_variable(driver_key, self_key, &scene->id, camera_ptr, rna_path);
animated = true;
}
}
/* If timeline indeed switches the camera, this variable also implicitly depends on time. */
if (animated) {
TimeSourceKey time_src_key;
add_relation(time_src_key, driver_key, "TimeSrc -> Driver Camera Ref");
}
}
void DepsgraphRelationBuilder::build_driver_rna_path_variable(const OperationKey &driver_key,
const RNAPathKey &self_key,
ID *target_id,
const PointerRNA &target_prop,
const char *rna_path)
{
RNAPathKey variable_exit_key(target_prop, rna_path, RNAPointerSource::EXIT);
if (RNA_pointer_is_null(&variable_exit_key.ptr)) {
continue;
return;
}
if (is_same_bone_dependency(variable_exit_key, self_key) ||
is_same_nodetree_node_dependency(variable_exit_key, self_key))
{
continue;
return;
}
add_relation(variable_exit_key, driver_key, "RNA Target -> Driver");
@ -1979,30 +2046,6 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu)
add_relation(target_id_key, driver_key, "Target ID -> Driver");
}
}
/* The RNA getter for `object.data` can write to the mesh datablock due
* to the call to `BKE_mesh_wrapper_ensure_subdivision()`. This relation
* ensures it is safe to call when the driver is evaluated.
*
* For the sake of making the code more generic/defensive, the relation
* is added for any geometry type.
*
* See #96289 for more info. */
if (object != nullptr && OB_TYPE_IS_GEOMETRY(object->type)) {
StringRef rna_path(dtar->rna_path);
if (rna_path == "data" || rna_path.startswith("data.")) {
ComponentKey ob_key(target_id, NodeType::GEOMETRY);
add_relation(ob_key, driver_key, "ID -> Driver");
}
}
}
else {
/* If rna_path is nullptr, and DTAR_FLAG_STRUCT_REF isn't set, this
* is an incomplete target reference, so nothing to do here. */
}
}
DRIVER_TARGETS_LOOPER_END;
}
}
void DepsgraphRelationBuilder::build_driver_id_property(const PointerRNA &target_prop,

View File

@ -177,6 +177,16 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
virtual void build_driver_data(ID *id, FCurve *fcurve);
virtual void build_driver_variables(ID *id, FCurve *fcurve);
virtual void build_driver_scene_camera_variable(const OperationKey &driver_key,
const RNAPathKey &self_key,
Scene *scene,
const char *rna_path);
virtual void build_driver_rna_path_variable(const OperationKey &driver_key,
const RNAPathKey &self_key,
ID *target_id,
const PointerRNA &target_prop,
const char *rna_path);
/* Build operations of a property value from which is read by a driver target.
*
* The driver target points to a data-block (or a sub-data-block like View Layer).