Blender V4.3
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
9#include "BLI_math_matrix.h"
10#include "BLI_math_vector.h"
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.curves_num() == 0) {
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) {
205 *frame = {item.value, duration};
206 },
207 [&](InsertKeyframe *frame) {
208 /* The destination frame is always an implicit hold if at least on of the source
209 * frame is an implicit hold. */
210 if (duration == 0) {
211 frame->duration = 0;
212 frame->frame.flag |= GP_FRAME_IMPLICIT_HOLD;
213 }
214 else if (frame->duration > 0) {
215 /* For fixed duration frames, use the longest duration of the source frames. */
216 frame->duration = std::max(frame->duration, duration);
217 }
218 });
219 }
220 }
221
222 Array<FramesMapKeyT> sorted_keys(dst_frames.size());
223 {
224 int i = 0;
225 for (const FramesMapKeyT key : dst_frames.keys()) {
226 sorted_keys[i++] = key;
227 }
228 std::sort(sorted_keys.begin(), sorted_keys.end());
229 }
230
231 Array<Vector<int>> src_drawing_indices_by_frame(sorted_keys.size());
232 Array<Vector<float4x4>> src_transforms_by_frame(sorted_keys.size());
233 for (const int src_layer_i : src_layer_indices) {
234 const Layer &src_layer = *src_layers[src_layer_i];
235 for (const int key_i : sorted_keys.index_range()) {
236 const FramesMapKeyT key = sorted_keys[key_i];
237 const int drawing_index = src_layer.drawing_index_at(key);
238 if (drawing_index != -1) {
239 src_drawing_indices_by_frame[key_i].append(drawing_index);
240 src_transforms_by_frame[key_i].append(dst_layer_transform_inv *
241 src_layer.local_transform());
242 }
243 }
244 }
245
246 /* Add all the destination frames. */
247 VectorSet<Vector<int>> unique_src_indices_per_drawing;
248 for (const int key_i : sorted_keys.index_range()) {
249 const FramesMapKeyT key = sorted_keys[key_i];
250 const InsertKeyframe value = dst_frames.lookup(key);
251 const Vector<int> &src_drawing_indices = src_drawing_indices_by_frame[key_i];
252 const Vector<float4x4> &src_transforms = src_transforms_by_frame[key_i];
253
254 GreasePencilFrame *frame = dst_layer.add_frame(key, value.duration);
255 /* Copy frame parameters. */
256 frame->flag = value.frame.flag;
257 frame->type = value.frame.type;
258
259 /* In case drawings are shared in the source, keep sharing the drawings if possible. */
260 int index = unique_src_indices_per_drawing.index_of_try(src_drawing_indices);
261 if (index == -1) {
262 unique_src_indices_per_drawing.add_new(src_drawing_indices);
263 index = src_drawing_indices_by_dst_drawing.append_and_get_index(src_drawing_indices);
264 src_transforms_by_dst_drawing.append(src_transforms);
265 num_dst_drawings++;
266 }
267 else {
268 index = index + dst_drawing_start_index;
269 }
270 frame->drawing_index = index;
271 }
272
273 dst_layer.tag_frames_map_changed();
274 }
275
276 /* The destination layers don't map to the order of elements in #src_layer_indices_by_dst_layer.
277 * This map maps between the old order and the final order in the destination Grease Pencil. */
278 Array<int> old_to_new_index_map(num_dst_layers);
279 for (const int layer_i : dst_grease_pencil.layers().index_range()) {
280 const Layer *layer = &dst_grease_pencil.layer(layer_i);
281 old_to_new_index_map[dst_layer_to_old_index_map.lookup(layer)] = layer_i;
282 }
283
284 /* Add all the drawings. */
285 if (num_dst_drawings > 0) {
286 dst_grease_pencil.add_empty_drawings(num_dst_drawings);
287 }
288
289 MutableSpan<GreasePencilDrawingBase *> dst_drawings = dst_grease_pencil.drawings();
290 threading::parallel_for(dst_drawings.index_range(), 32, [&](const IndexRange range) {
291 for (const int dst_drawing_i : range) {
292 const Span<int> src_drawing_indices = src_drawing_indices_by_dst_drawing[dst_drawing_i];
293 const Span<float4x4> src_transforms_to_apply = src_transforms_by_dst_drawing[dst_drawing_i];
294 const GreasePencilDrawingBase *src_first_base = src_drawings[src_drawing_indices.first()];
295 BLI_assert(src_first_base->type == GP_DRAWING);
296 GreasePencilDrawingBase *dst_base = dst_drawings[dst_drawing_i];
297 BLI_assert(dst_base->type == GP_DRAWING);
298 /* Copy the parameters of the first source drawing. */
299 dst_base->flag = src_first_base->flag;
300
301 Drawing &dst_drawing = reinterpret_cast<GreasePencilDrawing *>(dst_base)->wrap();
302 if (src_drawing_indices.size() == 1) {
303 const Drawing &src_drawing =
304 reinterpret_cast<const GreasePencilDrawing *>(src_first_base)->wrap();
305 dst_drawing.strokes_for_write() = src_drawing.strokes();
306 dst_drawing.tag_topology_changed();
307 continue;
308 }
309
310 /* Gather all the source curves to be merged. */
311 Vector<const bke::CurvesGeometry *> all_src_curves;
312 for (const int src_darwing_i : src_drawing_indices) {
313 const GreasePencilDrawingBase *src_base = src_drawings[src_darwing_i];
314 BLI_assert(src_base->type == GP_DRAWING);
315 const Drawing &src_drawing =
316 reinterpret_cast<const GreasePencilDrawing *>(src_base)->wrap();
317 all_src_curves.append(&src_drawing.strokes());
318 }
319
320 dst_drawing.strokes_for_write() = join_curves(
321 src_grease_pencil, all_src_curves, src_transforms_to_apply);
322 dst_drawing.tag_topology_changed();
323 }
324 });
325
326 /* Update the user count for all the drawings. */
327 for (const Layer *dst_layer : dst_grease_pencil.layers()) {
328 dst_grease_pencil.update_drawing_users_for_layer(*dst_layer);
329 }
330
331 /* Gather all the layer attributes. */
332 const bke::AttributeAccessor src_attributes = src_grease_pencil.attributes();
333 bke::MutableAttributeAccessor dst_attributes = dst_grease_pencil.attributes_for_write();
334 src_attributes.foreach_attribute([&](const blender::bke::AttributeIter &iter) {
335 if (iter.data_type == CD_PROP_STRING) {
336 return;
337 }
338 bke::GAttributeReader src_attribute = src_attributes.lookup(iter.name);
339 if (!src_attribute) {
340 return;
341 }
342 bke::GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
343 iter.name, bke::AttrDomain::Layer, iter.data_type);
344
345 const CPPType &type = dst_attribute.span.type();
346 bke::attribute_math::convert_to_static_type(type, [&](auto type) {
347 using T = decltype(type);
348 const VArraySpan<T> src_span = src_attribute.varray.typed<T>();
349 MutableSpan<T> new_span = dst_attribute.span.typed<T>();
350
351 bke::attribute_math::DefaultMixer<T> mixer(new_span);
352 for (const int dst_layer_i : IndexRange(num_dst_layers)) {
353 const Span<int> src_layer_indices = src_layer_indices_by_dst_layer[dst_layer_i];
354 const int new_index = old_to_new_index_map[dst_layer_i];
355 for (const int src_layer_i : src_layer_indices) {
356 const T &src_value = src_span[src_layer_i];
357 mixer.mix_in(new_index, src_value);
358 }
359 }
360 mixer.finalize();
361 });
362
363 dst_attribute.finish();
364 });
365}
366
367} // 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:50
#define LISTBASE_FOREACH(type, var, list)
@ CD_PROP_STRING
@ GP_FRAME_IMPLICIT_HOLD
int64_t size() const
Definition BLI_array.hh:245
const T * end() const
Definition BLI_array.hh:314
IndexRange index_range() const
Definition BLI_array.hh:349
const T * begin() const
Definition BLI_array.hh:310
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:271
const Value & lookup(const Key &key) const
Definition BLI_map.hh:506
ItemIterator items() const
Definition BLI_map.hh:864
auto add_or_modify(const Key &key, const CreateValueF &create_value, const ModifyValueF &modify_value) -> decltype(create_value(nullptr))
Definition BLI_map.hh:457
Value & lookup_or_add(const Key &key, const Value &value)
Definition BLI_map.hh:551
constexpr int64_t first_index_try(const T &search_value) const
Definition BLI_span.hh:388
constexpr const T & first() const
Definition BLI_span.hh:316
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
void add_new(const Key &key)
int64_t index_of_try(const Key &key) const
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)
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()
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
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)
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:95
CurvesGeometry geometry
struct Material ** mat
struct Material ** material_array
const Curves * get_curves() const