Fix #78916: unpredictable results with merge by distance
The merge by distance operator now has an optional merge centroid option, when it is enabled, groups of merged vertices are averaged and moved to their centroid position. This allows for more predictable results in cases where vertices that form loops would have otherwise collapsed unevenly and ended up with jagged lines. Ref !146478
This commit is contained in:
@@ -623,6 +623,9 @@ static BMOpDefine bmo_weld_verts_def = {
|
||||
{
|
||||
/* Maps welded vertices to verts they should weld to. */
|
||||
{"targetmap", BMO_OP_SLOT_MAPPING, {eBMOpSlotSubType_Elem(BMO_OP_SLOT_SUBTYPE_MAP_ELEM)}},
|
||||
/* Merged vertices to their centroid position,
|
||||
* otherwise the position of the target vertex is used. */
|
||||
{"use_centroid", BMO_OP_SLOT_BOOL},
|
||||
{{'\0'}},
|
||||
},
|
||||
/*slot_types_out*/
|
||||
|
||||
@@ -187,6 +187,7 @@ void bmo_weld_verts_exec(BMesh *bm, BMOperator *op)
|
||||
BMLoop *l;
|
||||
BMFace *f;
|
||||
BMOpSlot *slot_targetmap = BMO_slot_get(op->slots_in, "targetmap");
|
||||
const bool use_centroid = BMO_slot_bool_get(op->slots_in, "use_centroid");
|
||||
|
||||
/* Maintain selection history. */
|
||||
const bool has_selected = !BLI_listbase_is_empty(&bm->selected);
|
||||
@@ -197,6 +198,51 @@ void bmo_weld_verts_exec(BMesh *bm, BMOperator *op)
|
||||
targetmap_all = BLI_ghash_ptr_new(__func__);
|
||||
}
|
||||
|
||||
if (use_centroid) {
|
||||
GHash *clusters = BLI_ghash_ptr_new(__func__);
|
||||
|
||||
/* Group vertices by their survivor. */
|
||||
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
BMVert *v_dst = static_cast<BMVert *>(BMO_slot_map_elem_get(slot_targetmap, v));
|
||||
if (v_dst && v_dst != v) {
|
||||
void **cluster_p;
|
||||
if (!BLI_ghash_ensure_p(clusters, v_dst, &cluster_p)) {
|
||||
*cluster_p = MEM_new<blender::Vector<BMVert *>>(__func__);
|
||||
}
|
||||
blender::Vector<BMVert *> *cluster = static_cast<blender::Vector<BMVert *> *>(*cluster_p);
|
||||
cluster->append(v);
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute centroid for each survivor. */
|
||||
GHashIterator gh_iter;
|
||||
GHASH_ITER (gh_iter, clusters) {
|
||||
BMVert *v_dst = static_cast<BMVert *>(BLI_ghashIterator_getKey(&gh_iter));
|
||||
blender::Vector<BMVert *> *cluster = static_cast<blender::Vector<BMVert *> *>(
|
||||
BLI_ghashIterator_getValue(&gh_iter));
|
||||
|
||||
float centroid[3];
|
||||
copy_v3_v3(centroid, v_dst->co);
|
||||
int count = 1; /* Include `v_dst`. */
|
||||
|
||||
for (BMVert *v_duplicate : *cluster) {
|
||||
add_v3_v3(centroid, v_duplicate->co);
|
||||
count++;
|
||||
}
|
||||
|
||||
mul_v3_fl(centroid, 1.0f / float(count));
|
||||
copy_v3_v3(v_dst->co, centroid);
|
||||
}
|
||||
|
||||
/* Free temporary cluster storage. */
|
||||
GHASH_ITER (gh_iter, clusters) {
|
||||
blender::Vector<BMVert *> *cluster = static_cast<blender::Vector<BMVert *> *>(
|
||||
BLI_ghashIterator_getValue(&gh_iter));
|
||||
MEM_delete(cluster);
|
||||
}
|
||||
BLI_ghash_free(clusters, nullptr, nullptr);
|
||||
}
|
||||
|
||||
/* mark merge verts for deletion */
|
||||
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
BMVert *v_dst = static_cast<BMVert *>(BMO_slot_map_elem_get(slot_targetmap, v));
|
||||
|
||||
@@ -3581,7 +3581,13 @@ static wmOperatorStatus edbm_remove_doubles_exec(bContext *C, wmOperator *op)
|
||||
|
||||
BMO_op_exec(em->bm, &bmop);
|
||||
|
||||
if (!EDBM_op_callf(em, op, "weld_verts targetmap=%S", &bmop, "targetmap.out")) {
|
||||
if (!EDBM_op_callf(em,
|
||||
op,
|
||||
"weld_verts targetmap=%S use_centroid=%b",
|
||||
&bmop,
|
||||
"targetmap.out",
|
||||
RNA_boolean_get(op->ptr, "use_centroid")))
|
||||
{
|
||||
BMO_op_finish(em->bm, &bmop);
|
||||
continue;
|
||||
}
|
||||
@@ -3640,6 +3646,13 @@ void MESH_OT_remove_doubles(wmOperatorType *ot)
|
||||
"Maximum distance between elements to merge",
|
||||
1e-5f,
|
||||
10.0f);
|
||||
RNA_def_boolean(ot->srna,
|
||||
"use_centroid",
|
||||
true,
|
||||
"Centroid Merge",
|
||||
"Move vertices to the centroid of the duplicate cluster, "
|
||||
"otherwise the vertex closest to the centroid is used.");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"use_unselected",
|
||||
false,
|
||||
|
||||
Reference in New Issue
Block a user