Drivers: Introduce the Context Properties

Drivers: Introduce the Context Properties

The goal: allow accessing context dependent data, such as active scene camera
without linking to a specific scene data-block. This is useful in cases when,
for example, geometry node setup needs to be aware of the camera position.

A possible work-around without changes like this is to have some scene
evaluation hook which will update driver variables for the currently evaluating
scene. But this raises an issue of linking: it is undesirable that the asset
scene is linked to the shot file.
Surely, it is possible to have post-evaluation handler to clear the variables,
but it all starts to be quite messy. Not to mention possible threading
conflicts.

Another possibility of introducing a way to achieve the goal is to make it so
the dependency graph somehow parses the python expression where artists can
(and already are trying to) type something like:

  depsgraph.scene.camera.matrix_world.col[3][0]

But this is not only tricky to implement properly and reliably, it hits two
limitations:

- Currently dependency graph can only easily resolve dependencies to a RNA
  property.

- Some properties access which are valid in Python are not considered valid
  RNA properties by the existing property resolution functions:

  `camera.matrix_world[3][0]` is a valid RNA property, but
  `camera.matrix_world.col[3][0]` is not.

Using driver variables allows to have visual feedback when the path resolution
fails, and there is no way to visualize errors in the python expression itself.

This change introduces the new variable type: Context Property. Using this
variable type makes allows to choose between Active Scene and Active View
Layer. These scene and view layer are resolved during the driver evaluation
time, based on the current dependency graph.

This allows to create a driver variable in the following configuration:

- Type: Context Property
- Context Property: Active Scene
- Path: camera.matrix_world[3][0]

The naming is a bit confusing. Tried my best to keep it clear keeping two
aspects in mind: using UI naming when possible, and follow the existing
naming.

A lot of the changes are related on making it so the required data is available
from the variable evaluation functions. It wasn't really clear what the data
would be, and the scope of the changes, so it is done together with the
functional changes.

It seems that there is some variable evaluation logic duplicated in the
`bpy_rna_driver.c`. This change does not change it. It is not really clear why
this separate code path with much more limited scope of supported target types
is even needed.

There is also a possible change in the behavior of the dependency graph: it
is now using ID of the resolved path when building driver variables. It used
to use the variable ID. In common cases they match, but when going into nested
data-blocks it is actually correct to use relation to the resolved ID. Not sure
if there was some code to ensure that, which now can be resolved. Also not sure
whether it is still needed to ensure the ID specified in the driver target is
build as well. Intuitively it is not needed.

Pull Request #105132
This commit is contained in:
2023-03-06 16:01:47 +01:00
committed by Sergey Sharybin
parent 4fb6e45b37
commit c26566ad27
12 changed files with 356 additions and 88 deletions

View File

@@ -21,6 +21,8 @@ struct FCurve;
struct PathResolvedRNA;
struct PointerRNA;
struct PropertyRNA;
struct Scene;
struct ViewLayer;
/* ************** F-Curve Drivers ***************** */
@@ -59,6 +61,32 @@ void fcurve_free_driver(struct FCurve *fcu);
*/
struct ChannelDriver *fcurve_copy_driver(const struct ChannelDriver *driver);
/**
* Get property from which the specific property can be found from.
*
* This depends on the type of `dvar`:
*
* - For the Single Property the `r_prop` is a pointer to an ID, which is used to resolve the
* target rna_path.
*
* - For Transform Channel, Rotational Difference, Distance the `r_prop` is a pointer to an
* object from which transformation is read.
*
* - For Context Property the `r_prop` points to a resolved data corresponding to the
* dtar->context_property accessed from the given evaluated context. This could either be an ID
* property for Active Scene, or a data property for Active View Layer.
*
* If the target property can not be resolved false is returned.
*/
typedef struct DriverTargetContext {
struct Scene *scene;
struct ViewLayer *view_layer;
} DriverTargetContext;
bool driver_get_target_property(const DriverTargetContext *driver_target_context,
struct DriverVar *dvar,
struct DriverTarget *dtar,
struct PointerRNA *r_prop);
/**
* Copy driver variables from src_vars list to dst_vars list.
*/
@@ -102,11 +130,15 @@ struct DriverVar *driver_add_new_variable(struct ChannelDriver *driver);
/**
* Evaluate a Driver Variable to get a value that contributes to the final.
*/
float driver_get_variable_value(struct ChannelDriver *driver, struct DriverVar *dvar);
float driver_get_variable_value(const struct AnimationEvalContext *anim_eval_context,
struct ChannelDriver *driver,
struct DriverVar *dvar);
/**
* Same as 'dtar_get_prop_val'. but get the RNA property.
*/
bool driver_get_variable_property(struct ChannelDriver *driver,
bool driver_get_variable_property(const struct AnimationEvalContext *anim_eval_context,
struct ChannelDriver *driver,
struct DriverVar *dvar,
struct DriverTarget *dtar,
struct PointerRNA *r_ptr,
struct PropertyRNA **r_prop,

View File

@@ -10,6 +10,7 @@
#include "DNA_anim_types.h"
#include "DNA_constraint_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_alloca.h"
#include "BLI_expr_pylike_eval.h"
@@ -31,11 +32,14 @@
#include "RNA_access.h"
#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "atomic_ops.h"
#include "CLG_log.h"
#include "DEG_depsgraph_query.h"
#ifdef WITH_PYTHON
# include "BPY_extern.h"
#endif
@@ -53,7 +57,9 @@ static CLG_LogRef LOG = {"bke.fcurve"};
/* TypeInfo for Driver Variables (dvti) */
typedef struct DriverVarTypeInfo {
/* Evaluation callback. */
float (*get_value)(ChannelDriver *driver, DriverVar *dvar);
float (*get_value)(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
DriverVar *dvar);
/* Allocation of target slots. */
int num_targets; /* Number of target slots required. */
@@ -73,27 +79,89 @@ typedef struct DriverVarTypeInfo {
/** \name Driver Target Utilities
* \{ */
static DriverTargetContext driver_target_context_from_animation_context(
const AnimationEvalContext *anim_eval_context)
{
DriverTargetContext driver_target_context;
driver_target_context.scene = DEG_get_evaluated_scene(anim_eval_context->depsgraph);
driver_target_context.view_layer = DEG_get_evaluated_view_layer(anim_eval_context->depsgraph);
return driver_target_context;
}
/* Specialized implementation of driver_get_target_property() used for the
* DVAR_TYPE_CONTEXT_PROP variable type. */
static bool driver_get_target_context_property(const DriverTargetContext *driver_target_context,
DriverTarget *dtar,
PointerRNA *r_property_ptr)
{
switch (dtar->context_property) {
case DTAR_CONTEXT_PROPERTY_ACTIVE_SCENE:
RNA_id_pointer_create(&driver_target_context->scene->id, r_property_ptr);
return true;
case DTAR_CONTEXT_PROPERTY_ACTIVE_VIEW_LAYER: {
RNA_pointer_create(&driver_target_context->scene->id,
&RNA_ViewLayer,
driver_target_context->view_layer,
r_property_ptr);
return true;
}
}
BLI_assert_unreachable();
/* Reset to a NULL RNA pointer.
* This allows to more gracefully handle issues with unsupported configuration (forward
* compatibility. for example). */
/* TODO(sergey): Replace with utility null-RNA-pointer creation once that is available. */
r_property_ptr->data = NULL;
r_property_ptr->type = NULL;
r_property_ptr->owner_id = NULL;
return false;
}
bool driver_get_target_property(const DriverTargetContext *driver_target_context,
DriverVar *dvar,
DriverTarget *dtar,
PointerRNA *r_prop)
{
if (dvar->type == DVAR_TYPE_CONTEXT_PROP) {
return driver_get_target_context_property(driver_target_context, dtar, r_prop);
}
if (dtar->id == NULL) {
return false;
}
RNA_id_pointer_create(dtar->id, r_prop);
return true;
}
/**
* Helper function to obtain a value using RNA from the specified source
* (for evaluating drivers).
*/
static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
static float dtar_get_prop_val(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
DriverVar *dvar,
DriverTarget *dtar)
{
PointerRNA id_ptr, ptr;
PropertyRNA *prop;
ID *id;
int index = -1;
float value = 0.0f;
/* Sanity check. */
if (ELEM(NULL, driver, dtar)) {
if (driver == NULL) {
return 0.0f;
}
id = dtar->id;
/* Error check for missing pointer. */
if (id == NULL) {
/* Get property to resolve the target from.
* Naming is a bit confusing, but this is what is exposed as "Prop" or "Context Property" in
* interface. */
const DriverTargetContext driver_target_context = driver_target_context_from_animation_context(
anim_eval_context);
PointerRNA property_ptr;
if (!driver_get_target_property(&driver_target_context, dvar, dtar, &property_ptr)) {
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path);
}
@@ -103,16 +171,18 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
return 0.0f;
}
/* Get RNA-pointer for the ID-block given in target. */
RNA_id_pointer_create(id, &id_ptr);
/* Get property to read from, and get value as appropriate. */
if (!RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) {
PointerRNA value_ptr;
PropertyRNA *value_prop;
int index = -1;
float value = 0.0f;
if (!RNA_path_resolve_property_full(
&property_ptr, dtar->rna_path, &value_ptr, &value_prop, &index)) {
/* Path couldn't be resolved. */
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG,
"Driver Evaluation Error: cannot resolve target for %s -> %s",
id->name,
property_ptr.owner_id->name,
dtar->rna_path);
}
@@ -121,14 +191,14 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
return 0.0f;
}
if (RNA_property_array_check(prop)) {
if (RNA_property_array_check(value_prop)) {
/* Array. */
if (index < 0 || index >= RNA_property_array_length(&ptr, prop)) {
if (index < 0 || index >= RNA_property_array_length(&value_ptr, value_prop)) {
/* Out of bounds. */
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG,
"Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)",
id->name,
property_ptr.owner_id->name,
dtar->rna_path,
index);
}
@@ -138,15 +208,15 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
return 0.0f;
}
switch (RNA_property_type(prop)) {
switch (RNA_property_type(value_prop)) {
case PROP_BOOLEAN:
value = (float)RNA_property_boolean_get_index(&ptr, prop, index);
value = (float)RNA_property_boolean_get_index(&value_ptr, value_prop, index);
break;
case PROP_INT:
value = (float)RNA_property_int_get_index(&ptr, prop, index);
value = (float)RNA_property_int_get_index(&value_ptr, value_prop, index);
break;
case PROP_FLOAT:
value = RNA_property_float_get_index(&ptr, prop, index);
value = RNA_property_float_get_index(&value_ptr, value_prop, index);
break;
default:
break;
@@ -154,18 +224,18 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
}
else {
/* Not an array. */
switch (RNA_property_type(prop)) {
switch (RNA_property_type(value_prop)) {
case PROP_BOOLEAN:
value = (float)RNA_property_boolean_get(&ptr, prop);
value = (float)RNA_property_boolean_get(&value_ptr, value_prop);
break;
case PROP_INT:
value = (float)RNA_property_int_get(&ptr, prop);
value = (float)RNA_property_int_get(&value_ptr, value_prop);
break;
case PROP_FLOAT:
value = RNA_property_float_get(&ptr, prop);
value = RNA_property_float_get(&value_ptr, value_prop);
break;
case PROP_ENUM:
value = (float)RNA_property_enum_get(&ptr, prop);
value = (float)RNA_property_enum_get(&value_ptr, value_prop);
break;
default:
break;
@@ -177,16 +247,16 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
return value;
}
bool driver_get_variable_property(ChannelDriver *driver,
bool driver_get_variable_property(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
DriverVar *dvar,
DriverTarget *dtar,
PointerRNA *r_ptr,
PropertyRNA **r_prop,
int *r_index)
{
PointerRNA id_ptr;
PointerRNA ptr;
PropertyRNA *prop;
ID *id;
int index = -1;
/* Sanity check. */
@@ -194,10 +264,11 @@ bool driver_get_variable_property(ChannelDriver *driver,
return false;
}
id = dtar->id;
/* Error check for missing pointer. */
if (id == NULL) {
/* Get RNA-pointer for the data-block given in target. */
const DriverTargetContext driver_target_context = driver_target_context_from_animation_context(
anim_eval_context);
PointerRNA target_ptr;
if (!driver_get_target_property(&driver_target_context, dvar, dtar, &target_ptr)) {
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path);
}
@@ -207,15 +278,12 @@ bool driver_get_variable_property(ChannelDriver *driver,
return false;
}
/* Get RNA-pointer for the ID-block given in target. */
RNA_id_pointer_create(id, &id_ptr);
/* Get property to read from, and get value as appropriate. */
if (dtar->rna_path == NULL || dtar->rna_path[0] == '\0') {
ptr = PointerRNA_NULL;
prop = NULL; /* OK. */
}
else if (RNA_path_resolve_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) {
else if (RNA_path_resolve_full(&target_ptr, dtar->rna_path, &ptr, &prop, &index)) {
/* OK. */
}
else {
@@ -223,7 +291,7 @@ bool driver_get_variable_property(ChannelDriver *driver,
if (G.debug & G_DEBUG) {
CLOG_ERROR(&LOG,
"Driver Evaluation Error: cannot resolve target for %s -> %s",
id->name,
target_ptr.owner_id->name,
dtar->rna_path);
}
@@ -276,14 +344,18 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar)
* \{ */
/* Evaluate 'single prop' driver variable. */
static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar)
static float dvar_eval_singleProp(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
DriverVar *dvar)
{
/* Just evaluate the first target slot. */
return dtar_get_prop_val(driver, &dvar->targets[0]);
return dtar_get_prop_val(anim_eval_context, driver, dvar, &dvar->targets[0]);
}
/* Evaluate 'rotation difference' driver variable. */
static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar)
static float dvar_eval_rotDiff(const AnimationEvalContext *UNUSED(anim_eval_context),
ChannelDriver *driver,
DriverVar *dvar)
{
short valid_targets = driver_check_valid_targets(driver, dvar);
@@ -344,7 +416,9 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar)
*
* TODO: this needs to take into account space conversions.
*/
static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
static float dvar_eval_locDiff(const AnimationEvalContext *UNUSED(anim_eval_context),
ChannelDriver *driver,
DriverVar *dvar)
{
float loc1[3] = {0.0f, 0.0f, 0.0f};
float loc2[3] = {0.0f, 0.0f, 0.0f};
@@ -446,7 +520,9 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
/**
* Evaluate 'transform channel' driver variable.
*/
static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
static float dvar_eval_transChan(const AnimationEvalContext *UNUSED(anim_eval_context),
ChannelDriver *driver,
DriverVar *dvar)
{
DriverTarget *dtar = &dvar->targets[0];
Object *ob = (Object *)dtar->id;
@@ -578,6 +654,15 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
return mat[3][dtar->transChan];
}
/* Evaluate 'context prop' driver variable. */
static float dvar_eval_contextProp(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
DriverVar *dvar)
{
/* Just evaluate the first target slot. */
return dtar_get_prop_val(anim_eval_context, driver, dvar, &dvar->targets[0]);
}
/* Convert a quaternion to pseudo-angles representing the weighted amount of rotation. */
static void quaternion_to_angles(float quat[4], int channel)
{
@@ -675,6 +760,12 @@ static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = {
{"Object/Bone"}, /* UI names for targets */
{DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */
END_DVAR_TYPEDEF,
BEGIN_DVAR_TYPEDEF(DVAR_TYPE_CONTEXT_PROP) dvar_eval_contextProp, /* Eval callback. */
1, /* Number of targets used. */
{"Property"}, /* UI names for targets */
{0} /* Flags. */
END_DVAR_TYPEDEF,
};
/* Get driver variable typeinfo */
@@ -972,7 +1063,8 @@ static bool driver_check_simple_expr_depends_on_time(ExprPyLike_Parsed *expr)
return BLI_expr_pylike_is_using_param(expr, VAR_INDEX_FRAME);
}
static bool driver_evaluate_simple_expr(ChannelDriver *driver,
static bool driver_evaluate_simple_expr(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
ExprPyLike_Parsed *expr,
float *result,
float time)
@@ -985,7 +1077,7 @@ static bool driver_evaluate_simple_expr(ChannelDriver *driver,
vars[VAR_INDEX_FRAME] = time;
LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
vars[i++] = driver_get_variable_value(driver, dvar);
vars[i++] = driver_get_variable_value(anim_eval_context, driver, dvar);
}
/* Evaluate expression. */
@@ -1042,7 +1134,8 @@ static bool driver_compile_simple_expr(ChannelDriver *driver)
/* Try using the simple expression evaluator to compute the result of the driver.
* On success, stores the result and returns true; on failure result is set to 0. */
static bool driver_try_evaluate_simple_expr(ChannelDriver *driver,
static bool driver_try_evaluate_simple_expr(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
ChannelDriver *driver_orig,
float *result,
float time)
@@ -1051,7 +1144,8 @@ static bool driver_try_evaluate_simple_expr(ChannelDriver *driver,
return driver_compile_simple_expr(driver_orig) &&
BLI_expr_pylike_is_valid(driver_orig->expr_simple) &&
driver_evaluate_simple_expr(driver, driver_orig->expr_simple, result, time);
driver_evaluate_simple_expr(
anim_eval_context, driver, driver_orig->expr_simple, result, time);
}
bool BKE_driver_has_simple_expression(ChannelDriver *driver)
@@ -1121,7 +1215,9 @@ void BKE_driver_invalidate_expression(ChannelDriver *driver,
/** \name Driver Evaluation
* \{ */
float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar)
float driver_get_variable_value(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver,
DriverVar *dvar)
{
const DriverVarTypeInfo *dvti;
@@ -1136,7 +1232,7 @@ float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar)
dvti = get_dvar_typeinfo(dvar->type);
if (dvti && dvti->get_value) {
dvar->curval = dvti->get_value(driver, dvar);
dvar->curval = dvti->get_value(anim_eval_context, driver, dvar);
}
else {
dvar->curval = 0.0f;
@@ -1145,7 +1241,8 @@ float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar)
return dvar->curval;
}
static void evaluate_driver_sum(ChannelDriver *driver)
static void evaluate_driver_sum(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver)
{
DriverVar *dvar;
@@ -1153,7 +1250,7 @@ static void evaluate_driver_sum(ChannelDriver *driver)
if (BLI_listbase_is_single(&driver->variables)) {
/* Just one target, so just use that. */
dvar = driver->variables.first;
driver->curval = driver_get_variable_value(driver, dvar);
driver->curval = driver_get_variable_value(anim_eval_context, driver, dvar);
return;
}
@@ -1163,7 +1260,7 @@ static void evaluate_driver_sum(ChannelDriver *driver)
/* Loop through targets, adding (hopefully we don't get any overflow!). */
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
value += driver_get_variable_value(driver, dvar);
value += driver_get_variable_value(anim_eval_context, driver, dvar);
tot++;
}
@@ -1176,7 +1273,8 @@ static void evaluate_driver_sum(ChannelDriver *driver)
}
}
static void evaluate_driver_min_max(ChannelDriver *driver)
static void evaluate_driver_min_max(const AnimationEvalContext *anim_eval_context,
ChannelDriver *driver)
{
DriverVar *dvar;
float value = 0.0f;
@@ -1184,7 +1282,7 @@ static void evaluate_driver_min_max(ChannelDriver *driver)
/* Loop through the variables, getting the values and comparing them to existing ones. */
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
/* Get value. */
float tmp_val = driver_get_variable_value(driver, dvar);
float tmp_val = driver_get_variable_value(anim_eval_context, driver, dvar);
/* Store this value if appropriate. */
if (dvar->prev) {
@@ -1221,8 +1319,11 @@ static void evaluate_driver_python(PathResolvedRNA *anim_rna,
if ((driver_orig->expression[0] == '\0') || (driver_orig->flag & DRIVER_FLAG_INVALID)) {
driver->curval = 0.0f;
}
else if (!driver_try_evaluate_simple_expr(
driver, driver_orig, &driver->curval, anim_eval_context->eval_time)) {
else if (!driver_try_evaluate_simple_expr(anim_eval_context,
driver,
driver_orig,
&driver->curval,
anim_eval_context->eval_time)) {
#ifdef WITH_PYTHON
/* This evaluates the expression using Python, and returns its result:
* - on errors it reports, then returns 0.0f. */
@@ -1250,11 +1351,11 @@ float evaluate_driver(PathResolvedRNA *anim_rna,
switch (driver->type) {
case DRIVER_TYPE_AVERAGE: /* Average values of driver targets. */
case DRIVER_TYPE_SUM: /* Sum values of driver targets. */
evaluate_driver_sum(driver);
evaluate_driver_sum(anim_eval_context, driver);
break;
case DRIVER_TYPE_MIN: /* Smallest value. */
case DRIVER_TYPE_MAX: /* Largest value. */
evaluate_driver_min_max(driver);
evaluate_driver_min_max(anim_eval_context, driver);
break;
case DRIVER_TYPE_PYTHON: /* Expression. */
evaluate_driver_python(anim_rna, driver, driver_orig, anim_eval_context);

View File

@@ -1192,29 +1192,46 @@ void DepsgraphNodeBuilder::build_driver(ID *id, FCurve *fcurve, int driver_index
void DepsgraphNodeBuilder::build_driver_variables(ID *id, FCurve *fcurve)
{
build_driver_id_property(id, fcurve->rna_path);
PointerRNA id_ptr;
RNA_id_pointer_create(id, &id_ptr);
build_driver_id_property(id_ptr, fcurve->rna_path);
DriverTargetContext driver_target_context;
driver_target_context.scene = graph_->scene;
driver_target_context.view_layer = graph_->view_layer;
LISTBASE_FOREACH (DriverVar *, dvar, &fcurve->driver->variables) {
DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
if (dtar->id == nullptr) {
PointerRNA target_prop;
if (!driver_get_target_property(&driver_target_context, dvar, dtar, &target_prop)) {
continue;
}
build_id(dtar->id);
build_driver_id_property(dtar->id, dtar->rna_path);
/* Property is always expected to be resolved to a non-null RNA property, which is always
* relative to some ID. */
BLI_assert(target_prop.owner_id);
ID *target_id = target_prop.owner_id;
build_id(target_id);
build_driver_id_property(target_prop, dtar->rna_path);
}
DRIVER_TARGETS_LOOPER_END;
}
}
void DepsgraphNodeBuilder::build_driver_id_property(ID *id, const char *rna_path)
void DepsgraphNodeBuilder::build_driver_id_property(const PointerRNA &target_prop,
const char *rna_path_from_target_prop)
{
if (id == nullptr || rna_path == nullptr) {
if (rna_path_from_target_prop == nullptr || rna_path_from_target_prop[0] == '\0') {
return;
}
PointerRNA id_ptr, ptr;
PointerRNA ptr;
PropertyRNA *prop;
int index;
RNA_id_pointer_create(id, &id_ptr);
if (!RNA_path_resolve_full(&id_ptr, rna_path, &ptr, &prop, &index)) {
if (!RNA_path_resolve_full(&target_prop, rna_path_from_target_prop, &ptr, &prop, &index)) {
return;
}
if (prop == nullptr) {

View File

@@ -49,6 +49,7 @@ struct bNodeSocket;
struct bNodeTree;
struct bPoseChannel;
struct bSound;
struct PointerRNA;
namespace blender::deg {
@@ -207,6 +208,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
*/
virtual void build_animation_images(ID *id);
virtual void build_action(bAction *action);
/**
* Build graph node(s) for Driver
* \param id: ID-Block that driver is attached to
@@ -214,8 +216,22 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
* \param driver_index: Index in animation data drivers list
*/
virtual void build_driver(ID *id, FCurve *fcurve, int driver_index);
virtual void build_driver_variables(ID *id, FCurve *fcurve);
virtual void build_driver_id_property(ID *id, 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).
* This data-block is presented in the interface as a "Prop" and its resolved RNA pointer is
* passed here as `target_prop`.
*
* The tricky part (and a bit confusing naming) is that the driver target accesses a property of
* the `target_prop` to get its value. The property which is read to give an actual target value
* is denoted by its RNA path relative to the `target_prop`. In the interface it is called "Path"
* and here it is called `rna_path_from_target_prop`. */
virtual void build_driver_id_property(const PointerRNA &target_prop,
const char *rna_path_from_target_prop);
virtual void build_parameters(ID *id);
virtual void build_dimensions(Object *object);
virtual void build_ik_pose(Object *object, bPoseChannel *pchan, bConstraint *con);

View File

@@ -1743,16 +1743,30 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu)
fcu->rna_path ? fcu->rna_path : "",
fcu->array_index);
const char *rna_path = fcu->rna_path ? fcu->rna_path : "";
const RNAPathKey self_key(id, rna_path, RNAPointerSource::ENTRY);
DriverTargetContext driver_target_context;
driver_target_context.scene = graph_->scene;
driver_target_context.view_layer = graph_->view_layer;
LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
/* Only used targets. */
DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
ID *target_id = dtar->id;
if (target_id == nullptr) {
PointerRNA target_prop;
if (!driver_get_target_property(&driver_target_context, dvar, dtar, &target_prop)) {
continue;
}
/* Property is always expected to be resolved to a non-null RNA property, which is always
* relative to some ID. */
BLI_assert(target_prop.owner_id);
ID *target_id = target_prop.owner_id;
build_id(target_id);
build_driver_id_property(target_id, dtar->rna_path);
build_driver_id_property(target_prop, dtar->rna_path);
Object *object = nullptr;
if (GS(target_id->name) == ID_OB) {
object = (Object *)target_id;
@@ -1849,16 +1863,17 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu)
}
}
void DepsgraphRelationBuilder::build_driver_id_property(ID *id, const char *rna_path)
void DepsgraphRelationBuilder::build_driver_id_property(const PointerRNA &target_prop,
const char *rna_path_from_target_prop)
{
if (id == nullptr || rna_path == nullptr) {
if (rna_path_from_target_prop == nullptr || rna_path_from_target_prop[0] == '\0') {
return;
}
PointerRNA id_ptr, ptr;
PointerRNA ptr;
PropertyRNA *prop;
int index;
RNA_id_pointer_create(id, &id_ptr);
if (!RNA_path_resolve_full(&id_ptr, rna_path, &ptr, &prop, &index)) {
if (!RNA_path_resolve_full(&target_prop, rna_path_from_target_prop, &ptr, &prop, &index)) {
return;
}
if (prop == nullptr) {

View File

@@ -171,7 +171,20 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
virtual void build_driver(ID *id, FCurve *fcurve);
virtual void build_driver_data(ID *id, FCurve *fcurve);
virtual void build_driver_variables(ID *id, FCurve *fcurve);
virtual void build_driver_id_property(ID *id, 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).
* This data-block is presented in the interface as a "Prop" and its resolved RNA pointer is
* passed here as `target_prop`.
*
* The tricky part (and a bit confusing naming) is that the driver target accesses a property of
* the `target_prop` to get its value. The property which is read to give an actual target value
* is denoted by its RNA path relative to the `target_prop`. In the interface it is called "Path"
* and here it is called `rna_path_from_target_prop`. */
virtual void build_driver_id_property(const PointerRNA &target_prop,
const char *rna_path_from_target_prop);
virtual void build_parameters(ID *id);
virtual void build_dimensions(Object *object);
virtual void build_world(World *world);

View File

@@ -902,6 +902,29 @@ static void graph_panel_driverVar__transChan(uiLayout *layout, ID *id, DriverVar
uiItemR(sub, &dtar_ptr, "transform_space", 0, IFACE_("Space"), ICON_NONE);
}
/* Settings for 'Context Property' driver variable type. */
static void graph_panel_driverVar__contextProp(uiLayout *layout, ID *id, DriverVar *dvar)
{
DriverTarget *dtar = &dvar->targets[0];
/* Initialize RNA pointer to the target. */
PointerRNA dtar_ptr;
RNA_pointer_create(id, &RNA_DriverTarget, dtar, &dtar_ptr);
/* Target Property. */
{
uiLayout *row = uiLayoutRow(layout, false);
uiItemR(row, &dtar_ptr, "context_property", 0, NULL, ICON_NONE);
}
/* Target Path */
{
uiLayout *col = uiLayoutColumn(layout, true);
uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID));
uiTemplatePathBuilder(col, &dtar_ptr, "data_path", NULL, IFACE_("Path"));
}
}
/* ----------------------------------------------------------------- */
/* property driven by the driver - duplicates Active FCurve, but useful for clarity */
@@ -1213,6 +1236,9 @@ static void graph_draw_driver_settings_panel(uiLayout *layout,
case DVAR_TYPE_TRANSFORM_CHAN: /* transform channel */
graph_panel_driverVar__transChan(box, id, dvar);
break;
case DVAR_TYPE_CONTEXT_PROP: /* context property */
graph_panel_driverVar__contextProp(box, id, dvar);
break;
}
/* 3) value of variable */

View File

@@ -322,6 +322,13 @@ typedef struct DriverTarget {
short flag;
/** Type of ID-block that this target can use. */
int idtype;
/* Context-dependent property of a "Context Property" type target.
* The `rna_path` of this property is used as a target.
* This is a value of enumerator #eDriverTarget_ContextProperty. */
int context_property;
int _pad1;
} DriverTarget;
/** Driver Target flags. */
@@ -386,6 +393,11 @@ typedef enum eDriverTarget_RotationMode {
DTAR_ROTMODE_EULER_MAX = DTAR_ROTMODE_EULER_ZYX,
} eDriverTarget_RotationMode;
typedef enum eDriverTarget_ContextProperty {
DTAR_CONTEXT_PROPERTY_ACTIVE_SCENE = 0,
DTAR_CONTEXT_PROPERTY_ACTIVE_VIEW_LAYER = 1,
} eDriverTarget_ContextProperty;
/* --- */
/* maximum number of driver targets per variable */
@@ -432,6 +444,8 @@ typedef enum eDriverVar_Types {
DVAR_TYPE_LOC_DIFF,
/** 'final' transform for object/bones */
DVAR_TYPE_TRANSFORM_CHAN,
/** Property within a current evaluation context */
DVAR_TYPE_CONTEXT_PROP,
/**
* Maximum number of variable types.

View File

@@ -161,6 +161,20 @@ const EnumPropertyItem rna_enum_driver_target_rotation_mode_items[] = {
{0, NULL, 0, NULL, NULL},
};
const EnumPropertyItem rna_enum_driver_target_context_property_items[] = {
{DTAR_CONTEXT_PROPERTY_ACTIVE_SCENE,
"ACTIVE_SCENE",
ICON_NONE,
"Active Scene",
"Currently evaluating scene"},
{DTAR_CONTEXT_PROPERTY_ACTIVE_VIEW_LAYER,
"ACTIVE_VIEW_LAYER",
ICON_NONE,
"Active View Layer",
"Currently evaluating view layer"},
{0, NULL, 0, NULL, NULL},
};
#ifdef RNA_RUNTIME
# include "WM_api.h"
@@ -1879,6 +1893,14 @@ static void rna_def_drivertarget(BlenderRNA *brna)
RNA_def_property_enum_items(prop, prop_local_space_items);
RNA_def_property_ui_text(prop, "Transform Space", "Space in which transforms are used");
RNA_def_property_update(prop, 0, "rna_DriverTarget_update_data");
prop = RNA_def_property(srna, "context_property", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "context_property");
RNA_def_property_enum_items(prop, rna_enum_driver_target_context_property_items);
RNA_def_property_enum_default(prop, DTAR_CONTEXT_PROPERTY_ACTIVE_SCENE);
RNA_def_property_ui_text(
prop, "Context Property", "Type of a context-dependent data-block to access property from");
RNA_def_property_update(prop, 0, "rna_DriverTarget_update_data");
}
static void rna_def_drivervar(BlenderRNA *brna)
@@ -1907,6 +1929,11 @@ static void rna_def_drivervar(BlenderRNA *brna)
ICON_DRIVER_DISTANCE,
"Distance",
"Distance between two bones or objects"},
{DVAR_TYPE_CONTEXT_PROP,
"CONTEXT_PROP",
ICON_RNA,
"Context Property",
"Use the value from some RNA property within the current evaluation context"},
{0, NULL, 0, NULL, NULL},
};

View File

@@ -688,7 +688,8 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
/* Support for any RNA data. */
#ifdef USE_RNA_AS_PYOBJECT
if (dvar->type == DVAR_TYPE_SINGLE_PROP) {
driver_arg = pyrna_driver_get_variable_value(driver, &dvar->targets[0]);
driver_arg = pyrna_driver_get_variable_value(
anim_eval_context, driver, dvar, &dvar->targets[0]);
if (driver_arg == NULL) {
driver_arg = PyFloat_FromDouble(0.0);
@@ -714,7 +715,7 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
#endif
{
/* Try to get variable value. */
const float tval = driver_get_variable_value(driver, dvar);
const float tval = driver_get_variable_value(anim_eval_context, driver, dvar);
driver_arg = PyFloat_FromDouble((double)tval);
}

View File

@@ -20,14 +20,17 @@
#include "bpy_rna_driver.h" /* own include */
PyObject *pyrna_driver_get_variable_value(struct ChannelDriver *driver, struct DriverTarget *dtar)
PyObject *pyrna_driver_get_variable_value(const struct AnimationEvalContext *anim_eval_context,
struct ChannelDriver *driver,
struct DriverVar *dvar,
struct DriverTarget *dtar)
{
PyObject *driver_arg = NULL;
PointerRNA ptr;
PropertyRNA *prop = NULL;
int index;
if (driver_get_variable_property(driver, dtar, &ptr, &prop, &index)) {
if (driver_get_variable_property(anim_eval_context, driver, dvar, dtar, &ptr, &prop, &index)) {
if (prop) {
if (index != -1) {
if (index < RNA_property_array_length(&ptr, prop) && index >= 0) {

View File

@@ -17,7 +17,10 @@ extern "C" {
/**
* A version of #driver_get_variable_value which returns a #PyObject.
*/
PyObject *pyrna_driver_get_variable_value(struct ChannelDriver *driver, struct DriverTarget *dtar);
PyObject *pyrna_driver_get_variable_value(const struct AnimationEvalContext *anim_eval_context,
struct ChannelDriver *driver,
struct DriverVar *dvar,
struct DriverTarget *dtar);
PyObject *pyrna_driver_self_from_anim_rna(struct PathResolvedRNA *anim_rna);
bool pyrna_driver_is_equal_anim_rna(const struct PathResolvedRNA *anim_rna,