From 66da2f537ae80ce2b31d1eaf34ad8c03d858938d Mon Sep 17 00:00:00 2001 From: Antonioya Date: Tue, 31 Jul 2018 10:22:19 +0200 Subject: New Grease Pencil object for 2D animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit merge the full development done in greasepencil-object branch and include mainly the following features. - New grease pencil object. - New drawing engine. - New grease pencil modes Draw/Sculpt/Edit and Weight Paint. - New brushes for grease pencil. - New modifiers for grease pencil. - New shaders FX. - New material system (replace old palettes and colors). - Split of annotations (old grease pencil) and new grease pencil object. - UI adapted to blender 2.8. You can get more info here: https://code.blender.org/2017/12/drawing-2d-animation-in-blender-2-8/ https://code.blender.org/2018/07/grease-pencil-status-update/ This is the result of nearly two years of development and I want thanks firstly the other members of the grease pencil team: Daniel M. Lara, Matias Mendiola and Joshua Leung for their support, ideas and to keep working in the project all the time, without them this project had been impossible. Also, I want thanks other Blender developers for their help, advices and to be there always to help me, and specially to Clément Foucault, Dalai Felinto, Pablo Vázquez and Campbell Barton. --- source/blender/gpencil_modifiers/CMakeLists.txt | 70 +++ .../gpencil_modifiers/MOD_gpencil_modifiertypes.h | 53 ++ .../gpencil_modifiers/intern/MOD_gpencil_util.c | 142 ++++++ .../gpencil_modifiers/intern/MOD_gpencil_util.h | 47 ++ .../gpencil_modifiers/intern/MOD_gpencilbuild.c | 558 +++++++++++++++++++++ .../gpencil_modifiers/intern/MOD_gpencilcolor.c | 178 +++++++ .../gpencil_modifiers/intern/MOD_gpencilhook.c | 355 +++++++++++++ .../gpencil_modifiers/intern/MOD_gpencilinstance.c | 360 +++++++++++++ .../gpencil_modifiers/intern/MOD_gpencillattice.c | 213 ++++++++ .../gpencil_modifiers/intern/MOD_gpencilmirror.c | 239 +++++++++ .../gpencil_modifiers/intern/MOD_gpencilnoise.c | 285 +++++++++++ .../gpencil_modifiers/intern/MOD_gpenciloffset.c | 143 ++++++ .../gpencil_modifiers/intern/MOD_gpencilopacity.c | 171 +++++++ .../gpencil_modifiers/intern/MOD_gpencilsimplify.c | 123 +++++ .../gpencil_modifiers/intern/MOD_gpencilsmooth.c | 152 ++++++ .../gpencil_modifiers/intern/MOD_gpencilsubdiv.c | 193 +++++++ .../gpencil_modifiers/intern/MOD_gpencilthick.c | 171 +++++++ .../gpencil_modifiers/intern/MOD_gpenciltint.c | 186 +++++++ 18 files changed, 3639 insertions(+) create mode 100644 source/blender/gpencil_modifiers/CMakeLists.txt create mode 100644 source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilinstance.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c (limited to 'source/blender/gpencil_modifiers') diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt new file mode 100644 index 00000000000..5ad91d4e01b --- /dev/null +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -0,0 +1,70 @@ +# ***** 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) 2018, Blender Foundation +# All rights reserved. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + intern + ../blenkernel + ../blenlib + ../blenfont + ../depsgraph + ../makesdna + ../makesrna + ../bmesh + ../render/extern/include + ../../../intern/elbeem/extern + ../../../intern/guardedalloc + ../../../intern/eigen +) + +set(INC_SYS + ${ZLIB_INCLUDE_DIRS} +) + +set(SRC + intern/MOD_gpencil_util.h + + intern/MOD_gpencil_util.c + intern/MOD_gpencilnoise.c + intern/MOD_gpencilsubdiv.c + intern/MOD_gpencilsimplify.c + intern/MOD_gpencilthick.c + intern/MOD_gpenciltint.c + intern/MOD_gpencilcolor.c + intern/MOD_gpencilinstance.c + intern/MOD_gpencilbuild.c + intern/MOD_gpencilopacity.c + intern/MOD_gpencillattice.c + intern/MOD_gpencilmirror.c + intern/MOD_gpencilsmooth.c + intern/MOD_gpencilhook.c + intern/MOD_gpenciloffset.c + + MOD_gpencil_modifiertypes.h +) + +if(WITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) +endif() + +add_definitions(${GL_DEFINITIONS}) + +blender_add_lib(bf_gpencil_modifiers "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h new file mode 100644 index 00000000000..ca941017ff9 --- /dev/null +++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h @@ -0,0 +1,53 @@ +/* + * ***** 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. + * + * Contributor(s): Ben Batt + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file MOD_modifiertypes.h + * \ingroup modifiers + */ + +#ifndef __MOD_GP_MODIFIERTYPES_H__ +#define __MOD_GP_MODIFIERTYPES_H__ + +#include "BKE_gpencil_modifier.h" + +/* ****************** Type structures for all modifiers ****************** */ + +extern GpencilModifierTypeInfo modifierType_Gpencil_None; +extern GpencilModifierTypeInfo modifierType_Gpencil_Noise; +extern GpencilModifierTypeInfo modifierType_Gpencil_Subdiv; +extern GpencilModifierTypeInfo modifierType_Gpencil_Simplify; +extern GpencilModifierTypeInfo modifierType_Gpencil_Thick; +extern GpencilModifierTypeInfo modifierType_Gpencil_Tint; +extern GpencilModifierTypeInfo modifierType_Gpencil_Color; +extern GpencilModifierTypeInfo modifierType_Gpencil_Instance; +extern GpencilModifierTypeInfo modifierType_Gpencil_Build; +extern GpencilModifierTypeInfo modifierType_Gpencil_Opacity; +extern GpencilModifierTypeInfo modifierType_Gpencil_Lattice; +extern GpencilModifierTypeInfo modifierType_Gpencil_Mirror; +extern GpencilModifierTypeInfo modifierType_Gpencil_Smooth; +extern GpencilModifierTypeInfo modifierType_Gpencil_Hook; +extern GpencilModifierTypeInfo modifierType_Gpencil_Offset; + +/* MOD_gpencil_util.c */ +void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]); + +#endif /* __MOD_GP_MODIFIERTYPES_H__ */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c new file mode 100644 index 00000000000..97d28863095 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -0,0 +1,142 @@ +/* + * ***** 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 + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/modifiers/intern/MOD_gpencil_util.c + * \ingroup bke + */ + + +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" +#include "BLI_math_color.h" +#include "BLI_rand.h" + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BKE_global.h" +#include "BKE_object.h" +#include "BKE_lattice.h" +#include "BKE_material.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_colortools.h" + +#include "MOD_gpencil_modifiertypes.h" +#include "MOD_gpencil_util.h" + +void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]) +{ +#define INIT_GP_TYPE(typeName) (types[eGpencilModifierType_##typeName] = &modifierType_Gpencil_##typeName) + INIT_GP_TYPE(Noise); + INIT_GP_TYPE(Subdiv); + INIT_GP_TYPE(Simplify); + INIT_GP_TYPE(Thick); + INIT_GP_TYPE(Tint); + INIT_GP_TYPE(Color); + INIT_GP_TYPE(Instance); + INIT_GP_TYPE(Build); + INIT_GP_TYPE(Opacity); + INIT_GP_TYPE(Lattice); + INIT_GP_TYPE(Mirror); + INIT_GP_TYPE(Smooth); + INIT_GP_TYPE(Hook); + INIT_GP_TYPE(Offset); +#undef INIT_GP_TYPE +} + +/* verify if valid layer and pass index */ +bool is_stroke_affected_by_modifier( + Object *ob, char *mlayername, int mpassindex, int minpoints, + bGPDlayer *gpl, bGPDstroke *gps, bool inv1, bool inv2) +{ + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + + /* omit if filter by layer */ + if (mlayername[0] != '\0') { + if (inv1 == false) { + if (!STREQ(mlayername, gpl->info)) { + return false; + } + } + else { + if (STREQ(mlayername, gpl->info)) { + return false; + } + } + } + /* verify pass */ + if (mpassindex > 0) { + if (inv2 == false) { + if (gp_style->index != mpassindex) { + return false; + } + } + else { + if (gp_style->index == mpassindex) { + return false; + } + } + } + /* need to have a minimum number of points */ + if ((minpoints > 0) && (gps->totpoints < minpoints)) { + return false; + } + + return true; +} + +/* verify if valid vertex group *and return weight */ +float get_modifier_point_weight(MDeformVert *dvert, int inverse, int vindex) +{ + float weight = 1.0f; + + if (vindex >= 0) { + weight = BKE_gpencil_vgroup_use_index(dvert, vindex); + if ((weight >= 0.0f) && (inverse == 1)) { + return -1.0f; + } + + if ((weight < 0.0f) && (inverse == 0)) { + return -1.0f; + } + + /* if inverse, weight is always 1 */ + if ((weight < 0.0f) && (inverse == 1)) { + return 1.0f; + } + + } + + return weight; +} diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h new file mode 100644 index 00000000000..50ac557042d --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h @@ -0,0 +1,47 @@ +/* + * ***** 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) Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/modifiers/intern/MOD_gpencil_util.h + * \ingroup modifiers + */ + + +#ifndef __MOD_GPENCIL_UTIL_H__ +#define __MOD_GPENCIL_UTIL_H__ + +struct Object; +struct bGPDlayer; +struct bGPDstroke; +struct MDeformVert; + +bool is_stroke_affected_by_modifier( + struct Object *ob, char *mlayername, int mpassindex, int minpoints, + bGPDlayer *gpl, bGPDstroke *gps, bool inv1, bool inv2); + +float get_modifier_point_weight(struct MDeformVert *dvert, int inverse, int vindex); + +#endif /* __MOD_GPENCIL_UTIL_H__ */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c new file mode 100644 index 00000000000..6b959659a60 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c @@ -0,0 +1,558 @@ +/* + * ***** 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 + * + * Contributor(s): Antonio Vazquez, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilbuild.c + * \ingroup modifiers + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_meshdata_types.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_math.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + BuildGpencilModifierData *gpmd = (BuildGpencilModifierData *)md; + + /* We deliberately set this range to the half the default + * frame-range to have an immediate effect ot suggest use-cases + */ + gpmd->start_frame = 1; + gpmd->end_frame = 125; + + /* Init default length of each build effect - Nothing special */ + gpmd->start_delay = 0.0f; + gpmd->length = 100.0f; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +static bool dependsOnTime(GpencilModifierData *UNUSED(md)) +{ + return true; +} + +/* ******************************************** */ +/* Build Modifier - Stroke generation logic + * + * There are two modes for how the strokes are sequenced (at a macro-level): + * - Sequential Mode - Strokes appear/disappear one after the other. Only a single one changes at a time. + * - Concurrent Mode - Multiple strokes appear/disappear at once. + * + * Assumptions: + * - Stroke points are generally equally spaced. This implies that we can just add/remove points, + * without worrying about distances between them / adding extra interpolated points between + * an visible point and one about to be added/removed (or any similar tapering effects). + + * - All strokes present are fully visible (i.e. we don't have to ignore any) + */ + +/* Remove a particular stroke */ +static void clear_stroke(bGPDframe *gpf, bGPDstroke *gps) +{ + BLI_remlink(&gpf->strokes, gps); + BKE_gpencil_free_stroke(gps); +} + +/* Clear all strokes in frame */ +static void gpf_clear_all_strokes(bGPDframe *gpf) +{ + bGPDstroke *gps, *gps_next; + for (gps = gpf->strokes.first; gps; gps = gps_next) { + gps_next = gps->next; + clear_stroke(gpf, gps); + } + BLI_listbase_clear(&gpf->strokes); +} + +/* Reduce the number of points in the stroke + * + * Note: This won't be called if all points are present/removed + * TODO: Allow blending of growing/shrinking tip (e.g. for more gradual transitions) + */ +static void reduce_stroke_points(bGPDstroke *gps, const int num_points, const eBuildGpencil_Transition transition) +{ + bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * num_points, __func__); + MDeformVert *new_dvert = MEM_callocN(sizeof(MDeformVert) * num_points, __func__); + + /* Which end should points be removed from */ + // TODO: free stroke weights + switch (transition) { + case GP_BUILD_TRANSITION_GROW: /* Show in forward order = Remove ungrown-points from end of stroke */ + case GP_BUILD_TRANSITION_SHRINK: /* Hide in reverse order = Remove dead-points from end of stroke */ + { + /* copy over point data */ + memcpy(new_points, gps->points, sizeof(bGPDspoint) * num_points); + memcpy(new_dvert, gps->dvert, sizeof(MDeformVert) * num_points); + + /* free unused point weights */ + for (int i = num_points; i < gps->totpoints; i++) { + MDeformVert *dvert = &gps->dvert[i]; + BKE_gpencil_free_point_weights(dvert); + } + + break; + } + + /* Hide in forward order = Remove points from start of stroke */ + case GP_BUILD_TRANSITION_FADE: + { + /* num_points is the number of points left after reducing. + * We need to know how many to remove + */ + const int offset = gps->totpoints - num_points; + + /* copy over point data */ + memcpy(new_points, gps->points + offset, sizeof(bGPDspoint) * num_points); + memcpy(new_dvert, gps->dvert + offset, sizeof(MDeformVert) * num_points); + + /* free unused weights */ + for (int i = 0; i < offset; i++) { + MDeformVert *dvert = &gps->dvert[i]; + BKE_gpencil_free_point_weights(dvert); + } + + break; + } + + default: + printf("ERROR: Unknown transition %d in %s()\n", (int)transition, __func__); + break; + } + + /* replace stroke geometry */ + MEM_SAFE_FREE(gps->points); + MEM_SAFE_FREE(gps->dvert); + gps->points = new_points; + gps->dvert = new_dvert; + gps->totpoints = num_points; + + /* mark stroke as needing to have its geometry caches rebuilt */ + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + MEM_SAFE_FREE(gps->triangles); +} + +/* --------------------------------------------- */ + +/* Stroke Data Table Entry - This represents one stroke being generated */ +typedef struct tStrokeBuildDetails { + bGPDstroke *gps; + + /* Indices - first/last indices for the stroke's points (overall) */ + size_t start_idx, end_idx; + + /* Number of points - Cache for more convenient access */ + int totpoints; +} tStrokeBuildDetails; + + +/* Sequential - Show strokes one after the other */ +static void build_sequential(BuildGpencilModifierData *mmd, bGPDframe *gpf, float fac) +{ + const size_t tot_strokes = BLI_listbase_count(&gpf->strokes); + bGPDstroke *gps; + size_t i; + + /* 1) Compute proportion of time each stroke should occupy */ + /* NOTE: This assumes that the total number of points won't overflow! */ + tStrokeBuildDetails *table = MEM_callocN(sizeof(tStrokeBuildDetails) * tot_strokes, __func__); + size_t totpoints = 0; + + /* 1.1) First pass - Tally up points */ + for (gps = gpf->strokes.first, i = 0; gps; gps = gps->next, i++) { + tStrokeBuildDetails *cell = &table[i]; + + cell->gps = gps; + cell->totpoints = gps->totpoints; + + totpoints += cell->totpoints; + } + + /* 1.2) Second pass - Compute the overall indices for points */ + for (i = 0; i < tot_strokes; i++) { + tStrokeBuildDetails *cell = &table[i]; + + if (i == 0) { + cell->start_idx = 0; + } + else { + cell->start_idx = (cell - 1)->end_idx; + } + cell->end_idx = cell->start_idx + cell->totpoints - 1; + } + + + /* 2) Determine the global indices for points that should be visible */ + size_t first_visible = 0; + size_t last_visible = 0; + + switch (mmd->transition) { + /* Show in forward order + * - As fac increases, the number of visible points increases + */ + case GP_BUILD_TRANSITION_GROW: + first_visible = 0; /* always visible */ + last_visible = (size_t)roundf(totpoints * fac); + break; + + /* Hide in reverse order + * - As fac increases, the number of points visible at the end decreases + */ + case GP_BUILD_TRANSITION_SHRINK: + first_visible = 0; /* always visible (until last point removed) */ + last_visible = (size_t)(totpoints * (1.0f - fac)); + break; + + /* Hide in forward order + * - As fac increases, the early points start getting hidden + */ + case GP_BUILD_TRANSITION_FADE: + first_visible = (size_t)(totpoints * fac); + last_visible = totpoints; /* i.e. visible until the end, unless first overlaps this */ + break; + } + + + /* 3) Go through all strokes, deciding which to keep, and/or how much of each to keep */ + for (i = 0; i < tot_strokes; i++) { + tStrokeBuildDetails *cell = &table[i]; + + /* Determine what portion of the stroke is visible */ + if ((cell->end_idx < first_visible) || (cell->start_idx > last_visible)) { + /* Not visible at all - Either ended before */ + clear_stroke(gpf, cell->gps); + } + else { + /* Some proportion of stroke is visible */ + /* XXX: Will the transition settings still be valid now? */ + if ((first_visible <= cell->start_idx) && (last_visible >= cell->end_idx)) { + /* Do nothing - whole stroke is visible */ + } + else if (first_visible > cell->start_idx) { + /* Starts partway through this stroke */ + int num_points = cell->end_idx - first_visible; + reduce_stroke_points(cell->gps, num_points, mmd->transition); + } + else { + /* Ends partway through this stroke */ + int num_points = last_visible - cell->start_idx; + reduce_stroke_points(cell->gps, num_points, mmd->transition); + } + } + } + + /* Free table */ + MEM_freeN(table); +} + +/* --------------------------------------------- */ + +/* Concurrent - Show multiple strokes at once */ +// TODO: Allow random offsets to start times +// TODO: Allow varying speeds? Scaling of progress? +static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, float fac) +{ + bGPDstroke *gps, *gps_next; + int max_points = 0; + + const bool reverse = (mmd->transition != GP_BUILD_TRANSITION_GROW); + + /* 1) Determine the longest stroke, to figure out when short strokes should start */ + /* FIXME: A *really* long stroke here could dwarf everything else, causing bad timings */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + if (gps->totpoints > max_points) { + max_points = gps->totpoints; + } + } + if (max_points == 0) { + printf("ERROR: Strokes are all empty (GP Build Modifier: %s)\n", __func__); + return; + } + + /* 2) For each stroke, determine how it should be handled */ + for (gps = gpf->strokes.first; gps; gps = gps_next) { + gps_next = gps->next; + + /* Relative Length of Stroke - Relative to the longest stroke, + * what proportion of the available time should this stroke use + */ + const float relative_len = (float)gps->totpoints / (float)max_points; + + /* Determine how many points should be left in the stroke */ + int num_points = 0; + + switch (mmd->time_alignment) { + case GP_BUILD_TIMEALIGN_START: /* all start on frame 1 */ + { + /* Build effect occurs over when fac = 0, to fac = relative_len */ + if (fac <= relative_len) { + /* Scale fac to fit relative_len */ + /* FIXME: prevent potential div by zero (e.g. very short stroke vs one very long one) */ + const float scaled_fac = fac / relative_len; + + if (reverse) { + num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints); + } + else { + num_points = (int)roundf(scaled_fac * gps->totpoints); + } + } + else { + /* Build effect has ended */ + if (reverse) { + num_points = 0; + } + else { + num_points = gps->totpoints; + } + } + + break; + } + case GP_BUILD_TIMEALIGN_END: /* all end on same frame */ + { + /* Build effect occurs over 1.0 - relative_len, to 1.0 (i.e. over the end of the range) */ + const float start_fac = 1.0f - relative_len; + + if (fac >= start_fac) { + /* FIXME: prevent potential div by zero (e.g. very short stroke vs one very long one) */ + const float scaled_fac = (fac - start_fac) / relative_len; + + if (reverse) { + num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints); + } + else { + num_points = (int)roundf(scaled_fac * gps->totpoints); + } + } + else { + /* Build effect hasn't started */ + if (reverse) { + num_points = gps->totpoints; + } + else { + num_points = 0; + } + } + + break; + } + + /* TODO... */ + } + + /* Modify the stroke geometry */ + if (num_points <= 0) { + /* Nothing Left - Delete the stroke */ + clear_stroke(gpf, gps); + } + else if (num_points < gps->totpoints) { + /* Remove some points */ + reduce_stroke_points(gps, num_points, mmd->transition); + } + } +} + +/* --------------------------------------------- */ + +/* Entry-point for Build Modifier */ +static void generateStrokes( + GpencilModifierData *md, Depsgraph *depsgraph, + Object *UNUSED(ob), bGPDlayer *gpl, bGPDframe *gpf) +{ + BuildGpencilModifierData *mmd = (BuildGpencilModifierData *)md; + const bool reverse = (mmd->transition != GP_BUILD_TRANSITION_GROW); + + const float ctime = DEG_get_ctime(depsgraph); + //printf("GP Build Modifier - %f\n", ctime); + + /* Early exit if it's an empty frame */ + if (gpf->strokes.first == NULL) { + return; + } + + /* Omit layer if filter by layer */ + if (mmd->layername[0] != '\0') { + if ((mmd->flag & GP_BUILD_INVERT_LAYER) == 0) { + if (!STREQ(mmd->layername, gpl->info)) { + return; + } + } + else { + if (STREQ(mmd->layername, gpl->info)) { + return; + } + } + } + + /* Early exit if outside of the frame range for this modifier + * (e.g. to have one forward, and one backwards modifier) + */ + if (mmd->flag & GP_BUILD_RESTRICT_TIME) { + if ((ctime < mmd->start_frame) || (ctime > mmd->end_frame)) { + return; + } + } + + /* Compute start and end frames for the animation effect + * By default, the upper bound is given by the "maximum length" setting + */ + float start_frame = gpf->framenum + mmd->start_delay; + float end_frame = gpf->framenum + mmd->length; + + if (gpf->next) { + /* Use the next frame or upper bound as end frame, whichever is lower/closer */ + end_frame = MIN2(end_frame, gpf->next->framenum); + } + + + /* Early exit if current frame is outside start/end bounds */ + /* NOTE: If we're beyond the next/prev frames (if existent), then we wouldn't have this problem anyway... */ + if (ctime < start_frame) { + /* Before Start - Animation hasn't started. Display initial state. */ + if (reverse) { + /* 1) Reverse = Start with all, end with nothing. + * ==> Do nothing (everything already present) + */ + } + else { + /* 2) Forward Order = Start with nothing, end with the full frame. + * ==> Free all strokes, and return an empty frame + */ + gpf_clear_all_strokes(gpf); + } + + /* Early exit */ + return; + } + else if (ctime >= end_frame) { + /* Past End - Animation finished. Display final result. */ + if (reverse) { + /* 1) Reverse = Start with all, end with nothing. + * ==> Free all strokes, and return an empty frame + */ + gpf_clear_all_strokes(gpf); + } + else { + /* 2) Forward Order = Start with nothing, end with the full frame. + * ==> Do Nothing (everything already present) + */ + } + + /* Early exit */ + return; + } + + + /* Determine how far along we are between the keyframes */ + float fac = (ctime - start_frame) / (end_frame - start_frame); + //printf(" Progress on %d = %f (%f - %f)\n", gpf->framenum, fac, start_frame, end_frame); + + /* Time management mode */ + switch (mmd->mode) { + case GP_BUILD_MODE_SEQUENTIAL: + build_sequential(mmd, gpf, fac); + break; + + case GP_BUILD_MODE_CONCURRENT: + build_concurrent(mmd, gpf, fac); + break; + + default: + printf("Unsupported build mode (%d) for GP Build Modifier: '%s'\n", mmd->mode, mmd->modifier.name); + break; + } +} + +/* ******************************************** */ + +/* FIXME: Baking the Build Modifier is currently unsupported. + * Adding support for this is more complicated than for other + * modifiers, as to implement this, we'd have to add more frames, + * which would in turn break how the modifier functions. + */ +#if 0 +static void bakeModifier( + Main *bmain, const Depsgraph *UNUSED(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) { + + } + } +} +#endif + +/* ******************************************** */ + +GpencilModifierTypeInfo modifierType_Gpencil_Build = { + /* name */ "Build", + /* structName */ "BuildGpencilModifierData", + /* structSize */ sizeof(BuildGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ 0, + + /* copyData */ copyData, + + /* deformStroke */ NULL, + /* generateStrokes */ generateStrokes, + /* bakeModifier */ NULL, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ dependsOnTime, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c new file mode 100644 index 00000000000..af00b24715f --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c @@ -0,0 +1,178 @@ +/* + * ***** 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 + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilcolor.c + * \ingroup modifiers + */ + +#include + +#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_ghash.h" +#include "BLI_math_color.h" +#include "BLI_math_vector.h" +#include "BLI_utildefines.h" + +#include "BKE_global.h" +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_main.h" +#include "BKE_material.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + ColorGpencilModifierData *gpmd = (ColorGpencilModifierData *)md; + gpmd->pass_index = 0; + ARRAY_SET_ITEMS(gpmd->hsv, 1.0f, 1.0f, 1.0f); + gpmd->layername[0] = '\0'; + gpmd->flag |= GP_COLOR_CREATE_COLORS; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* color correction strokes */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + + ColorGpencilModifierData *mmd = (ColorGpencilModifierData *)md; + float hsv[3], factor[3]; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 1, gpl, gps, + mmd->flag & GP_COLOR_INVERT_LAYER, mmd->flag & GP_COLOR_INVERT_PASS)) + { + return; + } + + copy_v3_v3(factor, mmd->hsv); + add_v3_fl(factor, -1.0f); + + rgb_to_hsv_v(gps->runtime.tmp_stroke_rgba, hsv); + add_v3_v3(hsv, factor); + CLAMP3(hsv, 0.0f, 1.0f); + hsv_to_rgb_v(hsv, gps->runtime.tmp_stroke_rgba); + + rgb_to_hsv_v(gps->runtime.tmp_fill_rgba, hsv); + add_v3_v3(hsv, factor); + CLAMP3(hsv, 0.0f, 1.0f); + hsv_to_rgb_v(hsv, gps->runtime.tmp_fill_rgba); +} + +static void bakeModifier( + Main *bmain, Depsgraph *depsgraph, + GpencilModifierData *md, Object *ob) +{ + ColorGpencilModifierData *mmd = (ColorGpencilModifierData *)md; + bGPdata *gpd = ob->data; + + GHash *gh_color = BLI_ghash_str_new("GP_Color modifier"); + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + + Material *mat = give_current_material(ob, gps->mat_nr + 1); + if (mat == NULL) + continue; + MaterialGPencilStyle *gp_style = mat->gp_style; + /* skip stroke if it doesn't have color info */ + if (ELEM(NULL, gp_style)) + continue; + + copy_v4_v4(gps->runtime.tmp_stroke_rgba, gp_style->stroke_rgba); + copy_v4_v4(gps->runtime.tmp_fill_rgba, gp_style->fill_rgba); + + /* look for color */ + if (mmd->flag & GP_TINT_CREATE_COLORS) { + Material *newmat = BLI_ghash_lookup(gh_color, mat->id.name); + if (newmat == NULL) { + BKE_object_material_slot_add(bmain, ob); + newmat = BKE_material_copy(bmain, mat); + assign_material(bmain, ob, newmat, ob->totcol, BKE_MAT_ASSIGN_EXISTING); + + copy_v4_v4(newmat->gp_style->stroke_rgba, gps->runtime.tmp_stroke_rgba); + copy_v4_v4(newmat->gp_style->fill_rgba, gps->runtime.tmp_fill_rgba); + + BLI_ghash_insert(gh_color, mat->id.name, newmat); + } + /* reasign color index */ + int idx = BKE_object_material_slot_find_index(ob, newmat); + gps->mat_nr = idx - 1; + } + else { + /* reuse existing color */ + copy_v4_v4(gp_style->stroke_rgba, gps->runtime.tmp_stroke_rgba); + copy_v4_v4(gp_style->fill_rgba, gps->runtime.tmp_fill_rgba); + } + + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } + /* free hash buffers */ + if (gh_color) { + BLI_ghash_free(gh_color, NULL, NULL); + gh_color = NULL; + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Color = { + /* name */ "Hue/Saturation", + /* structName */ "ColorGpencilModifierData", + /* structSize */ sizeof(ColorGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c new file mode 100644 index 00000000000..e036b6b78be --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c @@ -0,0 +1,355 @@ +/* + * ***** 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 + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilhook.c + * \ingroup modifiers + */ + +#include + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_modifier_types.h" +#include "BLI_math.h" + +#include "BLI_utildefines.h" + +#include "BKE_action.h" +#include "BKE_context.h" +#include "BKE_colortools.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_modifier.h" +#include "BKE_library_query.h" +#include "BKE_scene.h" +#include "BKE_main.h" +#include "BKE_layer.h" + +#include "MEM_guardedalloc.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +/* temp struct to hold data */ +struct GPHookData_cb { + struct CurveMapping *curfalloff; + + char falloff_type; + float falloff; + float falloff_sq; + float fac_orig; + + unsigned int use_falloff : 1; + unsigned int use_uniform : 1; + + float cent[3]; + + float mat_uniform[3][3]; + float mat[4][4]; +}; + +static void initData(GpencilModifierData *md) +{ + HookGpencilModifierData *gpmd = (HookGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; + gpmd->object = NULL; + gpmd->force = 0.5f; + gpmd->falloff_type = eGPHook_Falloff_Smooth; + gpmd->curfalloff = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (gpmd->curfalloff) { + curvemapping_initialize(gpmd->curfalloff); + } +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + HookGpencilModifierData *gmd = (HookGpencilModifierData *)md; + HookGpencilModifierData *tgmd = (HookGpencilModifierData *)target; + + if (tgmd->curfalloff != NULL) { + curvemapping_free(tgmd->curfalloff); + tgmd->curfalloff = NULL; + } + + BKE_gpencil_modifier_copyData_generic(md, target); + + tgmd->curfalloff = curvemapping_copy(gmd->curfalloff); +} + +/* calculate factor of fallof */ +static float gp_hook_falloff(const struct GPHookData_cb *tData, const float len_sq) +{ + BLI_assert(tData->falloff_sq); + if (len_sq > tData->falloff_sq) { + return 0.0f; + } + else if (len_sq > 0.0f) { + float fac; + + if (tData->falloff_type == eGPHook_Falloff_Const) { + fac = 1.0f; + goto finally; + } + else if (tData->falloff_type == eGPHook_Falloff_InvSquare) { + /* avoid sqrt below */ + fac = 1.0f - (len_sq / tData->falloff_sq); + goto finally; + } + + fac = 1.0f - (sqrtf(len_sq) / tData->falloff); + + switch (tData->falloff_type) { + case eGPHook_Falloff_Curve: + fac = curvemapping_evaluateF(tData->curfalloff, 0, fac); + break; + case eGPHook_Falloff_Sharp: + fac = fac * fac; + break; + case eGPHook_Falloff_Smooth: + fac = 3.0f * fac * fac - 2.0f * fac * fac * fac; + break; + case eGPHook_Falloff_Root: + fac = sqrtf(fac); + break; + case eGPHook_Falloff_Linear: + /* pass */ + break; + case eGPHook_Falloff_Sphere: + fac = sqrtf(2 * fac - fac * fac); + break; + default: + fac = fac; + break; + } + + finally: + return fac * tData->fac_orig; + } + else { + return tData->fac_orig; + } +} + +/* apply point deformation */ +static void gp_hook_co_apply(struct GPHookData_cb *tData, float weight, bGPDspoint *pt) +{ + float fac; + + if (tData->use_falloff) { + float len_sq; + + if (tData->use_uniform) { + float co_uniform[3]; + mul_v3_m3v3(co_uniform, tData->mat_uniform, &pt->x); + len_sq = len_squared_v3v3(tData->cent, co_uniform); + } + else { + len_sq = len_squared_v3v3(tData->cent, &pt->x); + } + + fac = gp_hook_falloff(tData, len_sq); + } + else { + fac = tData->fac_orig; + } + + if (fac) { + float co_tmp[3]; + mul_v3_m4v3(co_tmp, tData->mat, &pt->x); + interp_v3_v3v3(&pt->x, &pt->x, co_tmp, fac * weight); + } +} + +/* deform stroke */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + HookGpencilModifierData *mmd = (HookGpencilModifierData *)md; + if (!mmd->object) { + return; + } + + int vindex = defgroup_name_index(ob, mmd->vgname); + float weight = 1.0f; + + bPoseChannel *pchan = BKE_pose_channel_find_name(mmd->object->pose, mmd->subtarget); + float dmat[4][4]; + struct GPHookData_cb tData; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_HOOK_INVERT_LAYER, mmd->flag & GP_HOOK_INVERT_PASS)) + { + return; + } + + /* init struct */ + tData.curfalloff = mmd->curfalloff; + tData.falloff_type = mmd->falloff_type; + tData.falloff = (mmd->falloff_type == eHook_Falloff_None) ? 0.0f : mmd->falloff; + tData.falloff_sq = SQUARE(tData.falloff); + tData.fac_orig = mmd->force; + tData.use_falloff = (tData.falloff_sq != 0.0f); + tData.use_uniform = (mmd->flag & GP_HOOK_UNIFORM_SPACE) != 0; + + if (tData.use_uniform) { + copy_m3_m4(tData.mat_uniform, mmd->parentinv); + mul_v3_m3v3(tData.cent, tData.mat_uniform, mmd->cent); + } + else { + unit_m3(tData.mat_uniform); + copy_v3_v3(tData.cent, mmd->cent); + } + + /* get world-space matrix of target, corrected for the space the verts are in */ + if (mmd->subtarget[0] && pchan) { + /* bone target if there's a matching pose-channel */ + mul_m4_m4m4(dmat, mmd->object->obmat, pchan->pose_mat); + } + else { + /* just object target */ + copy_m4_m4(dmat, mmd->object->obmat); + } + invert_m4_m4(ob->imat, ob->obmat); + mul_m4_series(tData.mat, ob->imat, dmat, mmd->parentinv); + + /* loop points and apply deform */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + /* verify vertex group */ + weight = get_modifier_point_weight(dvert, (int)(!(mmd->flag & GP_HOOK_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + continue; + } + gp_hook_co_apply(&tData, weight, pt); + } +} + +/* FIXME: Ideally we be doing this on a copy of the main depsgraph + * (i.e. one where we don't have to worry about restoring state) + */ +static void bakeModifier( + Main *bmain, Depsgraph *depsgraph, + GpencilModifierData *md, Object *ob) +{ + HookGpencilModifierData *mmd = (HookGpencilModifierData *)md; + Scene *scene = DEG_get_evaluated_scene(depsgraph); + bGPdata *gpd = ob->data; + int oldframe = (int)DEG_get_ctime(depsgraph); + + if (mmd->object == NULL) + return; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* apply hook effects on this frame + * NOTE: this assumes that we don't want hook animation on non-keyframed frames + */ + CFRA = gpf->framenum; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); + + /* compute hook effects on this frame */ + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } + + /* return frame state and DB to original state */ + CFRA = oldframe; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); +} + +static void freeData(GpencilModifierData *md) +{ + HookGpencilModifierData *mmd = (HookGpencilModifierData *)md; + + if (mmd->curfalloff) { + curvemapping_free(mmd->curfalloff); + } +} + +static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) +{ + HookGpencilModifierData *mmd = (HookGpencilModifierData *)md; + + return !mmd->object; +} + +static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + HookGpencilModifierData *lmd = (HookGpencilModifierData *)md; + if (lmd->object != NULL) { + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_GEOMETRY, "Hook Modifier"); + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_TRANSFORM, "Hook Modifier"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Hook Modifier"); +} + +static void foreachObjectLink( + GpencilModifierData *md, Object *ob, + ObjectWalkFunc walk, void *userData) +{ + HookGpencilModifierData *mmd = (HookGpencilModifierData *)md; + + walk(userData, ob, &mmd->object, IDWALK_CB_NOP); +} + +GpencilModifierTypeInfo modifierType_Gpencil_Hook = { + /* name */ "Hook", + /* structName */ "HookGpencilModifierData", + /* structSize */ sizeof(HookGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ 0, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ freeData, + /* isDisabled */ isDisabled, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilinstance.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilinstance.c new file mode 100644 index 00000000000..64f3fbc4a95 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilinstance.c @@ -0,0 +1,360 @@ +/* + * ***** 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 + * + * Contributor(s): Antonio Vazquez, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilinstance.c + * \ingroup modifiers + */ + +#include + +#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 "BKE_gpencil.h" +#include "BKE_gpencil_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_collection.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) +{ + InstanceGpencilModifierData *gpmd = (InstanceGpencilModifierData *)md; + gpmd->count[0] = 1; + gpmd->count[1] = 1; + gpmd->count[2] = 1; + gpmd->offset[0] = 1.0f; + gpmd->offset[1] = 1.0f; + gpmd->offset[2] = 1.0f; + gpmd->shift[0] = 0.0f; + gpmd->shift[1] = 0.0f; + gpmd->shift[2] = 0.0f; + gpmd->scale[0] = 1.0f; + gpmd->scale[1] = 1.0f; + gpmd->scale[2] = 1.0f; + gpmd->rnd_rot = 0.5f; + gpmd->rnd_size = 0.5f; + gpmd->lock_axis |= GP_LOCKAXIS_X; + gpmd->flag |= GP_INSTANCE_MAKE_OBJECTS; + + /* fill random values */ + BLI_array_frand(gpmd->rnd, 20, 1); + gpmd->rnd[0] = 1; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* -------------------------------- */ + +/* array modifier - generate geometry callback (for viewport/rendering) */ +/* TODO: How to skip this for the simplify options? --> !GP_SIMPLIFY_MODIF(ts, playing) */ +static void generate_geometry( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDframe *gpf) +{ + InstanceGpencilModifierData *mmd = (InstanceGpencilModifierData *)md; + ListBase stroke_cache = {NULL, NULL}; + bGPDstroke *gps; + int idx; + + /* Check which strokes we can use once, and store those results in an array + * for quicker checking of what's valid (since string comparisons are expensive) + */ + const int num_strokes = BLI_listbase_count(&gpf->strokes); + int num_valid = 0; + + bool *valid_strokes = MEM_callocN(sizeof(bool) * num_strokes, __func__); + + for (gps = gpf->strokes.first, idx = 0; gps; gps = gps->next, idx++) { + /* Record whether this stroke can be used + * ATTENTION: The logic here is the inverse of what's used everywhere else! + */ + if (is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 1, gpl, gps, + mmd->flag & GP_INSTANCE_INVERT_LAYER, mmd->flag & GP_INSTANCE_INVERT_PASS)) + { + valid_strokes[idx] = true; + num_valid++; + } + } + + /* Early exit if no strokes can be copied */ + if (num_valid == 0) { + if (G.debug & G_DEBUG) { + printf("GP Array Mod - No strokes to be included\n"); + } + + MEM_SAFE_FREE(valid_strokes); + return; + } + + + /* Generate new instances of all existing strokes, + * keeping each instance together so they maintain + * the correct ordering relative to each other + */ + for (int x = 0; x < mmd->count[0]; x++) { + for (int y = 0; y < mmd->count[1]; y++) { + for (int z = 0; z < mmd->count[2]; z++) { + /* original strokes are at index = 0,0,0 */ + if ((x == 0) && (y == 0) && (z == 0)) { + continue; + } + + /* Compute transforms for this instance */ + const int elem_idx[3] = {x, y, z}; + float mat[4][4]; + + BKE_gpencil_instance_modifier_instance_tfm(mmd, elem_idx, mat); + + /* apply shift */ + int sh = x; + if (mmd->lock_axis == GP_LOCKAXIS_Y) { + sh = y; + } + if (mmd->lock_axis == GP_LOCKAXIS_Z) { + sh = z; + } + madd_v3_v3fl(mat[3], mmd->shift, sh); + + /* Duplicate original strokes to create this instance */ + for (gps = gpf->strokes.first, idx = 0; gps; gps = gps->next, idx++) { + /* check if stroke can be duplicated */ + if (valid_strokes[idx]) { + /* Duplicate stroke */ + bGPDstroke *gps_dst = MEM_dupallocN(gps); + gps_dst->points = MEM_dupallocN(gps->points); + gps_dst->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, gps_dst); + + gps_dst->triangles = MEM_dupallocN(gps->triangles); + + /* Move points */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps_dst->points[i]; + mul_m4_v3(mat, &pt->x); + } + + /* Add new stroke to cache, to be added to the frame once + * all duplicates have been made + */ + BLI_addtail(&stroke_cache, gps_dst); + } + } + } + } + } + + /* merge newly created stroke instances back into the main stroke list */ + BLI_movelisttolist(&gpf->strokes, &stroke_cache); + + /* free temp data */ + MEM_SAFE_FREE(valid_strokes); +} + +/* bakeModifier - "Bake to Data" Mode */ +static void bakeModifierGP_strokes( + 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) { + generate_geometry(md, depsgraph, ob, gpl, gpf); + } + } +} + +/* -------------------------------- */ + +/* helper to create a new object */ +static Object *array_instance_add_ob_copy(Main *bmain, Scene *scene, Object *from_ob) +{ + Object *ob; + + ob = BKE_object_copy(bmain, from_ob); + BKE_collection_object_add_from(bmain, scene, from_ob, ob); + + zero_v3(ob->loc); + zero_v3(ob->rot); + + DEG_id_type_tag(bmain, ID_OB); + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&scene->id, 0); + + return ob; +} + +/* bakeModifier - "Make Objects" Mode */ +static void bakeModifierGP_objects(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob) +{ + InstanceGpencilModifierData *mmd = (InstanceGpencilModifierData *)md; + Scene *scene = DEG_get_evaluated_scene(depsgraph); + /* reset random */ + mmd->rnd[0] = 1; + + /* generate instances as objects */ + for (int x = 0; x < mmd->count[0]; x++) { + for (int y = 0; y < mmd->count[1]; y++) { + for (int z = 0; z < mmd->count[2]; z++) { + Object *newob; + GpencilModifierData *fmd; + + const int elem_idx[3] = {x, y, z}; + float mat[4][4], finalmat[4][4]; + int sh; + + /* original strokes are at index = 0,0,0 */ + if ((x == 0) && (y == 0) && (z == 0)) { + continue; + } + + /* compute transform for instance */ + BKE_gpencil_instance_modifier_instance_tfm(mmd, elem_idx, mat); + mul_m4_m4m4(finalmat, ob->obmat, mat); + + /* moves to new origin */ + sh = x; + if (mmd->lock_axis == GP_LOCKAXIS_Y) { + sh = y; + } + if (mmd->lock_axis == GP_LOCKAXIS_Z) { + sh = z; + } + madd_v3_v3fl(finalmat[3], mmd->shift, sh); + + /* Create a new object + * + * NOTE: Copies share the same original GP datablock + * Artists can later user make_single_user on these + * to make them unique (if necessary), without too + * much extra memory usage. + */ + newob = array_instance_add_ob_copy(bmain, scene, ob); + + /* remove array on destination object */ + fmd = (GpencilModifierData *)BLI_findstring(&newob->greasepencil_modifiers, md->name, offsetof(GpencilModifierData, name)); + if (fmd) { + BLI_remlink(&newob->greasepencil_modifiers, fmd); + BKE_gpencil_modifier_free(fmd); + } + + /* copy transforms to destination object */ + copy_m4_m4(newob->obmat, finalmat); + + copy_v3_v3(newob->loc, finalmat[3]); + mat4_to_eul(newob->rot, finalmat); + mat4_to_size(newob->size, finalmat); + } + } + } +} + +/* -------------------------------- */ + +/* Generic "generateStrokes" callback */ +static void generateStrokes( + GpencilModifierData *md, Depsgraph *depsgraph, + Object *ob, bGPDlayer *gpl, bGPDframe *gpf) +{ + InstanceGpencilModifierData *mmd = (InstanceGpencilModifierData *)md; + + /* When the "make_objects" flag is set, this modifier is handled as part of the + * draw engine instead. The main benefit is that the instances won't suffer from + * z-ordering problems. + * + * FIXME: Ultimately, the draw-engine hack here shouldn't be necessary, but until + * we find a better fix to the z-ordering problems, it's better to have + * working functionality + */ + if ((mmd->flag & GP_INSTANCE_MAKE_OBJECTS) == 0) { + generate_geometry(md, depsgraph, ob, gpl, gpf); + } +} + +/* Generic "bakeModifier" callback */ +static void bakeModifier( + Main *bmain, Depsgraph *depsgraph, + GpencilModifierData *md, Object *ob) +{ + InstanceGpencilModifierData *mmd = (InstanceGpencilModifierData *)md; + + /* Create new objects or add all to current datablock. + * Sometimes it's useful to have the option to do either of these... + */ + if (mmd->flag & GP_INSTANCE_MAKE_OBJECTS) { + bakeModifierGP_objects(bmain, depsgraph, md, ob); + } + else { + bakeModifierGP_strokes(depsgraph, md, ob); + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Instance = { + /* name */ "Instance", + /* structName */ "InstanceGpencilModifierData", + /* structSize */ sizeof(InstanceGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ 0, + + /* copyData */ copyData, + + /* deformStroke */ NULL, + /* generateStrokes */ generateStrokes, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c new file mode 100644 index 00000000000..944e787020e --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c @@ -0,0 +1,213 @@ +/* + * ***** 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 + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencillattice.c + * \ingroup modifiers + */ + +#include + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_modifier.h" +#include "BKE_lattice.h" +#include "BKE_library_query.h" +#include "BKE_scene.h" +#include "BKE_main.h" +#include "BKE_layer.h" + +#include "MEM_guardedalloc.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +static void initData(GpencilModifierData *md) +{ + LatticeGpencilModifierData *gpmd = (LatticeGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; + gpmd->object = NULL; + gpmd->cache_data = NULL; + gpmd->strength = 1.0f; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + int vindex = defgroup_name_index(ob, mmd->vgname); + float weight = 1.0f; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_LATTICE_INVERT_LAYER, mmd->flag & GP_LATTICE_INVERT_PASS)) + { + return; + } + + if (mmd->cache_data == NULL) { + return; + } + + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + /* verify vertex group */ + weight = get_modifier_point_weight(dvert, (int)(!(mmd->flag & GP_LATTICE_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + continue; + } + + calc_latt_deform((struct LatticeDeformData *)mmd->cache_data, &pt->x, mmd->strength * weight); + } +} + +/* FIXME: Ideally we be doing this on a copy of the main depsgraph + * (i.e. one where we don't have to worry about restoring state) + */ +static void bakeModifier( + Main *bmain, Depsgraph *depsgraph, + GpencilModifierData *md, Object *ob) +{ + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + Scene *scene = DEG_get_evaluated_scene(depsgraph); + struct LatticeDeformData *ldata = NULL; + bGPdata *gpd = ob->data; + int oldframe = (int)DEG_get_ctime(depsgraph); + + if (mmd->object == NULL) + return; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* apply lattice effects on this frame + * NOTE: this assumes that we don't want lattice animation on non-keyframed frames + */ + CFRA = gpf->framenum; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); + + /* recalculate lattice data */ + BKE_gpencil_lattice_init(ob); + + /* compute lattice effects on this frame */ + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } + + /* free lingering data */ + ldata = (struct LatticeDeformData *)mmd->cache_data; + if (ldata) { + end_latt_deform(ldata); + mmd->cache_data = NULL; + } + + /* return frame state and DB to original state */ + CFRA = oldframe; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); +} + +static void freeData(GpencilModifierData *md) +{ + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + struct LatticeDeformData *ldata = (struct LatticeDeformData *)mmd->cache_data; + /* free deform data */ + if (ldata) { + end_latt_deform(ldata); + } +} + +static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) +{ + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + + return !mmd->object; +} + +static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + LatticeGpencilModifierData *lmd = (LatticeGpencilModifierData *)md; + if (lmd->object != NULL) { + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_GEOMETRY, "Lattice Modifier"); + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_TRANSFORM, "Lattice Modifier"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Lattice Modifier"); +} + +static void foreachObjectLink( + GpencilModifierData *md, Object *ob, + ObjectWalkFunc walk, void *userData) +{ + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + + walk(userData, ob, &mmd->object, IDWALK_CB_NOP); +} + +GpencilModifierTypeInfo modifierType_Gpencil_Lattice = { + /* name */ "Lattice", + /* structName */ "LatticeGpencilModifierData", + /* structSize */ sizeof(LatticeGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_Single | eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ freeData, + /* isDisabled */ isDisabled, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c new file mode 100644 index 00000000000..9b5186755d6 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c @@ -0,0 +1,239 @@ +/* + * ***** 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) 2018, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilmirror.c + * \ingroup modifiers + */ + +#include + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_modifier.h" +#include "BKE_library_query.h" +#include "BKE_scene.h" +#include "BKE_main.h" +#include "BKE_layer.h" + +#include "MEM_guardedalloc.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +static void initData(GpencilModifierData *md) +{ + MirrorGpencilModifierData *gpmd = (MirrorGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->layername[0] = '\0'; + gpmd->object = NULL; + gpmd->flag |= GP_MIRROR_AXIS_X; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +static void clip_stroke(MirrorGpencilModifierData *mmd, bGPDstroke *gps) +{ + int i; + bGPDspoint *pt; + float fpt[3]; + if ((mmd->flag & GP_MIRROR_CLIPPING) == 0) { + return; + } + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + copy_v3_v3(fpt, &pt->x); + for (int xi = 0; xi < 3; ++xi) { + if (mmd->flag & (GP_MIRROR_AXIS_X << xi)) { + if (fpt[xi] >= 0.0f) { + fpt[xi] = 0.0f; + } + } + } + copy_v3_v3(&pt->x, fpt); + } + +} + +static void update_position(Object *ob, MirrorGpencilModifierData *mmd, bGPDstroke *gps, int axis) +{ + int i; + bGPDspoint *pt; + float factor[3] = { 1.0f, 1.0f, 1.0f }; + factor[axis] = -1.0f; + + float clear[3] = { 0.0f, 0.0f, 0.0f }; + clear[axis] = 1.0f; + + float origin[3]; + float mirror_origin[3]; + + copy_v3_v3(origin, ob->loc); + /* only works with current axis */ + mul_v3_v3(origin, clear); + zero_v3(mirror_origin); + + if (mmd->object) { + copy_v3_v3(mirror_origin, mmd->object->loc); + mul_v3_v3(mirror_origin, clear); + sub_v3_v3(origin, mirror_origin); + } + /* clear other axis */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + add_v3_v3(&pt->x, origin); + mul_v3_v3(&pt->x, factor); + add_v3_v3(&pt->x, mirror_origin); + } + +} + +/* Generic "generateStrokes" callback */ +static void generateStrokes( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDframe *gpf) +{ + MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)md; + bGPDstroke *gps, *gps_new = NULL; + int tot_strokes; + int i; + + /* count strokes to avoid infinite loop after adding new strokes to tail of listbase */ + tot_strokes = BLI_listbase_count(&gpf->strokes); + + for (i = 0, gps = gpf->strokes.first; i < tot_strokes; i++, gps = gps->next) { + if (is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 1, gpl, gps, + mmd->flag & GP_MIRROR_INVERT_LAYER, mmd->flag & GP_MIRROR_INVERT_PASS)) + { + /* check each axis for mirroring */ + for (int xi = 0; xi < 3; ++xi) { + if (mmd->flag & (GP_MIRROR_AXIS_X << xi)) { + /* clip before duplicate */ + clip_stroke(mmd, gps); + + gps_new = BKE_gpencil_stroke_duplicate(gps); + update_position(ob, mmd, gps_new, xi); + BLI_addtail(&gpf->strokes, gps_new); + } + } + } + } +} + +static void bakeModifier( + Main *bmain, Depsgraph *depsgraph, + GpencilModifierData *md, Object *ob) +{ + MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)md; + Scene *scene = DEG_get_evaluated_scene(depsgraph); + bGPdata *gpd = ob->data; + int oldframe = (int)DEG_get_ctime(depsgraph); + + if (mmd->object == NULL) + return; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* apply mirror effects on this frame */ + CFRA = gpf->framenum; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); + + /* compute mirror effects on this frame */ + generateStrokes(md, depsgraph, ob, gpl, gpf); + } + } + + /* return frame state and DB to original state */ + CFRA = oldframe; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); +} + +static bool isDisabled(GpencilModifierData *UNUSED(md), int UNUSED(userRenderParams)) +{ + //MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)md; + + return false; +} + +static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + MirrorGpencilModifierData *lmd = (MirrorGpencilModifierData *)md; + if (lmd->object != NULL) { + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_GEOMETRY, "Mirror Modifier"); + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_TRANSFORM, "Mirror Modifier"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Mirror Modifier"); +} + +static void foreachObjectLink( + GpencilModifierData *md, Object *ob, + ObjectWalkFunc walk, void *userData) +{ + MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)md; + + walk(userData, ob, &mmd->object, IDWALK_CB_NOP); +} + +GpencilModifierTypeInfo modifierType_Gpencil_Mirror = { + /* name */ "Mirror", + /* structName */ "MirrorGpencilModifierData", + /* structSize */ sizeof(MirrorGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ NULL, + /* generateStrokes */ generateStrokes, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ isDisabled, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c new file mode 100644 index 00000000000..37c8bf0b0f0 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c @@ -0,0 +1,285 @@ +/* + * ***** 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 + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilnoise.c + * \ingroup modifiers + */ + +#include + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" +#include "BLI_rand.h" + +#include "PIL_time.h" + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_object.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + NoiseGpencilModifierData *gpmd = (NoiseGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->flag |= GP_NOISE_MOD_LOCATION; + gpmd->flag |= GP_NOISE_FULL_STROKE; + gpmd->flag |= GP_NOISE_USE_RANDOM; + gpmd->factor = 0.5f; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; + gpmd->step = 1; + gpmd->scene_frame = -999999; + gpmd->gp_frame = -999999; + + gpmd->vrand1 = 1.0; + gpmd->vrand2 = 1.0; +} + +static void freeData(GpencilModifierData *md) +{ + NoiseGpencilModifierData *mmd = (NoiseGpencilModifierData *)md; + + if (mmd->rng != NULL) { + BLI_rng_free(mmd->rng); + } +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +static bool dependsOnTime(GpencilModifierData *md) +{ + NoiseGpencilModifierData *mmd = (NoiseGpencilModifierData *)md; + return (mmd->flag & GP_NOISE_USE_RANDOM) != 0; +} + +/* aply noise effect based on stroke direction */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *depsgraph, + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + NoiseGpencilModifierData *mmd = (NoiseGpencilModifierData *)md; + bGPDspoint *pt0, *pt1; + MDeformVert *dvert; + float shift, vran, vdir; + float normal[3]; + float vec1[3], vec2[3]; +#if 0 + Scene *scene = DEG_get_evaluated_scene(depsgraph); +#endif + int sc_frame = 0; + int sc_diff = 0; + int vindex = defgroup_name_index(ob, mmd->vgname); + float weight = 1.0f; + + /* Random generator, only init once. */ + if (mmd->rng == NULL) { + uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); + rng_seed ^= GET_UINT_FROM_POINTER(mmd); + mmd->rng = BLI_rng_new(rng_seed); + } + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_NOISE_INVERT_LAYER, mmd->flag & GP_NOISE_INVERT_PASS)) + { + return; + } + + sc_frame = (int)DEG_get_ctime(depsgraph); + + zero_v3(vec2); + + /* calculate stroke normal*/ + BKE_gpencil_stroke_normal(gps, normal); + + /* move points */ + for (int i = 0; i < gps->totpoints; i++) { + if (((i == 0) || (i == gps->totpoints - 1)) && ((mmd->flag & GP_NOISE_MOVE_EXTREME) == 0)) { + continue; + } + + /* last point is special */ + if (i == gps->totpoints) { + dvert = &gps->dvert[i - 2]; + pt0 = &gps->points[i - 2]; + pt1 = &gps->points[i - 1]; + } + else { + dvert = &gps->dvert[i - 1]; + pt0 = &gps->points[i - 1]; + pt1 = &gps->points[i]; + + } + + /* verify vertex group */ + weight = get_modifier_point_weight(dvert, (int)(!(mmd->flag & GP_NOISE_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + continue; + } + + /* initial vector (p0 -> p1) */ + sub_v3_v3v3(vec1, &pt1->x, &pt0->x); + vran = len_v3(vec1); + /* vector orthogonal to normal */ + cross_v3_v3v3(vec2, vec1, normal); + normalize_v3(vec2); + /* use random noise */ + if (mmd->flag & GP_NOISE_USE_RANDOM) { + sc_diff = abs(mmd->scene_frame - sc_frame); + /* only recalc if the gp frame change or the number of scene frames is bigger than step */ + if ((!gpl->actframe) || (mmd->gp_frame != gpl->actframe->framenum) || + (sc_diff >= mmd->step)) + { + vran = mmd->vrand1 = BLI_rng_get_float(mmd->rng); + vdir = mmd->vrand2 = BLI_rng_get_float(mmd->rng); + mmd->gp_frame = gpl->actframe->framenum; + mmd->scene_frame = sc_frame; + } + else { + vran = mmd->vrand1; + if (mmd->flag & GP_NOISE_FULL_STROKE) { + vdir = mmd->vrand2; + } + else { + int f = (mmd->vrand2 * 10.0f) + i; + vdir = f % 2; + } + } + } + else { + vran = 1.0f; + if (mmd->flag & GP_NOISE_FULL_STROKE) { + vdir = gps->totpoints % 2; + } + else { + vdir = i % 2; + } + mmd->gp_frame = -999999; + } + + /* apply randomness to location of the point */ + if (mmd->flag & GP_NOISE_MOD_LOCATION) { + /* factor is too sensitive, so need divide */ + shift = ((vran * mmd->factor) / 1000.0f) * weight; + if (vdir > 0.5f) { + mul_v3_fl(vec2, shift); + } + else { + mul_v3_fl(vec2, shift * -1.0f); + } + add_v3_v3(&pt1->x, vec2); + } + + /* apply randomness to thickness */ + if (mmd->flag & GP_NOISE_MOD_THICKNESS) { + if (vdir > 0.5f) { + pt1->pressure -= pt1->pressure * vran * mmd->factor; + } + else { + pt1->pressure += pt1->pressure * vran * mmd->factor; + } + CLAMP_MIN(pt1->pressure, GPENCIL_STRENGTH_MIN); + } + + /* apply randomness to color strength */ + if (mmd->flag & GP_NOISE_MOD_STRENGTH) { + if (vdir > 0.5f) { + pt1->strength -= pt1->strength * vran * mmd->factor; + } + else { + pt1->strength += pt1->strength * vran * mmd->factor; + } + CLAMP_MIN(pt1->strength, GPENCIL_STRENGTH_MIN); + } + /* apply randomness to uv rotation */ + if (mmd->flag & GP_NOISE_MOD_UV) { + if (vdir > 0.5f) { + pt1->uv_rot -= pt1->uv_rot * vran * mmd->factor; + } + else { + pt1->uv_rot += pt1->uv_rot * vran * mmd->factor; + } + CLAMP(pt1->uv_rot, -M_PI_2, M_PI_2); + } + } +} + +static void bakeModifier( + struct 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Noise = { + /* name */ "Noise", + /* structName */ "NoiseGpencilModifierData", + /* structSize */ sizeof(NoiseGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ freeData, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ dependsOnTime, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c new file mode 100644 index 00000000000..8a96c705f08 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c @@ -0,0 +1,143 @@ +/* + * ***** 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 + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpenciloffset.c + * \ingroup modifiers + */ + +#include + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + OffsetGpencilModifierData *gpmd = (OffsetGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; + ARRAY_SET_ITEMS(gpmd->loc, 0.0f, 0.0f, 0.0f); + ARRAY_SET_ITEMS(gpmd->rot, 0.0f, 0.0f, 0.0f); + ARRAY_SET_ITEMS(gpmd->scale, 0.0f, 0.0f, 0.0f); +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* change stroke offsetness */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + OffsetGpencilModifierData *mmd = (OffsetGpencilModifierData *)md; + int vindex = defgroup_name_index(ob, mmd->vgname); + + float mat[4][4]; + float loc[3], rot[3], scale[3]; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 1, gpl, gps, + mmd->flag & GP_OFFSET_INVERT_LAYER, mmd->flag & GP_OFFSET_INVERT_PASS)) + { + return; + } + + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + /* verify vertex group */ + float weight = get_modifier_point_weight(dvert, (int)(!(mmd->flag & GP_OFFSET_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + continue; + } + /* calculate matrix */ + mul_v3_v3fl(loc, mmd->loc, weight); + mul_v3_v3fl(rot, mmd->rot, weight); + mul_v3_v3fl(scale, mmd->scale, weight); + add_v3_fl(scale, 1.0); + loc_eul_size_to_mat4(mat, loc, rot, scale); + + mul_m4_v3(mat, &pt->x); + } +} + +static void bakeModifier( + struct 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Offset = { + /* name */ "Offset", + /* structName */ "OffsetGpencilModifierData", + /* structSize */ sizeof(OffsetGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c new file mode 100644 index 00000000000..bdd651a69fc --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c @@ -0,0 +1,171 @@ +/* + * ***** 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 + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilopacity.c + * \ingroup modifiers + */ + +#include + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_material.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + OpacityGpencilModifierData *gpmd = (OpacityGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->factor = 1.0f; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* opacity strokes */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md; + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + int vindex = defgroup_name_index(ob, mmd->vgname); + + if (!is_stroke_affected_by_modifier( + ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_OPACITY_INVERT_LAYER, mmd->flag & GP_OPACITY_INVERT_PASS)) + { + return; + } + + gp_style->fill_rgba[3]*= mmd->factor; + + /* if factor is > 1, then force opacity */ + if (mmd->factor > 1.0f) { + gp_style->stroke_rgba[3] += mmd->factor - 1.0f; + if (gp_style->fill_rgba[3] > 1e-5) { + gp_style->fill_rgba[3] += mmd->factor - 1.0f; + } + } + + CLAMP(gp_style->stroke_rgba[3], 0.0f, 1.0f); + CLAMP(gp_style->fill_rgba[3], 0.0f, 1.0f); + + /* if opacity > 1.0, affect the strength of the stroke */ + if (mmd->factor > 1.0f) { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + /* verify vertex group */ + float weight = get_modifier_point_weight(dvert, (!(mmd->flag & GP_OPACITY_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + pt->strength += mmd->factor - 1.0f; + } + else { + pt->strength += (mmd->factor - 1.0f) * weight; + } + CLAMP(pt->strength, 0.0f, 1.0f); + } + } + else { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + /* verify vertex group */ + if (mmd->vgname == NULL) { + pt->strength *= mmd->factor; + } + else { + float weight = get_modifier_point_weight(dvert, (!(mmd->flag & GP_OPACITY_INVERT_VGROUP) == 0), vindex); + if (weight >= 0) { + pt->strength *= mmd->factor * weight; + } + } + CLAMP(pt->strength, 0.0f, 1.0f); + } + } + +} + +static void bakeModifier( + struct 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Opacity = { + /* name */ "Opacity", + /* structName */ "OpacityGpencilModifierData", + /* structSize */ sizeof(OpacityGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c new file mode 100644 index 00000000000..f0400e39b73 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c @@ -0,0 +1,123 @@ +/* ***** 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 + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilsimplify.c + * \ingroup modifiers + */ + +#include + +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_vec_types.h" + +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + SimplifyGpencilModifierData *gpmd = (SimplifyGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->step = 1; + gpmd->factor = 0.0f; + gpmd->layername[0] = '\0'; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + SimplifyGpencilModifierData *mmd = (SimplifyGpencilModifierData *)md; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 4, gpl, gps, + mmd->flag & GP_SIMPLIFY_INVERT_LAYER, mmd->flag & GP_SIMPLIFY_INVERT_PASS)) + { + return; + } + + if (mmd->mode == GP_SIMPLIFY_FIXED) { + for (int i = 0; i < mmd->step; i++) { + BKE_gpencil_simplify_fixed(gps); + } + } + else { + /* simplify stroke using Ramer-Douglas-Peucker algorithm */ + BKE_gpencil_simplify_stroke(gps, mmd->factor); + } +} + +static void bakeModifier( + struct 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Simplify = { + /* name */ "Simplify", + /* structName */ "SimplifyGpencilModifierData", + /* structSize */ sizeof(SimplifyGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c new file mode 100644 index 00000000000..b83c8ed98b1 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c @@ -0,0 +1,152 @@ +/* + * ***** 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 + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilsmooth.c + * \ingroup modifiers + */ + +#include + +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->flag |= GP_SMOOTH_MOD_LOCATION; + gpmd->factor = 0.5f; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; + gpmd->step = 1; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* aply smooth effect based on stroke direction */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + SmoothGpencilModifierData *mmd = (SmoothGpencilModifierData *)md; + int vindex = defgroup_name_index(ob, mmd->vgname); + float weight = 1.0f; + float val; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_SMOOTH_INVERT_LAYER, mmd->flag & GP_SMOOTH_INVERT_PASS)) + { + return; + } + + /* smooth stroke */ + if (mmd->factor > 0.0f) { + for (int r = 0; r < mmd->step; r++) { + for (int i = 0; i < gps->totpoints; i++) { + // bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + /* verify vertex group */ + weight = get_modifier_point_weight(dvert, (int)(!(mmd->flag & GP_SMOOTH_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + continue; + } + + val = mmd->factor * weight; + /* perform smoothing */ + if (mmd->flag & GP_SMOOTH_MOD_LOCATION) { + BKE_gpencil_smooth_stroke(gps, i, val); + } + if (mmd->flag & GP_SMOOTH_MOD_STRENGTH) { + BKE_gpencil_smooth_stroke_strength(gps, i, val); + } + if ((mmd->flag & GP_SMOOTH_MOD_THICKNESS) && (val > 0)) { + /* thickness need to repeat process several times */ + for (int r2 = 0; r2 < r * 10; r2++) { + BKE_gpencil_smooth_stroke_thickness(gps, i, val); + } + } + if (mmd->flag & GP_SMOOTH_MOD_UV) { + BKE_gpencil_smooth_stroke_uv(gps, i, val); + } + } + } + } +} + +static void bakeModifier( + struct 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Smooth = { + /* name */ "Smooth", + /* structName */ "SmoothGpencilModifierData", + /* structSize */ sizeof(SmoothGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c new file mode 100644 index 00000000000..6fe9c34c06b --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c @@ -0,0 +1,193 @@ +/* + * ***** 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 + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilsubdiv.c + * \ingroup modifiers + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + SubdivGpencilModifierData *gpmd = (SubdivGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->level = 1; + gpmd->layername[0] = '\0'; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* subdivide stroke to get more control points */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + SubdivGpencilModifierData *mmd = (SubdivGpencilModifierData *)md; + bGPDspoint *temp_points; + int totnewpoints, oldtotpoints; + int i2; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_SUBDIV_INVERT_LAYER, mmd->flag & GP_SUBDIV_INVERT_PASS)) + { + return; + } + + /* loop as many times as levels */ + for (int s = 0; s < mmd->level; s++) { + totnewpoints = gps->totpoints - 1; + /* duplicate points in a temp area */ + temp_points = MEM_dupallocN(gps->points); + oldtotpoints = gps->totpoints; + + /* resize the points arrys */ + gps->totpoints += totnewpoints; + gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + gps->flag |= GP_STROKE_RECALC_CACHES; + + /* move points from last to first to new place */ + i2 = gps->totpoints - 1; + for (int i = oldtotpoints - 1; i > 0; i--) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *pt_final = &gps->points[i2]; + + MDeformVert *dvert = &gps->dvert[i]; + MDeformVert *dvert_final = &gps->dvert[i2]; + + copy_v3_v3(&pt_final->x, &pt->x); + pt_final->pressure = pt->pressure; + pt_final->strength = pt->strength; + pt_final->time = pt->time; + pt_final->flag = pt->flag; + + dvert_final->totweight = dvert->totweight; + dvert_final->dw = dvert->dw; + i2 -= 2; + } + /* interpolate mid points */ + i2 = 1; + for (int i = 0; i < oldtotpoints - 1; i++) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *next = &temp_points[i + 1]; + bGPDspoint *pt_final = &gps->points[i2]; + MDeformVert *dvert_final = &gps->dvert[i2]; + + /* add a half way point */ + interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f); + pt_final->strength = interpf(pt->strength, next->strength, 0.5f); + CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt_final->time = interpf(pt->time, next->time, 0.5f); + + dvert_final->totweight = 0; + dvert_final->dw = NULL; + i2 += 2; + } + + MEM_SAFE_FREE(temp_points); + + /* move points to smooth stroke (not simple flag )*/ + if ((mmd->flag & GP_SUBDIV_SIMPLE) == 0) { + /* duplicate points in a temp area with the new subdivide data */ + temp_points = MEM_dupallocN(gps->points); + + /* extreme points are not changed */ + for (int i = 0; i < gps->totpoints - 2; i++) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *next = &temp_points[i + 1]; + bGPDspoint *pt_final = &gps->points[i + 1]; + + /* move point */ + interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + } + /* free temp memory */ + MEM_SAFE_FREE(temp_points); + } + } +} + +static void bakeModifier( + struct 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Subdiv = { + /* name */ "Subdivision", + /* structName */ "SubdivGpencilModifierData", + /* structSize */ sizeof(SubdivGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c new file mode 100644 index 00000000000..118cc33255f --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -0,0 +1,171 @@ +/* + * ***** 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 + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilthick.c + * \ingroup modifiers + */ + +#include + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BLI_utildefines.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + ThickGpencilModifierData *gpmd = (ThickGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->thickness = 0; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; + gpmd->curve_thickness = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (gpmd->curve_thickness) { + curvemapping_initialize(gpmd->curve_thickness); + } +} + +static void freeData(GpencilModifierData *md) +{ + ThickGpencilModifierData *gpmd = (ThickGpencilModifierData *)md; + + if (gpmd->curve_thickness) { + curvemapping_free(gpmd->curve_thickness); + } +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + ThickGpencilModifierData *gmd = (ThickGpencilModifierData *)md; + ThickGpencilModifierData *tgmd = (ThickGpencilModifierData *)target; + + if (tgmd->curve_thickness != NULL) { + curvemapping_free(tgmd->curve_thickness); + tgmd->curve_thickness = NULL; + } + + BKE_gpencil_modifier_copyData_generic(md, target); + + tgmd->curve_thickness = curvemapping_copy(gmd->curve_thickness); +} + +/* change stroke thickness */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md; + int vindex = defgroup_name_index(ob, mmd->vgname); + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_THICK_INVERT_LAYER, mmd->flag & GP_THICK_INVERT_PASS)) + { + return; + } + + /* if normalize, set stroke thickness */ + if (mmd->flag & GP_THICK_NORMALIZE) { + gps->thickness = mmd->thickness; + } + + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + float curvef = 1.0f; + /* verify vertex group */ + float weight = get_modifier_point_weight(dvert, (int)(!(mmd->flag & GP_THICK_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + continue; + } + + if (mmd->flag & GP_THICK_NORMALIZE) { + pt->pressure = 1.0f; + } + else { + if ((mmd->flag & GP_THICK_CUSTOM_CURVE) && (mmd->curve_thickness)) { + /* normalize value to evaluate curve */ + float value = (float)i / (gps->totpoints - 1); + curvef = curvemapping_evaluateF(mmd->curve_thickness, 0, value); + } + + pt->pressure += mmd->thickness * weight * curvef; + CLAMP(pt->strength, 0.0f, 1.0f); + } + } +} + +static void bakeModifier( + struct 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Thick = { + /* name */ "Thickness", + /* structName */ "ThickGpencilModifierData", + /* structSize */ sizeof(ThickGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ freeData, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c new file mode 100644 index 00000000000..9d1e9eccb8c --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c @@ -0,0 +1,186 @@ +/* + * ***** 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 + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpenciltint.c + * \ingroup modifiers + */ + +#include + +#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_ghash.h" +#include "BLI_math_vector.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_material.h" +#include "BKE_main.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + TintGpencilModifierData *gpmd = (TintGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->factor = 0.5f; + gpmd->layername[0] = '\0'; + ARRAY_SET_ITEMS(gpmd->rgb, 1.0f, 1.0f, 1.0f); + gpmd->flag |= GP_TINT_CREATE_COLORS; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* tint strokes */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + TintGpencilModifierData *mmd = (TintGpencilModifierData *)md; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 1, gpl, gps, + mmd->flag & GP_TINT_INVERT_LAYER, mmd->flag & GP_TINT_INVERT_PASS)) + { + return; + } + + interp_v3_v3v3(gps->runtime.tmp_stroke_rgba, gps->runtime.tmp_stroke_rgba, mmd->rgb, mmd->factor); + interp_v3_v3v3(gps->runtime.tmp_fill_rgba, gps->runtime.tmp_fill_rgba, mmd->rgb, mmd->factor); + + /* if factor is > 1, the alpha must be changed to get full tint */ + if (mmd->factor > 1.0f) { + gps->runtime.tmp_stroke_rgba[3] += mmd->factor - 1.0f; + if (gps->runtime.tmp_fill_rgba[3] > 1e-5) { + gps->runtime.tmp_fill_rgba[3] += mmd->factor - 1.0f; + } + } + + CLAMP4(gps->runtime.tmp_stroke_rgba, 0.0f, 1.0f); + CLAMP4(gps->runtime.tmp_fill_rgba, 0.0f, 1.0f); + + /* if factor > 1.0, affect the strength of the stroke */ + if (mmd->factor > 1.0f) { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + pt->strength += mmd->factor - 1.0f; + CLAMP(pt->strength, 0.0f, 1.0f); + } + } +} + +static void bakeModifier( + Main *bmain, Depsgraph *depsgraph, + GpencilModifierData *md, Object *ob) +{ + TintGpencilModifierData *mmd = (TintGpencilModifierData *)md; + bGPdata *gpd = ob->data; + + GHash *gh_color = BLI_ghash_str_new("GP_Tint modifier"); + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + + Material *mat = give_current_material(ob, gps->mat_nr + 1); + if (mat == NULL) + continue; + MaterialGPencilStyle *gp_style = mat->gp_style; + /* skip stroke if it doesn't have color info */ + if (ELEM(NULL, gp_style)) + continue; + + copy_v4_v4(gps->runtime.tmp_stroke_rgba, gp_style->stroke_rgba); + copy_v4_v4(gps->runtime.tmp_fill_rgba, gp_style->fill_rgba); + + /* look for color */ + if (mmd->flag & GP_TINT_CREATE_COLORS) { + Material *newmat = (Material *)BLI_ghash_lookup(gh_color, mat->id.name); + if (newmat == NULL) { + BKE_object_material_slot_add(bmain, ob); + newmat = BKE_material_copy(bmain, mat); + assign_material(bmain, ob, newmat, ob->totcol, BKE_MAT_ASSIGN_EXISTING); + + copy_v4_v4(newmat->gp_style->stroke_rgba, gps->runtime.tmp_stroke_rgba); + copy_v4_v4(newmat->gp_style->fill_rgba, gps->runtime.tmp_fill_rgba); + + BLI_ghash_insert(gh_color, mat->id.name, newmat); + } + /* reasign color index */ + int idx = BKE_object_material_slot_find_index(ob, newmat); + gps->mat_nr = idx - 1; + } + else { + /* reuse existing color */ + copy_v4_v4(gp_style->stroke_rgba, gps->runtime.tmp_stroke_rgba); + copy_v4_v4(gp_style->fill_rgba, gps->runtime.tmp_fill_rgba); + } + + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } + /* free hash buffers */ + if (gh_color) { + BLI_ghash_free(gh_color, NULL, NULL); + gh_color = NULL; + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Tint = { + /* name */ "Tint", + /* structName */ "TintGpencilModifierData", + /* structSize */ sizeof(TintGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; -- cgit v1.2.3