Merging r59182 through r59257 from trunk into soc-2013-depsgraph_mt
[blender.git] / source / blender / bmesh / operators / bmo_inset.c
index ee23dc8c36200a70f34a632787b1c05860cc8358..731b895a6b5221d69261854c6fb43cd15f2f462c 100644 (file)
  * ***** END GPL LICENSE BLOCK *****
  */
 
+/** \file blender/bmesh/operators/bmo_inset.c
+ *  \ingroup bmesh
+ *
+ * Inset face regions.
+ * Inset individual faces.
+ *
+ */
+
 #include "MEM_guardedalloc.h"
 
 #include "BLI_math.h"
+#include "BLI_array.h"
+#include "BLI_memarena.h"
+#include "BKE_customdata.h"
 
 #include "bmesh.h"
 
 
 #define ELE_NEW                1
 
+
+
+/* -------------------------------------------------------------------- */
+/* Inset Individual */
+
+
+/* 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");
+       const bool use_relative_offset = BMO_slot_bool_get(op->slots_in, "use_relative_offset");
+       const bool use_interpolate = BMO_slot_bool_get(op->slots_in, "use_interpolate");
+
+       /* 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) {
+               BMFace *f_new_inner;
+               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 */
+                       if (use_relative_offset) {
+                               mul_v3_fl(tvec, (BM_edge_calc_length(l_iter->e) + BM_edge_calc_length(l_iter->prev->e)) / 2.0f);
+                       }
+
+                       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);
+
+
+               /* Create New Inset Faces */
+               f_new_inner = BM_face_create(bm, f_verts, f_edges, f->len, 0);
+               BLI_assert(f_new_inner != NULL);  /* no reason it should fail */
+
+
+               // Don't tag, gives more useful inner/outer select option
+               // BMO_elem_flag_enable(bm, f_new_inner, ELE_NEW);
+
+
+               /* Copy Face Data */
+               /* interpolate loop data or just stretch */
+               if (use_interpolate) {
+                       BM_face_interp_from_face(bm, f_new_inner, f, true);
+               }
+               else {
+                       BM_elem_attrs_copy(bm, bm, f, f_new_inner);
+               }
+
+               l_iter_inner = BM_FACE_FIRST_LOOP(f_new_inner);
+               l_iter = l_first;
+               do {
+                       BMFace *f_new_outer;
+
+                       BMLoop *l_a;
+                       BMLoop *l_b;
+
+                       if (use_interpolate == false) {
+                               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);
+
+                       BLI_assert(f_new_outer != NULL);  /* no reason it should fail */
+
+                       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;
+
+                       /* first pair */
+                       BM_elem_attrs_copy(bm, bm, l_iter, l_a);
+                       BM_elem_attrs_copy(bm, bm,  l_iter->next, 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_inner, l_b);
+                       BM_elem_attrs_copy(bm, bm, use_interpolate ? l_iter_inner->next : 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;
        BMEdge *e_old;
        BMEdge *e_new;
+       BMLoop *l;
 } SplitEdgeInfo;
 
-static void edge_loop_tangent(BMEdge *e, BMLoop *e_loop, float r_no[3])
+
+/**
+ * Interpolation, this is more complex for regions since we're not creating new faces
+ * and throwing away old ones, so instead, store face data needed for interpolation.
+ *
+ * \note This uses CustomData functions in quite a low-level way which should be
+ * avoided, but in this case its hard to do without storing a duplicate mesh. */
+
+/* just enough of a face to store interpolation data we can use once the inset is done */
+typedef struct InterpFace {
+       BMFace *f;
+       void **blocks_l;
+       void **blocks_v;
+       float (*cos_2d)[2];
+       float axis_mat[3][3];
+} InterpFace;
+
+/* basically a clone of #BM_vert_interp_from_face */
+static void bm_interp_face_store(InterpFace *iface, BMesh *bm, BMFace *f, MemArena *interp_arena)
+{
+       BMLoop *l_iter, *l_first;
+       void **blocks_l    = iface->blocks_l = BLI_memarena_alloc(interp_arena, sizeof(*iface->blocks_l) * f->len);
+       void **blocks_v    = iface->blocks_v = BLI_memarena_alloc(interp_arena, sizeof(*iface->blocks_v) * f->len);
+       float (*cos_2d)[2] = iface->cos_2d = BLI_memarena_alloc(interp_arena, sizeof(*iface->cos_2d) * f->len);
+       void *axis_mat     = iface->axis_mat;
+       int i;
+
+       BLI_assert(BM_face_is_normal_valid(f));
+
+       axis_dominant_v3_to_m3(axis_mat, f->no);
+
+       iface->f = f;
+
+       i = 0;
+       l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+       do {
+               mul_v2_m3v3(cos_2d[i], axis_mat, l_iter->v->co);
+               blocks_l[i] = NULL;
+               CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l_iter->head.data, &blocks_l[i]);
+               /* if we were not modifying the loops later we would do... */
+               // blocks[i] = l_iter->head.data;
+
+               blocks_v[i] = NULL;
+               CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, l_iter->v->head.data, &blocks_v[i]);
+
+               /* use later for index lookups */
+               BM_elem_index_set(l_iter, i); /* set_ok */
+       } while (i++, (l_iter = l_iter->next) != l_first);
+}
+static void bm_interp_face_free(InterpFace *iface, BMesh *bm)
 {
-       float tvec[3];
-       BMVert *v1, *v2;
-       BM_edge_ordered_verts_ex(e, &v1, &v2, e_loop);
+       void **blocks_l = iface->blocks_l;
+       void **blocks_v = iface->blocks_v;
+       int i;
 
-       sub_v3_v3v3(tvec, v1->co, v2->co); /* use for temp storage */
-       cross_v3_v3v3(r_no, tvec, e_loop->f->no);
-       normalize_v3(r_no);
+       for (i = 0; i < iface->f->len; i++) {
+               CustomData_bmesh_free_block(&bm->ldata, &blocks_l[i]);
+               CustomData_bmesh_free_block(&bm->vdata, &blocks_v[i]);
+       }
+}
+
+
+/**
+ * return the tag loop where there is...
+ * - only 1 tagged face attached to this edge.
+ * - 1 or more untagged faces.
+ *
+ * \note this function looks to be expensive
+ * but in most cases it will only do 2 iterations.
+ */
+static BMLoop *bm_edge_is_mixed_face_tag(BMLoop *l)
+{
+       if (LIKELY(l != NULL)) {
+               int tot_tag = 0;
+               int tot_untag = 0;
+               BMLoop *l_iter;
+               BMLoop *l_tag = NULL;
+               l_iter = l;
+               do {
+                       if (BM_elem_flag_test(l_iter->f, BM_ELEM_TAG)) {
+                               /* more than one tagged face - bail out early! */
+                               if (tot_tag == 1) {
+                                       return NULL;
+                               }
+                               l_tag = l_iter;
+                               tot_tag++;
+                       }
+                       else {
+                               tot_untag++;
+                       }
+
+               } while ((l_iter = l_iter->radial_next) != l);
+
+               return ((tot_tag == 1) && (tot_untag >= 1)) ? l_tag : NULL;
+       }
+       else {
+               return NULL;
+       }
 }
 
 /**
- * functionality is as follows
+ * implementation is as follows...
  *
  * - set all faces as tagged/untagged based on selection.
  * - find all edges that have 1 tagged, 1 untagged face.
  * - separate these edges and tag vertices, set their index to point to the original edge.
  * - build faces between old/new edges.
  * - inset the new edges into their faces.
- *
- * TODO
- * - close tares when 2 corners touch.
  */
 
-void bmo_inset_exec(BMesh *bm, BMOperator *op)
+void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
 {
-       const int use_even_offset     = BMO_slot_bool_get(op, "use_even_offset");
-       const int use_even_boundry    = use_even_offset; /* could make own option */
-       const int use_relative_offset = BMO_slot_bool_get(op, "use_relative_offset");
-       const float thickness = BMO_slot_float_get(op, "thickness");
+       const bool use_outset          = BMO_slot_bool_get(op->slots_in, "use_outset");
+       const bool use_boundary        = BMO_slot_bool_get(op->slots_in, "use_boundary") && (use_outset == false);
+       const bool use_even_offset     = BMO_slot_bool_get(op->slots_in, "use_even_offset");
+       const bool use_even_boundry    = use_even_offset; /* could make own option */
+       const bool use_relative_offset = BMO_slot_bool_get(op->slots_in, "use_relative_offset");
+       const bool use_interpolate     = BMO_slot_bool_get(op->slots_in, "use_interpolate");
+       const float thickness          = BMO_slot_float_get(op->slots_in, "thickness");
+       const float depth              = BMO_slot_float_get(op->slots_in, "depth");
 
        int edge_info_len = 0;
 
@@ -74,20 +377,42 @@ void bmo_inset_exec(BMesh *bm, BMOperator *op)
        SplitEdgeInfo *edge_info;
        SplitEdgeInfo *es;
 
+       /* Interpolation Vars */
+       /* an array alligned with faces but only fill items which are used. */
+       InterpFace **iface_array = NULL;
+       int          iface_array_len;
+       MemArena *interp_arena = NULL;
+
        BMVert *v;
        BMEdge *e;
        BMFace *f;
        int i, j, k;
 
-       BM_mesh_elem_flag_disable_all(bm, BM_FACE, BM_ELEM_TAG);
-       BMO_slot_buffer_hflag_enable(bm, op, "faces", BM_ELEM_TAG, BM_FACE, FALSE);
+       if (use_interpolate) {
+               interp_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+               /* warning, we could be more clever here and not over alloc */
+               iface_array = MEM_callocN(sizeof(*iface_array) * bm->totface, __func__);
+               iface_array_len = bm->totface;
+       }
+
+       if (use_outset == false) {
+               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);
+       }
+       else {
+               BM_mesh_elem_hflag_enable_all(bm, BM_FACE, BM_ELEM_TAG, false);
+               BMO_slot_buffer_hflag_disable(bm, op->slots_in, "faces", BM_FACE, BM_ELEM_TAG, false);
+       }
 
        /* first count all inset edges we will split */
        /* fill in array and initialize tagging */
-       BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
-               BMLoop *la, *lb;
-               if ((BM_edge_loop_pair(e, &la, &lb)) &&
-                   (BM_elem_flag_test(la->f, BM_ELEM_TAG) != BM_elem_flag_test(lb->f, BM_ELEM_TAG)))
+       BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+               if (
+                   /* tag if boundary is enabled */
+                   (use_boundary && BM_edge_is_boundary(e) && BM_elem_flag_test(e->l->f, BM_ELEM_TAG)) ||
+
+                   /* tag if edge is an interior edge inbetween a tagged and untagged face */
+                   (bm_edge_is_mixed_face_tag(e->l)))
                {
                        /* tag */
                        BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
@@ -111,11 +436,11 @@ void bmo_inset_exec(BMesh *bm, BMOperator *op)
 
        /* fill in array and initialize tagging */
        es = edge_info;
-       BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+       BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
                i = BM_elem_index_get(e);
                if (i != -1) {
                        /* calc edge-split info */
-                       es->length = BM_edge_length_calc(e);
+                       es->length = BM_edge_calc_length(e);
                        es->e_old = e;
                        es++;
                        /* initialize no and e_new after */
@@ -123,17 +448,25 @@ void bmo_inset_exec(BMesh *bm, BMOperator *op)
        }
 
        for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
-               BMLoop *l, *la, *lb;
+               if ((es->l = bm_edge_is_mixed_face_tag(es->e_old->l))) {
+                       /* do nothing */
+               }
+               else {
+                       es->l = es->e_old->l; /* must be a boundary */
+               }
 
-               BM_edge_loop_pair(es->e_old, &la, &lb); /* we know this will succeed, already checked above */
-               l = BM_elem_flag_test(la->f, BM_ELEM_TAG) ? la : lb;
 
                /* run the separate arg */
-               bmesh_edge_separate(bm, es->e_old, l);
+               bmesh_edge_separate(bm, es->e_old, es->l, false);
 
                /* calc edge-split info */
-               es->e_new = l->e;
-               edge_loop_tangent(es->e_new, l, es->no);
+               es->e_new = es->l->e;
+               BM_edge_calc_face_tangent(es->e_new, es->l, es->no);
+
+               if (es->e_new == es->e_old) { /* happens on boundary edges */
+                       /* take care here, we're creating this double edge which _must_ have its verts replaced later on */
+                       es->e_old = BM_edge_create(bm, es->e_new->v1, es->e_new->v2, es->e_new, 0);
+               }
 
                /* store index back to original in 'edge_info' */
                BM_elem_index_set(es->e_new, i);
@@ -142,9 +475,31 @@ void bmo_inset_exec(BMesh *bm, BMOperator *op)
                /* important to tag again here */
                BM_elem_flag_enable(es->e_new->v1, BM_ELEM_TAG);
                BM_elem_flag_enable(es->e_new->v2, BM_ELEM_TAG);
-       }
 
 
+               /* initialize interpolation vars */
+               /* this could go in its own loop,
+                * only use the 'es->l->f' so we don't store loops for faces which have no mixed selection
+                *
+                * note: faces on the other side of the inset will be interpolated too since this is hard to
+                * detect, just allow it even though it will cause some redundant interpolation */
+               if (use_interpolate) {
+                       BMIter viter;
+                       BM_ITER_ELEM (v, &viter, es->l->e, BM_VERTS_OF_EDGE) {
+                               BMIter fiter;
+                               BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) {
+                                       const int j = BM_elem_index_get(f);
+                                       if (iface_array[j] == NULL) {
+                                               InterpFace *iface = BLI_memarena_alloc(interp_arena, sizeof(*iface));
+                                               bm_interp_face_store(iface, bm, f, interp_arena);
+                                               iface_array[j] = iface;
+                                       }
+                               }
+                       }
+               }
+               /* done interpolation */
+       }
+
        /* show edge normals for debugging */
 #if 0
        for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
@@ -156,7 +511,7 @@ void bmo_inset_exec(BMesh *bm, BMOperator *op)
                v1 = BM_vert_create(bm, tvec, NULL);
                v2 = BM_vert_create(bm, tvec, NULL);
                madd_v3_v3fl(v2->co, es->no, 0.1f);
-               BM_edge_create(bm, v1, v2, NULL, FALSE);
+               BM_edge_create(bm, v1, v2, NULL, 0);
        }
 #endif
 
@@ -166,42 +521,46 @@ void bmo_inset_exec(BMesh *bm, BMOperator *op)
        for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
                for (j = 0; j < 2; j++) {
                        v = (j == 0) ? es->e_new->v1 : es->e_new->v2;
-                       /* end confusinug part - just pretend this is a typical loop on verts */
 
+                       /* end confusing part - just pretend this is a typical loop on verts */
 
                        /* only split of tagged verts - used by separated edges */
 
                        /* comment the first part because we know this verts in a tagged face */
                        if (/* v->e && */BM_elem_flag_test(v, BM_ELEM_TAG)) {
                                BMVert **vout;
-                               int r_vout_len = 0;
+                               int r_vout_len;
+                               BMVert *v_glue = NULL;
 
-                               bmesh_vert_separate(bm, v, &vout, &r_vout_len);
+                               /* disable touching twice, this _will_ happen if the flags not disabled */
+                               BM_elem_flag_disable(v, BM_ELEM_TAG);
+
+                               bmesh_vert_separate(bm, v, &vout, &r_vout_len, false);
                                v = NULL; /* don't use again */
 
+                               /* in some cases the edge doesn't split off */
+                               if (r_vout_len == 1) {
+                                       MEM_freeN(vout);
+                                       continue;
+                               }
+
                                for (k = 0; k < r_vout_len; k++) {
                                        BMVert *v_split = vout[k]; /* only to avoid vout[k] all over */
 
                                        /* need to check if this vertex is from a */
-                                       BMIter itersub;
                                        int vert_edge_tag_tot = 0;
                                        int vecpair[2];
 
                                        /* find adjacent */
-                                       BM_ITER(e, &itersub, bm, BM_EDGES_OF_VERT, v_split) {
-                                               if (BM_edge_is_boundary(e) && /* this will be true because bmesh_edge_separate() has run */
-                                                   BM_elem_flag_test(e, BM_ELEM_TAG) &&
-                                                   BM_elem_flag_test(e->l->f, BM_ELEM_TAG))
+                                       BM_ITER_ELEM (e, &iter, v_split, BM_EDGES_OF_VERT) {
+                                               if (BM_elem_flag_test(e, BM_ELEM_TAG) &&
+                                                   e->l && BM_elem_flag_test(e->l->f, BM_ELEM_TAG))
                                                {
-                                                       /* disable touching twice, this _will_ happen if the flags not disabled */
-                                                       BM_elem_flag_disable(v_split, BM_ELEM_TAG);
-
                                                        if (vert_edge_tag_tot < 2) {
                                                                vecpair[vert_edge_tag_tot] = BM_elem_index_get(e);
                                                                BLI_assert(vecpair[vert_edge_tag_tot] != -1);
                                                        }
 
-                                                       // BM_elem_flag_disable(e, BM_ELEM_SMOOTH); // testing only
                                                        vert_edge_tag_tot++;
                                                }
                                        }
@@ -210,15 +569,59 @@ void bmo_inset_exec(BMesh *bm, BMOperator *op)
                                                float tvec[3];
 
                                                if (vert_edge_tag_tot >= 2) { /* 2 edge users - common case */
-                                                       const float *e_no_a = edge_info[vecpair[0]].no;
-                                                       const float *e_no_b = edge_info[vecpair[1]].no;
+                                                       /* now there are 2 cases to check for,
+                                                        *
+                                                        * if both edges use the same face OR both faces have the same normal,
+                                                        * ...then we can calculate an edge that fits nicely between the 2 edge normals.
+                                                        *
+                                                        * Otherwise use the shared edge OR the corner defined by these 2 face normals,
+                                                        * when both edges faces are adjacent this works best but even when this vertex
+                                                        * fans out faces it should work ok.
+                                                        */
+
+                                                       SplitEdgeInfo *e_info_a = &edge_info[vecpair[0]];
+                                                       SplitEdgeInfo *e_info_b = &edge_info[vecpair[1]];
+
+                                                       BMFace *f_a = e_info_a->l->f;
+                                                       BMFace *f_b = e_info_b->l->f;
+
+                                                       /* we use this as either the normal OR to find the right direction for the
+                                                        * cross product between both face normals */
+                                                       add_v3_v3v3(tvec, e_info_a->no, e_info_b->no);
+
+                                                       /* epsilon increased to fix [#32329] */
+                                                       if ((f_a == f_b) || compare_v3v3(f_a->no, f_b->no, 0.001f)) {
+                                                               normalize_v3(tvec);
+                                                       }
+                                                       else {
+                                                               /* these lookups are very quick */
+                                                               BMLoop *l_other_a = BM_loop_other_vert_loop(e_info_a->l, v_split);
+                                                               BMLoop *l_other_b = BM_loop_other_vert_loop(e_info_b->l, v_split);
+
+                                                               if (l_other_a->v == l_other_b->v) {
+                                                                       /* both edges faces are adjacent, but we don't need to know the shared edge
+                                                                        * having both verts is enough. */
+                                                                       sub_v3_v3v3(tvec, l_other_a->v->co, v_split->co);
+                                                               }
+                                                               else {
+                                                                       /* faces don't touch,
+                                                                        * just get cross product of their normals, its *good enough*
+                                                                        */
+                                                                       float tno[3];
+                                                                       cross_v3_v3v3(tno, f_a->no, f_b->no);
+                                                                       if (dot_v3v3(tvec, tno) < 0.0f) {
+                                                                               negate_v3(tno);
+                                                                       }
+                                                                       copy_v3_v3(tvec, tno);
+                                                               }
 
-                                                       add_v3_v3v3(tvec, e_no_a, e_no_b);
-                                                       normalize_v3(tvec);
+                                                               normalize_v3(tvec);
+                                                       }
 
                                                        /* scale by edge angle */
                                                        if (use_even_offset) {
-                                                               mul_v3_fl(tvec, shell_angle_to_dist(angle_normalized_v3v3(e_no_a, e_no_b) / 2.0f));
+                                                               mul_v3_fl(tvec, shell_angle_to_dist(angle_normalized_v3v3(e_info_a->no,
+                                                                                                                         e_info_b->no) / 2.0f));
                                                        }
 
                                                        /* scale relative to edge lengths */
@@ -234,24 +637,24 @@ void bmo_inset_exec(BMesh *bm, BMOperator *op)
                                                                /* This case where only one edge attached to v_split
                                                                 * is used - ei - the face to inset is on a boundary.
                                                                 *
-                                 *                  We want the inset to align flush with the
-                                 *                  boundary edge, not the normal of the interior
-                                 *             <--- edge which would give an unsligtly bump.
-                                 * --+-------------------------+---------------+--
-                                 *   |^v_other    ^e_other    /^v_split        |
-                                 *   |                       /                 |
-                                 *   |                      /                  |
-                                 *   |                     / <- tag split edge |
-                                 *   |                    /                    |
-                                 *   |                   /                     |
-                                 *   |                  /                      |
-                                 * --+-----------------+-----------------------+--
-                                 *   |                                         |
-                                 *   |                                         |
-                                 *
-                                 * note, the fact we are doing location comparisons on verts that are moved about
-                                 * doesnt matter becaise the direction will remain the same in this case.
-                                 */
+                                                                *                  We want the inset to align flush with the
+                                                                *                  boundary edge, not the normal of the interior
+                                                                *             <--- edge which would give an unsightly bump.
+                                                                * --+-------------------------+---------------+--
+                                                                *   |^v_other    ^e_other    /^v_split        |
+                                                                *   |                       /                 |
+                                                                *   |                      /                  |
+                                                                *   |                     / <- tag split edge |
+                                                                *   |                    /                    |
+                                                                *   |                   /                     |
+                                                                *   |                  /                      |
+                                                                * --+-----------------+-----------------------+--
+                                                                *   |                                         |
+                                                                *   |                                         |
+                                                                *
+                                                                * note, the fact we are doing location comparisons on verts that are moved about
+                                                                * doesn't matter because the direction will remain the same in this case.
+                                                                */
 
                                                                BMEdge *e_other;
                                                                BMVert *v_other;
@@ -308,40 +711,202 @@ void bmo_inset_exec(BMesh *bm, BMOperator *op)
                                                        zero_v3(tvec);
                                                }
 
+                                               /* apply the offset */
                                                madd_v3_v3fl(v_split->co, tvec, thickness);
                                        }
+
+                                       /* this saves expensive/slow glue check for common cases */
+                                       if (r_vout_len > 2) {
+                                               bool ok = true;
+                                               /* last step, NULL this vertex if has a tagged face */
+                                               BM_ITER_ELEM (f, &iter, v_split, BM_FACES_OF_VERT) {
+                                                       if (BM_elem_flag_test(f, BM_ELEM_TAG)) {
+                                                               ok = false;
+                                                               break;
+                                                       }
+                                               }
+
+                                               if (ok) {
+                                                       if (v_glue == NULL) {
+                                                               v_glue = v_split;
+                                                       }
+                                                       else {
+                                                               BM_vert_splice(bm, v_split, v_glue);
+                                                       }
+                                               }
+                                       }
+                                       /* end glue */
+
                                }
                                MEM_freeN(vout);
                        }
                }
        }
 
-       /* create faces */
-       es = edge_info;
-       for (j = 0; j < edge_info_len; j++) {
-               BMVert *v1, *v2, *v3, *v4;
+       if (use_interpolate) {
+               for (i = 0; i < iface_array_len; i++) {
+                       if (iface_array[i]) {
+                               InterpFace *iface = iface_array[i];
+                               BM_face_interp_from_face_ex(bm, iface->f, iface->f, true,
+                                                           iface->blocks_l, iface->blocks_v, iface->cos_2d, iface->axis_mat);
+                       }
+               }
+       }
 
+       /* create faces */
+       for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
+               BMVert *varr[4] = {NULL};
                /* get the verts in the correct order */
-               BM_edge_ordered_verts(es->e_new, &v1, &v2);
-               if (v1 == es->e_new->v1) {
-                       v3 = es->e_old->v2;
-                       v4 = es->e_old->v1;
+               BM_edge_ordered_verts_ex(es->e_new, &varr[1], &varr[0], es->l);
+#if 0
+               if (varr[0] == es->e_new->v1) {
+                       varr[2] = es->e_old->v2;
+                       varr[3] = es->e_old->v1;
                }
                else {
-                       v3 = es->e_old->v1;
-                       v4 = es->e_old->v2;
+                       varr[2] = es->e_old->v1;
+                       varr[3] = es->e_old->v2;
+               }
+               j = 4;
+#else
+               /* slightly trickier check - since we can't assume the verts are split */
+               j = 2; /* 2 edges are set */
+               if (varr[0] == es->e_new->v1) {
+                       if (es->e_old->v2 != es->e_new->v2) { varr[j++] = es->e_old->v2; }
+                       if (es->e_old->v1 != es->e_new->v1) { varr[j++] = es->e_old->v1; }
+               }
+               else {
+                       if (es->e_old->v1 != es->e_new->v1) { varr[j++] = es->e_old->v1; }
+                       if (es->e_old->v2 != es->e_new->v2) { varr[j++] = es->e_old->v2; }
                }
 
-               /* no need to check doubles, we KNOW there wont be any */
-               /* yes - reverse face is correct in fhis case */
-               f = BM_face_create_quad_tri(bm, v4, v3, v2, v1, es->e_new->l->f, FALSE);
+               if (j == 2) {
+                       /* can't make face! */
+                       continue;
+               }
+#endif
+               /* no need to check doubles, we KNOW there won't be any */
+               /* yes - reverse face is correct in this case */
+               f = BM_face_create_quad_tri_v(bm, varr, j, es->l->f, false);
                BMO_elem_flag_enable(bm, f, ELE_NEW);
 
-               es++;
+               /* copy for loop data, otherwise UV's and vcols are no good.
+                * tiny speedup here we could be more clever and copy from known adjacent data
+                * also - we could attempt to interpolate the loop data, this would be much slower but more useful too */
+#if 0
+               /* don't use this because face boundaries have no adjacent loops and won't be filled in.
+                * instead copy from the opposite side with the code below */
+               BM_face_copy_shared(bm, f, NULL, NULL);
+#else
+               {
+                       /* 2 inner loops on the edge between the new face and the original */
+                       BMLoop *l_a;
+                       BMLoop *l_b;
+                       BMLoop *l_a_other;
+                       BMLoop *l_b_other;
+
+                       l_a = BM_FACE_FIRST_LOOP(f);
+                       l_b = l_a->next;
+
+                       /* we know this side has a radial_next because of the order of created verts in the quad */
+                       l_a_other = BM_edge_other_loop(l_a->e, l_a);
+                       l_b_other = BM_edge_other_loop(l_a->e, l_b);
+                       BM_elem_attrs_copy(bm, bm, l_a_other, l_a);
+                       BM_elem_attrs_copy(bm, bm, l_b_other, l_b);
+
+                       /* step around to the opposite side of the quad - warning, this may have no other edges! */
+                       l_a = l_a->next->next;
+                       l_b = l_a->next;
+
+                       /* swap a<->b intentionally */
+                       if (use_interpolate) {
+                               InterpFace *iface = iface_array[BM_elem_index_get(es->l->f)];
+                               const int i_a = BM_elem_index_get(l_a_other);
+                               const int i_b = BM_elem_index_get(l_b_other);
+                               CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, iface->blocks_l[i_a], &l_b->head.data);
+                               CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, iface->blocks_l[i_b], &l_a->head.data);
+                       }
+                       else {
+                               BM_elem_attrs_copy(bm, bm, l_a_other, l_b);
+                               BM_elem_attrs_copy(bm, bm, l_b_other, l_a);
+                       }
+               }
        }
+#endif
 
-       MEM_freeN(edge_info);
+       if (use_interpolate) {
+               for (i = 0; i < iface_array_len; i++) {
+                       if (iface_array[i]) {
+                               bm_interp_face_free(iface_array[i], bm);
+                       }
+               }
+
+               BLI_memarena_free(interp_arena);
+               MEM_freeN(iface_array);
+       }
 
        /* we could flag new edges/verts too, is it useful? */
-       BMO_slot_buffer_from_flag(bm, op, "faceout", ELE_NEW, BM_FACE);
+       BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, ELE_NEW);
+
+       /* cheap feature to add depth to the inset */
+       if (depth != 0.0f) {
+               float (*varr_co)[3];
+               BMOIter oiter;
+
+               /* we need to re-calculate tagged normals, but for this purpose we can copy tagged verts from the
+                * faces they inset from,  */
+               for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
+                       zero_v3(es->e_new->v1->no);
+                       zero_v3(es->e_new->v2->no);
+               }
+               for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
+                       float *no = es->l->f->no;
+                       add_v3_v3(es->e_new->v1->no, no);
+                       add_v3_v3(es->e_new->v2->no, no);
+               }
+               for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
+                       /* annoying, avoid normalizing twice */
+                       if (len_squared_v3(es->e_new->v1->no) != 1.0f) {
+                               normalize_v3(es->e_new->v1->no);
+                       }
+                       if (len_squared_v3(es->e_new->v2->no) != 1.0f) {
+                               normalize_v3(es->e_new->v2->no);
+                       }
+               }
+               /* done correcting edge verts normals */
+
+               /* untag verts */
+               BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false);
+
+               /* tag face verts */
+               BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
+                       BM_ITER_ELEM (v, &iter, f, BM_VERTS_OF_FACE) {
+                               BM_elem_flag_enable(v, BM_ELEM_TAG);
+                       }
+               }
+
+               /* do in 2 passes so moving the verts doesn't feed back into face angle checks
+                * which BM_vert_calc_shell_factor uses. */
+
+               /* over allocate */
+               varr_co = MEM_callocN(sizeof(*varr_co) * bm->totvert, __func__);
+
+               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_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);
+                       }
+               }
+
+               BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
+                       if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
+                               copy_v3_v3(v->co, varr_co[i]);
+                       }
+               }
+               MEM_freeN(varr_co);
+       }
+
+       MEM_freeN(edge_info);
 }