From e6e9f1ac5a2dae42dc523784b03395a50671afaa Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Thu, 16 Jun 2022 09:51:48 +1200 Subject: Fix T98239: During UV Unwrap, create unique indices for each pinned UV Originally reported in T75007. Differential Revision: https://developer.blender.org/D15199 --- source/blender/editors/uvedit/uvedit_unwrap_ops.c | 101 ++++++++++++---------- source/blender/geometry/GEO_uv_parametrizer.h | 5 ++ source/blender/geometry/intern/uv_parametrizer.c | 84 +++++++++++++++++- 3 files changed, 141 insertions(+), 49 deletions(-) (limited to 'source') diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index cbc9b2a8d06..3618286ec01 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -298,6 +298,44 @@ void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy) ED_uvedit_get_aspect_from_material(ob, efa->mat_nr, r_aspx, r_aspy); } +static bool uvedit_is_face_affected(const Scene *scene, + BMFace *efa, + const UnwrapOptions *options, + const int cd_loop_uv_offset) +{ + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + return false; + } + + if (options->only_selected_faces && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + return false; + } + + if (options->topology_from_uvs && options->only_selected_uvs && + !uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) { + return false; + } + + return true; +} + +/* Prepare unique indices for each unique pinned UV, even if it shares a BMVert. + */ +static void uvedit_prepare_pinned_indices(ParamHandle *handle, + BMFace *efa, + const int cd_loop_uv_offset) +{ + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (luv->flag & MLOOPUV_PINNED) { + int bmvertindex = BM_elem_index_get(l->v); + GEO_uv_prepare_pin_index(handle, bmvertindex, luv->uv); + } + } +} + static void construct_param_handle_face_add(ParamHandle *handle, const Scene *scene, BMFace *efa, @@ -319,7 +357,7 @@ static void construct_param_handle_face_add(ParamHandle *handle, BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - vkeys[i] = (ParamKey)BM_elem_index_get(l->v); + vkeys[i] = GEO_uv_find_pin_index(handle, BM_elem_index_get(l->v), luv->uv); co[i] = l->v->co; uv[i] = luv->uv; pin[i] = (luv->flag & MLOOPUV_PINNED) != 0; @@ -337,13 +375,10 @@ static ParamHandle *construct_param_handle(const Scene *scene, UnwrapResultInfo *result_info) { BMFace *efa; - BMLoop *l; BMEdge *eed; - BMIter iter, liter; + BMIter iter; int i; - const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); - ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); if (options->correct_aspect) { @@ -359,30 +394,17 @@ static ParamHandle *construct_param_handle(const Scene *scene, /* we need the vert indices */ BM_mesh_elem_index_ensure(bm, BM_VERT); + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { - - if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || - (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { - continue; + if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) { + uvedit_prepare_pinned_indices(handle, efa, cd_loop_uv_offset); } + } - if (options->topology_from_uvs) { - bool is_loopsel = false; - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (options->only_selected_uvs && - (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) { - continue; - } - is_loopsel = true; - break; - } - if (is_loopsel == false) { - continue; - } + BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { + if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) { + construct_param_handle_face_add(handle, scene, efa, i, cd_loop_uv_offset); } - - construct_param_handle_face_add(handle, scene, efa, i, cd_loop_uv_offset); } if (!options->topology_from_uvs || options->topology_from_uvs_use_seams) { @@ -414,9 +436,8 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, int *count_fail) { BMFace *efa; - BMLoop *l; BMEdge *eed; - BMIter iter, liter; + BMIter iter; int i; ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); @@ -448,29 +469,15 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, } BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { - - if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || - (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { - continue; + if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) { + uvedit_prepare_pinned_indices(handle, efa, cd_loop_uv_offset); } + } - if (options->topology_from_uvs) { - bool is_loopsel = false; - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (options->only_selected_uvs && - (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) { - continue; - } - is_loopsel = true; - break; - } - if (is_loopsel == false) { - continue; - } + BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { + if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) { + construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset); } - - construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset); } if (!options->topology_from_uvs || options->topology_from_uvs_use_seams) { diff --git a/source/blender/geometry/GEO_uv_parametrizer.h b/source/blender/geometry/GEO_uv_parametrizer.h index 7fe60a3a855..2181f95945e 100644 --- a/source/blender/geometry/GEO_uv_parametrizer.h +++ b/source/blender/geometry/GEO_uv_parametrizer.h @@ -14,6 +14,7 @@ extern "C" { typedef struct ParamHandle ParamHandle; /* Handle to an array of charts. */ typedef intptr_t ParamKey; /* Key (hash) for identifying verts and faces. */ +#define PARAM_KEY_MAX INTPTR_MAX /* -------------------------------------------------------------------- */ /** \name Chart Construction: @@ -34,6 +35,10 @@ ParamHandle *GEO_uv_parametrizer_construct_begin(void); void GEO_uv_parametrizer_aspect_ratio(ParamHandle *handle, float aspx, float aspy); +void GEO_uv_prepare_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2]); + +ParamKey GEO_uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2]); + void GEO_uv_parametrizer_face_add(ParamHandle *handle, const ParamKey key, const int nverts, diff --git a/source/blender/geometry/intern/uv_parametrizer.c b/source/blender/geometry/intern/uv_parametrizer.c index af3bcc3bdec..c0b9907ea81 100644 --- a/source/blender/geometry/intern/uv_parametrizer.c +++ b/source/blender/geometry/intern/uv_parametrizer.c @@ -8,6 +8,7 @@ #include "BLI_boxpack_2d.h" #include "BLI_convexhull_2d.h" +#include "BLI_ghash.h" #include "BLI_heap.h" #include "BLI_math.h" #include "BLI_memarena.h" @@ -190,6 +191,9 @@ typedef struct ParamHandle { PHash *hash_edges; PHash *hash_faces; + struct GHash *pin_hash; + int unique_pin_count; + PChart **charts; int ncharts; @@ -3817,8 +3821,11 @@ void GEO_uv_parametrizer_delete(ParamHandle *phandle) p_chart_delete(phandle->charts[i]); } - if (phandle->charts) { - MEM_freeN(phandle->charts); + MEM_SAFE_FREE(phandle->charts); + + if (phandle->pin_hash) { + BLI_ghash_free(phandle->pin_hash, NULL, NULL); + phandle->pin_hash = NULL; } if (phandle->construction_chart) { @@ -3835,6 +3842,79 @@ void GEO_uv_parametrizer_delete(ParamHandle *phandle) MEM_freeN(phandle); } +typedef struct GeoUVPinIndex { + struct GeoUVPinIndex *next; + float uv[2]; + ParamKey reindex; +} GeoUVPinIndex; + +/* Find a (mostly) unique ParamKey given a BMVert index and UV co-ordinates. + * For each unique pinned UVs, return a unique ParamKey, starting with + * a very large number, and decreasing steadily from there. + * For non-pinned UVs which share a BMVert with a pinned UV, + * return the index corresponding to the closest pinned UV. + * For everything else, just return the BMVert index. + * Note that ParamKeys will eventually be hashed, so they don't need to be contiguous. + */ +ParamKey GEO_uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2]) +{ + if (!handle->pin_hash) { + return bmvertindex; /* No verts pinned. */ + } + + GeoUVPinIndex *pinuvlist = BLI_ghash_lookup(handle->pin_hash, bmvertindex); + if (!pinuvlist) { + return bmvertindex; /* Vert not pinned. */ + } + + /* At least one of the UVs associated with bmvertindex is pinned. Find the best one. */ + float bestdistsquared = len_squared_v2v2(pinuvlist->uv, uv); + ParamKey bestkey = pinuvlist->reindex; + pinuvlist = pinuvlist->next; + while (pinuvlist) { + const float distsquared = len_squared_v2v2(pinuvlist->uv, uv); + if (bestdistsquared > distsquared) { + bestdistsquared = distsquared; + bestkey = pinuvlist->reindex; + } + pinuvlist = pinuvlist->next; + } + return bestkey; +} + +static GeoUVPinIndex *new_geo_uv_pinindex(ParamHandle *handle, const float uv[2]) +{ + GeoUVPinIndex *pinuv = BLI_memarena_alloc(handle->arena, sizeof(*pinuv)); + pinuv->next = NULL; + copy_v2_v2(pinuv->uv, uv); + pinuv->reindex = PARAM_KEY_MAX - (handle->unique_pin_count++); + return pinuv; +} + +void GEO_uv_prepare_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2]) +{ + if (!handle->pin_hash) { + handle->pin_hash = BLI_ghash_int_new("uv pin reindex"); + } + + GeoUVPinIndex *pinuvlist = BLI_ghash_lookup(handle->pin_hash, bmvertindex); + if (!pinuvlist) { + BLI_ghash_insert(handle->pin_hash, bmvertindex, new_geo_uv_pinindex(handle, uv)); + return; + } + + while (true) { + if (equals_v2v2(pinuvlist->uv, uv)) { + return; + } + if (!pinuvlist->next) { + pinuvlist->next = new_geo_uv_pinindex(handle, uv); + return; + } + pinuvlist = pinuvlist->next; + } +} + static void p_add_ngon(ParamHandle *handle, const ParamKey key, const int nverts, -- cgit v1.2.3