Merge branch 'blender-v2.82-release'
This commit is contained in:
@@ -107,6 +107,7 @@ bool driver_get_variable_property(struct ChannelDriver *driver,
|
|||||||
int *r_index);
|
int *r_index);
|
||||||
|
|
||||||
bool BKE_driver_has_simple_expression(struct ChannelDriver *driver);
|
bool BKE_driver_has_simple_expression(struct ChannelDriver *driver);
|
||||||
|
bool BKE_driver_expression_depends_on_time(struct ChannelDriver *driver);
|
||||||
void BKE_driver_invalidate_expression(struct ChannelDriver *driver,
|
void BKE_driver_invalidate_expression(struct ChannelDriver *driver,
|
||||||
bool expr_changed,
|
bool expr_changed,
|
||||||
bool varname_changed);
|
bool varname_changed);
|
||||||
|
@@ -2142,20 +2142,34 @@ ChannelDriver *fcurve_copy_driver(const ChannelDriver *driver)
|
|||||||
|
|
||||||
/* Driver Expression Evaluation --------------- */
|
/* Driver Expression Evaluation --------------- */
|
||||||
|
|
||||||
|
/* Index constants for the expression parameter array. */
|
||||||
|
enum {
|
||||||
|
/* Index of the 'frame' variable. */
|
||||||
|
VAR_INDEX_FRAME = 0,
|
||||||
|
/* Index of the first user-defined driver variable. */
|
||||||
|
VAR_INDEX_CUSTOM
|
||||||
|
};
|
||||||
|
|
||||||
static ExprPyLike_Parsed *driver_compile_simple_expr_impl(ChannelDriver *driver)
|
static ExprPyLike_Parsed *driver_compile_simple_expr_impl(ChannelDriver *driver)
|
||||||
{
|
{
|
||||||
/* Prepare parameter names. */
|
/* Prepare parameter names. */
|
||||||
int names_len = BLI_listbase_count(&driver->variables);
|
int names_len = BLI_listbase_count(&driver->variables);
|
||||||
const char **names = BLI_array_alloca(names, names_len + 1);
|
const char **names = BLI_array_alloca(names, names_len + VAR_INDEX_CUSTOM);
|
||||||
int i = 0;
|
int i = VAR_INDEX_CUSTOM;
|
||||||
|
|
||||||
names[i++] = "frame";
|
names[VAR_INDEX_FRAME] = "frame";
|
||||||
|
|
||||||
for (DriverVar *dvar = driver->variables.first; dvar; dvar = dvar->next) {
|
for (DriverVar *dvar = driver->variables.first; dvar; dvar = dvar->next) {
|
||||||
names[i++] = dvar->name;
|
names[i++] = dvar->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return BLI_expr_pylike_parse(driver->expression, names, names_len + 1);
|
return BLI_expr_pylike_parse(driver->expression, names, names_len + VAR_INDEX_CUSTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool driver_check_simple_expr_depends_on_time(ExprPyLike_Parsed *expr)
|
||||||
|
{
|
||||||
|
/* Check if the 'frame' parameter is actually used. */
|
||||||
|
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(ChannelDriver *driver,
|
||||||
@@ -2165,10 +2179,10 @@ static bool driver_evaluate_simple_expr(ChannelDriver *driver,
|
|||||||
{
|
{
|
||||||
/* Prepare parameter values. */
|
/* Prepare parameter values. */
|
||||||
int vars_len = BLI_listbase_count(&driver->variables);
|
int vars_len = BLI_listbase_count(&driver->variables);
|
||||||
double *vars = BLI_array_alloca(vars, vars_len + 1);
|
double *vars = BLI_array_alloca(vars, vars_len + VAR_INDEX_CUSTOM);
|
||||||
int i = 0;
|
int i = VAR_INDEX_CUSTOM;
|
||||||
|
|
||||||
vars[i++] = time;
|
vars[VAR_INDEX_FRAME] = time;
|
||||||
|
|
||||||
for (DriverVar *dvar = driver->variables.first; dvar; dvar = dvar->next) {
|
for (DriverVar *dvar = driver->variables.first; dvar; dvar = dvar->next) {
|
||||||
vars[i++] = driver_get_variable_value(driver, dvar);
|
vars[i++] = driver_get_variable_value(driver, dvar);
|
||||||
@@ -2176,7 +2190,8 @@ static bool driver_evaluate_simple_expr(ChannelDriver *driver,
|
|||||||
|
|
||||||
/* Evaluate expression. */
|
/* Evaluate expression. */
|
||||||
double result_val;
|
double result_val;
|
||||||
eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, vars, vars_len + 1, &result_val);
|
eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(
|
||||||
|
expr, vars, vars_len + VAR_INDEX_CUSTOM, &result_val);
|
||||||
const char *message;
|
const char *message;
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@@ -2245,6 +2260,44 @@ bool BKE_driver_has_simple_expression(ChannelDriver *driver)
|
|||||||
return driver_compile_simple_expr(driver) && BLI_expr_pylike_is_valid(driver->expr_simple);
|
return driver_compile_simple_expr(driver) && BLI_expr_pylike_is_valid(driver->expr_simple);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO(sergey): This is somewhat weak, but we don't want neither false-positive
|
||||||
|
* time dependencies nor special exceptions in the depsgraph evaluation. */
|
||||||
|
static bool python_driver_exression_depends_on_time(const char *expression)
|
||||||
|
{
|
||||||
|
if (expression[0] == '\0') {
|
||||||
|
/* Empty expression depends on nothing. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (strchr(expression, '(') != NULL) {
|
||||||
|
/* Function calls are considered dependent on a time. */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strstr(expression, "frame") != NULL) {
|
||||||
|
/* Variable `frame` depends on time. */
|
||||||
|
/* TODO(sergey): This is a bit weak, but not sure about better way of handling this. */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/* Possible indirect time relation s should be handled via variable targets. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the expression in the driver may depend on the current frame. */
|
||||||
|
bool BKE_driver_expression_depends_on_time(ChannelDriver *driver)
|
||||||
|
{
|
||||||
|
if (driver->type != DRIVER_TYPE_PYTHON) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BKE_driver_has_simple_expression(driver)) {
|
||||||
|
/* Simple expressions can be checked exactly. */
|
||||||
|
return driver_check_simple_expr_depends_on_time(driver->expr_simple);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Otherwise, heuristically scan the expression string for certain patterns. */
|
||||||
|
return python_driver_exression_depends_on_time(driver->expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Reset cached compiled expression data */
|
/* Reset cached compiled expression data */
|
||||||
void BKE_driver_invalidate_expression(ChannelDriver *driver,
|
void BKE_driver_invalidate_expression(ChannelDriver *driver,
|
||||||
bool expr_changed,
|
bool expr_changed,
|
||||||
|
@@ -45,6 +45,7 @@ typedef enum eExprPyLike_EvalStatus {
|
|||||||
void BLI_expr_pylike_free(struct ExprPyLike_Parsed *expr);
|
void BLI_expr_pylike_free(struct ExprPyLike_Parsed *expr);
|
||||||
bool BLI_expr_pylike_is_valid(struct ExprPyLike_Parsed *expr);
|
bool BLI_expr_pylike_is_valid(struct ExprPyLike_Parsed *expr);
|
||||||
bool BLI_expr_pylike_is_constant(struct ExprPyLike_Parsed *expr);
|
bool BLI_expr_pylike_is_constant(struct ExprPyLike_Parsed *expr);
|
||||||
|
bool BLI_expr_pylike_is_using_param(struct ExprPyLike_Parsed *expr, int index);
|
||||||
ExprPyLike_Parsed *BLI_expr_pylike_parse(const char *expression,
|
ExprPyLike_Parsed *BLI_expr_pylike_parse(const char *expression,
|
||||||
const char **param_names,
|
const char **param_names,
|
||||||
int param_names_len);
|
int param_names_len);
|
||||||
|
@@ -140,6 +140,24 @@ bool BLI_expr_pylike_is_constant(ExprPyLike_Parsed *expr)
|
|||||||
return expr != NULL && expr->ops_count == 1 && expr->ops[0].opcode == OPCODE_CONST;
|
return expr != NULL && expr->ops_count == 1 && expr->ops[0].opcode == OPCODE_CONST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check if the parsed expression uses the parameter with the given index. */
|
||||||
|
bool BLI_expr_pylike_is_using_param(ExprPyLike_Parsed *expr, int index)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (expr == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < expr->ops_count; i++) {
|
||||||
|
if (expr->ops[i].opcode == OPCODE_PARAMETER && expr->ops[i].arg.ival == index) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
@@ -124,28 +124,6 @@ namespace DEG {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/* TODO(sergey): This is somewhat weak, but we don't want neither false-positive
|
|
||||||
* time dependencies nor special exceptions in the depsgraph evaluation. */
|
|
||||||
|
|
||||||
bool python_driver_exression_depends_on_time(const char *expression)
|
|
||||||
{
|
|
||||||
if (expression[0] == '\0') {
|
|
||||||
/* Empty expression depends on nothing. */
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (strchr(expression, '(') != NULL) {
|
|
||||||
/* Function calls are considered dependent on a time. */
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strstr(expression, "frame") != NULL) {
|
|
||||||
/* Variable `frame` depends on time. */
|
|
||||||
/* TODO(sergey): This is a bit weak, but not sure about better way of handling this. */
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
/* Possible indirect time relation s should be handled via variable targets. */
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool driver_target_depends_on_time(const DriverTarget *target)
|
bool driver_target_depends_on_time(const DriverTarget *target)
|
||||||
{
|
{
|
||||||
if (target->idtype == ID_SCE &&
|
if (target->idtype == ID_SCE &&
|
||||||
@@ -177,11 +155,9 @@ bool driver_variables_depends_on_time(const ListBase *variables)
|
|||||||
|
|
||||||
bool driver_depends_on_time(ChannelDriver *driver)
|
bool driver_depends_on_time(ChannelDriver *driver)
|
||||||
{
|
{
|
||||||
if (driver->type == DRIVER_TYPE_PYTHON) {
|
if (BKE_driver_expression_depends_on_time(driver)) {
|
||||||
if (python_driver_exression_depends_on_time(driver->expression)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (driver_variables_depends_on_time(&driver->variables)) {
|
if (driver_variables_depends_on_time(&driver->variables)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -282,6 +282,19 @@ TEST(expr_pylike, MultipleArgs)
|
|||||||
BLI_expr_pylike_free(expr);
|
BLI_expr_pylike_free(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(expr_pylike, UsingParam)
|
||||||
|
{
|
||||||
|
const char *names[3] = {"x", "y", "z"};
|
||||||
|
|
||||||
|
ExprPyLike_Parsed *expr = BLI_expr_pylike_parse("x + z", names, ARRAY_SIZE(names));
|
||||||
|
|
||||||
|
EXPECT_TRUE(BLI_expr_pylike_is_using_param(expr, 0));
|
||||||
|
EXPECT_FALSE(BLI_expr_pylike_is_using_param(expr, 1));
|
||||||
|
EXPECT_TRUE(BLI_expr_pylike_is_using_param(expr, 2));
|
||||||
|
|
||||||
|
BLI_expr_pylike_free(expr);
|
||||||
|
}
|
||||||
|
|
||||||
#define TEST_ERROR(name, str, x, code) \
|
#define TEST_ERROR(name, str, x, code) \
|
||||||
TEST(expr_pylike, Error_##name) \
|
TEST(expr_pylike, Error_##name) \
|
||||||
{ \
|
{ \
|
||||||
|
Reference in New Issue
Block a user