[Bf-blender-cvs] [e6e9f1ac5a2] master: Fix T98239: During UV Unwrap, create unique indices for each pinned UV

Chris Blackbourn noreply at git.blender.org
Wed Jun 15 23:57:57 CEST 2022


Commit: e6e9f1ac5a2dae42dc523784b03395a50671afaa
Author: Chris Blackbourn
Date:   Thu Jun 16 09:51:48 2022 +1200
Branches: master
https://developer.blender.org/rBe6e9f1ac5a2dae42dc523784b03395a50671afaa

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

Originally reported in T75007.

Differential Revision: https://developer.blender.org/D15199

===================================================================

M	source/blender/editors/uvedit/uvedit_unwrap_ops.c
M	source/blender/geometry/GEO_uv_parametrizer.h
M	source/blender/geometry/intern/uv_parametrizer.c

===================================================================

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)) {
+      retur

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list