From 41a5e731a2e13a18110f3f1919c340425f32f452 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 29 Apr 2012 10:44:00 +0000 Subject: [PATCH] bmesh: new wireframe tool - makes wireframe from faces. - options similar to inset (even offset, relative scale) - copies face settings and loops (uvs, vcolors) - optionally replaces the existing geometry. --- release/scripts/startup/bl_ui/space_view3d.py | 1 + source/blender/blenlib/BLI_math_vector.h | 1 + source/blender/blenlib/intern/math_vector.c | 19 + source/blender/bmesh/CMakeLists.txt | 1 + source/blender/bmesh/intern/bmesh_opdefines.c | 20 + .../bmesh/intern/bmesh_operators_private.h | 1 + source/blender/bmesh/intern/bmesh_queries.c | 22 ++ source/blender/bmesh/intern/bmesh_queries.h | 1 + source/blender/bmesh/operators/bmo_inset.c | 19 +- .../blender/bmesh/operators/bmo_wireframe.c | 371 ++++++++++++++++++ source/blender/editors/mesh/editmesh_tools.c | 67 ++++ source/blender/editors/mesh/mesh_intern.h | 1 + source/blender/editors/mesh/mesh_ops.c | 1 + 13 files changed, 507 insertions(+), 18 deletions(-) create mode 100644 source/blender/bmesh/operators/bmo_wireframe.c diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 9cf52056875..ff9484e6baf 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -1767,6 +1767,7 @@ class VIEW3D_MT_edit_mesh_faces(Menu): layout.operator("mesh.inset") layout.operator("mesh.bevel") layout.operator("mesh.solidify") + layout.operator("mesh.wireframe") layout.operator("mesh.sort_faces") layout.separator() diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index af3df9c9ed2..df5199e19e7 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -186,6 +186,7 @@ float angle_normalized_v2v2(const float a[2], const float b[2]); float angle_v3v3(const float a[3], const float b[3]); float angle_v3v3v3(const float a[3], const float b[3], const float c[3]); float angle_normalized_v3v3(const float v1[3], const float v2[3]); +float angle_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3]); void angle_tri_v3(float angles[3], const float v1[3], const float v2[3], const float v3[3]); void angle_quad_v3(float angles[4], const float v1[3], const float v2[3], const float v3[3], const float v4[3]); void angle_poly_v3(float* angles, const float* verts[3], int len); diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index f734e01943f..90e6a4cb945 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -217,6 +217,25 @@ float angle_normalized_v2v2(const float v1[2], const float v2[2]) return 2.0f * (float)saasin(len_v2v2(v2, v1) / 2.0f); } +/** + * angle between 2 vectors defined by 3 coords, about an axis. */ +float angle_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3]) +{ + float v1_proj[3], v2_proj[3], tproj[3]; + + sub_v3_v3v3(v1_proj, v1, v2); + sub_v3_v3v3(v2_proj, v3, v2); + + /* project the vectors onto the axis */ + project_v3_v3v3(tproj, v1_proj, axis); + sub_v3_v3(v1_proj, tproj); + + project_v3_v3v3(tproj, v2_proj, axis); + sub_v3_v3(v2_proj, tproj); + + return angle_v3v3(v1_proj, v2_proj); +} + void angle_tri_v3(float angles[3], const float v1[3], const float v2[3], const float v3[3]) { float ed1[3], ed2[3], ed3[3]; diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 1cf2b9113b2..a23f1935ae0 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -51,6 +51,7 @@ set(SRC operators/bmo_subdivide.h operators/bmo_triangulate.c operators/bmo_utils.c + operators/bmo_wireframe.c intern/bmesh_construct.c intern/bmesh_construct.h diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index 4b5c67c8671..8ffaf1875cf 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -1108,6 +1108,25 @@ static BMOpDefine bmo_inset_def = { 0 }; +/* + * Wire Frame + * + * Makes a wire copy of faces. + */ +static BMOpDefine bmo_wireframe_def = { + "wireframe", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, /* output faces */ + {BMO_OP_SLOT_BOOL, "use_boundary"}, + {BMO_OP_SLOT_BOOL, "use_even_offset"}, + {BMO_OP_SLOT_FLT, "thickness"}, + {BMO_OP_SLOT_BOOL, "use_relative_offset"}, + {BMO_OP_SLOT_FLT, "depth"}, + {0} /* null-terminating sentinel */}, + bmo_wireframe_exec, + 0 +}; + /* * Vertex Slide * @@ -1192,6 +1211,7 @@ BMOpDefine *opdefines[] = { &bmo_bridge_loops_def, &bmo_solidify_def, &bmo_inset_def, + &bmo_wireframe_def, &bmo_vertex_slide_def, }; diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h index 423b30a503a..e222c3422c0 100644 --- a/source/blender/bmesh/intern/bmesh_operators_private.h +++ b/source/blender/bmesh/intern/bmesh_operators_private.h @@ -100,5 +100,6 @@ void bmo_create_circle_exec(BMesh *bm, BMOperator *op); void bmo_bridge_loops_exec(BMesh *bm, BMOperator *op); void bmo_solidify_face_region_exec(BMesh *bm, BMOperator *op); void bmo_inset_exec(BMesh *bm, BMOperator *op); +void bmo_wireframe_exec(BMesh *bm, BMOperator *op); #endif /* __BMESH_OPERATORS_PRIVATE_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index 3543fd952bb..e9a35ff70a2 100644 --- a/source/blender/bmesh/intern/bmesh_queries.c +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -927,6 +927,28 @@ float BM_vert_calc_shell_factor(BMVert *v) return accum_shell / accum_angle; } +/** + * \note quite an obscure function. + * used in bmesh operators that have a relative scale options, + */ +float BM_vert_calc_mean_tagged_edge_length(BMVert *v) +{ + BMIter iter; + BMEdge *e; + int tot; + float length = 0.0f; + + BM_ITER_ELEM_INDEX (e, &iter, v, BM_EDGES_OF_VERT, tot) { + BMVert *v_other = BM_edge_other_vert(e, v); + if (BM_elem_flag_test(v_other, BM_ELEM_TAG)) { + length += BM_edge_calc_length(e); + } + } + + return length / (float)tot; +} + + /** * Returns the edge existing between v1 and v2, or NULL if there isn't one. * diff --git a/source/blender/bmesh/intern/bmesh_queries.h b/source/blender/bmesh/intern/bmesh_queries.h index aefeb80c4f3..08e15884b3f 100644 --- a/source/blender/bmesh/intern/bmesh_queries.h +++ b/source/blender/bmesh/intern/bmesh_queries.h @@ -65,6 +65,7 @@ void BM_edge_calc_face_tangent(BMEdge *e, BMLoop *e_loop, float r_tangent[3]) float BM_vert_calc_edge_angle(BMVert *v); float BM_vert_calc_shell_factor(BMVert *v); +float BM_vert_calc_mean_tagged_edge_length(BMVert *v); BMEdge *BM_edge_exists(BMVert *v1, BMVert *v2); diff --git a/source/blender/bmesh/operators/bmo_inset.c b/source/blender/bmesh/operators/bmo_inset.c index 712f6b736d6..e08f08baacd 100644 --- a/source/blender/bmesh/operators/bmo_inset.c +++ b/source/blender/bmesh/operators/bmo_inset.c @@ -80,23 +80,6 @@ static BMLoop *bm_edge_is_mixed_face_tag(BMLoop *l) } } -float bm_vert_avg_tag_dist(BMVert *v) -{ - BMIter iter; - BMEdge *e; - int tot; - float length = 0.0f; - - BM_ITER_ELEM_INDEX (e, &iter, v, BM_EDGES_OF_VERT, tot) { - BMVert *v_other = BM_edge_other_vert(e, v); - if (BM_elem_flag_test(v_other, BM_ELEM_TAG)) { - length += BM_edge_calc_length(e); - } - } - - return length / (float)tot; -} - /** * implementation is as follows... * @@ -544,7 +527,7 @@ void bmo_inset_exec(BMesh *bm, BMOperator *op) BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { if (BM_elem_flag_test(v, BM_ELEM_TAG)) { const float fac = (depth * - (use_relative_offset ? bm_vert_avg_tag_dist(v) : 1.0f) * + (use_relative_offset ? BM_vert_calc_mean_tagged_edge_length(v) : 1.0f) * (use_even_boundry ? BM_vert_calc_shell_factor(v) : 1.0f)); madd_v3_v3v3fl(varr_co[i], v->co, v->no, fac); } diff --git a/source/blender/bmesh/operators/bmo_wireframe.c b/source/blender/bmesh/operators/bmo_wireframe.c new file mode 100644 index 00000000000..49aff164b7d --- /dev/null +++ b/source/blender/bmesh/operators/bmo_wireframe.c @@ -0,0 +1,371 @@ +/* + * ***** 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_wireframe.c + * \ingroup bmesh + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" + +#include "bmesh.h" + +#include "intern/bmesh_operators_private.h" /* own include */ + +BMLoop *bm_edge_tag_faceloop(BMEdge *e) +{ + BMLoop *l, *l_first; + + l = l_first = e->l; + do { + if (BM_elem_flag_test(l->f, BM_ELEM_TAG)) { + return l; + } + } while ((l = l->radial_next) != l_first); + + /* in the case this is used, we know this will never happen */ + return NULL; +} + +static void bm_vert_boundary_tangent(BMVert *v, float r_no[3], float r_no_face[3], + BMVert **r_va_other, BMVert **r_vb_other) +{ + BMIter iter; + BMEdge *e_iter; + + BMEdge *e_a = NULL, *e_b = NULL; + BMVert *v_a, *v_b; + + BMLoop *l_a, *l_b; + + float no_face[3], no_edge[3]; + float tvec_a[3], tvec_b[3]; + + /* get 2 boundary edges, there should only _be_ 2, + * in case there are more - results wont be valid of course */ + BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) { + if (BM_elem_flag_test(e_iter, BM_ELEM_TAG)) { + if (e_a == NULL) { + e_a = e_iter; + } + else { + e_b = e_iter; + break; + } + } + } + + l_a = bm_edge_tag_faceloop(e_a); + l_b = bm_edge_tag_faceloop(e_b); + + /* average edge face normal */ + add_v3_v3v3(no_face, l_a->f->no, l_b->f->no); + + /* average edge direction */ + v_a = BM_edge_other_vert(e_a, v); + v_b = BM_edge_other_vert(e_b, v); + + sub_v3_v3v3(tvec_a, v->co, v_a->co); + sub_v3_v3v3(tvec_b, v_b->co, v->co); + normalize_v3(tvec_a); + normalize_v3(tvec_b); + add_v3_v3v3(no_edge, tvec_a, tvec_b); /* not unit length but this is ok */ + + + /* find the normal */ + cross_v3_v3v3(r_no, no_edge, no_face); + normalize_v3(r_no); + + /* check are we flipped the right way */ + BM_edge_calc_face_tangent(e_a, l_a, tvec_a); + BM_edge_calc_face_tangent(e_b, l_b, tvec_b); + add_v3_v3(tvec_a, tvec_b); + + if (dot_v3v3(r_no, tvec_a) > 0.0) { + negate_v3(r_no); + } + + copy_v3_v3(r_no_face, no_face); + *r_va_other = v_a; + *r_vb_other = v_b; +} + +/* check if we are the only tagged loop-face around this edge */ +static int bm_loop_is_radial_boundary(BMLoop *l_first) +{ + BMLoop *l = l_first->radial_next; + + if (l == l_first) { + return TRUE; /* a real boundary */ + } + else { + do { + if (BM_elem_flag_test(l->f, BM_ELEM_TAG)) { + return FALSE; + } + } while ((l = l->radial_next) != l_first); + } + return TRUE; +} + +extern float BM_vert_calc_mean_tagged_edge_length(BMVert *v); + +void bmo_wireframe_exec(BMesh *bm, BMOperator *op) +{ + const int use_boundary = BMO_slot_bool_get(op, "use_boundary"); + const int use_even_offset = BMO_slot_bool_get(op, "use_even_offset"); + const int use_relative_offset = BMO_slot_bool_get(op, "use_relative_offset"); + const float depth = BMO_slot_float_get(op, "thickness"); + const float inset = depth; + + const int totvert_orig = bm->totvert; + + BMOIter oiter; + BMIter iter; + BMIter itersub; + + /* filled only with boundary verts */ + BMVert **verts_src = MEM_mallocN(sizeof(BMVert **) * totvert_orig, __func__); + BMVert **verts_neg = MEM_mallocN(sizeof(BMVert **) * totvert_orig, __func__); + BMVert **verts_pos = MEM_mallocN(sizeof(BMVert **) * totvert_orig, __func__); + + /* will over-alloc, but makes for easy lookups by index to keep aligned */ + BMVert **verts_boundary = use_boundary ? + MEM_mallocN(sizeof(BMVert **) * totvert_orig, __func__) : NULL; + + float *verts_relfac = use_relative_offset ? + MEM_mallocN(sizeof(float) * totvert_orig, __func__) : NULL; + + /* may over-alloc if not all faces have wire */ + BMVert **verts_loop; + int verts_loop_tot = 0; + + BMVert *v_src; + + BMFace *f_src; + BMLoop *l; + + float tvec[3]; + float fac; + + int i; + + BM_mesh_elem_index_ensure(bm, BM_VERT); + + BM_ITER_MESH_INDEX (v_src, &iter, bm, BM_VERTS_OF_MESH, i) { + BM_elem_flag_disable(v_src, BM_ELEM_TAG); + verts_src[i] = v_src; + } + + /* setup tags, all faces and verts will be tagged which will be duplicated */ + BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, FALSE); + + BMO_ITER (f_src, &oiter, bm, op, "faces", BM_FACE) { + verts_loop_tot += f_src->len; + BM_elem_flag_enable(f_src, BM_ELEM_TAG); + BM_ITER_ELEM (l, &itersub, f_src, BM_LOOPS_OF_FACE) { + BM_elem_flag_enable(l->v, BM_ELEM_TAG); + + /* also tag boundary edges */ + BM_elem_flag_set(l->e, BM_ELEM_TAG, bm_loop_is_radial_boundary(l)); + } + } + + /* duplicate tagged verts */ + for (i = 0, v_src = verts_src[i]; i < totvert_orig; i++, v_src = verts_src[i]) { + if (BM_elem_flag_test(v_src, BM_ELEM_TAG)) { + fac = depth; + + if (use_relative_offset) { + verts_relfac[i] = BM_vert_calc_mean_tagged_edge_length(v_src); + fac *= verts_relfac[i]; + } + + madd_v3_v3v3fl(tvec, v_src->co, v_src->no, -fac); + verts_neg[i] = BM_vert_create(bm, tvec, v_src); + madd_v3_v3v3fl(tvec, v_src->co, v_src->no, fac); + verts_pos[i] = BM_vert_create(bm, tvec, v_src); + } + else { + /* could skip this */ + verts_src[i] = NULL; + verts_neg[i] = NULL; + verts_pos[i] = NULL; + } + + /* conflicts with BM_vert_calc_mean_tagged_edge_length */ + if (use_relative_offset == FALSE) { + BM_elem_flag_disable(v_src, BM_ELEM_TAG); + } + } + + if (use_relative_offset) { + BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, FALSE); + } + + verts_loop = MEM_mallocN(sizeof(BMVert **) * verts_loop_tot, __func__); + verts_loop_tot = 0; /* count up again */ + + BMO_ITER (f_src, &oiter, bm, op, "faces", BM_FACE) { + BM_ITER_ELEM (l, &itersub, f_src, BM_LOOPS_OF_FACE) { + BM_elem_index_set(l, verts_loop_tot); /* set_loop */ + + BM_loop_calc_face_tangent(l, tvec); + + /* create offset vert */ + fac = inset; + if (use_even_offset) { + fac *= shell_angle_to_dist((M_PI - BM_loop_calc_face_angle(l)) * 0.5f); + } + if (use_relative_offset) { + fac *= verts_relfac[BM_elem_index_get(l->v)]; + } + + madd_v3_v3v3fl(tvec, l->v->co, tvec, fac); + verts_loop[verts_loop_tot] = BM_vert_create(bm, tvec, l->v); + + + if (use_boundary) { + if (BM_elem_flag_test(l->e, BM_ELEM_TAG)) { /* is this a boundary? */ + + BMLoop *l_pair[2] = {l, l->next}; + + BM_elem_flag_enable(l->e, BM_ELEM_TAG); + for (i = 0; i < 2; i++) { + if (!BM_elem_flag_test(l_pair[i]->v, BM_ELEM_TAG)) { + float no_face[3]; + BMVert *va_other; + BMVert *vb_other; + + BM_elem_flag_enable(l_pair[i]->v, BM_ELEM_TAG); + + bm_vert_boundary_tangent(l_pair[i]->v, tvec, no_face, &va_other, &vb_other); + + /* create offset vert */ + /* similar to code above but different angle calc */ + fac = inset; + if (use_even_offset) { + fac *= shell_angle_to_dist((M_PI - angle_on_axis_v3v3v3_v3(va_other->co, + l_pair[i]->v->co, + vb_other->co, + no_face)) * 0.5f); + } + if (use_relative_offset) { + fac *= verts_relfac[BM_elem_index_get(l_pair[i]->v)]; + } + madd_v3_v3v3fl(tvec, l_pair[i]->v->co, tvec, fac); + verts_boundary[BM_elem_index_get(l_pair[i]->v)] = BM_vert_create(bm, tvec, l_pair[i]->v); + } + } + } + } + + verts_loop_tot++; + } + } + + BMO_ITER (f_src, &oiter, bm, op, "faces", BM_FACE) { + BM_elem_flag_disable(f_src, BM_ELEM_TAG); + BM_ITER_ELEM (l, &itersub, f_src, BM_LOOPS_OF_FACE) { + BMFace *f_new; + BMLoop *l_new; + BMLoop *l_next = l->next; + BMVert *v_l1 = verts_loop[BM_elem_index_get(l)]; + BMVert *v_l2 = verts_loop[BM_elem_index_get(l_next)]; + + BMVert *v_src_l1 = l->v; + BMVert *v_src_l2 = l_next->v; + + const int i_1 = BM_elem_index_get(v_src_l1); + const int i_2 = BM_elem_index_get(v_src_l2); + + BMVert *v_neg1 = verts_neg[i_1]; + BMVert *v_neg2 = verts_neg[i_2]; + + BMVert *v_pos1 = verts_pos[i_1]; + BMVert *v_pos2 = verts_pos[i_2]; + + f_new = BM_face_create_quad_tri(bm, v_l1, v_l2, v_neg2, v_neg1, f_src, FALSE); + BM_elem_flag_enable(f_new, BM_ELEM_TAG); + l_new = BM_FACE_FIRST_LOOP(f_new); + + BM_elem_attrs_copy(bm, bm, l, l_new); + BM_elem_attrs_copy(bm, bm, l, l_new->prev); + BM_elem_attrs_copy(bm, bm, l_next, l_new->next); + BM_elem_attrs_copy(bm, bm, l_next, l_new->next->next); + + f_new = BM_face_create_quad_tri(bm, v_l2, v_l1, v_pos1, v_pos2, f_src, FALSE); + BM_elem_flag_enable(f_new, BM_ELEM_TAG); + l_new = BM_FACE_FIRST_LOOP(f_new); + + BM_elem_attrs_copy(bm, bm, l_next, l_new); + BM_elem_attrs_copy(bm, bm, l_next, l_new->prev); + BM_elem_attrs_copy(bm, bm, l, l_new->next); + BM_elem_attrs_copy(bm, bm, l, l_new->next->next); + + + + if (use_boundary) { + if (BM_elem_flag_test(l->e, BM_ELEM_TAG)) { + /* we know its a boundary and this is the only face user (which is being wire'd) */ + /* we know we only touch this edge/face once */ + BMVert *v_b1 = verts_boundary[i_1]; + BMVert *v_b2 = verts_boundary[i_2]; + + f_new = BM_face_create_quad_tri(bm, v_b2, v_b1, v_neg1, v_neg2, f_src, FALSE); + BM_elem_flag_enable(f_new, BM_ELEM_TAG); + l_new = BM_FACE_FIRST_LOOP(f_new); + + BM_elem_attrs_copy(bm, bm, l_next, l_new); + BM_elem_attrs_copy(bm, bm, l_next, l_new->prev); + BM_elem_attrs_copy(bm, bm, l, l_new->next); + BM_elem_attrs_copy(bm, bm, l, l_new->next->next); + + f_new = BM_face_create_quad_tri(bm, v_b1, v_b2, v_pos2, v_pos1, f_src, FALSE); + BM_elem_flag_enable(f_new, BM_ELEM_TAG); + l_new = BM_FACE_FIRST_LOOP(f_new); + + BM_elem_attrs_copy(bm, bm, l, l_new); + BM_elem_attrs_copy(bm, bm, l, l_new->prev); + BM_elem_attrs_copy(bm, bm, l_next, l_new->next); + BM_elem_attrs_copy(bm, bm, l_next, l_new->next->next); + } + } + } + } + + if (use_boundary) { + MEM_freeN(verts_boundary); + } + + if (use_relative_offset) { + MEM_freeN(verts_relfac); + } + + MEM_freeN(verts_src); + MEM_freeN(verts_neg); + MEM_freeN(verts_pos); + MEM_freeN(verts_loop); + + BMO_slot_buffer_from_enabled_hflag(bm, op, "faceout", BM_FACE, BM_ELEM_TAG); +} diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 7eae8b4d67e..a5053978186 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -4266,3 +4266,70 @@ void MESH_OT_inset(wmOperatorType *ot) RNA_def_boolean(ot->srna, "use_select_inset", TRUE, "Select Outer", "Select the new inset faces"); } +static int edbm_wireframe_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BMEdit_FromObject(obedit); + BMOperator bmop; + const int use_boundary = RNA_boolean_get(op->ptr, "use_boundary"); + const int use_even_offset = RNA_boolean_get(op->ptr, "use_even_offset"); + const int use_replace = RNA_boolean_get(op->ptr, "use_replace"); + const int use_relative_offset = RNA_boolean_get(op->ptr, "use_relative_offset"); + const float thickness = RNA_float_get(op->ptr, "thickness"); + + EDBM_op_init(em, &bmop, op, + "wireframe faces=%hf use_boundary=%b use_even_offset=%b use_relative_offset=%b " + "thickness=%f", + BM_ELEM_SELECT, use_boundary, use_even_offset, use_relative_offset, + thickness); + + BMO_op_exec(em->bm, &bmop); + + if (use_replace) { + BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, FALSE); + BMO_slot_buffer_hflag_enable(em->bm, &bmop, "faces", BM_FACE, BM_ELEM_TAG, FALSE); + + BMO_op_callf(em->bm, "del geom=%hvef context=%i", BM_ELEM_TAG, DEL_FACES); + } + + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, FALSE); + BMO_slot_buffer_hflag_enable(em->bm, &bmop, "faceout", BM_FACE, BM_ELEM_SELECT, TRUE); + + if (!EDBM_op_finish(em, &bmop, op, TRUE)) { + return OPERATOR_CANCELLED; + } + else { + EDBM_update_generic(C, em, TRUE); + return OPERATOR_FINISHED; + } +} + +void MESH_OT_wireframe(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Wire Frame"; + ot->idname = "MESH_OT_wireframe"; + ot->description = "Inset new faces into selected faces"; + + /* api callbacks */ + ot->exec = edbm_wireframe_exec; + ot->poll = ED_operator_editmesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "use_boundary", TRUE, "Boundary", "Inset face boundaries"); + RNA_def_boolean(ot->srna, "use_even_offset", TRUE, "Offset Even", "Scale the offset to give more even thickness"); + RNA_def_boolean(ot->srna, "use_relative_offset", FALSE, "Offset Relative", "Scale the offset by surrounding geometry"); + + prop = RNA_def_float(ot->srna, "thickness", 0.01f, 0.0f, FLT_MAX, "Thickness", "", 0.0f, 10.0f); + /* use 1 rather then 10 for max else dragging the button moves too far */ + RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 4); + + + RNA_def_boolean(ot->srna, "use_replace", TRUE, "Replace", "Remove original faces"); +} + diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index b6403f33bc9..ca989a25ccd 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -211,6 +211,7 @@ void MESH_OT_bevel(struct wmOperatorType *ot); void MESH_OT_bridge_edge_loops(struct wmOperatorType *ot); void MESH_OT_inset(struct wmOperatorType *ot); +void MESH_OT_wireframe(struct wmOperatorType *ot); void MESH_OT_vert_slide(struct wmOperatorType *ot); /* ******************* mesh_navmesh.c */ diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 4b4fef53275..0b2a6d2bd66 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -164,6 +164,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_bridge_edge_loops); WM_operatortype_append(MESH_OT_inset); + WM_operatortype_append(MESH_OT_wireframe); WM_operatortype_append(MESH_OT_edge_split); #ifdef WITH_GAMEENGINE