Cleanup: Replace eTexPaintMode with classes.
For an upcoming prototype we would introduced a new eTexPaintMode option. That would add more cases and if statements. This change migrate the eTexPaintMode to 3 classes. AbstractPaintMode contains a shared interface. ImagePaintMode for 2d painting and ProjectionPaintMode for 3d painting.
This commit is contained in:
parent
683a77256d
commit
0b4c56b010
|
@ -30,6 +30,7 @@ set(SRC
|
|||
paint_curve_undo.c
|
||||
paint_hide.c
|
||||
paint_image.cc
|
||||
paint_image_ops_paint.cc
|
||||
paint_image_2d.c
|
||||
paint_image_2d_curve_mask.cc
|
||||
paint_image_proj.c
|
||||
|
|
|
@ -286,7 +286,7 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool image_paint_poll(bContext *C)
|
||||
bool image_paint_poll(bContext *C)
|
||||
{
|
||||
return image_paint_poll_ex(C, true);
|
||||
}
|
||||
|
@ -312,24 +312,6 @@ static bool image_paint_2d_clone_poll(bContext *C)
|
|||
}
|
||||
|
||||
/************************ paint operator ************************/
|
||||
enum class eTexPaintMode {
|
||||
_2D,
|
||||
_3D_PROJECT,
|
||||
};
|
||||
|
||||
struct PaintOperation {
|
||||
eTexPaintMode mode;
|
||||
|
||||
void *stroke_handle;
|
||||
|
||||
float prevmouse[2];
|
||||
float startmouse[2];
|
||||
double starttime;
|
||||
|
||||
wmPaintCursor *cursor;
|
||||
ViewContext vc;
|
||||
};
|
||||
|
||||
bool paint_use_opacity_masking(Brush *brush)
|
||||
{
|
||||
return ((brush->flag & BRUSH_AIRBRUSH) || (brush->flag & BRUSH_DRAG_DOT) ||
|
||||
|
@ -419,366 +401,6 @@ void paint_brush_exit_tex(Brush *brush)
|
|||
}
|
||||
}
|
||||
|
||||
static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata)
|
||||
{
|
||||
PaintOperation *pop = (PaintOperation *)customdata;
|
||||
|
||||
if (pop) {
|
||||
GPU_line_smooth(true);
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
|
||||
|
||||
ARegion *region = pop->vc.region;
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
||||
|
||||
GPU_line_width(4.0);
|
||||
immUniformColor4ub(0, 0, 0, 255);
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 2);
|
||||
immVertex2i(pos, x, y);
|
||||
immVertex2i(
|
||||
pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin);
|
||||
immEnd();
|
||||
|
||||
GPU_line_width(2.0);
|
||||
immUniformColor4ub(255, 255, 255, 255);
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 2);
|
||||
immVertex2i(pos, x, y);
|
||||
immVertex2i(
|
||||
pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin);
|
||||
immEnd();
|
||||
|
||||
immUnbindProgram();
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
GPU_line_smooth(false);
|
||||
}
|
||||
}
|
||||
|
||||
static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2])
|
||||
{
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ToolSettings *settings = scene->toolsettings;
|
||||
PaintOperation *pop = MEM_cnew<PaintOperation>("PaintOperation"); /* caller frees */
|
||||
Brush *brush = BKE_paint_brush(&settings->imapaint.paint);
|
||||
int mode = RNA_enum_get(op->ptr, "mode");
|
||||
ED_view3d_viewcontext_init(C, &pop->vc, depsgraph);
|
||||
|
||||
copy_v2_v2(pop->prevmouse, mouse);
|
||||
copy_v2_v2(pop->startmouse, mouse);
|
||||
|
||||
/* initialize from context */
|
||||
if (CTX_wm_region_view3d(C)) {
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *ob = OBACT(view_layer);
|
||||
bool uvs, mat, tex, stencil;
|
||||
if (!ED_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, &stencil)) {
|
||||
ED_paint_data_warning(op->reports, uvs, mat, tex, stencil);
|
||||
MEM_delete(pop);
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
pop->mode = eTexPaintMode::_3D_PROJECT;
|
||||
pop->stroke_handle = paint_proj_new_stroke(C, ob, mouse, mode);
|
||||
}
|
||||
else {
|
||||
pop->mode = eTexPaintMode::_2D;
|
||||
pop->stroke_handle = paint_2d_new_stroke(C, op, mode);
|
||||
}
|
||||
|
||||
if (!pop->stroke_handle) {
|
||||
MEM_delete(pop);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) {
|
||||
pop->cursor = WM_paint_cursor_activate(
|
||||
SPACE_TYPE_ANY, RGN_TYPE_ANY, image_paint_poll, gradient_draw_line, pop);
|
||||
}
|
||||
|
||||
settings->imapaint.flag |= IMAGEPAINT_DRAWING;
|
||||
ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D);
|
||||
|
||||
return pop;
|
||||
}
|
||||
|
||||
static void paint_stroke_update_step(bContext *C,
|
||||
wmOperator *UNUSED(op),
|
||||
struct PaintStroke *stroke,
|
||||
PointerRNA *itemptr)
|
||||
{
|
||||
PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke));
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ToolSettings *toolsettings = CTX_data_tool_settings(C);
|
||||
UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings;
|
||||
Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint);
|
||||
|
||||
float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f;
|
||||
|
||||
/* initial brush values. Maybe it should be considered moving these to stroke system */
|
||||
float startalpha = BKE_brush_alpha_get(scene, brush);
|
||||
|
||||
float mouse[2];
|
||||
float pressure;
|
||||
float size;
|
||||
float distance = paint_stroke_distance_get(stroke);
|
||||
int eraser;
|
||||
|
||||
RNA_float_get_array(itemptr, "mouse", mouse);
|
||||
pressure = RNA_float_get(itemptr, "pressure");
|
||||
eraser = RNA_boolean_get(itemptr, "pen_flip");
|
||||
size = RNA_float_get(itemptr, "size");
|
||||
|
||||
/* stroking with fill tool only acts on stroke end */
|
||||
if (brush->imagepaint_tool == PAINT_TOOL_FILL) {
|
||||
copy_v2_v2(pop->prevmouse, mouse);
|
||||
return;
|
||||
}
|
||||
|
||||
if (BKE_brush_use_alpha_pressure(brush)) {
|
||||
BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac));
|
||||
}
|
||||
else {
|
||||
BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac));
|
||||
}
|
||||
|
||||
if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) {
|
||||
UndoStack *ustack = CTX_wm_manager(C)->undo_stack;
|
||||
ED_image_undo_restore(ustack->step_init);
|
||||
}
|
||||
|
||||
switch (pop->mode) {
|
||||
case eTexPaintMode::_2D:
|
||||
paint_2d_stroke(pop->stroke_handle, pop->prevmouse, mouse, eraser, pressure, distance, size);
|
||||
break;
|
||||
|
||||
case eTexPaintMode::_3D_PROJECT:
|
||||
paint_proj_stroke(
|
||||
C, pop->stroke_handle, pop->prevmouse, mouse, eraser, pressure, distance, size);
|
||||
break;
|
||||
}
|
||||
|
||||
copy_v2_v2(pop->prevmouse, mouse);
|
||||
|
||||
/* restore brush values */
|
||||
BKE_brush_alpha_set(scene, brush, startalpha);
|
||||
}
|
||||
|
||||
static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final)
|
||||
{
|
||||
PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke));
|
||||
|
||||
switch (pop->mode) {
|
||||
case eTexPaintMode::_2D:
|
||||
paint_2d_redraw(C, pop->stroke_handle, final);
|
||||
break;
|
||||
|
||||
case eTexPaintMode::_3D_PROJECT:
|
||||
paint_proj_redraw(C, pop->stroke_handle, final);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ToolSettings *toolsettings = scene->toolsettings;
|
||||
PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke));
|
||||
Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint);
|
||||
|
||||
toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING;
|
||||
|
||||
if (brush->imagepaint_tool == PAINT_TOOL_FILL) {
|
||||
if (brush->flag & BRUSH_USE_GRADIENT) {
|
||||
switch (pop->mode) {
|
||||
case eTexPaintMode::_2D:
|
||||
paint_2d_gradient_fill(C, brush, pop->startmouse, pop->prevmouse, pop->stroke_handle);
|
||||
break;
|
||||
|
||||
case eTexPaintMode::_3D_PROJECT:
|
||||
paint_proj_stroke(C,
|
||||
pop->stroke_handle,
|
||||
pop->startmouse,
|
||||
pop->prevmouse,
|
||||
paint_stroke_flipped(stroke),
|
||||
1.0,
|
||||
0.0,
|
||||
BKE_brush_size_get(scene, brush));
|
||||
/* two redraws, one for GPU update, one for notification */
|
||||
paint_proj_redraw(C, pop->stroke_handle, false);
|
||||
paint_proj_redraw(C, pop->stroke_handle, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (pop->mode) {
|
||||
case eTexPaintMode::_2D: {
|
||||
float color[3];
|
||||
if (paint_stroke_inverted(stroke)) {
|
||||
srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush));
|
||||
}
|
||||
else {
|
||||
srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush));
|
||||
}
|
||||
paint_2d_bucket_fill(
|
||||
C, color, brush, pop->startmouse, pop->prevmouse, pop->stroke_handle);
|
||||
break;
|
||||
}
|
||||
|
||||
case eTexPaintMode::_3D_PROJECT:
|
||||
paint_proj_stroke(C,
|
||||
pop->stroke_handle,
|
||||
pop->startmouse,
|
||||
pop->prevmouse,
|
||||
paint_stroke_flipped(stroke),
|
||||
1.0,
|
||||
0.0,
|
||||
BKE_brush_size_get(scene, brush));
|
||||
/* two redraws, one for GPU update, one for notification */
|
||||
paint_proj_redraw(C, pop->stroke_handle, false);
|
||||
paint_proj_redraw(C, pop->stroke_handle, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (pop->mode) {
|
||||
case eTexPaintMode::_2D:
|
||||
paint_2d_stroke_done(pop->stroke_handle);
|
||||
break;
|
||||
|
||||
case eTexPaintMode::_3D_PROJECT:
|
||||
paint_proj_stroke_done(pop->stroke_handle);
|
||||
break;
|
||||
}
|
||||
|
||||
if (pop->cursor) {
|
||||
WM_paint_cursor_end(pop->cursor);
|
||||
}
|
||||
|
||||
ED_image_undo_push_end();
|
||||
|
||||
/* duplicate warning, see texpaint_init */
|
||||
#if 0
|
||||
if (pop->s.warnmultifile) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_WARNING,
|
||||
"Image requires 4 color channels to paint: %s",
|
||||
pop->s.warnmultifile);
|
||||
}
|
||||
if (pop->s.warnpackedfile) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_WARNING,
|
||||
"Packed MultiLayer files cannot be painted: %s",
|
||||
pop->s.warnpackedfile);
|
||||
}
|
||||
#endif
|
||||
MEM_delete(pop);
|
||||
}
|
||||
|
||||
static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
|
||||
{
|
||||
PaintOperation *pop;
|
||||
|
||||
/* TODO: Should avoid putting this here. Instead, last position should be requested
|
||||
* from stroke system. */
|
||||
|
||||
if (!(pop = texture_paint_init(C, op, mouse))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
paint_stroke_set_mode_data(static_cast<PaintStroke *>(op->customdata), pop);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
int retval;
|
||||
|
||||
op->customdata = paint_stroke_new(C,
|
||||
op,
|
||||
nullptr,
|
||||
paint_stroke_test_start,
|
||||
paint_stroke_update_step,
|
||||
paint_stroke_redraw,
|
||||
paint_stroke_done,
|
||||
event->type);
|
||||
|
||||
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
|
||||
paint_stroke_free(C, op, static_cast<PaintStroke *>(op->customdata));
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
/* add modal handler */
|
||||
WM_event_add_modal_handler(C, op);
|
||||
|
||||
OPERATOR_RETVAL_CHECK(retval);
|
||||
BLI_assert(retval == OPERATOR_RUNNING_MODAL);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static int paint_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
PropertyRNA *strokeprop;
|
||||
PointerRNA firstpoint;
|
||||
float mouse[2];
|
||||
|
||||
strokeprop = RNA_struct_find_property(op->ptr, "stroke");
|
||||
|
||||
if (!RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
RNA_float_get_array(&firstpoint, "mouse", mouse);
|
||||
|
||||
op->customdata = paint_stroke_new(C,
|
||||
op,
|
||||
nullptr,
|
||||
paint_stroke_test_start,
|
||||
paint_stroke_update_step,
|
||||
paint_stroke_redraw,
|
||||
paint_stroke_done,
|
||||
0);
|
||||
/* frees op->customdata */
|
||||
return paint_stroke_exec(C, op, static_cast<PaintStroke *>(op->customdata));
|
||||
}
|
||||
|
||||
static int paint_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
return paint_stroke_modal(C, op, event, static_cast<PaintStroke *>(op->customdata));
|
||||
}
|
||||
|
||||
static void paint_cancel(bContext *C, wmOperator *op)
|
||||
{
|
||||
paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
|
||||
}
|
||||
|
||||
void PAINT_OT_image_paint(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Image Paint";
|
||||
ot->idname = "PAINT_OT_image_paint";
|
||||
ot->description = "Paint a stroke into the image";
|
||||
|
||||
/* api callbacks */
|
||||
ot->invoke = paint_invoke;
|
||||
ot->modal = paint_modal;
|
||||
ot->exec = paint_exec;
|
||||
ot->poll = image_paint_poll;
|
||||
ot->cancel = paint_cancel;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_BLOCKING;
|
||||
|
||||
paint_stroke_operator_properties(ot);
|
||||
}
|
||||
|
||||
bool get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy)
|
||||
{
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
|
|
|
@ -0,0 +1,530 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
|
||||
|
||||
/** \file
|
||||
* \ingroup edsculpt
|
||||
* \brief Painting operator to paint in 2D and 3D.
|
||||
*/
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_color_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "BKE_brush.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_paint.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "ED_paint.h"
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "GPU_immediate.h"
|
||||
#include "GPU_state.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_message.h"
|
||||
#include "WM_toolsystem.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "paint_intern.h"
|
||||
|
||||
namespace blender::ed::sculpt_paint::image::ops::paint {
|
||||
|
||||
/**
|
||||
* Interface to use the same painting operator for 3D and 2D painting. Interface removes the
|
||||
* differences between the actual calls that are being performed.
|
||||
*/
|
||||
class AbstractPaintMode {
|
||||
public:
|
||||
virtual void *paint_new_stroke(
|
||||
bContext *C, wmOperator *op, Object *ob, const float mouse[2], int mode) = 0;
|
||||
virtual void paint_stroke(bContext *C,
|
||||
void *stroke_handle,
|
||||
float prev_mouse[2],
|
||||
float mouse[2],
|
||||
int eraser,
|
||||
float pressure,
|
||||
float distance,
|
||||
float size) = 0;
|
||||
|
||||
virtual void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) = 0;
|
||||
virtual void paint_stroke_done(void *stroke_handle) = 0;
|
||||
virtual void paint_gradient_fill(const bContext *C,
|
||||
const Scene *scene,
|
||||
Brush *brush,
|
||||
struct PaintStroke *stroke,
|
||||
void *stroke_handle,
|
||||
float mouse_start[2],
|
||||
float mouse_end[2]) = 0;
|
||||
virtual void paint_bucket_fill(const bContext *C,
|
||||
const Scene *scene,
|
||||
Brush *brush,
|
||||
struct PaintStroke *stroke,
|
||||
void *stroke_handle,
|
||||
float mouse_start[2],
|
||||
float mouse_end[2]) = 0;
|
||||
};
|
||||
|
||||
class ImagePaintMode : public AbstractPaintMode {
|
||||
public:
|
||||
void *paint_new_stroke(bContext *C,
|
||||
wmOperator *op,
|
||||
Object *UNUSED(ob),
|
||||
const float UNUSED(mouse[2]),
|
||||
int mode) override
|
||||
{
|
||||
return paint_2d_new_stroke(C, op, mode);
|
||||
}
|
||||
|
||||
void paint_stroke(bContext *UNUSED(C),
|
||||
void *stroke_handle,
|
||||
float prev_mouse[2],
|
||||
float mouse[2],
|
||||
int eraser,
|
||||
float pressure,
|
||||
float distance,
|
||||
float size) override
|
||||
{
|
||||
paint_2d_stroke(stroke_handle, prev_mouse, mouse, eraser, pressure, distance, size);
|
||||
}
|
||||
|
||||
void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) override
|
||||
{
|
||||
paint_2d_redraw(C, stroke_handle, final);
|
||||
}
|
||||
|
||||
void paint_stroke_done(void *stroke_handle) override
|
||||
{
|
||||
paint_2d_stroke_done(stroke_handle);
|
||||
}
|
||||
|
||||
void paint_gradient_fill(const bContext *C,
|
||||
const Scene *UNUSED(scene),
|
||||
Brush *brush,
|
||||
struct PaintStroke *UNUSED(stroke),
|
||||
void *stroke_handle,
|
||||
float mouse_start[2],
|
||||
float mouse_end[2]) override
|
||||
{
|
||||
paint_2d_gradient_fill(C, brush, mouse_start, mouse_end, stroke_handle);
|
||||
}
|
||||
|
||||
void paint_bucket_fill(const bContext *C,
|
||||
const Scene *scene,
|
||||
Brush *brush,
|
||||
struct PaintStroke *stroke,
|
||||
void *stroke_handle,
|
||||
float mouse_start[2],
|
||||
float mouse_end[2]) override
|
||||
{
|
||||
float color[3];
|
||||
if (paint_stroke_inverted(stroke)) {
|
||||
srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush));
|
||||
}
|
||||
else {
|
||||
srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush));
|
||||
}
|
||||
paint_2d_bucket_fill(C, color, brush, mouse_start, mouse_end, stroke_handle);
|
||||
}
|
||||
};
|
||||
|
||||
class ProjectionPaintMode : public AbstractPaintMode {
|
||||
public:
|
||||
void *paint_new_stroke(
|
||||
bContext *C, wmOperator *UNUSED(op), Object *ob, const float mouse[2], int mode) override
|
||||
{
|
||||
return paint_proj_new_stroke(C, ob, mouse, mode);
|
||||
}
|
||||
|
||||
void paint_stroke(bContext *C,
|
||||
void *stroke_handle,
|
||||
float prev_mouse[2],
|
||||
float mouse[2],
|
||||
int eraser,
|
||||
float pressure,
|
||||
float distance,
|
||||
float size) override
|
||||
{
|
||||
paint_proj_stroke(C, stroke_handle, prev_mouse, mouse, eraser, pressure, distance, size);
|
||||
};
|
||||
|
||||
void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) override
|
||||
{
|
||||
paint_proj_redraw(C, stroke_handle, final);
|
||||
}
|
||||
|
||||
void paint_stroke_done(void *stroke_handle) override
|
||||
{
|
||||
paint_proj_stroke_done(stroke_handle);
|
||||
}
|
||||
|
||||
void paint_gradient_fill(const bContext *C,
|
||||
const Scene *scene,
|
||||
Brush *brush,
|
||||
struct PaintStroke *stroke,
|
||||
void *stroke_handle,
|
||||
float mouse_start[2],
|
||||
float mouse_end[2]) override
|
||||
{
|
||||
paint_fill(C, scene, brush, stroke, stroke_handle, mouse_start, mouse_end);
|
||||
}
|
||||
|
||||
void paint_bucket_fill(const bContext *C,
|
||||
const Scene *scene,
|
||||
Brush *brush,
|
||||
struct PaintStroke *stroke,
|
||||
void *stroke_handle,
|
||||
float mouse_start[2],
|
||||
float mouse_end[2]) override
|
||||
{
|
||||
paint_fill(C, scene, brush, stroke, stroke_handle, mouse_start, mouse_end);
|
||||
}
|
||||
|
||||
private:
|
||||
void paint_fill(const bContext *C,
|
||||
const Scene *scene,
|
||||
Brush *brush,
|
||||
struct PaintStroke *stroke,
|
||||
void *stroke_handle,
|
||||
float mouse_start[2],
|
||||
float mouse_end[2])
|
||||
{
|
||||
paint_proj_stroke(C,
|
||||
stroke_handle,
|
||||
mouse_start,
|
||||
mouse_end,
|
||||
paint_stroke_flipped(stroke),
|
||||
1.0,
|
||||
0.0,
|
||||
BKE_brush_size_get(scene, brush));
|
||||
/* two redraws, one for GPU update, one for notification */
|
||||
paint_proj_redraw(C, stroke_handle, false);
|
||||
paint_proj_redraw(C, stroke_handle, true);
|
||||
}
|
||||
};
|
||||
|
||||
struct PaintOperation {
|
||||
AbstractPaintMode *mode = nullptr;
|
||||
|
||||
void *stroke_handle = nullptr;
|
||||
|
||||
float prevmouse[2] = {0.0f, 0.0f};
|
||||
float startmouse[2] = {0.0f, 0.0f};
|
||||
double starttime = 0.0;
|
||||
|
||||
wmPaintCursor *cursor = nullptr;
|
||||
ViewContext vc = {nullptr};
|
||||
|
||||
PaintOperation() = default;
|
||||
~PaintOperation()
|
||||
{
|
||||
MEM_delete(mode);
|
||||
mode = nullptr;
|
||||
|
||||
if (cursor) {
|
||||
WM_paint_cursor_end(cursor);
|
||||
cursor = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata)
|
||||
{
|
||||
PaintOperation *pop = (PaintOperation *)customdata;
|
||||
|
||||
if (pop) {
|
||||
GPU_line_smooth(true);
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
|
||||
|
||||
ARegion *region = pop->vc.region;
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
||||
|
||||
GPU_line_width(4.0);
|
||||
immUniformColor4ub(0, 0, 0, 255);
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 2);
|
||||
immVertex2i(pos, x, y);
|
||||
immVertex2i(
|
||||
pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin);
|
||||
immEnd();
|
||||
|
||||
GPU_line_width(2.0);
|
||||
immUniformColor4ub(255, 255, 255, 255);
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 2);
|
||||
immVertex2i(pos, x, y);
|
||||
immVertex2i(
|
||||
pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin);
|
||||
immEnd();
|
||||
|
||||
immUnbindProgram();
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
GPU_line_smooth(false);
|
||||
}
|
||||
}
|
||||
|
||||
static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2])
|
||||
{
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ToolSettings *settings = scene->toolsettings;
|
||||
PaintOperation *pop = MEM_new<PaintOperation>("PaintOperation"); /* caller frees */
|
||||
Brush *brush = BKE_paint_brush(&settings->imapaint.paint);
|
||||
int mode = RNA_enum_get(op->ptr, "mode");
|
||||
ED_view3d_viewcontext_init(C, &pop->vc, depsgraph);
|
||||
|
||||
copy_v2_v2(pop->prevmouse, mouse);
|
||||
copy_v2_v2(pop->startmouse, mouse);
|
||||
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *ob = OBACT(view_layer);
|
||||
|
||||
/* initialize from context */
|
||||
if (CTX_wm_region_view3d(C)) {
|
||||
bool uvs, mat, tex, stencil;
|
||||
if (!ED_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, &stencil)) {
|
||||
ED_paint_data_warning(op->reports, uvs, mat, tex, stencil);
|
||||
MEM_delete(pop);
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
pop->mode = MEM_new<ProjectionPaintMode>("ProjectionPaintMode");
|
||||
}
|
||||
else {
|
||||
pop->mode = MEM_new<ImagePaintMode>("ImagePaintMode");
|
||||
}
|
||||
|
||||
pop->stroke_handle = pop->mode->paint_new_stroke(C, op, ob, mouse, mode);
|
||||
if (!pop->stroke_handle) {
|
||||
MEM_delete(pop);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) {
|
||||
pop->cursor = WM_paint_cursor_activate(
|
||||
SPACE_TYPE_ANY, RGN_TYPE_ANY, image_paint_poll, gradient_draw_line, pop);
|
||||
}
|
||||
|
||||
settings->imapaint.flag |= IMAGEPAINT_DRAWING;
|
||||
ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D);
|
||||
|
||||
return pop;
|
||||
}
|
||||
|
||||
static void paint_stroke_update_step(bContext *C,
|
||||
wmOperator *UNUSED(op),
|
||||
struct PaintStroke *stroke,
|
||||
PointerRNA *itemptr)
|
||||
{
|
||||
PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke));
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ToolSettings *toolsettings = CTX_data_tool_settings(C);
|
||||
UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings;
|
||||
Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint);
|
||||
|
||||
float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f;
|
||||
|
||||
/* initial brush values. Maybe it should be considered moving these to stroke system */
|
||||
float startalpha = BKE_brush_alpha_get(scene, brush);
|
||||
|
||||
float mouse[2];
|
||||
float pressure;
|
||||
float size;
|
||||
float distance = paint_stroke_distance_get(stroke);
|
||||
int eraser;
|
||||
|
||||
RNA_float_get_array(itemptr, "mouse", mouse);
|
||||
pressure = RNA_float_get(itemptr, "pressure");
|
||||
eraser = RNA_boolean_get(itemptr, "pen_flip");
|
||||
size = RNA_float_get(itemptr, "size");
|
||||
|
||||
/* stroking with fill tool only acts on stroke end */
|
||||
if (brush->imagepaint_tool == PAINT_TOOL_FILL) {
|
||||
copy_v2_v2(pop->prevmouse, mouse);
|
||||
return;
|
||||
}
|
||||
|
||||
if (BKE_brush_use_alpha_pressure(brush)) {
|
||||
BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac));
|
||||
}
|
||||
else {
|
||||
BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac));
|
||||
}
|
||||
|
||||
if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) {
|
||||
UndoStack *ustack = CTX_wm_manager(C)->undo_stack;
|
||||
ED_image_undo_restore(ustack->step_init);
|
||||
}
|
||||
|
||||
pop->mode->paint_stroke(
|
||||
C, pop->stroke_handle, pop->prevmouse, mouse, eraser, pressure, distance, size);
|
||||
|
||||
copy_v2_v2(pop->prevmouse, mouse);
|
||||
|
||||
/* restore brush values */
|
||||
BKE_brush_alpha_set(scene, brush, startalpha);
|
||||
}
|
||||
|
||||
static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final)
|
||||
{
|
||||
PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke));
|
||||
pop->mode->paint_stroke_redraw(C, pop->stroke_handle, final);
|
||||
}
|
||||
|
||||
static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ToolSettings *toolsettings = scene->toolsettings;
|
||||
PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke));
|
||||
Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint);
|
||||
|
||||
toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING;
|
||||
|
||||
if (brush->imagepaint_tool == PAINT_TOOL_FILL) {
|
||||
if (brush->flag & BRUSH_USE_GRADIENT) {
|
||||
pop->mode->paint_gradient_fill(
|
||||
C, scene, brush, stroke, pop->stroke_handle, pop->startmouse, pop->prevmouse);
|
||||
}
|
||||
else {
|
||||
pop->mode->paint_bucket_fill(
|
||||
C, scene, brush, stroke, pop->stroke_handle, pop->startmouse, pop->prevmouse);
|
||||
}
|
||||
}
|
||||
pop->mode->paint_stroke_done(pop->stroke_handle);
|
||||
pop->stroke_handle = nullptr;
|
||||
|
||||
ED_image_undo_push_end();
|
||||
|
||||
/* duplicate warning, see texpaint_init */
|
||||
#if 0
|
||||
if (pop->s.warnmultifile) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_WARNING,
|
||||
"Image requires 4 color channels to paint: %s",
|
||||
pop->s.warnmultifile);
|
||||
}
|
||||
if (pop->s.warnpackedfile) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_WARNING,
|
||||
"Packed MultiLayer files cannot be painted: %s",
|
||||
pop->s.warnpackedfile);
|
||||
}
|
||||
#endif
|
||||
MEM_delete(pop);
|
||||
}
|
||||
|
||||
static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
|
||||
{
|
||||
PaintOperation *pop;
|
||||
|
||||
/* TODO: Should avoid putting this here. Instead, last position should be requested
|
||||
* from stroke system. */
|
||||
|
||||
if (!(pop = texture_paint_init(C, op, mouse))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
paint_stroke_set_mode_data(static_cast<PaintStroke *>(op->customdata), pop);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
int retval;
|
||||
|
||||
op->customdata = paint_stroke_new(C,
|
||||
op,
|
||||
nullptr,
|
||||
paint_stroke_test_start,
|
||||
paint_stroke_update_step,
|
||||
paint_stroke_redraw,
|
||||
paint_stroke_done,
|
||||
event->type);
|
||||
|
||||
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
|
||||
paint_stroke_free(C, op, static_cast<PaintStroke *>(op->customdata));
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
/* add modal handler */
|
||||
WM_event_add_modal_handler(C, op);
|
||||
|
||||
OPERATOR_RETVAL_CHECK(retval);
|
||||
BLI_assert(retval == OPERATOR_RUNNING_MODAL);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static int paint_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
PropertyRNA *strokeprop;
|
||||
PointerRNA firstpoint;
|
||||
float mouse[2];
|
||||
|
||||
strokeprop = RNA_struct_find_property(op->ptr, "stroke");
|
||||
|
||||
if (!RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
RNA_float_get_array(&firstpoint, "mouse", mouse);
|
||||
|
||||
op->customdata = paint_stroke_new(C,
|
||||
op,
|
||||
nullptr,
|
||||
paint_stroke_test_start,
|
||||
paint_stroke_update_step,
|
||||
paint_stroke_redraw,
|
||||
paint_stroke_done,
|
||||
0);
|
||||
/* frees op->customdata */
|
||||
return paint_stroke_exec(C, op, static_cast<PaintStroke *>(op->customdata));
|
||||
}
|
||||
|
||||
static int paint_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
return paint_stroke_modal(C, op, event, static_cast<PaintStroke *>(op->customdata));
|
||||
}
|
||||
|
||||
static void paint_cancel(bContext *C, wmOperator *op)
|
||||
{
|
||||
paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
|
||||
}
|
||||
} // namespace blender::ed::sculpt_paint::image::ops::paint
|
||||
|
||||
extern "C" {
|
||||
void PAINT_OT_image_paint(wmOperatorType *ot)
|
||||
{
|
||||
using namespace blender::ed::sculpt_paint::image::ops::paint;
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Image Paint";
|
||||
ot->idname = "PAINT_OT_image_paint";
|
||||
ot->description = "Paint a stroke into the image";
|
||||
|
||||
/* api callbacks */
|
||||
ot->invoke = paint_invoke;
|
||||
ot->modal = paint_modal;
|
||||
ot->exec = paint_exec;
|
||||
ot->poll = image_paint_poll;
|
||||
ot->cancel = paint_cancel;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_BLOCKING;
|
||||
|
||||
paint_stroke_operator_properties(ot);
|
||||
}
|
||||
}
|
|
@ -269,6 +269,7 @@ void paint_brush_color_get(struct Scene *scene,
|
|||
bool paint_use_opacity_masking(struct Brush *brush);
|
||||
void paint_brush_init_tex(struct Brush *brush);
|
||||
void paint_brush_exit_tex(struct Brush *brush);
|
||||
bool image_paint_poll(struct bContext *C);
|
||||
|
||||
void PAINT_OT_grab_clone(struct wmOperatorType *ot);
|
||||
void PAINT_OT_sample_color(struct wmOperatorType *ot);
|
||||
|
|
Loading…
Reference in New Issue