forked from bartvdbraak/blender
Add mesh tool to split faces by wire edges (face menu)
This can be used in a similar way to the knife tool, but the edges are manually setup first.
This commit is contained in:
parent
48abc65c39
commit
dd9a33dc83
@ -2278,6 +2278,7 @@ class VIEW3D_MT_edit_mesh_faces(Menu):
|
||||
layout.operator("mesh.poke")
|
||||
layout.operator("mesh.quads_convert_to_tris")
|
||||
layout.operator("mesh.tris_convert_to_quads")
|
||||
layout.operator("mesh.face_split_by_edges")
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -45,6 +45,7 @@ set(SRC
|
||||
editmesh_bisect.c
|
||||
editmesh_extrude.c
|
||||
editmesh_inset.c
|
||||
editmesh_intersect.c
|
||||
editmesh_knife.c
|
||||
editmesh_knife_project.c
|
||||
editmesh_loopcut.c
|
||||
|
276
source/blender/editors/mesh/editmesh_intersect.c
Normal file
276
source/blender/editors/mesh/editmesh_intersect.c
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* ***** 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.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/mesh/editmesh_intersect.c
|
||||
* \ingroup edmesh
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_array.h"
|
||||
#include "BLI_linklist_stack.h"
|
||||
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_editmesh.h"
|
||||
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "ED_mesh.h"
|
||||
#include "ED_screen.h"
|
||||
|
||||
#include "intern/bmesh_private.h"
|
||||
|
||||
#include "mesh_intern.h" /* own include */
|
||||
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Face Split by Edges */
|
||||
|
||||
|
||||
/** \name Face/Edge Split
|
||||
* \{ */
|
||||
|
||||
static void bm_face_split_by_edges(BMesh *bm, BMFace *f, const char hflag)
|
||||
{
|
||||
BMEdge **edge_net = NULL;
|
||||
BLI_array_declare(edge_net);
|
||||
|
||||
const int f_index = BM_elem_index_get(f);
|
||||
|
||||
BMLoop *l_iter;
|
||||
BMLoop *l_first;
|
||||
BMVert *v;
|
||||
|
||||
BMFace **face_arr;
|
||||
int face_arr_len;
|
||||
|
||||
/* likely this will stay very small
|
||||
* all verts pushed into this stack _must_ have their previous edges set! */
|
||||
BLI_SMALLSTACK_DECLARE(vert_stack, BMVert *);
|
||||
BLI_SMALLSTACK_DECLARE(vert_stack_next, BMVert *);
|
||||
|
||||
|
||||
/* collect all edges */
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
BMIter iter;
|
||||
BMEdge *e;
|
||||
|
||||
BM_ITER_ELEM (e, &iter, l_iter->v, BM_EDGES_OF_VERT) {
|
||||
if (BM_elem_flag_test(e, hflag) &&
|
||||
(BM_elem_index_get(e) == f_index))
|
||||
{
|
||||
BMVert *v;
|
||||
v = BM_edge_other_vert(e, l_iter->v);
|
||||
v->e = e;
|
||||
|
||||
BLI_SMALLSTACK_PUSH(vert_stack, v);
|
||||
BLI_array_append(edge_net, e);
|
||||
}
|
||||
}
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
|
||||
|
||||
/* now assign all */
|
||||
/* pop free values into the next stack */
|
||||
while ((v = BLI_SMALLSTACK_POP_EX(vert_stack, vert_stack_next))) {
|
||||
BMIter eiter;
|
||||
BMEdge *e_next;
|
||||
|
||||
BM_ITER_ELEM (e_next, &eiter, v, BM_EDGES_OF_VERT) {
|
||||
if (BM_elem_flag_test(e_next, hflag) &&
|
||||
(BM_elem_index_get(e_next) == -1))
|
||||
{
|
||||
BMVert *v_next;
|
||||
v_next = BM_edge_other_vert(e_next, v);
|
||||
BM_elem_index_set(e_next, f_index);
|
||||
BLI_SMALLSTACK_PUSH(vert_stack_next, v_next);
|
||||
BLI_array_append(edge_net, e_next);
|
||||
}
|
||||
}
|
||||
|
||||
if (BLI_SMALLSTACK_IS_EMPTY(vert_stack)) {
|
||||
BLI_SMALLSTACK_SWAP(vert_stack, vert_stack_next);
|
||||
}
|
||||
}
|
||||
|
||||
BM_face_split_edgenet(bm, f, edge_net, BLI_array_count(edge_net), &face_arr, &face_arr_len);
|
||||
BLI_array_free(edge_net);
|
||||
|
||||
if (face_arr_len) {
|
||||
int i;
|
||||
for (i = 0; i < face_arr_len; i++) {
|
||||
BM_face_select_set(bm, face_arr[i], true);
|
||||
BM_elem_flag_disable(face_arr[i], hflag);
|
||||
}
|
||||
}
|
||||
|
||||
if (face_arr) {
|
||||
MEM_freeN(face_arr);
|
||||
}
|
||||
}
|
||||
|
||||
static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
BMesh *bm = em->bm;
|
||||
const char hflag = BM_ELEM_TAG;
|
||||
|
||||
BMVert *v;
|
||||
BMEdge *e;
|
||||
BMFace *f;
|
||||
BMIter iter;
|
||||
|
||||
BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
|
||||
|
||||
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
BM_elem_flag_disable(v, hflag);
|
||||
}
|
||||
|
||||
/* edge index is set to -1 then used to assosiate them with faces */
|
||||
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
|
||||
if (BM_elem_flag_test(e, BM_ELEM_SELECT) && BM_edge_is_wire(e)) {
|
||||
BM_elem_flag_enable(e, hflag);
|
||||
|
||||
BM_elem_flag_enable(e->v1, hflag);
|
||||
BM_elem_flag_enable(e->v2, hflag);
|
||||
|
||||
}
|
||||
else {
|
||||
BM_elem_flag_disable(e, hflag);
|
||||
}
|
||||
BM_elem_index_set(e, -1); /* set_dirty */
|
||||
}
|
||||
|
||||
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
|
||||
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
|
||||
BM_elem_flag_enable(f, hflag);
|
||||
}
|
||||
else {
|
||||
BM_elem_flag_disable(f, hflag);
|
||||
}
|
||||
}
|
||||
|
||||
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
|
||||
if (BM_elem_flag_test(e, hflag)) {
|
||||
BMIter viter;
|
||||
BMVert *v;
|
||||
BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) {
|
||||
BMIter liter;
|
||||
BMLoop *l;
|
||||
|
||||
unsigned int loop_stack_len;
|
||||
BMLoop *l_best = NULL;
|
||||
|
||||
BLI_assert(BLI_SMALLSTACK_IS_EMPTY(loop_stack));
|
||||
loop_stack_len = 0;
|
||||
|
||||
BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
|
||||
if (BM_elem_flag_test(l->f, hflag)) {
|
||||
BLI_SMALLSTACK_PUSH(loop_stack, l);
|
||||
loop_stack_len++;
|
||||
}
|
||||
}
|
||||
|
||||
if (loop_stack_len == 0) {
|
||||
/* pass */
|
||||
}
|
||||
else if (loop_stack_len == 1) {
|
||||
l_best = BLI_SMALLSTACK_POP(loop_stack);
|
||||
}
|
||||
else {
|
||||
/* complicated case, match the edge with a face-loop */
|
||||
|
||||
BMVert *v_other = BM_edge_other_vert(e, v);
|
||||
float e_dir[3];
|
||||
|
||||
/* we wan't closest to zero */
|
||||
float dot_best = FLT_MAX;
|
||||
|
||||
sub_v3_v3v3(e_dir, v_other->co, v->co);
|
||||
normalize_v3(e_dir);
|
||||
|
||||
while ((l = BLI_SMALLSTACK_POP(loop_stack))) {
|
||||
float dot_test;
|
||||
|
||||
/* Check dot first to save on expensive angle-comparison.
|
||||
* ideal case is 90d difference == 0.0 dot */
|
||||
dot_test = fabsf(dot_v3v3(e_dir, l->f->no));
|
||||
if (dot_test < dot_best) {
|
||||
|
||||
/* check we're in the correct corner (works with convex loops too) */
|
||||
if (angle_signed_on_axis_v3v3v3_v3(l->prev->v->co, l->v->co, v_other->co, l->f->no) <
|
||||
angle_signed_on_axis_v3v3v3_v3(l->prev->v->co, l->v->co, l->next->v->co, l->f->no))
|
||||
{
|
||||
dot_best = dot_test;
|
||||
l_best = l;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (l_best) {
|
||||
BM_elem_index_set(e, BM_elem_index_get(l_best->f)); /* set_dirty */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bm->elem_index_dirty |= BM_EDGE;
|
||||
|
||||
|
||||
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
|
||||
if (BM_elem_flag_test(f, hflag)) {
|
||||
bm_face_split_by_edges(bm, f, hflag);
|
||||
}
|
||||
}
|
||||
|
||||
EDBM_mesh_normals_update(em);
|
||||
EDBM_update_generic(em, true, true);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
|
||||
void MESH_OT_face_split_by_edges(struct wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Split by Edges";
|
||||
ot->description = "Split faces by loose edges";
|
||||
ot->idname = "MESH_OT_face_split_by_edges";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = edbm_face_split_by_edges_exec;
|
||||
ot->poll = ED_operator_editmesh;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/** \} */
|
@ -112,6 +112,9 @@ void MESH_OT_screw(struct wmOperatorType *ot);
|
||||
/* *** editmesh_inset.c *** */
|
||||
void MESH_OT_inset(struct wmOperatorType *ot);
|
||||
|
||||
/* *** editmesh_intersect.c *** */
|
||||
void MESH_OT_face_split_by_edges(struct wmOperatorType *ot);
|
||||
|
||||
|
||||
/* *** editmesh_knife.c *** */
|
||||
void MESH_OT_knife_tool(struct wmOperatorType *ot);
|
||||
|
@ -170,6 +170,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_face_split_by_edges);
|
||||
WM_operatortype_append(MESH_OT_poke);
|
||||
WM_operatortype_append(MESH_OT_wireframe);
|
||||
WM_operatortype_append(MESH_OT_edge_split);
|
||||
|
Loading…
Reference in New Issue
Block a user