UV: support uv seams when computing uv islands
An edge can be marked BM_ELEM_SEAM, which means the UV co-ordinates on either side of the edge are actually independent, even if they happen to currently have the same value. This commit optionally add support for UV Seams when computing islands. Affects UV sculpt tools, individual origins, UV stitch and changing UV selection modes etc. Required for upcoming packing refactor which requires seam support when computing islands. Differential Revision: https://developer.blender.org/D15875
This commit is contained in:
@@ -139,6 +139,7 @@ struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm,
|
||||
const struct Scene *scene,
|
||||
bool uv_selected,
|
||||
bool use_winding,
|
||||
bool use_seams,
|
||||
bool do_islands);
|
||||
void BM_uv_element_map_free(struct UvElementMap *element_map);
|
||||
struct UvElement *BM_uv_element_get(const struct UvElementMap *map,
|
||||
|
||||
@@ -851,10 +851,99 @@ static void bm_uv_build_islands(UvElementMap *element_map,
|
||||
MEM_SAFE_FREE(map);
|
||||
}
|
||||
|
||||
/* return true if `loop` has UV co-ordinates which match `luv_a` and `luv_b` */
|
||||
static bool loop_uv_match(BMLoop *loop, MLoopUV *luv_a, MLoopUV *luv_b, int cd_loop_uv_offset)
|
||||
{
|
||||
MLoopUV *luv_c = BM_ELEM_CD_GET_VOID_P(loop, cd_loop_uv_offset);
|
||||
MLoopUV *luv_d = BM_ELEM_CD_GET_VOID_P(loop->next, cd_loop_uv_offset);
|
||||
return compare_v2v2(luv_a->uv, luv_c->uv, STD_UV_CONNECT_LIMIT) &&
|
||||
compare_v2v2(luv_b->uv, luv_d->uv, STD_UV_CONNECT_LIMIT);
|
||||
}
|
||||
|
||||
/* Given `anchor` and `edge`, return true if there are edges that fan between them that are
|
||||
* seam-free. */
|
||||
static bool seam_connected_recursive(BMVert *anchor,
|
||||
BMEdge *edge,
|
||||
MLoopUV *luv_anchor,
|
||||
MLoopUV *luv_fan,
|
||||
BMLoop *needle,
|
||||
GSet *visited,
|
||||
int cd_loop_uv_offset)
|
||||
{
|
||||
BLI_assert(edge->v1 == anchor || edge->v2 == anchor);
|
||||
BLI_assert(needle->v == anchor || needle->next->v == anchor);
|
||||
|
||||
if (BM_elem_flag_test(edge, BM_ELEM_SEAM)) {
|
||||
return false; /* Edge is a seam, don't traverse. */
|
||||
}
|
||||
|
||||
if (!BLI_gset_add(visited, edge)) {
|
||||
return false; /* Already visited. */
|
||||
}
|
||||
|
||||
BMLoop *loop;
|
||||
BMIter liter;
|
||||
BM_ITER_ELEM (loop, &liter, edge, BM_LOOPS_OF_EDGE) {
|
||||
if (loop->v == anchor) {
|
||||
if (!loop_uv_match(loop, luv_anchor, luv_fan, cd_loop_uv_offset)) {
|
||||
continue; /* `loop` is disjoint in UV space. */
|
||||
}
|
||||
|
||||
if (loop->prev == needle) {
|
||||
return true; /* Success. */
|
||||
}
|
||||
|
||||
MLoopUV *luv_far = BM_ELEM_CD_GET_VOID_P(loop->prev, cd_loop_uv_offset);
|
||||
if (seam_connected_recursive(
|
||||
anchor, loop->prev->e, luv_anchor, luv_far, needle, visited, cd_loop_uv_offset)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert(loop->next->v == anchor);
|
||||
if (!loop_uv_match(loop, luv_fan, luv_anchor, cd_loop_uv_offset)) {
|
||||
continue; /* `loop` is disjoint in UV space. */
|
||||
}
|
||||
|
||||
if (loop->next == needle) {
|
||||
return true; /* Success. */
|
||||
}
|
||||
|
||||
MLoopUV *luv_far = BM_ELEM_CD_GET_VOID_P(loop->next->next, cd_loop_uv_offset);
|
||||
if (seam_connected_recursive(
|
||||
anchor, loop->next->e, luv_anchor, luv_far, needle, visited, cd_loop_uv_offset)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Given `loop_a` and `loop_b` originate from the same vertex and share a UV,
|
||||
* return true if there are edges that fan between them that are seam-free.
|
||||
* return false otherwise.
|
||||
*/
|
||||
static bool seam_connected(BMLoop *loop_a, BMLoop *loop_b, GSet *visited, int cd_loop_uv_offset)
|
||||
{
|
||||
BLI_assert(loop_a && loop_b);
|
||||
BLI_assert(loop_a != loop_b);
|
||||
BLI_assert(loop_a->v == loop_b->v);
|
||||
|
||||
BLI_gset_clear(visited, NULL);
|
||||
|
||||
MLoopUV *luv_anchor = BM_ELEM_CD_GET_VOID_P(loop_a, cd_loop_uv_offset);
|
||||
MLoopUV *luv_fan = BM_ELEM_CD_GET_VOID_P(loop_a->next, cd_loop_uv_offset);
|
||||
const bool result = seam_connected_recursive(
|
||||
loop_a->v, loop_a->e, luv_anchor, luv_fan, loop_b, visited, cd_loop_uv_offset);
|
||||
return result;
|
||||
}
|
||||
|
||||
UvElementMap *BM_uv_element_map_create(BMesh *bm,
|
||||
const Scene *scene,
|
||||
const bool uv_selected,
|
||||
const bool use_winding,
|
||||
const bool use_seams,
|
||||
const bool do_islands)
|
||||
{
|
||||
/* In uv sync selection, all UVs are visible. */
|
||||
@@ -956,6 +1045,8 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
|
||||
}
|
||||
BLI_buffer_free(&tf_uv_buf);
|
||||
|
||||
GSet *seam_visited_gset = use_seams ? BLI_gset_ptr_new(__func__) : NULL;
|
||||
|
||||
/* For each BMVert, sort associated linked list into unique uvs. */
|
||||
int ev_index;
|
||||
BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, ev_index) {
|
||||
@@ -1001,6 +1092,10 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
|
||||
winding[BM_elem_index_get(v->l->f)];
|
||||
}
|
||||
|
||||
if (connected && use_seams) {
|
||||
connected = seam_connected(iterv->l, v->l, seam_visited_gset, cd_loop_uv_offset);
|
||||
}
|
||||
|
||||
if (connected) {
|
||||
if (lastv) {
|
||||
lastv->next = next;
|
||||
@@ -1026,6 +1121,10 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
|
||||
element_map->vertex[ev_index] = newvlist;
|
||||
}
|
||||
|
||||
if (seam_visited_gset) {
|
||||
BLI_gset_free(seam_visited_gset, NULL);
|
||||
seam_visited_gset = NULL;
|
||||
}
|
||||
MEM_SAFE_FREE(winding);
|
||||
|
||||
/* at this point, every UvElement in vert points to a UvElement sharing the same vertex.
|
||||
|
||||
@@ -686,9 +686,10 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
|
||||
/* Winding was added to island detection in 5197aa04c6bd
|
||||
* However the sculpt tools can flip faces, potentially creating orphaned islands.
|
||||
* See T100132 */
|
||||
bool use_winding = false;
|
||||
const bool use_winding = false;
|
||||
const bool use_seams = true;
|
||||
data->elementMap = BM_uv_element_map_create(
|
||||
bm, scene, false, use_winding, do_island_optimization);
|
||||
bm, scene, false, use_winding, use_seams, do_island_optimization);
|
||||
|
||||
if (!data->elementMap) {
|
||||
uv_sculpt_stroke_exit(C, op);
|
||||
|
||||
@@ -265,7 +265,7 @@ static void createTransUVs(bContext *C, TransInfo *t)
|
||||
/* count */
|
||||
if (is_island_center) {
|
||||
/* create element map with island information */
|
||||
elementmap = BM_uv_element_map_create(em->bm, scene, true, false, true);
|
||||
elementmap = BM_uv_element_map_create(em->bm, scene, true, false, true, true);
|
||||
if (elementmap == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -535,7 +535,7 @@ static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool)
|
||||
return false;
|
||||
}
|
||||
|
||||
UvElementMap *element_map = BM_uv_element_map_create(bm, scene, true, false, true);
|
||||
UvElementMap *element_map = BM_uv_element_map_create(bm, scene, true, false, true, true);
|
||||
if (element_map == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5384,7 +5384,7 @@ static void uv_isolate_selected_islands(const Scene *scene,
|
||||
BLI_assert((scene->toolsettings->uv_flag & UV_SYNC_SELECTION) == 0);
|
||||
BMFace *efa;
|
||||
BMIter iter, liter;
|
||||
UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, false, false, true);
|
||||
UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, false, false, true, true);
|
||||
if (elementmap == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1855,7 +1855,7 @@ static StitchState *stitch_init(bContext *C,
|
||||
* for stitch this isn't useful behavior, see T86924. */
|
||||
const int selectmode_orig = scene->toolsettings->selectmode;
|
||||
scene->toolsettings->selectmode = SCE_SELECT_VERTEX;
|
||||
state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, true, true);
|
||||
state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, true, true, true);
|
||||
scene->toolsettings->selectmode = selectmode_orig;
|
||||
|
||||
if (!state->element_map) {
|
||||
|
||||
Reference in New Issue
Block a user