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