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:
authorAntonio Vazquez <blendergit@gmail.com>2022-08-18 17:19:03 +0300
committerAntonio Vazquez <blendergit@gmail.com>2022-08-18 17:30:27 +0300
commitaa7b2f1dd9f76b54be15c7fcd23bc7981f29f13e (patch)
treefefe15094c7b994a9e684943aaaf1a20e08e350b
parent4d4a84bbeb95388c8a968441ae089428a2acd39d (diff)
GPencil: New operator to convert strokes to perimeter.
This operator converts any stroke of gpencil with a center line into a stroke with the perimeter. It's possible to assign the active material, keep current or create a new material for all perimeters. The conversion is only done for strokes with a material using `Stroke`. Only `Fill` strokes are not converted. Known issues: As the perimter has not boolean implementation, some perimeters can be overlaped. This could be solved in the future when a new 2D boolean library will be developed. Reviewed By: mendio, pepeland, frogstomp Differential Revision: https://developer.blender.org/D15664
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py3
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c263
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h1
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c1
4 files changed, 268 insertions, 0 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 6f52fbcdec9..1c661cf75ec 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -5178,6 +5178,9 @@ class VIEW3D_MT_edit_gpencil_stroke(Menu):
layout.separator()
layout.operator("gpencil.reset_transform_fill", text="Reset Fill Transform")
+ layout.separator()
+ layout.operator("gpencil.stroke_outline", text="Outline")
+
class VIEW3D_MT_edit_gpencil_point(Menu):
bl_label = "Point"
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index c05ab8c6b28..64696c38997 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -3960,6 +3960,269 @@ static int gpencil_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
+/* -------------------------------------------------------------------- */
+/** \name Stroke Perimeter from View Operator
+ * \{ */
+
+enum {
+ GP_PERIMETER_VIEW = 0,
+ GP_PERIMETER_FRONT = 1,
+ GP_PERIMETER_SIDE = 2,
+ GP_PERIMETER_TOP = 3,
+ GP_PERIMETER_CAMERA = 4,
+};
+
+enum {
+ GP_STROKE_USE_ACTIVE_MATERIAL = 0,
+ GP_STROKE_USE_CURRENT_MATERIAL = 1,
+ GP_STROKE_USE_NEW_MATERIAL = 2,
+};
+
+static int gpencil_stroke_outline_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+ const int subdivisions = RNA_int_get(op->ptr, "subdivisions");
+ const float length = RNA_float_get(op->ptr, "length");
+
+ const int view_mode = RNA_enum_get(op->ptr, "view_mode");
+ const int mode = RNA_enum_get(op->ptr, "mode");
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bool changed = false;
+
+ float viewmat[4][4], viewinv[4][4];
+ copy_m4_m4(viewmat, rv3d->viewmat);
+ copy_m4_m4(viewinv, rv3d->viewinv);
+
+ switch (view_mode) {
+ case GP_PERIMETER_FRONT:
+ unit_m4(rv3d->viewmat);
+ rv3d->viewmat[1][1] = 0.0f;
+ rv3d->viewmat[1][2] = -1.0f;
+
+ rv3d->viewmat[2][1] = 1.0f;
+ rv3d->viewmat[2][2] = 0.0f;
+
+ rv3d->viewmat[3][2] = -10.0f;
+ invert_m4_m4(rv3d->viewinv, rv3d->viewmat);
+ break;
+ case GP_PERIMETER_SIDE:
+ zero_m4(rv3d->viewmat);
+ rv3d->viewmat[0][2] = 1.0f;
+ rv3d->viewmat[1][0] = 1.0f;
+ rv3d->viewmat[2][1] = 1.0f;
+ rv3d->viewmat[3][3] = 1.0f;
+ invert_m4_m4(rv3d->viewinv, rv3d->viewmat);
+ break;
+ case GP_PERIMETER_TOP:
+ unit_m4(rv3d->viewmat);
+ unit_m4(rv3d->viewinv);
+ break;
+ case GP_PERIMETER_CAMERA: {
+ Scene *scene = CTX_data_scene(C);
+ Object *cam_ob = scene->camera;
+ if (cam_ob != NULL) {
+ invert_m4_m4(rv3d->viewmat, cam_ob->obmat);
+ copy_m4_m4(rv3d->viewinv, cam_ob->obmat);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Untag strokes to be sure nothing is pending. */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ gps->flag &= ~GP_STROKE_TAG;
+ }
+ }
+ }
+ /* Create a new material. */
+ int mat_idx = 0;
+ if (mode == GP_STROKE_USE_NEW_MATERIAL) {
+ Material *ma = BKE_gpencil_object_material_new(bmain, ob, "Material", NULL);
+ MaterialGPencilStyle *gp_style = ma->gp_style;
+
+ gp_style->flag |= GP_MATERIAL_FILL_SHOW;
+ mat_idx = ob->totcol - 1;
+ }
+
+ /* loop all selected strokes */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ if (gpl->flag & GP_LAYER_HIDE) {
+ continue;
+ }
+ /* Prepare transform matrix. */
+ float diff_mat[4][4];
+ BKE_gpencil_layer_transform_matrix_get(depsgraph, ob, gpl, diff_mat);
+
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+
+ 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 (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ if ((gps->flag & GP_STROKE_SELECT) == 0) {
+ continue;
+ }
+ if (gps->totpoints == 0) {
+ continue;
+ }
+ if (!ED_gpencil_stroke_material_visible(ob, gps)) {
+ continue;
+ }
+
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
+ const bool is_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0);
+
+ if (!is_stroke) {
+ continue;
+ }
+
+ /* Duplicate the stroke to apply any layer thickness change. */
+ bGPDstroke *gps_duplicate = BKE_gpencil_stroke_duplicate(gps, true, false);
+
+ /* Apply layer thickness change. */
+ gps_duplicate->thickness += gpl->line_change;
+ /* Apply object scale to thickness. */
+ gps_duplicate->thickness *= mat4_to_scale(ob->obmat);
+ CLAMP_MIN(gps_duplicate->thickness, 1.0f);
+
+ /* Stroke. */
+ bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
+ rv3d, gpd, gpl, gps_duplicate, subdivisions, diff_mat);
+ gps_perimeter->flag &= ~GP_STROKE_SELECT;
+ /* Assign material. */
+ switch (mode) {
+ case GP_STROKE_USE_ACTIVE_MATERIAL: {
+ if (ob->actcol - 1 < 0) {
+ gps_perimeter->mat_nr = 0;
+ }
+ else {
+ gps_perimeter->mat_nr = ob->actcol - 1;
+ }
+ break;
+ }
+ case GP_STROKE_USE_CURRENT_MATERIAL:
+ gps_perimeter->mat_nr = gps_perimeter->mat_nr;
+ break;
+ case GP_STROKE_USE_NEW_MATERIAL:
+ gps_perimeter->mat_nr = mat_idx;
+ break;
+ default:
+ break;
+ }
+
+ /* Sample stroke. */
+ if (length > 0.0f) {
+ BKE_gpencil_stroke_sample(gpd, gps_perimeter, length, false, 0);
+ }
+
+ /* Set pressure constant. */
+ bGPDspoint *pt;
+ for (int i = 0; i < gps_perimeter->totpoints; i++) {
+ pt = &gps_perimeter->points[i];
+ pt->pressure = 1.0f;
+ }
+
+ /* Add perimeter stroke to frame. */
+ BLI_insertlinkafter(&gpf->strokes, gps, gps_perimeter);
+
+ /* Tag original stroke to be removed. */
+ gps->flag |= GP_STROKE_TAG;
+
+ /* Free Temp stroke. */
+ BKE_gpencil_free_stroke(gps_duplicate);
+ changed = true;
+ }
+
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
+ }
+ }
+ /* Free old strokes. */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->flag & GP_STROKE_TAG) {
+ BLI_remlink(&gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+ }
+ }
+ }
+ }
+
+ /* Back to view matrix. */
+ copy_m4_m4(rv3d->viewmat, viewmat);
+ copy_m4_m4(rv3d->viewinv, viewinv);
+
+ if (changed) {
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_outline(wmOperatorType *ot)
+{
+ static const EnumPropertyItem view_mode[] = {
+ {GP_PERIMETER_VIEW, "VIEW", 0, "View", ""},
+ {GP_PERIMETER_FRONT, "FRONT", 0, "Front", ""},
+ {GP_PERIMETER_SIDE, "SIDE", 0, "Side", ""},
+ {GP_PERIMETER_TOP, "TOP", 0, "Top", ""},
+ {GP_PERIMETER_CAMERA, "CAMERA", 0, "Camera", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+ static const EnumPropertyItem material_mode[] = {
+ {GP_STROKE_USE_ACTIVE_MATERIAL, "ACTIVE", 0, "Active Material", ""},
+ {GP_STROKE_USE_CURRENT_MATERIAL, "KEEP", 0, "Keep Material", "Keep current stroke material"},
+ {GP_STROKE_USE_NEW_MATERIAL, "NEW", 0, "New Material", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Convert Stroke to Outline";
+ ot->idname = "GPENCIL_OT_stroke_outline";
+ ot->description = "Convert stroke to perimeter";
+
+ /* api callbacks */
+ ot->exec = gpencil_stroke_outline_exec;
+ ot->poll = gpencil_stroke_edit_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "view_mode", view_mode, GP_PERIMETER_VIEW, "View", "");
+ RNA_def_enum(
+ ot->srna, "mode", material_mode, GP_STROKE_USE_ACTIVE_MATERIAL, "Material Mode", "");
+
+ RNA_def_int(ot->srna, "subdivisions", 3, 0, 10, "Subdivisions", "", 0, 10);
+
+ RNA_def_float(ot->srna, "length", 0.0f, 0.0f, 100.0f, "Sample Length", "", 0.0f, 100.0f);
+}
+
+/** \} */
+
void GPENCIL_OT_recalc_geometry(wmOperatorType *ot)
{
/* identifiers */
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index d656241c463..3cb3a50e702 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -608,6 +608,7 @@ void GPENCIL_OT_stroke_merge_by_distance(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_merge_material(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_reset_vertex_color(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_normalize(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_outline(struct wmOperatorType *ot);
void GPENCIL_OT_material_to_vertex_color(struct wmOperatorType *ot);
void GPENCIL_OT_extract_palette_vertex(struct wmOperatorType *ot);
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index 99e28270c3e..3d92fbabfc4 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -635,6 +635,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_stroke_merge_material);
WM_operatortype_append(GPENCIL_OT_stroke_reset_vertex_color);
WM_operatortype_append(GPENCIL_OT_stroke_normalize);
+ WM_operatortype_append(GPENCIL_OT_stroke_outline);
WM_operatortype_append(GPENCIL_OT_material_to_vertex_color);
WM_operatortype_append(GPENCIL_OT_extract_palette_vertex);