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
This commit is contained in:
parent
057cb7e5e7
commit
5d59b38605
Notes:
blender-bot
2023-04-17 13:25:03 +02:00
Referenced by issue #96964, Setting a manual frame range in an action is only recognized after reloading the file Referenced by issue #54724, Workflow improvements for creating Animation Loops Referenced by issue #107030, `action.frame_range` span always >=1 when there's only 1 key frame in it.
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -1573,6 +1573,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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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). */
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
/* ACTION CREATION */
|
||||
|
||||
/* Helper function to find the active AnimData block from the Action Editor context */
|
||||
AnimData *ED_actedit_animdata_from_context(bContext *C, ID **r_adt_id_owner)
|
||||
AnimData *ED_actedit_animdata_from_context(const bContext *C, ID **r_adt_id_owner)
|
||||
{
|
||||
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
|
|
|
@ -131,6 +131,54 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region)
|
|||
/* extra padding for lengths (to go under scrollers) */
|
||||
#define EXTRA_SCROLL_PAD 100.0f
|
||||
|
||||
/* Draw manually set intended playback frame ranges for actions. */
|
||||
static void draw_channel_action_ranges(bAnimContext *ac, ListBase *anim_data, View2D *v2d)
|
||||
{
|
||||
/* Variables for coalescing the Y region of one action. */
|
||||
bAction *cur_action = NULL;
|
||||
AnimData *cur_adt = NULL;
|
||||
float cur_ymax;
|
||||
|
||||
/* Walk through channels, grouping contiguous spans referencing the same action. */
|
||||
float ymax = ACHANNEL_FIRST_TOP(ac) + ACHANNEL_SKIP / 2;
|
||||
float ystep = ACHANNEL_STEP(ac);
|
||||
float ymin = ymax - ystep;
|
||||
|
||||
for (bAnimListElem *ale = anim_data->first; ale; ale = ale->next, ymax = ymin, ymin -= ystep) {
|
||||
bAction *action = NULL;
|
||||
AnimData *adt = NULL;
|
||||
|
||||
/* check if visible */
|
||||
if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
|
||||
IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax)) {
|
||||
/* check if anything to show for this channel */
|
||||
if (ale->datatype != ALE_NONE) {
|
||||
action = ANIM_channel_action_get(ale);
|
||||
|
||||
if (action) {
|
||||
adt = ale->adt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Extend the current region, or flush and restart. */
|
||||
if (action != cur_action || adt != cur_adt) {
|
||||
if (cur_action) {
|
||||
ANIM_draw_action_framerange(cur_adt, cur_action, v2d, ymax, cur_ymax);
|
||||
}
|
||||
|
||||
cur_action = action;
|
||||
cur_adt = adt;
|
||||
cur_ymax = ymax;
|
||||
}
|
||||
}
|
||||
|
||||
/* Flush the last region. */
|
||||
if (cur_action) {
|
||||
ANIM_draw_action_framerange(cur_adt, cur_action, v2d, ymax, cur_ymax);
|
||||
}
|
||||
}
|
||||
|
||||
/* draw keyframes in each channel */
|
||||
void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region)
|
||||
{
|
||||
|
@ -166,6 +214,13 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region
|
|||
int height = ACHANNEL_TOT_HEIGHT(ac, items);
|
||||
v2d->tot.ymin = -height;
|
||||
|
||||
/* Draw the manual frame ranges for actions in the background of the dopesheet.
|
||||
* The action editor has already drawn the range for its action so it's not needed. */
|
||||
if (ac->datatype == ANIMCONT_DOPESHEET) {
|
||||
draw_channel_action_ranges(ac, &anim_data, v2d);
|
||||
}
|
||||
|
||||
/* Draw the background strips. */
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "DNA_action_types.h"
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
@ -35,6 +36,7 @@
|
|||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_nla.h"
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
@ -204,6 +206,13 @@ static void action_main_region_draw(const bContext *C, ARegion *region)
|
|||
/* start and end frame */
|
||||
ANIM_draw_framerange(scene, v2d);
|
||||
|
||||
/* Draw the manually set intended playback frame range highlight in the Action editor. */
|
||||
if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY) && saction->action) {
|
||||
AnimData *adt = ED_actedit_animdata_from_context(C, NULL);
|
||||
|
||||
ANIM_draw_action_framerange(adt, saction->action, v2d, -FLT_MAX, FLT_MAX);
|
||||
}
|
||||
|
||||
/* data */
|
||||
if (ANIM_animdata_get_context(C, &ac)) {
|
||||
draw_channel_strips(&ac, saction, region);
|
||||
|
|
|
@ -111,11 +111,11 @@ static void nla_action_draw_keyframes(
|
|||
|
||||
/* draw a darkened region behind the strips
|
||||
* - get and reset the background color, this time without the alpha to stand out better
|
||||
* (amplified alpha is used instead)
|
||||
* (amplified alpha is used instead, but clamped to avoid 100% opacity)
|
||||
*/
|
||||
float color[4];
|
||||
nla_action_get_color(adt, act, color);
|
||||
color[3] *= 2.5f;
|
||||
color[3] = min_ff(0.7f, color[3] * 2.5f);
|
||||
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
||||
|
@ -786,6 +786,11 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
|
|||
case ANIMTYPE_NLAACTION: {
|
||||
AnimData *adt = ale->adt;
|
||||
|
||||
/* Draw the manually set intended playback frame range highlight. */
|
||||
if (ale->data) {
|
||||
ANIM_draw_action_framerange(adt, ale->data, v2d, ymin, ymax);
|
||||
}
|
||||
|
||||
uint pos = GPU_vertformat_attr_add(
|
||||
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
||||
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
||||
|
|
|
@ -2193,8 +2193,19 @@ static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op))
|
|||
* and recalculate the extents of the action now that it has been scaled
|
||||
* but leave everything else alone
|
||||
*/
|
||||
const float start = nlastrip_get_frame(strip, strip->actstart, NLATIME_CONVERT_MAP);
|
||||
const float end = nlastrip_get_frame(strip, strip->actend, NLATIME_CONVERT_MAP);
|
||||
|
||||
if (strip->act->flag & ACT_FRAME_RANGE) {
|
||||
strip->act->frame_start = nlastrip_get_frame(
|
||||
strip, strip->act->frame_start, NLATIME_CONVERT_MAP);
|
||||
strip->act->frame_end = nlastrip_get_frame(
|
||||
strip, strip->act->frame_end, NLATIME_CONVERT_MAP);
|
||||
}
|
||||
|
||||
strip->scale = 1.0f;
|
||||
calc_action_range(strip->act, &strip->actstart, &strip->actend, 0);
|
||||
strip->actstart = start;
|
||||
strip->actend = end;
|
||||
|
||||
ale->update |= ANIM_UPDATE_DEPS;
|
||||
}
|
||||
|
|
|
@ -682,6 +682,10 @@ typedef struct bAction {
|
|||
int idroot;
|
||||
char _pad[4];
|
||||
|
||||
/** Start and end of the manually set intended playback frame range. Used by UI and
|
||||
* some editing tools, but doesn't directly affect animation evaluation in any way. */
|
||||
float frame_start, frame_end;
|
||||
|
||||
PreviewImage *preview;
|
||||
} bAction;
|
||||
|
||||
|
@ -695,6 +699,8 @@ typedef enum eAction_Flags {
|
|||
ACT_MUTED = (1 << 9),
|
||||
/* ACT_PROTECTED = (1 << 10), */ /* UNUSED */
|
||||
/* ACT_DISABLED = (1 << 11), */ /* UNUSED */
|
||||
/** The action has a manually set intended playback frame range. */
|
||||
ACT_FRAME_RANGE = (1 << 12),
|
||||
} eAction_Flags;
|
||||
|
||||
/* ************************************************ */
|
||||
|
|
|
@ -246,12 +246,60 @@ static void rna_Action_active_pose_marker_index_range(
|
|||
*max = max_ii(0, BLI_listbase_count(&act->markers) - 1);
|
||||
}
|
||||
|
||||
static void rna_Action_frame_range_get(PointerRNA *ptr, float *values)
|
||||
static void rna_Action_frame_range_get(PointerRNA *ptr, float *r_values)
|
||||
{
|
||||
BKE_action_get_frame_range((bAction *)ptr->owner_id, &r_values[0], &r_values[1]);
|
||||
}
|
||||
|
||||
static void rna_Action_frame_range_set(PointerRNA *ptr, const float *values)
|
||||
{
|
||||
bAction *data = (bAction *)ptr->owner_id;
|
||||
|
||||
data->flag |= ACT_FRAME_RANGE;
|
||||
data->frame_start = values[0];
|
||||
data->frame_end = values[1];
|
||||
CLAMP_MIN(data->frame_end, data->frame_start);
|
||||
}
|
||||
|
||||
static void rna_Action_curve_frame_range_get(PointerRNA *ptr, float *values)
|
||||
{ /* don't include modifiers because they too easily can have very large
|
||||
* ranges: MINAFRAMEF to MAXFRAMEF. */
|
||||
calc_action_range((bAction *)ptr->owner_id, values, values + 1, false);
|
||||
}
|
||||
|
||||
static void rna_Action_use_frame_range_set(PointerRNA *ptr, bool value)
|
||||
{
|
||||
bAction *data = (bAction *)ptr->owner_id;
|
||||
|
||||
if (value) {
|
||||
/* If the frame range is blank, initialize it by scanning F-Curves. */
|
||||
if ((data->frame_start == data->frame_end) && (data->frame_start == 0)) {
|
||||
calc_action_range(data, &data->frame_start, &data->frame_end, false);
|
||||
}
|
||||
|
||||
data->flag |= ACT_FRAME_RANGE;
|
||||
}
|
||||
else {
|
||||
data->flag &= ~ACT_FRAME_RANGE;
|
||||
}
|
||||
}
|
||||
|
||||
static void rna_Action_start_frame_set(PointerRNA *ptr, float value)
|
||||
{
|
||||
bAction *data = (bAction *)ptr->owner_id;
|
||||
|
||||
data->frame_start = value;
|
||||
CLAMP_MIN(data->frame_end, data->frame_start);
|
||||
}
|
||||
|
||||
static void rna_Action_end_frame_set(PointerRNA *ptr, float value)
|
||||
{
|
||||
bAction *data = (bAction *)ptr->owner_id;
|
||||
|
||||
data->frame_end = value;
|
||||
CLAMP_MAX(data->frame_start, data->frame_end);
|
||||
}
|
||||
|
||||
/* Used to check if an action (value pointer)
|
||||
* is suitable to be assigned to the ID-block that is ptr. */
|
||||
bool rna_Action_id_poll(PointerRNA *ptr, PointerRNA value)
|
||||
|
@ -834,17 +882,63 @@ static void rna_def_action(BlenderRNA *brna)
|
|||
rna_def_action_pose_markers(brna, prop);
|
||||
|
||||
/* properties */
|
||||
prop = RNA_def_property(srna, "use_frame_range", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", ACT_FRAME_RANGE);
|
||||
RNA_def_property_boolean_funcs(prop, NULL, "rna_Action_use_frame_range_set");
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Manual Frame Range",
|
||||
"Manually specify the intended playback frame range for the action "
|
||||
"(this range is used by some tools, but does not affect animation evaluation)");
|
||||
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_TIME);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "frame_start");
|
||||
RNA_def_property_float_funcs(prop, NULL, "rna_Action_start_frame_set", NULL);
|
||||
RNA_def_property_ui_range(prop, MINFRAME, MAXFRAME, 100, 0);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Start Frame", "The start frame of the manually set intended playback range");
|
||||
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_TIME);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "frame_end");
|
||||
RNA_def_property_float_funcs(prop, NULL, "rna_Action_end_frame_set", NULL);
|
||||
RNA_def_property_ui_range(prop, MINFRAME, MAXFRAME, 100, 0);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "End Frame", "The end frame of the manually set intended playback range");
|
||||
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
|
||||
|
||||
prop = RNA_def_float_vector(
|
||||
srna,
|
||||
"frame_range",
|
||||
2,
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
"Frame Range",
|
||||
"The intended playback frame range of this action, using the manually set range "
|
||||
"if available, or the combined frame range of all F-Curves within this action "
|
||||
"if not (assigning sets the manual frame range)",
|
||||
0,
|
||||
0);
|
||||
RNA_def_property_float_funcs(
|
||||
prop, "rna_Action_frame_range_get", "rna_Action_frame_range_set", NULL);
|
||||
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
|
||||
|
||||
prop = RNA_def_float_vector(srna,
|
||||
"frame_range",
|
||||
"curve_frame_range",
|
||||
2,
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
"Frame Range",
|
||||
"The final frame range of all F-Curves within this action",
|
||||
"Curve Frame Range",
|
||||
"The combined frame range of all F-Curves within this action",
|
||||
0,
|
||||
0);
|
||||
RNA_def_property_float_funcs(prop, "rna_Action_frame_range_get", NULL, NULL);
|
||||
RNA_def_property_float_funcs(prop, "rna_Action_curve_frame_range_get", NULL, NULL);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
/* special "type" limiter - should not really be edited in general,
|
||||
|
|
Loading…
Reference in New Issue