Fix T41445: Inset creates separated UV's
authorCampbell Barton <ideasman42@gmail.com>
Tue, 9 Sep 2014 08:54:50 +0000 (18:54 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Tue, 9 Sep 2014 08:54:50 +0000 (18:54 +1000)
source/blender/bmesh/operators/bmo_inset.c

index f2e5ebad9c8dfa86e5a2291dfffe84f7087a4961..d1d7129133d8721d214dc1c190888d029509b4f0 100644 (file)
@@ -39,6 +39,9 @@
 
 #include "intern/bmesh_operators_private.h" /* own include */
 
 
 #include "intern/bmesh_operators_private.h" /* own include */
 
+/* Merge loop-data that diverges, see: T41445 */
+#define USE_LOOP_CUSTOMDATA_MERGE
+
 #define ELE_NEW                1
 
 
 #define ELE_NEW                1
 
 
@@ -106,6 +109,155 @@ static void bm_interp_face_free(InterpFace *iface, BMesh *bm)
        }
 }
 
        }
 }
 
+#ifdef USE_LOOP_CUSTOMDATA_MERGE
+/**
+ * This function merges loop customdata (UV's)
+ * where interpolating the values across the face causes values to diverge.
+ */
+static void bm_loop_customdata_merge(
+        BMesh *bm,
+        BMEdge *e_connect,
+        BMLoop *l_a_outer, BMLoop *l_b_outer,
+        BMLoop *l_a_inner, BMLoop *l_b_inner)
+{
+       /**
+        * Check for diverged values at the vert shared by
+        * \a l_a_inner & \a l_b_inner.
+        *
+        * <pre>
+        *  -----------------------+
+        *           l_a_outer--> /|<--l_b_outer
+        *                       / |
+        *      (face a)        /  |
+        *                     / <--e_connect
+        *                    /    |
+        * e_a  l_a_inner--> / <--l_b_inner
+        * -----------------+      |
+        *                 /|      |
+        * l_a/b_inner_inset| (face b)
+        *               /  |      |
+        *              /   |e_b   |
+        *  (inset face(s)) |      |
+        *            /     |      |
+        * </pre>
+        */
+
+       const bool is_flip = (l_a_inner->next == l_a_outer);
+       BMLoop *l_a_inner_inset, *l_b_inner_inset;
+       BMEdge *e_a, *e_b;
+       int layer_n;
+
+       /* paranoid sanity checks */
+       BLI_assert(l_a_outer->v == l_b_outer->v);
+       BLI_assert(l_a_inner->v == l_b_inner->v);
+
+       BLI_assert(l_b_inner->f != l_a_inner->f);
+
+       BLI_assert(l_a_outer->f == l_a_inner->f);
+       BLI_assert(l_b_outer->f == l_b_inner->f);
+
+       BLI_assert(BM_edge_in_face(e_connect, l_a_inner->f));
+       BLI_assert(BM_edge_in_face(e_connect, l_b_inner->f));
+
+       if (is_flip) {
+               e_a = l_a_inner->prev->e;
+               e_b = l_b_inner->e;
+       }
+       else {
+               e_a = l_a_inner->e;
+               e_b = l_b_inner->prev->e;
+       }
+
+       l_a_inner_inset = BM_edge_other_loop(e_a, l_a_inner);
+       l_b_inner_inset = BM_edge_other_loop(e_b, l_b_inner);
+       BLI_assert(l_a_inner_inset->v == l_b_inner_inset->v);
+
+       /* check if ther is no chance of diversion */
+       if (l_a_inner_inset->f == l_b_inner_inset->f) {
+               return;
+       }
+
+       for (layer_n = 0; layer_n < bm->ldata.totlayer; layer_n++) {
+               const int type = bm->ldata.layers[layer_n].type;
+               const int offset = bm->ldata.layers[layer_n].offset;
+               if (!CustomData_layer_has_math(&bm->ldata, layer_n))
+                       continue;
+
+               /* check we begin with merged data */
+               if ((CustomData_data_equals(
+                        type,
+                        BM_ELEM_CD_GET_VOID_P(l_a_outer, offset),
+                        BM_ELEM_CD_GET_VOID_P(l_b_outer, offset))  == true)
+
+               /* epsilon for comparing UV's is too big, gives noticable problems */
+#if 0
+                   &&
+                   /* check if the data ends up diverged */
+                   (CustomData_data_equals(
+                        type,
+                        BM_ELEM_CD_GET_VOID_P(l_a_inner, offset),
+                        BM_ELEM_CD_GET_VOID_P(l_b_inner, offset)) == false)
+#endif
+                   )
+               {
+                       /* no need to allocate a temp block:
+                        * a = (a + b);
+                        * a *= 0.5f;
+                        * b = a;
+                        */
+                       const void *data_src;
+
+                       CustomData_data_add(
+                               type,
+                               BM_ELEM_CD_GET_VOID_P(l_a_inner_inset, offset),
+                               BM_ELEM_CD_GET_VOID_P(l_b_inner_inset, offset));
+                       CustomData_data_multiply(
+                               type,
+                               BM_ELEM_CD_GET_VOID_P(l_a_inner_inset, offset),
+                               0.5f);
+                       CustomData_data_copy_value(
+                               type,
+                               BM_ELEM_CD_GET_VOID_P(l_a_inner_inset, offset),
+                               BM_ELEM_CD_GET_VOID_P(l_b_inner_inset, offset));
+
+                       /* use this as a reference (could be 'l_b_inner_inset' too) */
+                       data_src = BM_ELEM_CD_GET_VOID_P(l_a_inner_inset, offset);
+
+                       /* check if the 2 faces share an edge */
+                       if (is_flip ?
+                           (l_b_inner_inset->e == l_a_inner_inset->prev->e) :
+                           (l_a_inner_inset->e == l_b_inner_inset->prev->e))
+                       {
+                               /* simple case, we have all loops already */
+                       }
+                       else {
+                               /* compare with (l_a_inner / l_b_inner) and assign the blended value if they match */
+                               BMIter iter;
+                               BMLoop *l_iter;
+                               const void *data_cmp_a = BM_ELEM_CD_GET_VOID_P(l_b_inner, offset);
+                               const void *data_cmp_b = BM_ELEM_CD_GET_VOID_P(l_a_inner, offset);
+                               BM_ITER_ELEM (l_iter, &iter, l_a_inner_inset->v, BM_LOOPS_OF_VERT) {
+                                       if (BM_elem_flag_test(l_iter->f, BM_ELEM_TAG)) {
+                                               if (!ELEM(l_iter, l_a_inner, l_b_inner, l_a_inner_inset, l_b_inner_inset)) {
+                                                       void *data_dst = BM_ELEM_CD_GET_VOID_P(l_iter, offset);
+
+                                                       if (CustomData_data_equals(type, data_dst, data_cmp_a) ||
+                                                           CustomData_data_equals(type, data_dst, data_cmp_b))
+                                                       {
+                                                               CustomData_data_copy_value(type, data_src, data_dst);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       CustomData_data_copy_value(type, data_src, BM_ELEM_CD_GET_VOID_P(l_b_inner, offset));
+                       CustomData_data_copy_value(type, data_src, BM_ELEM_CD_GET_VOID_P(l_a_inner, offset));
+               }
+       }
+}
+#endif  /* USE_LOOP_CUSTOMDATA_MERGE */
+
 
 /* -------------------------------------------------------------------- */
 /* Inset Individual */
 
 /* -------------------------------------------------------------------- */
 /* Inset Individual */
@@ -395,6 +547,9 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
        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");
        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");
+#ifdef USE_LOOP_CUSTOMDATA_MERGE
+       const bool has_math_ldata      = (use_interpolate && CustomData_has_math(&bm->ldata));
+#endif
 
        int edge_info_len = 0;
 
 
        int edge_info_len = 0;
 
@@ -903,10 +1058,34 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                        BM_elem_attrs_copy(bm, bm, l_a_other, l_a);
                        BM_elem_attrs_copy(bm, bm, l_b_other, l_b);
 
                        BM_elem_attrs_copy(bm, bm, l_a_other, l_a);
                        BM_elem_attrs_copy(bm, bm, l_b_other, l_b);
 
+                       BLI_assert(l_a->f != l_a_other->f);
+                       BLI_assert(l_b->f != l_b_other->f);
+
                        /* 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;
 
                        /* 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;
 
+                       /**
+                        * Loops vars from newly created face (face_a/b)
+                        * <pre>
+                        *              l_a->e & l_b->prev->e
+                        * +------------------------------------+
+                        * |\ l_a                          l_b /|
+                        * | \ l_a->prev->e            l_b->e / |
+                        * |  \ l_a->prev          l_b->next /  |
+                        * |   +----------------------------+   |
+                        * |   |l_a_other    ^     l_b_other|   |
+                        * |   |        l_b->next->e &...   |   |
+                        * |   |        l_a->prev->prev->e  |   |
+                        * |   |        (inset face)        |   |
+                        * |   +----------------------------+   |
+                        * |  /                              \  |
+                        * | /                                \ |
+                        * |/                                  \|
+                        * +------------------------------------+
+                        * </pre>
+                        */
+
                        /* swap a<->b intentionally */
                        if (use_interpolate) {
                                InterpFace *iface = iface_array[BM_elem_index_get(es->l->f)];
                        /* swap a<->b intentionally */
                        if (use_interpolate) {
                                InterpFace *iface = iface_array[BM_elem_index_get(es->l->f)];
@@ -914,6 +1093,31 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                                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);
                                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);
+
+#ifdef USE_LOOP_CUSTOMDATA_MERGE
+                               if (has_math_ldata) {
+                                       BMEdge *e_connect;
+
+                                       /* connecting edge 'a' */
+                                       e_connect = l_a->prev->e;
+                                       if (BM_edge_is_manifold(e_connect)) {
+                                               bm_loop_customdata_merge(
+                                                       bm, e_connect,
+                                                       l_a,       BM_edge_other_loop(e_connect, l_a),
+                                                       l_a->prev, BM_edge_other_loop(e_connect, l_a->prev));
+                                       }
+
+                                       /* connecting edge 'b' */
+                                       e_connect = l_b->e;
+                                       if (BM_edge_is_manifold(e_connect)) {
+                                               /* swap arg order to maintain winding */
+                                               bm_loop_customdata_merge(
+                                                       bm, e_connect,
+                                                       l_b,       BM_edge_other_loop(e_connect, l_b),
+                                                       l_b->next, BM_edge_other_loop(e_connect, l_b->next));
+                                       }
+                               }
+#endif  /* USE_LOOP_CUSTOMDATA_MERGE */
                        }
                        else {
                                BM_elem_attrs_copy(bm, bm, l_a_other, l_b);
                        }
                        else {
                                BM_elem_attrs_copy(bm, bm, l_a_other, l_b);