Mesh: Parallelize BMesh to Mesh conversion

This is very similar to ebe8f8ce71, but applies the same
changes to conversions to non-evaluated meshes meant for original
data. The function also handles shape keys, UVs, selection history,
"scan the database" object vertex index remapping, and multires fixes.
Those operations are handled in parallel with the other conversions now.

Similar to before, the improvement is better the more attributes/data
contained in the BMesh. This time I observed an improvement of 50%
(182ms to 123ms) for a large grid mesh with many attributes, and 20%
for a large grid mesh with less data. Shape keys, selection, hiding, and
multires data should have less of a detriment to performance now too.

One remaining thing to improve is the recently added UV selection/pin
handling. This should be moved into the first single-threaded loop over
faces, or changed further.

Pull Request: https://projects.blender.org/blender/blender/pulls/105602
This commit is contained in:
Hans Goudey
2023-03-12 21:50:14 +01:00
committed by Hans Goudey
parent e2030fbc33
commit 5669c5a61b

View File

@@ -85,6 +85,7 @@
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
#include "BLI_task.hh"
#include "BLI_timeit.hh"
#include "BLI_vector.hh"
#include "BKE_attribute.hh"
@@ -949,23 +950,6 @@ static void bm_to_mesh_shape(BMesh *bm,
/** \} */
template<typename T, typename GetFn>
static void write_fn_to_attribute(blender::bke::MutableAttributeAccessor attributes,
const StringRef attribute_name,
const eAttrDomain domain,
const GetFn &get_fn)
{
using namespace blender;
bke::SpanAttributeWriter<T> attribute = attributes.lookup_or_add_for_write_only_span<T>(
attribute_name, domain);
threading::parallel_for(attribute.span.index_range(), 4096, [&](IndexRange range) {
for (const int i : range) {
attribute.span[i] = get_fn(i);
}
});
attribute.finish();
}
static void assert_bmesh_has_no_mesh_only_attributes(const BMesh &bm)
{
(void)bm; /* Unused in the release builds. */
@@ -981,68 +965,70 @@ static void assert_bmesh_has_no_mesh_only_attributes(const BMesh &bm)
BLI_assert(CustomData_get_layer_named(&bm.pdata, CD_PROP_BOOL, ".select_poly") == nullptr);
}
static void convert_bmesh_hide_flags_to_mesh_attributes(BMesh &bm,
const bool need_hide_vert,
const bool need_hide_edge,
const bool need_hide_poly,
Mesh &mesh)
static void bmesh_to_mesh_calc_object_remap(Main &bmain,
Mesh &me,
BMesh &bm,
const int old_totvert)
{
using namespace blender;
/* The "hide" attributes are stored as flags on #BMesh. */
assert_bmesh_has_no_mesh_only_attributes(bm);
BMVert **vertMap = nullptr;
BMVert *eve;
if (!(need_hide_vert || need_hide_edge || need_hide_poly)) {
return;
LISTBASE_FOREACH (Object *, ob, &bmain.objects) {
if ((ob->parent) && (ob->parent->data == &me) && ELEM(ob->partype, PARVERT1, PARVERT3)) {
if (vertMap == nullptr) {
vertMap = bm_to_mesh_vertex_map(&bm, old_totvert);
}
if (ob->par1 < old_totvert) {
eve = vertMap[ob->par1];
if (eve) {
ob->par1 = BM_elem_index_get(eve);
}
}
if (ob->par2 < old_totvert) {
eve = vertMap[ob->par2];
if (eve) {
ob->par2 = BM_elem_index_get(eve);
}
}
if (ob->par3 < old_totvert) {
eve = vertMap[ob->par3];
if (eve) {
ob->par3 = BM_elem_index_get(eve);
}
}
}
if (ob->data == &me) {
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == eModifierType_Hook) {
HookModifierData *hmd = (HookModifierData *)md;
if (vertMap == nullptr) {
vertMap = bm_to_mesh_vertex_map(&bm, old_totvert);
}
int i, j;
for (i = j = 0; i < hmd->indexar_num; i++) {
if (hmd->indexar[i] < old_totvert) {
eve = vertMap[hmd->indexar[i]];
if (eve) {
hmd->indexar[j++] = BM_elem_index_get(eve);
}
}
else {
j++;
}
}
hmd->indexar_num = j;
}
}
}
}
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
BM_mesh_elem_table_ensure(&bm, BM_VERT | BM_EDGE | BM_FACE);
if (need_hide_vert) {
write_fn_to_attribute<bool>(attributes, ".hide_vert", ATTR_DOMAIN_POINT, [&](const int i) {
return BM_elem_flag_test(BM_vert_at_index(&bm, i), BM_ELEM_HIDDEN);
});
}
if (need_hide_edge) {
write_fn_to_attribute<bool>(attributes, ".hide_edge", ATTR_DOMAIN_EDGE, [&](const int i) {
return BM_elem_flag_test(BM_edge_at_index(&bm, i), BM_ELEM_HIDDEN);
});
}
if (need_hide_poly) {
write_fn_to_attribute<bool>(attributes, ".hide_poly", ATTR_DOMAIN_FACE, [&](const int i) {
return BM_elem_flag_test(BM_face_at_index(&bm, i), BM_ELEM_HIDDEN);
});
}
}
static void convert_bmesh_selection_flags_to_mesh_attributes(BMesh &bm,
const bool need_select_vert,
const bool need_select_edge,
const bool need_select_poly,
Mesh &mesh)
{
using namespace blender;
if (!(need_select_vert || need_select_edge || need_select_poly)) {
return;
}
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
BM_mesh_elem_table_ensure(&bm, BM_VERT | BM_EDGE | BM_FACE);
if (need_select_vert) {
write_fn_to_attribute<bool>(attributes, ".select_vert", ATTR_DOMAIN_POINT, [&](const int i) {
return BM_elem_flag_test(BM_vert_at_index(&bm, i), BM_ELEM_SELECT);
});
}
if (need_select_edge) {
write_fn_to_attribute<bool>(attributes, ".select_edge", ATTR_DOMAIN_EDGE, [&](const int i) {
return BM_elem_flag_test(BM_edge_at_index(&bm, i), BM_ELEM_SELECT);
});
}
if (need_select_poly) {
write_fn_to_attribute<bool>(attributes, ".select_poly", ATTR_DOMAIN_FACE, [&](const int i) {
return BM_elem_flag_test(BM_face_at_index(&bm, i), BM_ELEM_SELECT);
});
if (vertMap) {
MEM_freeN(vertMap);
}
}
@@ -1110,6 +1096,7 @@ static void bm_vert_table_build(BMesh &bm,
BM_elem_index_set(vert, i); /* set_inline */
table[i] = vert;
hflag |= vert->head.hflag;
BM_CHECK_ELEMENT(vert);
}
need_select_vert = (hflag & BM_ELEM_SELECT) != 0;
need_hide_vert = (hflag & BM_ELEM_HIDDEN) != 0;
@@ -1130,6 +1117,7 @@ static void bm_edge_table_build(BMesh &bm,
BM_elem_index_set(edge, i); /* set_inline */
table[i] = edge;
hflag |= edge->head.hflag;
BM_CHECK_ELEMENT(edge);
}
need_select_edge = (hflag & BM_ELEM_SELECT) != 0;
need_hide_edge = (hflag & BM_ELEM_HIDDEN) != 0;
@@ -1156,11 +1144,13 @@ static void bm_face_loop_table_build(BMesh &bm,
hflag |= face->head.hflag;
need_sharp_face |= (face->head.hflag & BM_ELEM_SMOOTH) == 0;
need_material_index |= face->mat_nr != 0;
BM_CHECK_ELEMENT(face);
BMLoop *loop = BM_FACE_FIRST_LOOP(face);
for ([[maybe_unused]] const int i : IndexRange(face->len)) {
BM_elem_index_set(loop, loop_i); /* set_inline */
loop_table[loop_i] = loop;
BM_CHECK_ELEMENT(loop);
loop = loop->next;
loop_i++;
}
@@ -1186,6 +1176,8 @@ static void bm_to_mesh_verts(const BMesh &bm,
MutableSpan<bool> select_vert,
MutableSpan<bool> hide_vert)
{
CustomData_add_layer_named(
&mesh.vdata, CD_PROP_FLOAT3, CD_CONSTRUCT, nullptr, mesh.totvert, "position");
const Vector<BMeshToMeshLayerInfo> info = bm_to_mesh_copy_info_calc(bm.vdata, mesh.vdata);
MutableSpan<float3> dst_vert_positions = mesh.vert_positions_for_write();
threading::parallel_for(dst_vert_positions.index_range(), 1024, [&](const IndexRange range) {
@@ -1215,6 +1207,7 @@ static void bm_to_mesh_edges(const BMesh &bm,
MutableSpan<bool> sharp_edge,
MutableSpan<bool> uv_seams)
{
CustomData_add_layer(&mesh.edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, mesh.totedge);
const Vector<BMeshToMeshLayerInfo> info = bm_to_mesh_copy_info_calc(bm.edata, mesh.edata);
MutableSpan<MEdge> dst_edges = mesh.edges_for_write();
threading::parallel_for(dst_edges.index_range(), 512, [&](const IndexRange range) {
@@ -1256,6 +1249,7 @@ static void bm_to_mesh_faces(const BMesh &bm,
MutableSpan<bool> sharp_faces,
MutableSpan<int> material_indices)
{
CustomData_add_layer(&mesh.pdata, CD_MPOLY, CD_CONSTRUCT, nullptr, mesh.totpoly);
const Vector<BMeshToMeshLayerInfo> info = bm_to_mesh_copy_info_calc(bm.pdata, mesh.pdata);
MutableSpan<MPoly> dst_polys = mesh.polys_for_write();
threading::parallel_for(dst_polys.index_range(), 1024, [&](const IndexRange range) {
@@ -1291,6 +1285,7 @@ static void bm_to_mesh_faces(const BMesh &bm,
static void bm_to_mesh_loops(const BMesh &bm, const Span<const BMLoop *> bm_loops, Mesh &mesh)
{
CustomData_add_layer(&mesh.ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, mesh.totloop);
const Vector<BMeshToMeshLayerInfo> info = bm_to_mesh_copy_info_calc(bm.ldata, mesh.ldata);
MutableSpan<MLoop> dst_loops = mesh.loops_for_write();
threading::parallel_for(dst_loops.index_range(), 1024, [&](const IndexRange range) {
@@ -1309,13 +1304,8 @@ static void bm_to_mesh_loops(const BMesh &bm, const Span<const BMLoop *> bm_loop
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params)
{
using namespace blender;
BMVert *v, *eve;
BMEdge *e;
BMFace *f;
BMIter iter;
int i, j;
const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
const int ototvert = me->totvert;
@@ -1333,11 +1323,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
/* Add new custom data. */
me->totvert = bm->totvert;
me->totedge = bm->totedge;
me->totface = 0;
me->totloop = bm->totloop;
me->totpoly = bm->totface;
/* Will be overwritten with a valid value if 'dotess' is set, otherwise we
* end up with 'me->totface' and `me->mface == nullptr` which can crash #28625. */
me->totface = 0;
me->act_face = -1;
/* Mark UV selection layers which are all false as 'nocopy'. */
@@ -1418,26 +1406,6 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
CustomData_copy(&bm->pdata, &me->pdata, mask.pmask, CD_SET_DEFAULT, me->totpoly);
}
const Vector<BMeshToMeshLayerInfo> vert_info = bm_to_mesh_copy_info_calc(bm->vdata, me->vdata);
const Vector<BMeshToMeshLayerInfo> edge_info = bm_to_mesh_copy_info_calc(bm->edata, me->edata);
const Vector<BMeshToMeshLayerInfo> poly_info = bm_to_mesh_copy_info_calc(bm->pdata, me->pdata);
const Vector<BMeshToMeshLayerInfo> loop_info = bm_to_mesh_copy_info_calc(bm->ldata, me->ldata);
/* Clear the CD_FLAG_NOCOPY flags for the layers they were temporarily set on */
for (const int i : ldata_layers_marked_nocopy) {
bm->ldata.layers[i].flag &= ~CD_FLAG_NOCOPY;
}
CustomData_add_layer_named(
&me->vdata, CD_PROP_FLOAT3, CD_CONSTRUCT, nullptr, me->totvert, "position");
CustomData_add_layer(&me->edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, me->totedge);
CustomData_add_layer(&me->ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, me->totloop);
CustomData_add_layer(&me->pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, me->totpoly);
MutableSpan<float3> positions = me->vert_positions_for_write();
MutableSpan<MEdge> edges = me->edges_for_write();
MutableSpan<MPoly> polys = me->polys_for_write();
MutableSpan<MLoop> mloop = me->loops_for_write();
bool need_select_vert = false;
bool need_select_edge = false;
bool need_select_poly = false;
@@ -1445,239 +1413,180 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
bool need_hide_edge = false;
bool need_hide_poly = false;
bool need_material_index = false;
bool need_sharp_face = false;
bool need_sharp_edge = false;
bool need_uv_seam = false;
bool need_sharp_face = false;
bool need_uv_seams = false;
Array<const BMVert *> vert_table;
Array<const BMEdge *> edge_table;
Array<const BMFace *> face_table;
Array<const BMLoop *> loop_table;
threading::parallel_invoke(
me->totface > 1024,
[&]() {
vert_table.reinitialize(bm->totvert);
bm_vert_table_build(*bm, vert_table, need_select_vert, need_hide_vert);
},
[&]() {
edge_table.reinitialize(bm->totedge);
bm_edge_table_build(
*bm, edge_table, need_select_edge, need_hide_edge, need_sharp_edge, need_uv_seams);
},
[&]() {
face_table.reinitialize(bm->totface);
loop_table.reinitialize(bm->totloop);
bm_face_loop_table_build(*bm,
face_table,
loop_table,
need_select_poly,
need_hide_poly,
need_sharp_face,
need_material_index);
});
bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE | BM_LOOP);
i = 0;
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
copy_v3_v3(positions[i], v->co);
if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
need_hide_vert = true;
}
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
need_select_vert = true;
}
BM_elem_index_set(v, i); /* set_inline */
bmesh_block_copy_to_mesh_attributes(vert_info, i, v->head.data);
i++;
BM_CHECK_ELEMENT(v);
/* Add optional mesh attributes before parallel iteration. */
assert_bmesh_has_no_mesh_only_attributes(*bm);
bke::MutableAttributeAccessor attrs = me->attributes_for_write();
bke::SpanAttributeWriter<bool> select_vert;
bke::SpanAttributeWriter<bool> hide_vert;
bke::SpanAttributeWriter<bool> select_edge;
bke::SpanAttributeWriter<bool> hide_edge;
bke::SpanAttributeWriter<bool> sharp_edge;
bke::SpanAttributeWriter<bool> uv_seams;
bke::SpanAttributeWriter<bool> select_poly;
bke::SpanAttributeWriter<bool> hide_poly;
bke::SpanAttributeWriter<bool> sharp_face;
bke::SpanAttributeWriter<int> material_index;
if (need_select_vert) {
select_vert = attrs.lookup_or_add_for_write_only_span<bool>(".select_vert", ATTR_DOMAIN_POINT);
}
bm->elem_index_dirty &= ~BM_VERT;
i = 0;
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
edges[i].v1 = BM_elem_index_get(e->v1);
edges[i].v2 = BM_elem_index_get(e->v2);
if (BM_elem_flag_test(e, BM_ELEM_SEAM)) {
need_uv_seam = true;
}
if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
need_hide_edge = true;
}
if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
need_select_edge = true;
}
if (!BM_elem_flag_test(e, BM_ELEM_SMOOTH)) {
need_sharp_edge = true;
}
BM_elem_index_set(e, i); /* set_inline */
bmesh_block_copy_to_mesh_attributes(edge_info, i, e->head.data);
i++;
BM_CHECK_ELEMENT(e);
if (need_hide_vert) {
hide_vert = attrs.lookup_or_add_for_write_only_span<bool>(".hide_vert", ATTR_DOMAIN_POINT);
}
bm->elem_index_dirty &= ~BM_EDGE;
i = 0;
j = 0;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
BMLoop *l_iter, *l_first;
polys[i].loopstart = j;
polys[i].totloop = f->len;
if (f->mat_nr != 0) {
need_material_index = true;
}
if (!BM_elem_flag_test(f, BM_ELEM_SMOOTH)) {
need_sharp_face = true;
}
if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
need_hide_poly = true;
}
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
need_select_poly = true;
}
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
mloop[j].e = BM_elem_index_get(l_iter->e);
mloop[j].v = BM_elem_index_get(l_iter->v);
bmesh_block_copy_to_mesh_attributes(loop_info, j, l_iter->head.data);
j++;
BM_CHECK_ELEMENT(l_iter);
BM_CHECK_ELEMENT(l_iter->e);
BM_CHECK_ELEMENT(l_iter->v);
} while ((l_iter = l_iter->next) != l_first);
if (f == bm->act_face) {
me->act_face = i;
}
bmesh_block_copy_to_mesh_attributes(poly_info, i, f->head.data);
i++;
BM_CHECK_ELEMENT(f);
}
if (need_material_index) {
BM_mesh_elem_table_ensure(bm, BM_FACE);
write_fn_to_attribute<int>(me->attributes_for_write(),
"material_index",
ATTR_DOMAIN_FACE,
[&](const int i) { return int(BM_face_at_index(bm, i)->mat_nr); });
if (need_select_edge) {
select_edge = attrs.lookup_or_add_for_write_only_span<bool>(".select_edge", ATTR_DOMAIN_EDGE);
}
if (need_sharp_edge) {
BM_mesh_elem_table_ensure(bm, BM_EDGE);
write_fn_to_attribute<bool>(
me->attributes_for_write(), "sharp_edge", ATTR_DOMAIN_EDGE, [&](const int i) {
return !BM_elem_flag_test(BM_edge_at_index(bm, i), BM_ELEM_SMOOTH);
});
sharp_edge = attrs.lookup_or_add_for_write_only_span<bool>("sharp_edge", ATTR_DOMAIN_EDGE);
}
if (need_uv_seams) {
uv_seams = attrs.lookup_or_add_for_write_only_span<bool>(".uv_seam", ATTR_DOMAIN_EDGE);
}
if (need_hide_edge) {
hide_edge = attrs.lookup_or_add_for_write_only_span<bool>(".hide_edge", ATTR_DOMAIN_EDGE);
}
if (need_select_poly) {
select_poly = attrs.lookup_or_add_for_write_only_span<bool>(".select_poly", ATTR_DOMAIN_FACE);
}
if (need_hide_poly) {
hide_poly = attrs.lookup_or_add_for_write_only_span<bool>(".hide_poly", ATTR_DOMAIN_FACE);
}
if (need_sharp_face) {
BM_mesh_elem_table_ensure(bm, BM_FACE);
write_fn_to_attribute<bool>(
me->attributes_for_write(), "sharp_face", ATTR_DOMAIN_FACE, [&](const int i) {
return !BM_elem_flag_test(BM_face_at_index(bm, i), BM_ELEM_SMOOTH);
});
sharp_face = attrs.lookup_or_add_for_write_only_span<bool>("sharp_face", ATTR_DOMAIN_FACE);
}
if (need_uv_seam) {
BM_mesh_elem_table_ensure(bm, BM_EDGE);
write_fn_to_attribute<bool>(
me->attributes_for_write(), ".uv_seam", ATTR_DOMAIN_EDGE, [&](const int i) {
return BM_elem_flag_test(BM_edge_at_index(bm, i), BM_ELEM_SEAM);
});
if (need_material_index) {
material_index = attrs.lookup_or_add_for_write_only_span<int>("material_index",
ATTR_DOMAIN_FACE);
}
/* Patch hook indices and vertex parents. */
if (params->calc_object_remap && (ototvert > 0)) {
BLI_assert(bmain != nullptr);
BMVert **vertMap = nullptr;
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
if ((ob->parent) && (ob->parent->data == me) && ELEM(ob->partype, PARVERT1, PARVERT3)) {
if (vertMap == nullptr) {
vertMap = bm_to_mesh_vertex_map(bm, ototvert);
/* Loop over all elements in parallel, copying attributes and building the Mesh topology. */
threading::parallel_invoke(
me->totvert > 1024,
[&]() {
bm_to_mesh_verts(*bm, vert_table, *me, select_vert.span, hide_vert.span);
if (me->key) {
bm_to_mesh_shape(
bm, me->key, me->vert_positions_for_write(), params->active_shapekey_to_mvert);
}
},
[&]() {
bm_to_mesh_edges(*bm,
edge_table,
*me,
select_edge.span,
hide_edge.span,
sharp_edge.span,
uv_seams.span);
},
[&]() {
bm_to_mesh_faces(*bm,
face_table,
*me,
select_poly.span,
hide_poly.span,
sharp_face.span,
material_index.span);
if (bm->act_face) {
me->act_face = BM_elem_index_get(bm->act_face);
}
},
[&]() {
bm_to_mesh_loops(*bm, loop_table, *me);
/* Topology could be changed, ensure #CD_MDISPS are ok. */
multires_topology_changed(me);
/* Clear the CD_FLAG_NOCOPY flags for the layers they were temporarily set on */
for (const int i : ldata_layers_marked_nocopy) {
bm->ldata.layers[i].flag &= ~CD_FLAG_NOCOPY;
}
},
[&]() {
/* Patch hook indices and vertex parents. */
if (params->calc_object_remap && (ototvert > 0)) {
bmesh_to_mesh_calc_object_remap(*bmain, *me, *bm, ototvert);
}
},
[&]() {
me->totselect = BLI_listbase_count(&(bm->selected));
if (ob->par1 < ototvert) {
eve = vertMap[ob->par1];
if (eve) {
ob->par1 = BM_elem_index_get(eve);
MEM_SAFE_FREE(me->mselect);
if (me->totselect != 0) {
me->mselect = static_cast<MSelect *>(
MEM_mallocN(sizeof(MSelect) * me->totselect, "Mesh selection history"));
}
int i;
LISTBASE_FOREACH_INDEX (BMEditSelection *, selected, &bm->selected, i) {
if (selected->htype == BM_VERT) {
me->mselect[i].type = ME_VSEL;
}
}
if (ob->par2 < ototvert) {
eve = vertMap[ob->par2];
if (eve) {
ob->par2 = BM_elem_index_get(eve);
else if (selected->htype == BM_EDGE) {
me->mselect[i].type = ME_ESEL;
}
}
if (ob->par3 < ototvert) {
eve = vertMap[ob->par3];
if (eve) {
ob->par3 = BM_elem_index_get(eve);
else if (selected->htype == BM_FACE) {
me->mselect[i].type = ME_FSEL;
}
}
}
if (ob->data == me) {
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == eModifierType_Hook) {
HookModifierData *hmd = (HookModifierData *)md;
if (vertMap == nullptr) {
vertMap = bm_to_mesh_vertex_map(bm, ototvert);
me->mselect[i].index = BM_elem_index_get(selected->ele);
}
},
[&]() {
/* Run this even when shape keys aren't used since it may be used for hooks or vertex
* parents. */
if (params->update_shapekey_indices) {
/* We have written a new shape key, if this mesh is _not_ going to be freed,
* update the shape key indices to match the newly updated. */
const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata,
CD_SHAPE_KEYINDEX);
if (cd_shape_keyindex_offset != -1) {
BMIter iter;
BMVert *vert;
int i;
BM_ITER_MESH_INDEX (vert, &iter, bm, BM_VERTS_OF_MESH, i) {
BM_ELEM_CD_SET_INT(vert, cd_shape_keyindex_offset, i);
}
for (i = j = 0; i < hmd->indexar_num; i++) {
if (hmd->indexar[i] < ototvert) {
eve = vertMap[hmd->indexar[i]];
if (eve) {
hmd->indexar[j++] = BM_elem_index_get(eve);
}
}
else {
j++;
}
}
hmd->indexar_num = j;
}
}
}
}
});
if (vertMap) {
MEM_freeN(vertMap);
}
}
convert_bmesh_hide_flags_to_mesh_attributes(
*bm, need_hide_vert, need_hide_edge, need_hide_poly, *me);
convert_bmesh_selection_flags_to_mesh_attributes(
*bm, need_select_vert, need_select_edge, need_select_poly, *me);
{
me->totselect = BLI_listbase_count(&(bm->selected));
MEM_SAFE_FREE(me->mselect);
if (me->totselect != 0) {
me->mselect = static_cast<MSelect *>(
MEM_mallocN(sizeof(MSelect) * me->totselect, "Mesh selection history"));
}
LISTBASE_FOREACH_INDEX (BMEditSelection *, selected, &bm->selected, i) {
if (selected->htype == BM_VERT) {
me->mselect[i].type = ME_VSEL;
}
else if (selected->htype == BM_EDGE) {
me->mselect[i].type = ME_ESEL;
}
else if (selected->htype == BM_FACE) {
me->mselect[i].type = ME_FSEL;
}
me->mselect[i].index = BM_elem_index_get(selected->ele);
}
}
if (me->key) {
bm_to_mesh_shape(bm, me->key, positions, params->active_shapekey_to_mvert);
}
/* Run this even when shape keys aren't used since it may be used for hooks or vertex parents. */
if (params->update_shapekey_indices) {
/* We have written a new shape key, if this mesh is _not_ going to be freed,
* update the shape key indices to match the newly updated. */
if (cd_shape_keyindex_offset != -1) {
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
BM_ELEM_CD_SET_INT(eve, cd_shape_keyindex_offset, i);
}
}
}
/* Topology could be changed, ensure #CD_MDISPS are ok. */
multires_topology_changed(me);
select_vert.finish();
hide_vert.finish();
select_edge.finish();
hide_edge.finish();
sharp_edge.finish();
uv_seams.finish();
select_poly.finish();
hide_poly.finish();
sharp_face.finish();
material_index.finish();
}
/* NOTE: The function is called from multiple threads with the same input BMesh and different
@@ -1698,14 +1607,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
me->totloop = bm->totloop;
me->totpoly = bm->totface;
if (!CustomData_get_layer_named(&me->vdata, CD_PROP_FLOAT3, "position")) {
CustomData_add_layer_named(
&me->vdata, CD_PROP_FLOAT3, CD_CONSTRUCT, nullptr, bm->totvert, "position");
}
CustomData_add_layer(&me->edata, CD_MEDGE, CD_CONSTRUCT, nullptr, bm->totedge);
CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CONSTRUCT, nullptr, bm->totloop);
CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CONSTRUCT, nullptr, bm->totface);
/* Don't process shape-keys, we only feed them through the modifier stack as needed,
* e.g. for applying modifiers or the like. */
CustomData_MeshMasks mask = CD_MASK_DERIVEDMESH;