diff options
author | Falk David <falkdavid@gmx.de> | 2020-11-13 23:43:00 +0300 |
---|---|---|
committer | Falk David <falkdavid@gmx.de> | 2020-11-13 23:43:00 +0300 |
commit | 0be88c7d15d2ad1af284c6283370173647ae74eb (patch) | |
tree | 5fff573c512e284547ebe0c921ecffdae2c377c4 /source/blender/draw | |
parent | 9d28353b525ecfbcca1501be72e4276dfb2bbc2a (diff) |
GPencil: Merge GSoC curve edit mode
Differential Revision: https://developer.blender.org/D8660
This patch is the result of the GSoC 2020 "Editing Grease Pencil Strokes
Using Curves" project. It adds a submode to greasepencil edit mode that
allows for the transformation of greasepencil strokes using bezier
curves. More information about the project can be found
here: https://wiki.blender.org/wiki/User:Filedescriptor/GSoC_2020.
Diffstat (limited to 'source/blender/draw')
6 files changed, 254 insertions, 3 deletions
diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c index ccc914e0422..7f9290a6c3a 100644 --- a/source/blender/draw/engines/overlay/overlay_gpencil.c +++ b/source/blender/draw/engines/overlay/overlay_gpencil.c @@ -49,6 +49,10 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata) pd->edit_gpencil_wires_grp = NULL; psl->edit_gpencil_ps = NULL; + pd->edit_gpencil_curve_handle_grp = NULL; + pd->edit_gpencil_curve_points_grp = NULL; + psl->edit_gpencil_curve_ps = NULL; + const DRWContextState *draw_ctx = DRW_context_state_get(); View3D *v3d = draw_ctx->v3d; Object *ob = draw_ctx->obact; @@ -105,7 +109,8 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata) (GPENCIL_EDIT_MODE(gpd) && (ts->gpencil_selectmode_edit != GP_SELECTMODE_STROKE)); - if ((!GPENCIL_VERTEX_MODE(gpd) && !GPENCIL_PAINT_MODE(gpd)) || use_vertex_mask) { + if ((!GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) && + ((!GPENCIL_VERTEX_MODE(gpd) && !GPENCIL_PAINT_MODE(gpd)) || use_vertex_mask)) { DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA; DRW_PASS_CREATE(psl->edit_gpencil_ps, state | pd->clipping_state); @@ -132,6 +137,37 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata) } } + /* Handles and curve point for Curve Edit submode. */ + if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) { + DRWState state = DRW_STATE_WRITE_COLOR; + DRW_PASS_CREATE(psl->edit_gpencil_curve_ps, state | pd->clipping_state); + + /* Edit lines. */ + if (show_lines) { + sh = OVERLAY_shader_edit_gpencil_wire(); + pd->edit_gpencil_wires_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_curve_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "doMultiframe", show_multi_edit_lines); + DRW_shgroup_uniform_bool_copy(grp, "doWeightColor", is_weight_paint); + DRW_shgroup_uniform_bool_copy(grp, "hideSelect", hide_select); + DRW_shgroup_uniform_float_copy(grp, "gpEditOpacity", v3d->vertex_opacity); + DRW_shgroup_uniform_texture(grp, "weightTex", G_draw.weight_ramp); + } + + sh = OVERLAY_shader_edit_curve_handle(); + pd->edit_gpencil_curve_handle_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_curve_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "showCurveHandles", pd->edit_curve.show_handles); + DRW_shgroup_uniform_int_copy(grp, "curveHandleDisplay", pd->edit_curve.handle_display); + DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA); + + sh = OVERLAY_shader_edit_curve_point(); + pd->edit_gpencil_curve_points_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_curve_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "showCurveHandles", pd->edit_curve.show_handles); + DRW_shgroup_uniform_int_copy(grp, "curveHandleDisplay", pd->edit_curve.handle_display); + } + /* control points for primitives and speed guide */ const bool is_cppoint = (gpd->runtime.tot_cp_points > 0); const bool is_speed_guide = (ts->gp_sculpt.guide.use_guide && @@ -182,6 +218,7 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata) void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) { OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; struct GPUShader *sh; DRWShadingGroup *grp; @@ -196,6 +233,9 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) ToolSettings *ts = scene->toolsettings; const View3DCursor *cursor = &scene->cursor; + pd->edit_curve.show_handles = v3d->overlay.handle_display != CURVE_HANDLE_NONE; + pd->edit_curve.handle_display = v3d->overlay.handle_display; + if (gpd == NULL || ob->type != OB_GPENCIL) { return; } @@ -303,6 +343,20 @@ static void OVERLAY_edit_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob struct GPUBatch *geom = DRW_cache_gpencil_edit_points_get(ob, pd->cfra); DRW_shgroup_call_no_cull(grp, geom, ob); } + + if (pd->edit_gpencil_curve_handle_grp) { + struct GPUBatch *geom = DRW_cache_gpencil_edit_curve_handles_get(ob, pd->cfra); + if (geom) { + DRW_shgroup_call_no_cull(pd->edit_gpencil_curve_handle_grp, geom, ob); + } + } + + if (pd->edit_gpencil_curve_points_grp) { + struct GPUBatch *geom = DRW_cache_gpencil_edit_curve_points_get(ob, pd->cfra); + if (geom) { + DRW_shgroup_call_no_cull(pd->edit_gpencil_curve_points_grp, geom, ob); + } + } } static void overlay_gpencil_draw_stroke_color_name(bGPDlayer *UNUSED(gpl), @@ -407,4 +461,9 @@ void OVERLAY_edit_gpencil_draw(OVERLAY_Data *vedata) if (psl->edit_gpencil_ps) { DRW_draw_pass(psl->edit_gpencil_ps); } + + /* Curve edit handles. */ + if (psl->edit_gpencil_curve_ps) { + DRW_draw_pass(psl->edit_gpencil_curve_ps); + } } diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index c259d9351f3..b9d591a5642 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -77,6 +77,7 @@ typedef struct OVERLAY_PassList { DRWPass *edit_curve_handle_ps; DRWPass *edit_gpencil_ps; DRWPass *edit_gpencil_gizmos_ps; + DRWPass *edit_gpencil_curve_ps; DRWPass *edit_lattice_ps; DRWPass *edit_mesh_depth_ps[2]; DRWPass *edit_mesh_verts_ps[2]; @@ -252,6 +253,8 @@ typedef struct OVERLAY_PrivateData { DRWShadingGroup *edit_lattice_wires_grp; DRWShadingGroup *edit_gpencil_points_grp; DRWShadingGroup *edit_gpencil_wires_grp; + DRWShadingGroup *edit_gpencil_curve_handle_grp; + DRWShadingGroup *edit_gpencil_curve_points_grp; DRWShadingGroup *edit_mesh_depth_grp[2]; DRWShadingGroup *edit_mesh_faces_grp[2]; DRWShadingGroup *edit_mesh_faces_cage_grp[2]; diff --git a/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl b/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl index 9311542a79e..442f69aec7e 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl @@ -53,6 +53,9 @@ void main() bool edge_selected = (((vertFlag[1] | vertFlag[0]) & VERT_SELECTED) != 0); bool handle_selected = (showCurveHandles && (((vertFlag[1] | vertFlag[0]) & VERT_SELECTED_BEZT_HANDLE) != 0)); + /* It reuses freestyle flag because the flag is 8 bits and all are already used and this + * flag is not used in this context. */ + bool is_gpencil = ((vertFlag[1] & EDGE_FREESTYLE) != 0); /* If handle type is only selected and the edge is not selected, don't show. */ if ((curveHandleDisplay != CURVE_HANDLE_ALL) && (!handle_selected)) { @@ -61,6 +64,9 @@ void main() if ((!is_u_segment) && (color_id <= 4)) { return; } + if (is_gpencil) { + return; + } } vec4 inner_color; diff --git a/source/blender/draw/engines/overlay/shaders/edit_curve_point_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_curve_point_vert.glsl index a811fcca0d4..6b4edbfe578 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_curve_point_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_curve_point_vert.glsl @@ -17,16 +17,17 @@ void main() { GPU_INTEL_VERTEX_SHADER_WORKAROUND + /* Reuse the FREESTYLE flag to determine is GPencil. */ if ((data & VERT_SELECTED) != 0) { if ((data & VERT_ACTIVE) != 0) { finalColor = colorEditMeshActive; } else { - finalColor = colorVertexSelect; + finalColor = ((data & EDGE_FREESTYLE) == 0) ? colorVertexSelect : colorGpencilVertexSelect; } } else { - finalColor = colorVertex; + finalColor = ((data & EDGE_FREESTYLE) == 0) ? colorVertex : colorGpencilVertex; } vec3 world_pos = point_object_to_world(pos); diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index afbf9903dbc..9fd7ffd4692 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -253,6 +253,8 @@ struct GPUBatch *DRW_cache_gpencil_strokes_get(struct Object *ob, int cfra); struct GPUBatch *DRW_cache_gpencil_fills_get(struct Object *ob, int cfra); struct GPUBatch *DRW_cache_gpencil_edit_lines_get(struct Object *ob, int cfra); struct GPUBatch *DRW_cache_gpencil_edit_points_get(struct Object *ob, int cfra); +struct GPUBatch *DRW_cache_gpencil_edit_curve_handles_get(struct Object *ob, int cfra); +struct GPUBatch *DRW_cache_gpencil_edit_curve_points_get(struct Object *ob, int cfra); struct GPUBatch *DRW_cache_gpencil_sbuffer_stroke_get(struct Object *ob); struct GPUBatch *DRW_cache_gpencil_sbuffer_fill_get(struct Object *ob); diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c index 220c7041c7f..0a80f7df877 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -21,6 +21,7 @@ * \ingroup draw */ +#include "DNA_curve_types.h" #include "DNA_gpencil_types.h" #include "DNA_meshdata_types.h" #include "DNA_screen_types.h" @@ -43,6 +44,9 @@ #include "draw_cache.h" #include "draw_cache_impl.h" +#define BEZIER_HANDLE 1 << 3 +#define COLOR_SHIFT 5 + /* ---------------------------------------------------------------------- */ typedef struct GpencilBatchCache { /** Instancing Data */ @@ -59,6 +63,10 @@ typedef struct GpencilBatchCache { GPUVertBuf *edit_vbo; GPUBatch *edit_lines_batch; GPUBatch *edit_points_batch; + /** Edit Curve Mode */ + GPUVertBuf *edit_curve_vbo; + GPUBatch *edit_curve_handles_batch; + GPUBatch *edit_curve_points_batch; /** Cache is dirty */ bool is_dirty; @@ -123,6 +131,10 @@ static void gpencil_batch_cache_clear(GpencilBatchCache *cache) GPU_BATCH_DISCARD_SAFE(cache->edit_points_batch); GPU_VERTBUF_DISCARD_SAFE(cache->edit_vbo); + GPU_BATCH_DISCARD_SAFE(cache->edit_curve_handles_batch); + GPU_BATCH_DISCARD_SAFE(cache->edit_curve_points_batch); + GPU_VERTBUF_DISCARD_SAFE(cache->edit_curve_vbo); + cache->is_dirty = true; } @@ -197,6 +209,23 @@ static GPUVertFormat *gpencil_edit_stroke_format(void) } /* MUST match the format below. */ +typedef struct gpEditCurveVert { + float pos[3]; + int data; +} gpEditCurveVert; + +static GPUVertFormat *gpencil_edit_curve_format(void) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* initialize vertex formats */ + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 1, GPU_FETCH_INT); + } + return &format; +} + +/* MUST match the format below. */ typedef struct gpColorVert { float vcol[4]; /* Vertex color */ float fcol[4]; /* Fill color */ @@ -228,6 +257,7 @@ typedef struct gpIterData { GPUIndexBufBuilder ibo; int vert_len; int tri_len; + int curve_len; } gpIterData; static GPUVertBuf *gpencil_dummy_buffer_get(void) @@ -383,6 +413,7 @@ static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfr .ibo = {0}, .vert_len = 1, /* Start at 1 for the gl_InstanceID trick to work (see vert shader). */ .tri_len = 0, + .curve_len = 0, }; BKE_gpencil_visible_stroke_iter( NULL, ob, NULL, gpencil_object_verts_count_cb, &iter, do_onion, cfra); @@ -653,6 +684,11 @@ typedef struct gpEditIterData { int vgindex; } gpEditIterData; +typedef struct gpEditCurveIterData { + gpEditCurveVert *verts; + int vgindex; +} gpEditCurveIterData; + static uint32_t gpencil_point_edit_flag(const bool layer_lock, const bGPDspoint *pt, int v, @@ -698,6 +734,92 @@ static void gpencil_edit_stroke_iter_cb(bGPDlayer *gpl, vert_ptr->weight = gpencil_point_edit_weight(dvert, 0, iter->vgindex); } +static void gpencil_edit_curve_stroke_count_cb(bGPDlayer *gpl, + bGPDframe *UNUSED(gpf), + bGPDstroke *gps, + void *thunk) +{ + if (gpl->flag & GP_LAYER_LOCKED) { + return; + } + + gpIterData *iter = (gpIterData *)thunk; + + if (gps->editcurve == NULL) { + return; + } + + /* Store first index offset */ + gps->runtime.curve_start = iter->curve_len; + iter->curve_len += gps->editcurve->tot_curve_points * 4; +} + +static char gpencil_beztriple_vflag_get(char flag, + char col_id, + bool handle_point, + const bool handle_selected) +{ + char vflag = 0; + SET_FLAG_FROM_TEST(vflag, (flag & SELECT), VFLAG_VERT_SELECTED); + SET_FLAG_FROM_TEST(vflag, handle_point, BEZIER_HANDLE); + SET_FLAG_FROM_TEST(vflag, handle_selected, VFLAG_VERT_SELECTED_BEZT_HANDLE); + /* Reuse flag of Freestyle to indicate is GPencil data. */ + vflag |= VFLAG_EDGE_FREESTYLE; + + /* Handle color id. */ + vflag |= col_id << COLOR_SHIFT; + return vflag; +} + +static void gpencil_edit_curve_stroke_iter_cb(bGPDlayer *gpl, + bGPDframe *gpf, + bGPDstroke *gps, + void *thunk) +{ + if (gpl->flag & GP_LAYER_LOCKED) { + return; + } + + if (gps->editcurve == NULL) { + return; + } + bGPDcurve *editcurve = gps->editcurve; + gpEditCurveIterData *iter = (gpEditCurveIterData *)thunk; + const int v = gps->runtime.curve_start; + gpEditCurveVert *vert_ptr = iter->verts + v; + /* Hide points when the curve is unselected. Passing the control point + * as handle produces the point shader skip it if you are not in ALL mode. */ + const bool hide = !(editcurve->flag & GP_CURVE_SELECT); + + for (int i = 0; i < editcurve->tot_curve_points; i++) { + BezTriple *bezt = &editcurve->curve_points[i].bezt; + const bool handle_selected = BEZT_ISSEL_ANY(bezt); + const char vflag[3] = { + gpencil_beztriple_vflag_get(bezt->f1, bezt->h1, true, handle_selected), + gpencil_beztriple_vflag_get(bezt->f2, bezt->h1, hide, handle_selected), + gpencil_beztriple_vflag_get(bezt->f3, bezt->h2, true, handle_selected), + }; + + /* First segment. */ + copy_v3_v3(vert_ptr->pos, bezt->vec[0]); + vert_ptr->data = vflag[0]; + vert_ptr++; + + copy_v3_v3(vert_ptr->pos, bezt->vec[1]); + vert_ptr->data = vflag[1]; + vert_ptr++; + + /* Second segment. */ + copy_v3_v3(vert_ptr->pos, bezt->vec[1]); + vert_ptr->data = vflag[1]; + vert_ptr++; + + copy_v3_v3(vert_ptr->pos, bezt->vec[2]); + vert_ptr->data = vflag[2]; + vert_ptr++; + } +} + static void gpencil_edit_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfra) { bGPdata *gpd = (bGPdata *)ob->data; @@ -737,6 +859,46 @@ static void gpencil_edit_batches_ensure(Object *ob, GpencilBatchCache *cache, in cache->edit_lines_batch = GPU_batch_create(GPU_PRIM_LINE_STRIP, cache->vbo, NULL); GPU_batch_vertbuf_add(cache->edit_lines_batch, cache->edit_vbo); + } + + /* Curve Handles and Points for Editing. */ + if (cache->edit_curve_vbo == NULL) { + gpIterData iterdata = { + .gpd = gpd, + .verts = NULL, + .ibo = {0}, + .vert_len = 0, + .tri_len = 0, + .curve_len = 0, + }; + + /* Create VBO. */ + GPUVertFormat *format = gpencil_edit_curve_format(); + cache->edit_curve_vbo = GPU_vertbuf_create_with_format(format); + + /* Count data. */ + BKE_gpencil_visible_stroke_iter( + NULL, ob, NULL, gpencil_edit_curve_stroke_count_cb, &iterdata, false, cfra); + + gpEditCurveIterData iter; + int vert_len = iterdata.curve_len; + if (vert_len > 0) { + + GPU_vertbuf_data_alloc(cache->edit_curve_vbo, vert_len); + iter.verts = (gpEditCurveVert *)GPU_vertbuf_get_data(cache->edit_curve_vbo); + + /* Fill buffers with data. */ + BKE_gpencil_visible_stroke_iter( + NULL, ob, NULL, gpencil_edit_curve_stroke_iter_cb, &iter, false, cfra); + + cache->edit_curve_handles_batch = GPU_batch_create( + GPU_PRIM_LINES, cache->edit_curve_vbo, NULL); + GPU_batch_vertbuf_add(cache->edit_curve_handles_batch, cache->edit_curve_vbo); + + cache->edit_curve_points_batch = GPU_batch_create( + GPU_PRIM_POINTS, cache->edit_curve_vbo, NULL); + GPU_batch_vertbuf_add(cache->edit_curve_points_batch, cache->edit_curve_vbo); + } gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY; cache->is_dirty = false; @@ -761,4 +923,22 @@ GPUBatch *DRW_cache_gpencil_edit_points_get(Object *ob, int cfra) return cache->edit_points_batch; } +GPUBatch *DRW_cache_gpencil_edit_curve_handles_get(Object *ob, int cfra) +{ + GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra); + gpencil_batches_ensure(ob, cache, cfra); + gpencil_edit_batches_ensure(ob, cache, cfra); + + return cache->edit_curve_handles_batch; +} + +GPUBatch *DRW_cache_gpencil_edit_curve_points_get(Object *ob, int cfra) +{ + GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra); + gpencil_batches_ensure(ob, cache, cfra); + gpencil_edit_batches_ensure(ob, cache, cfra); + + return cache->edit_curve_points_batch; +} + /** \} */ |