[Bf-blender-cvs] [aa7b2f1dd9f] master: GPencil: New operator to convert strokes to perimeter.

Antonio Vazquez noreply at git.blender.org
Thu Aug 18 16:30:30 CEST 2022


Commit: aa7b2f1dd9f76b54be15c7fcd23bc7981f29f13e
Author: Antonio Vazquez
Date:   Thu Aug 18 16:19:03 2022 +0200
Branches: master
https://developer.blender.org/rBaa7b2f1dd9f76b54be15c7fcd23bc7981f29f13e

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

===================================================================

M	release/scripts/startup/bl_ui/space_view3d.py
M	source/blender/editors/gpencil/gpencil_edit.c
M	source/blender/editors/gpencil/gpencil_intern.h
M	source/blender/editors/gpencil/gpencil_ops.c

===================================================================

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/blen

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list