diff options
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/BKE_fcurve.h | 34 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/anim_sys.c | 26 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/depsgraph.c | 49 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/fcurve.c | 549 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/ipo.c | 10 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/object.c | 17 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 46 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 21 | ||||
-rw-r--r-- | source/blender/editors/animation/drivers.c | 6 | ||||
-rw-r--r-- | source/blender/editors/space_graph/graph_buttons.c | 192 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner.c | 22 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_anim_types.h | 80 | ||||
-rw-r--r-- | source/blender/makesrna/RNA_access.h | 1 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_fcurve.c | 129 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_driver.c | 22 | ||||
-rw-r--r-- | source/blender/windowmanager/WM_types.h | 5 |
16 files changed, 853 insertions, 356 deletions
diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index fa6b8969edb..d3457a5f5ae 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -31,6 +31,7 @@ struct FCurve; struct FModifier; struct ChannelDriver; +struct DriverVar; struct DriverTarget; struct BezTriple; @@ -40,7 +41,6 @@ struct StructRNA; /* ************** Keyframe Tools ***************** */ -// XXX this stuff is defined in BKE_ipo.h too, so maybe skip for now? typedef struct CfraElem { struct CfraElem *next, *prev; float cfra; @@ -51,13 +51,39 @@ void bezt_add_to_cfra_elem(ListBase *lb, struct BezTriple *bezt); /* ************** F-Curve Drivers ***************** */ +/* With these iterators for convenience, the variables "tarIndex" and "dtar" can be + * accessed directly from the code using them, but it is not recommended that their + * values be changed to point at other slots... + */ + +/* convenience looper over ALL driver targets for a given variable (even the unused ones) */ +#define DRIVER_TARGETS_LOOPER(dvar) \ + { \ + DriverTarget *dtar= &dvar->targets[0]; \ + int tarIndex= 0; \ + for (; tarIndex < MAX_DRIVER_TARGETS; tarIndex++, dtar++) + +/* convenience looper over USED driver targets only */ +#define DRIVER_TARGETS_USED_LOOPER(dvar) \ + { \ + DriverTarget *dtar= &dvar->targets[0]; \ + int tarIndex= 0; \ + for (; tarIndex < dvar->num_targets; tarIndex++, dtar++) + +/* tidy up for driver targets loopers */ +#define DRIVER_TARGETS_LOOPER_END \ + } + +/* ---------------------- */ + void fcurve_free_driver(struct FCurve *fcu); 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); +void driver_free_variable(struct ChannelDriver *driver, struct DriverVar *dvar); +void driver_change_variable_type(struct DriverVar *dvar, int type); +struct DriverVar *driver_add_new_variable(struct ChannelDriver *driver); -float driver_get_target_value(struct ChannelDriver *driver, struct DriverTarget *dtar); +float driver_get_variable_value (struct ChannelDriver *driver, struct DriverVar *dvar); /* ************** F-Curve Modifiers *************** */ diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 4f96b031daa..4c63c64ccb9 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -319,16 +319,32 @@ static void fcurves_path_rename_fix (ID *owner_id, char *prefix, char *oldName, /* we need to check every curve... */ for (fcu= curves->first; fcu; fcu= fcu->next) { /* firstly, handle the F-Curve's own path */ - fcu->rna_path= rna_path_rename_fix(owner_id, prefix, oldName, newName, fcu->rna_path); + if (fcu->rna_path) + fcu->rna_path= rna_path_rename_fix(owner_id, prefix, oldName, newName, fcu->rna_path); /* driver? */ if (fcu->driver) { ChannelDriver *driver= fcu->driver; - DriverTarget *dtar; + DriverVar *dvar; - /* driver targets */ - for (dtar= driver->targets.first; dtar; dtar=dtar->next) { - dtar->rna_path= rna_path_rename_fix(dtar->id, prefix, oldName, newName, dtar->rna_path); + /* driver variables */ + for (dvar= driver->variables.first; dvar; dvar=dvar->next) { + /* all targets (even unused ones) */ + // XXX maybe we only need to modify the used ones, since the others can be manually fixed anyways + DRIVER_TARGETS_LOOPER(dvar) + { + /* rename RNA path */ + if (dtar->rna_path) + dtar->rna_path= rna_path_rename_fix(dtar->id, prefix, oldName, newName, dtar->rna_path); + + /* also fix the bone-name (if applicable) */ + if ( ((dtar->id) && (GS(dtar->id->name) == ID_OB)) && + (dtar->pchan_name[0]) && (strcmp(oldName, dtar->pchan_name)==0) ) + { + BLI_strncpy(dtar->pchan_name, newName, sizeof(dtar->pchan_name)); + } + } + DRIVER_TARGETS_LOOPER_END } } } diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index 2f08abd82b3..e664dd01f4d 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -68,6 +68,7 @@ #include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_effect.h" +#include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_group.h" #include "BKE_key.h" @@ -316,28 +317,38 @@ static void dag_add_driver_relation(AnimData *adt, DagForest *dag, DagNode *node for (fcu= adt->drivers.first; fcu; fcu= fcu->next) { ChannelDriver *driver= fcu->driver; - DriverTarget *dtar; + DriverVar *dvar; - /* loop over targets, adding relationships as appropriate */ - for (dtar= driver->targets.first; dtar; dtar= dtar->next) { - if (dtar->id) { - if (GS(dtar->id->name)==ID_OB) { - Object *ob= (Object *)dtar->id; - - /* normal channel-drives-channel */ - node1 = dag_get_node(dag, dtar->id); - - /* check if bone... */ - if ((ob->type==OB_ARMATURE) && dtar->rna_path && strstr(dtar->rna_path, "pose.bones[")) - 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"); + /* loop over variables to get the target relationships */ + for (dvar= driver->variables.first; dvar; dvar= dvar->next) { + /* only used targets */ + DRIVER_TARGETS_USED_LOOPER(dvar) + { + if (dtar->id) { + // FIXME: other data types need to be added here so that they can work! + if (GS(dtar->id->name)==ID_OB) { + Object *ob= (Object *)dtar->id; + + /* normal channel-drives-channel */ + node1 = dag_get_node(dag, dtar->id); + + /* check if bone... */ + if ((ob->type==OB_ARMATURE) && + ( ((dtar->rna_path) && strstr(dtar->rna_path, "pose.bones[")) || + ((dtar->flag & DTAR_FLAG_STRUCT_REF) && (dtar->pchan_name[0])) )) + { + 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"); + } } } + DRIVER_TARGETS_LOOPER_END } } } diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index d5e033652a8..9e84727fbf1 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -41,6 +41,7 @@ #include "MEM_guardedalloc.h" #include "DNA_anim_types.h" +#include "DNA_object_types.h" #include "BLI_blenlib.h" #include "BLI_math.h" @@ -48,6 +49,7 @@ #include "BKE_fcurve.h" #include "BKE_animsys.h" +#include "BKE_action.h" #include "BKE_curve.h" #include "BKE_global.h" @@ -735,108 +737,31 @@ short test_time_fcurve (FCurve *fcu) /* ***************************** Drivers ********************************* */ -/* Driver API --------------------------------- */ +/* Driver Variables --------------------------- */ -/* This frees the driver target itself */ -void driver_free_target (ChannelDriver *driver, DriverTarget *dtar) -{ - /* sanity checks */ - if (dtar == NULL) - return; - - /* free target vars */ - if (dtar->rna_path) - MEM_freeN(dtar->rna_path); +/* TypeInfo for Driver Variables (dvti) */ +typedef struct DriverVarTypeInfo { + /* evaluation callback */ + float (*get_value)(ChannelDriver *driver, DriverVar *dvar); - /* remove the target from the driver */ - if (driver) - BLI_freelinkN(&driver->targets, dtar); - else - MEM_freeN(dtar); -} + /* allocation of target slots */ + int num_targets; /* number of target slots required */ + char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots */ + int target_flags[MAX_DRIVER_TARGETS]; /* flags defining the requirements for each slot */ +} DriverVarTypeInfo; -/* Add a new driver target variable */ -DriverTarget *driver_add_new_target (ChannelDriver *driver) -{ - DriverTarget *dtar; - - /* sanity checks */ - if (driver == NULL) - return NULL; - - /* make a new target */ - dtar= MEM_callocN(sizeof(DriverTarget), "DriverTarget"); - BLI_addtail(&driver->targets, dtar); - - /* make the default ID-type ID_OB, since most driver targets refer to objects */ - dtar->idtype= ID_OB; - - /* give the target a 'unique' name */ - strcpy(dtar->name, "var"); - BLI_uniquename(&driver->targets, dtar, "var", '_', offsetof(DriverTarget, name), 64); - - /* return the target */ - return dtar; -} - -/* This frees the driver itself */ -void fcurve_free_driver(FCurve *fcu) -{ - ChannelDriver *driver; - DriverTarget *dtar, *dtarn; - - /* sanity checks */ - if ELEM(NULL, fcu, fcu->driver) - return; - driver= fcu->driver; - - /* free driver targets */ - for (dtar= driver->targets.first; dtar; dtar= dtarn) { - dtarn= dtar->next; - driver_free_target(driver, dtar); - } - -#ifndef DISABLE_PYTHON - if(driver->expr_comp) - BPY_DECREF(driver->expr_comp); -#endif - - /* free driver itself, then set F-Curve's point to this to NULL (as the curve may still be used) */ - MEM_freeN(driver); - fcu->driver= NULL; -} - -/* This makes a copy of the given driver */ -ChannelDriver *fcurve_copy_driver (ChannelDriver *driver) -{ - ChannelDriver *ndriver; - DriverTarget *dtar; - - /* sanity checks */ - if (driver == NULL) - return NULL; - - /* copy all data */ - ndriver= MEM_dupallocN(driver); - - /* copy targets */ - ndriver->targets.first= ndriver->targets.last= NULL; - BLI_duplicatelist(&ndriver->targets, &driver->targets); +/* Macro to begin definitions */ +#define BEGIN_DVAR_TYPEDEF(type) \ + { - for (dtar= ndriver->targets.first; dtar; dtar= dtar->next) { - /* make a copy of target's rna path if available */ - if (dtar->rna_path) - dtar->rna_path = MEM_dupallocN(dtar->rna_path); +/* Macro to end definitions */ +#define END_DVAR_TYPEDEF \ } - - /* return the new driver */ - return ndriver; -} -/* Driver Evaluation -------------------------- */ +/* ......... */ /* Helper function to obtain a value using RNA from the specified source (for evaluating drivers) */ -float driver_get_target_value (ChannelDriver *driver, DriverTarget *dtar) +static float dtar_get_prop_val (ChannelDriver *driver, DriverTarget *dtar) { PointerRNA id_ptr, ptr; PropertyRNA *prop; @@ -853,22 +778,18 @@ float driver_get_target_value (ChannelDriver *driver, DriverTarget *dtar) RNA_id_pointer_create(dtar->id, &id_ptr); id= dtar->id; path= dtar->rna_path; - index= dtar->array_index; /* error check for missing pointer... */ + // TODO: tag the specific target too as having issues if (id == NULL) { - printf("Error: driver doesn't have any valid target to use \n"); - if (G.f & G_DEBUG) printf("\tpath = %s [%d] \n", path, index); + printf("Error: driver has an invalid target to use \n"); + if (G.f & G_DEBUG) printf("\tpath = %s\n", path); driver->flag |= DRIVER_FLAG_INVALID; return 0.0f; } /* get property to read from, and get value as appropriate */ if (RNA_path_resolve_full(&id_ptr, path, &ptr, &prop, &index)) { - /* for now, if there is no valid index, fall back to the array-index specified separately */ - if (index == -1) - index= dtar->array_index; - switch (RNA_property_type(prop)) { case PROP_BOOLEAN: if (RNA_property_array_length(&ptr, prop)) @@ -906,39 +827,326 @@ float driver_get_target_value (ChannelDriver *driver, DriverTarget *dtar) return value; } -/* Get two PoseChannels from the targets of the given Driver */ -static void driver_get_target_pchans2 (ChannelDriver *driver, bPoseChannel **pchan1, bPoseChannel **pchan2) +/* Helper function to obtain a pointer to a Pose Channel (for evaluating drivers) */ +static bPoseChannel *dtar_get_pchan_ptr (ChannelDriver *driver, DriverTarget *dtar) { - DriverTarget *dtar; - short i = 0; + /* sanity check */ + if ELEM(NULL, driver, dtar) + return NULL; + + /* check if the ID here is a valid object */ + if ((dtar->id) && GS(dtar->id->name)) { + Object *ob= (Object *)dtar->id; + + /* get pose, and subsequently, posechannel */ + return get_pose_channel(ob->pose, dtar->pchan_name); + } + else { + /* cannot find a posechannel this way */ + return NULL; + } +} + +/* ......... */ + +/* evaluate 'single prop' driver variable */ +static float dvar_eval_singleProp (ChannelDriver *driver, DriverVar *dvar) +{ + /* just evaluate the first target slot */ + return dtar_get_prop_val(driver, &dvar->targets[0]); +} + +/* evaluate 'rotation difference' driver variable */ +static float dvar_eval_rotDiff (ChannelDriver *driver, DriverVar *dvar) +{ + bPoseChannel *pchan, *pchan2; + float q1[4], q2[4], quat[4], angle; - /* before doing anything */ - *pchan1= NULL; - *pchan2= NULL; + /* get pose channels, and check if we've got two */ + pchan= dtar_get_pchan_ptr(driver, &dvar->targets[0]); + pchan2= dtar_get_pchan_ptr(driver, &dvar->targets[1]); - /* only take the first two targets */ - for (dtar= driver->targets.first; (dtar) && (i < 2); dtar=dtar->next, i++) { - PointerRNA id_ptr, ptr; - PropertyRNA *prop; + if (ELEM(NULL, pchan, pchan2)) { + /* disable this driver, since it doesn't work correctly... */ + driver->flag |= DRIVER_FLAG_INVALID; - /* get RNA-pointer for the ID-block given in target */ - if (dtar->id) - RNA_id_pointer_create(dtar->id, &id_ptr); - else - continue; + /* check what the error was */ + if ((pchan == NULL) && (pchan2 == NULL)) + printf("Driver Evaluation Error: Rotational difference failed - first 2 targets invalid \n"); + else if (pchan == NULL) + printf("Driver Evaluation Error: Rotational difference failed - first target not valid PoseChannel \n"); + else if (pchan2 == NULL) + printf("Driver Evaluation Error: Rotational difference failed - second target not valid PoseChannel \n"); + + /* stop here... */ + return 0.0f; + } + + /* use the final posed locations */ + mat4_to_quat(q1, pchan->pose_mat); + mat4_to_quat(q2, pchan2->pose_mat); + + invert_qt(q1); + mul_qt_qtqt(quat, q1, q2); + angle = 2.0f * (saacos(quat[0])); + angle= ABS(angle); + + return (angle > M_PI) ? (float)((2.0f * M_PI) - angle) : (float)(angle); +} + +/* evaluate 'location difference' driver variable */ +// TODO: this needs to take into account space conversions... +static float dvar_eval_locDiff (ChannelDriver *driver, DriverVar *dvar) +{ + float loc1[3] = {0.0f,0.0f,0.0f}; + float loc2[3] = {0.0f,0.0f,0.0f}; + + /* get two location values */ + // NOTE: for now, these are all just worldspace + DRIVER_TARGETS_USED_LOOPER(dvar) + { + /* get pointer to loc values to store in */ + Object *ob= (Object *)dtar->id; + bPoseChannel *pchan; + float tmp_loc[3]; - /* resolve path so that we have pointer to the right posechannel */ - if (RNA_path_resolve(&id_ptr, dtar->rna_path, &ptr, &prop)) { - /* is pointer valid (i.e. pointing to an actual posechannel */ - if ((ptr.type == &RNA_PoseBone) && (ptr.data)) { - /* first or second target? */ - if (i) - *pchan1= ptr.data; - else - *pchan2= ptr.data; - } + /* check if this target has valid data */ + if ((ob == NULL) || (GS(dtar->id->name) != ID_OB)) { + /* invalid target, so will not have enough targets */ + driver->flag |= DRIVER_FLAG_INVALID; + return 0.0f; + } + + /* try to get posechannel */ + pchan= get_pose_channel(ob->pose, dtar->pchan_name); + + /* check if object or bone */ + if (pchan) { + /* bone - need to convert to worldspace */ + VECCOPY(tmp_loc, pchan->pose_head); + mul_m4_v3(ob->obmat, tmp_loc); + } + else { + /* object, already in worldspace */ + VECCOPY(tmp_loc, ob->obmat[3]); + } + + /* copy the location to the right place */ + if (tarIndex) { + VECCOPY(loc2, tmp_loc); + } + else { + VECCOPY(loc1, tmp_loc); + } + } + DRIVER_TARGETS_LOOPER_END + + + /* if we're still here, there should now be two targets to use, + * so just take the length of the vector between these points + */ + return len_v3v3(loc1, loc2); +} + +/* ......... */ + +/* Table of Driver Varaiable Type Info Data */ +DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = { + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) + dvar_eval_singleProp, /* eval callback */ + 1, /* number of targets used */ + {"Property"}, /* UI names for targets */ + {0} /* flags */ + END_DVAR_TYPEDEF, + + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) + dvar_eval_rotDiff, /* eval callback */ + 2, /* number of targets used */ + {"Bone 1", "Bone 2"}, /* UI names for targets */ + {DTAR_FLAG_STRUCT_REF|DTAR_FLAG_ID_OB_ONLY, DTAR_FLAG_STRUCT_REF|DTAR_FLAG_ID_OB_ONLY} /* flags */ + END_DVAR_TYPEDEF, + + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) + dvar_eval_locDiff, /* eval callback */ + 2, /* number of targets used */ + {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ + {DTAR_FLAG_STRUCT_REF|DTAR_FLAG_ID_OB_ONLY, DTAR_FLAG_STRUCT_REF|DTAR_FLAG_ID_OB_ONLY} /* flags */ + END_DVAR_TYPEDEF +}; + +/* Get driver variable typeinfo */ +DriverVarTypeInfo *get_dvar_typeinfo (int type) +{ + /* check if valid type */ + if ((type >= 0) && (type < MAX_DVAR_TYPES)) + return &dvar_types[type]; + else + return NULL; +} + +/* Driver API --------------------------------- */ + +/* This frees the driver variable itself */ +void driver_free_variable (ChannelDriver *driver, DriverVar *dvar) +{ + /* sanity checks */ + if (dvar == NULL) + return; + + /* free target vars + * - need to go over all of them, not just up to the ones that are used + * currently, since there may be some lingering RNA paths from + * previous users needing freeing + */ + DRIVER_TARGETS_LOOPER(dvar) + { + /* free RNA path if applicable */ + if (dtar->rna_path) + MEM_freeN(dtar->rna_path); + } + DRIVER_TARGETS_LOOPER_END + + /* remove the variable from the driver */ + if (driver) + BLI_freelinkN(&driver->variables, dvar); + else + MEM_freeN(dvar); +} + +/* Change the type of driver variable */ +void driver_change_variable_type (DriverVar *dvar, int type) +{ + DriverVarTypeInfo *dvti= get_dvar_typeinfo(type); + + /* sanity check */ + if (ELEM(NULL, dvar, dvti)) + return; + + /* set the new settings */ + dvar->type= type; + dvar->num_targets= dvti->num_targets; + + /* make changes to the targets based on the defines for these types + * NOTE: only need to make sure the ones we're using here are valid... + */ + DRIVER_TARGETS_USED_LOOPER(dvar) + { + int flags = dvti->target_flags[tarIndex]; + + /* store the flags */ + dtar->flag = flags; + + /* object ID types only, or idtype not yet initialised*/ + if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) + dtar->idtype= ID_OB; + } + DRIVER_TARGETS_LOOPER_END +} + +/* Add a new driver variable */ +DriverVar *driver_add_new_variable (ChannelDriver *driver) +{ + DriverVar *dvar; + + /* sanity checks */ + if (driver == NULL) + return NULL; + + /* make a new variable */ + dvar= MEM_callocN(sizeof(DriverVar), "DriverVar"); + BLI_addtail(&driver->variables, dvar); + + /* give the variable a 'unique' name */ + strcpy(dvar->name, "var"); + BLI_uniquename(&driver->variables, dvar, "var", '_', offsetof(DriverVar, name), 64); + + /* set the default type to 'single prop' */ + driver_change_variable_type(dvar, DVAR_TYPE_SINGLE_PROP); + + /* return the target */ + return dvar; +} + +/* This frees the driver itself */ +void fcurve_free_driver(FCurve *fcu) +{ + ChannelDriver *driver; + DriverVar *dvar, *dvarn; + + /* sanity checks */ + if ELEM(NULL, fcu, fcu->driver) + return; + driver= fcu->driver; + + /* free driver targets */ + for (dvar= driver->variables.first; dvar; dvar= dvarn) { + dvarn= dvar->next; + driver_free_variable(driver, dvar); + } + +#ifndef DISABLE_PYTHON + /* free compiled driver expression */ + if (driver->expr_comp) + BPY_DECREF(driver->expr_comp); +#endif + + /* free driver itself, then set F-Curve's point to this to NULL (as the curve may still be used) */ + MEM_freeN(driver); + fcu->driver= NULL; +} + +/* This makes a copy of the given driver */ +ChannelDriver *fcurve_copy_driver (ChannelDriver *driver) +{ + ChannelDriver *ndriver; + DriverVar *dvar; + + /* sanity checks */ + if (driver == NULL) + return NULL; + + /* copy all data */ + ndriver= MEM_dupallocN(driver); + + /* copy variables */ + ndriver->variables.first= ndriver->variables.last= NULL; + BLI_duplicatelist(&ndriver->variables, &driver->variables); + + for (dvar= ndriver->variables.first; dvar; dvar= dvar->next) { + /* need to go over all targets so that we don't leave any dangling paths */ + DRIVER_TARGETS_LOOPER(dvar) + { + /* make a copy of target's rna path if available */ + if (dtar->rna_path) + dtar->rna_path = MEM_dupallocN(dtar->rna_path); } + DRIVER_TARGETS_LOOPER_END } + + /* return the new driver */ + return ndriver; +} + +/* Driver Evaluation -------------------------- */ + +/* Evaluate a Driver Variable to get a value that contributes to the final */ +float driver_get_variable_value (ChannelDriver *driver, DriverVar *dvar) +{ + DriverVarTypeInfo *dvti; + + /* sanity check */ + if (ELEM(NULL, driver, dvar)) + return 0.0f; + + /* call the relevant callbacks to get the variable value + * using the variable type info + */ + dvti= get_dvar_typeinfo(dvar->type); + + if (dvti && dvti->get_value) + return dvti->get_value(driver, dvar); + else + return 0.0f; } /* Evaluate an Channel-Driver to get a 'time' value to use instead of "evaltime" @@ -947,22 +1155,21 @@ static void driver_get_target_pchans2 (ChannelDriver *driver, bPoseChannel **pch */ static float evaluate_driver (ChannelDriver *driver, float evaltime) { - DriverTarget *dtar; + DriverVar *dvar; /* check if driver can be evaluated */ if (driver->flag & DRIVER_FLAG_INVALID) return 0.0f; - // TODO: the flags for individual targets need to be used too for more fine-grained support... switch (driver->type) { case DRIVER_TYPE_AVERAGE: /* average values of driver targets */ case DRIVER_TYPE_SUM: /* sum values of driver targets */ { - /* check how many targets there are first (i.e. just one?) */ - if (driver->targets.first == driver->targets.last) { + /* check how many variables there are first (i.e. just one?) */ + if (driver->variables.first == driver->variables.last) { /* just one target, so just use that */ - dtar= driver->targets.first; - return driver_get_target_value(driver, dtar); + dvar= driver->variables.first; + return driver_get_variable_value(driver, dvar); } else { /* more than one target, so average the values of the targets */ @@ -970,12 +1177,12 @@ static float evaluate_driver (ChannelDriver *driver, float evaltime) float value = 0.0f; /* loop through targets, adding (hopefully we don't get any overflow!) */ - for (dtar= driver->targets.first; dtar; dtar=dtar->next) { - value += driver_get_target_value(driver, dtar); + for (dvar= driver->variables.first; dvar; dvar=dvar->next) { + value += driver_get_variable_value(driver, dvar); tot++; } - /* return the average of these */ + /* perform operations on the total if appropriate */ if (driver->type == DRIVER_TYPE_AVERAGE) return (value / (float)tot); else @@ -984,6 +1191,39 @@ static float evaluate_driver (ChannelDriver *driver, float evaltime) } } break; + + case DRIVER_TYPE_MIN: /* smallest value */ + case DRIVER_TYPE_MAX: /* largest value */ + { + float value = 0.0f; + + /* loop through the variables, getting the values and comparing them to existing ones */ + for (dvar= driver->variables.first; dvar; dvar= dvar->next) { + /* get value */ + float tmp_val= driver_get_variable_value(driver, dvar); + + /* store this value if appropriate */ + if (dvar->prev) { + /* check if greater/smaller than the baseline */ + if (driver->type == DRIVER_TYPE_MAX) { + /* max? */ + if (tmp_val > value) + value= tmp_val; + } + else { + /* min? */ + if (tmp_val < value) + value= tmp_val; + } + } + else { + /* first item - make this the baseline for comparisons */ + value= tmp_val; + } + } + } + break; + case DRIVER_TYPE_PYTHON: /* expression */ { #ifndef DISABLE_PYTHON @@ -1001,43 +1241,6 @@ static float evaluate_driver (ChannelDriver *driver, float evaltime) #endif /* DISABLE_PYTHON*/ } break; - - - case DRIVER_TYPE_ROTDIFF: /* difference of rotations of 2 bones (should ideally be in same armature) */ - { - bPoseChannel *pchan, *pchan2; - float q1[4], q2[4], quat[4], angle; - - /* get pose channels, and check if we've got two */ - driver_get_target_pchans2(driver, &pchan, &pchan2); - if (ELEM(NULL, pchan, pchan2)) { - /* disable this driver, since it doesn't work correctly... */ - driver->flag |= DRIVER_FLAG_INVALID; - - /* check what the error was */ - if ((pchan == NULL) && (pchan2 == NULL)) - printf("Driver Evaluation Error: Rotational difference failed - first 2 targets invalid \n"); - else if (pchan == NULL) - printf("Driver Evaluation Error: Rotational difference failed - first target not valid PoseChannel \n"); - else if (pchan2 == NULL) - printf("Driver Evaluation Error: Rotational difference failed - second target not valid PoseChannel \n"); - - /* stop here... */ - return 0.0f; - } - - /* use the final posed locations */ - mat4_to_quat(q1, pchan->pose_mat); - mat4_to_quat(q2, pchan2->pose_mat); - - invert_qt(q1); - mul_qt_qtqt(quat, q1, q2); - angle = 2.0f * (saacos(quat[0])); - angle= ABS(angle); - - return (angle > M_PI) ? (float)((2.0f * M_PI) - angle) : (float)(angle); - } - break; default: { diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 6fc3fc547df..988bd429185 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -969,7 +969,6 @@ static char *get_rna_access (int blocktype, int adrcode, char actname[], char co static ChannelDriver *idriver_to_cdriver (IpoDriver *idriver) { ChannelDriver *cdriver; - DriverTarget *dtar=NULL, *dtar2=NULL; /* allocate memory for new driver */ cdriver= MEM_callocN(sizeof(ChannelDriver), "ChannelDriver"); @@ -981,7 +980,10 @@ static ChannelDriver *idriver_to_cdriver (IpoDriver *idriver) cdriver->type = DRIVER_TYPE_PYTHON; strcpy(cdriver->expression, idriver->name); // XXX is this safe? } +#if 0 // XXX needs changes for the new system else { + DriverTarget *dtar=NULL, *dtar2=NULL; + /* what to store depends on the 'blocktype' (ID_OB or ID_PO - object or posechannel) */ if (idriver->blocktype == ID_AR) { /* ID_PO */ @@ -1058,6 +1060,7 @@ static ChannelDriver *idriver_to_cdriver (IpoDriver *idriver) dtar->rna_path= get_rna_access(ID_OB, idriver->adrcode, NULL, NULL, &dtar->array_index); } } +#endif // XXX fixme /* return the new one */ return cdriver; @@ -1288,8 +1291,9 @@ static void icu_to_fcurves (ListBase *groups, ListBase *list, IpoCurve *icu, cha * - need to go from degrees to radians... * - there's only really 1 target to worry about */ - if (fcu->driver && fcu->driver->targets.first) { - DriverTarget *dtar= fcu->driver->targets.first; + if (fcu->driver && fcu->driver->variables.first) { + DriverVar *dvar= fcu->driver->variables.first; + DriverTarget *dtar= &dvar->targets[0]; /* since drivers could only be for objects, we should just check for 'rotation' being * in the name of the path given diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 0f9198df263..762ad2432b0 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1500,13 +1500,18 @@ void object_make_proxy(Object *ob, Object *target, Object *gob) for (fcu= ob->adt->drivers.first; fcu; fcu= fcu->next) { ChannelDriver *driver= fcu->driver; - DriverTarget *dtar; + DriverVar *dvar; - for (dtar= driver->targets.first; dtar; dtar= dtar->next) { - if ((Object *)dtar->id == target) - dtar->id= (ID *)ob; - else - id_lib_extern((ID *)dtar->id); + for (dvar= driver->variables.first; dvar; dvar= dvar->next) { + /* all drivers */ + DRIVER_TARGETS_LOOPER(dvar) + { + if ((Object *)dtar->id == target) + dtar->id= (ID *)ob; + else + id_lib_extern((ID *)dtar->id); + } + DRIVER_TARGETS_LOOPER_END } } } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 3e35250bd74..1eff6c8f8e3 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1699,10 +1699,19 @@ static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list) /* driver data */ if (fcu->driver) { ChannelDriver *driver= fcu->driver; - DriverTarget *dtar; + DriverVar *dvar; - for (dtar= driver->targets.first; dtar; dtar= dtar->next) - dtar->id= newlibadr(fd, id->lib, dtar->id); + for (dvar= driver->variables.first; dvar; dvar= dvar->next) { + DRIVER_TARGETS_LOOPER(dvar) + { + /* only relink if still used */ + if (tarIndex < dvar->num_targets) + dtar->id= newlibadr(fd, id->lib, dtar->id); + else + dtar->id= NULL; + } + DRIVER_TARGETS_LOOPER_END + } } /* modifiers */ @@ -1770,12 +1779,21 @@ static void direct_link_fcurves(FileData *fd, ListBase *list) fcu->driver= newdataadr(fd, fcu->driver); if (fcu->driver) { ChannelDriver *driver= fcu->driver; - DriverTarget *dtar; + DriverVar *dvar; - /* relink targets and their paths */ - link_list(fd, &driver->targets); - for (dtar= driver->targets.first; dtar; dtar= dtar->next) - dtar->rna_path= newdataadr(fd, dtar->rna_path); + /* relink variables, targets and their paths */ + link_list(fd, &driver->variables); + for (dvar= driver->variables.first; dvar; dvar= dvar->next) { + DRIVER_TARGETS_LOOPER(dvar) + { + /* only relink the targets being used */ + if (tarIndex < dvar->num_targets) + dtar->rna_path= newdataadr(fd, dtar->rna_path); + else + dtar->rna_path= NULL; + } + DRIVER_TARGETS_LOOPER_END + } } /* modifiers */ @@ -10764,10 +10782,16 @@ static void expand_fcurves(FileData *fd, Main *mainvar, ListBase *list) /* Driver targets if there is a driver */ if (fcu->driver) { ChannelDriver *driver= fcu->driver; - DriverTarget *dtar; + DriverVar *dvar; - for (dtar= driver->targets.first; dtar; dtar= dtar->next) - expand_doit(fd, mainvar, dtar->id); + for (dvar= driver->variables.first; dvar; dvar= dvar->next) { + DRIVER_TARGETS_LOOPER(dvar) + { + // TODO: only expand those that are going to get used? + expand_doit(fd, mainvar, dtar->id); + } + DRIVER_TARGETS_LOOPER_END + } } /* F-Curve Modifiers */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 98d3dc5188a..061ed71d77e 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -932,22 +932,27 @@ static void write_fcurves(WriteData *wd, ListBase *fcurves) /* driver data */ if (fcu->driver) { ChannelDriver *driver= fcu->driver; - DriverTarget *dtar; + DriverVar *dvar; /* don't save compiled python bytecode */ void *expr_comp= driver->expr_comp; driver->expr_comp= NULL; - + writestruct(wd, DATA, "ChannelDriver", 1, driver); driver->expr_comp= expr_comp; /* restore */ - - /* targets */ - for (dtar= driver->targets.first; dtar; dtar= dtar->next) { - writestruct(wd, DATA, "DriverTarget", 1, dtar); + + + /* variables */ + for (dvar= driver->variables.first; dvar; dvar= dvar->next) { + writestruct(wd, DATA, "DriverVar", 1, dvar); - if (dtar->rna_path) - writedata(wd, DATA, strlen(dtar->rna_path)+1, dtar->rna_path); + DRIVER_TARGETS_USED_LOOPER(dvar) + { + if (dtar->rna_path) + writedata(wd, DATA, strlen(dtar->rna_path)+1, dtar->rna_path); + } + DRIVER_TARGETS_LOOPER_END } } diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index 59e52c0d489..b816ffbe905 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -404,8 +404,7 @@ static int add_driver_button_exec (bContext *C, wmOperator *op) /* send updates */ DAG_ids_flush_update(0); - /* for now, only send ND_KEYS for KeyingSets */ - WM_event_add_notifier(C, ND_KEYS, NULL); // XXX + WM_event_add_notifier(C, NC_ANIMATION|ND_FCURVES_ORDER, NULL); // XXX } return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED; @@ -468,8 +467,7 @@ static int remove_driver_button_exec (bContext *C, wmOperator *op) /* send updates */ DAG_ids_flush_update(0); - /* for now, only send ND_KEYS for KeyingSets */ - WM_event_add_notifier(C, ND_KEYS, NULL); // XXX + WM_event_add_notifier(C, NC_ANIMATION|ND_FCURVES_ORDER, NULL); // XXX } return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED; diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 3faba9bbba7..c42dfd0015d 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -264,18 +264,18 @@ static void driver_add_var_cb (bContext *C, void *driver_v, void *dummy_v) { ChannelDriver *driver= (ChannelDriver *)driver_v; - /* add a new var */ - driver_add_new_target(driver); + /* add a new variable */ + driver_add_new_variable(driver); } /* callback to remove target variable from active driver */ -static void driver_delete_var_cb (bContext *C, void *driver_v, void *dtar_v) +static void driver_delete_var_cb (bContext *C, void *driver_v, void *dvar_v) { ChannelDriver *driver= (ChannelDriver *)driver_v; - DriverTarget *dtar= (DriverTarget *)dtar_v; + DriverVar *dvar= (DriverVar *)dvar_v; - /* remove the active target */ - driver_free_target(driver, dtar); + /* remove the active variable */ + driver_free_variable(driver, dvar); } /* callback to reset the driver's flags */ @@ -301,13 +301,115 @@ static int graph_panel_drivers_poll(const bContext *C, PanelType *pt) } +/* settings for 'single property' driver variable type */ +static void graph_panel_driverVar__singleProp(const bContext *C, uiLayout *layout, ID *id, DriverVar *dvar) +{ + DriverTarget *dtar= &dvar->targets[0]; + PointerRNA dtar_ptr; + uiLayout *row, *col; + uiBlock *block; + + /* initialise RNA pointer to the target */ + RNA_pointer_create(id, &RNA_DriverTarget, dtar, &dtar_ptr); + + /* Target ID */ + row= uiLayoutRow(layout, 0); + uiTemplateAnyID(row, (bContext *)C, &dtar_ptr, "id", "id_type", "Value:"); + + /* Target Property */ + // TODO: make this less technical... + if (dtar->id) { + PointerRNA root_ptr; + + /* get pointer for resolving the property selected */ + RNA_id_pointer_create(dtar->id, &root_ptr); + + col= uiLayoutColumn(layout, 1); + block= uiLayoutGetBlock(col); + /* rna path */ + uiTemplatePathBuilder(col, (bContext *)C, &dtar_ptr, "data_path", &root_ptr, "Path"); + } +} + +/* settings for 'rotation difference' driver variable type */ +static void graph_panel_driverVar__rotDiff(const bContext *C, uiLayout *layout, ID *id, DriverVar *dvar) +{ + DriverTarget *dtar= &dvar->targets[0]; + DriverTarget *dtar2= &dvar->targets[1]; + Object *ob1 = (Object *)dtar->id; + Object *ob2 = (Object *)dtar2->id; + PointerRNA dtar_ptr, dtar2_ptr; + uiLayout *col; + + /* initialise RNA pointer to the target */ + RNA_pointer_create(id, &RNA_DriverTarget, dtar, &dtar_ptr); + RNA_pointer_create(id, &RNA_DriverTarget, dtar2, &dtar2_ptr); + + /* Bone 1 */ + col= uiLayoutColumn(layout, 1); + uiTemplateAnyID(col, (bContext *)C, &dtar_ptr, "id", "id_type", "Bone 1:"); + + if (dtar->id && ob1->pose) { + PointerRNA tar_ptr; + + RNA_pointer_create(dtar2->id, &RNA_Pose, ob1->pose, &tar_ptr); + uiItemPointerR(col, "", ICON_BONE_DATA, &dtar_ptr, "bone_target", &tar_ptr, "bones"); + } + + col= uiLayoutColumn(layout, 1); + uiTemplateAnyID(col, (bContext *)C, &dtar2_ptr, "id", "id_type", "Bone 2:"); + + if (dtar2->id && ob2->pose) { + PointerRNA tar_ptr; + + RNA_pointer_create(dtar2->id, &RNA_Pose, ob2->pose, &tar_ptr); + uiItemPointerR(col, "", ICON_BONE_DATA, &dtar2_ptr, "bone_target", &tar_ptr, "bones"); + } +} + +/* settings for 'rotation difference' driver variable type */ +static void graph_panel_driverVar__locDiff(const bContext *C, uiLayout *layout, ID *id, DriverVar *dvar) +{ + DriverTarget *dtar= &dvar->targets[0]; + DriverTarget *dtar2= &dvar->targets[1]; + Object *ob1 = (Object *)dtar->id; + Object *ob2 = (Object *)dtar2->id; + PointerRNA dtar_ptr, dtar2_ptr; + uiLayout *col; + + /* initialise RNA pointer to the target */ + RNA_pointer_create(id, &RNA_DriverTarget, dtar, &dtar_ptr); + RNA_pointer_create(id, &RNA_DriverTarget, dtar2, &dtar2_ptr); + + /* Bone 1 */ + col= uiLayoutColumn(layout, 1); + uiTemplateAnyID(col, (bContext *)C, &dtar_ptr, "id", "id_type", "Ob/Bone 1:"); + + if (dtar->id && ob1->pose) { + PointerRNA tar_ptr; + + RNA_pointer_create(dtar2->id, &RNA_Pose, ob1->pose, &tar_ptr); + uiItemPointerR(col, "", ICON_BONE_DATA, &dtar_ptr, "bone_target", &tar_ptr, "bones"); + } + + col= uiLayoutColumn(layout, 1); + uiTemplateAnyID(col, (bContext *)C, &dtar2_ptr, "id", "id_type", "Ob/Bone 2:"); + + if (dtar2->id && ob2->pose) { + PointerRNA tar_ptr; + + RNA_pointer_create(dtar2->id, &RNA_Pose, ob2->pose, &tar_ptr); + uiItemPointerR(col, "", ICON_BONE_DATA, &dtar2_ptr, "bone_target", &tar_ptr, "bones"); + } +} + /* driver settings for active F-Curve (only for 'Drivers' mode) */ static void graph_panel_drivers(const bContext *C, Panel *pa) { bAnimListElem *ale; FCurve *fcu; ChannelDriver *driver; - DriverTarget *dtar; + DriverVar *dvar; PointerRNA driver_ptr; uiLayout *col; @@ -354,56 +456,54 @@ static void graph_panel_drivers(const bContext *C, Panel *pa) uiItemL(col, "ERROR: invalid target channel(s)", ICON_ERROR); } - /* add driver target variables */ + /* add driver variables */ col= uiLayoutColumn(pa->layout, 0); block= uiLayoutGetBlock(col); - but= uiDefBut(block, BUT, B_IPO_DEPCHANGE, "Add Target", 0, 0, 10*UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "Add a new target variable for this Driver"); + but= uiDefBut(block, BUT, B_IPO_DEPCHANGE, "Add Variable", 0, 0, 10*UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "Add a new target variable for this Driver"); uiButSetFunc(but, driver_add_var_cb, driver, NULL); /* loop over targets, drawing them */ - for (dtar= driver->targets.first; dtar; dtar= dtar->next) { - PointerRNA dtar_ptr; + for (dvar= driver->variables.first; dvar; dvar= dvar->next) { + PointerRNA dvar_ptr; uiLayout *box, *row; - /* panel holding the buttons */ - box= uiLayoutBox(pa->layout); - - /* first row context info for driver */ - RNA_pointer_create(ale->id, &RNA_DriverTarget, dtar, &dtar_ptr); - - row= uiLayoutRow(box, 0); - block= uiLayoutGetBlock(row); - /* variable name */ - uiItemR(row, "", 0, &dtar_ptr, "name", 0); - - /* remove button */ - but= uiDefIconBut(block, BUT, B_IPO_DEPCHANGE, ICON_X, 290, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0.0, 0.0, "Delete target variable."); - uiButSetFunc(but, driver_delete_var_cb, driver, dtar); - - - /* Target ID */ - row= uiLayoutRow(box, 0); - uiTemplateAnyID(row, (bContext *)C, &dtar_ptr, "id", "id_type", "Value:"); + /* sub-layout column for this variable's settings */ + col= uiLayoutColumn(pa->layout, 1); - /* Target Property */ - // TODO: make this less technical... - if (dtar->id) { - PointerRNA root_ptr; + /* header panel */ + box= uiLayoutBox(col); + /* first row context info for driver */ + RNA_pointer_create(ale->id, &RNA_DriverVariable, dvar, &dvar_ptr); - /* get pointer for resolving the property selected */ - RNA_id_pointer_create(dtar->id, &root_ptr); + row= uiLayoutRow(box, 0); + block= uiLayoutGetBlock(row); + /* variable name */ + uiItemR(row, "", 0, &dvar_ptr, "name", 0); + + /* remove button */ + uiBlockSetEmboss(block, UI_EMBOSSN); + but= uiDefIconBut(block, BUT, B_IPO_DEPCHANGE, ICON_X, 290, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0.0, 0.0, "Delete target variable."); + uiButSetFunc(but, driver_delete_var_cb, driver, dvar); + uiBlockSetEmboss(block, UI_EMBOSS); - col= uiLayoutColumn(box, 1); - block= uiLayoutGetBlock(col); - /* rna path */ - uiTemplatePathBuilder(col, (bContext *)C, &dtar_ptr, "data_path", &root_ptr, "Path"); + /* variable type */ + row= uiLayoutRow(box, 0); + uiItemR(row, "", 0, &dvar_ptr, "type", 0); - /* array index */ - // TODO: this needs selector which limits it to ok values - // NOTE: for for now, the array index box still gets shown when non-zero (i.e. for tweaking rigs as necessary) - if (dtar->array_index) - uiItemR(col, "Index", 0, &dtar_ptr, "array_index", 0); - } + /* variable type settings */ + box= uiLayoutBox(col); + /* controls to draw depends on the type of variable */ + switch (dvar->type) { + case DVAR_TYPE_SINGLE_PROP: /* single property */ + graph_panel_driverVar__singleProp(C, box, ale->id, dvar); + break; + case DVAR_TYPE_ROT_DIFF: /* rotational difference */ + graph_panel_driverVar__rotDiff(C, box, ale->id, dvar); + break; + case DVAR_TYPE_LOC_DIFF: /* location difference */ + graph_panel_driverVar__locDiff(C, box, ale->id, dvar); + break; + } } /* cleanup */ diff --git a/source/blender/editors/space_outliner/outliner.c b/source/blender/editors/space_outliner/outliner.c index 1303168e78d..93b32086614 100644 --- a/source/blender/editors/space_outliner/outliner.c +++ b/source/blender/editors/space_outliner/outliner.c @@ -69,6 +69,7 @@ #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_depsgraph.h" +#include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_group.h" #include "BKE_library.h" @@ -913,18 +914,25 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i TreeElement *ted= outliner_add_element(soops, &te->subtree, adt, te, TSE_DRIVER_BASE, 0); ID *lastadded= NULL; FCurve *fcu; - DriverTarget *dtar; ted->name= "Drivers"; for (fcu= adt->drivers.first; fcu; fcu= fcu->next) { - if (fcu->driver && fcu->driver->targets.first) { - for (dtar= fcu->driver->targets.first; dtar; dtar= dtar->next) { - if (lastadded != dtar->id) { - // XXX this lastadded check is rather lame, and also fails quite badly... - outliner_add_element(soops, &ted->subtree, dtar->id, ted, TSE_LINKED_OB, 0); - lastadded= dtar->id; + if (fcu->driver && fcu->driver->variables.first) { + ChannelDriver *driver= fcu->driver; + DriverVar *dvar; + + for (dvar= driver->variables.first; dvar; dvar= dvar->next) { + /* loop over all targets used here */ + DRIVER_TARGETS_USED_LOOPER(dvar) + { + if (lastadded != dtar->id) { + // XXX this lastadded check is rather lame, and also fails quite badly... + outliner_add_element(soops, &ted->subtree, dtar->id, ted, TSE_LINKED_OB, 0); + lastadded= dtar->id; + } } + DRIVER_TARGETS_LOOPER_END } } } diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index dcb10d0f2cf..f2d4471607f 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -232,26 +232,72 @@ typedef enum eFMod_Noise_Modifications { /* Drivers -------------------------------------- */ -/* Driver Target +/* Driver Target (dtar) * - * A 'variable' for use as a target of the driver/expression. + * Defines how to access a dependency needed for a driver variable. + */ +typedef struct DriverTarget { + ID *id; /* ID-block which owns the target */ + + char *rna_path; /* target channel to use as driver value */ + char pchan_name[32]; /* name of the posebone to use (for certain types of variable only) */ + + int idtype; /* type of ID-block that this target can use */ + int flag; /* flags for the validity of the target (NOTE: these get reset everytime the types change) */ +} DriverTarget; + +/* Driver Target flags */ +typedef enum eDriverTarget_Flag { + /* used for targets that use the pchan_name instead of RNA path + * (i.e. rotation difference) + */ + DTAR_FLAG_STRUCT_REF = (1<<0), + /* idtype can only be 'Object' */ + DTAR_FLAG_ID_OB_ONLY = (1<<1), +} eDriverTarget_Flag; + +/* --- */ + +/* maximum number of driver targets per variable */ +// FIXME: make this get used below (for DriverVariable defines) if DNA supports preprocessor stuff.. +#define MAX_DRIVER_TARGETS 8 + + +/* Driver Variable (dvar) + * + * A 'variable' for use as an input for the driver evaluation. * Defines a way of accessing some channel to use, that can be * referred to in the expression as a variable, thus simplifying * expressions and also Depsgraph building. */ -typedef struct DriverTarget { - struct DriverTarget *next, *prev; +typedef struct DriverVar { + struct DriverVar *next, *prev; - ID *id; /* ID-block which owns the target */ - char *rna_path; /* target channel to use as driver value */ - int array_index; /* if applicable, the index of the RNA-array item to use as driver */ + char name[64]; /* name of the variable to use in py-expression (must be valid python identifier) */ - int idtype; /* type of ID-block that this target can use */ - int flags; /* flags for the validity of the target */ - int pad; + DriverTarget targets[8]; /* MAX_DRIVER_TARGETS - targets available for use for this type of variable */ + int num_targets; /* number of targets used by this variable */ - char name[64]; /* name of the variable */ -} DriverTarget; + int type; /* type of driver target (eDriverTarget_Types) */ +} DriverVar; + +/* Driver Variable Types */ +typedef enum eDriverVar_Types { + /* single RNA property */ + DVAR_TYPE_SINGLE_PROP = 0, + /* rotation difference (between 2 bones) */ + DVAR_TYPE_ROT_DIFF, + /* distance between objects/bones */ + DVAR_TYPE_LOC_DIFF, + + /* maximum number of variable types + * NOTE: this must always be th last item in this list, + * so add new types above this line + */ + MAX_DVAR_TYPES +} eDriverVar_Types; + +/* --- */ /* Channel Driver (i.e. Drivers / Expressions) (driver) * @@ -265,7 +311,7 @@ typedef struct DriverTarget { * evaluated in. This order is set by the Depsgraph's sorting stuff. */ typedef struct ChannelDriver { - ListBase targets; /* targets for this driver (i.e. list of DriverTarget) */ + ListBase variables; /* targets for this driver (i.e. list of DriverVar) */ /* python expression to execute (may call functions defined in an accessory file) * which relates the target 'variables' in some way to yield a single usable value @@ -287,10 +333,12 @@ typedef enum eDriver_Types { DRIVER_TYPE_AVERAGE = 0, /* python expression/function relates targets */ DRIVER_TYPE_PYTHON, - /* rotational difference (must use rotation channels only) */ - DRIVER_TYPE_ROTDIFF, /* sum of all values */ DRIVER_TYPE_SUM, + /* smallest value */ + DRIVER_TYPE_MIN, + /* largest value */ + DRIVER_TYPE_MAX, } eDriver_Types; /* driver flags */ @@ -301,7 +349,7 @@ typedef enum eDriver_Flags { DRIVER_FLAG_RECALC = (1<<1), /* driver does replace value, but overrides (for layering of animation over driver) */ // TODO: this needs to be implemented at some stage or left out... - DRIVER_FLAG_LAYERING = (1<<2), + //DRIVER_FLAG_LAYERING = (1<<2), /* use when the expression needs to be recompiled */ DRIVER_FLAG_RECOMPILE = (1<<3), } eDriver_Flags; diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index e631e037e74..5697747fcab 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -182,6 +182,7 @@ extern StructRNA RNA_DistortedNoiseTexture; extern StructRNA RNA_DomainFluidSettings; extern StructRNA RNA_Driver; extern StructRNA RNA_DriverTarget; +extern StructRNA RNA_DriverVariable; extern StructRNA RNA_DupliObject; extern StructRNA RNA_EdgeSplitModifier; extern StructRNA RNA_EditBone; diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 62ee19df352..10fece3391f 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -116,10 +116,12 @@ static void rna_DriverTarget_update_data(Main *bmain, Scene *scene, PointerRNA * AnimData *adt= BKE_animdata_from_id(ptr->id.data); /* find the driver this belongs to and update it */ - for(fcu=adt->drivers.first; fcu; fcu=fcu->next) { + for (fcu=adt->drivers.first; fcu; fcu=fcu->next) { driver= fcu->driver; - - if(driver && BLI_findindex(&driver->targets, ptr->data) != -1) { + + if (driver) { + // FIXME: need to be able to search targets for required one... + //BLI_findindex(&driver->targets, ptr->data) != -1) RNA_pointer_create(ptr->id.data, &RNA_Driver, driver, &driverptr); rna_ChannelDriver_update_data(bmain, scene, &driverptr); return; @@ -185,6 +187,14 @@ static void rna_DriverTarget_RnaPath_set(PointerRNA *ptr, const char *value) dtar->rna_path= NULL; } +static void rna_DriverVariable_type_set(PointerRNA *ptr, int value) +{ + DriverVar *dvar= (DriverVar *)ptr->data; + + /* call the API function for this */ + driver_change_variable_type(dvar, value); +} + /* ****************************** */ static void rna_FCurve_RnaPath_get(PointerRNA *ptr, char *value) @@ -220,15 +230,16 @@ static void rna_FCurve_RnaPath_set(PointerRNA *ptr, const char *value) fcu->rna_path= NULL; } -DriverTarget *rna_Driver_new_target(ChannelDriver *driver) +DriverVar *rna_Driver_new_variable(ChannelDriver *driver) { - return driver_add_new_target(driver); + /* call the API function for this */ + return driver_add_new_variable(driver); } -void rna_Driver_remove_target(ChannelDriver *driver, DriverTarget *dtar) +void rna_Driver_remove_variable(ChannelDriver *driver, DriverVar *dvar) { /* call the API function for this */ - driver_free_target(driver, dtar); + driver_free_variable(driver, dvar); } @@ -677,13 +688,7 @@ static void rna_def_drivertarget(BlenderRNA *brna) PropertyRNA *prop; srna= RNA_def_struct(brna, "DriverTarget", NULL); - RNA_def_struct_ui_text(srna, "Driver Target", "Variable from some source/target for driver relationship."); - - /* Variable Name */ - prop= RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_struct_name_property(srna, prop); - RNA_def_property_ui_text(prop, "Name", "Name to use in scripted expressions/functions. (No spaces or dots are allowed. Also, must not start with a symbol or digit)"); - RNA_def_property_update(prop, 0, "rna_DriverTarget_update_data"); + RNA_def_struct_ui_text(srna, "Driver Target", "Source of input values for driver variables."); /* Target Properties - ID-block to Drive */ prop= RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE); @@ -698,6 +703,7 @@ static void rna_def_drivertarget(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "idtype"); RNA_def_property_enum_items(prop, id_type_items); RNA_def_property_enum_default(prop, ID_OB); + // XXX need to add an 'editable func' for this, in the case where certain flags are set already... RNA_def_property_enum_funcs(prop, NULL, "rna_DriverTarget_id_type_set", NULL); RNA_def_property_ui_text(prop, "ID Type", "Type of ID-block that can be used."); RNA_def_property_update(prop, 0, "rna_DriverTarget_update_data"); @@ -708,41 +714,78 @@ static void rna_def_drivertarget(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Data Path", "RNA Path (from Object) to property used"); RNA_def_property_update(prop, 0, "rna_DriverTarget_update_data"); - prop= RNA_def_property(srna, "array_index", PROP_INT, PROP_NONE); - RNA_def_property_ui_text(prop, "RNA Array Index", "Index to the specific property used (if applicable)"); + prop= RNA_def_property(srna, "bone_target", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "pchan_name"); + RNA_def_property_ui_text(prop, "Bone Name", "Name of PoseBone to use as target."); RNA_def_property_update(prop, 0, "rna_DriverTarget_update_data"); } +static void rna_def_drivervar(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem prop_type_items[] = { + {DVAR_TYPE_SINGLE_PROP, "SINGLE_PROP", 0, "Single Property", "Use the value from some RNA property (Default)"}, + {DVAR_TYPE_ROT_DIFF, "ROTATION_DIFF", 0, "Rotational Difference", "Use the angle between two bones"}, + {DVAR_TYPE_LOC_DIFF, "LOC_DIFF", 0, "Distance", "Distance between two bones or "}, + {0, NULL, 0, NULL, NULL}}; + + + srna= RNA_def_struct(brna, "DriverVariable", NULL); + RNA_def_struct_sdna(srna, "DriverVar"); + RNA_def_struct_ui_text(srna, "Driver Variable", "Variable from some source/target for driver relationship."); + + /* Variable Name */ + prop= RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_ui_text(prop, "Name", "Name to use in scripted expressions/functions. (No spaces or dots are allowed. Also, must not start with a symbol or digit)"); + RNA_def_property_update(prop, 0, "rna_DriverTarget_update_data"); // XXX + + /* Enums */ + prop= RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_type_items); + RNA_def_property_enum_funcs(prop, NULL, "rna_DriverVariable_type_set", NULL); + RNA_def_property_ui_text(prop, "Type", "Driver variable type."); + RNA_def_property_update(prop, 0, "rna_ChannelDriver_update_data"); // XXX + + /* Targets */ + // TODO: for nicer api, only expose the relevant props via subclassing, instead of exposing the collection of targets + prop= RNA_def_property(srna, "targets", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "targets", "num_targets"); + RNA_def_property_struct_type(prop, "DriverTarget"); + RNA_def_property_ui_text(prop, "Targets", "Sources of input data for evaluating this variable."); +} + -/* channeldriver.targets.* */ -static void rna_def_channeldriver_targets(BlenderRNA *brna, PropertyRNA *cprop) +/* channeldriver.variables.* */ +static void rna_def_channeldriver_variables(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; // PropertyRNA *prop; - + FunctionRNA *func; PropertyRNA *parm; - - RNA_def_property_srna(cprop, "ChannelDriverTargets"); - srna= RNA_def_struct(brna, "ChannelDriverTargets", NULL); + + RNA_def_property_srna(cprop, "ChannelDriverVariables"); + srna= RNA_def_struct(brna, "ChannelDriverVariables", NULL); RNA_def_struct_sdna(srna, "ChannelDriver"); - RNA_def_struct_ui_text(srna, "ChannelDriver Targets", "Collection of channel driver Targets."); - - - /* add target */ - func= RNA_def_function(srna, "new", "rna_Driver_new_target"); - RNA_def_function_ui_description(func, "Add a new target for the driver."); + RNA_def_struct_ui_text(srna, "ChannelDriver Variables", "Collection of channel driver Variables."); + + + /* add variable */ + func= RNA_def_function(srna, "new", "rna_Driver_new_variable"); + RNA_def_function_ui_description(func, "Add a new variable for the driver."); /* return type */ - parm= RNA_def_pointer(func, "target", "DriverTarget", "", "Newly created Driver Target."); + parm= RNA_def_pointer(func, "var", "DriverVariable", "", "Newly created Driver Variable."); RNA_def_function_return(func, parm); - /* remove target */ - func= RNA_def_function(srna, "remove", "rna_Driver_remove_target"); - RNA_def_function_ui_description(func, "Remove an existing target from the driver."); - /* target to remove*/ - parm= RNA_def_pointer(func, "target", "DriverTarget", "", "Target to remove from the driver."); + /* remove variable */ + func= RNA_def_function(srna, "remove", "rna_Driver_remove_variable"); + RNA_def_function_ui_description(func, "Remove an existing variable from the driver."); + /* target to remove */ + parm= RNA_def_pointer(func, "var", "DriverVariable", "", "Variable to remove from the driver."); RNA_def_property_flag(parm, PROP_REQUIRED); - } static void rna_def_channeldriver(BlenderRNA *brna) @@ -754,7 +797,8 @@ static void rna_def_channeldriver(BlenderRNA *brna) {DRIVER_TYPE_AVERAGE, "AVERAGE", 0, "Averaged Value", ""}, {DRIVER_TYPE_SUM, "SUM", 0, "Sum Values", ""}, {DRIVER_TYPE_PYTHON, "SCRIPTED", 0, "Scripted Expression", ""}, - {DRIVER_TYPE_ROTDIFF, "ROTDIFF", 0, "Rotational Difference", ""}, + {DRIVER_TYPE_MIN, "MIN", 0, "Minimum Value", ""}, + {DRIVER_TYPE_MAX, "MAX", 0, "Maximum Value", ""}, {0, NULL, 0, NULL, NULL}}; srna= RNA_def_struct(brna, "Driver", NULL); @@ -764,7 +808,7 @@ static void rna_def_channeldriver(BlenderRNA *brna) /* Enums */ prop= RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, prop_type_items); - RNA_def_property_ui_text(prop, "Type", "Driver types."); + RNA_def_property_ui_text(prop, "Type", "Driver type."); RNA_def_property_update(prop, 0, "rna_ChannelDriver_update_data"); /* String values */ @@ -773,11 +817,11 @@ static void rna_def_channeldriver(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_ChannelDriver_update_expr"); /* Collections */ - prop= RNA_def_property(srna, "targets", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "targets", NULL); - RNA_def_property_struct_type(prop, "DriverTarget"); - RNA_def_property_ui_text(prop, "Target Variables", "Properties acting as targets for this driver."); - rna_def_channeldriver_targets(brna, prop); + prop= RNA_def_property(srna, "variables", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "variables", NULL); + RNA_def_property_struct_type(prop, "DriverVariable"); + RNA_def_property_ui_text(prop, "Variables", "Properties acting as inputs for this driver."); + rna_def_channeldriver_variables(brna, prop); /* Functions */ RNA_api_drivers(srna); @@ -933,6 +977,7 @@ void RNA_def_fcurve(BlenderRNA *brna) rna_def_fpoint(brna); rna_def_drivertarget(brna); + rna_def_drivervar(brna); rna_def_channeldriver(brna); rna_def_fmodifier(brna); diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index 76df28494ac..016aed70c31 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -147,7 +147,7 @@ float BPY_pydriver_eval (ChannelDriver *driver) PyObject *retval= NULL; PyGILState_STATE gilstate; - DriverTarget *dtar; + DriverVar *dvar; float result = 0.0f; /* default return */ char *expr = NULL; short targets_ok= 1; @@ -174,24 +174,24 @@ float BPY_pydriver_eval (ChannelDriver *driver) /* add target values to a dict that will be used as '__locals__' dict */ driver_vars = PyDict_New(); // XXX do we need to decref this? - for (dtar= driver->targets.first; dtar; dtar= dtar->next) { + for (dvar= driver->variables.first; dvar; dvar= dvar->next) { PyObject *driver_arg = NULL; float tval = 0.0f; - + /* try to get variable value */ - tval= driver_get_target_value(driver, dtar); + tval= driver_get_variable_value(driver, dvar); driver_arg= PyFloat_FromDouble((double)tval); - + /* try to add to dictionary */ - if (PyDict_SetItemString(driver_vars, dtar->name, driver_arg)) { + if (PyDict_SetItemString(driver_vars, dvar->name, driver_arg)) { /* this target failed - bad name */ 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); + + fprintf(stderr, "\tBPY_pydriver_eval() - couldn't add variable '%s' to namespace \n", dvar->name); // BPy_errors_to_report(NULL); // TODO - reports PyErr_Print(); PyErr_Clear(); @@ -202,12 +202,14 @@ float BPY_pydriver_eval (ChannelDriver *driver) /* execute expression to get a value */ retval = PyRun_String(expr, Py_eval_input, bpy_pydriver_Dict, driver_vars); #else - if(driver->flag & DRIVER_FLAG_RECOMPILE || driver->expr_comp==NULL) { + /* compile the expression first if it hasn't been compiled or needs to be rebuilt */ + if((driver->flag & DRIVER_FLAG_RECOMPILE) || (driver->expr_comp==NULL)) { Py_XDECREF(driver->expr_comp); driver->expr_comp= Py_CompileString(expr, "<bpy driver>", Py_eval_input); driver->flag &= ~DRIVER_FLAG_RECOMPILE; } - if(driver->expr_comp) + /* evaluate the compiled expression */ + if (driver->expr_comp) retval= PyEval_EvalCode(driver->expr_comp, bpy_pydriver_Dict, driver_vars); #endif diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 78125954816..9f52cde27e1 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -213,11 +213,12 @@ typedef struct wmNotifier { #define ND_NLA_SELECT (75<<16) #define ND_NLA_EDIT (76<<16) #define ND_NLA_ACTCHANGE (77<<16) +#define ND_FCURVES_ORDER (78<<16) /* NC_GEOM Geometry */ /* Mesh, Curve, MetaBall, Armature, .. */ -#define ND_SELECT (80<<16) -#define ND_DATA (81<<16) +#define ND_SELECT (90<<16) +#define ND_DATA (91<<16) /* NC_NODE Nodes */ #define ND_NODE_SELECT (1<<16) |