[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [19810] branches/blender2.5/blender/source /blender: Drivers: "Scripted Expression" drivers (i.e.
Joshua Leung
aligorith at gmail.com
Mon Apr 20 11:17:43 CEST 2009
Revision: 19810
http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=19810
Author: aligorith
Date: 2009-04-20 11:17:43 +0200 (Mon, 20 Apr 2009)
Log Message:
-----------
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.)
Modified Paths:
--------------
branches/blender2.5/blender/source/blender/blenkernel/BKE_fcurve.h
branches/blender2.5/blender/source/blender/blenkernel/intern/anim_sys.c
branches/blender2.5/blender/source/blender/blenkernel/intern/depsgraph.c
branches/blender2.5/blender/source/blender/blenkernel/intern/fcurve.c
branches/blender2.5/blender/source/blender/python/BPY_extern.h
branches/blender2.5/blender/source/blender/python/intern/bpy_interface.c
branches/blender2.5/blender/source/blender/python/intern/stubs.c
Modified: branches/blender2.5/blender/source/blender/blenkernel/BKE_fcurve.h
===================================================================
--- branches/blender2.5/blender/source/blender/blenkernel/BKE_fcurve.h 2009-04-20 09:13:59 UTC (rev 19809)
+++ branches/blender2.5/blender/source/blender/blenkernel/BKE_fcurve.h 2009-04-20 09:17:43 UTC (rev 19810)
@@ -33,6 +33,8 @@
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):
Modified: branches/blender2.5/blender/source/blender/blenkernel/intern/anim_sys.c
===================================================================
--- branches/blender2.5/blender/source/blender/blenkernel/intern/anim_sys.c 2009-04-20 09:13:59 UTC (rev 19809)
+++ branches/blender2.5/blender/source/blender/blenkernel/intern/anim_sys.c 2009-04-20 09:17:43 UTC (rev 19810)
@@ -401,12 +401,12 @@
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 {
Modified: branches/blender2.5/blender/source/blender/blenkernel/intern/depsgraph.c
===================================================================
--- branches/blender2.5/blender/source/blender/blenkernel/intern/depsgraph.c 2009-04-20 09:13:59 UTC (rev 19809)
+++ branches/blender2.5/blender/source/blender/blenkernel/intern/depsgraph.c 2009-04-20 09:17:43 UTC (rev 19810)
@@ -326,6 +326,10 @@
/* 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");
}
Modified: branches/blender2.5/blender/source/blender/blenkernel/intern/fcurve.c
===================================================================
--- branches/blender2.5/blender/source/blender/blenkernel/intern/fcurve.c 2009-04-20 09:13:59 UTC (rev 19809)
+++ branches/blender2.5/blender/source/blender/blenkernel/intern/fcurve.c 2009-04-20 09:17:43 UTC (rev 19810)
@@ -650,7 +650,7 @@
/* 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;
Modified: branches/blender2.5/blender/source/blender/python/BPY_extern.h
===================================================================
--- branches/blender2.5/blender/source/blender/python/BPY_extern.h 2009-04-20 09:13:59 UTC (rev 19809)
+++ branches/blender2.5/blender/source/blender/python/BPY_extern.h 2009-04-20 09:17:43 UTC (rev 19810)
@@ -126,7 +126,6 @@
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);
Modified: branches/blender2.5/blender/source/blender/python/intern/bpy_interface.c
===================================================================
--- branches/blender2.5/blender/source/blender/python/intern/bpy_interface.c 2009-04-20 09:13:59 UTC (rev 19809)
+++ branches/blender2.5/blender/source/blender/python/intern/bpy_interface.c 2009-04-20 09:17:43 UTC (rev 19810)
@@ -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 @@
#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;
+}
Modified: branches/blender2.5/blender/source/blender/python/intern/stubs.c
===================================================================
--- branches/blender2.5/blender/source/blender/python/intern/stubs.c 2009-04-20 09:13:59 UTC (rev 19809)
+++ branches/blender2.5/blender/source/blender/python/intern/stubs.c 2009-04-20 09:17:43 UTC (rev 19810)
@@ -33,8 +33,6 @@
void BPY_do_all_scripts() {}
void BPY_call_importloader() {}
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list