diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d.py | 1 | ||||
-rw-r--r-- | source/blender/bmesh/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_opdefines.c | 22 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_operators_private.h | 1 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_connect_nonplanar.c | 228 | ||||
-rw-r--r-- | source/blender/editors/mesh/editmesh_tools.c | 45 | ||||
-rw-r--r-- | source/blender/editors/mesh/mesh_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/mesh/mesh_ops.c | 1 |
8 files changed, 300 insertions, 0 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 18008553368..689b1a9c9b7 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2092,6 +2092,7 @@ class VIEW3D_MT_edit_mesh_clean(Menu): layout = self.layout layout.operator("mesh.fill_holes") + layout.operator("mesh.vert_connect_nonplanar") class VIEW3D_MT_edit_mesh_delete(Menu): diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index c49c290e81e..228ebcb96c4 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -43,6 +43,7 @@ set(SRC operators/bmo_bevel.c operators/bmo_bridge.c operators/bmo_connect.c + operators/bmo_connect_nonplanar.c operators/bmo_connect_pair.c operators/bmo_create.c operators/bmo_dissolve.c diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index b56ed15b901..4f8a851c780 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -868,6 +868,27 @@ static BMOpDefine bmo_connect_verts_def = { }; /* + * Connect Verts Across non Planer Faces. + * + * Split faces by connecting edges along non planer **faces**. + */ +static BMOpDefine bmo_connect_verts_nonplanar_def = { + "connect_verts_nonplanar", + /* slots_in */ + {{"angle_limit", BMO_OP_SLOT_FLT}, /* total rotation angle (radians) */ + {"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, + {{'\0'}}, + }, + /* slots_out */ + {{"edges.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_EDGE}}, + {"faces.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, + {{'\0'}}, + }, + bmo_connect_verts_nonplanar_exec, + BMO_OPTYPE_FLAG_UNTAN_MULTIRES | BMO_OPTYPE_FLAG_NORMALS_CALC | BMO_OPTYPE_FLAG_SELECT_FLUSH, +}; + +/* * Connect Verts. * * Split faces by adding edges that connect **verts**. @@ -1734,6 +1755,7 @@ const BMOpDefine *bmo_opdefines[] = { &bmo_collapse_def, &bmo_collapse_uvs_def, &bmo_connect_verts_def, + &bmo_connect_verts_nonplanar_def, &bmo_connect_vert_pair_def, &bmo_contextual_create_def, #ifdef WITH_BULLET diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h index e029ad0d124..33c10411c0f 100644 --- a/source/blender/bmesh/intern/bmesh_operators_private.h +++ b/source/blender/bmesh/intern/bmesh_operators_private.h @@ -40,6 +40,7 @@ void bmo_bridge_loops_exec(BMesh *bm, BMOperator *op); void bmo_collapse_exec(BMesh *bm, BMOperator *op); void bmo_collapse_uvs_exec(BMesh *bm, BMOperator *op); void bmo_connect_verts_exec(BMesh *bm, BMOperator *op); +void bmo_connect_verts_nonplanar_exec(BMesh *bm, BMOperator *op); void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op); void bmo_contextual_create_exec(BMesh *bm, BMOperator *op); void bmo_convex_hull_exec(BMesh *bm, BMOperator *op); diff --git a/source/blender/bmesh/operators/bmo_connect_nonplanar.c b/source/blender/bmesh/operators/bmo_connect_nonplanar.c new file mode 100644 index 00000000000..74f42e73aab --- /dev/null +++ b/source/blender/bmesh/operators/bmo_connect_nonplanar.c @@ -0,0 +1,228 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/operators/bmo_connect_nonplanar.c + * \ingroup bmesh + * + * Connect verts non-planer faces iteratively (splits faces). + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_alloca.h" + +#include "bmesh.h" + +#include "intern/bmesh_operators_private.h" /* own include */ + +#define EDGE_OUT (1 << 0) +#define FACE_OUT (1 << 1) + +/** + * Calculates the face subset normal. + */ +static bool bm_face_subset_calc_normal(BMLoop *l_first, BMLoop *l_last, float r_no[3]) +{ + float const *v_prev, *v_curr; + + /* Newell's Method */ + BMLoop *l_iter = l_first; + BMLoop *l_term = l_last->next; + + zero_v3(r_no); + + v_prev = l_last->v->co; + do { + v_curr = l_iter->v->co; + add_newell_cross_v3_v3v3(r_no, v_prev, v_curr); + v_prev = v_curr; + } while ((l_iter = l_iter->next) != l_term); + + return (normalize_v3(r_no) != 0.0f); +} + +/** + * Calculates how non-planar the face subset is. + */ +static float bm_face_subset_calc_planar(BMLoop *l_first, BMLoop *l_last, const float no[3]) +{ + float axis_mat[3][3]; + float z_prev, z_curr; + float delta_z = 0.0f; + + /* Newell's Method */ + BMLoop *l_iter = l_first; + BMLoop *l_term = l_last->next; + + axis_dominant_v3_to_m3(axis_mat, no); + + z_prev = mul_m3_v3_single_z(axis_mat, l_last->v->co); + do { + z_curr = mul_m3_v3_single_z(axis_mat, l_iter->v->co); + delta_z += fabsf(z_curr - z_prev); + z_prev = z_curr; + } while ((l_iter = l_iter->next) != l_term); + + return delta_z; +} + +static bool bm_face_split_find(BMFace *f, BMVert *v_pair[2], float *r_angle) +{ + BMLoop *l_iter, *l_first; + BMLoop **l_arr = BLI_array_alloca(l_arr, f->len); + const unsigned int f_len = f->len; + unsigned int i_a, i_b; + bool found = false; + + /* angle finding */ + float err_best = FLT_MAX; + float angle_best = FLT_MAX; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + i_a = 0; + do { + l_arr[i_a++] = l_iter; + } while ((l_iter = l_iter->next) != l_first); + + /* now for the big search, O(N^2), however faces normally aren't so large */ + for (i_a = 0; i_a < f_len; i_a++) { + BMLoop *l_a = l_arr[i_a]; + for (i_b = i_a + 2; i_b < f_len; i_b++) { + BMLoop *l_b = l_arr[i_b]; + /* check these are not touching + * (we could be smarter here) */ + if ((l_a->next != l_b) && + (l_a->prev != l_b)) + { + /* first calculate normals */ + float no_a[3], no_b[3]; + + if (bm_face_subset_calc_normal(l_a, l_b, no_a) && + bm_face_subset_calc_normal(l_b, l_a, no_b)) + { + const float err_a = bm_face_subset_calc_planar(l_a, l_b, no_a); + const float err_b = bm_face_subset_calc_planar(l_b, l_a, no_b); + const float err_test = err_a + err_b; + + if (err_test < err_best) { + /* check we're legal (we could batch this) */ + BMLoop *l_split[2] = {l_a, l_b}; + BM_face_legal_splits(f, &l_split, 1); + if (l_split[0]) { + err_best = err_test; + v_pair[0] = l_a->v; + v_pair[1] = l_b->v; + + angle_best = angle_normalized_v3v3(no_a, no_b); + found = true; + } + } + } + } + } + } + + *r_angle = angle_best; + + return found; + + +} + +static bool bm_face_split_by_angle(BMesh *bm, BMFace *f, BMFace *r_f_pair[2], const float angle_limit) +{ + BMVert *v_pair[2]; + float angle; + + if (bm_face_split_find(f, v_pair, &angle) && (angle > angle_limit)) { + BMFace *f_new; + BMLoop *l_new; + f_new = BM_face_split(bm, f, v_pair[0], v_pair[1], &l_new, NULL, false); + if (f_new) { + r_f_pair[0] = f; + r_f_pair[1] = f_new; + + BMO_elem_flag_enable(bm, f, FACE_OUT); + BMO_elem_flag_enable(bm, f_new, FACE_OUT); + BMO_elem_flag_enable(bm, l_new->e, EDGE_OUT); + return true; + } + } + + return false; + +} + +void bmo_connect_verts_nonplanar_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMFace *f; + int totface = 0, totloop = 0; + int tottris; + BMFace **fstack; + STACK_DECLARE(fstack); + + const float angle_limit = BMO_slot_float_get(op->slots_in, "angle_limit"); + + + BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { + if (f->len > 3) { + totface += 1; + totloop += f->len; + } + } + + if (totface == 0) { + return; + } + + /* over alloc, if we split every face */ + tottris = poly_to_tri_count(totface, totloop); + fstack = MEM_mallocN(sizeof(BMFace *) * tottris, __func__); + + STACK_INIT(fstack); + + BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { + if (f->len > 3) { + STACK_PUSH(fstack, f); + } + } + + while ((f = STACK_POP(fstack))) { + BMFace *f_pair[2]; + if (bm_face_split_by_angle(bm, f, f_pair, angle_limit)) { + int j; + for (j = 0; j < 2; j++) { + if (f_pair[j]->len > 3) { + STACK_PUSH(fstack, f_pair[j]); + } + } + } + } + + MEM_freeN(fstack); + + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, EDGE_OUT); + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, FACE_OUT); +} diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index dd96778caad..868db4fa746 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -802,6 +802,51 @@ void MESH_OT_vert_connect(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +static int edbm_vert_connect_nonplaner_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + const float angle_limit = RNA_float_get(op->ptr, "angle_limit"); + + if (!EDBM_op_call_and_selectf( + em, op, + "faces.out", true, + "connect_verts_nonplanar faces=%hf angle_limit=%f", + BM_ELEM_SELECT, angle_limit)) + { + return OPERATOR_CANCELLED; + } + + + EDBM_update_generic(em, true, true); + return OPERATOR_FINISHED; +} + +void MESH_OT_vert_connect_nonplanar(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Split Non-Planar Faces"; + ot->idname = "MESH_OT_vert_connect_nonplanar"; + ot->description = "Split non-planar faces that exceed the angle threshold"; + + /* api callbacks */ + ot->exec = edbm_vert_connect_nonplaner_exec; + ot->poll = ED_operator_editmesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + prop = RNA_def_float_rotation(ot->srna, "angle_limit", 0, NULL, 0.0f, DEG2RADF(180.0f), + "Max Angle", "Angle limit", 0.0f, DEG2RADF(180.0f)); + RNA_def_property_float_default(prop, DEG2RADF(5.0f)); +} + + static int edbm_edge_split_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index c76b9819f75..b2d6e872206 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -166,6 +166,7 @@ void MESH_OT_normals_make_consistent(struct wmOperatorType *ot); void MESH_OT_vertices_smooth(struct wmOperatorType *ot); void MESH_OT_vertices_smooth_laplacian(struct wmOperatorType *ot); void MESH_OT_vert_connect(struct wmOperatorType *ot); +void MESH_OT_vert_connect_nonplanar(struct wmOperatorType *ot); void MESH_OT_edge_split(struct wmOperatorType *ot); void MESH_OT_bridge_edge_loops(struct wmOperatorType *ot); void MESH_OT_wireframe(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 74aaa58d32d..90dc803ea50 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -156,6 +156,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_solidify); WM_operatortype_append(MESH_OT_select_nth); WM_operatortype_append(MESH_OT_vert_connect); + WM_operatortype_append(MESH_OT_vert_connect_nonplanar); WM_operatortype_append(MESH_OT_knife_tool); WM_operatortype_append(MESH_OT_knife_project); |