diff --git a/scripts/startup/bl_ui/space_dopesheet.py b/scripts/startup/bl_ui/space_dopesheet.py index d6390dc47ae..2be9afceb1d 100644 --- a/scripts/startup/bl_ui/space_dopesheet.py +++ b/scripts/startup/bl_ui/space_dopesheet.py @@ -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. diff --git a/scripts/startup/bl_ui/space_graph.py b/scripts/startup/bl_ui/space_graph.py index 317e4939929..54a66c4cd98 100644 --- a/scripts/startup/bl_ui/space_graph.py +++ b/scripts/startup/bl_ui/space_graph.py @@ -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. diff --git a/scripts/startup/bl_ui/space_nla.py b/scripts/startup/bl_ui/space_nla.py index bba6572a6a7..938149c59d3 100644 --- a/scripts/startup/bl_ui/space_nla.py +++ b/scripts/startup/bl_ui/space_nla.py @@ -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. diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 495010eedce..f8459e480ef 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -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)) { diff --git a/source/blender/editors/space_action/space_action.cc b/source/blender/editors/space_action/space_action.cc index cb68bb451e8..6179a799d8d 100644 --- a/source/blender/editors/space_action/space_action.cc +++ b/source/blender/editors/space_action/space_action.cc @@ -62,7 +62,6 @@ static SpaceLink *action_create(const ScrArea *area, const Scene *scene) saction = MEM_cnew("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; diff --git a/source/blender/editors/space_graph/space_graph.cc b/source/blender/editors/space_graph/space_graph.cc index c39f13c3f9f..e4ede92c454 100644 --- a/source/blender/editors/space_graph/space_graph.cc +++ b/source/blender/editors/space_graph/space_graph.cc @@ -62,8 +62,6 @@ static SpaceLink *graph_create(const ScrArea * /*area*/, const Scene *scene) sipo = static_cast(MEM_callocN(sizeof(SpaceGraph), "init graphedit")); sipo->spacetype = SPACE_GRAPH; - sipo->autosnap = SACTSNAP_FRAME; - /* allocate DopeSheet data for Graph Editor */ sipo->ads = static_cast(MEM_callocN(sizeof(bDopeSheet), "GraphEdit DopeSheet")); sipo->ads->source = (ID *)scene; diff --git a/source/blender/editors/space_nla/space_nla.cc b/source/blender/editors/space_nla/space_nla.cc index cdaf635c9a3..2150b7fcc5d 100644 --- a/source/blender/editors/space_nla/space_nla.cc +++ b/source/blender/editors/space_nla/space_nla.cc @@ -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 */ diff --git a/source/blender/editors/transform/transform_convert_action.cc b/source/blender/editors/transform/transform_convert_action.cc index f233dfb4bd3..1a2d721fed7 100644 --- a/source/blender/editors/transform/transform_convert_action.cc +++ b/source/blender/editors/transform/transform_convert_action.cc @@ -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. */ diff --git a/source/blender/editors/transform/transform_convert_graph.cc b/source/blender/editors/transform/transform_convert_graph.cc index 14003bb458c..feaa0a97305 100644 --- a/source/blender/editors/transform/transform_convert_graph.cc +++ b/source/blender/editors/transform/transform_convert_graph.cc @@ -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 */ diff --git a/source/blender/editors/transform/transform_convert_nla.cc b/source/blender/editors/transform/transform_convert_nla.cc index d5854360871..f56259ed25d 100644 --- a/source/blender/editors/transform/transform_convert_nla.cc +++ b/source/blender/editors/transform/transform_convert_nla.cc @@ -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); } } } diff --git a/source/blender/editors/transform/transform_mode_timetranslate.cc b/source/blender/editors/transform/transform_mode_timetranslate.cc index afba8fac24f..4a94fe1fdd5 100644 --- a/source/blender/editors/transform/transform_mode_timetranslate.cc +++ b/source/blender/editors/transform/transform_mode_timetranslate.cc @@ -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); } diff --git a/source/blender/editors/transform/transform_mode_translate.cc b/source/blender/editors/transform/transform_mode_translate.cc index fe9b06f7d69..44cbebe7bda 100644 --- a/source/blender/editors/transform/transform_mode_translate.cc +++ b/source/blender/editors/transform/transform_mode_translate.cc @@ -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; } diff --git a/source/blender/editors/transform/transform_snap.cc b/source/blender/editors/transform/transform_snap.cc index 487d345a409..8715d518668 100644 --- a/source/blender/editors/transform/transform_snap.cc +++ b/source/blender/editors/transform/transform_snap.cc @@ -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; diff --git a/source/blender/editors/transform/transform_snap.hh b/source/blender/editors/transform/transform_snap.hh index a23c9a7e8ad..335a89a0a54 100644 --- a/source/blender/editors/transform/transform_snap.hh +++ b/source/blender/editors/transform/transform_snap.hh @@ -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); diff --git a/source/blender/editors/transform/transform_snap_animation.cc b/source/blender/editors/transform/transform_snap_animation.cc index f994123c772..e3d1b135f4a 100644 --- a/source/blender/editors/transform/transform_snap_animation.cc +++ b/source/blender/editors/transform/transform_snap_animation.cc @@ -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) { diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index df80d10c538..f432c546f4b 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -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 { diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 7b8e4bf4a16..13e2573e848 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -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, \ \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 5c4599e6952..051c8eac182 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -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) diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 13fd2ac8949..237ba302e7e 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -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]; diff --git a/source/blender/makesrna/RNA_enum_items.hh b/source/blender/makesrna/RNA_enum_items.hh index 874440df81a..5ba7d647479 100644 --- a/source/blender/makesrna/RNA_enum_items.hh +++ b/source/blender/makesrna/RNA_enum_items.hh @@ -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) diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 9c2b9adc88c..0e7a0c76f7a 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -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"); diff --git a/source/blender/makesrna/intern/rna_space.cc b/source/blender/makesrna/intern/rna_space.cc index df8458ec65a..76b54773460 100644 --- a/source/blender/makesrna/intern/rna_space.cc +++ b/source/blender/makesrna/intern/rna_space.cc @@ -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)