diff options
author | Chris Blackbourn <chrisbblend@gmail.com> | 2022-09-06 06:51:53 +0300 |
---|---|---|
committer | Chris Blackbourn <chrisbblend@gmail.com> | 2022-09-06 06:52:26 +0300 |
commit | d9db79dbe5bec8ba541660940bf981de1c7c5c52 (patch) | |
tree | 50c78af59f5ed98d1e2457e7dcdd81405a061de6 /source/blender/editors/uvedit/uvedit_islands.c | |
parent | f0a36599007d2c5185d040202b84775ae1343785 (diff) |
Cleanup: Move uvedit_islands to c++
Differential Revision: https://developer.blender.org/D15870
Diffstat (limited to 'source/blender/editors/uvedit/uvedit_islands.c')
-rw-r--r-- | source/blender/editors/uvedit/uvedit_islands.c | 636 |
1 files changed, 0 insertions, 636 deletions
diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c deleted file mode 100644 index 68c00b18b09..00000000000 --- a/source/blender/editors/uvedit/uvedit_islands.c +++ /dev/null @@ -1,636 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup eduv - * - * Utilities for manipulating UV islands. - * - * \note This is similar to `GEO_uv_parametrizer.h`, - * however the data structures there don't support arbitrary topology - * such as an edge with 3 or more faces using it. - * This API uses #BMesh data structures and doesn't have limitations for manifold meshes. - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_meshdata_types.h" -#include "DNA_scene_types.h" -#include "DNA_space_types.h" - -#include "BLI_boxpack_2d.h" -#include "BLI_convexhull_2d.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_rect.h" - -#include "BKE_customdata.h" -#include "BKE_editmesh.h" -#include "BKE_image.h" - -#include "DEG_depsgraph.h" - -#include "ED_uvedit.h" /* Own include. */ - -#include "WM_api.h" -#include "WM_types.h" - -#include "bmesh.h" - -/* -------------------------------------------------------------------- */ -/** \name UV Face Utilities - * \{ */ - -static void bm_face_uv_scale_y(BMFace *f, const float scale_y, const int cd_loop_uv_offset) -{ - BMLoop *l_iter; - BMLoop *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); - luv->uv[1] *= scale_y; - } while ((l_iter = l_iter->next) != l_first); -} - -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) -{ - BMLoop *l_iter; - BMLoop *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); - for (int i = 0; i < 2; i++) { - luv->uv[i] = offset[i] + (((luv->uv[i] - pivot[i]) * scale[i]) + pivot[i]); - } - } while ((l_iter = l_iter->next) != l_first); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name UV Face Array Utilities - * \{ */ - -static void bm_face_array_calc_bounds(BMFace **faces, - int faces_len, - const int cd_loop_uv_offset, - rctf *r_bounds_rect) -{ - BLI_assert(cd_loop_uv_offset >= 0); - float bounds_min[2], bounds_max[2]; - INIT_MINMAX2(bounds_min, bounds_max); - for (int i = 0; i < faces_len; i++) { - BMFace *f = faces[i]; - BM_face_uv_minmax(f, bounds_min, bounds_max, cd_loop_uv_offset); - } - r_bounds_rect->xmin = bounds_min[0]; - r_bounds_rect->ymin = bounds_min[1]; - r_bounds_rect->xmax = bounds_max[0]; - r_bounds_rect->ymax = bounds_max[1]; -} - -/** - * Return an array of un-ordered UV coordinates, - * without duplicating coordinates for loops that share a vertex. - */ -static float (*bm_face_array_calc_unique_uv_coords( - BMFace **faces, int faces_len, const int cd_loop_uv_offset, int *r_coords_len))[2] -{ - BLI_assert(cd_loop_uv_offset >= 0); - int coords_len_alloc = 0; - for (int i = 0; i < faces_len; i++) { - BMFace *f = faces[i]; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - BM_elem_flag_enable(l_iter, BM_ELEM_TAG); - } while ((l_iter = l_iter->next) != l_first); - coords_len_alloc += f->len; - } - - float(*coords)[2] = MEM_mallocN(sizeof(*coords) * coords_len_alloc, __func__); - int coords_len = 0; - - for (int i = 0; i < faces_len; i++) { - BMFace *f = faces[i]; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) { - /* Already walked over, continue. */ - continue; - } - - BM_elem_flag_disable(l_iter, BM_ELEM_TAG); - const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); - copy_v2_v2(coords[coords_len++], luv->uv); - - /* Un tag all connected so we don't add them twice. - * Note that we will tag other loops not part of `faces` but this is harmless, - * since we're only turning off a tag. */ - BMVert *v_pivot = l_iter->v; - BMEdge *e_first = v_pivot->e; - const BMEdge *e = e_first; - do { - if (e->l != NULL) { - const BMLoop *l_radial = e->l; - do { - if (l_radial->v == l_iter->v) { - if (BM_elem_flag_test(l_radial, BM_ELEM_TAG)) { - const MLoopUV *luv_radial = BM_ELEM_CD_GET_VOID_P(l_radial, cd_loop_uv_offset); - if (equals_v2v2(luv->uv, luv_radial->uv)) { - /* Don't add this UV when met in another face in `faces`. */ - BM_elem_flag_disable(l_iter, BM_ELEM_TAG); - } - } - } - } while ((l_radial = l_radial->radial_next) != e->l); - } - } while ((e = BM_DISK_EDGE_NEXT(e, v_pivot)) != e_first); - } while ((l_iter = l_iter->next) != l_first); - } - coords = MEM_reallocN(coords, sizeof(*coords) * coords_len); - *r_coords_len = coords_len; - return coords; -} - -/** - * \param align_to_axis: - * - -1: don't align to an axis. - * - 0: align horizontally. - * - 1: align vertically. - */ -static void bm_face_array_uv_rotate_fit_aabb(BMFace **faces, - int faces_len, - int align_to_axis, - const int cd_loop_uv_offset) -{ - /* Calculate unique coordinates since calculating a convex hull can be an expensive operation. */ - int coords_len; - float(*coords)[2] = bm_face_array_calc_unique_uv_coords( - faces, faces_len, cd_loop_uv_offset, &coords_len); - - float angle = BLI_convexhull_aabb_fit_points_2d(coords, coords_len); - - if (align_to_axis != -1) { - if (angle != 0.0f) { - float matrix[2][2]; - angle_to_mat2(matrix, angle); - for (int i = 0; i < coords_len; i++) { - mul_m2_v2(matrix, coords[i]); - } - } - - float bounds_min[2], bounds_max[2]; - INIT_MINMAX2(bounds_min, bounds_max); - for (int i = 0; i < coords_len; i++) { - minmax_v2v2_v2(bounds_min, bounds_max, coords[i]); - } - - float size[2]; - sub_v2_v2v2(size, bounds_max, bounds_min); - if (align_to_axis ? (size[1] < size[0]) : (size[0] < size[1])) { - angle += DEG2RAD(90.0); - } - } - - MEM_freeN(coords); - - if (angle != 0.0f) { - float matrix[2][2]; - angle_to_mat2(matrix, angle); - for (int i = 0; i < faces_len; i++) { - BM_face_uv_transform(faces[i], matrix, cd_loop_uv_offset); - } - } -} - -static void bm_face_array_uv_scale_y(BMFace **faces, - int faces_len, - const float scale_y, - const int cd_loop_uv_offset) -{ - for (int i = 0; i < faces_len; i++) { - BMFace *f = faces[i]; - bm_face_uv_scale_y(f, scale_y, cd_loop_uv_offset); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name UDIM packing helper functions - * \{ */ - -bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], const float coords[2]) -{ - const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])}; - const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); - - if (coords[0] < udim_grid[0] && coords[0] > 0 && coords[1] < udim_grid[1] && coords[1] > 0) { - return true; - } - /* Check if selection lies on a valid UDIM image tile. */ - if (is_tiled_image) { - LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) { - const int tile_index = tile->tile_number - 1001; - const int target_x = (tile_index % 10); - const int target_y = (tile_index / 10); - if (coords_floor[0] == target_x && coords_floor[1] == target_y) { - return true; - } - } - } - /* Probably not required since UDIM grid checks for 1001. */ - else if (image && !is_tiled_image) { - if (is_zero_v2(coords_floor)) { - return true; - } - } - - return false; -} - -/** - * Calculates distance to nearest UDIM image tile in UV space and its UDIM tile number. - */ -static float uv_nearest_image_tile_distance(const Image *image, - const float coords[2], - float nearest_tile_co[2]) -{ - BKE_image_find_nearest_tile_with_offset(image, coords, nearest_tile_co); - - /* Add 0.5 to get tile center coordinates. */ - float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]}; - add_v2_fl(nearest_tile_center_co, 0.5f); - - return len_squared_v2v2(coords, nearest_tile_center_co); -} - -/** - * Calculates distance to nearest UDIM grid tile in UV space and its UDIM tile number. - */ -static float uv_nearest_grid_tile_distance(const int udim_grid[2], - float coords[2], - float nearest_tile_co[2]) -{ - const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])}; - - if (coords[0] > udim_grid[0]) { - nearest_tile_co[0] = udim_grid[0] - 1; - } - else if (coords[0] < 0) { - nearest_tile_co[0] = 0; - } - else { - nearest_tile_co[0] = coords_floor[0]; - } - - if (coords[1] > udim_grid[1]) { - nearest_tile_co[1] = udim_grid[1] - 1; - } - else if (coords[1] < 0) { - nearest_tile_co[1] = 0; - } - else { - nearest_tile_co[1] = coords_floor[1]; - } - - /* Add 0.5 to get tile center coordinates. */ - float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]}; - add_v2_fl(nearest_tile_center_co, 0.5f); - - return len_squared_v2v2(coords, nearest_tile_center_co); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Calculate UV Islands - * \{ */ - -struct SharedUVLoopData { - int cd_loop_uv_offset; - bool use_seams; -}; - -static bool bm_loop_uv_shared_edge_check(const BMLoop *l_a, const BMLoop *l_b, void *user_data) -{ - const struct SharedUVLoopData *data = user_data; - - if (data->use_seams) { - if (BM_elem_flag_test(l_a->e, BM_ELEM_SEAM)) { - return false; - } - } - - return BM_loop_uv_share_edge_check((BMLoop *)l_a, (BMLoop *)l_b, data->cd_loop_uv_offset); -} - -/** - * Calculate islands and add them to \a island_list returning the number of items added. - */ -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 int cd_loop_uv_offset) -{ - BLI_assert(cd_loop_uv_offset >= 0); - int island_added = 0; - BM_mesh_elem_table_ensure(bm, BM_FACE); - - struct SharedUVLoopData user_data = { - .cd_loop_uv_offset = cd_loop_uv_offset, - .use_seams = use_seams, - }; - - int *groups_array = MEM_mallocN(sizeof(*groups_array) * (size_t)bm->totface, __func__); - - int(*group_index)[2]; - - /* Calculate the tag to use. */ - uchar hflag_face_test = 0; - if (only_selected_faces) { - if (only_selected_uvs) { - BMFace *f; - BMIter iter; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - bool value = false; - if (BM_elem_flag_test(f, BM_ELEM_SELECT) && - uvedit_face_select_test(scene, f, cd_loop_uv_offset)) { - value = true; - } - BM_elem_flag_set(f, BM_ELEM_TAG, value); - } - hflag_face_test = BM_ELEM_TAG; - } - else { - hflag_face_test = BM_ELEM_SELECT; - } - } - - const int group_len = BM_mesh_calc_face_groups(bm, - groups_array, - &group_index, - NULL, - bm_loop_uv_shared_edge_check, - &user_data, - hflag_face_test, - BM_EDGE); - - for (int i = 0; i < group_len; i++) { - const int faces_start = group_index[i][0]; - const int faces_len = group_index[i][1]; - BMFace **faces = MEM_mallocN(sizeof(*faces) * faces_len, __func__); - - float bounds_min[2], bounds_max[2]; - INIT_MINMAX2(bounds_min, bounds_max); - - for (int j = 0; j < faces_len; j++) { - faces[j] = BM_face_at_index(bm, groups_array[faces_start + j]); - } - - struct FaceIsland *island = MEM_callocN(sizeof(*island), __func__); - island->faces = faces; - island->faces_len = faces_len; - island->cd_loop_uv_offset = cd_loop_uv_offset; - island->aspect_y = aspect_y; - BLI_addtail(island_list, island); - island_added += 1; - } - - MEM_freeN(groups_array); - MEM_freeN(group_index); - return island_added; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Public UV Island Packing - * - * \note This behavior follows #param_pack. - * \{ */ - -void ED_uvedit_pack_islands_multi(const Scene *scene, - Object **objects, - const uint objects_len, - const struct UVMapUDIM_Params *udim_params, - 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; - } - - float aspect_y = 1.0f; - if (params->correct_aspect) { - float aspx, aspy; - ED_uvedit_get_aspect(obedit, &aspx, &aspy); - if (aspx != aspy) { - aspect_y = aspx / aspy; - } - } - - island_list_len += bm_mesh_calc_uv_islands(scene, - bm, - &island_list, - params->only_selected_faces, - params->only_selected_uvs, - params->use_seams, - aspect_y, - cd_loop_uv_offset); - } - - 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; - /* Coordinates of bounding box containing all selected UVs. */ - float selection_min_co[2], selection_max_co[2]; - INIT_MINMAX2(selection_min_co, selection_max_co); - - LISTBASE_FOREACH_INDEX (struct FaceIsland *, island, &island_list, index) { - - /* Skip calculation if using specified UDIM option. */ - if (udim_params && (udim_params->use_target_udim == false)) { - float bounds_min[2], bounds_max[2]; - INIT_MINMAX2(bounds_min, bounds_max); - for (int i = 0; i < island->faces_len; i++) { - BMFace *f = island->faces[i]; - BM_face_uv_minmax(f, bounds_min, bounds_max, island->cd_loop_uv_offset); - } - - selection_min_co[0] = MIN2(bounds_min[0], selection_min_co[0]); - selection_min_co[1] = MIN2(bounds_min[1], selection_min_co[1]); - selection_max_co[0] = MAX2(bounds_max[0], selection_max_co[0]); - selection_max_co[1] = MAX2(bounds_max[1], selection_max_co[1]); - } - - 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); - } - - 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( - island->faces, island->faces_len, island->cd_loop_uv_offset, &island->bounds_rect); - - BoxPack *box = &boxarray[index]; - box->index = index; - box->x = 0.0f; - box->y = 0.0f; - box->w = BLI_rctf_size_x(&island->bounds_rect); - box->h = BLI_rctf_size_y(&island->bounds_rect); - - island_array[index] = island; - - if (margin > 0.0f) { - area += (double)sqrtf(box->w * box->h); - } - } - - /* Center of bounding box containing all selected UVs. */ - float selection_center[2]; - if (udim_params && (udim_params->use_target_udim == false)) { - selection_center[0] = (selection_min_co[0] + selection_max_co[0]) / 2.0f; - selection_center[1] = (selection_min_co[1] + selection_max_co[1]) / 2.0f; - } - - if (margin > 0.0f) { - /* Logic matches behavior from #param_pack, - * use area so multiply the margin by the area to give - * predictable results not dependent on UV scale. */ - margin = (margin * (float)area) * 0.1f; - for (int i = 0; i < island_list_len; i++) { - struct FaceIsland *island = island_array[i]; - BoxPack *box = &boxarray[i]; - - BLI_rctf_pad(&island->bounds_rect, margin, margin); - box->w = BLI_rctf_size_x(&island->bounds_rect); - box->h = BLI_rctf_size_y(&island->bounds_rect); - } - } - - float boxarray_size[2]; - BLI_box_pack_2d(boxarray, island_list_len, &boxarray_size[0], &boxarray_size[1]); - - /* Don't change the aspect when scaling. */ - boxarray_size[0] = boxarray_size[1] = max_ff(boxarray_size[0], boxarray_size[1]); - - const float scale[2] = {1.0f / boxarray_size[0], 1.0f / boxarray_size[1]}; - - /* Tile offset. */ - float base_offset[2] = {0.0f, 0.0f}; - - /* CASE: ignore UDIM. */ - if (udim_params == NULL) { - /* pass */ - } - /* CASE: Active/specified(smart uv project) UDIM. */ - else if (udim_params->use_target_udim) { - - /* Calculate offset based on specified_tile_index. */ - base_offset[0] = (udim_params->target_udim - 1001) % 10; - base_offset[1] = (udim_params->target_udim - 1001) / 10; - } - - /* CASE: Closest UDIM. */ - else { - const Image *image = udim_params->image; - const int *udim_grid = udim_params->grid_shape; - /* Check if selection lies on a valid UDIM grid tile. */ - bool is_valid_udim = uv_coords_isect_udim(image, udim_grid, selection_center); - if (is_valid_udim) { - base_offset[0] = floorf(selection_center[0]); - base_offset[1] = floorf(selection_center[1]); - } - /* If selection doesn't lie on any UDIM then find the closest UDIM grid or image tile. */ - else { - float nearest_image_tile_co[2] = {FLT_MAX, FLT_MAX}; - float nearest_image_tile_dist = FLT_MAX, nearest_grid_tile_dist = FLT_MAX; - if (image) { - nearest_image_tile_dist = uv_nearest_image_tile_distance( - image, selection_center, nearest_image_tile_co); - } - - float nearest_grid_tile_co[2] = {0.0f, 0.0f}; - nearest_grid_tile_dist = uv_nearest_grid_tile_distance( - udim_grid, selection_center, nearest_grid_tile_co); - - base_offset[0] = (nearest_image_tile_dist < nearest_grid_tile_dist) ? - nearest_image_tile_co[0] : - nearest_grid_tile_co[0]; - base_offset[1] = (nearest_image_tile_dist < nearest_grid_tile_dist) ? - nearest_image_tile_co[1] : - nearest_grid_tile_co[1]; - } - } - - for (int i = 0; i < island_list_len; i++) { - struct FaceIsland *island = island_array[boxarray[i].index]; - const float pivot[2] = { - island->bounds_rect.xmin, - island->bounds_rect.ymin, - }; - const float offset[2] = { - ((boxarray[i].x * scale[0]) - island->bounds_rect.xmin) + base_offset[0], - ((boxarray[i].y * scale[1]) - island->bounds_rect.ymin) + base_offset[1], - }; - for (int j = 0; j < island->faces_len; j++) { - BMFace *efa = island->faces[j]; - bm_face_uv_translate_and_scale_around_pivot( - efa, offset, scale, pivot, island->cd_loop_uv_offset); - } - } - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data); - } - - for (int i = 0; i < island_list_len; i++) { - MEM_freeN(island_array[i]->faces); - MEM_freeN(island_array[i]); - } - - MEM_freeN(island_array); - MEM_freeN(boxarray); -} - -/** \} */ |