GPencil: Add Texture modifier

This patch aims to add a new modifier for grease pencil objects that gives more control over the strokes texture UVs.

There are 3 modes.
1 Control the stroke texture UVs alone
2 Control the fill texture UVs alone
3 Control both the fill and stroke texture UVs

For the stroke texture UVs there are 2 options for fitting the texture to the stroke.
1 The texture uvs are kept a consistent length how it currently is set by default.
2 The uvs are normalized to fit the length of the stroke regardless of how long or short it gets allowing the texture to fit the length of the stroke.

 There are then 2 controls to scale up and down the uvs and an offset value that allows moving the texture along the stroke.

For the fill texture UVs it includes all of the transformational controls. Location offset, scale, and rotation.

Reviewed By: antoniov, mendio

Differential Revision: https://developer.blender.org/D7439
This commit is contained in:
Cody Winchester 2020-04-19 17:25:36 +02:00 committed by Antonio Vazquez
parent a0a59972e7
commit a39a6517af
7 changed files with 382 additions and 0 deletions

View File

@ -1976,6 +1976,27 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
self.gpencil_masking(layout, ob, md, True, True)
def GP_TEXTURE(self, layout, ob, md):
col = layout.column()
col.prop(md, "mode")
if md.mode in {'STROKE', 'STROKE_AND_FILL'}:
col.label(text="Stroke Texture Control:")
col.prop(md, "fit_method")
col.prop(md, "uv_offset")
col.prop(md, "uv_scale")
if md.mode == 'STROKE_AND_FILL':
col.separator()
if md.mode in {'FILL', 'STROKE_AND_FILL'}:
col.label(text="Fill Texture Control:")
col.prop(md, "fill_rotation", text="Rotation")
col.prop(md, "fill_offset", text="Location")
col.prop(md, "fill_scale", text="Scale")
self.gpencil_masking(layout, ob, md, True)
def GP_TINT(self, layout, ob, md):
layout.row().prop(md, "tint_type", expand=True)

View File

@ -59,6 +59,7 @@ set(SRC
intern/MOD_gpencilthick.c
intern/MOD_gpenciltime.c
intern/MOD_gpenciltint.c
intern/MOD_gpenciltexture.c
MOD_gpencil_modifiertypes.h
)

View File

@ -43,6 +43,7 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_Offset;
extern GpencilModifierTypeInfo modifierType_Gpencil_Armature;
extern GpencilModifierTypeInfo modifierType_Gpencil_Time;
extern GpencilModifierTypeInfo modifierType_Gpencil_Multiply;
extern GpencilModifierTypeInfo modifierType_Gpencil_Texture;
/* MOD_gpencil_util.c */
void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]);

View File

@ -72,6 +72,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[])
INIT_GP_TYPE(Armature);
INIT_GP_TYPE(Time);
INIT_GP_TYPE(Multiply);
INIT_GP_TYPE(Texture);
#undef INIT_GP_TYPE
}

View File

@ -0,0 +1,167 @@
/*
* 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
*/
/** \file
* \ingroup modifiers
*/
#include <stdio.h>
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BKE_gpencil_geom.h"
#include "BKE_colortools.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
#include "DEG_depsgraph.h"
#include "MOD_gpencil_modifiertypes.h"
#include "MOD_gpencil_util.h"
static void initData(GpencilModifierData *md)
{
TextureGpencilModifierData *gpmd = (TextureGpencilModifierData *)md;
gpmd->fit_method = GP_TEX_CONSTANT_LENGTH;
gpmd->fill_rotation = 0.0f;
gpmd->fill_scale = 1.0f;
gpmd->fill_offset[0] = 0.0f;
gpmd->fill_offset[1] = 0.0f;
gpmd->uv_offset = 0.0f;
gpmd->uv_scale = 1.0f;
gpmd->pass_index = 0;
gpmd->layername[0] = '\0';
gpmd->materialname[0] = '\0';
gpmd->vgname[0] = '\0';
}
static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
{
BKE_gpencil_modifier_copyData_generic(md, target);
}
/* change stroke uv texture values */
static void deformStroke(GpencilModifierData *md,
Depsgraph *UNUSED(depsgraph),
Object *ob,
bGPDlayer *gpl,
bGPDframe *UNUSED(gpf),
bGPDstroke *gps)
{
TextureGpencilModifierData *mmd = (TextureGpencilModifierData *)md;
const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
if (!is_stroke_affected_by_modifier(ob,
mmd->layername,
mmd->materialname,
mmd->pass_index,
mmd->layer_pass,
1,
gpl,
gps,
mmd->flag & GP_TEX_INVERT_LAYER,
mmd->flag & GP_TEX_INVERT_PASS,
mmd->flag & GP_TEX_INVERT_LAYERPASS,
mmd->flag & GP_TEX_INVERT_MATERIAL)) {
return;
}
if ((mmd->mode == FILL) || (mmd->mode == STROKE_AND_FILL)) {
gps->uv_rotation += mmd->fill_rotation;
gps->uv_translation[0] += mmd->fill_offset[0];
gps->uv_translation[1] += mmd->fill_offset[1];
gps->uv_scale *= mmd->fill_scale;
BKE_gpencil_stroke_geometry_update(gps);
}
if ((mmd->mode == STROKE) || (mmd->mode == STROKE_AND_FILL)) {
float totlen = 1.0f;
if (mmd->fit_method == GP_TEX_FIT_STROKE) {
totlen = 0.0f;
for (int i = 1; i < gps->totpoints; i++) {
totlen += len_v3v3(&gps->points[i - 1].x, &gps->points[i].x);
}
}
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL;
/* Verify point is part of vertex group. */
float weight = get_modifier_point_weight(
dvert, (mmd->flag & GP_TEX_INVERT_VGROUP) != 0, def_nr);
if (weight < 0.0f) {
continue;
}
pt->uv_fac /= totlen;
pt->uv_fac *= mmd->uv_scale;
pt->uv_fac += mmd->uv_offset;
}
}
}
static void bakeModifier(struct Main *UNUSED(bmain),
Depsgraph *depsgraph,
GpencilModifierData *md,
Object *ob)
{
bGPdata *gpd = ob->data;
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
deformStroke(md, depsgraph, ob, gpl, gpf, gps);
}
}
}
}
GpencilModifierTypeInfo modifierType_Gpencil_Texture = {
/* name */ "Texture",
/* structName */ "TextureGpencilModifierData",
/* structSize */ sizeof(TextureGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
/* flags */ eGpencilModifierTypeFlag_SupportsEditmode,
/* copyData */ copyData,
/* deformStroke */ deformStroke,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
/* initData */ initData,
/* freeData */ NULL,
/* isDisabled */ NULL,
/* updateDepsgraph */ NULL,
/* dependsOnTime */ NULL,
/* foreachObjectLink */ NULL,
/* foreachIDLink */ NULL,
/* foreachTexLink */ NULL,
};

View File

@ -47,6 +47,7 @@ typedef enum GpencilModifierType {
eGpencilModifierType_Armature = 15,
eGpencilModifierType_Time = 16,
eGpencilModifierType_Multiply = 17,
eGpencilModifierType_Texture = 18,
NUM_GREASEPENCIL_MODIFIER_TYPES,
} GpencilModifierType;
@ -713,4 +714,50 @@ typedef enum eTintGpencil_Flag {
GP_TINT_CUSTOM_CURVE = (1 << 6),
} eTintGpencil_Flag;
typedef struct TextureGpencilModifierData {
GpencilModifierData modifier;
/** Layer name. */
char layername[64];
/** Material name. */
char materialname[64];
/** Optional vertexgroup name, MAX_VGROUP_NAME. */
char vgname[64];
/** Custom index for passes. */
int pass_index;
/** Flags. */
int flag;
/** Offset value to add to uv_fac. */
float uv_offset;
float uv_scale;
float fill_rotation;
float fill_offset[2];
float fill_scale;
/** Custom index for passes. */
int layer_pass;
/** Texture fit options. */
short fit_method;
short mode;
} TextureGpencilModifierData;
typedef enum eTextureGpencil_Flag {
GP_TEX_INVERT_LAYER = (1 << 0),
GP_TEX_INVERT_PASS = (1 << 1),
GP_TEX_INVERT_VGROUP = (1 << 2),
GP_TEX_INVERT_LAYERPASS = (1 << 3),
GP_TEX_INVERT_MATERIAL = (1 << 4),
} eTextureGpencil_Flag;
/* Texture->fit_method */
typedef enum eTextureGpencil_Fit {
GP_TEX_FIT_STROKE = 0,
GP_TEX_CONSTANT_LENGTH = 1,
} eTextureGpencil_Fit;
/* Texture->mode */
typedef enum eTextureGpencil_Mode {
STROKE = 0,
FILL = 1,
STROKE_AND_FILL = 2,
} eTextureGpencil_Mode;
#endif /* __DNA_GPENCIL_MODIFIER_TYPES_H__ */

View File

@ -129,7 +129,13 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
"Opacity",
"Opacity of the strokes"},
{eGpencilModifierType_Tint, "GP_TINT", ICON_MOD_TINT, "Tint", "Tint strokes with new color"},
{eGpencilModifierType_Texture,
"GP_TEXTURE",
ICON_TEXTURE,
"Texture",
"Change stroke uv texture values"},
{0, NULL, 0, NULL, NULL},
};
#ifndef RNA_RUNTIME
@ -234,6 +240,8 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr)
return &RNA_ArmatureGpencilModifier;
case eGpencilModifierType_Multiply:
return &RNA_MultiplyGpencilModifier;
case eGpencilModifierType_Texture:
return &RNA_TextureGpencilModifier;
/* Default */
case eGpencilModifierType_None:
case NUM_GREASEPENCIL_MODIFIER_TYPES:
@ -302,6 +310,7 @@ RNA_GP_MOD_VGROUP_NAME_SET(Smooth, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Hook, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Offset, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Armature, vgname);
RNA_GP_MOD_VGROUP_NAME_SET(Texture, vgname);
# undef RNA_GP_MOD_VGROUP_NAME_SET
@ -2138,6 +2147,140 @@ static void rna_def_modifier_gpencilmultiply(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
}
static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static const EnumPropertyItem fit_type_items[] = {
{GP_TEX_CONSTANT_LENGTH,
"CONSTANT_LENGTH",
0,
"Keep Texture at Constant Length",
"Keep the texture at a constant length regardless of the length of each stroke"},
{GP_TEX_FIT_STROKE,
"FIT_STROKE",
0,
"Fit Texture to Stroke Length",
"Scale the texture to fit the length of each stroke"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem mode_items[] = {
{STROKE, "STROKE", 0, "Stroke UVs", "Manipulate only stroke UVs"},
{FILL, "FILL", 0, "Fill UVs", "Manipulate only fill UVs"},
{STROKE_AND_FILL,
"STROKE_AND_FILL",
0,
"Stroke and Fill UVs",
"Manipulate both stroke and fill UVs"},
{0, NULL, 0, NULL, NULL},
};
srna = RNA_def_struct(brna, "TextureGpencilModifier", "GpencilModifier");
RNA_def_struct_ui_text(srna, "Texture Modifier", "Transform stroke texture UVs Modifier");
RNA_def_struct_sdna(srna, "TextureGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_TEXTURE);
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TEX_INVERT_LAYER);
RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "materialname");
RNA_def_property_ui_text(prop, "Material", "Material name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TEX_INVERT_MATERIAL);
RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "vgname");
RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for modulating the deform");
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_TextureGpencilModifier_vgname_set");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "invert_vertex", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TEX_INVERT_VGROUP);
RNA_def_property_ui_text(prop, "Inverse VertexGroup", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "pass_index");
RNA_def_property_range(prop, 0, 100);
RNA_def_property_ui_text(prop, "Pass", "Pass index");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TEX_INVERT_PASS);
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "layer_pass", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "layer_pass");
RNA_def_property_range(prop, 0, 100);
RNA_def_property_ui_text(prop, "Pass", "Layer pass index");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TEX_INVERT_LAYERPASS);
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "uv_offset", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "uv_offset");
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_range(prop, -100.0, 100.0, 0.1, 3);
RNA_def_property_float_default(prop, 0.0f);
RNA_def_property_ui_text(prop, "Offset UVs", "Offset value to add to stroke UVs");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "uv_scale", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "uv_scale");
RNA_def_property_range(prop, 0.0, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0, 100.0, 0.1, 3);
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_ui_text(prop, "UV Scale", "Factor to scale the UVs");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "fill_rotation", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "fill_rotation");
RNA_def_property_ui_text(prop, "Fill Rotation", "Additional rotation of the fill UV");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "fill_offset", PROP_FLOAT, PROP_COORDS);
RNA_def_property_float_sdna(prop, NULL, "fill_offset");
RNA_def_property_array(prop, 2);
RNA_def_property_ui_text(prop, "Fill Offset", "Additional offset of the fill UV");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "fill_scale", PROP_FLOAT, PROP_COORDS);
RNA_def_property_float_sdna(prop, NULL, "fill_scale");
RNA_def_property_range(prop, 0.01f, 100.0f);
RNA_def_property_ui_text(prop, "Fill Scale", "Additional scale of the fill UV");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "fit_method", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "fit_method");
RNA_def_property_enum_items(prop, fit_type_items);
RNA_def_property_ui_text(prop, "Fit Method", "");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "mode");
RNA_def_property_enum_items(prop, mode_items);
RNA_def_property_ui_text(prop, "Mode", "");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
}
void RNA_def_greasepencil_modifier(BlenderRNA *brna)
{
StructRNA *srna;
@ -2211,6 +2354,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna)
rna_def_modifier_gpencilhook(brna);
rna_def_modifier_gpencilarmature(brna);
rna_def_modifier_gpencilmultiply(brna);
rna_def_modifier_gpenciltexture(brna);
}
#endif