/* SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup RNA */ #include #include "DNA_action_types.h" #include "DNA_anim_types.h" #include "DNA_scene_types.h" #include "BLI_utildefines.h" #include "MEM_guardedalloc.h" #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" #include "rna_internal.h" #include "WM_api.h" #include "WM_types.h" /* enum defines exported for rna_animation.c */ const EnumPropertyItem rna_enum_nla_mode_blend_items[] = { {NLASTRIP_MODE_REPLACE, "REPLACE", 0, "Replace", "The strip values replace the accumulated results by amount specified by influence"}, {NLASTRIP_MODE_COMBINE, "COMBINE", 0, "Combine", "The strip values are combined with accumulated results by appropriately using addition, " "multiplication, or quaternion math, based on channel type"}, RNA_ENUM_ITEM_SEPR, {NLASTRIP_MODE_ADD, "ADD", 0, "Add", "Weighted result of strip is added to the accumulated results"}, {NLASTRIP_MODE_SUBTRACT, "SUBTRACT", 0, "Subtract", "Weighted result of strip is removed from the accumulated results"}, {NLASTRIP_MODE_MULTIPLY, "MULTIPLY", 0, "Multiply", "Weighted result of strip is multiplied with the accumulated results"}, {0, NULL, 0, NULL, NULL}, }; const EnumPropertyItem rna_enum_nla_mode_extend_items[] = { {NLASTRIP_EXTEND_NOTHING, "NOTHING", 0, "Nothing", "Strip has no influence past its extents"}, {NLASTRIP_EXTEND_HOLD, "HOLD", 0, "Hold", "Hold the first frame if no previous strips in track, and always hold last frame"}, {NLASTRIP_EXTEND_HOLD_FORWARD, "HOLD_FORWARD", 0, "Hold Forward", "Only hold last frame"}, {0, NULL, 0, NULL, NULL}, }; #ifdef RNA_RUNTIME # include # include /* needed for some of the validation stuff... */ # include "BKE_anim_data.h" # include "BKE_fcurve.h" # include "BKE_nla.h" # include "DNA_object_types.h" # include "ED_anim_api.h" # include "DEG_depsgraph.h" # include "DEG_depsgraph_build.h" static void rna_NlaStrip_name_set(PointerRNA *ptr, const char *value) { NlaStrip *data = (NlaStrip *)ptr->data; /* copy the name first */ BLI_strncpy_utf8(data->name, value, sizeof(data->name)); /* validate if there's enough info to do so */ if (ptr->owner_id) { AnimData *adt = BKE_animdata_from_id(ptr->owner_id); BKE_nlastrip_validate_name(adt, data); } } static char *rna_NlaStrip_path(const PointerRNA *ptr) { NlaStrip *strip = (NlaStrip *)ptr->data; AnimData *adt = BKE_animdata_from_id(ptr->owner_id); /* if we're attached to AnimData, try to resolve path back to AnimData */ if (adt) { NlaTrack *nlt; NlaStrip *nls; for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) { for (nls = nlt->strips.first; nls; nls = nls->next) { if (nls == strip) { /* XXX but if we animate like this, the control will never work... */ char name_esc_nlt[sizeof(nlt->name) * 2]; char name_esc_strip[sizeof(strip->name) * 2]; BLI_str_escape(name_esc_nlt, nlt->name, sizeof(name_esc_nlt)); BLI_str_escape(name_esc_strip, strip->name, sizeof(name_esc_strip)); return BLI_sprintfN( "animation_data.nla_tracks[\"%s\"].strips[\"%s\"]", name_esc_nlt, name_esc_strip); } } } } /* no path */ return BLI_strdup(""); } static void rna_NlaStrip_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { ID *id = ptr->owner_id; ANIM_id_update(bmain, id); } static void rna_NlaStrip_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr) { DEG_relations_tag_update(bmain); rna_NlaStrip_update(bmain, scene, ptr); } static void rna_NlaStrip_transform_update(Main *bmain, Scene *scene, PointerRNA *ptr) { NlaStrip *strip = (NlaStrip *)ptr->data; BKE_nlameta_flush_transforms(strip); /* set the flag */ if ((strip->flag & NLASTRIP_FLAG_AUTO_BLENDS) && ptr->owner_id) { /* validate state to ensure that auto-blend gets applied immediately */ IdAdtTemplate *iat = (IdAdtTemplate *)ptr->owner_id; if (iat->adt) { BKE_nla_validate_state(iat->adt); } } rna_NlaStrip_update(bmain, scene, ptr); } static void rna_NlaStrip_start_frame_set(PointerRNA *ptr, float value) { /* Simply set the frame start in a valid range : if there are any NLA strips before/after, clamp * the start value. If the new start value is past-the-end, clamp it. Otherwise, set it. * * NOTE: Unless neighboring strips are transitions, NLASTRIP_MIN_LEN_THRESH is not needed, as * strips can be 'glued' to one another. If they are however, ensure transitions have a bit of * time allotted in order to be performed. */ NlaStrip *data = (NlaStrip *)ptr->data; const float limit_prev = BKE_nlastrip_compute_frame_from_previous_strip(data); const float limit_next = BKE_nlastrip_compute_frame_to_next_strip(data); CLAMP(value, limit_prev, limit_next); data->start = value; /* The ONLY case where we actively modify the value set by the user, is in case the start value * value is past the old end frame (here delta = NLASTRIP_MIN_LEN_THRESH) : * - if there's no "room" for the end frame to be placed at (new_start + delta), move old_end to * the limit, and new_start to (limit - delta) * - otherwise, do _not_ change the end frame. This property is not accessible from the UI, and * can only be set via scripts. The script should be responsible of setting the end frame. */ if (data->start > (data->end - NLASTRIP_MIN_LEN_THRESH)) { /* If past-the-allowed-end : */ if ((data->start + NLASTRIP_MIN_LEN_THRESH) > limit_next) { data->end = limit_next; data->start = data->end - NLASTRIP_MIN_LEN_THRESH; } } /* Ensure transitions are kept 'glued' to the strip : */ if (data->prev && data->prev->type == NLASTRIP_TYPE_TRANSITION) { data->prev->end = data->start; } } static void rna_NlaStrip_frame_start_ui_set(PointerRNA *ptr, float value) { NlaStrip *data = (NlaStrip *)ptr->data; /* Changing the NLA strip's start frame is exactly the same as translating it in the NLA editor. * When 'translating' the clip, the length of it should stay identical. Se we also need to set * this strip's end frame after modifying its start (to `start + (old_end - old_start)`). * Of course, we might have a few other strips on this NLA track, so we have to respect the * previous strip's end frame. * * Also, different types of NLA strips (*_CLIP, *_TRANSITION, *_META, *_SOUND) have their own * properties to respect. Needs testing on a real-world use case for the transition, meta, and * sound types. */ /* The strip's total length before modifying it & also how long we'd like it to be afterwards. */ const float striplen = data->end - data->start; /* We're only modifying one strip at a time. The start and end times of its neighbors should not * change. As such, here are the 'bookends' (frame limits) for the start position to respect : * - if a next strip exists, don't allow the strip to start after (next->end - striplen - delta), * (delta being the min length of a Nla Strip : the NLASTRIP_MIN_THRESH macro) * - if a previous strip exists, don't allow this strip to start before it (data->prev) ends * - otherwise, limit to the program limit macros defined in DNA_scene_types.h : {MINA|MAX}FRAMEF */ const float limit_prev = BKE_nlastrip_compute_frame_from_previous_strip(data); const float limit_next = BKE_nlastrip_compute_frame_to_next_strip(data) - striplen; /* For above : we want to be able to fit the entire strip before the next frame limit, so shift * the next limit by 'striplen' no matter the context. */ CLAMP(value, limit_prev, limit_next); data->start = value; if (data->type != NLASTRIP_TYPE_TRANSITION) { data->end = data->start + striplen; } /* Update properties of the prev/next strips if they are transitions to ensure consistency : */ if (data->prev && data->prev->type == NLASTRIP_TYPE_TRANSITION) { data->prev->end = data->start; } if (data->next && data->next->type == NLASTRIP_TYPE_TRANSITION) { data->next->start = data->end; } } static void rna_NlaStrip_end_frame_set(PointerRNA *ptr, float value) { NlaStrip *data = (NlaStrip *)ptr->data; const float limit_prev = BKE_nlastrip_compute_frame_from_previous_strip(data); const float limit_next = BKE_nlastrip_compute_frame_to_next_strip(data); CLAMP(value, limit_prev, limit_next); data->end = value; /* The ONLY case where we actively modify the value set by the user, is in case the start value * value is past the old end frame (here delta = NLASTRIP_MIN_LEN_THRESH): * - if there's no "room" for the end frame to be placed at (new_start + delta), move old_end to * the limit, and new_start to (limit - delta) * - otherwise, do _not_ change the end frame. This property is not accessible from the UI, and * can only be set via scripts. The script should be responsible for setting the end frame. */ if (data->end < (data->start + NLASTRIP_MIN_LEN_THRESH)) { /* If before-the-allowed-start : */ if ((data->end - NLASTRIP_MIN_LEN_THRESH) < limit_prev) { data->start = limit_prev; data->end = data->start + NLASTRIP_MIN_LEN_THRESH; } } /* Ensure transitions are kept "glued" to the strip: */ if (data->next && data->next->type == NLASTRIP_TYPE_TRANSITION) { data->next->start = data->end; } } static void rna_NlaStrip_frame_end_ui_set(PointerRNA *ptr, float value) { NlaStrip *data = (NlaStrip *)ptr->data; /* Changing the strip's end frame will update its action 'range' (defined by actstart->actend) to * accommodate the extra length of the strip. No other parameters of the strip will change. But * this means we have to get the current strip's end frame right now : */ const float old_strip_end = data->end; /* clamp value to lie within valid limits * - must not have zero or negative length strip, so cannot start before the first frame * + some minimum-strip-length threshold * - cannot end later than the start of the next strip (if present) * -> relies on the BKE_nlastrip_compute_frame_to_next_strip() function */ const float limit_prev = data->start + NLASTRIP_MIN_LEN_THRESH; const float limit_next = BKE_nlastrip_compute_frame_to_next_strip(data); CLAMP(value, limit_prev, limit_next); data->end = value; /* Only adjust transitions at this stage : */ if (data->next && data->next->type == NLASTRIP_TYPE_TRANSITION) { data->next->start = value; } /* calculate the lengths the strip and its action : * * (Meta and transitions shouldn't be updated, but clip and sound should) */ if (data->type == NLASTRIP_TYPE_CLIP || data->type == NLASTRIP_TYPE_SOUND) { float actlen = data->actend - data->actstart; if (IS_EQF(actlen, 0.0f)) { actlen = 1.0f; /* Only sanity check needed : we use this as divisor later on. */ } /* Modify the strip's action end frame, or repeat based on : * - if data->repeat == 1.0f, modify the action end frame : * - if the number of frames to subtract is the number of frames, set the action end frame * to the action start + 1 and modify the end of the strip to add that frame * - if the number of frames * - otherwise, modify the repeat property to accommodate for the new length */ float action_length_delta = (old_strip_end - data->end) / data->scale; /* If no repeats are used, then modify the action end frame : */ if (IS_EQF(data->repeat, 1.0f)) { /* If they're equal, strip has been reduced by the same amount as the whole strip length, so * clamp the action clip length to 1 frame, and add a frame to end so that len(strip)!=0 :*/ if (IS_EQF(action_length_delta, actlen)) { data->actend = data->actstart + 1.0f; data->end += 1.0f; } else if (action_length_delta < actlen) { /* Now, adjust the new strip's actend to the value it's supposed to have : */ data->actend = data->actend - action_length_delta; } /* The case where the delta is bigger than the action length should not be possible, since * data->end is guaranteed to be clamped to data->start + threshold above. */ } else { data->repeat -= (action_length_delta / actlen); } } } static void rna_NlaStrip_scale_set(PointerRNA *ptr, float value) { NlaStrip *data = (NlaStrip *)ptr->data; /* set scale value */ /* NOTE: these need to be synced with the values in the * property definition in rna_def_nlastrip() */ CLAMP(value, 0.0001f, 1000.0f); data->scale = value; /* adjust the strip extents in response to this */ BKE_nlastrip_recalculate_bounds(data); } static void rna_NlaStrip_repeat_set(PointerRNA *ptr, float value) { NlaStrip *data = (NlaStrip *)ptr->data; /* set repeat value */ /* NOTE: these need to be synced with the values in the * property definition in rna_def_nlastrip() */ CLAMP(value, 0.01f, 1000.0f); data->repeat = value; /* adjust the strip extents in response to this */ BKE_nlastrip_recalculate_bounds(data); } static void rna_NlaStrip_blend_in_set(PointerRNA *ptr, float value) { NlaStrip *data = (NlaStrip *)ptr->data; float len; /* blend-in is limited to the length of the strip, and also cannot overlap with blendout */ len = (data->end - data->start) - data->blendout; CLAMP(value, 0, len); data->blendin = value; } static void rna_NlaStrip_blend_out_set(PointerRNA *ptr, float value) { NlaStrip *data = (NlaStrip *)ptr->data; float len; /* blend-out is limited to the length of the strip */ len = (data->end - data->start); CLAMP(value, 0, len); /* it also cannot overlap with blendin */ if ((len - value) < data->blendin) { value = len - data->blendin; } data->blendout = value; } static void rna_NlaStrip_use_auto_blend_set(PointerRNA *ptr, bool value) { NlaStrip *data = (NlaStrip *)ptr->data; if (value) { /* set the flag */ data->flag |= NLASTRIP_FLAG_AUTO_BLENDS; /* validate state to ensure that auto-blend gets applied immediately */ if (ptr->owner_id) { IdAdtTemplate *iat = (IdAdtTemplate *)ptr->owner_id; if (iat->adt) { BKE_nla_validate_state(iat->adt); } } } else { /* clear the flag */ data->flag &= ~NLASTRIP_FLAG_AUTO_BLENDS; /* clear the values too, so that it's clear that there has been an effect */ /* TODO: it's somewhat debatable whether it's better to leave these in instead... */ data->blendin = 0.0f; data->blendout = 0.0f; } } static int rna_NlaStrip_action_editable(PointerRNA *ptr, const char **UNUSED(r_info)) { NlaStrip *strip = (NlaStrip *)ptr->data; /* Strip actions shouldn't be editable if NLA tweak-mode is on. */ if (ptr->owner_id) { AnimData *adt = BKE_animdata_from_id(ptr->owner_id); if (adt) { /* active action is only editable when it is not a tweaking strip */ if ((adt->flag & ADT_NLA_EDIT_ON) || (adt->actstrip) || (adt->tmpact)) { return 0; } } } /* check for clues that strip probably shouldn't be used... */ if (strip->flag & NLASTRIP_FLAG_TWEAKUSER) { return 0; } /* should be ok, though we may still miss some cases */ return PROP_EDITABLE; } static void rna_NlaStrip_action_start_frame_set(PointerRNA *ptr, float value) { NlaStrip *data = (NlaStrip *)ptr->data; /* prevent start frame from occurring after end of action */ CLAMP(value, MINAFRAME, data->actend); data->actstart = value; /* adjust the strip extents in response to this */ /* TODO: should the strip be moved backwards instead as a special case? */ BKE_nlastrip_recalculate_bounds(data); } static void rna_NlaStrip_action_end_frame_set(PointerRNA *ptr, float value) { NlaStrip *data = (NlaStrip *)ptr->data; /* prevent end frame from starting before start of action */ CLAMP(value, data->actstart, MAXFRAME); data->actend = value; /* adjust the strip extents in response to this */ BKE_nlastrip_recalculate_bounds(data); } static void rna_NlaStrip_animated_influence_set(PointerRNA *ptr, bool value) { NlaStrip *data = (NlaStrip *)ptr->data; if (value) { /* set the flag, then make sure a curve for this exists */ data->flag |= NLASTRIP_FLAG_USR_INFLUENCE; BKE_nlastrip_validate_fcurves(data); } else { data->flag &= ~NLASTRIP_FLAG_USR_INFLUENCE; } } static void rna_NlaStrip_animated_time_set(PointerRNA *ptr, bool value) { NlaStrip *data = (NlaStrip *)ptr->data; if (value) { /* set the flag, then make sure a curve for this exists */ data->flag |= NLASTRIP_FLAG_USR_TIME; BKE_nlastrip_validate_fcurves(data); } else { data->flag &= ~NLASTRIP_FLAG_USR_TIME; } } static FCurve *rna_NlaStrip_fcurve_find(NlaStrip *strip, ReportList *reports, const char *data_path, int index) { if (data_path[0] == '\0') { BKE_report(reports, RPT_ERROR, "F-Curve data path empty, invalid argument"); return NULL; } /* Returns NULL if not found. */ return BKE_fcurve_find(&strip->fcurves, data_path, index); } static NlaStrip *rna_NlaStrip_new(ID *id, NlaTrack *track, Main *bmain, bContext *C, ReportList *reports, const char *UNUSED(name), int start, bAction *action) { NlaStrip *strip = BKE_nlastrip_new(action); if (strip == NULL) { BKE_report(reports, RPT_ERROR, "Unable to create new strip"); return NULL; } strip->end += (start - strip->start); strip->start = start; if (BKE_nlastrips_add_strip(&track->strips, strip) == 0) { BKE_report( reports, RPT_ERROR, "Unable to add strip (the track does not have any space to accommodate this new strip)"); BKE_nlastrip_free(NULL, strip, true); return NULL; } /* create dummy AnimData block so that BKE_nlastrip_validate_name() * can be used to ensure a valid name, as we don't have one here... * - only the nla_tracks list is needed there, which we aim to reverse engineer here... */ { AnimData adt = {NULL}; NlaTrack *nlt, *nlt_p; /* 'first' NLA track is found by going back up chain of given * track's parents until we fall off. */ nlt_p = track; nlt = track; while ((nlt = nlt->prev) != NULL) { nlt_p = nlt; } adt.nla_tracks.first = nlt_p; /* do the same thing to find the last track */ nlt_p = track; nlt = track; while ((nlt = nlt->next) != NULL) { nlt_p = nlt; } adt.nla_tracks.last = nlt_p; /* now we can just auto-name as usual */ BKE_nlastrip_validate_name(&adt, strip); } WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_ADDED, NULL); DEG_relations_tag_update(bmain); DEG_id_tag_update_ex(bmain, id, ID_RECALC_ANIMATION | ID_RECALC_COPY_ON_WRITE); return strip; } static void rna_NlaStrip_remove( ID *id, NlaTrack *track, Main *bmain, bContext *C, ReportList *reports, PointerRNA *strip_ptr) { NlaStrip *strip = strip_ptr->data; if (BLI_findindex(&track->strips, strip) == -1) { BKE_reportf( reports, RPT_ERROR, "NLA strip '%s' not found in track '%s'", strip->name, track->name); return; } BKE_nlastrip_free(&track->strips, strip, true); RNA_POINTER_INVALIDATE(strip_ptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_REMOVED, NULL); DEG_relations_tag_update(bmain); DEG_id_tag_update_ex(bmain, id, ID_RECALC_ANIMATION | ID_RECALC_COPY_ON_WRITE); } /* Set the 'solo' setting for the given NLA-track, making sure that it is the only one * that has this status in its AnimData block. */ static void rna_NlaTrack_solo_set(PointerRNA *ptr, bool value) { NlaTrack *data = (NlaTrack *)ptr->data; AnimData *adt = BKE_animdata_from_id(ptr->owner_id); NlaTrack *nt; if (data == NULL) { return; } /* firstly, make sure 'solo' flag for all tracks is disabled */ for (nt = data; nt; nt = nt->next) { nt->flag &= ~NLATRACK_SOLO; } for (nt = data; nt; nt = nt->prev) { nt->flag &= ~NLATRACK_SOLO; } /* now, enable 'solo' for the given track if appropriate */ if (value) { /* set solo status */ data->flag |= NLATRACK_SOLO; /* set solo-status on AnimData */ adt->flag |= ADT_NLA_SOLO_TRACK; } else { /* solo status was already cleared on track */ /* clear solo-status on AnimData */ adt->flag &= ~ADT_NLA_SOLO_TRACK; } } #else static void rna_def_strip_fcurves(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; FunctionRNA *func; PropertyRNA *parm; RNA_def_property_srna(cprop, "NlaStripFCurves"); srna = RNA_def_struct(brna, "NlaStripFCurves", NULL); RNA_def_struct_sdna(srna, "NlaStrip"); RNA_def_struct_ui_text(srna, "NLA-Strip F-Curves", "Collection of NLA strip F-Curves"); /* Strip.fcurves.find(...) */ func = RNA_def_function(srna, "find", "rna_NlaStrip_fcurve_find"); RNA_def_function_ui_description( func, "Find an F-Curve. Note that this function performs a linear scan " "of all F-Curves in the NLA strip."); RNA_def_function_flag(func, FUNC_USE_REPORTS); parm = RNA_def_string(func, "data_path", NULL, 0, "Data Path", "F-Curve data path"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); RNA_def_int(func, "index", 0, 0, INT_MAX, "Index", "Array index", 0, INT_MAX); parm = RNA_def_pointer( func, "fcurve", "FCurve", "", "The found F-Curve, or None if it doesn't exist"); RNA_def_function_return(func, parm); } static void rna_def_nlastrip(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; /* enum defs */ static const EnumPropertyItem prop_type_items[] = { {NLASTRIP_TYPE_CLIP, "CLIP", 0, "Action Clip", "NLA Strip references some Action"}, {NLASTRIP_TYPE_TRANSITION, "TRANSITION", 0, "Transition", "NLA Strip 'transitions' between adjacent strips"}, {NLASTRIP_TYPE_META, "META", 0, "Meta", "NLA Strip acts as a container for adjacent strips"}, {NLASTRIP_TYPE_SOUND, "SOUND", 0, "Sound Clip", "NLA Strip representing a sound event for speakers"}, {0, NULL, 0, NULL, NULL}, }; /* struct definition */ srna = RNA_def_struct(brna, "NlaStrip", NULL); RNA_def_struct_ui_text(srna, "NLA Strip", "A container referencing an existing Action"); RNA_def_struct_path_func(srna, "rna_NlaStrip_path"); RNA_def_struct_ui_icon(srna, ICON_NLA); /* XXX */ RNA_define_lib_overridable(true); /* name property */ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_ui_text(prop, "Name", ""); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_NlaStrip_name_set"); RNA_def_struct_name_property(srna, prop); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ /* Enums */ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "type"); RNA_def_property_clear_flag( prop, PROP_EDITABLE); /* XXX for now, not editable, since this is dangerous */ RNA_def_property_enum_items(prop, prop_type_items); RNA_def_property_ui_text(prop, "Type", "Type of NLA Strip"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "extrapolation", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "extendmode"); RNA_def_property_enum_items(prop, rna_enum_nla_mode_extend_items); RNA_def_property_ui_text( prop, "Extrapolation", "Action to take for gaps past the strip extents"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "blend_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "blendmode"); RNA_def_property_enum_items(prop, rna_enum_nla_mode_blend_items); RNA_def_property_ui_text( prop, "Blending", "Method used for combining strip's result with accumulated result"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); /* Strip extents */ prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "start"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_start_frame_set", NULL); RNA_def_property_ui_text(prop, "Start Frame", ""); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "end"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_end_frame_set", NULL); RNA_def_property_ui_text(prop, "End Frame", ""); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); /* Strip extents, when called from UI elements : */ prop = RNA_def_property(srna, "frame_start_ui", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "start"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_frame_start_ui_set", NULL); RNA_def_property_ui_text( prop, "Start Frame (manipulated from UI)", "Start frame of the NLA strip. Note: changing this value also updates the value of " "the strip's end frame. If only the start frame should be changed, see the \"frame_start\" " "property instead"); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); prop = RNA_def_property(srna, "frame_end_ui", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "end"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_frame_end_ui_set", NULL); RNA_def_property_ui_text( prop, "End Frame (manipulated from UI)", "End frame of the NLA strip. Note: changing this value also updates the value of " "the strip's repeats or its action's end frame. If only the end frame should be " "changed, see the \"frame_end\" property instead"); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); /* Blending */ prop = RNA_def_property(srna, "blend_in", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "blendin"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_blend_in_set", NULL); RNA_def_property_ui_text( prop, "Blend In", "Number of frames at start of strip to fade in influence"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "blend_out", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "blendout"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_blend_out_set", NULL); RNA_def_property_ui_text(prop, "Blend Out", ""); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "use_auto_blend", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_AUTO_BLENDS); RNA_def_property_boolean_funcs(prop, NULL, "rna_NlaStrip_use_auto_blend_set"); RNA_def_property_ui_text(prop, "Auto Blend In/Out", "Number of frames for Blending In/Out is automatically determined from " "overlapping strips"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); /* 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 | PROP_ID_REFCOUNT); RNA_def_property_editable_func(prop, "rna_NlaStrip_action_editable"); RNA_def_property_ui_text(prop, "Action", "Action referenced by this strip"); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_dependency_update"); /* Action extents */ prop = RNA_def_property(srna, "action_frame_start", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "actstart"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_action_start_frame_set", NULL); RNA_def_property_ui_text(prop, "Action Start Frame", "First frame from action to use"); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); prop = RNA_def_property(srna, "action_frame_end", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "actend"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_action_end_frame_set", NULL); RNA_def_property_ui_text(prop, "Action End Frame", "Last frame from action to use"); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); /* Action Reuse */ prop = RNA_def_property(srna, "repeat", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "repeat"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_repeat_set", NULL); /* these limits have currently be chosen arbitrarily, but could be extended * (minimum should still be > 0 though) if needed... */ RNA_def_property_range(prop, 0.1f, 1000.0f); RNA_def_property_ui_text(prop, "Repeat", "Number of times to repeat the action range"); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "scale"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_scale_set", NULL); /* these limits can be extended, but beyond this, we can get some crazy+annoying bugs * due to numeric errors */ RNA_def_property_range(prop, 0.0001f, 1000.0f); RNA_def_property_ui_text(prop, "Scale", "Scaling factor for action"); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); /* Strip's F-Curves */ prop = RNA_def_property(srna, "fcurves", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "fcurves", NULL); RNA_def_property_struct_type(prop, "FCurve"); RNA_def_property_ui_text( prop, "F-Curves", "F-Curves for controlling the strip's influence and timing"); rna_def_strip_fcurves(brna, prop); /* Strip's F-Modifiers */ prop = RNA_def_property(srna, "modifiers", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "FModifier"); RNA_def_property_ui_text( prop, "Modifiers", "Modifiers affecting all the F-Curves in the referenced Action"); /* Strip's Sub-Strips (for Meta-Strips) */ prop = RNA_def_property(srna, "strips", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "NlaStrip"); RNA_def_property_ui_text( prop, "NLA Strips", "NLA Strips that this strip acts as a container for (if it is of type Meta)"); /* Settings - Values necessary for evaluation */ prop = RNA_def_property(srna, "influence", PROP_FLOAT, PROP_FACTOR); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text( prop, "Influence", "Amount the strip contributes to the current result"); /* XXX: Update temporarily disabled so that the property can be edited at all! * Even auto-key only applies after the curves have been re-evaluated, * causing the unkeyed values to be lost. */ RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, /*"rna_NlaStrip_update"*/ NULL); prop = RNA_def_property(srna, "strip_time", PROP_FLOAT, PROP_TIME); RNA_def_property_ui_text(prop, "Strip Time", "Frame of referenced Action to evaluate"); /* XXX: Update temporarily disabled so that the property can be edited at all! * Even auto-key only applies after the curves have been re-evaluated, * causing the unkeyed values to be lost. */ RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, /*"rna_NlaStrip_update"*/ NULL); /* TODO: should the animated_influence/time settings be animatable themselves? */ prop = RNA_def_property(srna, "use_animated_influence", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_INFLUENCE); RNA_def_property_boolean_funcs(prop, NULL, "rna_NlaStrip_animated_influence_set"); RNA_def_property_ui_text( prop, "Animated Influence", "Influence setting is controlled by an F-Curve rather than automatically determined"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "use_animated_time", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_TIME); RNA_def_property_boolean_funcs(prop, NULL, "rna_NlaStrip_animated_time_set"); RNA_def_property_ui_text( prop, "Animated Strip Time", "Strip time is controlled by an F-Curve rather than automatically determined"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "use_animated_time_cyclic", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_TIME_CYCLIC); RNA_def_property_ui_text( prop, "Cyclic Strip Time", "Cycle the animated time within the action start and end"); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); /* settings */ prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); /* can be made editable by hooking it up to the necessary NLA API methods */ RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_ACTIVE); RNA_def_property_ui_text(prop, "Active", "NLA Strip is active"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_SELECT); RNA_def_property_ui_text(prop, "Select", "NLA Strip is selected"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_MUTED); RNA_def_property_ui_icon(prop, ICON_CHECKBOX_HLT, -1); RNA_def_property_ui_text(prop, "Mute", "Disable NLA Strip evaluation"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "use_reverse", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_REVERSE); RNA_def_property_ui_text(prop, "Reversed", "NLA Strip is played back in reverse order (only when timing is " "automatically determined)"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "use_sync_length", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_SYNC_LENGTH); RNA_def_property_ui_text(prop, "Sync Action Length", "Update range of frames referenced from action " "after tweaking strip and its keyframes"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); RNA_define_lib_overridable(false); } static void rna_api_nlatrack_strips(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; PropertyRNA *parm; FunctionRNA *func; RNA_def_property_srna(cprop, "NlaStrips"); srna = RNA_def_struct(brna, "NlaStrips", NULL); RNA_def_struct_sdna(srna, "NlaTrack"); RNA_def_struct_ui_text(srna, "Nla Strips", "Collection of Nla Strips"); func = RNA_def_function(srna, "new", "rna_NlaStrip_new"); RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_CONTEXT | FUNC_USE_REPORTS); RNA_def_function_ui_description(func, "Add a new Action-Clip strip to the track"); parm = RNA_def_string(func, "name", "NlaStrip", 0, "", "Name for the NLA Strips"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_int(func, "start", 0, INT_MIN, INT_MAX, "Start Frame", "Start frame for this strip", INT_MIN, INT_MAX); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_pointer(func, "action", "Action", "", "Action to assign to this strip"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); /* return type */ parm = RNA_def_pointer(func, "strip", "NlaStrip", "", "New NLA Strip"); RNA_def_function_return(func, parm); func = RNA_def_function(srna, "remove", "rna_NlaStrip_remove"); RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_CONTEXT | FUNC_USE_REPORTS); RNA_def_function_ui_description(func, "Remove a NLA Strip"); parm = RNA_def_pointer(func, "strip", "NlaStrip", "", "NLA Strip to remove"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); } static void rna_def_nlatrack(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; srna = RNA_def_struct(brna, "NlaTrack", NULL); RNA_def_struct_ui_text( srna, "NLA Track", "A animation layer containing Actions referenced as NLA strips"); RNA_def_struct_ui_icon(srna, ICON_NLA); /* strips collection */ prop = RNA_def_property(srna, "strips", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "NlaStrip"); /* We do not support inserting or removing strips in overrides of tracks for now. */ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "NLA Strips", "NLA Strips on this NLA-track"); rna_api_nlatrack_strips(brna, prop); prop = RNA_def_boolean(srna, "is_override_data", false, "Override Track", "In a local override data, whether this NLA track comes from the linked " "reference data, or is local to the override"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", NLATRACK_OVERRIDELIBRARY_LOCAL); RNA_define_lib_overridable(true); /* name property */ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_ui_text(prop, "Name", ""); RNA_def_struct_name_property(srna, prop); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ /* settings */ prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); /* can be made editable by hooking it up to the necessary NLA API methods */ RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLATRACK_ACTIVE); RNA_def_property_ui_text(prop, "Active", "NLA Track is active"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ prop = RNA_def_property(srna, "is_solo", PROP_BOOLEAN, PROP_NONE); /* can be made editable by hooking it up to the necessary NLA API methods */ RNA_def_property_boolean_sdna(prop, NULL, "flag", NLATRACK_SOLO); RNA_def_property_ui_text( prop, "Solo", "NLA Track is evaluated itself (i.e. active Action and all other NLA Tracks in the " "same AnimData block are disabled)"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); RNA_def_property_boolean_funcs(prop, NULL, "rna_NlaTrack_solo_set"); prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLATRACK_SELECTED); RNA_def_property_ui_text(prop, "Select", "NLA Track is selected"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLATRACK_MUTED); RNA_def_property_ui_text(prop, "Muted", "Disable NLA Track evaluation"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "lock", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLATRACK_PROTECTED); RNA_def_property_ui_text(prop, "Locked", "NLA Track is locked"); RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ RNA_define_lib_overridable(false); } /* --------- */ void RNA_def_nla(BlenderRNA *brna) { rna_def_nlatrack(brna); rna_def_nlastrip(brna); } #endif