forked from blender/blender
main sync #3
@ -26,7 +26,6 @@ struct Object;
|
|||||||
struct Scene;
|
struct Scene;
|
||||||
struct SpaceImage;
|
struct SpaceImage;
|
||||||
struct ToolSettings;
|
struct ToolSettings;
|
||||||
struct UVPackIsland_Params;
|
|
||||||
struct View2D;
|
struct View2D;
|
||||||
struct ViewLayer;
|
struct ViewLayer;
|
||||||
struct bContext;
|
struct bContext;
|
||||||
@ -356,26 +355,6 @@ bool uv_coords_isect_udim(const struct Image *image,
|
|||||||
const int udim_grid[2],
|
const int udim_grid[2],
|
||||||
const float coords[2]);
|
const float coords[2]);
|
||||||
|
|
||||||
/**
|
|
||||||
* Pack UV islands from multiple objects.
|
|
||||||
*
|
|
||||||
* \param scene: Scene containing the objects to be packed.
|
|
||||||
* \param objects: Array of Objects to pack.
|
|
||||||
* \param objects_len: Length of `objects` array.
|
|
||||||
* \param bmesh_override: BMesh array aligned with `objects`.
|
|
||||||
* Optional, when non-null this overrides object's BMesh.
|
|
||||||
* This is needed to perform UV packing on objects that aren't in edit-mode.
|
|
||||||
* \param udim_params: Parameters to specify UDIM target and UDIM source image.
|
|
||||||
* \param params: Parameters and options to pass to the packing engine.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void ED_uvedit_pack_islands_multi(const struct Scene *scene,
|
|
||||||
Object **objects,
|
|
||||||
uint objects_len,
|
|
||||||
struct BMesh **bmesh_override,
|
|
||||||
const struct UVMapUDIM_Params *closest_udim,
|
|
||||||
const struct UVPackIsland_Params *params);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -11,223 +11,14 @@
|
|||||||
* This API uses #BMesh data structures and doesn't have limitations for manifold meshes.
|
* 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_editmesh.h"
|
||||||
#include "BKE_image.h"
|
|
||||||
|
|
||||||
#include "DEG_depsgraph.h"
|
#include "BLI_math.h"
|
||||||
|
|
||||||
|
#include "DNA_image_types.h"
|
||||||
|
|
||||||
#include "ED_uvedit.h" /* Own include. */
|
#include "ED_uvedit.h" /* Own include. */
|
||||||
|
|
||||||
#include "GEO_uv_pack.hh"
|
|
||||||
|
|
||||||
#include "WM_api.h"
|
|
||||||
#include "WM_types.h"
|
|
||||||
|
|
||||||
#include "bmesh.h"
|
|
||||||
|
|
||||||
static void mul_v2_m2_add_v2v2(float r[2],
|
|
||||||
const float mat[2][2],
|
|
||||||
const float a[2],
|
|
||||||
const float b[2])
|
|
||||||
{
|
|
||||||
/* Compute `r = mat * (a + b)` with high precision. */
|
|
||||||
const double x = double(a[0]) + double(b[0]);
|
|
||||||
const double y = double(a[1]) + double(b[1]);
|
|
||||||
|
|
||||||
r[0] = float(mat[0][0] * x + mat[1][0] * y);
|
|
||||||
r[1] = float(mat[0][1] * x + mat[1][1] * y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void island_uv_transform(FaceIsland *island,
|
|
||||||
const float matrix[2][2], /* Scale and rotation. */
|
|
||||||
const float pre_translate[2] /* (pre) Translation. */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
/* Use a pre-transform to compute `A * (x+b)`
|
|
||||||
*
|
|
||||||
* \note Ordinarily, we'd use a post_transform like `A * x + b`
|
|
||||||
* In general, post-transforms are easier to work with when using homogenous co-ordinates.
|
|
||||||
*
|
|
||||||
* When UV mapping into the unit square, post-transforms can lose precision on small islands.
|
|
||||||
* Instead we're using a pre-transform to maintain precision.
|
|
||||||
*
|
|
||||||
* To convert post-transform to pre-transform, use `A * x + b == A * (x + c), c = A^-1 * b`
|
|
||||||
*/
|
|
||||||
|
|
||||||
const int cd_loop_uv_offset = island->offsets.uv;
|
|
||||||
const int faces_len = island->faces_len;
|
|
||||||
for (int i = 0; i < faces_len; i++) {
|
|
||||||
BMFace *f = island->faces[i];
|
|
||||||
BMLoop *l;
|
|
||||||
BMIter iter;
|
|
||||||
BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
|
|
||||||
float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
|
|
||||||
mul_v2_m2_add_v2v2(luv, matrix, luv, pre_translate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
|
||||||
/** \name UV Face Array Utilities
|
|
||||||
* \{ */
|
|
||||||
|
|
||||||
static void bm_face_array_calc_bounds(BMFace **faces,
|
|
||||||
const 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] = static_cast<float(*)[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 float *luv = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_loop_uv_offset);
|
|
||||||
copy_v2_v2(coords[coords_len++], luv);
|
|
||||||
|
|
||||||
/* 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 != nullptr) {
|
|
||||||
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 float *luv_radial = BM_ELEM_CD_GET_FLOAT_P(l_radial, cd_loop_uv_offset);
|
|
||||||
if (equals_v2v2(luv, luv_radial)) {
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
*r_coords_len = coords_len;
|
|
||||||
return coords;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void face_island_uv_rotate_fit_aabb(FaceIsland *island)
|
|
||||||
{
|
|
||||||
BMFace **faces = island->faces;
|
|
||||||
const int faces_len = island->faces_len;
|
|
||||||
const float aspect_y = island->aspect_y;
|
|
||||||
const int cd_loop_uv_offset = island->offsets.uv;
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* Correct aspect ratio. */
|
|
||||||
if (aspect_y != 1.0f) {
|
|
||||||
for (int i = 0; i < coords_len; i++) {
|
|
||||||
coords[i][1] /= aspect_y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float angle = BLI_convexhull_aabb_fit_points_2d(coords, coords_len);
|
|
||||||
|
|
||||||
/* Rotate coords by `angle` before computing bounding box. */
|
|
||||||
if (angle != 0.0f) {
|
|
||||||
float matrix[2][2];
|
|
||||||
angle_to_mat2(matrix, angle);
|
|
||||||
matrix[0][1] *= aspect_y;
|
|
||||||
matrix[1][1] *= aspect_y;
|
|
||||||
for (int i = 0; i < coords_len; i++) {
|
|
||||||
mul_m2_v2(matrix, coords[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compute new AABB. */
|
|
||||||
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 (size[1] < size[0]) {
|
|
||||||
angle += DEG2RADF(90.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
MEM_freeN(coords);
|
|
||||||
|
|
||||||
/* Apply rotation back to BMesh. */
|
|
||||||
if (angle != 0.0f) {
|
|
||||||
float matrix[2][2];
|
|
||||||
float pre_translate[2] = {0, 0};
|
|
||||||
angle_to_mat2(matrix, angle);
|
|
||||||
matrix[1][0] *= 1.0f / aspect_y;
|
|
||||||
/* matrix[1][1] *= aspect_y / aspect_y; */
|
|
||||||
matrix[0][1] *= aspect_y;
|
|
||||||
island_uv_transform(island, matrix, pre_translate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \} */
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
/** \name UDIM packing helper functions
|
/** \name UDIM packing helper functions
|
||||||
* \{ */
|
* \{ */
|
||||||
@ -261,58 +52,6 @@ bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], const floa
|
|||||||
return false;
|
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],
|
|
||||||
const 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
@ -436,211 +175,3 @@ int bm_mesh_calc_uv_islands(const Scene *scene,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
static bool island_has_pins(const Scene *scene,
|
|
||||||
FaceIsland *island,
|
|
||||||
const UVPackIsland_Params *params)
|
|
||||||
{
|
|
||||||
const bool pin_unselected = params->pin_unselected;
|
|
||||||
const bool only_selected_faces = params->only_selected_faces;
|
|
||||||
BMLoop *l;
|
|
||||||
BMIter iter;
|
|
||||||
const int pin_offset = island->offsets.pin;
|
|
||||||
for (int i = 0; i < island->faces_len; i++) {
|
|
||||||
BMFace *efa = island->faces[i];
|
|
||||||
if (pin_unselected && only_selected_faces && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
BM_ITER_ELEM (l, &iter, island->faces[i], BM_LOOPS_OF_FACE) {
|
|
||||||
if (BM_ELEM_CD_GET_BOOL(l, pin_offset)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (pin_unselected && !uvedit_uv_select_test(scene, l, island->offsets)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
|
||||||
/** \name Public UV Island Packing
|
|
||||||
*
|
|
||||||
* \note This behavior loosely follows #geometry::uv_parametrizer_pack.
|
|
||||||
* \{ */
|
|
||||||
|
|
||||||
void ED_uvedit_pack_islands_multi(const Scene *scene,
|
|
||||||
Object **objects,
|
|
||||||
const uint objects_len,
|
|
||||||
BMesh **bmesh_override,
|
|
||||||
const UVMapUDIM_Params *closest_udim,
|
|
||||||
const UVPackIsland_Params *params)
|
|
||||||
{
|
|
||||||
blender::Vector<FaceIsland *> island_vector;
|
|
||||||
|
|
||||||
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
||||||
Object *obedit = objects[ob_index];
|
|
||||||
BMesh *bm = nullptr;
|
|
||||||
if (bmesh_override) {
|
|
||||||
/* Note: obedit is still required for aspect ratio and ID_RECALC_GEOMETRY. */
|
|
||||||
bm = bmesh_override[ob_index];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
|
||||||
bm = em->bm;
|
|
||||||
}
|
|
||||||
BLI_assert(bm);
|
|
||||||
|
|
||||||
const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
|
|
||||||
if (offsets.uv == -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float aspect_y = params->correct_aspect ? ED_uvedit_get_aspect_y(obedit) : 1.0f;
|
|
||||||
|
|
||||||
bool only_selected_faces = params->only_selected_faces;
|
|
||||||
bool only_selected_uvs = params->only_selected_uvs;
|
|
||||||
if (params->ignore_pinned && params->pin_unselected) {
|
|
||||||
only_selected_faces = false;
|
|
||||||
only_selected_uvs = false;
|
|
||||||
}
|
|
||||||
ListBase island_list = {nullptr};
|
|
||||||
bm_mesh_calc_uv_islands(scene,
|
|
||||||
bm,
|
|
||||||
&island_list,
|
|
||||||
only_selected_faces,
|
|
||||||
only_selected_uvs,
|
|
||||||
params->use_seams,
|
|
||||||
aspect_y,
|
|
||||||
offsets);
|
|
||||||
|
|
||||||
/* Remove from linked list and append to blender::Vector. */
|
|
||||||
LISTBASE_FOREACH_MUTABLE (struct FaceIsland *, island, &island_list) {
|
|
||||||
BLI_remlink(&island_list, island);
|
|
||||||
if (params->ignore_pinned && island_has_pins(scene, island, params)) {
|
|
||||||
MEM_freeN(island->faces);
|
|
||||||
MEM_freeN(island);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
island_vector.append(island);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (island_vector.size() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
for (int index = 0; index < island_vector.size(); index++) {
|
|
||||||
FaceIsland *island = island_vector[index];
|
|
||||||
if (closest_udim) {
|
|
||||||
/* Only calculate selection bounding box if using closest_udim. */
|
|
||||||
for (int i = 0; i < island->faces_len; i++) {
|
|
||||||
BMFace *f = island->faces[i];
|
|
||||||
BM_face_uv_minmax(f, selection_min_co, selection_max_co, island->offsets.uv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params->rotate) {
|
|
||||||
face_island_uv_rotate_fit_aabb(island);
|
|
||||||
}
|
|
||||||
|
|
||||||
bm_face_array_calc_bounds(
|
|
||||||
island->faces, island->faces_len, island->offsets.uv, &island->bounds_rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Center of bounding box containing all selected UVs. */
|
|
||||||
float selection_center[2];
|
|
||||||
if (closest_udim) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
float scale[2] = {1.0f, 1.0f};
|
|
||||||
blender::Vector<blender::geometry::PackIsland *> pack_island_vector;
|
|
||||||
for (int i = 0; i < island_vector.size(); i++) {
|
|
||||||
FaceIsland *face_island = island_vector[i];
|
|
||||||
blender::geometry::PackIsland *pack_island = new blender::geometry::PackIsland();
|
|
||||||
pack_island->bounds_rect = face_island->bounds_rect;
|
|
||||||
pack_island_vector.append(pack_island);
|
|
||||||
}
|
|
||||||
BoxPack *box_array = pack_islands(pack_island_vector, *params, scale);
|
|
||||||
|
|
||||||
float base_offset[2] = {0.0f, 0.0f};
|
|
||||||
copy_v2_v2(base_offset, params->udim_base_offset);
|
|
||||||
|
|
||||||
if (closest_udim) {
|
|
||||||
const Image *image = closest_udim->image;
|
|
||||||
const int *udim_grid = closest_udim->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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float matrix[2][2];
|
|
||||||
float matrix_inverse[2][2];
|
|
||||||
float pre_translate[2];
|
|
||||||
for (int i = 0; i < island_vector.size(); i++) {
|
|
||||||
FaceIsland *island = island_vector[box_array[i].index];
|
|
||||||
matrix[0][0] = scale[0];
|
|
||||||
matrix[0][1] = 0.0f;
|
|
||||||
matrix[1][0] = 0.0f;
|
|
||||||
matrix[1][1] = scale[1];
|
|
||||||
invert_m2_m2(matrix_inverse, matrix);
|
|
||||||
|
|
||||||
/* Add base_offset, post transform. */
|
|
||||||
mul_v2_m2v2(pre_translate, matrix_inverse, base_offset);
|
|
||||||
|
|
||||||
/* Translate to box_array from bounds_rect. */
|
|
||||||
pre_translate[0] += box_array[i].x - island->bounds_rect.xmin;
|
|
||||||
pre_translate[1] += box_array[i].y - island->bounds_rect.ymin;
|
|
||||||
island_uv_transform(island, matrix, pre_translate);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
||||||
Object *obedit = objects[ob_index];
|
|
||||||
DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
|
|
||||||
WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (FaceIsland *island : island_vector) {
|
|
||||||
MEM_freeN(island->faces);
|
|
||||||
MEM_freeN(island);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < pack_island_vector.size(); i++) {
|
|
||||||
blender::geometry::PackIsland *pack_island = pack_island_vector[i];
|
|
||||||
pack_island_vector[i] = nullptr;
|
|
||||||
delete pack_island;
|
|
||||||
}
|
|
||||||
|
|
||||||
MEM_freeN(box_array);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \} */
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "DNA_scene_types.h"
|
#include "DNA_scene_types.h"
|
||||||
|
|
||||||
#include "BLI_array.hh"
|
#include "BLI_array.hh"
|
||||||
|
#include "BLI_convexhull_2d.h"
|
||||||
#include "BLI_linklist.h"
|
#include "BLI_linklist.h"
|
||||||
#include "BLI_listbase.h"
|
#include "BLI_listbase.h"
|
||||||
#include "BLI_math.h"
|
#include "BLI_math.h"
|
||||||
@ -1016,6 +1017,455 @@ void UV_OT_minimize_stretch(wmOperatorType *ot)
|
|||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
|
/** Compute `r = mat * (a + b)` with high precision. */
|
||||||
|
static void mul_v2_m2_add_v2v2(float r[2],
|
||||||
|
const float mat[2][2],
|
||||||
|
const float a[2],
|
||||||
|
const float b[2])
|
||||||
|
{
|
||||||
|
const double x = double(a[0]) + double(b[0]);
|
||||||
|
const double y = double(a[1]) + double(b[1]);
|
||||||
|
|
||||||
|
r[0] = float(mat[0][0] * x + mat[1][0] * y);
|
||||||
|
r[1] = float(mat[0][1] * x + mat[1][1] * y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void island_uv_transform(FaceIsland *island,
|
||||||
|
const float matrix[2][2], /* Scale and rotation. */
|
||||||
|
const float pre_translate[2] /* (pre) Translation. */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
/* Use a pre-transform to compute `A * (x+b)`
|
||||||
|
*
|
||||||
|
* \note Ordinarily, we'd use a post_transform like `A * x + b`
|
||||||
|
* In general, post-transforms are easier to work with when using homogenous co-ordinates.
|
||||||
|
*
|
||||||
|
* When UV mapping into the unit square, post-transforms can lose precision on small islands.
|
||||||
|
* Instead we're using a pre-transform to maintain precision.
|
||||||
|
*
|
||||||
|
* To convert post-transform to pre-transform, use `A * x + b == A * (x + c), c = A^-1 * b`
|
||||||
|
*/
|
||||||
|
|
||||||
|
const int cd_loop_uv_offset = island->offsets.uv;
|
||||||
|
const int faces_len = island->faces_len;
|
||||||
|
for (int i = 0; i < faces_len; i++) {
|
||||||
|
BMFace *f = island->faces[i];
|
||||||
|
BMLoop *l;
|
||||||
|
BMIter iter;
|
||||||
|
BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
|
||||||
|
float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
|
||||||
|
mul_v2_m2_add_v2v2(luv, matrix, luv, pre_translate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bm_face_array_calc_bounds(BMFace **faces,
|
||||||
|
const 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] = static_cast<float(*)[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 float *luv = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_loop_uv_offset);
|
||||||
|
copy_v2_v2(coords[coords_len++], luv);
|
||||||
|
|
||||||
|
/* 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 != nullptr) {
|
||||||
|
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 float *luv_radial = BM_ELEM_CD_GET_FLOAT_P(l_radial, cd_loop_uv_offset);
|
||||||
|
if (equals_v2v2(luv, luv_radial)) {
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
*r_coords_len = coords_len;
|
||||||
|
return coords;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void face_island_uv_rotate_fit_aabb(FaceIsland *island)
|
||||||
|
{
|
||||||
|
BMFace **faces = island->faces;
|
||||||
|
const int faces_len = island->faces_len;
|
||||||
|
const float aspect_y = island->aspect_y;
|
||||||
|
const int cd_loop_uv_offset = island->offsets.uv;
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* Correct aspect ratio. */
|
||||||
|
if (aspect_y != 1.0f) {
|
||||||
|
for (int i = 0; i < coords_len; i++) {
|
||||||
|
coords[i][1] /= aspect_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float angle = BLI_convexhull_aabb_fit_points_2d(coords, coords_len);
|
||||||
|
|
||||||
|
/* Rotate coords by `angle` before computing bounding box. */
|
||||||
|
if (angle != 0.0f) {
|
||||||
|
float matrix[2][2];
|
||||||
|
angle_to_mat2(matrix, angle);
|
||||||
|
matrix[0][1] *= aspect_y;
|
||||||
|
matrix[1][1] *= aspect_y;
|
||||||
|
for (int i = 0; i < coords_len; i++) {
|
||||||
|
mul_m2_v2(matrix, coords[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute new AABB. */
|
||||||
|
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 (size[1] < size[0]) {
|
||||||
|
angle += DEG2RADF(90.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
MEM_freeN(coords);
|
||||||
|
|
||||||
|
/* Apply rotation back to BMesh. */
|
||||||
|
if (angle != 0.0f) {
|
||||||
|
float matrix[2][2];
|
||||||
|
float pre_translate[2] = {0, 0};
|
||||||
|
angle_to_mat2(matrix, angle);
|
||||||
|
matrix[1][0] *= 1.0f / aspect_y;
|
||||||
|
/* matrix[1][1] *= aspect_y / aspect_y; */
|
||||||
|
matrix[0][1] *= aspect_y;
|
||||||
|
island_uv_transform(island, matrix, pre_translate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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],
|
||||||
|
const 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool island_has_pins(const Scene *scene,
|
||||||
|
FaceIsland *island,
|
||||||
|
const UVPackIsland_Params *params)
|
||||||
|
{
|
||||||
|
const bool pin_unselected = params->pin_unselected;
|
||||||
|
const bool only_selected_faces = params->only_selected_faces;
|
||||||
|
BMLoop *l;
|
||||||
|
BMIter iter;
|
||||||
|
const int pin_offset = island->offsets.pin;
|
||||||
|
for (int i = 0; i < island->faces_len; i++) {
|
||||||
|
BMFace *efa = island->faces[i];
|
||||||
|
if (pin_unselected && only_selected_faces && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
BM_ITER_ELEM (l, &iter, island->faces[i], BM_LOOPS_OF_FACE) {
|
||||||
|
if (BM_ELEM_CD_GET_BOOL(l, pin_offset)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (pin_unselected && !uvedit_uv_select_test(scene, l, island->offsets)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pack UV islands from multiple objects.
|
||||||
|
*
|
||||||
|
* \param scene: Scene containing the objects to be packed.
|
||||||
|
* \param objects: Array of Objects to pack.
|
||||||
|
* \param objects_len: Length of `objects` array.
|
||||||
|
* \param bmesh_override: BMesh array aligned with `objects`.
|
||||||
|
* Optional, when non-null this overrides object's BMesh.
|
||||||
|
* This is needed to perform UV packing on objects that aren't in edit-mode.
|
||||||
|
* \param udim_params: Parameters to specify UDIM target and UDIM source image.
|
||||||
|
* \param params: Parameters and options to pass to the packing engine.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void ED_uvedit_pack_islands_multi(const Scene *scene,
|
||||||
|
Object **objects,
|
||||||
|
const int objects_len,
|
||||||
|
BMesh **bmesh_override,
|
||||||
|
const UVMapUDIM_Params *closest_udim,
|
||||||
|
const UVPackIsland_Params *params)
|
||||||
|
{
|
||||||
|
blender::Vector<FaceIsland *> island_vector;
|
||||||
|
|
||||||
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
||||||
|
Object *obedit = objects[ob_index];
|
||||||
|
BMesh *bm = nullptr;
|
||||||
|
if (bmesh_override) {
|
||||||
|
/* Note: obedit is still required for aspect ratio and ID_RECALC_GEOMETRY. */
|
||||||
|
bm = bmesh_override[ob_index];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||||
|
bm = em->bm;
|
||||||
|
}
|
||||||
|
BLI_assert(bm);
|
||||||
|
|
||||||
|
const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
|
||||||
|
if (offsets.uv == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float aspect_y = params->correct_aspect ? ED_uvedit_get_aspect_y(obedit) : 1.0f;
|
||||||
|
|
||||||
|
bool only_selected_faces = params->only_selected_faces;
|
||||||
|
bool only_selected_uvs = params->only_selected_uvs;
|
||||||
|
if (params->ignore_pinned && params->pin_unselected) {
|
||||||
|
only_selected_faces = false;
|
||||||
|
only_selected_uvs = false;
|
||||||
|
}
|
||||||
|
ListBase island_list = {nullptr};
|
||||||
|
bm_mesh_calc_uv_islands(scene,
|
||||||
|
bm,
|
||||||
|
&island_list,
|
||||||
|
only_selected_faces,
|
||||||
|
only_selected_uvs,
|
||||||
|
params->use_seams,
|
||||||
|
aspect_y,
|
||||||
|
offsets);
|
||||||
|
|
||||||
|
/* Remove from linked list and append to blender::Vector. */
|
||||||
|
LISTBASE_FOREACH_MUTABLE (struct FaceIsland *, island, &island_list) {
|
||||||
|
BLI_remlink(&island_list, island);
|
||||||
|
if (params->ignore_pinned && island_has_pins(scene, island, params)) {
|
||||||
|
MEM_freeN(island->faces);
|
||||||
|
MEM_freeN(island);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
island_vector.append(island);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (island_vector.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
for (int index = 0; index < island_vector.size(); index++) {
|
||||||
|
FaceIsland *island = island_vector[index];
|
||||||
|
if (closest_udim) {
|
||||||
|
/* Only calculate selection bounding box if using closest_udim. */
|
||||||
|
for (int i = 0; i < island->faces_len; i++) {
|
||||||
|
BMFace *f = island->faces[i];
|
||||||
|
BM_face_uv_minmax(f, selection_min_co, selection_max_co, island->offsets.uv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params->rotate) {
|
||||||
|
face_island_uv_rotate_fit_aabb(island);
|
||||||
|
}
|
||||||
|
|
||||||
|
bm_face_array_calc_bounds(
|
||||||
|
island->faces, island->faces_len, island->offsets.uv, &island->bounds_rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Center of bounding box containing all selected UVs. */
|
||||||
|
float selection_center[2];
|
||||||
|
if (closest_udim) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
float scale[2] = {1.0f, 1.0f};
|
||||||
|
blender::Vector<blender::geometry::PackIsland *> pack_island_vector;
|
||||||
|
for (int i = 0; i < island_vector.size(); i++) {
|
||||||
|
FaceIsland *face_island = island_vector[i];
|
||||||
|
blender::geometry::PackIsland *pack_island = new blender::geometry::PackIsland();
|
||||||
|
pack_island->bounds_rect = face_island->bounds_rect;
|
||||||
|
pack_island_vector.append(pack_island);
|
||||||
|
}
|
||||||
|
BoxPack *box_array = pack_islands(pack_island_vector, *params, scale);
|
||||||
|
|
||||||
|
float base_offset[2] = {0.0f, 0.0f};
|
||||||
|
copy_v2_v2(base_offset, params->udim_base_offset);
|
||||||
|
|
||||||
|
if (closest_udim) {
|
||||||
|
const Image *image = closest_udim->image;
|
||||||
|
const int *udim_grid = closest_udim->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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float matrix[2][2];
|
||||||
|
float matrix_inverse[2][2];
|
||||||
|
float pre_translate[2];
|
||||||
|
for (int i = 0; i < island_vector.size(); i++) {
|
||||||
|
FaceIsland *island = island_vector[box_array[i].index];
|
||||||
|
matrix[0][0] = scale[0];
|
||||||
|
matrix[0][1] = 0.0f;
|
||||||
|
matrix[1][0] = 0.0f;
|
||||||
|
matrix[1][1] = scale[1];
|
||||||
|
invert_m2_m2(matrix_inverse, matrix);
|
||||||
|
|
||||||
|
/* Add base_offset, post transform. */
|
||||||
|
mul_v2_m2v2(pre_translate, matrix_inverse, base_offset);
|
||||||
|
|
||||||
|
/* Translate to box_array from bounds_rect. */
|
||||||
|
pre_translate[0] += box_array[i].x - island->bounds_rect.xmin;
|
||||||
|
pre_translate[1] += box_array[i].y - island->bounds_rect.ymin;
|
||||||
|
island_uv_transform(island, matrix, pre_translate);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
||||||
|
Object *obedit = objects[ob_index];
|
||||||
|
DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
|
||||||
|
WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (FaceIsland *island : island_vector) {
|
||||||
|
MEM_freeN(island->faces);
|
||||||
|
MEM_freeN(island);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < pack_island_vector.size(); i++) {
|
||||||
|
blender::geometry::PackIsland *pack_island = pack_island_vector[i];
|
||||||
|
pack_island_vector[i] = nullptr;
|
||||||
|
delete pack_island;
|
||||||
|
}
|
||||||
|
|
||||||
|
MEM_freeN(box_array);
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
/** \name Pack UV Islands Operator
|
/** \name Pack UV Islands Operator
|
||||||
* \{ */
|
* \{ */
|
||||||
|
@ -11,9 +11,6 @@
|
|||||||
* \ingroup geo
|
* \ingroup geo
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Workaround to forward-declare C type in C++ header. */
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
enum eUVPackIsland_MarginMethod {
|
enum eUVPackIsland_MarginMethod {
|
||||||
ED_UVPACK_MARGIN_SCALED = 0, /* Use scale of existing UVs to multiply margin. */
|
ED_UVPACK_MARGIN_SCALED = 0, /* Use scale of existing UVs to multiply margin. */
|
||||||
ED_UVPACK_MARGIN_ADD, /* Just add the margin, ignoring any UV scale. */
|
ED_UVPACK_MARGIN_ADD, /* Just add the margin, ignoring any UV scale. */
|
||||||
@ -43,7 +40,6 @@ struct UVPackIsland_Params {
|
|||||||
/** Additional translation for bottom left corner. */
|
/** Additional translation for bottom left corner. */
|
||||||
float udim_base_offset[2];
|
float udim_base_offset[2];
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
namespace blender::geometry {
|
namespace blender::geometry {
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user