Blender V5.0
grease_pencil_layers.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 "BLI_listbase.h"
10#include "BLI_math_matrix.h"
11#include "BLI_math_matrix.hh"
12#include "BLI_string.h"
13
14#include "BKE_attribute_math.hh"
15#include "BKE_context.hh"
16#include "BKE_grease_pencil.hh"
17#include "BKE_object.hh"
18#include "BKE_report.hh"
19
20#include "BLT_translation.hh"
21
22#include "DEG_depsgraph.hh"
23
24#include "ED_grease_pencil.hh"
25
26#include "RNA_access.hh"
27#include "RNA_define.hh"
28
29#include "UI_interface.hh"
30
31#include "DNA_scene_types.h"
32
33#include "WM_api.hh"
34#include "WM_message.hh"
35
37
38/* This utility function is modified from `BKE_object_get_parent_matrix()`. */
39static float4x4 get_bone_mat(const Object *parent, const char *parsubstr)
40{
41 if (parent->type != OB_ARMATURE) {
42 return float4x4::identity();
43 }
44
45 const bPoseChannel *pchan = BKE_pose_channel_find_name(parent->pose, parsubstr);
46 if (!pchan || !pchan->bone) {
47 return float4x4::identity();
48 }
49
50 if (pchan->bone->flag & BONE_RELATIVE_PARENTING) {
51 return float4x4(pchan->chan_mat);
52 }
53 return float4x4(pchan->pose_mat);
54}
55
57 Object *parent,
58 StringRefNull bone,
59 const bool keep_transform)
60{
61 if (keep_transform) {
62 /* TODO apply current transform to geometry. */
63 }
64
65 layer.parent = parent;
66 layer.parsubstr = BLI_strdup_null(bone.c_str());
67 /* Calculate inverse parent matrix. */
68 if (parent) {
69 float4x4 inverse = parent->world_to_object();
70 if (layer.parsubstr) {
71 const float4x4 bone_mat = get_bone_mat(parent, layer.parsubstr);
72 inverse = math::invert(bone_mat) * inverse;
73 }
74 copy_m4_m4(layer.parentinv, inverse.ptr());
75 }
76 else {
77 unit_m4(layer.parentinv);
78 }
79
80 return true;
81}
82
83void grease_pencil_layer_parent_clear(bke::greasepencil::Layer &layer, const bool keep_transform)
84{
85 if (layer.parent == nullptr) {
86 return;
87 }
88 if (keep_transform) {
89 /* TODO apply current transform to geometry. */
90 }
91
92 layer.parent = nullptr;
94
96}
97
99{
100 using namespace blender::bke::greasepencil;
101
102 if (layer != nullptr) {
103 layer->set_selected(true);
104 }
105
106 if (grease_pencil.get_active_layer() != layer) {
107 grease_pencil.set_active_layer(layer);
109 }
110}
111
113{
114 using namespace blender::bke::greasepencil;
115 Scene *scene = CTX_data_scene(C);
117
118 std::string new_layer_name = RNA_string_get(op->ptr, "new_layer_name");
119 Layer &new_layer = grease_pencil.add_layer(new_layer_name);
121 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencil, layers);
122
123 if (grease_pencil.has_active_layer()) {
124 grease_pencil.move_node_after(new_layer.as_node(),
125 grease_pencil.get_active_layer()->as_node());
126 }
127 else if (grease_pencil.has_active_group()) {
128 grease_pencil.move_node_into(new_layer.as_node(), *grease_pencil.get_active_group());
130 &grease_pencil.id,
131 &grease_pencil,
132 GreasePencilv3LayerGroup,
133 active);
134 }
135
136 grease_pencil.set_active_layer(&new_layer);
138 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3Layers, active);
139
140 grease_pencil.insert_frame(new_layer, scene->r.cfra);
141
142 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
144
145 return OPERATOR_FINISHED;
146}
147
149 wmOperator *op,
150 const wmEvent *event)
151{
153 op,
154 event,
155 IFACE_("Add New Grease Pencil Layer"),
157}
158
160{
161 /* identifiers */
162 ot->name = "Add New Layer";
163 ot->idname = "GREASE_PENCIL_OT_layer_add";
164 ot->description = "Add a new Grease Pencil layer in the active object";
165
166 /* callbacks */
170
172
174 ot->srna, "new_layer_name", "Layer", INT16_MAX, "Name", "Name of the new layer");
176 ot->prop = prop;
177}
178
180{
181 using namespace blender::bke::greasepencil;
183
184 if (!grease_pencil.has_active_layer()) {
185 return OPERATOR_CANCELLED;
186 }
187
188 grease_pencil.remove_layer(*grease_pencil.get_active_layer());
189
191 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3Layers, active);
193 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencil, layers);
194
195 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
197
198 return OPERATOR_FINISHED;
199}
200
202{
203 /* identifiers */
204 ot->name = "Remove Layer";
205 ot->idname = "GREASE_PENCIL_OT_layer_remove";
206 ot->description = "Remove the active Grease Pencil layer";
207
208 /* callbacks */
211
213}
214
215enum class LayerMoveDirection : int8_t { Up = -1, Down = 1 };
216
218 {int(LayerMoveDirection::Up), "UP", 0, "Up", ""},
219 {int(LayerMoveDirection::Down), "DOWN", 0, "Down", ""},
220 {0, nullptr, 0, nullptr, nullptr},
221};
222
224{
225 using namespace blender::bke::greasepencil;
227 return false;
228 }
229
231 const TreeNode *active_node = grease_pencil.get_active_node();
232
233 if (active_node == nullptr) {
234 return false;
235 }
236
237 const LayerGroup *parent = active_node->parent_group();
238
239 if (parent == nullptr || parent->num_direct_nodes() < 2) {
240 return false;
241 }
242
243 return true;
244}
245
247{
248 using namespace blender::bke::greasepencil;
250
251 const LayerMoveDirection direction = LayerMoveDirection(RNA_enum_get(op->ptr, "direction"));
252
253 TreeNode &active_node = *grease_pencil.get_active_node();
254
255 if (direction == LayerMoveDirection::Up) {
256 grease_pencil.move_node_up(active_node);
257 }
258 else if (direction == LayerMoveDirection::Down) {
259 grease_pencil.move_node_down(active_node);
260 }
261
262 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
263 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
264
266 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencil, layers);
267
268 return OPERATOR_FINISHED;
269}
270
272{
273 /* identifiers */
274 ot->name = "Reorder Layer";
275 ot->idname = "GREASE_PENCIL_OT_layer_move";
276 ot->description = "Move the active Grease Pencil layer or Group";
277
278 /* callbacks */
281
283
284 ot->prop = RNA_def_enum(ot->srna, "direction", enum_layer_move_direction, 0, "Direction", "");
285}
286
288{
289 using namespace blender::bke::greasepencil;
291 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
292 int layer_index = RNA_int_get(op->ptr, "layer");
293
294 if (!grease_pencil.layers().index_range().contains(layer_index)) {
295 return OPERATOR_CANCELLED;
296 }
297
298 Layer &layer = grease_pencil.layer(layer_index);
299 if (grease_pencil.is_layer_active(&layer)) {
300 return OPERATOR_CANCELLED;
301 }
302
303 if (grease_pencil.has_active_group()) {
305 &grease_pencil.id,
306 &grease_pencil,
307 GreasePencilv3LayerGroup,
308 active);
309 }
310 grease_pencil.set_active_layer(&layer);
311
313 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3Layers, active);
314
316
317 return OPERATOR_FINISHED;
318}
319
321{
322 /* identifiers */
323 ot->name = "Set Active Layer";
324 ot->idname = "GREASE_PENCIL_OT_layer_active";
325 ot->description = "Set the active Grease Pencil layer";
326
327 /* callbacks */
330
332
333 PropertyRNA *prop = RNA_def_int(
334 ot->srna, "layer", 0, 0, INT_MAX, "Grease Pencil Layer", "", 0, INT_MAX);
336}
337
339{
340 using namespace blender::bke::greasepencil;
342
343 std::string new_layer_group_name = RNA_string_get(op->ptr, "new_layer_group_name");
344
345 LayerGroup &new_group = grease_pencil.add_layer_group(new_layer_group_name);
347 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencil, layer_groups);
348
349 if (grease_pencil.has_active_layer()) {
350 grease_pencil.move_node_after(new_group.as_node(),
351 grease_pencil.get_active_layer()->as_node());
353 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3Layers, active);
354 }
355 else if (grease_pencil.has_active_group()) {
356 grease_pencil.move_node_into(new_group.as_node(), *grease_pencil.get_active_group());
358 &grease_pencil.id,
359 &grease_pencil,
360 GreasePencilv3LayerGroup,
361 active);
362 }
363
364 grease_pencil.set_active_node(&new_group.as_node());
365
367 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3LayerGroup, active);
368
369 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
370 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
372
373 return OPERATOR_FINISHED;
374}
375
377{
378 /* identifiers */
379 ot->name = "Add New Layer Group";
380 ot->idname = "GREASE_PENCIL_OT_layer_group_add";
381 ot->description = "Add a new Grease Pencil layer group in the active object";
382
383 /* callbacks */
386
388
390 ot->srna, "new_layer_group_name", nullptr, INT16_MAX, "Name", "Name of the new layer group");
392 ot->prop = prop;
393}
394
396{
397 using namespace blender::bke::greasepencil;
398 const bool keep_children = RNA_boolean_get(op->ptr, "keep_children");
400
401 if (!grease_pencil.has_active_group()) {
402 return OPERATOR_CANCELLED;
403 }
404
405 grease_pencil.remove_group(*grease_pencil.get_active_group(), keep_children);
406
407 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
409
411 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3LayerGroup, active);
413 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencil, layer_groups);
414
415 return OPERATOR_FINISHED;
416}
417
419{
420 /* identifiers */
421 ot->name = "Remove Layer Group";
422 ot->idname = "GREASE_PENCIL_OT_layer_group_remove";
423 ot->description = "Remove Grease Pencil layer group in the active object";
424
425 /* callbacks */
428
430
431 RNA_def_boolean(ot->srna,
432 "keep_children",
433 false,
434 "Keep children nodes",
435 "Keep the children nodes of the group and only delete the group itself");
436}
437
439{
440 using namespace blender::bke::greasepencil;
442 const bool unselected = RNA_boolean_get(op->ptr, "unselected");
443
444 TreeNode *active_node = grease_pencil.get_active_node();
445
446 if (!active_node) {
447 return OPERATOR_CANCELLED;
448 }
449
450 if (unselected) {
451 /* If active node is a layer group, only show parent layer groups and child nodes.
452 * If active node is a layer, only show parent layer groups and active node. */
453
454 for (TreeNode *node : grease_pencil.nodes_for_write()) {
455 bool should_be_visible = false;
456
457 if (active_node->is_group()) {
458 should_be_visible = node->is_child_of(active_node->as_group());
459 if (node->is_group()) {
460 should_be_visible |= active_node->is_child_of(node->as_group());
461 }
462 }
463 else if (node->is_group()) {
464 should_be_visible = active_node->is_child_of(node->as_group());
465 }
466
467 node->set_visible(should_be_visible);
468 }
469 active_node->set_visible(true);
470 }
471 else {
472 /* hide selected/active */
473 active_node->set_visible(false);
474 }
475
476 /* notifiers */
477 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
478 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
480
481 return OPERATOR_FINISHED;
482}
483
485{
486 /* identifiers */
487 ot->name = "Hide Layer(s)";
488 ot->idname = "GREASE_PENCIL_OT_layer_hide";
489 ot->description = "Hide selected/unselected Grease Pencil layers";
490
491 /* callbacks */
494
495 /* flags */
497
498 /* props */
500 ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected layers");
502 ot->prop = prop;
503}
504
506{
507 using namespace blender::bke::greasepencil;
509
510 if (!grease_pencil.get_active_node()) {
511 return OPERATOR_CANCELLED;
512 }
513
514 for (TreeNode *node : grease_pencil.nodes_for_write()) {
515 node->set_visible(true);
516 }
517
518 /* notifiers */
519 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
520 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
522
523 return OPERATOR_FINISHED;
524}
525
527{
528 /* identifiers */
529 ot->name = "Show All Layers";
530 ot->idname = "GREASE_PENCIL_OT_layer_reveal";
531 ot->description = "Show all Grease Pencil layers";
532
533 /* callbacks */
536
537 /* flags */
539}
540
542{
543 using namespace blender::bke::greasepencil;
545 const int affect_visibility = RNA_boolean_get(op->ptr, "affect_visibility");
546 bool isolate = false;
547
548 for (const Layer *layer : grease_pencil.layers()) {
549 if (grease_pencil.is_layer_active(layer)) {
550 continue;
551 }
552 if ((affect_visibility && layer->is_visible()) || !layer->is_locked()) {
553 isolate = true;
554 break;
555 }
556 }
557
558 for (Layer *layer : grease_pencil.layers_for_write()) {
559 if (grease_pencil.is_layer_active(layer) || !isolate) {
560 layer->set_locked(false);
561 if (affect_visibility) {
562 layer->set_visible(true);
563 }
564 }
565 else {
566 layer->set_locked(true);
567 if (affect_visibility) {
568 layer->set_visible(false);
569 }
570 }
571 }
572
573 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
574 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
576
577 return OPERATOR_FINISHED;
578}
579
581{
582 /* identifiers */
583 ot->name = "Isolate Layers";
584 ot->idname = "GREASE_PENCIL_OT_layer_isolate";
585 ot->description = "Make only active layer visible/editable";
586
587 /* callbacks */
590
591 /* flags */
593
594 /* properties */
596 ot->srna, "affect_visibility", false, "Affect Visibility", "Also affect the visibility");
597}
598
600{
601 using namespace blender::bke::greasepencil;
603 const bool lock_value = RNA_boolean_get(op->ptr, "lock");
604
605 if (grease_pencil.nodes().is_empty()) {
606 return OPERATOR_CANCELLED;
607 }
608
609 for (TreeNode *node : grease_pencil.nodes_for_write()) {
610 node->set_locked(lock_value);
611 }
612
613 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
614 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
616
617 return OPERATOR_FINISHED;
618}
619
621{
622 /* identifiers */
623 ot->name = "Lock All Layers";
624 ot->idname = "GREASE_PENCIL_OT_layer_lock_all";
625 ot->description =
626 "Lock all Grease Pencil layers to prevent them from being accidentally modified";
627
628 /* callbacks */
631
632 /* flags */
634
635 /* properties */
636 RNA_def_boolean(ot->srna, "lock", true, "Lock Value", "Lock/Unlock all layers");
637}
638
640{
641 using namespace blender::bke::greasepencil;
643 const bool empty_keyframes = RNA_boolean_get(op->ptr, "empty_keyframes");
644
645 if (!grease_pencil.has_active_layer()) {
646 BKE_reportf(op->reports, RPT_ERROR, "No active layer to duplicate");
647 return OPERATOR_CANCELLED;
648 }
649
650 /* Duplicate layer. */
651 Layer &active_layer = *grease_pencil.get_active_layer();
652 const bool duplicate_frames = true;
653 const bool duplicate_drawings = !empty_keyframes;
654 Layer &new_layer = grease_pencil.duplicate_layer(
655 active_layer, duplicate_frames, duplicate_drawings);
656
658 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencil, layers);
659
660 grease_pencil.move_node_after(new_layer.as_node(), active_layer.as_node());
661 grease_pencil.set_active_layer(&new_layer);
662
663 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
665
667 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3Layers, active);
668
669 return OPERATOR_FINISHED;
670}
671
673{
674 /* identifiers */
675 ot->name = "Duplicate Layer";
676 ot->idname = "GREASE_PENCIL_OT_layer_duplicate";
677 ot->description = "Make a copy of the active Grease Pencil layer";
678
679 /* callbacks */
682
683 /* flags */
685
686 /* properties */
687 RNA_def_boolean(ot->srna, "empty_keyframes", false, "Empty Keyframes", "Add Empty Keyframes");
688}
689
690enum class MergeMode : int8_t {
691 Down = 0,
692 Group = 1,
693 All = 2,
694};
695
697{
698 using namespace blender::bke::greasepencil;
699 Main *bmain = CTX_data_main(C);
701 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
702 const MergeMode mode = MergeMode(RNA_enum_get(op->ptr, "mode"));
703
704 Vector<Vector<int>> src_layer_indices_by_dst_layer;
705 std::string merged_layer_name;
706 if (mode == MergeMode::Down) {
707 if (!grease_pencil.has_active_layer()) {
708 BKE_report(op->reports, RPT_ERROR, "No active layer");
709 return OPERATOR_CANCELLED;
710 }
711 const Layer &active_layer = *grease_pencil.get_active_layer();
712 GreasePencilLayerTreeNode *prev_node = active_layer.as_node().prev;
713 if (prev_node == nullptr || !prev_node->wrap().is_layer()) {
714 /* No layer below the active one. */
715 return OPERATOR_CANCELLED;
716 }
717 const Layer &prev_layer = prev_node->wrap().as_layer();
718 /* Get the indices of the two layers to be merged. */
719 const int prev_layer_index = *grease_pencil.get_layer_index(prev_layer);
720 const int active_layer_index = *grease_pencil.get_layer_index(active_layer);
721
722 /* Map all the other layers to their own index. */
723 const Span<const Layer *> layers = grease_pencil.layers();
724 for (const int layer_i : layers.index_range()) {
725 if (layer_i == active_layer_index) {
726 /* Active layer is merged into previous, skip. */
727 }
728 else if (layer_i == prev_layer_index) {
729 /* Previous layer merges itself and the active layer. */
730 src_layer_indices_by_dst_layer.append({prev_layer_index, active_layer_index});
731 }
732 else {
733 /* Other layers remain unchanged. */
734 src_layer_indices_by_dst_layer.append({layer_i});
735 }
736 }
737
738 /* Store the name of the current active layer as the name of the merged layer. */
739 merged_layer_name = grease_pencil.layer(prev_layer_index).name();
740 }
741 else if (mode == MergeMode::Group) {
742 if (!grease_pencil.has_active_group()) {
743 BKE_report(op->reports, RPT_ERROR, "No active group");
744 return OPERATOR_CANCELLED;
745 }
746 LayerGroup &active_group = *grease_pencil.get_active_group();
747
748 if (active_group.layers().is_empty()) {
749 BKE_report(op->reports, RPT_INFO, "No child layers to merge");
750 return OPERATOR_CANCELLED;
751 }
752
753 /* Remove all sub groups of the active group since they won't be needed anymore, but keep the
754 * layers. */
755 Array<LayerGroup *> groups = active_group.groups_for_write();
756 for (LayerGroup *group : groups) {
757 grease_pencil.remove_group(*group, true);
758 }
759
760 const Span<const Layer *> layers = grease_pencil.layers();
762 for (const int layer_i : layers.index_range()) {
763 const Layer &layer = grease_pencil.layer(layer_i);
764 if (!layer.is_child_of(active_group)) {
765 src_layer_indices_by_dst_layer.append({layer_i});
766 }
767 else {
768 indices.append(layer_i);
769 }
770 }
771 src_layer_indices_by_dst_layer.append(indices);
772
773 /* Store the name of the group as the name of the merged layer. */
774 merged_layer_name = active_group.name();
775
776 /* Remove the active group. */
777 grease_pencil.remove_group(active_group, true);
779 &grease_pencil.id,
780 &grease_pencil,
781 GreasePencilv3LayerGroup,
782 active);
783
784 /* Rename the first node so that the merged layer will have the name of the group. */
785 grease_pencil.rename_node(
786 *bmain, grease_pencil.layer(indices[0]).as_node(), merged_layer_name);
787 }
788 else if (mode == MergeMode::All) {
789 if (grease_pencil.layers().is_empty()) {
790 return OPERATOR_CANCELLED;
791 }
792 /* Remove all groups, keep the layers. */
793 Array<LayerGroup *> groups = grease_pencil.layer_groups_for_write();
794 for (LayerGroup *group : groups) {
795 grease_pencil.remove_group(*group, true);
796 }
797
799 for (const int layer_i : grease_pencil.layers().index_range()) {
800 indices.append(layer_i);
801 }
802 src_layer_indices_by_dst_layer.append(indices);
803
804 merged_layer_name = N_("Layer");
805 grease_pencil.rename_node(
806 *bmain, grease_pencil.layer(indices[0]).as_node(), merged_layer_name);
807 }
808 else {
810 }
811
812 GreasePencil *merged_grease_pencil = BKE_grease_pencil_new_nomain();
813 BKE_grease_pencil_copy_parameters(grease_pencil, *merged_grease_pencil);
815 grease_pencil, src_layer_indices_by_dst_layer, *merged_grease_pencil);
816 BKE_grease_pencil_nomain_to_grease_pencil(merged_grease_pencil, &grease_pencil);
817
819 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencil, layers);
820
821 /* Try to set the active (merged) layer. */
822 TreeNode *node = grease_pencil.find_node_by_name(merged_layer_name);
823 if (node && node->is_layer()) {
824 Layer &layer = node->as_layer();
825 grease_pencil.set_active_layer(&layer);
826
828 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3Layers, active);
829 }
830
831 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
833
834 return OPERATOR_FINISHED;
835}
836
838{
839 static const EnumPropertyItem merge_modes[] = {
840 {int(MergeMode::Down),
841 "ACTIVE",
842 0,
843 "Active",
844 "Combine the active layer with the layer just below (if it exists)"},
845 {int(MergeMode::Group),
846 "GROUP",
847 0,
848 "Group",
849 "Combine layers in the active group into a single layer"},
850 {int(MergeMode::All), "ALL", 0, "All", "Combine all layers into a single layer"},
851 {0, nullptr, 0, nullptr, nullptr},
852 };
853
854 ot->name = "Merge";
855 ot->idname = "GREASE_PENCIL_OT_layer_merge";
856 ot->description = "Combine layers based on the mode into one layer";
857
860
862
863 ot->prop = RNA_def_enum(ot->srna, "mode", merge_modes, int(MergeMode::Down), "Mode", "");
864}
865
867{
868 using namespace blender::bke::greasepencil;
870
871 if (!grease_pencil.has_active_layer()) {
872 return OPERATOR_CANCELLED;
873 }
874 Layer &active_layer = *grease_pencil.get_active_layer();
875
876 std::string mask_name = RNA_string_get(op->ptr, "name");
877
878 if (TreeNode *node = grease_pencil.find_node_by_name(mask_name)) {
879 if (grease_pencil.is_layer_active(&node->as_layer())) {
880 BKE_report(op->reports, RPT_ERROR, "Cannot add active layer as mask");
881 return OPERATOR_CANCELLED;
882 }
883
884 if (BLI_findstring_ptr(&active_layer.masks,
885 mask_name.c_str(),
886 offsetof(GreasePencilLayerMask, layer_name)) != nullptr)
887 {
888 BKE_report(op->reports, RPT_ERROR, "Layer already added");
889 return OPERATOR_CANCELLED;
890 }
891
892 LayerMask *new_mask = MEM_new<LayerMask>(__func__, mask_name);
893 BLI_addtail(&active_layer.masks, reinterpret_cast<GreasePencilLayerMask *>(new_mask));
894 /* Make the newly added mask active. */
895 active_layer.active_mask_index = BLI_listbase_count(&active_layer.masks) - 1;
896 }
897 else {
898 BKE_report(op->reports, RPT_ERROR, "Unable to find layer to add");
899 return OPERATOR_CANCELLED;
900 }
901
902 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
904
905 return OPERATOR_FINISHED;
906}
907
909{
910 /* identifiers */
911 ot->name = "Add New Mask Layer";
912 ot->idname = "GREASE_PENCIL_OT_layer_mask_add";
913 ot->description = "Add new layer as masking";
914
916
917 /* callbacks */
920
921 /* properties */
922 RNA_def_string(ot->srna, "name", nullptr, 0, "Layer", "Name of the layer");
923}
924
926{
927 using namespace blender::bke::greasepencil;
929 return false;
930 }
931
933 Layer &active_layer = *grease_pencil.get_active_layer();
934
935 return !BLI_listbase_is_empty(&active_layer.masks);
936}
937
939{
940 using namespace blender::bke::greasepencil;
942
943 if (!grease_pencil.has_active_layer()) {
944 return OPERATOR_CANCELLED;
945 }
946
947 Layer &active_layer = *grease_pencil.get_active_layer();
948 if (GreasePencilLayerMask *mask = reinterpret_cast<GreasePencilLayerMask *>(
949 BLI_findlink(&active_layer.masks, active_layer.active_mask_index)))
950 {
951 BLI_remlink(&active_layer.masks, mask);
952 MEM_delete(reinterpret_cast<LayerMask *>(mask));
953 active_layer.active_mask_index = std::max(active_layer.active_mask_index - 1, 0);
954 }
955 else {
956 return OPERATOR_CANCELLED;
957 }
958
959 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
961
962 return OPERATOR_FINISHED;
963}
964
966{
967 /* identifiers */
968 ot->name = "Remove Mask Layer";
969 ot->idname = "GREASE_PENCIL_OT_layer_mask_remove";
970 ot->description = "Remove Layer Mask";
971
973
974 /* callbacks */
977}
978
980{
981 using namespace blender::bke::greasepencil;
983 return false;
984 }
985
987 Layer &active_layer = *grease_pencil.get_active_layer();
988
989 return BLI_listbase_count(&active_layer.masks) > 1;
990}
991
993{
994 using namespace blender::bke::greasepencil;
996
997 if (!grease_pencil.has_active_layer()) {
998 return OPERATOR_CANCELLED;
999 }
1000 Layer &active_layer = *grease_pencil.get_active_layer();
1001 const int direction = RNA_enum_get(op->ptr, "direction");
1002
1003 bool changed = false;
1004 if (GreasePencilLayerMask *mask = reinterpret_cast<GreasePencilLayerMask *>(
1005 BLI_findlink(&active_layer.masks, active_layer.active_mask_index)))
1006 {
1007 if (BLI_listbase_link_move(&active_layer.masks, mask, direction)) {
1008 active_layer.active_mask_index = std::max(active_layer.active_mask_index + direction, 0);
1009 changed = true;
1010 }
1011 }
1012 else {
1013 return OPERATOR_CANCELLED;
1014 }
1015
1016 if (changed) {
1017 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1019 }
1020
1021 return OPERATOR_FINISHED;
1022}
1023
1025{
1026 /* identifiers */
1027 ot->name = "Reorder Grease Pencil Layer Mask";
1028 ot->idname = "GREASE_PENCIL_OT_layer_mask_reorder";
1029 ot->description = "Reorder the active Grease Pencil mask layer up/down in the list";
1030
1031 /* API callbacks. */
1034
1035 /* flags */
1036 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1037
1038 ot->prop = RNA_def_enum(ot->srna, "direction", enum_layer_move_direction, 0, "Direction", "");
1039}
1040
1042 {LAYERGROUP_COLOR_NONE, "NONE", ICON_X, "Set Default icon", ""},
1043 {LAYERGROUP_COLOR_01, "COLOR1", ICON_LAYERGROUP_COLOR_01, "Color tag 1", ""},
1044 {LAYERGROUP_COLOR_02, "COLOR2", ICON_LAYERGROUP_COLOR_02, "Color tag 2", ""},
1045 {LAYERGROUP_COLOR_03, "COLOR3", ICON_LAYERGROUP_COLOR_03, "Color tag 3", ""},
1046 {LAYERGROUP_COLOR_04, "COLOR4", ICON_LAYERGROUP_COLOR_04, "Color tag 4", ""},
1047 {LAYERGROUP_COLOR_05, "COLOR5", ICON_LAYERGROUP_COLOR_05, "Color tag 5", ""},
1048 {LAYERGROUP_COLOR_06, "COLOR6", ICON_LAYERGROUP_COLOR_06, "Color tag 6", ""},
1049 {LAYERGROUP_COLOR_07, "COLOR7", ICON_LAYERGROUP_COLOR_07, "Color tag 7", ""},
1050 {LAYERGROUP_COLOR_08, "COLOR8", ICON_LAYERGROUP_COLOR_08, "Color tag 8", ""},
1051 {0, nullptr, 0, nullptr, nullptr},
1052};
1053
1055{
1056 using namespace blender::bke::greasepencil;
1058
1059 const int color_tag = RNA_enum_get(op->ptr, "color_tag");
1060 LayerGroup *active_group = grease_pencil.get_active_group();
1061 active_group->color_tag = color_tag;
1062
1063 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1065
1066 return OPERATOR_FINISHED;
1067}
1068
1070{
1071 /* identifiers */
1072 ot->name = "Grease Pencil Group Color Tag";
1073 ot->idname = "GREASE_PENCIL_OT_layer_group_color_tag";
1074 ot->description = "Change layer group icon";
1075
1078
1079 ot->flag = OPTYPE_UNDO;
1080
1081 ot->prop = RNA_def_enum(ot->srna, "color_tag", enum_layergroup_color_items, 0, "Color Tag", "");
1082}
1083
1085 All = 0,
1087};
1088
1090 GreasePencil &dst_grease_pencil,
1091 const GreasePencil &src_grease_pencil,
1092 const blender::bke::greasepencil::Layer &src_layer,
1093 const DuplicateCopyMode copy_frame_mode,
1094 const int current_frame)
1095{
1096 using namespace blender::bke::greasepencil;
1097 BLI_assert(&src_grease_pencil != &dst_grease_pencil);
1098
1099 /* When copying from another object a new layer is created and all drawings are copied. */
1100 const int src_layer_index = *src_grease_pencil.get_layer_index(src_layer);
1101
1102 Layer &dst_layer = dst_grease_pencil.add_layer(src_layer.name());
1103 const int dst_layer_index = dst_grease_pencil.layers().size() - 1;
1104
1105 BKE_grease_pencil_copy_layer_parameters(src_layer, dst_layer);
1106
1107 const bke::AttributeAccessor src_attributes = src_grease_pencil.attributes();
1108 bke::MutableAttributeAccessor dst_attributes = dst_grease_pencil.attributes_for_write();
1109 src_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
1110 if (iter.domain != bke::AttrDomain::Layer) {
1111 return;
1112 }
1113 bke::GAttributeReader reader = src_attributes.lookup(iter.name, iter.domain, iter.data_type);
1114 BLI_assert(reader);
1115 bke::GAttributeWriter writer = dst_attributes.lookup_or_add_for_write(
1116 iter.name, iter.domain, iter.data_type);
1117 if (writer) {
1118 const CPPType &cpptype = bke::attribute_type_to_cpp_type(iter.data_type);
1119 BUFFER_FOR_CPP_TYPE_VALUE(cpptype, buffer);
1120 reader.varray.get(src_layer_index, buffer);
1121 writer.varray.set_by_copy(dst_layer_index, buffer);
1122 }
1123 writer.finish();
1124 });
1125
1126 std::optional<int> frame_select = std::nullopt;
1127 if (copy_frame_mode == DuplicateCopyMode::Active) {
1128 frame_select = current_frame;
1129 }
1130 dst_grease_pencil.copy_frames_from_layer(dst_layer, src_grease_pencil, src_layer, frame_select);
1131}
1132
1134{
1135 using namespace blender::bke::greasepencil;
1136 Object *src_object = CTX_data_active_object(C);
1137 const Scene *scene = CTX_data_scene(C);
1138 const int current_frame = scene->r.cfra;
1139 const GreasePencil &src_grease_pencil = *static_cast<GreasePencil *>(src_object->data);
1140 const bool only_active = RNA_boolean_get(op->ptr, "only_active");
1141 const DuplicateCopyMode copy_frame_mode = DuplicateCopyMode(RNA_enum_get(op->ptr, "mode"));
1142
1143 CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
1144 if (ob == src_object || ob->type != OB_GREASE_PENCIL) {
1145 continue;
1146 }
1147 GreasePencil &dst_grease_pencil = *static_cast<GreasePencil *>(ob->data);
1148
1149 if (only_active) {
1150 const Layer &active_layer = *src_grease_pencil.get_active_layer();
1152 dst_grease_pencil, src_grease_pencil, active_layer, copy_frame_mode, current_frame);
1153 }
1154 else {
1155 for (const Layer *layer : src_grease_pencil.layers()) {
1157 dst_grease_pencil, src_grease_pencil, *layer, copy_frame_mode, current_frame);
1158 }
1159 }
1160
1161 DEG_id_tag_update(&dst_grease_pencil.id, ID_RECALC_GEOMETRY);
1163 }
1165
1166 return OPERATOR_FINISHED;
1167}
1168
1170{
1171 /* identifiers */
1172 ot->name = "Duplicate Layer to New Object";
1173 ot->idname = "GREASE_PENCIL_OT_layer_duplicate_object";
1174 ot->description = "Make a copy of the active Grease Pencil layer to selected object";
1175
1176 /* API callbacks. */
1179
1180 /* flags */
1181 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1182
1183 RNA_def_boolean(ot->srna,
1184 "only_active",
1185 true,
1186 "Only Active",
1187 "Copy only active Layer, uncheck to append all layers");
1188
1189 static const EnumPropertyItem copy_mode[] = {
1190 {int(DuplicateCopyMode::All), "ALL", 0, "All Frames", ""},
1191 {int(DuplicateCopyMode::Active), "ACTIVE", 0, "Active Frame", ""},
1192 {0, nullptr, 0, nullptr, nullptr},
1193 };
1194
1195 ot->prop = RNA_def_enum(ot->srna, "mode", copy_mode, 0, "Mode", "");
1196}
1197
1198} // namespace blender::ed::greasepencil
1199
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
#define CTX_DATA_BEGIN(C, Type, instance, member)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
#define CTX_DATA_END
wmMsgBus * CTX_wm_message_bus(const bContext *C)
Low-level operations for grease pencil.
void BKE_grease_pencil_copy_parameters(const GreasePencil &src, GreasePencil &dst)
void BKE_grease_pencil_nomain_to_grease_pencil(GreasePencil *grease_pencil_src, GreasePencil *grease_pencil_dst)
GreasePencil * BKE_grease_pencil_new_nomain()
void BKE_grease_pencil_copy_layer_parameters(const blender::bke::greasepencil::Layer &src, blender::bke::greasepencil::Layer &dst)
General operations, lookup, etc. for blender objects.
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
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
void * BLI_findstring_ptr(const ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:651
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void unit_m4(float m[4][4])
char * BLI_strdup_null(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_MALLOC
Definition string.cc:46
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ BONE_RELATIVE_PARENTING
@ LAYERGROUP_COLOR_01
@ LAYERGROUP_COLOR_NONE
@ LAYERGROUP_COLOR_06
@ LAYERGROUP_COLOR_04
@ LAYERGROUP_COLOR_05
@ LAYERGROUP_COLOR_03
@ LAYERGROUP_COLOR_08
@ LAYERGROUP_COLOR_07
@ LAYERGROUP_COLOR_02
@ OB_GREASE_PENCIL
@ OB_ARMATURE
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
#define MEM_SAFE_FREE(v)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_EDITED
Definition WM_types.hh:584
#define NC_GPENCIL
Definition WM_types.hh:399
#define NA_SELECTED
Definition WM_types.hh:589
void get(int64_t index, void *r_value) const
void set_by_copy(int64_t index, const void *value)
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr const char * c_str() const
void append(const T &value)
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
GAttributeReader lookup(const StringRef attribute_id) const
GAttributeWriter lookup_or_add_for_write(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
Span< const Layer * > layers() const
const LayerGroup & as_group() const
const LayerGroup * parent_group() const
#define offsetof(t, d)
static ushort indices[]
#define INT16_MAX
#define active
MatBase< C, R > inverse(MatBase< C, R >) RET
void ED_operatortypes_grease_pencil_layers()
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
const CPPType & attribute_type_to_cpp_type(AttrType type)
static void GREASE_PENCIL_OT_layer_lock_all(wmOperatorType *ot)
static wmOperatorStatus grease_pencil_layer_mask_reorder_exec(bContext *C, wmOperator *op)
static wmOperatorStatus grease_pencil_layer_mask_remove_exec(bContext *C, wmOperator *)
static void copy_layer_and_frames_to_target_object(GreasePencil &dst_grease_pencil, const GreasePencil &src_grease_pencil, const blender::bke::greasepencil::Layer &src_layer, const DuplicateCopyMode copy_frame_mode, const int current_frame)
static bool grease_pencil_layer_mask_reorder_poll(bContext *C)
static wmOperatorStatus grease_pencil_layer_move_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem enum_layer_move_direction[]
static wmOperatorStatus grease_pencil_layer_hide_exec(bContext *C, wmOperator *op)
static wmOperatorStatus grease_pencil_layer_add_invoke(bContext *C, wmOperator *op, const wmEvent *event)
GreasePencil * from_context(bContext &C)
static void GREASE_PENCIL_OT_layer_group_add(wmOperatorType *ot)
static wmOperatorStatus grease_pencil_layer_group_add_exec(bContext *C, wmOperator *op)
void grease_pencil_layer_parent_clear(bke::greasepencil::Layer &layer, const bool keep_transform)
static wmOperatorStatus grease_pencil_merge_layer_exec(bContext *C, wmOperator *op)
static wmOperatorStatus grease_pencil_layer_active_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_layer_group_color_tag(wmOperatorType *ot)
static float4x4 get_bone_mat(const Object *parent, const char *parsubstr)
static void GREASE_PENCIL_OT_layer_remove(wmOperatorType *ot)
static void GREASE_PENCIL_OT_layer_isolate(wmOperatorType *ot)
static wmOperatorStatus grease_pencil_layer_duplicate_object_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_layer_hide(wmOperatorType *ot)
static void GREASE_PENCIL_OT_layer_merge(wmOperatorType *ot)
static void GREASE_PENCIL_OT_layer_add(wmOperatorType *ot)
static bool grease_pencil_layer_mask_poll(bContext *C)
static wmOperatorStatus grease_pencil_layer_add_exec(bContext *C, wmOperator *op)
bool editable_grease_pencil_poll(bContext *C)
static void GREASE_PENCIL_OT_layer_mask_add(wmOperatorType *ot)
static wmOperatorStatus grease_pencil_layer_mask_add_exec(bContext *C, wmOperator *op)
static wmOperatorStatus grease_pencil_layer_remove_exec(bContext *C, wmOperator *)
static void GREASE_PENCIL_OT_layer_active(wmOperatorType *ot)
const EnumPropertyItem enum_layergroup_color_items[]
static void GREASE_PENCIL_OT_layer_move(wmOperatorType *ot)
static wmOperatorStatus grease_pencil_layer_lock_all_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
static void GREASE_PENCIL_OT_layer_group_remove(wmOperatorType *ot)
static void GREASE_PENCIL_OT_layer_duplicate(wmOperatorType *ot)
static wmOperatorStatus grease_pencil_layer_group_remove_exec(bContext *C, wmOperator *op)
bool grease_pencil_layer_parent_set(bke::greasepencil::Layer &layer, Object *parent, StringRefNull bone, const bool keep_transform)
bool grease_pencil_context_poll(bContext *C)
static wmOperatorStatus grease_pencil_layer_duplicate_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_layer_mask_reorder(wmOperatorType *ot)
void merge_layers(const GreasePencil &src_grease_pencil, const Span< Vector< int > > src_layer_indices_by_dst_layer, GreasePencil &dst_grease_pencil)
bool active_grease_pencil_layer_group_poll(bContext *C)
static wmOperatorStatus grease_pencil_layer_group_color_tag_exec(bContext *C, wmOperator *op)
void select_layer_channel(GreasePencil &grease_pencil, bke::greasepencil::Layer *layer)
static void GREASE_PENCIL_OT_layer_reveal(wmOperatorType *ot)
bool active_grease_pencil_layer_poll(bContext *C)
static wmOperatorStatus grease_pencil_layer_reveal_exec(bContext *C, wmOperator *)
static void GREASE_PENCIL_OT_layer_mask_remove(wmOperatorType *ot)
static wmOperatorStatus grease_pencil_layer_isolate_exec(bContext *C, wmOperator *op)
static bool grease_pencil_layer_move_poll(bContext *C)
CartesianBasis invert(const CartesianBasis &basis)
MatBase< float, 4, 4 > float4x4
int RNA_int_get(PointerRNA *ptr, const char *name)
std::string RNA_string_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
struct GreasePencilLayerTreeNode * prev
struct bPose * pose
struct RenderData r
struct Bone * bone
float chan_mat[4][4]
float pose_mat[4][4]
struct ReportList * reports
struct PointerRNA * ptr
#define N_(msgid)
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
#define WM_msg_publish_rna_prop(mbus, id_, data_, type_, prop_)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
wmOperatorStatus WM_operator_props_popup_confirm_ex(bContext *C, wmOperator *op, const wmEvent *, std::optional< std::string > title, std::optional< std::string > confirm_text, const bool cancel_default, std::optional< std::string > message)