patch [#34886] BMesh Individual Face Inset
from Francisco De La Cruz (xercesblue) with some simplifications to the patch.
This commit is contained in:
parent
716ed32d90
commit
8fd42b2aae
@ -1499,6 +1499,28 @@ static BMOpDefine bmo_solidify_def = {
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* Face Inset (Individual).
|
||||
*
|
||||
* Insets individual faces.
|
||||
*/
|
||||
static BMOpDefine bmo_inset_individual_def = {
|
||||
"inset_individual",
|
||||
/* slots_in */
|
||||
{{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* input faces */
|
||||
{"thickness", BMO_OP_SLOT_FLT},
|
||||
{"depth", BMO_OP_SLOT_FLT},
|
||||
{"use_even_offset", BMO_OP_SLOT_BOOL},
|
||||
{{'\0'}},
|
||||
},
|
||||
/* slots_out */
|
||||
{{"faces.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* output faces */
|
||||
{{'\0'}},
|
||||
},
|
||||
bmo_inset_individual_exec,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* Face Inset.
|
||||
*
|
||||
@ -1647,6 +1669,7 @@ const BMOpDefine *bmo_opdefines[] = {
|
||||
&bmo_extrude_face_region_def,
|
||||
&bmo_extrude_vert_indiv_def,
|
||||
&bmo_find_doubles_def,
|
||||
&bmo_inset_individual_def,
|
||||
&bmo_inset_def,
|
||||
&bmo_join_triangles_def,
|
||||
&bmo_mesh_to_bmesh_def,
|
||||
|
@ -65,6 +65,7 @@ void bmo_extrude_edge_only_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_extrude_vert_indiv_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_find_doubles_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_inset_individual_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_inset_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_join_triangles_exec(BMesh *bm, BMOperator *op);
|
||||
void bmo_mesh_to_bmesh_exec(BMesh *bm, BMOperator *op);
|
||||
|
@ -24,14 +24,14 @@
|
||||
* \ingroup bmesh
|
||||
*
|
||||
* Inset face regions.
|
||||
* Inset individual faces.
|
||||
*
|
||||
* TODO
|
||||
* - Inset indervidual faces.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_array.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
@ -39,6 +39,227 @@
|
||||
|
||||
#define ELE_NEW 1
|
||||
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Inset Indervidual */
|
||||
|
||||
|
||||
/* Holds Per-Face Inset Edge Data */
|
||||
typedef struct EdgeInsetInfo {
|
||||
float no[3];
|
||||
BMEdge *e_old;
|
||||
BMEdge *e_new;
|
||||
} EdgeInsetInfo;
|
||||
|
||||
/**
|
||||
* Individual Face Inset.
|
||||
* Find all tagged faces (f), duplicate edges around faces, inset verts of
|
||||
* created edges, create new faces between old and new edges, fill face
|
||||
* between connected new edges, kill old face (f).
|
||||
*/
|
||||
void bmo_inset_individual_exec(BMesh *bm, BMOperator *op)
|
||||
{
|
||||
BMEdge **f_edges = NULL;
|
||||
BMVert **f_verts = NULL;
|
||||
BMFace *f;
|
||||
|
||||
BMOIter oiter;
|
||||
EdgeInsetInfo *eiinfo_arr = NULL;
|
||||
|
||||
BLI_array_declare(eiinfo_arr);
|
||||
BLI_array_declare(f_edges);
|
||||
BLI_array_declare(f_verts);
|
||||
|
||||
const float thickness = BMO_slot_float_get(op->slots_in, "thickness");
|
||||
const float depth = BMO_slot_float_get(op->slots_in, "depth");
|
||||
const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset");
|
||||
|
||||
/* Only tag faces in slot */
|
||||
BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false);
|
||||
|
||||
BMO_slot_buffer_hflag_enable(bm, op->slots_in, "faces", BM_FACE, BM_ELEM_TAG, false);
|
||||
|
||||
BMO_ITER(f, &oiter, op->slots_in, "faces", BM_FACE) {
|
||||
BMLoop *l_iter, *l_first;
|
||||
BMLoop *l_iter_inner = NULL;
|
||||
int i;
|
||||
|
||||
BLI_array_empty(f_verts);
|
||||
BLI_array_empty(f_edges);
|
||||
BLI_array_empty(eiinfo_arr);
|
||||
BLI_array_grow_items(f_verts, f->len);
|
||||
BLI_array_grow_items(f_edges, f->len);
|
||||
BLI_array_grow_items(eiinfo_arr, f->len);
|
||||
|
||||
/* create verts */
|
||||
i = 0;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
f_verts[i] = BM_vert_create(bm, l_iter->v->co, l_iter->v, 0);
|
||||
i++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
/* make edges */
|
||||
i = 0;
|
||||
l_iter = l_first;
|
||||
do {
|
||||
f_edges[i] = BM_edge_create(bm, f_verts[i], f_verts[(i + 1) % f->len], l_iter->e, 0);
|
||||
|
||||
eiinfo_arr[i].e_new = f_edges[i];
|
||||
eiinfo_arr[i].e_old = l_iter->e;
|
||||
BM_edge_calc_face_tangent(l_iter->e, l_iter, eiinfo_arr[i].no);
|
||||
|
||||
/* Tagging (old elements) required when iterating over edges
|
||||
* connected to verts for translation vector calculation */
|
||||
BM_elem_flag_enable(l_iter->e, BM_ELEM_TAG);
|
||||
BM_elem_index_set(l_iter->e, i); /* set_dirty! */
|
||||
i++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
/* done with edges */
|
||||
|
||||
bm->elem_index_dirty |= BM_EDGE;
|
||||
|
||||
/* Calculate translation vector for new */
|
||||
l_iter = l_first;
|
||||
do {
|
||||
EdgeInsetInfo *ei_prev = &eiinfo_arr[BM_elem_index_get(l_iter->prev->e)];
|
||||
EdgeInsetInfo *ei_next = &eiinfo_arr[BM_elem_index_get(l_iter->e)];
|
||||
float tvec[3];
|
||||
float v_new_co[3];
|
||||
int index = 0;
|
||||
|
||||
add_v3_v3v3(tvec, ei_prev->no, ei_next->no);
|
||||
normalize_v3(tvec);
|
||||
|
||||
/* l->e is traversed in order */
|
||||
index = BM_elem_index_get(l_iter->e);
|
||||
|
||||
copy_v3_v3(v_new_co, eiinfo_arr[index].e_new->v1->co);
|
||||
|
||||
if (use_even_offset) {
|
||||
mul_v3_fl(tvec, shell_angle_to_dist(angle_normalized_v3v3(ei_prev->no, ei_next->no) / 2.0f));
|
||||
}
|
||||
|
||||
/* Modify vertices and their normals */
|
||||
madd_v3_v3fl(v_new_co, tvec, thickness);
|
||||
|
||||
/* Set normal, add depth and write new vertex position*/
|
||||
copy_v3_v3(eiinfo_arr[index].e_new->v1->no, f->no);
|
||||
|
||||
madd_v3_v3fl(v_new_co, f->no, depth);
|
||||
|
||||
copy_v3_v3(eiinfo_arr[index].e_new->v1->co, v_new_co);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
||||
{
|
||||
BMFace *f_new_inner;
|
||||
/* Create New Inset Faces */
|
||||
f_new_inner = BM_face_create(bm, f_verts, f_edges, f->len, 0);
|
||||
if (UNLIKELY(f_new_inner == NULL)) {
|
||||
BMO_error_raise(bm, op, BMERR_MESH_ERROR, "Inset failed: could not create inner face.");
|
||||
BLI_array_free(f_edges);
|
||||
BLI_array_free(f_verts);
|
||||
BLI_array_free(eiinfo_arr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy Face Data */
|
||||
BM_elem_attrs_copy(bm, bm, f, f_new_inner);
|
||||
// Don't tag, gives more useful inner/outer select option
|
||||
// BMO_elem_flag_enable(bm, f_new_inner, ELE_NEW);
|
||||
|
||||
l_iter_inner = BM_FACE_FIRST_LOOP(f_new_inner);
|
||||
}
|
||||
|
||||
l_iter = l_first;
|
||||
do {
|
||||
BMFace *f_new_outer;
|
||||
|
||||
BMLoop *l_iter_sub;
|
||||
BMLoop *l_a = NULL;
|
||||
BMLoop *l_b = NULL;
|
||||
BMLoop *l_a_other = NULL;
|
||||
BMLoop *l_b_other = NULL;
|
||||
BMLoop *l_shared = NULL;
|
||||
|
||||
BM_elem_attrs_copy(bm, bm, l_iter, l_iter_inner);
|
||||
|
||||
f_new_outer = BM_face_create_quad_tri(bm,
|
||||
l_iter->v,
|
||||
l_iter->next->v,
|
||||
l_iter_inner->next->v,
|
||||
l_iter_inner->v,
|
||||
f, false);
|
||||
|
||||
if (UNLIKELY(f_new_outer == NULL)) {
|
||||
BMO_error_raise(bm, op, BMERR_MESH_ERROR, "Inset failed: could not create an outer face.");
|
||||
BLI_array_free(f_edges);
|
||||
BLI_array_free(f_verts);
|
||||
BLI_array_free(eiinfo_arr);
|
||||
return;
|
||||
}
|
||||
|
||||
BM_elem_attrs_copy(bm, bm, f, f_new_outer);
|
||||
BMO_elem_flag_enable(bm, f_new_outer, ELE_NEW);
|
||||
BM_elem_flag_enable(f_new_outer, BM_ELEM_TAG);
|
||||
|
||||
/* Copy Loop Data */
|
||||
l_a = BM_FACE_FIRST_LOOP(f_new_outer);
|
||||
l_b = l_a->next;
|
||||
|
||||
l_iter_sub = l_iter;
|
||||
|
||||
/* Skip old face f and new inset face.
|
||||
* If loop if found we are a boundary. This
|
||||
* is required as opposed to BM_edge_is_boundary()
|
||||
* Because f_new_outer shares an edge with f */
|
||||
do {
|
||||
if (l_iter_sub->f != f && l_iter_sub->f != f_new_outer) {
|
||||
l_shared = l_iter_sub;
|
||||
break;
|
||||
}
|
||||
} while ((l_iter_sub = l_iter_sub->radial_next) != l_iter);
|
||||
|
||||
if (l_shared) {
|
||||
BM_elem_attrs_copy(bm, bm, l_shared, l_a->next);
|
||||
BM_elem_attrs_copy(bm, bm, l_shared->next, l_a);
|
||||
}
|
||||
else {
|
||||
l_a_other = BM_edge_other_loop(l_a->e, l_a);
|
||||
l_b_other = l_a_other->next;
|
||||
BM_elem_attrs_copy(bm, bm, l_a_other, l_a);
|
||||
BM_elem_attrs_copy(bm, bm, l_b_other, l_b);
|
||||
}
|
||||
|
||||
/* Move to the last two loops in new face */
|
||||
l_a = l_b->next;
|
||||
l_b = l_a->next;
|
||||
|
||||
/* This loop should always have >1 radials
|
||||
* (associated edge connects new and old face) */
|
||||
BM_elem_attrs_copy(bm, bm, l_iter, l_b);
|
||||
BM_elem_attrs_copy(bm, bm, l_iter->next, l_a);
|
||||
|
||||
} while ((l_iter_inner = l_iter_inner->next),
|
||||
(l_iter = l_iter->next) != l_first);
|
||||
|
||||
BM_face_kill(bm, f);
|
||||
}
|
||||
|
||||
/* we could flag new edges/verts too, is it useful? */
|
||||
BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, ELE_NEW);
|
||||
|
||||
BLI_array_free(f_verts);
|
||||
BLI_array_free(f_edges);
|
||||
BLI_array_free(eiinfo_arr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Inset Region */
|
||||
|
||||
typedef struct SplitEdgeInfo {
|
||||
float no[3];
|
||||
float length;
|
||||
|
@ -80,7 +80,7 @@ static void edbm_inset_update_header(wmOperator *op, bContext *C)
|
||||
InsetData *opdata = op->customdata;
|
||||
|
||||
const char *str = IFACE_("Confirm: Enter/LClick, Cancel: (Esc/RClick), Thickness: %s, "
|
||||
"Depth (Ctrl to tweak): %s (%s), Outset (O): (%s), Boundary (B): (%s)");
|
||||
"Depth (Ctrl to tweak): %s (%s), Outset (O): (%s), Boundary (B): (%s), Individual (I): (%s)");
|
||||
|
||||
char msg[HEADER_LENGTH];
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
@ -98,7 +98,8 @@ static void edbm_inset_update_header(wmOperator *op, bContext *C)
|
||||
flts_str + NUM_STR_REP_LEN,
|
||||
opdata->modify_depth ? IFACE_("On") : IFACE_("Off"),
|
||||
RNA_boolean_get(op->ptr, "use_outset") ? IFACE_("On") : IFACE_("Off"),
|
||||
RNA_boolean_get(op->ptr, "use_boundary") ? IFACE_("On") : IFACE_("Off")
|
||||
RNA_boolean_get(op->ptr, "use_boundary") ? IFACE_("On") : IFACE_("Off"),
|
||||
RNA_boolean_get(op->ptr, "individual") ? IFACE_("On") : IFACE_("Off")
|
||||
);
|
||||
|
||||
ED_area_headerprint(sa, msg);
|
||||
@ -191,6 +192,7 @@ static int edbm_inset_calc(wmOperator *op)
|
||||
const float depth = RNA_float_get(op->ptr, "depth");
|
||||
const bool use_outset = RNA_boolean_get(op->ptr, "use_outset");
|
||||
const bool use_select_inset = RNA_boolean_get(op->ptr, "use_select_inset"); /* not passed onto the BMO */
|
||||
const bool individual = RNA_boolean_get(op->ptr, "individual");
|
||||
|
||||
opdata = op->customdata;
|
||||
em = opdata->em;
|
||||
@ -199,12 +201,18 @@ static int edbm_inset_calc(wmOperator *op)
|
||||
EDBM_redo_state_restore(opdata->mesh_backup, em, false);
|
||||
}
|
||||
|
||||
EDBM_op_init(em, &bmop, op,
|
||||
"inset faces=%hf use_boundary=%b use_even_offset=%b use_relative_offset=%b "
|
||||
"thickness=%f depth=%f use_outset=%b",
|
||||
BM_ELEM_SELECT, use_boundary, use_even_offset, use_relative_offset,
|
||||
thickness, depth, use_outset);
|
||||
|
||||
if (individual) {
|
||||
EDBM_op_init(em, &bmop, op,
|
||||
"inset_individual faces=%hf thickness=%f depth=%f use_even_offset=%b",
|
||||
BM_ELEM_SELECT, thickness, depth, use_even_offset);
|
||||
}
|
||||
else {
|
||||
EDBM_op_init(em, &bmop, op,
|
||||
"inset faces=%hf use_boundary=%b use_even_offset=%b use_relative_offset=%b "
|
||||
"thickness=%f depth=%f use_outset=%b",
|
||||
BM_ELEM_SELECT, use_boundary, use_even_offset, use_relative_offset,
|
||||
thickness, depth, use_outset);
|
||||
}
|
||||
BMO_op_exec(em->bm, &bmop);
|
||||
|
||||
if (use_select_inset) {
|
||||
@ -410,6 +418,20 @@ static int edbm_inset_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IKEY:
|
||||
if (event->val == KM_PRESS) {
|
||||
int individual = RNA_boolean_get(op->ptr, "individual");
|
||||
RNA_boolean_set(op->ptr, "individual", !individual);
|
||||
if (edbm_inset_calc(op)) {
|
||||
edbm_inset_update_header(op, C);
|
||||
}
|
||||
else {
|
||||
edbm_inset_cancel(C, op);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
@ -448,4 +470,5 @@ void MESH_OT_inset(wmOperatorType *ot)
|
||||
|
||||
RNA_def_boolean(ot->srna, "use_outset", false, "Outset", "Outset rather than inset");
|
||||
RNA_def_boolean(ot->srna, "use_select_inset", true, "Select Outer", "Select the new inset faces");
|
||||
RNA_def_boolean(ot->srna, "individual", false, "Individual", "Individual Face Inset");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user