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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Leung <aligorith@gmail.com>2009-02-12 13:41:57 +0300
committerJoshua Leung <aligorith@gmail.com>2009-02-12 13:41:57 +0300
commit4075c601336967f1714d8eee12e24b3b1f503978 (patch)
tree9311db72f035fe3d8a6e5b252be76234d78f2a08
parent517d9b882a75f177e68c83252e5b560b735b1842 (diff)
KeyingSets: First working prototype
To use KeyingSets, simply Outliner-select items in the Datablocks view and press K to add to the active KeyingSet. Then keyframes can be inserted by choosing the 'Active Keying Set' option when inserting keyframes. Important notes on the current implementation: * Only properties directly inside some ID-block that is close to the root (i.e. main -> objects -> "someobj" -> location, or main -> materials -> "somemat" -> colour) can be accessed for now, as I haven't got the code for building the inner-parts of the paths working yet. Help on getting this working is welcome (hint to Brecht). * Properties that can be safely included include simple properties "object -> Dupli Verts", entire arrays "object -> Location" or individual array elements "object -> Location -> y" --- Also added typo fix for KeyingSet freeing. It was freeing the KeyingSet instead of it's paths.
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c2
-rw-r--r--source/blender/editors/animation/keyframing.c186
-rw-r--r--source/blender/editors/space_graph/graph_draw.c2
-rw-r--r--source/blender/editors/space_outliner/outliner.c193
4 files changed, 299 insertions, 84 deletions
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 55597b635c9..098d0ad7a32 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -248,7 +248,7 @@ void BKE_keyingset_free (KeyingSet *ks)
MEM_freeN(ksp->rna_path);
/* free path itself */
- BLI_freelinkN(&ks->paths, ks);
+ BLI_freelinkN(&ks->paths, ksp);
}
}
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 08ee83277a8..157609ed640 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -111,7 +111,7 @@ FCurve *verify_fcurve (ID *id, const char group[], const char rna_path[], const
/* use default settings to make a F-Curve */
fcu= MEM_callocN(sizeof(FCurve), "FCurve");
- fcu->flag |= (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES);
+ fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
if (act->curves.first==NULL)
fcu->flag |= FCURVE_ACTIVE; /* first one added active */
@@ -119,6 +119,9 @@ FCurve *verify_fcurve (ID *id, const char group[], const char rna_path[], const
fcu->rna_path= BLI_strdupn(rna_path, strlen(rna_path));
fcu->array_index= array_index;
+ /* set additional flags */
+ // TODO: need to set the FCURVE_INT_VALUES flag must be set if property is not float!
+
/* if a group name has been provided, try to add or find a group, then add F-Curve to it */
if (group) {
@@ -2019,13 +2022,14 @@ static int commonkey_modifykey (ListBase *dsources, KeyingSet *ks, short mode, f
else if (mode == COMMONKEY_MODE_DELETE)
kflag= 0;
-
/* check if the KeyingSet is absolute or not (i.e. does it requires sources info) */
if (ks->flag & KEYINGSET_ABSOLUTE) {
/* Absolute KeyingSets are simpler to use, as all the destination info has already been
* provided by the user, and is stored, ready to use, in the KeyingSet paths.
*/
for (ksp= ks->paths.first; ksp; ksp= ksp->next) {
+ int arraylen, i;
+
/* get pointer to name of group to add channels to */
if (ksp->flag & KSP_FLAG_GROUP_NONE)
groupname= NULL;
@@ -2034,11 +2038,34 @@ static int commonkey_modifykey (ListBase *dsources, KeyingSet *ks, short mode, f
else
groupname= ksp->group;
- /* action to take depends on mode */
- if (mode == COMMONKEY_MODE_INSERT)
- success+= insertkey(ksp->id, groupname, ksp->rna_path, ksp->array_index, cfra, kflag);
- else if (mode == COMMONKEY_MODE_DELETE)
- success+= deletekey(ksp->id, groupname, ksp->rna_path, ksp->array_index, cfra, kflag);
+ /* init arraylen and i - arraylen should be greater than i so that
+ * normal non-array entries get keyframed correctly
+ */
+ i= ksp->array_index;
+ arraylen= i+1;
+
+ /* get length of array if whole array option is enabled */
+ if (ksp->flag & KSP_FLAG_WHOLE_ARRAY) {
+ PointerRNA id_ptr, ptr;
+ PropertyRNA *prop;
+
+ RNA_id_pointer_create(ksp->id, &id_ptr);
+ if (RNA_path_resolve(&id_ptr, ksp->rna_path, &ptr, &prop))
+ arraylen= RNA_property_array_length(&ptr, prop);
+ }
+
+ /* for each possible index, perform operation
+ * - assume that arraylen is greater than index
+ */
+ for (; i < arraylen; i++) {
+ /* action to take depends on mode */
+ if (mode == COMMONKEY_MODE_INSERT)
+ success+= insertkey(ksp->id, groupname, ksp->rna_path, i, cfra, kflag);
+ else if (mode == COMMONKEY_MODE_DELETE)
+ success+= deletekey(ksp->id, groupname, ksp->rna_path, i, cfra, kflag);
+ }
+
+ // TODO: set recalc tags on the ID?
}
}
#if 0 // XXX still need to figure out how to get such keyingsets working
@@ -2148,96 +2175,97 @@ static int insert_key_exec (bContext *C, wmOperator *op)
else
return OPERATOR_FINISHED;
}
-
- // XXX more comprehensive tests will be needed
- CTX_DATA_BEGIN(C, Base*, base, selected_bases)
- {
- Object *ob= base->object;
- ID *id= (ID *)ob;
- short success= 0;
-
- /* check which keyframing mode chosen for this object */
- if (mode < 4) {
- /* object-based keyframes */
- switch (mode) {
- case 4: /* color of active material (only for geometry...) */
- // NOTE: this is just a demo... but ideally we'd go through materials instead of active one only so reference stays same
- // XXX no group for now
- success+= insertkey(id, NULL, "active_material.diffuse_color", 0, cfra, 0);
- success+= insertkey(id, NULL, "active_material.diffuse_color", 1, cfra, 0);
- success+= insertkey(id, NULL, "active_material.diffuse_color", 2, cfra, 0);
- break;
- case 3: /* object scale */
- success+= insertkey(id, "Object Transforms", "scale", 0, cfra, 0);
- success+= insertkey(id, "Object Transforms", "scale", 1, cfra, 0);
- success+= insertkey(id, "Object Transforms", "scale", 2, cfra, 0);
- break;
- case 2: /* object rotation */
- success+= insertkey(id, "Object Transforms", "rotation", 0, cfra, 0);
- success+= insertkey(id, "Object Transforms", "rotation", 1, cfra, 0);
- success+= insertkey(id, "Object Transforms", "rotation", 2, cfra, 0);
- break;
- case 1: /* object location */
- success+= insertkey(id, "Object Transforms", "location", 0, cfra, 0);
- success+= insertkey(id, "Object Transforms", "location", 1, cfra, 0);
- success+= insertkey(id, "Object Transforms", "location", 2, cfra, 0);
- break;
- }
+ else {
+ // more comprehensive tests will be needed
+ CTX_DATA_BEGIN(C, Base*, base, selected_bases)
+ {
+ Object *ob= base->object;
+ ID *id= (ID *)ob;
+ short success= 0;
- ob->recalc |= OB_RECALC_OB;
- }
- else if ((ob->pose) && (ob->flag & OB_POSEMODE)) {
- /* PoseChannel based keyframes */
- bPoseChannel *pchan;
-
- for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
- /* only if selected */
- if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED)) {
- char buf[512];
-
- switch (mode) {
- case 6: /* pchan scale */
- sprintf(buf, "pose.pose_channels[\"%s\"].scale", pchan->name);
- success+= insertkey(id, pchan->name, buf, 0, cfra, 0);
- success+= insertkey(id, pchan->name, buf, 1, cfra, 0);
- success+= insertkey(id, pchan->name, buf, 2, cfra, 0);
- break;
- case 5: /* pchan rotation */
- if (pchan->rotmode == PCHAN_ROT_QUAT) {
- sprintf(buf, "pose.pose_channels[\"%s\"].rotation", pchan->name);
+ /* check which keyframing mode chosen for this object */
+ if (mode < 4) {
+ /* object-based keyframes */
+ switch (mode) {
+ case 4: /* color of active material (only for geometry...) */
+ // NOTE: this is just a demo... but ideally we'd go through materials instead of active one only so reference stays same
+ // XXX no group for now
+ success+= insertkey(id, NULL, "active_material.diffuse_color", 0, cfra, 0);
+ success+= insertkey(id, NULL, "active_material.diffuse_color", 1, cfra, 0);
+ success+= insertkey(id, NULL, "active_material.diffuse_color", 2, cfra, 0);
+ break;
+ case 3: /* object scale */
+ success+= insertkey(id, "Object Transforms", "scale", 0, cfra, 0);
+ success+= insertkey(id, "Object Transforms", "scale", 1, cfra, 0);
+ success+= insertkey(id, "Object Transforms", "scale", 2, cfra, 0);
+ break;
+ case 2: /* object rotation */
+ success+= insertkey(id, "Object Transforms", "rotation", 0, cfra, 0);
+ success+= insertkey(id, "Object Transforms", "rotation", 1, cfra, 0);
+ success+= insertkey(id, "Object Transforms", "rotation", 2, cfra, 0);
+ break;
+ case 1: /* object location */
+ success+= insertkey(id, "Object Transforms", "location", 0, cfra, 0);
+ success+= insertkey(id, "Object Transforms", "location", 1, cfra, 0);
+ success+= insertkey(id, "Object Transforms", "location", 2, cfra, 0);
+ break;
+ }
+
+ ob->recalc |= OB_RECALC_OB;
+ }
+ else if ((ob->pose) && (ob->flag & OB_POSEMODE)) {
+ /* PoseChannel based keyframes */
+ bPoseChannel *pchan;
+
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ /* only if selected */
+ if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED)) {
+ char buf[512];
+
+ switch (mode) {
+ case 6: /* pchan scale */
+ sprintf(buf, "pose.pose_channels[\"%s\"].scale", pchan->name);
success+= insertkey(id, pchan->name, buf, 0, cfra, 0);
success+= insertkey(id, pchan->name, buf, 1, cfra, 0);
success+= insertkey(id, pchan->name, buf, 2, cfra, 0);
- success+= insertkey(id, pchan->name, buf, 3, cfra, 0);
- }
- else {
- sprintf(buf, "pose.pose_channels[\"%s\"].euler_rotation", pchan->name);
+ break;
+ case 5: /* pchan rotation */
+ if (pchan->rotmode == PCHAN_ROT_QUAT) {
+ sprintf(buf, "pose.pose_channels[\"%s\"].rotation", pchan->name);
+ success+= insertkey(id, pchan->name, buf, 0, cfra, 0);
+ success+= insertkey(id, pchan->name, buf, 1, cfra, 0);
+ success+= insertkey(id, pchan->name, buf, 2, cfra, 0);
+ success+= insertkey(id, pchan->name, buf, 3, cfra, 0);
+ }
+ else {
+ sprintf(buf, "pose.pose_channels[\"%s\"].euler_rotation", pchan->name);
+ success+= insertkey(id, pchan->name, buf, 0, cfra, 0);
+ success+= insertkey(id, pchan->name, buf, 1, cfra, 0);
+ success+= insertkey(id, pchan->name, buf, 2, cfra, 0);
+ }
+ break;
+ default: /* pchan location */
+ sprintf(buf, "pose.pose_channels[\"%s\"].location", pchan->name);
success+= insertkey(id, pchan->name, buf, 0, cfra, 0);
success+= insertkey(id, pchan->name, buf, 1, cfra, 0);
success+= insertkey(id, pchan->name, buf, 2, cfra, 0);
+ break;
}
- break;
- default: /* pchan location */
- sprintf(buf, "pose.pose_channels[\"%s\"].location", pchan->name);
- success+= insertkey(id, pchan->name, buf, 0, cfra, 0);
- success+= insertkey(id, pchan->name, buf, 1, cfra, 0);
- success+= insertkey(id, pchan->name, buf, 2, cfra, 0);
- break;
}
}
+
+ ob->recalc |= OB_RECALC_OB;
}
- ob->recalc |= OB_RECALC_OB;
+ printf("Ob '%s' - Successfully added %d Keyframes \n", id->name+2, success);
}
-
- printf("Ob '%s' - Successfully added %d Keyframes \n", id->name+2, success);
+ CTX_DATA_END;
}
- CTX_DATA_END;
/* send updates */
ED_anim_dag_flush_update(C);
- if (mode == 3) // material color requires different notifiers
+ if (mode == 4) // material color requires different notifiers
WM_event_add_notifier(C, NC_MATERIAL|ND_KEYS, NULL);
else
WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, NULL);
diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c
index 077706f0c2b..86316d93214 100644
--- a/source/blender/editors/space_graph/graph_draw.c
+++ b/source/blender/editors/space_graph/graph_draw.c
@@ -708,7 +708,7 @@ void graph_draw_curves (bAnimContext *ac, SpaceIpo *sipo, ARegion *ar)
/* draw curve - we currently calculate colour on the fly, but that should probably be done in advance instead */
if ( ((fcu->bezt) || (fcu->fpt)) && (fcu->totvert) ) {
/* set color/drawing style for curve itself */
- if (fcu->flag & FCURVE_PROTECTED) {
+ if ( ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)) || (fcu->flag & FCURVE_PROTECTED) ) {
/* protected curves (non editable) are drawn with dotted lines */
setlinestyle(2);
}
diff --git a/source/blender/editors/space_outliner/outliner.c b/source/blender/editors/space_outliner/outliner.c
index 2d3ccedd201..e91d478d327 100644
--- a/source/blender/editors/space_outliner/outliner.c
+++ b/source/blender/editors/space_outliner/outliner.c
@@ -76,6 +76,7 @@
#include "BKE_material.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
+#include "BKE_report.h"
#include "BKE_screen.h"
#include "BKE_scene.h"
#include "BKE_sequence.h"
@@ -3092,13 +3093,38 @@ void outliner_operation_menu(Scene *scene, ARegion *ar, SpaceOops *soops)
/* These operators are only available in databrowser mode for now, as
* they depend on having RNA paths and/or hierarchies available.
*/
-
+enum {
+ KEYINGSET_EDITMODE_ADD = 0,
+ KEYINGSET_EDITMODE_REMOVE,
+} eKeyingSet_EditModes;
+
+/* typedef'd function-prototype style for KeyingSet operation callbacks */
+typedef void (*ksEditOp)(SpaceOops *soops, KeyingSet *ks, TreeElement *te, TreeStoreElem *tselem);
+
+/* Utilities ---------------------------------- */
+
+/* specialised poll callback for these operators to work in Datablocks view only */
+static int ed_operator_outliner_datablocks_active(bContext *C)
+{
+ ScrArea *sa= CTX_wm_area(C);
+ if ((sa) && (sa->spacetype==SPACE_OOPS)) {
+ SpaceOops *so= (SpaceOops *)CTX_wm_space_data(C);
+ return ((so->type == SO_OUTLINER) && (so->outlinevis == SO_DATABLOCKS));
+ }
+ return 0;
+}
+
+
/* find the 'active' KeyingSet, and add if not found (if adding is allowed) */
// TODO: should this be an API func?
static KeyingSet *verify_active_keyingset(Scene *scene, short add)
{
KeyingSet *ks= NULL;
+ /* sanity check */
+ if (scene == NULL)
+ return NULL;
+
/* try to find one from scene */
if (scene->active_keyingset > 0)
ks= BLI_findlink(&scene->keyingsets, scene->active_keyingset-1);
@@ -3113,22 +3139,183 @@ static KeyingSet *verify_active_keyingset(Scene *scene, short add)
return ks;
}
-/* ---------------------------------- */
+/* helper func to add a new KeyingSet Path */
+static void ks_editop_add_cb(SpaceOops *soops, KeyingSet *ks, TreeElement *te, TreeStoreElem *tselem)
+{
+ ListBase hierarchy = {NULL, NULL};
+ LinkData *ld;
+ TreeElement *tem;
+ TreeStoreElem *tse;
+ PointerRNA *ptr;
+ PropertyRNA *prop;
+ ID *id = NULL;
+ char *path=NULL, *newpath=NULL;
+ int array_index= 0;
+ int flag= KSP_FLAG_GROUP_KSNAME;
+
+ /* optimise tricks:
+ * - Don't do anything if the selected item is a 'struct', but arrays are allowed
+ */
+ if (tselem->type == TSE_RNA_STRUCT)
+ return;
+
+ //printf("ks_editop_add_cb() \n");
+
+ /* Overview of Algorithm:
+ * 1. Go up the chain of parents until we find the 'root', taking note of the
+ * levels encountered in reverse-order (i.e. items are added to the start of the list
+ * for more convenient looping later)
+ * 2. Walk down the chain, adding from the first ID encountered
+ * (which will become the 'ID' for the KeyingSet Path), and build a
+ * path as we step through the chain
+ */
+ // XXX do we want to separate this part out to a helper func for the other editing op at some point?
+
+ /* step 1: flatten out hierarchy of parents into a flat chain */
+ for (tem= te->parent; tem; tem= tem->parent) {
+ ld= MEM_callocN(sizeof(LinkData), "LinkData for ks_editop_add_cb()");
+ ld->data= tem;
+ BLI_addhead(&hierarchy, ld);
+ }
+
+ /* step 2: step down hierarchy building the path (NOTE: addhead in previous loop was needed so that we can loop like this) */
+ for (ld= hierarchy.first; ld; ld= ld->next) {
+ /* get data */
+ tem= (TreeElement *)ld->data;
+ tse= TREESTORE(tem);
+ ptr= &tem->rnaptr;
+ prop= tem->directdata;
+
+ /* check if we're looking for first ID, or appending to path */
+ if (id) {
+ /* just 'append' property to path
+ * - to prevent memory leaks, we must write to newpath not path, then free old path + swap them
+ */
+ // TODO: how to do this?
+ //newpath= RNA_path_append(path, NULL, prop, index, NULL);
+
+ if (path) MEM_freeN(path);
+ path= newpath;
+ }
+ else {
+ /* no ID, so check if entry is RNA-struct, and if that RNA-struct is an ID datablock to extract info from */
+ if (tse->type == TSE_RNA_STRUCT) {
+ /* ptr->data not ptr->id.data seems to be the one we want, since ptr->data is sometimes the owner of this ID? */
+ if (RNA_struct_is_ID(ptr))
+ id= (ID *)ptr->data;
+ }
+ }
+ }
+
+ /* step 3: if we've got an ID, add the current item to the path */
+ if (id) {
+ /* add the active property to the path */
+ // if array base, add KSP_FLAG_WHOLE_ARRAY
+ ptr= &te->rnaptr;
+ prop= te->directdata;
+
+ /* array checks */
+ if (tselem->type == TSE_RNA_ARRAY_ELEM) {
+ /* item is part of an array, so must set the array_index */
+ array_index= te->index;
+ }
+ else if (RNA_property_array_length(ptr, prop)) {
+ /* entire array was selected, so keyframe all */
+ flag |= KSP_FLAG_WHOLE_ARRAY;
+ }
+
+ /* path */
+ newpath= RNA_path_append(path, NULL, prop, 0, NULL);
+ if (path) MEM_freeN(path);
+ path= newpath;
+
+ printf("Adding KeyingSet '%s': Path %s %d \n", ks->name, path, array_index);
+
+ /* add a new path with the information obtained (only if valid) */
+ // TODO: what do we do with group name? for now, we don't supply one, and just let this use the KeyingSet name
+ if (path)
+ BKE_keyingset_add_destination(ks, id, NULL, path, array_index, flag);
+ }
+
+ /* free temp data */
+ if (path) MEM_freeN(path);
+ BLI_freelistN(&hierarchy);
+}
+
+/* Recursively iterate over tree, finding and working on selected items */
+static void do_outliner_keyingset_editop(SpaceOops *soops, KeyingSet *ks, ListBase *tree, ksEditOp edit_cb)
+{
+ TreeElement *te;
+ TreeStoreElem *tselem;
+
+ for (te= tree->first; te; te=te->next) {
+ tselem= TREESTORE(te);
+
+ /* if item is selected, perform operation */
+ if (tselem->flag & TSE_SELECTED) {
+ if (edit_cb) edit_cb(soops, ks, te, tselem);
+ }
+
+ /* go over sub-tree */
+ if ((tselem->flag & TSE_CLOSED)==0)
+ do_outliner_keyingset_editop(soops, ks, &te->subtree, edit_cb);
+ }
+}
+/* Add Operator ---------------------------------- */
+
+static int outliner_keyingset_additems_exec(bContext *C, wmOperator *op)
+{
+ SpaceOops *soutliner= (SpaceOops*)CTX_wm_space_data(C);
+ Scene *scene= CTX_data_scene(C);
+ KeyingSet *ks= verify_active_keyingset(scene, 1);
+
+ /* check for invalid states */
+ if (ks == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Operation requires an Active Keying Set");
+ return OPERATOR_CANCELLED;
+ }
+ if (soutliner == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* recursively go into tree, adding selected items */
+ // TODO: make the last arg a callback func instead...
+ do_outliner_keyingset_editop(soutliner, ks, &soutliner->tree, ks_editop_add_cb);
+
+ /* send notifiers */
+ WM_event_add_notifier(C, NC_SCENE|ND_KEYINGSET, NULL);
+
+ return OPERATOR_FINISHED;
+}
void OUTLINER_OT_keyingset_add_selected(wmOperatorType *ot)
{
+ /* identifiers */
ot->idname= "OUTLINER_OT_keyingset_add_selected";
ot->name= "Keyingset Add Selected";
+
+ /* api callbacks */
+ ot->exec= outliner_keyingset_additems_exec;
+ ot->poll= ed_operator_outliner_datablocks_active;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO;
}
-/* ---------------------------------- */
+/* Remove Operator ---------------------------------- */
void OUTLINER_OT_keyingset_remove_selected(wmOperatorType *ot)
{
+ /* identifiers */
ot->idname= "OUTLINER_OT_keyingset_remove_selected";
ot->name= "Keyingset Remove Selected";
+
+ /* api callbacks */
+ ot->poll= ed_operator_outliner_datablocks_active;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO;
}
/* ***************** DRAW *************** */