diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index d5da13edc19..e23e52eba61 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -3705,6 +3705,10 @@ def km_frames(params): {"properties": [("end", True)]}), ("screen.frame_jump", {"type": 'LEFT_ARROW', "value": 'PRESS', "shift": True, "repeat": True}, {"properties": [("end", False)]}), + ("screen.time_jump", {"type": 'RIGHT_ARROW', "value": 'PRESS', "ctrl": True, "repeat": True}, + {"properties": [("backward", False)]}), + ("screen.time_jump", {"type": 'LEFT_ARROW', "value": 'PRESS', "ctrl": True, "repeat": True}, + {"properties": [("backward", True)]}), ("screen.keyframe_jump", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True}, {"properties": [("next", False)]}), ("screen.keyframe_jump", {"type": 'DOWN_ARROW', "value": 'PRESS', "repeat": True}, diff --git a/scripts/startup/bl_ui/space_time.py b/scripts/startup/bl_ui/space_time.py index 9748dd1784f..dfbbb228f0b 100644 --- a/scripts/startup/bl_ui/space_time.py +++ b/scripts/startup/bl_ui/space_time.py @@ -97,6 +97,12 @@ def playback_controls(layout, context): row.operator("screen.keyframe_jump", text="", icon='NEXT_KEYFRAME').next = True row.operator("screen.frame_jump", text="", icon='FF').end = True + # Time jump + row = layout.row(align=True) + row.operator("screen.time_jump", text="", icon='FRAME_PREV').backward = True + row.operator("screen.time_jump", text="", icon='FRAME_NEXT').backward = False + row.popover(panel="TIME_PT_jump", text="") + if tool_settings: row = layout.row(align=True) row.prop(tool_settings, "use_snap_playhead", text="") @@ -289,12 +295,30 @@ class TIME_PT_auto_keyframing(TimelinePanelButtons, Panel): col.prop(tool_settings, "use_record_with_nla", text="Layered Recording") +class TIME_PT_jump(TimelinePanelButtons, Panel): + bl_label = "Time Jump" + bl_options = {'HIDE_HEADER'} + bl_region_type = 'HEADER' + bl_ui_units_x = 10 + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + scene = context.scene + + layout.prop(scene, "time_jump_unit", expand=True, text="Jump Unit") + layout.prop(scene, "time_jump_delta", text="Delta") + + ################################### classes = ( TIME_PT_playback, TIME_PT_keyframing_settings, TIME_PT_auto_keyframing, + TIME_PT_jump, TIME_PT_playhead_snapping, ) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 242a98ff31c..c803d12d89f 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 101 +#define BLENDER_FILE_SUBVERSION 102 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenloader/intern/versioning_500.cc b/source/blender/blenloader/intern/versioning_500.cc index aa2ca673ff9..a59c7e32505 100644 --- a/source/blender/blenloader/intern/versioning_500.cc +++ b/source/blender/blenloader/intern/versioning_500.cc @@ -3819,6 +3819,13 @@ void blo_do_versions_500(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 102)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + scene->r.time_jump_delta = 1.0f; + scene->r.time_jump_unit = 1; + } + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/editors/screen/screen_ops.cc b/source/blender/editors/screen/screen_ops.cc index fd13a21e1c1..2fd8cc7a89c 100644 --- a/source/blender/editors/screen/screen_ops.cc +++ b/source/blender/editors/screen/screen_ops.cc @@ -3365,6 +3365,68 @@ static void SCREEN_OT_frame_jump(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Time Jump Operator + * \{ */ + +/* function to be called outside UI context, or for redo */ +static wmOperatorStatus frame_jump_delta_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + const bool backward = RNA_boolean_get(op->ptr, "backward"); + + float delta = scene->r.time_jump_delta; + + if (scene->r.time_jump_unit == SCE_TIME_JUMP_SECOND) { + delta *= scene->r.frs_sec / scene->r.frs_sec_base; + } + + int step = (int)delta; + float fraction = delta - step; + if (backward) { + scene->r.cfra -= step; + scene->r.subframe -= fraction; + } + else { + scene->r.cfra += step; + scene->r.subframe += fraction; + } + + /* Check if subframe has a non-fractional component, and roll that into cfra. */ + if (scene->r.subframe < 0.0f || scene->r.subframe >= 1.0f) { + const float subframe_offset = floorf(scene->r.subframe); + const int frame_offset = (int)subframe_offset; + scene->r.cfra += frame_offset; + scene->r.subframe -= subframe_offset; + } + + ED_areas_do_frame_follow(C, true); + + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); + + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); + + return OPERATOR_FINISHED; +} + +static void SCREEN_OT_time_jump(wmOperatorType *ot) +{ + ot->name = "Jump Time by Delta"; + ot->description = "Jump forward/backward by a given number of frames or seconds"; + ot->idname = "SCREEN_OT_time_jump"; + + ot->exec = frame_jump_delta_exec; + + ot->poll = operator_screenactive_norender; + ot->flag = OPTYPE_UNDO_GROUPED; + ot->undo_group = "Frame Change"; + + /* rna */ + RNA_def_boolean(ot->srna, "backward", false, "Backwards", "Jump backwards in time"); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Jump to Key-Frame Operator * \{ */ @@ -6884,6 +6946,7 @@ void ED_operatortypes_screen() /* Frame changes. */ WM_operatortype_append(SCREEN_OT_frame_offset); WM_operatortype_append(SCREEN_OT_frame_jump); + WM_operatortype_append(SCREEN_OT_time_jump); WM_operatortype_append(SCREEN_OT_keyframe_jump); WM_operatortype_append(SCREEN_OT_marker_jump); diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index b3cf4367e02..a75a8179017 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -65,6 +65,8 @@ .sfra = 1, \ .efra = 250, \ .frame_step = 1, \ + .time_jump_delta = 1.0, \ + .time_jump_unit = 1, \ .xsch = 1920, \ .ysch = 1080, \ .xasp = 1, \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index ef99159ae7b..c42297d522f 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -971,7 +971,10 @@ typedef struct RenderData { int compositor_denoise_preview_quality; /* eCompositorDenoiseQaulity */ int compositor_denoise_final_quality; /* eCompositorDenoiseQaulity */ - char _pad6[4]; + /** Frames to jump manually. */ + float time_jump_delta; + int time_jump_unit; + char _pad10[4]; } RenderData; /** #RenderData::quality_flag */ @@ -1020,6 +1023,12 @@ typedef enum eCompositorDenoiseQaulity { SCE_COMPOSITOR_DENOISE_FAST = 2, } eCompositorDenoiseQaulity; +/** #RenderData::time_jump_unit */ +enum { + SCE_TIME_JUMP_FRAME = 0, + SCE_TIME_JUMP_SECOND = 1, +}; + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index c0e432aaefd..85ddc7f4f93 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -8651,6 +8651,12 @@ void RNA_def_scene(BlenderRNA *brna) {0, nullptr, 0, nullptr, nullptr}, }; + static const EnumPropertyItem time_jump_unit_items[] = { + {SCE_TIME_JUMP_FRAME, "FRAME", 0, "Frame", "Jump by frames"}, + {SCE_TIME_JUMP_SECOND, "SECOND", 0, "Second", "Jump by seconds"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + /* Struct definition */ srna = RNA_def_struct(brna, "Scene", "ID"); RNA_def_struct_ui_text(srna, @@ -8755,6 +8761,22 @@ void RNA_def_scene(BlenderRNA *brna) "Number of frames to skip forward while rendering/playing back each frame"); RNA_def_property_update(prop, NC_SCENE | ND_FRAME, nullptr); + prop = RNA_def_property(srna, "time_jump_unit", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, nullptr, "r.time_jump_unit"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_enum_items(prop, time_jump_unit_items); + RNA_def_property_ui_text( + prop, "Time Jump Unit", "Which unit to use for time jumps in the timeline"); + RNA_def_property_update(prop, NC_SCENE | ND_FRAME_RANGE, nullptr); + + prop = RNA_def_property(srna, "time_jump_delta", PROP_FLOAT, PROP_TIME); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_float_sdna(prop, nullptr, "r.time_jump_delta"); + RNA_def_property_range(prop, 0.1f, FLT_MAX); + RNA_def_property_ui_text( + prop, "Time Jump Delta", "Number of frames or seconds to jump forward or backward"); + RNA_def_property_update(prop, NC_SCENE | ND_FRAME_RANGE, nullptr); + prop = RNA_def_property(srna, "frame_current_final", PROP_FLOAT, PROP_TIME); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE); RNA_def_property_range(prop, MINAFRAME, MAXFRAME);