From 8df5e0eb66f7f222f182205ff7ff32f765f6cb27 Mon Sep 17 00:00:00 2001 From: Antonioya Date: Mon, 4 Mar 2019 19:31:36 +0100 Subject: GPencil: New extrude operator Keymap: E - Extrude Gizmo supported. If the extrude points are the first or last of the stroke, the stroke is extended. If the extrude points are in the middle of the stroke, a new stroke is created because the grease pencil strokes can be only with 2 extremes and fold the stroke to get a new point gets very bad results. Still pending define a new icon. Also, it could be good to set by default XYZ axis in the gizmo. Note: There is a change in the transform_gizmo_extrude_3d.c gizmo for OB_MODE_EDIT_GPENCIL. This change must be undo when the mode will be integrated into OB_MODE_EDIT, but while we have both modes, we need to keep this code in order to keep running the gizmo. --- source/blender/editors/gpencil/gpencil_edit.c | 213 +++++++++++++++++++++ source/blender/editors/gpencil/gpencil_intern.h | 1 + source/blender/editors/gpencil/gpencil_ops.c | 8 + .../editors/transform/transform_gizmo_extrude_3d.c | 8 +- 4 files changed, 229 insertions(+), 1 deletion(-) (limited to 'source') diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 4ba74582774..c9a2cd5907f 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -754,6 +754,219 @@ void GPENCIL_OT_duplicate(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ************** Extrude Selected Strokes **************** */ + +/* helper to copy a point to temp area */ +static void copy_point(bGPDstroke *gps, + bGPDspoint *temp_points, + MDeformVert *temp_dverts, + int from_idx, int to_idx) +{ + bGPDspoint *pt = &temp_points[from_idx]; + bGPDspoint *pt_final = &gps->points[to_idx]; + + copy_v3_v3(&pt_final->x, &pt->x); + pt_final->pressure = pt->pressure; + pt_final->strength = pt->strength; + pt_final->time = pt->time; + pt_final->flag = pt->flag; + pt_final->uv_fac = pt->uv_fac; + pt_final->uv_rot = pt->uv_rot; + + if (gps->dvert != NULL) { + MDeformVert *dvert = &temp_dverts[from_idx]; + MDeformVert *dvert_final = &gps->dvert[to_idx]; + + dvert_final->totweight = dvert->totweight; + dvert_final->dw = dvert->dw; + } +} + +static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps) +{ + bGPDspoint *temp_points = NULL; + MDeformVert *temp_dverts = NULL; + bGPDspoint *pt = NULL; + const bGPDspoint *pt_start = &gps->points[0]; + const bGPDspoint *pt_last = &gps->points[gps->totpoints - 1]; + const bool do_first = (pt_start->flag & GP_SPOINT_SELECT); + const bool do_last = ((pt_last->flag & GP_SPOINT_SELECT) && (pt_start != pt_last)); + const bool do_stroke = (do_first || do_last); + + /* review points in the midle of stroke to create new strokes */ + for (int i = 0; i < gps->totpoints; i++) { + /* skip first and last point */ + if ((i == 0) || (i == gps->totpoints - 1)) { + continue; + } + + pt = &gps->points[i]; + if (pt->flag == GP_SPOINT_SELECT) { + /* duplicate original stroke data */ + bGPDstroke *gps_new = MEM_dupallocN(gps); + gps_new->prev = gps_new->next = NULL; + + /* add new points array */ + gps_new->totpoints = 1; + gps_new->points = MEM_callocN(sizeof(bGPDspoint), __func__); + gps_new->dvert = NULL; + + if (gps->dvert != NULL) { + gps_new->dvert = MEM_callocN(sizeof(MDeformVert), __func__); + } + + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps_new->triangles = NULL; + gps_new->tot_triangles = 0; + BLI_insertlinkafter(&gpf->strokes, gps, gps_new); + + /* copy selected point data to new stroke */ + copy_point(gps_new, gps->points, gps->dvert, i, 0); + + /* deselect orinal point */ + pt->flag &= ~GP_SPOINT_SELECT; + } + } + + /* review first and last point to reuse same stroke */ + int i2 = 0; + int totnewpoints, oldtotpoints; + /* if first or last, reuse stroke and resize */ + if ((do_first) || (do_last)) { + totnewpoints = gps->totpoints; + if (do_first) { + totnewpoints++; + } + if (do_last) { + totnewpoints++; + } + + /* duplicate points in a temp area */ + temp_points = MEM_dupallocN(gps->points); + oldtotpoints = gps->totpoints; + if (gps->dvert != NULL) { + temp_dverts = MEM_dupallocN(gps->dvert); + } + + /* if first point, need move all one position */ + if (do_first) { + i2 = 1; + } + + /* resize the points arrays */ + gps->totpoints = totnewpoints; + gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + if (gps->dvert != NULL) { + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + } + + /* move points to new position */ + for (int i = 0; i < oldtotpoints; i++) { + copy_point(gps, temp_points, temp_dverts, i, i2); + i2++; + } + gps->flag |= GP_STROKE_RECALC_GEOMETRY; + + /* if first point, add new point at the begining */ + if (do_first) { + copy_point(gps, temp_points, temp_dverts, 0, 0); + /* deselect old */ + pt = &gps->points[1]; + pt->flag &= ~GP_SPOINT_SELECT; + /* select new */ + pt = &gps->points[0]; + pt->flag |= GP_SPOINT_SELECT; + } + + /* if last point, add new point at the end */ + if (do_last) { + copy_point(gps, temp_points, temp_dverts, + oldtotpoints - 1, gps->totpoints - 1); + + /* deselect old */ + pt = &gps->points[gps->totpoints - 2]; + pt->flag &= ~GP_SPOINT_SELECT; + /* select new */ + pt = &gps->points[gps->totpoints - 1]; + pt->flag |= GP_SPOINT_SELECT; + } + + MEM_SAFE_FREE(temp_points); + MEM_SAFE_FREE(temp_dverts); + } + + /* if the stroke is not reused, deselect */ + if (!do_stroke) { + gps->flag &= ~GP_STROKE_SELECT; + } +} + +static int gp_extrude_exec(bContext *C, wmOperator *op) +{ + Object *obact = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)obact->data; + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + bGPDstroke *gps = NULL; + + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); + return OPERATOR_CANCELLED; + } + + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) + continue; + + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + if (gps->flag & GP_STROKE_SELECT) { + gpencil_add_move_points(gpf, gps); + } + } + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + } + CTX_DATA_END; + + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_extrude(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Extrude Stroke Points"; + ot->idname = "GPENCIL_OT_extrude"; + ot->description = "Extrude the selected Grease Pencil points"; + + /* callbacks */ + ot->exec = gp_extrude_exec; + ot->poll = gp_stroke_edit_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + /* ******************* Copy/Paste Strokes ************************* */ /* Grease Pencil stroke data copy/paste buffer: * - The copy operation collects all segments of selected strokes, diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 9341133d520..4fba83a5f02 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -389,6 +389,7 @@ void GPENCIL_OT_delete(struct wmOperatorType *ot); void GPENCIL_OT_dissolve(struct wmOperatorType *ot); void GPENCIL_OT_copy(struct wmOperatorType *ot); void GPENCIL_OT_paste(struct wmOperatorType *ot); +void GPENCIL_OT_extrude(struct wmOperatorType *ot); void GPENCIL_OT_move_to_layer(struct wmOperatorType *ot); void GPENCIL_OT_layer_change(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 60b2fb5d64e..e3b13b26672 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -254,6 +254,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_dissolve); WM_operatortype_append(GPENCIL_OT_copy); WM_operatortype_append(GPENCIL_OT_paste); + WM_operatortype_append(GPENCIL_OT_extrude); WM_operatortype_append(GPENCIL_OT_move_to_layer); WM_operatortype_append(GPENCIL_OT_layer_change); @@ -361,6 +362,13 @@ void ED_operatormacros_gpencil(void) otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); RNA_boolean_set(otmacro->ptr, "gpencil_strokes", true); + /* Extrude + Move = Interactively add new points */ + ot = WM_operatortype_append_macro("GPENCIL_OT_extrude_move", "Extrude Stroke Points", + "Extrude selected points and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); + WM_operatortype_macro_define(ot, "GPENCIL_OT_extrude"); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_boolean_set(otmacro->ptr, "gpencil_strokes", true); } /* ****************************************** */ diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c index b1ea7b87efb..81f4ec8c30a 100644 --- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c +++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c @@ -147,7 +147,13 @@ static void gizmo_mesh_extrude_setup(const bContext *C, wmGizmoGroup *gzgroup) { const Object *obedit = CTX_data_edit_object(C); const char *op_idname = NULL; - if (obedit->type == OB_MESH) { + /* grease pencil does not use obedit */ + /* GPXX: Remove if OB_MODE_EDIT_GPENCIL is merged with OB_MODE_EDIT */ + const Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + op_idname = "GPENCIL_OT_extrude_move"; + } + else if (obedit->type == OB_MESH) { op_idname = "MESH_OT_extrude_context_move"; ggd->normal_axis = 2; } -- cgit v1.2.3