Initial Grease Pencil 3.0 stage #106848

Merged
Falk David merged 224 commits from filedescriptor/blender:grease-pencil-v3 into main 2023-05-30 11:14:22 +02:00
7 changed files with 258 additions and 13 deletions
Showing only changes of commit 3bff9cb5d6 - Show all commits

View File

@ -3812,16 +3812,22 @@ def km_grease_pencil_stroke_paint_draw_brush(params):
items.extend([
# Draw
("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}),
("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}),
# Draw - straight lines
("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
{"properties": [("mode", 'DRAW_STRAIGHT'), ("wait_for_input", False)]}),
# Erase
("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
{"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}),
("grease_pencil.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("mode", 'NORMAL')]}),
("grease_pencil.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
{"properties": [("mode", 'INVERT')]}),
("grease_pencil.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("mode", 'SMOOTH')]}),
# ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS'},
# {"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}),
# ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
# {"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}),
# # Draw - straight lines
# ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
# {"properties": [("mode", 'DRAW_STRAIGHT'), ("wait_for_input", False)]}),
# # Erase
# ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
# {"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}),
# Constrain Guides Speedlines
# Freehand
("gpencil.draw", {"type": 'O', "value": 'PRESS'}, None),

View File

@ -1940,7 +1940,16 @@ class _defs_gpencil_paint:
operator="gpencil.draw",
),
)
@ToolDef.from_fn
def draw():
return dict(
idname="builtin_brush.draw",
label="Draw",
icon="brush.gpencil_draw.draw",
data_block='DRAW',
)
@ToolDef.from_fn
def cutter():
def draw_settings(_context, layout, tool):
@ -3121,7 +3130,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
'PAINT_GPENCIL': [
_defs_view3d_generic.cursor,
None,
_defs_gpencil_paint.generate_from_brushes,
_defs_gpencil_paint.draw,
_defs_gpencil_paint.cutter,
None,
_defs_gpencil_paint.eyedropper,

View File

@ -138,7 +138,12 @@ static bool gpencil_stroke_weightmode_poll_with_tool(bContext *C, const char gpe
/* Poll callback for stroke painting (draw brush) */
static bool gpencil_stroke_paintmode_draw_poll(bContext *C)
{
return gpencil_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_DRAW);
Object *object = CTX_data_active_object(C);
if (object == NULL || object->type != OB_GREASE_PENCIL) {
return false;
}
return true;
// return gpencil_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_DRAW);
}
/* Poll callback for stroke painting (erase brush) */

View File

@ -42,6 +42,7 @@ set(SRC
curves_sculpt_smooth.cc
curves_sculpt_snake_hook.cc
grease_pencil_draw_ops.cc
grease_pencil_paint.cc
paint_canvas.cc
paint_cursor.cc
paint_curve.c
@ -88,6 +89,7 @@ set(SRC
curves_sculpt_intern.h
curves_sculpt_intern.hh
grease_pencil_intern.hh
paint_intern.h
sculpt_intern.hh
)

View File

@ -11,6 +11,7 @@
#include "ED_grease_pencil_draw.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "RNA_access.h"
#include "RNA_define.h"
@ -20,10 +21,151 @@
#include "WM_message.h"
#include "WM_toolsystem.h"
#include "grease_pencil_intern.hh"
#include "paint_intern.h"
namespace blender::ed::sculpt_paint {
/* -------------------------------------------------------------------- */
/** \name Brush Stroke Operator
* \{ */
static bool start_brush_operation(bContext &C,
wmOperator &op,
PaintStroke *paint_stroke,
const StrokeExtension &stroke_start)
{
const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op.ptr, "mode"));
const Scene &scene = *CTX_data_scene(&C);
const GpPaint &gp_paint = *scene.toolsettings->gp_paint;
const Brush &brush = *BKE_paint_brush_for_read(&gp_paint.paint);
GreasePencilStrokeOperation *operation = nullptr;
switch (brush.gpencil_tool) {
case GPAINT_TOOL_DRAW:
/* FIXME: Somehow store the unique_ptr in the PaintStroke. */
operation = gpencil::new_paint_operation().release();
break;
default:
BLI_assert_unreachable();
Review

I am not really sure what the goal is here. If the goal is to somehow ensure all cases are handled it is much better to not use default and let compiler tell you where cases are missing at the compile time.

I am not really sure what the goal is here. If the goal is to somehow ensure all cases are handled it is much better to not use `default` and let compiler tell you where cases are missing at the compile time.
}
if (operation) {
paint_stroke_set_mode_data(paint_stroke, operation);
return true;
}
return false;
}
static bool stroke_get_location(bContext * /*C*/,
float out[3],
const float mouse[2],
bool /*force_original*/)
{
out[0] = mouse[0];
out[1] = mouse[1];
out[2] = 0;
return true;
}
static bool stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2])
{
PaintStroke *paint_stroke = static_cast<PaintStroke *>(op->customdata);
StrokeExtension stroke_extension;
stroke_extension.mouse_position = float2(mouse);
stroke_extension.pressure = 0.0f;
stroke_extension.is_first = true;
if (!start_brush_operation(*C, *op, paint_stroke, stroke_extension)) {
return false;
}
return true;
}
static void stroke_update_step(bContext *C,
wmOperator *op,
PaintStroke *stroke,
PointerRNA *stroke_element)
{
GreasePencilStrokeOperation *operation = static_cast<GreasePencilStrokeOperation *>(
paint_stroke_mode_data(stroke));
StrokeExtension stroke_extension;
RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position);
stroke_extension.pressure = RNA_float_get(stroke_element, "pressure");
stroke_extension.is_first = false;
if (operation) {
operation->on_stroke_extended(*C, stroke_extension);
}
}
static void stroke_redraw(const bContext *C, PaintStroke * /*stroke*/, bool /*final*/)
{
ED_region_tag_redraw(CTX_wm_region(C));
}
static void stroke_done(const bContext *C, PaintStroke *stroke)
{
GreasePencilStrokeOperation *operation = static_cast<GreasePencilStrokeOperation *>(
paint_stroke_mode_data(stroke));
operation->on_stroke_done(*C);
}
static int grease_pencil_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
const Paint *paint = BKE_paint_get_active_from_context(C);
const Brush *brush = BKE_paint_brush_for_read(paint);
if (brush == nullptr) {
return OPERATOR_CANCELLED;
}
op->customdata = static_cast<void *>(paint_stroke_new(C,
filedescriptor marked this conversation as resolved Outdated

Is the explicit cast to void* needed?

Is the explicit cast to `void*` needed?
op,
stroke_get_location,
stroke_test_start,
stroke_update_step,
stroke_redraw,
stroke_done,
event->type));
int return_value = op->type->modal(C, op, event);
filedescriptor marked this conversation as resolved
Review

const int

`const int`
if (return_value == OPERATOR_FINISHED) {
return OPERATOR_FINISHED;
}
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int grease_pencil_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
}
static void grease_pencil_stroke_cancel(bContext *C, wmOperator *op)
{
paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
}
static void GREASE_PENCIL_OT_brush_stroke(struct wmOperatorType *ot)
{
ot->name = "Stroke Curves Sculpt";
ot->idname = "GREASE_PENCIL_OT_brush_stroke";
ot->description = "Sculpt curves using a brush";
ot->invoke = grease_pencil_stroke_invoke;
ot->modal = grease_pencil_stroke_modal;
ot->cancel = grease_pencil_stroke_cancel;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
paint_stroke_operator_properties(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Toggle Draw Mode
@ -121,6 +263,7 @@ static void GREASE_PENCIL_OT_draw_mode_toggle(wmOperatorType *ot)
void ED_operatortypes_grease_pencil_draw()
{
using namespace blender::ed::sculpt_paint;
WM_operatortype_append(GREASE_PENCIL_OT_brush_stroke);
WM_operatortype_append(GREASE_PENCIL_OT_draw_mode_toggle);
}

View File

@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation. */
#pragma once
#include "paint_intern.h"
#include "BLI_math_vector.hh"
namespace blender::ed::sculpt_paint {
struct StrokeExtension {
bool is_first;
float2 mouse_position;
float pressure;
};
class GreasePencilStrokeOperation {
public:
virtual ~GreasePencilStrokeOperation() = default;
virtual void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) = 0;
virtual void on_stroke_done(const bContext &C) = 0;
};
namespace gpencil {
std::unique_ptr<GreasePencilStrokeOperation> new_paint_operation();
} // namespace gpencil
} // namespace blender::ed::sculpt_paint

View File

@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation. */
#include "grease_pencil_intern.hh"
namespace blender::ed::sculpt_paint::gpencil {
class PaintOperation : public GreasePencilStrokeOperation {
public:
~PaintOperation() override {}
void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
void on_stroke_done(const bContext &C) override;
};
/**
* Utility class that actually executes the update when the stroke is updated. That's useful
* because it avoids passing a very large number of parameters between functions.
*/
struct PaintOperationExecutor {
PaintOperationExecutor(const bContext &C) {}
void execute(PaintOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
{
std::cout << "pos: (" << stroke_extension.mouse_position.x << ", "
<< stroke_extension.mouse_position.x << "), pressure: " << stroke_extension.pressure
<< std::endl;
}
};
void PaintOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
{
PaintOperationExecutor executor{C};
executor.execute(*this, C, stroke_extension);
}
void PaintOperation::on_stroke_done(const bContext &C)
{
std::cout << "Done!" << std::endl;
}
std::unique_ptr<GreasePencilStrokeOperation> new_paint_operation()
{
return std::make_unique<PaintOperation>();
}
Review

It seems a bit weird to modifier the evaluated object here. Is that purposeful? Or am I missing something?

It seems a bit weird to modifier the evaluated object here. Is that purposeful? Or am I missing something?
Review

Yes, this is done on purpose so that the extra copy from orig to eval for rendering is not needed every update. Once drawing is done, the stroke is copied from the eval buffer to orig.

Yes, this is done on purpose so that the extra copy from orig to eval for rendering is not needed every update. Once drawing is done, the stroke is copied from the eval buffer to orig.
Review

Okay, makes sense. This definitely deserves a comment, since typically changing the evaluated data isn't a good idea.

Okay, makes sense. This definitely deserves a comment, since typically changing the evaluated data isn't a good idea.
} // namespace blender::ed::sculpt_paint::gpencil