Index: source/blender/bmesh/intern/bmesh_opdefines.c =================================================================== --- source/blender/bmesh/intern/bmesh_opdefines.c (revision 55827) +++ source/blender/bmesh/intern/bmesh_opdefines.c (working copy) @@ -1500,6 +1500,28 @@ }; /* + * 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. * * Inset or outset faces. @@ -1514,6 +1536,7 @@ {"thickness", BMO_OP_SLOT_FLT}, {"depth", BMO_OP_SLOT_FLT}, {"use_outset", BMO_OP_SLOT_BOOL}, + {"individual", BMO_OP_SLOT_BOOL}, {{'\0'}}, }, /* slots_out */ @@ -1647,6 +1670,7 @@ &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, Index: source/blender/bmesh/intern/bmesh_operators_private.h =================================================================== --- source/blender/bmesh/intern/bmesh_operators_private.h (revision 55827) +++ source/blender/bmesh/intern/bmesh_operators_private.h (working copy) @@ -65,6 +65,7 @@ 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); Index: source/blender/bmesh/operators/bmo_inset.c =================================================================== --- source/blender/bmesh/operators/bmo_inset.c (revision 55827) +++ source/blender/bmesh/operators/bmo_inset.c (working copy) @@ -24,20 +24,21 @@ * \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" #include "intern/bmesh_operators_private.h" /* own include */ #define ELE_NEW 1 +#define ELE_DEL 2 typedef struct SplitEdgeInfo { float no[3]; @@ -47,6 +48,13 @@ BMLoop *l; } SplitEdgeInfo; +/* Holds Per-Face Inset Edge Data */ +typedef struct EdgeInsetInfo { + float no[3]; + BMEdge *e_old; + BMEdge *e_new; +} EdgeInsetInfo; + /** * return the tag loop where there is... * - only 1 tagged face attached to this edge. @@ -86,6 +94,256 @@ } /** + * 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) +{ + BMVert *v = NULL; + BMVert *new_v = NULL; + + BMVert *prev_v = NULL; + BMVert *first_v = NULL; + BMEdge *prev_e = NULL; + BMEdge *e = NULL; + + BMLoop *l = NULL; + BMLoop *prev_l = NULL; + BMLoop *l2 = NULL; + BMLoop *l3 = NULL; + BMLoop *l4 = NULL; + + BMFace *f = NULL; + BMFace *nface = NULL; + BMFace *nface2 = NULL; + + BMEdge **edges = NULL; + BMVert **verts = NULL; + + BMOIter oiter; + BMIter liter, liter2; + + int len = 0; + EdgeInsetInfo *eiinfo_arr = NULL; + + BLI_array_declare(eiinfo_arr); + BLI_array_declare(edges); + BLI_array_declare(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) { + BLI_array_empty(verts); + BLI_array_empty(edges); + BLI_array_empty(eiinfo_arr); + BLI_array_grow_items(verts, f->len); + BLI_array_grow_items(edges, f->len); + BLI_array_grow_items(eiinfo_arr, f->len); + + len = 0; + prev_v = NULL, first_v = NULL, prev_e = NULL, prev_l = NULL; + BM_ITER_ELEM(l, &liter, f, BM_LOOPS_OF_FACE) { + new_v = BM_vert_create(bm, l->v->co, l->v, 0); + if (prev_v != NULL) { + e = BM_edge_create(bm, prev_v, new_v, prev_e, 0); + + eiinfo_arr[len].e_new = e; + eiinfo_arr[len].e_old = prev_e; + BM_edge_calc_face_tangent(prev_e, prev_l, eiinfo_arr[len].no); + + /* Tagging (old elements) required when iterating over edges + * connected to verts for translation vector calculation */ + BM_elem_flag_enable(prev_e, BM_ELEM_TAG); + BM_elem_index_set(prev_e, len); + + verts[len] = prev_v; + edges[len] = e; + len++; + } + + if (!first_v) first_v = new_v; + prev_v = new_v; + prev_e = l->e; + prev_l = l; + } + + /* Close the "edge ring" */ + e = BM_edge_create(bm, prev_v, first_v, prev_e, 0); + + verts[len] = prev_v; + edges[len] = e; + + eiinfo_arr[len].e_new = e; + eiinfo_arr[len].e_old = prev_e; + BM_edge_calc_face_tangent(prev_e, prev_l, eiinfo_arr[len].no); + + BM_elem_flag_enable(prev_e, BM_ELEM_TAG); + BM_elem_index_set(prev_e, len); + + len++; + + bm->elem_index_dirty |= BM_EDGE; + + /* Calculate translation vector for new */ + BM_ITER_ELEM(l, &liter, f, BM_LOOPS_OF_FACE) { + BMIter eiter; + EdgeInsetInfo *e1 = NULL, *e2 = NULL; + int edgepair[2]; + int ep_count = 0; + float tvec[3] = {0}; + float vco[3] = {0}; + float vnorm[3] = {0}; + int index = 0; + + v = l->v; + /* Some edges (boundaries) might have more than one loop */ + BM_ITER_ELEM(e, &eiter, v, BM_EDGES_OF_VERT) { + /* Test required to prevent looping over processed edges */ + if (BM_elem_flag_test(e, BM_ELEM_TAG) && BM_edge_in_face(f, e)) { + if (ep_count < 2) { + edgepair[ep_count++] = BM_elem_index_get(e); + } else if (ep_count == 2) continue; + } + } + + if (ep_count >= 2) { + e1 = &eiinfo_arr[edgepair[0]]; + e2 = &eiinfo_arr[edgepair[1]]; + add_v3_v3v3(tvec, e1->no, e2->no); + normalize_v3(tvec); + } else { /* Unable to find two coinciding edges */ + e1 = &eiinfo_arr[edgepair[0]]; + copy_v3_v3(tvec, e1->no); + } + + /* l->e is traversed in order */ + index = BM_elem_index_get(l->e); + + copy_v3_v3(vco, eiinfo_arr[index].e_new->v1->co); + + if (use_even_offset) { + mul_v3_fl(tvec, shell_angle_to_dist(angle_normalized_v3v3(e1->no, e2->no) / 2.0f)); + } + + /* Modify vertices and their normals */ + madd_v3_v3fl(vco, tvec, thickness); + copy_v3_v3(vnorm, f->no); + normalize_v3(vnorm); + + /* Set normal, add depth and write new vertex position*/ + copy_v3_v3(eiinfo_arr[index].e_new->v1->no, vnorm); + + madd_v3_v3fl(vco, vnorm, depth); + + copy_v3_v3(eiinfo_arr[index].e_new->v1->co, vco); + } + + /* Create New Inset Faces */ + nface = BM_face_create(bm, verts, edges, f->len, 0); + if (UNLIKELY(nface == NULL)) { + BMO_error_raise(bm, op, BMERR_MESH_ERROR, "Inset failed: could not create inner face."); + BLI_array_free(edges); + BLI_array_free(verts); + BLI_array_free(eiinfo_arr); + return; + } + + /* Copy Face Data */ + BM_elem_attrs_copy(bm, bm, f, nface); + + BM_elem_flag_enable(nface, BM_ELEM_SELECT); + + /* Mark old faces for for delete operator later */ + BMO_elem_flag_enable(bm, f, ELE_DEL); + + l4 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, nface); + + BM_ITER_ELEM(l, &liter, f, BM_LOOPS_OF_FACE) { + BMLoop *l_a = NULL; + BMLoop *l_b = NULL; + BMLoop *l_a_other = NULL; + BMLoop *l_b_other = NULL; + BMLoop *l_iter = NULL; + BMLoop *l_shared = NULL; + + BM_elem_attrs_copy(bm, bm, l, l4); + + l2 = l->next; + l3 = l4->next; + + nface2 = BM_face_create_quad_tri(bm, l->v, l2->v, l3->v, l4->v, f, false); + + if (UNLIKELY(nface2 == NULL)) { + BMO_error_raise(bm, op, BMERR_MESH_ERROR, "Inset failed: could not create an outer face."); + BLI_array_free(edges); + BLI_array_free(verts); + BLI_array_free(eiinfo_arr); + return; + } + + BMO_elem_flag_enable(bm, nface2, ELE_NEW); + BM_elem_flag_enable(nface2, BM_ELEM_TAG); + + /* Copy Loop Data */ + l_a = BM_FACE_FIRST_LOOP(nface2); + l_b = l_a->next; + + l_iter = l; + + /* 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 nface2 shares an edge with f */ + do { + if (l_iter->f != f && l_iter->f != nface2) { + l_shared = l_iter; + break; + } + } while ( (l_iter = l_iter->radial_next) != l); + + 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, l_b); + BM_elem_attrs_copy(bm, bm, l->next, l_a); + + l4 = BM_iter_step(&liter2); + } + } + + /* Delete all old faces */ + BMO_op_callf(bm, op->flag,"delete geom=%ff context=%i", ELE_DEL, DEL_ONLYFACES); + + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, ELE_NEW); + + BLI_array_free(verts); + BLI_array_free(edges); + BLI_array_free(eiinfo_arr); +} + +/** * implementation is as follows... * * - set all faces as tagged/untagged based on selection. Index: source/blender/editors/mesh/editmesh_inset.c =================================================================== --- source/blender/editors/mesh/editmesh_inset.c (revision 55827) +++ source/blender/editors/mesh/editmesh_inset.c (working copy) @@ -80,7 +80,7 @@ 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 @@ 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 @@ 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,17 @@ EDBM_redo_state_restore(opdata->mesh_backup, em, false); } - EDBM_op_init(em, &bmop, op, + 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 +417,19 @@ } } 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 +468,5 @@ 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"); }