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