Fix T98239: During UV Unwrap, create unique indices for each pinned UV

Originally reported in T75007.

Differential Revision: https://developer.blender.org/D15199
This commit is contained in:
Chris Blackbourn 2022-06-16 09:51:48 +12:00
parent 2804497312
commit e6e9f1ac5a
Notes: blender-bot 2023-02-14 05:28:01 +01:00
Referenced by commit 7a44f62bdb, Fix T99156: UV parameterizer respects both Pins and Seams
Referenced by issue #99502, OBJ/MTL import: behavior changed for missing texture files
Referenced by issue #99156, Regression: UV Pins interfere with UV seams (regression)
Referenced by issue #99036, Add Color Attribute: hex colors don't work
Referenced by issue #98239, Unwrap and Live Unwrap can merge pinned UVs
Referenced by issue #75007, Live unwrap interaction changes unwrapping and ignores pinned vertices
3 changed files with 141 additions and 49 deletions

View File

@ -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) {

View File

@ -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,

View File

@ -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,