Blender V5.0
MOD_grease_pencil_util.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
10
11#include "BLI_set.hh"
12#include "BLI_vector_set.hh"
13
15#include "DNA_material_types.h"
16#include "DNA_modifier_types.h"
17#include "DNA_screen_types.h"
18
19#include "BKE_colortools.hh"
20#include "BKE_curves.hh"
21#include "BKE_grease_pencil.hh"
22#include "BKE_lib_query.hh"
23#include "BKE_material.hh"
24
25#include "BLT_translation.hh"
26
27#include "BLO_read_write.hh"
28
29#include "RNA_access.hh"
30#include "RNA_prototypes.hh"
31
32#include "UI_interface.hh"
34
36
38
43
45 const bool has_custom_curve)
46{
47 if (has_custom_curve) {
48 influence_data->custom_curve = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
49 BKE_curvemapping_init(influence_data->custom_curve);
50 }
51}
52
54 GreasePencilModifierInfluenceData *influence_data_dst,
55 const int /*flag*/)
56{
57 memcpy(influence_data_dst, influence_data_src, sizeof(GreasePencilModifierInfluenceData));
58 influence_data_dst->custom_curve = BKE_curvemapping_copy(influence_data_src->custom_curve);
59}
60
62{
63 if (influence_data->custom_curve) {
64 BKE_curvemapping_free(influence_data->custom_curve);
65 influence_data->custom_curve = nullptr;
66 }
67}
68
70 Object *ob,
71 IDWalkFunc walk,
72 void *user_data)
73{
74 walk(user_data, ob, (ID **)&influence_data->material, IDWALK_CB_USER);
75}
76
78 const GreasePencilModifierInfluenceData *influence_data)
79{
80 if (influence_data->custom_curve) {
81 BKE_curvemapping_blend_write(writer, influence_data->custom_curve);
82 }
83}
84
87{
88 BLO_read_struct(reader, CurveMapping, &influence_data->custom_curve);
89 if (influence_data->custom_curve) {
90 BKE_curvemapping_blend_read(reader, influence_data->custom_curve);
91 /* Make sure the internal table exists. */
92 BKE_curvemapping_init(influence_data->custom_curve);
93 }
94}
95
97{
98 PointerRNA ob_ptr = RNA_pointer_create_discrete(ptr->owner_id, &RNA_Object, ptr->owner_id);
99 PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
100 const bool use_layer_pass = RNA_boolean_get(ptr, "use_layer_pass_filter");
101 const bool use_layer_group_filter = RNA_boolean_get(ptr, "use_layer_group_filter");
102 uiLayout *row, *col, *sub, *subsub;
103
104 layout->use_property_split_set(true);
105
106 col = &layout->column(true);
107 row = &col->row(true);
108 row->use_property_decorate_set(false);
109 if (use_layer_group_filter) {
110 row->prop_search(ptr,
111 "tree_node_filter",
112 &obj_data_ptr,
113 "layer_groups",
114 IFACE_("Group"),
115 ICON_GREASEPENCIL_LAYER_GROUP);
116 }
117 else {
118 row->prop_search(ptr,
119 "tree_node_filter",
120 &obj_data_ptr,
121 "layers",
122 std::nullopt,
123 ICON_OUTLINER_DATA_GP_LAYER);
124 }
125 sub = &row->row(true);
126 sub->prop(ptr, "use_layer_group_filter", UI_ITEM_NONE, "", ICON_GREASEPENCIL_LAYER_GROUP);
127 sub->prop(ptr, "invert_layer_filter", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
128
129 row = &col->row(true, IFACE_("Layer Pass"));
130 row->use_property_decorate_set(false);
131 sub = &row->row(true);
132 sub->prop(ptr, "use_layer_pass_filter", UI_ITEM_NONE, "", ICON_NONE);
133 subsub = &sub->row(true);
134 subsub->active_set(use_layer_pass);
135 subsub->prop(ptr, "layer_pass_filter", UI_ITEM_NONE, "", ICON_NONE);
136 subsub->prop(ptr, "invert_layer_pass_filter", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
137}
138
140{
141 PointerRNA ob_ptr = RNA_pointer_create_discrete(ptr->owner_id, &RNA_Object, ptr->owner_id);
142 PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
143 const bool use_material_pass = RNA_boolean_get(ptr, "use_material_pass_filter");
144 uiLayout *row, *col, *sub, *subsub;
145
146 layout->use_property_split_set(true);
147
148 col = &layout->column(true);
149 row = &col->row(true);
150 row->use_property_decorate_set(false);
151 row->prop_search(
152 ptr, "material_filter", &obj_data_ptr, "materials", std::nullopt, ICON_SHADING_TEXTURE);
153 sub = &row->row(true);
154 sub->prop(ptr, "invert_material_filter", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
155
156 row = &col->row(true, IFACE_("Material Pass"));
157 row->use_property_decorate_set(false);
158 sub = &row->row(true);
159 sub->prop(ptr, "use_material_pass_filter", UI_ITEM_NONE, "", ICON_NONE);
160 subsub = &sub->row(true);
161 subsub->active_set(use_material_pass);
162 subsub->prop(ptr, "material_pass_filter", UI_ITEM_NONE, "", ICON_NONE);
163 subsub->prop(ptr, "invert_material_pass_filter", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
164}
165
167{
168 PointerRNA ob_ptr = RNA_pointer_create_discrete(ptr->owner_id, &RNA_Object, ptr->owner_id);
169 bool has_vertex_group = RNA_string_length(ptr, "vertex_group_name") != 0;
170 uiLayout *row, *col, *sub;
171
172 layout->use_property_split_set(true);
173
174 col = &layout->column(true);
175 row = &col->row(true);
176 row->use_property_decorate_set(false);
177 row->prop_search(ptr, "vertex_group_name", &ob_ptr, "vertex_groups", std::nullopt, ICON_NONE);
178 sub = &row->row(true);
179 sub->active_set(has_vertex_group);
180 sub->use_property_decorate_set(false);
181 sub->prop(ptr, "invert_vertex_group", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT);
182}
183
185{
186 bool use_custom_curve = RNA_boolean_get(ptr, "use_custom_curve");
187 uiLayout *row;
188
189 layout->use_property_split_set(true);
190 row = &layout->row(true);
191 row->use_property_decorate_set(false);
192 row->prop(ptr, "use_custom_curve", UI_ITEM_NONE, IFACE_("Custom Curve"), ICON_NONE);
193 if (use_custom_curve) {
194 uiTemplateCurveMapping(layout, ptr, "custom_curve", 0, false, false, false, false, false);
195 }
196}
197
204{
205 short *totcol = BKE_object_material_len_p(const_cast<Object *>(ob));
206 Vector<int> result(*totcol, 0);
207 for (short i = 0; i < *totcol; i++) {
208 const Material *ma = BKE_object_material_get(const_cast<Object *>(ob), i + 1);
209 if (ma) {
210 /* Pass index of the grease pencil material. */
211 result[i] = ma->gp_style->index;
212 }
213 }
214 return result;
215}
216
218 const std::optional<StringRef> tree_node_name_filter,
219 const std::optional<int> layer_pass_filter,
220 const bool layer_filter_invert,
221 const bool layer_pass_filter_invert,
222 IndexMaskMemory &memory)
223{
224 const IndexMask full_mask = grease_pencil.layers().index_range();
225 if (!tree_node_name_filter && !layer_pass_filter) {
226 return full_mask;
227 }
228
229 bke::AttributeAccessor layer_attributes = grease_pencil.attributes();
230 const Span<const Layer *> layers = grease_pencil.layers();
231 const VArray<int> layer_passes =
232 layer_attributes.lookup_or_default<int>("pass_index", bke::AttrDomain::Layer, 0).varray;
233
234 const LayerGroup *filter_layer_group = nullptr;
235 if (tree_node_name_filter) {
236 for (const LayerGroup *group : grease_pencil.layer_groups()) {
237 if (group->name() == tree_node_name_filter.value()) {
238 filter_layer_group = group;
239 break;
240 }
241 }
242 }
243
245 full_mask, GrainSize(4096), memory, [&](const int64_t layer_i) {
246 if (tree_node_name_filter) {
247 const Layer *layer = layers[layer_i];
248 if (filter_layer_group) {
249 const bool match = layer->is_child_of(*filter_layer_group);
250 if (match == layer_filter_invert) {
251 return false;
252 }
253 }
254 else {
255 const bool match = (layer->name() == tree_node_name_filter.value());
256 if (match == layer_filter_invert) {
257 return false;
258 }
259 }
260 }
261 if (layer_pass_filter) {
262 const int layer_pass = layer_passes.get(layer_i);
263 const bool match = (layer_pass == layer_pass_filter.value());
264 if (match == layer_pass_filter_invert) {
265 return false;
266 }
267 }
268 return true;
269 });
270 return result;
271}
272
274 const GreasePencilModifierInfluenceData &influence_data,
275 IndexMaskMemory &memory)
276{
278 grease_pencil,
279 influence_data.layer_name[0] != '\0' ?
280 std::make_optional<StringRef>(influence_data.layer_name) :
281 std::nullopt,
283 std::make_optional<int>(influence_data.layer_pass) :
284 std::nullopt,
287 memory);
288}
289
291 const bke::CurvesGeometry &curves,
292 const Material *material_filter,
293 const std::optional<int> material_pass_filter,
294 const bool material_filter_invert,
295 const bool material_pass_filter_invert,
296 IndexMaskMemory &memory)
297{
298 const IndexMask full_mask = curves.curves_range();
299 if (!material_filter && !material_pass_filter) {
300 return full_mask;
301 }
302
303 const int material_filter_index = BKE_object_material_index_get(
304 const_cast<Object *>(ob), const_cast<Material *>(material_filter));
305 const Vector<int> material_pass_by_index = get_grease_pencil_material_passes(ob);
306
307 bke::AttributeAccessor attributes = curves.attributes();
308 VArray<int> stroke_materials =
309 attributes.lookup_or_default<int>("material_index", bke::AttrDomain::Curve, 0).varray;
310
312 full_mask, GrainSize(4096), memory, [&](const int64_t stroke_i) {
313 const int material_index = stroke_materials.get(stroke_i);
314 if (material_filter != nullptr) {
315 const bool match = (material_index == material_filter_index);
316 if (match == material_filter_invert) {
317 return false;
318 }
319 }
320 if (material_pass_filter) {
321 const int material_pass = material_pass_by_index[material_index];
322 const bool match = (material_pass == material_pass_filter.value());
323 if (match == material_pass_filter_invert) {
324 return false;
325 }
326 }
327 return true;
328 });
329 return result;
330}
331
333 const bke::CurvesGeometry &curves,
334 const GreasePencilModifierInfluenceData &influence_data,
335 IndexMaskMemory &memory)
336{
338 ob,
339 curves,
340 influence_data.material,
342 std::make_optional<int>(influence_data.material_pass) :
343 std::nullopt,
346 memory);
347}
348
350 const GreasePencilModifierInfluenceData &influence_data)
351{
352 if (influence_data.vertex_group_name[0] == '\0') {
353 /* If vertex group is not set, use full weight for all vertices. */
354 return VArray<float>::from_single(1.0f, curves.point_num);
355 }
356 /* Vertex group weights, with zero weight as a fallback. */
357 VArray<float> influence_weights = *curves.attributes().lookup_or_default<float>(
358 influence_data.vertex_group_name, bke::AttrDomain::Point, 0.0f);
359
361 Array<float> influence_weights_inverted(influence_weights.size());
363 influence_weights_inverted.index_range(), 8192, [&](const IndexRange range) {
364 for (const int i : range) {
365 influence_weights_inverted[i] = 1.0f - influence_weights[i];
366 }
367 });
368 return VArray<float>::from_container(influence_weights_inverted);
369 }
370
371 return influence_weights;
372}
373
375 const IndexMask &layer_mask,
376 const int frame)
377{
378 using namespace blender::bke::greasepencil;
379 VectorSet<Drawing *> drawings;
380 layer_mask.foreach_index([&](const int64_t layer_i) {
381 const Layer &layer = grease_pencil.layer(layer_i);
382 /* Set of owned drawings, ignore drawing references to other data blocks. */
383 if (Drawing *drawing = grease_pencil.get_drawing_at(layer, frame)) {
384 drawings.add(drawing);
385 }
386 });
387 return Vector<Drawing *>(drawings.as_span());
388}
389
391 const IndexMask &layer_mask,
392 const int frame)
393{
394 using namespace blender::bke::greasepencil;
395 Set<Drawing *> drawings;
396 Vector<LayerDrawingInfo> drawing_infos;
397 layer_mask.foreach_index([&](const int64_t layer_i) {
398 const Layer &layer = grease_pencil.layer(layer_i);
399 Drawing *drawing = grease_pencil.get_drawing_at(layer, frame);
400 if (drawing == nullptr) {
401 return;
402 }
403
404 if (!drawings.contains(drawing)) {
405 drawings.add_new(drawing);
406 drawing_infos.append({drawing, int(layer_i)});
407 }
408 });
409 return drawing_infos;
410}
411
413 const IndexMask &layer_mask,
414 const int frame)
415{
416 using namespace blender::bke::greasepencil;
417 Set<Drawing *> drawings;
418 Vector<FrameDrawingInfo> drawing_infos;
419 layer_mask.foreach_index([&](const int64_t layer_i) {
420 const Layer &layer = grease_pencil.layer(layer_i);
421 const std::optional<int> start_frame = layer.start_frame_at(frame);
422 if (!start_frame) {
423 return;
424 }
425 Drawing *drawing = grease_pencil.get_drawing_at(layer, *start_frame);
426 if (drawing == nullptr) {
427 return;
428 }
429
430 if (!drawings.contains(drawing)) {
431 drawings.add_new(drawing);
432 drawing_infos.append({drawing, *start_frame});
433 }
434 });
435 return drawing_infos;
436}
437
439{
440 const bke::CurvesGeometry &curves = drawing.strokes();
441 IndexMaskMemory memory;
442 const IndexMask bezier_selection = curves.indices_for_curve_type(CURVE_TYPE_BEZIER, memory);
443 if (bezier_selection.is_empty()) {
444 return;
445 }
446 drawing.strokes_for_write() = geometry::resample_to_evaluated(curves, bezier_selection);
447 drawing.tag_topology_changed();
448}
449
450} // namespace blender::modifier::greasepencil
void BKE_curvemapping_blend_read(BlendDataReader *reader, CurveMapping *cumap)
CurveMapping * BKE_curvemapping_copy(const CurveMapping *cumap)
void BKE_curvemapping_init(CurveMapping *cumap)
CurveMapping * BKE_curvemapping_add(int tot, float minx, float miny, float maxx, float maxy)
Definition colortools.cc:89
void BKE_curvemapping_free(CurveMapping *cumap)
void BKE_curvemapping_blend_write(BlendWriter *writer, const CurveMapping *cumap)
Low-level operations for curves.
Low-level operations for grease pencil.
@ IDWALK_CB_USER
General operations, lookup, etc. for materials.
short * BKE_object_material_len_p(Object *ob)
Material * BKE_object_material_get(Object *ob, short act)
int BKE_object_material_index_get(Object *ob, const Material *ma)
void(*)(void *user_data, Object *ob, ID **idpoin, LibraryForeachIDCallbackFlag cb_flag) IDWalkFunc
#define BLO_read_struct(reader, struct_name, ptr_p)
#define IFACE_(msgid)
@ GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP
@ GREASE_PENCIL_INFLUENCE_USE_MATERIAL_PASS_FILTER
@ GREASE_PENCIL_INFLUENCE_USE_LAYER_PASS_FILTER
@ GREASE_PENCIL_INFLUENCE_INVERT_LAYER_FILTER
@ GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_PASS_FILTER
@ GREASE_PENCIL_INFLUENCE_INVERT_MATERIAL_FILTER
@ GREASE_PENCIL_INFLUENCE_INVERT_LAYER_PASS_FILTER
void uiTemplateCurveMapping(uiLayout *layout, PointerRNA *ptr, blender::StringRefNull propname, int type, bool levels, bool brush, bool neg_slope, bool tone, bool presets)
#define UI_ITEM_NONE
long long int int64_t
IndexRange index_range() const
Definition BLI_array.hh:360
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
bool contains(const Key &key) const
Definition BLI_set.hh:310
void add_new(const Key &key)
Definition BLI_set.hh:233
T get(const int64_t index) const
static VArray from_single(T value, const int64_t size)
static VArray from_container(ContainerT container)
bool add(const Key &key)
Span< Key > as_span() const
void append(const T &value)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
IndexRange curves_range() const
AttributeAccessor attributes() const
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
std::optional< int > start_frame_at(int frame_number) const
void foreach_index(Fn &&fn) const
uint col
CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, const IndexMask &selection, const ResampleCurvesOutputAttributeIDs &output_ids={})
void read_influence_data(BlendDataReader *reader, GreasePencilModifierInfluenceData *influence_data)
void init_influence_data(GreasePencilModifierInfluenceData *influence_data, const bool has_custom_curve)
Vector< LayerDrawingInfo > get_drawing_infos_by_layer(GreasePencil &grease_pencil, const IndexMask &layer_mask, const int frame)
Vector< FrameDrawingInfo > get_drawing_infos_by_frame(GreasePencil &grease_pencil, const IndexMask &layer_mask, const int frame)
static IndexMask get_filtered_stroke_mask(const Object *ob, const bke::CurvesGeometry &curves, const Material *material_filter, const std::optional< int > material_pass_filter, const bool material_filter_invert, const bool material_pass_filter_invert, IndexMaskMemory &memory)
void write_influence_data(BlendWriter *writer, const GreasePencilModifierInfluenceData *influence_data)
void draw_vertex_group_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
VArray< float > get_influence_vertex_weights(const bke::CurvesGeometry &curves, const GreasePencilModifierInfluenceData &influence_data)
static IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil, const std::optional< StringRef > tree_node_name_filter, const std::optional< int > layer_pass_filter, const bool layer_filter_invert, const bool layer_pass_filter_invert, IndexMaskMemory &memory)
Vector< bke::greasepencil::Drawing * > get_drawings_for_write(GreasePencil &grease_pencil, const IndexMask &layer_mask, const int frame)
void draw_material_filter_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
void draw_layer_filter_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
static Vector< int > get_grease_pencil_material_passes(const Object *ob)
void draw_custom_curve_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
void free_influence_data(GreasePencilModifierInfluenceData *influence_data)
void foreach_influence_ID_link(GreasePencilModifierInfluenceData *influence_data, Object *ob, IDWalkFunc walk, void *user_data)
void copy_influence_data(const GreasePencilModifierInfluenceData *influence_data_src, GreasePencilModifierInfluenceData *influence_data_dst, const int)
void ensure_no_bezier_curves(Drawing &drawing)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
PointerRNA RNA_pointer_get(PointerRNA *ptr, const char *name)
int RNA_string_length(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
Definition DNA_ID.h:414
struct MaterialGPencilStyle * gp_style
void use_property_decorate_set(bool is_sep)
uiLayout & column(bool align)
void prop_search(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, PropertyRNA *item_searchpropname, std::optional< blender::StringRefNull > name, int icon, bool results_are_suggestions)
void active_set(bool active)
uiLayout & row(bool align)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4238