[Bf-blender-cvs] [e5ab28d3921] soc-2021-uv-editor-improvements: UV : Pack islands to box area

Siddhartha Jejurkar noreply at git.blender.org
Sat Jun 19 18:49:49 CEST 2021


Commit: e5ab28d3921e77bac464c717610e2bf1b5c58b5c
Author: Siddhartha Jejurkar
Date:   Sat Jun 19 22:10:34 2021 +0530
Branches: soc-2021-uv-editor-improvements
https://developer.blender.org/rBe5ab28d3921e77bac464c717610e2bf1b5c58b5c

UV : Pack islands to box area

Adds a new operator to the UV editor - Pack islands to area
Allows the users to pack selected UV islands to a specified
area in the UV editor

Refer T78398

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

M	release/scripts/startup/bl_ui/space_image.py
M	source/blender/blenlib/BLI_boxpack_2d.h
M	source/blender/blenlib/intern/boxpack_2d.c
M	source/blender/editors/include/ED_uvedit.h
M	source/blender/editors/uvedit/uvedit_intern.h
M	source/blender/editors/uvedit/uvedit_islands.c
M	source/blender/editors/uvedit/uvedit_ops.c
M	source/blender/editors/uvedit/uvedit_unwrap_ops.c
M	source/blender/windowmanager/intern/wm_operators.c

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

diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index 3fafa328289..54b7dab5d1b 100644
--- a/release/scripts/startup/bl_ui/space_image.py
+++ b/release/scripts/startup/bl_ui/space_image.py
@@ -436,6 +436,7 @@ class IMAGE_MT_uvs(Menu):
         layout.separator()
 
         layout.operator("uv.pack_islands")
+        layout.operator("uv.pack_islands_to_area")
         layout.operator("uv.average_islands_scale")
 
         layout.separator()
diff --git a/source/blender/blenlib/BLI_boxpack_2d.h b/source/blender/blenlib/BLI_boxpack_2d.h
index 7e347d0b0d7..5b70cd3f410 100644
--- a/source/blender/blenlib/BLI_boxpack_2d.h
+++ b/source/blender/blenlib/BLI_boxpack_2d.h
@@ -52,11 +52,23 @@ typedef struct FixedSizeBoxPack {
   int w, h;
 } FixedSizeBoxPack;
 
+/* Similar to FixedSizeBoxPack. Uses float variables */
+typedef struct RectSizeBoxPack {
+  struct RectSizeBoxPack *next, *prev;
+  float x, y;
+  float w, h;
+} RectSizeBoxPack;
+
 void BLI_box_pack_2d_fixedarea(struct ListBase *boxes,
                                int width,
                                int height,
                                struct ListBase *packed);
 
+void BLI_rect_pack_2d(BoxPack *boxarray,
+                      const uint len,
+                      const float rect_width,
+                      const float rect_height);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/source/blender/blenlib/intern/boxpack_2d.c b/source/blender/blenlib/intern/boxpack_2d.c
index 84b3f728884..4f2d9747975 100644
--- a/source/blender/blenlib/intern/boxpack_2d.c
+++ b/source/blender/blenlib/intern/boxpack_2d.c
@@ -784,3 +784,90 @@ void BLI_box_pack_2d_fixedarea(ListBase *boxes, int width, int height, ListBase
 
   BLI_freelistN(&spaces);
 }
+
+/* Similar implementation of BLI_box_pack_2d_fixedarea() that works with BoxPack array and float
+ * variables.
+ * A current problem with the algorithm is that boxes that do not fit are not packed (skipped), so
+ * that finally causes those boxes to be placed at the bottom left position overlapping with other
+ * boxes.
+ * TODO : Fix this issue by setting a callback that cancels the operator (and possibly prints an
+ * error message saying the area is too small for packing islands without scaling) when a
+ * particular box does not fit any empty space */
+void BLI_rect_pack_2d(BoxPack *boxarray,
+                      const uint len,
+                      const float rect_width,
+                      const float rect_height)
+{
+  ListBase spaces = {NULL};
+  RectSizeBoxPack *full_rect = MEM_callocN(sizeof(RectSizeBoxPack), __func__);
+  full_rect->w = rect_width;
+  full_rect->h = rect_height;
+
+  BLI_addhead(&spaces, full_rect);
+  qsort(boxarray, (size_t)len, sizeof(BoxPack), box_areasort);
+
+  for (uint i = 0; i < len; i++) {
+    LISTBASE_FOREACH (RectSizeBoxPack *, space, &spaces) {
+      /* Skip this space if it's too small. */
+      if (boxarray[i].w > space->w || boxarray[i].h > space->h) {
+        continue;
+      }
+
+      /* Pack this box into this space. */
+      boxarray[i].x = space->x;
+      boxarray[i].y = space->y;
+
+      if (boxarray[i].w == space->w && boxarray[i].h == space->h) {
+        /* Box exactly fills space, so just remove the space. */
+        BLI_remlink(&spaces, space);
+        MEM_freeN(space);
+      }
+      else if (boxarray[i].w == space->w) {
+        /* Box fills the entire width, so we can just contract the box
+         * to the upper part that remains. */
+        space->y += boxarray[i].h;
+        space->h -= boxarray[i].h;
+      }
+      else if (boxarray[i].h == space->h) {
+        /* Box fills the entire height, so we can just contract the box
+         * to the right part that remains. */
+        space->x += boxarray[i].w;
+        space->w -= boxarray[i].w;
+      }
+      else {
+        /* Split the remaining L-shaped space into two spaces.
+         * There are two ways to do so, we pick the one that produces the biggest
+         * remaining space */
+        float area_hsplit_large = space->w * (space->h - boxarray[i].h);
+        float area_vsplit_large = (space->w - boxarray[i].w) * space->h;
+
+        /* Perform split. This space becomes the larger space,
+         * while the new smaller space is inserted _before_ it. */
+        RectSizeBoxPack *new_space = MEM_callocN(sizeof(RectSizeBoxPack), __func__);
+        if (area_hsplit_large > area_vsplit_large) {
+          new_space->x = space->x + boxarray[i].w;
+          new_space->y = space->y;
+          new_space->w = space->w - boxarray[i].w;
+          new_space->h = boxarray[i].h;
+
+          space->y += boxarray[i].h;
+          space->h -= boxarray[i].h;
+        }
+        else {
+          new_space->x = space->x;
+          new_space->y = space->y + boxarray[i].h;
+          new_space->w = boxarray[i].w;
+          new_space->h = space->h - boxarray[i].h;
+
+          space->x += boxarray[i].w;
+          space->w -= boxarray[i].w;
+        }
+        BLI_addhead(&spaces, new_space);
+      }
+
+      break;
+    }
+  }
+
+  BLI_freelistN(&spaces);
+}
\ No newline at end of file
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index dd61768a312..2dc9635a494 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -253,6 +253,14 @@ void ED_uvedit_pack_islands_multi(const struct Scene *scene,
                                   bool use_target,
                                   const struct UVPackIsland_Params *params);
 
+void ED_uvedit_pack_islands_to_area_multi(const struct Scene *scene,
+                                          Object **objects,
+                                          const uint objects_len,
+                                          const float min_co[2],
+                                          const float max_co[2],
+                                          const bool scale_islands,
+                                          const struct UVPackIsland_Params *params);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h
index cd8fbd00316..c78ad0f0345 100644
--- a/source/blender/editors/uvedit/uvedit_intern.h
+++ b/source/blender/editors/uvedit/uvedit_intern.h
@@ -137,6 +137,7 @@ void UV_OT_cylinder_project(struct wmOperatorType *ot);
 void UV_OT_project_from_view(struct wmOperatorType *ot);
 void UV_OT_minimize_stretch(struct wmOperatorType *ot);
 void UV_OT_pack_islands(struct wmOperatorType *ot);
+void UV_OT_pack_islands_to_area(struct wmOperatorType *ot);
 void UV_OT_reset(struct wmOperatorType *ot);
 void UV_OT_sphere_project(struct wmOperatorType *ot);
 void UV_OT_unwrap(struct wmOperatorType *ot);
diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c
index a03b6670dae..07c8263d618 100644
--- a/source/blender/editors/uvedit/uvedit_islands.c
+++ b/source/blender/editors/uvedit/uvedit_islands.c
@@ -589,3 +589,173 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
 }
 
 /** \} */
+
+/* Almost similar to ED_uvedit_pack_islands_multi().
+ * TODO : Break some of the code into smaller functions since same operations are being done in
+ * both ED_uvedit_pack_islands_to_area_multi() and ED_uvedit_pack_islands_multi() */
+void ED_uvedit_pack_islands_to_area_multi(const Scene *scene,
+                                          Object **objects,
+                                          const uint objects_len,
+                                          const float min_co[2],
+                                          const float max_co[2],
+                                          const bool scale_islands,
+                                          const struct UVPackIsland_Params *params)
+{
+  /* Align to the Y axis, could make this configurable. */
+  const int rotate_align_axis = 1;
+  ListBase island_list = {NULL};
+  int island_list_len = 0;
+
+  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+    Object *obedit = objects[ob_index];
+    BMEditMesh *em = BKE_editmesh_from_object(obedit);
+    BMesh *bm = em->bm;
+
+    const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+    if (cd_loop_uv_offset == -1) {
+      continue;
+    }
+
+    island_list_len += bm_mesh_calc_uv_islands(scene,
+                                               bm,
+                                               &island_list,
+                                               params->only_selected_faces,
+                                               params->only_selected_uvs,
+                                               params->use_seams,
+                                               1.0f,
+                                               cd_loop_uv_offset);
+  }
+
+  /* This check could probably be removed */
+  if (island_list_len == 0) {
+    return;
+  }
+
+  float margin = scene->toolsettings->uvcalc_margin;
+  double area = 0.0f;
+
+  struct FaceIsland **island_array = MEM_mallocN(sizeof(*island_array) * island_list_len,
+                                                 __func__);
+  BoxPack *boxarray = MEM_mallocN(sizeof(*boxarray) * island_list_len, __func__);
+
+  int index;
+  LISTBASE_FOREACH_INDEX (struct FaceIsland *, island, &island_list, index) {
+    /* For now using the same conditions for rotating islands when scaling is enabled and disabled.
+     * TODO : New heuristic for rotating islands when scaling is disabled (using the
+     * RectPack2D algorithm) */
+    if (params->rotate) {
+      if (island->aspect_y != 1.0f) {
+        bm_face_array_uv_scale_y(
+            island->faces, island->faces_len, 1.0f / island->aspect_y, island->cd_loop_uv_offset);
+      }
+
+      // AABB - Axis Aligned Bounding Box
+      bm_face_array_uv_rotate_fit_aabb(
+          island->faces, island->faces_len, rotate_align_axis, island->cd_loop_uv_offset);
+
+      if (island->aspect_y != 1.0f) {
+        bm_face_array_uv_scale_y(
+            island->faces, island->faces_len, island->aspect_y, island->cd_loop_uv_offset);
+      }
+    }
+
+    bm_face_array_calc_bounds(


@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list