diff options
author | Joshua Leung <aligorith@gmail.com> | 2009-02-12 13:41:57 +0300 |
---|---|---|
committer | Joshua Leung <aligorith@gmail.com> | 2009-02-12 13:41:57 +0300 |
commit | 4075c601336967f1714d8eee12e24b3b1f503978 (patch) | |
tree | 9311db72f035fe3d8a6e5b252be76234d78f2a08 /source | |
parent | 517d9b882a75f177e68c83252e5b560b735b1842 (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.
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/intern/anim_sys.c | 2 | ||||
-rw-r--r-- | source/blender/editors/animation/keyframing.c | 186 | ||||
-rw-r--r-- | source/blender/editors/space_graph/graph_draw.c | 2 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner.c | 193 |
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 *************** */ |