Blender V5.0
grease_pencil_merge.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_listbase.h"
10#include "BLI_math_matrix.hh"
11
12#include "BKE_attribute_math.hh"
13#include "BKE_curves.hh"
14#include "BKE_geometry_set.hh"
15
17
18#include "ED_grease_pencil.hh"
19
21
22using bke::greasepencil::Layer;
23using bke::greasepencil::LayerGroup;
24using bke::greasepencil::TreeNode;
25
26static void copy_layer_groups_without_layers(GreasePencil &dst_grease_pencil,
27 const LayerGroup &src_parent,
28 LayerGroup &dst_parent)
29{
30 using namespace bke::greasepencil;
31 /* Note: Don't loop over all children, just the direct children. */
33 if (!node->wrap().is_group()) {
34 continue;
35 }
36 const LayerGroup &src_group = node->wrap().as_group();
37 LayerGroup &new_group = dst_grease_pencil.add_layer_group(dst_parent, src_group.name(), false);
39 /* Repeat recursively for groups in group. */
40 copy_layer_groups_without_layers(dst_grease_pencil, src_group, new_group);
41 }
42}
43
45{
47 const TreeNode *node = &layer.as_node();
48 while (node->parent_group()) {
49 parents.append(node->parent_group());
50 node = node->parent_node();
51 }
52 /* Reverse so that the root group is the first element. */
53 std::reverse(parents.begin(), parents.end());
54 return parents;
55}
56
57static const LayerGroup &find_lowest_common_ancestor(const GreasePencil &grease_pencil,
58 const Span<int> src_layer_indices)
59{
60 using namespace bke::greasepencil;
61 BLI_assert(src_layer_indices.size() > 0);
62 const Span<const Layer *> layers = grease_pencil.layers();
63 if (src_layer_indices.size() == 1) {
64 return layers[src_layer_indices.first()]->parent_group();
65 }
66
68 *layers[src_layer_indices.first()]);
69 for (const int layer_i : src_layer_indices) {
70 const Layer &layer = *layers[layer_i];
72 /* Possibly shrink set of candidates so that it only contains the parents common with the
73 * current layer. */
74 candidates.resize(std::min(candidates.size(), parents.size()));
75 for (const int i : candidates.index_range()) {
76 if (candidates[i] != parents[i]) {
77 candidates.resize(i);
78 break;
79 }
80 }
81 }
82
83 BLI_assert(!candidates.is_empty());
84 return *candidates.last();
85}
86
87static bke::CurvesGeometry join_curves(const GreasePencil &src_grease_pencil,
88 const Span<const bke::CurvesGeometry *> all_src_curves,
89 const Span<float4x4> transforms_to_apply)
90{
91 BLI_assert(all_src_curves.size() == transforms_to_apply.size());
92 Vector<bke::GeometrySet> src_geometries(all_src_curves.size());
93 for (const int src_curves_i : all_src_curves.index_range()) {
94 bke::CurvesGeometry src_curves = *all_src_curves[src_curves_i];
95 if (src_curves.is_empty()) {
96 continue;
97 }
98 const float4x4 &transform = transforms_to_apply[src_curves_i];
99 src_curves.transform(transform);
100 Curves *src_curves_id = bke::curves_new_nomain(std::move(src_curves));
101 src_curves_id->mat = static_cast<Material **>(MEM_dupallocN(src_grease_pencil.material_array));
102 src_curves_id->totcol = src_grease_pencil.material_array_num;
103 src_geometries[src_curves_i].replace_curves(src_curves_id);
104 }
105 bke::GeometrySet joined_geometry = geometry::join_geometries(src_geometries, {});
106 if (joined_geometry.has_curves()) {
107 return joined_geometry.get_curves()->geometry.wrap();
108 }
109 return {};
110}
111
112void merge_layers(const GreasePencil &src_grease_pencil,
113 const Span<Vector<int>> src_layer_indices_by_dst_layer,
114 GreasePencil &dst_grease_pencil)
115{
116 using namespace bke::greasepencil;
117 const int num_dst_layers = src_layer_indices_by_dst_layer.size();
118 const Span<const Layer *> src_layers = src_grease_pencil.layers();
119 const Span<const LayerGroup *> src_groups = src_grease_pencil.layer_groups();
120 const Span<const GreasePencilDrawingBase *> src_drawings = src_grease_pencil.drawings();
121
122 /* Reconstruct the same layer tree structure from the source. */
124 dst_grease_pencil, src_grease_pencil.root_group(), dst_grease_pencil.root_group());
125 BLI_assert(src_groups.size() == dst_grease_pencil.layer_groups().size());
126
127 /* Find the parent group indices for all the dst layers. */
128 Array<int> parent_group_index_by_dst_layer(num_dst_layers);
129 for (const int dst_layer_i : src_layer_indices_by_dst_layer.index_range()) {
130 const Span<int> src_layer_indices = src_layer_indices_by_dst_layer[dst_layer_i];
131 const LayerGroup &parent = find_lowest_common_ancestor(src_grease_pencil, src_layer_indices);
132 /* Note: For layers in the root group the index will be -1. */
133 parent_group_index_by_dst_layer[dst_layer_i] = src_groups.first_index_try(&parent);
134 }
135
136 /* Important: The cache for the groups changes when layers are added. We have to make a copy of
137 * all the pointers here. */
138 const Array<LayerGroup *> dst_groups = dst_grease_pencil.layer_groups_for_write();
139
140 /* Add all the layers in the destination under the right parent groups. */
141 int num_dst_drawings = 0;
142 Vector<Vector<int>> src_drawing_indices_by_dst_drawing;
143 Vector<Vector<float4x4>> src_transforms_by_dst_drawing;
144 Map<const Layer *, int> dst_layer_to_old_index_map;
145 for (const int dst_layer_i : src_layer_indices_by_dst_layer.index_range()) {
146 const Span<int> src_layer_indices = src_layer_indices_by_dst_layer[dst_layer_i];
147 const Layer &src_first = *src_layers[src_layer_indices.first()];
148 const int parent_index = parent_group_index_by_dst_layer[dst_layer_i];
149 /* If the parent index is -1 then the layer is added in the root group. */
150 LayerGroup &dst_parent = (parent_index == -1) ? dst_grease_pencil.root_group() :
151 *dst_groups[parent_index];
152 Layer &dst_layer = dst_grease_pencil.add_layer(dst_parent, src_first.name(), false);
153 /* Copy the layer parameters of the first source layer. */
154 BKE_grease_pencil_copy_layer_parameters(src_first, dst_layer);
155
156 dst_layer_to_old_index_map.add(&dst_layer, dst_layer_i);
157
158 const int dst_drawing_start_index = src_drawing_indices_by_dst_drawing.size();
159 const float4x4 dst_layer_transform = dst_layer.local_transform();
160 const float4x4 dst_layer_transform_inv = math::invert(dst_layer_transform);
161 if (src_layer_indices.size() == 1) {
162 const Map<FramesMapKeyT, GreasePencilFrame> &src_frames = src_first.frames();
164
165 VectorSet<int> unique_src_indices;
166 for (const FramesMapKeyT key : src_first.sorted_keys()) {
167 const GreasePencilFrame &src_frame = src_frames.lookup(key);
168 GreasePencilFrame &value = dst_frames.lookup_or_add(key, src_frame);
169
170 int index = unique_src_indices.index_of_try(src_frame.drawing_index);
171 if (index == -1) {
172 unique_src_indices.add_new(src_frame.drawing_index);
173 index = src_drawing_indices_by_dst_drawing.append_and_get_index(
174 {src_frame.drawing_index});
175 src_transforms_by_dst_drawing.append(
176 {dst_layer_transform_inv * src_first.local_transform()});
177 num_dst_drawings++;
178 }
179 else {
180 index = index + dst_drawing_start_index;
181 }
182 value.drawing_index = index;
183 }
184
185 dst_layer.tag_frames_map_changed();
186 continue;
187 }
188
189 struct InsertKeyframe {
190 GreasePencilFrame frame;
191 int duration = -1;
192 };
194 for (const int src_layer_i : src_layer_indices) {
195 const Layer &src_layer = *src_layers[src_layer_i];
196 for (const auto &item : src_layer.frames().items()) {
197 if (item.value.is_end()) {
198 continue;
199 }
200 const int duration = src_layer.get_frame_duration_at(item.key);
201 BLI_assert(duration >= 0);
202 dst_frames.add_or_modify(
203 item.key,
204 [&](InsertKeyframe *frame) { *frame = {item.value, duration}; },
205 [&](InsertKeyframe *frame) {
206 /* The destination frame is always an implicit hold if at least on of the source
207 * frame is an implicit hold. */
208 if (duration == 0) {
209 frame->duration = 0;
210 frame->frame.flag |= GP_FRAME_IMPLICIT_HOLD;
211 }
212 else if (frame->duration > 0) {
213 /* For fixed duration frames, use the longest duration of the source frames. */
214 frame->duration = std::max(frame->duration, duration);
215 }
216 });
217 }
218 }
219
220 Array<FramesMapKeyT> sorted_keys(dst_frames.size());
221 {
222 int i = 0;
223 for (const FramesMapKeyT key : dst_frames.keys()) {
224 sorted_keys[i++] = key;
225 }
226 std::sort(sorted_keys.begin(), sorted_keys.end());
227 }
228
229 Array<Vector<int>> src_drawing_indices_by_frame(sorted_keys.size());
230 Array<Vector<float4x4>> src_transforms_by_frame(sorted_keys.size());
231 for (const int src_layer_i : src_layer_indices) {
232 const Layer &src_layer = *src_layers[src_layer_i];
233 for (const int key_i : sorted_keys.index_range()) {
234 const FramesMapKeyT key = sorted_keys[key_i];
235 const int drawing_index = src_layer.drawing_index_at(key);
236 if (drawing_index != -1) {
237 src_drawing_indices_by_frame[key_i].append(drawing_index);
238 src_transforms_by_frame[key_i].append(dst_layer_transform_inv *
239 src_layer.local_transform());
240 }
241 }
242 }
243
244 /* Add all the destination frames. */
245 VectorSet<Vector<int>> unique_src_indices_per_drawing;
246 for (const int key_i : sorted_keys.index_range()) {
247 const FramesMapKeyT key = sorted_keys[key_i];
248 const InsertKeyframe value = dst_frames.lookup(key);
249 const Vector<int> &src_drawing_indices = src_drawing_indices_by_frame[key_i];
250 const Vector<float4x4> &src_transforms = src_transforms_by_frame[key_i];
251
252 GreasePencilFrame *frame = dst_layer.add_frame(key, value.duration);
253 /* Copy frame parameters. */
254 frame->flag = value.frame.flag;
255 frame->type = value.frame.type;
256
257 /* In case drawings are shared in the source, keep sharing the drawings if possible. */
258 int index = unique_src_indices_per_drawing.index_of_try(src_drawing_indices);
259 if (index == -1) {
260 unique_src_indices_per_drawing.add_new(src_drawing_indices);
261 index = src_drawing_indices_by_dst_drawing.append_and_get_index(src_drawing_indices);
262 src_transforms_by_dst_drawing.append(src_transforms);
263 num_dst_drawings++;
264 }
265 else {
266 index = index + dst_drawing_start_index;
267 }
268 frame->drawing_index = index;
269 }
270
271 dst_layer.tag_frames_map_changed();
272 }
273
274 /* The destination layers don't map to the order of elements in #src_layer_indices_by_dst_layer.
275 * This map maps between the old order and the final order in the destination Grease Pencil. */
276 Array<int> old_to_new_index_map(num_dst_layers);
277 for (const int layer_i : dst_grease_pencil.layers().index_range()) {
278 const Layer *layer = &dst_grease_pencil.layer(layer_i);
279 old_to_new_index_map[dst_layer_to_old_index_map.lookup(layer)] = layer_i;
280 }
281
282 /* Add all the drawings. */
283 if (num_dst_drawings > 0) {
284 dst_grease_pencil.add_empty_drawings(num_dst_drawings);
285 }
286
287 MutableSpan<GreasePencilDrawingBase *> dst_drawings = dst_grease_pencil.drawings();
288 threading::parallel_for(dst_drawings.index_range(), 32, [&](const IndexRange range) {
289 for (const int dst_drawing_i : range) {
290 const Span<int> src_drawing_indices = src_drawing_indices_by_dst_drawing[dst_drawing_i];
291 const Span<float4x4> src_transforms_to_apply = src_transforms_by_dst_drawing[dst_drawing_i];
292 const GreasePencilDrawingBase *src_first_base = src_drawings[src_drawing_indices.first()];
293 BLI_assert(src_first_base->type == GP_DRAWING);
294 GreasePencilDrawingBase *dst_base = dst_drawings[dst_drawing_i];
295 BLI_assert(dst_base->type == GP_DRAWING);
296 /* Copy the parameters of the first source drawing. */
297 dst_base->flag = src_first_base->flag;
298
299 Drawing &dst_drawing = reinterpret_cast<GreasePencilDrawing *>(dst_base)->wrap();
300 if (src_drawing_indices.size() == 1) {
301 const Drawing &src_drawing =
302 reinterpret_cast<const GreasePencilDrawing *>(src_first_base)->wrap();
303 dst_drawing.strokes_for_write() = src_drawing.strokes();
304 dst_drawing.tag_topology_changed();
305 continue;
306 }
307
308 /* Gather all the source curves to be merged. */
309 Vector<const bke::CurvesGeometry *> all_src_curves;
310 for (const int src_darwing_i : src_drawing_indices) {
311 const GreasePencilDrawingBase *src_base = src_drawings[src_darwing_i];
312 BLI_assert(src_base->type == GP_DRAWING);
313 const Drawing &src_drawing =
314 reinterpret_cast<const GreasePencilDrawing *>(src_base)->wrap();
315 all_src_curves.append(&src_drawing.strokes());
316 }
317
318 dst_drawing.strokes_for_write() = join_curves(
319 src_grease_pencil, all_src_curves, src_transforms_to_apply);
320 dst_drawing.tag_topology_changed();
321 }
322 });
323
324 /* Update the user count for all the drawings. */
325 for (const Layer *dst_layer : dst_grease_pencil.layers()) {
326 dst_grease_pencil.update_drawing_users_for_layer(*dst_layer);
327 }
328
329 /* Gather all the layer attributes. */
330 const bke::AttributeAccessor src_attributes = src_grease_pencil.attributes();
331 bke::MutableAttributeAccessor dst_attributes = dst_grease_pencil.attributes_for_write();
332 src_attributes.foreach_attribute([&](const blender::bke::AttributeIter &iter) {
333 if (iter.data_type == bke::AttrType::String) {
334 return;
335 }
336 bke::GAttributeReader src_attribute = iter.get();
337 bke::GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
338 iter.name, bke::AttrDomain::Layer, iter.data_type);
339 if (!dst_attribute) {
340 return;
341 }
342
343 const CPPType &type = dst_attribute.span.type();
344 bke::attribute_math::convert_to_static_type(type, [&](auto type) {
345 using T = decltype(type);
346 const VArraySpan<T> src_span = src_attribute.varray.typed<T>();
347 MutableSpan<T> new_span = dst_attribute.span.typed<T>();
348
349 bke::attribute_math::DefaultMixer<T> mixer(new_span);
350 for (const int dst_layer_i : IndexRange(num_dst_layers)) {
351 const Span<int> src_layer_indices = src_layer_indices_by_dst_layer[dst_layer_i];
352 const int new_index = old_to_new_index_map[dst_layer_i];
353 for (const int src_layer_i : src_layer_indices) {
354 const T &src_value = src_span[src_layer_i];
355 mixer.mix_in(new_index, src_value);
356 }
357 }
358 mixer.finalize();
359 });
360
361 dst_attribute.finish();
362 });
363}
364
365} // namespace blender::ed::greasepencil
Low-level operations for curves.
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)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
@ GP_FRAME_IMPLICIT_HOLD
ItemIterator items() const &
Definition BLI_map.hh:902
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
int64_t size() const
Definition BLI_array.hh:256
const T * end() const
Definition BLI_array.hh:325
IndexRange index_range() const
Definition BLI_array.hh:360
const T * begin() const
Definition BLI_array.hh:321
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
auto add_or_modify(const Key &key, const CreateValueF &create_value, const ModifyValueF &modify_value) -> decltype(create_value(nullptr))
Definition BLI_map.hh:481
Value & lookup_or_add(const Key &key, const Value &value)
Definition BLI_map.hh:588
constexpr int64_t first_index_try(const T &search_value) const
Definition BLI_span.hh:387
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
int64_t index_of_try(const Key &key) const
void add_new(const Key &key)
int64_t size() const
int64_t append_and_get_index(const T &value)
void append(const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
IndexRange index_range() const
void resize(const int64_t new_size)
GAttributeReader get() const
void transform(const float4x4 &matrix)
const Map< FramesMapKeyT, GreasePencilFrame > & frames() const
int drawing_index_at(const int frame_number) const
int get_frame_duration_at(const int frame_number) const
Span< FramesMapKeyT > sorted_keys() const
Map< FramesMapKeyT, GreasePencilFrame > & frames_for_write()
const TreeNode * parent_node() const
const LayerGroup * parent_group() const
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
#define T
Curves * curves_new_nomain(int points_num, int curves_num)
static void copy_layer_groups_without_layers(GreasePencil &dst_grease_pencil, const LayerGroup &src_parent, LayerGroup &dst_parent)
static const LayerGroup & find_lowest_common_ancestor(const GreasePencil &grease_pencil, const Span< int > src_layer_indices)
static bke::CurvesGeometry join_curves(const GreasePencil &src_grease_pencil, const Span< const bke::CurvesGeometry * > all_src_curves, const Span< float4x4 > transforms_to_apply)
static Vector< const LayerGroup * > get_sorted_layer_parents(const Layer &layer)
void merge_layers(const GreasePencil &src_grease_pencil, const Span< Vector< int > > src_layer_indices_by_dst_layer, GreasePencil &dst_grease_pencil)
bke::GeometrySet join_geometries(Span< bke::GeometrySet > geometries, const bke::AttributeFilter &attribute_filter, const std::optional< Span< bke::GeometryComponent::Type > > &component_types_to_join=std::nullopt, bool allow_merging_instance_references=true)
CartesianBasis invert(const CartesianBasis &basis)
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< float, 4, 4 > float4x4
CurvesGeometry geometry
struct Material ** mat
struct Material ** material_array
const Curves * get_curves() const
i
Definition text_draw.cc:230