diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index d8f4a71160e..bbeaabe3ab0 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1056,6 +1056,7 @@ def km_time_scrub(_params): items.extend([ ("anim.change_frame", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), + ("anim.change_onion_skin_range", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, None), ]) return keymap diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index bf3c322f377..f83ecc7e20f 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -715,6 +715,33 @@ class RENDER_PT_gpencil(RenderButtonsPanel, Panel): col.prop(props, "antialias_threshold") +class RENDER_PT_onion_skins(RenderButtonsPanel, Panel): + bl_label = "Onion Skins" + bl_options = {'DEFAULT_CLOSED'} + bl_order = 10 + COMPAT_ENGINES = { + 'BLENDER_RENDER', + 'BLENDER_EEVEE', + 'BLENDER_EEVEE_NEXT', + 'BLENDER_WORKBENCH', + 'BLENDER_WORKBENCH_NEXT'} + + def draw(self, context): + layout = self.layout + # layout.use_property_split = True + # layout.use_property_decorate = False # No animation. + + scene = context.scene + + col = layout.column() + col.prop(scene, "onion_skin_color_left") + col.prop(scene, "onion_skin_color_right") + col.prop(scene, "os_relative_left") + col.prop(scene, "os_relative_right") + col.prop(scene, "onion_skin_alpha") + col.prop(scene, "onion_skin_draw_modes") + + class RENDER_PT_opengl_sampling(RenderButtonsPanel, Panel): bl_label = "Sampling" COMPAT_ENGINES = {'BLENDER_WORKBENCH', 'BLENDER_WORKBENCH_NEXT'} @@ -921,6 +948,8 @@ classes = ( RENDER_PT_simplify_viewport, RENDER_PT_simplify_render, RENDER_PT_simplify_greasepencil, + + RENDER_PT_onion_skins, ) if __name__ == "__main__": # only for live edit. diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 7837df869b4..595b90ef24f 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -6532,7 +6532,7 @@ class VIEW3D_PT_overlay_geometry(Panel): col.prop(overlay, "show_face_orientation") - # sub.prop(overlay, "show_onion_skins") + sub.prop(overlay, "show_onion_skins") class VIEW3D_PT_overlay_motion_tracking(Panel): diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 6edd63698b2..4f15f4d88a9 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -225,6 +225,7 @@ set(SRC engines/overlay/overlay_viewer_attribute.cc engines/overlay/overlay_volume.cc engines/overlay/overlay_wireframe.cc + engines/overlay/overlay_onion_skin.cc DRW_engine.h DRW_pbvh.hh @@ -698,6 +699,8 @@ set(GLSL_SRC engines/overlay/shaders/overlay_motion_path_line_vert.glsl engines/overlay/shaders/overlay_motion_path_line_vert_no_geom.glsl engines/overlay/shaders/overlay_motion_path_point_vert.glsl + engines/overlay/shaders/overlay_onion_skin_mesh_frag.glsl + engines/overlay/shaders/overlay_onion_skin_mesh_vert.glsl engines/overlay/shaders/overlay_outline_detect_frag.glsl engines/overlay/shaders/overlay_outline_prepass_curves_vert.glsl engines/overlay/shaders/overlay_outline_prepass_frag.glsl diff --git a/source/blender/draw/engines/overlay/overlay_engine.cc b/source/blender/draw/engines/overlay/overlay_engine.cc index b78bed0f2fa..4afcda99bd3 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.cc +++ b/source/blender/draw/engines/overlay/overlay_engine.cc @@ -137,6 +137,7 @@ static void OVERLAY_engine_init(void *vedata) OVERLAY_outline_init(data); OVERLAY_wireframe_init(data); OVERLAY_paint_init(data); + OVERLAY_onion_skin_init(data); } static void OVERLAY_cache_init(void *vedata) @@ -365,6 +366,8 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) const bool draw_motion_paths = (pd->overlay.flag & V3D_OVERLAY_HIDE_MOTION_PATHS) == 0; + const bool draw_onion_skins = pd->overlay.flag & V3D_OVERLAY_ONION_SKINS; + bool do_init; OVERLAY_DupliData *dupli = OVERLAY_duplidata_get(ob, vedata, &do_init); @@ -386,6 +389,9 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) if (draw_bone_selection) { OVERLAY_pose_cache_populate(data, ob); } + if (draw_onion_skins) { + OVERLAY_onion_skin_populate(data, ob); + } if (pd->overlay.flag & V3D_OVERLAY_VIEWER_ATTRIBUTE) { if (is_preview) { @@ -686,6 +692,7 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_image_in_front_draw(data); OVERLAY_motion_path_draw(data); + OVERLAY_onion_skin_draw(data); OVERLAY_extra_centers_draw(data); if (DRW_state_is_select() || DRW_state_is_depth()) { diff --git a/source/blender/draw/engines/overlay/overlay_onion_skin.cc b/source/blender/draw/engines/overlay/overlay_onion_skin.cc new file mode 100644 index 00000000000..a0fbcb3aa26 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_onion_skin.cc @@ -0,0 +1,88 @@ +#include "BKE_mesh.h" +#include "DRW_render.h" +#include "draw_cache_impl.h" +#include "overlay_private.hh" + +void OVERLAY_onion_skin_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->onion_skin_ps, state | pd->clipping_state); + + DRWShadingGroup *grp; + + GPUShader *shader = OVERLAY_shader_onion_skin_mesh(); + pd->onion_skin_grp = grp = DRW_shgroup_create(shader, psl->onion_skin_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + + GPUShader *shader_outline = OVERLAY_shader_onion_skin_outline(); + pd->onion_skin_outline_grp = grp = DRW_shgroup_create(shader_outline, psl->onion_skin_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); +} + +static bool str_equals(const char *__restrict str, const char *__restrict start) +{ + for (; *str && *start; str++, start++) { + if (*str != *start) { + return false; + } + } + + return (*start == *str); +} + +void OVERLAY_onion_skin_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + + const DRWContextState *draw_ctx = DRW_context_state_get(); + + Scene *scene = draw_ctx->scene; + const float current_frame = BKE_scene_ctime_get(scene); + + DRWShadingGroup *grp = nullptr; + if (scene->onion_skin_cache.draw_method == ONION_SKIN_DRAW_SOLID) { + grp = pd->onion_skin_grp; + } + else { + grp = pd->onion_skin_outline_grp; + } + DRW_shgroup_uniform_float_copy(grp, "alpha", scene->onion_skin_cache.alpha); + + LISTBASE_FOREACH (OnionSkinMeshLink *, mesh_link, &scene->onion_skin_cache.objects) { + if (!str_equals(ob->id.name, mesh_link->object->id.name)) { + continue; + } + + if (compare_ff(mesh_link->frame, current_frame, FLT_EPSILON)) { + continue; + } + + if (mesh_link->frame < current_frame - scene->onion_skin_cache.relative_left) { + continue; + } + + if (mesh_link->frame > current_frame + scene->onion_skin_cache.relative_right) { + continue; + } + if (mesh_link->frame < current_frame) { + DRW_shgroup_uniform_vec3_copy(grp, "color", draw_ctx->scene->onion_skin_cache.color_left); + } + else { + DRW_shgroup_uniform_vec3_copy(grp, "color", draw_ctx->scene->onion_skin_cache.color_right); + } + + struct GPUBatch *geom = DRW_cache_object_surface_get(ob); + if (geom) { + DRW_shgroup_call(grp, geom, ob); + } + } +} + +void OVERLAY_onion_skin_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + DRW_draw_pass(psl->onion_skin_ps); +} \ No newline at end of file diff --git a/source/blender/draw/engines/overlay/overlay_private.hh b/source/blender/draw/engines/overlay/overlay_private.hh index b0a921688dc..9893ddeac3a 100644 --- a/source/blender/draw/engines/overlay/overlay_private.hh +++ b/source/blender/draw/engines/overlay/overlay_private.hh @@ -113,6 +113,7 @@ typedef struct OVERLAY_PassList { DRWPass *image_foreground_scene_ps; DRWPass *metaball_ps[2]; DRWPass *motion_paths_ps; + DRWPass *onion_skin_ps; DRWPass *outlines_prepass_ps; DRWPass *outlines_detect_ps; DRWPass *outlines_resolve_ps; @@ -279,6 +280,8 @@ typedef struct OVERLAY_PrivateData { DRWShadingGroup *flash_grp[2]; DRWShadingGroup *motion_path_lines_grp; DRWShadingGroup *motion_path_points_grp; + DRWShadingGroup *onion_skin_grp; + DRWShadingGroup *onion_skin_outline_grp; DRWShadingGroup *outlines_grp; DRWShadingGroup *outlines_curves_grp; DRWShadingGroup *outlines_ptcloud_grp; @@ -653,6 +656,7 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_image_cache_finish(OVERLAY_Data *vedata); void OVERLAY_image_draw(OVERLAY_Data *vedata); void OVERLAY_image_background_draw(OVERLAY_Data *vedata); + /** * This function draws images that needs the view transform applied. * It draws these images directly into the scene color buffer. @@ -670,6 +674,10 @@ void OVERLAY_motion_path_cache_init(OVERLAY_Data *vedata); void OVERLAY_motion_path_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_motion_path_draw(OVERLAY_Data *vedata); +void OVERLAY_onion_skin_init(OVERLAY_Data *vedata); +void OVERLAY_onion_skin_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_onion_skin_draw(OVERLAY_Data *vedata); + void OVERLAY_outline_init(OVERLAY_Data *vedata); void OVERLAY_outline_cache_init(OVERLAY_Data *vedata); void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata, @@ -769,6 +777,8 @@ GPUShader *OVERLAY_shader_grid_image(void); GPUShader *OVERLAY_shader_image(void); GPUShader *OVERLAY_shader_motion_path_line(void); GPUShader *OVERLAY_shader_motion_path_vert(void); +GPUShader *OVERLAY_shader_onion_skin_mesh(void); +GPUShader *OVERLAY_shader_onion_skin_outline(void); GPUShader *OVERLAY_shader_uniform_color(void); GPUShader *OVERLAY_shader_uniform_color_pointcloud(void); GPUShader *OVERLAY_shader_outline_prepass(bool use_wire); diff --git a/source/blender/draw/engines/overlay/overlay_shader.cc b/source/blender/draw/engines/overlay/overlay_shader.cc index 5b189c60378..52af0f080f3 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.cc +++ b/source/blender/draw/engines/overlay/overlay_shader.cc @@ -78,6 +78,8 @@ struct OVERLAY_Shaders { GPUShader *image; GPUShader *motion_path_line; GPUShader *motion_path_vert; + GPUShader *onion_skin_mesh; + GPUShader *onion_skin_outline; GPUShader *outline_prepass; GPUShader *outline_prepass_curves; GPUShader *outline_prepass_gpencil; @@ -685,6 +687,26 @@ GPUShader *OVERLAY_shader_motion_path_vert(void) return sh_data->motion_path_vert; } +GPUShader *OVERLAY_shader_onion_skin_mesh(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->onion_skin_mesh) { + sh_data->onion_skin_mesh = GPU_shader_create_from_info_name("overlay_onion_skin_mesh"); + } + return sh_data->onion_skin_mesh; +} + +GPUShader *OVERLAY_shader_onion_skin_outline(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->onion_skin_outline) { + sh_data->onion_skin_outline = GPU_shader_create_from_info_name("overlay_outline_detect"); + } + return sh_data->onion_skin_outline; +} + GPUShader *OVERLAY_shader_outline_prepass(bool use_wire) { const DRWContextState *draw_ctx = DRW_context_state_get(); diff --git a/source/blender/draw/engines/overlay/shaders/infos/overlay_extra_info.hh b/source/blender/draw/engines/overlay/shaders/infos/overlay_extra_info.hh index aff132cd414..0b817b0f565 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/overlay_extra_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/overlay_extra_info.hh @@ -248,6 +248,26 @@ GPU_SHADER_CREATE_INFO(overlay_motion_path_point_clipped) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Onion Skin + * \{ */ + +GPU_SHADER_INTERFACE_INFO(onion_skin_iface, "interp").smooth(Type::VEC4, "color"); + +GPU_SHADER_CREATE_INFO(overlay_onion_skin_mesh) + .do_static_compilation(true) + .vertex_in(0, Type::VEC3, "pos") + .vertex_out(onion_skin_iface) + .push_constant(Type::VEC3, "color") + .push_constant(Type::FLOAT, "alpha") + .push_constant(Type::INT, "draw_method") + .fragment_out(0, Type::VEC4, "fragColor") + .fragment_source("overlay_onion_skin_mesh_frag.glsl") + .vertex_source("overlay_onion_skin_mesh_vert.glsl") + .additional_info("draw_mesh", "draw_globals"); + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Image Empty * \{ */ diff --git a/source/blender/draw/engines/overlay/shaders/overlay_onion_skin_mesh_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_onion_skin_mesh_frag.glsl new file mode 100644 index 00000000000..dcf65b15cdf --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/overlay_onion_skin_mesh_frag.glsl @@ -0,0 +1,4 @@ +void main() +{ + fragColor = interp.color; +} \ No newline at end of file diff --git a/source/blender/draw/engines/overlay/shaders/overlay_onion_skin_mesh_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_onion_skin_mesh_vert.glsl new file mode 100644 index 00000000000..beeead0b6b9 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/overlay_onion_skin_mesh_vert.glsl @@ -0,0 +1,11 @@ +#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +void main() +{ + interp.color.rgb = color; + interp.color.a = alpha; + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + /* view_clipping_distances(world_pos); */ +} \ No newline at end of file diff --git a/source/blender/editors/animation/CMakeLists.txt b/source/blender/editors/animation/CMakeLists.txt index 66db6eb7924..2d3e76fd863 100644 --- a/source/blender/editors/animation/CMakeLists.txt +++ b/source/blender/editors/animation/CMakeLists.txt @@ -29,6 +29,7 @@ set(SRC anim_ipo_utils.c anim_markers.c anim_motion_paths.c + anim_onion_skin.c anim_ops.c drivers.c fmodifier_ui.c diff --git a/source/blender/editors/animation/anim_onion_skin.c b/source/blender/editors/animation/anim_onion_skin.c new file mode 100644 index 00000000000..441a58c46fe --- /dev/null +++ b/source/blender/editors/animation/anim_onion_skin.c @@ -0,0 +1,99 @@ +#include "BKE_context.h" +#include "BKE_layer.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_scene.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DNA_scene_types.h" +#include "DNA_windowmanager_types.h" +#include "ED_onion_skin.h" +#include "ED_outliner.h" +#include "MEM_guardedalloc.h" +#include "WM_api.h" +#include "WM_types.h" + +static void graveyard() +{ + ListBase selected = {NULL, NULL}; + LISTBASE_FOREACH (CollectionPointerLink *, object_ptr_link, &selected) { + /* Mesh *mesh = BKE_mesh_from_object(ob); + if (!mesh) { + continue; + } */ + /* Mesh *copy = BKE_mesh_copy_for_eval(mesh); */ + /* Mesh *mesh_result = (Mesh *)BKE_id_copy_ex( + NULL, &mesh->id, NULL, LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT); */ + /* + OnionSkinMeshLink *link = MEM_callocN(sizeof(OnionSkinMeshLink), "onion skin mesh link"); + link->mesh = copy; + BLI_addtail(&scene->onion_skin_cache.meshes, link); */ + } + BLI_freelistN(&selected); +} + +static int onion_skin_add_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Main *bmain = CTX_data_main(C); + BKE_main_id_newptr_and_tag_clear(bmain); + ViewLayer *view_layer = CTX_data_view_layer(C); + + Object *ob = CTX_data_active_object(C); + if (!ob) { + return OPERATOR_CANCELLED; + } + Mesh *mesh = BKE_mesh_from_object(ob); + if (!mesh) { + return OPERATOR_CANCELLED; + } + /* Mesh *copy = BKE_mesh_copy_for_eval(mesh); */ + /* Mesh *copy = (Mesh *)BKE_id_copy_ex(bmain, &mesh->id, NULL, LIB_ID_COPY_DEFAULT); */ + Object *ob_new = BKE_object_duplicate(bmain, + ob, + (eDupli_ID_Flags)U.dupflag, + LIB_ID_DUPLICATE_IS_SUBPROCESS | + LIB_ID_DUPLICATE_IS_ROOT_ID); + LayerCollection *layer_collection = BKE_layer_collection_from_index(view_layer, 2); + BKE_collection_object_add(bmain, layer_collection->collection, ob_new); + OnionSkinMeshLink *link = MEM_callocN(sizeof(OnionSkinMeshLink), "onion skin mesh link"); + link->object = ob_new; + ob_new->adt = NULL; + link->frame = BKE_scene_ctime_get(scene); + BLI_addtail(&scene->onion_skin_cache.objects, link); + + ED_outliner_select_sync_from_object_tag(C); + + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); + + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT | ND_LAYER_CONTENT, scene); + + return OPERATOR_FINISHED; +} + +static void ANIM_OT_onion_skin_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Onion Skin"; + ot->description = "foobar"; + ot->idname = "ANIM_OT_onion_skin_add"; + + /* api callbacks */ + ot->exec = onion_skin_add_exec; + /* ot->cancel = WM_gesture_box_cancel; */ + + /* ot->poll = ed_markers_poll_markers_exist; */ + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* properties */ +} + +void ED_operatortypes_onion_skin(void) +{ + WM_operatortype_append(ANIM_OT_onion_skin_add); +} diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 22c9217dc35..4371d19315d 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -395,6 +395,138 @@ static void ANIM_OT_change_frame(wmOperatorType *ot) /** \} */ +/* Set the new frame number */ +static void change_onion_skin_range_apply(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + const int left = RNA_int_get(op->ptr, "left"); + const int right = RNA_int_get(op->ptr, "right"); + + scene->onion_skin_cache.relative_left = left; + scene->onion_skin_cache.relative_right = right; + + DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE); + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); +} + +static void onion_skin_range_foo(bContext *C, wmOperator *op, const wmEvent *event) +{ + Scene *scene = CTX_data_scene(C); + const int clicked_frame = roundf(frame_from_event(C, event)); + const int current_scene_frame = BKE_scene_ctime_get(scene); + if (clicked_frame > current_scene_frame) { + RNA_int_set(op->ptr, "right", clicked_frame - current_scene_frame); + } + else if (clicked_frame < current_scene_frame) { + RNA_int_set(op->ptr, "left", current_scene_frame - clicked_frame); + } +} + +static void change_onion_skin_range_cancel(bContext *C, wmOperator *UNUSED(op)) +{ + bScreen *screen = CTX_wm_screen(C); + screen->scrubbing = false; + + SpaceSeq *sseq = CTX_wm_space_seq(C); + if (sseq != NULL) { + change_frame_seq_preview_end(sseq); + } + + if (need_extra_redraw_after_scrubbing_ends(C)) { + Scene *scene = CTX_data_scene(C); + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); + } +} + +/* Modal event handling of frame changing */ +static int change_onion_skin_range_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + int ret = OPERATOR_RUNNING_MODAL; + /* execute the events */ + switch (event->type) { + case EVT_ESCKEY: + ret = OPERATOR_FINISHED; + break; + + case MOUSEMOVE: + onion_skin_range_foo(C, op, event); + change_onion_skin_range_apply(C, op); + break; + + case LEFTMOUSE: + case RIGHTMOUSE: + case MIDDLEMOUSE: + /* We check for either mouse-button to end, to work with all user keymaps. */ + if (event->val == KM_RELEASE) { + ret = OPERATOR_FINISHED; + } + break; + } + + if (ret != OPERATOR_RUNNING_MODAL) { + bScreen *screen = CTX_wm_screen(C); + screen->scrubbing = false; + + if (need_extra_redraw_after_scrubbing_ends(C)) { + Scene *scene = CTX_data_scene(C); + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); + } + } + + return ret; +} + +/* Modal Operator init */ +static int change_onion_skin_range_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + bScreen *screen = CTX_wm_screen(C); + if (CTX_wm_space_seq(C) != NULL && region->regiontype == RGN_TYPE_PREVIEW) { + return OPERATOR_CANCELLED; + } + + Scene *scene = CTX_data_scene(C); + RNA_int_set(op->ptr, "right", scene->onion_skin_cache.relative_right); + RNA_int_set(op->ptr, "left", scene->onion_skin_cache.relative_left); + + onion_skin_range_foo(C, op, event); + + screen->scrubbing = true; + + change_onion_skin_range_apply(C, op); + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int change_onion_skin_range_exec(bContext *C, wmOperator *op) +{ + change_onion_skin_range_apply(C, op); + return OPERATOR_FINISHED; +} + +static void ANIM_OT_change_onion_skin_range(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Onion Skin Range"; + ot->idname = "ANIM_OT_change_onion_skin_range"; + ot->description = "Interactively change the amount of onion skins shown"; + + ot->exec = change_onion_skin_range_exec; + ot->poll = change_frame_poll; + ot->invoke = change_onion_skin_range_invoke; + ot->modal = change_onion_skin_range_modal; + ot->cancel = change_onion_skin_range_cancel; + + /* rna */ + ot->prop = RNA_def_int( + ot->srna, "left", 0, MINAFRAME, MAXFRAME, "Left", "", MINAFRAME, MAXFRAME); + ot->prop = RNA_def_int( + ot->srna, "right", 0, MINAFRAME, MAXFRAME, "Right", "", MINAFRAME, MAXFRAME); +} + /* -------------------------------------------------------------------- */ /** \name Start/End Frame Operators * \{ */ @@ -652,6 +784,7 @@ void ED_operatortypes_anim(void) { /* Animation Editors only -------------------------- */ WM_operatortype_append(ANIM_OT_change_frame); + WM_operatortype_append(ANIM_OT_change_onion_skin_range); WM_operatortype_append(ANIM_OT_start_frame_set); WM_operatortype_append(ANIM_OT_end_frame_set); diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index 5b7eee00521..4a299ba51e8 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -122,6 +122,41 @@ static void draw_current_frame(const Scene *scene, float outline_color[4]; UI_GetThemeColorShade4fv(TH_CFRAME, 5, outline_color); + if (scene->onion_skin_cache.relative_left != 0) { + const int frame_relative_left = UI_view2d_view_to_region_x( + v2d, current_frame - scene->onion_skin_cache.relative_left); + UI_draw_roundbox_4fv_ex( + &(const rctf){ + .xmin = frame_relative_left - box_width / 4 + U.pixelsize / 2, + .xmax = frame_relative_left + box_width / 4 + U.pixelsize / 2, + .ymin = scrub_region_rect->ymin + box_padding, + .ymax = scrub_region_rect->ymax - box_padding, + }, + bg_color, + NULL, + 1.0f, + outline_color, + U.pixelsize, + 4 * UI_SCALE_FAC); + } + if (scene->onion_skin_cache.relative_right != 0) { + const int frame_relative_right = UI_view2d_view_to_region_x( + v2d, current_frame + scene->onion_skin_cache.relative_right); + UI_draw_roundbox_4fv_ex( + &(const rctf){ + .xmin = frame_relative_right - box_width / 4 + U.pixelsize / 2, + .xmax = frame_relative_right + box_width / 4 + U.pixelsize / 2, + .ymin = scrub_region_rect->ymin + box_padding, + .ymax = scrub_region_rect->ymax - box_padding, + }, + bg_color, + NULL, + 1.0f, + outline_color, + U.pixelsize, + 4 * UI_SCALE_FAC); + } + UI_draw_roundbox_4fv_ex( &(const rctf){ .xmin = frame_x - box_width / 2 + U.pixelsize / 2, diff --git a/source/blender/editors/include/ED_onion_skin.h b/source/blender/editors/include/ED_onion_skin.h new file mode 100644 index 00000000000..cabf3095c58 --- /dev/null +++ b/source/blender/editors/include/ED_onion_skin.h @@ -0,0 +1,2 @@ + +void ED_operatortypes_onion_skin(void); diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 56aa0104329..76bfb15cebf 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -41,6 +41,7 @@ #include "ED_mesh.h" #include "ED_node.h" #include "ED_object.h" +#include "ED_onion_skin.h" #include "ED_paint.h" #include "ED_physics.h" #include "ED_render.h" @@ -105,6 +106,7 @@ void ED_spacetypes_init(void) ED_operatortypes_curves(); ED_operatortypes_armature(); ED_operatortypes_marker(); + ED_operatortypes_onion_skin(); ED_operatortypes_metaball(); ED_operatortypes_sound(); ED_operatortypes_render(); diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index e54b70023a2..56f71870ae2 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1857,6 +1857,28 @@ typedef struct SceneGpencil { char _pad[4]; } SceneGpencil; +typedef struct OnionSkinMeshLink { + struct OnionSkinMeshLink *prev, *next; + /* Having a Mesh pointer here breaks compilation. Complains about "extern C" issues. */ + struct Object *object; + float frame; + char _pad[4]; +} OnionSkinMeshLink; + +enum { + ONION_SKIN_DRAW_SOLID = 0, + ONION_SKIN_DRAW_OUTLINE = 1, +}; + +typedef struct SceneOnionSkin { + float color_left[3]; + float color_right[3]; + float alpha; + int relative_left, relative_right; + int draw_method; + ListBase objects /* OnionSkinMeshLink */; +} SceneOnionSkin; + /** \} */ /* -------------------------------------------------------------------- */ @@ -2003,6 +2025,7 @@ typedef struct Scene { struct SceneDisplay display; struct SceneEEVEE eevee; struct SceneGpencil grease_pencil_settings; + struct SceneOnionSkin onion_skin_cache; } Scene; /** \} */ diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 6b6783ba593..0091d271ca9 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -671,6 +671,12 @@ static const EnumPropertyItem plane_orientation_items[] = { {0, NULL, 0, NULL, NULL}, }; +static const EnumPropertyItem onion_skin_draw_modes[] = { + {ONION_SKIN_DRAW_SOLID, "SOLID", 0, "Solid", "Draw solid shape"}, + {ONION_SKIN_DRAW_OUTLINE, "OUTLINE", 0, "Outline", "Draw outline"}, + {0, NULL, 0, NULL, NULL}, +}; + static const EnumPropertyItem snap_to_items[] = { {SCE_SNAP_MODE_GEOM, "GEOMETRY", 0, "Geometry", "Snap to all geometry"}, {SCE_SNAP_MODE_NONE, "DEFAULT", 0, "Default", "Use the current snap settings"}, @@ -8442,6 +8448,48 @@ void RNA_def_scene(BlenderRNA *brna) RNA_def_property_struct_type(prop, "SceneGpencil"); RNA_def_property_ui_text(prop, "Grease Pencil", "Grease Pencil settings for the scene"); + prop = RNA_def_property(srna, "onion_skin_color_left", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, NULL, "onion_skin_cache.color_left"); + RNA_def_property_array(prop, 3); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Onion Skin Color Left", "Define the color of onion skins"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); + + prop = RNA_def_property(srna, "onion_skin_color_right", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, NULL, "onion_skin_cache.color_right"); + RNA_def_property_array(prop, 3); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Onion Skin Color Right", "Define the color of onion skins"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); + + prop = RNA_def_property(srna, "os_relative_left", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "onion_skin_cache.relative_left"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text( + prop, "Onion Skin Range Left", "how far to the lef to draw onion skins"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); + + prop = RNA_def_property(srna, "os_relative_right", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "onion_skin_cache.relative_right"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text( + prop, "Onion Skin Range Right", "how far to the lef to draw onion skins"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); + + prop = RNA_def_property(srna, "onion_skin_alpha", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "onion_skin_cache.alpha"); + RNA_def_property_ui_text(prop, "Onion Skin Alpha", "Alpha of drawn onion skins"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1, 2); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); + + prop = RNA_def_property(srna, "onion_skin_draw_modes", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "onion_skin_cache.draw_method"); + RNA_def_property_enum_items(prop, onion_skin_draw_modes); + RNA_def_property_enum_default(prop, ONION_SKIN_DRAW_SOLID); + RNA_def_property_ui_text(prop, "Onion Skin Draw Modes", "How to draw os's"); + /* Nestled Data. */ /* *** Non-Animated *** */ RNA_define_animate_sdna(false); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index c4862befdf5..cc45f7d3dd4 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -1542,7 +1542,8 @@ static void rna_3DViewShading_render_pass_set(PointerRNA *ptr, int value) STRNCPY(shading->aov_name, aov->name); } else if (value == EEVEE_RENDER_PASS_BLOOM && - ((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) { + ((scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED) == 0)) + { shading->render_pass = EEVEE_RENDER_PASS_COMBINED; } else { @@ -3299,7 +3300,8 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UN ATTR_DOMAIN_CORNER, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_POINT, - ATTR_DOMAIN_FACE)) { + ATTR_DOMAIN_FACE)) + { continue; } }