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:
m---------release/datafiles/locale0
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/startup/bl_ui/properties_data_modifier.py78
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h18
-rw-r--r--source/blender/blenkernel/intern/gpencil.c267
-rw-r--r--source/blender/gpencil_modifiers/CMakeLists.txt2
-rw-r--r--source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillength.c179
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c349
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h60
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c190
m---------source/tools0
15 files changed, 1149 insertions, 0 deletions
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject 8a05b618f031582c006c6f62b9e60619ab3eef8
+Subproject ad82c4ce43ef2801ef51e75af1f9702992478b0
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject 854b986732d643e696973175e480c15594bc478
+Subproject 8e6f485cf5b160c425d7da7c743879b20f3d6a9
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject 786f4704328507a95b6c1d254bf4cf400a5e8f0
+Subproject 7077ff07384491d1f7630484995557f1c7302da
diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py
index 124fe77cb52..02d8f82b4a9 100644
--- a/release/scripts/startup/bl_ui/properties_data_modifier.py
+++ b/release/scripts/startup/bl_ui/properties_data_modifier.py
@@ -2334,7 +2334,85 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
sub.active = bool(md.vertex_group)
sub.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT')
+ def GP_LENGTH(self, layout, ob, md):
+ gpd = ob.data
+ sp = layout.split()
+ col = sp.column()
+ col.label(text="Absolute:")
+ col.prop(md, "length")
+
+ col = sp.column()
+ col.label(text="Relative:")
+ col.prop(md, "percentage")
+
+ col = layout.column()
+ col.prop(md, "tip_length");
+
+ col.separator()
+
+ col.label(text="Material:")
+ row = col.row(align=True)
+ row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE')
+ row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT')
+ row = layout.row(align=True)
+ row.prop(md, "pass_index", text="Pass")
+ row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT')
+
+ col = layout.column()
+ col.separator()
+ col.label(text="Layer:")
+ row = col.row(align=True)
+ row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL')
+ row.prop(md, "invert_layers", text="", icon='ARROW_LEFTRIGHT')
+ row = layout.row(align=True)
+ row.prop(md, "layer_pass", text="Pass")
+ row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT')
+
+ def GP_MULTIPLY(self, layout, ob, md):
+ gpd = ob.data
+ sp = layout.split(factor = 0.5)
+ col = sp.column()
+ col.prop(md, "enable_duplication")
+ if md.enable_duplication:
+ col.prop(md,"duplications")
+ col.prop(md,"distance")
+ col.prop(md,"offset", slider=True)
+
+ col.prop(md,"enable_fading")
+ if md.enable_fading:
+ col.prop(md, "fading_center")
+ c = col.column(align = True)
+ c.prop(md, "fading_thickness", slider=True)
+ c.prop(md, "fading_opacity", slider=True)
+
+ col = sp.column()
+ col.prop(md, "enable_angle_splitting")
+ if md.enable_angle_splitting:
+ col.prop(md,"split_angle")
+
+ col = layout.column()
+ col.separator()
+
+ col.label(text="Material:")
+ row = col.row(align=True)
+ row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE')
+ row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT')
+ row = layout.row(align=True)
+ row.prop(md, "pass_index", text="Pass")
+ row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT')
+
+ col = layout.column()
+ col.separator()
+
+ col.label(text="Layer:")
+ row = col.row(align=True)
+ row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL')
+ row.prop(md, "invert_layers", text="", icon='ARROW_LEFTRIGHT')
+ row = layout.row(align=True)
+ row.prop(md, "layer_pass", text="Pass")
+ row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT')
+
classes = (
DATA_PT_modifiers,
DATA_PT_gpencil_modifiers,
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index d09917a9e41..21356db8abf 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -125,6 +125,12 @@ struct bGPDstroke *BKE_gpencil_add_stroke(struct bGPDframe *gpf,
int totpoints,
short thickness);
+struct bGPDstroke *BKE_gpencil_add_stroke_existing_style(struct bGPDframe *gpf,
+ struct bGPDstroke *existing,
+ int mat_idx,
+ int totpoints,
+ short thickness);
+
/* Stroke and Fill - Alpha Visibility Threshold */
#define GPENCIL_ALPHA_OPACITY_THRESH 0.001f
#define GPENCIL_STRENGTH_MIN 0.003f
@@ -238,6 +244,18 @@ bool BKE_gpencil_smooth_stroke_uv(struct bGPDstroke *gps, int point_index, float
bool BKE_gpencil_close_stroke(struct bGPDstroke *gps);
void BKE_gpencil_dissolve_points(struct bGPDframe *gpf, struct bGPDstroke *gps, const short tag);
+bool BKE_gpencil_stretch_stroke(struct bGPDstroke *gps, const float dist, const float tip_length);
+bool BKE_gpencil_trim_stroke_points(struct bGPDstroke *gps,
+ const int index_from,
+ const int index_to);
+bool BKE_gpencil_split_stroke(struct bGPDframe *gpf,
+ struct bGPDstroke *gps,
+ const int before_index,
+ struct bGPDstroke **remaining_gps);
+bool BKE_gpencil_shrink_stroke(struct bGPDstroke *gps, const float dist);
+
+float BKE_gpencil_stroke_length(const struct bGPDstroke *gps, bool use_3d);
+
void BKE_gpencil_get_range_selected(struct bGPDlayer *gpl, int *r_initframe, int *r_endframe);
float BKE_gpencil_multiframe_falloff_calc(
struct bGPDframe *gpf, int actnum, int f_init, int f_end, struct CurveMapping *cur_falloff);
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index e885aa04881..e4d65d23dc5 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -505,6 +505,18 @@ bGPDstroke *BKE_gpencil_add_stroke(bGPDframe *gpf, int mat_idx, int totpoints, s
return gps;
}
+/* Add a stroke and copy the temporary drawing color value from one of the existing stroke */
+bGPDstroke *BKE_gpencil_add_stroke_existing_style(
+ bGPDframe *gpf, bGPDstroke *existing, int mat_idx, int totpoints, short thickness)
+{
+ bGPDstroke *gps = BKE_gpencil_add_stroke(gpf, mat_idx, totpoints, thickness);
+ /* Copy runtime color data so that strokes added in the modifier has the style.
+ * There are depsgrapgh reference pointers inside,
+ * change the copy function if interfere with future drawing implementation. */
+ memcpy(&gps->runtime, &existing->runtime, sizeof(bGPDstroke_Runtime));
+ return gps;
+}
+
/* ************************************************** */
/* Data Duplication */
@@ -1753,6 +1765,239 @@ bool BKE_gpencil_sample_stroke(bGPDstroke *gps, const float dist, const bool sel
}
/**
+ * Backbone stretch similar to Freestyle.
+ * \param gps: Stroke to sample
+ * \param dist: Distance of one segment
+ * \param tip_length: Ignore tip jittering, set zero to use default value.
+ */
+bool BKE_gpencil_stretch_stroke(bGPDstroke *gps, const float dist, const float tip_length)
+{
+ bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt;
+ int i;
+ float threshold = (tip_length == 0 ? 0.001f : tip_length);
+
+ if (gps->totpoints < 2 || dist < FLT_EPSILON) {
+ return false;
+ }
+
+ last_pt = &pt[gps->totpoints - 1];
+ second_last = &pt[gps->totpoints - 2];
+ next_pt = &pt[1];
+
+ float len1 = 0.0f;
+ float len2 = 0.0f;
+
+ i = 1;
+ while (len1 < threshold && gps->totpoints > i) {
+ next_pt = &pt[i];
+ len1 = len_v3v3(&next_pt->x, &pt->x);
+ i++;
+ }
+
+ i = 2;
+ while (len2 < threshold && gps->totpoints >= i) {
+ second_last = &pt[gps->totpoints - i];
+ len2 = len_v3v3(&last_pt->x, &second_last->x);
+ i++;
+ }
+
+ float extend1 = (len1 + dist) / len1;
+ float extend2 = (len2 + dist) / len2;
+
+ float result1[3], result2[3];
+
+ interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
+ interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
+
+ copy_v3_v3(&pt->x, result1);
+ copy_v3_v3(&last_pt->x, result2);
+
+ return true;
+}
+
+/**
+ * Trim stroke to needed segments
+ * \param gps: Target stroke
+ * \param index_from: the index of the first point to be used in the trimmed result
+ * \param index_to: the index of the last point to be used in the trimmed result
+ */
+bool BKE_gpencil_trim_stroke_points(bGPDstroke *gps, const int index_from, const int index_to)
+{
+ bGPDspoint *pt = gps->points, *new_pt;
+ MDeformVert *dv, *new_dv;
+
+ const int new_count = index_to - index_from + 1;
+
+ if (new_count >= gps->totpoints) {
+ return false;
+ }
+
+ if (new_count == 1) {
+ BKE_gpencil_free_stroke_weights(gps);
+ MEM_freeN(gps->points);
+ gps->points = NULL;
+ gps->dvert = NULL;
+ gps->totpoints = 0;
+ return false;
+ }
+
+ new_pt = MEM_callocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
+
+ for (int i = 0; i < new_count; i++) {
+ memcpy(&new_pt[i], &pt[i + index_from], sizeof(bGPDspoint));
+ }
+
+ if (gps->dvert) {
+ new_dv = MEM_callocN(sizeof(MDeformVert) * new_count, "gp_stroke_dverts_trimmed");
+ for (int i = 0; i < new_count; i++) {
+ dv = &gps->dvert[i + index_from];
+ new_dv[i].flag = dv->flag;
+ new_dv[i].totweight = dv->totweight;
+ new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
+ "gp_stroke_dverts_dw_trimmed");
+ for (int j = 0; j < dv->totweight; j++) {
+ new_dv[i].dw[j].weight = dv->dw[j].weight;
+ new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
+ }
+ }
+ MEM_freeN(gps->dvert);
+ gps->dvert = new_dv;
+ }
+
+ MEM_freeN(gps->points);
+ gps->points = new_pt;
+ gps->totpoints = new_count;
+
+ return true;
+}
+
+bool BKE_gpencil_split_stroke(bGPDframe *gpf,
+ bGPDstroke *gps,
+ const int before_index,
+ bGPDstroke **remaining_gps)
+{
+ bGPDstroke *new_gps;
+ bGPDspoint *pt = gps->points, *new_pt;
+ MDeformVert *dv, *new_dv;
+
+ if (before_index >= gps->totpoints || before_index == 0) {
+ return false;
+ }
+
+ const int new_count = gps->totpoints - before_index;
+ const int old_count = before_index;
+
+ /* Handle remaining segments first. */
+
+ new_gps = BKE_gpencil_add_stroke_existing_style(gpf, gps, gps->mat_nr, new_count, gps->thickness);
+
+ new_pt = new_gps->points; /* Allocated from above. */
+
+ for (int i = 0; i < new_count; i++) {
+ memcpy(&new_pt[i], &pt[i + before_index], sizeof(bGPDspoint));
+ }
+
+ if (gps->dvert) {
+ new_dv = MEM_callocN(sizeof(MDeformVert) * new_count, "gp_stroke_dverts_remaining");
+ for (int i = 0; i < new_count; i++) {
+ dv = &gps->dvert[i + before_index];
+ new_dv[i].flag = dv->flag;
+ new_dv[i].totweight = dv->totweight;
+ new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
+ "gp_stroke_dverts_dw_remaining");
+ for (int j = 0; j < dv->totweight; j++) {
+ new_dv[i].dw[j].weight = dv->dw[j].weight;
+ new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
+ }
+ }
+ new_gps->dvert = new_dv;
+ }
+
+ (*remaining_gps) = new_gps;
+
+ /* Trim the original stroke into a shorter one.
+ * Keep the end point. */
+
+ BKE_gpencil_trim_stroke_points(gps, 0, old_count);
+
+ return true;
+}
+
+/**
+ * Shrink the stroke by length.
+ * \param gps: Stroke to shrink
+ * \param dist: delta length
+ */
+bool BKE_gpencil_shrink_stroke(bGPDstroke *gps, const float dist)
+{
+ bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt;
+ int i;
+
+ if (gps->totpoints < 2 || dist < FLT_EPSILON) {
+ return false;
+ }
+
+ last_pt = &pt[gps->totpoints - 1];
+ second_last = &pt[gps->totpoints - 2];
+ next_pt = &pt[1];
+
+ float len1, this_len1, cut_len1;
+ float len2, this_len2, cut_len2;
+ int index_start, index_end;
+
+ len1 = len2 = this_len1 = this_len2 = cut_len1 = cut_len2 = 0.0f;
+
+ i = 1;
+ while (len1 < dist && gps->totpoints > i - 1) {
+ next_pt = &pt[i];
+ this_len1 = len_v3v3(&pt[i].x, &pt[i + 1].x);
+ len1 += this_len1;
+ cut_len1 = len1 - dist;
+ i++;
+ }
+ index_start = i - 2;
+
+ i = 2;
+ while (len2 < dist && gps->totpoints >= i) {
+ second_last = &pt[gps->totpoints - i];
+ this_len2 = len_v3v3(&second_last[1].x, &second_last->x);
+ len2 += this_len2;
+ cut_len2 = len2 - dist;
+ i++;
+ }
+ index_end = gps->totpoints - i + 2;
+
+ if (len1 < dist || len2 < dist || index_end <= index_start) {
+ index_start = index_end = 0; /* empty stroke */
+ }
+
+ if ((index_end == index_start + 1) && (cut_len1 + cut_len2 > 1.0f)) {
+ index_start = index_end = 0; /* no length left to cut */
+ }
+
+ BKE_gpencil_trim_stroke_points(gps, index_start, index_end);
+
+ if (gps->totpoints == 0) {
+ return false;
+ }
+
+ pt = gps->points;
+
+ float cut1 = cut_len1 / this_len1;
+ float cut2 = cut_len2 / this_len2;
+
+ float result1[3], result2[3];
+
+ interp_v3_v3v3(result1, &pt[1].x, &pt[0].x, cut1);
+ interp_v3_v3v3(result2, &pt[gps->totpoints - 2].x, &pt[gps->totpoints - 1].x, cut2);
+
+ copy_v3_v3(&pt[0].x, result1);
+ copy_v3_v3(&pt[gps->totpoints - 1].x, result2);
+
+ return true;
+}
+
+/**
* Apply smooth to stroke point
* \param gps: Stroke to smooth
* \param i: Point index
@@ -2273,6 +2518,28 @@ void BKE_gpencil_stroke_2d_flat_ref(const bGPDspoint *ref_points,
*r_direction = (int)locy[2];
}
+float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d)
+{
+ if (!gps->points || gps->totpoints < 2) {
+ return 0.0f;
+ }
+ float *last_pt = &gps->points[0].x;
+ int i;
+ bGPDspoint *pt;
+ float total_length = 0.0f;
+ for (i = 1; i < gps->totpoints; i++) {
+ pt = &gps->points[i];
+ if (use_3d) {
+ total_length += len_v3v3(&pt->x, last_pt);
+ }
+ else {
+ total_length += len_v2v2(&pt->x, last_pt);
+ }
+ last_pt = &pt->x;
+ }
+ return total_length;
+}
+
/**
* Trim stroke to the first intersection or loop
* \param gps: Stroke data
diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index 41543448a15..fa01a1cbad3 100644
--- a/source/blender/gpencil_modifiers/CMakeLists.txt
+++ b/source/blender/gpencil_modifiers/CMakeLists.txt
@@ -49,7 +49,9 @@ set(SRC
intern/MOD_gpencilcolor.c
intern/MOD_gpencilhook.c
intern/MOD_gpencillattice.c
+ intern/MOD_gpencillength.c
intern/MOD_gpencilmirror.c
+ intern/MOD_gpencilmultiply.c
intern/MOD_gpencilnoise.c
intern/MOD_gpenciloffset.c
intern/MOD_gpencilopacity.c
diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
index 9fc00754744..b84ccbcab64 100644
--- a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
+++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
@@ -42,6 +42,8 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_Hook;
extern GpencilModifierTypeInfo modifierType_Gpencil_Offset;
extern GpencilModifierTypeInfo modifierType_Gpencil_Armature;
extern GpencilModifierTypeInfo modifierType_Gpencil_Time;
+extern GpencilModifierTypeInfo modifierType_Gpencil_Length;
+extern GpencilModifierTypeInfo modifierType_Gpencil_Multiply;
/* 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 2d1a845330b..b3321b693a4 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
@@ -71,6 +71,8 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[])
INIT_GP_TYPE(Offset);
INIT_GP_TYPE(Armature);
INIT_GP_TYPE(Time);
+ INIT_GP_TYPE(Length);
+ INIT_GP_TYPE(Multiply);
#undef INIT_GP_TYPE
}
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
new file mode 100644
index 00000000000..4828a0e3dd8
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
@@ -0,0 +1,179 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017, Blender Foundation
+ * This is a new part of Blender
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+/** \file blender/gpencil_modifiers/intern/MOD_gpencilstrokes.c
+ * \ingroup modifiers
+ */
+
+#include <stdio.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_object_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_gpencil_modifier_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_rand.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_linklist.h"
+#include "BLI_alloca.h"
+
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_modifier.h"
+#include "BKE_modifier.h"
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_object.h"
+#include "BKE_main.h"
+#include "BKE_scene.h"
+#include "BKE_layer.h"
+#include "BKE_library_query.h"
+#include "BKE_collection.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
+
+#include "MOD_gpencil_util.h"
+#include "MOD_gpencil_modifiertypes.h"
+
+static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
+{
+ BKE_gpencil_modifier_copyData_generic(md, target);
+}
+
+static void stretchOrShrinkStroke(bGPDstroke *gps, float length, float tip_length)
+{
+ if (length > 0.0f) {
+ BKE_gpencil_stretch_stroke(gps, length, tip_length);
+ }
+ else {
+ BKE_gpencil_shrink_stroke(gps, -length);
+ }
+}
+
+static void applyLength(bGPDstroke *gps, float length, float percentage, float tip_length)
+{
+
+ stretchOrShrinkStroke(gps, length, tip_length);
+
+ float len = BKE_gpencil_stroke_length(gps, true);
+ if (len < FLT_EPSILON) {
+ return;
+ }
+ float length2 = len * percentage / 2.0f; /* Srinking from two tips. */
+
+ stretchOrShrinkStroke(gps, length2, tip_length);
+}
+
+static void bakeModifier(Main *UNUSED(bmain),
+ Depsgraph *depsgraph,
+ GpencilModifierData *md,
+ Object *ob)
+{
+
+ bGPdata *gpd = ob->data;
+
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md;
+ bGPDstroke *gps;
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ if (is_stroke_affected_by_modifier(ob,
+ lmd->layername,
+ lmd->materialname,
+ lmd->pass_index,
+ lmd->layer_pass,
+ 1,
+ gpl,
+ gps,
+ lmd->flag & GP_MIRROR_INVERT_LAYER,
+ lmd->flag & GP_MIRROR_INVERT_PASS,
+ lmd->flag & GP_MIRROR_INVERT_LAYERPASS,
+ lmd->flag & GP_MIRROR_INVERT_MATERIAL)) {
+ applyLength(gps, lmd->length, lmd->percentage, lmd->tip_length);
+ }
+ }
+ }
+ }
+}
+
+/* -------------------------------- */
+
+/* Generic "generateStrokes" callback */
+static void deformStroke(GpencilModifierData *md,
+ Depsgraph *depsgraph,
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
+{
+ LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md;
+ if (is_stroke_affected_by_modifier(ob,
+ lmd->layername,
+ lmd->materialname,
+ lmd->pass_index,
+ lmd->layer_pass,
+ 1,
+ gpl,
+ gps,
+ lmd->flag & GP_MIRROR_INVERT_LAYER,
+ lmd->flag & GP_MIRROR_INVERT_PASS,
+ lmd->flag & GP_MIRROR_INVERT_LAYERPASS,
+ lmd->flag & GP_MIRROR_INVERT_MATERIAL)) {
+ applyLength(gps, lmd->length, lmd->percentage, lmd->tip_length);
+ }
+}
+
+GpencilModifierTypeInfo modifierType_Gpencil_Length = {
+ /* name */ "Length",
+ /* structName */ "LengthGpencilModifierData",
+ /* structSize */ sizeof(LengthGpencilModifierData),
+ /* type */ eGpencilModifierTypeType_Gpencil,
+ /* flags */ eGpencilModifierTypeFlag_SupportsEditmode,
+
+ /* copyData */ copyData,
+
+ /* deformStroke */ deformStroke,
+ /* generateStrokes */ NULL,
+ /* bakeModifier */ bakeModifier,
+ /* remapTime */ NULL,
+
+ /* initData */ NULL,
+ /* freeData */ NULL,
+ /* isDisabled */ NULL,
+ /* updateDepsgraph */ NULL,
+ /* dependsOnTime */ NULL,
+ /* foreachObjectLink */ NULL,
+ /* foreachIDLink */ NULL,
+ /* foreachTexLink */ NULL,
+};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
new file mode 100644
index 00000000000..6dd25a2bf56
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
@@ -0,0 +1,349 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017, Blender Foundation
+ * This is a new part of Blender
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+/** \file blender/gpencil_modifiers/intern/MOD_gpencilstrokes.c
+ * \ingroup modifiers
+ */
+
+#include <stdio.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_object_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_gpencil_modifier_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_rand.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_linklist.h"
+#include "BLI_alloca.h"
+
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_modifier.h"
+#include "BKE_modifier.h"
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_object.h"
+#include "BKE_main.h"
+#include "BKE_scene.h"
+#include "BKE_layer.h"
+#include "BKE_library_query.h"
+#include "BKE_collection.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
+
+#include "MOD_gpencil_util.h"
+#include "MOD_gpencil_modifiertypes.h"
+
+static void initData(GpencilModifierData *md)
+{
+ MultiplyGpencilModifierData *mmd = (MultiplyGpencilModifierData *)md;
+ mmd->distance = 0.5f;
+ mmd->split_angle = 1.0f;
+}
+
+static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
+{
+ BKE_gpencil_modifier_copyData_generic(md, target);
+}
+
+static void splitStroke(bGPDframe *gpf, bGPDstroke *gps, float split_angle)
+{
+ bGPDspoint *pt = gps->points;
+ bGPDstroke *new_gps = gps;
+ int i;
+ volatile float angle;
+
+ if (split_angle <= FLT_EPSILON) {
+ return;
+ }
+
+ for (i = 1; i < new_gps->totpoints - 1; i++) {
+ angle = angle_v3v3v3(&pt[i - 1].x, &pt[i].x, &pt[i + 1].x);
+ if (angle < split_angle) {
+ if (BKE_gpencil_split_stroke(gpf, new_gps, i, &new_gps)) {
+ pt = new_gps->points;
+ i = 0;
+ continue; /* then i == 1 again */
+ }
+ }
+ }
+}
+
+static void minter_v3_v3v3v3_ref(
+ float *result, float *left, float *middle, float *right, float *stroke_normal)
+{
+ float left_arm[3], right_arm[3], inter1[3], inter2[3];
+ float minter[3];
+ if (left) {
+ sub_v3_v3v3(left_arm, middle, left);
+ cross_v3_v3v3(inter1, stroke_normal, left_arm);
+ }
+ if (right) {
+ sub_v3_v3v3(right_arm, right, middle);
+ cross_v3_v3v3(inter2, stroke_normal, right_arm);
+ }
+ if (!left) {
+ normalize_v3(inter2);
+ copy_v3_v3(result, inter2);
+ return;
+ }
+
+ if (!right) {
+ normalize_v3(inter1);
+ copy_v3_v3(result, inter1);
+ return;
+ }
+
+ interp_v3_v3v3(minter, inter1, inter2, 0.5);
+ normalize_v3(minter);
+ copy_v3_v3(result, minter);
+}
+
+static void duplicateStroke(bGPDframe *gpf,
+ bGPDstroke *gps,
+ int count,
+ float dist,
+ float offset,
+ ListBase *results,
+ int fading,
+ float fading_center,
+ float fading_thickness,
+ float fading_opacity)
+{
+ int i;
+ bGPDstroke *new_gps;
+ float stroke_normal[3];
+ float minter[3];
+ bGPDspoint *pt;
+ float offset_factor;
+ float thickness_factor;
+ float opacity_factor;
+
+ BKE_gpencil_stroke_normal(gps, stroke_normal);
+ if (len_v3(stroke_normal) < FLT_EPSILON) {
+ add_v3_fl(stroke_normal, 1);
+ normalize_v3(stroke_normal);
+ }
+
+ float *t1_array = MEM_callocN(sizeof(float) * 3 * gps->totpoints,
+ "duplicate_temp_result_array_1");
+ float *t2_array = MEM_callocN(sizeof(float) * 3 * gps->totpoints,
+ "duplicate_temp_result_array_2");
+
+ pt = gps->points;
+
+ for (int j = 0; j < gps->totpoints; j++) {
+ if (j == 0) {
+ minter_v3_v3v3v3_ref(minter, NULL, &pt[j].x, &pt[j + 1].x, stroke_normal);
+ }
+ else if (j == gps->totpoints - 1) {
+ minter_v3_v3v3v3_ref(minter, &pt[j - 1].x, &pt[j].x, NULL, stroke_normal);
+ }
+ else {
+ minter_v3_v3v3v3_ref(minter, &pt[j - 1].x, &pt[j].x, &pt[j + 1].x, stroke_normal);
+ }
+ mul_v3_fl(minter, dist);
+ add_v3_v3v3(&t1_array[j * 3], &pt[j].x, minter);
+ sub_v3_v3v3(&t2_array[j * 3], &pt[j].x, minter);
+ }
+
+ /* This ensures the original stroke is the last one to be processed. */
+ for (i = count - 1; i >= 0; i--) {
+ if (i != 0) {
+ new_gps = BKE_gpencil_stroke_duplicate(gps);
+ new_gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ BLI_addtail(results, new_gps);
+ }
+ else {
+ new_gps = gps;
+ }
+
+ pt = new_gps->points;
+
+ if (count == 1) {
+ offset_factor = 0;
+ }
+ else {
+ offset_factor = (float)i / (float)(count - 1);
+ }
+
+ if (fading) {
+ thickness_factor = (offset_factor > fading_center) ?
+ (interpf(1 - fading_thickness, 1.0f, offset_factor - fading_center)) :
+ (interpf(
+ 1.0f, 1 - fading_thickness, offset_factor - fading_center + 1));
+ opacity_factor = (offset_factor > fading_center) ?
+ (interpf(1 - fading_opacity, 1.0f, offset_factor - fading_center)) :
+ (interpf(1.0f, 1 - fading_opacity, offset_factor - fading_center + 1));
+ }
+
+ for (int j = 0; j < new_gps->totpoints; j++) {
+ interp_v3_v3v3(&pt[j].x,
+ &t1_array[j * 3],
+ &t2_array[j * 3],
+ interpf(1 + offset, offset, offset_factor));
+ if (fading) {
+ pt[j].pressure = gps->points[j].pressure * thickness_factor;
+ pt[j].strength = gps->points[j].strength * opacity_factor;
+ }
+ }
+ }
+ MEM_freeN(t1_array);
+ MEM_freeN(t2_array);
+}
+
+static void bakeModifier(Main *UNUSED(bmain),
+ Depsgraph *depsgraph,
+ GpencilModifierData *md,
+ Object *ob)
+{
+
+ bGPdata *gpd = ob->data;
+
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ ListBase duplicates = {0};
+ MultiplyGpencilModifierData *mmd = (MultiplyGpencilModifierData *)md;
+ bGPDstroke *gps;
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ if (!is_stroke_affected_by_modifier(ob,
+ mmd->layername,
+ mmd->materialname,
+ mmd->pass_index,
+ mmd->layer_pass,
+ 1,
+ gpl,
+ gps,
+ mmd->flag & GP_MIRROR_INVERT_LAYER,
+ mmd->flag & GP_MIRROR_INVERT_PASS,
+ mmd->flag & GP_MIRROR_INVERT_LAYERPASS,
+ mmd->flag & GP_MIRROR_INVERT_MATERIAL)) {
+ continue;
+ }
+ if (mmd->flags & GP_MULTIPLY_ENABLE_ANGLE_SPLITTING) {
+ splitStroke(gpf, gps, mmd->split_angle);
+ }
+ if (mmd->flags & GP_MULTIPLY_ENABLE_DUPLICATION) {
+ duplicateStroke(gpf,
+ gps,
+ mmd->duplications,
+ mmd->distance,
+ mmd->offset,
+ &duplicates,
+ mmd->flags & GP_MULTIPLY_ENABLE_FADING,
+ mmd->fading_center,
+ mmd->fading_thickness,
+ mmd->fading_opacity);
+ }
+ }
+ if (duplicates.first) {
+ ((bGPDstroke *)gpf->strokes.last)->next = duplicates.first;
+ ((bGPDstroke *)duplicates.first)->prev = gpf->strokes.last;
+ gpf->strokes.last = duplicates.first;
+ }
+ }
+ }
+}
+
+/* -------------------------------- */
+
+/* Generic "generateStrokes" callback */
+static void generateStrokes(
+ GpencilModifierData *md, Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, bGPDframe *gpf)
+{
+ MultiplyGpencilModifierData *mmd = (MultiplyGpencilModifierData *)md;
+ bGPDstroke *gps;
+ ListBase duplicates = {0};
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ if (!is_stroke_affected_by_modifier(ob,
+ mmd->layername,
+ mmd->materialname,
+ mmd->pass_index,
+ mmd->layer_pass,
+ 1,
+ gpl,
+ gps,
+ mmd->flag & GP_MIRROR_INVERT_LAYER,
+ mmd->flag & GP_MIRROR_INVERT_PASS,
+ mmd->flag & GP_MIRROR_INVERT_LAYERPASS,
+ mmd->flag & GP_MIRROR_INVERT_MATERIAL)) {
+ continue;
+ }
+ if (mmd->flags & GP_MULTIPLY_ENABLE_ANGLE_SPLITTING) {
+ splitStroke(gpf, gps, mmd->split_angle);
+ }
+ if (mmd->flags & GP_MULTIPLY_ENABLE_DUPLICATION) {
+ duplicateStroke(gpf,
+ gps,
+ mmd->duplications,
+ mmd->distance,
+ mmd->offset,
+ &duplicates,
+ mmd->flags & GP_MULTIPLY_ENABLE_FADING,
+ mmd->fading_center,
+ mmd->fading_thickness,
+ mmd->fading_opacity);
+ }
+ }
+ if (duplicates.first) {
+ ((bGPDstroke *)gpf->strokes.last)->next = duplicates.first;
+ ((bGPDstroke *)duplicates.first)->prev = gpf->strokes.last;
+ gpf->strokes.last = duplicates.first;
+ }
+}
+
+GpencilModifierTypeInfo modifierType_Gpencil_Multiply = {
+ /* name */ "Multiple Strokes",
+ /* structName */ "MultiplyGpencilModifierData",
+ /* structSize */ sizeof(MultiplyGpencilModifierData),
+ /* type */ eGpencilModifierTypeType_Gpencil,
+ /* flags */ 0,
+
+ /* copyData */ copyData,
+
+ /* deformStroke */ NULL,
+ /* generateStrokes */ generateStrokes,
+ /* bakeModifier */ bakeModifier,
+ /* remapTime */ NULL,
+
+ /* initData */ initData,
+ /* freeData */ NULL,
+ /* isDisabled */ NULL,
+ /* updateDepsgraph */ NULL,
+ /* dependsOnTime */ NULL,
+ /* foreachObjectLink */ NULL,
+ /* foreachIDLink */ NULL,
+ /* foreachTexLink */ NULL,
+};
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index 7d407dc85bc..dc51e9669b2 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -46,6 +46,8 @@ typedef enum GpencilModifierType {
eGpencilModifierType_Mirror = 14,
eGpencilModifierType_Armature = 15,
eGpencilModifierType_Time = 16,
+ eGpencilModifierType_Length = 17,
+ eGpencilModifierType_Multiply = 18,
NUM_GREASEPENCIL_MODIFIER_TYPES,
} GpencilModifierType;
@@ -640,4 +642,62 @@ typedef struct ArmatureGpencilModifierData {
} ArmatureGpencilModifierData;
+/* XXX: all the length and similar parameters should have an image space behavior. */
+/* Need future investigations */
+
+typedef struct LengthGpencilModifierData {
+ GpencilModifierData modifier;
+ /** Layer name. */
+ char layername[64];
+ /** Material name. */
+ char materialname[64];
+ /** Custom index for passes. */
+ int pass_index;
+ /** Flags. */
+ int flag;
+ /** Custom index for passes. */
+ int layer_pass;
+ float length;
+ float percentage;
+ /** This ignores tip jittering when extending stroke. */
+ float tip_length;
+} LengthGpencilModifierData;
+
+typedef struct MultiplyGpencilModifierData {
+ GpencilModifierData modifier;
+ /** Layer name. */
+ char layername[64];
+ /** Material name. */
+ char materialname[64];
+ /** Custom index for passes. */
+ int pass_index;
+ /** Flags. */
+ int flag;
+ /** Custom index for passes. */
+ int layer_pass;
+ char _pad[4];
+
+ int flags;
+
+ int duplications;
+ float distance;
+ /* -1:inner 0:middle 1:outer */
+ float offset;
+
+ float fading_center;
+ float fading_thickness;
+ float fading_opacity;
+
+ /* in rad not deg */
+ float split_angle;
+
+ /* char _pad[4]; */
+} MultiplyGpencilModifierData;
+
+typedef enum eMultiplyGpencil_Flag {
+ GP_MULTIPLY_ENABLE_DUPLICATION = (1 << 0),
+ GP_MULTIPLY_ENABLE_ANGLE_SPLITTING = (1 << 1),
+ GP_MULTIPLY_ENABLE_FADING = (1 << 2),
+} eMultiplyGpencil_Flag;
+
#endif /* __DNA_GPENCIL_MODIFIER_TYPES_H__ */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 85bab0666ab..54462df6f48 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -322,6 +322,7 @@ extern StructRNA RNA_LatticeModifier;
extern StructRNA RNA_LatticePoint;
extern StructRNA RNA_LayerCollection;
extern StructRNA RNA_LayerObjects;
+extern StructRNA RNA_LengthGpencilModifier;
extern StructRNA RNA_Library;
extern StructRNA RNA_Light;
extern StructRNA RNA_LightProbe;
@@ -432,6 +433,7 @@ extern StructRNA RNA_MovieTrackingObject;
extern StructRNA RNA_MovieTrackingStabilization;
extern StructRNA RNA_MovieTrackingTrack;
extern StructRNA RNA_MulticamSequence;
+extern StructRNA RNA_MultiplyGpencilModifier;
extern StructRNA RNA_MultiresModifier;
extern StructRNA RNA_MusgraveTexture;
extern StructRNA RNA_NandController;
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index d3094eca9fd..b5279c40e19 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -81,6 +81,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_SUBSURF,
"Subdivide",
"Subdivide stroke adding more control points"},
+ {eGpencilModifierType_Multiply,
+ "GP_MULTIPLY",
+ ICON_GP_MULTIFRAME_EDITING,
+ "Multiple Strokes",
+ "Produce multiple strokes along one stroke"},
{0, "", 0, N_("Deform"), ""},
{eGpencilModifierType_Armature,
"GP_ARMATURE",
@@ -110,6 +115,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
"Thickness",
"Change stroke thickness"},
{eGpencilModifierType_Time, "GP_TIME", ICON_MOD_TIME, "Time Offset", "Offset keyframes"},
+ {eGpencilModifierType_Length,
+ "GP_LENGTH",
+ ICON_MOD_EDGESPLIT,
+ "Length",
+ "Extend or shrink strokes"},
{0, "", 0, N_("Color"), ""},
{eGpencilModifierType_Color,
"GP_COLOR",
@@ -217,6 +227,10 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr)
return &RNA_OffsetGpencilModifier;
case eGpencilModifierType_Armature:
return &RNA_ArmatureGpencilModifier;
+ case eGpencilModifierType_Length:
+ return &RNA_LengthGpencilModifier;
+ case eGpencilModifierType_Multiply:
+ return &RNA_MultiplyGpencilModifier;
/* Default */
case eGpencilModifierType_None:
case NUM_GREASEPENCIL_MODIFIER_TYPES:
@@ -1866,6 +1880,180 @@ static void rna_def_modifier_gpencilarmature(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
}
+static void rna_def_modifier_gpencillength(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "LengthGpencilModifier", "GpencilModifier");
+ RNA_def_struct_ui_text(srna, "Length Modifier", "Stretch or shrink strokes");
+ RNA_def_struct_sdna(srna, "LengthGpencilModifierData");
+ RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT);
+
+ prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "layername");
+ RNA_def_property_ui_text(prop, "Layer", "Layer name");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "material", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "materialname");
+ RNA_def_property_ui_text(prop, "Material", "Material name");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "pass_index");
+ RNA_def_property_range(prop, 0, 100);
+ RNA_def_property_ui_text(prop, "Pass", "Pass index");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_INVERT_LAYER);
+ RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_INVERT_MATERIAL);
+ RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_INVERT_PASS);
+ RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "layer_pass", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "layer_pass");
+ RNA_def_property_range(prop, 0, 100);
+ RNA_def_property_ui_text(prop, "Pass", "Layer pass index");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_INVERT_LAYERPASS);
+ RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "length", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, -10.0f, 10.0f);
+ RNA_def_property_ui_text(prop, "Length", "Length of each segment");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "percentage", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, -1.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Percentage", "Length based on the curve's original length");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "tip_length", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_float_default(prop, 0.01);
+ RNA_def_property_ui_text(prop, "Tip Length", "Ignore tip jittering when extending a stroke");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+}
+
+static void rna_def_modifier_gpencilmultiply(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "MultiplyGpencilModifier", "GpencilModifier");
+ RNA_def_struct_ui_text(srna, "Multiply Modifier", "Generate multiple strokes from one stroke");
+ RNA_def_struct_sdna(srna, "MultiplyGpencilModifierData");
+ RNA_def_struct_ui_icon(srna, ICON_GP_MULTIFRAME_EDITING);
+
+ prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "layername");
+ RNA_def_property_ui_text(prop, "Layer", "Layer name");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "material", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "materialname");
+ RNA_def_property_ui_text(prop, "Material", "Material name");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "pass_index");
+ RNA_def_property_range(prop, 0, 100);
+ RNA_def_property_ui_text(prop, "Pass", "Pass index");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_INVERT_LAYER);
+ RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_INVERT_MATERIAL);
+ RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_INVERT_PASS);
+ RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "layer_pass", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "layer_pass");
+ RNA_def_property_range(prop, 0, 100);
+ RNA_def_property_ui_text(prop, "Pass", "Layer pass index");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_INVERT_LAYERPASS);
+ RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "enable_duplication", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", GP_MULTIPLY_ENABLE_DUPLICATION);
+ RNA_def_property_ui_text(prop, "Duplication", "Enable stroke duplication");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "enable_angle_splitting", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", GP_MULTIPLY_ENABLE_ANGLE_SPLITTING);
+ RNA_def_property_ui_text(prop, "Angle Splitting", "Enable angle splitting");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "enable_fading", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", GP_MULTIPLY_ENABLE_FADING);
+ RNA_def_property_ui_text(prop, "Enable Fading", "Enable fading");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "split_angle", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0, M_PI);
+ RNA_def_property_ui_text(prop, "Angle", "Split angle for segments");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "duplications", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 0, 10);
+ RNA_def_property_ui_text(prop, "Duplications", "How many copies of strokes be displayed");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0, M_PI);
+ RNA_def_property_ui_text(prop, "Distance", "Distance of duplications.");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_ui_range(prop, -1, 1, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Offset", "Offset of duplications. -1 to 1: inner to outer");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_thickness", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0, 1);
+ RNA_def_property_float_default(prop, 0.5);
+ RNA_def_property_ui_text(prop, "Thickness", "Fade influence of stroke's thickness");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_opacity", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0, 1);
+ RNA_def_property_float_default(prop, 0.5);
+ RNA_def_property_ui_text(prop, "Opacity", "Fade influence of stroke's opacity");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_center", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0, 1);
+ RNA_def_property_ui_text(prop, "Center", "Fade center");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+}
+
void RNA_def_greasepencil_modifier(BlenderRNA *brna)
{
StructRNA *srna;
@@ -1938,6 +2126,8 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna)
rna_def_modifier_gpencilmirror(brna);
rna_def_modifier_gpencilhook(brna);
rna_def_modifier_gpencilarmature(brna);
+ rna_def_modifier_gpencillength(brna);
+ rna_def_modifier_gpencilmultiply(brna);
}
#endif
diff --git a/source/tools b/source/tools
-Subproject ce943dad8a21b4784e2dcef12d8f893473cddb7
+Subproject 2afbb8ec472cac5102eb239f57b006f8c938768