Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py10
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py1
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py12
-rw-r--r--release/scripts/startup/bl_ui/space_topbar.py29
-rw-r--r--source/blender/blenkernel/intern/colortools.c11
-rw-r--r--source/blender/blenkernel/intern/scene.c12
-rw-r--r--source/blender/blenloader/intern/readfile.c5
-rw-r--r--source/blender/blenloader/intern/versioning_280.c15
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c11
-rw-r--r--source/blender/blenloader/intern/writefile.c4
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c83
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_utils.c68
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c3
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h3
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c57
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h27
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c1083
-rw-r--r--source/blender/makesdna/DNA_color_types.h1
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h11
-rw-r--r--source/blender/makesdna/DNA_scene_types.h3
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c17
22 files changed, 1174 insertions, 294 deletions
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 72b3e166cc1..8c386af57be 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -5709,6 +5709,15 @@ def km_3d_view_tool_paint_gpencil_arc(params):
]},
)
+def km_3d_view_tool_paint_gpencil_curve(params):
+ return (
+ "3D View Tool: Paint Gpencil, Curve",
+ {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+ {"items": [
+ ("gpencil.primitive", {"type": params.tool_tweak, "value": 'ANY'},
+ {"properties": [("type", 'CURVE'), ("wait_for_input", False)]}),
+ ]},
+ )
def km_3d_view_tool_edit_gpencil_select(params):
return (
@@ -6019,6 +6028,7 @@ def generate_keymaps(params=None):
km_3d_view_tool_paint_gpencil_box(params),
km_3d_view_tool_paint_gpencil_circle(params),
km_3d_view_tool_paint_gpencil_arc(params),
+ km_3d_view_tool_paint_gpencil_curve(params),
km_3d_view_tool_edit_gpencil_select(params),
km_3d_view_tool_edit_gpencil_select_box(params),
km_3d_view_tool_edit_gpencil_select_circle(params),
diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
index 09e81115d6a..32c0aa6ab0b 100644
--- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
+++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
@@ -645,6 +645,7 @@ class GPENCIL_MT_gpencil_draw_specials(Menu):
layout.operator("gpencil.primitive", text="Rectangle", icon='UV_FACESEL').type = 'BOX'
layout.operator("gpencil.primitive", text="Circle", icon='ANTIALIASED').type = 'CIRCLE'
layout.operator("gpencil.primitive", text="Arc", icon='SPHERECURVE').type = 'ARC'
+ layout.operator("gpencil.primitive", text="Curve", icon='CURVE_BEZCURVE').type = 'CURVE'
class GPENCIL_MT_gpencil_draw_delete(Menu):
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 4da5455298f..aa6adee0d19 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -1076,6 +1076,17 @@ class _defs_gpencil_paint:
widget=None,
keymap=(),
)
+
+
+ @ToolDef.from_fn
+ def curve():
+ return dict(
+ text="Curve",
+ icon="ops.gpencil.primitive_curve",
+ cursor='CROSSHAIR',
+ widget=None,
+ keymap=(),
+ )
class _defs_gpencil_edit:
@ToolDef.from_fn
@@ -1583,6 +1594,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
_defs_gpencil_paint.box,
_defs_gpencil_paint.circle,
_defs_gpencil_paint.arc,
+ _defs_gpencil_paint.curve,
],
'EDIT_GPENCIL': [
*_tools_gpencil_select,
diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py
index f73d21dcfb8..a94ebea4561 100644
--- a/release/scripts/startup/bl_ui/space_topbar.py
+++ b/release/scripts/startup/bl_ui/space_topbar.py
@@ -301,7 +301,7 @@ class _draw_left_context_mode:
return
is_paint = True
- if (tool.name in {"Line", "Box", "Circle", "Arc"}):
+ if (tool.name in {"Line", "Box", "Circle", "Arc", "Curve"}):
is_paint = False
elif (not tool.has_datablock):
return
@@ -375,6 +375,17 @@ class _draw_left_context_mode:
draw_color_selector()
+ if tool.name in {"Arc", "Curve", "Line", "Box", "Circle"}:
+ settings = context.tool_settings.gpencil_sculpt
+ row = layout.row(align=True)
+ row.prop(settings, "use_thickness_curve", text="", icon='CURVE_DATA')
+ sub = row.row(align=True)
+ sub.active = settings.use_thickness_curve
+ sub.popover(
+ panel="TOPBAR_PT_gpencil_primitive",
+ text="Thickness Profile"
+ )
+
@staticmethod
def SCULPT_GPENCIL(context, layout, tool):
if (tool is None) or (not tool.has_datablock):
@@ -1039,6 +1050,21 @@ class TOPBAR_PT_active_tool(Panel):
ToolSelectPanelHelper.draw_active_tool_header(context, layout, show_tool_name=True)
+# Grease Pencil Object - Primitive curve
+class TOPBAR_PT_gpencil_primitive(Panel):
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'HEADER'
+ bl_label = "Primitives"
+
+ @staticmethod
+ def draw(self, context):
+ settings = context.tool_settings.gpencil_sculpt
+
+ layout = self.layout
+ # Curve
+ layout.template_curve_mapping(settings, "thickness_primitive_curve", brush=True)
+
+
classes = (
TOPBAR_HT_upper_bar,
TOPBAR_HT_lower_bar,
@@ -1059,6 +1085,7 @@ classes = (
TOPBAR_MT_help,
TOPBAR_PT_active_tool,
TOPBAR_PT_gpencil_layers,
+ TOPBAR_PT_gpencil_primitive,
)
if __name__ == "__main__": # only for live edit.
diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c
index aad59e32187..bc34b24c8f2 100644
--- a/source/blender/blenkernel/intern/colortools.c
+++ b/source/blender/blenkernel/intern/colortools.c
@@ -283,6 +283,7 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope)
case CURVE_PRESET_ROUND: cuma->totpoint = 4; break;
case CURVE_PRESET_ROOT: cuma->totpoint = 4; break;
case CURVE_PRESET_GAUSS: cuma->totpoint = 7; break;
+ case CURVE_PRESET_BELL: cuma->totpoint = 3; break;
}
cuma->curve = MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), "curve points");
@@ -371,6 +372,16 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope)
cuma->curve[6].x = 1.0f;
cuma->curve[6].y = 0.025f;
break;
+ case CURVE_PRESET_BELL:
+ cuma->curve[0].x = 0;
+ cuma->curve[0].y = 0.025f;
+
+ cuma->curve[1].x = 0.50f;
+ cuma->curve[1].y = 1.0f;
+
+ cuma->curve[2].x = 1.0f;
+ cuma->curve[2].y = 0.025f;
+ break;
}
/* mirror curve in x direction to have positive slope
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index a070bf97317..3894d83c169 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -188,6 +188,7 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag)
ts->gp_interpolate.custom_ipo = curvemapping_copy(ts->gp_interpolate.custom_ipo);
/* duplicate Grease Pencil multiframe fallof */
ts->gp_sculpt.cur_falloff = curvemapping_copy(ts->gp_sculpt.cur_falloff);
+ ts->gp_sculpt.cur_primitive = curvemapping_copy(ts->gp_sculpt.cur_primitive);
return ts;
}
@@ -226,6 +227,9 @@ void BKE_toolsettings_free(ToolSettings *toolsettings)
if (toolsettings->gp_sculpt.cur_falloff) {
curvemapping_free(toolsettings->gp_sculpt.cur_falloff);
}
+ if (toolsettings->gp_sculpt.cur_primitive) {
+ curvemapping_free(toolsettings->gp_sculpt.cur_primitive);
+ }
MEM_freeN(toolsettings);
}
@@ -699,6 +703,14 @@ void BKE_scene_init(Scene *sce)
CURVE_PRESET_GAUSS,
CURVEMAP_SLOPE_POSITIVE);
+ sce->toolsettings->gp_sculpt.cur_primitive = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ CurveMapping *gp_primitive_curve = sce->toolsettings->gp_sculpt.cur_primitive;
+ curvemapping_initialize(gp_primitive_curve);
+ curvemap_reset(gp_primitive_curve->cm,
+ &gp_primitive_curve->clipr,
+ CURVE_PRESET_BELL,
+ CURVEMAP_SLOPE_POSITIVE);
+
sce->physics_settings.gravity[0] = 0.0f;
sce->physics_settings.gravity[1] = 0.0f;
sce->physics_settings.gravity[2] = -9.81f;
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 6aff6f6506f..6dd9c80b5d9 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -6275,6 +6275,11 @@ static void direct_link_scene(FileData *fd, Scene *sce)
if (sce->toolsettings->gp_sculpt.cur_falloff) {
direct_link_curvemapping(fd, sce->toolsettings->gp_sculpt.cur_falloff);
}
+ /* relink grease pencil primitive curve */
+ sce->toolsettings->gp_sculpt.cur_primitive = newdataadr(fd, sce->toolsettings->gp_sculpt.cur_primitive);
+ if (sce->toolsettings->gp_sculpt.cur_primitive) {
+ direct_link_curvemapping(fd, sce->toolsettings->gp_sculpt.cur_primitive);
+ }
}
if (sce->ed) {
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 56ca663e3f0..5a1002702b3 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -2516,6 +2516,21 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ /* Grease pencil primitive curve */
+ if (!DNA_struct_elem_find(fd->filesdna, "GP_Sculpt_Settings", "CurveMapping", "cur_primitive")) {
+ for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
+ GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt;
+ if ((gset) && (gset->cur_primitive == NULL)) {
+ gset->cur_primitive = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ curvemapping_initialize(gset->cur_primitive);
+ curvemap_reset(gset->cur_primitive->cm,
+ &gset->cur_primitive->clipr,
+ CURVE_PRESET_BELL,
+ CURVEMAP_SLOPE_POSITIVE);
+ }
+ }
+ }
}
{
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index 4dd046d9a73..f3ae1426fa8 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -187,7 +187,7 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
}
}
- /* Be sure curfalloff is initializated */
+ /* Be sure curfalloff and primitive are initializated */
for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
ToolSettings *ts = scene->toolsettings;
if (ts->gp_sculpt.cur_falloff == NULL) {
@@ -199,6 +199,15 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
CURVE_PRESET_GAUSS,
CURVEMAP_SLOPE_POSITIVE);
}
+ if (ts->gp_sculpt.cur_primitive == NULL) {
+ ts->gp_sculpt.cur_primitive = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ CurveMapping *gp_primitive_curve = ts->gp_sculpt.cur_primitive;
+ curvemapping_initialize(gp_primitive_curve);
+ curvemap_reset(gp_primitive_curve->cm,
+ &gp_primitive_curve->clipr,
+ CURVE_PRESET_BELL,
+ CURVEMAP_SLOPE_POSITIVE);
+ }
}
}
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 0854af0f818..c1d09a836cc 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -2555,6 +2555,10 @@ static void write_scene(WriteData *wd, Scene *sce)
if (tos->gp_sculpt.cur_falloff) {
write_curvemapping(wd, tos->gp_sculpt.cur_falloff);
}
+ /* write grease-pencil primitive curve to file */
+ if (tos->gp_sculpt.cur_primitive) {
+ write_curvemapping(wd, tos->gp_sculpt.cur_primitive);
+ }
write_paint(wd, &tos->imapaint.paint);
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c b/source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c
index c75445f3ae5..a098fb74bc8 100644
--- a/source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c
@@ -252,6 +252,9 @@ GPUBatch *DRW_gpencil_get_buffer_stroke_geom(bGPdata *gpd, short thickness)
tGPspoint *points = gpd->runtime.sbuffer;
int totpoints = gpd->runtime.sbuffer_size;
+ /* if cyclic needs more vertex */
+ int cyclic_add = (gpd->runtime.sbuffer_sflag & GP_STROKE_CYCLIC) ? 1 : 0;
+ int totvertex = totpoints + cyclic_add + 2;
static GPUVertFormat format = { 0 };
static uint pos_id, color_id, thickness_id, uvdata_id;
@@ -263,11 +266,11 @@ GPUBatch *DRW_gpencil_get_buffer_stroke_geom(bGPdata *gpd, short thickness)
}
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
- GPU_vertbuf_data_alloc(vbo, totpoints + 2);
+ GPU_vertbuf_data_alloc(vbo, totvertex);
/* draw stroke curve */
const tGPspoint *tpt = points;
- bGPDspoint pt, pt2;
+ bGPDspoint pt, pt2, pt3;
int idx = 0;
/* get origin to reproject point */
@@ -281,19 +284,22 @@ GPUBatch *DRW_gpencil_get_buffer_stroke_geom(bGPdata *gpd, short thickness)
/* first point for adjacency (not drawn) */
if (i == 0) {
- if (totpoints > 1) {
- ED_gpencil_tpoint_to_point(ar, origin, &points[1], &pt2);
+ if (gpd->runtime.sbuffer_sflag & GP_STROKE_CYCLIC && totpoints > 2) {
+ ED_gpencil_tpoint_to_point(ar, origin, &points[totpoints - 1], &pt2);
gpencil_set_stroke_point(
- vbo, &pt2, idx,
- pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ vbo, &pt2, idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ idx++;
}
else {
+ ED_gpencil_tpoint_to_point(ar, origin, &points[1], &pt2);
gpencil_set_stroke_point(
- vbo, &pt, idx,
- pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ vbo, &pt2, idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ idx++;
}
- idx++;
}
+
/* set point */
gpencil_set_stroke_point(
vbo, &pt, idx,
@@ -302,16 +308,27 @@ GPUBatch *DRW_gpencil_get_buffer_stroke_geom(bGPdata *gpd, short thickness)
}
/* last adjacency point (not drawn) */
- if (totpoints > 2) {
- ED_gpencil_tpoint_to_point(ar, origin, &points[totpoints - 2], &pt2);
+ if (gpd->runtime.sbuffer_sflag & GP_STROKE_CYCLIC && totpoints > 2) {
+ /* draw line to first point to complete the cycle */
+ ED_gpencil_tpoint_to_point(ar, origin, &points[0], &pt2);
gpencil_set_stroke_point(
- vbo, &pt2, idx,
- pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ vbo, &pt2, idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ idx++;
+ /* now add adjacency point (not drawn) */
+ ED_gpencil_tpoint_to_point(ar, origin, &points[1], &pt3);
+ gpencil_set_stroke_point(
+ vbo, &pt3, idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ idx++;
}
+ /* last adjacency point (not drawn) */
else {
+ ED_gpencil_tpoint_to_point(ar, origin, &points[totpoints - 2], &pt2);
gpencil_set_stroke_point(
- vbo, &pt, idx,
- pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ vbo, &pt2, idx,
+ pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor);
+ idx++;
}
return GPU_batch_create_ex(GPU_PRIM_LINE_STRIP_ADJ, vbo, NULL, GPU_BATCH_OWNS_VBO);
@@ -367,6 +384,42 @@ GPUBatch *DRW_gpencil_get_buffer_point_geom(bGPdata *gpd, short thickness)
return GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO);
}
+/* create batch geometry data for current buffer control point shader */
+GPUBatch *DRW_gpencil_get_buffer_ctrlpoint_geom(bGPdata *gpd)
+{
+ bGPDcontrolpoint *cps = gpd->runtime.cp_points;
+ int totpoints = gpd->runtime.tot_cp_points;
+
+ static GPUVertFormat format = { 0 };
+ static uint pos_id, color_id, size_id;
+ if (format.attr_len == 0) {
+ pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ size_id = GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, totpoints);
+
+ int idx = 0;
+ for (int i = 0; i < gpd->runtime.tot_cp_points; i++) {
+ bGPDcontrolpoint *cp = &cps[i];
+ float color[4];
+ copy_v3_v3(color, cp->color);
+ color[3] = 0.5f;
+ GPU_vertbuf_attr_set(vbo, color_id, idx, color);
+
+ /* scale size */
+ float size = cp->size * 0.8f;
+ GPU_vertbuf_attr_set(vbo, size_id, idx, &size);
+
+ GPU_vertbuf_attr_set(vbo, pos_id, idx, &cp->x);
+ idx++;
+ }
+
+ return GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO);
+}
+
/* create batch geometry data for current buffer fill shader */
GPUBatch *DRW_gpencil_get_buffer_fill_geom(bGPdata *gpd)
{
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c
index 87772a72015..3b1c51cb8eb 100644
--- a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c
@@ -434,7 +434,7 @@ DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(
DRW_shgroup_uniform_int(grp, "xraymode", (const int *) &gpd->xray_mode, 1);
}
else {
- /* for drawing always on front */
+ /* for drawing always on predefined z-depth */
DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1);
}
@@ -527,7 +527,7 @@ static DRWShadingGroup *DRW_gpencil_shgroup_point_create(
DRW_shgroup_uniform_int(grp, "xraymode", (const int *)&gpd->xray_mode, 1);
}
else {
- /* for drawing always on front */
+ /* for drawing always on on predefined z-depth */
DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1);
}
@@ -1161,6 +1161,9 @@ void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, T
{
GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ View3D *v3d = draw_ctx->v3d;
+ const bool overlay = v3d != NULL ? (bool)((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) : true;
Brush *brush = BKE_paint_brush(&ts->gp_paint->paint);
bGPdata *gpd_eval = ob->data;
/* need the original to avoid cow overhead while drawing */
@@ -1184,7 +1187,7 @@ void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, T
/* Check if may need to draw the active stroke cache, only if this layer is the active layer
* that is being edited. (Stroke buffer is currently stored in gp-data)
*/
- if (ED_gpencil_session_active() && (gpd->runtime.sbuffer_size > 0)) {
+ if (gpd->runtime.sbuffer_size > 0) {
if ((gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) {
/* It should also be noted that sbuffer contains temporary point types
* i.e. tGPspoints NOT bGPDspoints
@@ -1194,11 +1197,11 @@ void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, T
if (gpd->runtime.sbuffer_size > 1) {
if ((gp_style) && (gp_style->mode == GP_STYLE_MODE_LINE)) {
stl->g_data->shgrps_drawing_stroke = DRW_gpencil_shgroup_stroke_create(
- e_data, vedata, psl->drawing_pass, e_data->gpencil_stroke_sh, NULL, gpd, gp_style, -1, false);
+ e_data, vedata, psl->drawing_pass, e_data->gpencil_stroke_sh, NULL, gpd, gp_style, -1, false);
}
else {
stl->g_data->shgrps_drawing_stroke = DRW_gpencil_shgroup_point_create(
- e_data, vedata, psl->drawing_pass, e_data->gpencil_point_sh, NULL, gpd, gp_style, -1, false);
+ e_data, vedata, psl->drawing_pass, e_data->gpencil_point_sh, NULL, gpd, gp_style, -1, false);
}
/* clean previous version of the batch */
@@ -1211,32 +1214,32 @@ void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, T
/* use unit matrix because the buffer is in screen space and does not need conversion */
if (gpd->runtime.mode == GP_STYLE_MODE_LINE) {
e_data->batch_buffer_stroke = DRW_gpencil_get_buffer_stroke_geom(
- gpd, lthick);
+ gpd, lthick);
}
else {
e_data->batch_buffer_stroke = DRW_gpencil_get_buffer_point_geom(
- gpd, lthick);
+ gpd, lthick);
}
if (gp_style->flag & GP_STYLE_STROKE_SHOW) {
DRW_shgroup_call_add(
- stl->g_data->shgrps_drawing_stroke,
- e_data->batch_buffer_stroke,
- stl->storage->unit_matrix);
+ stl->g_data->shgrps_drawing_stroke,
+ e_data->batch_buffer_stroke,
+ stl->storage->unit_matrix);
}
if ((gpd->runtime.sbuffer_size >= 3) &&
- (gpd->runtime.sfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) &&
- ((gpd->runtime.sbuffer_sflag & GP_STROKE_NOFILL) == 0) &&
- ((brush->gpencil_settings->flag & GP_BRUSH_DISSABLE_LASSO) == 0) &&
- (gp_style->flag & GP_STYLE_FILL_SHOW))
+ (gpd->runtime.sfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) &&
+ ((gpd->runtime.sbuffer_sflag & GP_STROKE_NOFILL) == 0) &&
+ ((brush->gpencil_settings->flag & GP_BRUSH_DISSABLE_LASSO) == 0) &&
+ (gp_style->flag & GP_STYLE_FILL_SHOW))
{
/* if not solid, fill is simulated with solid color */
if (gpd->runtime.bfill_style > 0) {
gpd->runtime.sfill[3] = 0.5f;
}
stl->g_data->shgrps_drawing_fill = DRW_shgroup_create(
- e_data->gpencil_drawing_fill_sh, psl->drawing_pass);
+ e_data->gpencil_drawing_fill_sh, psl->drawing_pass);
/* clean previous version of the batch */
if (stl->storage->buffer_fill) {
@@ -1247,15 +1250,44 @@ void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, T
e_data->batch_buffer_fill = DRW_gpencil_get_buffer_fill_geom(gpd);
DRW_shgroup_call_add(
- stl->g_data->shgrps_drawing_fill,
- e_data->batch_buffer_fill,
- stl->storage->unit_matrix);
+ stl->g_data->shgrps_drawing_fill,
+ e_data->batch_buffer_fill,
+ stl->storage->unit_matrix);
stl->storage->buffer_fill = true;
}
stl->storage->buffer_stroke = true;
}
}
}
+
+ /* control points */
+ if ((overlay) && (gpd->runtime.tot_cp_points > 0) &&
+ ((gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) &&
+ ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) &&
+ ((v3d->gizmo_flag & V3D_GIZMO_HIDE_TOOL) == 0))
+ {
+
+ DRWShadingGroup *shgrp = DRW_shgroup_create(
+ e_data->gpencil_edit_point_sh, psl->drawing_pass);
+ const float *viewport_size = DRW_viewport_size_get();
+ DRW_shgroup_uniform_vec2(shgrp, "Viewport", viewport_size, 1);
+
+ /* clean previous version of the batch */
+ if (stl->storage->buffer_ctrlpoint) {
+ GPU_BATCH_DISCARD_SAFE(e_data->batch_buffer_ctrlpoint);
+ MEM_SAFE_FREE(e_data->batch_buffer_ctrlpoint);
+ stl->storage->buffer_ctrlpoint = false;
+ }
+
+ e_data->batch_buffer_ctrlpoint = DRW_gpencil_get_buffer_ctrlpoint_geom(gpd);
+
+ DRW_shgroup_call_add(
+ shgrp,
+ e_data->batch_buffer_ctrlpoint,
+ stl->storage->unit_matrix);
+
+ stl->storage->buffer_ctrlpoint = true;
+ }
}
/* create all missing batches */
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c
index 66d9bac1fac..dc62ca34c92 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.c
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.c
@@ -289,6 +289,9 @@ static void GPENCIL_engine_free(void)
GPU_BATCH_DISCARD_SAFE(e_data.batch_buffer_fill);
MEM_SAFE_FREE(e_data.batch_buffer_fill);
+ GPU_BATCH_DISCARD_SAFE(e_data.batch_buffer_ctrlpoint);
+ MEM_SAFE_FREE(e_data.batch_buffer_ctrlpoint);
+
GPU_BATCH_DISCARD_SAFE(e_data.batch_grid);
MEM_SAFE_FREE(e_data.batch_grid);
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h
index 3c0675c5e8c..6f2b40136ca 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.h
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.h
@@ -133,6 +133,7 @@ typedef struct GPENCIL_Storage {
bool reset_cache;
bool buffer_stroke;
bool buffer_fill;
+ bool buffer_ctrlpoint;
const float *pixsize;
float render_pixsize;
int tonemapping;
@@ -299,6 +300,7 @@ typedef struct GPENCIL_e_data {
/* for buffer only one batch is nedeed because the drawing is only of one stroke */
GPUBatch *batch_buffer_stroke;
GPUBatch *batch_buffer_fill;
+ GPUBatch *batch_buffer_ctrlpoint;
/* grid geometry */
GPUBatch *batch_grid;
@@ -386,6 +388,7 @@ void DRW_gpencil_get_edlin_geom(struct GpencilBatchCacheElem *be, struct bGPDstr
struct GPUBatch *DRW_gpencil_get_buffer_stroke_geom(struct bGPdata *gpd, short thickness);
struct GPUBatch *DRW_gpencil_get_buffer_fill_geom(struct bGPdata *gpd);
struct GPUBatch *DRW_gpencil_get_buffer_point_geom(struct bGPdata *gpd, short thickness);
+struct GPUBatch *DRW_gpencil_get_buffer_ctrlpoint_geom(struct bGPdata *gpd);
struct GPUBatch *DRW_gpencil_get_grid(Object *ob);
/* object cache functions */
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index b44c9105e10..c16ea84ec81 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -1430,63 +1430,6 @@ void ED_gp_draw_interpolation(const bContext *C, tGPDinterpolate *tgpi, const in
glDisable(GL_BLEND);
}
-/* draw interpolate strokes (used only while operator is running) */
-void ED_gp_draw_primitives(const bContext *C, tGPDprimitive *tgpi, const int type)
-{
- tGPDdraw tgpw;
- ARegion *ar = CTX_wm_region(C);
- RegionView3D *rv3d = ar->regiondata;
-
- /* if idle, do not draw */
- if (tgpi->flag == 0) {
- return;
- }
-
- Object *obact = CTX_data_active_object(C);
- Depsgraph *depsgraph = CTX_data_depsgraph(C);
-
- float color[4];
- UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, color);
- color[3] = 0.6f;
- int dflag = 0;
- /* if 3d stuff, enable flags */
- if (type == REGION_DRAW_POST_VIEW) {
- dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS);
- }
-
- tgpw.rv3d = rv3d;
- tgpw.depsgraph = depsgraph;
- tgpw.ob = obact;
- tgpw.gpd = tgpi->gpd;
- tgpw.offsx = 0;
- tgpw.offsy = 0;
- tgpw.winx = tgpi->ar->winx;
- tgpw.winy = tgpi->ar->winy;
- tgpw.dflag = dflag;
-
- /* turn on alpha-blending */
- GPU_blend(true);
- /* calculate parent position */
- ED_gpencil_parent_location(depsgraph, obact, tgpi->gpd, tgpi->gpl, tgpw.diff_mat);
- if (tgpi->gpf) {
- tgpw.gps = tgpi->gpf->strokes.first;
- if (tgpw.gps->totpoints > 0) {
- tgpw.gpl = tgpi->gpl;
- tgpw.gpf = tgpi->gpf;
- tgpw.t_gpf = tgpi->gpf;
-
- tgpw.lthick = tgpi->gpl->line_change;
- tgpw.opacity = 1.0;
- copy_v4_v4(tgpw.tintcolor, color);
- tgpw.onion = true;
- tgpw.custonion = true;
-
- gp_draw_strokes(&tgpw);
- }
- }
- GPU_blend(false);
-}
-
/* wrapper to draw strokes for filling operator */
void ED_gp_draw_fill(tGPDdraw *tgpw)
{
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index 2a92185cdd5..e2cbeda2733 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -138,6 +138,7 @@ typedef struct tGPDinterpolate {
/* Temporary primitive operation data */
typedef struct tGPDprimitive {
+ struct Main *bmain; /* main database pointer */
struct Depsgraph *depsgraph;
struct wmWindow *win; /* window where painting originated */
struct Scene *scene; /* current scene from context */
@@ -154,25 +155,34 @@ typedef struct tGPDprimitive {
struct bGPDlayer *gpl; /* layer */
struct bGPDframe *gpf; /* frame */
int type; /* type of primitive */
- short cyclic; /* cyclic option */
- short flip; /* flip option */
+ int orign_type; /* original type of primitive */
+ bool curve; /* type of primitive is a curve */
+ short flip; /* flip option */
+ tGPspoint *points; /* array of data-points for stroke */
+ int point_count; /* number of edges allocated */
+ int tot_stored_edges; /* stored number of polygon edges */
int tot_edges; /* number of polygon edges */
- int top[2]; /* first box corner */
- int bottom[2]; /* last box corner */
- int origin[2]; /* initial box corner */
+ float origin[2]; /* initial box corner */
+ float start[2]; /* first box corner */
+ float end[2]; /* last box corner */
+ float midpoint[2]; /* midpoint box corner */
+ float cp1[2]; /* first control point */
+ float cp2[2]; /* second control point */
+ int sel_cp; /* flag to determine control point is selected */
int flag; /* flag to determine operations in progress */
+ float mval[2]; /* recorded mouse-position */
+ float mvalo[2]; /* previous recorded mouse-position */
int lock_axis; /* lock to viewport axis */
+ struct RNG *rng;
NumInput num; /* numeric input */
- void *draw_handle_3d; /* handle for drawing strokes while operator is running 3d stuff */
} tGPDprimitive;
/* Modal Operator Drawing Callbacks ------------------------ */
void ED_gp_draw_interpolation(const struct bContext *C, struct tGPDinterpolate *tgpi, const int type);
-void ED_gp_draw_primitives(const struct bContext *C, struct tGPDprimitive *tgpi, const int type);
void ED_gp_draw_fill(struct tGPDdraw *tgpw);
/* ***************************************************** */
@@ -369,7 +379,8 @@ enum {
GP_STROKE_BOX = -1,
GP_STROKE_LINE = 1,
GP_STROKE_CIRCLE = 2,
- GP_STROKE_ARC = 3
+ GP_STROKE_ARC = 3,
+ GP_STROKE_CURVE = 4
};
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 68d0f6506d0..6d8f337bfc2 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -1066,7 +1066,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
}
last_valid = i;
}
- /* invalidate any point other point, to interpolate between
+ /* invalidate any other point, to interpolate between
* first and last contact in an imaginary line between them */
for (i = 0; i < gpd->runtime.sbuffer_size; i++) {
if ((i != first_valid) && (i != last_valid)) {
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index 029db23499e..0e4f9558842 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -41,9 +41,12 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BLI_math.h"
+#include "BLI_rand.h"
#include "BLT_translation.h"
+#include "PIL_time.h"
+
#include "DNA_brush_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
@@ -54,6 +57,7 @@
#include "DNA_view3d_types.h"
#include "BKE_brush.h"
+#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_global.h"
@@ -64,6 +68,7 @@
#include "BKE_report.h"
#include "UI_interface.h"
+#include "UI_resources.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -85,13 +90,162 @@
#define MIN_EDGES 2
#define MAX_EDGES 128
+#define MAX_CP 128
#define IDLE 0
#define IN_PROGRESS 1
+#define IN_CURVE_EDIT 2
+#define IN_MOVE 3
+
+#define SELECT_NONE 0
+#define SELECT_START 1
+#define SELECT_CP1 2
+#define SELECT_CP2 3
+#define SELECT_END 4
+
+#define BIG_SIZE_CTL 15
+#define MID_SIZE_CTL 10
+#define SMALL_SIZE_CTL 8
+
+#define MOVE_NONE 0
+#define MOVE_ENDS 1
+#define MOVE_CP 2
/* ************************************************ */
/* Core/Shared Utilities */
+/* clear the session buffers (call this before AND after a paint operation) */
+static void gp_session_validatebuffer(tGPDprimitive *p)
+{
+ bGPdata *gpd = p->gpd;
+
+ /* clear memory of buffer (or allocate it if starting a new session) */
+ if (gpd->runtime.sbuffer) {
+ memset(gpd->runtime.sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX);
+ }
+ else {
+ gpd->runtime.sbuffer = MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer");
+ }
+
+ /* reset indices */
+ gpd->runtime.sbuffer_size = 0;
+
+ /* reset flags */
+ gpd->runtime.sbuffer_sflag = 0;
+ gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE;
+
+ if (ELEM(p->type, GP_STROKE_BOX, GP_STROKE_CIRCLE))
+ gpd->runtime.sbuffer_sflag |= GP_STROKE_CYCLIC;
+}
+
+static void gp_init_colors(tGPDprimitive *p)
+{
+ bGPdata *gpd = p->gpd;
+ Brush *brush = p->brush;
+
+ Material *ma = NULL;
+ MaterialGPencilStyle *gp_style = NULL;
+
+ /* use brush material */
+ ma = BKE_gpencil_get_material_from_brush(brush);
+
+ /* if no brush defaults, get material and color info */
+ if ((ma == NULL) || (ma->gp_style == NULL)) {
+ BKE_gpencil_material_ensure(p->bmain, p->ob);
+
+ /* assign always the first material to the brush */
+ p->mat = give_current_material(p->ob, 1);
+ brush->gpencil_settings->material = p->mat;
+ }
+ else {
+ p->mat = ma;
+ }
+
+ /* check if the material is already on object material slots and add it if missing */
+ if (BKE_gpencil_get_material_index(p->ob, p->mat) == 0) {
+ BKE_object_material_slot_add(p->bmain, p->ob);
+ assign_material(p->bmain, p->ob, ma, p->ob->totcol, BKE_MAT_ASSIGN_USERPREF);
+ }
+
+ /* assign color information to temp data */
+ gp_style = p->mat->gp_style;
+ if (gp_style) {
+
+ /* set colors */
+ copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba);
+ copy_v4_v4(gpd->runtime.sfill, gp_style->fill_rgba);
+ /* add some alpha to make easy the filling without hide strokes */
+ if (gpd->runtime.sfill[3] > 0.8f) {
+ gpd->runtime.sfill[3] = 0.8f;
+ }
+
+ gpd->runtime.mode = (short)gp_style->mode;
+ gpd->runtime.bstroke_style = gp_style->stroke_style;
+ gpd->runtime.bfill_style = gp_style->fill_style;
+ }
+}
+
+/* Helper to square a primitive */
+static void gpencil_primitive_to_square(tGPDprimitive *tgpi, const float x, const float y)
+{
+ float w = fabsf(x);
+ float h = fabsf(y);
+ if ((x > 0 && y > 0) || (x < 0 && y < 0)) {
+ if (w > h)
+ tgpi->end[1] = tgpi->origin[1] + x;
+ else
+ tgpi->end[0] = tgpi->origin[0] + y;
+ }
+ else {
+ if (w > h)
+ tgpi->end[1] = tgpi->origin[1] - x;
+ else
+ tgpi->end[0] = tgpi->origin[0] - y;
+ }
+}
+
+/* Helper to rotate point around origin */
+static void gp_rotate_v2_v2v2fl(float v[2], const float p[2], const float origin[2], const float angle)
+{
+ float pt[2];
+ float r[2];
+ sub_v2_v2v2(pt, p, origin);
+ rotate_v2_v2fl(r, pt, angle);
+ add_v2_v2v2(v, r, origin);
+}
+
+/* Helper to rotate line around line centre */
+static void gp_primitive_rotate_line(float va[2], float vb[2], const float a[2], const float b[2], const float angle)
+{
+ float midpoint[2];
+ mid_v2_v2v2(midpoint, a, b);
+ gp_rotate_v2_v2v2fl(va, a, midpoint, angle);
+ gp_rotate_v2_v2v2fl(vb, b, midpoint, angle);
+}
+
+/* Helper to update cps */
+static void gp_primitive_update_cps(tGPDprimitive *tgpi)
+{
+ if (!tgpi->curve) {
+ mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
+ copy_v2_v2(tgpi->cp1, tgpi->midpoint);
+ copy_v2_v2(tgpi->cp2, tgpi->cp1);
+ }
+ else if (tgpi->type == GP_STROKE_CURVE) {
+ mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
+ copy_v2_v2(tgpi->cp1, tgpi->midpoint);
+ copy_v2_v2(tgpi->cp2, tgpi->cp1);
+ }
+ else if (tgpi->type == GP_STROKE_ARC) {
+ if (tgpi->flip) {
+ gp_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->start, tgpi->end, M_PI_2);
+ }
+ else {
+ gp_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->end, tgpi->start, M_PI_2);
+ }
+ }
+}
+
/* Poll callback for primitive operators */
static bool gpencil_primitive_add_poll(bContext *C)
{
@@ -129,6 +283,15 @@ static bool gpencil_primitive_add_poll(bContext *C)
return 1;
}
+/* Allocate memory to stroke, adds MAX_EDGES on every call */
+static void gpencil_primitive_allocate_memory(tGPDprimitive *tgpi) {
+ tgpi->point_count += (tgpi->type == GP_STROKE_BOX) ? (MAX_EDGES * 4 + 1) : (MAX_EDGES + 1);
+ bGPDstroke *gpsf = tgpi->gpf->strokes.first;
+ gpsf->points = MEM_reallocN(gpsf->points, sizeof(bGPDspoint) * tgpi->point_count);
+ if (gpsf->dvert != NULL)
+ gpsf->dvert = MEM_reallocN(gpsf->dvert, sizeof(MDeformVert) * tgpi->point_count);
+ tgpi->points = MEM_reallocN(tgpi->points, sizeof(tGPspoint) * tgpi->point_count);
+}
/* ****************** Primitive Interactive *********************** */
@@ -167,13 +330,11 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
/* enable recalculation flag by default */
gps->flag |= GP_STROKE_RECALC_CACHES;
+ gps->flag &= ~GP_STROKE_SELECT;
/* the polygon must be closed, so enabled cyclic */
- if (tgpi->type != GP_STROKE_LINE && tgpi->type != GP_STROKE_ARC) {
+ if (ELEM(tgpi->type,GP_STROKE_BOX ,GP_STROKE_CIRCLE))
gps->flag |= GP_STROKE_CYCLIC;
- }
- else {
- gps->flag &= ~GP_STROKE_CYCLIC;
- }
+
gps->flag |= GP_STROKE_3DSPACE;
gps->mat_nr = BKE_gpencil_get_material_index(tgpi->ob, tgpi->mat) - 1;
@@ -188,19 +349,38 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
/* add to strokes */
BLI_addtail(&tgpi->gpf->strokes, gps);
-}
-/* ----------------------- */
-/* Drawing Callbacks */
+ /* allocate memory for storage points */
+ gpencil_primitive_allocate_memory(tgpi);
+
+ /* Random generator, only init once. */
+ tgpi->rng = BLI_rng_new((uint)0);
+}
-/* Drawing callback for modal operator in 3d mode */
-static void gpencil_primitive_draw_3d(const bContext *C, ARegion *UNUSED(ar), void *arg)
+/* add new segment to curve */
+static void gpencil_primitive_add_segment(tGPDprimitive *tgpi)
{
- tGPDprimitive *tgpi = (tGPDprimitive *)arg;
- ED_gp_draw_primitives(C, tgpi, REGION_DRAW_POST_VIEW);
+ if(tgpi->tot_stored_edges > 0)
+ tgpi->tot_stored_edges += (tgpi->tot_edges - 1);
+ else
+ tgpi->tot_stored_edges += tgpi->tot_edges;
+ gpencil_primitive_allocate_memory(tgpi);
}
-/* ----------------------- */
+/* Helper: set control point */
+static void gp_primitive_set_cp(tGPDprimitive *tgpi, float p[2], float color[4], int size)
+{
+ bGPDcontrolpoint *cp_points = tgpi->gpd->runtime.cp_points;
+
+ if (tgpi->gpd->runtime.tot_cp_points < MAX_CP) {
+ CLAMP(size, 5, 20);
+ bGPDcontrolpoint *cp = &cp_points[tgpi->gpd->runtime.tot_cp_points];
+ copy_v2_v2(&cp->x, p);
+ copy_v4_v4(cp->color, color);
+ cp->size = size;
+ tgpi->gpd->runtime.tot_cp_points += 1;
+ }
+}
/* Helper: Draw status message while the user is running the operator */
static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi)
@@ -209,20 +389,23 @@ static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi
char status_str[UI_MAX_DRAW_STR];
char msg_str[UI_MAX_DRAW_STR];
- if (tgpi->type == GP_STROKE_BOX) {
- BLI_strncpy(msg_str, IFACE_("Rectangle: ESC/RMB to cancel, LMB set origin, Enter/LMB to confirm, Shift to square, Alt to center"), UI_MAX_DRAW_STR);
+ if (tgpi->type == GP_STROKE_LINE) {
+ BLI_strncpy(msg_str, IFACE_("Line: ESC to cancel, LMB set origin, Enter/RMB to confirm, WHEEL/+- to adjust subdivision number, Shift to align, Alt to center"), UI_MAX_DRAW_STR);
+ }
+ else if (tgpi->type == GP_STROKE_BOX) {
+ BLI_strncpy(msg_str, IFACE_("Rectangle: ESC to cancel, LMB set origin, Enter/RMB to confirm, WHEEL/+- to adjust subdivision number, Shift to square, Alt to center"), UI_MAX_DRAW_STR);
}
- else if (tgpi->type == GP_STROKE_LINE) {
- BLI_strncpy(msg_str, IFACE_("Line: ESC/RMB to cancel, LMB set origin, Enter/LMB to confirm, Alt to center"), UI_MAX_DRAW_STR);
+ else if (tgpi->type == GP_STROKE_CIRCLE) {
+ BLI_strncpy(msg_str, IFACE_("Circle: ESC to cancel, Enter/RMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center"), UI_MAX_DRAW_STR);
}
else if (tgpi->type == GP_STROKE_ARC) {
- BLI_strncpy(msg_str, IFACE_("Arc: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center, F to flip, C to Close"), UI_MAX_DRAW_STR);
+ BLI_strncpy(msg_str, IFACE_("Arc: ESC to cancel, Enter/RMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center, M: Flip"), UI_MAX_DRAW_STR);
}
- else {
- BLI_strncpy(msg_str, IFACE_("Circle: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center"), UI_MAX_DRAW_STR);
+ else if (tgpi->type == GP_STROKE_CURVE) {
+ BLI_strncpy(msg_str, IFACE_("Curve: ESC to cancel, Enter/RMB to confirm, WHEEL/+- to adjust edge number, Shift to square, Alt to center, E: extrude"), UI_MAX_DRAW_STR);
}
- if (tgpi->type == GP_STROKE_CIRCLE || tgpi->type == GP_STROKE_ARC) {
+ if (ELEM(tgpi->type, GP_STROKE_CIRCLE, GP_STROKE_ARC, GP_STROKE_LINE, GP_STROKE_BOX)) {
if (hasNumInput(&tgpi->num)) {
char str_offs[NUM_STR_REP_LEN];
@@ -233,114 +416,208 @@ static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi
if (tgpi->flag == IN_PROGRESS) {
BLI_snprintf(
status_str, sizeof(status_str), "%s: %d (%d, %d) (%d, %d)", msg_str, (int)tgpi->tot_edges,
- tgpi->top[0], tgpi->top[1], tgpi->bottom[0], tgpi->bottom[1]);
+ tgpi->start[0], tgpi->start[1], tgpi->end[0], tgpi->end[1]);
}
else {
BLI_snprintf(
status_str, sizeof(status_str), "%s: %d (%d, %d)", msg_str, (int)tgpi->tot_edges,
- tgpi->bottom[0], tgpi->bottom[1]);
+ tgpi->end[0], tgpi->end[1]);
}
}
}
else {
if (tgpi->flag == IN_PROGRESS) {
BLI_snprintf(
- status_str, sizeof(status_str), "%s: (%d, %d) (%d, %d)", msg_str,
- tgpi->top[0], tgpi->top[1], tgpi->bottom[0], tgpi->bottom[1]);
+ status_str, sizeof(status_str), "%s: %d (%d, %d) (%d, %d)", msg_str, (int)tgpi->tot_edges,
+ tgpi->start[0], tgpi->start[1], tgpi->end[0], tgpi->end[1]);
}
else {
BLI_snprintf(
status_str, sizeof(status_str), "%s: (%d, %d)", msg_str,
- tgpi->bottom[0], tgpi->bottom[1]);
+ tgpi->end[0], tgpi->end[1]);
}
}
ED_workspace_status_text(C, status_str);
}
-/* ----------------------- */
-
/* create a rectangle */
static void gp_primitive_rectangle(tGPDprimitive *tgpi, tGPspoint *points2D)
{
- BLI_assert(tgpi->tot_edges == 4);
-
- points2D[0].x = (float)tgpi->top[0];
- points2D[0].y = (float)tgpi->top[1];
-
- points2D[1].x = (float)tgpi->bottom[0];
- points2D[1].y = (float)tgpi->top[1];
-
- points2D[2].x = (float)tgpi->bottom[0];
- points2D[2].y = (float)tgpi->bottom[1];
+ float coords[5][2];
+
+ coords[0][0] = tgpi->start[0];
+ coords[0][1] = tgpi->start[1];
+ coords[1][0] = tgpi->end[0];
+ coords[1][1] = tgpi->start[1];
+ coords[2][0] = tgpi->end[0];
+ coords[2][1] = tgpi->end[1];
+ coords[3][0] = tgpi->start[0];
+ coords[3][1] = tgpi->end[1];
+ coords[4][0] = tgpi->start[0];
+ coords[4][1] = tgpi->start[1];
+
+ const float step = 1.0f / (float)(tgpi->tot_edges);
+ int i = tgpi->tot_stored_edges;
+
+ for (int j = 0; j < 4; j++) {
+ float a = 0.0f;
+ for (int k = 0; k < tgpi->tot_edges; k++) {
+ tGPspoint *p2d = &points2D[i];
+ interp_v2_v2v2(&p2d->x, coords[j], coords[j + 1], a);
+ a += step;
+ i++;
+ }
+ }
- points2D[3].x = (float)tgpi->top[0];
- points2D[3].y = (float)tgpi->bottom[1];
+ mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
+ float color[4];
+ UI_GetThemeColor4fv(TH_ACTIVE_VERT, color);
+ gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+ if (tgpi->tot_stored_edges) {
+ UI_GetThemeColor4fv(TH_REDALERT, color);
+ gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
+ }
+ else
+ gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
+ UI_GetThemeColor4fv(TH_REDALERT, color);
+ gp_primitive_set_cp(tgpi, tgpi->midpoint, color, SMALL_SIZE_CTL);
}
/* create a line */
static void gp_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D)
{
- BLI_assert(tgpi->tot_edges == 2);
+ if (tgpi->tot_edges == 2) {
+ int i = tgpi->tot_stored_edges;
- points2D[0].x = (float)tgpi->top[0];
- points2D[0].y = (float)tgpi->top[1];
+ points2D[i].x = tgpi->start[0];
+ points2D[i].y = tgpi->start[1];
- points2D[1].x = (float)tgpi->bottom[0];
- points2D[1].y = (float)tgpi->bottom[1];
+ points2D[i + 1].x = tgpi->end[0];
+ points2D[i + 1].y = tgpi->end[1];
+ }
+ else {
+ const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
+ const float step = 1.0f / (float)(tgpi->tot_edges - 1);
+ float a = tgpi->tot_stored_edges ? step : 0.0f;
+
+ for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
+ tGPspoint *p2d = &points2D[i];
+ interp_v2_v2v2(&p2d->x, tgpi->start, tgpi->end, a);
+ a += step;
+ }
+ }
+ float color[4];
+ UI_GetThemeColor4fv(TH_ACTIVE_VERT, color);
+ gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+ if (tgpi->tot_stored_edges) {
+ UI_GetThemeColor4fv(TH_REDALERT, color);
+ gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
+ }
+ else
+ gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
}
/* create an arc */
static void gp_primitive_arc(tGPDprimitive *tgpi, tGPspoint *points2D)
{
- const int totpoints = tgpi->tot_edges;
- const float step = M_PI_2 / (float)(totpoints - 1);
- float length[2];
- int start[2];
- int end[2];
- float a = 0.0f;
-
- start[0] = tgpi->top[0];
- start[1] = tgpi->top[1];
- end[0] = tgpi->bottom[0];
- end[1] = tgpi->bottom[1];
-
- if (tgpi->flip) {
- SWAP(int, end[0], start[0]);
- SWAP(int, end[1], start[1]);
+ const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
+ const float step = M_PI_2 / (float)(tgpi->tot_edges - 1);
+ float start[2];
+ float end[2];
+ float cp1[2];
+ float corner[2];
+ float midpoint[2];
+ float a = tgpi->tot_stored_edges ? step : 0.0f;
+
+ mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
+ copy_v2_v2(start, tgpi->start);
+ copy_v2_v2(end, tgpi->end);
+ copy_v2_v2(cp1, tgpi->cp1);
+ copy_v2_v2(midpoint, tgpi->midpoint);
+
+ corner[0] = midpoint[0] - (cp1[0] - midpoint[0]);
+ corner[1] = midpoint[1] - (cp1[1] - midpoint[1]);
+
+ for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
+ tGPspoint *p2d = &points2D[i];
+ p2d->x = corner[0] + (end[0] - corner[0]) * sinf(a) + (start[0] - corner[0]) * cosf(a);
+ p2d->y = corner[1] + (end[1] - corner[1]) * sinf(a) + (start[1] - corner[1]) * cosf(a);
+ a += step;
}
+ float color[4];
+ UI_GetThemeColor4fv(TH_ACTIVE_VERT, color);
+ gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+ if (tgpi->tot_stored_edges) {
+ UI_GetThemeColor4fv(TH_REDALERT, color);
+ gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
+ }
+ else
+ gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
+ UI_GetThemeColor4fv(TH_GP_VERTEX_SELECT, color);
+ gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f);
+}
- length[0] = end[0] - start[0];
- length[1] = end[1] - start[1];
-
- for (int i = 0; i < totpoints; i++) {
+/* create a bezier */
+static void gp_primitive_bezier(tGPDprimitive *tgpi, tGPspoint *points2D)
+{
+ const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
+ const float step = 1.0f / (float)(tgpi->tot_edges - 1);
+ float bcp1[2];
+ float bcp2[2];
+ float bcp3[2];
+ float bcp4[2];
+ float a = tgpi->tot_stored_edges ? step : 0.0f;
+
+ copy_v2_v2(bcp1, tgpi->start);
+ copy_v2_v2(bcp2, tgpi->cp1);
+ copy_v2_v2(bcp3, tgpi->cp2);
+ copy_v2_v2(bcp4, tgpi->end);
+
+ for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
tGPspoint *p2d = &points2D[i];
- p2d->x = (start[0] + sinf(a) * length[0]);
- p2d->y = (end[1] - cosf(a) * length[1]);
+ interp_v2_v2v2v2v2_cubic(&p2d->x, bcp1, bcp2, bcp3, bcp4, a);
a += step;
}
+ float color[4];
+ UI_GetThemeColor4fv(TH_ACTIVE_VERT, color);
+ gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+ if (tgpi->tot_stored_edges) {
+ UI_GetThemeColor4fv(TH_REDALERT, color);
+ gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
+ }
+ else
+ gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
+ UI_GetThemeColor4fv(TH_GP_VERTEX_SELECT, color);
+ gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f);
+ gp_primitive_set_cp(tgpi, tgpi->cp2, color, BIG_SIZE_CTL * 0.9f);
}
/* create a circle */
static void gp_primitive_circle(tGPDprimitive *tgpi, tGPspoint *points2D)
{
- const int totpoints = tgpi->tot_edges;
- const float step = (2.0f * M_PI) / (float)(totpoints);
+ const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
+ const float step = (2.0f * M_PI) / (float)(tgpi->tot_edges);
float center[2];
float radius[2];
float a = 0.0f;
- /* TODO: Use math-lib functions for these? */
- center[0] = tgpi->top[0] + ((tgpi->bottom[0] - tgpi->top[0]) / 2.0f);
- center[1] = tgpi->top[1] + ((tgpi->bottom[1] - tgpi->top[1]) / 2.0f);
- radius[0] = fabsf(((tgpi->bottom[0] - tgpi->top[0]) / 2.0f));
- radius[1] = fabsf(((tgpi->bottom[1] - tgpi->top[1]) / 2.0f));
+ center[0] = tgpi->start[0] + ((tgpi->end[0] - tgpi->start[0]) / 2.0f);
+ center[1] = tgpi->start[1] + ((tgpi->end[1] - tgpi->start[1]) / 2.0f);
+ radius[0] = fabsf(((tgpi->end[0] - tgpi->start[0]) / 2.0f));
+ radius[1] = fabsf(((tgpi->end[1] - tgpi->start[1]) / 2.0f));
- for (int i = 0; i < totpoints; i++) {
+ for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
tGPspoint *p2d = &points2D[i];
p2d->x = (center[0] + cosf(a) * radius[0]);
p2d->y = (center[1] + sinf(a) * radius[1]);
a += step;
}
+ float color[4];
+ UI_GetThemeColor4fv(TH_ACTIVE_VERT, color);
+ gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
+ gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
+ UI_GetThemeColor4fv(TH_REDALERT, color);
+ gp_primitive_set_cp(tgpi, center, color, SMALL_SIZE_CTL);
}
/* Helper: Update shape of the stroke */
@@ -348,18 +625,26 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
{
ToolSettings *ts = tgpi->scene->toolsettings;
bGPdata *gpd = tgpi->gpd;
+ Brush *brush = tgpi->brush;
bGPDstroke *gps = tgpi->gpf->strokes.first;
+ GP_Sculpt_Settings *gset = &ts->gp_sculpt;
+ int depth_margin = (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0;
+ char *align_flag = &ts->gpencil_v3d_align;
+ bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE));
- /* realloc points to new size */
- /* TODO: only do this if the size has changed? */
- gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * tgpi->tot_edges);
- if (gps->dvert != NULL) {
- gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * tgpi->tot_edges);
- }
- gps->totpoints = tgpi->tot_edges;
+ if (tgpi->type == GP_STROKE_BOX)
+ gps->totpoints = (tgpi->tot_edges * 4 + tgpi->tot_stored_edges);
+ else
+ gps->totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
+
+ if (tgpi->tot_stored_edges)
+ gps->totpoints--;
+
+ tgpi->gpd->runtime.tot_cp_points = 0;
/* compute screen-space coordinates for points */
- tGPspoint *points2D = MEM_callocN(sizeof(tGPspoint) * tgpi->tot_edges, "gp primitive points2D");
+ tGPspoint *points2D = tgpi->points;
+
switch (tgpi->type) {
case GP_STROKE_BOX:
gp_primitive_rectangle(tgpi, points2D);
@@ -372,46 +657,254 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
break;
case GP_STROKE_ARC:
gp_primitive_arc(tgpi, points2D);
- if (tgpi->cyclic)
- gps->flag |= GP_STROKE_CYCLIC;
- else
- gps->flag &= ~GP_STROKE_CYCLIC;
break;
+ case GP_STROKE_CURVE:
+ gp_primitive_bezier(tgpi, points2D);
default:
break;
- }
+ }
/* convert screen-coordinates to 3D coordinates */
+ gp_session_validatebuffer(tgpi);
+ gp_init_colors(tgpi);
+ if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) {
+ curvemapping_initialize(ts->gp_sculpt.cur_primitive);
+ }
+ if (tgpi->brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) {
+ curvemapping_initialize(tgpi->brush->gpencil_settings->curve_jitter);
+ }
+ if (tgpi->brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) {
+ curvemapping_initialize(tgpi->brush->gpencil_settings->curve_strength);
+ }
+
+ /* get an array of depths, far depths are blended */
+ float *depth_arr = NULL;
+ if (is_depth) {
+ int i;
+ int mval_i[2], mval_prev[2] = { 0 };
+ bool interp_depth = false;
+ bool found_depth = false;
+
+ /* need to restore the original projection settings before packing up */
+ view3d_region_operator_needs_opengl(tgpi->win, tgpi->ar);
+ ED_view3d_autodist_init(tgpi->depsgraph, tgpi->ar, tgpi->v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+
+ depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points");
+ tGPspoint *ptc = &points2D[0];
+ for (i = 0; i < gps->totpoints; i++, ptc++) {
+ round_v2i_v2fl(mval_i, &ptc->x);
+ if ((ED_view3d_autodist_depth(tgpi->ar, mval_i, depth_margin, depth_arr + i) == 0) &&
+ (i && (ED_view3d_autodist_depth_seg(tgpi->ar, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0)))
+ {
+ interp_depth = true;
+ }
+ else {
+ found_depth = true;
+ }
+ copy_v2_v2_int(mval_prev, mval_i);
+ }
+
+ if (!found_depth) {
+ for (i = 0; i < gps->totpoints; i++) {
+ depth_arr[i] = 0.9999f;
+ }
+ }
+ else {
+ /* if all depth are too high disable */
+ bool valid_depth = false;
+ for (i = 0; i < gps->totpoints; i++) {
+ if (depth_arr[i] < 0.9999f) {
+ valid_depth = true;
+ break;
+ }
+ }
+ if (!valid_depth) {
+ MEM_SAFE_FREE(depth_arr);
+ is_depth = false;
+ }
+ else {
+ if ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_ENDPOINTS) ||
+ (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_FIRST))
+ {
+ int first_valid = 0;
+ int last_valid = 0;
+
+ /* find first valid contact point */
+ for (i = 0; i < gps->totpoints; i++) {
+ if (depth_arr[i] != FLT_MAX)
+ break;
+ }
+ first_valid = i;
+
+ /* find last valid contact point */
+ if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_FIRST) {
+ last_valid = first_valid;
+ }
+ else {
+ for (i = gps->totpoints - 1; i >= 0; i--) {
+ if (depth_arr[i] != FLT_MAX)
+ break;
+ }
+ last_valid = i;
+ }
+
+ /* invalidate any other point, to interpolate between
+ * first and last contact in an imaginary line between them */
+ for (i = 0; i < gps->totpoints; i++) {
+ if ((i != first_valid) && (i != last_valid)) {
+ depth_arr[i] = FLT_MAX;
+ }
+ }
+ interp_depth = true;
+ }
+
+ if (interp_depth) {
+ interp_sparse_array(depth_arr, gps->totpoints, FLT_MAX);
+ }
+ }
+ }
+ }
+
+ /* load stroke points and sbuffer */
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
tGPspoint *p2d = &points2D[i];
+ /* Copy points to buffer */
+ tGPspoint *tpt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_size);
+
+ /* Store original points */
+ float tmp_xyp[2];
+ copy_v2_v2(tmp_xyp, &p2d->x);
+
+ /* calc pressure */
+ float curve_pressure = 1.0;
+ float pressure = 1.0;
+ float strength = brush->gpencil_settings->draw_strength;
+
+ /* normalize value to evaluate curve */
+ if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) {
+ float value = (float)i / (gps->totpoints - 1);
+ curve_pressure = curvemapping_evaluateF(gset->cur_primitive, 0, value);
+ pressure = curve_pressure;
+ }
+
+ /* apply jitter to position */
+ if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
+ (brush->gpencil_settings->draw_jitter > 0.0f))
+ {
+ float jitter;
+
+ if (brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) {
+ jitter = curvemapping_evaluateF(brush->gpencil_settings->curve_jitter, 0, curve_pressure);
+ jitter *= brush->gpencil_settings->draw_sensitivity;
+ }
+ else {
+ jitter = brush->gpencil_settings->draw_jitter;
+ }
+
+ const float exfactor = (brush->gpencil_settings->draw_jitter + 2.0f) * (brush->gpencil_settings->draw_jitter + 2.0f); /* exponential value */
+ const float rnd = BLI_rng_get_float(tgpi->rng);
+ const float fac = rnd * exfactor * jitter;
+ if (rnd > 0.5f) {
+ add_v2_fl(&p2d->x, -fac);
+ }
+ else {
+ add_v2_fl(&p2d->x, fac);
+ }
+ }
+
+ /* apply randomness to pressure */
+ if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
+ (brush->gpencil_settings->draw_random_press > 0.0f))
+ {
+ float rnd = BLI_rng_get_float(tgpi->rng);
+ if (rnd > 0.5f) {
+ pressure -= brush->gpencil_settings->draw_random_press * rnd;
+ }
+ else {
+ pressure += brush->gpencil_settings->draw_random_press * rnd;
+ }
+ }
+
+ /* color strength */
+ if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) {
+ float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_strength, 0, curve_pressure);
+ strength *= curvef * brush->gpencil_settings->draw_sensitivity;
+ strength *= brush->gpencil_settings->draw_strength;
+ }
+
+ CLAMP(strength, GPENCIL_STRENGTH_MIN, 1.0f);
+
+ /* apply randomness to color strength */
+ if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
+ (brush->gpencil_settings->draw_random_strength > 0.0f))
+ {
+ const float rnd = BLI_rng_get_float(tgpi->rng);
+ if (rnd > 0.5f) {
+ strength -= strength * brush->gpencil_settings->draw_random_strength * rnd;
+ }
+ else {
+ strength += strength * brush->gpencil_settings->draw_random_strength * rnd;
+ }
+ CLAMP(strength, GPENCIL_STRENGTH_MIN, 1.0f);
+ }
+
+ copy_v2_v2(&tpt->x, &p2d->x);
+
+ CLAMP_MIN(pressure, 0.1f);
+
+ tpt->pressure = pressure;
+ tpt->strength = strength;
+ tpt->time = p2d->time;
+ tpt->uv_fac = 1.0f;
+ tpt->uv_rot = p2d->uv_rot;
+
+ gpd->runtime.sbuffer_size++;
+
+ /* add small offset to keep stroke over the surface */
+ if ((depth_arr) && (gpd->zdepth_offset > 0.0f)) {
+ depth_arr[i] *= (1.0f - gpd->zdepth_offset);
+ }
+
/* convert screen-coordinates to 3D coordinates */
- gp_stroke_convertcoords_tpoint(tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl, p2d, NULL, &pt->x);
+ gp_stroke_convertcoords_tpoint(
+ tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl,
+ p2d, depth_arr ? depth_arr + i : NULL,
+ &pt->x);
- pt->pressure = 1.0f;
- pt->strength = tgpi->brush->gpencil_settings->draw_strength;
+ pt->pressure = pressure;
+ pt->strength = strength;
pt->time = 0.0f;
+ pt->flag = 0;
+ pt->uv_fac = 1.0f;
if (gps->dvert != NULL) {
MDeformVert *dvert = &gps->dvert[i];
dvert->totweight = 0;
dvert->dw = NULL;
}
+
+ /* Restore original points */
+ copy_v2_v2(&p2d->x, tmp_xyp);
}
- /* if axis locked, reproject to plane locked */
- if (tgpi->lock_axis > GP_LOCKAXIS_VIEW) {
- bGPDspoint *tpt = gps->points;
+ /* store cps and convert coords */
+ if (tgpi->gpd->runtime.tot_cp_points > 0) {
+ bGPDcontrolpoint *cps = tgpi->gpd->runtime.cp_points;
+ for (int i = 0; i < tgpi->gpd->runtime.tot_cp_points; i++) {
+ bGPDcontrolpoint *cp = &cps[i];
+ gp_stroke_convertcoords_tpoint(tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl, (tGPspoint *)cp, NULL, &cp->x);
+ }
+ }
+
+ /* reproject to plane */
+ if (!is_depth) {
float origin[3];
ED_gp_get_drawing_reference(tgpi->scene, tgpi->ob, tgpi->gpl,
ts->gpencil_v3d_align, origin);
-
- for (int i = 0; i < gps->totpoints; i++, tpt++) {
- ED_gp_project_point_to_plane(tgpi->ob, tgpi->rv3d, origin,
- ts->gp_sculpt.lock_axis - 1,
- tpt);
- }
+ ED_gp_project_stroke_to_plane(
+ tgpi->ob, tgpi->rv3d, gps, origin, ts->gp_sculpt.lock_axis - 1);
}
/* if parented change position relative to parent object */
@@ -423,8 +916,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
/* force fill recalc */
gps->flag |= GP_STROKE_RECALC_CACHES;
- /* free temp data */
- MEM_SAFE_FREE(points2D);
+ MEM_SAFE_FREE(depth_arr);
DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -443,18 +935,14 @@ static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive
gp_primitive_update_strokes(C, tgpi);
}
-/* ----------------------- */
-
static void gpencil_primitive_interaction_begin(tGPDprimitive *tgpi, const wmEvent *event)
{
- tgpi->origin[0] = event->mval[0];
- tgpi->origin[1] = event->mval[1];
-
- tgpi->top[0] = event->mval[0];
- tgpi->top[1] = event->mval[1];
-
- tgpi->bottom[0] = event->mval[0];
- tgpi->bottom[1] = event->mval[1];
+ copy_v2fl_v2i(tgpi->mval, event->mval);
+ copy_v2_v2(tgpi->origin, tgpi->mval);
+ copy_v2_v2(tgpi->start, tgpi->mval);
+ copy_v2_v2(tgpi->end, tgpi->mval);
+ copy_v2_v2(tgpi->cp1, tgpi->mval);
+ copy_v2_v2(tgpi->cp2, tgpi->mval);
}
/* Exit and free memory */
@@ -465,20 +953,35 @@ static void gpencil_primitive_exit(bContext *C, wmOperator *op)
/* don't assume that operator data exists at all */
if (tgpi) {
- /* remove drawing handler */
- if (tgpi->draw_handle_3d) {
- ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d);
- }
-
/* clear status message area */
ED_workspace_status_text(C, NULL);
+ MEM_SAFE_FREE(tgpi->points);
+ tgpi->gpd->runtime.tot_cp_points = 0;
+ MEM_SAFE_FREE(tgpi->gpd->runtime.cp_points);
/* finally, free memory used by temp data */
BKE_gpencil_free_strokes(tgpi->gpf);
- MEM_freeN(tgpi->gpf);
+ MEM_SAFE_FREE(tgpi->gpf);
+
+ /* free random seed */
+ if (tgpi->rng != NULL) {
+ BLI_rng_free(tgpi->rng);
+ }
+
MEM_freeN(tgpi);
}
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+
+ /* free stroke buffer */
+ if ((gpd != NULL) && (gpd->runtime.sbuffer)) {
+ MEM_SAFE_FREE(gpd->runtime.sbuffer);
+ gpd->runtime.sbuffer = NULL;
+
+ /* clear flags */
+ gpd->runtime.sbuffer_size = 0;
+ gpd->runtime.sbuffer_sflag = 0;
+ }
+
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
/* clear pointer */
@@ -499,7 +1002,10 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
tGPDprimitive *tgpi = MEM_callocN(sizeof(tGPDprimitive), "GPencil Primitive Data");
op->customdata = tgpi;
+ tgpi->points = MEM_callocN(sizeof(tGPspoint), "gp primitive points2D");
+
/* set current scene and window info */
+ tgpi->bmain = CTX_data_main(C);
tgpi->scene = scene;
tgpi->ob = CTX_data_active_object(C);
tgpi->sa = CTX_wm_area(C);
@@ -509,11 +1015,20 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
tgpi->depsgraph = CTX_data_depsgraph(C);
tgpi->win = CTX_wm_window(C);
+ /* save original type */
+ tgpi->orign_type = RNA_enum_get(op->ptr, "type");
+
/* set current frame number */
tgpi->cframe = cfra_eval;
/* set GP datablock */
tgpi->gpd = gpd;
+ /* region where paint was originated */
+ tgpi->gpd->runtime.ar = tgpi->ar;
+
+ /* control points */
+ tgpi->gpd->runtime.cp_points = MEM_callocN(sizeof(bGPDcontrolpoint) * MAX_CP, "gp primitive cpoint");
+ tgpi->gpd->runtime.tot_cp_points = 0;
/* getcolor info */
tgpi->mat = BKE_gpencil_material_ensure(bmain, tgpi->ob);
@@ -521,31 +1036,44 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
/* set parameters */
tgpi->type = RNA_enum_get(op->ptr, "type");
+ if(ELEM(tgpi->type, GP_STROKE_ARC, GP_STROKE_CURVE))
+ tgpi->curve = true;
+ else
+ tgpi->curve = false;
+
/* set default edge count */
- if (tgpi->type == GP_STROKE_CIRCLE) {
- RNA_int_set(op->ptr, "edges", 64);
- }
- else if (tgpi->type == GP_STROKE_ARC) {
- RNA_int_set(op->ptr, "edges", 32);
- }
- else if (tgpi->type == GP_STROKE_BOX) {
- RNA_int_set(op->ptr, "edges", 4);
- }
- else { /* LINE */
- RNA_int_set(op->ptr, "edges", 2);
+ switch (tgpi->type) {
+ case GP_STROKE_LINE:
+ {
+ RNA_int_set(op->ptr, "edges", 8);
+ break;
+ }
+ case GP_STROKE_BOX:
+ {
+ RNA_int_set(op->ptr, "edges", 8);
+ break;
+ }
+ case GP_STROKE_CIRCLE:
+ {
+ RNA_int_set(op->ptr, "edges", 96);
+ break;
+ }
+ default:
+ {
+ RNA_int_set(op->ptr, "edges", 64);
+ break;
+ }
}
+ tgpi->tot_stored_edges = 0;
tgpi->tot_edges = RNA_int_get(op->ptr, "edges");
tgpi->flag = IDLE;
-
tgpi->lock_axis = ts->gp_sculpt.lock_axis;
/* set temp layer, frame and stroke */
gp_primitive_set_initdata(C, tgpi);
}
-/* ----------------------- */
-
/* Invoke handler: Initialize the operator */
static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
@@ -568,9 +1096,6 @@ static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *
*/
op->flag |= OP_IS_MODAL_CURSOR_REGION;
- /* Enable custom drawing handlers */
- tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_primitive_draw_3d, tgpi, REGION_DRAW_POST_VIEW);
-
/* set cursor to indicate modal */
WM_cursor_modal_set(win, BC_CROSSCURSOR);
@@ -624,7 +1149,6 @@ static void gpencil_primitive_interaction_end(bContext *C, wmOperator *op, wmWin
if (dw) {
dw->weight = ts->vgroup_weight;
}
-
}
}
@@ -635,25 +1159,136 @@ static void gpencil_primitive_interaction_end(bContext *C, wmOperator *op, wmWin
gpencil_primitive_exit(C, op);
}
-/* Helper to square a primitive */
-static void gpencil_primitive_to_square(tGPDprimitive *tgpi, const int x, const int y)
+/* edit event handling */
+static void gpencil_primitive_edit_event_handling(bContext *C, wmOperator *op, wmWindow *win, const wmEvent *event, tGPDprimitive *tgpi)
{
- int w = abs(x);
- int h = abs(y);
- if ((x > 0 && y > 0) || (x < 0 && y < 0)) {
- if (w > h)
- tgpi->bottom[1] = tgpi->origin[1] + x;
- else
- tgpi->bottom[0] = tgpi->origin[0] + y;
+ /* calculate nearest point then set cursor */
+ int move = MOVE_NONE;
+ float a = len_v2v2(tgpi->mval, tgpi->start);
+ float b = len_v2v2(tgpi->mval, tgpi->end);
+
+ float c = len_v2v2(tgpi->mval, tgpi->cp1);
+ float d = len_v2v2(tgpi->mval, tgpi->cp2);
+
+ if (tgpi->flag == IN_CURVE_EDIT) {
+ if ((a < BIG_SIZE_CTL && tgpi->tot_stored_edges == 0) || b < BIG_SIZE_CTL) {
+ move = MOVE_ENDS;
+ WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
+ }
+ else if(tgpi->curve) {
+ move = MOVE_CP;
+ WM_cursor_modal_set(win, BC_HANDCURSOR);
+ }
+ else {
+ WM_cursor_modal_set(win, BC_CROSSCURSOR);
+ }
}
- else {
- if (w > h)
- tgpi->bottom[1] = tgpi->origin[1] - x;
- else
- tgpi->bottom[0] = tgpi->origin[0] - y;
+ else if (tgpi->flag == IN_PROGRESS) {
+ WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
+ }
+
+ switch (event->type) {
+ case MOUSEMOVE:
+ {
+ if ((event->val == KM_PRESS) && tgpi->sel_cp != SELECT_NONE) {
+ if (tgpi->sel_cp == SELECT_START && tgpi->tot_stored_edges == 0) {
+ copy_v2_v2(tgpi->start, tgpi->mval);
+ }
+ else if (tgpi->sel_cp == SELECT_END) {
+ copy_v2_v2(tgpi->end, tgpi->mval);
+ }
+ else if (tgpi->sel_cp == SELECT_CP1 || (tgpi->sel_cp == SELECT_CP2 && tgpi->type != GP_STROKE_CURVE)) {
+ float dx = (tgpi->mval[0] - tgpi->mvalo[0]);
+ float dy = (tgpi->mval[1] - tgpi->mvalo[1]);
+ tgpi->cp1[0] += dx;
+ tgpi->cp1[1] += dy;
+ if (event->shift)
+ copy_v2_v2(tgpi->cp2, tgpi->cp1);
+ }
+ else if (tgpi->sel_cp == SELECT_CP2) {
+ float dx = (tgpi->mval[0] - tgpi->mvalo[0]);
+ float dy = (tgpi->mval[1] - tgpi->mvalo[1]);
+ tgpi->cp2[0] += dx;
+ tgpi->cp2[1] += dy;
+ if (event->shift)
+ copy_v2_v2(tgpi->cp1, tgpi->cp2);
+ }
+ /* update screen */
+ gpencil_primitive_update(C, op, tgpi);
+ }
+ break;
+ }
+ case LEFTMOUSE:
+ {
+ if ((event->val == KM_PRESS)) {
+ /* find nearest cp based on stroke end points */
+ if (move == MOVE_ENDS)
+ tgpi->sel_cp = (a < b) ? SELECT_START : SELECT_END;
+ else if (move == MOVE_CP)
+ tgpi->sel_cp = (c < d) ? SELECT_CP1 : SELECT_CP2;
+ else
+ tgpi->sel_cp = SELECT_NONE;
+ break;
+ }
+ else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) {
+ /* set control points and enter edit mode */
+ tgpi->flag = IN_CURVE_EDIT;
+ gp_primitive_update_cps(tgpi);
+ gpencil_primitive_update(C, op, tgpi);
+ }
+ else {
+ tgpi->sel_cp = SELECT_NONE;
+ }
+ break;
+ }
+ case MKEY:
+ {
+ if ((event->val == KM_PRESS) &&
+ (tgpi->curve) &&
+ (tgpi->orign_type == GP_STROKE_ARC))
+ {
+ tgpi->flip ^= 1;
+ gp_primitive_update_cps(tgpi);
+ gpencil_primitive_update(C, op, tgpi);
+ }
+ break;
+ }
+ case EKEY:
+ {
+ if (tgpi->flag == IN_CURVE_EDIT && !ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) {
+ tgpi->flag = IN_PROGRESS;
+ WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
+ gpencil_primitive_add_segment(tgpi);
+ copy_v2_v2(tgpi->start, tgpi->end);
+ copy_v2_v2(tgpi->origin, tgpi->start);
+ gp_primitive_update_cps(tgpi);
+ }
+ break;
+ }
}
}
+/* move */
+static void gpencil_primitive_move(tGPDprimitive *tgpi)
+{
+ float move[2];
+ sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo);
+
+ bGPDstroke *gps = tgpi->gpf->strokes.first;
+ tGPspoint *points2D = tgpi->points;
+
+ for (int i = 0; i < gps->totpoints; i++) {
+ tGPspoint *p2d = &points2D[i];
+ add_v2_v2(&p2d->x, move);
+ }
+
+ add_v2_v2(tgpi->start, move);
+ add_v2_v2(tgpi->end, move);
+ add_v2_v2(tgpi->cp1, move);
+ add_v2_v2(tgpi->cp2, move);
+ add_v2_v2(tgpi->origin, move);
+}
+
/* Modal handler: Events handling during interactive part */
static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
@@ -661,15 +1296,47 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
wmWindow *win = CTX_wm_window(C);
const bool has_numinput = hasNumInput(&tgpi->num);
+ copy_v2fl_v2i(tgpi->mval, event->mval);
+
+ if (tgpi->flag == IN_MOVE) {
+
+ switch (event->type) {
+ case MOUSEMOVE:
+ gpencil_primitive_move(tgpi);
+ gpencil_primitive_update(C, op, tgpi);
+ break;
+ case ESCKEY:
+ case RIGHTMOUSE:
+ case LEFTMOUSE:
+ tgpi->flag = IN_CURVE_EDIT;
+ break;
+ }
+ copy_v2_v2(tgpi->mvalo, tgpi->mval);
+ return OPERATOR_RUNNING_MODAL;
+ }
+ else if (tgpi->flag != IDLE) {
+ gpencil_primitive_edit_event_handling(C, op, win, event, tgpi);
+ }
+
switch (event->type) {
case LEFTMOUSE:
+ {
if ((event->val == KM_PRESS) && (tgpi->flag == IDLE)) {
/* start drawing primitive */
/* TODO: Ignore if not in main region yet */
tgpi->flag = IN_PROGRESS;
gpencil_primitive_interaction_begin(tgpi, event);
}
+ else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_MOVE)) {
+ tgpi->flag = IN_CURVE_EDIT;
+ }
else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) {
+ /* set control points and enter edit mode */
+ tgpi->flag = IN_CURVE_EDIT;
+ gp_primitive_update_cps(tgpi);
+ gpencil_primitive_update(C, op, tgpi);
+ }
+ else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS) && (tgpi->type != GP_STROKE_CURVE)) {
/* stop drawing primitive */
tgpi->flag = IDLE;
gpencil_primitive_interaction_end(C, op, win, tgpi);
@@ -682,16 +1349,26 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
}
}
break;
- case RETKEY: /* confirm */
+ }
+ case SPACEKEY: /* confirm */
+ case RETKEY:
{
tgpi->flag = IDLE;
gpencil_primitive_interaction_end(C, op, win, tgpi);
/* done! */
return OPERATOR_FINISHED;
}
- case ESCKEY: /* cancel */
case RIGHTMOUSE:
{
+ if (tgpi->flag == IN_CURVE_EDIT || (tgpi->flag == IN_PROGRESS && tgpi->tot_stored_edges > 0)) {
+ tgpi->flag = IDLE;
+ gpencil_primitive_interaction_end(C, op, win, tgpi);
+ /* done! */
+ return OPERATOR_FINISHED;
+ }
+ }
+ case ESCKEY:
+ {
/* return to normal cursor and header status */
ED_workspace_status_text(C, NULL);
WM_cursor_modal_restore(win);
@@ -702,76 +1379,95 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
/* canceled! */
return OPERATOR_CANCELLED;
}
- case CKEY:
+ case PADPLUSKEY:
+ case WHEELUPMOUSE:
{
- if ((event->val == KM_RELEASE) && tgpi->type == GP_STROKE_ARC) {
- tgpi->cyclic ^= 1;
+ if ((event->val != KM_RELEASE)) {
+ tgpi->tot_edges = tgpi->tot_edges + 1;
+ CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
+ RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
/* update screen */
gpencil_primitive_update(C, op, tgpi);
}
break;
}
- case FKEY:
+ case PADMINUS:
+ case WHEELDOWNMOUSE:
{
- if ((event->val == KM_RELEASE) && tgpi->type == GP_STROKE_ARC) {
- tgpi->flip ^= 1;
+ if ((event->val != KM_RELEASE)) {
+ tgpi->tot_edges = tgpi->tot_edges - 1;
+ CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
+ RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
/* update screen */
gpencil_primitive_update(C, op, tgpi);
}
break;
}
- case PADPLUSKEY:
- case WHEELUPMOUSE:
+ case GKEY: /* grab mode */
{
- if ((event->val != KM_RELEASE) && (tgpi->type == GP_STROKE_CIRCLE || tgpi->type == GP_STROKE_ARC)) {
- tgpi->tot_edges = tgpi->tot_edges + 1;
- CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
- RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
+ if ((event->val == KM_PRESS)) {
+ tgpi->flag = IN_MOVE;
+ WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
+ }
+ break;
+ }
+ case CKEY: /* curve mode */
+ {
+ if ((event->val == KM_PRESS) &&
+ (tgpi->orign_type == GP_STROKE_CURVE))
+ {
+ switch (tgpi->type) {
+ case GP_STROKE_CURVE:
+ tgpi->type = GP_STROKE_ARC;
+ break;
+ default:
+ case GP_STROKE_ARC:
+ tgpi->type = GP_STROKE_CURVE;
+ break;
+ }
- /* update screen */
+ RNA_enum_set(op->ptr, "type", tgpi->type);
+ gp_primitive_update_cps(tgpi);
gpencil_primitive_update(C, op, tgpi);
}
break;
}
- case PADMINUS:
- case WHEELDOWNMOUSE:
+ case TABKEY:
{
- if ((event->val != KM_RELEASE) && (tgpi->type == GP_STROKE_CIRCLE || tgpi->type == GP_STROKE_ARC)) {
- tgpi->tot_edges = tgpi->tot_edges - 1;
- CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
- RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
-
- /* update screen */
+ if (tgpi->flag == IN_CURVE_EDIT) {
+ tgpi->flag = IN_PROGRESS;
+ WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
+ gp_primitive_update_cps(tgpi);
gpencil_primitive_update(C, op, tgpi);
}
break;
}
case MOUSEMOVE: /* calculate new position */
{
+ if (tgpi->flag == IN_CURVE_EDIT) {
+ break;
+ }
/* only handle mousemove if not doing numinput */
if (has_numinput == false) {
/* update position of mouse */
- tgpi->bottom[0] = event->mval[0];
- tgpi->bottom[1] = event->mval[1];
- tgpi->top[0] = tgpi->origin[0];
- tgpi->top[1] = tgpi->origin[1];
+ copy_v2_v2(tgpi->end, tgpi->mval);
+ copy_v2_v2(tgpi->start, tgpi->origin);
if (tgpi->flag == IDLE) {
- tgpi->origin[0] = event->mval[0];
- tgpi->origin[1] = event->mval[1];
+ copy_v2_v2(tgpi->origin, tgpi->mval);
}
/* Keep square if shift key */
if (event->shift) {
- int x = tgpi->bottom[0] - tgpi->origin[0];
- int y = tgpi->bottom[1] - tgpi->origin[1];
- if (tgpi->type == GP_STROKE_LINE) {
- float angle = fabsf(atan2f((float)y, (float)x));
- if (angle < 0.4f || angle > (M_PI - 0.4f)) {
- tgpi->bottom[1] = tgpi->origin[1];
+ float x = tgpi->end[0] - tgpi->origin[0];
+ float y = tgpi->end[1] - tgpi->origin[1];
+ if (tgpi->type == GP_STROKE_LINE || tgpi->curve) {
+ float angle = fabsf(atan2f(y, x));
+ if (angle < 0.4f || angle >(M_PI - 0.4f)) {
+ tgpi->end[1] = tgpi->origin[1];
}
else if (angle > (M_PI_2 - 0.4f) && angle < (M_PI_2 + 0.4f)) {
- tgpi->bottom[0] = tgpi->origin[0];
+ tgpi->end[0] = tgpi->origin[0];
}
else {
gpencil_primitive_to_square(tgpi, x, y);
@@ -783,9 +1479,10 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
}
/* Center primitive if alt key */
if (event->alt) {
- tgpi->top[0] = tgpi->origin[0] - (tgpi->bottom[0] - tgpi->origin[0]);
- tgpi->top[1] = tgpi->origin[1] - (tgpi->bottom[1] - tgpi->origin[1]);
+ tgpi->start[0] = tgpi->origin[0] - (tgpi->end[0] - tgpi->origin[0]);
+ tgpi->start[1] = tgpi->origin[1] - (tgpi->end[1] - tgpi->origin[1]);
}
+ gp_primitive_update_cps(tgpi);
/* update screen */
gpencil_primitive_update(C, op, tgpi);
}
@@ -793,7 +1490,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
}
default:
{
- if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) {
+ if (tgpi->flag != IN_CURVE_EDIT && (event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) {
float value;
/* Grab data from numeric input, and store this new value (the user see an int) */
@@ -816,6 +1513,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
}
}
+ copy_v2_v2(tgpi->mvalo, tgpi->mval);
/* still running... */
return OPERATOR_RUNNING_MODAL;
}
@@ -834,6 +1532,7 @@ void GPENCIL_OT_primitive(wmOperatorType *ot)
{GP_STROKE_LINE, "LINE", 0, "Line", ""},
{GP_STROKE_CIRCLE, "CIRCLE", 0, "Circle", ""},
{GP_STROKE_ARC, "ARC", 0, "Arc", ""},
+ {GP_STROKE_CURVE, "CURVE", 0, "Curve", ""},
{0, NULL, 0, NULL, NULL}
};
@@ -854,11 +1553,11 @@ void GPENCIL_OT_primitive(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
- RNA_def_int(ot->srna, "edges", 4, MIN_EDGES, MAX_EDGES, "Edges", "Number of polygon edges", MIN_EDGES, MAX_EDGES);
+ prop = RNA_def_int(ot->srna, "edges", 4, MIN_EDGES, MAX_EDGES, "Edges", "Number of polygon edges", MIN_EDGES, MAX_EDGES);
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
RNA_def_enum(ot->srna, "type", primitive_type, GP_STROKE_BOX, "Type", "Type of shape");
prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
-
-/* *************************************************************** */
diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h
index 01f0b06c178..bf205729a72 100644
--- a/source/blender/makesdna/DNA_color_types.h
+++ b/source/blender/makesdna/DNA_color_types.h
@@ -103,6 +103,7 @@ typedef enum eCurveMappingPreset {
CURVE_PRESET_ROUND = 5,
CURVE_PRESET_ROOT = 6,
CURVE_PRESET_GAUSS = 7,
+ CURVE_PRESET_BELL = 8,
} eCurveMappingPreset;
/* CurveMapping->tone */
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 43ce3649db3..934ef4a4829 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -48,6 +48,13 @@ struct MDeformVert;
/* ***************************************** */
/* GP Stroke Points */
+/* 'Control Point' data for primitives and curves */
+typedef struct bGPDcontrolpoint {
+ float x, y, z; /* x and y coordinates of control point */
+ float color[4]; /* point color */
+ int size; /* radius */
+} bGPDcontrolpoint;
+
/* Grease-Pencil Annotations - 'Stroke Point'
* -> Coordinates may either be 2d or 3d depending on settings at the time
* -> Coordinates of point on stroke, in proportions of window size
@@ -345,6 +352,10 @@ typedef struct bGPdata_Runtime {
short sbuffer_size; /* number of elements currently in cache */
short sbuffer_sflag; /* flags for stroke that cache represents */
char pad_[6];
+
+ int tot_cp_points; /* number of control-points for stroke */
+ char pad1_[4];
+ bGPDcontrolpoint *cp_points; /* array of control-points for stroke */
} bGPdata_Runtime;
/* grid configuration */
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index c1853ce1745..b06ab596059 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1035,6 +1035,7 @@ typedef struct GP_Sculpt_Settings {
int weighttype; /* eGP_Sculpt_Types (weight paint) */
char pad[4];
struct CurveMapping *cur_falloff; /* multiframe edit falloff effect by frame */
+ struct CurveMapping *cur_primitive; /* Curve used for primitve tools */
} GP_Sculpt_Settings;
/* GP_Sculpt_Settings.flag */
@@ -1053,6 +1054,8 @@ typedef enum eGP_Sculpt_SettingsFlag {
GP_SCULPT_SETT_FLAG_FRAME_FALLOFF = (1 << 5),
/* apply brush to uv data */
GP_SCULPT_SETT_FLAG_APPLY_UV = (1 << 6),
+ /* apply primitve curve */
+ GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE = (1 << 7),
} eGP_Sculpt_SettingsFlag;
/* Settings for GP Interpolation Operators */
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 8ee542fcc51..1086abd0c6f 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -69,11 +69,11 @@ const EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[] = {
{GP_SCULPT_TYPE_SMOOTH, "SMOOTH", ICON_GPBRUSH_SMOOTH, "Smooth", "Smooth stroke points"},
{GP_SCULPT_TYPE_THICKNESS, "THICKNESS", ICON_GPBRUSH_THICKNESS, "Thickness", "Adjust thickness of strokes"},
{GP_SCULPT_TYPE_STRENGTH, "STRENGTH", ICON_GPBRUSH_STRENGTH, "Strength", "Adjust color strength of strokes" },
+ {GP_SCULPT_TYPE_RANDOMIZE, "RANDOMIZE", ICON_GPBRUSH_RANDOMIZE, "Randomize", "Introduce jitter/randomness into strokes"},
{GP_SCULPT_TYPE_GRAB, "GRAB", ICON_GPBRUSH_GRAB, "Grab", "Translate the set of points initially within the brush circle" },
{GP_SCULPT_TYPE_PUSH, "PUSH", ICON_GPBRUSH_PUSH, "Push", "Move points out of the way, as if combing them"},
{GP_SCULPT_TYPE_TWIST, "TWIST", ICON_GPBRUSH_TWIST, "Twist", "Rotate points around the midpoint of the brush"},
{GP_SCULPT_TYPE_PINCH, "PINCH", ICON_GPBRUSH_PINCH, "Pinch", "Pull points towards the midpoint of the brush"},
- {GP_SCULPT_TYPE_RANDOMIZE, "RANDOMIZE", ICON_GPBRUSH_RANDOMIZE, "Randomize", "Introduce jitter/randomness into strokes"},
{GP_SCULPT_TYPE_CLONE, "CLONE", ICON_GPBRUSH_CLONE, "Clone", "Paste copies of the strokes stored on the clipboard"},
{ 0, NULL, 0, NULL, NULL }
};
@@ -1250,6 +1250,12 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna)
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+ prop = RNA_def_property(srna, "use_thickness_curve", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE);
+ RNA_def_property_ui_text(prop, "Use Curve", "Use curve to define primitive stroke thickness");
+ RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
/* custom falloff curve */
prop = RNA_def_property(srna, "multiframe_falloff_curve", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "cur_falloff");
@@ -1259,6 +1265,15 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna)
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+ /* custom primitive curve */
+ prop = RNA_def_property(srna, "thickness_primitive_curve", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "cur_primitive");
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_ui_text(prop, "Curve",
+ "Custom curve to control primitive thickness");
+ RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
/* lock axis */
prop = RNA_def_property(srna, "lock_axis", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "lock_axis");