diff options
author | YimingWu <xp8110t@outlook.com> | 2019-11-14 21:18:23 +0300 |
---|---|---|
committer | Antonio Vazquez <blendergit@gmail.com> | 2019-11-14 21:24:49 +0300 |
commit | 91248876e517983531c44ffc1692674684c67eed (patch) | |
tree | b2e1bf063dedac5103a8105c50cea6ca80335b18 /source/blender | |
parent | 8ff9eb97fb7c0e25acaa051ea8ae196c932b10b6 (diff) |
GPencil MultiStroke modifier
This patch includes a modifiers that developed for NPR rendering.
- MultiStroke modifier that generates multiple strokes around the original ones.
Differential Revision: https://developer.blender.org/D5795
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/BKE_gpencil.h | 18 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/gpencil.c | 268 | ||||
-rw-r--r-- | source/blender/gpencil_modifiers/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h | 1 | ||||
-rw-r--r-- | source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c | 1 | ||||
-rw-r--r-- | source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c | 350 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_gpencil_modifier_types.h | 37 | ||||
-rw-r--r-- | source/blender/makesrna/RNA_access.h | 1 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_gpencil_modifier.c | 108 |
9 files changed, 785 insertions, 0 deletions
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..df18f89da6f 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,240 @@ 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 +2519,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..44335f4da25 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -50,6 +50,7 @@ set(SRC intern/MOD_gpencilhook.c intern/MOD_gpencillattice.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..f5c064c1c07 100644 --- a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h +++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h @@ -42,6 +42,7 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_Hook; extern GpencilModifierTypeInfo modifierType_Gpencil_Offset; extern GpencilModifierTypeInfo modifierType_Gpencil_Armature; extern GpencilModifierTypeInfo modifierType_Gpencil_Time; +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..16e585705df 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -71,6 +71,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]) INIT_GP_TYPE(Offset); INIT_GP_TYPE(Armature); INIT_GP_TYPE(Time); + INIT_GP_TYPE(Multiply); #undef INIT_GP_TYPE } 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..98988783862 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c @@ -0,0 +1,350 @@ +/* + * ***** 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->duplications = 3; + mmd->distance = 0.1f; + 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->duplications > 0) { + 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->duplications > 0) { + 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..5fe12998998 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -46,6 +46,7 @@ typedef enum GpencilModifierType { eGpencilModifierType_Mirror = 14, eGpencilModifierType_Armature = 15, eGpencilModifierType_Time = 16, + eGpencilModifierType_Multiply = 17, NUM_GREASEPENCIL_MODIFIER_TYPES, } GpencilModifierType; @@ -640,4 +641,40 @@ typedef struct ArmatureGpencilModifierData { } ArmatureGpencilModifierData; +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_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..a1369d84786 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -432,6 +432,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..4352b6628e2 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", @@ -217,6 +222,8 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr) return &RNA_OffsetGpencilModifier; case eGpencilModifierType_Armature: return &RNA_ArmatureGpencilModifier; + case eGpencilModifierType_Multiply: + return &RNA_MultiplyGpencilModifier; /* Default */ case eGpencilModifierType_None: case NUM_GREASEPENCIL_MODIFIER_TYPES: @@ -1866,6 +1873,106 @@ static void rna_def_modifier_gpencilarmature(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_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_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 +2045,7 @@ 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_gpencilmultiply(brna); } #endif |