Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Leung <aligorith@gmail.com>2009-04-20 13:17:43 +0400
committerJoshua Leung <aligorith@gmail.com>2009-04-20 13:17:43 +0400
commit332e00198914bb6cd34199fffb3832e170066959 (patch)
treef0f46d21fe9ee0ffecfa41eadd72f24eb10adda7
parent64822060783deab359160e669515417787516164 (diff)
Drivers: "Scripted Expression" drivers (i.e. PyDrivers) work again
Now it is possible to write a Python Expression using the variable names for driver targets (see mockup from initial commit) to substitute the appropriate values into the expression. In the __global__ namespace for PyDriver evaluation, the following modules are available: * __builtins__ - i.e. the builtin Python functions * bpy - new Python API * math or m - math module For example: Consider a driver with three targets, named: A, B, C Now, you could write an expression like: C if A < 5 else B or even: 2*C if A < 5 or A > 20 else m.PI*B Of course, you don't have to have three targets, the above was just an example. TODO: * Bring back way to load pydrivers.py * Blender.Noise equivalent would be nice to have P.S. I hope I haven't made any terrible Python API coding errors here (i.e. mem leaks, etc.)
-rw-r--r--source/blender/blenkernel/BKE_fcurve.h2
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c4
-rw-r--r--source/blender/blenkernel/intern/depsgraph.c4
-rw-r--r--source/blender/blenkernel/intern/fcurve.c2
-rw-r--r--source/blender/python/BPY_extern.h1
-rw-r--r--source/blender/python/intern/bpy_interface.c199
-rw-r--r--source/blender/python/intern/stubs.c2
7 files changed, 208 insertions, 6 deletions
diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h
index f973f86972d..3dd083476ce 100644
--- a/source/blender/blenkernel/BKE_fcurve.h
+++ b/source/blender/blenkernel/BKE_fcurve.h
@@ -33,6 +33,8 @@ struct ChannelDriver *fcurve_copy_driver(struct ChannelDriver *driver);
void driver_free_target(struct ChannelDriver *driver, struct DriverTarget *dtar);
struct DriverTarget *driver_add_new_target(struct ChannelDriver *driver);
+float driver_get_target_value(struct ChannelDriver *driver, struct DriverTarget *dtar);
+
/* ************** F-Curve Modifiers *************** */
/* F-Curve Modifier Type-Info (fmi):
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 5b10f921d7d..30dcb383ef6 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -401,12 +401,12 @@ static short animsys_write_rna_setting (PointerRNA *ptr, char *path, int array_i
RNA_property_enum_set(&new_ptr, prop, (int)value);
break;
default:
- break;
+ /* nothing can be done here... so it is unsuccessful? */
+ return 0;
}
}
/* successful */
- // XXX should the unhandled case also be successful?
return 1;
}
else {
diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c
index a34f99d253f..8bd848ea9e6 100644
--- a/source/blender/blenkernel/intern/depsgraph.c
+++ b/source/blender/blenkernel/intern/depsgraph.c
@@ -326,6 +326,10 @@ static void dag_add_driver_relation(AnimData *adt, DagForest *dag, DagNode *node
/* check if bone... */
if ((ob->type==OB_ARMATURE) && dtar->rna_path && strstr(dtar->rna_path, "pose.pose_channels["))
dag_add_relation(dag, node1, node, isdata?DAG_RL_DATA_DATA:DAG_RL_DATA_OB, "Driver");
+ /* check if ob data */
+ else if (dtar->rna_path && strstr(dtar->rna_path, "data."))
+ dag_add_relation(dag, node1, node, isdata?DAG_RL_DATA_DATA:DAG_RL_DATA_OB, "Driver");
+ /* normal */
else
dag_add_relation(dag, node1, node, isdata?DAG_RL_OB_DATA:DAG_RL_OB_OB, "Driver");
}
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index cce4f3a1217..49100be5216 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -650,7 +650,7 @@ ChannelDriver *fcurve_copy_driver (ChannelDriver *driver)
/* Driver Evaluation -------------------------- */
/* Helper function to obtain a value using RNA from the specified source (for evaluating drivers) */
-static float driver_get_target_value (ChannelDriver *driver, DriverTarget *dtar)
+float driver_get_target_value (ChannelDriver *driver, DriverTarget *dtar)
{
PointerRNA id_ptr, ptr;
PropertyRNA *prop;
diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h
index 6039ad05962..d8e4611d9df 100644
--- a/source/blender/python/BPY_extern.h
+++ b/source/blender/python/BPY_extern.h
@@ -126,7 +126,6 @@ extern "C" {
void BPY_pydriver_update(void);
float BPY_pydriver_eval(struct ChannelDriver *driver);
- struct Object **BPY_pydriver_get_objects(struct ChannelDriver *driver);
int BPY_button_eval(char *expr, double *value);
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index ecf1cc828f3..86406134253 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -20,6 +20,7 @@
#include "bpy_operator.h"
#include "bpy_ui.h"
+#include "DNA_anim_types.h"
#include "DNA_space_types.h"
#include "DNA_text_types.h"
@@ -29,6 +30,7 @@
#include "BLI_string.h"
#include "BKE_context.h"
+#include "BKE_fcurve.h"
#include "BKE_text.h"
#include "BPY_extern.h"
@@ -415,3 +417,200 @@ void BPY_run_ui_scripts(bContext *C)
#endif
}
+/* ****************************************** */
+/* Drivers - PyExpression Evaluation */
+// XXX Hopefully I haven't committed any PyAPI coding sins here ;) - Aligorith, 2009Apr20
+
+/* for pydrivers (drivers using one-line Python expressions to express relationships between targets) */
+PyObject *bpy_pydriver_Dict = NULL;
+
+/* For faster execution we keep a special dictionary for pydrivers, with
+ * the needed modules and aliases.
+ */
+static int bpy_pydriver_create_dict(void)
+{
+ PyObject *d, *mod;
+
+ /* validate namespace for driver evaluation */
+ if (bpy_pydriver_Dict) return -1;
+
+ d = PyDict_New();
+ if (d == NULL)
+ return -1;
+ else
+ bpy_pydriver_Dict = d;
+
+ /* import some modules: builtins, bpy, math, (Blender.noise )*/
+ PyDict_SetItemString(d, "__builtins__", PyEval_GetBuiltins());
+
+ mod = PyImport_ImportModule("math");
+ if (mod) {
+ PyDict_Merge(d, PyModule_GetDict(mod), 0); /* 0 - dont overwrite existing values */
+
+ /* Only keep for backwards compat! - just import all math into root, they are standard */
+ PyDict_SetItemString(d, "math", mod);
+ PyDict_SetItemString(d, "m", mod);
+ Py_DECREF(mod);
+ }
+
+ /* add bpy to global namespace */
+ mod= PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0);
+ if (mod) {
+ PyDict_SetItemString(bpy_pydriver_Dict, "bpy", mod);
+ Py_DECREF(mod);
+ }
+
+
+#if 0 // non existant yet
+ mod = PyImport_ImportModule("Blender.Noise");
+ if (mod) {
+ PyDict_SetItemString(d, "noise", mod);
+ PyDict_SetItemString(d, "n", mod);
+ Py_DECREF(mod);
+ } else {
+ PyErr_Clear();
+ }
+
+ /* If there's a Blender text called pydrivers.py, import it.
+ * Users can add their own functions to this module.
+ */
+ if (G.f & G_DOSCRIPTLINKS) {
+ mod = importText("pydrivers"); /* can also use PyImport_Import() */
+ if (mod) {
+ PyDict_SetItemString(d, "pydrivers", mod);
+ PyDict_SetItemString(d, "p", mod);
+ Py_DECREF(mod);
+ } else {
+ PyErr_Clear();
+ }
+ }
+#endif // non existant yet
+
+ return 0;
+}
+
+/* Update function, it gets rid of pydrivers global dictionary, forcing
+ * BPY_pydriver_eval to recreate it. This function is used to force
+ * reloading the Blender text module "pydrivers.py", if available, so
+ * updates in it reach pydriver evaluation.
+ */
+void BPY_pydriver_update(void)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ if (bpy_pydriver_Dict) { /* free the global dict used by pydrivers */
+ PyDict_Clear(bpy_pydriver_Dict);
+ Py_DECREF(bpy_pydriver_Dict);
+ bpy_pydriver_Dict = NULL;
+ }
+
+ PyGILState_Release(gilstate);
+
+ return;
+}
+
+/* error return function for BPY_eval_pydriver */
+static float pydriver_error(ChannelDriver *driver)
+{
+ if (bpy_pydriver_Dict) { /* free the global dict used by pydrivers */
+ PyDict_Clear(bpy_pydriver_Dict);
+ Py_DECREF(bpy_pydriver_Dict);
+ bpy_pydriver_Dict = NULL;
+ }
+
+ driver->flag |= DRIVER_FLAG_INVALID; /* py expression failed */
+ fprintf(stderr, "\nError in Driver: The following Python expression failed:\n\t'%s'\n\n", driver->expression);
+
+ PyErr_Print();
+
+ return 0.0f;
+}
+
+/* This evals py driver expressions, 'expr' is a Python expression that
+ * should evaluate to a float number, which is returned.
+ */
+float BPY_pydriver_eval (ChannelDriver *driver)
+{
+ PyObject *driver_vars=NULL;
+ PyObject *retval;
+ PyGILState_STATE gilstate;
+
+ DriverTarget *dtar;
+ float result = 0.0f; /* default return */
+ char *expr = NULL;
+ short targets_ok= 1;
+
+ /* sanity checks - should driver be executed? */
+ if ((driver == NULL) /*|| (G.f & G_DOSCRIPTLINKS)==0*/)
+ return result;
+
+ /* get the py expression to be evaluated */
+ expr = driver->expression;
+ if ((expr == NULL) || (expr[0]=='\0'))
+ return result;
+
+ gilstate = PyGILState_Ensure();
+
+ /* init global dictionary for py-driver evaluation settings */
+ if (!bpy_pydriver_Dict) {
+ if (bpy_pydriver_create_dict() != 0) {
+ fprintf(stderr, "Pydriver error: couldn't create Python dictionary");
+ PyGILState_Release(gilstate);
+ return result;
+ }
+ }
+
+ /* add target values to a py dictionary that we add to the drivers dict as 'd' */
+ driver_vars = PyDict_New(); // XXX do we need to decref this?
+ for (dtar= driver->targets.first; dtar; dtar= dtar->next) {
+ PyObject *driver_arg = NULL;
+ float tval = 0.0f;
+
+ /* try to get variable value */
+ tval= driver_get_target_value(driver, dtar);
+ driver_arg= PyFloat_FromDouble((double)tval);
+ if (driver_arg == NULL) continue;
+
+ /* try to add to dictionary */
+ if (PyDict_SetItemString(driver_vars, dtar->name, driver_arg)) {
+ /* this target failed */
+ if (targets_ok) {
+ /* first one - print some extra info for easier identification */
+ fprintf(stderr, "\nBPY_pydriver_eval() - Error while evaluating PyDriver:\n");
+ targets_ok= 0;
+ }
+
+ fprintf(stderr, "\tBPY_pydriver_eval() - couldn't add variable '%s' to namespace \n", dtar->name);
+ PyErr_Print();
+ }
+ }
+
+ /* execute expression to get a value */
+ retval = PyRun_String(expr, Py_eval_input, bpy_pydriver_Dict, driver_vars);
+
+ /* decref the driver vars first... */
+ Py_DECREF(driver_vars);
+
+ /* process the result */
+ if (retval == NULL) {
+ result = pydriver_error(driver);
+ PyGILState_Release(gilstate);
+ return result;
+ }
+
+ result = (float)PyFloat_AsDouble(retval);
+ Py_DECREF(retval);
+
+ if ((result == -1) && PyErr_Occurred()) {
+ result = pydriver_error(driver);
+ PyGILState_Release(gilstate);
+ return result;
+ }
+
+ /* all fine, make sure the "invalid expression" flag is cleared */
+ driver->flag &= ~DRIVER_FLAG_INVALID;
+
+ PyGILState_Release(gilstate);
+
+ return result;
+}
diff --git a/source/blender/python/intern/stubs.c b/source/blender/python/intern/stubs.c
index e438966f324..8d6b5ad9f19 100644
--- a/source/blender/python/intern/stubs.c
+++ b/source/blender/python/intern/stubs.c
@@ -33,8 +33,6 @@ void BPY_post_start_python() {}
void BPY_do_all_scripts() {}
void BPY_call_importloader() {}
void BPY_do_pyscript() {}
-void BPY_pydriver_eval() {}
-void BPY_pydriver_get_objects() {}
void BPY_clear_script() {}
//void BPY_free_compiled_text() {}
void BPY_pyconstraint_eval() {}