Fix: GPv3: Duplicate previous key using auto key

When erasing, sculpting or tinting, the previous key was not being
duplicated.

The `ed::greasepencil::ensure_active_keyframe` didn't have a good
mechanism to make this work. This adds a parameter to the
`ensure_active_keyframe` function so that the caller can decide
what should be done.

For the sculpting tools, eraser, and tint tool, this will now duplicate
the previous key, when auto-key is on.

Resolves #124082.

Pull Request: https://projects.blender.org/blender/blender/pulls/125224
This commit is contained in:
Falk David
2024-07-22 18:09:17 +02:00
committed by Falk David
parent d0ff8d89c9
commit fc49a5928b
6 changed files with 54 additions and 23 deletions

View File

@@ -2251,7 +2251,7 @@ static int grease_pencil_paste_strokes_exec(bContext *C, wmOperator *op)
/* Ensure active keyframe. */
bool inserted_keyframe = false;
if (!ensure_active_keyframe(C, grease_pencil, inserted_keyframe)) {
if (!ensure_active_keyframe(C, grease_pencil, false, inserted_keyframe)) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
return OPERATOR_CANCELLED;
}

View File

@@ -337,7 +337,10 @@ void create_keyframe_edit_data_selected_frames_list(KeyframeEditData *ked,
}
}
bool ensure_active_keyframe(bContext *C, GreasePencil &grease_pencil, bool &r_inserted_keyframe)
bool ensure_active_keyframe(bContext *C,
GreasePencil &grease_pencil,
const bool duplicate_previous_key,
bool &r_inserted_keyframe)
{
Scene &scene = *CTX_data_scene(C);
const int current_frame = scene.r.cfra;
@@ -351,24 +354,17 @@ bool ensure_active_keyframe(bContext *C, GreasePencil &grease_pencil, bool &r_in
* keyframe needs to be inserted. */
const bool is_first = active_layer.is_empty() ||
(active_layer.sorted_keys().first() > current_frame);
const std::optional<int> current_start_frame = active_layer.start_frame_at(current_frame);
const bool needs_new_drawing = is_first || !current_start_frame ||
(current_start_frame < current_frame);
const std::optional<int> previous_key_frame_start = active_layer.start_frame_at(current_frame);
const bool has_previous_key = previous_key_frame_start.has_value();
const bool needs_new_drawing = is_first || !has_previous_key ||
(previous_key_frame_start < current_frame);
if (blender::animrig::is_autokey_on(&scene) && needs_new_drawing) {
ViewLayer *view_layer = CTX_data_view_layer(C);
const Brush *brush = BKE_paint_brush_for_read(BKE_paint_get_active(&scene, view_layer));
const bool use_additive_drawing = (scene.toolsettings->gpencil_flags &
GP_TOOL_FLAG_RETAIN_LAST) != 0;
/* Eraser tool makes no sense on empty drawings, don't insert new frames. */
const bool allow_empty_frame = (brush->gpencil_tool != GPAINT_TOOL_ERASE);
if (current_start_frame && (use_additive_drawing || !allow_empty_frame)) {
/* For additive drawing, we duplicate the frame that's currently visible and insert it at the
* current frame.
* NOTE: Also duplicate the frame when erasing, Otherwise empty drawing is added, see
* !119051.
*/
if (has_previous_key && (use_additive_drawing || duplicate_previous_key)) {
/* We duplicate the frame that's currently visible and insert it at the current frame. */
grease_pencil.insert_duplicate_frame(
active_layer, *current_start_frame, current_frame, false);
active_layer, *previous_key_frame_start, current_frame, false);
}
else {
/* Otherwise we just insert a blank keyframe at the current frame. */

View File

@@ -656,7 +656,7 @@ static void grease_pencil_primitive_update_view(bContext *C, PrimitiveToolOperat
/* Invoke handler: Initialize the operator. */
static int grease_pencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int return_value = ed::greasepencil::grease_pencil_draw_operator_invoke(C, op);
int return_value = ed::greasepencil::grease_pencil_draw_operator_invoke(C, op, false);
if (return_value != OPERATOR_RUNNING_MODAL) {
return return_value;
}

View File

@@ -1237,7 +1237,9 @@ float opacity_from_input_sample(const float pressure,
return opacity;
}
int grease_pencil_draw_operator_invoke(bContext *C, wmOperator *op)
int grease_pencil_draw_operator_invoke(bContext *C,
wmOperator *op,
const bool use_duplicate_previous_key)
{
const Object *object = CTX_data_active_object(C);
if (!object || object->type != OB_GREASE_PENCIL) {
@@ -1265,7 +1267,9 @@ int grease_pencil_draw_operator_invoke(bContext *C, wmOperator *op)
/* Ensure a drawing at the current keyframe. */
bool inserted_keyframe = false;
if (!ed::greasepencil::ensure_active_keyframe(C, grease_pencil, inserted_keyframe)) {
if (!ed::greasepencil::ensure_active_keyframe(
C, grease_pencil, use_duplicate_previous_key, inserted_keyframe))
{
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
return OPERATOR_CANCELLED;
}

View File

@@ -227,7 +227,10 @@ bool has_any_frame_selected(const bke::greasepencil::Layer &layer);
* create one when auto-key is on (taking additive drawing setting into account).
* \return false when no keyframe could be found or created.
*/
bool ensure_active_keyframe(bContext *C, GreasePencil &grease_pencil, bool &r_inserted_keyframe);
bool ensure_active_keyframe(bContext *C,
GreasePencil &grease_pencil,
bool duplicate_previous_key,
bool &r_inserted_keyframe);
void create_keyframe_edit_data_selected_frames_list(KeyframeEditData *ked,
const bke::greasepencil::Layer &layer);
@@ -250,7 +253,9 @@ float radius_from_input_sample(const RegionView3D *rv3d,
float3 location,
float4x4 to_world,
const BrushGpencilSettings *settings);
int grease_pencil_draw_operator_invoke(bContext *C, wmOperator *op);
int grease_pencil_draw_operator_invoke(bContext *C,
wmOperator *op,
bool use_duplicate_previous_key);
float4x2 calculate_texture_space(const Scene *scene,
const ARegion *region,
const float2 &mouse,

View File

@@ -210,7 +210,28 @@ static bool grease_pencil_brush_stroke_poll(bContext *C)
static int grease_pencil_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int return_value = ed::greasepencil::grease_pencil_draw_operator_invoke(C, op);
const bool use_duplicate_previous_key = [&]() -> bool {
const Paint *paint = BKE_paint_get_active_from_context(C);
const Brush &brush = *BKE_paint_brush_for_read(paint);
const PaintMode mode = BKE_paintmode_get_active_from_context(C);
const BrushStrokeMode stroke_mode = BrushStrokeMode(RNA_enum_get(op->ptr, "mode"));
if (mode == PaintMode::GPencil) {
/* For the eraser and tint tool, we don't want auto-key to create an empty keyframe, so we
* duplicate the previous frame. */
if (ELEM(eBrushGPaintTool(brush.gpencil_tool), GPAINT_TOOL_ERASE, GPAINT_TOOL_TINT)) {
return true;
}
/* Same for the temporary eraser when using the draw tool. */
if (eBrushGPaintTool(brush.gpencil_tool) == GPAINT_TOOL_DRAW &&
stroke_mode == BRUSH_STROKE_ERASE)
{
return true;
}
}
return false;
}();
int return_value = ed::greasepencil::grease_pencil_draw_operator_invoke(
C, op, use_duplicate_previous_key);
if (return_value != OPERATOR_RUNNING_MODAL) {
return return_value;
}
@@ -304,7 +325,12 @@ static int grease_pencil_sculpt_paint_invoke(bContext *C, wmOperator *op, const
/* Ensure a drawing at the current keyframe. */
bool inserted_keyframe = false;
if (!ed::greasepencil::ensure_active_keyframe(C, grease_pencil, inserted_keyframe)) {
/* For the sculpt tools, we don't want the auto-key to create an empty keyframe, so we duplicate
* the previous key. */
const bool use_duplicate_previous_key = true;
if (!ed::greasepencil::ensure_active_keyframe(
C, grease_pencil, use_duplicate_previous_key, inserted_keyframe))
{
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
return OPERATOR_CANCELLED;
}