Blender V5.0
mesh_join.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10
11#include "MEM_guardedalloc.h"
12
13#include "BLI_listbase.h"
14#include "BLI_math_matrix.h"
15#include "BLI_math_matrix.hh"
16#include "BLI_vector.hh"
17#include "BLI_virtual_array.hh"
18
19#include "DNA_key_types.h"
20#include "DNA_material_types.h"
21#include "DNA_mesh_types.h"
22#include "DNA_meshdata_types.h"
23#include "DNA_modifier_types.h"
24#include "DNA_object_types.h"
25#include "DNA_scene_types.h"
26
27#include "BKE_attribute.hh"
28#include "BKE_context.hh"
29#include "BKE_customdata.hh"
30#include "BKE_deform.hh"
31#include "BKE_key.hh"
32#include "BKE_lib_id.hh"
33#include "BKE_material.hh"
34#include "BKE_mesh.hh"
35#include "BKE_mesh_runtime.hh"
36#include "BKE_multires.hh"
37#include "BKE_object.hh"
38#include "BKE_object_deform.h"
39#include "BKE_report.hh"
40
41#include "DEG_depsgraph.hh"
44
45#include "ED_geometry.hh"
46#include "ED_mesh.hh"
47#include "ED_object.hh"
48#include "ED_view3d.hh"
49
50#include "WM_api.hh"
51#include "WM_types.hh"
52
53#include "mesh_intern.hh"
54
55namespace blender::ed::mesh {
56
58 const OffsetIndices<int> vert_ranges,
59 Mesh &dst_mesh)
60{
61 VectorSet<std::string> vertex_group_names;
62 bool any_vertex_group_data = false;
63 for (const int i : objects_to_join.index_range()) {
64 const Mesh &mesh = *static_cast<const Mesh *>(objects_to_join[i]->data);
65 any_vertex_group_data |= CustomData_has_layer(&mesh.vert_data, CD_MDEFORMVERT);
66 LISTBASE_FOREACH (const bDeformGroup *, dg, &mesh.vertex_group_names) {
67 if (vertex_group_names.add_as(dg->name)) {
69 }
70 }
71 }
72
73 if (!any_vertex_group_data) {
74 return vertex_group_names;
75 }
76
78 &dst_mesh.vert_data, CD_MDEFORMVERT, CD_CONSTRUCT, dst_mesh.verts_num);
79
80 for (const int i : objects_to_join.index_range()) {
81 const Mesh &src_mesh = *static_cast<const Mesh *>(objects_to_join[i]->data);
82 const Span<MDeformVert> src_dverts = src_mesh.deform_verts();
83 if (src_dverts.is_empty()) {
84 continue;
85 }
86 Vector<int, 32> index_map;
87 LISTBASE_FOREACH (const bDeformGroup *, dg, &src_mesh.vertex_group_names) {
88 index_map.append(vertex_group_names.index_of_as(dg->name));
89 }
90 for (const int vert : src_dverts.index_range()) {
91 const MDeformVert &src = src_dverts[vert];
92 MDeformVert &dst = dvert[vert_ranges[i][vert]];
93 dst = src;
95 for (const int weight : IndexRange(src.totweight)) {
96 dst.dw[weight].def_nr = index_map[src.dw[weight].def_nr];
97 dst.dw[weight].weight = src.dw[weight].weight;
98 }
99 }
100 }
101
102 return vertex_group_names;
103}
104
105static void join_positions(const Span<const Object *> objects_to_join,
106 const OffsetIndices<int> vert_ranges,
107 const float4x4 &world_to_dst_mesh,
108 Mesh &dst_mesh)
109{
110 MutableSpan<float3> dst_positions = dst_mesh.vert_positions_for_write();
111 for (const int i : objects_to_join.index_range()) {
112 const Object &src_object = *objects_to_join[i];
113 const IndexRange dst_range = vert_ranges[i];
114 const Mesh &src_mesh = *static_cast<const Mesh *>(src_object.data);
115 const Span<float3> src_positions = src_mesh.vert_positions();
116 const float4x4 transform = world_to_dst_mesh * src_object.object_to_world();
117 math::transform_points(src_positions, transform, dst_positions.slice(dst_range));
118 }
119}
120
121static void join_normals(const Span<const Object *> objects_to_join,
122 const OffsetIndices<int> vert_ranges,
123 const OffsetIndices<int> face_ranges,
124 const OffsetIndices<int> corner_ranges,
125 const float4x4 &world_to_dst_mesh,
126 Mesh &dst_mesh)
127{
128 bke::mesh::NormalJoinInfo normal_info;
129 for (const Object *object : objects_to_join) {
130 const Mesh &mesh = *static_cast<const Mesh *>(object->data);
131 normal_info.add_mesh(mesh);
132 }
133
134 bke::MutableAttributeAccessor dst_attributes = dst_mesh.attributes_for_write();
135 switch (normal_info.result_type) {
137 break;
138 }
141 "custom_normal", bke::AttrDomain::Corner);
142 for (const int i : objects_to_join.index_range()) {
143 const Object &src_object = *objects_to_join[i];
144 const Mesh &src_mesh = *static_cast<const Mesh *>(src_object.data);
145 const bke::AttributeAccessor attributes = src_mesh.attributes();
146 const bke::GAttributeReader src = attributes.lookup("custom_normal");
147 if (!src) {
148 dst_attr.span.slice(corner_ranges[i]).fill(short2(0));
149 continue;
150 }
152 if (!bke::mesh::is_corner_fan_normals({src.domain, data_type})) {
153 dst_attr.span.slice(corner_ranges[i]).fill(short2(0));
154 continue;
155 }
156 src.typed<short2>().varray.materialize(dst_attr.span.slice(corner_ranges[i]));
157 }
158 dst_attr.finish();
159 break;
160 }
163 "custom_normal", *normal_info.result_domain);
164 for (const int i : objects_to_join.index_range()) {
165 const Object &src_object = *objects_to_join[i];
166 const Mesh &src_mesh = *static_cast<const Mesh *>(src_object.data);
167 switch (*normal_info.result_domain) {
169 math::transform_normals(src_mesh.vert_normals(),
170 float3x3(world_to_dst_mesh),
171 dst_attr.span.slice(vert_ranges[i]));
172 break;
174 math::transform_normals(src_mesh.face_normals(),
175 float3x3(world_to_dst_mesh),
176 dst_attr.span.slice(face_ranges[i]));
177 break;
179 math::transform_normals(src_mesh.corner_normals(),
180 float3x3(world_to_dst_mesh),
181 dst_attr.span.slice(corner_ranges[i]));
182 break;
183 default:
185 }
186 }
187 dst_attr.finish();
188 break;
189 }
190 }
191}
192
193static void join_shape_keys(Main *bmain,
194 const Span<const Object *> objects_to_join,
195 const OffsetIndices<int> vert_ranges,
196 const float4x4 &world_to_active_mesh,
197 Mesh &active_mesh)
198{
199 const int dst_verts_num = vert_ranges.total_size();
200 Vector<KeyBlock *> key_blocks;
201 VectorSet<std::string> key_names;
202 if (Key *key = active_mesh.key) {
203 LISTBASE_FOREACH (KeyBlock *, kb, &key->block) {
204 kb->data = MEM_reallocN(kb->data, sizeof(float3) * dst_verts_num);
205 kb->totelem = dst_verts_num;
206 key_names.add_new(kb->name);
207 key_blocks.append(kb);
208 }
209 }
210
211 const auto ensure_dst_key = [&]() {
212 if (!active_mesh.key) {
213 active_mesh.key = BKE_key_add(bmain, &active_mesh.id);
214 active_mesh.key->type = KEY_RELATIVE;
215 }
216 };
217
218 const Span<float3> active_mesh_positions = active_mesh.vert_positions();
219
220 for (const int i : objects_to_join.index_range().drop_front(1)) {
221 const Key *src_key = static_cast<const Mesh *>(objects_to_join[i]->data)->key;
222 if (!src_key) {
223 continue;
224 }
225 ensure_dst_key();
226 LISTBASE_FOREACH (const KeyBlock *, src_kb, &src_key->block) {
227 if (key_names.add_as(src_kb->name)) {
228 KeyBlock *dst_kb = BKE_keyblock_add(active_mesh.key, src_kb->name);
229 BKE_keyblock_copy_settings(dst_kb, src_kb);
230 dst_kb->data = MEM_malloc_arrayN<float3>(dst_verts_num, __func__);
231 dst_kb->totelem = dst_verts_num;
232
233 /* Initialize the new shape key data with the base positions for the active object. */
234 MutableSpan<float3> key_data(static_cast<float3 *>(dst_kb->data), dst_kb->totelem);
235 key_data.take_front(vert_ranges[0].size()).copy_from(active_mesh_positions);
236
237 /* Remap `KeyBlock::relative`. */
238 if (const KeyBlock *src_kb_relative = static_cast<KeyBlock *>(
239 BLI_findlink(&src_key->block, src_kb->relative)))
240 {
241 dst_kb->relative = key_names.index_of_as(src_kb_relative->name);
242 }
243 }
244 }
245 }
246
247 Key *dst_key = active_mesh.key;
248 if (!dst_key) {
249 return;
250 }
251
252 for (const int i : objects_to_join.index_range().drop_front(1)) {
253 const Object &src_object = *objects_to_join[i];
254 const IndexRange dst_range = vert_ranges[i];
255 const Mesh &src_mesh = *static_cast<const Mesh *>(src_object.data);
256 const Span<float3> src_positions = src_mesh.vert_positions();
257 const float4x4 transform = world_to_active_mesh * src_object.object_to_world();
258
259 LISTBASE_FOREACH (KeyBlock *, kb, &dst_key->block) {
260 MutableSpan<float3> key_data(static_cast<float3 *>(kb->data), kb->totelem);
261 if (const KeyBlock *src_kb = src_mesh.key ? BKE_keyblock_find_name(src_mesh.key, kb->name) :
262 nullptr)
263 {
264 const Span<float3> src_kb_data(static_cast<float3 *>(src_kb->data), dst_range.size());
265 math::transform_points(src_kb_data, transform, key_data.slice(dst_range));
266 }
267 else {
268 math::transform_points(src_positions, transform, key_data.slice(dst_range));
269 }
270 }
271 }
272}
273
274static void join_generic_attributes(const Span<const Object *> objects_to_join,
275 const VectorSet<std::string> &all_vertex_group_names,
276 const OffsetIndices<int> vert_ranges,
277 const OffsetIndices<int> edge_ranges,
278 const OffsetIndices<int> face_ranges,
279 const OffsetIndices<int> corner_ranges,
280 Mesh &dst_mesh)
281{
282 Set<StringRef> skip_names{"position",
283 ".edge_verts",
284 ".corner_vert",
285 ".corner_edge",
286 "material_index",
287 "custom_normal",
288 ".sculpt_face_set"};
289
290 Array<std::string> names;
292 {
294 for (const int i : objects_to_join.index_range()) {
295 const Mesh &mesh = *static_cast<const Mesh *>(objects_to_join[i]->data);
296 mesh.attributes().foreach_attribute([&](const bke::AttributeIter &attr) {
297 if (skip_names.contains(attr.name) || all_vertex_group_names.contains(attr.name)) {
298 return;
299 }
300 attr_info.add(attr.name, {attr.domain, attr.data_type});
301 });
302 }
303 names.reinitialize(attr_info.names.size());
304 kinds.reinitialize(attr_info.names.size());
305 for (const int i : attr_info.names.index_range()) {
306 names[i] = attr_info.names[i];
307 kinds[i] = attr_info.kinds[i];
308 }
309 }
310
311 bke::MutableAttributeAccessor dst_attributes = dst_mesh.attributes_for_write();
312
313 const Set<StringRefNull> attribute_names = dst_attributes.all_ids();
314 for (const int attr_i : names.index_range()) {
315 const StringRef name = names[attr_i];
316 const bke::AttrDomain domain = kinds[attr_i].domain;
317 const bke::AttrType data_type = kinds[attr_i].data_type;
318 if (const std::optional<bke::AttributeMetaData> meta_data = dst_attributes.lookup_meta_data(
319 name))
320 {
321 if (meta_data->domain != domain || meta_data->data_type != data_type) {
322 AttributeOwner owner = AttributeOwner::from_id(&dst_mesh.id);
324 owner, dst_attributes, name, meta_data->domain, meta_data->data_type, nullptr);
325 }
326 }
327 else {
328 dst_attributes.add(name, domain, data_type, bke::AttributeInitConstruct());
329 }
330 }
331
332 for (const int attr_i : names.index_range()) {
333 const StringRef name = names[attr_i];
334 const bke::AttrDomain domain = kinds[attr_i].domain;
335 const bke::AttrType data_type = kinds[attr_i].data_type;
336
338 for (const int i : objects_to_join.index_range()) {
339 const Mesh &src_mesh = *static_cast<const Mesh *>(objects_to_join[i]->data);
340 const bke::AttributeAccessor src_attributes = src_mesh.attributes();
341 const GVArray src = *src_attributes.lookup_or_default(name, domain, data_type);
342
343 const IndexRange dst_range = [&]() {
344 switch (domain) {
346 return vert_ranges[i];
348 return edge_ranges[i];
350 return face_ranges[i];
352 return corner_ranges[i];
353 default:
355 return IndexRange();
356 }
357 }();
358
359 src.materialize(dst.span.slice(dst_range).data());
360 }
361 dst.finish();
362 }
363}
364
366 const OffsetIndices<int> face_ranges,
367 Mesh &dst_mesh)
368{
369 VectorSet<Material *> materials;
370 for (const int i : objects_to_join.index_range()) {
371 const Object &src_object = *objects_to_join[i];
372 const Mesh &src_mesh = *static_cast<const Mesh *>(src_object.data);
373 if (src_mesh.totcol == 0) {
374 materials.add(nullptr);
375 continue;
376 }
377 for (const int material_index : IndexRange(src_mesh.totcol)) {
378 Material *material = BKE_object_material_get(&const_cast<Object &>(src_object),
379 material_index + 1);
380 if (materials.size() < MAXMAT) {
381 materials.add(material);
382 }
383 }
384 }
385
386 bke::MutableAttributeAccessor dst_attributes = dst_mesh.attributes_for_write();
387 if (materials.size() <= 1) {
388 dst_attributes.remove("material_index");
389 return materials;
390 }
391
392 bke::SpanAttributeWriter dst_attr = dst_attributes.lookup_or_add_for_write_only_span<int>(
393 "material_index", bke::AttrDomain::Face);
394 if (!dst_attr) {
395 return {};
396 }
397
398 for (const int i : objects_to_join.index_range()) {
399 const Object &src_object = *objects_to_join[i];
400 const IndexRange dst_range = face_ranges[i];
401 const Mesh &src_mesh = *static_cast<const Mesh *>(src_object.data);
402 const bke::AttributeAccessor src_attributes = src_mesh.attributes();
403
404 const VArray<int> material_indices = *src_attributes.lookup<int>("material_index",
406 if (material_indices.is_empty()) {
407 Material *first_material = src_mesh.totcol == 0 ?
408 nullptr :
409 BKE_object_material_get(&const_cast<Object &>(src_object), 1);
410 dst_attr.span.slice(dst_range).fill(materials.index_of(first_material));
411 continue;
412 }
413
414 if (src_mesh.totcol == 0) {
415 /* These material indices are invalid, but copy them anyway to avoid destroying user data. */
416 material_indices.materialize(dst_range.index_range(), dst_attr.span.slice(dst_range));
417 continue;
418 }
419
420 Array<int, 32> index_map(src_mesh.totcol);
421 for (const int material_index : IndexRange(src_mesh.totcol)) {
422 Material *material = BKE_object_material_get(&const_cast<Object &>(src_object),
423 material_index + 1);
424 const int dst_index = materials.index_of_try(material);
425 index_map[material_index] = dst_index == -1 ? 0 : dst_index;
426 }
427
428 const int max = src_mesh.totcol - 1;
429 for (const int face : dst_range.index_range()) {
430 const int src = std::clamp(material_indices[face], 0, max);
431 dst_attr.span[dst_range[face]] = index_map[src];
432 }
433 }
434
435 dst_attr.finish();
436
437 return materials;
438}
439
440/* Face Sets IDs are a sparse sequence, so this function offsets all the IDs by face_set_offset and
441 * updates face_set_offset with the maximum ID value. This way, when used in multiple meshes, all
442 * of them will have different IDs for their Face Sets. */
443static void join_face_sets(const Span<const Object *> objects_to_join,
444 const OffsetIndices<int> face_ranges,
445 Mesh &dst_mesh)
446{
447 if (std::none_of(objects_to_join.begin(), objects_to_join.end(), [](const Object *object) {
448 const Mesh &mesh = *static_cast<const Mesh *>(object->data);
449 return mesh.attributes().contains(".sculpt_face_set");
450 }))
451 {
452 return;
453 }
454
455 bke::MutableAttributeAccessor dst_attributes = dst_mesh.attributes_for_write();
456 bke::SpanAttributeWriter dst_face_sets = dst_attributes.lookup_or_add_for_write_span<int>(
457 ".sculpt_face_set", bke::AttrDomain::Face);
458 if (!dst_face_sets) {
459 return;
460 }
461
462 int max_face_set = 0;
463 for (const int i : objects_to_join.index_range()) {
464 const Object &src_object = *objects_to_join[i];
465 const IndexRange dst_range = face_ranges[i];
466 const Mesh &src_mesh = *static_cast<const Mesh *>(src_object.data);
467 const bke::AttributeAccessor src_attributes = src_mesh.attributes();
468 const VArraySpan src_face_sets = *src_attributes.lookup<int>(".sculpt_face_set",
470 if (src_face_sets.is_empty()) {
471 dst_face_sets.span.slice(dst_range).fill(max_face_set);
472 }
473 else {
474 for (const int face : dst_range.index_range()) {
475 dst_face_sets.span[dst_range[face]] = src_face_sets[face] + max_face_set;
476 }
477 max_face_set = std::max(
478 max_face_set,
479 *std::max_element(src_face_sets.begin(), src_face_sets.begin() + dst_range.size()));
480 }
481 max_face_set++;
482 }
483 dst_face_sets.finish();
484}
485
487{
488 Main *bmain = CTX_data_main(C);
489 Scene *scene = CTX_data_scene(C);
490 Object *const active_object = CTX_data_active_object(C);
491
492 if (active_object->mode & OB_MODE_EDIT) {
493 BKE_report(op->reports, RPT_WARNING, "Cannot join while in edit mode");
494 return OPERATOR_CANCELLED;
495 }
496
497 /* active_object is the object we are adding geometry to */
498 if (!active_object || active_object->type != OB_MESH) {
499 BKE_report(op->reports, RPT_WARNING, "Active object is not a mesh");
500 return OPERATOR_CANCELLED;
501 }
502
504
505 Vector<Object *> objects_to_join;
506 CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
507 if (ob_iter->type == OB_MESH) {
508 objects_to_join.append(ob_iter);
509 }
510 }
512
513 {
514 /* Make sure the active object is first, that way its data will be first in the new mesh. */
515 const int active_index = objects_to_join.as_span().first_index_try(active_object);
516 if (active_index == -1) {
517 BKE_report(op->reports, RPT_WARNING, "Active object is not a selected mesh");
518 return OPERATOR_CANCELLED;
519 }
520 objects_to_join.remove(active_index);
521 objects_to_join.prepend(active_object);
522 }
523
524 Array<int> vert_offset_data(objects_to_join.size() + 1);
525 Array<int> edge_offset_data(objects_to_join.size() + 1);
526 Array<int> face_offset_data(objects_to_join.size() + 1);
527 Array<int> corner_offset_data(objects_to_join.size() + 1);
528 for (const int i : objects_to_join.index_range()) {
529 const Mesh &mesh = *static_cast<const Mesh *>(objects_to_join[i]->data);
530 vert_offset_data[i] = mesh.verts_num;
531 edge_offset_data[i] = mesh.edges_num;
532 face_offset_data[i] = mesh.faces_num;
533 corner_offset_data[i] = mesh.corners_num;
534 }
535
537 vert_offset_data);
539 edge_offset_data);
541 face_offset_data);
543 corner_offset_data);
544
545 /* Apply parent transform if the active object's parent was joined to it.
546 * NOTE: This doesn't apply recursive parenting. */
547 if (objects_to_join.contains(active_object->parent)) {
548 active_object->parent = nullptr;
549 BKE_object_apply_mat4_ex(active_object,
550 active_object->object_to_world().ptr(),
551 active_object->parent,
552 active_object->parentinv,
553 false);
554 }
555
556 /* Only join meshes if there are verts to join,
557 * there aren't too many, and we only had one mesh selected. */
558 Mesh *active_mesh = (Mesh *)active_object->data;
559
560 if (ELEM(vert_ranges.total_size(), 0, active_mesh->verts_num)) {
561 BKE_report(op->reports, RPT_WARNING, "No mesh data to join");
562 return OPERATOR_CANCELLED;
563 }
564
565 if (vert_ranges.total_size() > MESH_MAX_VERTS) {
568 "Joining results in %d vertices, limit is %ld",
569 vert_ranges.total_size(),
571 return OPERATOR_CANCELLED;
572 }
573
574 Mesh *dst_mesh = BKE_mesh_new_nomain(vert_ranges.total_size(),
575 edge_ranges.total_size(),
576 face_ranges.total_size(),
577 corner_ranges.total_size());
578
579 /* Inverse transform for all selected meshes in this object,
580 * See #object_join_exec for detailed comment on why the safe version is used. */
581 float4x4 world_to_active_object;
582 invert_m4_m4_safe_ortho(world_to_active_object.ptr(), active_object->object_to_world().ptr());
583
584 join_shape_keys(bmain, objects_to_join, vert_ranges, world_to_active_object, *active_mesh);
585 join_positions(objects_to_join, vert_ranges, world_to_active_object, *dst_mesh);
587 objects_to_join, vert_ranges, face_ranges, corner_ranges, world_to_active_object, *dst_mesh);
588
589 MutableSpan<int2> dst_edges = dst_mesh->edges_for_write();
590 for (const int i : objects_to_join.index_range()) {
591 const Object &src_object = *objects_to_join[i];
592 const IndexRange dst_range = edge_ranges[i];
593 const Mesh &src_mesh = *static_cast<const Mesh *>(src_object.data);
594 const Span<int2> src_edges = src_mesh.edges();
595 for (const int edge : dst_range.index_range()) {
596 dst_edges[dst_range[edge]] = src_edges[edge] + int(vert_ranges[i].start());
597 }
598 }
599
600 MutableSpan<int> dst_corner_verts = dst_mesh->corner_verts_for_write();
601 for (const int i : objects_to_join.index_range()) {
602 const Object &src_object = *objects_to_join[i];
603 const IndexRange dst_range = corner_ranges[i];
604 const Mesh &src_mesh = *static_cast<const Mesh *>(src_object.data);
605 const Span<int> src_corner_verts = src_mesh.corner_verts();
606 for (const int corner : dst_range.index_range()) {
607 dst_corner_verts[dst_range[corner]] = src_corner_verts[corner] + int(vert_ranges[i].start());
608 }
609 }
610
611 MutableSpan<int> dst_corner_edges = dst_mesh->corner_edges_for_write();
612 for (const int i : objects_to_join.index_range()) {
613 const Object &src_object = *objects_to_join[i];
614 const IndexRange dst_range = corner_ranges[i];
615 const Mesh &src_mesh = *static_cast<const Mesh *>(src_object.data);
616 const Span<int> src_corner_edges = src_mesh.corner_edges();
617 for (const int corner : dst_range.index_range()) {
618 dst_corner_edges[dst_range[corner]] = src_corner_edges[corner] + int(edge_ranges[i].start());
619 }
620 }
621
622 if (dst_mesh->faces_num > 0) {
623 MutableSpan<int> dst_face_offsets = dst_mesh->face_offsets_for_write();
624 for (const int i : objects_to_join.index_range()) {
625 const Object &src_object = *objects_to_join[i];
626 const IndexRange dst_range = face_ranges[i];
627 const Mesh &src_mesh = *static_cast<const Mesh *>(src_object.data);
628 const Span<int> src_face_offsets = src_mesh.face_offsets();
629 for (const int face : dst_range.index_range()) {
630 dst_face_offsets[dst_range[face]] = src_face_offsets[face] + corner_ranges[i].start();
631 }
632 }
633 dst_face_offsets.last() = dst_mesh->corners_num;
634 }
635
636 join_face_sets(objects_to_join, face_ranges, *dst_mesh);
637
638 VectorSet<Material *> materials = join_materials(objects_to_join, face_ranges, *dst_mesh);
639
640 VectorSet<std::string> vertex_group_names = join_vertex_groups(
641 objects_to_join, vert_ranges, *dst_mesh);
642
643 join_generic_attributes(objects_to_join,
644 vertex_group_names,
645 vert_ranges,
646 edge_ranges,
647 face_ranges,
648 corner_ranges,
649 *dst_mesh);
650
655
656 /* Copy multires data to the out-of-main mesh. */
657 if (get_multires_modifier(scene, active_object, true)) {
658 if (std::any_of(objects_to_join.begin(), objects_to_join.end(), [](const Object *object) {
659 const Mesh &src_mesh = *static_cast<const Mesh *>(object->data);
660 return CustomData_has_layer(&src_mesh.corner_data, CD_MDISPS);
661 }))
662 {
663 MDisps *dst = static_cast<MDisps *>(CustomData_add_layer(
664 &dst_mesh->corner_data, CD_MDISPS, CD_CONSTRUCT, dst_mesh->corners_num));
665 for (const int i : objects_to_join.index_range()) {
666 const Mesh &src_mesh = *static_cast<const Mesh *>(objects_to_join[i]->data);
667 if (const void *src = CustomData_get_layer(&src_mesh.corner_data, CD_MDISPS)) {
669 CD_MDISPS, src, &dst[corner_ranges[i].first()], src_mesh.corners_num);
670 }
671 }
672 }
673 if (std::any_of(objects_to_join.begin(), objects_to_join.end(), [](const Object *object) {
674 const Mesh &src_mesh = *static_cast<const Mesh *>(object->data);
675 return CustomData_has_layer(&src_mesh.corner_data, CD_GRID_PAINT_MASK);
676 }))
677 {
679 &dst_mesh->corner_data, CD_GRID_PAINT_MASK, CD_CONSTRUCT, dst_mesh->corners_num));
680 for (const int i : objects_to_join.index_range()) {
681 const Mesh &src_mesh = *static_cast<const Mesh *>(objects_to_join[i]->data);
682 if (const void *src = CustomData_get_layer(&src_mesh.corner_data, CD_GRID_PAINT_MASK)) {
684 CD_GRID_PAINT_MASK, src, &dst[corner_ranges[i].first()], src_mesh.corners_num);
685 }
686 }
687 }
688 }
689 for (const int i : objects_to_join.index_range().drop_front(1)) {
690 Object &src_object = *objects_to_join[i];
691 multiresModifier_prepare_join(depsgraph, scene, &src_object, active_object);
692 if (MultiresModifierData *mmd = get_multires_modifier(scene, &src_object, true)) {
694 bmain, &src_object, true, object::multires_update_totlevels, &mmd->totlvl);
695 }
696 }
697
698 BKE_mesh_nomain_to_mesh(dst_mesh, active_mesh, active_object, false);
699
700 for (Object *object : objects_to_join.as_span().drop_front(1)) {
701 object::base_free_and_unlink(bmain, scene, object);
702 }
703
704 /* old material array */
705 for (const int a : IndexRange(active_object->totcol)) {
706 if (Material *ma = active_object->mat[a]) {
707 id_us_min(&ma->id);
708 }
709 }
710 for (const int a : IndexRange(active_object->totcol)) {
711 if (Material *ma = active_mesh->mat[a]) {
712 id_us_min(&ma->id);
713 }
714 }
715 MEM_SAFE_FREE(active_object->mat);
716 MEM_SAFE_FREE(active_object->matbits);
717 MEM_SAFE_FREE(active_mesh->mat);
718
719 /* If the object had no slots, don't add an empty one. */
720 if (active_object->totcol == 0 && materials.size() == 1 && materials[0] == nullptr) {
721 materials.clear();
722 }
723
724 const int totcol = materials.size();
725 if (totcol) {
726 VectorData data = materials.extract_vector().release();
727 active_mesh->mat = data.data;
728 for (const int i : IndexRange(totcol)) {
729 if (Material *ma = active_mesh->mat[i]) {
730 id_us_plus((ID *)ma);
731 }
732 }
733 active_object->mat = MEM_calloc_arrayN<Material *>(totcol, __func__);
734 active_object->matbits = MEM_calloc_arrayN<char>(totcol, __func__);
735 }
736
737 active_object->totcol = active_mesh->totcol = totcol;
738
739 /* other mesh users */
740 BKE_objects_materials_sync_length_all(bmain, &active_mesh->id);
741
742 /* ensure newly inserted keys are time sorted */
743 if (Key *key = active_mesh->key) {
744 if (key->type != KEY_RELATIVE) {
745 BKE_key_sort(key);
746 }
747 }
748
749 /* Due to dependency cycle some other object might access old derived data. */
750 BKE_object_free_derived_caches(active_object);
751
752 DEG_relations_tag_update(bmain); /* removed objects, need to rebuild dag */
753
755
759
760 return OPERATOR_FINISHED;
761}
762
763} // namespace blender::ed::mesh
void BKE_id_attributes_default_color_set(struct ID *id, std::optional< blender::StringRef > name)
std::optional< blender::StringRef > BKE_id_attributes_default_color_name(const struct ID *id)
std::optional< blender::StringRef > BKE_id_attributes_active_color_name(const struct ID *id)
void BKE_id_attributes_active_color_set(struct ID *id, std::optional< blender::StringRef > name)
Definition attribute.cc:985
#define CTX_DATA_BEGIN(C, Type, instance, member)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
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
CustomData interface, see also DNA_customdata_types.h.
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
@ CD_CONSTRUCT
void CustomData_copy_elements(eCustomDataType type, const void *src_data, void *dst_data, int count)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
void * CustomData_add_layer(CustomData *data, eCustomDataType type, eCDAllocType alloctype, int totelem)
support for deformation groups and hooks.
bDeformGroup * BKE_defgroup_duplicate(const bDeformGroup *ingroup)
Definition deform.cc:82
Key * BKE_key_add(Main *bmain, ID *id)
Definition key.cc:225
KeyBlock * BKE_keyblock_add(Key *key, const char *name)
Definition key.cc:1802
void BKE_keyblock_copy_settings(KeyBlock *kb_dst, const KeyBlock *kb_src)
copy shape-key attributes, but not key data or name/UID.
Definition key.cc:1931
KeyBlock * BKE_keyblock_find_name(Key *key, const char name[])
Definition key.cc:1916
void BKE_key_sort(Key *key)
Definition key.cc:277
void id_us_plus(ID *id)
Definition lib_id.cc:358
void id_us_min(ID *id)
Definition lib_id.cc:366
General operations, lookup, etc. for materials.
void BKE_objects_materials_sync_length_all(Main *bmain, ID *id)
Material * BKE_object_material_get(Object *ob, short act)
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob, bool process_shape_keys=true)
void multiresModifier_prepare_join(Depsgraph *depsgraph, Scene *scene, Object *ob, Object *to_ob)
Definition multires.cc:718
MultiresModifierData * get_multires_modifier(Scene *scene, Object *ob, bool use_first)
Definition multires.cc:178
General operations, lookup, etc. for blender objects.
void BKE_object_apply_mat4_ex(Object *ob, const float mat[4][4], Object *parent, const float parentinv[4][4], bool use_compat)
void BKE_object_free_derived_caches(Object *ob)
Functions for dealing with objects and deform verts, used by painting and tools.
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void invert_m4_m4_safe_ortho(float inverse[4][4], const float mat[4][4])
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1054
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ CD_MDEFORMVERT
@ CD_GRID_PAINT_MASK
@ KEY_RELATIVE
#define MAXMAT
#define MESH_MAX_VERTS
@ OB_MODE_EDIT
Object is a sort of wrapper for general info.
@ OB_MESH
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
#define C
Definition RandGen.cpp:29
#define ND_OB_ACTIVE
Definition WM_types.hh:440
#define NC_SCENE
Definition WM_types.hh:378
#define ND_LAYER_CONTENT
Definition WM_types.hh:453
BMesh const char void * data
BPy_StructRNA * depsgraph
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static AttributeOwner from_id(ID *id)
Definition attribute.cc:44
AttributeSet attributes
IndexRange index_range() const
Definition BLI_array.hh:360
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
GMutableSpan slice(const int64_t start, int64_t size) const
void materialize(void *dst) const
constexpr int64_t size() const
constexpr IndexRange index_range() const
constexpr IndexRange drop_front(int64_t n) const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
constexpr MutableSpan take_front(const int64_t n) const
Definition BLI_span.hh:629
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
bool contains(const Key &key) const
Definition BLI_set.hh:310
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr const T * begin() const
Definition BLI_span.hh:220
constexpr bool is_empty() const
Definition BLI_span.hh:260
void materialize(MutableSpan< T > r_span) const
int64_t index_of_try(const Key &key) const
bool add(const Key &key)
int64_t index_of_as(const ForwardKey &key) const
bool add_as(ForwardKey &&key)
int64_t size() const
int64_t index_of(const Key &key) const
bool contains(const Key &key) const
void add_new(const Key &key)
int64_t size() const
void prepend(const T &value)
bool contains(const T &value) const
void append(const T &value)
void remove(const int64_t index)
IndexRange index_range() const
Span< T > as_span() const
VectorData< T, Allocator > release()
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
GAttributeReader lookup(const StringRef attribute_id) const
Set< StringRefNull > all_ids() const
std::optional< AttributeMetaData > lookup_meta_data(StringRef attribute_id) const
bool add(const StringRef attribute_id, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer)
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bool remove(const StringRef attribute_id)
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
std::optional< bke::AttrDomain > result_domain
Definition BKE_mesh.hh:263
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
bool is_corner_fan_normals(const AttributeMetaData &meta_data)
AttrType cpp_type_to_attribute_type(const CPPType &type)
bool convert_attribute(AttributeOwner &owner, bke::MutableAttributeAccessor attributes, const StringRef name, const bke::AttrDomain dst_domain, const bke::AttrType dst_type, ReportList *reports)
static void join_generic_attributes(const Span< const Object * > objects_to_join, const VectorSet< std::string > &all_vertex_group_names, const OffsetIndices< int > vert_ranges, const OffsetIndices< int > edge_ranges, const OffsetIndices< int > face_ranges, const OffsetIndices< int > corner_ranges, Mesh &dst_mesh)
Definition mesh_join.cc:274
wmOperatorStatus join_objects_exec(bContext *C, wmOperator *op)
Definition mesh_join.cc:486
static void join_positions(const Span< const Object * > objects_to_join, const OffsetIndices< int > vert_ranges, const float4x4 &world_to_dst_mesh, Mesh &dst_mesh)
Definition mesh_join.cc:105
static void join_face_sets(const Span< const Object * > objects_to_join, const OffsetIndices< int > face_ranges, Mesh &dst_mesh)
Definition mesh_join.cc:443
static VectorSet< Material * > join_materials(const Span< const Object * > objects_to_join, const OffsetIndices< int > face_ranges, Mesh &dst_mesh)
Definition mesh_join.cc:365
static void join_shape_keys(Main *bmain, const Span< const Object * > objects_to_join, const OffsetIndices< int > vert_ranges, const float4x4 &world_to_active_mesh, Mesh &active_mesh)
Definition mesh_join.cc:193
static VectorSet< std::string > join_vertex_groups(const Span< const Object * > objects_to_join, const OffsetIndices< int > vert_ranges, Mesh &dst_mesh)
Definition mesh_join.cc:57
static void join_normals(const Span< const Object * > objects_to_join, const OffsetIndices< int > vert_ranges, const OffsetIndices< int > face_ranges, const OffsetIndices< int > corner_ranges, const float4x4 &world_to_dst_mesh, Mesh &dst_mesh)
Definition mesh_join.cc:121
bool iter_other(Main *bmain, Object *orig_ob, bool include_orig, bool(*callback)(Object *ob, void *callback_data), void *callback_data)
bool multires_update_totlevels(Object *ob, void *totlevel_v)
void base_free_and_unlink(Main *bmain, Scene *scene, Object *ob)
void transform_normals(const float3x3 &transform, MutableSpan< float3 > normals)
void transform_points(const float4x4 &transform, MutableSpan< float3 > points, bool use_threading=true)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
MatBase< float, 4, 4 > float4x4
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
blender::VecBase< int16_t, 2 > short2
const char * name
Definition DNA_ID.h:414
short relative
void * data
char type
ListBase block
struct MDeformWeight * dw
unsigned int def_nr
int corners_num
struct Material ** mat
CustomData corner_data
ListBase vertex_group_names
CustomData vert_data
short totcol
struct Key * key
int faces_num
int verts_num
struct Material ** mat
char * matbits
float parentinv[4][4]
struct Object * parent
const c_style_mat & ptr() const
AttributeReader< T > typed() const
void add(const StringRef name, const AttributeDomainAndType &kind)
Vector< AttributeDomainAndType, 16 > kinds
struct ReportList * reports
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
void WM_event_add_notifier(const bContext *C, uint type, void *reference)