Fix T41445: Inset creates separated UV's

This commit is contained in:
Campbell Barton
2014-09-09 18:54:50 +10:00
parent 03c889683a
commit 1abcbe775b

View File

@@ -39,6 +39,9 @@
#include "intern/bmesh_operators_private.h" /* own include */
/* Merge loop-data that diverges, see: T41445 */
#define USE_LOOP_CUSTOMDATA_MERGE
#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 */
@@ -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");
#ifdef USE_LOOP_CUSTOMDATA_MERGE
const bool has_math_ldata = (use_interpolate && CustomData_has_math(&bm->ldata));
#endif
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);
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;
/**
* 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)];
@@ -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);
#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);