== PyConstraints ==

I've added the ability for PyConstraints to define a function (doDriver) that is able to directly modify values of the owner/target, so that certain setups can be created reliably. Users should take note that this is against the basic concept of what a constraint does, and that under no circumstances may they set the values of any variables controlling the transforms. For more details, check out the information in the PyConstraint template script.

I've also updated PyConstraints to be aware of geometry targets. The script template has been updated with this information.
This commit is contained in:
Joshua Leung 2007-08-18 06:17:50 +00:00
parent 373ac35c71
commit 982d45162b
5 changed files with 170 additions and 10 deletions

@ -16,8 +16,8 @@ script_data = \
PyConstraints are text buffers that start with #BPYCONSTRAINT.
They must define a doConstraint function. The doConstraint
function is called with the world-space matrix of the parent object/posebone
as the first argument, the world-space matrix of the target object/posebone as
function is called with the matrix of the parent object/posebone
as the first argument, the matrix of the target object/posebone as
the second, and an ID property that's attached to the current constraint
instance. The function then must return a 4x4 Mathutils.Matrix() object.
@ -30,6 +30,13 @@ When a constraint needs to have a Target Object/Bone, the USE_TARGET line
below must be present. Also, if any special matrix creation needs to be performed
for the target, a doTarget function must also be defined.
Optionally, a doDriver function may be defined. This function is used
to get and/or modify settings of the owner and target, and as such, should
be used with caution. Under no circumstances, should you modify the transforms
of either the owner or the target in this function, as they will either have
no effect, or will result in other things not being updated correctly. Therefore,
it should be used sparringly.
<------- End removable description section -----------> """
# Add a licence here if you wish to re-distribute, we recommend the GPL
@ -64,12 +71,37 @@ def getSettings(idproperty):
# this optional function performs special actions that only require
# access to the target data - calculation of special information
# targetobject: (Object) wrapped data referring to the target object
# subtarget: (String/PoseChannel)
# - If the target is a PoseChannel in an armature, then this
# is a wrapped copy of that PoseChannel.
# - Otherwise, this field will either be an empty string or the
# name of the vertex group
# targetmatrix: (Matrix) matrix that will be used as the target matrix
# idprop: (IDProperties) wrapped data referring to this
# constraint instance's idproperties
"""
def doTarget (targetobject, subtarget, targetmatix, idproperty):
# return a 4x4 matrix (which acts as the matrix of the target)
return targetmatrix;
"""
# This optional function is used to modify/get values on the owner and the
# target for creating certain setups. It should be used sparingly
# ownerobject: (Object) wrapped data referring to the owning object
# subowner: (PoseChannel) wrapped data referring to the PoseChannel that
# owns the constraint (where applicable)
# target: (Object) wrapped data referring to the target
# subtarget: (String/PoseChannel)
# - If the target is a PoseChannel in an armature, then this
# is a wrapped copy of that PoseChannel.
# - Otherwise, this field will either be an empty string or the
# name of the vertex group
"""
def doDriver (ownerobject, subowner, targetobject, subtarget, idproperty):
pass;
"""
'''
new_text = bpy.data.texts.new('pyconstraint_template.py')

@ -125,10 +125,13 @@ int BPY_button_eval(char *expr, double *value)
return 0;
}
/* constraint.c */
/* PyConstraints - BPY_interface.c */
void BPY_pyconstraint_eval(struct bPythonConstraint *con, float ownermat[][4], float targetmat[][4])
{
}
void BPY_pyconstraint_driver(struct bPythonConstraint *con, struct bConstraintOb *cob, struct Object *target, char subtarget[])
{
}
int BPY_pyconstraint_targets(struct bPythonConstraint *con, float targetmat[][4])
{
return 0;

@ -2815,16 +2815,29 @@ void solve_constraints (ListBase *conlist, bConstraintOb *cob, float ctime)
/* value should have been set from IPO's/Constraint Channels already */
enf = con->enforce;
/* move owner into right space */
/* move owner matrix into right space */
constraint_mat_convertspace(cob->ob, cob->pchan, cob->matrix, CONSTRAINT_SPACE_WORLD, con->ownspace);
Mat4CpyMat4(oldmat, cob->matrix);
/* Get the target matrix - in right space to be used */
/* get the target matrix - in right space to be used */
ownerdata= ((cob->pchan)? (void *)cob->pchan : (void *)cob->ob);
get_constraint_target_matrix(con, cob->type, ownerdata, tarmat, ctime);
Mat4CpyMat4(oldmat, cob->matrix);
/* solve the constraint */
/* Special Hack for PyConstraints to be able to set settings on the owner and/or
* target. Technically, this violates the design of constraints (as constraints should
* only act on matrices to alter the final transform of an owner), but on the other
* hand, this makes PyConstraints more powerful as it enables certain setups to be created
* and work reliably.
*/
if (con->type == CONSTRAINT_TYPE_PYTHON) {
bPythonConstraint *pycon= (bPythonConstraint *)con->data;
/* as usual, the function for this is defined in BPY_interface.c */
BPY_pyconstraint_driver(pycon, cob, pycon->tar, pycon->subtarget);
}
/* Solve the constraint */
evaluate_constraint(con, cob->matrix, tarmat);
/* Interpolate the enforcement, to blend result of constraint into final owner transform */

@ -47,6 +47,7 @@ struct Script; /* BPI_script.h */
struct ScrArea; /* DNA_screen_types.h */
struct bScreen; /* DNA_screen_types.h */
struct bPythonConstraint; /* DNA_constraint_types.h */
struct bConstraintOb; /* BKE_constraint.h */
#ifdef __cplusplus
extern "C" {
#endif
@ -71,6 +72,7 @@ extern "C" {
void BPy_Free_DrawButtonsList(void);
void BPY_pyconstraint_eval(struct bPythonConstraint *con, float ownermat[][4], float targetmat[][4]);
void BPY_pyconstraint_driver(struct bPythonConstraint *con, struct bConstraintOb *cob, struct Object *target, char subtarget[]);
void BPY_pyconstraint_settings(void *arg1, void *arg2);
int BPY_pyconstraint_targets(struct bPythonConstraint *con, float targetmat[][4]);
int BPY_is_pyconstraint(struct Text *text);

@ -44,6 +44,7 @@
#include "BKE_library.h"
#include "BKE_object.h" /* during_scriptlink() */
#include "BKE_text.h"
#include "BKE_constraint.h" /* for bConstraintOb */
#include "DNA_curve_types.h" /* for struct IpoDriver */
#include "DNA_ID.h" /* ipo driver */
@ -1163,7 +1164,9 @@ int BPY_is_pyconstraint(Text *text)
return 0;
}
/* This evals py constraints. It is passed all the arguments the normal constraints recieve */
/* PyConstraints Evaluation Function (only called from evaluate_constraint)
* This function is responsible for modifying the ownermat that it is passed.
*/
void BPY_pyconstraint_eval(bPythonConstraint *con, float ownermat[][4], float targetmat[][4])
{
PyObject *srcmat, *tarmat, *idprop;
@ -1290,6 +1293,110 @@ void BPY_pyconstraint_eval(bPythonConstraint *con, float ownermat[][4], float ta
Py_XDECREF( retval );
}
/* PyConstraints 'Driver' Function
* This function is responsible for running any code that requires full access to the owner and the target
* It should be used sparringly, and only for doing 'hacks' which are not possible any other way.
*/
void BPY_pyconstraint_driver(bPythonConstraint *con, bConstraintOb *cob, Object *target, char subtarget[])
{
PyObject *owner, *subowner, *tar, *subtar;
PyObject *idprop;
PyObject *globals, *gval;
PyObject *pyargs, *retval;
if ( !con->text ) return;
if ( con->flag & PYCON_SCRIPTERROR) return;
globals = CreateGlobalDictionary();
owner = Object_CreatePyObject( cob->ob );
subowner = PyPoseBone_FromPosechannel( cob->pchan );
tar = Object_CreatePyObject( target );
if ( (target) && (target->type==OB_ARMATURE) ) {
bPoseChannel *pchan;
pchan = get_pose_channel( target->pose, subtarget );
subtar = PyPoseBone_FromPosechannel( pchan );
}
else
subtar = PyString_FromString(subtarget);
idprop = BPy_Wrap_IDProperty( NULL, con->prop, NULL);
/* since I can't remember what the armature weakrefs do, I'll just leave this here
commented out. This function was based on pydrivers, and it might still be relevent.
if( !setup_armature_weakrefs()){
fprintf( stderr, "Oops - weakref dict setup\n");
return result;
}
*/
retval = RunPython( con->text, globals );
if ( retval == NULL ) {
BPY_Err_Handle(con->text->id.name);
ReleaseGlobalDictionary( globals );
con->flag |= PYCON_SCRIPTERROR;
/* free temp objects */
Py_XDECREF( idprop );
Py_XDECREF( owner );
Py_XDECREF( subowner );
Py_XDECREF( tar );
Py_XDECREF( subtar );
return;
}
if (retval) {Py_XDECREF( retval );}
retval = NULL;
gval = PyDict_GetItemString(globals, "doDriver");
if (!gval) {
ReleaseGlobalDictionary( globals );
/* free temp objects */
Py_XDECREF( idprop );
Py_XDECREF( owner );
Py_XDECREF( subowner );
Py_XDECREF( tar );
Py_XDECREF( subtar );
return;
}
/* Now for the fun part! Try and find the functions we need. */
if (PyFunction_Check(gval) ) {
pyargs = Py_BuildValue("OOOOO", owner, subowner, tar, subtar, idprop);
retval = PyObject_CallObject(gval, pyargs);
Py_XDECREF( pyargs );
} else {
printf("ERROR: doDriver is supposed to be a function!\n");
con->flag |= PYCON_SCRIPTERROR;
ReleaseGlobalDictionary( globals );
Py_XDECREF( idprop );
Py_XDECREF( owner );
Py_XDECREF( subowner );
Py_XDECREF( tar );
Py_XDECREF( subtar );
return;
}
/* an error occurred while running the function? */
if (!retval) {
BPY_Err_Handle(con->text->id.name);
con->flag |= PYCON_SCRIPTERROR;
}
/* clear globals */
ReleaseGlobalDictionary( globals );
/* free temp objects */
Py_XDECREF( idprop );
Py_XDECREF( owner );
Py_XDECREF( subowner );
Py_XDECREF( tar );
Py_XDECREF( subtar );
}
/* This evaluates whether constraint uses targets, and also the target matrix
* Return code of 0 = doesn't use targets, 1 = uses targets + matrix set, -1 = uses targets + matrix not set
*/
@ -1310,10 +1417,13 @@ int BPY_pyconstraint_targets(bPythonConstraint *con, float targetmat[][4])
globals = CreateGlobalDictionary();
tar = Object_CreatePyObject( con->tar );
if ( con->tar )
if ( (con->tar) && (con->tar->type==OB_ARMATURE) ) {
bPoseChannel *pchan;
pchan = get_pose_channel( con->tar->pose, con->subtarget );
subtar = PyPoseBone_FromPosechannel( pchan );
}
else
pchan = NULL;
subtar = PyString_FromString(subtarget);
subtar = PyPoseBone_FromPosechannel( pchan );
tarmat = newMatrixObject( (float*)targetmat, 4, 4, Py_NEW );