diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 2708239a4c3..f7eaff475fa 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1199,6 +1199,39 @@ static void animsys_evaluate_drivers (PointerRNA *ptr, AnimData *adt, float ctim /* ***************************************** */ /* Actions Evaluation */ +/* strictly not necessary for actual "evaluation", but it is a useful safety check + * to reduce the amount of times that users end up having to "revive" wrongly-assigned + * actions + */ +static void action_idcode_patch_check (ID *id, bAction *act) +{ + int idcode = 0; + + /* just in case */ + if (ELEM(NULL, id, act)) + return; + else + idcode = GS(id->name); + + /* the actual checks... hopefully not too much of a performance hit in the long run... */ + if (act->idroot == 0) { + /* use the current root if not set already (i.e. newly created actions and actions from 2.50-2.57 builds) + * - this has problems if there are 2 users, and the first one encountered is the invalid one + * in which case, the user will need to manually fix this (?) + */ + act->idroot = idcode; + } + else if (act->idroot != idcode) { + /* only report this error if debug mode is enabled (to save performance everywhere else) */ + if (G.f & G_DEBUG) { + printf("AnimSys Safety Check Failed: Action '%s' is not meant to be used from ID-Blocks of type %d such as '%s'\n", + act->id.name+2, idcode, id->name); + } + } +} + +/* ----------------------------------------- */ + /* Evaluate Action Group */ void animsys_evaluate_action_group (PointerRNA *ptr, bAction *act, bActionGroup *agrp, AnimMapper *remap, float ctime) { @@ -1208,6 +1241,8 @@ void animsys_evaluate_action_group (PointerRNA *ptr, bAction *act, bActionGroup if ELEM(NULL, act, agrp) return; if ((remap) && (remap->target != act)) remap= NULL; + action_idcode_patch_check(ptr->id.data, act); + /* if group is muted, don't evaluated any of the F-Curve */ if (agrp->flag & AGRP_MUTED) return; @@ -1231,6 +1266,8 @@ void animsys_evaluate_action (PointerRNA *ptr, bAction *act, AnimMapper *remap, if (act == NULL) return; if ((remap) && (remap->target != act)) remap= NULL; + action_idcode_patch_check(ptr->id.data, act); + /* calculate then execute each curve */ animsys_evaluate_fcurves(ptr, &act->curves, remap, ctime); } @@ -1630,6 +1667,17 @@ static void nlastrip_evaluate_actionclip (PointerRNA *ptr, ListBase *channels, L FCurve *fcu; float evaltime; + /* sanity checks for action */ + if (strip == NULL) + return; + + if (strip->act == NULL) { + printf("NLA-Strip Eval Error: Strip '%s' has no Action\n", strip->name); + return; + } + + action_idcode_patch_check(ptr->id.data, strip->act); + /* join this strip's modifiers to the parent's modifiers (own modifiers first) */ nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers); diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 08e0ad4f3ff..689e17958fe 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -1372,7 +1372,7 @@ static void icu_to_fcurves (ID *id, ListBase *groups, ListBase *list, IpoCurve * * This does not assume that any ID or AnimData uses it, but does assume that * it is given two lists, which it will perform driver/animation-data separation. */ -static void ipo_to_animato (ID *id, Ipo *ipo, char actname[], char constname[], Sequence * seq, ListBase *animgroups, ListBase *anim, ListBase *drivers) +static void ipo_to_animato (ID *id, Ipo *ipo, char actname[], char constname[], Sequence *seq, ListBase *animgroups, ListBase *anim, ListBase *drivers) { IpoCurve *icu; @@ -1804,6 +1804,10 @@ void do_versions_ipos_to_animato(Main *main) BLI_freelinkN(&ob->constraintChannels, conchan); } } + + /* object's action will always be object-rooted */ + if (adt->action) + adt->action->idroot = ID_OB; } /* shapekeys */ @@ -1822,6 +1826,10 @@ void do_versions_ipos_to_animato(Main *main) /* Convert Shapekey data... */ ipo_to_animdata(id, key->ipo, NULL, NULL, NULL); + + if (adt->action) + adt->action->idroot = key->ipo->blocktype; + key->ipo->id.us--; key->ipo= NULL; } @@ -1840,6 +1848,10 @@ void do_versions_ipos_to_animato(Main *main) /* Convert Material data... */ ipo_to_animdata(id, ma->ipo, NULL, NULL, NULL); + + if (adt->action) + adt->action->idroot = ma->ipo->blocktype; + ma->ipo->id.us--; ma->ipo= NULL; } @@ -1858,6 +1870,10 @@ void do_versions_ipos_to_animato(Main *main) /* Convert World data... */ ipo_to_animdata(id, wo->ipo, NULL, NULL, NULL); + + if (adt->action) + adt->action->idroot = wo->ipo->blocktype; + wo->ipo->id.us--; wo->ipo= NULL; } @@ -1904,6 +1920,10 @@ void do_versions_ipos_to_animato(Main *main) /* convert IPO */ ipo_to_animdata((ID *)scene, seq->ipo, NULL, NULL, seq); + + if (adt->action) + adt->action->idroot = ID_SCE; /* scene-rooted */ + seq->ipo->id.us--; seq->ipo = NULL; } @@ -1925,6 +1945,10 @@ void do_versions_ipos_to_animato(Main *main) /* Convert Texture data... */ ipo_to_animdata(id, te->ipo, NULL, NULL, NULL); + + if (adt->action) + adt->action->idroot = te->ipo->blocktype; + te->ipo->id.us--; te->ipo= NULL; } @@ -1943,6 +1967,10 @@ void do_versions_ipos_to_animato(Main *main) /* Convert Camera data... */ ipo_to_animdata(id, ca->ipo, NULL, NULL, NULL); + + if (adt->action) + adt->action->idroot = ca->ipo->blocktype; + ca->ipo->id.us--; ca->ipo= NULL; } @@ -1961,6 +1989,10 @@ void do_versions_ipos_to_animato(Main *main) /* Convert Lamp data... */ ipo_to_animdata(id, la->ipo, NULL, NULL, NULL); + + if (adt->action) + adt->action->idroot = la->ipo->blocktype; + la->ipo->id.us--; la->ipo= NULL; } @@ -1979,6 +2011,10 @@ void do_versions_ipos_to_animato(Main *main) /* Convert Curve data... */ ipo_to_animdata(id, cu->ipo, NULL, NULL, NULL); + + if (adt->action) + adt->action->idroot = cu->ipo->blocktype; + cu->ipo->id.us--; cu->ipo= NULL; } @@ -2001,6 +2037,10 @@ void do_versions_ipos_to_animato(Main *main) if (G.f & G_DEBUG) printf("\tconverting action %s \n", id->name+2); + /* if old action, it will be object-only... */ + if (act->chanbase.first) + act->idroot = ID_OB; + /* be careful! some of the actions we encounter will be converted ones... */ action_to_animato(NULL, act, &act->groups, &act->curves, &drivers); } @@ -2018,6 +2058,7 @@ void do_versions_ipos_to_animato(Main *main) /* add a new action for this, and convert all data into that action */ new_act= add_empty_action("ConvIPO_Action"); // XXX need a better name... ipo_to_animato(NULL, ipo, NULL, NULL, NULL, NULL, &new_act->curves, &drivers); + new_act->idroot = ipo->blocktype; } /* clear fake-users, and set user-count to zero to make sure it is cleared on file-save */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 4ff65aeb213..a87d993bc15 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1863,6 +1863,10 @@ static void lib_link_nladata_strips(FileData *fd, ID *id, ListBase *list) /* reassign the counted-reference to action */ strip->act = newlibadr_us(fd, id->lib, strip->act); + + /* fix action id-root (i.e. if it comes from a pre 2.57 .blend file) */ + if ((strip->act) && (strip->act->idroot == 0)) + strip->act->idroot = GS(id->name); } } @@ -1956,6 +1960,12 @@ static void lib_link_animdata(FileData *fd, ID *id, AnimData *adt) adt->action= newlibadr_us(fd, id->lib, adt->action); adt->tmpact= newlibadr_us(fd, id->lib, adt->tmpact); + /* fix action id-roots (i.e. if they come from a pre 2.57 .blend file) */ + if ((adt->action) && (adt->action->idroot == 0)) + adt->action->idroot = GS(id->name); + if ((adt->tmpact) && (adt->tmpact->idroot == 0)) + adt->tmpact->idroot = GS(id->name); + /* link drivers */ lib_link_fcurves(fd, id, &adt->drivers); diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 0bcf84c1870..77c91b28a63 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -271,6 +271,12 @@ static int nlaedit_add_actionclip_exec (bContext *C, wmOperator *op) //printf("Add strip - actname = '%s' \n", actname); return OPERATOR_CANCELLED; } + else if (act->idroot == 0) { + /* hopefully in this case (i.e. library of userless actions), the user knows what they're doing... */ + BKE_reportf(op->reports, RPT_WARNING, + "Action '%s' does not specify what datablocks it can be used on. Try setting the 'ID Root Type' setting from the Datablocks Editor for this Action to avoid future problems", + act->id.name+2); + } /* get a list of the editable tracks being shown in the NLA * - this is limited to active ones for now, but could be expanded to @@ -289,6 +295,16 @@ static int nlaedit_add_actionclip_exec (bContext *C, wmOperator *op) AnimData *adt= ale->adt; NlaStrip *strip= NULL; + /* sanity check: only apply actions of the right type for this ID + * NOTE: in the case that this hasn't been set, we've already warned the user about this already + */ + if ((act->idroot) && (act->idroot != GS(ale->id->name))) { + BKE_reportf(op->reports, RPT_ERROR, + "Couldn't add action '%s' as it cannot be used relative to ID-blocks of type '%s'", + act->id.name+2, ale->id->name); + continue; + } + /* create a new strip, and offset it to start on the current frame */ strip= add_nlastrip(act); diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 412b9bc56f6..0716d1ddbf2 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -487,6 +487,9 @@ typedef struct bAction { int flag; /* settings for this action */ int active_marker; /* index of the active marker */ + + int idroot; /* type of ID-blocks that action can be assigned to (if 0, will be set to whatever ID first evaluates it) */ + int pad; } bAction; diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c index 418451801ca..7fdb96fda6e 100644 --- a/source/blender/makesrna/intern/rna_action.c +++ b/source/blender/makesrna/intern/rna_action.c @@ -30,6 +30,7 @@ #include #include "RNA_define.h" +#include "RNA_enum_types.h" #include "rna_internal.h" @@ -194,6 +195,56 @@ static void rna_Action_frame_range_get(PointerRNA *ptr,float *values) calc_action_range(ptr->id.data, values, values+1, 1); } + +/* used to check if an action (value pointer) is suitable to be assigned to the ID-block that is ptr */ +int rna_Action_id_poll(PointerRNA *ptr, PointerRNA value) +{ + ID *srcId = (ID *)ptr->id.data; + bAction *act = (bAction *)value.id.data; + + if (act) { + /* there can still be actions that will have undefined id-root + * (i.e. floating "action-library" members) which we will not + * be able to resolve an idroot for automatically, so let these through + */ + if (act->idroot == 0) + return 1; + else if (srcId) + return GS(srcId->name) == act->idroot; + } + + return 0; +} + +/* used to check if an action (value pointer) can be assigned to Action Editor given current mode */ +int rna_Action_actedit_assign_poll(PointerRNA *ptr, PointerRNA value) +{ + SpaceAction *saction = (SpaceAction *)ptr->data; + bAction *act = (bAction *)value.id.data; + + if (act) { + /* there can still be actions that will have undefined id-root + * (i.e. floating "action-library" members) which we will not + * be able to resolve an idroot for automatically, so let these through + */ + if (act->idroot == 0) + return 1; + + if (saction) { + if (saction->mode == SACTCONT_ACTION) { + /* this is only Object-level for now... */ + return act->idroot == ID_OB; + } + else if (saction->mode == SACTCONT_SHAPEKEY) { + /* obviously shapekeys only */ + return act->idroot == ID_KE; + } + } + } + + return 0; +} + #else static void rna_def_dopesheet(BlenderRNA *brna) @@ -515,34 +566,43 @@ static void rna_def_action(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; - + srna= RNA_def_struct(brna, "Action", "ID"); RNA_def_struct_sdna(srna, "bAction"); RNA_def_struct_ui_text(srna, "Action", "A collection of F-Curves for animation"); RNA_def_struct_ui_icon(srna, ICON_ACTION); - + + /* collections */ prop= RNA_def_property(srna, "fcurves", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "curves", NULL); RNA_def_property_struct_type(prop, "FCurve"); RNA_def_property_ui_text(prop, "F-Curves", "The individual F-Curves that make up the Action"); rna_def_action_fcurves(brna, prop); - + prop= RNA_def_property(srna, "groups", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "groups", NULL); RNA_def_property_struct_type(prop, "ActionGroup"); RNA_def_property_ui_text(prop, "Groups", "Convenient groupings of F-Curves"); rna_def_action_groups(brna, prop); - + prop= RNA_def_property(srna, "pose_markers", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "markers", NULL); RNA_def_property_struct_type(prop, "TimelineMarker"); RNA_def_property_ui_text(prop, "Pose Markers", "Markers specific to this Action, for labeling poses"); rna_def_action_pose_markers(brna, prop); - + + /* properties */ prop= RNA_def_float_vector(srna, "frame_range" , 2 , NULL , 0, 0, "Frame Range" , "The final frame range of all fcurves within this action" , 0 , 0); RNA_def_property_float_funcs(prop, "rna_Action_frame_range_get" , NULL, NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - + + /* special "type" limiter - should not really be edited in general, but is still available/editable in 'emergencies' */ + prop= RNA_def_property(srna, "id_root", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "idroot"); + RNA_def_property_enum_items(prop, id_type_items); + RNA_def_property_ui_text(prop, "ID Root Type", "Type of ID-block that action can be used on. DO NOT CHANGE UNLESS YOU KNOW WHAT YOU'RE DOING"); + + /* API calls */ RNA_api_action(srna); } diff --git a/source/blender/makesrna/intern/rna_animation.c b/source/blender/makesrna/intern/rna_animation.c index 17d2e27c462..8e210a7a4cb 100644 --- a/source/blender/makesrna/intern/rna_animation.c +++ b/source/blender/makesrna/intern/rna_animation.c @@ -70,6 +70,41 @@ static int rna_AnimData_action_editable(PointerRNA *ptr) return 1; } +static void rna_AnimData_action_set(PointerRNA *ptr, PointerRNA value) +{ + ID *ownerId = (ID *)ptr->id.data; + AnimData *adt = (AnimData *)ptr->data; + + /* assume that AnimData's action can in fact be edited... */ + if ((value.data) && (ownerId)) { + bAction *act = (bAction *)value.data; + + /* action must have same type as owner */ + if (ownerId) { + if (ELEM(act->idroot, 0, GS(ownerId->name))) { + /* can set */ + adt->action = act; + } + else { + /* cannot set */ + printf("ERROR: Couldn't set Action '%s' onto ID '%s', as it doesn't have suitably rooted paths for this purpose\n", + act->id.name+2, ownerId->name); + } + } + else { + /* cannot tell if we can set, so let's just be generous... */ + printf("Warning: Set Action '%s' onto AnimData block with an unknown ID-owner. May have attached invalid data\n", + act->id.name+2); + + adt->action = act; + } + } + else { + /* just clearing the action... */ + adt->action = NULL; + } +} + /* ****************************** */ /* wrapper for poll callback */ @@ -739,6 +774,7 @@ void rna_def_animdata(BlenderRNA *brna) /* Active Action */ prop= RNA_def_property(srna, "action", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); /* this flag as well as the dynamic test must be defined for this to be editable... */ + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Action_id_poll"); RNA_def_property_editable_func(prop, "rna_AnimData_action_editable"); RNA_def_property_ui_text(prop, "Action", "Active Action for this datablock"); RNA_def_property_update(prop, NC_ANIMATION, NULL); /* this will do? */ diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 4dc98ceb0a4..1d060c8a362 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -222,6 +222,9 @@ int rna_Curve_object_poll(struct PointerRNA *ptr, struct PointerRNA value); int rna_Lattice_object_poll(struct PointerRNA *ptr, struct PointerRNA value); int rna_Mesh_object_poll(struct PointerRNA *ptr, struct PointerRNA value); +/* basic poll functions for actions (to prevent actions getting set in wrong places) */ +int rna_Action_id_poll(struct PointerRNA *ptr, struct PointerRNA value); +int rna_Action_actedit_assign_poll(struct PointerRNA *ptr, struct PointerRNA value); char *rna_TextureSlot_path(struct PointerRNA *ptr); diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c index 09837579949..9bf5f53f8b7 100644 --- a/source/blender/makesrna/intern/rna_nla.c +++ b/source/blender/makesrna/intern/rna_nla.c @@ -426,6 +426,7 @@ static void rna_def_nlastrip(BlenderRNA *brna) /* Action */ prop= RNA_def_property(srna, "action", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "act"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Action_id_poll"); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Action", "Action referenced by this strip"); RNA_def_property_update(prop, NC_ANIMATION|ND_NLA, NULL); /* this will do? */ diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 3d9d8a8e9f4..52fae14a023 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -644,7 +644,32 @@ static void rna_ConsoleLine_cursor_index_range(PointerRNA *ptr, int *min, int *m static void rna_SpaceDopeSheetEditor_action_set(PointerRNA *ptr, PointerRNA value) { SpaceAction *saction= (SpaceAction*)(ptr->data); - saction->action= value.data; + bAction *act = (bAction*)value.data; + + if ((act == NULL) || (act->idroot == 0)) { + /* just set if we're clearing the action or if the action is "amorphous" still */ + saction->action= act; + } + else { + /* action to set must strictly meet the mode criteria... */ + if (saction->mode == SACTCONT_ACTION) { + /* currently, this is "object-level" only, until we have some way of specifying this */ + if (act->idroot == ID_OB) + saction->action = act; + else + printf("ERROR: cannot assign Action '%s' to Action Editor, as action is not object-level animation\n", act->id.name+2); + } + else if (saction->mode == SACTCONT_SHAPEKEY) { + /* as the name says, "shapekey-level" only... */ + if (act->idroot == ID_KE) + saction->action = act; + else + printf("ERROR: cannot assign Action '%s' to Shape Key Editor, as action doesn't animate Shape Keys\n", act->id.name+2); + } + else { + printf("ACK: who's trying to set an action while not in a mode displaying a single Action only?\n"); + } + } } static void rna_SpaceDopeSheetEditor_action_update(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -1771,7 +1796,7 @@ static void rna_def_space_dopesheet(BlenderRNA *brna) /* data */ prop= RNA_def_property(srna, "action", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_pointer_funcs(prop, NULL, "rna_SpaceDopeSheetEditor_action_set", NULL, NULL); + RNA_def_property_pointer_funcs(prop, NULL, "rna_SpaceDopeSheetEditor_action_set", NULL, "rna_Action_actedit_assign_poll"); RNA_def_property_ui_text(prop, "Action", "Action displayed and edited in this space"); RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, "rna_SpaceDopeSheetEditor_action_update");