Fix T101721: Knife project crashes

The crash was caused by [0] however knife-project functionality has been
incorrect since [1] which would loop over each edit-mode object and run
the knife project function which operated on all edit-mode objects too.

- Resolve the crash by postponing face-tessellation recalculation
  until the knife tool has cut all objects

- Objects occluding each other is now supported
  (an old TODO and something that was never supported).

[0]: 690ecaae20
[1]: 6e77afe6ec
This commit is contained in:
Campbell Barton 2022-10-11 15:47:40 +11:00 committed by Philipp Oeser
parent 07ba515b21
commit 7881a797a0
Notes: blender-bot 2023-02-14 10:29:32 +01:00
Referenced by issue #100749, Blender LTS: Maintenance Task 3.3
Referenced by issue #101721, Regression: Knife Project crashes Blender 3.4 and 3.3
3 changed files with 50 additions and 14 deletions

View File

@ -218,6 +218,7 @@ typedef struct KnifeTool_OpData {
/* Used for swapping current object when in multi-object edit mode. */
Object **objects;
uint objects_len;
bool objects_free;
/** Array `objects_len` length of additional per-object data. */
KnifeObjectInfo *objects_info;
@ -4081,6 +4082,8 @@ static void knife_init_colors(KnifeColors *colors)
/* called when modal loop selection gets set up... */
static void knifetool_init(ViewContext *vc,
KnifeTool_OpData *kcd,
Object **objects,
const int objects_len,
const bool only_select,
const bool cut_through,
const bool xray,
@ -4101,8 +4104,16 @@ static void knifetool_init(ViewContext *vc,
kcd->scene = scene;
kcd->region = vc->region;
kcd->objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
vc->view_layer, vc->v3d, &kcd->objects_len);
if (objects) {
kcd->objects = objects;
kcd->objects_len = objects_len;
kcd->objects_free = false;
}
else {
kcd->objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
vc->view_layer, vc->v3d, &kcd->objects_len);
kcd->objects_free = true;
}
Object *ob;
BMEditMesh *em;
@ -4225,7 +4236,9 @@ static void knifetool_exit_ex(KnifeTool_OpData *kcd)
}
/* Free object bases. */
MEM_freeN(kcd->objects);
if (kcd->objects_free) {
MEM_freeN(kcd->objects);
}
/* Destroy kcd itself. */
MEM_freeN(kcd);
@ -4318,9 +4331,15 @@ static void knifetool_finish_single_post(KnifeTool_OpData *UNUSED(kcd), Object *
/* Called on tool confirmation. */
static void knifetool_finish_ex(KnifeTool_OpData *kcd)
{
/* Separate pre/post passes are needed because `em->looptris` recalculation from the 'post' pass
* causes causes triangle indices in #KnifeTool_OpData.bvh to get out of sync.
* So perform all the cuts before doing any mesh recalculation, see: T101721. */
for (uint b = 0; b < kcd->objects_len; b++) {
Object *ob = kcd->objects[b];
knifetool_finish_single_pre(kcd, ob);
}
for (uint b = 0; b < kcd->objects_len; b++) {
Object *ob = kcd->objects[b];
knifetool_finish_single_post(kcd, ob);
}
}
@ -4789,6 +4808,8 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
knifetool_init(&vc,
kcd,
NULL,
0,
only_select,
cut_through,
xray,
@ -4941,7 +4962,12 @@ static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2])
return false;
}
void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through)
void EDBM_mesh_knife(ViewContext *vc,
Object **objects,
const int objects_len,
LinkNode *polys,
bool use_tag,
bool cut_through)
{
KnifeTool_OpData *kcd;
@ -4958,6 +4984,8 @@ void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_th
knifetool_init(vc,
kcd,
objects,
objects_len,
only_select,
cut_through,
xray,
@ -4999,18 +5027,21 @@ void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_th
/* Finish. */
{
Object *ob;
BMEditMesh *em;
/* See #knifetool_finish_ex for why multiple passes are needed. */
for (uint b = 0; b < kcd->objects_len; b++) {
ob = kcd->objects[b];
em = BKE_editmesh_from_object(ob);
Object *ob = kcd->objects[b];
BMEditMesh *em = BKE_editmesh_from_object(ob);
if (use_tag) {
BM_mesh_elem_hflag_enable_all(em->bm, BM_EDGE, BM_ELEM_TAG, false);
}
knifetool_finish_single_pre(kcd, ob);
}
for (uint b = 0; b < kcd->objects_len; b++) {
Object *ob = kcd->objects[b];
BMEditMesh *em = BKE_editmesh_from_object(ob);
/* Tag faces inside! */
if (use_tag) {
@ -5103,9 +5134,12 @@ void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_th
#undef F_ISECT_SET_UNKNOWN
#undef F_ISECT_SET_OUTSIDE
}
}
for (uint b = 0; b < kcd->objects_len; b++) {
/* Defer freeing data until the BVH tree is finished with, see: #point_is_visible and
* the doc-string for #knifetool_finish_single_post. */
Object *ob = kcd->objects[b];
knifetool_finish_single_post(kcd, ob);
}

View File

@ -132,22 +132,21 @@ static int knifeproject_exec(bContext *C, wmOperator *op)
ViewContext vc;
em_setup_viewcontext(C, &vc);
/* TODO: Ideally meshes would occlude each other, currently they don't
* since each knife-project runs as a separate operation. */
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
vc.view_layer, vc.v3d, &objects_len);
EDBM_mesh_knife(&vc, objects, objects_len, polys, true, cut_through);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ED_view3d_viewcontext_init_object(&vc, obedit);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
EDBM_mesh_knife(&vc, polys, true, cut_through);
/* select only tagged faces */
BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false);
EDBM_selectmode_disable_multi(C, SCE_SELECT_VERTEX, SCE_SELECT_EDGE);
EDBM_selectmode_disable(scene, em, SCE_SELECT_VERTEX, SCE_SELECT_EDGE);
BM_mesh_elem_hflag_enable_test(em->bm, BM_FACE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG);

View File

@ -18,6 +18,7 @@ struct BMElem;
struct BMOperator;
struct EnumPropertyItem;
struct LinkNode;
struct Object;
struct bContext;
struct wmKeyConfig;
struct wmKeyMap;
@ -175,6 +176,8 @@ void MESH_OT_knife_project(struct wmOperatorType *ot);
* \param use_tag: When set, tag all faces inside the polylines.
*/
void EDBM_mesh_knife(struct ViewContext *vc,
struct Object **objects,
int objects_len,
struct LinkNode *polys,
bool use_tag,
bool cut_through);