Animation: Move Snapping to Scene

Part of #91973

Moving the snapping code for the
* Graph Editor
* Action Editor
* and NLA editor

into the common system that lives on the scene.
This includes the Magnet icon for turning
snapping on and off.

The old settings translate to the new in the following way:
* `Frame Step` -> `Frame`
* `Second Step` -> `Second`
* `Nearest Frame` -> `Frame` + `Absolute Time Snap`
* `Nearest Second` -> `Second` + `Absolute Time Snap`
* `Nearest Marker` -> `Nearest Marker`

Since this moves the location of the snapping settings
from the editor to the scene, it changes the behavior.
Previously each editor could have different snapping
settings, where now they are all synced.

Pull Request: https://projects.blender.org/blender/blender/pulls/109015
This commit is contained in:
Christoph Lendenfeld 2023-09-05 10:06:55 +02:00 committed by Christoph Lendenfeld
parent 7365f0b094
commit 11fe57cab8
22 changed files with 239 additions and 180 deletions

View File

@ -277,7 +277,14 @@ class DOPESHEET_HT_editor_buttons:
# Grease Pencil mode doesn't need snapping, as it's frame-aligned only
if st.mode != 'GPENCIL':
layout.prop(st, "auto_snap", text="")
row = layout.row(align=True)
row.prop(tool_settings, "use_snap_anim", text="")
sub = row.row(align=True)
sub.popover(
panel="DOPESHEET_PT_snapping",
icon='NONE',
text="Modes",
)
row = layout.row(align=True)
row.prop(tool_settings, "use_proportional_action", text="", icon_only=True)
@ -292,6 +299,21 @@ class DOPESHEET_HT_editor_buttons:
)
class DOPESHEET_PT_snapping(Panel):
bl_space_type = 'DOPESHEET_EDITOR'
bl_region_type = 'HEADER'
bl_label = "Snapping"
def draw(self, context):
layout = self.layout
col = layout.column()
col.label(text="Snap To")
tool_settings = context.tool_settings
col.prop(tool_settings, "snap_anim_element", expand=True)
if tool_settings.snap_anim_element not in ('MARKER', ):
col.prop(tool_settings, "use_snap_time_absolute")
class DOPESHEET_PT_proportional_edit(Panel):
bl_space_type = 'DOPESHEET_EDITOR'
bl_region_type = 'HEADER'
@ -872,6 +894,7 @@ classes = (
DOPESHEET_PT_gpencil_layer_relations,
DOPESHEET_PT_gpencil_layer_display,
DOPESHEET_PT_custom_props_action,
DOPESHEET_PT_snapping
)
if __name__ == "__main__": # only for live edit.

View File

@ -49,7 +49,14 @@ class GRAPH_HT_header(Header):
layout.prop(st, "pivot_point", icon_only=True)
layout.prop(st, "auto_snap", text="")
row = layout.row(align=True)
row.prop(tool_settings, "use_snap_anim", text="")
sub = row.row(align=True)
sub.popover(
panel="GRAPH_PT_snapping",
icon='NONE',
text="Modes",
)
row = layout.row(align=True)
row.prop(tool_settings, "use_proportional_fcurve", text="", icon_only=True)
@ -94,6 +101,20 @@ class GRAPH_PT_filters(DopesheetFilterPopoverBase, Panel):
layout.separator()
DopesheetFilterPopoverBase.draw_standard_filters(context, layout)
class GRAPH_PT_snapping(Panel):
bl_space_type = 'GRAPH_EDITOR'
bl_region_type = 'HEADER'
bl_label = "Snapping"
def draw(self, context):
layout = self.layout
col = layout.column()
col.label(text="Snap To")
tool_settings = context.tool_settings
col.prop(tool_settings, "snap_anim_element", expand=True)
if tool_settings.snap_anim_element not in ('MARKER', ):
col.prop(tool_settings, "use_snap_time_absolute")
class GRAPH_MT_editor_menus(Menu):
bl_idname = "GRAPH_MT_editor_menus"
@ -527,6 +548,7 @@ classes = (
GRAPH_MT_snap_pie,
GRAPH_MT_view_pie,
GRAPH_PT_filters,
GRAPH_PT_snapping,
)
if __name__ == "__main__": # only for live edit.

View File

@ -33,7 +33,30 @@ class NLA_HT_header(Header):
icon='FILTER',
)
layout.prop(st, "auto_snap", text="")
row = layout.row(align=True)
tool_settings = context.tool_settings
row.prop(tool_settings, "use_snap_anim", text="")
sub = row.row(align=True)
sub.popover(
panel="NLA_PT_snapping",
icon='NONE',
text="Modes",
)
class NLA_PT_snapping(Panel):
bl_space_type = 'NLA_EDITOR'
bl_region_type = 'HEADER'
bl_label = "Snapping"
def draw(self, context):
layout = self.layout
col = layout.column()
col.label(text="Snap To")
tool_settings = context.tool_settings
col.prop(tool_settings, "snap_anim_element", expand=True)
if tool_settings.snap_anim_element not in ('MARKER', ):
col.prop(tool_settings, "use_snap_time_absolute")
class NLA_PT_filters(DopesheetFilterPopoverBase, Panel):
@ -350,6 +373,7 @@ classes = (
NLA_MT_channel_context_menu,
NLA_PT_filters,
NLA_PT_action,
NLA_PT_snapping,
)
if __name__ == "__main__": # only for live edit.

View File

@ -969,6 +969,11 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
scene->eevee.gi_irradiance_pool_size = 16;
}
}
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->toolsettings->snap_flag_anim |= SCE_SNAP;
scene->toolsettings->snap_anim_mode |= SCE_SNAP_TO_FRAME;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 20)) {

View File

@ -62,7 +62,6 @@ static SpaceLink *action_create(const ScrArea *area, const Scene *scene)
saction = MEM_cnew<SpaceAction>("initaction");
saction->spacetype = SPACE_ACTION;
saction->autosnap = SACTSNAP_FRAME;
saction->mode = SACTCONT_DOPESHEET;
saction->mode_prev = SACTCONT_DOPESHEET;
saction->flag = SACTION_SHOW_INTERPOLATION | SACTION_SHOW_MARKERS;

View File

@ -62,8 +62,6 @@ static SpaceLink *graph_create(const ScrArea * /*area*/, const Scene *scene)
sipo = static_cast<SpaceGraph *>(MEM_callocN(sizeof(SpaceGraph), "init graphedit"));
sipo->spacetype = SPACE_GRAPH;
sipo->autosnap = SACTSNAP_FRAME;
/* allocate DopeSheet data for Graph Editor */
sipo->ads = static_cast<bDopeSheet *>(MEM_callocN(sizeof(bDopeSheet), "GraphEdit DopeSheet"));
sipo->ads->source = (ID *)scene;

View File

@ -57,7 +57,6 @@ static SpaceLink *nla_create(const ScrArea *area, const Scene *scene)
snla->ads->source = (ID *)(scene);
/* set auto-snapping settings */
snla->autosnap = SACTSNAP_FRAME;
snla->flag = SNLA_SHOW_MARKERS;
/* header */

View File

@ -820,6 +820,20 @@ static void flushTransIntFrameActionData(TransInfo *t)
}
}
static void invert_snap(eSnapMode &snap_mode)
{
/* Make snapping work like before 4.0 where pressing CTRL will switch between snapping to seconds
* and frames. */
if (snap_mode & SCE_SNAP_TO_FRAME) {
snap_mode &= ~SCE_SNAP_TO_FRAME;
snap_mode |= SCE_SNAP_TO_SECOND;
}
else if (snap_mode & SCE_SNAP_TO_SECOND) {
snap_mode &= ~SCE_SNAP_TO_SECOND;
snap_mode |= SCE_SNAP_TO_FRAME;
}
}
static void recalcData_actedit(TransInfo *t)
{
ViewLayer *view_layer = t->view_layer;
@ -853,13 +867,17 @@ static void recalcData_actedit(TransInfo *t)
/* Flush 2d vector. */
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
const short autosnap = getAnimEdit_SnapMode(t);
eSnapMode snap_mode = t->tsnap.mode;
if (t->modifiers & MOD_SNAP_INVERT) {
invert_snap(snap_mode);
}
TransData *td;
TransData2D *td2d;
int i = 0;
for (td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) {
if ((autosnap != SACTSNAP_OFF) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) {
transform_snap_anim_flush_data(t, td, eAnimEdit_AutoSnap(autosnap), td->loc);
if ((t->tsnap.flag & SCE_SNAP) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) {
transform_snap_anim_flush_data(t, td, snap_mode, td->loc);
}
/* Constrain Y. */

View File

@ -644,6 +644,18 @@ static bool fcu_test_selected(FCurve *fcu)
return false;
}
static void invert_snap(eSnapMode &snap_mode)
{
if (snap_mode & SCE_SNAP_TO_FRAME) {
snap_mode &= ~SCE_SNAP_TO_FRAME;
snap_mode |= SCE_SNAP_TO_SECOND;
}
else if (snap_mode & SCE_SNAP_TO_SECOND) {
snap_mode &= ~SCE_SNAP_TO_SECOND;
snap_mode |= SCE_SNAP_TO_FRAME;
}
}
/* This function is called on recalc_data to apply the transforms applied
* to the transdata on to the actual keyframe data
*/
@ -654,9 +666,13 @@ static void flushTransGraphData(TransInfo *t)
TransDataGraph *tdg;
int a;
const short autosnap = getAnimEdit_SnapMode(t);
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
eSnapMode snap_mode = t->tsnap.mode;
if (t->modifiers & MOD_SNAP_INVERT) {
invert_snap(snap_mode);
}
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* flush to 2d vector from internally used 3d vector */
for (a = 0,
td = tc->data,
@ -675,8 +691,8 @@ static void flushTransGraphData(TransInfo *t)
* - Only apply to keyframes (but never to handles).
* - Don't do this when canceling, or else these changes won't go away.
*/
if ((autosnap != SACTSNAP_OFF) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) {
transform_snap_anim_flush_data(t, td, eAnimEdit_AutoSnap(autosnap), td->loc);
if ((t->tsnap.flag & SCE_SNAP) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) {
transform_snap_anim_flush_data(t, td, snap_mode, td->loc);
}
/* we need to unapply the nla-mapping from the time in some situations */

View File

@ -629,6 +629,18 @@ static void createTransNlaData(bContext *C, TransInfo *t)
ANIM_animdata_freelist(&anim_data);
}
static void invert_snap(eSnapMode &snap_mode)
{
if (snap_mode & SCE_SNAP_TO_FRAME) {
snap_mode &= ~SCE_SNAP_TO_FRAME;
snap_mode |= SCE_SNAP_TO_SECOND;
}
else if (snap_mode & SCE_SNAP_TO_SECOND) {
snap_mode &= ~SCE_SNAP_TO_SECOND;
snap_mode |= SCE_SNAP_TO_FRAME;
}
}
static void recalcData_nla(TransInfo *t)
{
SpaceNla *snla = (SpaceNla *)t->area->spacedata.first;
@ -639,11 +651,14 @@ static void recalcData_nla(TransInfo *t)
* NOTE: only do this when transform is still running, or we can't restore
*/
if (t->state != TRANS_CANCEL) {
const short autosnap = getAnimEdit_SnapMode(t);
if (autosnap != SACTSNAP_OFF) {
if (t->tsnap.flag & SCE_SNAP) {
eSnapMode snap_mode = t->tsnap.mode;
if (t->modifiers & MOD_SNAP_INVERT) {
invert_snap(snap_mode);
}
TransData *td = tc->data;
for (int i = 0; i < tc->data_len; i++, td++) {
transform_snap_anim_flush_data(t, td, eAnimEdit_AutoSnap(autosnap), td->loc);
transform_snap_anim_flush_data(t, td, snap_mode, td->loc);
}
}
}

View File

@ -43,30 +43,26 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR])
outputNumInput(&(t->num), tvec, &t->scene->unit);
}
else {
const short autosnap = getAnimEdit_SnapMode(t);
eSnapMode snap_mode = t->tsnap.mode;
float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->data->ival;
float val = ival + t->values_final[0];
snapFrameTransform(t, eAnimEdit_AutoSnap(autosnap), ival, val, &val);
snapFrameTransform(t, snap_mode, ival, val, &val);
float delta_x = val - ival;
if (ELEM(autosnap, SACTSNAP_SECOND, SACTSNAP_TSTEP)) {
if (snap_mode == SCE_SNAP_TO_SECOND) {
/* Convert to seconds. */
const Scene *scene = t->scene;
const double secf = FPS;
delta_x /= secf;
val /= secf;
delta_x /= FPS;
val /= FPS;
}
if (autosnap == SACTSNAP_FRAME) {
if (snap_mode == SCE_SNAP_TO_FRAME) {
BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.2f (%.4f)", delta_x, val);
}
else if (autosnap == SACTSNAP_SECOND) {
else if (snap_mode == SCE_SNAP_TO_SECOND) {
BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.2f sec (%.4f)", delta_x, val);
}
else if (autosnap == SACTSNAP_TSTEP) {
BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f sec", delta_x);
}
else {
BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", delta_x);
}

View File

@ -212,10 +212,10 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_
/* WORKAROUND:
* Special case where snapping is done in #recalData.
* Update the header based on the #center_local. */
const short autosnap = getAnimEdit_SnapMode(t);
eSnapMode autosnap = t->tsnap.mode;
float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->center_local[0];
float val = ival + dvec[0];
snapFrameTransform(t, eAnimEdit_AutoSnap(autosnap), ival, val, &val);
snapFrameTransform(t, autosnap, ival, val, &val);
dvec[0] = val - ival;
}

View File

@ -128,6 +128,10 @@ bool validSnap(const TransInfo *t)
void transform_snap_flag_from_modifiers_set(TransInfo *t)
{
if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) {
/* Those spacetypes define their own invert behaviour instead of toggling it on/off. */
return;
}
SET_FLAG_FROM_TEST(t->tsnap.flag,
(((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) == MOD_SNAP) ||
((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) == MOD_SNAP_INVERT)),
@ -151,7 +155,7 @@ bool transformModeUseSnap(const TransInfo *t)
if (t->mode == TFM_RESIZE) {
return (ts->snap_transform_mode_flag & SCE_SNAP_TRANSFORM_MODE_SCALE) != 0;
}
if (ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE, TFM_SEQ_SLIDE)) {
if (ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE, TFM_SEQ_SLIDE, TFM_TIME_TRANSLATE)) {
return true;
}
@ -164,11 +168,6 @@ static bool doForceIncrementSnap(const TransInfo *t)
return false;
}
if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA)) {
/* No incremental snapping. */
return false;
}
return !transformModeUseSnap(t);
}
@ -632,9 +631,7 @@ static eSnapFlag snap_flag_from_spacetype(TransInfo *t)
case SPACE_GRAPH:
case SPACE_ACTION:
case SPACE_NLA:
/* These editors have their own "Auto-Snap" activation option.
* See #getAnimEdit_SnapMode. */
return eSnapFlag(0);
return eSnapFlag(ts->snap_flag_anim);
}
/* #SPACE_EMPTY.
* It can happen when the operator is called via a handle in `bpy.app.handlers`. */
@ -680,9 +677,8 @@ static eSnapMode snap_mode_from_spacetype(TransInfo *t)
return snap_mode;
}
if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA)) {
/* No incremental snapping. */
return eSnapMode(0);
if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_GRAPH)) {
return eSnapMode(ts->snap_anim_mode);
}
return SCE_SNAP_TO_INCREMENT;

View File

@ -11,6 +11,7 @@
#define SNAP_MIN_DISTANCE 30
/* For enum. */
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
bool peelObjectsTransform(TransInfo *t,
@ -73,25 +74,14 @@ void transform_snap_sequencer_data_free(TransSeqSnapData *data);
bool transform_snap_sequencer_calc(TransInfo *t);
void transform_snap_sequencer_apply_translate(TransInfo *t, float *vec);
/* `transform_snap_animation.cc` */
/**
* This function returns the snapping 'mode' for Animation Editors only.
* We cannot use the standard snapping due to NLA-strip scaling complexities.
*
* TODO: these modifier checks should be accessible from the key-map.
*/
short getAnimEdit_SnapMode(TransInfo *t);
void snapFrameTransform(TransInfo *t,
eAnimEdit_AutoSnap autosnap,
float val_initial,
float val_final,
float *r_val_final);
/* transform_snap_animation.cc */
void snapFrameTransform(
TransInfo *t, eSnapMode autosnap, float val_initial, float val_final, float *r_val_final);
/**
* This function is used by Animation Editor specific transform functions to do
* the Snap Keyframe to Nearest Frame/Marker
*/
void transform_snap_anim_flush_data(TransInfo *t,
TransData *td,
eAnimEdit_AutoSnap autosnap,
eSnapMode autosnap,
float *r_val_final);

View File

@ -21,96 +21,59 @@
/** \name Snapping in Anim Editors
* \{ */
short getAnimEdit_SnapMode(TransInfo *t)
{
short autosnap = SACTSNAP_OFF;
if (t->spacetype == SPACE_ACTION) {
SpaceAction *saction = (SpaceAction *)t->area->spacedata.first;
if (saction) {
autosnap = saction->autosnap;
}
}
else if (t->spacetype == SPACE_GRAPH) {
if ((t->mode == TFM_TRANSLATION) && transform_snap_is_active(t)) {
return autosnap;
}
SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
if (sipo) {
autosnap = sipo->autosnap;
}
}
else if (t->spacetype == SPACE_NLA) {
SpaceNla *snla = (SpaceNla *)t->area->spacedata.first;
if (snla) {
autosnap = snla->autosnap;
}
}
else {
autosnap = SACTSNAP_OFF;
}
/* toggle autosnap on/off
* - when toggling on, prefer nearest frame over 1.0 frame increments
*/
if (t->modifiers & MOD_SNAP_INVERT) {
if (autosnap) {
autosnap = SACTSNAP_OFF;
}
else {
autosnap = SACTSNAP_FRAME;
}
}
return autosnap;
}
void snapFrameTransform(TransInfo *t,
const eAnimEdit_AutoSnap autosnap,
const eSnapMode snap_mode,
const float val_initial,
const float val_final,
float *r_val_final)
{
float deltax = val_final - val_initial;
switch (autosnap) {
case SACTSNAP_FRAME:
*r_val_final = floorf(val_final + 0.5f);
break;
case SACTSNAP_MARKER:
/* Snap to nearest marker. */
/* TODO: need some more careful checks for where data comes from. */
*r_val_final = float(ED_markers_find_nearest_marker_time(&t->scene->markers, val_final));
break;
case SACTSNAP_SECOND:
case SACTSNAP_TSTEP: {
const Scene *scene = t->scene;
const double secf = FPS;
if (autosnap == SACTSNAP_SECOND) {
*r_val_final = floorf((val_final / secf) + 0.5) * secf;
/* This is needed for the FPS macro. */
const Scene *scene = t->scene;
const eSnapFlag snap_flag = t->tsnap.flag;
switch (snap_mode) {
case SCE_SNAP_TO_FRAME: {
if (snap_flag & SCE_SNAP_ABS_TIME_STEP) {
*r_val_final = floorf(val_final + 0.5f);
}
else {
deltax = float(floor((deltax / secf) + 0.5) * secf);
deltax = floorf(deltax + 0.5f);
*r_val_final = val_initial + deltax;
}
break;
}
case SACTSNAP_STEP:
deltax = floorf(deltax + 0.5f);
*r_val_final = val_initial + deltax;
case SCE_SNAP_TO_SECOND: {
if (snap_flag & SCE_SNAP_ABS_TIME_STEP) {
*r_val_final = floorf((val_final / FPS) + 0.5) * FPS;
}
else {
deltax = float(floor((deltax / FPS) + 0.5) * FPS);
*r_val_final = val_initial + deltax;
}
break;
case SACTSNAP_OFF:
}
case SCE_SNAP_TO_MARKERS: {
/* Snap to nearest marker. */
/* TODO: need some more careful checks for where data comes from. */
const float nearest_marker_time = float(
ED_markers_find_nearest_marker_time(&t->scene->markers, val_final));
*r_val_final = nearest_marker_time;
break;
}
default: {
*r_val_final = val_final;
break;
}
}
}
void transform_snap_anim_flush_data(TransInfo *t,
TransData *td,
const eAnimEdit_AutoSnap autosnap,
const eSnapMode snap_mode,
float *r_val_final)
{
BLI_assert(autosnap != SACTSNAP_OFF);
BLI_assert(t->tsnap.flag);
float val = td->loc[0];
float ival = td->iloc[0];
@ -123,7 +86,7 @@ void transform_snap_anim_flush_data(TransInfo *t,
ival = BKE_nla_tweakedit_remap(adt, ival, NLATIME_CONVERT_MAP);
}
snapFrameTransform(t, autosnap, ival, val, &val);
snapFrameTransform(t, snap_mode, ival, val, &val);
/* Convert frame out of nla-action time. */
if (adt) {

View File

@ -853,8 +853,8 @@ typedef struct SpaceAction {
char mode;
/* Storage for sub-space types. */
char mode_prev;
/** Automatic keyframe snapping mode. */
char autosnap;
/* Snapping now lives on the Scene. */
char autosnap DNA_DEPRECATED;
/** (eTimeline_Cache_Flag). */
char cache_display;
char _pad1[6];
@ -917,10 +917,8 @@ typedef enum eAnimEdit_Context {
SACTCONT_TIMELINE = 6,
} eAnimEdit_Context;
/* SpaceAction AutoSnap Settings (also used by other Animation Editors) */
/* Old snapping enum that is only needed because of the versioning code. */
typedef enum eAnimEdit_AutoSnap {
/* no auto-snap */
SACTSNAP_OFF = 0,
/* snap to 1.0 frame/second intervals */
SACTSNAP_STEP = 1,
/* snap to actual frames/seconds (nla-action time) */
@ -931,7 +929,7 @@ typedef enum eAnimEdit_AutoSnap {
SACTSNAP_SECOND = 4,
/* snap to 1.0 second increments */
SACTSNAP_TSTEP = 5,
} eAnimEdit_AutoSnap;
} eAnimEdit_AutoSnap DNA_DEPRECATED;
/* SAction->cache_display */
typedef enum eTimeline_Cache_Flag {

View File

@ -365,7 +365,9 @@
.snap_mode = SCE_SNAP_TO_INCREMENT, \
.snap_node_mode = SCE_SNAP_TO_GRID, \
.snap_uv_mode = SCE_SNAP_TO_INCREMENT, \
.snap_anim_mode = SCE_SNAP_TO_FRAME, \
.snap_flag = SCE_SNAP_TO_INCLUDE_EDITED | SCE_SNAP_TO_INCLUDE_NONEDITED, \
.snap_flag_anim = SCE_SNAP, \
.snap_transform_mode_flag = SCE_SNAP_TRANSFORM_MODE_TRANSLATE, \
.snap_face_nearest_steps = 1, \
\

View File

@ -1639,11 +1639,14 @@ typedef struct ToolSettings {
short snap_mode;
char snap_node_mode;
char snap_uv_mode;
short snap_anim_mode;
/** Generic flags (per space-type), #eSnapFlag. */
short snap_flag;
short snap_flag_node;
short snap_flag_seq;
short snap_flag_anim;
short snap_uv_flag;
char _pad[4];
/** Default snap source, #eSnapSourceOP. */
/**
* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of
@ -2324,6 +2327,8 @@ typedef enum eSnapFlag {
/** Was `SCE_SNAP_NO_SELF`, but self should be active. */
SCE_SNAP_NOT_TO_ACTIVE = (1 << 4),
SCE_SNAP_ABS_GRID = (1 << 5),
/* Same value with different name to make it easier to understand in time based code. */
SCE_SNAP_ABS_TIME_STEP = (1 << 5),
SCE_SNAP_BACKFACE_CULLING = (1 << 6),
SCE_SNAP_KEEP_ON_SAME_OBJECT = (1 << 7),
/** see #eSnapTargetOP */
@ -2383,11 +2388,17 @@ typedef enum eSnapMode {
/** For snap individual elements. */
SCE_SNAP_INDIVIDUAL_NEAREST = (1 << 8),
SCE_SNAP_INDIVIDUAL_PROJECT = (1 << 9),
/** #ToolSettings::snap_anim_mode */
SCE_SNAP_TO_FRAME = (1 << 10),
SCE_SNAP_TO_SECOND = (1 << 11),
SCE_SNAP_TO_MARKERS = (1 << 12),
} eSnapMode;
/* Due to dependency conflicts with Cycles, header cannot directly include `BLI_utildefines.h`. */
/* TODO: move this macro to a more general place. */
#ifdef ENUM_OPERATORS
ENUM_OPERATORS(eSnapMode, SCE_SNAP_INDIVIDUAL_PROJECT)
ENUM_OPERATORS(eSnapMode, SCE_SNAP_TO_MARKERS)
#endif
#define SCE_SNAP_TO_VERTEX (SCE_SNAP_TO_POINT | SCE_SNAP_TO_EDGE_ENDPOINT)

View File

@ -470,11 +470,8 @@ typedef struct SpaceGraph {
/** Mode for the Graph editor (eGraphEdit_Mode). */
short mode;
/**
* Time-transform auto-snapping settings for Graph editor
* (eAnimEdit_AutoSnap in DNA_action_types.h).
*/
short autosnap;
/* Snapping now lives on the Scene. */
short autosnap DNA_DEPRECATED;
/** Settings for Graph editor (eGraphEdit_Flag). */
int flag;
@ -560,8 +557,8 @@ typedef struct SpaceNla {
char _pad0[6];
/* End 'SpaceLink' header. */
/** This uses the same settings as autosnap for Action Editor. */
short autosnap;
/* Snapping now lives on the Scene. */
short autosnap DNA_DEPRECATED;
short flag;
char _pad[4];

View File

@ -28,6 +28,7 @@ DEF_ENUM(rna_enum_proportional_falloff_curve_only_items)
DEF_ENUM(rna_enum_snap_source_items)
DEF_ENUM(rna_enum_snap_element_items)
DEF_ENUM(rna_enum_snap_node_element_items)
DEF_ENUM(rna_enum_snap_animation_element_items)
DEF_ENUM(rna_enum_curve_fit_method_items)
DEF_ENUM(rna_enum_mesh_select_mode_items)
DEF_ENUM(rna_enum_mesh_select_mode_uv_items)

View File

@ -201,6 +201,13 @@ const EnumPropertyItem rna_enum_snap_node_element_items[] = {
{0, nullptr, 0, nullptr, nullptr},
};
const EnumPropertyItem rna_enum_snap_animation_element_items[] = {
{SCE_SNAP_TO_FRAME, "FRAME", 0, "Frame", "Snap to frame"},
{SCE_SNAP_TO_SECOND, "SECOND", 0, "Second", "Snap to seconds"},
{SCE_SNAP_TO_MARKERS, "MARKER", 0, "Nearest Marker", "Snap to nearest marker"},
{0, nullptr, 0, nullptr, nullptr},
};
#ifndef RNA_RUNTIME
static const EnumPropertyItem snap_uv_element_items[] = {
{SCE_SNAP_TO_INCREMENT,
@ -3449,6 +3456,24 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Snap Node Element", "Type of element to snap to");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); /* header redraw */
prop = RNA_def_property(srna, "use_snap_anim", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "snap_flag_anim", SCE_SNAP);
RNA_def_property_ui_text(prop, "Snap", "Enable snapping when transforming keyframes");
RNA_def_property_ui_icon(prop, ICON_SNAP_OFF, 1);
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
prop = RNA_def_property(srna, "use_snap_time_absolute", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "snap_flag_anim", SCE_SNAP_ABS_TIME_STEP);
RNA_def_property_ui_text(
prop, "Absolute Time Snap", "Absolute time alignment while translating");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); /* header redraw */
prop = RNA_def_property(srna, "snap_anim_element", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, nullptr, "snap_anim_mode");
RNA_def_property_enum_items(prop, rna_enum_snap_animation_element_items);
RNA_def_property_ui_text(prop, "Snap Anim Element", "Type of element to snap to");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); /* header redraw */
/* image editor uses own set of snap modes */
prop = RNA_def_property(srna, "snap_uv_element", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, nullptr, "snap_uv_mode");

View File

@ -392,21 +392,6 @@ static const EnumPropertyItem display_channels_items[] = {
{0, nullptr, 0, nullptr, nullptr},
};
#ifndef RNA_RUNTIME
static const EnumPropertyItem autosnap_items[] = {
{SACTSNAP_OFF, "NONE", 0, "No Auto-Snap", ""},
/* {-1, "", 0, "", ""}, */
{SACTSNAP_STEP, "STEP", 0, "Frame Step", "Snap to 1.0 frame intervals"},
{SACTSNAP_TSTEP, "TIME_STEP", 0, "Second Step", "Snap to 1.0 second intervals"},
/* {-1, "", 0, "", ""}, */
{SACTSNAP_FRAME, "FRAME", 0, "Nearest Frame", "Snap to actual frames (nla-action time)"},
{SACTSNAP_SECOND, "SECOND", 0, "Nearest Second", "Snap to actual seconds (nla-action time)"},
/* {-1, "", 0, "", ""}, */
{SACTSNAP_MARKER, "MARKER", 0, "Nearest Marker", "Snap to nearest marker"},
{0, nullptr, 0, nullptr, nullptr},
};
#endif
const EnumPropertyItem rna_enum_shading_type_items[] = {
{OB_WIRE, "WIREFRAME", ICON_SHADING_WIRE, "Wireframe", "Display the object as wire edges"},
{OB_SOLID, "SOLID", ICON_SHADING_SOLID, "Solid", "Display in solid mode"},
@ -6283,14 +6268,6 @@ static void rna_def_space_dopesheet(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, nullptr, "ads");
RNA_def_property_ui_text(prop, "Dope Sheet", "Settings for filtering animation data");
/* autosnap */
prop = RNA_def_property(srna, "auto_snap", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "autosnap");
RNA_def_property_enum_items(prop, autosnap_items);
RNA_def_property_ui_text(
prop, "Auto Snap", "Automatic time snapping settings for transformations");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_DOPESHEET, nullptr);
/* displaying cache status */
prop = RNA_def_property(srna, "show_cache", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "cache_display", TIME_CACHE_DISPLAY);
@ -6452,14 +6429,6 @@ static void rna_def_space_graph(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, nullptr, "ads");
RNA_def_property_ui_text(prop, "Dope Sheet", "Settings for filtering animation data");
/* Auto-snap. */
prop = RNA_def_property(srna, "auto_snap", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "autosnap");
RNA_def_property_enum_items(prop, autosnap_items);
RNA_def_property_ui_text(
prop, "Auto Snap", "Automatic time snapping settings for transformations");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, nullptr);
/* Read-only state info. */
prop = RNA_def_property(srna, "has_ghost_curves", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_SpaceGraphEditor_has_ghost_curves_get", nullptr);
@ -6539,14 +6508,6 @@ static void rna_def_space_nla(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "DopeSheet");
RNA_def_property_pointer_sdna(prop, nullptr, "ads");
RNA_def_property_ui_text(prop, "Dope Sheet", "Settings for filtering animation data");
/* autosnap */
prop = RNA_def_property(srna, "auto_snap", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "autosnap");
RNA_def_property_enum_items(prop, autosnap_items);
RNA_def_property_ui_text(
prop, "Auto Snap", "Automatic time snapping settings for transformations");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NLA, nullptr);
}
static void rna_def_console_line(BlenderRNA *brna)