GPv3: Thickness modifier #117631

Merged
YimingWu merged 9 commits from ChengduLittleA/blender:gp3-mod-thickness into main 2024-01-30 13:04:42 +01:00
9 changed files with 355 additions and 12 deletions

View File

@ -189,9 +189,10 @@ class OBJECT_MT_modifier_add_deform(ModifierAddMenu, Menu):
if ob_type == 'VOLUME':
self.operator_modifier_add(layout, 'VOLUME_DISPLACE')
if ob_type == 'GREASEPENCIL':
self.operator_modifier_add(layout, 'GREASE_PENCIL_SMOOTH')
self.operator_modifier_add(layout, 'GREASE_PENCIL_OFFSET')
self.operator_modifier_add(layout, 'GREASE_PENCIL_NOISE')
self.operator_modifier_add(layout, 'GREASE_PENCIL_OFFSET')
self.operator_modifier_add(layout, 'GREASE_PENCIL_SMOOTH')
self.operator_modifier_add(layout, 'GREASE_PENCIL_THICKNESS')
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)

View File

@ -865,5 +865,11 @@
.flag = MOD_GREASE_PENCIL_MIRROR_AXIS_X, \
}
#define _DNA_DEFAULT_GreasePencilThickModifierData \
{ \
.flag = 0, \
.thickness_fac = 1.0f, \
.thickness = 0.02, \
}
/* clang-format off */

View File

@ -101,6 +101,7 @@ typedef enum ModifierType {
eModifierType_GreasePencilOffset = 66,
eModifierType_GreasePencilNoise = 67,
eModifierType_GreasePencilMirror = 68,
eModifierType_GreasePencilThickness = 69,
NUM_MODIFIER_TYPES,
} ModifierType;
@ -2708,3 +2709,21 @@ typedef enum GreasePencilMirrorModifierFlag {
MOD_GREASE_PENCIL_MIRROR_AXIS_Y = (1 << 1),
MOD_GREASE_PENCIL_MIRROR_AXIS_Z = (1 << 2),
} GreasePencilMirrorModifierFlag;
typedef struct GreasePencilThickModifierData {
ModifierData modifier;
GreasePencilModifierInfluenceData influence;
/** #GreasePencilThicknessModifierFlag */
int flag;
/** Relative thickness factor. */
float thickness_fac;
/** Absolute thickness override. */
float thickness;
char _pad[4];
void *_pad1;
} GreasePencilThickModifierData;
typedef enum GreasePencilThicknessModifierFlag {
MOD_GREASE_PENCIL_THICK_NORMALIZE = (1 << 0),
MOD_GREASE_PENCIL_THICK_WEIGHT_FACTOR = (1 << 1),
} GreasePencilThicknessModifierFlag;

View File

@ -332,6 +332,7 @@ SDNA_DEFAULT_DECL_STRUCT(GreasePencilColorModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilTintModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilOffsetModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilMirrorModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilThickModifierData);
#undef SDNA_DEFAULT_DECL_STRUCT
@ -586,6 +587,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(GreasePencilTintModifierData),
SDNA_DEFAULT_DECL(GreasePencilOffsetModifierData),
SDNA_DEFAULT_DECL(GreasePencilMirrorModifierData),
SDNA_DEFAULT_DECL(GreasePencilThickModifierData),
};
#undef SDNA_DEFAULT_DECL
#undef SDNA_DEFAULT_DECL_EX

View File

@ -301,21 +301,26 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_VOLUME_DATA,
"Volume Displace",
"Deform volume based on noise or other vector fields"}, /* TODO: Use correct icon. */
{eModifierType_GreasePencilSmooth,
"GREASE_PENCIL_SMOOTH",
ICON_SMOOTHCURVE,
"Smooth",
"Smooth grease pencil strokes"},
{eModifierType_GreasePencilOffset,
"GREASE_PENCIL_OFFSET",
ICON_MOD_OFFSET,
"Offset",
"Change stroke location, rotation, or scale"},
{eModifierType_GreasePencilNoise,
"GREASE_PENCIL_NOISE",
ICON_MOD_NOISE,
"Noise",
"Generate noise wobble in grease pencil strokes"},
{eModifierType_GreasePencilOffset,
"GREASE_PENCIL_OFFSET",
ICON_MOD_OFFSET,
"Offset",
"Change stroke location, rotation, or scale"},
{eModifierType_GreasePencilSmooth,
"GREASE_PENCIL_SMOOTH",
ICON_SMOOTHCURVE,
"Smooth",
"Smooth grease pencil strokes"},
{eModifierType_GreasePencilThickness,
"GREASE_PENCIL_THICKNESS",
ICON_MOD_THICKNESS,
"Thickness",
"Change stroke thickness"},
RNA_ENUM_ITEM_HEADING(N_("Physics"), nullptr),
{eModifierType_Cloth, "CLOTH", ICON_MOD_CLOTH, "Cloth", ""},
@ -1841,6 +1846,7 @@ RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilSubdiv);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilTint);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilSmooth);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilNoise);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilThick);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOffset);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOpacity);
@ -1848,6 +1854,7 @@ RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilSubdiv);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilTint);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilSmooth);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilNoise);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilThick);
static void rna_GreasePencilOpacityModifier_opacity_factor_range(
PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax)
@ -8285,6 +8292,54 @@ static void rna_def_modifier_grease_pencil_mirror(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_grease_pencil_thickness(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "GreasePencilThickModifierData", "Modifier");
RNA_def_struct_ui_text(srna, "Grease Pencil Thickness Modifier", "Adjust stroke thickness");
RNA_def_struct_sdna(srna, "GreasePencilThickModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_THICKNESS);
rna_def_modifier_grease_pencil_layer_filter(srna);
rna_def_modifier_grease_pencil_material_filter(
srna, "rna_GreasePencilThickModifier_material_filter_set");
rna_def_modifier_grease_pencil_vertex_group(
srna, "rna_GreasePencilThickModifier_vertex_group_name_set");
rna_def_modifier_grease_pencil_custom_curve(srna);
rna_def_modifier_panel_open_prop(srna, "open_influence_panel", 0);
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "thickness", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "thickness");
RNA_def_property_range(prop, -10.0f, 100.0f);
RNA_def_property_ui_range(prop, -1.0f, 1.0f, 0.005, 3);
RNA_def_property_ui_text(prop, "Thickness", "Absolute thickness to apply everywhere");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "thickness_factor", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "thickness_fac");
RNA_def_property_range(prop, 0.0, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0, 10.0, 0.1, 3);
RNA_def_property_ui_text(prop, "Thickness Factor", "Factor to multiply the thickness with");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_weight_factor", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", MOD_GREASE_PENCIL_THICK_WEIGHT_FACTOR);
RNA_def_property_ui_text(prop, "Weighted", "Use weight to modulate effect");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_uniform_thickness", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", MOD_GREASE_PENCIL_THICK_NORMALIZE);
RNA_def_property_ui_text(prop, "Uniform Thickness", "Replace the stroke thickness");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
RNA_define_lib_overridable(false);
}
void RNA_def_modifier(BlenderRNA *brna)
{
StructRNA *srna;
@ -8453,6 +8508,7 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_grease_pencil_offset(brna);
rna_def_modifier_grease_pencil_noise(brna);
rna_def_modifier_grease_pencil_mirror(brna);
rna_def_modifier_grease_pencil_thickness(brna);
}
#endif

View File

@ -51,6 +51,7 @@ set(SRC
intern/MOD_grease_pencil_opacity.cc
intern/MOD_grease_pencil_smooth.cc
intern/MOD_grease_pencil_subdiv.cc
intern/MOD_grease_pencil_thickness.cc
intern/MOD_grease_pencil_tint.cc
intern/MOD_grease_pencil_util.cc
intern/MOD_hook.cc

View File

@ -81,6 +81,7 @@ extern ModifierTypeInfo modifierType_GreasePencilSmooth;
extern ModifierTypeInfo modifierType_GreasePencilOffset;
extern ModifierTypeInfo modifierType_GreasePencilNoise;
extern ModifierTypeInfo modifierType_GreasePencilMirror;
extern ModifierTypeInfo modifierType_GreasePencilThickness;
/* MOD_util.cc */

View File

@ -0,0 +1,256 @@
/* SPDX-FileCopyrightText: 2005 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup modifiers
*/
#include "BLI_index_mask.hh"
#include "BLT_translation.h"
#include "BLO_read_write.hh"
#include "DNA_defaults.h"
#include "DNA_modifier_types.h"
#include "DNA_screen_types.h"
#include "RNA_access.hh"
#include "BKE_colortools.hh"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_lib_query.hh"
#include "BKE_modifier.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "MOD_grease_pencil_util.hh"
#include "MOD_modifiertypes.hh"
#include "MOD_ui_common.hh"
#include "RNA_prototypes.h"
namespace blender {
static void init_data(ModifierData *md)
{
GreasePencilThickModifierData *gpmd = reinterpret_cast<GreasePencilThickModifierData *>(md);
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier));
MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(GreasePencilThickModifierData), modifier);
modifier::greasepencil::init_influence_data(&gpmd->influence, true);
}
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
{
const GreasePencilThickModifierData *gmd =
reinterpret_cast<const GreasePencilThickModifierData *>(md);
GreasePencilThickModifierData *tgmd = reinterpret_cast<GreasePencilThickModifierData *>(target);
BKE_modifier_copydata_generic(md, target, flag);
modifier::greasepencil::copy_influence_data(&gmd->influence, &tgmd->influence, flag);
}
static void free_data(ModifierData *md)
{
GreasePencilThickModifierData *mmd = reinterpret_cast<GreasePencilThickModifierData *>(md);
modifier::greasepencil::free_influence_data(&mmd->influence);
}
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
{
GreasePencilThickModifierData *mmd = reinterpret_cast<GreasePencilThickModifierData *>(md);
modifier::greasepencil::foreach_influence_ID_link(&mmd->influence, ob, walk, user_data);
}
static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
{
const GreasePencilThickModifierData *mmd =
reinterpret_cast<const GreasePencilThickModifierData *>(md);
BLO_write_struct(writer, GreasePencilThickModifierData, mmd);
modifier::greasepencil::write_influence_data(writer, &mmd->influence);
}
static void blend_read(BlendDataReader *reader, ModifierData *md)
{
GreasePencilThickModifierData *mmd = reinterpret_cast<GreasePencilThickModifierData *>(md);
modifier::greasepencil::read_influence_data(reader, &mmd->influence);
}
static void deform_drawing(const ModifierData &md,
const Object &ob,
bke::greasepencil::Drawing &drawing)
{
auto &mmd = reinterpret_cast<const GreasePencilThickModifierData &>(md);
bke::CurvesGeometry &curves = drawing.strokes_for_write();
if (curves.points_num() == 0) {
return;
}
IndexMaskMemory memory;
const IndexMask strokes = modifier::greasepencil::get_filtered_stroke_mask(
&ob, curves, mmd.influence, memory);
if (strokes.is_empty()) {
return;
}
blender::MutableSpan<float> radii = drawing.radii_for_write();
const OffsetIndices points_by_curve = curves.points_by_curve();
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
const VArray<float> vgroup_weights = *attributes.lookup_or_default<float>(
mmd.influence.vertex_group_name, bke::AttrDomain::Point, 1.0f);
const bool is_normalized = (mmd.flag & MOD_GREASE_PENCIL_THICK_NORMALIZE) != 0;
const bool is_inverted = ((mmd.flag & MOD_GREASE_PENCIL_THICK_WEIGHT_FACTOR) == 0) &&
ChengduLittleA marked this conversation as resolved Outdated

Instead of using .varray you can do = *attributes.lookup_or_default<float>(...);

Instead of using `.varray` you can do ` = *attributes.lookup_or_default<float>(...);`
((mmd.influence.flag & GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP) !=
ChengduLittleA marked this conversation as resolved Outdated

Use (...) != 0;

Use ` (...) != 0;`
0);
ChengduLittleA marked this conversation as resolved Outdated

Use const

Use `const`
threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) {
ChengduLittleA marked this conversation as resolved Outdated

curves_range -> range

`curves_range` -> `range`
for (const int curve : range) {
ChengduLittleA marked this conversation as resolved Outdated

We're only writing to the radii, so this should be a threading::parallel_for loop over the curves range.

We're only writing to the radii, so this should be a `threading::parallel_for` loop over the curves range.
const IndexRange points = points_by_curve[curve];
ChengduLittleA marked this conversation as resolved Outdated

Since you're looking up the points_by_curve[curve] multiple times, better put it in a variable above the inner loop.

const OffsetIndices<int> points = points_by_curve[curve];
Since you're looking up the `points_by_curve[curve]` multiple times, better put it in a variable above the inner loop. ``` const OffsetIndices<int> points = points_by_curve[curve]; ```
for (const int local_point : points.index_range()) {
const int point = local_point + points.first();
ChengduLittleA marked this conversation as resolved Outdated

Use const

Use `const`
const float weight = vgroup_weights[point];
if (weight <= 0.0f) {
continue;
}
if ((!is_normalized) && (mmd.flag & MOD_GREASE_PENCIL_THICK_WEIGHT_FACTOR)) {
radii[point] *= (is_inverted ? 1.0f - weight : weight);
ChengduLittleA marked this conversation as resolved Outdated

Use math::clamp instead of the if here

Use `math::clamp` instead of the if here
radii[point] = math::max(radii[point], 0.0f);
continue;
}
const float influence = [&]() {
if (mmd.influence.flag & GREASE_PENCIL_INFLUENCE_USE_CUSTOM_CURVE &&
ChengduLittleA marked this conversation as resolved Outdated

You can use a lambda here to make this variable const. Maybe a better name would be influence too.

const float influence = [&]() {
   if (mmd.influence.flag & GREASE_PENCIL_INFLUENCE_USE_CUSTOM_CURVE &&
          (mmd.influence.custom_curve))
   {
      /* Normalize value to evaluate curve. */
      const float value = float(local_point) / (points_by_curve[curve].size() - 1);
      return BKE_curvemapping_evaluateF(mmd.influence.custom_curve, 0, value);
   }
   return 1.0f;
}();
You can use a lambda here to make this variable `const`. Maybe a better name would be `influence` too. ``` const float influence = [&]() { if (mmd.influence.flag & GREASE_PENCIL_INFLUENCE_USE_CUSTOM_CURVE && (mmd.influence.custom_curve)) { /* Normalize value to evaluate curve. */ const float value = float(local_point) / (points_by_curve[curve].size() - 1); return BKE_curvemapping_evaluateF(mmd.influence.custom_curve, 0, value); } return 1.0f; }(); ```
(mmd.influence.custom_curve))
{
/* Normalize value to evaluate curve. */
const float value = float(local_point) / (points.size() - 1);
return BKE_curvemapping_evaluateF(mmd.influence.custom_curve, 0, value);
}
return 1.0f;
}();
ChengduLittleA marked this conversation as resolved Outdated

Same as above

const float target = [&]() {
   if (is_normalized) {
      return mmd.thickness * influence;
   }
   weight *= curvef; // why is this needed?
   return radii[point] * mmd.thickness_fac;
}();
Same as above ``` const float target = [&]() { if (is_normalized) { return mmd.thickness * influence; } weight *= curvef; // why is this needed? return radii[point] * mmd.thickness_fac; }(); ```
const float target = [&]() {
if (is_normalized) {
return mmd.thickness * influence;
}
return radii[point] * math::interpolate(1.0f, mmd.thickness_fac, influence);
}();
const float radius = math::interpolate(radii[point], target, weight);
radii[point] = math::max(radius, 0.0f);
}
ChengduLittleA marked this conversation as resolved Outdated

Use const

Use `const`
}
ChengduLittleA marked this conversation as resolved Outdated

Use math::clamp instead of the if here

Use `math::clamp` instead of the if here
});
}
static void modify_geometry_set(ModifierData *md,
const ModifierEvalContext *ctx,
bke::GeometrySet *geometry_set)
{
GreasePencilThickModifierData *mmd = reinterpret_cast<GreasePencilThickModifierData *>(md);
if (!geometry_set->has_grease_pencil()) {
return;
}
GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
const int current_frame = grease_pencil.runtime->eval_frame;
IndexMaskMemory mask_memory;
const IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask(
grease_pencil, mmd->influence, mask_memory);
const Vector<bke::greasepencil::Drawing *> drawings =
modifier::greasepencil::get_drawings_for_write(grease_pencil, layer_mask, current_frame);
threading::parallel_for_each(drawings, [&](bke::greasepencil::Drawing *drawing) {
deform_drawing(*md, *ctx->object, *drawing);
});
}
static void panel_draw(const bContext *C, Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
uiLayoutSetPropSep(layout, true);
uiItemR(layout, ptr, "use_uniform_thickness", UI_ITEM_NONE, nullptr, ICON_NONE);
if (RNA_boolean_get(ptr, "use_uniform_thickness")) {
uiItemR(layout, ptr, "thickness", UI_ITEM_NONE, nullptr, ICON_NONE);
}
else {
const bool is_weighted = !RNA_boolean_get(ptr, "use_weight_factor");
uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetActive(row, is_weighted);
uiItemR(row, ptr, "thickness_factor", UI_ITEM_NONE, nullptr, ICON_NONE);
uiLayout *sub = uiLayoutRow(row, true);
uiLayoutSetActive(sub, true);
uiItemR(row, ptr, "use_weight_factor", UI_ITEM_NONE, "", ICON_MOD_VERTEX_WEIGHT);
}
if (uiLayout *influence_panel = uiLayoutPanel(
C, layout, "Influence", ptr, "open_influence_panel"))
{
modifier::greasepencil::draw_layer_filter_settings(C, influence_panel, ptr);
modifier::greasepencil::draw_material_filter_settings(C, influence_panel, ptr);
modifier::greasepencil::draw_vertex_group_settings(C, influence_panel, ptr);
modifier::greasepencil::draw_custom_curve_settings(C, influence_panel, ptr);
}
modifier_panel_end(layout, ptr);
}
static void panel_register(ARegionType *region_type)
{
modifier_panel_register(region_type, eModifierType_GreasePencilThickness, panel_draw);
}
} // namespace blender
ModifierTypeInfo modifierType_GreasePencilThickness = {
/*idname*/ "GreasePencilThicknessModifier",
/*name*/ N_("Thickness"),
/*struct_name*/ "GreasePencilThickModifierData",
/*struct_size*/ sizeof(GreasePencilThickModifierData),
/*srna*/ &RNA_GreasePencilThickModifierData,
/*type*/ ModifierTypeType::OnlyDeform,
/*flags*/
eModifierTypeFlag_AcceptsGreasePencil | eModifierTypeFlag_SupportsEditmode |
eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping,
/*icon*/ ICON_MOD_THICKNESS,
/*copy_data*/ blender::copy_data,
/*deform_verts*/ nullptr,
/*deform_matrices*/ nullptr,
/*deform_verts_EM*/ nullptr,
/*deform_matrices_EM*/ nullptr,
/*modify_mesh*/ nullptr,
/*modify_geometry_set*/ blender::modify_geometry_set,
/*init_data*/ blender::init_data,
/*required_data_mask*/ nullptr,
/*free_data*/ blender::free_data,
/*is_disabled*/ nullptr,
/*update_depsgraph*/ nullptr,
/*depends_on_time*/ nullptr,
/*depends_on_normals*/ nullptr,
/*foreach_ID_link*/ blender::foreach_ID_link,
/*foreach_tex_link*/ nullptr,
/*free_runtime_data*/ nullptr,
/*panel_register*/ blender::panel_register,
/*blend_write*/ blender::blend_write,
/*blend_read*/ blender::blend_read,
};

View File

@ -278,5 +278,6 @@ void modifier_type_init(ModifierTypeInfo *types[])
INIT_TYPE(GreasePencilOffset);
INIT_TYPE(GreasePencilNoise);
INIT_TYPE(GreasePencilMirror);
INIT_TYPE(GreasePencilThickness);
#undef INIT_TYPE
}