Fix T99191: Boolean modifier creates invalid material indices
Similar to1a6d0ec71c
which changed the mesh boolean node (and also caused this bug), this commit changes the material mapping for the exact mode of the boolean modifier. Now the result should contain any material on the faces of the input objects (including materials linked to objects and meshes). The improvement is possible because materials can be changed during evaluation (as of1a81d268a1
). Differential Revision: https://developer.blender.org/D15365
This commit is contained in:
parent
becb1530b1
commit
5723bf926d
Notes:
blender-bot
2023-02-14 08:25:14 +01:00
Referenced by issue #99704, Regression: Crash with boolean modifier Referenced by issue #99592, Regression: Boolean Difference modifier - material of hole created by hidden object different for fast and exact Referenced by issue #99191, Regression: Blender crashes when texture painting (caused by boolean modifers)
|
@ -14,6 +14,7 @@
|
|||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
|
@ -62,7 +63,11 @@
|
|||
|
||||
using blender::Array;
|
||||
using blender::float4x4;
|
||||
using blender::IndexRange;
|
||||
using blender::MutableSpan;
|
||||
using blender::Span;
|
||||
using blender::Vector;
|
||||
using blender::VectorSet;
|
||||
|
||||
static void initData(ModifierData *md)
|
||||
{
|
||||
|
@ -375,19 +380,23 @@ static void BMD_mesh_intersection(BMesh *bm,
|
|||
|
||||
#ifdef WITH_GMP
|
||||
|
||||
/* Get a mapping from material slot numbers in the src_ob to slot numbers in the dst_ob.
|
||||
* If a material doesn't exist in the dst_ob, the mapping just goes to the same slot
|
||||
* or to zero if there aren't enough slots in the destination.
|
||||
* Caller owns the returned array. */
|
||||
static Array<short> get_material_remap(Object *dest_ob, Object *src_ob)
|
||||
/* Get a mapping from material slot numbers in the source geometry to slot numbers in the result
|
||||
* geometry. The material is added to the result geometry if it doesn't already use it. */
|
||||
static Array<short> get_material_remap(Object &object,
|
||||
const Mesh &mesh,
|
||||
VectorSet<Material *> &materials)
|
||||
{
|
||||
int n = src_ob->totcol;
|
||||
if (n <= 0) {
|
||||
n = 1;
|
||||
const int material_num = mesh.totcol;
|
||||
if (material_num == 0) {
|
||||
/* Necessary for faces using the default material when there are no material slots. */
|
||||
return Array<short>({materials.index_of_or_add(nullptr)});
|
||||
}
|
||||
Array<short> remap(n);
|
||||
BKE_object_material_remap_calc(dest_ob, src_ob, remap.data());
|
||||
return remap;
|
||||
Array<short> map(material_num);
|
||||
for (const int i : IndexRange(material_num)) {
|
||||
Material *material = BKE_object_material_get_eval(&object, i + 1);
|
||||
map[i] = materials.index_of_or_add(material);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
|
||||
|
@ -396,6 +405,8 @@ static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
|
|||
{
|
||||
Vector<const Mesh *> meshes;
|
||||
Vector<float4x4 *> obmats;
|
||||
|
||||
VectorSet<Material *> materials;
|
||||
Vector<Array<short>> material_remaps;
|
||||
|
||||
# ifdef DEBUG_TIME
|
||||
|
@ -409,6 +420,14 @@ static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
|
|||
meshes.append(mesh);
|
||||
obmats.append((float4x4 *)&ctx->object->obmat);
|
||||
material_remaps.append({});
|
||||
if (mesh->totcol == 0) {
|
||||
/* Necessary for faces using the default material when there are no material slots. */
|
||||
materials.add(nullptr);
|
||||
}
|
||||
else {
|
||||
materials.add_multiple({mesh->mat, mesh->totcol});
|
||||
}
|
||||
|
||||
if (bmd->flag & eBooleanModifierFlag_Object) {
|
||||
Mesh *mesh_operand = BKE_modifier_get_evaluated_mesh_from_evaluated_object(bmd->object, false);
|
||||
if (!mesh_operand) {
|
||||
|
@ -417,7 +436,7 @@ static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
|
|||
BKE_mesh_wrapper_ensure_mdata(mesh_operand);
|
||||
meshes.append(mesh_operand);
|
||||
obmats.append((float4x4 *)&bmd->object->obmat);
|
||||
material_remaps.append(get_material_remap(ctx->object, bmd->object));
|
||||
material_remaps.append(get_material_remap(*bmd->object, *mesh_operand, materials));
|
||||
}
|
||||
else if (bmd->flag & eBooleanModifierFlag_Collection) {
|
||||
Collection *collection = bmd->collection;
|
||||
|
@ -432,7 +451,7 @@ static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
|
|||
BKE_mesh_wrapper_ensure_mdata(collection_mesh);
|
||||
meshes.append(collection_mesh);
|
||||
obmats.append((float4x4 *)&ob->obmat);
|
||||
material_remaps.append(get_material_remap(ctx->object, ob));
|
||||
material_remaps.append(get_material_remap(*ob, *collection_mesh, materials));
|
||||
}
|
||||
}
|
||||
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
|
||||
|
@ -441,14 +460,19 @@ static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
|
|||
|
||||
const bool use_self = (bmd->flag & eBooleanModifierFlag_Self) != 0;
|
||||
const bool hole_tolerant = (bmd->flag & eBooleanModifierFlag_HoleTolerant) != 0;
|
||||
return blender::meshintersect::direct_mesh_boolean(meshes,
|
||||
obmats,
|
||||
*(float4x4 *)&ctx->object->obmat,
|
||||
material_remaps,
|
||||
use_self,
|
||||
hole_tolerant,
|
||||
bmd->operation,
|
||||
nullptr);
|
||||
Mesh *result = blender::meshintersect::direct_mesh_boolean(meshes,
|
||||
obmats,
|
||||
*(float4x4 *)&ctx->object->obmat,
|
||||
material_remaps,
|
||||
use_self,
|
||||
hole_tolerant,
|
||||
bmd->operation,
|
||||
nullptr);
|
||||
MEM_SAFE_FREE(result->mat);
|
||||
result->mat = (Material **)MEM_malloc_arrayN(materials.size(), sizeof(Material *), __func__);
|
||||
result->totcol = materials.size();
|
||||
MutableSpan(result->mat, result->totcol).copy_from(materials);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in New Issue