Fix repeated Face Sets IDs when joining meshes

As Face Sets IDs start from 0 and increase by 1 each time a new face set
is created in a mesh, when joining multiple meshes it could happen that
the same ID is used by several unrelated areas in multiple objects. This
checks the Face Sets IDs when joining meshes and ensures that they are
not repeated between different objects when joining them, so in the
resulting mesh all previous face sets will have different IDs.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D8224
This commit is contained in:
Pablo Dobarro 2020-07-09 17:56:00 +02:00
parent 7b1c406b54
commit 6778949e01
1 changed files with 40 additions and 1 deletions

View File

@ -297,6 +297,37 @@ static void join_mesh_single(Depsgraph *depsgraph,
*mpoly_pp += me->totpoly;
}
/* Face Sets IDs are a sparse sequence, so this function offsets all the IDs by face_set_offset and
* updates face_set_offset with the maximum ID value. This way, when used in multiple meshes, all
* of them will have different IDs for their Face Sets. */
static void mesh_join_offset_face_sets_ID(const Mesh *mesh, int *face_set_offset)
{
if (!mesh->totpoly) {
return;
}
int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
if (!face_sets) {
return;
}
int max_face_set = 0;
for (int f = 0; f < mesh->totpoly; f++) {
/* As face sets enconde the visibility in the integer sign, the offset needs to be added or
* substracted depending on the initial sign of the integer to get the new ID. */
if (abs(face_sets[f]) <= *face_set_offset) {
if (face_sets[f] > 0) {
face_sets[f] += *face_set_offset;
}
else {
face_sets[f] -= *face_set_offset;
}
}
max_face_set = max_ii(max_face_set, abs(face_sets[f]));
}
*face_set_offset = max_face_set;
}
int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@ -431,7 +462,13 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
key->type = KEY_RELATIVE;
}
/* First pass over objects: Copying materials, vertex-groups & face-maps across. */
/* Update face_set_id_offset with the face set data in the active object first. This way the Face
* Sets IDs in the active object are not the ones that are modified. */
Mesh *mesh_active = BKE_mesh_from_object(ob);
int face_set_id_offset = 0;
mesh_join_offset_face_sets_ID(mesh_active, &face_set_id_offset);
/* Copy materials, vertex-groups, face sets & face-maps across objects. */
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
/* only act if a mesh, and not the one we're joining to */
if ((ob != ob_iter) && (ob_iter->type == OB_MESH)) {
@ -463,6 +500,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
ob->actfmap = 1;
}
mesh_join_offset_face_sets_ID(me, &face_set_id_offset);
if (me->totvert) {
/* Add this object's materials to the base one's if they don't exist already
* (but only if limits not exceeded yet) */