This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/editors/mesh/editmesh_polybuild.c
Monique Dewanchand 68589a31eb ViewLayer: Lazy sync of scene data.
When a change happens which invalidates view layers the syncing will be postponed until the first usage.
This will improve importing or adding many objects in a single operation/script.

`BKE_view_layer_need_resync_tag` is used to tag the view layer to be out of sync. Before accessing
`BKE_view_layer_active_base_get`, `BKE_view_layer_active_object_get`, `BKE_view_layer_active_collection`
or `BKE_view_layer_object_bases` the caller should call `BKE_view_layer_synced_ensure`.

Having two functions ensures that partial syncing could be added as smaller patches in the future. Tagging a
view layer out of sync could be replaced with a partial sync. Eventually the number of full resyncs could be
reduced. After all tagging has been replaced with partial syncs the ensure_sync could be phased out.

This patch has been added to discuss the details and consequences of the current approach. For clarity
the call to BKE_view_layer_ensure_sync is placed close to the getters.
In the future this could be placed in more strategical places to reduce the number of calls or improve
performance. Finding those strategical places isn't that clear. When multiple operations are grouped
in a single script you might want to always check for resync.

Some areas found that can be improved. This list isn't complete.
These areas aren't addressed by this patch as these changes would be hard to detect to the reviewer.
The idea is to add changes to these areas as a separate patch. It might be that the initial commit would reduce
performance compared to master, but will be fixed by the additional patches.

**Object duplication**
During object duplication the syncing is temporarily disabled. With this patch this isn't useful as when disabled
the view_layer is accessed to locate bases. This can be improved by first locating the source bases, then duplicate
and sync and locate the new bases. Will be solved in a separate patch for clarity reasons ({D15886}).

**Object add**
`BKE_object_add` not only adds a new object, but also selects and activates the new base. This requires the
view_layer to be resynced. Some callers reverse the selection and activation (See `get_new_constraint_target`).
We should make the selection and activation optional. This would make it possible to add multiple objects
without having to resync per object.

**Postpone Activate Base**
Setting the basact is done in many locations. They follow a rule as after an action find the base and set
the basact. Finding the base could require a resync. The idea is to store in the view_layer the object which
base will be set in the basact during the next sync, reducing the times resyncing needs to happen.

Reviewed By: mont29

Maniphest Tasks: T73411

Differential Revision: https://developer.blender.org/D15885
2022-09-14 21:34:38 +02:00

626 lines
19 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edmesh
*
* Tools to implement polygon building tool,
* an experimental tool for quickly constructing/manipulating faces.
*/
#include "MEM_guardedalloc.h"
#include "DNA_object_types.h"
#include "BLI_math.h"
#include "BKE_context.h"
#include "BKE_editmesh.h"
#include "BKE_layer.h"
#include "BKE_mesh.h"
#include "BKE_report.h"
#include "WM_types.h"
#include "ED_mesh.h"
#include "ED_object.h"
#include "ED_scene.h"
#include "ED_screen.h"
#include "ED_transform.h"
#include "ED_view3d.h"
#include "bmesh.h"
#include "mesh_intern.h" /* own include */
#include "RNA_access.h"
#include "RNA_define.h"
#include "WM_api.h"
#include "DEG_depsgraph.h"
/* -------------------------------------------------------------------- */
/** \name Local Utilities
* \{ */
static void edbm_selectmode_ensure(Scene *scene, BMEditMesh *em, short selectmode)
{
if ((scene->toolsettings->selectmode & selectmode) == 0) {
scene->toolsettings->selectmode |= selectmode;
em->selectmode = scene->toolsettings->selectmode;
EDBM_selectmode_set(em);
}
}
/* Could make public, for now just keep here. */
static void edbm_flag_disable_all_multi(const Scene *scene,
ViewLayer *view_layer,
View3D *v3d,
const char hflag)
{
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
scene, view_layer, v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob_iter = objects[ob_index];
BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
BMesh *bm_iter = em_iter->bm;
if (bm_iter->totvertsel) {
EDBM_flag_disable_all(em_iter, hflag);
DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT);
}
}
MEM_freeN(objects);
}
/* When accessed as a tool, get the active edge from the preselection gizmo. */
static bool edbm_preselect_or_active(bContext *C, const View3D *v3d, Base **r_base, BMElem **r_ele)
{
ARegion *region = CTX_wm_region(C);
const bool show_gizmo = !((v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)));
wmGizmoMap *gzmap = show_gizmo ? region->gizmo_map : NULL;
wmGizmoGroup *gzgroup = gzmap ? WM_gizmomap_group_find(gzmap, "VIEW3D_GGT_mesh_preselect_elem") :
NULL;
if (gzgroup != NULL) {
wmGizmo *gz = gzgroup->gizmos.first;
ED_view3d_gizmo_mesh_preselect_get_active(C, gz, r_base, r_ele);
}
else {
const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_active_base_get(view_layer);
Object *obedit = base->object;
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMesh *bm = em->bm;
*r_base = base;
*r_ele = BM_mesh_active_elem_get(bm);
}
return (*r_ele != NULL);
}
static bool edbm_preselect_or_active_init_viewcontext(bContext *C,
ViewContext *vc,
Base **r_base,
BMElem **r_ele)
{
em_setup_viewcontext(C, vc);
bool ok = edbm_preselect_or_active(C, vc->v3d, r_base, r_ele);
if (ok) {
ED_view3d_viewcontext_init_object(vc, (*r_base)->object);
}
return ok;
}
static int edbm_polybuild_transform_at_cursor_invoke(bContext *C,
wmOperator *UNUSED(op),
const wmEvent *UNUSED(event))
{
ViewContext vc;
Base *basact = NULL;
BMElem *ele_act = NULL;
edbm_preselect_or_active_init_viewcontext(C, &vc, &basact, &ele_act);
BMEditMesh *em = vc.em;
BMesh *bm = em->bm;
invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
if (!ele_act) {
return OPERATOR_CANCELLED;
}
edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX);
edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
if (ele_act->head.htype == BM_VERT) {
BM_vert_select_set(bm, (BMVert *)ele_act, true);
}
if (ele_act->head.htype == BM_EDGE) {
BM_edge_select_set(bm, (BMEdge *)ele_act, true);
}
if (ele_act->head.htype == BM_FACE) {
BM_face_select_set(bm, (BMFace *)ele_act, true);
}
EDBM_update(vc.obedit->data,
&(const struct EDBMUpdate_Params){
.calc_looptri = true,
.calc_normals = true,
.is_destructive = true,
});
if (basact != NULL) {
BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
}
BM_select_history_store(bm, ele_act);
WM_event_add_mousemove(vc.win);
return OPERATOR_FINISHED;
}
void MESH_OT_polybuild_transform_at_cursor(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Poly Build Transform at Cursor";
ot->idname = "MESH_OT_polybuild_transform_at_cursor";
/* api callbacks */
ot->invoke = edbm_polybuild_transform_at_cursor_invoke;
ot->poll = EDBM_view3d_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* to give to transform */
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY);
}
static int edbm_polybuild_delete_at_cursor_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
bool changed = false;
ViewContext vc;
Base *basact = NULL;
BMElem *ele_act = NULL;
edbm_preselect_or_active_init_viewcontext(C, &vc, &basact, &ele_act);
BMEditMesh *em = vc.em;
BMesh *bm = em->bm;
invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
if (!ele_act) {
return OPERATOR_CANCELLED;
}
edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX);
if (ele_act->head.htype == BM_FACE) {
BMFace *f_act = (BMFace *)ele_act;
EDBM_flag_disable_all(em, BM_ELEM_TAG);
BM_elem_flag_enable(f_act, BM_ELEM_TAG);
if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_TAG, DEL_FACES)) {
return OPERATOR_CANCELLED;
}
changed = true;
}
if (ele_act->head.htype == BM_VERT) {
BMVert *v_act = (BMVert *)ele_act;
if (BM_vert_is_edge_pair(v_act) && !BM_vert_is_wire(v_act)) {
BM_edge_collapse(bm, v_act->e, v_act, true, true);
changed = true;
}
else {
EDBM_flag_disable_all(em, BM_ELEM_TAG);
BM_elem_flag_enable(v_act, BM_ELEM_TAG);
if (!EDBM_op_callf(em,
op,
"dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b",
BM_ELEM_TAG,
false,
false)) {
return OPERATOR_CANCELLED;
}
changed = true;
}
}
if (changed) {
EDBM_update(vc.obedit->data,
&(const struct EDBMUpdate_Params){
.calc_looptri = true,
.calc_normals = true,
.is_destructive = true,
});
if (basact != NULL) {
BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
}
WM_event_add_mousemove(vc.win);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MESH_OT_polybuild_delete_at_cursor(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Poly Build Delete at Cursor";
ot->idname = "MESH_OT_polybuild_delete_at_cursor";
/* api callbacks */
ot->invoke = edbm_polybuild_delete_at_cursor_invoke;
ot->poll = EDBM_view3d_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* to give to transform */
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Face at Cursor
* \{ */
static int edbm_polybuild_face_at_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
float center[3];
bool changed = false;
ViewContext vc;
Base *basact = NULL;
BMElem *ele_act = NULL;
edbm_preselect_or_active_init_viewcontext(C, &vc, &basact, &ele_act);
BMEditMesh *em = vc.em;
BMesh *bm = em->bm;
invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX);
if (ele_act == NULL || ele_act->head.htype == BM_FACE) {
/* Just add vert */
copy_v3_v3(center, vc.scene->cursor.location);
mul_v3_m4v3(center, vc.obedit->obmat, center);
ED_view3d_win_to_3d_int(vc.v3d, vc.region, center, event->mval, center);
mul_m4_v3(vc.obedit->imat, center);
BMVert *v_new = BM_vert_create(bm, center, NULL, BM_CREATE_NOP);
edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
BM_vert_select_set(bm, v_new, true);
BM_select_history_store(bm, v_new);
changed = true;
}
else if (ele_act->head.htype == BM_EDGE) {
BMEdge *e_act = (BMEdge *)ele_act;
BMFace *f_reference = e_act->l ? e_act->l->f : NULL;
mid_v3_v3v3(center, e_act->v1->co, e_act->v2->co);
mul_m4_v3(vc.obedit->obmat, center);
ED_view3d_win_to_3d_int(vc.v3d, vc.region, center, event->mval, center);
mul_m4_v3(vc.obedit->imat, center);
if (f_reference->len == 3 && RNA_boolean_get(op->ptr, "create_quads")) {
const float fac = line_point_factor_v3(center, e_act->v1->co, e_act->v2->co);
BMVert *v_new = BM_edge_split(bm, e_act, e_act->v1, NULL, CLAMPIS(fac, 0.0f, 1.0f));
copy_v3_v3(v_new->co, center);
edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
BM_vert_select_set(bm, v_new, true);
BM_select_history_store(bm, v_new);
}
else {
BMVert *v_tri[3];
v_tri[0] = e_act->v1;
v_tri[1] = e_act->v2;
v_tri[2] = BM_vert_create(bm, center, NULL, BM_CREATE_NOP);
if (e_act->l && e_act->l->v == v_tri[0]) {
SWAP(BMVert *, v_tri[0], v_tri[1]);
}
BM_face_create_verts(bm, v_tri, 3, f_reference, BM_CREATE_NOP, true);
edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
BM_vert_select_set(bm, v_tri[2], true);
BM_select_history_store(bm, v_tri[2]);
}
changed = true;
}
else if (ele_act->head.htype == BM_VERT) {
BMVert *v_act = (BMVert *)ele_act;
BMEdge *e_pair[2] = {NULL};
if (v_act->e != NULL) {
for (uint allow_wire = 0; allow_wire < 2 && (e_pair[1] == NULL); allow_wire++) {
int i = 0;
BMEdge *e_iter = v_act->e;
do {
if ((BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN) == false) &&
(allow_wire ? BM_edge_is_wire(e_iter) : BM_edge_is_boundary(e_iter))) {
if (i == 2) {
e_pair[0] = e_pair[1] = NULL;
break;
}
e_pair[i++] = e_iter;
}
} while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_act)) != v_act->e);
}
}
if (e_pair[1] != NULL) {
/* Quad from edge pair. */
if (BM_edge_calc_length_squared(e_pair[0]) < BM_edge_calc_length_squared(e_pair[1])) {
SWAP(BMEdge *, e_pair[0], e_pair[1]);
}
BMFace *f_reference = e_pair[0]->l ? e_pair[0]->l->f : NULL;
mul_v3_m4v3(center, vc.obedit->obmat, v_act->co);
ED_view3d_win_to_3d_int(vc.v3d, vc.region, center, event->mval, center);
mul_m4_v3(vc.obedit->imat, center);
BMVert *v_quad[4];
v_quad[0] = v_act;
v_quad[1] = BM_edge_other_vert(e_pair[0], v_act);
v_quad[2] = BM_vert_create(bm, center, NULL, BM_CREATE_NOP);
v_quad[3] = BM_edge_other_vert(e_pair[1], v_act);
if (e_pair[0]->l && e_pair[0]->l->v == v_quad[0]) {
SWAP(BMVert *, v_quad[1], v_quad[3]);
}
// BMFace *f_new =
BM_face_create_verts(bm, v_quad, 4, f_reference, BM_CREATE_NOP, true);
edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
BM_vert_select_set(bm, v_quad[2], true);
BM_select_history_store(bm, v_quad[2]);
changed = true;
}
else {
/* Just add edge */
mul_m4_v3(vc.obedit->obmat, center);
ED_view3d_win_to_3d_int(vc.v3d, vc.region, v_act->co, event->mval, center);
mul_m4_v3(vc.obedit->imat, center);
BMVert *v_new = BM_vert_create(bm, center, NULL, BM_CREATE_NOP);
BM_edge_create(bm, v_act, v_new, NULL, BM_CREATE_NOP);
BM_vert_select_set(bm, v_new, true);
BM_select_history_store(bm, v_new);
changed = true;
}
}
if (changed) {
EDBM_update(vc.obedit->data,
&(const struct EDBMUpdate_Params){
.calc_looptri = true,
.calc_normals = true,
.is_destructive = true,
});
if (basact != NULL) {
BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
}
WM_event_add_mousemove(vc.win);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MESH_OT_polybuild_face_at_cursor(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Poly Build Face at Cursor";
ot->idname = "MESH_OT_polybuild_face_at_cursor";
/* api callbacks */
ot->invoke = edbm_polybuild_face_at_cursor_invoke;
ot->poll = EDBM_view3d_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna,
"create_quads",
true,
"Create Quads",
"Automatically split edges in triangles to maintain quad topology");
/* to give to transform */
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Split at Cursor
* \{ */
static int edbm_polybuild_split_at_cursor_invoke(bContext *C,
wmOperator *UNUSED(op),
const wmEvent *event)
{
float center[3];
bool changed = false;
ViewContext vc;
Base *basact = NULL;
BMElem *ele_act = NULL;
edbm_preselect_or_active_init_viewcontext(C, &vc, &basact, &ele_act);
BMEditMesh *em = vc.em;
BMesh *bm = em->bm;
invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX);
if (ele_act == NULL || ele_act->head.hflag == BM_FACE) {
return OPERATOR_PASS_THROUGH;
}
if (ele_act->head.htype == BM_EDGE) {
BMEdge *e_act = (BMEdge *)ele_act;
mid_v3_v3v3(center, e_act->v1->co, e_act->v2->co);
mul_m4_v3(vc.obedit->obmat, center);
ED_view3d_win_to_3d_int(vc.v3d, vc.region, center, event->mval, center);
mul_m4_v3(vc.obedit->imat, center);
const float fac = line_point_factor_v3(center, e_act->v1->co, e_act->v2->co);
BMVert *v_new = BM_edge_split(bm, e_act, e_act->v1, NULL, CLAMPIS(fac, 0.0f, 1.0f));
copy_v3_v3(v_new->co, center);
edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
BM_vert_select_set(bm, v_new, true);
BM_select_history_store(bm, v_new);
changed = true;
}
else if (ele_act->head.htype == BM_VERT) {
/* Just do nothing, allow dragging. */
return OPERATOR_FINISHED;
}
if (changed) {
EDBM_update(vc.obedit->data,
&(const struct EDBMUpdate_Params){
.calc_looptri = true,
.calc_normals = true,
.is_destructive = true,
});
WM_event_add_mousemove(vc.win);
BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MESH_OT_polybuild_split_at_cursor(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Poly Build Split at Cursor";
ot->idname = "MESH_OT_polybuild_split_at_cursor";
/* api callbacks */
ot->invoke = edbm_polybuild_split_at_cursor_invoke;
ot->poll = EDBM_view3d_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* to give to transform */
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Dissolve at Cursor
* \{ */
static int edbm_polybuild_dissolve_at_cursor_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
bool changed = false;
ViewContext vc;
Base *basact = NULL;
BMElem *ele_act = NULL;
edbm_preselect_or_active_init_viewcontext(C, &vc, &basact, &ele_act);
BMEditMesh *em = vc.em;
BMesh *bm = em->bm;
if (ele_act == NULL) {
/* pass */
}
else if (ele_act->head.htype == BM_EDGE) {
BMEdge *e_act = (BMEdge *)ele_act;
BMLoop *l_a, *l_b;
if (BM_edge_loop_pair(e_act, &l_a, &l_b)) {
BMFace *f_new = BM_faces_join_pair(bm, l_a, l_b, true);
if (f_new) {
changed = true;
}
}
}
else if (ele_act->head.htype == BM_VERT) {
BMVert *v_act = (BMVert *)ele_act;
if (BM_vert_is_edge_pair(v_act)) {
BM_edge_collapse(bm, v_act->e, v_act, true, true);
}
else {
/* too involved to do inline */
/* Avoid using selection so failure won't leave modified state. */
EDBM_flag_disable_all(em, BM_ELEM_TAG);
BM_elem_flag_enable(v_act, BM_ELEM_TAG);
if (!EDBM_op_callf(em,
op,
"dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b",
BM_ELEM_TAG,
false,
false)) {
return OPERATOR_CANCELLED;
}
}
changed = true;
}
if (changed) {
edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
EDBM_update(vc.obedit->data,
&(const struct EDBMUpdate_Params){
.calc_looptri = true,
.calc_normals = true,
.is_destructive = true,
});
BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
WM_event_add_mousemove(vc.win);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MESH_OT_polybuild_dissolve_at_cursor(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Poly Build Dissolve at Cursor";
ot->idname = "MESH_OT_polybuild_dissolve_at_cursor";
/* api callbacks */
ot->invoke = edbm_polybuild_dissolve_at_cursor_invoke;
ot->poll = EDBM_view3d_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */