Blender V5.0
bmesh_mesh_convert.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
73
74#include "DNA_key_types.h"
75#include "DNA_mesh_types.h"
76#include "DNA_meshdata_types.h"
77#include "DNA_modifier_types.h"
78#include "DNA_object_types.h"
79
80#include "MEM_guardedalloc.h"
81
82#include "BLI_alloca.h"
83#include "BLI_array.hh"
84#include "BLI_index_range.hh"
85#include "BLI_listbase.h"
86#include "BLI_math_vector.h"
87#include "BLI_span.hh"
88#include "BLI_string_ref.hh"
89#include "BLI_task.hh"
90#include "BLI_vector.hh"
91
92#include "BKE_attribute.hh"
93#include "BKE_customdata.hh"
94#include "BKE_mesh.hh"
95#include "BKE_mesh_runtime.hh"
96#include "BKE_multires.hh"
97
98#include "BKE_key.hh"
99#include "BKE_main.hh"
100
101#include "DEG_depsgraph_query.hh"
102
103#include "bmesh.hh"
104
105#include "CLG_log.h"
106
107static CLG_LogRef LOG = {"geom.bmesh.convert"};
108
109using blender::Array;
110using blender::float3;
113using blender::Span;
115using blender::Vector;
117
119{
120 if (name.is_empty()) {
121 return false;
122 }
123 if (name[0] == '.') {
124 return ELEM(name,
125 ".edge_verts",
126 ".corner_vert",
127 ".corner_edge",
128 ".hide_vert",
129 ".hide_edge",
130 ".hide_poly",
131 ".select_vert",
132 ".select_edge",
133 ".select_poly",
134 ".uv_select_vert",
135 ".uv_select_edge",
136 ".uv_select_face");
137 }
138 return ELEM(name, "position", "uv_seam", "material_index", "sharp_face", "sharp_edge");
139}
140
142 Span<int> face_verts,
143 Span<int> face_edges,
144 Span<BMVert *> vtable,
145 Span<BMEdge *> etable)
146{
147 const int size = face_verts.size();
150
151 for (const int i : IndexRange(size)) {
152 verts[i] = vtable[face_verts[i]];
153 edges[i] = etable[face_edges[i]];
154 }
155
156 return BM_face_create(&bm, verts.data(), edges.data(), size, nullptr, BM_CREATE_SKIP_CD);
157}
158
168
173 CustomData &bm_data)
174{
176 std::array<int, CD_NUMTYPES> per_type_index;
177 per_type_index.fill(0);
178 for (const int i : IndexRange(bm_data.totlayer)) {
179 const CustomDataLayer &bm_layer = bm_data.layers[i];
180 const eCustomDataType type = eCustomDataType(bm_layer.type);
181 const int mesh_layer_index =
182 bm_layer.name[0] == '\0' ?
183 CustomData_get_layer_index_n(&mesh_data, type, per_type_index[type]) :
184 CustomData_get_named_layer_index(&mesh_data, type, bm_layer.name);
185
187 info.type = type;
188 info.bmesh_offset = bm_layer.offset;
189 info.mesh_data = (mesh_layer_index == -1) ? nullptr : mesh_data.layers[mesh_layer_index].data;
190 info.elem_size = CustomData_get_elem_size(&bm_layer);
191 infos.append(info);
192
193 per_type_index[type]++;
194 }
195 return infos;
196}
197
199 const Span<MeshToBMeshLayerInfo> copy_info,
200 const int mesh_index,
201 BMHeader &header)
202{
204 for (const MeshToBMeshLayerInfo &info : copy_info) {
205 if (info.mesh_data) {
207 POINTER_OFFSET(info.mesh_data, info.elem_size * mesh_index),
208 POINTER_OFFSET(header.data, info.bmesh_offset));
209 }
210 else {
211 CustomData_data_set_default_value(info.type, POINTER_OFFSET(header.data, info.bmesh_offset));
212 }
213 }
214}
215
217{
218 using namespace blender;
219 if (!mesh) {
220 /* Sanity check. */
221 return;
222 }
223 const bool is_new = !(bm->totvert || (bm->vdata.totlayer || bm->edata.totlayer ||
224 bm->pdata.totlayer || bm->ldata.totlayer));
225 KeyBlock *actkey;
226 float (*keyco)[3] = nullptr;
228 CustomData_MeshMasks_update(&mask, &params->cd_mask_extra);
229
231 mask.vmask);
233 mask.emask);
235 mask.pmask);
237 mask.lmask);
238
239 blender::Vector<std::string> temporary_layers_to_delete;
240
241 for (const int layer_index :
243 {
244 char buffer[MAX_CUSTOMDATA_LAYER_NAME];
245 {
247 CustomData_get_layer_name(&mesh_ldata, CD_PROP_FLOAT2, layer_index), buffer);
248 if (CustomData_get_named_layer_index(&mesh_ldata, CD_PROP_BOOL, name) < 0) {
250 &mesh_ldata, CD_PROP_BOOL, CD_SET_DEFAULT, mesh->corners_num, name);
251 temporary_layers_to_delete.append(std::string(name));
252 }
253 }
254 }
255
256 BLI_SCOPED_DEFER([&]() {
257 for (const std::string &name : temporary_layers_to_delete) {
258 CustomData_free_layer_named(&mesh_ldata, name);
259 }
260
261 MEM_SAFE_FREE(mesh_vdata.layers);
262 MEM_SAFE_FREE(mesh_edata.layers);
263 MEM_SAFE_FREE(mesh_pdata.layers);
264 MEM_SAFE_FREE(mesh_ldata.layers);
265 });
266
267 if (mesh->verts_num == 0) {
268 if (is_new) {
269 /* No verts? still copy custom-data layout. */
270 CustomData_init_layout_from(&mesh_vdata, &bm->vdata, mask.vmask, CD_CONSTRUCT, 0);
271 CustomData_init_layout_from(&mesh_edata, &bm->edata, mask.emask, CD_CONSTRUCT, 0);
272 CustomData_init_layout_from(&mesh_pdata, &bm->pdata, mask.pmask, CD_CONSTRUCT, 0);
273 CustomData_init_layout_from(&mesh_ldata, &bm->ldata, mask.lmask, CD_CONSTRUCT, 0);
274
279 }
280 return;
281 }
282
284 if (params->calc_vert_normal) {
285 vert_normals = mesh->vert_normals();
286 }
287
288 if (is_new) {
289 CustomData_init_layout_from(&mesh_vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, 0);
290 CustomData_init_layout_from(&mesh_edata, &bm->edata, mask.emask, CD_SET_DEFAULT, 0);
291 CustomData_init_layout_from(&mesh_pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, 0);
292 CustomData_init_layout_from(&mesh_ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, 0);
293 }
294 else {
296 &mesh_vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, bm, BM_VERT);
298 &mesh_edata, &bm->edata, mask.emask, CD_SET_DEFAULT, bm, BM_EDGE);
300 &mesh_pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, bm, BM_FACE);
302 &mesh_ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, bm, BM_LOOP);
303 }
304
305 /* -------------------------------------------------------------------- */
306 /* Shape Key */
307 int tot_shape_keys = 0;
308 if (mesh->key != nullptr && DEG_is_original(mesh)) {
309 /* Evaluated meshes can be topologically inconsistent with their shape keys.
310 * Shape keys are also already integrated into the state of the evaluated
311 * mesh, so considering them here would kind of apply them twice. */
312 tot_shape_keys = BLI_listbase_count(&mesh->key->block);
313
314 /* Original meshes must never contain a shape-key custom-data layers.
315 *
316 * This may happen if and object's mesh data is accidentally
317 * set to the output from the modifier stack, causing it to be an "original" ID,
318 * even though the data isn't fully compatible (hence this assert).
319 *
320 * This results in:
321 * - The newly created #BMesh having twice the number of custom-data layers.
322 * - When converting the #BMesh back to a regular mesh,
323 * At least one of the extra shape-key blocks will be created in #Mesh.key
324 * depending on the value of #CustomDataLayer.uid.
325 *
326 * We could support mixing both kinds of data if there is a compelling use-case for it.
327 * At the moment it's simplest to assume all original meshes use the key-block and meshes
328 * that are evaluated (through the modifier stack for example) use custom-data layers.
329 */
331 }
332 if (is_new == false) {
333 tot_shape_keys = min_ii(tot_shape_keys, CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY));
334 }
335 const float (**shape_key_table)[3] = tot_shape_keys ? (const float (**)[3])BLI_array_alloca(
336 shape_key_table, tot_shape_keys) :
337 nullptr;
338
339 if ((params->active_shapekey != 0) && tot_shape_keys > 0) {
340 actkey = static_cast<KeyBlock *>(BLI_findlink(&mesh->key->block, params->active_shapekey - 1));
341 }
342 else {
343 actkey = nullptr;
344 }
345
346 if (is_new) {
347 if (tot_shape_keys || params->add_key_index) {
349 }
350 }
351
352 if (tot_shape_keys) {
353 if (is_new) {
354 /* Check if we need to generate unique ids for the shape-keys.
355 * This also exists in the file reading code, but is here for a sanity check. */
356 if (!mesh->key->uidgen) {
357 fprintf(stderr,
358 "%s had to generate shape key uid's in a situation we shouldn't need to! "
359 "(bmesh internal error)\n",
360 __func__);
361
362 mesh->key->uidgen = 1;
363 LISTBASE_FOREACH (KeyBlock *, block, &mesh->key->block) {
364 block->uid = mesh->key->uidgen++;
365 }
366 }
367 }
368
369 if (actkey && actkey->totelem == mesh->verts_num) {
370 keyco = params->use_shapekey ? static_cast<float (*)[3]>(actkey->data) : nullptr;
371 if (is_new) {
372 bm->shapenr = params->active_shapekey;
373 }
374 }
375
376 int i;
377 KeyBlock *block;
378 for (i = 0, block = static_cast<KeyBlock *>(mesh->key->block.first); i < tot_shape_keys;
379 block = block->next, i++)
380 {
381 if (is_new) {
383 int j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i);
384 bm->vdata.layers[j].uid = block->uid;
385 }
386 shape_key_table[i] = static_cast<const float (*)[3]>(block->data);
387 }
388 }
389
390 const Vector<MeshToBMeshLayerInfo> vert_info = mesh_to_bm_copy_info_calc(mesh_vdata, bm->vdata);
391 const Vector<MeshToBMeshLayerInfo> edge_info = mesh_to_bm_copy_info_calc(mesh_edata, bm->edata);
392 const Vector<MeshToBMeshLayerInfo> poly_info = mesh_to_bm_copy_info_calc(mesh_pdata, bm->pdata);
393 const Vector<MeshToBMeshLayerInfo> loop_info = mesh_to_bm_copy_info_calc(mesh_ldata, bm->ldata);
394 if (is_new) {
399 }
400
401 /* Only copy these values over if the source mesh is flagged to be using them.
402 * Even if `bm` has these layers, they may have been added from another mesh, when `!is_new`. */
403 const int cd_shape_key_offset = tot_shape_keys ? CustomData_get_offset(&bm->vdata, CD_SHAPEKEY) :
404 -1;
405 const int cd_shape_keyindex_offset = is_new && (tot_shape_keys || params->add_key_index) ?
407 -1;
408
409 const bke::AttributeAccessor attributes = mesh->attributes();
410 const VArraySpan select_vert = *attributes.lookup<bool>(".select_vert", AttrDomain::Point);
411 const VArraySpan select_edge = *attributes.lookup<bool>(".select_edge", AttrDomain::Edge);
412 const VArraySpan select_poly = *attributes.lookup<bool>(".select_poly", AttrDomain::Face);
413 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", AttrDomain::Point);
414 const VArraySpan hide_edge = *attributes.lookup<bool>(".hide_edge", AttrDomain::Edge);
415 const VArraySpan hide_poly = *attributes.lookup<bool>(".hide_poly", AttrDomain::Face);
416 const VArraySpan material_indices = *attributes.lookup<int>("material_index", AttrDomain::Face);
417 const VArraySpan sharp_faces = *attributes.lookup<bool>("sharp_face", AttrDomain::Face);
418 const VArraySpan sharp_edges = *attributes.lookup<bool>("sharp_edge", AttrDomain::Edge);
419 const VArraySpan uv_seams = *attributes.lookup<bool>("uv_seam", AttrDomain::Edge);
420
421 const VArraySpan uv_select_vert = *attributes.lookup<bool>(".uv_select_vert",
423 const VArraySpan uv_select_edge = *attributes.lookup<bool>(".uv_select_edge",
425 const VArraySpan uv_select_face = *attributes.lookup<bool>(".uv_select_face", AttrDomain::Face);
426
427 const bool need_uv_select = is_new && (!uv_select_vert.is_empty() &&
428 !uv_select_edge.is_empty() && !uv_select_face.is_empty());
429
430 const Span<float3> positions = mesh->vert_positions();
431 Array<BMVert *> vtable(mesh->verts_num);
432 for (const int i : positions.index_range()) {
433 BMVert *v = vtable[i] = BM_vert_create(
434 bm, keyco ? keyco[i] : positions[i], nullptr, BM_CREATE_SKIP_CD);
435 BM_elem_index_set(v, i); /* set_ok */
436
437 if (!hide_vert.is_empty() && hide_vert[i]) {
439 }
440 if (!select_vert.is_empty() && select_vert[i]) {
441 BM_vert_select_set(bm, v, true);
442 }
443
444 if (!vert_normals.is_empty()) {
445 copy_v3_v3(v->no, vert_normals[i]);
446 }
447
448 mesh_attributes_copy_to_bmesh_block(bm->vdata, vert_info, i, v->head);
449
450 /* Set shape key original index. */
451 if (cd_shape_keyindex_offset != -1) {
452 BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, i);
453 }
454
455 /* Set shape-key data. */
456 if (tot_shape_keys) {
457 float (*co_dst)[3] = (float (*)[3])BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset);
458 for (int j = 0; j < tot_shape_keys; j++, co_dst++) {
459 copy_v3_v3(*co_dst, shape_key_table[j][i]);
460 }
461 }
462 }
463 if (is_new) {
464 bm->elem_index_dirty &= ~BM_VERT; /* Added in order, clear dirty flag. */
465 }
466
467 const Span<blender::int2> edges = mesh->edges();
468 Array<BMEdge *> etable(mesh->edges_num);
469 for (const int i : edges.index_range()) {
470 BMEdge *e = etable[i] = BM_edge_create(
471 bm, vtable[edges[i][0]], vtable[edges[i][1]], nullptr, BM_CREATE_SKIP_CD);
472 BM_elem_index_set(e, i); /* set_ok */
473
474 e->head.hflag = 0;
475 if (!uv_seams.is_empty() && uv_seams[i]) {
477 }
478 if (!hide_edge.is_empty() && hide_edge[i]) {
480 }
481 if (!select_edge.is_empty() && select_edge[i]) {
482 BM_edge_select_set(bm, e, true);
483 }
484 if (!(!sharp_edges.is_empty() && sharp_edges[i])) {
486 }
487
488 mesh_attributes_copy_to_bmesh_block(bm->edata, edge_info, i, e->head);
489 }
490 if (is_new) {
491 bm->elem_index_dirty &= ~BM_EDGE; /* Added in order, clear dirty flag. */
492 }
493
494 const blender::OffsetIndices faces = mesh->faces();
495 const Span<int> corner_verts = mesh->corner_verts();
496 const Span<int> corner_edges = mesh->corner_edges();
497
498 /* Only needed for selection. */
499
500 Array<BMFace *> ftable;
501 if (mesh->mselect && mesh->totselect != 0) {
502 ftable.reinitialize(mesh->faces_num);
503 }
504
505 int totloops = 0;
506 for (const int i : faces.index_range()) {
507 const IndexRange face = faces[i];
509 *bm, corner_verts.slice(face), corner_edges.slice(face), vtable, etable);
510 if (!ftable.is_empty()) {
511 ftable[i] = f;
512 }
513
514 if (UNLIKELY(f == nullptr)) {
515 printf(
516 "%s: Warning! Bad face in mesh"
517 " \"%s\" at index %d!, skipping\n",
518 __func__,
519 mesh->id.name + 2,
520 i);
521 continue;
522 }
523
524 /* Don't use 'i' since we may have skipped the face. */
525 BM_elem_index_set(f, bm->totface - 1); /* set_ok */
526
527 /* Transfer flag. */
528 if (!(!sharp_faces.is_empty() && sharp_faces[i])) {
530 }
531 if (!hide_poly.is_empty() && hide_poly[i]) {
533 }
534 if (!select_poly.is_empty() && select_poly[i]) {
535 BM_face_select_set(bm, f, true);
536 }
537
538 f->mat_nr = material_indices.is_empty() ? 0 : material_indices[i];
539 if (i == mesh->act_face) {
540 bm->act_face = f;
541 }
542
543 int j = face.start();
544 BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
545 BMLoop *l_iter = l_first;
546 do {
547 /* Don't use 'j' since we may have skipped some faces, hence some loops. */
548 BM_elem_index_set(l_iter, totloops++); /* set_ok */
549
550 mesh_attributes_copy_to_bmesh_block(bm->ldata, loop_info, j, l_iter->head);
551
552 if (need_uv_select) {
553 if (uv_select_vert[j]) {
555 }
556 if (uv_select_edge[j]) {
558 }
559 }
560
561 j++;
562 } while ((l_iter = l_iter->next) != l_first);
563
564 mesh_attributes_copy_to_bmesh_block(bm->pdata, poly_info, i, f->head);
565
566 if (need_uv_select) {
567 if (uv_select_face[i]) {
569 }
570 }
571
572 if (params->calc_face_normal) {
574 }
575 }
576 if (is_new) {
577 bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); /* Added in order, clear dirty flag. */
578 }
579
580 bm->uv_select_sync_valid = (need_uv_select && (mesh->flag & ME_FLAG_UV_SELECT_SYNC_VALID)) != 0;
581
582 /* -------------------------------------------------------------------- */
583 /* MSelect clears the array elements (to avoid adding multiple times).
584 *
585 * Take care to keep this last and not use (v/e/ftable) after this.
586 */
587
588 if (mesh->mselect && mesh->totselect != 0) {
589 for (const int i : IndexRange(mesh->totselect)) {
590 const MSelect &msel = mesh->mselect[i];
591
592 BMElem **ele_p;
593 switch (msel.type) {
594 case ME_VSEL:
595 ele_p = (BMElem **)&vtable[msel.index];
596 break;
597 case ME_ESEL:
598 ele_p = (BMElem **)&etable[msel.index];
599 break;
600 case ME_FSEL:
601 ele_p = (BMElem **)&ftable[msel.index];
602 break;
603 default:
604 continue;
605 }
606
607 if (*ele_p != nullptr) {
609 *ele_p = nullptr;
610 }
611 }
612 }
613 else {
615 }
616}
617
621static BMVert **bm_to_mesh_vertex_map(BMesh *bm, const int old_verts_num)
622{
623 const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
624 BMVert **vertMap = nullptr;
625 BMVert *eve;
626 int i = 0;
627 BMIter iter;
628
629 /* Caller needs to ensure this. */
630 BLI_assert(old_verts_num > 0);
631
632 vertMap = MEM_calloc_arrayN<BMVert *>(old_verts_num, "vertMap");
633 if (cd_shape_keyindex_offset != -1) {
634 BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
635 const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
636 if ((keyi != ORIGINDEX_NONE) && (keyi < old_verts_num) &&
637 /* Not fool-proof, but chances are if we have many verts with the same index,
638 * we will want to use the first one,
639 * since the second is more likely to be a duplicate. */
640 (vertMap[keyi] == nullptr))
641 {
642 vertMap[keyi] = eve;
643 }
644 }
645 }
646 else {
647 BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
648 if (i < old_verts_num) {
649 vertMap[i] = eve;
650 }
651 else {
652 break;
653 }
654 }
655 }
656
657 return vertMap;
658}
659
660/* -------------------------------------------------------------------- */
740
746{
747 int i;
748 int j = 0;
749
750 for (i = 0; i < bm->vdata.totlayer; i++) {
751 if (bm->vdata.layers[i].type == CD_SHAPEKEY) {
752 if (currkey->uid == bm->vdata.layers[i].uid) {
753 return j;
754 }
755 j++;
756 }
757 }
758 return -1;
759}
760
774 Key *key,
775 MutableSpan<float3> positions,
776 const bool active_shapekey_to_mvert)
777{
778 KeyBlock *actkey = static_cast<KeyBlock *>(BLI_findlink(&key->block, bm->shapenr - 1));
779
780 /* It's unlikely this ever remains false, check for correctness. */
781 bool actkey_has_layer = false;
782
783 /* Go through and find any shape-key custom-data layers
784 * that might not have corresponding KeyBlocks, and add them if necessary. */
785 for (int i = 0; i < bm->vdata.totlayer; i++) {
786 if (bm->vdata.layers[i].type != CD_SHAPEKEY) {
787 continue;
788 }
789
790 KeyBlock *currkey;
791 for (currkey = (KeyBlock *)key->block.first; currkey; currkey = currkey->next) {
792 if (currkey->uid == bm->vdata.layers[i].uid) {
793 break;
794 }
795 }
796
797 if (currkey) {
798 if (currkey == actkey) {
799 actkey_has_layer = true;
800 }
801 }
802 else {
803 currkey = BKE_keyblock_add(key, bm->vdata.layers[i].name);
804 currkey->uid = bm->vdata.layers[i].uid;
805 }
806 }
807
808 const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
809 BMIter iter;
810 BMVert *eve;
811 float (*ofs)[3] = nullptr;
812 std::optional<Array<bool>> dependent;
813
814 /* Editing the basis key updates others. */
815 if ((key->type == KEY_RELATIVE) &&
816 /* The shape-key coordinates used from entering edit-mode are used. */
817 (actkey_has_layer == true) &&
818 /* Original key-indices are only used to check the vertex existed when entering edit-mode. */
819 (cd_shape_keyindex_offset != -1) &&
820 /* Offsets are only needed if the current shape is a basis for others. */
821 (dependent = BKE_keyblock_get_dependent_keys(key, bm->shapenr - 1)).has_value())
822 {
823
824 BLI_assert(actkey != nullptr); /* Assured by `actkey_has_layer` check. */
825 const int actkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, actkey);
826
827 /* Since `actkey_has_layer == true`, this must never fail. */
828 BLI_assert(actkey_uuid != -1);
829
830 const int cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, actkey_uuid);
831
832 ofs = static_cast<float (*)[3]>(MEM_mallocN(sizeof(float[3]) * bm->totvert, __func__));
833 int i;
834 BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
835 const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
836 /* Check the vertex existed when entering edit-mode (otherwise don't apply an offset). */
837 if (keyi != ORIGINDEX_NONE) {
838 float *co_orig = (float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset);
839 /* Could use 'eve->co' or the destination position, they're the same at this point. */
840 sub_v3_v3v3(ofs[i], eve->co, co_orig);
841 }
842 else {
843 /* If there are new vertices in the mesh, we can't propagate the offset
844 * because it will only work for the existing vertices and not the new
845 * ones, creating a mess when doing e.g. subdivide + translate. */
846 MEM_freeN(ofs);
847 ofs = nullptr;
848 dependent.reset();
849 break;
850 }
851 }
852 }
853
854 /* Without this, the real mesh coordinates (uneditable) as soon as you create the Basis shape.
855 * while users might not notice since the shape-key is applied in the viewport,
856 * exporters for example may still use the underlying coordinates, see: #30771 & #96135.
857 *
858 * Needed when editing any shape that isn't the (`key->refkey`), the vertices in mesh positions
859 * currently have vertex coordinates set from the current-shape (initialized from #BMVert.co).
860 * In this case it's important to overwrite these coordinates with the basis-keys coordinates. */
861 bool update_vertex_coords_from_refkey = false;
862 int cd_shape_offset_refkey = -1;
863 if (active_shapekey_to_mvert == false) {
864 if ((actkey != key->refkey) && (cd_shape_keyindex_offset != -1)) {
865 const int refkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, key->refkey);
866 if (refkey_uuid != -1) {
867 cd_shape_offset_refkey = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, refkey_uuid);
868 if (cd_shape_offset_refkey != -1) {
869 update_vertex_coords_from_refkey = true;
870 }
871 }
872 }
873 }
874
875 int currkey_i;
876 LISTBASE_FOREACH_INDEX (KeyBlock *, currkey, &key->block, currkey_i) {
877 int keyi;
878 float (*currkey_data)[3];
879
880 const int currkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, currkey);
881 const int cd_shape_offset = (currkey_uuid == -1) ?
882 -1 :
883 CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, currkey_uuid);
884
885 /* Common case, the layer data is available, use it where possible. */
886 if (cd_shape_offset != -1) {
887 const bool apply_offset = (ofs != nullptr) && (currkey != actkey) && (*dependent)[currkey_i];
888
889 if (currkey->data && (currkey->totelem == bm->totvert)) {
890 /* Use memory in-place. */
891 }
892 else {
893 currkey->data = MEM_reallocN(currkey->data, key->elemsize * bm->totvert);
894 currkey->totelem = bm->totvert;
895 }
896 currkey_data = (float (*)[3])currkey->data;
897
898 int i;
899 BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
900 float *co_orig = (float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset);
901
902 if (currkey == actkey) {
903 copy_v3_v3(currkey_data[i], eve->co);
904
905 if (update_vertex_coords_from_refkey) {
906 BLI_assert(actkey != key->refkey);
907 keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
908 if (keyi != ORIGINDEX_NONE) {
909 float *co_refkey = (float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset_refkey);
910 copy_v3_v3(positions[i], co_refkey);
911 }
912 }
913 }
914 else {
915 copy_v3_v3(currkey_data[i], co_orig);
916 }
917
918 /* Propagate edited basis offsets to other shapes. */
919 if (apply_offset) {
920 add_v3_v3(currkey_data[i], ofs[i]);
921 }
922
923 /* Apply back new coordinates shape-keys that have offset into #BMesh.
924 * Otherwise, in case we call again #BM_mesh_bm_to_me on same #BMesh,
925 * we'll apply diff from previous call to #BM_mesh_bm_to_me,
926 * to shape-key values from original creation of the #BMesh. See #50524. */
927 copy_v3_v3(co_orig, currkey_data[i]);
928 }
929 }
930 else {
931 /* No original layer data, use fallback information. */
932 if (currkey->data && (cd_shape_keyindex_offset != -1)) {
933 CLOG_WARN(&LOG,
934 "Found shape-key but no CD_SHAPEKEY layers to read from, "
935 "using existing shake-key data where possible");
936 }
937 else {
938 CLOG_WARN(&LOG,
939 "Found shape-key but no CD_SHAPEKEY layers to read from, "
940 "using basis shape-key data");
941 }
942
943 currkey_data = static_cast<float (*)[3]>(
944 MEM_mallocN(key->elemsize * bm->totvert, "currkey->data"));
945
946 int i;
947 BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
948
949 if ((currkey->data != nullptr) && (cd_shape_keyindex_offset != -1) &&
950 ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) &&
951 (keyi < currkey->totelem))
952 {
953 /* Reconstruct keys via vertices original key indices.
954 * WARNING(@ideasman42): `currkey->data` is known to be unreliable as the edit-mesh
955 * coordinates may be flushed back to the shape-key when exporting or rendering.
956 * This is a last resort! If this branch is running as part of regular usage
957 * it can be considered a bug. */
958 const float (*oldkey)[3] = static_cast<const float (*)[3]>(currkey->data);
959 copy_v3_v3(currkey_data[i], oldkey[keyi]);
960 }
961 else {
962 /* Fail! fill in with dummy value. */
963 copy_v3_v3(currkey_data[i], eve->co);
964 }
965 }
966
967 currkey->totelem = bm->totvert;
968 if (currkey->data) {
969 MEM_freeN(currkey->data);
970 }
971 currkey->data = currkey_data;
972 }
973 }
974
975 MEM_SAFE_FREE(ofs);
976}
977
979
981{
982 (void)bm; /* Unused in the release builds. */
984 BLI_assert(!CustomData_has_layer_named(&bm.ldata, CD_PROP_FLOAT3, ".corner_vert"));
985 BLI_assert(!CustomData_has_layer_named(&bm.ldata, CD_PROP_FLOAT3, ".corner_edge"));
986
987 /* The "hide" attributes are stored as flags on #BMesh. */
988 BLI_assert(!CustomData_has_layer_named(&bm.vdata, CD_PROP_BOOL, ".hide_vert"));
989 BLI_assert(!CustomData_has_layer_named(&bm.edata, CD_PROP_BOOL, ".hide_edge"));
990 BLI_assert(!CustomData_has_layer_named(&bm.pdata, CD_PROP_BOOL, ".hide_poly"));
991 /* The "selection" attributes are stored as flags on #BMesh. */
992 BLI_assert(!CustomData_has_layer_named(&bm.vdata, CD_PROP_BOOL, ".select_vert"));
993 BLI_assert(!CustomData_has_layer_named(&bm.edata, CD_PROP_BOOL, ".select_edge"));
994 BLI_assert(!CustomData_has_layer_named(&bm.pdata, CD_PROP_BOOL, ".select_poly"));
995}
996
998 Mesh &mesh,
999 BMesh &bm,
1000 const int old_totvert)
1001{
1002 BMVert **vertMap = nullptr;
1003 BMVert *eve;
1004
1005 LISTBASE_FOREACH (Object *, ob, &bmain.objects) {
1006 if ((ob->parent) && (ob->parent->data == &mesh) && ELEM(ob->partype, PARVERT1, PARVERT3)) {
1007
1008 if (vertMap == nullptr) {
1009 vertMap = bm_to_mesh_vertex_map(&bm, old_totvert);
1010 }
1011
1012 if (ob->par1 < old_totvert) {
1013 eve = vertMap[ob->par1];
1014 if (eve) {
1015 ob->par1 = BM_elem_index_get(eve);
1016 }
1017 }
1018 if (ob->par2 < old_totvert) {
1019 eve = vertMap[ob->par2];
1020 if (eve) {
1021 ob->par2 = BM_elem_index_get(eve);
1022 }
1023 }
1024 if (ob->par3 < old_totvert) {
1025 eve = vertMap[ob->par3];
1026 if (eve) {
1027 ob->par3 = BM_elem_index_get(eve);
1028 }
1029 }
1030 }
1031 if (ob->data == &mesh) {
1032 LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
1033 if (md->type == eModifierType_Hook) {
1035
1036 if (vertMap == nullptr) {
1037 vertMap = bm_to_mesh_vertex_map(&bm, old_totvert);
1038 }
1039 int i, j;
1040 for (i = j = 0; i < hmd->indexar_num; i++) {
1041 if (hmd->indexar[i] < old_totvert) {
1042 eve = vertMap[hmd->indexar[i]];
1043
1044 if (eve) {
1045 hmd->indexar[j++] = BM_elem_index_get(eve);
1046 }
1047 }
1048 else {
1049 j++;
1050 }
1051 }
1052
1053 hmd->indexar_num = j;
1054 }
1055 }
1056 }
1057 }
1058
1059 if (vertMap) {
1060 MEM_freeN(vertMap);
1061 }
1062}
1063
1073
1078 CustomData &mesh_data)
1079{
1081 std::array<int, CD_NUMTYPES> per_type_index;
1082 per_type_index.fill(0);
1083 for (const int i : IndexRange(mesh_data.totlayer)) {
1084 const CustomDataLayer &mesh_layer = mesh_data.layers[i];
1085 const eCustomDataType type = eCustomDataType(mesh_layer.type);
1086 const int bm_layer_index =
1087 mesh_layer.name[0] == '\0' ?
1088 CustomData_get_layer_index_n(&bm_data, type, per_type_index[type]) :
1089 CustomData_get_named_layer_index(&bm_data, type, mesh_layer.name);
1090
1091 /* Skip layers that don't exist in `bm_data` or are explicitly set to not be
1092 * copied. The layers are either set separately or shouldn't exist on the mesh. */
1093 if (bm_layer_index == -1) {
1094 continue;
1095 }
1096 const CustomDataLayer &bm_layer = bm_data.layers[bm_layer_index];
1097 if (bm_layer.flag & CD_FLAG_NOCOPY) {
1098 continue;
1099 }
1100
1101 BMeshToMeshLayerInfo info{};
1102 info.type = type;
1103 info.bmesh_offset = bm_layer.offset;
1104 info.mesh_data = mesh_layer.data;
1105 info.elem_size = CustomData_get_elem_size(&mesh_layer);
1106 infos.append(info);
1107
1108 per_type_index[type]++;
1109 }
1110 return infos;
1111}
1112
1113namespace blender {
1114
1117 bool &need_select_vert,
1118 bool &need_hide_vert)
1119{
1120 char hflag = 0;
1121 BMIter iter;
1122 int i;
1123 BMVert *vert;
1124 BM_ITER_MESH_INDEX (vert, &iter, &bm, BM_VERTS_OF_MESH, i) {
1125 BM_elem_index_set(vert, i); /* set_inline */
1126 table[i] = vert;
1127 hflag |= vert->head.hflag;
1128 }
1129 need_select_vert = (hflag & BM_ELEM_SELECT) != 0;
1130 need_hide_vert = (hflag & BM_ELEM_HIDDEN) != 0;
1131}
1132
1135 bool &need_select_edge,
1136 bool &need_hide_edge,
1137 bool &need_sharp_edge,
1138 bool &need_uv_seams)
1139{
1140 char hflag = 0;
1141 BMIter iter;
1142 int i;
1143 BMEdge *edge;
1144 BM_ITER_MESH_INDEX (edge, &iter, &bm, BM_EDGES_OF_MESH, i) {
1145 BM_elem_index_set(edge, i); /* set_inline */
1146 table[i] = edge;
1147 hflag |= edge->head.hflag;
1148 need_sharp_edge |= (edge->head.hflag & BM_ELEM_SMOOTH) == 0;
1149 }
1150 need_select_edge = (hflag & BM_ELEM_SELECT) != 0;
1151 need_hide_edge = (hflag & BM_ELEM_HIDDEN) != 0;
1152 need_uv_seams = (hflag & BM_ELEM_SEAM) != 0;
1153}
1154
1162 MutableSpan<const BMFace *> face_table,
1163 MutableSpan<const BMLoop *> loop_table,
1164 bool &need_select_poly,
1165 bool &need_hide_poly,
1166 bool &need_sharp_face,
1167 bool &need_material_index,
1168 Vector<int> &loop_layers_not_to_copy)
1169{
1170 const CustomData &ldata = bm.ldata;
1171 Vector<int> pin_layers;
1172 for (const int i : IndexRange(CustomData_number_of_layers(&ldata, CD_PROP_FLOAT2))) {
1173 char const *layer_name = CustomData_get_layer_name(&ldata, CD_PROP_FLOAT2, i);
1174 char sub_layer_name[MAX_CUSTOMDATA_LAYER_NAME];
1175 auto add_bool_layer = [&](Vector<int> &layers, const StringRef name) {
1176 const int layer_index = CustomData_get_named_layer_index(&ldata, CD_PROP_BOOL, name);
1177 if (layer_index != -1) {
1178 layers.append(layer_index);
1179 }
1180 };
1181 add_bool_layer(pin_layers, BKE_uv_map_pin_name_get(layer_name, sub_layer_name));
1182 }
1183 Array<int> pin_offsets(pin_layers.size());
1184 for (const int i : pin_layers.index_range()) {
1185 pin_offsets[i] = ldata.layers[pin_layers[i]].offset;
1186 }
1187
1188 Array<bool> need_pin(pin_layers.size(), false);
1189 char hflag = 0;
1190 BMIter iter;
1191 int face_i = 0;
1192 int loop_i = 0;
1193 BMFace *face;
1194 BM_ITER_MESH_INDEX (face, &iter, &bm, BM_FACES_OF_MESH, face_i) {
1195 BM_elem_index_set(face, face_i); /* set_inline */
1196 face_table[face_i] = face;
1197 hflag |= face->head.hflag;
1198 need_sharp_face |= (face->head.hflag & BM_ELEM_SMOOTH) == 0;
1199 need_material_index |= face->mat_nr != 0;
1200
1201 BMLoop *loop = BM_FACE_FIRST_LOOP(face);
1202 for ([[maybe_unused]] const int i : IndexRange(face->len)) {
1203 BM_elem_index_set(loop, loop_i); /* set_inline */
1204 loop_table[loop_i] = loop;
1205 for (const int i : pin_offsets.index_range()) {
1206 if (BM_ELEM_CD_GET_BOOL(loop, pin_offsets[i])) {
1207 need_pin[i] = true;
1208 }
1209 }
1210 loop = loop->next;
1211 loop_i++;
1212 }
1213 }
1214 need_select_poly = (hflag & BM_ELEM_SELECT) != 0;
1215 need_hide_poly = (hflag & BM_ELEM_HIDDEN) != 0;
1216
1217 for (const int i : pin_layers.index_range()) {
1218 if (!need_pin[i]) {
1219 loop_layers_not_to_copy.append(pin_layers[i]);
1220 }
1221 }
1222}
1223
1225 const int mesh_index,
1226 const void *block)
1227{
1228 for (const BMeshToMeshLayerInfo &info : copy_info) {
1230 POINTER_OFFSET(block, info.bmesh_offset),
1231 POINTER_OFFSET(info.mesh_data, info.elem_size * mesh_index));
1232 }
1233}
1234
1235static void bm_to_mesh_verts(const BMesh &bm,
1236 const Span<const BMVert *> bm_verts,
1237 Mesh &mesh,
1238 MutableSpan<bool> select_vert,
1239 MutableSpan<bool> hide_vert)
1240{
1241 CustomData_free_layer_named(&mesh.vert_data, "position");
1243 &mesh.vert_data, CD_PROP_FLOAT3, CD_CONSTRUCT, mesh.verts_num, "position");
1245 MutableSpan<float3> dst_vert_positions = mesh.vert_positions_for_write();
1246
1247 std::atomic<bool> any_loose_vert = false;
1248 threading::parallel_for(dst_vert_positions.index_range(), 1024, [&](const IndexRange range) {
1249 bool any_loose_vert_local = false;
1250 for (const int vert_i : range) {
1251 const BMVert &src_vert = *bm_verts[vert_i];
1252 copy_v3_v3(dst_vert_positions[vert_i], src_vert.co);
1253 bmesh_block_copy_to_mesh_attributes(info, vert_i, src_vert.head.data);
1254 any_loose_vert_local = any_loose_vert_local || src_vert.e == nullptr;
1255 }
1256 if (any_loose_vert_local) {
1257 any_loose_vert.store(true, std::memory_order_relaxed);
1258 }
1259 if (!select_vert.is_empty()) {
1260 for (const int vert_i : range) {
1261 select_vert[vert_i] = BM_elem_flag_test(bm_verts[vert_i], BM_ELEM_SELECT);
1262 }
1263 }
1264 if (!hide_vert.is_empty()) {
1265 for (const int vert_i : range) {
1266 hide_vert[vert_i] = BM_elem_flag_test(bm_verts[vert_i], BM_ELEM_HIDDEN);
1267 }
1268 }
1269 });
1270
1271 if (!any_loose_vert) {
1272 mesh.tag_loose_verts_none();
1273 }
1274}
1275
1276static void bm_to_mesh_edges(const BMesh &bm,
1277 const Span<const BMEdge *> bm_edges,
1278 Mesh &mesh,
1279 MutableSpan<bool> select_edge,
1280 MutableSpan<bool> hide_edge,
1281 MutableSpan<bool> sharp_edge,
1282 MutableSpan<bool> uv_seams)
1283{
1284 CustomData_free_layer_named(&mesh.edge_data, ".edge_verts");
1286 &mesh.edge_data, CD_PROP_INT32_2D, CD_CONSTRUCT, mesh.edges_num, ".edge_verts");
1288 MutableSpan<int2> dst_edges = mesh.edges_for_write();
1289
1290 std::atomic<bool> any_loose_edge = false;
1291 threading::parallel_for(dst_edges.index_range(), 512, [&](const IndexRange range) {
1292 bool any_loose_edge_local = false;
1293 for (const int edge_i : range) {
1294 const BMEdge &src_edge = *bm_edges[edge_i];
1295 dst_edges[edge_i] = int2(BM_elem_index_get(src_edge.v1), BM_elem_index_get(src_edge.v2));
1296 bmesh_block_copy_to_mesh_attributes(info, edge_i, src_edge.head.data);
1297 any_loose_edge_local |= BM_edge_is_wire(&src_edge);
1298 }
1299 if (any_loose_edge_local) {
1300 any_loose_edge.store(true, std::memory_order_relaxed);
1301 }
1302 if (!select_edge.is_empty()) {
1303 for (const int edge_i : range) {
1304 select_edge[edge_i] = BM_elem_flag_test(bm_edges[edge_i], BM_ELEM_SELECT);
1305 }
1306 }
1307 if (!hide_edge.is_empty()) {
1308 for (const int edge_i : range) {
1309 hide_edge[edge_i] = BM_elem_flag_test(bm_edges[edge_i], BM_ELEM_HIDDEN);
1310 }
1311 }
1312 if (!sharp_edge.is_empty()) {
1313 for (const int edge_i : range) {
1314 sharp_edge[edge_i] = !BM_elem_flag_test(bm_edges[edge_i], BM_ELEM_SMOOTH);
1315 }
1316 }
1317 if (!uv_seams.is_empty()) {
1318 for (const int edge_i : range) {
1319 uv_seams[edge_i] = BM_elem_flag_test(bm_edges[edge_i], BM_ELEM_SEAM);
1320 }
1321 }
1322 });
1323
1324 if (!any_loose_edge) {
1325 mesh.tag_loose_edges_none();
1326 }
1327}
1328
1329static void bm_to_mesh_faces(const BMesh &bm,
1330 const Span<const BMFace *> bm_faces,
1331 Mesh &mesh,
1332 MutableSpan<bool> select_poly,
1333 MutableSpan<bool> hide_poly,
1334 MutableSpan<bool> sharp_faces,
1335 MutableSpan<bool> uv_select_face,
1336 MutableSpan<int> material_indices)
1337{
1340 MutableSpan<int> dst_face_offsets = mesh.face_offsets_for_write();
1341 threading::parallel_for(bm_faces.index_range(), 1024, [&](const IndexRange range) {
1342 for (const int face_i : range) {
1343 const BMFace &src_face = *bm_faces[face_i];
1344 dst_face_offsets[face_i] = BM_elem_index_get(BM_FACE_FIRST_LOOP(&src_face));
1345 bmesh_block_copy_to_mesh_attributes(info, face_i, src_face.head.data);
1346 }
1347 if (!select_poly.is_empty()) {
1348 for (const int face_i : range) {
1349 select_poly[face_i] = BM_elem_flag_test(bm_faces[face_i], BM_ELEM_SELECT);
1350 }
1351 }
1352 if (!hide_poly.is_empty()) {
1353 for (const int face_i : range) {
1354 hide_poly[face_i] = BM_elem_flag_test(bm_faces[face_i], BM_ELEM_HIDDEN);
1355 }
1356 }
1357 if (!material_indices.is_empty()) {
1358 for (const int face_i : range) {
1359 material_indices[face_i] = bm_faces[face_i]->mat_nr;
1360 }
1361 }
1362 if (!sharp_faces.is_empty()) {
1363 for (const int face_i : range) {
1364 sharp_faces[face_i] = !BM_elem_flag_test(bm_faces[face_i], BM_ELEM_SMOOTH);
1365 }
1366 }
1367 if (!uv_select_face.is_empty()) {
1368 for (const int face_i : range) {
1369 uv_select_face[face_i] = BM_elem_flag_test(bm_faces[face_i], BM_ELEM_SELECT_UV);
1370 }
1371 }
1372 });
1373}
1374
1375static void bm_to_mesh_loops(const BMesh &bm,
1376 const Span<const BMLoop *> bm_loops,
1377 Mesh &mesh,
1378 MutableSpan<bool> uv_select_vert,
1379 MutableSpan<bool> uv_select_edge)
1380{
1381 CustomData_free_layer_named(&mesh.corner_data, ".corner_vert");
1382 CustomData_free_layer_named(&mesh.corner_data, ".corner_edge");
1384 &mesh.corner_data, CD_PROP_INT32, CD_CONSTRUCT, mesh.corners_num, ".corner_vert");
1386 &mesh.corner_data, CD_PROP_INT32, CD_CONSTRUCT, mesh.corners_num, ".corner_edge");
1388
1389 MutableSpan<int> dst_corner_verts = mesh.corner_verts_for_write();
1390 MutableSpan<int> dst_corner_edges = mesh.corner_edges_for_write();
1391
1392 const bool need_uv_select = !uv_select_vert.is_empty() && !uv_select_edge.is_empty();
1393 threading::parallel_for(dst_corner_verts.index_range(), 1024, [&](const IndexRange range) {
1394 for (const int loop_i : range) {
1395 const BMLoop &src_loop = *bm_loops[loop_i];
1396 dst_corner_verts[loop_i] = BM_elem_index_get(src_loop.v);
1397 dst_corner_edges[loop_i] = BM_elem_index_get(src_loop.e);
1398 bmesh_block_copy_to_mesh_attributes(info, loop_i, src_loop.head.data);
1399 }
1400
1401 if (need_uv_select) {
1402 for (const int loop_i : range) {
1403 const BMLoop &src_loop = *bm_loops[loop_i];
1404 uv_select_vert[loop_i] = BM_elem_flag_test(&src_loop, BM_ELEM_SELECT_UV);
1405 uv_select_edge[loop_i] = BM_elem_flag_test(&src_loop, BM_ELEM_SELECT_UV_EDGE);
1406 }
1407 }
1408 });
1409}
1410
1411} // namespace blender
1412
1414{
1415 using namespace blender;
1416 const int old_verts_num = mesh->verts_num;
1417
1419
1420 mesh->verts_num = bm->totvert;
1421 mesh->edges_num = bm->totedge;
1422 mesh->totface_legacy = 0;
1423 mesh->corners_num = bm->totloop;
1424 mesh->faces_num = bm->totface;
1425 mesh->act_face = -1;
1426
1427 /* Will have been cleared when clearing geometry. */
1428 const bool need_uv_select = CustomData_has_layer(&bm->ldata, CD_PROP_FLOAT2);
1429 if (need_uv_select & bm->uv_select_sync_valid) {
1431 }
1432
1433 bool need_select_vert = false;
1434 bool need_select_edge = false;
1435 bool need_select_poly = false;
1436 bool need_hide_vert = false;
1437 bool need_hide_edge = false;
1438 bool need_hide_poly = false;
1439 bool need_material_index = false;
1440 bool need_sharp_edge = false;
1441 bool need_sharp_face = false;
1442 bool need_uv_seams = false;
1443 Array<const BMVert *> vert_table;
1444 Array<const BMEdge *> edge_table;
1445 Array<const BMFace *> face_table;
1446 Array<const BMLoop *> loop_table;
1447 Vector<int> loop_layers_not_to_copy;
1449 (mesh->faces_num + mesh->edges_num) > 1024,
1450 [&]() {
1451 vert_table.reinitialize(bm->totvert);
1452 bm_vert_table_build(*bm, vert_table, need_select_vert, need_hide_vert);
1453 },
1454 [&]() {
1455 edge_table.reinitialize(bm->totedge);
1456 bm_edge_table_build(
1457 *bm, edge_table, need_select_edge, need_hide_edge, need_sharp_edge, need_uv_seams);
1458 },
1459 [&]() {
1460 face_table.reinitialize(bm->totface);
1461 loop_table.reinitialize(bm->totloop);
1462 bm_face_loop_table_build(*bm,
1463 face_table,
1464 loop_table,
1465 need_select_poly,
1466 need_hide_poly,
1467 need_sharp_face,
1468 need_material_index,
1469 loop_layers_not_to_copy);
1470 for (const int i : loop_layers_not_to_copy) {
1471 bm->ldata.layers[i].flag |= CD_FLAG_NOCOPY;
1472 }
1473 });
1474 bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE | BM_LOOP);
1475
1476 {
1478 CustomData_MeshMasks_update(&mask, &params->cd_mask_extra);
1480 &bm->vdata, &mesh->vert_data, mask.vmask, CD_CONSTRUCT, mesh->verts_num);
1482 &bm->edata, &mesh->edge_data, mask.emask, CD_CONSTRUCT, mesh->edges_num);
1484 &bm->ldata, &mesh->corner_data, mask.lmask, CD_CONSTRUCT, mesh->corners_num);
1486 &bm->pdata, &mesh->face_data, mask.pmask, CD_CONSTRUCT, mesh->faces_num);
1487 }
1488
1489 /* Add optional mesh attributes before parallel iteration. */
1491 bke::MutableAttributeAccessor attrs = mesh->attributes_for_write();
1501 bke::SpanAttributeWriter<bool> uv_select_vert;
1502 bke::SpanAttributeWriter<bool> uv_select_edge;
1503 bke::SpanAttributeWriter<bool> uv_select_face;
1504 bke::SpanAttributeWriter<int> material_index;
1505 if (need_select_vert) {
1506 select_vert = attrs.lookup_or_add_for_write_only_span<bool>(".select_vert", AttrDomain::Point);
1507 }
1508 if (need_hide_vert) {
1509 hide_vert = attrs.lookup_or_add_for_write_only_span<bool>(".hide_vert", AttrDomain::Point);
1510 }
1511 if (need_select_edge) {
1512 select_edge = attrs.lookup_or_add_for_write_only_span<bool>(".select_edge", AttrDomain::Edge);
1513 }
1514 if (need_sharp_edge) {
1515 sharp_edge = attrs.lookup_or_add_for_write_only_span<bool>("sharp_edge", AttrDomain::Edge);
1516 }
1517 if (need_uv_seams) {
1518 uv_seams = attrs.lookup_or_add_for_write_only_span<bool>("uv_seam", AttrDomain::Edge);
1519 }
1520 if (need_hide_edge) {
1521 hide_edge = attrs.lookup_or_add_for_write_only_span<bool>(".hide_edge", AttrDomain::Edge);
1522 }
1523 if (need_select_poly) {
1524 select_poly = attrs.lookup_or_add_for_write_only_span<bool>(".select_poly", AttrDomain::Face);
1525 }
1526 if (need_hide_poly) {
1527 hide_poly = attrs.lookup_or_add_for_write_only_span<bool>(".hide_poly", AttrDomain::Face);
1528 }
1529 if (need_sharp_face) {
1530 sharp_face = attrs.lookup_or_add_for_write_only_span<bool>("sharp_face", AttrDomain::Face);
1531 }
1532 if (need_uv_select) {
1533 uv_select_vert = attrs.lookup_or_add_for_write_only_span<bool>(".uv_select_vert",
1535 uv_select_edge = attrs.lookup_or_add_for_write_only_span<bool>(".uv_select_edge",
1537 uv_select_face = attrs.lookup_or_add_for_write_only_span<bool>(".uv_select_face",
1539 }
1540 if (need_material_index) {
1541 material_index = attrs.lookup_or_add_for_write_only_span<int>("material_index",
1543 }
1544
1545 /* Loop over all elements in parallel, copying attributes and building the Mesh topology. */
1547 (mesh->faces_num + mesh->edges_num) > 1024,
1548 [&]() {
1549 bm_to_mesh_verts(*bm, vert_table, *mesh, select_vert.span, hide_vert.span);
1550 if (mesh->key) {
1551 bm_to_mesh_shape(
1552 bm, mesh->key, mesh->vert_positions_for_write(), params->active_shapekey_to_mvert);
1553 }
1554 },
1555 [&]() {
1557 edge_table,
1558 *mesh,
1559 select_edge.span,
1560 hide_edge.span,
1561 sharp_edge.span,
1562 uv_seams.span);
1563 },
1564 [&]() {
1566 face_table,
1567 *mesh,
1568 select_poly.span,
1569 hide_poly.span,
1570 sharp_face.span,
1571 uv_select_face.span,
1572 material_index.span);
1573 if (bm->act_face) {
1574 mesh->act_face = BM_elem_index_get(bm->act_face);
1575 }
1576 },
1577 [&]() {
1578 bm_to_mesh_loops(*bm, loop_table, *mesh, uv_select_vert.span, uv_select_edge.span);
1579 /* Topology could be changed, ensure #CD_MDISPS are ok. */
1581 for (const int i : loop_layers_not_to_copy) {
1582 bm->ldata.layers[i].flag &= ~CD_FLAG_NOCOPY;
1583 }
1584 },
1585 [&]() {
1586 /* Patch hook indices and vertex parents. */
1587 if (params->calc_object_remap && (old_verts_num > 0)) {
1588 bmesh_to_mesh_calc_object_remap(*bmain, *mesh, *bm, old_verts_num);
1589 }
1590 },
1591 [&]() {
1592 mesh->totselect = BLI_listbase_count(&(bm->selected));
1593
1594 MEM_SAFE_FREE(mesh->mselect);
1595 if (mesh->totselect != 0) {
1596 mesh->mselect = MEM_malloc_arrayN<MSelect>(mesh->totselect, "Mesh selection history");
1597 }
1598 int i;
1599 LISTBASE_FOREACH_INDEX (BMEditSelection *, selected, &bm->selected, i) {
1600 if (selected->htype == BM_VERT) {
1601 mesh->mselect[i].type = ME_VSEL;
1602 }
1603 else if (selected->htype == BM_EDGE) {
1604 mesh->mselect[i].type = ME_ESEL;
1605 }
1606 else if (selected->htype == BM_FACE) {
1607 mesh->mselect[i].type = ME_FSEL;
1608 }
1609
1610 mesh->mselect[i].index = BM_elem_index_get(selected->ele);
1611 }
1612 },
1613 [&]() {
1614 /* Run this even when shape keys aren't used since it may be used for hooks or vertex
1615 * parents. */
1616 if (params->update_shapekey_indices) {
1617 /* We have written a new shape key, if this mesh is _not_ going to be freed,
1618 * update the shape key indices to match the newly updated. */
1619 const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata,
1621 if (cd_shape_keyindex_offset != -1) {
1622 BMIter iter;
1623 BMVert *vert;
1624 int i;
1625 BM_ITER_MESH_INDEX (vert, &iter, bm, BM_VERTS_OF_MESH, i) {
1626 BM_ELEM_CD_SET_INT(vert, cd_shape_keyindex_offset, i);
1627 }
1628 }
1629 }
1630 });
1631
1632 select_vert.finish();
1633 hide_vert.finish();
1634 select_edge.finish();
1635 hide_edge.finish();
1636 sharp_edge.finish();
1637 uv_seams.finish();
1638 select_poly.finish();
1639 hide_poly.finish();
1640 sharp_face.finish();
1641 uv_select_vert.finish();
1642 uv_select_edge.finish();
1643 uv_select_face.finish();
1644 material_index.finish();
1645}
1646
1648 Mesh &mesh,
1650 const bool add_mesh_attributes)
1651{
1652 /* NOTE: The function is called from multiple threads with the same input BMesh and different
1653 * mesh objects. */
1654
1655 using namespace blender;
1656 /* Must be an empty mesh. */
1657 BLI_assert(mesh.verts_num == 0);
1658 /* Just in case, clear the derived geometry caches from the input mesh. */
1660
1661 mesh.verts_num = bm.totvert;
1662 mesh.edges_num = bm.totedge;
1663 mesh.totface_legacy = 0;
1664 mesh.corners_num = bm.totloop;
1665 mesh.faces_num = bm.totface;
1666
1667 /* Will have been cleared when clearing geometry. */
1668 const bool need_uv_select = CustomData_has_layer(&bm.ldata, CD_PROP_FLOAT2);
1669 if (need_uv_select && bm.uv_select_sync_valid) {
1671 }
1672
1673 mesh.runtime->deformed_only = true;
1674
1675 const bool use_threading = (mesh.faces_num + mesh.edges_num) > 1024;
1676
1677 /* In a first pass, update indices of BMesh elements and build tables for easy iteration later.
1678 * Also check if some optional mesh attributes should be added in the next step. Since each
1679 * domain has no effect on others, process the independent domains on separate threads. */
1680 bool need_select_vert = false;
1681 bool need_select_edge = false;
1682 bool need_select_poly = false;
1683 bool need_hide_vert = false;
1684 bool need_hide_edge = false;
1685 bool need_hide_poly = false;
1686 bool need_material_index = false;
1687 bool need_sharp_edge = false;
1688 bool need_sharp_face = false;
1689 bool need_uv_seams = false;
1690
1691 Array<const BMVert *> vert_table;
1692 Array<const BMEdge *> edge_table;
1693 Array<const BMFace *> face_table;
1694 Array<const BMLoop *> loop_table;
1695 Vector<int> loop_layers_not_to_copy;
1697 use_threading,
1698 [&]() {
1699 vert_table.reinitialize(bm.totvert);
1700 bm_vert_table_build(bm, vert_table, need_select_vert, need_hide_vert);
1701 },
1702 [&]() {
1703 edge_table.reinitialize(bm.totedge);
1705 bm, edge_table, need_select_edge, need_hide_edge, need_sharp_edge, need_uv_seams);
1706 },
1707 [&]() {
1708 face_table.reinitialize(bm.totface);
1709 loop_table.reinitialize(bm.totloop);
1711 face_table,
1712 loop_table,
1713 need_select_poly,
1714 need_hide_poly,
1715 need_sharp_face,
1716 need_material_index,
1717 loop_layers_not_to_copy);
1718 for (const int i : loop_layers_not_to_copy) {
1719 bm.ldata.layers[i].flag |= CD_FLAG_NOCOPY;
1720 }
1721 });
1722 bm.elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE | BM_LOOP);
1723
1724 if (mask) {
1725 CustomData_merge_layout(&bm.vdata, &mesh.vert_data, mask->vmask, CD_CONSTRUCT, mesh.verts_num);
1726 CustomData_merge_layout(&bm.edata, &mesh.edge_data, mask->emask, CD_CONSTRUCT, mesh.edges_num);
1728 &bm.ldata, &mesh.corner_data, mask->lmask, CD_CONSTRUCT, mesh.corners_num);
1729 CustomData_merge_layout(&bm.pdata, &mesh.face_data, mask->pmask, CD_CONSTRUCT, mesh.faces_num);
1730 }
1731
1732 /* Add optional mesh attributes before parallel iteration. */
1743 bke::SpanAttributeWriter<bool> uv_select_vert;
1744 bke::SpanAttributeWriter<bool> uv_select_edge;
1745 bke::SpanAttributeWriter<bool> uv_select_face;
1746 bke::SpanAttributeWriter<int> material_index;
1747
1748 if (add_mesh_attributes) {
1749 bke::MutableAttributeAccessor attrs = mesh.attributes_for_write();
1750 if (need_select_vert) {
1751 select_vert = attrs.lookup_or_add_for_write_only_span<bool>(".select_vert",
1753 }
1754 if (need_hide_vert) {
1755 hide_vert = attrs.lookup_or_add_for_write_only_span<bool>(".hide_vert", AttrDomain::Point);
1756 }
1757 if (need_select_edge) {
1758 select_edge = attrs.lookup_or_add_for_write_only_span<bool>(".select_edge",
1760 }
1761 if (need_sharp_edge) {
1762 sharp_edge = attrs.lookup_or_add_for_write_only_span<bool>("sharp_edge", AttrDomain::Edge);
1763 }
1764 if (need_uv_seams) {
1765 uv_seams = attrs.lookup_or_add_for_write_only_span<bool>("uv_seam", AttrDomain::Edge);
1766 }
1767 if (need_hide_edge) {
1768 hide_edge = attrs.lookup_or_add_for_write_only_span<bool>(".hide_edge", AttrDomain::Edge);
1769 }
1770 if (need_select_poly) {
1771 select_poly = attrs.lookup_or_add_for_write_only_span<bool>(".select_poly",
1773 }
1774 if (need_hide_poly) {
1775 hide_poly = attrs.lookup_or_add_for_write_only_span<bool>(".hide_poly", AttrDomain::Face);
1776 }
1777 if (need_sharp_face) {
1778 sharp_face = attrs.lookup_or_add_for_write_only_span<bool>("sharp_face", AttrDomain::Face);
1779 }
1780 if (need_uv_select) {
1781 uv_select_vert = attrs.lookup_or_add_for_write_only_span<bool>(".uv_select_vert",
1783 uv_select_edge = attrs.lookup_or_add_for_write_only_span<bool>(".uv_select_edge",
1785 uv_select_face = attrs.lookup_or_add_for_write_only_span<bool>(".uv_select_face",
1787 }
1788 if (need_material_index) {
1789 material_index = attrs.lookup_or_add_for_write_only_span<int>("material_index",
1791 }
1792 }
1793
1794 /* Loop over all elements in parallel, copying attributes and building the Mesh topology. */
1796 use_threading,
1797 [&]() { bm_to_mesh_verts(bm, vert_table, mesh, select_vert.span, hide_vert.span); },
1798 [&]() {
1800 edge_table,
1801 mesh,
1802 select_edge.span,
1803 hide_edge.span,
1804 sharp_edge.span,
1805 uv_seams.span);
1806 },
1807 [&]() {
1809 face_table,
1810 mesh,
1811 select_poly.span,
1812 hide_poly.span,
1813 sharp_face.span,
1814 uv_select_face.span,
1815 material_index.span);
1816 if (bm.act_face) {
1817 mesh.act_face = BM_elem_index_get(bm.act_face);
1818 }
1819 },
1820 [&]() {
1821 bm_to_mesh_loops(bm, loop_table, mesh, uv_select_vert.span, uv_select_edge.span);
1822 for (const int i : loop_layers_not_to_copy) {
1823 bm.ldata.layers[i].flag &= ~CD_FLAG_NOCOPY;
1824 }
1825 });
1826
1827 if (add_mesh_attributes) {
1828 select_vert.finish();
1829 hide_vert.finish();
1830 select_edge.finish();
1831 hide_edge.finish();
1832 sharp_edge.finish();
1833 uv_seams.finish();
1834 select_poly.finish();
1835 hide_poly.finish();
1836 sharp_face.finish();
1837 uv_select_vert.finish();
1838 uv_select_edge.finish();
1839 uv_select_face.finish();
1840 material_index.finish();
1841 }
1842}
1843
1845{
1846 /* Don't process shape-keys. We only feed them through the modifier stack as needed,
1847 * e.g. for applying modifiers or the like. */
1849 if (cd_mask_extra != nullptr) {
1850 CustomData_MeshMasks_update(&mask, cd_mask_extra);
1851 }
1852 mask.vmask &= ~CD_MASK_SHAPEKEY;
1853
1854 BM_mesh_bm_to_me_compact(bm, mesh, &mask, true);
1855}
blender::StringRef BKE_uv_map_pin_name_get(blender::StringRef uv_map_name, char *buffer)
CustomData interface, see also DNA_customdata_types.h.
void CustomData_data_set_default_value(eCustomDataType type, void *elem)
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
int CustomData_get_layer_index_n(const CustomData *data, eCustomDataType type, int n)
bool CustomData_bmesh_merge_layout(const CustomData *source, CustomData *dest, eCustomDataMask mask, eCDAllocType alloctype, BMesh *bm, char htype)
int CustomData_get_n_offset(const CustomData *data, eCustomDataType type, int n)
@ CD_SET_DEFAULT
@ CD_CONSTRUCT
void * CustomData_add_layer_named(CustomData *data, eCustomDataType type, eCDAllocType alloctype, int totelem, blender::StringRef name)
void CustomData_MeshMasks_update(CustomData_MeshMasks *mask_dst, const CustomData_MeshMasks *mask_src)
Definition customdata.cc:96
void CustomData_bmesh_init_pool(CustomData *data, int totelem, char htype)
int CustomData_get_named_layer_index(const CustomData *data, eCustomDataType type, blender::StringRef name)
bool CustomData_has_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
const char * CustomData_get_layer_name(const CustomData *data, eCustomDataType type, int n)
void CustomData_data_copy_value(eCustomDataType type, const void *source, void *dest)
const CustomData_MeshMasks CD_MASK_BMESH
#define ORIGINDEX_NONE
void CustomData_init_layout_from(const CustomData *source, CustomData *dest, eCustomDataMask mask, eCDAllocType alloctype, int totelem)
CustomData CustomData_shallow_copy_remove_non_bmesh_attributes(const CustomData *src, eCustomDataMask mask)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
size_t CustomData_get_elem_size(const CustomDataLayer *layer)
void * CustomData_add_layer(CustomData *data, eCustomDataType type, eCDAllocType alloctype, int totelem)
const CustomData_MeshMasks CD_MASK_DERIVEDMESH
bool CustomData_merge_layout(const CustomData *source, CustomData *dest, eCustomDataMask mask, eCDAllocType alloctype, int totelem)
int CustomData_number_of_layers(const CustomData *data, eCustomDataType type)
bool CustomData_free_layer_named(CustomData *data, blender::StringRef name)
void CustomData_bmesh_alloc_block(CustomData *data, void **block)
const CustomData_MeshMasks CD_MASK_MESH
KeyBlock * BKE_keyblock_add(Key *key, const char *name)
Definition key.cc:1802
std::optional< blender::Array< bool > > BKE_keyblock_get_dependent_keys(const Key *key, int index)
Definition key.cc:2371
void BKE_mesh_clear_geometry(Mesh *mesh)
void BKE_mesh_face_offsets_ensure_alloc(Mesh *mesh)
void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
void multires_topology_changed(Mesh *mesh)
Definition multires.cc:732
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:18
#define BLI_assert(a)
Definition BLI_assert.h:46
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)
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
MINLINE int min_ii(int a, int b)
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
#define BLI_SCOPED_DEFER(function_to_defer)
#define UNLIKELY(x)
#define ELEM(...)
#define POINTER_OFFSET(v, ofs)
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
bool DEG_is_original(const T *id)
#define MAX_CUSTOMDATA_LAYER_NAME
@ CD_PROP_FLOAT3
@ CD_PROP_INT32_2D
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
@ CD_SHAPE_KEYINDEX
@ CD_FLAG_NOCOPY
#define CD_MASK_SHAPEKEY
@ KEY_RELATIVE
@ ME_FLAG_UV_SELECT_SYNC_VALID
@ ME_VSEL
@ ME_FSEL
@ ME_ESEL
@ eModifierType_Hook
Object is a sort of wrapper for general info.
@ PARVERT1
@ PARVERT3
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
#define BM_ELEM_CD_GET_BOOL(ele, offset)
#define BM_ELEM_CD_GET_INT(ele, offset)
#define BM_ELEM_CD_SET_INT(ele, offset, f)
#define BM_ELEM_SELECT_UV_EDGE
#define BM_FACE_FIRST_LOOP(p)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
@ BM_ELEM_SELECT_UV
@ BM_LOOP
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
BMFace * BM_face_create(BMesh *bm, BMVert *const *verts, BMEdge *const *edges, const int len, const BMFace *f_example, const eBMCreateFlag create_flag)
BMVert * BM_vert_create(BMesh *bm, const float co[3], const BMVert *v_example, const eBMCreateFlag create_flag)
Main function for creating a new vertex.
Definition bmesh_core.cc:41
BMEdge * BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
Main function for creating a new edge.
@ BM_CREATE_SKIP_CD
Definition bmesh_core.hh:36
#define BM_elem_index_get(ele)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
BMesh const char void * data
BMesh * bm
void BM_select_history_clear(BMesh *bm)
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
#define BM_select_history_store_notest(bm, ele)
static Vector< MeshToBMeshLayerInfo > mesh_to_bm_copy_info_calc(const CustomData &mesh_data, CustomData &bm_data)
static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey)
void BM_mesh_bm_from_me(BMesh *bm, const Mesh *mesh, const BMeshFromMeshParams *params)
void BM_mesh_bm_to_me_for_eval(BMesh &bm, Mesh &mesh, const CustomData_MeshMasks *cd_mask_extra)
static void bm_to_mesh_shape(BMesh *bm, Key *key, MutableSpan< float3 > positions, const bool active_shapekey_to_mvert)
static BMFace * bm_face_create_from_mpoly(BMesh &bm, Span< int > face_verts, Span< int > face_edges, Span< BMVert * > vtable, Span< BMEdge * > etable)
static void bmesh_to_mesh_calc_object_remap(Main &bmain, Mesh &mesh, BMesh &bm, const int old_totvert)
void BM_mesh_bm_to_me_compact(BMesh &bm, Mesh &mesh, const CustomData_MeshMasks *mask, const bool add_mesh_attributes)
static BMVert ** bm_to_mesh_vertex_map(BMesh *bm, const int old_verts_num)
BMesh -> Mesh.
static Vector< BMeshToMeshLayerInfo > bm_to_mesh_copy_info_calc(const CustomData &bm_data, CustomData &mesh_data)
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMeshParams *params)
static void mesh_attributes_copy_to_bmesh_block(CustomData &data, const Span< MeshToBMeshLayerInfo > copy_info, const int mesh_index, BMHeader &header)
bool BM_attribute_stored_in_bmesh_builtin(const StringRef name)
static void assert_bmesh_has_no_mesh_only_attributes(const BMesh &bm)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
void BM_face_normal_update(BMFace *f)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
AttributeSet attributes
const T * data() const
Definition BLI_array.hh:312
IndexRange index_range() const
Definition BLI_array.hh:360
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
bool is_empty() const
Definition BLI_array.hh:264
constexpr int64_t start() const
constexpr bool is_empty() const
Definition BLI_span.hh:509
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
int64_t size() const
void append(const T &value)
IndexRange index_range() const
void fill(const T &value) const
GAttributeReader lookup(const StringRef attribute_id) const
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
nullptr float
static float verts[][3]
#define printf(...)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define LOG(level)
Definition log.h:97
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
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
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
static char faces[256]
void parallel_invoke(Functions &&...functions)
Definition BLI_task.hh:221
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
static void bm_to_mesh_loops(const BMesh &bm, const Span< const BMLoop * > bm_loops, Mesh &mesh, MutableSpan< bool > uv_select_vert, MutableSpan< bool > uv_select_edge)
static void bm_to_mesh_verts(const BMesh &bm, const Span< const BMVert * > bm_verts, Mesh &mesh, MutableSpan< bool > select_vert, MutableSpan< bool > hide_vert)
static void bm_to_mesh_edges(const BMesh &bm, const Span< const BMEdge * > bm_edges, Mesh &mesh, MutableSpan< bool > select_edge, MutableSpan< bool > hide_edge, MutableSpan< bool > sharp_edge, MutableSpan< bool > uv_seams)
static void bm_vert_table_build(BMesh &bm, MutableSpan< const BMVert * > table, bool &need_select_vert, bool &need_hide_vert)
static void bm_edge_table_build(BMesh &bm, MutableSpan< const BMEdge * > table, bool &need_select_edge, bool &need_hide_edge, bool &need_sharp_edge, bool &need_uv_seams)
static void bmesh_block_copy_to_mesh_attributes(const Span< BMeshToMeshLayerInfo > copy_info, const int mesh_index, const void *block)
static void bm_face_loop_table_build(BMesh &bm, MutableSpan< const BMFace * > face_table, MutableSpan< const BMLoop * > loop_table, bool &need_select_poly, bool &need_hide_poly, bool &need_sharp_face, bool &need_material_index, Vector< int > &loop_layers_not_to_copy)
static void bm_to_mesh_faces(const BMesh &bm, const Span< const BMFace * > bm_faces, Mesh &mesh, MutableSpan< bool > select_poly, MutableSpan< bool > hide_poly, MutableSpan< bool > sharp_faces, MutableSpan< bool > uv_select_face, MutableSpan< int > material_indices)
VecBase< float, 3 > float3
const char * name
BMHeader head
short mat_nr
BMHeader head
void * data
BMHeader head
struct BMLoop * next
float co[3]
BMHeader head
CustomDataLayer * layers
char name[258]
Definition DNA_ID.h:432
char name[64]
struct KeyBlock * next
void * data
int uidgen
int elemsize
char type
ListBase block
KeyBlock * refkey
void * first
ListBase objects
Definition BKE_main.hh:280
int corners_num
CustomData edge_data
int edges_num
MeshRuntimeHandle * runtime
uint16_t flag
CustomData corner_data
CustomData face_data
int act_face
CustomData vert_data
struct Key * key
int totselect
int totface_legacy
int faces_num
struct MSelect * mselect
int verts_num
i
Definition text_draw.cc:230