Compare commits
80 Commits
temp-pytho
...
soc-2021-u
Author | SHA1 | Date | |
---|---|---|---|
560e015a5b | |||
f87ed1547c | |||
1b9dd08d02 | |||
0c2bc843f5 | |||
98deceb5c1 | |||
da8963af2d | |||
72ee339710 | |||
1c74abfa4b | |||
f6e594f1ee | |||
8c3128baae | |||
798214c1f6 | |||
e981df9fbd | |||
112a532f41 | |||
aa0b05ef70 | |||
c3e996a118 | |||
c7a2086253 | |||
0ac3da0e45 | |||
![]() |
a39edbfb19 | ||
e6a68e9511 | |||
1ea018eb4d | |||
08697cff10 | |||
395056561b | |||
6669c559e3 | |||
cacfdcd041 | |||
4362228562 | |||
4ea6595af0 | |||
8506edc0b8 | |||
5c4bd878a2 | |||
![]() |
ccf4103c1a | ||
76e4ffdb90 | |||
97f82a2224 | |||
2581039d55 | |||
1e6fef3aa1 | |||
446f488685 | |||
91a2f5583e | |||
![]() |
aa33073004 | ||
![]() |
bb5373ad7b | ||
![]() |
791cb92b96 | ||
3f0d85de05 | |||
506a2f43b6 | |||
1f13ff614d | |||
c6e1b2f015 | |||
790c6740dc | |||
16cd543e4f | |||
![]() |
f36013a78d | ||
11ef414ba3 | |||
39df796ec9 | |||
e17731fc9c | |||
65c36dc583 | |||
8804c698eb | |||
d6ddacabc1 | |||
9974edc857 | |||
7d5ed35602 | |||
cd75125c48 | |||
e1abd5947f | |||
8d642bbba6 | |||
5777ec9af9 | |||
86023928ba | |||
708f375f76 | |||
28c85e60cb | |||
d615f4d65e | |||
eb88ce5146 | |||
1f65001cae | |||
8a57e48a8f | |||
be270d8c8a | |||
39aa006260 | |||
1965df11f4 | |||
ad5983895a | |||
37e185980a | |||
7e1e4889c6 | |||
62d2e8130f | |||
2b37edebdf | |||
e5ab28d392 | |||
2771b931b5 | |||
f76eca3af2 | |||
279a67ecc8 | |||
1a89001151 | |||
2126fc815d | |||
790d11899a | |||
6a422b6624 |
@@ -437,6 +437,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()
|
||||
@@ -933,6 +934,10 @@ class IMAGE_PT_snapping(Panel):
|
||||
col.label(text="Target")
|
||||
row = col.row(align=True)
|
||||
row.prop(tool_settings, "snap_target", expand=True)
|
||||
|
||||
col.separator()
|
||||
if 'INCREMENT' in tool_settings.snap_uv_element:
|
||||
col.prop(tool_settings, "use_snap_uv_grid_absolute")
|
||||
|
||||
col.label(text="Affect")
|
||||
row = col.row(align=True)
|
||||
@@ -1467,6 +1472,35 @@ class IMAGE_PT_udim_grid(Panel):
|
||||
col = layout.column()
|
||||
col.prop(uvedit, "tile_grid_shape", text="Grid Shape")
|
||||
|
||||
class IMAGE_PT_dynamic_grid(Panel):
|
||||
bl_space_type = 'IMAGE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "View"
|
||||
bl_label = "Dynamic Grid"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
sima = context.space_data
|
||||
#Grid becomes irrelevant once an image is loaded in the UV Editor
|
||||
return sima.show_uvedit and sima.image is None
|
||||
|
||||
#Not exposed in the Image editor and disabled by default
|
||||
def draw_header(self, context):
|
||||
sima = context.space_data
|
||||
uvedit = sima.uv_editor
|
||||
self.layout.prop(uvedit, "use_dynamic_grid", text="")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
sima = context.space_data
|
||||
uvedit = sima.uv_editor
|
||||
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
col = layout.column()
|
||||
col.prop(uvedit, "dynamic_grid_size", text="Grid Size")
|
||||
|
||||
class IMAGE_PT_overlay(Panel):
|
||||
bl_space_type = 'IMAGE_EDITOR'
|
||||
@@ -1652,6 +1686,7 @@ classes = (
|
||||
IMAGE_PT_uv_cursor,
|
||||
IMAGE_PT_annotation,
|
||||
IMAGE_PT_udim_grid,
|
||||
IMAGE_PT_dynamic_grid,
|
||||
IMAGE_PT_overlay,
|
||||
IMAGE_PT_overlay_uv_edit,
|
||||
IMAGE_PT_overlay_uv_edit_geometry,
|
||||
|
@@ -775,13 +775,15 @@ int BKE_image_find_nearest_tile(const Image *image, const float co[2])
|
||||
LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) {
|
||||
const int tile_index = tile->tile_number - 1001;
|
||||
/* Coordinates of the current tile. */
|
||||
const float tile_index_co[2] = {tile_index % 10, tile_index / 10};
|
||||
float tile_index_co[2] = {tile_index % 10, tile_index / 10};
|
||||
|
||||
if (equals_v2v2(co_floor, tile_index_co)) {
|
||||
return tile->tile_number;
|
||||
}
|
||||
|
||||
/* Distance between co[2] and UDIM tile. */
|
||||
/* Distance between co[2] and center of UDIM tile. */
|
||||
tile_index_co[0] += 0.5f;
|
||||
tile_index_co[1] += 0.5f;
|
||||
const float dist_sq = len_squared_v2v2(tile_index_co, co);
|
||||
|
||||
if (dist_sq < dist_best_sq) {
|
||||
|
@@ -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);
|
||||
|
||||
bool BLI_rect_pack_2d(BoxPack *boxarray,
|
||||
const uint len,
|
||||
const float rect_width,
|
||||
const float rect_height);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -784,3 +784,100 @@ 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 */
|
||||
bool 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;
|
||||
bool is_box_packed;
|
||||
|
||||
BLI_addhead(&spaces, full_rect);
|
||||
/* CHECK : Sorting is probably used incorrectly here */
|
||||
qsort(boxarray, (size_t)len, sizeof(BoxPack), box_areasort);
|
||||
|
||||
/* Rotating islands in uvedits_islands.c doesn't work well with this algorithm
|
||||
* TODO : Implement heuristic for rotating islands - Rotate islands if they don't fit in any
|
||||
* empty space, but can fit if they are rotated. A boolean might be needed per island to check if
|
||||
* islands need to be rotated when they are translated in uvedit_islands.c */
|
||||
|
||||
for (uint i = 0; i < len; i++) {
|
||||
is_box_packed = false;
|
||||
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;
|
||||
is_box_packed = true;
|
||||
|
||||
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;
|
||||
}
|
||||
/* Packing area not big enough to pack all boxes */
|
||||
if (!is_box_packed) {
|
||||
BLI_freelistN(&spaces);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* All boxes packed successfully */
|
||||
BLI_freelistN(&spaces);
|
||||
return true;
|
||||
}
|
@@ -23,6 +23,7 @@
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
@@ -46,6 +47,7 @@ enum {
|
||||
GRID_BACK = (1 << 9),
|
||||
GRID_CAMERA = (1 << 10),
|
||||
PLANE_IMAGE = (1 << 11),
|
||||
DYNAMIC_GRID = (1 << 12),
|
||||
};
|
||||
|
||||
void OVERLAY_grid_init(OVERLAY_Data *vedata)
|
||||
@@ -61,6 +63,7 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata)
|
||||
|
||||
if (pd->space_type == SPACE_IMAGE) {
|
||||
SpaceImage *sima = (SpaceImage *)draw_ctx->space_data;
|
||||
View2D *v2d = &draw_ctx->region->v2d;
|
||||
if (sima->mode == SI_MODE_UV || !ED_space_image_has_buffer(sima)) {
|
||||
shd->grid_flag = GRID_BACK | PLANE_IMAGE | SHOW_GRID;
|
||||
}
|
||||
@@ -74,9 +77,32 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata)
|
||||
shd->grid_size[0] = (float)sima->tile_grid_shape[0];
|
||||
shd->grid_size[1] = (float)sima->tile_grid_shape[1];
|
||||
}
|
||||
for (int step = 0; step < 8; step++) {
|
||||
shd->grid_steps[step] = powf(4, step) * (1.0f / 16.0f);
|
||||
/**
|
||||
* Previously the UV/Image Editor grid was static :
|
||||
* - 0-1 UV space divided into 4x4 divisions marked by thick grid lines (1 grid unit = 0.25 UV
|
||||
* units)
|
||||
* - 0-1 UV space divided into 16x16 divisions marked by thin grid lines (1 grid unit = 0.0625
|
||||
* UV units)
|
||||
*
|
||||
* The new UV/Image Editor grid now supports 2 grid types :
|
||||
* - Subdividing grid : Similar to the 3D viewport grid (zooming in adds more divisions to the
|
||||
* grid) [T89789]
|
||||
* - Dynamic grid : Users create a desired NxN grid by using options exposed in UI [T78389]
|
||||
*/
|
||||
if (sima->flag & SI_DYNAMIC_GRID) {
|
||||
shd->grid_flag |= DYNAMIC_GRID;
|
||||
/* Temporary fix : dynamic_grid_size is not using the default value (=1) assignd in RNA */
|
||||
sima->dynamic_grid_size = (sima->dynamic_grid_size == 0) ? 1 : sima->dynamic_grid_size;
|
||||
}
|
||||
/* N denotes the grid dimension when zoomed out (NxN grid).
|
||||
* While zooming in, each grid division further subdivides into smaller NxN divisions
|
||||
*
|
||||
* If this value is changed, then also update the value in initSnapSpatial()
|
||||
* TODO? : Probably best to move this value to SpaceImage/View2D struct */
|
||||
int N = 8;
|
||||
shd->zoom_factor = ED_space_image_zoom_level(v2d, N);
|
||||
|
||||
ED_space_image_grid_steps(sima, shd->grid_steps, N);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -248,6 +274,7 @@ void OVERLAY_grid_cache_init(OVERLAY_Data *vedata)
|
||||
|
||||
grp = DRW_shgroup_create(sh, psl->grid_ps);
|
||||
DRW_shgroup_uniform_int(grp, "gridFlag", &shd->grid_flag, 1);
|
||||
DRW_shgroup_uniform_float_copy(grp, "zoomFactor", shd->zoom_factor);
|
||||
DRW_shgroup_uniform_vec3(grp, "planeAxes", shd->grid_axes, 1);
|
||||
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
|
||||
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
|
||||
|
@@ -142,6 +142,7 @@ typedef struct OVERLAY_ShadingData {
|
||||
float grid_steps[8];
|
||||
float inv_viewport_size[2];
|
||||
float grid_line_size;
|
||||
float zoom_factor; /* Only for UV/Image editor */
|
||||
int grid_flag;
|
||||
int zpos_flag;
|
||||
int zneg_flag;
|
||||
|
@@ -15,6 +15,7 @@ uniform float lineKernel = 0.0;
|
||||
uniform sampler2D depthBuffer;
|
||||
|
||||
uniform int gridFlag;
|
||||
uniform float zoomFactor;
|
||||
|
||||
#define STEPS_LEN 8
|
||||
uniform float gridSteps[STEPS_LEN] = float[](0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0, 10000.0);
|
||||
@@ -28,6 +29,8 @@ uniform float gridSteps[STEPS_LEN] = float[](0.001, 0.01, 0.1, 1.0, 10.0, 100.0,
|
||||
#define PLANE_YZ (1 << 6)
|
||||
#define GRID_BACK (1 << 9) /* grid is behind objects */
|
||||
#define GRID_CAMERA (1 << 10) /* In camera view */
|
||||
#define PLANE_IMAGE (1 << 11) /* For UV/Image Image editor */
|
||||
#define DYNAMIC_GRID (1 << 12) /* Dynamic grid is used in the UV/Image Editor */
|
||||
|
||||
#define M_1_SQRTPI 0.5641895835477563 /* 1/sqrt(pi) */
|
||||
|
||||
@@ -122,9 +125,16 @@ void main()
|
||||
* would be more accurate, but not really necessary. */
|
||||
float grid_res = dot(dFdxPos, screenVecs[0].xyz);
|
||||
|
||||
/* The gride begins to appear when it comprises 4 pixels */
|
||||
/* The grid begins to appear when it comprises 4 pixels */
|
||||
grid_res *= 4;
|
||||
|
||||
/* For UV/Image editor use zoomFactor */
|
||||
if((gridFlag & PLANE_IMAGE) != 0 && (gridFlag & DYNAMIC_GRID) == 0)
|
||||
{/* Grid begins to appear when the length of one grid unit is at least 256/N pixels (for NxN grid)
|
||||
* Value of N defined in overlay_grid.c */
|
||||
grid_res = zoomFactor;
|
||||
}
|
||||
|
||||
/* from biggest to smallest */
|
||||
vec4 scale;
|
||||
#if 0
|
||||
|
@@ -41,6 +41,16 @@ struct SpaceImage;
|
||||
struct bContext;
|
||||
struct wmOperator;
|
||||
struct wmWindowManager;
|
||||
struct View2D;
|
||||
|
||||
/* image_draw.c (Utility functions - should probably be moved to a BKE header) */
|
||||
float ED_space_image_zoom_level(const struct View2D *v2d, const int grid_dimension);
|
||||
void ED_space_image_grid_steps(struct SpaceImage *sima,
|
||||
float grid_steps[8],
|
||||
const int grid_dimension);
|
||||
float ED_space_image_increment_snap_value(const int grid_dimesnions,
|
||||
const float grid_steps[8],
|
||||
const float zoom_factor);
|
||||
|
||||
/* image_edit.c, exported for transform */
|
||||
struct Image *ED_space_image(struct SpaceImage *sima);
|
||||
|
@@ -247,10 +247,21 @@ struct UVPackIsland_Params {
|
||||
uint correct_aspect : 1;
|
||||
};
|
||||
void ED_uvedit_pack_islands_multi(const struct Scene *scene,
|
||||
const struct SpaceImage *sima,
|
||||
Object **objects,
|
||||
const uint objects_len,
|
||||
const bool use_target_udim,
|
||||
int target_udim,
|
||||
const struct UVPackIsland_Params *params);
|
||||
|
||||
bool 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
|
||||
|
@@ -34,6 +34,7 @@
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_view2d_types.h"
|
||||
|
||||
#include "PIL_time.h"
|
||||
|
||||
@@ -576,3 +577,57 @@ void draw_image_cache(const bContext *C, ARegion *region)
|
||||
ED_mask_draw_frames(mask, region, cfra, sfra, efra);
|
||||
}
|
||||
}
|
||||
|
||||
float ED_space_image_zoom_level(const View2D *v2d, const int grid_dimension)
|
||||
{
|
||||
/* UV-space length per pixel */
|
||||
float xzoom = (v2d->cur.xmax - v2d->cur.xmin) / ((float)(v2d->mask.xmax - v2d->mask.xmin));
|
||||
float yzoom = (v2d->cur.ymax - v2d->cur.ymin) / ((float)(v2d->mask.ymax - v2d->mask.ymin));
|
||||
|
||||
/* zoom_factor for UV/Image editor is calculated based on :
|
||||
* - Default grid size on startup, which is 256x256 pixels
|
||||
* - How blend factor for grid lines is set up in the fragment shader (grid_frag.glsl) */
|
||||
float zoom_factor;
|
||||
zoom_factor = (xzoom + yzoom) / 2.0f; /* Average for accuracy */
|
||||
zoom_factor *= 256.0f / (powf(grid_dimension, 2));
|
||||
return zoom_factor;
|
||||
}
|
||||
|
||||
void ED_space_image_grid_steps(SpaceImage *sima, float grid_steps[8], const int grid_dimension)
|
||||
{
|
||||
if (sima->flag & SI_DYNAMIC_GRID) {
|
||||
for (int step = 0; step < 8; step++) {
|
||||
grid_steps[step] = powf(1, step) * (1.0f / ((float)sima->dynamic_grid_size));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int step = 0; step < 8; step++) {
|
||||
grid_steps[step] = powf(grid_dimension, step) * (1.0f / (powf(grid_dimension, 8)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the increment snapping value for UV/image editor based on the zoom factor
|
||||
* The code in here (except the offset part) is used in `grid_frag.glsl` (see `grid_res`) for
|
||||
* drawing the grid overlay for the UV/Image editor */
|
||||
float ED_space_image_increment_snap_value(const int grid_dimesnions,
|
||||
const float grid_steps[8],
|
||||
const float zoom_factor)
|
||||
{
|
||||
/* Small offset on each grid_steps[] so that snapping value doesn't change until grid lines are
|
||||
* significantly visible.
|
||||
* Offset = 3/4 * (grid_steps[i] - (grid_steps[i]/grid_dimesnsions))
|
||||
*
|
||||
* Refer grid_frag.glsl to find out when grid lines actually start appearing */
|
||||
|
||||
for (int step = 0; step < 8; step++) {
|
||||
float offset = (3.0f / 4.0f) * (grid_steps[step] - (grid_steps[step] / grid_dimesnions));
|
||||
|
||||
if ((grid_steps[step] - offset) > zoom_factor) {
|
||||
return grid_steps[step];
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback */
|
||||
return grid_steps[0];
|
||||
}
|
@@ -28,6 +28,7 @@
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_mask_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_rect.h"
|
||||
@@ -1609,8 +1610,21 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2])
|
||||
}
|
||||
}
|
||||
else if (t->spacetype == SPACE_IMAGE) {
|
||||
r_snap[0] = 0.0625f;
|
||||
r_snap[1] = 0.03125f;
|
||||
SpaceImage *sima = t->area->spacedata.first;
|
||||
View2D *v2d = &t->region->v2d;
|
||||
/* N denotes the grid dimension when zoomed out (NxN grid).
|
||||
* While zooming in, each grid division further subdivides into smaller NxN divisions
|
||||
*
|
||||
* If this value is changed, then also update the value in OVERLAY_grid_init()
|
||||
* TODO? : Probably best to move this value to SpaceImage/View2D struct */
|
||||
int N = 8;
|
||||
float zoom_factor = ED_space_image_zoom_level(v2d, N);
|
||||
float grid_steps[8];
|
||||
|
||||
ED_space_image_grid_steps(sima, grid_steps, N);
|
||||
/* Snapping value based on what type of grid is used (subdividing or dynamic) */
|
||||
r_snap[0] = ED_space_image_increment_snap_value(N, grid_steps, zoom_factor);
|
||||
r_snap[1] = r_snap[0] / 2.0f;
|
||||
}
|
||||
else if (t->spacetype == SPACE_CLIP) {
|
||||
r_snap[0] = 0.125f;
|
||||
|
@@ -590,6 +590,12 @@ static void initSnappingMode(TransInfo *t)
|
||||
t->tsnap.project = 0;
|
||||
|
||||
t->tsnap.mode = ts->snap_uv_mode;
|
||||
/* NOTE : For now, absolute grid snap only works with translation */
|
||||
if ((t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_flag & SCE_SNAP_ABS_UV_GRID) &&
|
||||
(t->mode == TFM_TRANSLATION)) {
|
||||
t->tsnap.mode &= ~SCE_SNAP_MODE_INCREMENT;
|
||||
t->tsnap.mode |= SCE_SNAP_MODE_GRID;
|
||||
}
|
||||
}
|
||||
else if (t->spacetype == SPACE_SEQ) {
|
||||
t->tsnap.mode = SEQ_tool_settings_snap_mode_get(t->scene);
|
||||
@@ -1502,7 +1508,8 @@ bool transform_snap_grid(TransInfo *t, float *val)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (t->spacetype != SPACE_VIEW3D) {
|
||||
/* Don't do grid snapping if not in 3D viewport or UV editor */
|
||||
if (!((t->spacetype == SPACE_VIEW3D) || (t->spacetype == SPACE_IMAGE))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -29,6 +29,7 @@
|
||||
|
||||
#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"
|
||||
@@ -38,6 +39,7 @@
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_editmesh.h"
|
||||
#include "BKE_image.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
@@ -357,8 +359,11 @@ static int bm_mesh_calc_uv_islands(const Scene *scene,
|
||||
* \{ */
|
||||
|
||||
void ED_uvedit_pack_islands_multi(const Scene *scene,
|
||||
const SpaceImage *sima,
|
||||
Object **objects,
|
||||
const uint objects_len,
|
||||
const bool use_target_udim,
|
||||
int target_udim,
|
||||
const struct UVPackIsland_Params *params)
|
||||
{
|
||||
/* Align to the Y axis, could make this configurable. */
|
||||
@@ -407,8 +412,25 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
|
||||
BoxPack *boxarray = MEM_mallocN(sizeof(*boxarray) * island_list_len, __func__);
|
||||
|
||||
int index;
|
||||
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 not using Specified UDIM option */
|
||||
if (!use_target_udim) {
|
||||
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(
|
||||
@@ -441,6 +463,265 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
|
||||
}
|
||||
}
|
||||
|
||||
/* Center of the selected UV bounding boxes */
|
||||
float selection_center[2];
|
||||
if (!use_target_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;
|
||||
}
|
||||
|
||||
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: Specified UDIM */
|
||||
if (use_target_udim) {
|
||||
const int specified_tile_index = target_udim - 1001;
|
||||
/* Calculate offset based on specified_tile_index */
|
||||
base_offset[0] = specified_tile_index % 10;
|
||||
base_offset[1] = specified_tile_index / 10;
|
||||
}
|
||||
|
||||
/* CASE: Closest UDIM */
|
||||
else {
|
||||
const Image *image;
|
||||
bool is_tiled_image = false;
|
||||
int udim_grid[2] = {1, 1};
|
||||
|
||||
/* To handle cases where sima=NULL - Smart UV project in 3D viewport */
|
||||
if (sima != NULL) {
|
||||
image = sima->image;
|
||||
is_tiled_image = image && (image->source == IMA_SRC_TILED);
|
||||
udim_grid[0] = sima->tile_grid_shape[0];
|
||||
udim_grid[1] = sima->tile_grid_shape[1];
|
||||
}
|
||||
|
||||
/* Check if selection lies on a valid UDIM grid tile */
|
||||
bool is_valid_udim = false;
|
||||
const float selection_co_floor[2] = {floorf(selection_center[0]), floorf(selection_center[1])};
|
||||
if (selection_center[0] < udim_grid[0] && selection_center[0] > 0 &&
|
||||
selection_center[1] < udim_grid[1] && selection_center[1] > 0) {
|
||||
base_offset[0] = selection_co_floor[0];
|
||||
base_offset[1] = selection_co_floor[1];
|
||||
is_valid_udim = true;
|
||||
}
|
||||
/* Check if selection lies on a valid UDIM image tile */
|
||||
else 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 (selection_co_floor[0] == target_x && selection_co_floor[1] == target_y) {
|
||||
base_offset[0] = selection_co_floor[0];
|
||||
base_offset[1] = selection_co_floor[1];
|
||||
is_valid_udim = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Probably not required since UDIM grid checks for 1001 */
|
||||
else if (image && !is_tiled_image) {
|
||||
if (is_zero_v2(selection_co_floor)) {
|
||||
base_offset[0] = selection_co_floor[0];
|
||||
base_offset[1] = selection_co_floor[1];
|
||||
is_valid_udim = true;
|
||||
}
|
||||
}
|
||||
/* If selection doesn't lie on any UDIM then compare both closest grid tile and image tile.
|
||||
* Save the one that is closest */
|
||||
if (!is_valid_udim) {
|
||||
float nearest_image_tile_co[2] = {FLT_MAX, FLT_MAX};
|
||||
if (image) {
|
||||
int nearest_image_tile_index = BKE_image_find_nearest_tile(image, selection_center);
|
||||
if (nearest_image_tile_index == -1) {
|
||||
nearest_image_tile_index = 1001;
|
||||
}
|
||||
/* Calculate offset based on nearest_tile_index */
|
||||
|
||||
nearest_image_tile_co[0] = (nearest_image_tile_index - 1001) % 10;
|
||||
nearest_image_tile_co[1] = (nearest_image_tile_index - 1001) / 10;
|
||||
/* + 0.5f to get tile center coordinates */
|
||||
nearest_image_tile_co[0] += 0.5f;
|
||||
nearest_image_tile_co[1] += 0.5f;
|
||||
}
|
||||
|
||||
float nearest_grid_tile_co[2] = {0.0f, 0.0f};
|
||||
if (selection_center[0] > udim_grid[0]) {
|
||||
nearest_grid_tile_co[0] = udim_grid[0] - 1;
|
||||
}
|
||||
else if (selection_center[0] < 0) {
|
||||
nearest_grid_tile_co[0] = 0;
|
||||
}
|
||||
else {
|
||||
nearest_grid_tile_co[0] = selection_co_floor[0];
|
||||
}
|
||||
|
||||
if (selection_center[1] > udim_grid[1]) {
|
||||
nearest_grid_tile_co[1] = udim_grid[1] - 1;
|
||||
}
|
||||
else if (selection_center[1] < 0) {
|
||||
nearest_grid_tile_co[1] = 0;
|
||||
}
|
||||
else {
|
||||
nearest_grid_tile_co[1] = selection_co_floor[1];
|
||||
}
|
||||
/* + 0.5f to get tile center coordinates */
|
||||
nearest_grid_tile_co[0] += 0.5f;
|
||||
nearest_grid_tile_co[1] += 0.5f;
|
||||
|
||||
float nearest_image_tile_dist = len_squared_v2v2(selection_center, nearest_image_tile_co);
|
||||
float nearest_grid_tile_dist = len_squared_v2v2(selection_center, nearest_grid_tile_co);
|
||||
|
||||
base_offset[0] = (nearest_image_tile_dist < nearest_grid_tile_dist) ?
|
||||
(nearest_image_tile_co[0] - 0.5f) :
|
||||
(nearest_grid_tile_co[0] - 0.5f);
|
||||
base_offset[1] = (nearest_image_tile_dist < nearest_grid_tile_dist) ?
|
||||
(nearest_image_tile_co[1] - 0.5f) :
|
||||
(nearest_grid_tile_co[1] - 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* 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() */
|
||||
bool 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 true;
|
||||
}
|
||||
|
||||
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 : Use 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(
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (margin > 0.0f) {
|
||||
/* Logic matches behavior from #param_pack,
|
||||
* use area so multiply the margin by the area to give
|
||||
@@ -456,13 +737,41 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
|
||||
}
|
||||
}
|
||||
|
||||
float boxarray_size[2];
|
||||
BLI_box_pack_2d(boxarray, island_list_len, &boxarray_size[0], &boxarray_size[1]);
|
||||
if (scale_islands) {
|
||||
/* The problem statement for pack islands to box area operator is : Pack a given set of
|
||||
* rectangular boxes to a rectangular area. Scaling down the width by W (width of rectangualr
|
||||
* area) and height by H (height of rectangualr area) for all the boxes and the packing area,
|
||||
* the new problem statement becomes : packing a set of rectangular boxes to a square area ->
|
||||
* Similar to the original pack islands operator */
|
||||
for (int i = 0; i < island_list_len; i++) {
|
||||
boxarray[i].w /= (max_co[0] - min_co[0]);
|
||||
boxarray[i].h /= (max_co[1] - min_co[1]);
|
||||
}
|
||||
}
|
||||
float boxarray_size[2] = {0.0f, 0.0f};
|
||||
float scale[2] = {1.0f, 1.0f};
|
||||
|
||||
/* 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]};
|
||||
/* If scaling is enabled then use the original pack islands algorithm, otherwise use the
|
||||
* RectPack2D algorithm for packing */
|
||||
if (scale_islands) {
|
||||
BLI_box_pack_2d(boxarray, island_list_len, &boxarray_size[0], &boxarray_size[1]);
|
||||
boxarray_size[0] = boxarray_size[1] = max_ff(boxarray_size[0], boxarray_size[1]);
|
||||
scale[0] = 1.0f / boxarray_size[0];
|
||||
scale[1] = 1.0f / boxarray_size[1];
|
||||
}
|
||||
else {
|
||||
if (!BLI_rect_pack_2d(
|
||||
boxarray, island_list_len, (max_co[0] - min_co[0]), (max_co[1] - min_co[1]))) {
|
||||
/* Cancel operator : Packing area not big enough for selected islands */
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < island_list_len; i++) {
|
||||
struct FaceIsland *island = island_array[boxarray[i].index];
|
||||
@@ -470,10 +779,21 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
|
||||
island->bounds_rect.xmin,
|
||||
island->bounds_rect.ymin,
|
||||
};
|
||||
const float offset[2] = {
|
||||
(boxarray[i].x * scale[0]) - island->bounds_rect.xmin,
|
||||
(boxarray[i].y * scale[1]) - island->bounds_rect.ymin,
|
||||
};
|
||||
|
||||
float offset[2];
|
||||
if (scale_islands) {
|
||||
/* Scale boxes back to original dimensions and offset them to the position of the packing
|
||||
* area */
|
||||
offset[0] = (boxarray[i].x * scale[0] * (max_co[0] - min_co[0])) - island->bounds_rect.xmin +
|
||||
min_co[0];
|
||||
offset[1] = (boxarray[i].y * scale[1] * (max_co[1] - min_co[1])) - island->bounds_rect.ymin +
|
||||
min_co[1];
|
||||
}
|
||||
else {
|
||||
offset[0] = (boxarray[i].x) - island->bounds_rect.xmin + min_co[0];
|
||||
offset[1] = (boxarray[i].y) - island->bounds_rect.ymin + min_co[1];
|
||||
}
|
||||
|
||||
for (int j = 0; j < island->faces_len; j++) {
|
||||
BMFace *efa = island->faces[j];
|
||||
bm_face_uv_translate_and_scale_around_pivot(
|
||||
@@ -494,6 +814,7 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
|
||||
|
||||
MEM_freeN(island_array);
|
||||
MEM_freeN(boxarray);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
/* All islands packed successfully */
|
||||
return true;
|
||||
}
|
||||
|
@@ -1338,6 +1338,172 @@ static void UV_OT_snap_selected(wmOperatorType *ot)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV offset Operator
|
||||
* \{ */
|
||||
|
||||
/* Since pixel resolution is not implemented yet (refer T78405), pixel offset is not implemented in
|
||||
* this operator */
|
||||
enum {
|
||||
UDIM_OFFSET_UP = 0,
|
||||
UDIM_OFFSET_DOWN,
|
||||
UDIM_OFFSET_LEFT,
|
||||
UDIM_OFFSET_RIGHT,
|
||||
DYNAMIC_GRID_OFFSET_UP,
|
||||
DYNAMIC_GRID_OFFSET_DOWN,
|
||||
DYNAMIC_GRID_OFFSET_LEFT,
|
||||
DYNAMIC_GRID_OFFSET_RIGHT,
|
||||
};
|
||||
|
||||
static void uv_calc_offset(const SpaceImage *sima, const int offset_direction, float uv_offset[2])
|
||||
{
|
||||
const bool is_dynamic_grid = sima->flag & SI_DYNAMIC_GRID;
|
||||
zero_v2(uv_offset);
|
||||
/* Assign offset based on the keymap input */
|
||||
switch (offset_direction) {
|
||||
case UDIM_OFFSET_UP: {
|
||||
uv_offset[1] = 1.0f;
|
||||
break;
|
||||
}
|
||||
case UDIM_OFFSET_DOWN: {
|
||||
uv_offset[1] = -1.0f;
|
||||
break;
|
||||
}
|
||||
case UDIM_OFFSET_LEFT: {
|
||||
uv_offset[0] = -1.0f;
|
||||
break;
|
||||
}
|
||||
case UDIM_OFFSET_RIGHT: {
|
||||
uv_offset[0] = 1.0f;
|
||||
break;
|
||||
}
|
||||
case DYNAMIC_GRID_OFFSET_UP: {
|
||||
if (is_dynamic_grid) {
|
||||
uv_offset[1] = 1.0f / ((float)sima->dynamic_grid_size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DYNAMIC_GRID_OFFSET_DOWN: {
|
||||
if (is_dynamic_grid) {
|
||||
uv_offset[1] = (-1.0f) / ((float)sima->dynamic_grid_size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DYNAMIC_GRID_OFFSET_RIGHT: {
|
||||
if (is_dynamic_grid) {
|
||||
uv_offset[0] = 1.0f / ((float)sima->dynamic_grid_size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DYNAMIC_GRID_OFFSET_LEFT: {
|
||||
if (is_dynamic_grid) {
|
||||
uv_offset[0] = (-1.0f) / ((float)sima->dynamic_grid_size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/* Pass */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int uv_offset_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
const SpaceImage *sima = CTX_wm_space_image(C);
|
||||
const ToolSettings *ts = scene->toolsettings;
|
||||
const int offset_direction = RNA_enum_get(op->ptr, "offset_direction");
|
||||
float uv_offset[2] = {0.0f, 0.0f};
|
||||
|
||||
uv_calc_offset(sima, offset_direction, uv_offset);
|
||||
if (is_zero_v2(uv_offset)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BMFace *efa;
|
||||
BMLoop *l;
|
||||
BMIter iter, liter;
|
||||
MLoopUV *luv;
|
||||
|
||||
uint objects_len = 0;
|
||||
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
|
||||
view_layer, ((View3D *)NULL), &objects_len);
|
||||
|
||||
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
||||
Object *obedit = objects[ob_index];
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
bool changed = false;
|
||||
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
|
||||
|
||||
if ((ts->uv_flag & UV_SYNC_SELECTION) && (em->bm->totvertsel == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
|
||||
if (!uvedit_face_visible_test(scene, efa)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
|
||||
if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
|
||||
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
|
||||
add_v2_v2(luv->uv, uv_offset);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
|
||||
WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
|
||||
}
|
||||
}
|
||||
MEM_freeN(objects);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
/* Refer Task T78405 */
|
||||
static void UV_OT_offset(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Offset UVs";
|
||||
ot->idname = "UV_OT_offset";
|
||||
ot->description = "Offset selected UVs by a fixed distance in a specified direction";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = uv_offset_exec;
|
||||
ot->poll = ED_operator_uvedit_space_image;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* RNA properties */
|
||||
static const EnumPropertyItem offset_direction[] = {
|
||||
{UDIM_OFFSET_UP, "UDIM_UP", 0, "Udim Up", ""},
|
||||
{UDIM_OFFSET_DOWN, "UDIM_DOWN", 0, "Udim Down", ""},
|
||||
{UDIM_OFFSET_LEFT, "UDIM_LEFT", 0, "Udim Left", ""},
|
||||
{UDIM_OFFSET_RIGHT, "UDIM_RIGHT", 0, "Udim Right", ""},
|
||||
{DYNAMIC_GRID_OFFSET_UP, "DYNAMIC_GRID_UP", 0, "Dynamic grid Up", ""},
|
||||
{DYNAMIC_GRID_OFFSET_DOWN, "DYNAMIC_GRID_DOWN", 0, "Dynamic grid Down", ""},
|
||||
{DYNAMIC_GRID_OFFSET_LEFT, "DYNAMIC_GRID_LEFT", 0, "Dynamic grid Left", ""},
|
||||
{DYNAMIC_GRID_OFFSET_RIGHT, "DYNAMIC_GRID_RIGHT", 0, "Dynamic grid Right", ""},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
PropertyRNA *prop;
|
||||
prop = RNA_def_enum(ot->srna,
|
||||
"offset_direction",
|
||||
offset_direction,
|
||||
0,
|
||||
"UV Offset Direction",
|
||||
"Offset and direction for moving selected UVs");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Pin UV's Operator
|
||||
* \{ */
|
||||
@@ -2056,6 +2222,7 @@ void ED_operatortypes_uvedit(void)
|
||||
WM_operatortype_append(UV_OT_weld);
|
||||
WM_operatortype_append(UV_OT_remove_doubles);
|
||||
WM_operatortype_append(UV_OT_pin);
|
||||
WM_operatortype_append(UV_OT_offset);
|
||||
|
||||
WM_operatortype_append(UV_OT_average_islands_scale);
|
||||
WM_operatortype_append(UV_OT_cube_project);
|
||||
@@ -2063,6 +2230,7 @@ void ED_operatortypes_uvedit(void)
|
||||
WM_operatortype_append(UV_OT_project_from_view);
|
||||
WM_operatortype_append(UV_OT_minimize_stretch);
|
||||
WM_operatortype_append(UV_OT_pack_islands);
|
||||
WM_operatortype_append(UV_OT_pack_islands_to_area);
|
||||
WM_operatortype_append(UV_OT_reset);
|
||||
WM_operatortype_append(UV_OT_sphere_project);
|
||||
WM_operatortype_append(UV_OT_unwrap);
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include "BLI_alloca.h"
|
||||
#include "BLI_array.h"
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_memarena.h"
|
||||
#include "BLI_string.h"
|
||||
@@ -64,6 +65,7 @@
|
||||
#include "PIL_time.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "ED_image.h"
|
||||
#include "ED_mesh.h"
|
||||
@@ -1005,10 +1007,20 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
||||
}
|
||||
}
|
||||
|
||||
/* Packing targets */
|
||||
enum {
|
||||
CLOSEST_UDIM = 0,
|
||||
SPECIFIED_UDIM = 1,
|
||||
};
|
||||
|
||||
static int pack_islands_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
const SpaceImage *sima = CTX_wm_space_image(C);
|
||||
|
||||
const Image *image = sima->image;
|
||||
const bool is_tiled_image = image && (image->source == IMA_SRC_TILED);
|
||||
|
||||
const UnwrapOptions options = {
|
||||
.topology_from_uvs = true,
|
||||
@@ -1018,17 +1030,19 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
|
||||
.correct_aspect = true,
|
||||
};
|
||||
|
||||
bool rotate = RNA_boolean_get(op->ptr, "rotate");
|
||||
|
||||
uint objects_len = 0;
|
||||
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
|
||||
view_layer, CTX_wm_view3d(C), &objects_len);
|
||||
|
||||
/* Early exit in case no UVs are selected */
|
||||
if (!uvedit_have_selection_multi(scene, objects, objects_len, &options)) {
|
||||
MEM_freeN(objects);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Set RNA props */
|
||||
bool rotate = RNA_boolean_get(op->ptr, "rotate");
|
||||
const bool use_target_udim = (RNA_enum_get(op->ptr, "packTo") == SPECIFIED_UDIM);
|
||||
if (RNA_struct_property_is_set(op->ptr, "margin")) {
|
||||
scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin");
|
||||
}
|
||||
@@ -1036,9 +1050,64 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
|
||||
RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin);
|
||||
}
|
||||
|
||||
int target_udim = 1001;
|
||||
if (use_target_udim) {
|
||||
target_udim = RNA_int_get(op->ptr, "target_udim");
|
||||
if (target_udim > 2000 || target_udim < 1001) {
|
||||
/* Early exit since invalid UDIM was specified.
|
||||
* Before exit, set RNA prop to the value specified when the operator was last used */
|
||||
RNA_int_set(op->ptr, "target_udim", scene->toolsettings->target_udim);
|
||||
MEM_freeN(objects);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Check if specifed UDIM index is valid */
|
||||
bool is_udim_valid = false;
|
||||
const int target_x = ((target_udim - 1001) % 10) + 1;
|
||||
const int target_y = ((target_udim - 1001) / 10) + 1;
|
||||
if (target_x <= sima->tile_grid_shape[0] && target_y <= sima->tile_grid_shape[1]) {
|
||||
scene->toolsettings->target_udim = target_udim;
|
||||
is_udim_valid = true;
|
||||
}
|
||||
else if (is_tiled_image) {
|
||||
LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) {
|
||||
if (target_udim == tile->tile_number) {
|
||||
scene->toolsettings->target_udim = target_udim;
|
||||
is_udim_valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (image && !is_tiled_image) {
|
||||
/* Non-tiled image. Always 0-1 UV space */
|
||||
if (target_udim == 1001) {
|
||||
scene->toolsettings->target_udim = target_udim;
|
||||
is_udim_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_udim_valid) {
|
||||
if (RNA_struct_property_is_set(op->ptr, "target_udim")) {
|
||||
/* Early exit since invalid UDIM was specified */
|
||||
RNA_int_set(op->ptr, "target_udim", scene->toolsettings->target_udim);
|
||||
MEM_freeN(objects);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
else {
|
||||
/* Fallback */
|
||||
target_udim = 1001;
|
||||
scene->toolsettings->target_udim = target_udim;
|
||||
RNA_int_set(op->ptr, "target_udim", scene->toolsettings->target_udim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ED_uvedit_pack_islands_multi(scene,
|
||||
sima,
|
||||
objects,
|
||||
objects_len,
|
||||
use_target_udim,
|
||||
target_udim,
|
||||
&(struct UVPackIsland_Params){
|
||||
.rotate = rotate,
|
||||
.rotate_align_axis = -1,
|
||||
@@ -1048,31 +1117,280 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
|
||||
});
|
||||
|
||||
MEM_freeN(objects);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void pack_islands_ui_draw(bContext *C, wmOperator *op)
|
||||
{
|
||||
uiLayout *layout = op->layout;
|
||||
uiLayout *col;
|
||||
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
uiLayoutSetPropDecorate(layout, false);
|
||||
|
||||
col = uiLayoutColumn(layout, false);
|
||||
|
||||
/* Expose target UDIM property only if packing target is set to Specified UDIM */
|
||||
uiItemR(col, op->ptr, "packTo", 0, NULL, 0);
|
||||
if (RNA_enum_get(op->ptr, "packTo") == SPECIFIED_UDIM) {
|
||||
uiItemR(col, op->ptr, "target_udim", 0, NULL, 0);
|
||||
}
|
||||
|
||||
uiItemR(col, op->ptr, "rotate", 0, NULL, 0);
|
||||
uiItemR(col, op->ptr, "margin", 0, NULL, 0);
|
||||
}
|
||||
|
||||
void UV_OT_pack_islands(wmOperatorType *ot)
|
||||
{
|
||||
static const EnumPropertyItem pack_target[] = {
|
||||
{CLOSEST_UDIM, "CLOSEST_UDIM", 0, "Closest UDIM", "Pack islands to closest UDIM"},
|
||||
{SPECIFIED_UDIM, "SPECIFIED_UDIM", 0, "Specified UDIM", "Pack islands to specified UDIM"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
/* identifiers */
|
||||
ot->name = "Pack Islands";
|
||||
ot->idname = "UV_OT_pack_islands";
|
||||
ot->description = "Transform all islands so that they fill up the UV space as much as possible";
|
||||
ot->description =
|
||||
"Transform all islands so that they fill up the UV/UDIM space as much as possible";
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = pack_islands_exec;
|
||||
ot->poll = ED_operator_uvedit;
|
||||
ot->ui = pack_islands_ui_draw;
|
||||
|
||||
/* properties */
|
||||
RNA_def_enum(ot->srna, "packTo", pack_target, CLOSEST_UDIM, "Pack to", "");
|
||||
RNA_def_boolean(ot->srna, "rotate", true, "Rotate", "Rotate islands for best fit");
|
||||
RNA_def_int(ot->srna,
|
||||
"target_udim",
|
||||
1001,
|
||||
1001,
|
||||
2000,
|
||||
"Target UDIM",
|
||||
"Pack islands to target UDIM",
|
||||
1001,
|
||||
1100);
|
||||
RNA_def_float_factor(
|
||||
ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Pack islands to area Operator
|
||||
* \{ */
|
||||
|
||||
static int pack_islands_to_area_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
|
||||
uint objects_len = 0;
|
||||
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
|
||||
view_layer, CTX_wm_view3d(C), &objects_len);
|
||||
|
||||
/* Packing area coordinates */
|
||||
float box_min_co[2] = {0.0f, 0.0f};
|
||||
float box_max_co[2] = {1.0f, 1.0f};
|
||||
/* Store coordinates for operator rerun */
|
||||
float pack_area[4] = {0.0f, 0.0f, 1.0f, 1.0f};
|
||||
|
||||
rctf bounds;
|
||||
WM_operator_properties_border_to_rctf(op, &bounds);
|
||||
UI_view2d_region_to_view_rctf(®ion->v2d, &bounds, &bounds);
|
||||
/* Bounding coordinates for the user-defined area */
|
||||
box_min_co[0] = bounds.xmin;
|
||||
box_min_co[1] = bounds.ymin;
|
||||
box_max_co[0] = bounds.xmax;
|
||||
box_max_co[1] = bounds.ymax;
|
||||
|
||||
RNA_float_get_array(op->ptr, "pack_area", pack_area);
|
||||
|
||||
/* Running operator through modal callback */
|
||||
if (!RNA_struct_property_is_set(op->ptr, "pack_area")) {
|
||||
RNA_float_set_array(op->ptr, "box_min_co", box_min_co);
|
||||
RNA_float_set_array(op->ptr, "box_max_co", box_max_co);
|
||||
|
||||
pack_area[0] = box_min_co[0];
|
||||
pack_area[1] = box_min_co[1];
|
||||
pack_area[2] = box_max_co[0];
|
||||
pack_area[3] = box_max_co[1];
|
||||
|
||||
RNA_float_set_array(op->ptr, "pack_area", pack_area);
|
||||
/* Scale always true when box select used to define area */
|
||||
RNA_boolean_set(op->ptr, "scale", true);
|
||||
}
|
||||
/* Re-running operator theough properties panel */
|
||||
else {
|
||||
RNA_float_get_array(op->ptr, "box_min_co", box_min_co);
|
||||
RNA_float_get_array(op->ptr, "box_max_co", box_max_co);
|
||||
if ((box_max_co[0] - box_min_co[0]) <= 0.001f || (box_max_co[1] - box_min_co[1]) <= 0.001f) {
|
||||
box_min_co[0] = pack_area[0];
|
||||
box_min_co[1] = pack_area[1];
|
||||
box_max_co[0] = pack_area[2];
|
||||
box_max_co[1] = pack_area[3];
|
||||
RNA_float_set_array(op->ptr, "box_min_co", box_min_co);
|
||||
RNA_float_set_array(op->ptr, "box_max_co", box_max_co);
|
||||
RNA_float_set_array(op->ptr, "pack_area", pack_area);
|
||||
/* CANCEL OPERATOR SINCE INVALID COORDINATES WERE ENTERED */
|
||||
MEM_freeN(objects);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
else {
|
||||
pack_area[0] = box_min_co[0];
|
||||
pack_area[1] = box_min_co[1];
|
||||
pack_area[2] = box_max_co[0];
|
||||
pack_area[3] = box_max_co[1];
|
||||
RNA_float_set_array(op->ptr, "pack_area", pack_area);
|
||||
}
|
||||
}
|
||||
|
||||
/* Keeping a lower bound of 0.001 for user-defined space, smaller than that and the UVs won't be
|
||||
* visible in the UV editor
|
||||
* NOTE : Could be removed/changed */
|
||||
if ((box_max_co[0] - box_min_co[0]) <= 0.001f || (box_max_co[1] - box_min_co[1]) <= 0.001f) {
|
||||
MEM_freeN(objects);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* RNA props */
|
||||
bool rotate_islands = RNA_boolean_get(op->ptr, "rotate");
|
||||
bool scale_islands = RNA_boolean_get(op->ptr, "scale");
|
||||
if (RNA_struct_property_is_set(op->ptr, "margin")) {
|
||||
scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin");
|
||||
}
|
||||
else {
|
||||
RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin);
|
||||
}
|
||||
|
||||
/* Cancel operator in case packing area is not big enough to pack all selected islands */
|
||||
if (!ED_uvedit_pack_islands_to_area_multi(scene,
|
||||
objects,
|
||||
objects_len,
|
||||
box_min_co,
|
||||
box_max_co,
|
||||
scale_islands,
|
||||
&(struct UVPackIsland_Params){
|
||||
.rotate = rotate_islands,
|
||||
.rotate_align_axis = -1,
|
||||
.only_selected_uvs = true,
|
||||
.only_selected_faces = true,
|
||||
.correct_aspect = true,
|
||||
})) {
|
||||
/* Warning might be better than using error. Something similar to
|
||||
* dyntopo_warning_popup() might be ideal in this case */
|
||||
BKE_report(op->reports,
|
||||
RPT_ERROR,
|
||||
"Operator Cancelled. Packing area not big enough for selected islands");
|
||||
RNA_boolean_set(op->ptr, "scale", true);
|
||||
MEM_freeN(objects);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
MEM_freeN(objects);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int pack_islands_to_area_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
/* Check if any UVs are selected in the UV editor before calling WM_gesture_box_invoke() */
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
|
||||
uint objects_len = 0;
|
||||
/* Get reference to objects currently in edit mode */
|
||||
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
|
||||
view_layer, CTX_wm_view3d(C), &objects_len);
|
||||
|
||||
const UnwrapOptions options = {
|
||||
.topology_from_uvs = true,
|
||||
.only_selected_faces = true,
|
||||
.only_selected_uvs = true,
|
||||
.fill_holes = false,
|
||||
.correct_aspect = false,
|
||||
};
|
||||
|
||||
/* If no islands selected then cancel operator */
|
||||
if (!uvedit_have_selection_multi(scene, objects, objects_len, &options)) {
|
||||
MEM_freeN(objects);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Free memory before running invoke */
|
||||
MEM_freeN(objects);
|
||||
return WM_gesture_box_invoke(C, op, event);
|
||||
}
|
||||
|
||||
void UV_OT_pack_islands_to_area(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Pack Islands to area";
|
||||
ot->idname = "UV_OT_pack_islands_to_area";
|
||||
ot->description = "Transform selected islands so that they fill up specified area in UV space";
|
||||
|
||||
/* api callbacks */
|
||||
ot->invoke = pack_islands_to_area_invoke;
|
||||
ot->exec = pack_islands_to_area_exec;
|
||||
ot->modal = WM_gesture_box_modal;
|
||||
ot->cancel = WM_gesture_box_cancel;
|
||||
|
||||
ot->poll = ED_operator_uvedit;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* RNA properties */
|
||||
/* Max coordinates of the packing area */
|
||||
static float default_max[2] = {1.0f, 1.0f};
|
||||
RNA_def_float_vector(ot->srna,
|
||||
"box_max_co",
|
||||
2,
|
||||
default_max,
|
||||
-100.0f,
|
||||
100.0f,
|
||||
"Maximum",
|
||||
"Maximum coordinates for the packing area",
|
||||
-100.0f,
|
||||
100.0f);
|
||||
/* Min coordinates of packing area */
|
||||
static float default_min[2] = {0.0f, 0.0f};
|
||||
RNA_def_float_vector(ot->srna,
|
||||
"box_min_co",
|
||||
2,
|
||||
default_min,
|
||||
-100.0f,
|
||||
100.0f,
|
||||
"Minimum",
|
||||
"Minimum coordinates for the packing area",
|
||||
-100.0f,
|
||||
100.0f);
|
||||
RNA_def_boolean(ot->srna, "rotate", true, "Rotate", "Rotate islands for best fit");
|
||||
RNA_def_boolean(ot->srna, "scale", true, "Scale", "Preserve island scale when packing");
|
||||
RNA_def_float_factor(
|
||||
ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
|
||||
|
||||
/* Store pack area coordinates for rerun from properties panel */
|
||||
PropertyRNA *prop;
|
||||
static float default_val[4] = {0.0f, 0.0f, 1.0f, 1.0f};
|
||||
prop = RNA_def_float_array(ot->srna,
|
||||
"pack_area",
|
||||
4,
|
||||
default_val,
|
||||
INT_MIN,
|
||||
INT_MAX,
|
||||
"Packing area coordinates",
|
||||
"",
|
||||
INT_MIN,
|
||||
INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||
|
||||
WM_operator_properties_border(ot);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Average UV Islands Scale Operator
|
||||
* \{ */
|
||||
@@ -2055,6 +2373,8 @@ static int smart_project_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
/* sima=NULL cases are handled in ED_uvedit_pack_islands_multi() */
|
||||
const SpaceImage *sima = CTX_wm_space_image(C);
|
||||
|
||||
/* May be NULL. */
|
||||
View3D *v3d = CTX_wm_view3d(C);
|
||||
@@ -2065,6 +2385,7 @@ static int smart_project_exec(bContext *C, wmOperator *op)
|
||||
|
||||
const float project_angle_limit_cos = cosf(project_angle_limit);
|
||||
const float project_angle_limit_half_cos = cosf(project_angle_limit / 2);
|
||||
const int target_udim = 1001; /* 0-1 UV space */
|
||||
|
||||
/* Memory arena for list links (cleared for each object). */
|
||||
MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
|
||||
@@ -2204,8 +2525,11 @@ static int smart_project_exec(bContext *C, wmOperator *op)
|
||||
/* Depsgraph refresh functions are called here. */
|
||||
const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
|
||||
ED_uvedit_pack_islands_multi(scene,
|
||||
sima,
|
||||
objects_changed,
|
||||
object_changed_len,
|
||||
true,
|
||||
target_udim,
|
||||
&(struct UVPackIsland_Params){
|
||||
.rotate = true,
|
||||
/* We could make this optional. */
|
||||
|
@@ -337,6 +337,7 @@
|
||||
.doublimit = 0.001, \
|
||||
.vgroup_weight = 1.0f, \
|
||||
.uvcalc_margin = 0.001f, \
|
||||
.target_udim = 1001, \
|
||||
.uvcalc_flag = UVCALC_TRANSFORM_CORRECT_SLIDE, \
|
||||
.unwrapper = 1, \
|
||||
.select_thresh = 0.01f, \
|
||||
|
@@ -1404,6 +1404,8 @@ typedef struct ToolSettings {
|
||||
char uv_selectmode;
|
||||
|
||||
float uvcalc_margin;
|
||||
int target_udim; /* Can be extended for unwrap operator as well */
|
||||
int _pad3[1];
|
||||
|
||||
/* Auto-IK */
|
||||
/** Runtime only. */
|
||||
@@ -1463,14 +1465,12 @@ typedef struct ToolSettings {
|
||||
|
||||
char edge_mode_live_unwrap;
|
||||
|
||||
char _pad1[1];
|
||||
|
||||
/* Transform */
|
||||
char transform_pivot_point;
|
||||
char transform_flag;
|
||||
char snap_mode, snap_node_mode;
|
||||
char snap_uv_mode;
|
||||
char snap_flag;
|
||||
short snap_flag;
|
||||
char snap_target;
|
||||
char snap_transform_mode_flag;
|
||||
|
||||
@@ -2051,6 +2051,7 @@ enum {
|
||||
#define SCE_SNAP_ABS_GRID (1 << 5)
|
||||
#define SCE_SNAP_BACKFACE_CULLING (1 << 6)
|
||||
#define SCE_SNAP_SEQ (1 << 7)
|
||||
#define SCE_SNAP_ABS_UV_GRID (1 << 8)
|
||||
|
||||
/** #ToolSettings.snap_target */
|
||||
#define SCE_SNAP_TARGET_CLOSEST 0
|
||||
|
@@ -1197,6 +1197,9 @@ typedef struct SpaceImage {
|
||||
float uv_opacity;
|
||||
|
||||
int tile_grid_shape[2];
|
||||
/** UV editor Dynamic Grid. Value of N will produce NxN grid. */
|
||||
int dynamic_grid_size;
|
||||
char _pad3[4];
|
||||
|
||||
MaskSpaceInfo mask_info;
|
||||
SpaceImageOverlay overlay;
|
||||
@@ -1252,7 +1255,9 @@ typedef enum eSpaceImage_Flag {
|
||||
SI_FLAG_UNUSED_7 = (1 << 7), /* cleared */
|
||||
SI_FLAG_UNUSED_8 = (1 << 8), /* cleared */
|
||||
SI_COORDFLOATS = (1 << 9),
|
||||
SI_FLAG_UNUSED_10 = (1 << 10),
|
||||
|
||||
/* Use dynamic grid in UV editor */
|
||||
SI_DYNAMIC_GRID = (1 << 10),
|
||||
SI_LIVE_UNWRAP = (1 << 11),
|
||||
SI_USE_ALPHA = (1 << 12),
|
||||
SI_SHOW_ALPHA = (1 << 13),
|
||||
|
@@ -3156,6 +3156,14 @@ static void rna_def_tool_settings(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Snap UV Element", "Type of element to snap to");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
|
||||
|
||||
prop = RNA_def_property(srna, "use_snap_uv_grid_absolute", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_ABS_UV_GRID);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Absolute Grid Snap",
|
||||
"Absolute grid alignment while translating (based on the pivot center)");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
|
||||
|
||||
prop = RNA_def_property(srna, "snap_target", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "snap_target");
|
||||
RNA_def_property_enum_items(prop, rna_enum_snap_target_items);
|
||||
|
@@ -1775,9 +1775,17 @@ static const EnumPropertyItem *rna_SpaceImageEditor_pivot_itemf(bContext *UNUSED
|
||||
bool *UNUSED(r_free))
|
||||
{
|
||||
static const EnumPropertyItem pivot_items[] = {
|
||||
{V3D_AROUND_CENTER_BOUNDS, "CENTER", ICON_PIVOT_BOUNDBOX, "Bounding Box Center", ""},
|
||||
{V3D_AROUND_CENTER_MEDIAN, "MEDIAN", ICON_PIVOT_MEDIAN, "Median Point", ""},
|
||||
{V3D_AROUND_CURSOR, "CURSOR", ICON_PIVOT_CURSOR, "2D Cursor", ""},
|
||||
{V3D_AROUND_CENTER_BOUNDS,
|
||||
"CENTER",
|
||||
ICON_PIVOT_BOUNDBOX,
|
||||
"Bounding Box Center",
|
||||
"Pivot around bounding box center of selected UVs"},
|
||||
{V3D_AROUND_CENTER_MEDIAN,
|
||||
"MEDIAN",
|
||||
ICON_PIVOT_MEDIAN,
|
||||
"Median Point",
|
||||
"Pivot around the median point of selected UVs"},
|
||||
{V3D_AROUND_CURSOR, "CURSOR", ICON_PIVOT_CURSOR, "2D Cursor", "Pivot around the 2D Cursor"},
|
||||
{V3D_AROUND_LOCAL_ORIGINS,
|
||||
"INDIVIDUAL_ORIGINS",
|
||||
ICON_PIVOT_INDIVIDUAL,
|
||||
@@ -3443,6 +3451,19 @@ static void rna_def_space_image_uv(BlenderRNA *brna)
|
||||
prop, "Tile Grid Shape", "How many tiles will be shown in the background");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "use_dynamic_grid", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_DYNAMIC_GRID);
|
||||
RNA_def_property_ui_text(prop, "Dynamic Grid", "Replace the default grid with a Dynamic grid");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "dynamic_grid_size", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_sdna(prop, NULL, "dynamic_grid_size");
|
||||
RNA_def_property_int_default(prop, 1);
|
||||
RNA_def_property_range(prop, 1, 5000);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Dynamic Grid Size", "How many grid units in UV space make one UV Unit");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "uv_opacity", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_float_sdna(prop, NULL, "uv_opacity");
|
||||
RNA_def_property_range(prop, 0.0f, 1.0f);
|
||||
|
@@ -75,7 +75,6 @@ void RNA_api_texture(StructRNA *srna)
|
||||
-1e4,
|
||||
1e4);
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
|
||||
/* return location and normal */
|
||||
parm = RNA_def_float_vector(
|
||||
func,
|
||||
|
@@ -4019,6 +4019,7 @@ static void gesture_box_modal_keymap(wmKeyConfig *keyconf)
|
||||
WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom_border");
|
||||
WM_modalkeymap_assign(keymap, "IMAGE_OT_render_border");
|
||||
WM_modalkeymap_assign(keymap, "IMAGE_OT_view_zoom_border");
|
||||
WM_modalkeymap_assign(keymap, "UV_OT_pack_islands_to_area");
|
||||
WM_modalkeymap_assign(keymap, "GPENCIL_OT_select_box");
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user