GPv3: Dash modifier
Reimplements the "Dash" (aka Dash-Dot) modifier of GPv2. Pull Request: https://projects.blender.org/blender/blender/pulls/117758
This commit is contained in:
parent
506556fb3f
commit
2c8de207b7
@ -150,6 +150,7 @@ class OBJECT_MT_modifier_add_generate(ModifierAddMenu, Menu):
|
||||
if ob_type == 'MESH':
|
||||
self.operator_modifier_add(layout, 'WIREFRAME')
|
||||
if ob_type == 'GREASEPENCIL':
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_DASH')
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_MIRROR')
|
||||
self.operator_modifier_add(layout, 'GREASE_PENCIL_SUBDIV')
|
||||
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
|
||||
|
@ -225,6 +225,9 @@ void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_geometry_nodes_input_attribute_toggle(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_geometry_node_tree_copy_assign(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_grease_pencil_dash_modifier_segment_add(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_grease_pencil_dash_modifier_segment_remove(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_grease_pencil_dash_modifier_segment_move(struct wmOperatorType *ot);
|
||||
|
||||
/* object_gpencil_modifiers.c */
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_array_utils.hh"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_defaults.h"
|
||||
#include "DNA_dynamicpaint_types.h"
|
||||
#include "DNA_fluid_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
@ -34,6 +35,7 @@
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_string_utils.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_DerivedMesh.hh"
|
||||
@ -3728,3 +3730,251 @@ void OBJECT_OT_geometry_node_tree_copy_assign(wmOperatorType *ot)
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
/** \name Dash Modifier
|
||||
* \{ */
|
||||
|
||||
namespace blender::ed::greasepencil {
|
||||
|
||||
static bool dash_modifier_segment_poll(bContext *C)
|
||||
{
|
||||
return edit_modifier_poll_generic(C, &RNA_GreasePencilDashModifierData, 0, false, false);
|
||||
}
|
||||
|
||||
static int dash_modifier_segment_add_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = ED_object_active_context(C);
|
||||
auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(
|
||||
edit_modifier_property_get(op, ob, eModifierType_GreasePencilDash));
|
||||
|
||||
if (dmd == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
GreasePencilDashModifierSegment *new_segments = static_cast<GreasePencilDashModifierSegment *>(
|
||||
MEM_malloc_arrayN(dmd->segments_num + 1, sizeof(GreasePencilDashModifierSegment), __func__));
|
||||
|
||||
const int new_active_index = dmd->segment_active_index + 1;
|
||||
if (dmd->segments_num != 0) {
|
||||
/* Copy the segments before the new segment. */
|
||||
memcpy(new_segments,
|
||||
dmd->segments_array,
|
||||
sizeof(GreasePencilDashModifierSegment) * new_active_index);
|
||||
/* Copy the segments after the new segment. */
|
||||
memcpy(new_segments + new_active_index + 1,
|
||||
dmd->segments_array + new_active_index,
|
||||
sizeof(GreasePencilDashModifierSegment) * (dmd->segments_num - new_active_index));
|
||||
}
|
||||
|
||||
/* Create the new segment. */
|
||||
GreasePencilDashModifierSegment *ds = &new_segments[new_active_index];
|
||||
memcpy(ds,
|
||||
DNA_struct_default_get(GreasePencilDashModifierSegment),
|
||||
sizeof(GreasePencilDashModifierSegment));
|
||||
BLI_uniquename_cb(
|
||||
[&](const blender::StringRef name) {
|
||||
for (const GreasePencilDashModifierSegment &ds : dmd->segments()) {
|
||||
if (STREQ(ds.name, name.data())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
'.',
|
||||
ds->name);
|
||||
|
||||
MEM_SAFE_FREE(dmd->segments_array);
|
||||
dmd->segments_array = new_segments;
|
||||
dmd->segments_num++;
|
||||
dmd->segment_active_index++;
|
||||
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int dash_modifier_segment_add_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
if (edit_modifier_invoke_properties(C, op)) {
|
||||
return dash_modifier_segment_add_exec(C, op);
|
||||
}
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::greasepencil
|
||||
|
||||
void OBJECT_OT_grease_pencil_dash_modifier_segment_add(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Add Segment";
|
||||
ot->description = "Add a segment to the dash modifier";
|
||||
ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_add";
|
||||
|
||||
/* api callbacks */
|
||||
ot->poll = blender::ed::greasepencil::dash_modifier_segment_poll;
|
||||
ot->invoke = blender::ed::greasepencil::dash_modifier_segment_add_invoke;
|
||||
ot->exec = blender::ed::greasepencil::dash_modifier_segment_add_exec;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
edit_modifier_properties(ot);
|
||||
}
|
||||
|
||||
namespace blender::ed::greasepencil {
|
||||
|
||||
static void dash_modifier_segment_free(GreasePencilDashModifierSegment * /*ds*/) {}
|
||||
|
||||
static int dash_modifier_segment_remove_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = ED_object_active_context(C);
|
||||
auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(
|
||||
edit_modifier_property_get(op, ob, eModifierType_GreasePencilDash));
|
||||
|
||||
if (dmd == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (!dmd->segments().index_range().contains(dmd->segment_active_index)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
blender::dna::array::remove_index(&dmd->segments_array,
|
||||
&dmd->segments_num,
|
||||
&dmd->segment_active_index,
|
||||
dmd->segment_active_index,
|
||||
dash_modifier_segment_free);
|
||||
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int dash_modifier_segment_remove_invoke(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent * /*event*/)
|
||||
{
|
||||
if (edit_modifier_invoke_properties(C, op)) {
|
||||
return dash_modifier_segment_remove_exec(C, op);
|
||||
}
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::greasepencil
|
||||
|
||||
void OBJECT_OT_grease_pencil_dash_modifier_segment_remove(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Remove Dash Segment";
|
||||
ot->description = "Remove the active segment from the dash modifier";
|
||||
ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_remove";
|
||||
|
||||
/* api callbacks */
|
||||
ot->poll = blender::ed::greasepencil::dash_modifier_segment_poll;
|
||||
ot->invoke = blender::ed::greasepencil::dash_modifier_segment_remove_invoke;
|
||||
ot->exec = blender::ed::greasepencil::dash_modifier_segment_remove_exec;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
edit_modifier_properties(ot);
|
||||
|
||||
RNA_def_int(
|
||||
ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of the segment to remove", 0, INT_MAX);
|
||||
}
|
||||
|
||||
namespace blender::ed::greasepencil {
|
||||
|
||||
enum class DashSegmentMoveDirection {
|
||||
Up = -1,
|
||||
Down = 1,
|
||||
};
|
||||
|
||||
static int dash_modifier_segment_move_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = ED_object_active_context(C);
|
||||
auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(
|
||||
edit_modifier_property_get(op, ob, eModifierType_GreasePencilDash));
|
||||
|
||||
if (dmd == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (dmd->segments_num < 2) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const DashSegmentMoveDirection direction = DashSegmentMoveDirection(
|
||||
RNA_enum_get(op->ptr, "type"));
|
||||
switch (direction) {
|
||||
case DashSegmentMoveDirection::Up:
|
||||
if (dmd->segment_active_index == 0) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
std::swap(dmd->segments_array[dmd->segment_active_index],
|
||||
dmd->segments_array[dmd->segment_active_index - 1]);
|
||||
|
||||
dmd->segment_active_index--;
|
||||
break;
|
||||
case DashSegmentMoveDirection::Down:
|
||||
if (dmd->segment_active_index == dmd->segments_num - 1) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
std::swap(dmd->segments_array[dmd->segment_active_index],
|
||||
dmd->segments_array[dmd->segment_active_index + 1]);
|
||||
|
||||
dmd->segment_active_index++;
|
||||
break;
|
||||
default:
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int dash_modifier_segment_move_invoke(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent * /*event*/)
|
||||
{
|
||||
if (edit_modifier_invoke_properties(C, op)) {
|
||||
return dash_modifier_segment_move_exec(C, op);
|
||||
}
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::greasepencil
|
||||
|
||||
void OBJECT_OT_grease_pencil_dash_modifier_segment_move(wmOperatorType *ot)
|
||||
{
|
||||
using blender::ed::greasepencil::DashSegmentMoveDirection;
|
||||
|
||||
static const EnumPropertyItem segment_move[] = {
|
||||
{int(DashSegmentMoveDirection::Up), "UP", 0, "Up", ""},
|
||||
{int(DashSegmentMoveDirection::Down), "DOWN", 0, "Down", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Move Dash Segment";
|
||||
ot->description = "Move the active dash segment up or down";
|
||||
ot->idname = "OBJECT_OT_grease_pencil_dash_modifier_segment_move";
|
||||
|
||||
/* api callbacks */
|
||||
ot->poll = blender::ed::greasepencil::dash_modifier_segment_poll;
|
||||
ot->invoke = blender::ed::greasepencil::dash_modifier_segment_move_invoke;
|
||||
ot->exec = blender::ed::greasepencil::dash_modifier_segment_move_exec;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
edit_modifier_properties(ot);
|
||||
|
||||
ot->prop = RNA_def_enum(ot->srna, "type", segment_move, 0, "Type", "");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -141,6 +141,9 @@ void ED_operatortypes_object()
|
||||
WM_operatortype_append(OBJECT_OT_skin_armature_create);
|
||||
WM_operatortype_append(OBJECT_OT_geometry_nodes_input_attribute_toggle);
|
||||
WM_operatortype_append(OBJECT_OT_geometry_node_tree_copy_assign);
|
||||
WM_operatortype_append(OBJECT_OT_grease_pencil_dash_modifier_segment_add);
|
||||
WM_operatortype_append(OBJECT_OT_grease_pencil_dash_modifier_segment_remove);
|
||||
WM_operatortype_append(OBJECT_OT_grease_pencil_dash_modifier_segment_move);
|
||||
|
||||
/* grease pencil modifiers */
|
||||
WM_operatortype_append(OBJECT_OT_gpencil_modifier_add);
|
||||
|
@ -878,4 +878,22 @@
|
||||
.strength = 1.0f, \
|
||||
}
|
||||
|
||||
#define _DNA_DEFAULT_GreasePencilDashModifierData \
|
||||
{ \
|
||||
.dash_offset = 0, \
|
||||
.segments_array = NULL, \
|
||||
.segments_num = 0, \
|
||||
.segment_active_index = 0, \
|
||||
}
|
||||
|
||||
#define _DNA_DEFAULT_GreasePencilDashModifierSegment \
|
||||
{ \
|
||||
.name = "", \
|
||||
.dash = 2, \
|
||||
.gap = 1, \
|
||||
.radius = 1.0f, \
|
||||
.opacity = 1.0f, \
|
||||
.mat_nr = -1, \
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include "DNA_session_uid_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include "BLI_span.hh"
|
||||
|
||||
namespace blender {
|
||||
struct NodesModifierRuntime;
|
||||
}
|
||||
@ -103,6 +105,7 @@ typedef enum ModifierType {
|
||||
eModifierType_GreasePencilMirror = 68,
|
||||
eModifierType_GreasePencilThickness = 69,
|
||||
eModifierType_GreasePencilLattice = 70,
|
||||
eModifierType_GreasePencilDash = 71,
|
||||
NUM_MODIFIER_TYPES,
|
||||
} ModifierType;
|
||||
|
||||
@ -2781,3 +2784,35 @@ typedef struct GreasePencilLatticeModifierData {
|
||||
float strength;
|
||||
char _pad[4];
|
||||
} GreasePencilLatticeModifierData;
|
||||
|
||||
typedef struct GreasePencilDashModifierSegment {
|
||||
char name[64];
|
||||
int dash;
|
||||
int gap;
|
||||
float radius;
|
||||
float opacity;
|
||||
int mat_nr;
|
||||
/** #GreasePencilDashModifierFlag */
|
||||
int flag;
|
||||
} GreasePencilDashModifierSegment;
|
||||
|
||||
typedef struct GreasePencilDashModifierData {
|
||||
ModifierData modifier;
|
||||
GreasePencilModifierInfluenceData influence;
|
||||
|
||||
GreasePencilDashModifierSegment *segments_array;
|
||||
int segments_num;
|
||||
int segment_active_index;
|
||||
|
||||
int dash_offset;
|
||||
char _pad[4];
|
||||
|
||||
#ifdef __cplusplus
|
||||
blender::Span<GreasePencilDashModifierSegment> segments() const;
|
||||
blender::MutableSpan<GreasePencilDashModifierSegment> segments();
|
||||
#endif
|
||||
} GreasePencilDashModifierData;
|
||||
|
||||
typedef enum GreasePencilDashModifierFlag {
|
||||
MOD_GREASE_PENCIL_DASH_USE_CYCLIC = (1 << 0),
|
||||
} GreasePencilDashModifierFlag;
|
||||
|
@ -334,6 +334,8 @@ SDNA_DEFAULT_DECL_STRUCT(GreasePencilOffsetModifierData);
|
||||
SDNA_DEFAULT_DECL_STRUCT(GreasePencilMirrorModifierData);
|
||||
SDNA_DEFAULT_DECL_STRUCT(GreasePencilThickModifierData);
|
||||
SDNA_DEFAULT_DECL_STRUCT(GreasePencilLatticeModifierData);
|
||||
SDNA_DEFAULT_DECL_STRUCT(GreasePencilDashModifierSegment);
|
||||
SDNA_DEFAULT_DECL_STRUCT(GreasePencilDashModifierData);
|
||||
|
||||
#undef SDNA_DEFAULT_DECL_STRUCT
|
||||
|
||||
@ -590,6 +592,8 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
|
||||
SDNA_DEFAULT_DECL(GreasePencilMirrorModifierData),
|
||||
SDNA_DEFAULT_DECL(GreasePencilThickModifierData),
|
||||
SDNA_DEFAULT_DECL(GreasePencilLatticeModifierData),
|
||||
SDNA_DEFAULT_DECL(GreasePencilDashModifierSegment),
|
||||
SDNA_DEFAULT_DECL(GreasePencilDashModifierData),
|
||||
};
|
||||
#undef SDNA_DEFAULT_DECL
|
||||
#undef SDNA_DEFAULT_DECL_EX
|
||||
|
@ -326,6 +326,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
|
||||
ICON_MOD_LATTICE,
|
||||
"Lattice",
|
||||
"Deform strokes using a lattice object"},
|
||||
{eModifierType_GreasePencilDash,
|
||||
"GREASE_PENCIL_DASH",
|
||||
ICON_MOD_DASH,
|
||||
"Dot Dash",
|
||||
"Generate dot-dash styled strokes"},
|
||||
|
||||
RNA_ENUM_ITEM_HEADING(N_("Physics"), nullptr),
|
||||
{eModifierType_Cloth, "CLOTH", ICON_MOD_CLOTH, "Cloth", ""},
|
||||
@ -736,6 +741,7 @@ const EnumPropertyItem rna_enum_subdivision_boundary_smooth_items[] = {
|
||||
# include "BKE_particle.h"
|
||||
|
||||
# include "BLI_sort_utils.h"
|
||||
# include "BLI_string_utils.hh"
|
||||
|
||||
# include "DEG_depsgraph.hh"
|
||||
# include "DEG_depsgraph_build.hh"
|
||||
@ -1863,6 +1869,7 @@ 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_MATERIAL_FILTER_SET(GreasePencilLattice);
|
||||
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilDash);
|
||||
|
||||
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOffset);
|
||||
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOpacity);
|
||||
@ -1893,6 +1900,80 @@ static void rna_GreasePencilOpacityModifier_opacity_factor_max_set(PointerRNA *p
|
||||
value;
|
||||
}
|
||||
|
||||
static const GreasePencilDashModifierData *find_grease_pencil_dash_modifier_of_segment(
|
||||
const Object &ob, const GreasePencilDashModifierSegment &dash_segment)
|
||||
{
|
||||
LISTBASE_FOREACH (const ModifierData *, md, &ob.modifiers) {
|
||||
if (md->type == eModifierType_GreasePencilDash) {
|
||||
const auto *dmd = reinterpret_cast<const GreasePencilDashModifierData *>(md);
|
||||
if (dmd->segments().contains_ptr(&dash_segment)) {
|
||||
return dmd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static std::optional<std::string> rna_GreasePencilDashModifierSegment_path(const PointerRNA *ptr)
|
||||
|
||||
{
|
||||
const Object *ob = reinterpret_cast<Object *>(ptr->owner_id);
|
||||
const auto *dash_segment = static_cast<GreasePencilDashModifierSegment *>(ptr->data);
|
||||
const GreasePencilDashModifierData *dmd = find_grease_pencil_dash_modifier_of_segment(
|
||||
*ob, *dash_segment);
|
||||
BLI_assert(dmd != nullptr);
|
||||
|
||||
char name_esc[sizeof(dmd->modifier.name) * 2];
|
||||
BLI_str_escape(name_esc, dmd->modifier.name, sizeof(name_esc));
|
||||
|
||||
char ds_name_esc[sizeof(dash_segment->name) * 2];
|
||||
BLI_str_escape(ds_name_esc, dash_segment->name, sizeof(ds_name_esc));
|
||||
|
||||
return fmt::format("modifiers[\"{}\"].segments[\"{}\"]", name_esc, ds_name_esc);
|
||||
}
|
||||
|
||||
static void rna_GreasePencilDashModifierSegment_name_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
const Object *ob = reinterpret_cast<Object *>(ptr->owner_id);
|
||||
auto *dash_segment = static_cast<GreasePencilDashModifierSegment *>(ptr->data);
|
||||
const GreasePencilDashModifierData *dmd = find_grease_pencil_dash_modifier_of_segment(
|
||||
*ob, *dash_segment);
|
||||
BLI_assert(dmd != nullptr);
|
||||
|
||||
const std::string oldname = dash_segment->name;
|
||||
STRNCPY_UTF8(dash_segment->name, value);
|
||||
BLI_uniquename_cb(
|
||||
[dmd, dash_segment](const blender::StringRef name) {
|
||||
for (const GreasePencilDashModifierSegment &ds : dmd->segments()) {
|
||||
if (&ds != dash_segment && ds.name == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
'.',
|
||||
dash_segment->name);
|
||||
|
||||
/* Fix all the animation data which may link to this. */
|
||||
char name_esc[sizeof(dmd->modifier.name) * 2];
|
||||
BLI_str_escape(name_esc, dmd->modifier.name, sizeof(name_esc));
|
||||
char rna_path_prefix[36 + sizeof(name_esc) + 1];
|
||||
SNPRINTF(rna_path_prefix, "modifiers[\"%s\"].segments", name_esc);
|
||||
BKE_animdata_fix_paths_rename_all(nullptr, rna_path_prefix, oldname.c_str(), dash_segment->name);
|
||||
}
|
||||
|
||||
static void rna_GreasePencilDashModifier_segments_begin(CollectionPropertyIterator *iter,
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
auto *dmd = static_cast<GreasePencilDashModifierData *>(ptr->data);
|
||||
rna_iterator_array_begin(iter,
|
||||
dmd->segments_array,
|
||||
sizeof(GreasePencilDashModifierSegment),
|
||||
dmd->segments_num,
|
||||
false,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void rna_def_modifier_panel_open_prop(StructRNA *srna, const char *identifier, const int id)
|
||||
@ -8449,6 +8530,111 @@ static void rna_def_modifier_grease_pencil_lattice(BlenderRNA *brna)
|
||||
RNA_define_lib_overridable(false);
|
||||
}
|
||||
|
||||
static void rna_def_modifier_grease_pencil_dash_segment(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "GreasePencilDashModifierSegment", nullptr);
|
||||
RNA_def_struct_ui_text(srna, "Dash Modifier Segment", "Configuration for a single dash segment");
|
||||
RNA_def_struct_sdna(srna, "GreasePencilDashModifierSegment");
|
||||
RNA_def_struct_path_func(srna, "rna_GreasePencilDashModifierSegment_path");
|
||||
|
||||
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Name", "Name of the dash segment");
|
||||
RNA_def_property_string_funcs(
|
||||
prop, nullptr, nullptr, "rna_GreasePencilDashModifierSegment_name_set");
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER | NA_RENAME, nullptr);
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "dash", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_range(prop, 1, INT16_MAX);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Dash",
|
||||
"The number of consecutive points from the original stroke to include in this segment");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "gap", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_range(prop, 0, INT16_MAX);
|
||||
RNA_def_property_ui_text(prop, "Gap", "The number of points skipped after this segment");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_FACTOR | PROP_UNSIGNED);
|
||||
RNA_def_property_ui_range(prop, 0, 1, 0.1, 2);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Radius", "The factor to apply to the original point's radius for the new points");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_ui_range(prop, 0, 1, 0.1, 2);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Opacity", "The factor to apply to the original point's opacity for the new points");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_sdna(prop, nullptr, "mat_nr");
|
||||
RNA_def_property_range(prop, -1, INT16_MAX);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Material Index",
|
||||
"Use this index on generated segment. -1 means using the existing material");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_cyclic", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", MOD_GREASE_PENCIL_DASH_USE_CYCLIC);
|
||||
RNA_def_property_ui_text(prop, "Cyclic", "Enable cyclic on individual stroke dashes");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
}
|
||||
|
||||
static void rna_def_modifier_grease_pencil_dash(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "GreasePencilDashModifierData", "Modifier");
|
||||
RNA_def_struct_ui_text(
|
||||
srna, "Grease Pencil Dash Modifier", "Create dot-dash effect for strokes");
|
||||
RNA_def_struct_sdna(srna, "GreasePencilDashModifierData");
|
||||
RNA_def_struct_ui_icon(srna, ICON_MOD_DASH);
|
||||
|
||||
rna_def_modifier_grease_pencil_layer_filter(srna);
|
||||
rna_def_modifier_grease_pencil_material_filter(
|
||||
srna, "rna_GreasePencilDashModifier_material_filter_set");
|
||||
|
||||
rna_def_modifier_panel_open_prop(srna, "open_influence_panel", 0);
|
||||
|
||||
RNA_define_lib_overridable(true);
|
||||
|
||||
prop = RNA_def_property(srna, "segments", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "GreasePencilDashModifierSegment");
|
||||
RNA_def_property_collection_sdna(prop, nullptr, "segments_array", nullptr);
|
||||
RNA_def_property_collection_funcs(prop,
|
||||
"rna_GreasePencilDashModifier_segments_begin",
|
||||
"rna_iterator_array_next",
|
||||
"rna_iterator_array_end",
|
||||
"rna_iterator_array_get",
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
RNA_def_property_ui_text(prop, "Segments", "");
|
||||
|
||||
prop = RNA_def_property(srna, "segment_active_index", PROP_INT, PROP_UNSIGNED);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_ui_text(prop, "Active Dash Segment Index", "Active index in the segment list");
|
||||
|
||||
prop = RNA_def_property(srna, "dash_offset", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Offset",
|
||||
"Offset into each stroke before the beginning of the dashed segment generation");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
RNA_define_lib_overridable(false);
|
||||
}
|
||||
|
||||
void RNA_def_modifier(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
@ -8626,6 +8812,8 @@ void RNA_def_modifier(BlenderRNA *brna)
|
||||
rna_def_modifier_grease_pencil_mirror(brna);
|
||||
rna_def_modifier_grease_pencil_thickness(brna);
|
||||
rna_def_modifier_grease_pencil_lattice(brna);
|
||||
rna_def_modifier_grease_pencil_dash_segment(brna);
|
||||
rna_def_modifier_grease_pencil_dash(brna);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -45,6 +45,7 @@ set(SRC
|
||||
intern/MOD_explode.cc
|
||||
intern/MOD_fluid.cc
|
||||
intern/MOD_grease_pencil_color.cc
|
||||
intern/MOD_grease_pencil_dash.cc
|
||||
intern/MOD_grease_pencil_lattice.cc
|
||||
intern/MOD_grease_pencil_mirror.cc
|
||||
intern/MOD_grease_pencil_noise.cc
|
||||
|
@ -83,6 +83,7 @@ extern ModifierTypeInfo modifierType_GreasePencilNoise;
|
||||
extern ModifierTypeInfo modifierType_GreasePencilMirror;
|
||||
extern ModifierTypeInfo modifierType_GreasePencilThickness;
|
||||
extern ModifierTypeInfo modifierType_GreasePencilLattice;
|
||||
extern ModifierTypeInfo modifierType_GreasePencilDash;
|
||||
|
||||
/* MOD_util.cc */
|
||||
|
||||
|
535
source/blender/modifiers/intern/MOD_grease_pencil_dash.cc
Normal file
535
source/blender/modifiers/intern/MOD_grease_pencil_dash.cc
Normal file
@ -0,0 +1,535 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup modifiers
|
||||
*/
|
||||
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "DNA_defaults.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_instances.hh"
|
||||
#include "BKE_lib_query.hh"
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "MOD_grease_pencil_util.hh"
|
||||
#include "MOD_modifiertypes.hh"
|
||||
#include "MOD_ui_common.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static void init_data(ModifierData *md)
|
||||
{
|
||||
auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(md);
|
||||
|
||||
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(dmd, modifier));
|
||||
|
||||
MEMCPY_STRUCT_AFTER(dmd, DNA_struct_default_get(GreasePencilDashModifierData), modifier);
|
||||
modifier::greasepencil::init_influence_data(&dmd->influence, false);
|
||||
|
||||
GreasePencilDashModifierSegment *ds = DNA_struct_default_alloc(GreasePencilDashModifierSegment);
|
||||
STRNCPY_UTF8(ds->name, DATA_("Segment"));
|
||||
dmd->segments_array = ds;
|
||||
dmd->segments_num = 1;
|
||||
}
|
||||
|
||||
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
|
||||
{
|
||||
const auto *dmd = reinterpret_cast<const GreasePencilDashModifierData *>(md);
|
||||
auto *tmmd = reinterpret_cast<GreasePencilDashModifierData *>(target);
|
||||
|
||||
modifier::greasepencil::free_influence_data(&tmmd->influence);
|
||||
|
||||
BKE_modifier_copydata_generic(md, target, flag);
|
||||
modifier::greasepencil::copy_influence_data(&dmd->influence, &tmmd->influence, flag);
|
||||
|
||||
tmmd->segments_array = static_cast<GreasePencilDashModifierSegment *>(
|
||||
MEM_dupallocN(dmd->segments_array));
|
||||
}
|
||||
|
||||
static void free_data(ModifierData *md)
|
||||
{
|
||||
auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(md);
|
||||
modifier::greasepencil::free_influence_data(&dmd->influence);
|
||||
|
||||
MEM_SAFE_FREE(dmd->segments_array);
|
||||
}
|
||||
|
||||
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
|
||||
{
|
||||
auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(md);
|
||||
modifier::greasepencil::foreach_influence_ID_link(&dmd->influence, ob, walk, user_data);
|
||||
}
|
||||
|
||||
static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/)
|
||||
{
|
||||
const auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(md);
|
||||
/* Enable if at least one segment has non-zero length. */
|
||||
for (const GreasePencilDashModifierSegment &dash_segment : dmd->segments()) {
|
||||
if (dash_segment.dash + dash_segment.gap - 1 > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int floored_modulo(const int a, const int b)
|
||||
{
|
||||
return a - math::floor(float(a) / float(b)) * b;
|
||||
}
|
||||
|
||||
/* Combined segment info used by all strokes. */
|
||||
struct PatternInfo {
|
||||
int offset = 0;
|
||||
int length = 0;
|
||||
Array<IndexRange> segments;
|
||||
Array<bool> cyclic;
|
||||
Array<int> material;
|
||||
Array<float> radius;
|
||||
Array<float> opacity;
|
||||
};
|
||||
|
||||
static PatternInfo get_pattern_info(const GreasePencilDashModifierData &dmd)
|
||||
{
|
||||
PatternInfo info;
|
||||
for (const GreasePencilDashModifierSegment &dash_segment : dmd.segments()) {
|
||||
info.length += dash_segment.dash + dash_segment.gap;
|
||||
}
|
||||
|
||||
info.segments.reinitialize(dmd.segments().size());
|
||||
info.cyclic.reinitialize(dmd.segments().size());
|
||||
info.material.reinitialize(dmd.segments().size());
|
||||
info.radius.reinitialize(dmd.segments().size());
|
||||
info.opacity.reinitialize(dmd.segments().size());
|
||||
info.offset = floored_modulo(dmd.dash_offset, info.length);
|
||||
|
||||
/* Store segments as ranges. */
|
||||
IndexRange dash_range(0);
|
||||
IndexRange gap_range(0);
|
||||
for (const int i : dmd.segments().index_range()) {
|
||||
const GreasePencilDashModifierSegment &dash_segment = dmd.segments()[i];
|
||||
dash_range = gap_range.after(dash_segment.dash);
|
||||
gap_range = dash_range.after(dash_segment.gap);
|
||||
info.segments[i] = dash_range;
|
||||
info.cyclic[i] = dash_segment.flag & MOD_GREASE_PENCIL_DASH_USE_CYCLIC;
|
||||
info.material[i] = dash_segment.mat_nr;
|
||||
info.radius[i] = dash_segment.radius;
|
||||
info.opacity[i] = dash_segment.opacity;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/* Returns the segment covering the given index, including repetitions.*/
|
||||
static int find_dash_segment(const PatternInfo &pattern_info, const int index)
|
||||
{
|
||||
const int repeat = index / pattern_info.length;
|
||||
const int segments_num = pattern_info.segments.size();
|
||||
|
||||
const int local_index = index - repeat * pattern_info.length;
|
||||
for (const int i : pattern_info.segments.index_range().drop_back(1)) {
|
||||
const IndexRange segment = pattern_info.segments[i];
|
||||
const IndexRange next_segment = pattern_info.segments[i + 1];
|
||||
if (local_index >= segment.start() && local_index < next_segment.start()) {
|
||||
return i + repeat * segments_num;
|
||||
}
|
||||
}
|
||||
return segments_num - 1 + repeat * segments_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all dash curves.
|
||||
* \param fn: Function taking an index range of source points describing new curves.
|
||||
* \note Point range can be larger than the source point range in case of cyclic curves.
|
||||
*/
|
||||
static void foreach_dash(const PatternInfo &pattern_info,
|
||||
const IndexRange src_points,
|
||||
const bool cyclic,
|
||||
FunctionRef<void(IndexRange, bool, int, float, float)> fn)
|
||||
{
|
||||
const int points_num = src_points.size();
|
||||
const int segments_num = pattern_info.segments.size();
|
||||
|
||||
const int first_segment = find_dash_segment(pattern_info, pattern_info.offset);
|
||||
const int last_segment = find_dash_segment(pattern_info, pattern_info.offset + points_num - 1);
|
||||
BLI_assert(first_segment < segments_num);
|
||||
BLI_assert(last_segment >= first_segment);
|
||||
|
||||
const IndexRange all_segments = IndexRange(first_segment, last_segment - first_segment + 1);
|
||||
for (const int i : all_segments) {
|
||||
const int repeat = i / segments_num;
|
||||
const int segment_index = i - repeat * segments_num;
|
||||
const IndexRange range = pattern_info.segments[segment_index].shift(repeat *
|
||||
pattern_info.length);
|
||||
|
||||
const int64_t point_shift = src_points.start() - pattern_info.offset;
|
||||
const int64_t min_point = src_points.start();
|
||||
const int64_t max_point = cyclic ? src_points.one_after_last() : src_points.last();
|
||||
const int64_t start = std::clamp(range.start() + point_shift, min_point, max_point);
|
||||
const int64_t end = std::clamp(range.one_after_last() + point_shift, min_point, max_point + 1);
|
||||
|
||||
IndexRange points(start, end - start);
|
||||
if (!points.is_empty()) {
|
||||
fn(points,
|
||||
pattern_info.cyclic[segment_index],
|
||||
pattern_info.material[segment_index],
|
||||
pattern_info.radius[segment_index],
|
||||
pattern_info.opacity[segment_index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bke::CurvesGeometry create_dashes(const PatternInfo &pattern_info,
|
||||
const bke::CurvesGeometry &src_curves,
|
||||
const IndexMask &curves_mask)
|
||||
{
|
||||
const bke::AttributeAccessor src_attributes = src_curves.attributes();
|
||||
const VArray<bool> src_cyclic = *src_attributes.lookup_or_default(
|
||||
"cyclic", bke::AttrDomain::Curve, false);
|
||||
const VArray<int> src_material = *src_attributes.lookup_or_default(
|
||||
"material_index", bke::AttrDomain::Curve, 0);
|
||||
const VArray<float> src_radius = *src_attributes.lookup<float>("radius", bke::AttrDomain::Point);
|
||||
const VArray<float> src_opacity = *src_attributes.lookup<float>("opacity",
|
||||
bke::AttrDomain::Point);
|
||||
|
||||
/* Count new curves and points. */
|
||||
int dst_point_num = 0;
|
||||
int dst_curve_num = 0;
|
||||
curves_mask.foreach_index([&](const int64_t src_curve_i) {
|
||||
const IndexRange src_points = src_curves.points_by_curve()[src_curve_i];
|
||||
|
||||
foreach_dash(pattern_info,
|
||||
src_points,
|
||||
src_cyclic[src_curve_i],
|
||||
[&](const IndexRange copy_points,
|
||||
bool /*cyclic*/,
|
||||
int /*material*/,
|
||||
float /*radius*/,
|
||||
float /*opacity*/) {
|
||||
dst_point_num += copy_points.size();
|
||||
dst_curve_num += 1;
|
||||
});
|
||||
});
|
||||
|
||||
bke::CurvesGeometry dst_curves(dst_point_num, dst_curve_num);
|
||||
bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
|
||||
bke::SpanAttributeWriter<bool> dst_cyclic = dst_attributes.lookup_or_add_for_write_span<bool>(
|
||||
"cyclic", bke::AttrDomain::Curve);
|
||||
bke::SpanAttributeWriter<int> dst_material = dst_attributes.lookup_or_add_for_write_span<int>(
|
||||
"material_index", bke::AttrDomain::Curve);
|
||||
bke::SpanAttributeWriter<float> dst_radius = dst_attributes.lookup_or_add_for_write_span<float>(
|
||||
"radius", bke::AttrDomain::Point);
|
||||
bke::SpanAttributeWriter<float> dst_opacity = dst_attributes.lookup_or_add_for_write_span<float>(
|
||||
"opacity", bke::AttrDomain::Point);
|
||||
/* Map each destination point and curve to its source. */
|
||||
Array<int> src_point_indices(dst_point_num);
|
||||
Array<int> src_curve_indices(dst_curve_num);
|
||||
|
||||
{
|
||||
/* Start at curve offset and add points for each dash. */
|
||||
IndexRange dst_point_range(0);
|
||||
int dst_curve_i = 0;
|
||||
auto add_dash_curve = [&](const int src_curve,
|
||||
const IndexRange src_points,
|
||||
const IndexRange copy_points,
|
||||
bool cyclic,
|
||||
int material,
|
||||
float radius,
|
||||
float opacity) {
|
||||
dst_point_range = dst_point_range.after(copy_points.size());
|
||||
dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.start();
|
||||
|
||||
if (src_points.contains(copy_points.last())) {
|
||||
array_utils::fill_index_range(src_point_indices.as_mutable_span().slice(dst_point_range),
|
||||
int(copy_points.start()));
|
||||
}
|
||||
else {
|
||||
/* Cyclic curve. */
|
||||
array_utils::fill_index_range(
|
||||
src_point_indices.as_mutable_span().slice(dst_point_range.drop_back(1)),
|
||||
int(copy_points.start()));
|
||||
src_point_indices[dst_point_range.last()] = src_points.first();
|
||||
}
|
||||
src_curve_indices[dst_curve_i] = src_curve;
|
||||
dst_cyclic.span[dst_curve_i] = cyclic;
|
||||
dst_material.span[dst_curve_i] = material >= 0 ? material : src_material[src_curve];
|
||||
for (const int i : dst_point_range) {
|
||||
dst_radius.span[i] = src_radius[src_point_indices[i]] * radius;
|
||||
dst_opacity.span[i] = src_opacity[src_point_indices[i]] * opacity;
|
||||
}
|
||||
|
||||
++dst_curve_i;
|
||||
};
|
||||
|
||||
curves_mask.foreach_index([&](const int64_t src_curve_i) {
|
||||
const IndexRange src_points = src_curves.points_by_curve()[src_curve_i];
|
||||
foreach_dash(pattern_info,
|
||||
src_points,
|
||||
src_cyclic[src_curve_i],
|
||||
[&](const IndexRange copy_points,
|
||||
bool cyclic,
|
||||
int material,
|
||||
float radius,
|
||||
float opacity) {
|
||||
add_dash_curve(
|
||||
src_curve_i, src_points, copy_points, cyclic, material, radius, opacity);
|
||||
});
|
||||
});
|
||||
if (dst_curve_i > 0) {
|
||||
/* Last offset entry is total point count. */
|
||||
dst_curves.offsets_for_write()[dst_curve_i] = dst_point_range.one_after_last();
|
||||
}
|
||||
}
|
||||
|
||||
bke::gather_attributes(src_attributes,
|
||||
bke::AttrDomain::Point,
|
||||
{},
|
||||
{"radius", "opacity"},
|
||||
src_point_indices,
|
||||
dst_attributes);
|
||||
bke::gather_attributes(src_attributes,
|
||||
bke::AttrDomain::Curve,
|
||||
{},
|
||||
{"cyclic", "material_index"},
|
||||
src_curve_indices,
|
||||
dst_attributes);
|
||||
|
||||
dst_cyclic.finish();
|
||||
dst_material.finish();
|
||||
dst_radius.finish();
|
||||
dst_opacity.finish();
|
||||
dst_curves.update_curve_types();
|
||||
|
||||
return dst_curves;
|
||||
}
|
||||
|
||||
static void modify_drawing(const GreasePencilDashModifierData &dmd,
|
||||
const ModifierEvalContext &ctx,
|
||||
const PatternInfo &pattern_info,
|
||||
bke::greasepencil::Drawing &drawing)
|
||||
{
|
||||
const bke::CurvesGeometry &src_curves = drawing.strokes();
|
||||
if (src_curves.curve_num == 0) {
|
||||
return;
|
||||
}
|
||||
/* Selected source curves. */
|
||||
IndexMaskMemory curve_mask_memory;
|
||||
const IndexMask curves_mask = modifier::greasepencil::get_filtered_stroke_mask(
|
||||
ctx.object, src_curves, dmd.influence, curve_mask_memory);
|
||||
|
||||
drawing.strokes_for_write() = create_dashes(pattern_info, src_curves, curves_mask);
|
||||
drawing.tag_topology_changed();
|
||||
}
|
||||
|
||||
static void modify_geometry_set(ModifierData *md,
|
||||
const ModifierEvalContext *ctx,
|
||||
bke::GeometrySet *geometry_set)
|
||||
{
|
||||
using bke::greasepencil::Drawing;
|
||||
|
||||
auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(md);
|
||||
|
||||
if (!geometry_set->has_grease_pencil()) {
|
||||
return;
|
||||
}
|
||||
GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
|
||||
const int frame = grease_pencil.runtime->eval_frame;
|
||||
|
||||
const PatternInfo pattern_info = get_pattern_info(*dmd);
|
||||
|
||||
IndexMaskMemory mask_memory;
|
||||
const IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask(
|
||||
grease_pencil, dmd->influence, mask_memory);
|
||||
|
||||
const Vector<Drawing *> drawings = modifier::greasepencil::get_drawings_for_write(
|
||||
grease_pencil, layer_mask, frame);
|
||||
threading::parallel_for_each(
|
||||
drawings, [&](Drawing *drawing) { modify_drawing(*dmd, *ctx, pattern_info, *drawing); });
|
||||
}
|
||||
|
||||
static void panel_draw(const bContext *C, Panel *panel)
|
||||
{
|
||||
uiLayout *layout = panel->layout;
|
||||
|
||||
PointerRNA ob_ptr;
|
||||
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
|
||||
auto *dmd = static_cast<GreasePencilDashModifierData *>(ptr->data);
|
||||
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
|
||||
uiItemR(layout, ptr, "dash_offset", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
|
||||
uiLayout *row = uiLayoutRow(layout, false);
|
||||
uiLayoutSetPropSep(row, false);
|
||||
|
||||
uiTemplateList(row,
|
||||
(bContext *)C,
|
||||
"MOD_UL_grease_pencil_dash_modifier_segments",
|
||||
"",
|
||||
ptr,
|
||||
"segments",
|
||||
ptr,
|
||||
"segment_active_index",
|
||||
nullptr,
|
||||
3,
|
||||
10,
|
||||
0,
|
||||
1,
|
||||
UI_TEMPLATE_LIST_FLAG_NONE);
|
||||
|
||||
uiLayout *col = uiLayoutColumn(row, false);
|
||||
uiLayout *sub = uiLayoutColumn(col, true);
|
||||
uiItemO(sub, "", ICON_ADD, "OBJECT_OT_grease_pencil_dash_modifier_segment_add");
|
||||
uiItemO(sub, "", ICON_REMOVE, "OBJECT_OT_grease_pencil_dash_modifier_segment_remove");
|
||||
uiItemS(col);
|
||||
sub = uiLayoutColumn(col, true);
|
||||
uiItemEnumO_string(
|
||||
sub, "", ICON_TRIA_UP, "OBJECT_OT_grease_pencil_dash_modifier_segment_move", "type", "UP");
|
||||
uiItemEnumO_string(sub,
|
||||
"",
|
||||
ICON_TRIA_DOWN,
|
||||
"OBJECT_OT_grease_pencil_dash_modifier_segment_move",
|
||||
"type",
|
||||
"DOWN");
|
||||
|
||||
if (dmd->segment_active_index >= 0 && dmd->segment_active_index < dmd->segments_num) {
|
||||
PointerRNA ds_ptr = RNA_pointer_create(ptr->owner_id,
|
||||
&RNA_GreasePencilDashModifierSegment,
|
||||
&dmd->segments()[dmd->segment_active_index]);
|
||||
|
||||
sub = uiLayoutColumn(layout, true);
|
||||
uiItemR(sub, &ds_ptr, "dash", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
uiItemR(sub, &ds_ptr, "gap", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
|
||||
sub = uiLayoutColumn(layout, false);
|
||||
uiItemR(sub, &ds_ptr, "radius", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
uiItemR(sub, &ds_ptr, "opacity", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
uiItemR(sub, &ds_ptr, "material_index", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
uiItemR(sub, &ds_ptr, "use_cyclic", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
}
|
||||
|
||||
if (uiLayout *influence_panel = uiLayoutPanelProp(
|
||||
C, layout, ptr, "open_influence_panel", "Influence"))
|
||||
{
|
||||
modifier::greasepencil::draw_layer_filter_settings(C, influence_panel, ptr);
|
||||
modifier::greasepencil::draw_material_filter_settings(C, influence_panel, ptr);
|
||||
}
|
||||
|
||||
modifier_panel_end(layout, ptr);
|
||||
}
|
||||
|
||||
static void segment_list_item_draw(uiList * /*ui_list*/,
|
||||
const bContext * /*C*/,
|
||||
uiLayout *layout,
|
||||
PointerRNA * /*idataptr*/,
|
||||
PointerRNA *itemptr,
|
||||
int /*icon*/,
|
||||
PointerRNA * /*active_dataptr*/,
|
||||
const char * /*active_propname*/,
|
||||
int /*index*/,
|
||||
int /*flt_flag*/)
|
||||
{
|
||||
uiLayout *row = uiLayoutRow(layout, true);
|
||||
uiItemR(row, itemptr, "name", UI_ITEM_R_NO_BG, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void panel_register(ARegionType *region_type)
|
||||
{
|
||||
modifier_panel_register(region_type, eModifierType_GreasePencilDash, panel_draw);
|
||||
|
||||
uiListType *list_type = static_cast<uiListType *>(
|
||||
MEM_callocN(sizeof(uiListType), "Grease Pencil Dash modifier segments"));
|
||||
STRNCPY(list_type->idname, "MOD_UL_grease_pencil_dash_modifier_segments");
|
||||
list_type->draw_item = segment_list_item_draw;
|
||||
WM_uilisttype_add(list_type);
|
||||
}
|
||||
|
||||
static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
|
||||
{
|
||||
const auto *dmd = reinterpret_cast<const GreasePencilDashModifierData *>(md);
|
||||
|
||||
BLO_write_struct(writer, GreasePencilDashModifierData, dmd);
|
||||
modifier::greasepencil::write_influence_data(writer, &dmd->influence);
|
||||
|
||||
BLO_write_struct_array(
|
||||
writer, GreasePencilDashModifierSegment, dmd->segments_num, dmd->segments_array);
|
||||
}
|
||||
|
||||
static void blend_read(BlendDataReader *reader, ModifierData *md)
|
||||
{
|
||||
auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(md);
|
||||
|
||||
modifier::greasepencil::read_influence_data(reader, &dmd->influence);
|
||||
|
||||
BLO_read_data_address(reader, &dmd->segments_array);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
||||
ModifierTypeInfo modifierType_GreasePencilDash = {
|
||||
/*idname*/ "GreasePencilDash",
|
||||
/*name*/ N_("Dot Dash"),
|
||||
/*struct_name*/ "GreasePencilDashModifierData",
|
||||
/*struct_size*/ sizeof(GreasePencilDashModifierData),
|
||||
/*srna*/ &RNA_GreasePencilDashModifierData,
|
||||
/*type*/ ModifierTypeType::Nonconstructive,
|
||||
/*flags*/ eModifierTypeFlag_AcceptsGreasePencil | eModifierTypeFlag_SupportsEditmode |
|
||||
eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping,
|
||||
/*icon*/ ICON_MOD_DASH,
|
||||
|
||||
/*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*/ blender::is_disabled,
|
||||
/*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,
|
||||
};
|
||||
|
||||
blender::Span<GreasePencilDashModifierSegment> GreasePencilDashModifierData::segments() const
|
||||
{
|
||||
return {this->segments_array, this->segments_num};
|
||||
}
|
||||
|
||||
blender::MutableSpan<GreasePencilDashModifierSegment> GreasePencilDashModifierData::segments()
|
||||
{
|
||||
return {this->segments_array, this->segments_num};
|
||||
}
|
@ -280,5 +280,6 @@ void modifier_type_init(ModifierTypeInfo *types[])
|
||||
INIT_TYPE(GreasePencilMirror);
|
||||
INIT_TYPE(GreasePencilThickness);
|
||||
INIT_TYPE(GreasePencilLattice);
|
||||
INIT_TYPE(GreasePencilDash);
|
||||
#undef INIT_TYPE
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user