Fix T81651, exact boolean modifier incorrect if operand hidden.
The code was trying to ignore hidden geometry when doing boolean, which is correct when used as a tool, but not when a modifier. Added a "keep_hidden" argument to bmesh_boolean to distinguish the two cases. Also fixed a bug when the tool is used with hidden geometry that is attached to unhidden geometry that is deleted by the operation.
This commit is contained in:
parent
7be47dadea
commit
39012146e1
Notes:
blender-bot
2023-02-14 10:21:15 +01:00
Referenced by issue #82783, Number keys cannot be mapped in the 3D viewport Referenced by issue #82555, Segfault when applying modifier to object copy from python Referenced by issue #82521, Bump OpenImageIO minimum version 1.8 > 2.2.1 for install_deps.sh Referenced by issue #82047, Boolean issues when duplicating and rotating mesh or object Referenced by issue #81651, New boolean - wrong result if part of mesh is hidden in Edit mode
|
@ -428,7 +428,9 @@ class Cell {
|
|||
BoolOpType bool_optype)
|
||||
{
|
||||
std::copy(from_cell.winding().begin(), from_cell.winding().end(), winding_.begin());
|
||||
winding_[shape] += delta;
|
||||
if (shape >= 0) {
|
||||
winding_[shape] += delta;
|
||||
}
|
||||
winding_assigned_ = true;
|
||||
in_output_volume_ = apply_bool_op(bool_optype, winding_);
|
||||
}
|
||||
|
|
|
@ -116,6 +116,18 @@ static bool bmvert_attached_to_wire(const BMVert *bmv)
|
|||
return BM_vert_is_wire(bmv);
|
||||
}
|
||||
|
||||
static bool bmvert_attached_to_hidden_face(BMVert *bmv)
|
||||
{
|
||||
BMIter iter;
|
||||
for (BMFace *bmf = static_cast<BMFace *>(BM_iter_new(&iter, NULL, BM_FACES_OF_VERT, bmv)); bmf;
|
||||
bmf = static_cast<BMFace *>(BM_iter_step(&iter))) {
|
||||
if (BM_elem_flag_test(bmf, BM_ELEM_HIDDEN)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool face_has_verts_in_order(BMesh *bm, BMFace *bmf, const BMVert *v1, const BMVert *v2)
|
||||
{
|
||||
BMIter liter;
|
||||
|
@ -139,17 +151,19 @@ constexpr uint KEEP_FLAG = (1 << 6);
|
|||
* Also, the #BM_ELEM_TAG header flag is set for those #BMEdge's that come from intersections
|
||||
* resulting from the intersection needed by the Boolean operation.
|
||||
*/
|
||||
static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out)
|
||||
static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out, bool keep_hidden)
|
||||
{
|
||||
bool any_change = false;
|
||||
|
||||
m_out.populate_vert();
|
||||
|
||||
/* Initially mark all existing verts as "don't keep", except hidden verts
|
||||
* and verts attached to wire edges. */
|
||||
* (if keep_hidden is true), and verts attached to wire edges. */
|
||||
for (int v = 0; v < bm->totvert; ++v) {
|
||||
BMVert *bmv = BM_vert_at_index(bm, v);
|
||||
if (BM_elem_flag_test(bmv, BM_ELEM_HIDDEN) || bmvert_attached_to_wire(bmv)) {
|
||||
if ((keep_hidden &&
|
||||
(BM_elem_flag_test(bmv, BM_ELEM_HIDDEN) || bmvert_attached_to_hidden_face(bmv))) ||
|
||||
bmvert_attached_to_wire(bmv)) {
|
||||
BM_elem_flag_enable(bmv, KEEP_FLAG);
|
||||
}
|
||||
else {
|
||||
|
@ -190,14 +204,14 @@ static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out)
|
|||
}
|
||||
}
|
||||
|
||||
/* Initially mark all existing faces as "don't keep", except hidden faces.
|
||||
/* Initially mark all existing faces as "don't keep", except hidden faces (if keep_hidden).
|
||||
* Also, save current #BMFace pointers as creating faces will disturb the table. */
|
||||
Array<BMFace *> old_bmfs(bm->totface);
|
||||
BM_mesh_elem_index_ensure(bm, BM_FACE);
|
||||
for (int f = 0; f < bm->totface; ++f) {
|
||||
BMFace *bmf = BM_face_at_index(bm, f);
|
||||
old_bmfs[f] = bmf;
|
||||
if (BM_elem_flag_test(bmf, BM_ELEM_HIDDEN)) {
|
||||
if (keep_hidden && BM_elem_flag_test(bmf, BM_ELEM_HIDDEN)) {
|
||||
BM_elem_flag_enable(bmf, KEEP_FLAG);
|
||||
}
|
||||
else {
|
||||
|
@ -334,6 +348,7 @@ static bool bmesh_boolean(BMesh *bm,
|
|||
int nshapes,
|
||||
const bool use_self,
|
||||
const bool use_separate_all,
|
||||
const bool keep_hidden,
|
||||
const BoolOpType boolean_mode)
|
||||
{
|
||||
IMeshArena arena;
|
||||
|
@ -363,7 +378,7 @@ static bool bmesh_boolean(BMesh *bm,
|
|||
}
|
||||
IMesh m_out = boolean_mesh(
|
||||
m_in, boolean_mode, nshapes, shape_fn, use_self, &m_triangulated, &arena);
|
||||
bool any_change = apply_mesh_output_to_bmesh(bm, m_out);
|
||||
bool any_change = apply_mesh_output_to_bmesh(bm, m_out, keep_hidden);
|
||||
if (use_separate_all) {
|
||||
/* We are supposed to separate all faces that are incident on intersection edges. */
|
||||
BM_mesh_edgesplit(bm, false, true, false);
|
||||
|
@ -400,6 +415,7 @@ bool BM_mesh_boolean(BMesh *bm,
|
|||
void *user_data,
|
||||
const int nshapes,
|
||||
const bool use_self,
|
||||
const bool keep_hidden,
|
||||
const int boolean_mode)
|
||||
{
|
||||
return blender::meshintersect::bmesh_boolean(
|
||||
|
@ -411,6 +427,7 @@ bool BM_mesh_boolean(BMesh *bm,
|
|||
nshapes,
|
||||
use_self,
|
||||
false,
|
||||
keep_hidden,
|
||||
static_cast<blender::meshintersect::BoolOpType>(boolean_mode));
|
||||
}
|
||||
|
||||
|
@ -429,7 +446,8 @@ bool BM_mesh_boolean_knife(BMesh *bm,
|
|||
void *user_data,
|
||||
const int nshapes,
|
||||
const bool use_self,
|
||||
const bool use_separate_all)
|
||||
const bool use_separate_all,
|
||||
const bool keep_hidden)
|
||||
{
|
||||
return blender::meshintersect::bmesh_boolean(bm,
|
||||
looptris,
|
||||
|
@ -439,6 +457,7 @@ bool BM_mesh_boolean_knife(BMesh *bm,
|
|||
nshapes,
|
||||
use_self,
|
||||
use_separate_all,
|
||||
keep_hidden,
|
||||
blender::meshintersect::BoolOpType::None);
|
||||
}
|
||||
#else
|
||||
|
@ -449,6 +468,7 @@ bool BM_mesh_boolean(BMesh *UNUSED(bm),
|
|||
void *UNUSED(user_data),
|
||||
const int UNUSED(nshapes),
|
||||
const bool UNUSED(use_self),
|
||||
const bool UNUSED(keep_hidden),
|
||||
const int UNUSED(boolean_mode))
|
||||
{
|
||||
UNUSED_VARS(looptris, test_fn);
|
||||
|
@ -470,7 +490,8 @@ bool BM_mesh_boolean_knife(BMesh *UNUSED(bm),
|
|||
void *UNUSED(user_data),
|
||||
const int UNUSED(nshapes),
|
||||
const bool UNUSED(use_self),
|
||||
const bool UNUSED(use_separate_all))
|
||||
const bool UNUSED(use_separate_all),
|
||||
const bool UNUSED(keep_boolean))
|
||||
{
|
||||
UNUSED_VARS(looptris, test_fn);
|
||||
return false;
|
||||
|
|
|
@ -31,6 +31,7 @@ bool BM_mesh_boolean(BMesh *bm,
|
|||
void *user_data,
|
||||
const int nshapes,
|
||||
const bool use_self,
|
||||
const bool keep_hidden,
|
||||
const int boolean_mode);
|
||||
|
||||
bool BM_mesh_boolean_knife(BMesh *bm,
|
||||
|
@ -40,7 +41,8 @@ bool BM_mesh_boolean_knife(BMesh *bm,
|
|||
void *user_data,
|
||||
const int nshapes,
|
||||
const bool use_self,
|
||||
const bool use_separate_all);
|
||||
const bool use_separate_all,
|
||||
const bool keep_hidden);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -204,8 +204,15 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op)
|
|||
|
||||
if (exact) {
|
||||
int nshapes = use_self ? 1 : 2;
|
||||
has_isect = BM_mesh_boolean_knife(
|
||||
em->bm, em->looptris, em->tottri, test_fn, NULL, nshapes, use_self, use_separate_all);
|
||||
has_isect = BM_mesh_boolean_knife(em->bm,
|
||||
em->looptris,
|
||||
em->tottri,
|
||||
test_fn,
|
||||
NULL,
|
||||
nshapes,
|
||||
use_self,
|
||||
use_separate_all,
|
||||
true);
|
||||
}
|
||||
else {
|
||||
has_isect = BM_mesh_intersect(em->bm,
|
||||
|
@ -369,7 +376,7 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op)
|
|||
|
||||
if (use_exact) {
|
||||
has_isect = BM_mesh_boolean(
|
||||
em->bm, em->looptris, em->tottri, test_fn, NULL, 2, use_self, boolean_operation);
|
||||
em->bm, em->looptris, em->tottri, test_fn, NULL, 2, use_self, true, boolean_operation);
|
||||
}
|
||||
else {
|
||||
has_isect = BM_mesh_intersect(em->bm,
|
||||
|
|
|
@ -1293,7 +1293,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
|
|||
BLI_assert(false);
|
||||
break;
|
||||
}
|
||||
BM_mesh_boolean(bm, looptris, tottri, bm_face_isect_pair, NULL, 2, true, boolean_mode);
|
||||
BM_mesh_boolean(bm, looptris, tottri, bm_face_isect_pair, NULL, 2, true, true, boolean_mode);
|
||||
}
|
||||
|
||||
MEM_freeN(looptris);
|
||||
|
|
|
@ -380,7 +380,8 @@ static void BMD_mesh_intersection(BMesh *bm,
|
|||
#endif
|
||||
|
||||
if (use_exact) {
|
||||
BM_mesh_boolean(bm, looptris, tottri, bm_face_isect_pair, NULL, 2, use_self, bmd->operation);
|
||||
BM_mesh_boolean(
|
||||
bm, looptris, tottri, bm_face_isect_pair, NULL, 2, use_self, false, bmd->operation);
|
||||
}
|
||||
else {
|
||||
BM_mesh_intersect(bm,
|
||||
|
@ -543,7 +544,7 @@ static Mesh *collection_boolean_exact(BooleanModifierData *bmd,
|
|||
|
||||
BM_mesh_elem_index_ensure(bm, BM_FACE);
|
||||
BM_mesh_boolean(
|
||||
bm, looptris, tottri, bm_face_isect_nary, shape, num_shapes, true, bmd->operation);
|
||||
bm, looptris, tottri, bm_face_isect_nary, shape, num_shapes, true, false, bmd->operation);
|
||||
|
||||
result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
|
||||
BM_mesh_free(bm);
|
||||
|
|
Loading…
Reference in New Issue