new mesh tool for cleaning up meshes, splits up non-planar faces while they exceed...
authorCampbell Barton <ideasman42@gmail.com>
Sun, 28 Jul 2013 19:53:46 +0000 (19:53 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Sun, 28 Jul 2013 19:53:46 +0000 (19:53 +0000)
(access from the 'Mesh -> Clean' menu).

release/scripts/startup/bl_ui/space_view3d.py
source/blender/bmesh/CMakeLists.txt
source/blender/bmesh/intern/bmesh_opdefines.c
source/blender/bmesh/intern/bmesh_operators_private.h
source/blender/bmesh/operators/bmo_connect_nonplanar.c [new file with mode: 0644]
source/blender/editors/mesh/editmesh_tools.c
source/blender/editors/mesh/mesh_intern.h
source/blender/editors/mesh/mesh_ops.c

index 18008553368c4c223cc495270aeb5adcb8bad7f0..689b1a9c9b7290310968cc1d5e779944b376a27e 100644 (file)
@@ -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):
index c49c290e81e6cebff62d410acbe8822f045417b2..228ebcb96c46398e583d3ae8efd46b1e69cc946a 100644 (file)
@@ -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
index b56ed15b901d58026d0947d23bf208695ffb5f1e..4f8a851c7802aea7f2f75c9e7a61caa86d47a378 100644 (file)
@@ -867,6 +867,27 @@ static BMOpDefine bmo_connect_verts_def = {
        BMO_OPTYPE_FLAG_UNTAN_MULTIRES | BMO_OPTYPE_FLAG_NORMALS_CALC | BMO_OPTYPE_FLAG_SELECT_FLUSH,
 };
 
+/*
+ * 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.
  *
@@ -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
index e029ad0d12420526d2744781e6a3cada115a2bb5..33c10411c0f49684924663d36b611439f752bb33 100644 (file)
@@ -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 (file)
index 0000000..74f42e7
--- /dev/null
@@ -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);
+}
index dd96778caad1260a04f236275f6e4b68526cf3ff..868db4fa7466299007b13bb58606bf46e2110ccf 100644 (file)
@@ -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);
index c76b9819f753989b27eab3082edbfdd1a584e15d..b2d6e87220669e556d227cc3799d51d990b922ed 100644 (file)
@@ -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);
index 74aaa58d32db9de7dac921aef62c4b5527db6116..90dc803ea50b0a1f1bbf09fbe0db148ea05d2859 100644 (file)
@@ -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);