[Bf-blender-cvs] [cee6af00567] master: GPencil: New Envelope Modifier

Henrik Dick noreply at git.blender.org
Thu Mar 24 13:09:38 CET 2022


Commit: cee6af00567f454983d0bd3f6508e0213c91fb1c
Author: Henrik Dick
Date:   Thu Mar 24 13:01:46 2022 +0100
Branches: master
https://developer.blender.org/rBcee6af00567f454983d0bd3f6508e0213c91fb1c

GPencil: New Envelope Modifier

This new modifier creates a shape known as envelope. It connects all
points that are n points apart. There is also a mode which fits a
single stroke to the envelope shape that is determined by that rule.

For more details, refer to the patch.

Reviewed By: NicksBest, antoniov, frogstomp, mendio

Differential Revision: http://developer.blender.org/D14341

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

M	source/blender/gpencil_modifiers/CMakeLists.txt
M	source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
M	source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
A	source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c
M	source/blender/makesdna/DNA_gpencil_modifier_defaults.h
M	source/blender/makesdna/DNA_gpencil_modifier_types.h
M	source/blender/makesdna/intern/dna_defaults.c
M	source/blender/makesrna/intern/rna_gpencil_modifier.c

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

diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index 752d4aea61c..6108629183c 100644
--- a/source/blender/gpencil_modifiers/CMakeLists.txt
+++ b/source/blender/gpencil_modifiers/CMakeLists.txt
@@ -37,6 +37,7 @@ set(SRC
   intern/MOD_gpencilbuild.c
   intern/MOD_gpencilcolor.c
   intern/MOD_gpencildash.c
+  intern/MOD_gpencilenvelope.c
   intern/MOD_gpencilhook.c
   intern/MOD_gpencillattice.c
   intern/MOD_gpencillength.c
diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
index ff280b9ca0d..e88d864a86e 100644
--- a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
+++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
@@ -35,6 +35,7 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_WeightAngle;
 extern GpencilModifierTypeInfo modifierType_Gpencil_Lineart;
 extern GpencilModifierTypeInfo modifierType_Gpencil_Dash;
 extern GpencilModifierTypeInfo modifierType_Gpencil_Shrinkwrap;
+extern GpencilModifierTypeInfo modifierType_Gpencil_Envelope;
 
 /* MOD_gpencil_util.c */
 void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
index e766615101a..6cf7f6f11e5 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
@@ -56,6 +56,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[])
   INIT_GP_TYPE(Lineart);
   INIT_GP_TYPE(Dash);
   INIT_GP_TYPE(Shrinkwrap);
+  INIT_GP_TYPE(Envelope);
 #undef INIT_GP_TYPE
 }
 
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c
new file mode 100644
index 00000000000..0f736cac464
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c
@@ -0,0 +1,629 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2017 Blender Foundation. */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#include <stdio.h>
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_math_geom.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_defaults.h"
+#include "DNA_gpencil_modifier_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_deform.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_gpencil_modifier.h"
+#include "BKE_lib_query.h"
+#include "BKE_modifier.h"
+#include "BKE_screen.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
+#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
+#include "MOD_gpencil_util.h"
+
+#include "MEM_guardedalloc.h"
+
+static void initData(GpencilModifierData *md)
+{
+  EnvelopeGpencilModifierData *gpmd = (EnvelopeGpencilModifierData *)md;
+
+  BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier));
+
+  MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(EnvelopeGpencilModifierData), modifier);
+}
+
+static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
+{
+  BKE_gpencil_modifier_copydata_generic(md, target);
+}
+
+static float calc_min_radius_v3v3(float p1[3], float p2[3], float dir[3])
+{
+  /* Use plane-conic-intersections to choose the maximal radius.
+   * The conic is deifned in 4D as f({x,y,z,t}) = x*x + y*y + z*z - t*t = 0
+   * Then a plane is defined parametrically as
+   * {p}(u, v) = {p1,0}*u + {p2,0}*(1-u) + {dir,1}*v with 0 <= u <= 1 and v >= 0
+   * Now compute the intersection point with the smallest t.
+   * To do so, compute the parameters u, v such that f(p(u, v)) = 0 and v is minimal.
+   * This can be done analytically and the solution is:
+   * u = -dot(p2,dir) / dot(p1-p2, dir) +/- sqrt((dot(p2,dir) / dot(p1-p2, dir))^2 -
+   * (2*dot(p1-p2,p2)*dot(p2,dir)-dot(p2,p2)*dot(p1-p2,dir))/(dot(p1-p2,dir)*dot(p1-p2,p1-p2)));
+   * v = ({p1}u + {p2}*(1-u))^2 / (2*(dot(p1,dir)*u + dot(p2,dir)*(1-u)));
+   */
+  float diff[3];
+  float p1_dir = dot_v3v3(p1, dir);
+  float p2_dir = dot_v3v3(p2, dir);
+  float p2_sqr = len_squared_v3(p2);
+  float diff_dir = p1_dir - p2_dir;
+  float u = 0.5f;
+  if (diff_dir != 0.0f) {
+    float p = p2_dir / diff_dir;
+    sub_v3_v3v3(diff, p1, p2);
+    float diff_sqr = len_squared_v3(diff);
+    float diff_p2 = dot_v3v3(diff, p2);
+    float q = (2 * diff_p2 * p2_dir - p2_sqr * diff_dir) / (diff_dir * diff_sqr);
+    if (p * p - q >= 0) {
+      u = -p - sqrtf(p * p - q) * copysign(1.0f, p);
+      CLAMP(u, 0.0f, 1.0f);
+    }
+    else {
+      u = 0.5f - copysign(0.5f, p);
+    }
+  }
+  else {
+    float p1_sqr = len_squared_v3(p1);
+    u = p1_sqr < p2_sqr ? 1.0f : 0.0f;
+  }
+  float p[3];
+  interp_v3_v3v3(p, p2, p1, u);
+  /* v is the determined minimal radius. In case p1 and p2 are the same, there is a
+   * simple proof for the following formula using the geometric mean theorem and Thales theorem. */
+  float v = len_squared_v3(p) / (2 * interpf(p1_dir, p2_dir, u));
+  if (v < 0 || !isfinite(v)) {
+    /* No limit to the radius from this segment. */
+    return 1e16f;
+  }
+  return v;
+}
+
+static float calc_radius_limit(
+    bGPDstroke *gps, bGPDspoint *points, float dir[3], int spread, const int i)
+{
+  const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
+  bGPDspoint *pt = &points[i];
+
+  /* NOTE this part is the second performance critical part. Improvements are welcome. */
+  float radius_limit = 1e16f;
+  float p1[3], p2[3];
+  if (is_cyclic) {
+    if (gps->totpoints / 2 < spread) {
+      spread = gps->totpoints / 2;
+    }
+    const int start = i + gps->totpoints;
+    for (int j = -spread; j <= spread; j++) {
+      j += (j == 0);
+      const int i1 = (start + j) % gps->totpoints;
+      const int i2 = (start + j + (j > 0) - (j < 0)) % gps->totpoints;
+      sub_v3_v3v3(p1, &points[i1].x, &pt->x);
+      sub_v3_v3v3(p2, &points[i2].x, &pt->x);
+      float r = calc_min_radius_v3v3(p1, p2, dir);
+      radius_limit = min_ff(radius_limit, r);
+    }
+  }
+  else {
+    const int start = max_ii(-spread, 1 - i);
+    const int end = min_ii(spread, gps->totpoints - 2 - i);
+    for (int j = start; j <= end; j++) {
+      if (j == 0) {
+        continue;
+      }
+      const int i1 = i + j;
+      const int i2 = i + j + (j > 0) - (j < 0);
+      sub_v3_v3v3(p1, &points[i1].x, &pt->x);
+      sub_v3_v3v3(p2, &points[i2].x, &pt->x);
+      float r = calc_min_radius_v3v3(p1, p2, dir);
+      radius_limit = min_ff(radius_limit, r);
+    }
+  }
+  return radius_limit;
+}
+
+static void apply_stroke_envelope(
+    bGPDstroke *gps, int spread, const int def_nr, const bool invert_vg, const float thickness)
+{
+  const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
+  if (is_cyclic) {
+    const int half = gps->totpoints / 2;
+    spread = abs(((spread + half) % gps->totpoints) - half);
+  }
+  else {
+    spread = min_ii(spread, gps->totpoints - 1);
+  }
+
+  const int spread_left = (spread + 2) / 2;
+  const int spread_right = (spread + 1) / 2;
+
+  /* Copy the point data. Only need positions, but extracting them
+   * is probably just as expensive as a full copy. */
+  bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points);
+
+  /* Deform the stroke to match the envelope shape. */
+  for (int i = 0; i < gps->totpoints; i++) {
+    MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL;
+
+    /* Verify in vertex group. */
+    float weight = get_modifier_point_weight(dvert, invert_vg, def_nr);
+    if (weight < 0.0f) {
+      continue;
+    }
+
+    int index1 = i - spread_left;
+    int index2 = i + spread_right;
+    CLAMP(index1, 0, gps->totpoints - 1);
+    CLAMP(index2, 0, gps->totpoints - 1);
+
+    bGPDspoint *point = &gps->points[i];
+    point->pressure *= interpf(thickness, 1.0f, weight);
+
+    float closest[3];
+    float closest2[3];
+    copy_v3_v3(closest2, &point->x);
+    float dist = 0.0f;
+    float dist2 = 0.0f;
+    /* Create plane from point and neighbors and intersect that with the line. */
+    float v1[3], v2[3], plane_no[3];
+    sub_v3_v3v3(
+        v1,
+        &old_points[is_cyclic ? (i - 1 + gps->totpoints) % gps->totpoints : max_ii(0, i - 1)].x,
+        &old_points[i].x);
+    sub_v3_v3v3(
+        v2,
+        &old_points[is_cyclic ? (i + 1) % gps->totpoints : min_ii(gps->totpoints - 1, i + 1)].x,
+        &old_points[i].x);
+    normalize_v3(v1);
+    normalize_v3(v2);
+    sub_v3_v3v3(plane_no, v1, v2);
+    if (normalize_v3(plane_no) == 0.0f) {
+      continue;
+    }
+    /* Now find the intersections with the plane. */
+    /* NOTE this part is the first performance critical part. Improvements are welcome. */
+    float tmp_closest[3];
+    for (int j = -spread_right; j <= spread_left; j++) {
+      const int i1 = is_cyclic ? (i + j - spread_left + gps->totpoints) % gps->totpoints :
+                                 max_ii(0, i + j - spread_left);
+      const int i2 = is_cyclic ? (i + j + spread_right) % gps->totpoints :
+                                 min_ii(gps->totpoints - 1, i + j + spread_right);
+      /*bool side = dot_v3v3(&old_points[i1].x, plane_no) < dot_v3v3(plane_no, &old_points[i2].x);
+      if (side) {
+        continue;
+      }*/
+      float lambda = line_plane_factor_v3(
+          &point->x, plane_no, &old_points[i1].x, &old_points[i2].x);
+      if (lambda <= 0.0f || lambda >= 1.0f) {
+        continue;
+      }
+      interp_v3_v3v3(tmp_closest, &old_points[i1].x, &old_points[i2].x, lambda);
+
+      float dir[3];
+      sub_v3_v3v3(dir, tmp_closest, &point->x);
+      float d = len_v3(dir);
+      /* Use a formula to find the diameter of the circle that would touch the line. */
+      float cos_angle = fabsf(dot_v3v3(plane_no, &old_points[i1].x) -
+                              dot_v3v3(plane_no, &ol

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list