Blender  V2.93
uvedit_islands.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
28 #include "MEM_guardedalloc.h"
29 
30 #include "DNA_meshdata_types.h"
31 #include "DNA_scene_types.h"
32 
33 #include "BLI_boxpack_2d.h"
34 #include "BLI_convexhull_2d.h"
35 #include "BLI_listbase.h"
36 #include "BLI_math.h"
37 #include "BLI_rect.h"
38 
39 #include "BKE_editmesh.h"
40 
41 #include "DEG_depsgraph.h"
42 
43 #include "ED_uvedit.h" /* Own include. */
44 
45 #include "WM_api.h"
46 #include "WM_types.h"
47 
48 #include "bmesh.h"
49 
50 /* -------------------------------------------------------------------- */
54 static void bm_face_uv_scale_y(BMFace *f, const float scale_y, const int cd_loop_uv_offset)
55 {
56  BMLoop *l_iter;
57  BMLoop *l_first;
58  l_iter = l_first = BM_FACE_FIRST_LOOP(f);
59  do {
60  MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
61  luv->uv[1] *= scale_y;
62  } while ((l_iter = l_iter->next) != l_first);
63 }
64 
66  const float offset[2],
67  const float scale[2],
68  const float pivot[2],
69  const int cd_loop_uv_offset)
70 {
71  BMLoop *l_iter;
72  BMLoop *l_first;
73  l_iter = l_first = BM_FACE_FIRST_LOOP(f);
74  do {
75  MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
76  for (int i = 0; i < 2; i++) {
77  luv->uv[i] = offset[i] + (((luv->uv[i] - pivot[i]) * scale[i]) + pivot[i]);
78  }
79  } while ((l_iter = l_iter->next) != l_first);
80 }
81 
84 /* -------------------------------------------------------------------- */
89  int faces_len,
90  const uint cd_loop_uv_offset,
91  rctf *r_bounds_rect)
92 {
93  float bounds_min[2], bounds_max[2];
94  INIT_MINMAX2(bounds_min, bounds_max);
95  for (int i = 0; i < faces_len; i++) {
96  BMFace *f = faces[i];
97  BM_face_uv_minmax(f, bounds_min, bounds_max, cd_loop_uv_offset);
98  }
99  r_bounds_rect->xmin = bounds_min[0];
100  r_bounds_rect->ymin = bounds_min[1];
101  r_bounds_rect->xmax = bounds_max[0];
102  r_bounds_rect->ymax = bounds_max[1];
103 }
104 
110  BMFace **faces, int faces_len, const uint cd_loop_uv_offset, int *r_coords_len))[2]
111 {
112  int coords_len_alloc = 0;
113  for (int i = 0; i < faces_len; i++) {
114  BMFace *f = faces[i];
115  BMLoop *l_iter, *l_first;
116  l_iter = l_first = BM_FACE_FIRST_LOOP(f);
117  do {
119  } while ((l_iter = l_iter->next) != l_first);
120  coords_len_alloc += f->len;
121  }
122 
123  float(*coords)[2] = MEM_mallocN(sizeof(*coords) * coords_len_alloc, __func__);
124  int coords_len = 0;
125 
126  for (int i = 0; i < faces_len; i++) {
127  BMFace *f = faces[i];
128  BMLoop *l_iter, *l_first;
129  l_iter = l_first = BM_FACE_FIRST_LOOP(f);
130  do {
131  if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
132  /* Already walked over, continue. */
133  continue;
134  }
135 
137  const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
138  copy_v2_v2(coords[coords_len++], luv->uv);
139 
140  /* Un tag all connected so we don't add them twice.
141  * Note that we will tag other loops not part of `faces` but this is harmless,
142  * since we're only turning off a tag. */
143  BMVert *v_pivot = l_iter->v;
144  BMEdge *e_first = v_pivot->e;
145  const BMEdge *e = e_first;
146  do {
147  if (e->l != NULL) {
148  const BMLoop *l_radial = e->l;
149  do {
150  if (l_radial->v == l_iter->v) {
151  if (BM_elem_flag_test(l_radial, BM_ELEM_TAG)) {
152  const MLoopUV *luv_radial = BM_ELEM_CD_GET_VOID_P(l_radial, cd_loop_uv_offset);
153  if (equals_v2v2(luv->uv, luv_radial->uv)) {
154  /* Don't add this UV when met in another face in `faces`. */
156  }
157  }
158  }
159  } while ((l_radial = l_radial->radial_next) != e->l);
160  }
161  } while ((e = BM_DISK_EDGE_NEXT(e, v_pivot)) != e_first);
162  } while ((l_iter = l_iter->next) != l_first);
163  }
164  coords = MEM_reallocN(coords, sizeof(*coords) * coords_len);
165  *r_coords_len = coords_len;
166  return coords;
167 }
168 
176  int faces_len,
177  int align_to_axis,
178  const uint cd_loop_uv_offset)
179 {
180  /* Calculate unique coordinates since calculating a convex hull can be an expensive operation. */
181  int coords_len;
183  faces, faces_len, cd_loop_uv_offset, &coords_len);
184 
185  float angle = BLI_convexhull_aabb_fit_points_2d(coords, coords_len);
186 
187  if (align_to_axis != -1) {
188  if (angle != 0.0f) {
189  float matrix[2][2];
190  angle_to_mat2(matrix, angle);
191  for (int i = 0; i < coords_len; i++) {
192  mul_m2_v2(matrix, coords[i]);
193  }
194  }
195 
196  float bounds_min[2], bounds_max[2];
197  INIT_MINMAX2(bounds_min, bounds_max);
198  for (int i = 0; i < coords_len; i++) {
199  minmax_v2v2_v2(bounds_min, bounds_max, coords[i]);
200  }
201 
202  float size[2];
203  sub_v2_v2v2(size, bounds_max, bounds_min);
204  if (align_to_axis ? (size[1] < size[0]) : (size[0] < size[1])) {
205  angle += DEG2RAD(90.0);
206  }
207  }
208 
209  MEM_freeN(coords);
210 
211  if (angle != 0.0f) {
212  float matrix[2][2];
213  angle_to_mat2(matrix, angle);
214  for (int i = 0; i < faces_len; i++) {
215  BM_face_uv_transform(faces[i], matrix, cd_loop_uv_offset);
216  }
217  }
218 }
219 
221  int faces_len,
222  const float scale_y,
223  const uint cd_loop_uv_offset)
224 {
225  for (int i = 0; i < faces_len; i++) {
226  BMFace *f = faces[i];
227  bm_face_uv_scale_y(f, scale_y, cd_loop_uv_offset);
228  }
229 }
230 
233 /* -------------------------------------------------------------------- */
239 struct FaceIsland {
240  struct FaceIsland *next, *prev;
249  float aspect_y;
250 };
251 
254  bool use_seams;
255 };
256 
257 static bool bm_loop_uv_shared_edge_check(const BMLoop *l_a, const BMLoop *l_b, void *user_data)
258 {
259  const struct SharedUVLoopData *data = user_data;
260 
261  if (data->use_seams) {
262  if (BM_elem_flag_test(l_a->e, BM_ELEM_SEAM)) {
263  return false;
264  }
265  }
266 
267  return BM_loop_uv_share_edge_check((BMLoop *)l_a, (BMLoop *)l_b, data->cd_loop_uv_offset);
268 }
269 
274  BMesh *bm,
275  ListBase *island_list,
276  const bool only_selected_faces,
277  const bool only_selected_uvs,
278  const bool use_seams,
279  const float aspect_y,
280  const uint cd_loop_uv_offset)
281 {
282  int island_added = 0;
284 
285  struct SharedUVLoopData user_data = {
286  .cd_loop_uv_offset = cd_loop_uv_offset,
287  .use_seams = use_seams,
288  };
289 
290  int *groups_array = MEM_mallocN(sizeof(*groups_array) * (size_t)bm->totface, __func__);
291 
292  int(*group_index)[2];
293 
294  /* Calculate the tag to use. */
295  uchar hflag_face_test = 0;
296  if (only_selected_faces) {
297  if (only_selected_uvs) {
298  BMFace *f;
299  BMIter iter;
300  BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
301  bool value = false;
304  value = true;
305  }
306  BM_elem_flag_set(f, BM_ELEM_TAG, value);
307  }
308  hflag_face_test = BM_ELEM_TAG;
309  }
310  else {
311  hflag_face_test = BM_ELEM_SELECT;
312  }
313  }
314 
315  const int group_len = BM_mesh_calc_face_groups(bm,
316  groups_array,
317  &group_index,
318  NULL,
320  &user_data,
321  hflag_face_test,
322  BM_EDGE);
323 
324  for (int i = 0; i < group_len; i++) {
325  const int faces_start = group_index[i][0];
326  const int faces_len = group_index[i][1];
327  BMFace **faces = MEM_mallocN(sizeof(*faces) * faces_len, __func__);
328 
329  float bounds_min[2], bounds_max[2];
330  INIT_MINMAX2(bounds_min, bounds_max);
331 
332  for (int j = 0; j < faces_len; j++) {
333  faces[j] = BM_face_at_index(bm, groups_array[faces_start + j]);
334  }
335 
336  struct FaceIsland *island = MEM_callocN(sizeof(*island), __func__);
337  island->faces = faces;
338  island->faces_len = faces_len;
340  island->aspect_y = aspect_y;
341  BLI_addtail(island_list, island);
342  island_added += 1;
343  }
344 
345  MEM_freeN(groups_array);
346  MEM_freeN(group_index);
347  return island_added;
348 }
349 
352 /* -------------------------------------------------------------------- */
359  Object **objects,
360  const uint objects_len,
361  const struct UVPackIsland_Params *params)
362 {
363  /* Align to the Y axis, could make this configurable. */
364  const int rotate_align_axis = 1;
365  ListBase island_list = {NULL};
366  int island_list_len = 0;
367 
368  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
369  Object *obedit = objects[ob_index];
370  BMEditMesh *em = BKE_editmesh_from_object(obedit);
371  BMesh *bm = em->bm;
372 
374  if (cd_loop_uv_offset == -1) {
375  continue;
376  }
377 
378  float aspect_y = 1.0f;
379  if (params->correct_aspect) {
380  float aspx, aspy;
381  ED_uvedit_get_aspect(obedit, &aspx, &aspy);
382  if (aspx != aspy) {
383  aspect_y = aspx / aspy;
384  }
385  }
386 
387  island_list_len += bm_mesh_calc_uv_islands(scene,
388  bm,
389  &island_list,
390  params->only_selected_faces,
391  params->only_selected_uvs,
392  params->use_seams,
393  aspect_y,
395  }
396 
397  if (island_list_len == 0) {
398  return;
399  }
400 
401  float margin = scene->toolsettings->uvcalc_margin;
402  double area = 0.0f;
403 
404  struct FaceIsland **island_array = MEM_mallocN(sizeof(*island_array) * island_list_len,
405  __func__);
406  BoxPack *boxarray = MEM_mallocN(sizeof(*boxarray) * island_list_len, __func__);
407 
408  int index;
409  LISTBASE_FOREACH_INDEX (struct FaceIsland *, island, &island_list, index) {
410 
411  if (params->rotate) {
412  if (island->aspect_y != 1.0f) {
414  island->faces, island->faces_len, 1.0f / island->aspect_y, island->cd_loop_uv_offset);
415  }
416 
418  island->faces, island->faces_len, rotate_align_axis, island->cd_loop_uv_offset);
419 
420  if (island->aspect_y != 1.0f) {
422  island->faces, island->faces_len, island->aspect_y, island->cd_loop_uv_offset);
423  }
424  }
425 
427  island->faces, island->faces_len, island->cd_loop_uv_offset, &island->bounds_rect);
428 
429  BoxPack *box = &boxarray[index];
430  box->index = index;
431  box->x = 0.0f;
432  box->y = 0.0f;
433  box->w = BLI_rctf_size_x(&island->bounds_rect);
434  box->h = BLI_rctf_size_y(&island->bounds_rect);
435 
436  island_array[index] = island;
437 
438  if (margin > 0.0f) {
439  area += (double)sqrtf(box->w * box->h);
440  }
441  }
442 
443  if (margin > 0.0f) {
444  /* Logic matches behavior from #param_pack,
445  * use area so multiply the margin by the area to give
446  * predictable results not dependent on UV scale. */
447  margin = (margin * (float)area) * 0.1f;
448  for (int i = 0; i < island_list_len; i++) {
449  struct FaceIsland *island = island_array[i];
450  BoxPack *box = &boxarray[i];
451 
452  BLI_rctf_pad(&island->bounds_rect, margin, margin);
453  box->w = BLI_rctf_size_x(&island->bounds_rect);
454  box->h = BLI_rctf_size_y(&island->bounds_rect);
455  }
456  }
457 
458  float boxarray_size[2];
459  BLI_box_pack_2d(boxarray, island_list_len, &boxarray_size[0], &boxarray_size[1]);
460 
461  /* Don't change the aspect when scaling. */
462  boxarray_size[0] = boxarray_size[1] = max_ff(boxarray_size[0], boxarray_size[1]);
463 
464  const float scale[2] = {1.0f / boxarray_size[0], 1.0f / boxarray_size[1]};
465 
466  for (int i = 0; i < island_list_len; i++) {
467  struct FaceIsland *island = island_array[boxarray[i].index];
468  const float pivot[2] = {
469  island->bounds_rect.xmin,
470  island->bounds_rect.ymin,
471  };
472  const float offset[2] = {
473  (boxarray[i].x * scale[0]) - island->bounds_rect.xmin,
474  (boxarray[i].y * scale[1]) - island->bounds_rect.ymin,
475  };
476  for (int j = 0; j < island->faces_len; j++) {
477  BMFace *efa = island->faces[j];
479  efa, offset, scale, pivot, island->cd_loop_uv_offset);
480  }
481  }
482 
483  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
484  Object *obedit = objects[ob_index];
487  }
488 
489  for (int i = 0; i < island_list_len; i++) {
490  MEM_freeN(island_array[i]->faces);
491  MEM_freeN(island_array[i]);
492  }
493 
494  MEM_freeN(island_array);
495  MEM_freeN(boxarray);
496 }
497 
typedef float(TangentPoint)[2]
int CustomData_get_offset(const struct CustomData *data, int type)
BMEditMesh * BKE_editmesh_from_object(struct Object *ob)
Return the BMEditMesh for a given object.
Definition: editmesh.c:85
void BLI_box_pack_2d(BoxPack *boxarray, const unsigned int len, float *r_tot_x, float *r_tot_y)
Definition: boxpack_2d.c:294
float BLI_convexhull_aabb_fit_points_2d(const float(*points)[2], unsigned int n)
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
Definition: BLI_listbase.h:180
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:110
MINLINE float max_ff(float a, float b)
void mul_m2_v2(const float M[2][2], float v[2])
Definition: math_matrix.c:788
void angle_to_mat2(float R[2][2], const float angle)
#define DEG2RAD(_deg)
MINLINE void copy_v2_v2(float r[2], const float a[2])
void minmax_v2v2_v2(float min[2], float max[2], const float vec[2])
Definition: math_vector.c:1043
MINLINE bool equals_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
void BLI_rctf_pad(struct rctf *rect, float pad_x, float pad_y)
Definition: rct.c:654
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition: BLI_rect.h:161
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition: BLI_rect.h:165
unsigned char uchar
Definition: BLI_sys_types.h:86
unsigned int uint
Definition: BLI_sys_types.h:83
#define INIT_MINMAX2(min, max)
typedef double(DMatrix)[4][4]
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:611
@ CD_MLOOPUV
bool uvedit_face_select_test(const struct Scene *scene, struct BMFace *efa, const int cd_loop_uv_offset)
void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy)
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
#define NC_GEOM
Definition: WM_types.h:294
#define ND_DATA
Definition: WM_types.h:408
#define BM_DISK_EDGE_NEXT(e, v)
Definition: bmesh_class.h:556
@ BM_FACE
Definition: bmesh_class.h:386
@ BM_EDGE
Definition: bmesh_class.h:384
@ BM_ELEM_SEAM
Definition: bmesh_class.h:473
@ BM_ELEM_SELECT
Definition: bmesh_class.h:471
@ BM_ELEM_TAG
Definition: bmesh_class.h:484
#define BM_FACE_FIRST_LOOP(p)
Definition: bmesh_class.h:553
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
Definition: bmesh_class.h:530
#define BM_elem_flag_disable(ele, hflag)
Definition: bmesh_inline.h:29
#define BM_elem_flag_set(ele, hflag, val)
Definition: bmesh_inline.h:30
#define BM_elem_flag_test(ele, hflag)
Definition: bmesh_inline.h:26
#define BM_elem_flag_enable(ele, hflag)
Definition: bmesh_inline.h:28
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_FACES_OF_MESH
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
Definition: bmesh_mesh.c:2276
BLI_INLINE BMFace * BM_face_at_index(BMesh *bm, const int index)
Definition: bmesh_mesh.h:110
int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int(**r_group_index)[2], BMLoopFilterFunc filter_fn, BMLoopPairFilterFunc filter_pair_fn, void *user_data, const char hflag_test, const char htype_step)
Definition: bmesh_query.c:2611
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
void BM_face_uv_transform(BMFace *f, const float matrix[2][2], const int cd_loop_uv_offset)
bool BM_loop_uv_share_edge_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
void BM_face_uv_minmax(const BMFace *f, float min[2], float max[2], const int cd_loop_uv_offset)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const
Return the angle between this and another vector.
Definition: btVector3.h:356
Scene scene
void * user_data
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define sqrtf(x)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static char faces[256]
static void area(int d1, int d2, int e1, int e2, float weights[2])
struct BMesh * bm
Definition: BKE_editmesh.h:52
int len
Definition: bmesh_class.h:279
struct BMVert * v
Definition: bmesh_class.h:165
struct BMEdge * e
Definition: bmesh_class.h:176
struct BMLoop * radial_next
Definition: bmesh_class.h:216
struct BMLoop * next
Definition: bmesh_class.h:245
struct BMEdge * e
Definition: bmesh_class.h:109
CustomData ldata
Definition: bmesh_class.h:337
int totface
Definition: bmesh_class.h:297
struct FaceIsland * prev
uint cd_loop_uv_offset
BMFace ** faces
struct FaceIsland * next
void * data
struct ToolSettings * toolsettings
float xmax
Definition: DNA_vec_types.h:85
float xmin
Definition: DNA_vec_types.h:85
float ymax
Definition: DNA_vec_types.h:86
float ymin
Definition: DNA_vec_types.h:86
void ED_uvedit_pack_islands_multi(const Scene *scene, Object **objects, const uint objects_len, const struct UVPackIsland_Params *params)
static void bm_face_array_uv_rotate_fit_aabb(BMFace **faces, int faces_len, int align_to_axis, const uint cd_loop_uv_offset)
static void bm_face_uv_scale_y(BMFace *f, const float scale_y, const int cd_loop_uv_offset)
static void bm_face_array_uv_scale_y(BMFace **faces, int faces_len, const float scale_y, const uint cd_loop_uv_offset)
static bool bm_loop_uv_shared_edge_check(const BMLoop *l_a, const BMLoop *l_b, void *user_data)
static void bm_face_uv_translate_and_scale_around_pivot(BMFace *f, const float offset[2], const float scale[2], const float pivot[2], const int cd_loop_uv_offset)
static void bm_face_array_calc_bounds(BMFace **faces, int faces_len, const uint cd_loop_uv_offset, rctf *r_bounds_rect)
static int bm_mesh_calc_uv_islands(const Scene *scene, BMesh *bm, ListBase *island_list, const bool only_selected_faces, const bool only_selected_uvs, const bool use_seams, const float aspect_y, const uint cd_loop_uv_offset)
static float(* bm_face_array_calc_unique_uv_coords(BMFace **faces, int faces_len, const uint cd_loop_uv_offset, int *r_coords_len))[2]
void WM_main_add_notifier(unsigned int type, void *reference)