Blender V4.5
grease_pencil.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <iostream>
10#include <optional>
11
12#include "BKE_action.hh"
13#include "BKE_anim_data.hh"
14#include "BKE_animsys.h"
15#include "BKE_asset_edit.hh"
20#include "BKE_curves.hh"
21#include "BKE_customdata.hh"
22#include "BKE_deform.hh"
23#include "BKE_fcurve.hh"
24#include "BKE_geometry_set.hh"
25#include "BKE_grease_pencil.h"
26#include "BKE_grease_pencil.hh"
27#include "BKE_idtype.hh"
28#include "BKE_lib_id.hh"
29#include "BKE_lib_query.hh"
30#include "BKE_main.hh"
31#include "BKE_material.hh"
32#include "BKE_modifier.hh"
33#include "BKE_object.hh"
34#include "BKE_object_types.hh"
35
36#include "BLI_array_utils.hh"
37#include "BLI_bounds.hh"
39#include "BLI_listbase.h"
40#include "BLI_map.hh"
42#include "BLI_math_geom.h"
43#include "BLI_math_matrix.h"
44#include "BLI_math_matrix.hh"
47#include "BLI_memarena.h"
48#include "BLI_memory_utils.hh"
49#include "BLI_polyfill_2d.h"
50#include "BLI_resource_scope.hh"
51#include "BLI_span.hh"
52#include "BLI_stack.hh"
53#include "BLI_string.h"
54#include "BLI_string_ref.hh"
55#include "BLI_string_utils.hh"
56#include "BLI_utildefines.h"
57#include "BLI_vector_set.hh"
58#include "BLI_virtual_array.hh"
59
60#include "BLO_read_write.hh"
61
62#include "BLT_translation.hh"
63
64#include "DNA_ID.h"
65#include "DNA_ID_enums.h"
66#include "DNA_brush_types.h"
67#include "DNA_defaults.h"
69#include "DNA_material_types.h"
70#include "DNA_modifier_types.h"
71
72#include "DEG_depsgraph.hh"
74
75#include "RNA_access.hh"
76#include "RNA_path.hh"
77#include "RNA_prototypes.hh"
78
79#include "MEM_guardedalloc.h"
80
81using blender::float3;
82using blender::int3;
83using blender::Span;
85
86static const char *ATTR_POSITION = "position";
87
88/* Forward declarations. */
89static void read_drawing_array(GreasePencil &grease_pencil, BlendDataReader *reader);
90static void write_drawing_array(GreasePencil &grease_pencil,
92 BlendWriter *writer);
93static void free_drawing_array(GreasePencil &grease_pencil);
94
95static void read_layer_tree(GreasePencil &grease_pencil, BlendDataReader *reader);
96static void write_layer_tree(GreasePencil &grease_pencil, BlendWriter *writer);
97
99{
100 using namespace blender::bke;
101
102 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(id);
103 BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(grease_pencil, id));
104
106
107 grease_pencil->root_group_ptr = MEM_new<greasepencil::LayerGroup>(__func__);
108 grease_pencil->set_active_node(nullptr);
109
110 CustomData_reset(&grease_pencil->layers_data);
111 new (&grease_pencil->attribute_storage.wrap()) blender::bke::AttributeStorage();
112
113 grease_pencil->runtime = MEM_new<GreasePencilRuntime>(__func__);
114}
115
116/* See if the layer visibility is animated. This is determined whenever a copy is made, so that
117 * this happens in the "create evaluation copy" node of the depsgraph. */
118static void grease_pencil_set_runtime_visibilities(ID &id_dst, GreasePencil &grease_pencil)
119{
120 using namespace blender::bke;
121
122 if (!DEG_is_evaluated(&id_dst) || !grease_pencil.adt) {
123 return;
124 }
125
126 PropertyRNA *layer_hide_prop = RNA_struct_type_find_property(&RNA_GreasePencilLayer, "hide");
127 BLI_assert_msg(layer_hide_prop,
128 "RNA struct GreasePencilLayer is expected to have a 'hide' property.");
129 PropertyRNA *group_hide_prop = RNA_struct_type_find_property(&RNA_GreasePencilLayerGroup,
130 "hide");
131 BLI_assert_msg(group_hide_prop,
132 "RNA struct GreasePencilLayerGroup is expected to have a 'hide' property.");
133
134 for (greasepencil::LayerGroup *layer_group : grease_pencil.layer_groups_for_write()) {
136 &id_dst, &RNA_GreasePencilLayerGroup, layer_group);
137 std::optional<std::string> rna_path = RNA_path_from_ID_to_property(&layer_ptr,
138 group_hide_prop);
140 rna_path,
141 "It should be possible to construct the RNA path of a grease pencil layer group.");
142
143 layer_group->runtime->is_visibility_animated_ = animdata::prop_is_animated(
144 grease_pencil.adt, rna_path->c_str(), 0);
145 }
146
147 std::function<bool(greasepencil::LayerGroup &)> parent_group_visibility_animated =
148 [&](greasepencil::LayerGroup &parent) {
149 if (parent.runtime->is_visibility_animated_) {
150 return true;
151 }
152 greasepencil::LayerGroup *parent_group = parent.as_node().parent_group();
153 if (parent_group) {
154 return parent_group_visibility_animated(*parent_group);
155 }
156 return false;
157 };
158
159 for (greasepencil::Layer *layer : grease_pencil.layers_for_write()) {
160 if (parent_group_visibility_animated(layer->parent_group())) {
161 layer->runtime->is_visibility_animated_ = true;
162 continue;
163 }
164 PointerRNA layer_ptr = RNA_pointer_create_discrete(&id_dst, &RNA_GreasePencilLayer, layer);
165 std::optional<std::string> rna_path = RNA_path_from_ID_to_property(&layer_ptr,
166 layer_hide_prop);
167 BLI_assert_msg(rna_path,
168 "It should be possible to construct the RNA path of a grease pencil layer.");
169 layer->runtime->is_visibility_animated_ = animdata::prop_is_animated(
170 grease_pencil.adt, rna_path.value(), 0);
171 }
172}
173
174static void grease_pencil_copy_data(Main * /*bmain*/,
175 std::optional<Library *> /*owner_library*/,
176 ID *id_dst,
177 const ID *id_src,
178 const int /*flag*/)
179{
180 using namespace blender;
181
182 GreasePencil *grease_pencil_dst = reinterpret_cast<GreasePencil *>(id_dst);
183 const GreasePencil *grease_pencil_src = reinterpret_cast<const GreasePencil *>(id_src);
184
185 /* Duplicate material array. */
186 grease_pencil_dst->material_array = static_cast<Material **>(
187 MEM_dupallocN(grease_pencil_src->material_array));
188
189 BKE_grease_pencil_duplicate_drawing_array(grease_pencil_src, grease_pencil_dst);
190
191 /* Duplicate layer tree. */
192 grease_pencil_dst->root_group_ptr = MEM_new<bke::greasepencil::LayerGroup>(
193 __func__, grease_pencil_src->root_group());
194
195 /* Set active node. */
196 if (grease_pencil_src->get_active_node()) {
197 bke::greasepencil::TreeNode *active_node = grease_pencil_dst->find_node_by_name(
198 grease_pencil_src->get_active_node()->name());
199 BLI_assert(active_node);
200 grease_pencil_dst->set_active_node(active_node);
201 }
202
203 CustomData_init_from(&grease_pencil_src->layers_data,
204 &grease_pencil_dst->layers_data,
206 grease_pencil_dst->layers().size());
207 new (&grease_pencil_dst->attribute_storage.wrap())
208 blender::bke::AttributeStorage(grease_pencil_src->attribute_storage.wrap());
209
210 BKE_defgroup_copy_list(&grease_pencil_dst->vertex_group_names,
211 &grease_pencil_src->vertex_group_names);
212
213 /* Make sure the runtime pointer exists. */
214 grease_pencil_dst->runtime = MEM_new<bke::GreasePencilRuntime>(__func__);
215
216 if (grease_pencil_src->runtime->bake_materials) {
217 grease_pencil_dst->runtime->bake_materials = std::make_unique<bke::bake::BakeMaterialsList>(
218 *grease_pencil_src->runtime->bake_materials);
219 }
220
221 grease_pencil_set_runtime_visibilities(*id_dst, *grease_pencil_dst);
222}
223
225{
226 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(id);
227 BKE_animdata_free(&grease_pencil->id, false);
228
229 MEM_SAFE_FREE(grease_pencil->material_array);
230
231 CustomData_free(&grease_pencil->layers_data);
232 grease_pencil->attribute_storage.wrap().~AttributeStorage();
233
234 free_drawing_array(*grease_pencil);
235 MEM_delete(&grease_pencil->root_group());
236
237 BLI_freelistN(&grease_pencil->vertex_group_names);
238
240
241 MEM_delete(grease_pencil->runtime);
242 grease_pencil->runtime = nullptr;
243}
244
246{
247 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(id);
248 for (int i = 0; i < grease_pencil->material_array_num; i++) {
250 }
251 for (GreasePencilDrawingBase *drawing_base : grease_pencil->drawings()) {
252 if (drawing_base->type == GP_DRAWING_REFERENCE) {
253 GreasePencilDrawingReference *drawing_reference =
254 reinterpret_cast<GreasePencilDrawingReference *>(drawing_base);
256 }
257 }
258 for (const blender::bke::greasepencil::Layer *layer : grease_pencil->layers()) {
259 if (layer->parent) {
261 }
262 }
263}
264
265static void grease_pencil_blend_write(BlendWriter *writer, ID *id, const void *id_address)
266{
267 using namespace blender;
268 using namespace blender::bke;
269 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(id);
270
272
273 blender::Vector<CustomDataLayer, 16> layers_data_layers;
276 {{AttrDomain::Layer, &layers_data_layers}},
277 attribute_data);
278 CustomData_blend_write_prepare(grease_pencil->layers_data,
280 grease_pencil->layers().size(),
281 layers_data_layers,
282 attribute_data);
283 grease_pencil->attribute_storage.dna_attributes = attribute_data.attributes.data();
284 grease_pencil->attribute_storage.dna_attributes_num = attribute_data.attributes.size();
285
286 /* Write LibData */
287 BLO_write_id_struct(writer, GreasePencil, id_address, &grease_pencil->id);
288 BKE_id_blend_write(writer, &grease_pencil->id);
289
291 &grease_pencil->layers_data,
292 layers_data_layers,
293 grease_pencil->layers().size(),
295 id);
296 grease_pencil->attribute_storage.wrap().blend_write(*writer, attribute_data);
297
298 /* Write drawings. */
299 write_drawing_array(*grease_pencil, scope, writer);
300 /* Write layer tree. */
301 write_layer_tree(*grease_pencil, writer);
302
303 /* Write materials. */
305 writer, grease_pencil->material_array_num, grease_pencil->material_array);
306 /* Write vertex group names. */
307 BKE_defbase_blend_write(writer, &grease_pencil->vertex_group_names);
308}
309
311{
312 using namespace blender::bke::greasepencil;
313 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(id);
314
315 /* Read drawings. */
316 read_drawing_array(*grease_pencil, reader);
317 /* Read layer tree. */
318 read_layer_tree(*grease_pencil, reader);
319
320 CustomData_blend_read(reader, &grease_pencil->layers_data, grease_pencil->layers().size());
321 grease_pencil->attribute_storage.wrap().blend_read(*reader);
322
323 /* Forward compatibility. To be removed when runtime format changes. */
325
326 /* Read materials. */
328 grease_pencil->material_array_num,
329 reinterpret_cast<void **>(&grease_pencil->material_array));
330 /* Read vertex group names. */
331 BLO_read_struct_list(reader, bDeformGroup, &grease_pencil->vertex_group_names);
332
333 grease_pencil->runtime = MEM_new<blender::bke::GreasePencilRuntime>(__func__);
334}
335
337 /*id_code*/ GreasePencil::id_type,
338 /*id_filter*/ FILTER_ID_GP,
339 /*dependencies_id_types*/ FILTER_ID_GP | FILTER_ID_MA | FILTER_ID_OB,
340 /*main_listbase_index*/ INDEX_ID_GP,
341 /*struct_size*/ sizeof(GreasePencil),
342 /*name*/ "GreasePencil",
343 /*name_plural*/ N_("grease_pencils_v3"),
344 /*translation_context*/ BLT_I18NCONTEXT_ID_GPENCIL,
346 /*asset_type_info*/ nullptr,
347
348 /*init_data*/ grease_pencil_init_data,
349 /*copy_data*/ grease_pencil_copy_data,
350 /*free_data*/ grease_pencil_free_data,
351 /*make_local*/ nullptr,
352 /*foreach_id*/ grease_pencil_foreach_id,
353 /*foreach_cache*/ nullptr,
354 /*foreach_path*/ nullptr,
355 /*owner_pointer_get*/ nullptr,
356
357 /*blend_write*/ grease_pencil_blend_write,
358 /*blend_read_data*/ grease_pencil_blend_read_data,
359 /*blend_read_after_liblink*/ nullptr,
360
361 /*blend_read_undo_preserve*/ nullptr,
362
363 /*lib_override_apply_post*/ nullptr,
364};
365
367
368constexpr StringRef ATTR_RADIUS = "radius";
369constexpr StringRef ATTR_OPACITY = "opacity";
370constexpr StringRef ATTR_VERTEX_COLOR = "vertex_color";
371constexpr StringRef ATTR_FILL_COLOR = "fill_color";
372
373/* Curves attributes getters */
374static int domain_num(const CurvesGeometry &curves, const AttrDomain domain)
375{
376 return domain == AttrDomain::Point ? curves.points_num() : curves.curves_num();
377}
379{
380 return domain == AttrDomain::Point ? curves.point_data : curves.curve_data;
381}
382template<typename T>
384 const AttrDomain domain,
385 const StringRef name,
386 const T default_value = T())
387{
388 const int num = domain_num(curves, domain);
389 if (num <= 0) {
390 return {};
391 }
393 CustomData &custom_data = domain_custom_data(curves, domain);
394
395 T *data = (T *)CustomData_get_layer_named_for_write(&custom_data, type, name, num);
396 if (data != nullptr) {
397 return {data, num};
398 }
399 data = (T *)CustomData_add_layer_named(&custom_data, type, CD_SET_DEFAULT, num, name);
400 MutableSpan<T> span = {data, num};
401 if (span.first() != default_value) {
402 span.fill(default_value);
403 }
404 return span;
405}
406
408{
409 this->base.type = GP_DRAWING;
410 this->base.flag = 0;
411
412 new (&this->geometry) bke::CurvesGeometry();
413 /* Initialize runtime data. */
414 this->runtime = MEM_new<bke::greasepencil::DrawingRuntime>(__func__);
415}
416
418{
419 this->base.type = GP_DRAWING;
420 this->base.flag = other.base.flag;
421
422 new (&this->geometry) bke::CurvesGeometry(other.strokes());
423 /* Initialize runtime data. */
424 this->runtime = MEM_new<bke::greasepencil::DrawingRuntime>(__func__);
425
426 this->runtime->triangle_offsets_cache = other.runtime->triangle_offsets_cache;
427 this->runtime->triangles_cache = other.runtime->triangles_cache;
428 this->runtime->curve_plane_normals_cache = other.runtime->curve_plane_normals_cache;
429 this->runtime->curve_texture_matrices = other.runtime->curve_texture_matrices;
430}
431
433{
434 this->base.type = GP_DRAWING;
435 other.base.type = GP_DRAWING;
436 this->base.flag = other.base.flag;
437 other.base.flag = 0;
438
439 new (&this->geometry) bke::CurvesGeometry(std::move(other.geometry.wrap()));
440
441 this->runtime = other.runtime;
442 other.runtime = nullptr;
443}
444
446{
447 if (this == &other) {
448 return *this;
449 }
450 std::destroy_at(this);
451 new (this) Drawing(other);
452 return *this;
453}
454
456{
457 if (this == &other) {
458 return *this;
459 }
460 std::destroy_at(this);
461 new (this) Drawing(std::move(other));
462 return *this;
463}
464
466{
467 this->strokes().~CurvesGeometry();
468 MEM_delete(this->runtime);
469 this->runtime = nullptr;
470}
471
472OffsetIndices<int> Drawing::triangle_offsets() const
473{
474 this->runtime->triangle_offsets_cache.ensure([&](Vector<int> &r_offsets) {
475 const CurvesGeometry &curves = this->strokes();
476 const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve();
477
478 int offset = 0;
479 r_offsets.reinitialize(curves.curves_num() + 1);
480 for (const int curve_i : points_by_curve.index_range()) {
481 const IndexRange points = points_by_curve[curve_i];
482 r_offsets[curve_i] = offset;
483 offset += std::max(int(points.size() - 2), 0);
484 }
485 r_offsets.last() = offset;
486 });
487 return this->runtime->triangle_offsets_cache.data().as_span();
488}
489
490static void update_triangle_cache(const Span<float3> positions,
491 const Span<float3> normals,
492 const OffsetIndices<int> points_by_curve,
493 const OffsetIndices<int> triangle_offsets,
494 const IndexMask &curve_mask,
495 MutableSpan<int3> triangles)
496{
497 struct LocalMemArena {
498 MemArena *pf_arena = nullptr;
499 LocalMemArena() : pf_arena(BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "Drawing::triangles")) {}
500
501 ~LocalMemArena()
502 {
503 if (pf_arena != nullptr) {
504 BLI_memarena_free(pf_arena);
505 }
506 }
507 };
509 curve_mask.foreach_segment(GrainSize(32), [&](const IndexMaskSegment mask_segment) {
510 MemArena *pf_arena = all_local_mem_arenas.local().pf_arena;
511 for (const int curve_i : mask_segment) {
512 const IndexRange points = points_by_curve[curve_i];
513 if (points.size() < 3) {
514 continue;
515 }
516 MutableSpan<int3> r_tris = triangles.slice(triangle_offsets[curve_i]);
517
518 float(*projverts)[2] = static_cast<float(*)[2]>(
519 BLI_memarena_alloc(pf_arena, sizeof(*projverts) * size_t(points.size())));
520
521 float3x3 axis_mat;
522 axis_dominant_v3_to_m3(axis_mat.ptr(), normals[curve_i]);
523
524 for (const int i : IndexRange(points.size())) {
525 mul_v2_m3v3(projverts[i], axis_mat.ptr(), positions[points[i]]);
526 }
527
529 projverts, points.size(), 0, reinterpret_cast<uint32_t(*)[3]>(r_tris.data()), pf_arena);
530 BLI_memarena_clear(pf_arena);
531 }
532 });
533}
534
536{
537 const CurvesGeometry &curves = this->strokes();
538 const OffsetIndices<int> triangle_offsets = this->triangle_offsets();
539 this->runtime->triangles_cache.ensure([&](Vector<int3> &r_data) {
540 const int total_triangles = triangle_offsets.total_size();
541 r_data.resize(total_triangles);
542
543 update_triangle_cache(curves.evaluated_positions(),
544 this->curve_plane_normals(),
545 curves.evaluated_points_by_curve(),
546 triangle_offsets,
547 curves.curves_range(),
548 r_data.as_mutable_span());
549 });
550
551 return this->runtime->triangles_cache.data().as_span();
552}
553
555 const OffsetIndices<int> points_by_curve,
556 const IndexMask &curve_mask,
558{
559 curve_mask.foreach_index(GrainSize(512), [&](const int curve_i) {
560 const IndexRange points = points_by_curve[curve_i];
561 if (points.size() < 2) {
562 normals[curve_i] = float3(1.0f, 0.0f, 0.0f);
563 return;
564 }
565
566 /* Calculate normal using Newell's method. */
567 float3 normal(0.0f);
568 float3 prev_point = positions[points.last()];
569 for (const int point_i : points) {
570 const float3 curr_point = positions[point_i];
571 add_newell_cross_v3_v3v3(normal, prev_point, curr_point);
572 prev_point = curr_point;
573 }
574
575 float length;
576 normal = math::normalize_and_get_length(normal, length);
577 /* Check for degenerate case where the points are on a line. */
578 if (math::is_zero(length)) {
579 for (const int point_i : points.drop_back(1)) {
580 float3 segment_vec = positions[point_i] - positions[point_i + 1];
581 if (math::length_squared(segment_vec) != 0.0f) {
582 normal = math::normalize(float3(segment_vec.y, -segment_vec.x, 0.0f));
583 break;
584 }
585 }
586 }
587
588 normals[curve_i] = normal;
589 });
590}
591
593{
594 this->runtime->curve_plane_normals_cache.ensure([&](Vector<float3> &r_data) {
595 const CurvesGeometry &curves = this->strokes();
596 r_data.reinitialize(curves.curves_num());
598 curves.points_by_curve(),
599 curves.curves_range(),
600 r_data.as_mutable_span());
601 });
602 return this->runtime->curve_plane_normals_cache.data().as_span();
603}
604
605/*
606 * Returns the matrix that transforms from a 3D point in layer-space to a 2D point in
607 * stroke-space for the stroke `curve_i`
608 */
609static float4x2 get_local_to_stroke_matrix(const Span<float3> positions, const float3 normal)
610{
611 using namespace blender::math;
612
613 if (positions.size() <= 2) {
614 return float4x2::identity();
615 }
616
617 const float3 point_0 = positions[0];
618 const float3 point_1 = positions[1];
619
620 /* Local X axis (p0 -> p1) */
621 const float3 local_x = normalize(point_1 - point_0);
622 /* Local Y axis (cross to normal/x axis). */
623 const float3 local_y = normalize(cross(normal, local_x));
624
625 if (length_squared(local_x) == 0.0f || length_squared(local_y) == 0.0f) {
626 return float4x2::identity();
627 }
628
629 /* Get local space using first point as origin. */
630 const float4x2 mat = transpose(
631 float2x4(float4(local_x, -dot(point_0, local_x)), float4(local_y, -dot(point_0, local_y))));
632
633 return mat;
634}
635
636/*
637 * Returns the matrix that transforms from a 2D point in stroke-space to a 2D point in
638 * texture-space for a stroke `curve_i`
639 */
640static float3x2 get_stroke_to_texture_matrix(const float uv_rotation,
641 const float2 uv_translation,
642 const float2 uv_scale)
643{
644 using namespace blender::math;
645
646 const float2 uv_scale_inv = safe_rcp(uv_scale);
647 const float s = sin(uv_rotation);
648 const float c = cos(uv_rotation);
649 const float2x2 rot = float2x2(float2(c, s), float2(-s, c));
650
651 float3x2 texture_matrix = float3x2::identity();
652 /*
653 * The order in which the three transforms are applied has been carefully chosen to be easy to
654 * invert.
655 *
656 * The translation is applied last so that the origin goes to `uv_translation`
657 * The rotation is applied after the scale so that the `u` direction's angle is `uv_rotation`
658 * Scale is the only transform that changes the length of the basis vectors and if it is applied
659 * first it's independent of the other transforms.
660 *
661 * These properties are not true with a different order.
662 */
663
664 /* Apply scale. */
665 texture_matrix = from_scale<float2x2>(uv_scale_inv) * texture_matrix;
666
667 /* Apply rotation. */
668 texture_matrix = rot * texture_matrix;
669
670 /* Apply translation. */
671 texture_matrix[2] += uv_translation;
672
673 return texture_matrix;
674}
675
677{
678 float4x3 strokemat4x3 = float4x3(strokemat);
679
680 /*
681 * We need the diagonal of ones to start from the bottom right instead top left to properly
682 * apply the two matrices.
683 *
684 * i.e.
685 * # # # # # # # #
686 * We need # # # # Instead of # # # #
687 * 0 0 0 1 0 0 1 0
688 *
689 */
690 strokemat4x3[2][2] = 0.0f;
691 strokemat4x3[3][2] = 1.0f;
692
693 return strokemat4x3;
694}
695
697{
698 this->runtime->curve_texture_matrices.ensure([&](Vector<float4x2> &r_data) {
699 const CurvesGeometry &curves = this->strokes();
700 const AttributeAccessor attributes = curves.attributes();
701
702 const VArray<float> uv_rotations = *attributes.lookup_or_default<float>(
703 "uv_rotation", AttrDomain::Curve, 0.0f);
704 const VArray<float2> uv_translations = *attributes.lookup_or_default<float2>(
705 "uv_translation", AttrDomain::Curve, float2(0.0f, 0.0f));
706 const VArray<float2> uv_scales = *attributes.lookup_or_default<float2>(
707 "uv_scale", AttrDomain::Curve, float2(1.0f, 1.0f));
708
709 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
710 const Span<float3> positions = curves.positions();
712
713 r_data.reinitialize(curves.curves_num());
714 threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) {
715 for (const int curve_i : range) {
716 const IndexRange points = points_by_curve[curve_i];
717 const float3 normal = normals[curve_i];
718 const float4x2 strokemat = get_local_to_stroke_matrix(positions.slice(points), normal);
719 const float3x2 texture_matrix = get_stroke_to_texture_matrix(
720 uv_rotations[curve_i], uv_translations[curve_i], uv_scales[curve_i]);
721
722 const float4x2 texspace = texture_matrix * expand_4x2_mat(strokemat);
723
724 r_data[curve_i] = texspace;
725 }
726 });
727 });
728 return this->runtime->curve_texture_matrices.data().as_span();
729}
730
732{
734 MutableAttributeAccessor attributes = curves.attributes_for_write();
735 SpanAttributeWriter<float> uv_rotations = attributes.lookup_or_add_for_write_span<float>(
736 "uv_rotation", AttrDomain::Curve);
738 "uv_translation", AttrDomain::Curve);
740 "uv_scale",
742 AttributeInitVArray(VArray<float2>::ForSingle(float2(1.0f, 1.0f), curves.curves_num())));
743
744 if (!uv_rotations || !uv_translations || !uv_scales) {
745 /* FIXME: It might be better to ensure the attributes exist and are on the right domain. */
746 return;
747 }
748
749 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
750 const Span<float3> positions = curves.positions();
752
753 selection.foreach_index(GrainSize(256), [&](const int64_t curve_i, const int64_t pos) {
754 const IndexRange points = points_by_curve[curve_i];
755 const float3 normal = normals[curve_i];
756 const float4x2 strokemat = get_local_to_stroke_matrix(positions.slice(points), normal);
757 const float4x2 texspace = matrices[pos];
758
759 /* We do the computation using doubles to avoid numerical precision errors. */
760 const double4x3 strokemat4x3 = double4x3(expand_4x2_mat(strokemat));
761
762 /*
763 * We want to solve for `texture_matrix` in the equation: `texspace = texture_matrix *
764 * strokemat4x3` Because these matrices are not square we can not use a standard inverse.
765 *
766 * Our problem has the form of: `X = A * Y`
767 * We can solve for `A` using: `A = X * B`
768 *
769 * Where `B` is the Right-sided inverse or Moore-Penrose pseudo inverse.
770 * Calculated as:
771 *
772 * |--------------------------|
773 * | B = T(Y) * (Y * T(Y))^-1 |
774 * |--------------------------|
775 *
776 * And `T()` is transpose and `()^-1` is the inverse.
777 */
778
779 const double3x4 transpose_strokemat = math::transpose(strokemat4x3);
780 const double3x4 right_inverse = transpose_strokemat *
781 math::invert(strokemat4x3 * transpose_strokemat);
782
783 const float3x2 texture_matrix = float3x2(double4x2(texspace) * right_inverse);
784
785 /* Solve for translation, the translation is simply the origin. */
786 const float2 uv_translation = texture_matrix[2];
787
788 /* Solve rotation, the angle of the `u` basis is the rotation. */
789 const float uv_rotation = math::atan2(texture_matrix[0][1], texture_matrix[0][0]);
790
791 /* Calculate the determinant to check if the `v` scale is negative. */
792 const float det = math::determinant(float2x2(texture_matrix));
793
794 /* Solve scale, scaling is the only transformation that changes the length, so scale factor
795 * is simply the length. And flip the sign of `v` if the determinant is negative. */
796 const float2 uv_scale = math::safe_rcp(float2(
797 math::length(texture_matrix[0]), math::sign(det) * math::length(texture_matrix[1])));
798
799 uv_rotations.span[curve_i] = uv_rotation;
800 uv_translations.span[curve_i] = uv_translation;
801 uv_scales.span[curve_i] = uv_scale;
802 });
803 uv_rotations.finish();
804 uv_translations.finish();
805 uv_scales.finish();
806
808}
809
811{
812 return this->geometry.wrap();
813}
814
816{
817 return this->geometry.wrap();
818}
819
821{
822 return *this->strokes().attributes().lookup_or_default<float>(
824}
825
831
833{
834 return *this->strokes().attributes().lookup_or_default<float>(
836}
837
843
845{
846 return *this->strokes().attributes().lookup_or_default<ColorGeometry4f>(
847 ATTR_VERTEX_COLOR, AttrDomain::Point, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
848}
849
857
859{
860 return *this->strokes().attributes().lookup_or_default<ColorGeometry4f>(
861 ATTR_FILL_COLOR, AttrDomain::Curve, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
862}
863
871
873{
874 this->runtime->curve_texture_matrices.tag_dirty();
875}
876
878{
879 this->strokes_for_write().tag_positions_changed();
880 this->runtime->curve_plane_normals_cache.tag_dirty();
881 this->runtime->triangles_cache.tag_dirty();
883}
884
885void Drawing::tag_positions_changed(const IndexMask &changed_curves)
886{
887 if (changed_curves.is_empty()) {
888 return;
889 }
890 /* If more than half of the curves have changed, update the entire cache instead.
891 * The assumption here is that it's better to lazily compute the caches if more than half of the
892 * curves need to be updated.
893 * TODO: This could probably be a bit more rigorous once this function gets used in more places.
894 */
895 if (changed_curves.size() > this->strokes().curves_num() / 2) {
896 this->tag_positions_changed();
897 return;
898 }
899 if (!this->runtime->triangles_cache.is_cached() ||
900 !this->runtime->curve_plane_normals_cache.is_cached())
901 {
902 this->tag_positions_changed();
903 return;
904 }
905 /* Positions needs to be tagged first, because the triangle cache updates just after need the
906 * positions to be up-to-date. */
907 this->strokes_for_write().tag_positions_changed();
908 this->runtime->curve_plane_normals_cache.update([&](Vector<float3> &normals) {
909 const CurvesGeometry &curves = this->strokes();
911 curves.positions(), curves.points_by_curve(), changed_curves, normals);
912 });
913 this->runtime->triangles_cache.update([&](Vector<int3> &triangles) {
914 const CurvesGeometry &curves = this->strokes();
915 update_triangle_cache(curves.evaluated_positions(),
916 this->curve_plane_normals(),
917 curves.evaluated_points_by_curve(),
918 this->triangle_offsets(),
919 curves.curves_range(),
920 triangles);
921 });
923}
924
926{
927 this->runtime->triangle_offsets_cache.tag_dirty();
928 this->tag_positions_changed();
929 this->strokes_for_write().tag_topology_changed();
930}
931
932void Drawing::tag_topology_changed(const IndexMask &changed_curves)
933{
934 if (changed_curves.is_empty()) {
935 return;
936 }
937 /* If more than half of the curves have changed, update the entire cache instead.
938 * The assumption here is that it's better to lazily compute the caches if more than half of the
939 * curves need to be updated.
940 * TODO: This could probably be a bit more rigorous once this function gets used in more places.
941 */
942 if (changed_curves.size() > this->strokes().curves_num() / 2) {
943 this->tag_topology_changed();
944 return;
945 }
946 if (!this->runtime->triangles_cache.is_cached() ||
947 !this->runtime->curve_plane_normals_cache.is_cached())
948 {
949 this->tag_topology_changed();
950 return;
951 }
952 /* Positions needs to be tagged first, because the triangle cache updates just after need the
953 * positions to be up-to-date. */
954 this->strokes_for_write().tag_positions_changed();
955 this->runtime->curve_plane_normals_cache.update([&](Vector<float3> &normals) {
956 const CurvesGeometry &curves = this->strokes();
958 curves.positions(), curves.points_by_curve(), changed_curves, normals);
959 });
960 /* Copy the current triangle offsets. These are used to copy over the triangle data for curves
961 * that don't need to be updated. */
962 const Array<int> src_triangle_offset_data(this->triangle_offsets().data());
963 const OffsetIndices<int> src_triangle_offsets = src_triangle_offset_data.as_span();
964 /* Tag the `triangle_offsets_cache` so that the `triangles_cache` update can use the up-to-date
965 * triangle offsets. */
966 this->runtime->triangle_offsets_cache.tag_dirty();
967
968 this->runtime->triangles_cache.update([&](Vector<int3> &triangles) {
969 const CurvesGeometry &curves = this->strokes();
970 const OffsetIndices<int> dst_triangle_offsets = this->triangle_offsets();
971
972 IndexMaskMemory memory;
973 const IndexMask curves_to_copy = changed_curves.complement(curves.curves_range(), memory);
974
975 const Vector<int3> src_triangles(triangles);
976 triangles.reinitialize(dst_triangle_offsets.total_size());
977 array_utils::copy_group_to_group(src_triangle_offsets,
978 dst_triangle_offsets,
979 curves_to_copy,
980 src_triangles.as_span(),
981 triangles.as_mutable_span());
982
983 update_triangle_cache(curves.evaluated_positions(),
984 this->curve_plane_normals(),
985 curves.evaluated_points_by_curve(),
986 dst_triangle_offsets,
987 changed_curves,
988 triangles);
989 });
991}
992
994{
995 this->base.type = GP_DRAWING_REFERENCE;
996 this->base.flag = 0;
997
998 this->id_reference = nullptr;
999}
1000
1002{
1003 this->base.type = GP_DRAWING_REFERENCE;
1004 this->base.flag = other.base.flag;
1005
1006 this->id_reference = other.id_reference;
1007}
1008
1010
1013{
1014 BLI_assert(src_drawings.size() == dst_drawings.size());
1015 for (const int i : src_drawings.index_range()) {
1016 const GreasePencilDrawingBase *src_drawing_base = src_drawings[i];
1017 switch (src_drawing_base->type) {
1018 case GP_DRAWING: {
1019 const GreasePencilDrawing *src_drawing = reinterpret_cast<const GreasePencilDrawing *>(
1020 src_drawing_base);
1021 dst_drawings[i] = reinterpret_cast<GreasePencilDrawingBase *>(
1022 MEM_new<bke::greasepencil::Drawing>(__func__, src_drawing->wrap()));
1023 break;
1024 }
1025 case GP_DRAWING_REFERENCE: {
1026 const GreasePencilDrawingReference *src_drawing_reference =
1027 reinterpret_cast<const GreasePencilDrawingReference *>(src_drawing_base);
1028 dst_drawings[i] = reinterpret_cast<GreasePencilDrawingBase *>(
1029 MEM_new<bke::greasepencil::DrawingReference>(__func__, src_drawing_reference->wrap()));
1030 break;
1031 }
1032 }
1033 }
1034}
1035
1037{
1038 this->next = this->prev = nullptr;
1039 this->parent = nullptr;
1040
1041 this->GreasePencilLayerTreeNode::name = nullptr;
1042 this->flag = 0;
1043 this->color[0] = this->color[1] = this->color[2] = 0;
1044}
1045
1047{
1048 this->type = type;
1049}
1050
1052{
1053 this->type = type;
1054 this->GreasePencilLayerTreeNode::name = BLI_strdupn(name.data(), name.size());
1055}
1056
1058{
1059 this->GreasePencilLayerTreeNode::name = BLI_strdup_null(other.GreasePencilLayerTreeNode::name);
1060 this->flag = other.flag;
1061 copy_v3_v3(this->color, other.color);
1062}
1063
1068
1069void TreeNode::set_name(const StringRef name)
1070{
1072 this->GreasePencilLayerTreeNode::name = BLI_strdupn(name.data(), name.size());
1073}
1074
1076{
1077 return *reinterpret_cast<const LayerGroup *>(this);
1078}
1079
1081{
1082 return *reinterpret_cast<const Layer *>(this);
1083}
1084
1086{
1087 return *reinterpret_cast<LayerGroup *>(this);
1088}
1089
1091{
1092 return *reinterpret_cast<Layer *>(this);
1093}
1094
1096{
1097 return (this->parent) ? &this->parent->wrap() : nullptr;
1098}
1100{
1101 return (this->parent) ? &this->parent->wrap() : nullptr;
1102}
1104{
1105 return this->parent_group() ? &this->parent->wrap().as_node() : nullptr;
1106}
1108{
1109 return this->parent_group() ? &this->parent->wrap().as_node() : nullptr;
1110}
1111
1113{
1114 const LayerGroup *parent = this->parent_group();
1115 if (parent == nullptr) {
1116 /* The root group has a depth of 0. */
1117 return 0;
1118 }
1119 return 1 + parent->as_node().depth();
1120}
1121
1123{
1124 this->layer_name = nullptr;
1125 this->flag = 0;
1126}
1127
1129{
1130 this->layer_name = BLI_strdupn(name.data(), name.size());
1131}
1132
1134{
1135 this->layer_name = BLI_strdup_null(other.layer_name);
1136 this->flag = other.flag;
1137}
1138
1140{
1141 if (this->layer_name) {
1142 MEM_freeN(this->layer_name);
1143 }
1144}
1145
1147{
1148 frames_.clear();
1149 sorted_keys_cache_.tag_dirty();
1150 masks_.clear_and_shrink();
1151 trans_data_ = {};
1152}
1153
1155{
1156 new (&this->base) TreeNode(GP_LAYER_TREE_LEAF);
1157
1158 this->frames_storage.num = 0;
1159 this->frames_storage.keys = nullptr;
1160 this->frames_storage.values = nullptr;
1161 this->frames_storage.flag = 0;
1162
1164 this->opacity = 1.0f;
1165
1166 this->parent = nullptr;
1167 this->parsubstr = nullptr;
1168 unit_m4(this->parentinv);
1169
1170 zero_v3(this->translation);
1171 zero_v3(this->rotation);
1172 copy_v3_fl(this->scale, 1.0f);
1173
1174 this->viewlayername = nullptr;
1175
1176 BLI_listbase_clear(&this->masks);
1177 this->active_mask_index = 0;
1178
1179 this->runtime = MEM_new<LayerRuntime>(__func__);
1180}
1181
1183{
1184 new (&this->base) TreeNode(GP_LAYER_TREE_LEAF, name);
1185}
1186
1187Layer::Layer(const Layer &other) : Layer()
1188{
1189 new (&this->base) TreeNode(other.base.wrap());
1190
1191 LISTBASE_FOREACH (GreasePencilLayerMask *, other_mask, &other.masks) {
1192 LayerMask *new_mask = MEM_new<LayerMask>(__func__, *reinterpret_cast<LayerMask *>(other_mask));
1193 BLI_addtail(&this->masks, reinterpret_cast<GreasePencilLayerMask *>(new_mask));
1194 }
1196
1197 this->blend_mode = other.blend_mode;
1198 this->opacity = other.opacity;
1199
1200 this->parent = other.parent;
1201 this->set_parent_bone_name(other.parsubstr);
1202 copy_m4_m4(this->parentinv, other.parentinv);
1203
1204 copy_v3_v3(this->translation, other.translation);
1205 copy_v3_v3(this->rotation, other.rotation);
1206 copy_v3_v3(this->scale, other.scale);
1207
1209
1210 /* NOTE: We do not duplicate the frame storage since it is only needed for writing to file. */
1211 this->runtime->frames_ = other.runtime->frames_;
1212 this->runtime->sorted_keys_cache_ = other.runtime->sorted_keys_cache_;
1213 /* Tag the frames map, so the frame storage is recreated once the DNA is saved. */
1214 this->tag_frames_map_changed();
1215
1216 /* TODO: what about masks cache? */
1217}
1218
1220{
1221 this->base.wrap().~TreeNode();
1222
1223 MEM_SAFE_FREE(this->frames_storage.keys);
1224 MEM_SAFE_FREE(this->frames_storage.values);
1225
1227 MEM_delete(reinterpret_cast<LayerMask *>(mask));
1228 }
1229 BLI_listbase_clear(&this->masks);
1230
1231 MEM_SAFE_FREE(this->parsubstr);
1233
1234 MEM_delete(this->runtime);
1235 this->runtime = nullptr;
1236}
1237
1239{
1240 return this->runtime->frames_;
1241}
1242
1244{
1245 return this->runtime->frames_;
1246}
1247
1248Layer::SortedKeysIterator Layer::remove_leading_end_frames_in_range(
1250{
1252 while (next_it != end && this->frames().lookup(*next_it).is_end()) {
1253 this->frames_for_write().remove_contained(*next_it);
1255 next_it = std::next(next_it);
1256 }
1257 return next_it;
1258}
1259
1260GreasePencilFrame *Layer::add_frame_internal(const FramesMapKeyT frame_number)
1261{
1262 if (!this->frames().contains(frame_number)) {
1263 GreasePencilFrame frame{};
1264 this->frames_for_write().add_new(frame_number, frame);
1266 return this->frames_for_write().lookup_ptr(frame_number);
1267 }
1268 /* Overwrite end-frames. */
1269 if (this->frames().lookup(frame_number).is_end()) {
1270 GreasePencilFrame frame{};
1271 this->frames_for_write().add_overwrite(frame_number, frame);
1272 this->tag_frames_map_changed();
1273 return this->frames_for_write().lookup_ptr(frame_number);
1274 }
1275 return nullptr;
1276}
1277
1278GreasePencilFrame *Layer::add_frame(const FramesMapKeyT key, const int duration)
1279{
1280 BLI_assert(duration >= 0);
1281 GreasePencilFrame *frame = this->add_frame_internal(key);
1282 if (frame == nullptr) {
1283 return nullptr;
1284 }
1286 const FramesMapKeyT end_key = key + duration;
1287 /* Finds the next greater key that is stored in the map. */
1288 SortedKeysIterator next_key_it = std::upper_bound(sorted_keys.begin(), sorted_keys.end(), key);
1289 /* If the next frame we found is at the end of the frame we're inserting, then we are done. */
1290 if (next_key_it != sorted_keys.end() && *next_key_it == end_key) {
1291 return frame;
1292 }
1293 next_key_it = this->remove_leading_end_frames_in_range(next_key_it, sorted_keys.end());
1294 /* If the duration is set to 0, the frame is marked as an implicit hold. */
1295 if (duration == 0) {
1296 frame->flag |= GP_FRAME_IMPLICIT_HOLD;
1297 return frame;
1298 }
1299 /* If the next frame comes after the end of the frame we're inserting (or if there are no more
1300 * frames), add an end-frame. */
1301 if (next_key_it == sorted_keys.end() || *next_key_it > end_key) {
1302 this->frames_for_write().add_new(end_key, GreasePencilFrame::end());
1304 }
1305 return frame;
1306}
1307
1309{
1310 /* If the frame number is not in the frames map, do nothing. */
1311 if (!this->frames().contains(key)) {
1312 return false;
1313 }
1314 if (this->frames().size() == 1) {
1317 return true;
1318 }
1320 /* Find the index of the frame to remove in the `sorted_keys` array. */
1321 SortedKeysIterator remove_key_it = std::lower_bound(sorted_keys.begin(), sorted_keys.end(), key);
1322 /* If there is a next frame: */
1323 if (std::next(remove_key_it) != sorted_keys.end()) {
1324 SortedKeysIterator next_key_it = std::next(remove_key_it);
1325 this->remove_leading_end_frames_in_range(next_key_it, sorted_keys.end());
1326 }
1327 /* If there is a previous frame: */
1328 if (remove_key_it != sorted_keys.begin()) {
1329 SortedKeysIterator prev_key_it = std::prev(remove_key_it);
1330 const GreasePencilFrame &prev_frame = this->frames().lookup(*prev_key_it);
1331 /* If the previous frame is not an implicit hold (e.g. it has a fixed duration) and it's not an
1332 * end frame, we cannot just delete the frame. We need to replace it with an end frame. */
1333 if (!prev_frame.is_implicit_hold() && !prev_frame.is_end()) {
1334 this->frames_for_write().lookup(key) = GreasePencilFrame::end();
1335 this->tag_frames_map_changed();
1336 /* Since the original frame was replaced with an end frame, we consider the frame to be
1337 * successfully removed here. */
1338 return true;
1339 }
1340 }
1341 /* Finally, remove the actual frame. */
1344 return true;
1345}
1346
1348{
1349 this->runtime->sorted_keys_cache_.ensure([&](Vector<FramesMapKeyT> &r_data) {
1350 r_data.reinitialize(this->frames().size());
1351 int i = 0;
1352 for (const FramesMapKeyT key : this->frames().keys()) {
1353 r_data[i++] = key;
1354 }
1355 std::sort(r_data.begin(), r_data.end());
1356 });
1357 return this->runtime->sorted_keys_cache_.data();
1358}
1359
1361{
1363 /* No keyframes, return nullptr. */
1364 if (sorted_keys.is_empty()) {
1365 return nullptr;
1366 }
1367 /* Before the first frame, return nullptr. */
1368 if (frame_number < sorted_keys.first()) {
1369 return nullptr;
1370 }
1371 /* After or at the last frame, return iterator to last. */
1372 if (frame_number >= sorted_keys.last()) {
1373 return std::prev(sorted_keys.end());
1374 }
1375 /* Search for the frame. std::upper_bound will get the frame just after. */
1376 SortedKeysIterator it = std::upper_bound(sorted_keys.begin(), sorted_keys.end(), frame_number);
1377 if (it == sorted_keys.end()) {
1378 return nullptr;
1379 }
1380 return std::prev(it);
1381}
1382
1383std::optional<FramesMapKeyT> Layer::frame_key_at(const int frame_number) const
1384{
1385 SortedKeysIterator it = this->sorted_keys_iterator_at(frame_number);
1386 if (it == nullptr) {
1387 return {};
1388 }
1389 return *it;
1390}
1391
1392std::optional<int> Layer::start_frame_at(int frame_number) const
1393{
1394 const std::optional<FramesMapKeyT> frame_key = this->frame_key_at(frame_number);
1395 /* Return the frame number only if the frame key exists and if it's not an end frame. */
1396 if (frame_key && !this->frames().lookup_ptr(*frame_key)->is_end()) {
1397 return frame_key;
1398 }
1399 return {};
1400}
1401
1402int Layer::sorted_keys_index_at(const int frame_number) const
1403{
1404 SortedKeysIterator it = this->sorted_keys_iterator_at(frame_number);
1405 if (it == nullptr) {
1406 return -1;
1407 }
1408 return std::distance(this->sorted_keys().begin(), it);
1409}
1410
1411const GreasePencilFrame *Layer::frame_at(const int frame_number) const
1412{
1413 const GreasePencilFrame *frame_ptr = [&]() -> const GreasePencilFrame * {
1414 if (const GreasePencilFrame *frame = this->frames().lookup_ptr(frame_number)) {
1415 return frame;
1416 }
1417 /* Look for a keyframe that starts before `frame_number` and ends after `frame_number`. */
1418 const std::optional<FramesMapKeyT> frame_key = this->frame_key_at(frame_number);
1419 if (!frame_key) {
1420 return nullptr;
1421 }
1422 return this->frames().lookup_ptr(*frame_key);
1423 }();
1424 if (frame_ptr == nullptr || frame_ptr->is_end()) {
1425 /* Not a valid frame. */
1426 return nullptr;
1427 }
1428 return frame_ptr;
1429}
1430
1431GreasePencilFrame *Layer::frame_at(const int frame_number)
1432{
1433 GreasePencilFrame *frame_ptr = [&]() -> GreasePencilFrame * {
1434 if (GreasePencilFrame *frame = this->frames_for_write().lookup_ptr(frame_number)) {
1435 return frame;
1436 }
1437 /* Look for a keyframe that starts before `frame_number`. */
1438 const std::optional<FramesMapKeyT> frame_key = this->frame_key_at(frame_number);
1439 if (!frame_key) {
1440 return nullptr;
1441 }
1442 return this->frames_for_write().lookup_ptr(*frame_key);
1443 }();
1444 if (frame_ptr == nullptr || frame_ptr->is_end()) {
1445 /* Not a valid frame. */
1446 return nullptr;
1447 }
1448 return frame_ptr;
1449}
1450
1451int Layer::drawing_index_at(const int frame_number) const
1452{
1453 const GreasePencilFrame *frame = frame_at(frame_number);
1454 return (frame != nullptr) ? frame->drawing_index : -1;
1455}
1456
1457bool Layer::has_drawing_at(const int frame_number) const
1458{
1459 return frame_at(frame_number) != nullptr;
1460}
1461
1462int Layer::get_frame_duration_at(const int frame_number) const
1463{
1464 SortedKeysIterator it = this->sorted_keys_iterator_at(frame_number);
1465 if (it == nullptr) {
1466 return -1;
1467 }
1468 const FramesMapKeyT key = *it;
1469 const GreasePencilFrame *frame = this->frames().lookup_ptr(key);
1470 /* For frames that are implicitly held, we return a duration of 0. */
1471 if (frame->is_implicit_hold()) {
1472 return 0;
1473 }
1474 /* Frame is an end frame, so there is no keyframe at `frame_number`. */
1475 if (frame->is_end()) {
1476 return -1;
1477 }
1478 /* Compute the distance in frames between this key and the next key. */
1479 const int next_frame_number = *(std::next(it));
1480 return math::distance(key, next_frame_number);
1481}
1482
1487
1489{
1490 this->tag_frames_map_changed();
1491 this->runtime->sorted_keys_cache_.tag_dirty();
1492}
1493
1495{
1496 /* Re-create the frames storage only if it was tagged dirty. */
1498 return;
1499 }
1500
1503
1504 const size_t frames_num = size_t(frames().size());
1505 frames_storage.num = int(frames_num);
1506 frames_storage.keys = MEM_calloc_arrayN<int>(frames_num, __func__);
1507 frames_storage.values = MEM_calloc_arrayN<GreasePencilFrame>(frames_num, __func__);
1508 const Span<int> sorted_keys_data = sorted_keys();
1509 for (const int64_t i : sorted_keys_data.index_range()) {
1510 frames_storage.keys[i] = sorted_keys_data[i];
1511 frames_storage.values[i] = frames().lookup(sorted_keys_data[i]);
1512 }
1513
1514 /* Reset the flag. */
1516}
1517
1519{
1520 /* Re-create frames data in runtime map. */
1521 /* NOTE: Avoid re-allocating runtime data to reduce 'false positive' change detection from
1522 * MEMFILE undo. */
1523 if (runtime) {
1524 runtime->clear();
1525 }
1526 else {
1527 runtime = MEM_new<blender::bke::greasepencil::LayerRuntime>(__func__);
1528 }
1530 for (int i = 0; i < frames_storage.num; i++) {
1531 frames.add_new(frames_storage.keys[i], frames_storage.values[i]);
1532 }
1533}
1534
1536{
1537 if (this->parent == nullptr) {
1538 return object.object_to_world() * this->local_transform();
1539 }
1540 const Object &parent = *this->parent;
1541 return this->parent_to_world(parent) * this->parent_inverse() * this->local_transform();
1542}
1543
1545{
1546 if (this->parent == nullptr) {
1547 return this->local_transform();
1548 }
1549 const Object &parent = *this->parent;
1550 return object.world_to_object() * this->parent_to_world(parent) * this->parent_inverse() *
1551 this->local_transform();
1552}
1553
1555{
1556 return (this->parsubstr != nullptr) ? StringRefNull(this->parsubstr) : StringRefNull();
1557}
1558
1560{
1561 if (this->parsubstr != nullptr) {
1562 MEM_freeN(this->parsubstr);
1563 this->parsubstr = nullptr;
1564 }
1565 if (!new_name.is_empty()) {
1566 this->parsubstr = BLI_strdupn(new_name.data(), new_name.size());
1567 }
1568}
1569
1570float4x4 Layer::parent_to_world(const Object &parent) const
1571{
1572 const float4x4 &parent_object_to_world = parent.object_to_world();
1573 if (parent.type == OB_ARMATURE && !this->parent_bone_name().is_empty()) {
1575 this->parent_bone_name().c_str()))
1576 {
1577 return parent_object_to_world * float4x4_view(channel->pose_mat);
1578 }
1579 }
1580 return parent_object_to_world;
1581}
1582
1584{
1585 return float4x4_view(this->parentinv);
1586}
1587
1593
1595{
1597 *reinterpret_cast<float3 *>(this->translation),
1598 *reinterpret_cast<math::EulerXYZ *>(this->rotation),
1599 *reinterpret_cast<float3 *>(this->scale));
1600}
1601
1603{
1604 return (this->viewlayername != nullptr) ? StringRefNull(this->viewlayername) : StringRefNull();
1605}
1606
1608{
1609 if (this->viewlayername != nullptr) {
1610 MEM_freeN(this->viewlayername);
1611 this->viewlayername = nullptr;
1612 }
1613 if (!new_name.is_empty()) {
1614 this->viewlayername = BLI_strdupn(new_name.data(), new_name.size());
1615 }
1616}
1617
1619{
1620 new (&this->base) TreeNode(GP_LAYER_TREE_GROUP);
1621
1624
1625 this->runtime = MEM_new<LayerGroupRuntime>(__func__);
1626}
1627
1629{
1630 new (&this->base) TreeNode(GP_LAYER_TREE_GROUP, name);
1631}
1632
1634{
1635 new (&this->base) TreeNode(other.base.wrap());
1636
1638 switch (child->type) {
1639 case GP_LAYER_TREE_LEAF: {
1640 GreasePencilLayer *layer = reinterpret_cast<GreasePencilLayer *>(child);
1641 Layer *dup_layer = MEM_new<Layer>(__func__, layer->wrap());
1642 this->add_node(dup_layer->as_node());
1643 break;
1644 }
1645 case GP_LAYER_TREE_GROUP: {
1646 GreasePencilLayerTreeGroup *group = reinterpret_cast<GreasePencilLayerTreeGroup *>(child);
1647 LayerGroup *dup_group = MEM_new<LayerGroup>(__func__, group->wrap());
1648 this->add_node(dup_group->as_node());
1649 break;
1650 }
1651 }
1652 }
1653
1654 this->color_tag = other.color_tag;
1655}
1656
1658{
1659 this->base.wrap().~TreeNode();
1660
1662 switch (child->type) {
1663 case GP_LAYER_TREE_LEAF: {
1664 GreasePencilLayer *layer = reinterpret_cast<GreasePencilLayer *>(child);
1665 MEM_delete(&layer->wrap());
1666 break;
1667 }
1668 case GP_LAYER_TREE_GROUP: {
1669 GreasePencilLayerTreeGroup *group = reinterpret_cast<GreasePencilLayerTreeGroup *>(child);
1670 MEM_delete(&group->wrap());
1671 break;
1672 }
1673 }
1674 }
1675
1676 MEM_delete(this->runtime);
1677 this->runtime = nullptr;
1678}
1679
1681{
1682 if (this == &other) {
1683 return *this;
1684 }
1685
1686 this->~LayerGroup();
1687 new (this) LayerGroup(other);
1688
1689 return *this;
1690}
1691
1693{
1694 return BLI_listbase_is_empty(&this->children);
1695}
1696
1698{
1699 BLI_addtail(&this->children, &node);
1700 node.parent = reinterpret_cast<GreasePencilLayerTreeGroup *>(this);
1701 this->tag_nodes_cache_dirty();
1702 return node;
1703}
1705{
1706 BLI_assert(BLI_findindex(&this->children, &link) != -1);
1707 BLI_insertlinkbefore(&this->children, &link, &node);
1708 node.parent = reinterpret_cast<GreasePencilLayerTreeGroup *>(this);
1709 this->tag_nodes_cache_dirty();
1710}
1712{
1713 BLI_assert(BLI_findindex(&this->children, &link) != -1);
1714 BLI_insertlinkafter(&this->children, &link, &node);
1715 node.parent = reinterpret_cast<GreasePencilLayerTreeGroup *>(this);
1716 this->tag_nodes_cache_dirty();
1717}
1718
1720{
1721 BLI_listbase_link_move(&this->children, &node, step);
1722 this->tag_nodes_cache_dirty();
1723}
1725{
1726 BLI_listbase_link_move(&this->children, &node, -step);
1727 this->tag_nodes_cache_dirty();
1728}
1730{
1731 BLI_remlink(&this->children, &node);
1732 BLI_insertlinkafter(&this->children, this->children.last, &node);
1733 this->tag_nodes_cache_dirty();
1734}
1736{
1737 BLI_remlink(&this->children, &node);
1738 BLI_insertlinkbefore(&this->children, this->children.first, &node);
1739 this->tag_nodes_cache_dirty();
1740}
1741
1743{
1744 return BLI_listbase_count(&this->children);
1745}
1746
1748{
1749 this->ensure_nodes_cache();
1750 return this->runtime->nodes_cache_.size();
1751}
1752
1753bool LayerGroup::unlink_node(TreeNode &link, const bool keep_children)
1754{
1755 if (link.is_group() && !link.as_group().is_empty() && keep_children) {
1756 if (BLI_findindex(&this->children, &link) == -1) {
1757 return false;
1758 }
1759
1760 /* Take ownership of the children of `link` by replacing the node with the listbase of its
1761 * children. */
1762 ListBase link_children = link.as_group().children;
1764 link_children.first);
1765 GreasePencilLayerTreeNode *last = static_cast<GreasePencilLayerTreeNode *>(link_children.last);
1766
1767 /* Rewrite the parent pointers. */
1768 LISTBASE_FOREACH (GreasePencilLayerTreeNode *, child, &link_children) {
1769 child->parent = this;
1770 }
1771
1772 /* Update previous and/or next link(s). */
1773 if (link.next != nullptr) {
1774 link.next->prev = last;
1775 last->next = link.next;
1776 }
1777 if (link.prev != nullptr) {
1778 link.prev->next = first;
1779 first->prev = link.prev;
1780 }
1781
1782 /* Update first and/or last link(s). */
1783 if (this->children.last == &link) {
1784 this->children.last = last;
1785 }
1786 if (this->children.first == &link) {
1787 this->children.first = first;
1788 }
1789
1790 /* Listbase has been inserted in `this->children` we can clear the pointers in `link`. */
1792 link.parent = nullptr;
1793
1794 this->tag_nodes_cache_dirty();
1795 return true;
1796 }
1797 if (BLI_remlink_safe(&this->children, &link)) {
1798 link.parent = nullptr;
1799 this->tag_nodes_cache_dirty();
1800 return true;
1801 }
1802 return false;
1803}
1804
1806{
1807 this->ensure_nodes_cache();
1808 return this->runtime->nodes_cache_.as_span();
1809}
1810
1812{
1813 this->ensure_nodes_cache();
1814 return this->runtime->nodes_cache_.as_span();
1815}
1816
1818{
1819 this->ensure_nodes_cache();
1820 return this->runtime->layer_cache_.as_span();
1821}
1822
1824{
1825 this->ensure_nodes_cache();
1826 return this->runtime->layer_cache_.as_span();
1827}
1828
1830{
1831 this->ensure_nodes_cache();
1832 return this->runtime->layer_group_cache_.as_span();
1833}
1834
1836{
1837 this->ensure_nodes_cache();
1838 return this->runtime->layer_group_cache_.as_span();
1839}
1840
1842{
1843 for (const TreeNode *node : this->nodes()) {
1844 if (node->name() == name) {
1845 return node;
1846 }
1847 }
1848 return nullptr;
1849}
1850
1852{
1853 for (TreeNode *node : this->nodes_for_write()) {
1854 if (node->name() == name) {
1855 return node;
1856 }
1857 }
1858 return nullptr;
1859}
1860
1862{
1863 return (this->base.flag & GP_LAYER_TREE_NODE_EXPANDED) != 0;
1864}
1865
1866void LayerGroup::set_expanded(const bool expanded)
1867{
1869}
1870
1871void LayerGroup::print_nodes(const StringRef header) const
1872{
1873 std::cout << header << std::endl;
1876 TreeNode *child = reinterpret_cast<TreeNode *>(child_);
1877 next_node.push(std::make_pair(1, child));
1878 }
1879 while (!next_node.is_empty()) {
1880 auto [indent, node] = next_node.pop();
1881 for (int i = 0; i < indent; i++) {
1882 std::cout << " ";
1883 }
1884 if (node->is_layer()) {
1885 std::cout << node->name();
1886 }
1887 else if (node->is_group()) {
1888 std::cout << node->name() << ": ";
1889 LISTBASE_FOREACH_BACKWARD (GreasePencilLayerTreeNode *, child_, &node->as_group().children) {
1890 TreeNode *child = reinterpret_cast<TreeNode *>(child_);
1891 next_node.push(std::make_pair(indent + 1, child));
1892 }
1893 }
1894 std::cout << std::endl;
1895 }
1896 std::cout << std::endl;
1897}
1898
1899void LayerGroup::ensure_nodes_cache() const
1900{
1901 this->runtime->nodes_cache_mutex_.ensure([&]() {
1902 this->runtime->nodes_cache_.clear_and_shrink();
1903 this->runtime->layer_cache_.clear_and_shrink();
1904 this->runtime->layer_group_cache_.clear_and_shrink();
1905
1907 TreeNode *node = reinterpret_cast<TreeNode *>(child_);
1908 this->runtime->nodes_cache_.append(node);
1909 switch (node->type) {
1910 case GP_LAYER_TREE_LEAF: {
1911 this->runtime->layer_cache_.append(&node->as_layer());
1912 break;
1913 }
1914 case GP_LAYER_TREE_GROUP: {
1915 this->runtime->layer_group_cache_.append(&node->as_group());
1916 for (TreeNode *child : node->as_group().nodes_for_write()) {
1917 this->runtime->nodes_cache_.append(child);
1918 if (child->is_layer()) {
1919 this->runtime->layer_cache_.append(&child->as_layer());
1920 }
1921 else if (child->is_group()) {
1922 this->runtime->layer_group_cache_.append(&child->as_group());
1923 }
1924 }
1925 break;
1926 }
1927 }
1928 }
1929 });
1930}
1931
1932void LayerGroup::tag_nodes_cache_dirty() const
1933{
1934 this->runtime->nodes_cache_mutex_.tag_dirty();
1935 if (this->base.parent) {
1936 this->base.parent->wrap().tag_nodes_cache_dirty();
1937 }
1938}
1939
1941{
1942 LISTBASE_FOREACH (TreeNode *, child, &children) {
1943 switch (child->type) {
1944 case GP_LAYER_TREE_LEAF: {
1945 child->as_layer().prepare_for_dna_write();
1946 break;
1947 }
1948 case GP_LAYER_TREE_GROUP: {
1949 child->as_group().prepare_for_dna_write();
1950 break;
1951 }
1952 }
1953 }
1954}
1955
1957{
1958 LISTBASE_FOREACH (TreeNode *, child, &children) {
1959 switch (child->type) {
1960 case GP_LAYER_TREE_LEAF: {
1961 child->as_layer().update_from_dna_read();
1962 break;
1963 }
1964 case GP_LAYER_TREE_GROUP: {
1965 child->as_group().update_from_dna_read();
1966 break;
1967 }
1968 }
1969 }
1970}
1971
1973{
1974 for (bke::greasepencil::Layer *layer : grease_pencil.layers_for_write()) {
1975 if (layer->name().is_empty()) {
1976 grease_pencil.rename_node(bmain, layer->as_node(), DATA_("Layer"));
1977 }
1978 }
1979}
1980
1981} // namespace blender::bke::greasepencil
1982
1983namespace blender::bke {
1984
1987
1988std::optional<Span<float3>> GreasePencilDrawingEditHints::positions() const
1989{
1990 if (!this->positions_data.has_value()) {
1991 return std::nullopt;
1992 }
1993 const int points_num = this->drawing_orig->geometry.wrap().points_num();
1994 return Span(static_cast<const float3 *>(this->positions_data.data), points_num);
1995}
1996
1997std::optional<MutableSpan<float3>> GreasePencilDrawingEditHints::positions_for_write()
1998{
1999 if (!this->positions_data.has_value()) {
2000 return std::nullopt;
2001 }
2002
2003 const int points_num = this->drawing_orig->geometry.wrap().points_num();
2005 if (data.sharing_info->is_mutable()) {
2006 /* If the referenced component is already mutable, return it directly. */
2007 data.sharing_info->tag_ensured_mutable();
2008 }
2009 else {
2010 auto *new_sharing_info = new ImplicitSharedValue<Array<float3>>(*this->positions());
2011 data.sharing_info = ImplicitSharingPtr<>(new_sharing_info);
2012 data.data = new_sharing_info->data.data();
2013 }
2014
2015 return MutableSpan(const_cast<float3 *>(static_cast<const float3 *>(data.data)), points_num);
2016}
2017
2018} // namespace blender::bke
2019
2020/* ------------------------------------------------------------------- */
2023
2025 const blender::StringRef name)
2026{
2027 return name == ATTR_POSITION;
2028}
2029
2030GreasePencil *BKE_grease_pencil_add(Main *bmain, const char *name)
2031{
2032 GreasePencil *grease_pencil = BKE_id_new<GreasePencil>(bmain, name);
2033
2034 return grease_pencil;
2035}
2036
2038{
2039 GreasePencil *grease_pencil = BKE_id_new_nomain<GreasePencil>(nullptr);
2040 return grease_pencil;
2041}
2042
2044{
2045 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(
2046 BKE_id_copy_ex(nullptr, &grease_pencil_src->id, nullptr, LIB_ID_COPY_LOCALIZE));
2047 grease_pencil->runtime->eval_frame = grease_pencil_src->runtime->eval_frame;
2048 return grease_pencil;
2049}
2050
2061
2064{
2065 using namespace blender::bke::greasepencil;
2066 dst.as_node().flag = src.as_node().flag;
2067 copy_v3_v3(dst.as_node().color, src.as_node().color);
2068
2069 dst.blend_mode = src.blend_mode;
2070 dst.opacity = src.opacity;
2071
2072 LISTBASE_FOREACH (GreasePencilLayerMask *, src_mask, &src.masks) {
2073 LayerMask *new_mask = MEM_new<LayerMask>(__func__, *reinterpret_cast<LayerMask *>(src_mask));
2074 BLI_addtail(&dst.masks, reinterpret_cast<GreasePencilLayerMask *>(new_mask));
2075 }
2077
2078 dst.parent = src.parent;
2080 copy_m4_m4(dst.parentinv, src.parentinv);
2081
2083 copy_v3_v3(dst.rotation, src.rotation);
2084 copy_v3_v3(dst.scale, src.scale);
2085
2087}
2088
2097
2099 GreasePencil *grease_pencil_dst)
2100{
2101 using namespace blender;
2104
2105 /* Drawings. */
2106 free_drawing_array(*grease_pencil_dst);
2107 grease_pencil_dst->resize_drawings(grease_pencil_src->drawing_array_num);
2108 for (const int i : IndexRange(grease_pencil_dst->drawing_array_num)) {
2109 switch (grease_pencil_src->drawing_array[i]->type) {
2110 case GP_DRAWING: {
2111 const Drawing &src_drawing =
2112 reinterpret_cast<GreasePencilDrawing *>(grease_pencil_src->drawing_array[i])->wrap();
2113 grease_pencil_dst->drawing_array[i] = &MEM_new<Drawing>(__func__, src_drawing)->base;
2114 break;
2115 }
2117 const DrawingReference &src_drawing_ref = reinterpret_cast<GreasePencilDrawingReference *>(
2118 grease_pencil_src->drawing_array[i])
2119 ->wrap();
2120 grease_pencil_dst->drawing_array[i] =
2121 &MEM_new<DrawingReference>(__func__, src_drawing_ref)->base;
2122 break;
2123 }
2124 }
2125
2126 /* Layers. */
2127 CustomData_free(&grease_pencil_dst->layers_data);
2128 if (grease_pencil_dst->root_group_ptr) {
2129 MEM_delete(&grease_pencil_dst->root_group());
2130 }
2131
2132 grease_pencil_dst->root_group_ptr = MEM_new<bke::greasepencil::LayerGroup>(
2133 __func__, grease_pencil_src->root_group_ptr->wrap());
2134 BLI_assert(grease_pencil_src->layers().size() == grease_pencil_dst->layers().size());
2135
2136 /* Reset the active node. */
2137 grease_pencil_dst->active_node = nullptr;
2138
2139 CustomData_init_from(&grease_pencil_src->layers_data,
2140 &grease_pencil_dst->layers_data,
2141 eCustomDataMask(CD_MASK_ALL),
2142 grease_pencil_src->layers().size());
2143
2144 DEG_id_tag_update(&grease_pencil_dst->id, ID_RECALC_GEOMETRY);
2145
2146 BKE_id_free(nullptr, grease_pencil_src);
2147}
2148
2149void BKE_grease_pencil_vgroup_name_update(Object *ob, const char *old_name, const char *new_name)
2150{
2151 using namespace blender::bke::greasepencil;
2152 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
2153 for (GreasePencilDrawingBase *base : grease_pencil.drawings()) {
2154 Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
2156 LISTBASE_FOREACH (bDeformGroup *, vgroup, &curves.vertex_group_names) {
2157 if (STREQ(vgroup->name, old_name)) {
2158 STRNCPY(vgroup->name, new_name);
2159 }
2160 }
2161 }
2162}
2163
2165 Scene *scene,
2166 Object *object,
2167 blender::bke::GeometrySet &geometry_set)
2168{
2169 /* Modifier evaluation modes. */
2170 const bool use_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER;
2171 ModifierMode required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
2172 if (BKE_object_is_in_editmode(object)) {
2173 required_mode |= eModifierMode_Editmode;
2174 }
2175 ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE;
2176 const ModifierEvalContext mectx = {depsgraph, object, apply_flag};
2177
2179
2180 /* Get effective list of modifiers to execute. Some effects like shape keys
2181 * are added as virtual modifiers before the user created modifiers. */
2182 VirtualModifierData virtualModifierData;
2183 ModifierData *md = BKE_modifiers_get_virtual_modifierlist(object, &virtualModifierData);
2184
2185 /* Evaluate time modifiers.
2186 * The time offset modifier can change what drawings are shown on the current frame. But it
2187 * doesn't affect the drawings data. Modifiers that modify the drawings data are only evaluated
2188 * for the current frame, so we run the time offset modifiers before all the other ones. */
2189 ModifierData *tmd = md;
2190 for (; tmd; tmd = tmd->next) {
2192
2193 if (!BKE_modifier_is_enabled(scene, tmd, required_mode) ||
2195 {
2196 continue;
2197 }
2198
2199 blender::bke::ScopedModifierTimer modifier_timer{*md};
2200
2201 if (mti->modify_geometry_set != nullptr) {
2202 mti->modify_geometry_set(tmd, &mectx, &geometry_set);
2203 }
2204 }
2205
2206 /* Evaluate drawing modifiers. */
2207 for (; md; md = md->next) {
2209
2210 if (!BKE_modifier_is_enabled(scene, md, required_mode) ||
2212 {
2213 continue;
2214 }
2215
2216 blender::bke::ScopedModifierTimer modifier_timer{*md};
2217
2218 if (mti->modify_geometry_set != nullptr) {
2219 mti->modify_geometry_set(md, &mectx, &geometry_set);
2220 }
2221 }
2222}
2223
2225{
2226 using namespace blender;
2227 using namespace bke::greasepencil;
2228
2229 const bke::AttributeAccessor layer_attributes = grease_pencil.attributes();
2230
2231 struct LayerDrawingInfo {
2232 Drawing *drawing;
2233 const int layer_index;
2234 };
2235
2236 Set<Drawing *> all_drawings;
2237 Vector<LayerDrawingInfo> drawing_infos;
2238 for (const int layer_i : grease_pencil.layers().index_range()) {
2239 const Layer &layer = grease_pencil.layer(layer_i);
2240 /* Set of owned drawings, ignore drawing references to other data blocks. */
2241 if (Drawing *drawing = grease_pencil.get_eval_drawing(layer)) {
2242 if (all_drawings.add(drawing)) {
2243 drawing_infos.append({drawing, layer_i});
2244 }
2245 }
2246 }
2247
2248 if (layer_attributes.contains("radius_offset")) {
2249 const VArray<float> radius_offsets = *layer_attributes.lookup_or_default<float>(
2250 "radius_offset", bke::AttrDomain::Layer, 0.0f);
2251 threading::parallel_for_each(drawing_infos, [&](LayerDrawingInfo &info) {
2252 if (radius_offsets[info.layer_index] == 0.0f) {
2253 return;
2254 }
2255 MutableSpan<float> radii = info.drawing->radii_for_write();
2256 threading::parallel_for(radii.index_range(), 4096, [&](const IndexRange range) {
2257 for (const int i : range) {
2258 radii[i] += radius_offsets[info.layer_index];
2259 }
2260 });
2261 });
2262 }
2263
2264 if (layer_attributes.contains("tint_color")) {
2265 auto mix_tint = [](const float4 base, const float4 tint) -> float4 {
2266 return base * (1.0 - tint.w) + tint * tint.w;
2267 };
2268 const VArray<ColorGeometry4f> tint_colors =
2269 *layer_attributes.lookup_or_default<ColorGeometry4f>(
2270 "tint_color", bke::AttrDomain::Layer, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
2271 threading::parallel_for_each(drawing_infos, [&](LayerDrawingInfo &info) {
2272 if (tint_colors[info.layer_index].a == 0.0f) {
2273 return;
2274 }
2275 MutableSpan<ColorGeometry4f> vertex_colors = info.drawing->vertex_colors_for_write();
2276 threading::parallel_for(vertex_colors.index_range(), 4096, [&](const IndexRange range) {
2277 for (const int i : range) {
2278 vertex_colors[i] = ColorGeometry4f(
2279 mix_tint(float4(vertex_colors[i]), float4(tint_colors[info.layer_index])));
2280 }
2281 });
2282 MutableSpan<ColorGeometry4f> fill_colors = info.drawing->fill_colors_for_write();
2283 threading::parallel_for(fill_colors.index_range(), 4096, [&](const IndexRange range) {
2284 for (const int i : range) {
2285 fill_colors[i] = ColorGeometry4f(
2286 mix_tint(float4(fill_colors[i]), float4(tint_colors[info.layer_index])));
2287 }
2288 });
2289 });
2290 }
2291}
2292
2294{
2295 using namespace blender;
2296 using namespace blender::bke::greasepencil;
2297
2298 /* Copy the layer cache into an array here, because removing a layer will invalidate the layer
2299 * cache. This will only copy the pointers to the layers, not the layers themselves. */
2300 Array<Layer *> layers = grease_pencil.layers_for_write();
2301
2302 for (const int layer_i : layers.index_range()) {
2303 Layer *layer = layers[layer_i];
2304 /* Store the original index of the layer. */
2305 layer->runtime->orig_layer_index_ = layer_i;
2306 /* When the visibility is animated, the layer should be retained even when it is invisible.
2307 * Changing the visibility through the animation system does NOT create another evaluated copy,
2308 * and thus the layer has to be kept for this future use. */
2309 if (layer->is_visible() || layer->runtime->is_visibility_animated_) {
2310 continue;
2311 }
2312
2313 /* Remove layer from evaluated data. */
2314 grease_pencil.remove_layer(*layer);
2315 }
2316}
2317
2319{
2320 using namespace blender;
2321 /* Store the frame that this grease pencil is evaluated on. */
2322 grease_pencil->runtime->eval_frame = int(DEG_get_ctime(depsgraph));
2323 /* This will remove layers that aren't visible. */
2324 grease_pencil_evaluate_layers(*grease_pencil);
2325}
2326
2327void BKE_object_eval_grease_pencil(Depsgraph *depsgraph, Scene *scene, Object *object)
2328{
2329 using namespace blender;
2330 using namespace blender::bke;
2331 /* Free any evaluated data and restore original data. */
2333
2334 GreasePencil *grease_pencil = static_cast<GreasePencil *>(object->data);
2335 GeometrySet geometry_set = GeometrySet::from_grease_pencil(grease_pencil,
2337 /* The layer adjustments for tinting and radii offsets are applied before modifier evaluation.
2338 * This ensures that the evaluated geometry contains the modifications. In the future, it would
2339 * be better to move these into modifiers. For now, these are hardcoded. */
2340 const bke::AttributeAccessor layer_attributes = grease_pencil->attributes();
2341 if (layer_attributes.contains("tint_color") || layer_attributes.contains("radius_offset")) {
2343 }
2344 /* Only add the edit hint component in modes where users can potentially interact with deformed
2345 * drawings. */
2346 if (ELEM(object->mode,
2351 {
2352 GeometryComponentEditData &edit_component =
2354 edit_component.grease_pencil_edit_hints_ = std::make_unique<GreasePencilEditHints>(
2355 *static_cast<const GreasePencil *>(DEG_get_original(object)->data));
2356 }
2357 grease_pencil_evaluate_modifiers(depsgraph, scene, object, geometry_set);
2358
2359 if (geometry_set.has_grease_pencil()) {
2360 /* Output geometry set may be different from the input,
2361 * set the frame again to ensure a correct value. */
2362 geometry_set.get_grease_pencil()->runtime->eval_frame = int(DEG_get_ctime(depsgraph));
2363 }
2364 else {
2365 GreasePencil *empty_grease_pencil = BKE_grease_pencil_new_nomain();
2366 empty_grease_pencil->runtime->eval_frame = int(DEG_get_ctime(depsgraph));
2367 geometry_set.replace_grease_pencil(empty_grease_pencil);
2368 }
2369
2370 /* For now the evaluated data is not const. We could use #get_grease_pencil_for_write, but that
2371 * would result in a copy when it's shared. So for now, we use a const_cast here. */
2372 GreasePencil *grease_pencil_eval = const_cast<GreasePencil *>(geometry_set.get_grease_pencil());
2373
2374 /* Assign evaluated object. */
2375 BKE_object_eval_assign_data(object, &grease_pencil_eval->id, false);
2376 object->runtime->geometry_set_eval = new GeometrySet(std::move(geometry_set));
2377}
2378
2380 GreasePencil *grease_pencil_dst)
2381{
2382 using namespace blender;
2383 grease_pencil_dst->drawing_array_num = grease_pencil_src->drawing_array_num;
2384 if (grease_pencil_dst->drawing_array_num > 0) {
2386 grease_pencil_src->drawing_array_num, __func__);
2387 bke::greasepencil::copy_drawing_array(grease_pencil_src->drawings(),
2388 grease_pencil_dst->drawings());
2389 }
2390}
2391
2393
2394/* ------------------------------------------------------------------- */
2398
2400{
2401 using namespace blender;
2402
2403 for (const GreasePencilDrawingBase *base : grease_pencil.drawings()) {
2404 if (base->type != GP_DRAWING) {
2405 continue;
2406 }
2407 const bke::greasepencil::Drawing &drawing =
2408 reinterpret_cast<const GreasePencilDrawing *>(base)->wrap();
2409 const bke::CurvesGeometry &curves = drawing.strokes();
2410 if (curves.has_curve_with_type(type)) {
2411 return true;
2412 }
2413 }
2414
2415 return false;
2416}
2417
2419{
2420 using namespace blender;
2421
2422 int total_points = 0;
2423
2424 for (const int layer_i : grease_pencil.layers().index_range()) {
2425 const bke::greasepencil::Layer &layer = grease_pencil.layer(layer_i);
2427 frames.foreach_item(
2428 [&](const bke::greasepencil::FramesMapKeyT /*key*/, const GreasePencilFrame frame) {
2429 const GreasePencilDrawingBase *base = grease_pencil.drawing(frame.drawing_index);
2430 if (base->type != GP_DRAWING) {
2431 return;
2432 }
2433 const bke::greasepencil::Drawing &drawing =
2434 reinterpret_cast<const GreasePencilDrawing *>(base)->wrap();
2435 const bke::CurvesGeometry &curves = drawing.strokes();
2436 total_points += curves.points_num();
2437 });
2438 }
2439
2440 return total_points;
2441}
2442
2446{
2447 using namespace blender;
2448 int64_t index = 0;
2449 for (const int layer_i : grease_pencil.layers().index_range()) {
2450 const bke::greasepencil::Layer &layer = grease_pencil.layer(layer_i);
2451 const float4x4 layer_to_object = layer.local_transform();
2453 frames.foreach_item(
2454 [&](const bke::greasepencil::FramesMapKeyT /*key*/, const GreasePencilFrame frame) {
2455 const GreasePencilDrawingBase *base = grease_pencil.drawing(frame.drawing_index);
2456 if (base->type != GP_DRAWING) {
2457 return;
2458 }
2459 const bke::greasepencil::Drawing &drawing =
2460 reinterpret_cast<const GreasePencilDrawing *>(base)->wrap();
2461 const bke::CurvesGeometry &curves = drawing.strokes();
2462 const Span<float3> positions = curves.positions();
2463 const VArray<float> radii = drawing.radii();
2464
2465 if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
2466 for (const int i : curves.points_range()) {
2467 all_positions[index] = math::transform_point(layer_to_object, positions[i]);
2468 all_radii[index] = radii[i];
2469 index++;
2470 }
2471 }
2472 else {
2473 const Span<float3> handle_positions_left = curves.handle_positions_left();
2474 const Span<float3> handle_positions_right = curves.handle_positions_right();
2475 for (const int i : curves.points_range()) {
2476 const int index_pos = index * 3;
2477 all_positions[index_pos] = math::transform_point(layer_to_object,
2478 handle_positions_left[i]);
2479 all_positions[index_pos + 1] = math::transform_point(layer_to_object, positions[i]);
2480 all_positions[index_pos + 2] = math::transform_point(layer_to_object,
2481 handle_positions_right[i]);
2482 all_radii[index] = radii[i];
2483 index++;
2484 }
2485 }
2486 });
2487 }
2488}
2489
2491 blender::Span<blender::float3> all_positions,
2492 blender::Span<float> all_radii)
2493{
2494 using namespace blender;
2495 int64_t index = 0;
2496 for (const int layer_i : grease_pencil.layers().index_range()) {
2497 bke::greasepencil::Layer &layer = grease_pencil.layer(layer_i);
2498 const float4x4 layer_to_object = layer.local_transform();
2499 const float4x4 object_to_layer = math::invert(layer_to_object);
2502 GreasePencilDrawingBase *base = grease_pencil.drawing(frame.drawing_index);
2503 if (base->type != GP_DRAWING) {
2504 return;
2505 }
2506 bke::greasepencil::Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
2508
2509 MutableSpan<float3> positions = curves.positions_for_write();
2510 MutableSpan<float> radii = drawing.radii_for_write();
2511
2512 if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
2513 for (const int i : curves.points_range()) {
2514 positions[i] = math::transform_point(object_to_layer, all_positions[index]);
2515 radii[i] = all_radii[index];
2516 index++;
2517 }
2518 }
2519 else {
2520 MutableSpan<float3> handle_positions_left = curves.handle_positions_left_for_write();
2521 MutableSpan<float3> handle_positions_right = curves.handle_positions_right_for_write();
2522 for (const int i : curves.points_range()) {
2523 const int index_pos = index * 3;
2524 handle_positions_left[i] = math::transform_point(object_to_layer,
2525 all_positions[index_pos]);
2526 positions[i] = math::transform_point(object_to_layer, all_positions[index_pos + 1]);
2527 handle_positions_right[i] = math::transform_point(object_to_layer,
2528 all_positions[index_pos + 2]);
2529 radii[i] = all_radii[index];
2530 index++;
2531 }
2532 }
2533
2534 curves.tag_radii_changed();
2535 drawing.tag_positions_changed();
2536 });
2537 }
2538}
2539
2541 blender::Span<blender::float3> all_positions,
2542 blender::Span<float> all_radii,
2543 const blender::float4x4 &mat)
2544{
2545 using namespace blender;
2546 const float scalef = mat4_to_scale(mat.ptr());
2547 int64_t index = 0;
2548 for (const int layer_i : grease_pencil.layers().index_range()) {
2549 bke::greasepencil::Layer &layer = grease_pencil.layer(layer_i);
2550 const float4x4 layer_to_object = layer.local_transform();
2551 const float4x4 object_to_layer = math::invert(layer_to_object);
2554 GreasePencilDrawingBase *base = grease_pencil.drawing(frame.drawing_index);
2555 if (base->type != GP_DRAWING) {
2556 return;
2557 }
2558 bke::greasepencil::Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
2560
2561 MutableSpan<float3> positions = curves.positions_for_write();
2562 MutableSpan<float> radii = drawing.radii_for_write();
2563
2564 if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
2565 for (const int i : curves.points_range()) {
2566 positions[i] = math::transform_point(object_to_layer * mat, all_positions[index]);
2567 radii[i] = all_radii[index] * scalef;
2568 index++;
2569 }
2570 }
2571 else {
2572 MutableSpan<float3> handle_positions_left = curves.handle_positions_left_for_write();
2573 MutableSpan<float3> handle_positions_right = curves.handle_positions_right_for_write();
2574 for (const int i : curves.points_range()) {
2575 const int index_pos = index * 3;
2576 handle_positions_left[i] = math::transform_point(object_to_layer * mat,
2577 all_positions[index_pos]);
2578 positions[i] = math::transform_point(object_to_layer * mat,
2579 all_positions[index_pos + 1]);
2580 handle_positions_right[i] = math::transform_point(object_to_layer * mat,
2581 all_positions[index_pos + 2]);
2582 radii[i] = all_radii[index] * scalef;
2583 index++;
2584 }
2585 }
2586
2587 curves.tag_radii_changed();
2588 drawing.tag_positions_changed();
2589 });
2590 }
2591}
2592
2594
2595/* ------------------------------------------------------------------- */
2598
2600{
2601 short *totcol = BKE_object_material_len_p(ob);
2602 Material *read_ma = nullptr;
2603 for (short i = 0; i < *totcol; i++) {
2604 read_ma = BKE_object_material_get(ob, i + 1);
2605 if (STREQ(name, read_ma->id.name + 2)) {
2606 return i;
2607 }
2608 }
2609
2610 return -1;
2611}
2612
2614 Object *ob,
2615 const char *name,
2616 int *r_index)
2617{
2618 Material *ma = BKE_gpencil_material_add(bmain, name);
2619 id_us_min(&ma->id); /* no users yet */
2620
2623
2624 if (r_index) {
2625 *r_index = ob->actcol - 1;
2626 }
2627 return ma;
2628}
2629
2631{
2632 if (brush && brush->gpencil_settings &&
2633 (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED))
2634 {
2635 return brush->gpencil_settings->material;
2636 }
2637
2638 return BKE_object_material_get(ob, ob->actcol);
2639}
2640
2642 Object *ob,
2643 const char *name,
2644 int *r_index)
2645{
2647 if (index != -1) {
2648 *r_index = index;
2649 return BKE_object_material_get(ob, index + 1);
2650 }
2651 return BKE_grease_pencil_object_material_new(bmain, ob, name, r_index);
2652}
2653
2655 Object *ob,
2656 Brush *brush)
2657{
2658 Material *ma = (brush->gpencil_settings) ? brush->gpencil_settings->material : nullptr;
2659
2660 if (ma) {
2661 /* Ensure we assign a local datablock if this is an editable asset. */
2662 ma = reinterpret_cast<Material *>(blender::bke::asset_edit_id_ensure_local(*bmain, ma->id));
2663 }
2664
2665 /* check if the material is already on object material slots and add it if missing */
2666 if (ma && BKE_object_material_index_get(ob, ma) < 0) {
2667 /* The object's active material is what's used for the unpinned material. Do not touch it
2668 * while using a pinned material. */
2669 const bool change_active_material = false;
2670
2671 BKE_object_material_slot_add(bmain, ob, change_active_material);
2673 }
2674
2675 return ma;
2676}
2677
2679 Object *ob,
2680 Brush *brush)
2681{
2682 /* Use pinned material. */
2683 if (brush && brush->gpencil_settings &&
2684 (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED))
2685 {
2687 return ma;
2688 }
2689
2690 /* It is easier to just unpin a null material, instead of setting a new one. */
2691 brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED;
2692 }
2693
2694 /* Use the active material. */
2695 if (Material *ma = BKE_object_material_get(ob, ob->actcol)) {
2696 return ma;
2697 }
2698
2699 /* Fall back to default material. */
2700 /* XXX FIXME This is critical abuse of the 'default material' feature, these IDs should never be
2701 * used/returned as 'regular' data. */
2703}
2704
2706 Object *ob,
2707 Brush *brush)
2708{
2709 Material *material_alt = (brush->gpencil_settings) ? brush->gpencil_settings->material_alt :
2710 nullptr;
2711 if (material_alt) {
2712 material_alt = reinterpret_cast<Material *>(
2713 blender::bke::asset_edit_id_find_local(*bmain, material_alt->id));
2714 if (material_alt && BKE_object_material_slot_find_index(ob, material_alt) != -1) {
2715 return material_alt;
2716 }
2717 }
2718
2720}
2721
2722void BKE_grease_pencil_material_remap(GreasePencil *grease_pencil, const uint *remap, int totcol)
2723{
2724 using namespace blender::bke;
2725
2726 for (GreasePencilDrawingBase *base : grease_pencil->drawings()) {
2727 if (base->type != GP_DRAWING) {
2728 continue;
2729 }
2730 greasepencil::Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
2732 SpanAttributeWriter<int> material_indices = attributes.lookup_for_write_span<int>(
2733 "material_index");
2734 if (!material_indices) {
2735 continue;
2736 }
2737 BLI_assert(material_indices.domain == AttrDomain::Curve);
2738 for (const int i : material_indices.span.index_range()) {
2739 BLI_assert(blender::IndexRange(totcol).contains(remap[material_indices.span[i]]));
2740 UNUSED_VARS_NDEBUG(totcol);
2741 material_indices.span[i] = remap[material_indices.span[i]];
2742 }
2743 material_indices.finish();
2744 }
2745}
2746
2747void BKE_grease_pencil_material_index_remove(GreasePencil *grease_pencil, const int index)
2748{
2749 using namespace blender;
2750 using namespace blender::bke;
2751
2752 for (GreasePencilDrawingBase *base : grease_pencil->drawings()) {
2753 if (base->type != GP_DRAWING) {
2754 continue;
2755 }
2756 greasepencil::Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
2758 SpanAttributeWriter<int> material_indices = attributes.lookup_for_write_span<int>(
2759 "material_index");
2760 if (!material_indices) {
2761 continue;
2762 }
2763 BLI_assert(material_indices.domain == AttrDomain::Curve);
2764 for (const int i : material_indices.span.index_range()) {
2765 if (material_indices.span[i] > 0 && material_indices.span[i] >= index) {
2766 material_indices.span[i]--;
2767 }
2768 }
2769 material_indices.finish();
2770 }
2771}
2772
2774{
2775 using namespace blender;
2776 using namespace blender::bke;
2777
2778 for (GreasePencilDrawingBase *base : grease_pencil->drawings()) {
2779 if (base->type != GP_DRAWING) {
2780 continue;
2781 }
2782 greasepencil::Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
2783 AttributeAccessor attributes = drawing.strokes().attributes();
2784 const VArraySpan<int> material_indices = *attributes.lookup_or_default<int>(
2785 "material_index", AttrDomain::Curve, 0);
2786
2787 if (material_indices.contains(index)) {
2788 return true;
2789 }
2790 }
2791 return false;
2792}
2793
2795
2796/* ------------------------------------------------------------------- */
2799
2801 const GreasePencil *grease_pencil)
2802{
2803 for (const GreasePencilDrawingBase *base : grease_pencil->drawings()) {
2804 if (base->type == GP_DRAWING_REFERENCE) {
2805 const auto *reference = reinterpret_cast<const GreasePencilDrawingReference *>(base);
2806 if (id_reference == reference->id_reference) {
2807 return true;
2808 }
2809
2810 if (grease_pencil_references_cyclic_check_internal(id_reference, reference->id_reference)) {
2811 return true;
2812 }
2813 }
2814 }
2815 return false;
2816}
2817
2819 const GreasePencil *grease_pencil)
2820{
2821 return grease_pencil_references_cyclic_check_internal(id_reference, grease_pencil);
2822}
2823
2825
2826/* ------------------------------------------------------------------- */
2829
2831 int mode) = nullptr;
2832void (*BKE_grease_pencil_batch_cache_free_cb)(GreasePencil *grease_pencil) = nullptr;
2833
2835{
2836 if (grease_pencil->runtime && grease_pencil->runtime->batch_cache) {
2837 BKE_grease_pencil_batch_cache_dirty_tag_cb(grease_pencil, mode);
2838 }
2839}
2840
2842{
2843 if (grease_pencil->runtime && grease_pencil->runtime->batch_cache) {
2845 }
2846}
2847
2849
2850/* ------------------------------------------------------------------- */
2853
2854template<typename T> static void grow_array(T **array, int *num, const int add_num)
2855{
2856 BLI_assert(add_num > 0);
2857 const int new_array_num = *num + add_num;
2858 T *new_array = MEM_calloc_arrayN<T>(new_array_num, __func__);
2859
2861 if (*array != nullptr) {
2862 MEM_freeN(*array);
2863 }
2864
2865 *array = new_array;
2866 *num = new_array_num;
2867}
2868template<typename T> static void shrink_array(T **array, int *num, const int shrink_num)
2869{
2870 BLI_assert(shrink_num > 0);
2871 const int new_array_num = *num - shrink_num;
2872 if (new_array_num == 0) {
2873 MEM_freeN(*array);
2874 *array = nullptr;
2875 *num = 0;
2876 return;
2877 }
2878
2879 T *new_array = MEM_calloc_arrayN<T>(new_array_num, __func__);
2880
2881 blender::uninitialized_move_n(*array, new_array_num, new_array);
2882 MEM_freeN(*array);
2883
2884 *array = new_array;
2885 *num = new_array_num;
2886}
2887
2888blender::Span<const GreasePencilDrawingBase *> GreasePencil::drawings() const
2889{
2890 return blender::Span<GreasePencilDrawingBase *>{this->drawing_array, this->drawing_array_num};
2891}
2892
2893blender::MutableSpan<GreasePencilDrawingBase *> GreasePencil::drawings()
2894{
2895 return blender::MutableSpan<GreasePencilDrawingBase *>{this->drawing_array,
2896 this->drawing_array_num};
2897}
2898
2899static void delete_drawing(GreasePencilDrawingBase *drawing_base)
2900{
2901 switch (GreasePencilDrawingType(drawing_base->type)) {
2902 case GP_DRAWING: {
2903 GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
2904 MEM_delete(&drawing->wrap());
2905 break;
2906 }
2907 case GP_DRAWING_REFERENCE: {
2908 GreasePencilDrawingReference *drawing_reference =
2909 reinterpret_cast<GreasePencilDrawingReference *>(drawing_base);
2910 MEM_delete(&drawing_reference->wrap());
2911 break;
2912 }
2913 }
2914}
2915
2916void GreasePencil::resize_drawings(const int new_num)
2917{
2918 using namespace blender;
2919 BLI_assert(new_num >= 0);
2920
2921 const int prev_num = int(this->drawings().size());
2922 if (new_num == prev_num) {
2923 return;
2924 }
2925 if (new_num > prev_num) {
2926 const int add_num = new_num - prev_num;
2927 grow_array<GreasePencilDrawingBase *>(&this->drawing_array, &this->drawing_array_num, add_num);
2928 }
2929 else { /* if (new_num < prev_num) */
2930 const int shrink_num = prev_num - new_num;
2931 MutableSpan<GreasePencilDrawingBase *> old_drawings = this->drawings().drop_front(new_num);
2932 for (const int64_t i : old_drawings.index_range()) {
2933 if (GreasePencilDrawingBase *drawing_base = old_drawings[i]) {
2934 delete_drawing(drawing_base);
2935 }
2936 }
2938 &this->drawing_array, &this->drawing_array_num, shrink_num);
2939 }
2940}
2941
2942void GreasePencil::add_empty_drawings(const int add_num)
2943{
2944 using namespace blender;
2945 BLI_assert(add_num > 0);
2946 const int prev_num = this->drawings().size();
2947 grow_array<GreasePencilDrawingBase *>(&this->drawing_array, &this->drawing_array_num, add_num);
2948 MutableSpan<GreasePencilDrawingBase *> new_drawings = this->drawings().drop_front(prev_num);
2949 for (const int i : new_drawings.index_range()) {
2950 new_drawings[i] = reinterpret_cast<GreasePencilDrawingBase *>(
2951 MEM_new<blender::bke::greasepencil::Drawing>(__func__));
2952 }
2953}
2954
2955void GreasePencil::add_duplicate_drawings(const int duplicate_num,
2956 const blender::bke::greasepencil::Drawing &drawing)
2957{
2958 using namespace blender;
2959 BLI_assert(duplicate_num > 0);
2960 const int prev_num = this->drawings().size();
2962 &this->drawing_array, &this->drawing_array_num, duplicate_num);
2963 MutableSpan<GreasePencilDrawingBase *> new_drawings = this->drawings().drop_front(prev_num);
2964 for (const int i : new_drawings.index_range()) {
2965 new_drawings[i] = reinterpret_cast<GreasePencilDrawingBase *>(
2966 MEM_new<bke::greasepencil::Drawing>(__func__, drawing));
2967 }
2968}
2969
2970blender::bke::greasepencil::Drawing *GreasePencil::insert_frame(
2971 blender::bke::greasepencil::Layer &layer,
2972 const int frame_number,
2973 const int duration,
2974 const eBezTriple_KeyframeType keytype)
2975{
2976 using namespace blender;
2977 GreasePencilFrame *frame = layer.add_frame(frame_number, duration);
2978 if (frame == nullptr) {
2979 return nullptr;
2980 }
2981 this->add_empty_drawings(1);
2982 frame->drawing_index = this->drawings().index_range().last();
2983 frame->type = int8_t(keytype);
2984
2985 GreasePencilDrawingBase *drawing_base = this->drawings().last();
2986 BLI_assert(drawing_base->type == GP_DRAWING);
2987 GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
2988 return &drawing->wrap();
2989}
2990
2991void GreasePencil::insert_frames(Span<blender::bke::greasepencil::Layer *> layers,
2992 const int frame_number,
2993 const int duration,
2994 const eBezTriple_KeyframeType keytype)
2995{
2996 using namespace blender;
2997 if (layers.is_empty()) {
2998 return;
2999 }
3001 frames.reserve(layers.size());
3002 for (bke::greasepencil::Layer *layer : layers) {
3003 BLI_assert(layer != nullptr);
3004 GreasePencilFrame *frame = layer->add_frame(frame_number, duration);
3005 if (frame != nullptr) {
3006 frames.append(frame);
3007 }
3008 }
3009
3010 if (frames.is_empty()) {
3011 return;
3012 }
3013
3014 this->add_empty_drawings(frames.size());
3015 const IndexRange new_drawings = this->drawings().index_range().take_back(frames.size());
3016 for (const int frame_i : frames.index_range()) {
3017 GreasePencilFrame *frame = frames[frame_i];
3018 frame->drawing_index = new_drawings[frame_i];
3019 frame->type = int8_t(keytype);
3020 }
3021}
3022
3023bool GreasePencil::insert_duplicate_frame(blender::bke::greasepencil::Layer &layer,
3024 const int src_frame_number,
3025 const int dst_frame_number,
3026 const bool do_instance)
3027{
3028 using namespace blender::bke::greasepencil;
3029
3030 if (!layer.frames().contains(src_frame_number)) {
3031 return false;
3032 }
3033
3034 if (layer.is_locked()) {
3035 return false;
3036 }
3037 const GreasePencilFrame src_frame = layer.frames().lookup(src_frame_number);
3038
3039 /* Create the new frame structure, with the same duration.
3040 * If we want to make an instance of the source frame, the drawing index gets copied from the
3041 * source frame. Otherwise, we set the drawing index to the size of the drawings array, since we
3042 * are going to add a new drawing copied from the source drawing. */
3043 const int duration = layer.get_frame_duration_at(src_frame_number);
3044 GreasePencilFrame *dst_frame = layer.add_frame(dst_frame_number, duration);
3045 if (dst_frame == nullptr) {
3046 return false;
3047 }
3048 dst_frame->drawing_index = do_instance ? src_frame.drawing_index : int(this->drawings().size());
3049 dst_frame->type = src_frame.type;
3050
3051 const GreasePencilDrawingBase *src_drawing_base = this->drawing(src_frame.drawing_index);
3052 switch (src_drawing_base->type) {
3053 case GP_DRAWING: {
3054 const Drawing &src_drawing =
3055 reinterpret_cast<const GreasePencilDrawing *>(src_drawing_base)->wrap();
3056 if (do_instance) {
3057 /* Adds the duplicate frame as a new instance of the same drawing. We thus increase the
3058 * user count of the corresponding drawing. */
3059 src_drawing.add_user();
3060 }
3061 else {
3062 /* Create a copy of the drawing, and add it at the end of the drawings array.
3063 * Note that the frame already points to this new drawing, as the drawing index was set to
3064 * `int(this->drawings().size())`. */
3065 this->add_duplicate_drawings(1, src_drawing);
3066 }
3067 break;
3068 }
3070 /* TODO: Duplicate drawing references is not yet implemented.
3071 * For now, just remove the frame that we inserted. */
3072 layer.remove_frame(dst_frame_number);
3073 return false;
3074 }
3075 return true;
3076}
3077
3078bool GreasePencil::remove_frames(blender::bke::greasepencil::Layer &layer,
3079 blender::Span<int> frame_numbers)
3080{
3081 using namespace blender::bke::greasepencil;
3082 bool removed_any_drawing_user = false;
3083 for (const int frame_number : frame_numbers) {
3084 if (!layer.frames().contains(frame_number)) {
3085 continue;
3086 }
3087 const GreasePencilFrame frame_to_remove = layer.frames().lookup(frame_number);
3088 const int64_t drawing_index_to_remove = frame_to_remove.drawing_index;
3089 if (!layer.remove_frame(frame_number)) {
3090 /* If removing the frame was not successful, continue. */
3091 continue;
3092 }
3093 if (frame_to_remove.is_end()) {
3094 /* End frames don't reference a drawing, continue. */
3095 continue;
3096 }
3097 GreasePencilDrawingBase *drawing_base = this->drawing(drawing_index_to_remove);
3098 if (drawing_base->type != GP_DRAWING) {
3099 /* If the drawing is referenced from another object, we don't track it's users because we
3100 * cannot delete drawings from another object. */
3101 continue;
3102 }
3103 Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base)->wrap();
3104 drawing.remove_user();
3105 removed_any_drawing_user = true;
3106 }
3107 if (removed_any_drawing_user) {
3108 this->remove_drawings_with_no_users();
3109 return true;
3110 }
3111 return false;
3112}
3113
3114void GreasePencil::copy_frames_from_layer(blender::bke::greasepencil::Layer &dst_layer,
3115 const GreasePencil &src_grease_pencil,
3116 const blender::bke::greasepencil::Layer &src_layer,
3117 const std::optional<int> frame_select)
3118{
3119 using namespace blender;
3120
3121 const Span<const GreasePencilDrawingBase *> src_drawings = src_grease_pencil.drawings();
3122 Array<int> drawing_index_map(src_grease_pencil.drawing_array_num, -1);
3123
3124 for (auto [frame_number, src_frame] : src_layer.frames().items()) {
3125 if (frame_select && *frame_select != frame_number) {
3126 continue;
3127 }
3128
3129 const int src_drawing_index = src_frame.drawing_index;
3130 int dst_drawing_index = drawing_index_map[src_drawing_index];
3131 if (dst_drawing_index < 0) {
3132 switch (src_drawings[src_drawing_index]->type) {
3133 case GP_DRAWING: {
3134 const bke::greasepencil::Drawing &src_drawing =
3135 reinterpret_cast<const GreasePencilDrawing *>(src_drawings[src_drawing_index])
3136 ->wrap();
3137 this->add_duplicate_drawings(1, src_drawing);
3138 break;
3139 }
3141 /* Dummy drawing to keep frame reference valid. */
3142 this->add_empty_drawings(1);
3143 break;
3144 }
3145 dst_drawing_index = this->drawings().size() - 1;
3146 drawing_index_map[src_drawing_index] = dst_drawing_index;
3147 }
3148 BLI_assert(this->drawings().index_range().contains(dst_drawing_index));
3149
3150 GreasePencilFrame *dst_frame = dst_layer.add_frame(frame_number);
3151 dst_frame->flag = src_frame.flag;
3152 dst_frame->drawing_index = dst_drawing_index;
3153 }
3154}
3155
3156void GreasePencil::add_layers_with_empty_drawings_for_eval(const int num)
3157{
3158 using namespace blender;
3159 using namespace blender::bke::greasepencil;
3160 const int old_drawings_num = this->drawing_array_num;
3161 const int old_layers_num = this->layers().size();
3162 this->add_empty_drawings(num);
3163 this->add_layers_for_eval(num);
3164 threading::parallel_for(IndexRange(num), 256, [&](const IndexRange range) {
3165 for (const int i : range) {
3166 const int new_drawing_i = old_drawings_num + i;
3167 const int new_layer_i = old_layers_num + i;
3168 Layer &layer = this->layer(new_layer_i);
3169 GreasePencilFrame *frame = layer.add_frame(this->runtime->eval_frame);
3170 BLI_assert(frame);
3171 frame->drawing_index = new_drawing_i;
3172 }
3173 });
3174}
3175
3176void GreasePencil::remove_drawings_with_no_users()
3177{
3178 using namespace blender;
3179 using namespace blender::bke::greasepencil;
3180
3181 /* Compress the drawings array by finding unused drawings.
3182 * In every step two indices are found:
3183 * - The next unused drawing from the start
3184 * - The last used drawing from the end
3185 * These two drawings are then swapped. Rinse and repeat until both iterators meet somewhere in
3186 * the middle. At this point the drawings array is fully compressed.
3187 * Then the drawing indices in frame data are remapped. */
3188
3189 const MutableSpan<GreasePencilDrawingBase *> drawings = this->drawings();
3190 if (drawings.is_empty()) {
3191 return;
3192 }
3193
3194 auto is_drawing_used = [&](const int drawing_index) {
3195 GreasePencilDrawingBase *drawing_base = drawings[drawing_index];
3196 /* NOTE: GreasePencilDrawingReference does not have a user count currently, but should
3197 * eventually be counted like GreasePencilDrawing. */
3198 if (drawing_base->type != GP_DRAWING) {
3199 return false;
3200 }
3201 GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
3202 return drawing->wrap().has_users();
3203 };
3204
3205 /* Index map to remap drawing indices in frame data.
3206 * Index -1 indicates that the drawing has not been moved. */
3207 constexpr const int unchanged_index = -1;
3208 Array<int> drawing_index_map(drawings.size(), unchanged_index);
3209
3210 int first_unused_drawing = -1;
3211 int last_used_drawing = drawings.size() - 1;
3212 /* Advance head and tail iterators to the next unused/used drawing respectively.
3213 * Returns true if an index pair was found that needs to be swapped. */
3214 auto find_next_swap_index = [&]() -> bool {
3215 do {
3216 ++first_unused_drawing;
3217 } while (first_unused_drawing <= last_used_drawing && is_drawing_used(first_unused_drawing));
3218 while (last_used_drawing >= 0 && !is_drawing_used(last_used_drawing)) {
3219 --last_used_drawing;
3220 }
3221
3222 return first_unused_drawing < last_used_drawing;
3223 };
3224
3225 while (find_next_swap_index()) {
3226 /* Found two valid iterators, now swap drawings. */
3227 std::swap(drawings[first_unused_drawing], drawings[last_used_drawing]);
3228 drawing_index_map[last_used_drawing] = first_unused_drawing;
3229 }
3230
3231 /* `last_used_drawing` is expected to be exactly the item before the first unused drawing, once
3232 * the loop above is fully done and all unused drawings are supposed to be at the end of the
3233 * array. */
3234 BLI_assert(last_used_drawing == first_unused_drawing - 1);
3235#ifndef NDEBUG
3236 for (const int i : drawings.index_range()) {
3237 if (i < first_unused_drawing) {
3238 BLI_assert(is_drawing_used(i));
3239 }
3240 else {
3241 BLI_assert(!is_drawing_used(i));
3242 }
3243 }
3244#endif
3245
3246 /* Tail range of unused drawings that can be removed. */
3247 const IndexRange drawings_to_remove = (first_unused_drawing > 0) ?
3248 drawings.index_range().drop_front(
3249 first_unused_drawing) :
3250 drawings.index_range();
3251 if (drawings_to_remove.is_empty()) {
3252 return;
3253 }
3254
3255 /* Free the unused drawings. */
3256 for (const int i : drawings_to_remove) {
3257 GreasePencilDrawingBase *unused_drawing_base = drawings[i];
3258 switch (unused_drawing_base->type) {
3259 case GP_DRAWING: {
3260 auto *unused_drawing = reinterpret_cast<GreasePencilDrawing *>(unused_drawing_base);
3261 MEM_delete(&unused_drawing->wrap());
3262 break;
3263 }
3264 case GP_DRAWING_REFERENCE: {
3265 auto *unused_drawing_ref = reinterpret_cast<GreasePencilDrawingReference *>(
3266 unused_drawing_base);
3267 MEM_delete(&unused_drawing_ref->wrap());
3268 break;
3269 }
3270 }
3271 }
3273 &this->drawing_array, &this->drawing_array_num, drawings_to_remove.size());
3274
3275 /* Remap drawing indices in frame data. */
3276 for (Layer *layer : this->layers_for_write()) {
3277 for (auto [key, value] : layer->frames_for_write().items()) {
3278 const int new_drawing_index = drawing_index_map[value.drawing_index];
3279 if (new_drawing_index != unchanged_index) {
3280 value.drawing_index = new_drawing_index;
3281 layer->tag_frames_map_changed();
3282 }
3283 }
3284 }
3285}
3286
3287void GreasePencil::update_drawing_users_for_layer(const blender::bke::greasepencil::Layer &layer)
3288{
3289 using namespace blender;
3290 for (auto [key, value] : layer.frames().items()) {
3291 BLI_assert(this->drawings().index_range().contains(value.drawing_index));
3292 GreasePencilDrawingBase *drawing_base = this->drawing(value.drawing_index);
3293 if (drawing_base->type != GP_DRAWING) {
3294 continue;
3295 }
3297 reinterpret_cast<GreasePencilDrawing *>(drawing_base)->wrap();
3298 if (!drawing.has_users()) {
3299 drawing.add_user();
3300 }
3301 }
3302}
3303
3304void GreasePencil::move_frames(blender::bke::greasepencil::Layer &layer,
3305 const blender::Map<int, int> &frame_number_destinations)
3306{
3307 this->move_duplicate_frames(
3308 layer, frame_number_destinations, blender::Map<int, GreasePencilFrame>());
3309}
3310
3311void GreasePencil::move_duplicate_frames(
3312 blender::bke::greasepencil::Layer &layer,
3313 const blender::Map<int, int> &frame_number_destinations,
3314 const blender::Map<int, GreasePencilFrame> &duplicate_frames)
3315{
3316 using namespace blender;
3317 Map<int, GreasePencilFrame> layer_frames_copy = layer.frames();
3318
3319 /* Copy frames durations. */
3320 Map<int, int> src_layer_frames_durations;
3321 for (const auto [frame_number, frame] : layer.frames().items()) {
3322 src_layer_frames_durations.add(frame_number, layer.get_frame_duration_at(frame_number));
3323 }
3324
3325 /* Remove original frames for duplicates before inserting any frames.
3326 * This has to be done early to avoid removing frames that may be inserted
3327 * in place of the source frames. */
3328 for (const auto src_frame_number : frame_number_destinations.keys()) {
3329 if (!duplicate_frames.contains(src_frame_number)) {
3330 /* User count not decremented here, the same frame is inserted again later. */
3331 layer.remove_frame(src_frame_number);
3332 }
3333 }
3334
3335 auto get_source_frame = [&](const int frame_number) -> const GreasePencilFrame * {
3336 if (const GreasePencilFrame *ptr = duplicate_frames.lookup_ptr(frame_number)) {
3337 return ptr;
3338 }
3339 return layer_frames_copy.lookup_ptr(frame_number);
3340 };
3341
3342 for (const auto [src_frame_number, dst_frame_number] : frame_number_destinations.items()) {
3343 const GreasePencilFrame *src_frame = get_source_frame(src_frame_number);
3344 if (!src_frame) {
3345 continue;
3346 }
3347 const int duration = src_layer_frames_durations.lookup_default(src_frame_number, 0);
3348
3349 /* Add and overwrite the frame at the destination number. */
3350 if (layer.frames().contains(dst_frame_number)) {
3351 GreasePencilFrame frame_to_overwrite = layer.frames().lookup(dst_frame_number);
3352 GreasePencilDrawingBase *drawing_base = this->drawing(frame_to_overwrite.drawing_index);
3353 if (drawing_base->type == GP_DRAWING) {
3354 reinterpret_cast<GreasePencilDrawing *>(drawing_base)->wrap().remove_user();
3355 }
3356 layer.remove_frame(dst_frame_number);
3357 }
3358 GreasePencilFrame *frame = layer.add_frame(dst_frame_number, duration);
3359 *frame = *src_frame;
3360 }
3361
3362 /* Remove drawings if they no longer have users. */
3363 this->remove_drawings_with_no_users();
3364}
3365
3366const blender::bke::greasepencil::Drawing *GreasePencil::get_drawing_at(
3367 const blender::bke::greasepencil::Layer &layer, const int frame_number) const
3368{
3369 if (this->drawings().is_empty()) {
3370 return nullptr;
3371 }
3372 const int drawing_index = layer.drawing_index_at(frame_number);
3373 if (drawing_index == -1) {
3374 /* No drawing found. */
3375 return nullptr;
3376 }
3377 const GreasePencilDrawingBase *drawing_base = this->drawing(drawing_index);
3378 if (drawing_base->type != GP_DRAWING) {
3379 /* TODO: Get reference drawing. */
3380 return nullptr;
3381 }
3382 const GreasePencilDrawing *drawing = reinterpret_cast<const GreasePencilDrawing *>(drawing_base);
3383 return &drawing->wrap();
3384}
3385
3386blender::bke::greasepencil::Drawing *GreasePencil::get_drawing_at(
3387 const blender::bke::greasepencil::Layer &layer, const int frame_number)
3388{
3389 if (this->drawings().is_empty()) {
3390 return nullptr;
3391 }
3392 const int drawing_index = layer.drawing_index_at(frame_number);
3393 if (drawing_index == -1) {
3394 /* No drawing found. */
3395 return nullptr;
3396 }
3397 GreasePencilDrawingBase *drawing_base = this->drawing(drawing_index);
3398 if (drawing_base->type != GP_DRAWING) {
3399 /* TODO: Get reference drawing. */
3400 return nullptr;
3401 }
3402 GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
3403 return &drawing->wrap();
3404}
3405
3406blender::bke::greasepencil::Drawing *GreasePencil::get_editable_drawing_at(
3407 const blender::bke::greasepencil::Layer &layer, const int frame_number)
3408{
3409 if (!layer.is_editable()) {
3410 return nullptr;
3411 }
3412 if (this->drawings().is_empty()) {
3413 return nullptr;
3414 }
3415 const int drawing_index = layer.drawing_index_at(frame_number);
3416 if (drawing_index == -1) {
3417 /* No drawing found. */
3418 return nullptr;
3419 }
3420 GreasePencilDrawingBase *drawing_base = this->drawing(drawing_index);
3421 if (drawing_base->type != GP_DRAWING) {
3422 /* Drawing references are not editable. */
3423 return nullptr;
3424 }
3425 GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
3426 return &drawing->wrap();
3427}
3428
3429const blender::bke::greasepencil::Drawing *GreasePencil::get_eval_drawing(
3430 const blender::bke::greasepencil::Layer &layer) const
3431{
3432 return this->get_drawing_at(layer, this->runtime->eval_frame);
3433}
3434
3435blender::bke::greasepencil::Drawing *GreasePencil::get_eval_drawing(
3436 const blender::bke::greasepencil::Layer &layer)
3437{
3438 return this->get_drawing_at(layer, this->runtime->eval_frame);
3439}
3440
3444{
3445 BLI_assert(src.size() == dst.size());
3446
3448 for (const int i : range) {
3449 dst[i] = blender::math::transform_point(transform, src[i]);
3450 }
3451 });
3452}
3453
3454std::optional<blender::Bounds<blender::float3>> GreasePencil::bounds_min_max(
3455 const int frame, const bool use_radius) const
3456{
3457 using namespace blender;
3458 std::optional<Bounds<float3>> bounds;
3459 const Span<const bke::greasepencil::Layer *> layers = this->layers();
3460 for (const int layer_i : layers.index_range()) {
3461 const bke::greasepencil::Layer &layer = *layers[layer_i];
3462 const float4x4 layer_to_object = layer.local_transform();
3463 if (!layer.is_visible()) {
3464 continue;
3465 }
3466 const bke::greasepencil::Drawing *drawing = this->get_drawing_at(layer, frame);
3467 if (!drawing) {
3468 continue;
3469 }
3470 const bke::CurvesGeometry &curves = drawing->strokes();
3471 if (curves.is_empty()) {
3472 continue;
3473 }
3474 if (layer_to_object == float4x4::identity()) {
3475 bounds = bounds::merge(bounds, curves.bounds_min_max(use_radius));
3476 continue;
3477 }
3478 const VArray<float> radius = curves.radius();
3479 Array<float3> positions_world(curves.evaluated_points_num());
3480 transform_positions(curves.evaluated_positions(), layer_to_object, positions_world);
3481 if (!use_radius) {
3482 const Bounds<float3> drawing_bounds = *bounds::min_max(positions_world.as_span());
3483 bounds = bounds::merge(bounds, drawing_bounds);
3484 continue;
3485 }
3486 if (const std::optional radius_single = radius.get_if_single()) {
3487 Bounds<float3> drawing_bounds = *curves.bounds_min_max(false);
3488 drawing_bounds.pad(*radius_single);
3489 bounds = bounds::merge(bounds, drawing_bounds);
3490 continue;
3491 }
3492 const Span radius_span = radius.get_internal_span();
3493 if (curves.is_single_type(CURVE_TYPE_POLY)) {
3494 const Bounds<float3> drawing_bounds = *bounds::min_max_with_radii(positions_world.as_span(),
3495 radius_span);
3496 bounds = bounds::merge(bounds, drawing_bounds);
3497 continue;
3498 }
3500 Array<float> radii_eval(curves.evaluated_points_num());
3501 curves.interpolate_to_evaluated(radius_span, radii_eval.as_mutable_span());
3502 const Bounds<float3> drawing_bounds = *bounds::min_max_with_radii(positions_world.as_span(),
3503 radii_eval.as_span());
3504 bounds = bounds::merge(bounds, drawing_bounds);
3505 }
3506 return bounds;
3507}
3508
3509std::optional<blender::Bounds<blender::float3>> GreasePencil::bounds_min_max_eval(
3510 const bool use_radius) const
3511{
3512 return this->bounds_min_max(this->runtime->eval_frame, use_radius);
3513}
3514
3515void GreasePencil::count_memory(blender::MemoryCounter &memory) const
3516{
3517 using namespace blender::bke;
3518 for (const GreasePencilDrawingBase *base : this->drawings()) {
3519 if (base->type != GP_DRAWING) {
3520 continue;
3521 }
3522 const greasepencil::Drawing &drawing =
3523 reinterpret_cast<const GreasePencilDrawing *>(base)->wrap();
3524 drawing.strokes().count_memory(memory);
3525 }
3526}
3527
3528std::optional<int> GreasePencil::material_index_max_eval() const
3529{
3530 using namespace blender;
3531 using namespace blender::bke;
3532 std::optional<int> max_index;
3533 for (const greasepencil::Layer *layer : this->layers()) {
3534 if (const greasepencil::Drawing *drawing = this->get_eval_drawing(*layer)) {
3535 const bke::CurvesGeometry &curves = drawing->strokes();
3536 const std::optional<int> max_index_on_layer = curves.material_index_max();
3537 if (max_index) {
3538 if (max_index_on_layer) {
3539 max_index = std::max(*max_index, *max_index_on_layer);
3540 }
3541 }
3542 else {
3543 max_index = max_index_on_layer;
3544 }
3545 }
3546 }
3547 return max_index;
3548}
3549
3550blender::Span<const blender::bke::greasepencil::Layer *> GreasePencil::layers() const
3551{
3552 BLI_assert(this->runtime != nullptr);
3553 return this->root_group().layers();
3554}
3555
3556blender::Span<blender::bke::greasepencil::Layer *> GreasePencil::layers_for_write()
3557{
3558 BLI_assert(this->runtime != nullptr);
3559 return this->root_group().layers_for_write();
3560}
3561
3562blender::Span<const blender::bke::greasepencil::LayerGroup *> GreasePencil::layer_groups() const
3563{
3564 BLI_assert(this->runtime != nullptr);
3565 return this->root_group().groups();
3566}
3567
3568blender::Span<blender::bke::greasepencil::LayerGroup *> GreasePencil::layer_groups_for_write()
3569{
3570 BLI_assert(this->runtime != nullptr);
3571 return this->root_group().groups_for_write();
3572}
3573
3574blender::Span<const blender::bke::greasepencil::TreeNode *> GreasePencil::nodes() const
3575{
3576 BLI_assert(this->runtime != nullptr);
3577 return this->root_group().nodes();
3578}
3579
3580blender::Span<blender::bke::greasepencil::TreeNode *> GreasePencil::nodes_for_write()
3581{
3582 BLI_assert(this->runtime != nullptr);
3583 return this->root_group().nodes_for_write();
3584}
3585
3586std::optional<int> GreasePencil::get_layer_index(
3587 const blender::bke::greasepencil::Layer &layer) const
3588{
3589 const int index = int(this->layers().first_index_try(&layer));
3590 if (index == -1) {
3591 return {};
3592 }
3593 return index;
3594}
3595
3596const blender::bke::greasepencil::Layer *GreasePencil::get_active_layer() const
3597{
3598 if (this->active_node == nullptr) {
3599 return nullptr;
3600 }
3601 const blender::bke::greasepencil::TreeNode &active_node = *this->get_active_node();
3602 if (!active_node.is_layer()) {
3603 return nullptr;
3604 }
3605 return &active_node.as_layer();
3606}
3607
3608blender::bke::greasepencil::Layer *GreasePencil::get_active_layer()
3609{
3610 if (this->active_node == nullptr) {
3611 return nullptr;
3612 }
3613 blender::bke::greasepencil::TreeNode &active_node = *this->get_active_node();
3614 if (!active_node.is_layer()) {
3615 return nullptr;
3616 }
3617 return &active_node.as_layer();
3618}
3619
3620void GreasePencil::set_active_layer(blender::bke::greasepencil::Layer *layer)
3621{
3622 this->active_node = reinterpret_cast<GreasePencilLayerTreeNode *>(&layer->as_node());
3623
3624 if (this->flag & GREASE_PENCIL_AUTOLOCK_LAYERS) {
3625 this->autolock_inactive_layers();
3626 }
3627}
3628
3629bool GreasePencil::is_layer_active(const blender::bke::greasepencil::Layer *layer) const
3630{
3631 if (layer == nullptr) {
3632 return false;
3633 }
3634 return this->get_active_layer() == layer;
3635}
3636
3637void GreasePencil::autolock_inactive_layers()
3638{
3639 using namespace blender::bke::greasepencil;
3640
3641 for (Layer *layer : this->layers_for_write()) {
3642 if (this->is_layer_active(layer)) {
3643 layer->set_locked(false);
3644 continue;
3645 }
3646 layer->set_locked(true);
3647 }
3648}
3649
3650const blender::bke::greasepencil::LayerGroup *GreasePencil::get_active_group() const
3651{
3652 if (this->active_node == nullptr) {
3653 return nullptr;
3654 }
3655 const blender::bke::greasepencil::TreeNode &active_node = *this->get_active_node();
3656 if (!active_node.is_group()) {
3657 return nullptr;
3658 }
3659 return &active_node.as_group();
3660}
3661
3662blender::bke::greasepencil::LayerGroup *GreasePencil::get_active_group()
3663{
3664 if (this->active_node == nullptr) {
3665 return nullptr;
3666 }
3667 blender::bke::greasepencil::TreeNode &active_node = *this->get_active_node();
3668 if (!active_node.is_group()) {
3669 return nullptr;
3670 }
3671 return &active_node.as_group();
3672}
3673
3674const blender::bke::greasepencil::TreeNode *GreasePencil::get_active_node() const
3675{
3676 if (this->active_node == nullptr) {
3677 return nullptr;
3678 }
3679 return &this->active_node->wrap();
3680}
3681
3682blender::bke::greasepencil::TreeNode *GreasePencil::get_active_node()
3683{
3684 if (this->active_node == nullptr) {
3685 return nullptr;
3686 }
3687 return &this->active_node->wrap();
3688}
3689
3690void GreasePencil::set_active_node(blender::bke::greasepencil::TreeNode *node)
3691{
3692 this->active_node = reinterpret_cast<GreasePencilLayerTreeNode *>(node);
3693}
3694
3696{
3697 using namespace blender;
3699 for (const blender::bke::greasepencil::TreeNode *node : grease_pencil.nodes()) {
3700 names.add(node->name());
3701 }
3702 return names;
3703}
3704
3705static std::string unique_node_name(const GreasePencil &grease_pencil,
3706 const blender::StringRef name)
3707{
3708 using namespace blender;
3709 BLI_assert(!name.is_empty());
3710 const VectorSet<StringRef> names = get_node_names(grease_pencil);
3711 return BLI_uniquename_cb(
3712 [&](const StringRef check_name) { return names.contains(check_name); }, '.', name);
3713}
3714
3715std::string GreasePencil::unique_layer_name(blender::StringRef name)
3716{
3717 if (name.is_empty()) {
3718 /* Default name is "Layer". */
3719 name = DATA_("Layer");
3720 }
3721 return unique_node_name(*this, name);
3722}
3723
3724static std::string unique_layer_group_name(const GreasePencil &grease_pencil,
3725 blender::StringRef name)
3726{
3727 if (name.is_empty()) {
3728 /* Default name is "Group". */
3729 name = DATA_("Group");
3730 }
3731 return unique_node_name(grease_pencil, name);
3732}
3733
3734blender::bke::greasepencil::Layer &GreasePencil::add_layer(const blender::StringRef name,
3735 const bool check_name_is_unique)
3736{
3737 using namespace blender;
3738 std::string unique_name = check_name_is_unique ? unique_layer_name(name) : std::string(name);
3739 const int numLayers = layers().size();
3740 CustomData_realloc(&layers_data, numLayers, numLayers + 1, CD_SET_DEFAULT);
3741 bke::greasepencil::Layer *new_layer = MEM_new<bke::greasepencil::Layer>(__func__, unique_name);
3742 /* Enable Lights by default. */
3744 /* Hide masks by default. */
3746 bke::greasepencil::Layer &layer = root_group().add_node(new_layer->as_node()).as_layer();
3747
3748 /* Initialize the attributes with default values. */
3749 bke::MutableAttributeAccessor attributes = this->attributes_for_write();
3753 IndexRange::from_single(numLayers));
3754
3755 return layer;
3756}
3757
3758blender::bke::greasepencil::Layer &GreasePencil::add_layer(
3759 blender::bke::greasepencil::LayerGroup &parent_group,
3760 const blender::StringRef name,
3761 const bool check_name_is_unique)
3762{
3763 using namespace blender;
3764 blender::bke::greasepencil::Layer &new_layer = this->add_layer(name, check_name_is_unique);
3765 move_node_into(new_layer.as_node(), parent_group);
3766 return new_layer;
3767}
3768
3769void GreasePencil::add_layers_for_eval(const int num_new_layers)
3770{
3771 using namespace blender;
3772 const int num_layers = this->layers().size();
3773 CustomData_realloc(&layers_data, num_layers, num_layers + num_new_layers);
3774 for ([[maybe_unused]] const int i : IndexRange(num_new_layers)) {
3775 bke::greasepencil::Layer *new_layer = MEM_new<bke::greasepencil::Layer>(__func__);
3776 /* Hide masks by default. */
3778 this->root_group().add_node(new_layer->as_node());
3779 }
3780}
3781
3782blender::bke::greasepencil::Layer &GreasePencil::duplicate_layer(
3783 const blender::bke::greasepencil::Layer &duplicate_layer)
3784{
3785 using namespace blender;
3786 std::string unique_name = unique_layer_name(duplicate_layer.name());
3787 std::optional<int> duplicate_layer_idx = get_layer_index(duplicate_layer);
3788 BLI_assert(duplicate_layer_idx.has_value());
3789 const int numLayers = layers().size();
3790 CustomData_realloc(&layers_data, numLayers, numLayers + 1);
3791 for (const int layer_index : IndexRange(layers_data.totlayer)) {
3793 &layers_data, &layers_data, layer_index, layer_index, *duplicate_layer_idx, numLayers, 1);
3794 }
3795 bke::greasepencil::Layer *new_layer = MEM_new<bke::greasepencil::Layer>(__func__,
3796 duplicate_layer);
3797 root_group().add_node(new_layer->as_node());
3798 this->update_drawing_users_for_layer(*new_layer);
3799 new_layer->set_name(unique_name);
3800 return *new_layer;
3801}
3802
3803blender::bke::greasepencil::Layer &GreasePencil::duplicate_layer(
3804 blender::bke::greasepencil::LayerGroup &parent_group,
3805 const blender::bke::greasepencil::Layer &duplicate_layer)
3806{
3807 using namespace blender;
3808 bke::greasepencil::Layer &new_layer = this->duplicate_layer(duplicate_layer);
3809 move_node_into(new_layer.as_node(), parent_group);
3810 return new_layer;
3811}
3812
3813blender::bke::greasepencil::LayerGroup &GreasePencil::add_layer_group(
3814 const blender::StringRef name, const bool check_name_is_unique)
3815{
3816 using namespace blender;
3817 std::string unique_name = check_name_is_unique ? unique_layer_group_name(*this, name) :
3818 std::string(name);
3819 bke::greasepencil::LayerGroup *new_group = MEM_new<bke::greasepencil::LayerGroup>(__func__,
3820 unique_name);
3821 return root_group().add_node(new_group->as_node()).as_group();
3822}
3823
3824blender::bke::greasepencil::LayerGroup &GreasePencil::add_layer_group(
3825 blender::bke::greasepencil::LayerGroup &parent_group,
3826 const blender::StringRef name,
3827 const bool check_name_is_unique)
3828{
3829 using namespace blender;
3830 bke::greasepencil::LayerGroup &new_group = this->add_layer_group(name, check_name_is_unique);
3831 move_node_into(new_group.as_node(), parent_group);
3832 return new_group;
3833}
3834
3835static void reorder_customdata(CustomData &data, const Span<int> new_by_old_map)
3836{
3837 CustomData new_data;
3838 CustomData_init_layout_from(&data, &new_data, CD_MASK_ALL, CD_CONSTRUCT, new_by_old_map.size());
3839
3840 for (const int old_i : new_by_old_map.index_range()) {
3841 const int new_i = new_by_old_map[old_i];
3842 CustomData_copy_data(&data, &new_data, old_i, new_i, 1);
3843 }
3845 data = new_data;
3846}
3847
3848static void reorder_layer_data(GreasePencil &grease_pencil,
3849 const blender::FunctionRef<void()> do_layer_order_changes)
3850{
3851 using namespace blender;
3852 Span<const bke::greasepencil::Layer *> layers = grease_pencil.layers();
3853
3854 /* Stash the initial layer order that we can refer back to later */
3855 Map<const bke::greasepencil::Layer *, int> old_layer_index_by_layer;
3856 old_layer_index_by_layer.reserve(layers.size());
3857 for (const int i : layers.index_range()) {
3858 old_layer_index_by_layer.add_new(layers[i], i);
3859 }
3860
3861 /* Execute the callback that changes the order of the layers. */
3862 do_layer_order_changes();
3863 layers = grease_pencil.layers();
3864 BLI_assert(layers.size() == old_layer_index_by_layer.size());
3865
3866 /* Compose the mapping from old layer indices to new layer indices */
3867 Array<int> new_by_old_map(layers.size());
3868 for (const int layer_i_new : layers.index_range()) {
3869 const bke::greasepencil::Layer *layer = layers[layer_i_new];
3870 BLI_assert(old_layer_index_by_layer.contains(layer));
3871 const int layer_i_old = old_layer_index_by_layer.pop(layer);
3872 new_by_old_map[layer_i_old] = layer_i_new;
3873 }
3874 BLI_assert(old_layer_index_by_layer.is_empty());
3875
3876 /* Use the mapping to re-order the custom data */
3877 reorder_customdata(grease_pencil.layers_data, new_by_old_map);
3878}
3879
3880void GreasePencil::move_node_up(blender::bke::greasepencil::TreeNode &node, const int step)
3881{
3882 using namespace blender;
3883 if (!node.parent_group()) {
3884 return;
3885 }
3886 reorder_layer_data(*this, [&]() { node.parent_group()->move_node_up(node, step); });
3887}
3888void GreasePencil::move_node_down(blender::bke::greasepencil::TreeNode &node, const int step)
3889{
3890 using namespace blender;
3891 if (!node.parent_group()) {
3892 return;
3893 }
3894 reorder_layer_data(*this, [&]() { node.parent_group()->move_node_down(node, step); });
3895}
3896void GreasePencil::move_node_top(blender::bke::greasepencil::TreeNode &node)
3897{
3898 using namespace blender;
3899 if (!node.parent_group()) {
3900 return;
3901 }
3902 reorder_layer_data(*this, [&]() { node.parent_group()->move_node_top(node); });
3903}
3904void GreasePencil::move_node_bottom(blender::bke::greasepencil::TreeNode &node)
3905{
3906 using namespace blender;
3907 if (!node.parent_group()) {
3908 return;
3909 }
3910 reorder_layer_data(*this, [&]() { node.parent_group()->move_node_bottom(node); });
3911}
3912
3913void GreasePencil::move_node_after(blender::bke::greasepencil::TreeNode &node,
3914 blender::bke::greasepencil::TreeNode &target_node)
3915{
3916 using namespace blender;
3917 if (!target_node.parent_group() || !node.parent_group()) {
3918 return;
3919 }
3920 reorder_layer_data(*this, [&]() {
3921 node.parent_group()->unlink_node(node);
3922 target_node.parent_group()->add_node_after(node, target_node);
3923 });
3924}
3925
3926void GreasePencil::move_node_before(blender::bke::greasepencil::TreeNode &node,
3927 blender::bke::greasepencil::TreeNode &target_node)
3928{
3929 using namespace blender;
3930 if (!target_node.parent_group() || !node.parent_group()) {
3931 return;
3932 }
3933 reorder_layer_data(*this, [&]() {
3934 node.parent_group()->unlink_node(node);
3935 target_node.parent_group()->add_node_before(node, target_node);
3936 });
3937}
3938
3939void GreasePencil::move_node_into(blender::bke::greasepencil::TreeNode &node,
3940 blender::bke::greasepencil::LayerGroup &parent_group)
3941{
3942 using namespace blender;
3943 if (!node.parent_group()) {
3944 return;
3945 }
3946 reorder_layer_data(*this, [&]() {
3947 node.parent_group()->unlink_node(node);
3948 parent_group.add_node(node);
3949 });
3950}
3951
3952const blender::bke::greasepencil::TreeNode *GreasePencil::find_node_by_name(
3953 const blender::StringRef name) const
3954{
3955 return this->root_group().find_node_by_name(name);
3956}
3957
3958blender::bke::greasepencil::TreeNode *GreasePencil::find_node_by_name(
3959 const blender::StringRef name)
3960{
3961 return this->root_group().find_node_by_name(name);
3962}
3963
3964blender::IndexMask GreasePencil::layer_selection_by_name(const blender::StringRef name,
3965 blender::IndexMaskMemory &memory) const
3966{
3967 using namespace blender::bke::greasepencil;
3968 const TreeNode *node = this->find_node_by_name(name);
3969 if (!node) {
3970 return {};
3971 }
3972
3973 if (node->is_layer()) {
3974 const int index = *this->get_layer_index(node->as_layer());
3975 return blender::IndexMask::from_indices(blender::Span<int>{index}, memory);
3976 }
3977 if (node->is_group()) {
3978 blender::Vector<int64_t> layer_indices;
3979 for (const int64_t layer_index : this->layers().index_range()) {
3980 const Layer &layer = *this->layers()[layer_index];
3981 if (layer.is_child_of(node->as_group())) {
3982 layer_indices.append(layer_index);
3983 }
3984 }
3985 return blender::IndexMask::from_indices(layer_indices.as_span(), memory);
3986 }
3987 return {};
3988}
3989
3991{
3992 switch (md->type) {
3994 auto *amd = reinterpret_cast<GreasePencilArmatureModifierData *>(md);
3995 return &amd->influence;
3996 }
3998 auto *mmd = reinterpret_cast<GreasePencilArrayModifierData *>(md);
3999 return &mmd->influence;
4000 }
4002 auto *bmd = reinterpret_cast<GreasePencilBuildModifierData *>(md);
4003 return &bmd->influence;
4004 }
4006 auto *cmd = reinterpret_cast<GreasePencilColorModifierData *>(md);
4007 return &cmd->influence;
4008 }
4010 auto *dmd = reinterpret_cast<GreasePencilDashModifierData *>(md);
4011 return &dmd->influence;
4012 }
4014 auto *emd = reinterpret_cast<GreasePencilEnvelopeModifierData *>(md);
4015 return &emd->influence;
4016 }
4018 auto *hmd = reinterpret_cast<GreasePencilHookModifierData *>(md);
4019 return &hmd->influence;
4020 }
4022 auto *lmd = reinterpret_cast<GreasePencilLatticeModifierData *>(md);
4023 return &lmd->influence;
4024 }
4026 auto *lmd = reinterpret_cast<GreasePencilLengthModifierData *>(md);
4027 return &lmd->influence;
4028 }
4030 auto *mmd = reinterpret_cast<GreasePencilMirrorModifierData *>(md);
4031 return &mmd->influence;
4032 }
4034 auto *mmd = reinterpret_cast<GreasePencilMultiModifierData *>(md);
4035 return &mmd->influence;
4036 }
4038 auto *nmd = reinterpret_cast<GreasePencilNoiseModifierData *>(md);
4039 return &nmd->influence;
4040 }
4042 auto *omd = reinterpret_cast<GreasePencilOffsetModifierData *>(md);
4043 return &omd->influence;
4044 }
4046 auto *omd = reinterpret_cast<GreasePencilOpacityModifierData *>(md);
4047 return &omd->influence;
4048 }
4050 auto *omd = reinterpret_cast<GreasePencilOutlineModifierData *>(md);
4051 return &omd->influence;
4052 }
4054 auto *smd = reinterpret_cast<GreasePencilShrinkwrapModifierData *>(md);
4055 return &smd->influence;
4056 }
4058 auto *smd = reinterpret_cast<GreasePencilSimplifyModifierData *>(md);
4059 return &smd->influence;
4060 }
4062 auto *smd = reinterpret_cast<GreasePencilSmoothModifierData *>(md);
4063 return &smd->influence;
4064 }
4066 auto *smd = reinterpret_cast<GreasePencilSubdivModifierData *>(md);
4067 return &smd->influence;
4068 }
4070 auto *tmd = reinterpret_cast<GreasePencilTextureModifierData *>(md);
4071 return &tmd->influence;
4072 }
4074 auto *tmd = reinterpret_cast<GreasePencilThickModifierData *>(md);
4075 return &tmd->influence;
4076 }
4078 auto *tmd = reinterpret_cast<GreasePencilTimeModifierData *>(md);
4079 return &tmd->influence;
4080 }
4082 auto *tmd = reinterpret_cast<GreasePencilTintModifierData *>(md);
4083 return &tmd->influence;
4084 }
4086 auto *wmd = reinterpret_cast<GreasePencilWeightAngleModifierData *>(md);
4087 return &wmd->influence;
4088 }
4090 auto *wmd = reinterpret_cast<GreasePencilWeightProximityModifierData *>(md);
4091 return &wmd->influence;
4092 }
4095 default:
4096 return nullptr;
4097 }
4098 return nullptr;
4099}
4100
4101void GreasePencil::rename_node(Main &bmain,
4102 blender::bke::greasepencil::TreeNode &node,
4103 const blender::StringRef new_name)
4104{
4105 using namespace blender;
4106 if (node.name() == new_name) {
4107 return;
4108 }
4109
4110 /* Rename the node. */
4111 std::string old_name = node.name();
4112 if (node.is_layer()) {
4113 node.set_name(unique_layer_name(new_name));
4114 }
4115 else if (node.is_group()) {
4116 node.set_name(unique_layer_group_name(*this, new_name));
4117 }
4118
4119 /* Update layer name dependencies. */
4120 if (node.is_layer()) {
4121 BKE_animdata_fix_paths_rename_all(&this->id, "layers", old_name.c_str(), node.name().c_str());
4122 /* Update names in layer masks. */
4123 for (bke::greasepencil::Layer *layer : this->layers_for_write()) {
4125 if (STREQ(mask->layer_name, old_name.c_str())) {
4126 mask->layer_name = BLI_strdup(node.name().c_str());
4127 }
4128 }
4129 }
4130 }
4131
4132 /* Update name dependencies outside of the ID. */
4133 LISTBASE_FOREACH (Object *, object, &bmain.objects) {
4134 if (object->data != this) {
4135 continue;
4136 }
4137
4138 /* Update the layer name of the influence data of the modifiers. */
4139 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
4140 char *dst_layer_name = nullptr;
4141 size_t dst_layer_name_len = 0;
4142 /* LineArt doesn't use the `GreasePencilModifierInfluenceData` struct. */
4143 if (md->type == eModifierType_GreasePencilLineart) {
4144 auto *lmd = reinterpret_cast<GreasePencilLineartModifierData *>(md);
4145 dst_layer_name = lmd->target_layer;
4146 dst_layer_name_len = sizeof(lmd->target_layer);
4147 }
4149 md))
4150 {
4151 dst_layer_name = influence_data->layer_name;
4152 dst_layer_name_len = sizeof(influence_data->layer_name);
4153 }
4154 if (dst_layer_name && STREQ(dst_layer_name, old_name.c_str())) {
4155 BLI_strncpy(dst_layer_name, node.name().c_str(), dst_layer_name_len);
4156 }
4157 }
4158 }
4159}
4160
4161static void shrink_customdata(CustomData &data, const int index_to_remove, const int size)
4162{
4163 using namespace blender;
4164 CustomData new_data;
4166 CustomData_realloc(&new_data, size, size - 1);
4167
4168 const IndexRange range_before(index_to_remove);
4169 const IndexRange range_after(index_to_remove + 1, size - index_to_remove - 1);
4170
4171 if (!range_before.is_empty()) {
4173 &data, &new_data, range_before.start(), range_before.start(), range_before.size());
4174 }
4175 if (!range_after.is_empty()) {
4177 &data, &new_data, range_after.start(), range_after.start() - 1, range_after.size());
4178 }
4179
4181 data = new_data;
4182}
4183
4185 GreasePencil &grease_pencil, const blender::bke::greasepencil::TreeNode &node)
4186{
4187 using namespace blender::bke::greasepencil;
4188 /* 1. Try setting the node below (within the same group) to be active. */
4189 if (node.prev != nullptr) {
4190 grease_pencil.set_active_node(reinterpret_cast<TreeNode *>(node.prev));
4191 }
4192 /* 2. If there is no node below, try setting the node above (within the same group) to be the
4193 * active one. */
4194 else if (node.next != nullptr) {
4195 grease_pencil.set_active_node(reinterpret_cast<TreeNode *>(node.next));
4196 }
4197 /* 3. If this is the only node within its parent group and the parent group is not the root
4198 * group, try setting the parent to be active. */
4199 else if (node.parent != grease_pencil.root_group_ptr) {
4200 grease_pencil.set_active_node(&node.parent->wrap().as_node());
4201 }
4202 /* 4. Otherwise, clear the active node. */
4203 else {
4204 grease_pencil.set_active_node(nullptr);
4205 }
4206}
4207
4208void GreasePencil::remove_layer(blender::bke::greasepencil::Layer &layer)
4209{
4210 using namespace blender::bke::greasepencil;
4211 /* If the layer is active, update the active layer. */
4212 if (&layer.as_node() == this->get_active_node()) {
4214 }
4215
4216 /* Remove all the layer attributes and shrink the `CustomData`. */
4217 const int layer_index = *this->get_layer_index(layer);
4218 shrink_customdata(this->layers_data, layer_index, this->layers().size());
4219
4220 /* Unlink the layer from the parent group. */
4221 layer.parent_group().unlink_node(layer.as_node());
4222
4223 /* Remove drawings. */
4224 for (const GreasePencilFrame frame : layer.frames().values()) {
4225 GreasePencilDrawingBase *drawing_base = this->drawing(frame.drawing_index);
4226 if (drawing_base->type != GP_DRAWING) {
4227 /* TODO: Remove drawing reference. */
4228 continue;
4229 }
4230 GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
4231 drawing->wrap().remove_user();
4232 }
4233 this->remove_drawings_with_no_users();
4234
4235 /* Delete the layer. */
4236 MEM_delete(&layer);
4237}
4238
4239void GreasePencil::remove_group(blender::bke::greasepencil::LayerGroup &group,
4240 const bool keep_children)
4241{
4242 using namespace blender::bke::greasepencil;
4243 /* If the group is active, update the active layer. */
4244 if (&group.as_node() == this->get_active_node()) {
4245 /* If we keep the children and there is at least one child, make it the active node. */
4246 if (keep_children && !group.is_empty()) {
4247 this->set_active_node(reinterpret_cast<TreeNode *>(group.children.last));
4248 }
4249 else {
4251 }
4252 }
4253
4254 if (!keep_children) {
4255 /* Recursively remove groups and layers. */
4257 switch (child->type) {
4258 case GP_LAYER_TREE_LEAF: {
4259 this->remove_layer(reinterpret_cast<GreasePencilLayer *>(child)->wrap());
4260 break;
4261 }
4262 case GP_LAYER_TREE_GROUP: {
4263 this->remove_group(reinterpret_cast<GreasePencilLayerTreeGroup *>(child)->wrap(), false);
4264 break;
4265 }
4266 default:
4268 }
4269 }
4271 }
4272
4273 /* Unlink then delete active group node. */
4274 group.as_node().parent_group()->unlink_node(group.as_node(), true);
4275 MEM_delete(&group);
4276}
4277
4278void GreasePencil::print_layer_tree()
4279{
4280 using namespace blender::bke::greasepencil;
4281 this->root_group().print_nodes("Layer Tree:");
4282}
4283
4284blender::bke::AttributeAccessor GreasePencil::attributes() const
4285{
4286 return blender::bke::AttributeAccessor(
4288}
4289
4290blender::bke::MutableAttributeAccessor GreasePencil::attributes_for_write()
4291{
4292 return blender::bke::MutableAttributeAccessor(
4294}
4295
4297
4298/* ------------------------------------------------------------------- */
4301
4302static void read_drawing_array(GreasePencil &grease_pencil, BlendDataReader *reader)
4303{
4305 grease_pencil.drawing_array_num,
4306 reinterpret_cast<void **>(&grease_pencil.drawing_array));
4307 for (int i = 0; i < grease_pencil.drawing_array_num; i++) {
4308 BLO_read_struct(reader, GreasePencilDrawingBase, &grease_pencil.drawing_array[i]);
4309 GreasePencilDrawingBase *drawing_base = grease_pencil.drawing_array[i];
4310 switch (GreasePencilDrawingType(drawing_base->type)) {
4311 case GP_DRAWING: {
4312 GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
4313 drawing->wrap().strokes_for_write().blend_read(*reader);
4314 /* Initialize runtime data. */
4315 drawing->runtime = MEM_new<blender::bke::greasepencil::DrawingRuntime>(__func__);
4316 break;
4317 }
4318 case GP_DRAWING_REFERENCE: {
4319 break;
4320 }
4321 }
4322 }
4323}
4324
4325static void write_drawing_array(GreasePencil &grease_pencil,
4327 BlendWriter *writer)
4328{
4329 using namespace blender;
4330 BLO_write_pointer_array(writer, grease_pencil.drawing_array_num, grease_pencil.drawing_array);
4331 for (int i = 0; i < grease_pencil.drawing_array_num; i++) {
4332 GreasePencilDrawingBase *drawing_base = grease_pencil.drawing_array[i];
4333 switch (GreasePencilDrawingType(drawing_base->type)) {
4334 case GP_DRAWING: {
4335 GreasePencilDrawing &drawing_copy = scope.construct<GreasePencilDrawing>();
4336 drawing_copy = *reinterpret_cast<GreasePencilDrawing *>(drawing_base);
4337 bke::CurvesGeometry &curves = drawing_copy.geometry.wrap();
4338
4339 bke::CurvesGeometry::BlendWriteData write_data(scope);
4340 curves.blend_write_prepare(write_data);
4341 drawing_copy.runtime = nullptr;
4342
4343 BLO_write_struct_at_address(writer, GreasePencilDrawing, drawing_base, &drawing_copy);
4344 curves.blend_write(*writer, grease_pencil.id, write_data);
4345 break;
4346 }
4347 case GP_DRAWING_REFERENCE: {
4348 GreasePencilDrawingReference *drawing_reference =
4349 reinterpret_cast<GreasePencilDrawingReference *>(drawing_base);
4350 BLO_write_struct(writer, GreasePencilDrawingReference, drawing_reference);
4351 break;
4352 }
4353 }
4354 }
4355}
4356
4357static void free_drawing_array(GreasePencil &grease_pencil)
4358{
4359 grease_pencil.resize_drawings(0);
4360}
4361
4363
4364/* ------------------------------------------------------------------- */
4367
4368static void read_layer(BlendDataReader *reader,
4369 GreasePencilLayer *node,
4371{
4372 BLO_read_string(reader, &node->base.name);
4373 node->base.parent = parent;
4374 BLO_read_string(reader, &node->parsubstr);
4375 BLO_read_string(reader, &node->viewlayername);
4376
4377 /* Read frames storage. */
4381
4382 /* Read layer masks. */
4385 BLO_read_string(reader, &mask->layer_name);
4386 }
4387
4388 /* NOTE: Ideally this should be cleared on write, to reduce false 'changes' detection in memfile
4389 * undo system. This is not easily doable currently though, since modifying to actual data during
4390 * write is not an option (a shallow copy of the #Layer data would be needed then). */
4391 node->runtime = nullptr;
4392 node->wrap().update_from_dna_read();
4393}
4394
4398{
4399 BLO_read_string(reader, &node->base.name);
4400 node->base.parent = parent;
4401 /* Read list of children. */
4404 switch (child->type) {
4405 case GP_LAYER_TREE_LEAF: {
4406 GreasePencilLayer *layer = reinterpret_cast<GreasePencilLayer *>(child);
4407 read_layer(reader, layer, node);
4408 break;
4409 }
4410 case GP_LAYER_TREE_GROUP: {
4411 GreasePencilLayerTreeGroup *group = reinterpret_cast<GreasePencilLayerTreeGroup *>(child);
4412 read_layer_tree_group(reader, group, node);
4413 break;
4414 }
4415 }
4416 }
4417
4418 node->wrap().runtime = MEM_new<blender::bke::greasepencil::LayerGroupRuntime>(__func__);
4419}
4420
4421static void read_layer_tree(GreasePencil &grease_pencil, BlendDataReader *reader)
4422{
4423 /* Read root group. */
4425 /* This shouldn't normally happen, but for files that were created before the root group became a
4426 * pointer, this address will not exist. In this case, we clear the pointer to the active layer
4427 * and create an empty root group to avoid crashes. */
4428 if (grease_pencil.root_group_ptr == nullptr) {
4429 grease_pencil.root_group_ptr = MEM_new<blender::bke::greasepencil::LayerGroup>(__func__);
4430 grease_pencil.set_active_node(nullptr);
4431 return;
4432 }
4433 /* Read active layer. */
4434 BLO_read_struct(reader, GreasePencilLayerTreeNode, &grease_pencil.active_node);
4435 read_layer_tree_group(reader, grease_pencil.root_group_ptr, nullptr);
4436
4437 grease_pencil.root_group_ptr->wrap().update_from_dna_read();
4438}
4439
4440static void write_layer(BlendWriter *writer, GreasePencilLayer *node)
4441{
4442 BLO_write_struct(writer, GreasePencilLayer, node);
4443 BLO_write_string(writer, node->base.name);
4444 BLO_write_string(writer, node->parsubstr);
4445 BLO_write_string(writer, node->viewlayername);
4446
4450
4453 BLO_write_string(writer, mask->layer_name);
4454 }
4455}
4456
4458{
4460 BLO_write_string(writer, node->base.name);
4462 switch (child->type) {
4463 case GP_LAYER_TREE_LEAF: {
4464 GreasePencilLayer *layer = reinterpret_cast<GreasePencilLayer *>(child);
4465 write_layer(writer, layer);
4466 break;
4467 }
4468 case GP_LAYER_TREE_GROUP: {
4469 GreasePencilLayerTreeGroup *group = reinterpret_cast<GreasePencilLayerTreeGroup *>(child);
4470 write_layer_tree_group(writer, group);
4471 break;
4472 }
4473 }
4474 }
4475}
4476
4477static void write_layer_tree(GreasePencil &grease_pencil, BlendWriter *writer)
4478{
4479 grease_pencil.root_group_ptr->wrap().prepare_for_dna_write();
4480 write_layer_tree_group(writer, grease_pencil.root_group_ptr);
4481}
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
void BKE_animdata_free(ID *id, bool do_id_user)
Definition anim_data.cc:187
void BKE_animdata_fix_paths_rename_all(struct ID *ref_id, const char *prefix, const char *oldName, const char *newName)
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
void CustomData_blend_write_prepare(CustomData &data, blender::bke::AttrDomain domain, int domain_size, blender::Vector< CustomDataLayer, 16 > &layers_to_write, blender::bke::AttributeStorage::BlendWriteData &write_data)
void CustomData_realloc(CustomData *data, int old_size, int new_size, eCDAllocType alloctype=CD_CONSTRUCT)
void * CustomData_get_layer_named_for_write(CustomData *data, eCustomDataType type, blender::StringRef name, int totelem)
@ CD_SET_DEFAULT
@ CD_CONSTRUCT
void * CustomData_add_layer_named(CustomData *data, eCustomDataType type, eCDAllocType alloctype, int totelem, blender::StringRef name)
void CustomData_reset(CustomData *data)
void CustomData_blend_write(BlendWriter *writer, CustomData *data, blender::Span< CustomDataLayer > layers_to_write, int count, eCustomDataMask cddata_mask, ID *id)
void CustomData_copy_data_layer(const CustomData *source, CustomData *dest, int src_layer_index, int dst_layer_index, int src_index, int dst_index, int count)
void CustomData_free(CustomData *data)
void CustomData_init_from(const CustomData *source, CustomData *dest, eCustomDataMask mask, int totelem)
void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count)
void CustomData_init_layout_from(const CustomData *source, CustomData *dest, eCustomDataMask mask, eCDAllocType alloctype, int totelem)
void CustomData_copy_data(const CustomData *source, CustomData *dest, int source_index, int dest_index, int count)
support for deformation groups and hooks.
void BKE_defbase_blend_write(BlendWriter *writer, const ListBase *defbase)
Definition deform.cc:1632
void BKE_defgroup_copy_list(ListBase *outbase, const ListBase *inbase)
Definition deform.cc:71
Low-level operations for grease pencil that cannot be defined in the C++ header yet.
void(* BKE_grease_pencil_batch_cache_dirty_tag_cb)(GreasePencil *grease_pencil, int mode)
void BKE_grease_pencil_batch_cache_dirty_tag(GreasePencil *grease_pencil, int mode)
void BKE_grease_pencil_batch_cache_free(GreasePencil *grease_pencil)
void(* BKE_grease_pencil_batch_cache_free_cb)(GreasePencil *grease_pencil)
Low-level operations for grease pencil.
void BKE_grease_pencil_point_coords_apply(GreasePencil &grease_pencil, blender::Span< blender::float3 > all_positions, blender::Span< float > all_radii)
void BKE_grease_pencil_copy_parameters(const GreasePencil &src, GreasePencil &dst)
void BKE_grease_pencil_material_remap(GreasePencil *grease_pencil, const uint *remap, int totcol)
void BKE_grease_pencil_material_index_remove(GreasePencil *grease_pencil, int index)
void BKE_grease_pencil_vgroup_name_update(Object *ob, const char *old_name, const char *new_name)
Material * BKE_grease_pencil_object_material_alt_ensure_from_brush(Main *bmain, Object *ob, Brush *brush)
void BKE_grease_pencil_point_coords_apply_with_mat4(GreasePencil &grease_pencil, blender::Span< blender::float3 > all_positions, blender::Span< float > all_radii, const blender::float4x4 &mat)
void BKE_grease_pencil_point_coords_get(const GreasePencil &grease_pencil, blender::MutableSpan< blender::float3 > all_positions, blender::MutableSpan< float > all_radii)
void BKE_object_eval_grease_pencil(Depsgraph *depsgraph, Scene *scene, Object *object)
bool BKE_grease_pencil_references_cyclic_check(const GreasePencil *id_reference, const GreasePencil *grease_pencil)
Material * BKE_grease_pencil_object_material_new(Main *bmain, Object *ob, const char *name, int *r_index)
GreasePencil * BKE_grease_pencil_add(Main *bmain, const char *name)
Material * BKE_grease_pencil_object_material_ensure_from_brush(Main *bmain, Object *ob, Brush *brush)
void BKE_grease_pencil_nomain_to_grease_pencil(GreasePencil *grease_pencil_src, GreasePencil *grease_pencil_dst)
void BKE_grease_pencil_eval_geometry(Depsgraph *depsgraph, GreasePencil *grease_pencil)
void BKE_grease_pencil_duplicate_drawing_array(const GreasePencil *grease_pencil_src, GreasePencil *grease_pencil_dst)
bool BKE_grease_pencil_has_curve_with_type(const GreasePencil &grease_pencil, CurveType type)
Material * BKE_grease_pencil_object_material_from_brush_get(Object *ob, Brush *brush)
int BKE_grease_pencil_stroke_point_count(const GreasePencil &grease_pencil)
GreasePencil * BKE_grease_pencil_new_nomain()
void BKE_grease_pencil_copy_layer_parameters(const blender::bke::greasepencil::Layer &src, blender::bke::greasepencil::Layer &dst)
void BKE_grease_pencil_copy_layer_group_parameters(const blender::bke::greasepencil::LayerGroup &src, blender::bke::greasepencil::LayerGroup &dst)
bool BKE_grease_pencil_drawing_attribute_required(const GreasePencilDrawing *, blender::StringRef name)
GreasePencil * BKE_grease_pencil_copy_for_eval(const GreasePencil *grease_pencil_src)
bool BKE_grease_pencil_material_index_used(GreasePencil *grease_pencil, int index)
int BKE_grease_pencil_object_material_index_get_by_name(Object *ob, const char *name)
Material * BKE_grease_pencil_object_material_ensure_by_name(Main *bmain, Object *ob, const char *name, int *r_index)
@ IDTYPE_FLAGS_APPEND_IS_REUSABLE
Definition BKE_idtype.hh:44
IDTypeInfo IDType_ID_GP
void BKE_id_free(Main *bmain, void *idv)
@ LIB_ID_COPY_LOCALIZE
ID * BKE_id_copy_ex(Main *bmain, const ID *id, ID **new_id_p, int flag)
Definition lib_id.cc:767
void * BKE_id_new(Main *bmain, short type, const char *name)
Definition lib_id.cc:1495
void id_us_min(ID *id)
Definition lib_id.cc:361
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1500
void BKE_id_blend_write(BlendWriter *writer, ID *id)
Definition lib_id.cc:2611
#define BKE_LIB_FOREACHID_PROCESS_IDSUPER(data_, id_super_, cb_flag_)
@ IDWALK_CB_USER
General operations, lookup, etc. for materials.
short * BKE_object_material_len_p(Object *ob)
short BKE_object_material_slot_find_index(Object *ob, Material *ma)
Material * BKE_gpencil_material_add(Main *bmain, const char *name)
@ BKE_MAT_ASSIGN_USERPREF
bool BKE_object_material_slot_add(Main *bmain, Object *ob, bool set_active=true)
Material * BKE_material_default_gpencil()
Material * BKE_object_material_get(Object *ob, short act)
void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act, int assign_type)
int BKE_object_material_index_get(Object *ob, const Material *ma)
void BKE_modifiers_clear_errors(Object *ob)
bool BKE_modifier_is_enabled(const Scene *scene, ModifierData *md, int required_mode)
const ModifierTypeInfo * BKE_modifier_get_info(ModifierType type)
ModifierData * BKE_modifiers_get_virtual_modifierlist(const Object *ob, VirtualModifierData *data)
ModifierApplyFlag
@ MOD_APPLY_USECACHE
@ MOD_APPLY_RENDER
General operations, lookup, etc. for blender objects.
bool BKE_object_is_in_editmode(const Object *ob)
void BKE_object_eval_assign_data(Object *object, ID *data, bool is_owned)
void BKE_object_free_derived_caches(Object *ob)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define ATTR_FALLTHROUGH
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:332
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void void void bool BLI_listbase_link_move(ListBase *listbase, void *vlink, int step) ATTR_NONNULL()
Definition listbase.cc:436
bool BLI_remlink_safe(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:154
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
void void void void void void BLI_duplicatelist(ListBase *dst, const ListBase *src) ATTR_NONNULL(1
void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:371
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
float mat4_to_scale(const float mat[4][4])
void mul_v2_m3v3(float r[2], const float M[3][3], const float a[3])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void unit_m4(float m[4][4])
MINLINE void add_newell_cross_v3_v3v3(float n[3], const float v_prev[3], const float v_curr[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE void zero_v3(float r[3])
#define BLI_MEMARENA_STD_BUFSIZE
MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void BLI_memarena_free(MemArena *ma) ATTR_NONNULL(1)
void * BLI_memarena_alloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
void BLI_memarena_clear(MemArena *ma) ATTR_NONNULL(1)
ATTR_WARN_UNUSED_RESULT const size_t num
void BLI_polyfill_calc_arena(const float(*coords)[2], unsigned int coords_num, int coords_sign, unsigned int(*r_tris)[3], struct MemArena *arena)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
char * BLI_strdup_null(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_MALLOC
Definition string.cc:46
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
size_t void BLI_uniquename_cb(blender::FunctionRef< bool(blender::StringRefNull)> unique_check, const char *defname, char delim, char *name, size_t name_maxncpy) ATTR_NONNULL(2
unsigned int uint
#define UNUSED_VARS_NDEBUG(...)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define STREQ(a, b)
void BLO_write_int32_array(BlendWriter *writer, int64_t num, const int32_t *data_ptr)
void BLO_read_int32_array(BlendDataReader *reader, int64_t array_size, int32_t **ptr_p)
Definition readfile.cc:5306
#define BLO_write_id_struct(writer, struct_name, id_address, id)
#define BLO_write_struct(writer, struct_name, data_ptr)
void BLO_read_string(BlendDataReader *reader, char **ptr_p)
Definition readfile.cc:5351
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
#define BLO_write_struct_array(writer, struct_name, array_size, data_ptr)
#define BLO_read_struct_list(reader, struct_name, list)
#define BLO_read_struct_array(reader, struct_name, array_size, ptr_p)
#define BLO_write_struct_list(writer, struct_name, list_ptr)
#define BLO_read_struct(reader, struct_name, ptr_p)
void BLO_write_pointer_array(BlendWriter *writer, int64_t num, const void *data_ptr)
void BLO_read_pointer_array(BlendDataReader *reader, int64_t array_size, void **ptr_p)
Definition readfile.cc:5402
#define BLO_write_struct_at_address(writer, struct_name, address, data_ptr)
#define BLT_I18NCONTEXT_ID_GPENCIL
#define DATA_(msgid)
float[3] Vector
void DEG_id_tag_update(ID *id, unsigned int flags)
@ DAG_EVAL_RENDER
float DEG_get_ctime(const Depsgraph *graph)
bool DEG_is_evaluated(const T *id)
eEvaluationMode DEG_get_mode(const Depsgraph *graph)
T * DEG_get_original(T *id)
ID and Library types, which are fundamental for SDNA.
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ INDEX_ID_GP
Definition DNA_ID.h:1245
Enumerations for DNA_ID.h.
@ GP_BRUSH_MATERIAL_PINNED
eBezTriple_KeyframeType
CurveType
@ CURVE_TYPE_BEZIER
@ CURVE_TYPE_POLY
#define DNA_struct_default_get(struct_name)
struct GreasePencilDrawingBase GreasePencilDrawingBase
struct GreasePencilLayerTreeGroup GreasePencilLayerTreeGroup
struct GreasePencilDrawingReference GreasePencilDrawingReference
@ GP_LAYER_BLEND_NONE
struct GreasePencilFrame GreasePencilFrame
@ GP_LAYER_FRAMES_STORAGE_DIRTY
@ LAYERGROUP_COLOR_NONE
struct GreasePencil GreasePencil
GreasePencilLayerTreeNodeType
@ GP_LAYER_TREE_GROUP
@ GP_FRAME_IMPLICIT_HOLD
@ GP_LAYER_TREE_NODE_EXPANDED
@ GP_LAYER_TREE_NODE_USE_LIGHTS
@ GP_LAYER_TREE_NODE_HIDE_MASKS
struct GreasePencilDrawing GreasePencilDrawing
GreasePencilDrawingType
@ GP_DRAWING_REFERENCE
struct GreasePencilLayerTreeNode GreasePencilLayerTreeNode
@ GREASE_PENCIL_AUTOLOCK_LAYERS
struct GreasePencilLayerMask GreasePencilLayerMask
struct GreasePencilLayer GreasePencilLayer
struct GreasePencilModifierInfluenceData GreasePencilModifierInfluenceData
@ eModifierMode_Render
@ eModifierMode_Editmode
@ eModifierMode_Realtime
struct ModifierData ModifierData
struct GreasePencilLineartModifierData GreasePencilLineartModifierData
@ eModifierType_GreasePencilSmooth
@ eModifierType_GreasePencilWeightProximity
@ eModifierType_GreasePencilMirror
@ eModifierType_GreasePencilOffset
@ eModifierType_GreasePencilThickness
@ eModifierType_GreasePencilEnvelope
@ eModifierType_GreasePencilArmature
@ eModifierType_GreasePencilSubdiv
@ eModifierType_GreasePencilTint
@ eModifierType_GreasePencilNoise
@ eModifierType_GreasePencilMultiply
@ eModifierType_GreasePencilBuild
@ eModifierType_GreasePencilTime
@ eModifierType_GreasePencilTexture
@ eModifierType_GreasePencilSimplify
@ eModifierType_GreasePencilColor
@ eModifierType_GreasePencilDash
@ eModifierType_GreasePencilLineart
@ eModifierType_GreasePencilOutline
@ eModifierType_GreasePencilWeightAngle
@ eModifierType_GreasePencilOpacity
@ eModifierType_GreasePencilLength
@ eModifierType_GreasePencilShrinkwrap
@ eModifierType_GreasePencilLattice
@ eModifierType_GreasePencilHook
@ eModifierType_GreasePencilArray
@ OB_MODE_VERTEX_GREASE_PENCIL
@ OB_MODE_EDIT
@ OB_MODE_SCULPT_GREASE_PENCIL
@ OB_MODE_WEIGHT_GREASE_PENCIL
@ OB_ARMATURE
struct Object Object
Read Guarded memory(de)allocation.
static const char * ATTR_POSITION
iter begin(iter)
BMesh const char void * data
BPy_StructRNA * depsgraph
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
IndexRange index_range() const
Definition BLI_array.hh:349
bke::CurvesGeometry & strokes_for_write()
void remove_user() const
void add_user() const
constexpr int64_t size() const
constexpr bool is_empty() const
constexpr int64_t start() const
GreasePencilFrame * add_frame(FramesMapKeyT key, int duration=0)
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
Value pop(const Key &key)
Definition BLI_map.hh:402
bool add_overwrite(const Key &key, const Value &value)
Definition BLI_map.hh:325
ValueIterator values() const &
Definition BLI_map.hh:884
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
void foreach_item(const FuncT &func) const
Definition BLI_map.hh:687
void remove_contained(const Key &key)
Definition BLI_map.hh:387
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
int64_t size() const
Definition BLI_map.hh:976
bool is_empty() const
Definition BLI_map.hh:986
void reserve(int64_t n)
Definition BLI_map.hh:1028
bool contains(const Key &key) const
Definition BLI_map.hh:353
ItemIterator items() const &
Definition BLI_map.hh:902
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr bool is_empty() const
Definition BLI_span.hh:509
constexpr MutableSpan drop_front(const int64_t n) const
Definition BLI_span.hh:607
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
bool add(const Key &key)
Definition BLI_set.hh:248
bool is_group() const
bool is_layer() const
const LayerGroup & as_group() const
const Layer & as_layer() const
int64_t size() const
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
void reserve(const int64_t min_capacity)
Span< T > as_span() const
Definition BLI_array.hh:232
static const CPPType & get()
VArray< ColorGeometry4f > vertex_colors() const
MutableSpan< float > opacities_for_write()
MutableSpan< float > radii_for_write()
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
VArray< float > radii() const
void tag_texture_matrices_changed()
VArray< ColorGeometry4f > fill_colors() const
VArray< float > opacities() const
MutableSpan< ColorGeometry4f > fill_colors_for_write()
MutableSpan< ColorGeometry4f > vertex_colors_for_write()
void set_texture_matrices(Span< float4x2 > matrices, const IndexMask &selection)
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr IndexRange take_back(int64_t n) const
static constexpr IndexRange from_single(const int64_t index)
constexpr IndexRange drop_front(int64_t n) const
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
KeyIterator keys() const &
Definition BLI_map.hh:875
bool contains(const Key &key) const
Definition BLI_map.hh:353
ItemIterator items() const &
Definition BLI_map.hh:902
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr T * data() const
Definition BLI_span.hh:539
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr MutableSpan drop_front(const int64_t n) const
Definition BLI_span.hh:607
constexpr T & first() const
Definition BLI_span.hh:679
T & construct(Args &&...args)
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
constexpr bool contains(const T &value) const
Definition BLI_span.hh:277
bool is_empty() const
Definition BLI_stack.hh:308
void push(const T &value)
Definition BLI_stack.hh:213
constexpr bool is_empty() const
constexpr int64_t size() const
constexpr const char * data() const
std::optional< T > get_if_single() const
Span< T > get_internal_span() const
static VArray ForSingle(T value, const int64_t size)
bool add(const Key &key)
bool contains(const Key &key) const
void append(const T &value)
const T & last(const int64_t n=0) const
void resize(const int64_t new_size)
MutableSpan< T > as_mutable_span()
void reinitialize(const int64_t new_size)
Span< T > as_span() const
bool contains(StringRef attribute_id) const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
std::optional< int > material_index_max() const
void ensure_can_interpolate_to_evaluated() const
MutableAttributeAccessor attributes_for_write()
VArray< float > radius() const
void interpolate_to_evaluated(int curve_index, GSpan src, GMutableSpan dst) const
AttributeAccessor attributes() const
void count_memory(MemoryCounter &memory) const
bool is_single_type(CurveType type) const
std::optional< Bounds< float3 > > bounds_min_max(bool use_radius=true) const
Span< float3 > evaluated_positions() const
std::unique_ptr< GreasePencilEditHints > grease_pencil_edit_hints_
std::optional< MutableSpan< float3 > > positions_for_write()
std::optional< Span< float3 > > positions() const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
Drawing & operator=(const Drawing &other)
Span< float3 > curve_plane_normals() const
Span< float4x2 > texture_matrices() const
MutableSpan< float > radii_for_write()
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
TreeNode & add_node(TreeNode &node)
void move_node_down(TreeNode &node, int step=1)
bool unlink_node(TreeNode &link, bool keep_children=false)
const TreeNode * find_node_by_name(StringRef name) const
Span< const Layer * > layers() const
void add_node_before(TreeNode &node, TreeNode &link)
void add_node_after(TreeNode &node, TreeNode &link)
void move_node_up(TreeNode &node, int step=1)
void print_nodes(StringRef header) const
LayerGroup & operator=(const LayerGroup &other)
Span< const LayerGroup * > groups() const
Span< const TreeNode * > nodes() const
SharedCache< Vector< FramesMapKeyT > > sorted_keys_cache_
Map< FramesMapKeyT, GreasePencilFrame > frames_
SortedKeysIterator sorted_keys_iterator_at(int frame_number) const
StringRefNull parent_bone_name() const
int sorted_keys_index_at(int frame_number) const
float4x4 to_world_space(const Object &object) const
StringRefNull view_layer_name() const
void set_local_transform(const float4x4 &transform)
void set_view_layer_name(StringRef new_name)
bool remove_frame(FramesMapKeyT key)
const Map< FramesMapKeyT, GreasePencilFrame > & frames() const
GreasePencilFrame * add_frame(FramesMapKeyT key, int duration=0)
const GreasePencilFrame * frame_at(const int frame_number) const
bool has_drawing_at(const int frame_number) const
int drawing_index_at(const int frame_number) const
int get_frame_duration_at(const int frame_number) const
std::optional< int > start_frame_at(int frame_number) const
float4x4 to_object_space(const Object &object) const
Span< FramesMapKeyT > sorted_keys() const
const LayerGroup & parent_group() const
Map< FramesMapKeyT, GreasePencilFrame > & frames_for_write()
void set_parent_bone_name(StringRef new_name)
const TreeNode * parent_node() const
const LayerGroup & as_group() const
const LayerGroup * parent_group() const
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
void foreach_segment(Fn &&fn) const
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
#define rot(x, k)
static float normals[][3]
uint pos
#define sin
#define this
#define cos
MatBase< 4, 4 > float4x4
VecBase< float, D > normalize(VecOp< float, D >) RET
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
MatBase< R, C > transpose(MatBase< C, R >) RET
VecBase< float, 3 > cross(VecOp< float, 3 >, VecOp< float, 3 >) RET
float length(VecOp< float, D >) RET
#define FILTER_ID_OB
#define FILTER_ID_MA
#define CD_MASK_ALL
#define MEM_SAFE_FREE(v)
#define FILTER_ID_GP
static void shrink_array(T **array, int *num, const int shrink_num)
static void transform_positions(const Span< blender::float3 > src, const blender::float4x4 &transform, blender::MutableSpan< blender::float3 > dst)
static void read_layer_tree_group(BlendDataReader *reader, GreasePencilLayerTreeGroup *node, GreasePencilLayerTreeGroup *parent)
static void read_drawing_array(GreasePencil &grease_pencil, BlendDataReader *reader)
static void grease_pencil_evaluate_layers(GreasePencil &grease_pencil)
static GreasePencilModifierInfluenceData * influence_data_from_modifier(ModifierData *md)
static void grow_array(T **array, int *num, const int add_num)
static bool grease_pencil_references_cyclic_check_internal(const GreasePencil *id_reference, const GreasePencil *grease_pencil)
static void grease_pencil_free_data(ID *id)
static std::string unique_node_name(const GreasePencil &grease_pencil, const blender::StringRef name)
static void reorder_layer_data(GreasePencil &grease_pencil, const blender::FunctionRef< void()> do_layer_order_changes)
static void free_drawing_array(GreasePencil &grease_pencil)
static void write_layer_tree_group(BlendWriter *writer, GreasePencilLayerTreeGroup *node)
static void update_active_node_from_node_to_remove(GreasePencil &grease_pencil, const blender::bke::greasepencil::TreeNode &node)
static void grease_pencil_do_layer_adjustments(GreasePencil &grease_pencil)
static void shrink_customdata(CustomData &data, const int index_to_remove, const int size)
static void grease_pencil_copy_data(Main *, std::optional< Library * >, ID *id_dst, const ID *id_src, const int)
void BKE_grease_pencil_batch_cache_free(GreasePencil *grease_pencil)
static void reorder_customdata(CustomData &data, const Span< int > new_by_old_map)
static void read_layer_tree(GreasePencil &grease_pencil, BlendDataReader *reader)
static void write_layer(BlendWriter *writer, GreasePencilLayer *node)
static Material * grease_pencil_object_material_ensure_from_brush_pinned(Main *bmain, Object *ob, Brush *brush)
static std::string unique_layer_group_name(const GreasePencil &grease_pencil, blender::StringRef name)
static void write_layer_tree(GreasePencil &grease_pencil, BlendWriter *writer)
static void grease_pencil_set_runtime_visibilities(ID &id_dst, GreasePencil &grease_pencil)
static void delete_drawing(GreasePencilDrawingBase *drawing_base)
static void grease_pencil_blend_read_data(BlendDataReader *reader, ID *id)
static void write_drawing_array(GreasePencil &grease_pencil, blender::ResourceScope &scope, BlendWriter *writer)
static blender::VectorSet< blender::StringRef > get_node_names(const GreasePencil &grease_pencil)
void BKE_grease_pencil_duplicate_drawing_array(const GreasePencil *grease_pencil_src, GreasePencil *grease_pencil_dst)
static void grease_pencil_foreach_id(ID *id, LibraryForeachIDData *data)
static void grease_pencil_blend_write(BlendWriter *writer, ID *id, const void *id_address)
static void grease_pencil_init_data(ID *id)
static void read_layer(BlendDataReader *reader, GreasePencilLayer *node, GreasePencilLayerTreeGroup *parent)
static void grease_pencil_evaluate_modifiers(Depsgraph *depsgraph, Scene *scene, Object *object, blender::bke::GeometrySet &geometry_set)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
#define T
void copy_group_to_group(OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, GSpan src, GMutableSpan dst)
bool prop_is_animated(const AnimData *adt, StringRefNull rna_path, int array_index)
GAttributeReader lookup(const void *owner, const StringRef name)
void ensure_non_empty_layer_names(Main &bmain, GreasePencil &grease_pencil)
static void update_curve_plane_normal_cache(const Span< float3 > positions, const OffsetIndices< int > points_by_curve, const IndexMask &curve_mask, MutableSpan< float3 > normals)
static int domain_num(const CurvesGeometry &curves, const AttrDomain domain)
static CustomData & domain_custom_data(CurvesGeometry &curves, const AttrDomain domain)
constexpr StringRef ATTR_RADIUS
static void update_triangle_cache(const Span< float3 > positions, const Span< float3 > normals, const OffsetIndices< int > points_by_curve, const OffsetIndices< int > triangle_offsets, const IndexMask &curve_mask, MutableSpan< int3 > triangles)
static float4x2 get_local_to_stroke_matrix(const Span< float3 > positions, const float3 normal)
constexpr StringRef ATTR_VERTEX_COLOR
static MutableSpan< T > get_mutable_attribute(CurvesGeometry &curves, const AttrDomain domain, const StringRef name, const T default_value=T())
static float3x2 get_stroke_to_texture_matrix(const float uv_rotation, const float2 uv_translation, const float2 uv_scale)
const AttributeAccessorFunctions & get_attribute_accessor_functions()
constexpr StringRef ATTR_OPACITY
constexpr StringRef ATTR_FILL_COLOR
static float4x3 expand_4x2_mat(float4x2 strokemat)
void copy_drawing_array(Span< const GreasePencilDrawingBase * > src_drawings, MutableSpan< GreasePencilDrawingBase * > dst_drawings)
static MutableSpan< T > get_mutable_attribute(CurvesGeometry &curves, const AttrDomain domain, const StringRef name, const T default_value=T())
auto attribute_filter_from_skip_ref(const Span< StringRef > skip)
void fill_attribute_range_default(MutableAttributeAccessor dst_attributes, AttrDomain domain, const AttributeFilter &attribute_filter, IndexRange range)
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
ID * asset_edit_id_find_local(Main &global_main, ID &id)
void grease_pencil_convert_storage_to_customdata(GreasePencil &grease_pencil)
ID * asset_edit_id_ensure_local(Main &global_main, ID &id)
void attribute_storage_blend_write_prepare(AttributeStorage &data, const Map< AttrDomain, Vector< CustomDataLayer, 16 > * > &layers_to_write, AttributeStorage::BlendWriteData &write_data)
Bounds< T > merge(const Bounds< T > &a, const Bounds< T > &b)
Definition BLI_bounds.hh:26
std::optional< Bounds< T > > min_max_with_radii(const Span< T > values, const Span< RadiusT > radii)
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:55
T length_squared(const VecBase< T, Size > &a)
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
T safe_rcp(const T &a)
T sign(const T &a)
T distance(const T &a, const T &b)
T length(const VecBase< T, Size > &a)
QuaternionBase< T > normalize_and_get_length(const QuaternionBase< T > &q, T &out_length)
EulerXYZBase< float > EulerXYZ
bool is_zero(const T &a)
CartesianBasis invert(const CartesianBasis &basis)
MatT from_scale(const VecBase< typename MatT::base_type, ScaleDim > &scale)
T atan2(const T &y, const T &x)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
void to_loc_rot_scale_safe(const MatBase< T, 4, 4 > &mat, VecBase< T, 3 > &r_location, RotationT &r_rotation, VecBase< T, 3 > &r_scale)
T determinant(const MatBase< T, Size, Size > &mat)
MatT from_loc_rot_scale(const typename MatT::loc_type &location, const RotationT &rotation, const VecBase< typename MatT::base_type, ScaleDim > &scale)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void parallel_for_each(Range &&range, const Function &function)
Definition BLI_task.hh:56
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
MatBase< double, 4, 2 > double4x2
MatBase< float, 2, 2 > float2x2
MatBase< float, 4, 4 > float4x4
MatBase< float, 2, 4 > float2x4
VecBase< float, 4 > float4
MatBase< double, 3, 4 > double3x4
MatView< float, 4, 4, 4, 4, 0, 0, alignof(float)> float4x4_view
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
MatBase< float, 4, 2 > float4x2
MatBase< float, 3, 2 > float3x2
MatBase< float, 4, 3 > float4x3
MatBase< float, 3, 3 > float3x3
void uninitialized_relocate_n(T *src, int64_t n, T *dst)
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:342
VecBase< float, 3 > float3
MatBase< double, 4, 3 > double4x3
void uninitialized_move_n(T *src, int64_t n, T *dst)
float wrap(float value, float max, float min)
Definition node_math.h:71
static void unique_name(bNode *node)
PropertyRNA * RNA_struct_type_find_property(StructRNA *srna, const char *identifier)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
std::optional< std::string > RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop)
Definition rna_path.cc:1173
void pad(const PaddingT &padding)
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
static GeometrySet from_grease_pencil(GreasePencil *grease_pencil, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
const GreasePencil * get_grease_pencil() const
bool has_grease_pencil() const
GreasePencil * get_grease_pencil_for_write()
void replace_grease_pencil(GreasePencil *grease_pencil, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilDrawingBase base
GreasePencilDrawingRuntimeHandle * runtime
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilLayerTreeNode base
GreasePencilLayerGroupRuntimeHandle * runtime
struct GreasePencilLayerTreeNode * next
struct GreasePencilLayerTreeNode * prev
struct GreasePencilLayerTreeGroup * parent
GreasePencilLayerRuntimeHandle * runtime
GreasePencilLayerTreeNode base
GreasePencilLayerFramesMapStorage frames_storage
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilModifierInfluenceData influence
GreasePencilLayerTreeNode * active_node
struct Material ** material_array
GreasePencilLayerTreeGroup * root_group_ptr
GreasePencilRuntimeHandle * runtime
GreasePencilDrawingBase ** drawing_array
GreasePencilOnionSkinningSettings onion_skinning_settings
struct AnimData * adt
struct AttributeStorage attribute_storage
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
void * last
void * first
ListBase objects
Definition BKE_main.hh:247
struct ModifierData * next
void(* modify_geometry_set)(ModifierData *md, const ModifierEvalContext *ctx, blender::bke::GeometrySet *geometry_set)
struct bPose * pose
const c_style_mat & ptr() const
i
Definition text_draw.cc:230
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4226
uint8_t flag
Definition wm_window.cc:139