From 7881a797a063bb8ed0aa8c6b7329812c027b6fbf Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 11 Oct 2022 15:47:40 +1100 Subject: 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]: 690ecaae208d5f72217c165621d0d036e4029e86 [1]: 6e77afe6ec7b6a73f218f1fef264758abcbc778a --- source/blender/editors/mesh/editmesh_knife.c | 52 ++++++++++++++++++---- .../blender/editors/mesh/editmesh_knife_project.c | 9 ++-- source/blender/editors/mesh/mesh_intern.h | 3 ++ 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 5680865ae67..a3398da0998 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -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); } diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index c32b1fa99c0..a99cb38601e 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -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); diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 303234df48c..826b34bbaa1 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -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); -- cgit v1.2.3