[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