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:
Diffstat (limited to 'source/blender/gpencil_modifiers/intern')
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencildash.c36
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c628
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c10
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c77
5 files changed, 701 insertions, 51 deletions
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_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c
index 439073752da..e57b9df03f5 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c
@@ -97,12 +97,13 @@ static bool stroke_dash(const bGPDstroke *gps,
int new_stroke_offset = 0;
int trim_start = 0;
+ int sequence_length = 0;
for (int i = 0; i < dmd->segments_len; i++) {
- if (dmd->segments[i].dash + real_gap(&dmd->segments[i]) < 1) {
- BLI_assert_unreachable();
- /* This means there's a part that doesn't have any length, can't do dot-dash. */
- return false;
- }
+ sequence_length += dmd->segments[i].dash + real_gap(&dmd->segments[i]);
+ }
+ if (sequence_length < 1) {
+ /* This means the whole segment has no length, can't do dot-dash. */
+ return false;
}
const DashGpencilModifierSegment *const first_segment = &dmd->segments[0];
@@ -147,6 +148,9 @@ static bool stroke_dash(const bGPDstroke *gps,
bGPDstroke *stroke = BKE_gpencil_stroke_new(
ds->mat_nr < 0 ? gps->mat_nr : ds->mat_nr, size, gps->thickness);
+ if (ds->flag & GP_DASH_USE_CYCLIC) {
+ stroke->flag |= GP_STROKE_CYCLIC;
+ }
for (int is = 0; is < size; is++) {
bGPDspoint *p = &gps->points[new_stroke_offset + is];
@@ -204,9 +208,10 @@ static void apply_dash_for_frame(
dmd->flag & GP_LENGTH_INVERT_PASS,
dmd->flag & GP_LENGTH_INVERT_LAYERPASS,
dmd->flag & GP_LENGTH_INVERT_MATERIAL)) {
- stroke_dash(gps, dmd, &result);
- BLI_remlink(&gpf->strokes, gps);
- BKE_gpencil_free_stroke(gps);
+ if (stroke_dash(gps, dmd, &result)) {
+ BLI_remlink(&gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+ }
}
}
bGPDstroke *gps_dash;
@@ -232,6 +237,18 @@ static void bakeModifier(Main *UNUSED(bmain),
/* -------------------------------- */
+static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
+{
+ DashGpencilModifierData *dmd = (DashGpencilModifierData *)md;
+
+ int sequence_length = 0;
+ for (int i = 0; i < dmd->segments_len; i++) {
+ sequence_length += dmd->segments[i].dash + real_gap(&dmd->segments[i]);
+ }
+ /* This means the whole segment has no length, can't do dot-dash. */
+ return sequence_length < 1;
+}
+
/* Generic "generateStrokes" callback */
static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob)
{
@@ -323,6 +340,7 @@ static void panel_draw(const bContext *C, Panel *panel)
uiItemR(sub, &ds_ptr, "radius", 0, NULL, ICON_NONE);
uiItemR(sub, &ds_ptr, "opacity", 0, NULL, ICON_NONE);
uiItemR(sub, &ds_ptr, "material_index", 0, NULL, ICON_NONE);
+ uiItemR(sub, &ds_ptr, "use_cyclic", 0, NULL, ICON_NONE);
}
gpencil_modifier_panel_end(layout, ptr);
@@ -362,7 +380,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Dash = {
/* initData */ initData,
/* freeData */ freeData,
- /* isDisabled */ NULL,
+ /* isDisabled */ isDisabled,
/* updateDepsgraph */ NULL,
/* dependsOnTime */ NULL,
/* foreachIDLink */ foreachIDLink,
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..f8ac8d95493
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c
@@ -0,0 +1,628 @@
+/* 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 defined 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, &old_points[i2].x)) /
+ len_v3v3(&old_points[i1].x, &old_points[i2].x);
+ d *= 2 * cos_angle / (1 + cos_angle);
+ float to_closest[3];
+ sub_v3_v3v3(to_closest, closest, &point->x);
+ if (dist == 0.0f) {
+ dist = d;
+ copy_v3_v3(closest, tmp_closest);
+ }
+ else if (dot_v3v3(to_closest, dir) >= 0) {
+ if (d > dist) {
+ dist = d;
+ copy_v3_v3(closest, tmp_closest);
+ }
+ }
+ else {
+ if (d > dist2) {
+ dist2 = d;
+ copy_v3_v3(closest2, tmp_closest);
+ }
+ }
+ }
+ if (dist == 0.0f) {
+ copy_v3_v3(closest, &point->x);
+ }
+ if (dist2 == 0.0f) {
+ copy_v3_v3(closest2, &point->x);
+ }
+ dist = dist + dist2;
+
+ if (dist < FLT_EPSILON) {
+ continue;
+ }
+
+ float use_dist = dist;
+
+ /* Apply radius limiting to not cross existing lines. */
+ float dir[3], new_center[3];
+ interp_v3_v3v3(new_center, closest2, closest, 0.5f);
+ sub_v3_v3v3(dir, new_center, &point->x);
+ if (normalize_v3(dir) != 0.0f && (is_cyclic || (i > 0 && i < gps->totpoints - 1))) {
+ const float max_radius = calc_radius_limit(gps, old_points, dir, spread, i);
+ use_dist = min_ff(use_dist, 2 * max_radius);
+ }
+
+ float fac = use_dist * weight;
+ /* The 50 is an internal constant for the default pixel size. The result can be messed up if
+ * #bGPdata.pixfactor is not default, but I think modifiers shouldn't access that. */
+ point->pressure += fac * 50.0f * GP_DEFAULT_PIX_FACTOR;
+ interp_v3_v3v3(&point->x, &point->x, new_center, fac / len_v3v3(closest, closest2));
+ }
+
+ MEM_freeN(old_points);
+}
+
+/**
+ * Apply envelope effect to the stroke.
+ */
+static void deformStroke(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
+{
+ EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md;
+ if (mmd->mode != GP_ENVELOPE_DEFORM) {
+ return;
+ }
+ const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
+
+ if (!is_stroke_affected_by_modifier(ob,
+ mmd->layername,
+ mmd->material,
+ mmd->pass_index,
+ mmd->layer_pass,
+ 3,
+ gpl,
+ gps,
+ mmd->flag & GP_ENVELOPE_INVERT_LAYER,
+ mmd->flag & GP_ENVELOPE_INVERT_PASS,
+ mmd->flag & GP_ENVELOPE_INVERT_LAYERPASS,
+ mmd->flag & GP_ENVELOPE_INVERT_MATERIAL)) {
+ return;
+ }
+
+ if (mmd->spread <= 0) {
+ return;
+ }
+
+ apply_stroke_envelope(
+ gps, mmd->spread, def_nr, (mmd->flag & GP_ENVELOPE_INVERT_VGROUP) != 0, mmd->thickness);
+}
+
+static void add_stroke(Object *ob,
+ bGPDstroke *gps,
+ const int point_index,
+ const int connection_index,
+ const int size,
+ const int mat_nr,
+ const float thickness,
+ const float strength,
+ ListBase *results)
+{
+ bGPdata *gpd = ob->data;
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, size, gps->thickness);
+
+ const int size1 = size == 4 ? 2 : 1;
+ const int size2 = size - size1;
+
+ memcpy(&gps_dst->points[0], &gps->points[connection_index], size1 * sizeof(bGPDspoint));
+ memcpy(&gps_dst->points[size1], &gps->points[point_index], size2 * sizeof(bGPDspoint));
+
+ for (int i = 0; i < size; i++) {
+ gps_dst->points[i].pressure *= thickness;
+ gps_dst->points[i].strength *= strength;
+ memset(&gps_dst->points[i].runtime, 0, sizeof(bGPDspoint_Runtime));
+ }
+
+ if (gps->dvert != NULL) {
+ gps_dst->dvert = MEM_malloc_arrayN(size, sizeof(MDeformVert), __func__);
+ BKE_defvert_array_copy(&gps_dst->dvert[0], &gps->dvert[connection_index], size1);
+ BKE_defvert_array_copy(&gps_dst->dvert[size1], &gps->dvert[point_index], size2);
+ }
+
+ BLI_addtail(results, gps_dst);
+
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gps_dst);
+}
+
+static void add_stroke_cyclic(Object *ob,
+ bGPDstroke *gps,
+ const int point_index,
+ const int connection_index,
+ const int mat_nr,
+ const float thickness,
+ const float strength,
+ ListBase *results)
+{
+ bGPdata *gpd = ob->data;
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, 4, gps->thickness);
+
+ int connection_index2 = (connection_index + 1) % gps->totpoints;
+ int point_index2 = (point_index + 1) % gps->totpoints;
+
+ gps_dst->points[0] = gps->points[connection_index];
+ gps_dst->points[1] = gps->points[connection_index2];
+ gps_dst->points[2] = gps->points[point_index];
+ gps_dst->points[3] = gps->points[point_index2];
+ for (int i = 0; i < 4; i++) {
+ gps_dst->points[i].pressure *= thickness;
+ gps_dst->points[i].strength *= strength;
+ memset(&gps_dst->points[i].runtime, 0, sizeof(bGPDspoint_Runtime));
+ }
+
+ if (gps->dvert != NULL) {
+ gps_dst->dvert = MEM_malloc_arrayN(4, sizeof(MDeformVert), __func__);
+ BKE_defvert_array_copy(&gps_dst->dvert[0], &gps->dvert[connection_index], 1);
+ BKE_defvert_array_copy(&gps_dst->dvert[1], &gps->dvert[connection_index2], 1);
+ BKE_defvert_array_copy(&gps_dst->dvert[2], &gps->dvert[point_index], 1);
+ BKE_defvert_array_copy(&gps_dst->dvert[3], &gps->dvert[point_index2], 1);
+ }
+
+ BLI_addtail(results, gps_dst);
+
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gps_dst);
+}
+
+static void add_stroke_simple(Object *ob,
+ bGPDstroke *gps,
+ const int point_index,
+ const int connection_index,
+ const int mat_nr,
+ const float thickness,
+ const float strength,
+ ListBase *results)
+{
+ bGPdata *gpd = ob->data;
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, 2, gps->thickness);
+
+ gps_dst->points[0] = gps->points[connection_index];
+ gps_dst->points[0].pressure *= thickness;
+ gps_dst->points[0].strength *= strength;
+ memset(&gps_dst->points[0].runtime, 0, sizeof(bGPDspoint_Runtime));
+ gps_dst->points[1] = gps->points[point_index];
+ gps_dst->points[1].pressure *= thickness;
+ gps_dst->points[1].strength *= strength;
+ memset(&gps_dst->points[1].runtime, 0, sizeof(bGPDspoint_Runtime));
+
+ if (gps->dvert != NULL) {
+ gps_dst->dvert = MEM_malloc_arrayN(2, sizeof(MDeformVert), __func__);
+ BKE_defvert_array_copy(&gps_dst->dvert[0], &gps->dvert[connection_index], 1);
+ BKE_defvert_array_copy(&gps_dst->dvert[1], &gps->dvert[point_index], 1);
+ }
+
+ BLI_addtail(results, gps_dst);
+
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gps_dst);
+}
+
+static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gpl, bGPDframe *gpf)
+{
+ EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md;
+ ListBase duplicates = {0};
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ if (!is_stroke_affected_by_modifier(ob,
+ mmd->layername,
+ mmd->material,
+ mmd->pass_index,
+ mmd->layer_pass,
+ 3,
+ gpl,
+ gps,
+ mmd->flag & GP_ENVELOPE_INVERT_LAYER,
+ mmd->flag & GP_ENVELOPE_INVERT_PASS,
+ mmd->flag & GP_ENVELOPE_INVERT_LAYERPASS,
+ mmd->flag & GP_ENVELOPE_INVERT_MATERIAL)) {
+ continue;
+ }
+
+ const int mat_nr = mmd->mat_nr < 0 ? gps->mat_nr : min_ii(mmd->mat_nr, ob->totcol - 1);
+ if (mmd->mode == GP_ENVELOPE_FILLS) {
+ if (gps->flag & GP_STROKE_CYCLIC) {
+ for (int i = 0; i < gps->totpoints; i++) {
+ const int connection_index = (i + mmd->spread) % gps->totpoints;
+ add_stroke_cyclic(
+ ob, gps, i, connection_index, mat_nr, mmd->thickness, mmd->strength, &duplicates);
+ }
+ }
+ else {
+ for (int i = 1; i < gps->totpoints - 1 && i < mmd->spread + 1; i++) {
+ add_stroke(ob, gps, i, 0, 3, mat_nr, mmd->thickness, mmd->strength, &duplicates);
+ }
+ for (int i = 0; i < gps->totpoints - 1; i++) {
+ const int connection_index = min_ii(i + mmd->spread, gps->totpoints - 1);
+ const int size = i == gps->totpoints - 2 ? 2 :
+ connection_index < gps->totpoints - 1 ? 4 :
+ 3;
+ add_stroke(ob,
+ gps,
+ i,
+ connection_index,
+ size,
+ mat_nr,
+ mmd->thickness,
+ mmd->strength,
+ &duplicates);
+ }
+ }
+ BLI_remlink(&gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+ }
+ else {
+ BLI_assert(mmd->mode == GP_ENVELOPE_SEGMENTS);
+ if (gps->flag & GP_STROKE_CYCLIC) {
+ for (int i = 0; i < gps->totpoints; i++) {
+ const int connection_index = (i + 1 + mmd->spread) % gps->totpoints;
+ add_stroke_simple(
+ ob, gps, i, connection_index, mat_nr, mmd->thickness, mmd->strength, &duplicates);
+ }
+ }
+ else {
+ for (int i = -mmd->spread; i < gps->totpoints - 1; i++) {
+ const int connection_index = min_ii(i + 1 + mmd->spread, gps->totpoints - 1);
+ add_stroke_simple(ob,
+ gps,
+ max_ii(0, i),
+ connection_index,
+ mat_nr,
+ mmd->thickness,
+ mmd->strength,
+ &duplicates);
+ }
+ }
+ }
+ }
+ if (!BLI_listbase_is_empty(&duplicates)) {
+ /* Add strokes to the start of the stroke list to ensure the new lines are drawn underneath the
+ * original line. */
+ BLI_movelisttolist_reverse(&gpf->strokes, &duplicates);
+ }
+}
+
+/**
+ * Apply envelope effect to the strokes.
+ */
+static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob)
+{
+ EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md;
+ if (mmd->mode == GP_ENVELOPE_DEFORM || mmd->spread <= 0) {
+ return;
+ }
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ bGPDframe *gpf = BKE_gpencil_frame_retime_get(depsgraph, scene, ob, gpl);
+ if (gpf == NULL) {
+ continue;
+ }
+ generate_geometry(md, ob, gpl, gpf);
+ }
+}
+
+static void bakeModifier(struct Main *UNUSED(bmain),
+ Depsgraph *depsgraph,
+ GpencilModifierData *md,
+ Object *ob)
+{
+ EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md;
+ if (mmd->mode == GP_ENVELOPE_DEFORM) {
+ generic_bake_deform_stroke(depsgraph, md, ob, false, deformStroke);
+ }
+ else {
+ bGPdata *gpd = ob->data;
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ generate_geometry(md, ob, gpl, gpf);
+ }
+ }
+ }
+}
+
+static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
+{
+ EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md;
+
+ walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
+}
+
+static void panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE);
+
+ uiItemR(layout, ptr, "spread", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "thickness", 0, NULL, ICON_NONE);
+
+ const int mode = RNA_enum_get(ptr, "mode");
+ if (mode != GP_ENVELOPE_DEFORM) {
+ uiItemR(layout, ptr, "strength", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "mat_nr", 0, NULL, ICON_NONE);
+ }
+
+ gpencil_modifier_panel_end(layout, ptr);
+}
+
+static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(panel, true, true);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Envelope, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
+GpencilModifierTypeInfo modifierType_Gpencil_Envelope = {
+ /* name */ "Envelope",
+ /* structName */ "EnvelopeGpencilModifierData",
+ /* structSize */ sizeof(EnvelopeGpencilModifierData),
+ /* type */ eGpencilModifierTypeType_Gpencil,
+ /* flags */ eGpencilModifierTypeFlag_SupportsEditmode,
+
+ /* copyData */ copyData,
+
+ /* deformStroke */ deformStroke,
+ /* generateStrokes */ generateStrokes,
+ /* bakeModifier */ bakeModifier,
+ /* remapTime */ NULL,
+
+ /* initData */ initData,
+ /* freeData */ NULL,
+ /* isDisabled */ NULL,
+ /* updateDepsgraph */ NULL,
+ /* dependsOnTime */ NULL,
+ /* foreachIDLink */ foreachIDLink,
+ /* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
+};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c
index 8eaed56dc58..d80224e6639 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c
@@ -105,15 +105,15 @@ static void deformStroke(GpencilModifierData *md,
/* Apply deformed coordinates. */
pt = gps->points;
+ bGPDstroke gps_old = *gps;
+ gps_old.points = (bGPDspoint *)MEM_dupallocN(gps->points);
for (i = 0; i < gps->totpoints; i++, pt++) {
copy_v3_v3(&pt->x, vert_coords[i]);
/* Smooth stroke. */
- if (mmd->smooth_factor > 0.0f) {
- for (int r = 0; r < mmd->smooth_step; r++) {
- BKE_gpencil_stroke_smooth_point(gps, i, mmd->smooth_factor, true);
- }
- }
+ BKE_gpencil_stroke_smooth_point(
+ &gps_old, i, mmd->smooth_factor, mmd->smooth_step, true, false, gps);
}
+ MEM_freeN(gps_old.points);
MEM_freeN(vert_coords);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
index f8201eb6b4f..1992ebd1508 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
@@ -34,10 +34,14 @@
#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)
{
SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md;
@@ -94,45 +98,40 @@ static void deformStroke(GpencilModifierData *md,
return;
}
- /* smooth stroke */
- if (mmd->factor > 0.0f) {
- for (int r = 0; r < mmd->step; r++) {
- for (int i = 0; i < gps->totpoints; i++) {
- MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL;
-
- /* verify vertex group */
- float weight = get_modifier_point_weight(
- dvert, (mmd->flag & GP_SMOOTH_INVERT_VGROUP) != 0, def_nr);
- if (weight < 0.0f) {
- continue;
- }
-
- /* Custom curve to modulate value. */
- if (use_curve) {
- float value = (float)i / (gps->totpoints - 1);
- weight *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value);
- }
-
- const float val = mmd->factor * weight;
- /* perform smoothing */
- if (mmd->flag & GP_SMOOTH_MOD_LOCATION) {
- BKE_gpencil_stroke_smooth_point(gps, i, val, false);
- }
- if (mmd->flag & GP_SMOOTH_MOD_STRENGTH) {
- BKE_gpencil_stroke_smooth_strength(gps, i, val);
- }
- if ((mmd->flag & GP_SMOOTH_MOD_THICKNESS) && (val > 0.0f)) {
- /* thickness need to repeat process several times */
- for (int r2 = 0; r2 < r * 10; r2++) {
- BKE_gpencil_stroke_smooth_thickness(gps, i, val);
- }
- }
- if (mmd->flag & GP_SMOOTH_MOD_UV) {
- BKE_gpencil_stroke_smooth_uv(gps, i, val);
- }
+ if (mmd->factor <= 0.0f || mmd->step <= 0) {
+ return;
+ }
+
+ float *weights = NULL;
+ if (def_nr != -1 || use_curve) {
+ weights = MEM_malloc_arrayN(gps->totpoints, sizeof(*weights), __func__);
+ /* Calculate weights. */
+ for (int i = 0; i < gps->totpoints; i++) {
+ MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL;
+
+ /* Verify vertex group. */
+ float weight = get_modifier_point_weight(
+ dvert, (mmd->flag & GP_SMOOTH_INVERT_VGROUP) != 0, def_nr);
+
+ /* Custom curve to modulate value. */
+ if (use_curve && weight > 0.0f) {
+ float value = (float)i / (gps->totpoints - 1);
+ weight *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value);
}
+
+ weights[i] = weight;
}
}
+ BKE_gpencil_stroke_smooth(gps,
+ mmd->factor,
+ mmd->step,
+ mmd->flag & GP_SMOOTH_MOD_LOCATION,
+ mmd->flag & GP_SMOOTH_MOD_STRENGTH,
+ mmd->flag & GP_SMOOTH_MOD_THICKNESS,
+ mmd->flag & GP_SMOOTH_MOD_UV,
+ mmd->flag & GP_SMOOTH_KEEP_SHAPE,
+ weights);
+ MEM_SAFE_FREE(weights);
}
static void bakeModifier(struct Main *UNUSED(bmain),
@@ -161,7 +160,7 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
{
- uiLayout *row;
+ uiLayout *row, *col;
uiLayout *layout = panel->layout;
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
@@ -177,6 +176,10 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(layout, ptr, "factor", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "step", 0, IFACE_("Repeat"), ICON_NONE);
+ col = uiLayoutColumn(layout, false);
+ uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_edit_position"));
+ uiItemR(col, ptr, "use_keep_shape", 0, NULL, ICON_NONE);
+
gpencil_modifier_panel_end(layout, ptr);
}