[Bf-blender-cvs] [7783f7ea1dc] temp-gpencil-bezier-v2: WIP: curve sculpting

Falk David noreply at git.blender.org
Fri Feb 12 01:18:20 CET 2021


Commit: 7783f7ea1dc48abe1335ca052ce0e4f4b4d361fe
Author: Falk David
Date:   Mon Feb 8 11:49:44 2021 +0100
Branches: temp-gpencil-bezier-v2
https://developer.blender.org/rB7783f7ea1dc48abe1335ca052ce0e4f4b4d361fe

WIP: curve sculpting

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

M	source/blender/blenkernel/BKE_gpencil_curve.h
M	source/blender/blenkernel/intern/gpencil_curve.c
M	source/blender/blenkernel/intern/gpencil_geom.c
M	source/blender/editors/gpencil/gpencil_sculpt_paint.c
M	source/blender/makesdna/DNA_gpencil_types.h

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

diff --git a/source/blender/blenkernel/BKE_gpencil_curve.h b/source/blender/blenkernel/BKE_gpencil_curve.h
index 0bde1dec5cd..b09c332a470 100644
--- a/source/blender/blenkernel/BKE_gpencil_curve.h
+++ b/source/blender/blenkernel/BKE_gpencil_curve.h
@@ -46,9 +46,13 @@ void BKE_gpencil_convert_curve(struct Main *bmain,
 struct bGPDcurve *BKE_gpencil_stroke_editcurve_generate(struct bGPDstroke *gps,
                                                         const float error_threshold,
                                                         const float corner_angle);
+struct bGPDcurve *BKE_gpencil_stroke_editcurve_regenerate(struct bGPDstroke *gps,
+                                                          const float error_threshold,
+                                                          const float corner_angle);
 void BKE_gpencil_stroke_editcurve_update(struct bGPDstroke *gps,
                                          const float threshold,
-                                         const float corner_angle);
+                                         const float corner_angle,
+                                         const bool do_partial_update);
 void BKE_gpencil_editcurve_stroke_sync_selection(struct bGPDstroke *gps, struct bGPDcurve *gpc);
 void BKE_gpencil_stroke_editcurve_sync_selection(struct bGPDstroke *gps, struct bGPDcurve *gpc);
 void BKE_gpencil_stroke_update_geometry_from_editcurve(struct bGPDstroke *gps,
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index 9c35404e16b..6101c562fd0 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -56,6 +56,13 @@
 
 #include "DEG_depsgraph_query.h"
 
+/* We treat pressure, strength and vertex colors as dimensions in the curve fitting like the
+ * position. But this means that e.g. a high pressure will change the shape of the
+ * higher-dimensional fitted curve which will in turn change the shape of the projected
+ * 3-dimensional curve. But we don't really want the pressure or something else than the position
+ * to influence the shape. So this COORD_FITTING_INFLUENCE will "dampen" the effect of the other
+ * attributes affecting the shape. Ideally, we fit the other attributes separate from the position.
+ */
 #define COORD_FITTING_INFLUENCE 20.0f
 
 /* -------------------------------------------------------------------- */
@@ -566,6 +573,37 @@ void BKE_gpencil_convert_curve(Main *bmain,
 /** \name Edit-Curve Kernel Functions
  * \{ */
 
+static int gpencil_count_tagged_curve_segments(bGPDstroke *gps)
+{
+  bGPDcurve *gpc = gps->editcurve;
+  /* Handle single point edgecase. */
+  if (gpc->tot_curve_points == 1) {
+    return (&gps->points[0]->flag & GP_SPOINT_TAG) ? 1 : 0;
+  }
+
+  int i, j, count = 0;
+  for (i = 0, j = 0; i < gps->totpoints; i++) {
+    bGPDspoint *pt = &gps->points[i];
+    if ((pt->flag & GP_SPOINT_TAG) == 0) {
+      if (j + 1 < gpc->tot_curve_points && i >= &gpc->curve_points[j + 1]->point_index) {
+        j++;
+      }
+      continue;
+    }
+    count++;
+
+    if (++j < gpc->tot_curve_points) {
+      bGPDcurve_point *cpt = &gpc->curve_points[j];
+      i = cpt->point_index;
+    }
+    else {
+      break;
+    }
+  }
+
+  return count;
+}
+
 static bGPDcurve *gpencil_stroke_editcurve_generate_edgecases(bGPDstroke *gps)
 {
   BLI_assert(gps->totpoints < 3);
@@ -650,6 +688,7 @@ bGPDcurve *BKE_gpencil_stroke_editcurve_generate(bGPDstroke *gps,
   if (gps->totpoints < 3) {
     return gpencil_stroke_editcurve_generate_edgecases(gps);
   }
+/* Point data: x, y, z, pressure, strength, r, g, b, a */
 #define POINT_DIM 9
 
   float *points = MEM_callocN(sizeof(float) * gps->totpoints * POINT_DIM, __func__);
@@ -754,20 +793,94 @@ bGPDcurve *BKE_gpencil_stroke_editcurve_generate(bGPDstroke *gps,
 }
 
 /**
- * Updates the editcurve for a stroke. Frees the old curve if one exists and generates a new one.
+ * Creates a bGPDcurve by doing a cubic curve fitting on the tagged segments (parts of the curve
+ * with at least one bGPDspoint with GP_SPOINT_TAG).
  */
-void BKE_gpencil_stroke_editcurve_update(bGPDstroke *gps, const float threshold, const float corner_angle)
+bGPDcurve *BKE_gpencil_stroke_editcurve_regenerate(bGPDstroke *gps,
+                                                   const float error_threshold,
+                                                   const float corner_angle)
+{
+  if (gps->totpoints < 3) {
+    BKE_gpencil_free_stroke_editcurve(gps);
+    return gpencil_stroke_editcurve_generate_edgecases(gps);
+  }
+
+  bGPDcurve *gpc = gps->editcurve;
+
+  int i, j, count = 0;
+  for (i = 0, j = 0; i < gps->totpoints; i++) {
+    bGPDspoint *pt = &gps->points[i];
+    if ((pt->flag & GP_SPOINT_TAG) == 0) {
+      if (j + 1 < gpc->tot_curve_points && i >= &gpc->curve_points[j + 1]->point_index) {
+        j++;
+      }
+      continue;
+    }
+    count++;
+
+    if (++j < gpc->tot_curve_points) {
+      bGPDcurve_point *cpt = &gpc->curve_points[j];
+      i = cpt->point_index;
+    }
+    else {
+      break;
+    }
+  }
+}
+
+/**
+ * Updates the editcurve for a stroke.
+ * \param gps: The stroke.
+ * \param threshold: Fitting threshold. The gernerated curve should not deviate more than this
+ * amount from the stroke.
+ * \param corner_angle: If angles greater than this amount are detected during fitting, they will
+ * be sharp (non-aligned handles).
+ * \param do_partial_update: If false, will delete the old curve and do a full update. If true,
+ * will check what points were changed using the GP_SPOINT_TAG point flag and only update the
+ * segments that should be updated and keep the others. If all segments were affected, will do a
+ * full update.
+ */
+void BKE_gpencil_stroke_editcurve_update(bGPDstroke *gps,
+                                         const float threshold,
+                                         const float corner_angle,
+                                         const bool do_partial_update)
 {
   if (gps == NULL || gps->totpoints < 0) {
     return;
   }
 
-  if (gps->editcurve != NULL) {
-    BKE_gpencil_free_stroke_editcurve(gps);
+  bGPDcurve *editcurve = NULL;
+
+  if (do_partial_update && gps->editcurve != NULL) {
+    /* Find the segments that need an update, then update them. */
+    const int tot_num_segments = (gps->flag & GP_STROKE_CYCLIC) ?
+                                     gps->editcurve->tot_curve_points :
+                                     gps->editcurve->tot_curve_points - 1;
+    const int num_sel_segments = gpencil_count_tagged_curve_segments(gps);
+    if (num_sel_segments == 0) {
+      /* No update needed. */
+      return;
+    }
+    else if (num_sel_segments == tot_num_segments) {
+      /* All segments need to be updated. Do a full update. */
+      BKE_gpencil_free_stroke_editcurve(gps);
+      editcurve = BKE_gpencil_stroke_editcurve_generate(gps, threshold, corner_angle);
+    }
+    else {
+      editcurve = BKE_gpencil_stroke_editcurve_regenerate(gps, threshold, corner_angle);
+    }
+  }
+  else {
+    /* Do a full update. Delete the old curve and generate a new one. */
+    if (gps->editcurve != NULL) {
+      BKE_gpencil_free_stroke_editcurve(gps);
+    }
+
+    editcurve = BKE_gpencil_stroke_editcurve_generate(gps, threshold, corner_angle);
   }
 
-  bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_generate(gps, threshold, corner_angle);
   if (editcurve == NULL) {
+    /* TODO: This should not happen. Maybe add an assert here? */
     return;
   }
 
@@ -1342,5 +1455,4 @@ void BKE_gpencil_editcurve_subdivide(bGPDstroke *gps, const int cuts)
   }
 }
 
-
 /** \} */
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index 3e1a096c231..28a1f14fc07 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -1295,7 +1295,7 @@ void BKE_gpencil_stroke_geometry_update(bGPdata *gpd, bGPDstroke *gps)
 
     /* If the stroke geometry was updated, refit the curve.
      * NOTE: Normally the stroke points of a curve should not be updated directly. Only if it is
-     * unavoidable. */
+     * unavoidable. Sculpting a curve also makes use of this. */
     if (gps->editcurve->flag & GP_CURVE_NEEDS_STROKE_UPDATE) {
       BKE_gpencil_stroke_editcurve_update(
           gps, gpd->curve_edit_threshold, gpd->curve_edit_corner_angle);
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index 0d3ab9011d6..d90716bfb69 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -304,6 +304,9 @@ static void gpencil_update_geometry(bGPdata *gpd)
 
       LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
         if (gps->flag & GP_STROKE_TAG) {
+          if (GPENCIL_STROKE_IS_CURVE(gps)) {
+            gps->editcurve->flag |= GP_CURVE_NEEDS_STROKE_UPDATE;
+          }
           BKE_gpencil_stroke_geometry_update(gpd, gps);
           gps->flag &= ~GP_STROKE_TAG;
         }
@@ -1440,6 +1443,7 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
   bool include_last = false;
   bool changed = false;
   float rot_eval = 0.0f;
+  const bool is_curve = GPENCIL_STROKE_IS_CURVE(gps_active);
 
   if (gps->totpoints == 1) {
     bGPDspoint pt_temp;
@@ -1456,6 +1460,9 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
       if (len_v2v2_int(mval_i, pc1) <= radius) {
         /* apply operation to this point */
         if (pt_active != NULL) {
+          if (is_curve) {
+            pt_active->flag |= GP_SPOINT_TAG;
+          }
           rot_eval = gpencil_sculpt_rotation_eval_get(gso, gps, pt, 0);
           changed = apply(gso, gps_active, rot_eval, 0, radius, pc1);
         }
@@ -1508,8 +1515,14 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
               ((pt_active->flag & GP_SPOINT_SELECT) == 0)) {
             continue;
           }
+
           index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
           if ((pt_active != NULL) && (index < gps_active->totpoi

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list