Commit: 7023600ecb9d3dc3230a45585b9db9933fefbdaf Author: Alexander Gavrilov Date: Mon May 3 00:03:00 2021 +0300 Branches: temp-angavrilov https://developer.blender.org/rB7023600ecb9d3dc3230a45585b9db9933fefbdaf
Animation: allow manually setting the intended playback range for actions. Some operations, e.g. adding a new action strip to NLA, require knowing the active frame range of an action. However, currently it can only be deduced by scanning the keyframes of the curves within it. This is not ideal if e.g. curves are staggered for overlap. As suggested by Nathan Vegdahl in comments to T54724, this patch adds Action properties that allow manually specifying its active frame range. The settings are exposed via a panel in the Dopesheet and Action Editor. When enabled, the range is highlighted in the background using a striped fill to distinguish it from the solid filled regular playback range. When set, the frame range is used when adding or updating NLA tracks, and by add-ons using `Action.frame_range`, e.g. FBX exporter. Differential Revision: https://developer.blender.org/D11803 =================================================================== M release/scripts/startup/bl_ui/space_dopesheet.py M release/scripts/startup/bl_ui/space_nla.py M source/blender/blenkernel/BKE_action.h M source/blender/blenkernel/intern/action.c M source/blender/blenkernel/intern/nla.c M source/blender/editors/animation/anim_draw.c M source/blender/editors/include/ED_anim_api.h M source/blender/editors/space_action/action_data.c M source/blender/editors/space_action/action_draw.c M source/blender/editors/space_action/space_action.c M source/blender/editors/space_nla/nla_draw.c M source/blender/editors/space_nla/nla_edit.c M source/blender/makesdna/DNA_action_types.h M source/blender/makesrna/intern/rna_action.c =================================================================== diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index 9b8f1cfeb0d..6e5eba7b734 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -539,6 +539,37 @@ class DOPESHEET_MT_key_transform(Menu): layout.operator("transform.transform", text="Scale").mode = 'TIME_SCALE' +class DopesheetActionPanelBase: + bl_region_type = 'UI' + bl_label = "Action" + + @classmethod + def draw_generic_panel(cls, context, layout, action): + layout.label(text=action.name, icon='ACTION') + + layout.prop(action, "use_frame_range") + + col = layout.column() + col.active = action.use_frame_range + + row = col.row(align=True) + row.prop(action, "frame_start", text="Start") + row.prop(action, "frame_end", text="End") + + +class DOPESHEET_PT_action(DopesheetActionPanelBase, Panel): + bl_space_type = 'DOPESHEET_EDITOR' + bl_category = "Item" + + @classmethod + def poll(cls, context): + return bool(context.selected_visible_actions) + + def draw(self, context): + action = context.selected_visible_actions[0] + self.draw_generic_panel(context, self.layout, action) + + ####################################### # Grease Pencil Editing @@ -792,6 +823,7 @@ classes = ( DOPESHEET_MT_snap_pie, DOPESHEET_MT_view_pie, DOPESHEET_PT_filters, + DOPESHEET_PT_action, DOPESHEET_PT_gpencil_mode, DOPESHEET_PT_gpencil_layer_masks, DOPESHEET_PT_gpencil_layer_transform, diff --git a/release/scripts/startup/bl_ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py index 9507d8296a9..62c02799a9f 100644 --- a/release/scripts/startup/bl_ui/space_nla.py +++ b/release/scripts/startup/bl_ui/space_nla.py @@ -22,6 +22,7 @@ from bpy.types import Header, Menu, Panel from bpy.app.translations import contexts as i18n_contexts from bl_ui.space_dopesheet import ( DopesheetFilterPopoverBase, + DopesheetActionPanelBase, dopesheet_filter, ) @@ -66,6 +67,21 @@ class NLA_PT_filters(DopesheetFilterPopoverBase, Panel): DopesheetFilterPopoverBase.draw_standard_filters(context, layout) +class NLA_PT_action(DopesheetActionPanelBase, Panel): + bl_space_type = 'NLA_EDITOR' + bl_category = "Strip" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + strip = context.active_nla_strip + return strip and strip.type == 'CLIP' and strip.action + + def draw(self, context): + action = context.active_nla_strip.action + self.draw_generic_panel(context, self.layout, action) + + class NLA_MT_editor_menus(Menu): bl_idname = "NLA_MT_editor_menus" bl_label = "" @@ -316,6 +332,7 @@ classes = ( NLA_MT_context_menu, NLA_MT_channel_context_menu, NLA_PT_filters, + NLA_PT_action, ) if __name__ == "__main__": # only for live edit. diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index 763e540fdd9..6d457ed32f0 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -91,6 +91,10 @@ short action_get_item_transforms(struct bAction *act, /* Some kind of bounding box operation on the action */ void calc_action_range(const struct bAction *act, float *start, float *end, short incl_modifiers); +/* Retrieve the intended playback frame range, using the manually set range if available, + * or falling back to scanning F-Curves for their first & last frames otherwise. */ +void BKE_action_get_frame_range(const struct bAction *act, float *r_start, float *r_end); + /* Does action have any motion data at all? */ bool action_has_motion(const struct bAction *act); diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 2cc1cba99cd..93f0ebd44e8 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -1574,6 +1574,24 @@ void calc_action_range(const bAction *act, float *start, float *end, short incl_ } } +/* Retrieve the intended playback frame range, using the manually set range if available, + * or falling back to scanning F-Curves for their first & last frames otherwise. */ +void BKE_action_get_frame_range(const struct bAction *act, float *r_start, float *r_end) +{ + if (act && (act->flag & ACT_FRAME_RANGE)) { + *r_start = act->frame_start; + *r_end = act->frame_end; + } + else { + calc_action_range(act, r_start, r_end, false); + } + + /* Ensure that action is at least 1 frame long (for NLA strips to have a valid length). */ + if (*r_start >= *r_end) { + *r_end = *r_start + 1.0f; + } +} + /* Return flags indicating which transforms the given object/posechannel has * - if 'curves' is provided, a list of links to these curves are also returned */ diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 124db07298d..0666c1dfc53 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -390,6 +390,11 @@ NlaStrip *BKE_nlastrip_new(bAction *act) */ strip->flag = NLASTRIP_FLAG_SELECT | NLASTRIP_FLAG_SYNC_LENGTH; + /* Disable sync for actions with a manual frame range, since it only syncs to range anyway. */ + if (act->flag & ACT_FRAME_RANGE) { + strip->flag &= ~NLASTRIP_FLAG_SYNC_LENGTH; + } + /* assign the action reference */ strip->act = act; id_us_plus(&act->id); @@ -397,7 +402,7 @@ NlaStrip *BKE_nlastrip_new(bAction *act) /* determine initial range * - strip length cannot be 0... ever... */ - calc_action_range(strip->act, &strip->actstart, &strip->actend, 0); + BKE_action_get_frame_range(strip->act, &strip->actstart, &strip->actend); strip->start = strip->actstart; strip->end = (IS_EQF(strip->actstart, strip->actend)) ? (strip->actstart + 1.0f) : @@ -1444,7 +1449,7 @@ void BKE_nlastrip_recalculate_bounds_sync_action(NlaStrip *strip) prev_actstart = strip->actstart; - calc_action_range(strip->act, &strip->actstart, &strip->actend, 0); + BKE_action_get_frame_range(strip->act, &strip->actstart, &strip->actend); /* Set start such that key's do not visually move, to preserve the overall animation result. */ strip->start += (strip->actstart - prev_actstart) * strip->scale; diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 993d10cf303..f5a5609aa2e 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -168,6 +168,74 @@ void ANIM_draw_framerange(Scene *scene, View2D *v2d) immUnbindProgram(); } +/** + * Draw manually set intended playback frame range guides for the action in the background. + * Allows specifying a subset of the Y range of the view. + */ +void ANIM_draw_action_framerange( + AnimData *adt, bAction *action, View2D *v2d, float ymin, float ymax) +{ + if ((action->flag & ACT_FRAME_RANGE) == 0) { + return; + } + + /* Compute the dimensions. */ + CLAMP_MIN(ymin, v2d->cur.ymin); + CLAMP_MAX(ymax, v2d->cur.ymax); + + if (ymin > ymax) { + return; + } + + const float sfra = BKE_nla_tweakedit_remap(adt, action->frame_start, NLATIME_CONVERT_MAP); + const float efra = BKE_nla_tweakedit_remap(adt, action->frame_end, NLATIME_CONVERT_MAP); + + /* Diagonal stripe filled area outside of the frame range. */ + GPU_blend(GPU_BLEND_ALPHA); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_DIAG_STRIPES); + + float color[4]; + UI_GetThemeColorShadeAlpha4fv(TH_BACK, -40, -50, color); + + immUniform4f("color1", color[0], color[1], color[2], color[3]); + immUniform4f("color2", 0.0f, 0.0f, 0.0f, 0.0f); + immUniform1i("size1", 2 * U.dpi_fac); + immUniform1i("size2", 4 * U.dpi_fac); + + if (sfra < efra) { + immRectf(pos, v2d->cur.xmin, ymin, sfra, ymax); + immRectf(pos, efra, ymin, v2d->cur.xmax, ymax); + } + else { + immRectf(pos, v2d->cur.xmin, ymin, v2d->cur.xmax, ymax); + } + + immUnbindProgram(); + + GPU_blend(GPU_BLEND_NONE); + + /* Thin lines where the actual frames are. */ + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColorShade(TH_BACK, -60); + + GPU_line_width(1.0f); + + immBegin(GPU_PRIM_LINES, 4); + + immVertex2f(pos, sfra, ymin); + immVertex2f(pos, sfra, ymax); + + immVertex2f(pos, efra, ymin); + immVertex2f(pos, efra, ymax); + + immEnd(); + immUnbindProgram(); +} + /* *************************************************** */ /* NLA-MAPPING UTILITIES (required for drawing and also editing keyframes). */ diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 5d5d9140a24..cab4c18211d 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -676,6 +676,10 @@ void ANIM_draw_previewrange(const struct bContext *C, struct View2D *v2d, int en /* main call to draw normal frame range indicators */ void ANIM_draw_framerange(struct Scene *scene, struct View2D *v2d); +/* Draw manually set intended playback frame range indicators for the action. */ +void ANIM_draw_action_framerange( + struct AnimData *adt, struct bAction *action, struct View2D *v2d, float ymin, float ymax); + /* ************************************************* */ /* F-MODIFIER TOOLS */ @@ -864,7 +868,8 @@ void ED_operatormacros_action(void); /* XXX: Should we be doing these here, or at all? */ /* Action Editor - Action Management */ -struct AnimData *ED_actedit_animdata_from_context(struct bContext *C, struct ID **r_adt_id_owner); +struct AnimData *ED_actedit_animdata_from_context(const struct bContext *C, + struct ID **r_adt_id_owner); void ED_animedit_unlink_action(struct bContext *C, struct ID *id, struct AnimData *adt, diff --git a/source/blender/editors/space_action/action_data.c b/source/blend @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs