[Bf-blender-cvs] [1c1e8428791] blender-v3.2-release: Fix T86358: Use per face aspect correction for primitive UV projections

Chris Blackbourn noreply at git.blender.org
Tue May 10 03:15:42 CEST 2022


Commit: 1c1e8428791fe376ec67ff96b15b5deae8f18296
Author: Chris Blackbourn
Date:   Tue May 10 11:09:29 2022 +1000
Branches: blender-v3.2-release
https://developer.blender.org/rB1c1e8428791fe376ec67ff96b15b5deae8f18296

Fix T86358: Use per face aspect correction for primitive UV projections

During UV unwrapping, Cube Projection, Sphere Projection, Cylinder
Projection and Project From View (in the 3D Viewport), when "Correct
Aspect" toggle is active, it now uses a query cache to perform a
per-face aspect ratio ("per_face_aspect") correction for the active
image of each face.

Reviewed By: campbellbarton

Ref D14852

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

M	source/blender/editors/include/ED_uvedit.h
M	source/blender/editors/uvedit/uvedit_unwrap_ops.c

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

diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index 4c01c75e06c..06134f1d7b5 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -268,6 +268,10 @@ struct BMLoop **ED_uvedit_selected_verts(const struct Scene *scene,
                                          int *r_verts_len);
 
 void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy);
+void ED_uvedit_get_aspect_from_material(Object *ob,
+                                        const int material_index,
+                                        float *r_aspx,
+                                        float *r_aspy);
 
 void ED_uvedit_active_vert_loop_set(struct BMesh *bm, struct BMLoop *l);
 struct BMLoop *ED_uvedit_active_vert_loop_get(struct BMesh *bm);
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index 34fae2ffb2a..c0ea753ed51 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -267,26 +267,35 @@ static bool uvedit_have_selection_multi(const Scene *scene,
   return have_select;
 }
 
+void ED_uvedit_get_aspect_from_material(Object *ob,
+                                        const int material_index,
+                                        float *r_aspx,
+                                        float *r_aspy)
+{
+  if (UNLIKELY(material_index < 0 || material_index >= ob->totcol)) {
+    *r_aspx = 1.0f;
+    *r_aspy = 1.0f;
+    return;
+  }
+  Image *ima;
+  ED_object_get_active_image(ob, material_index + 1, &ima, NULL, NULL, NULL);
+  ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy);
+}
+
 void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy)
 {
   BMEditMesh *em = BKE_editmesh_from_object(ob);
   BLI_assert(em != NULL);
   bool sloppy = true;
   bool selected = false;
-  BMFace *efa;
-  Image *ima;
-
-  efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
-
-  if (efa) {
-    ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL);
-
-    ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy);
-  }
-  else {
+  BMFace *efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
+  if (!efa) {
     *r_aspx = 1.0f;
     *r_aspy = 1.0f;
+    return;
   }
+
+  ED_uvedit_get_aspect_from_material(ob, efa->mat_nr, r_aspx, r_aspy);
 }
 
 static void construct_param_handle_face_add(ParamHandle *handle,
@@ -1527,49 +1536,88 @@ static void uv_transform_properties(wmOperatorType *ot, int radius)
   }
 }
 
-static void correct_uv_aspect(Object *ob, BMEditMesh *em)
+static void shrink_loop_uv_by_aspect_ratio(BMFace *efa,
+                                           const int cd_loop_uv_offset,
+                                           const float aspect_y)
 {
+  BLI_assert(aspect_y != 1.0f); /* Nothing to do, should be handled by caller. */
+  BLI_assert(aspect_y > 0.0f);  /* Negative aspect ratios are not supported. */
+
   BMLoop *l;
-  BMIter iter, liter;
-  MLoopUV *luv;
-  BMFace *efa;
-  float scale, aspx, aspy;
+  BMIter iter;
+  BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) {
+    MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+    if (aspect_y > 1.0f) {
+      /* Reduce round-off error, i.e. `u = (u - 0.5) / aspect_y + 0.5`. */
+      luv->uv[0] = luv->uv[0] / aspect_y + (0.5f - 0.5f / aspect_y);
+    }
+    else {
+      /* Reduce round-off error, i.e. `v = (v - 0.5) * aspect_y + 0.5`. */
+      luv->uv[1] = luv->uv[1] * aspect_y + (0.5f - 0.5f * aspect_y);
+    }
+  }
+}
 
+static void correct_uv_aspect(Object *ob, BMEditMesh *em)
+{
   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
-
+  float aspx, aspy;
   ED_uvedit_get_aspect(ob, &aspx, &aspy);
+  const float aspect_y = aspx / aspy;
+  if (aspect_y == 1.0f) {
+    /* Scaling by 1.0 has no effect. */
+    return;
+  }
+  BMFace *efa;
+  BMIter iter;
+  BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+    if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+      shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y);
+    }
+  }
+}
 
-  if (aspx == aspy) {
+static void correct_uv_aspect_per_face(Object *ob, BMEditMesh *em)
+{
+  const int materials_num = ob->totcol;
+  if (materials_num == 0) {
+    /* Without any materials, there is no aspect_y information and nothing to do. */
     return;
   }
 
-  if (aspx > aspy) {
-    scale = aspy / aspx;
+  float *material_aspect_y = BLI_array_alloca(material_aspect_y, materials_num);
+  /* Lazily initialize aspect ratio for materials. */
+  copy_vn_fl(material_aspect_y, materials_num, -1.0f);
 
-    BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
-      if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
-        continue;
-      }
+  const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
 
-      BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
-        luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
-        luv->uv[0] = ((luv->uv[0] - 0.5f) * scale) + 0.5f;
-      }
+  BMFace *efa;
+  BMIter iter;
+  BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+    if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+      continue;
     }
-  }
-  else {
-    scale = aspx / aspy;
 
-    BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
-      if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
-        continue;
-      }
+    const int material_index = efa->mat_nr;
+    if (UNLIKELY(material_index < 0 || material_index >= materials_num)) {
+      /* The index might be for a material slot which is not currently setup. */
+      continue;
+    }
 
-      BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
-        luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
-        luv->uv[1] = ((luv->uv[1] - 0.5f) * scale) + 0.5f;
-      }
+    float aspect_y = material_aspect_y[material_index];
+    if (aspect_y == -1.0f) {
+      /* Lazily initialize aspect ratio for materials. */
+      float aspx, aspy;
+      ED_uvedit_get_aspect_from_material(ob, material_index, &aspx, &aspy);
+      aspect_y = aspx / aspy;
+      material_aspect_y[material_index] = aspect_y;
     }
+
+    if (aspect_y == 1.0f) {
+      /* Scaling by 1.0 has no effect. */
+      continue;
+    }
+    shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y);
   }
 }
 
@@ -1613,7 +1661,17 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot)
   uv_map_clip_correct_properties_ex(ot, true);
 }
 
-static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op)
+/**
+ * \param per_face_aspect: Calculate the aspect ratio per-face,
+ * otherwise use a single aspect for all UV's based on the material of the active face.
+ * TODO: using per-face aspect may split UV islands so more advanced UV projection methods
+ * such as "Unwrap" & "Smart UV Projections" will need to handle aspect correction themselves.
+ * For now keep using a single aspect for all faces in this case.
+ */
+static void uv_map_clip_correct_multi(Object **objects,
+                                      uint objects_len,
+                                      wmOperator *op,
+                                      bool per_face_aspect)
 {
   BMFace *efa;
   BMLoop *l;
@@ -1633,9 +1691,14 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper
     BMEditMesh *em = BKE_editmesh_from_object(ob);
     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
 
-    /* correct for image aspect ratio */
+    /* Correct for image aspect ratio. */
     if (correct_aspect) {
-      correct_uv_aspect(ob, em);
+      if (per_face_aspect) {
+        correct_uv_aspect_per_face(ob, em);
+      }
+      else {
+        correct_uv_aspect(ob, em);
+      }
     }
 
     if (scale_to_bounds) {
@@ -1678,6 +1741,11 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper
       dy = 1.0f / dy;
     }
 
+    if (dx == 1.0f && dy == 1.0f) {
+      /* Scaling by 1.0 has no effect. */
+      return;
+    }
+
     for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
       Object *ob = objects[ob_index];
 
@@ -1702,7 +1770,7 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper
 
 static void uv_map_clip_correct(Object *ob, wmOperator *op)
 {
-  uv_map_clip_correct_multi(&ob, 1, op);
+  uv_map_clip_correct_multi(&ob, 1, op, true);
 }
 
 /** \} */
@@ -2283,7 +2351,9 @@ static int smart_project_exec(bContext *C, wmOperator *op)
                                      .use_seams = true,
                                  });
 
-    uv_map_clip_correct_multi(objects_changed, object_changed_len, op);
+    /* #ED_uvedit_pack_islands_multi only supports `per_face_aspect = false`. */
+    const bool per_face_aspect = false;
+    uv_map_clip_correct_multi(objects_changed, object_changed_len, op, per_face_aspect);
   }
 
   MEM_freeN(objects_changed);
@@ -2485,7 +2555,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op)
   }
 
   if (changed_multi) {
-    uv_map_clip_correct_multi(objects, objects_len, op);
+    uv_map_clip_correct_multi(objects, objects_len, op, true);
   }
 
   MEM_freeN(objects);



More information about the Bf-blender-cvs mailing list