Cleanup: Remove unecessary C API for direct mesh boolean
The main goal here is to remove the need for a C API to the code in `mesh_boolean_convert.cc`. This is achieved by moving `MOD_boolean.c` to C++ and making the necessary changes for it to compile. On top of that there are some other slight simplifications possible to the direct mesh boolean code: it doesn't need to copy the material remaps, and the modifier code can use some other C++ types directly.
This commit is contained in:
parent
77f6857740
commit
a0e1080428
Notes:
blender-bot
2023-02-13 19:04:56 +01:00
Referenced by issue #87138, Memory leak in boolean fast solver collection mode
|
@ -23,39 +23,21 @@
|
|||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_float4x4.hh"
|
||||
#include "BLI_mesh_boolean.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
Mesh *BKE_mesh_boolean(const Mesh **meshes,
|
||||
const float (*obmats[])[4][4],
|
||||
const float (*target_transform)[4][4],
|
||||
const short **material_remaps,
|
||||
const int meshes_len,
|
||||
const bool use_self,
|
||||
const bool hole_tolerant,
|
||||
const int boolean_mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
# include "BLI_float4x4.hh"
|
||||
# include "BLI_mesh_boolean.hh"
|
||||
# include "BLI_span.hh"
|
||||
struct Mesh;
|
||||
|
||||
namespace blender::meshintersect {
|
||||
|
||||
Mesh *direct_mesh_boolean(blender::Span<const Mesh *> meshes,
|
||||
blender::Span<const float4x4 *> obmats,
|
||||
const float4x4 &target_transform,
|
||||
blender::Span<const short *> material_remaps,
|
||||
blender::Span<blender::Array<short>> material_remaps,
|
||||
const bool use_self,
|
||||
const bool hole_tolerant,
|
||||
const int boolean_mode);
|
||||
|
||||
} // namespace blender::meshintersect
|
||||
|
||||
#endif
|
|
@ -371,7 +371,7 @@ set(SRC
|
|||
BKE_mball.h
|
||||
BKE_mball_tessellate.h
|
||||
BKE_mesh.h
|
||||
BKE_mesh_boolean_convert.h
|
||||
BKE_mesh_boolean_convert.hh
|
||||
BKE_mesh_fair.h
|
||||
BKE_mesh_iterators.h
|
||||
BKE_mesh_mapping.h
|
||||
|
|
|
@ -28,9 +28,10 @@
|
|||
#include "BKE_customdata.h"
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_boolean_convert.h"
|
||||
#include "BKE_mesh_boolean_convert.hh"
|
||||
|
||||
#include "BLI_alloca.h"
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_float2.hh"
|
||||
#include "BLI_float4x4.hh"
|
||||
#include "BLI_math.h"
|
||||
|
@ -97,7 +98,7 @@ class MeshesToIMeshInfo {
|
|||
Array<float4x4> to_target_transform;
|
||||
/* For each input mesh, how to remap the material slot numbers to
|
||||
* the material slots in the first mesh. */
|
||||
Array<const short *> material_remaps;
|
||||
Span<Array<short>> material_remaps;
|
||||
/* Total number of input mesh vertices. */
|
||||
int tot_meshes_verts;
|
||||
/* Total number of input mesh edges. */
|
||||
|
@ -243,7 +244,7 @@ const MEdge *MeshesToIMeshInfo::input_medge_for_orig_index(int orig_index,
|
|||
*/
|
||||
static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
|
||||
Span<const float4x4 *> obmats,
|
||||
Span<const short *> material_remaps,
|
||||
Span<Array<short>> material_remaps,
|
||||
const float4x4 &target_transform,
|
||||
IMeshArena &arena,
|
||||
MeshesToIMeshInfo *r_info)
|
||||
|
@ -274,7 +275,7 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
|
|||
r_info->mesh_edge_offset = Array<int>(nmeshes);
|
||||
r_info->mesh_poly_offset = Array<int>(nmeshes);
|
||||
r_info->to_target_transform = Array<float4x4>(nmeshes);
|
||||
r_info->material_remaps = Array<const short *>(nmeshes);
|
||||
r_info->material_remaps = material_remaps;
|
||||
int v = 0;
|
||||
int e = 0;
|
||||
int f = 0;
|
||||
|
@ -307,13 +308,6 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
|
|||
clean_obmat(*obmats[mi]);
|
||||
r_info->to_target_transform[mi] = inv_target_mat * objn_mat;
|
||||
|
||||
if (mi < material_remaps.size() && mi != 0) {
|
||||
r_info->material_remaps[mi] = material_remaps[mi];
|
||||
}
|
||||
else {
|
||||
r_info->material_remaps[mi] = nullptr;
|
||||
}
|
||||
|
||||
/* Skip the matrix multiplication for each point when there is no transform for a mesh,
|
||||
* for example when the first mesh is already in the target space. (Note the logic directly
|
||||
* above, which uses an identity matrix with a null input transform). */
|
||||
|
@ -393,14 +387,14 @@ static void copy_poly_attributes(Mesh *dest_mesh,
|
|||
const Mesh *orig_me,
|
||||
int mp_index,
|
||||
int index_in_orig_me,
|
||||
const short *material_remap)
|
||||
Span<short> material_remap)
|
||||
{
|
||||
mp->mat_nr = orig_mp->mat_nr;
|
||||
if (mp->mat_nr >= dest_mesh->totcol) {
|
||||
mp->mat_nr = 0;
|
||||
}
|
||||
else {
|
||||
if (material_remap) {
|
||||
if (material_remap.size() > 0) {
|
||||
short mat_nr = material_remap[orig_mp->mat_nr];
|
||||
if (mat_nr >= 0 && mat_nr < dest_mesh->totcol) {
|
||||
mp->mat_nr = mat_nr;
|
||||
|
@ -733,8 +727,15 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
|
|||
++cur_loop_index;
|
||||
}
|
||||
|
||||
copy_poly_attributes(
|
||||
result, mp, orig_mp, orig_me, fi, index_in_orig_me, mim.material_remaps[orig_me_index]);
|
||||
copy_poly_attributes(result,
|
||||
mp,
|
||||
orig_mp,
|
||||
orig_me,
|
||||
fi,
|
||||
index_in_orig_me,
|
||||
(mim.material_remaps.size() > 0) ?
|
||||
mim.material_remaps[orig_me_index].as_span() :
|
||||
Span<short>());
|
||||
copy_or_interp_loop_attributes(result, f, mp, orig_mp, orig_me, orig_me_index, mim);
|
||||
}
|
||||
|
||||
|
@ -771,26 +772,33 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
|
|||
#endif // WITH_GMP
|
||||
|
||||
/**
|
||||
* Do Exact Boolean directly, without a round trip through #BMesh.
|
||||
* The Mesh operands are in `meshes`, with corresponding transforms in in `obmats`.
|
||||
* Do a mesh boolean operation directly on meshes (without going back and forth to BMesh).
|
||||
* \param meshes: An array of of Mesh pointers.
|
||||
* \param obmats: An array of pointers to the obmat matrices that transform local
|
||||
* coordinates to global ones. It is allowed for the pointers to be null, meaning the
|
||||
* transformation is the identity.
|
||||
* \param material_remaps: An array of pointers to arrays of maps from material slot numbers in the
|
||||
* corresponding mesh to the material slot in the first mesh. It is OK for material_remaps or any
|
||||
* of its constituent arrays to be empty.
|
||||
*/
|
||||
Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
|
||||
Span<const float4x4 *> obmats,
|
||||
const float4x4 &target_transform,
|
||||
Span<const short *> material_remaps,
|
||||
Span<Array<short>> material_remaps,
|
||||
const bool use_self,
|
||||
const bool hole_tolerant,
|
||||
const int boolean_mode)
|
||||
{
|
||||
#ifdef WITH_GMP
|
||||
const int dbg_level = 0;
|
||||
BLI_assert(meshes.size() == obmats.size());
|
||||
const int meshes_len = meshes.size();
|
||||
if (meshes_len <= 0) {
|
||||
BLI_assert(material_remaps.size() == 0 || material_remaps.size() == meshes.size());
|
||||
if (meshes.size() <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int dbg_level = 0;
|
||||
if (dbg_level > 0) {
|
||||
std::cout << "\nDIRECT_MESH_INTERSECT, nmeshes = " << meshes_len << "\n";
|
||||
std::cout << "\nDIRECT_MESH_INTERSECT, nmeshes = " << meshes.size() << "\n";
|
||||
}
|
||||
MeshesToIMeshInfo mim;
|
||||
IMeshArena arena;
|
||||
|
@ -805,13 +813,13 @@ Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
|
|||
};
|
||||
IMesh m_out = boolean_mesh(m_in,
|
||||
static_cast<BoolOpType>(boolean_mode),
|
||||
meshes_len,
|
||||
meshes.size(),
|
||||
shape_fn,
|
||||
use_self,
|
||||
hole_tolerant,
|
||||
nullptr,
|
||||
&arena);
|
||||
if (dbg_level > 1) {
|
||||
if (dbg_level > 0) {
|
||||
std::cout << m_out;
|
||||
write_obj_mesh(m_out, "m_out");
|
||||
}
|
||||
|
@ -825,53 +833,3 @@ Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
|
|||
}
|
||||
|
||||
} // namespace blender::meshintersect
|
||||
|
||||
extern "C" {
|
||||
|
||||
#ifdef WITH_GMP
|
||||
/* Do a mesh boolean directly on meshes (without going back and forth to BMesh).
|
||||
* The \a meshes argument is an array of \a meshes_len of Mesh pointers.
|
||||
* The \a obmats argument is an array of \a meshes_len of pointers to the obmat
|
||||
* The \a material_remaps is an array of pointers to arrays of maps from material
|
||||
* slot numbers in the corresponding mesh to the material slot in the first mesh.
|
||||
* It is OK for material_remaps or any of its constituent arrays to be NULL.
|
||||
* matrices that transform local coordinates to global ones. It is allowed
|
||||
* for the pointers to be nullptr, meaning the transformation is the identity. */
|
||||
Mesh *BKE_mesh_boolean(const Mesh **meshes,
|
||||
const float (*obmats[])[4][4],
|
||||
const float (*target_transform)[4][4],
|
||||
const short **material_remaps,
|
||||
const int meshes_len,
|
||||
const bool use_self,
|
||||
const bool hole_tolerant,
|
||||
const int boolean_mode)
|
||||
{
|
||||
const blender::float4x4 **transforms = (const blender::float4x4 **)obmats;
|
||||
const blender::float4x4 &target_float4x4 = *(const blender::float4x4 *)target_transform;
|
||||
return blender::meshintersect::direct_mesh_boolean(
|
||||
blender::Span(meshes, meshes_len),
|
||||
blender::Span(transforms, meshes_len),
|
||||
target_float4x4,
|
||||
blender::Span(material_remaps, material_remaps ? meshes_len : 0),
|
||||
use_self,
|
||||
hole_tolerant,
|
||||
boolean_mode);
|
||||
}
|
||||
|
||||
#else
|
||||
Mesh *BKE_mesh_boolean(const Mesh **UNUSED(meshes),
|
||||
const float (*obmats[])[4][4],
|
||||
const float (*target_transform)[4][4],
|
||||
const short **UNUSED(material_remaps),
|
||||
const int UNUSED(meshes_len),
|
||||
const bool UNUSED(use_self),
|
||||
const bool UNUSED(hole_tolerant),
|
||||
const int UNUSED(boolean_mode))
|
||||
{
|
||||
UNUSED_VARS(obmats, target_transform);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // extern "C"
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
* \ingroup bmesh
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool BM_mesh_intersect(BMesh *bm,
|
||||
struct BMLoop *(*looptris)[3],
|
||||
const int looptris_tot,
|
||||
|
@ -41,3 +45,7 @@ enum {
|
|||
BMESH_ISECT_BOOLEAN_UNION = 1,
|
||||
BMESH_ISECT_BOOLEAN_DIFFERENCE = 2,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -50,7 +50,7 @@ set(SRC
|
|||
intern/MOD_armature.c
|
||||
intern/MOD_array.c
|
||||
intern/MOD_bevel.c
|
||||
intern/MOD_boolean.c
|
||||
intern/MOD_boolean.cc
|
||||
intern/MOD_build.c
|
||||
intern/MOD_cast.c
|
||||
intern/MOD_cloth.c
|
||||
|
|
|
@ -21,16 +21,15 @@
|
|||
* \ingroup modifiers
|
||||
*/
|
||||
|
||||
// #define DEBUG_TIME
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLI_alloca.h"
|
||||
#include "BLI_array.h"
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_float4x4.hh"
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
|
@ -49,10 +48,9 @@
|
|||
#include "BKE_lib_query.h"
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_boolean_convert.h"
|
||||
#include "BKE_mesh_boolean_convert.hh"
|
||||
#include "BKE_mesh_wrapper.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
@ -71,11 +69,16 @@
|
|||
#include "tools/bmesh_boolean.h"
|
||||
#include "tools/bmesh_intersect.h"
|
||||
|
||||
// #define DEBUG_TIME
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
# include "PIL_time.h"
|
||||
# include "PIL_time_utildefines.h"
|
||||
# include "BLI_timeit.hh"
|
||||
#endif
|
||||
|
||||
using blender::Array;
|
||||
using blender::float4x4;
|
||||
using blender::Vector;
|
||||
|
||||
static void initData(ModifierData *md)
|
||||
{
|
||||
BooleanModifierData *bmd = (BooleanModifierData *)md;
|
||||
|
@ -113,14 +116,14 @@ static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *u
|
|||
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
|
||||
{
|
||||
BooleanModifierData *bmd = (BooleanModifierData *)md;
|
||||
if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object != NULL) {
|
||||
if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object != nullptr) {
|
||||
DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_TRANSFORM, "Boolean Modifier");
|
||||
DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_GEOMETRY, "Boolean Modifier");
|
||||
}
|
||||
|
||||
Collection *col = bmd->collection;
|
||||
|
||||
if ((bmd->flag & eBooleanModifierFlag_Collection) && col != NULL) {
|
||||
if ((bmd->flag & eBooleanModifierFlag_Collection) && col != nullptr) {
|
||||
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, operand_ob) {
|
||||
if (operand_ob->type == OB_MESH && operand_ob != ctx->object) {
|
||||
DEG_add_object_relation(ctx->node, operand_ob, DEG_OB_COMP_TRANSFORM, "Boolean Modifier");
|
||||
|
@ -136,7 +139,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
|
|||
static Mesh *get_quick_mesh(
|
||||
Object *ob_self, Mesh *mesh_self, Object *ob_operand_ob, Mesh *mesh_operand_ob, int operation)
|
||||
{
|
||||
Mesh *result = NULL;
|
||||
Mesh *result = nullptr;
|
||||
|
||||
if (mesh_self->totpoly == 0 || mesh_operand_ob->totpoly == 0) {
|
||||
switch (operation) {
|
||||
|
@ -149,11 +152,11 @@ static Mesh *get_quick_mesh(
|
|||
result = mesh_self;
|
||||
}
|
||||
else {
|
||||
result = (Mesh *)BKE_id_copy_ex(NULL, &mesh_operand_ob->id, NULL, LIB_ID_COPY_LOCALIZE);
|
||||
result = (Mesh *)BKE_id_copy_ex(
|
||||
nullptr, &mesh_operand_ob->id, nullptr, LIB_ID_COPY_LOCALIZE);
|
||||
|
||||
float imat[4][4];
|
||||
float omat[4][4];
|
||||
|
||||
invert_m4_m4(imat, ob_self->obmat);
|
||||
mul_m4_m4m4(omat, imat, ob_operand_ob->obmat);
|
||||
|
||||
|
@ -189,9 +192,10 @@ static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
|
|||
return BM_elem_flag_test(f, BM_FACE_TAG) ? 1 : 0;
|
||||
}
|
||||
|
||||
static bool BMD_error_messages(const Object *ob, ModifierData *md, Collection *col)
|
||||
static bool BMD_error_messages(const Object *ob, ModifierData *md)
|
||||
{
|
||||
BooleanModifierData *bmd = (BooleanModifierData *)md;
|
||||
Collection *col = bmd->collection;
|
||||
|
||||
bool error_returns_result = false;
|
||||
|
||||
|
@ -239,22 +243,20 @@ static bool BMD_error_messages(const Object *ob, ModifierData *md, Collection *c
|
|||
static BMesh *BMD_mesh_bm_create(
|
||||
Mesh *mesh, Object *object, Mesh *mesh_operand_ob, Object *operand_ob, bool *r_is_flip)
|
||||
{
|
||||
BMesh *bm;
|
||||
#ifdef DEBUG_TIME
|
||||
SCOPED_TIMER(__func__)
|
||||
#endif
|
||||
|
||||
*r_is_flip = (is_negative_m4(object->obmat) != is_negative_m4(operand_ob->obmat));
|
||||
|
||||
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh, mesh_operand_ob);
|
||||
|
||||
bm = BM_mesh_create(&allocsize,
|
||||
&((struct BMeshCreateParams){
|
||||
.use_toolflags = false,
|
||||
}));
|
||||
BMeshCreateParams bmcp = {false};
|
||||
BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
|
||||
|
||||
BM_mesh_bm_from_me(bm,
|
||||
mesh_operand_ob,
|
||||
&((struct BMeshFromMeshParams){
|
||||
.calc_face_normal = true,
|
||||
}));
|
||||
BMeshFromMeshParams params{};
|
||||
params.calc_face_normal = true;
|
||||
BM_mesh_bm_from_me(bm, mesh_operand_ob, ¶ms);
|
||||
|
||||
if (UNLIKELY(*r_is_flip)) {
|
||||
const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
|
||||
|
@ -265,11 +267,7 @@ static BMesh *BMD_mesh_bm_create(
|
|||
}
|
||||
}
|
||||
|
||||
BM_mesh_bm_from_me(bm,
|
||||
mesh,
|
||||
&((struct BMeshFromMeshParams){
|
||||
.calc_face_normal = true,
|
||||
}));
|
||||
BM_mesh_bm_from_me(bm, mesh, ¶ms);
|
||||
|
||||
return bm;
|
||||
}
|
||||
|
@ -282,13 +280,18 @@ static void BMD_mesh_intersection(BMesh *bm,
|
|||
Object *operand_ob,
|
||||
bool is_flip)
|
||||
{
|
||||
#ifdef DEBUG_TIME
|
||||
SCOPED_TIMER(__func__)
|
||||
#endif
|
||||
|
||||
BooleanModifierData *bmd = (BooleanModifierData *)md;
|
||||
|
||||
/* main bmesh intersection setup */
|
||||
/* create tessface & intersect */
|
||||
const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
|
||||
int tottri;
|
||||
BMLoop *(*looptris)[3] = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
|
||||
BMLoop *(*looptris)[3] = (BMLoop * (*)[3])
|
||||
MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
|
||||
|
||||
BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
|
||||
|
||||
|
@ -324,12 +327,11 @@ static void BMD_mesh_intersection(BMesh *bm,
|
|||
negate_m3(nmat);
|
||||
}
|
||||
|
||||
const short ob_src_totcol = operand_ob->totcol;
|
||||
short *material_remap = BLI_array_alloca(material_remap, ob_src_totcol ? ob_src_totcol : 1);
|
||||
Array<short> material_remap(operand_ob->totcol ? operand_ob->totcol : 1);
|
||||
|
||||
/* Using original (not evaluated) object here since we are writing to it. */
|
||||
/* XXX Pretty sure comment above is fully wrong now with CoW & co ? */
|
||||
BKE_object_material_remap_calc(ctx->object, operand_ob, material_remap);
|
||||
BKE_object_material_remap_calc(ctx->object, operand_ob, material_remap.data());
|
||||
|
||||
BMFace *efa;
|
||||
i = 0;
|
||||
|
@ -341,7 +343,7 @@ static void BMD_mesh_intersection(BMesh *bm,
|
|||
BM_elem_flag_enable(efa, BM_FACE_TAG);
|
||||
|
||||
/* remap material */
|
||||
if (LIKELY(efa->mat_nr < ob_src_totcol)) {
|
||||
if (LIKELY(efa->mat_nr < operand_ob->totcol)) {
|
||||
efa->mat_nr = material_remap[efa->mat_nr];
|
||||
}
|
||||
|
||||
|
@ -370,7 +372,7 @@ static void BMD_mesh_intersection(BMesh *bm,
|
|||
looptris,
|
||||
tottri,
|
||||
bm_face_isect_pair,
|
||||
NULL,
|
||||
nullptr,
|
||||
false,
|
||||
use_separate,
|
||||
use_dissolve,
|
||||
|
@ -388,59 +390,50 @@ static void BMD_mesh_intersection(BMesh *bm,
|
|||
/* 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 must MEM_freeN the returned array. */
|
||||
static short *get_material_remap(Object *dest_ob, Object *src_ob)
|
||||
* Caller owns the returned array. */
|
||||
static Array<short> get_material_remap(Object *dest_ob, Object *src_ob)
|
||||
{
|
||||
short *remap;
|
||||
int n = dest_ob->totcol;
|
||||
if (n <= 0) {
|
||||
n = 1;
|
||||
}
|
||||
remap = MEM_mallocN(n * sizeof(short), __func__);
|
||||
BKE_object_material_remap_calc(dest_ob, src_ob, remap);
|
||||
Array<short> remap(n);
|
||||
BKE_object_material_remap_calc(dest_ob, src_ob, remap.data());
|
||||
return remap;
|
||||
}
|
||||
|
||||
/* New method: bypass trip through BMesh. */
|
||||
static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
|
||||
const ModifierEvalContext *ctx,
|
||||
Mesh *mesh)
|
||||
{
|
||||
Mesh *result;
|
||||
Mesh *mesh_operand;
|
||||
short *remap;
|
||||
Mesh **meshes = NULL;
|
||||
const float(**obmats)[4][4] = NULL;
|
||||
short **material_remaps = NULL;
|
||||
BLI_array_declare(meshes);
|
||||
BLI_array_declare(obmats);
|
||||
BLI_array_declare(material_remaps);
|
||||
Vector<const Mesh *> meshes;
|
||||
Vector<float4x4 *> obmats;
|
||||
Vector<Array<short>> material_remaps;
|
||||
|
||||
# ifdef DEBUG_TIME
|
||||
TIMEIT_START(boolean_bmesh);
|
||||
SCOPED_TIMER(__func__)
|
||||
# endif
|
||||
|
||||
if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object == NULL) {
|
||||
if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object == nullptr) {
|
||||
return mesh;
|
||||
}
|
||||
|
||||
BLI_array_append(meshes, mesh);
|
||||
BLI_array_append(obmats, &ctx->object->obmat);
|
||||
BLI_array_append(material_remaps, NULL);
|
||||
meshes.append(mesh);
|
||||
obmats.append((float4x4 *)&ctx->object->obmat);
|
||||
material_remaps.append({});
|
||||
if (bmd->flag & eBooleanModifierFlag_Object) {
|
||||
mesh_operand = BKE_modifier_get_evaluated_mesh_from_evaluated_object(bmd->object, false);
|
||||
Mesh *mesh_operand = BKE_modifier_get_evaluated_mesh_from_evaluated_object(bmd->object, false);
|
||||
if (!mesh_operand) {
|
||||
return mesh;
|
||||
}
|
||||
BKE_mesh_wrapper_ensure_mdata(mesh_operand);
|
||||
BLI_array_append(meshes, mesh_operand);
|
||||
BLI_array_append(obmats, &bmd->object->obmat);
|
||||
remap = get_material_remap(ctx->object, bmd->object);
|
||||
BLI_array_append(material_remaps, remap);
|
||||
meshes.append(mesh_operand);
|
||||
obmats.append((float4x4 *)&bmd->object->obmat);
|
||||
material_remaps.append(get_material_remap(ctx->object, bmd->object));
|
||||
}
|
||||
else if (bmd->flag & eBooleanModifierFlag_Collection) {
|
||||
Collection *collection = bmd->collection;
|
||||
/* Allow collection to be empty: then target mesh will just removed self-intersections. */
|
||||
/* Allow collection to be empty; then target mesh will just removed self-intersections. */
|
||||
if (collection) {
|
||||
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
|
||||
if (ob->type == OB_MESH && ob != ctx->object) {
|
||||
|
@ -449,10 +442,9 @@ static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
|
|||
continue;
|
||||
}
|
||||
BKE_mesh_wrapper_ensure_mdata(collection_mesh);
|
||||
BLI_array_append(meshes, collection_mesh);
|
||||
BLI_array_append(obmats, &ob->obmat);
|
||||
remap = get_material_remap(ctx->object, ob);
|
||||
BLI_array_append(material_remaps, remap);
|
||||
meshes.append(collection_mesh);
|
||||
obmats.append((float4x4 *)&ob->obmat);
|
||||
material_remaps.append(get_material_remap(ctx->object, ob));
|
||||
}
|
||||
}
|
||||
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
|
||||
|
@ -461,30 +453,13 @@ 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;
|
||||
result = BKE_mesh_boolean((const Mesh **)meshes,
|
||||
(const float(**)[4][4])obmats,
|
||||
&ctx->object->obmat,
|
||||
(const short **)material_remaps,
|
||||
BLI_array_len(meshes),
|
||||
use_self,
|
||||
hole_tolerant,
|
||||
bmd->operation);
|
||||
|
||||
BLI_array_free(meshes);
|
||||
BLI_array_free(obmats);
|
||||
for (int i = 0; i < BLI_array_len(material_remaps); i++) {
|
||||
remap = material_remaps[i];
|
||||
if (remap) {
|
||||
MEM_freeN(remap);
|
||||
}
|
||||
}
|
||||
BLI_array_free(material_remaps);
|
||||
|
||||
# ifdef DEBUG_TIME
|
||||
TIMEIT_END(boolean_bmesh);
|
||||
# endif
|
||||
|
||||
return result;
|
||||
return blender::meshintersect::direct_mesh_boolean(meshes,
|
||||
obmats,
|
||||
*(float4x4 *)&ctx->object->obmat,
|
||||
material_remaps,
|
||||
use_self,
|
||||
hole_tolerant,
|
||||
bmd->operation);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -493,12 +468,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
|
|||
BooleanModifierData *bmd = (BooleanModifierData *)md;
|
||||
Object *object = ctx->object;
|
||||
Mesh *result = mesh;
|
||||
Mesh *mesh_operand_ob;
|
||||
BMesh *bm;
|
||||
Collection *collection = bmd->collection;
|
||||
|
||||
bool is_flip = false;
|
||||
const bool confirm_return = true;
|
||||
/* Return result for certain errors. */
|
||||
if (BMD_error_messages(ctx->object, md)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef WITH_GMP
|
||||
if (bmd->solver == eBooleanModifierSolver_Exact) {
|
||||
return exact_boolean_mesh(bmd, ctx, mesh);
|
||||
|
@ -506,27 +482,18 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
|
|||
#endif
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_START(boolean_bmesh);
|
||||
SCOPED_TIMER(__func__)
|
||||
#endif
|
||||
|
||||
if (bmd->flag & eBooleanModifierFlag_Object) {
|
||||
if (bmd->object == NULL) {
|
||||
if (bmd->object == nullptr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
BMD_error_messages(ctx->object, md, NULL);
|
||||
|
||||
Object *operand_ob = bmd->object;
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_BLOCK_INIT(operand_get_evaluated_mesh);
|
||||
TIMEIT_BLOCK_START(operand_get_evaluated_mesh);
|
||||
#endif
|
||||
mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob, false);
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_BLOCK_END(operand_get_evaluated_mesh);
|
||||
TIMEIT_BLOCK_STATS(operand_get_evaluated_mesh);
|
||||
#endif
|
||||
Mesh *mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob,
|
||||
false);
|
||||
|
||||
if (mesh_operand_ob) {
|
||||
/* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
|
||||
|
@ -537,80 +504,49 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
|
|||
* Returning mesh is depended on modifiers operation (sergey) */
|
||||
result = get_quick_mesh(object, mesh, operand_ob, mesh_operand_ob, bmd->operation);
|
||||
|
||||
if (result == NULL) {
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_BLOCK_INIT(object_BMD_mesh_bm_create);
|
||||
TIMEIT_BLOCK_START(object_BMD_mesh_bm_create);
|
||||
#endif
|
||||
bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_BLOCK_END(object_BMD_mesh_bm_create);
|
||||
TIMEIT_BLOCK_STATS(object_BMD_mesh_bm_create);
|
||||
#endif
|
||||
if (result == nullptr) {
|
||||
bool is_flip;
|
||||
BMesh *bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_BLOCK_INIT(BMD_mesh_intersection);
|
||||
TIMEIT_BLOCK_START(BMD_mesh_intersection);
|
||||
#endif
|
||||
BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_BLOCK_END(BMD_mesh_intersection);
|
||||
TIMEIT_BLOCK_STATS(BMD_mesh_intersection);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_BLOCK_INIT(BKE_mesh_from_bmesh_for_eval_nomain);
|
||||
TIMEIT_BLOCK_START(BKE_mesh_from_bmesh_for_eval_nomain);
|
||||
#endif
|
||||
result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_BLOCK_END(BKE_mesh_from_bmesh_for_eval_nomain);
|
||||
TIMEIT_BLOCK_STATS(BKE_mesh_from_bmesh_for_eval_nomain);
|
||||
#endif
|
||||
result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh);
|
||||
|
||||
BM_mesh_free(bm);
|
||||
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
|
||||
}
|
||||
|
||||
/* if new mesh returned, return it; otherwise there was
|
||||
* an error, so delete the modifier object */
|
||||
if (result == NULL) {
|
||||
if (result == nullptr) {
|
||||
BKE_modifier_set_error(object, md, "Cannot execute boolean operation");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (collection == NULL) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Return result for certain errors. */
|
||||
if (BMD_error_messages(ctx->object, md, collection) == confirm_return) {
|
||||
if (collection == nullptr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, operand_ob) {
|
||||
if (operand_ob->type == OB_MESH && operand_ob != ctx->object) {
|
||||
|
||||
mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob, false);
|
||||
Mesh *mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob,
|
||||
false);
|
||||
|
||||
if (mesh_operand_ob) {
|
||||
/* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
|
||||
* But for 2.90 better not try to be smart here. */
|
||||
BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob);
|
||||
|
||||
bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
|
||||
bool is_flip;
|
||||
BMesh *bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
|
||||
|
||||
BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
|
||||
|
||||
/* Needed for multiple objects to work. */
|
||||
BM_mesh_bm_to_me(NULL,
|
||||
bm,
|
||||
mesh,
|
||||
(&(struct BMeshToMeshParams){
|
||||
.calc_object_remap = false,
|
||||
}));
|
||||
BMeshToMeshParams params{};
|
||||
params.calc_object_remap = false;
|
||||
BM_mesh_bm_to_me(nullptr, bm, mesh, ¶ms);
|
||||
|
||||
result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
|
||||
result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh);
|
||||
BM_mesh_free(bm);
|
||||
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
|
||||
}
|
||||
|
@ -619,10 +555,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
|
|||
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_END(boolean_bmesh);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -638,22 +570,21 @@ static void requiredDataMask(Object *UNUSED(ob),
|
|||
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
|
||||
{
|
||||
uiLayout *layout = panel->layout;
|
||||
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
|
||||
|
||||
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL);
|
||||
|
||||
uiItemR(layout, ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
|
||||
uiItemR(layout, ptr, "operation", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
|
||||
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
|
||||
uiItemR(layout, ptr, "operand_type", 0, NULL, ICON_NONE);
|
||||
uiItemR(layout, ptr, "operand_type", 0, nullptr, ICON_NONE);
|
||||
if (RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object) {
|
||||
uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE);
|
||||
uiItemR(layout, ptr, "object", 0, nullptr, ICON_NONE);
|
||||
}
|
||||
else {
|
||||
uiItemR(layout, ptr, "collection", 0, NULL, ICON_NONE);
|
||||
uiItemR(layout, ptr, "collection", 0, nullptr, ICON_NONE);
|
||||
}
|
||||
|
||||
uiItemR(layout, ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
|
||||
uiItemR(layout, ptr, "solver", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
|
||||
|
||||
modifier_panel_end(layout, ptr);
|
||||
}
|
||||
|
@ -661,8 +592,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
|
|||
static void solver_options_panel_draw(const bContext *UNUSED(C), Panel *panel)
|
||||
{
|
||||
uiLayout *layout = panel->layout;
|
||||
|
||||
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL);
|
||||
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
|
||||
|
||||
const bool use_exact = RNA_enum_get(ptr, "solver") == eBooleanModifierSolver_Exact;
|
||||
|
||||
|
@ -672,16 +602,16 @@ static void solver_options_panel_draw(const bContext *UNUSED(C), Panel *panel)
|
|||
if (use_exact) {
|
||||
/* When operand is collection, we always use_self. */
|
||||
if (RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object) {
|
||||
uiItemR(col, ptr, "use_self", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, ptr, "use_self", 0, nullptr, ICON_NONE);
|
||||
}
|
||||
uiItemR(col, ptr, "use_hole_tolerant", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, ptr, "use_hole_tolerant", 0, nullptr, ICON_NONE);
|
||||
}
|
||||
else {
|
||||
uiItemR(col, ptr, "double_threshold", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, ptr, "double_threshold", 0, nullptr, ICON_NONE);
|
||||
}
|
||||
|
||||
if (G.debug) {
|
||||
uiItemR(col, ptr, "debug_options", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, ptr, "debug_options", 0, nullptr, ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -689,7 +619,7 @@ static void panelRegister(ARegionType *region_type)
|
|||
{
|
||||
PanelType *panel = modifier_panel_register(region_type, eModifierType_Boolean, panel_draw);
|
||||
modifier_subpanel_register(
|
||||
region_type, "solver_options", "Solver Options", NULL, solver_options_panel_draw, panel);
|
||||
region_type, "solver_options", "Solver Options", nullptr, solver_options_panel_draw, panel);
|
||||
}
|
||||
|
||||
ModifierTypeInfo modifierType_Boolean = {
|
||||
|
@ -698,31 +628,32 @@ ModifierTypeInfo modifierType_Boolean = {
|
|||
/* structSize */ sizeof(BooleanModifierData),
|
||||
/* srna */ &RNA_BooleanModifier,
|
||||
/* type */ eModifierTypeType_Nonconstructive,
|
||||
/* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode,
|
||||
/* flags */
|
||||
(ModifierTypeFlag)(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode),
|
||||
/* icon */ ICON_MOD_BOOLEAN,
|
||||
|
||||
/* copyData */ BKE_modifier_copydata_generic,
|
||||
|
||||
/* deformVerts */ NULL,
|
||||
/* deformMatrices */ NULL,
|
||||
/* deformVertsEM */ NULL,
|
||||
/* deformMatricesEM */ NULL,
|
||||
/* deformVerts */ nullptr,
|
||||
/* deformMatrices */ nullptr,
|
||||
/* deformVertsEM */ nullptr,
|
||||
/* deformMatricesEM */ nullptr,
|
||||
/* modifyMesh */ modifyMesh,
|
||||
/* modifyHair */ NULL,
|
||||
/* modifyGeometrySet */ NULL,
|
||||
/* modifyVolume */ NULL,
|
||||
/* modifyHair */ nullptr,
|
||||
/* modifyGeometrySet */ nullptr,
|
||||
/* modifyVolume */ nullptr,
|
||||
|
||||
/* initData */ initData,
|
||||
/* requiredDataMask */ requiredDataMask,
|
||||
/* freeData */ NULL,
|
||||
/* freeData */ nullptr,
|
||||
/* isDisabled */ isDisabled,
|
||||
/* updateDepsgraph */ updateDepsgraph,
|
||||
/* dependsOnTime */ NULL,
|
||||
/* dependsOnNormals */ NULL,
|
||||
/* dependsOnTime */ nullptr,
|
||||
/* dependsOnNormals */ nullptr,
|
||||
/* foreachIDLink */ foreachIDLink,
|
||||
/* foreachTexLink */ NULL,
|
||||
/* freeRuntimeData */ NULL,
|
||||
/* foreachTexLink */ nullptr,
|
||||
/* freeRuntimeData */ nullptr,
|
||||
/* panelRegister */ panelRegister,
|
||||
/* blendWrite */ NULL,
|
||||
/* blendRead */ NULL,
|
||||
/* blendWrite */ nullptr,
|
||||
/* blendRead */ nullptr,
|
||||
};
|
|
@ -17,7 +17,7 @@
|
|||
#include "DNA_mesh_types.h"
|
||||
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_boolean_convert.h"
|
||||
#include "BKE_mesh_boolean_convert.hh"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
|
Loading…
Reference in New Issue