[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