UV Editor: Grid and snapping improvements

Implements T89789, T89792, custom grid (described as dynamic grid in
T78389) and UV grid snapping (T78391)
Replaces the default UV editor grid with 2 new types of grid :

* Custom grid: Allows the user to create an NxN grid, where the value
  of N is specified by the user.
* Subdividing grid: Subdivides the UV editor grid when the user
  zooms in the viewport and vice versa when zooming out.

UV snapping improvements :
* Increment snapping: Increment values for snapping are calculated based
  on which grid type is being used in the UV editor
  (subdividing or custom). In general the increment value is equal to
  the distance between 2 visible grid lines.
* Absolute grid snap: New toggle added to increment snapping option in
  the UV editor, allows UV grid snapping during translation.

Reviewed By: campbellbarton

Ref D12684
This commit is contained in:
Siddhartha Jejurkar 2021-09-29 17:47:32 +10:00 committed by Campbell Barton
parent 008ae26712
commit bf06f76be6
Notes: blender-bot 2023-02-14 06:17:17 +01:00
Referenced by issue #89789, UV editor: Subdividing Grid
Referenced by issue #89792, UV Editor: Increment snapping improvements
17 changed files with 218 additions and 38 deletions

View File

@ -934,6 +934,10 @@ class IMAGE_PT_snapping(Panel):
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)
row.prop(tool_settings, "use_snap_translate", text="Move", toggle=True)
@ -1467,6 +1471,33 @@ class IMAGE_PT_udim_grid(Panel):
col = layout.column()
col.prop(uvedit, "tile_grid_shape", text="Grid Shape")
class IMAGE_PT_custom_grid(Panel):
bl_space_type = 'IMAGE_EDITOR'
bl_region_type = 'UI'
bl_category = "View"
bl_label = "Custom Grid"
@classmethod
def poll(cls, context):
sima = context.space_data
return sima.show_uvedit
def draw_header(self, context):
sima = context.space_data
uvedit = sima.uv_editor
self.layout.prop(uvedit, "use_custom_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, "custom_grid_subdivisions", text="Subdivisions")
class IMAGE_PT_overlay(Panel):
bl_space_type = 'IMAGE_EDITOR'
@ -1652,6 +1683,7 @@ classes = (
IMAGE_PT_uv_cursor,
IMAGE_PT_annotation,
IMAGE_PT_udim_grid,
IMAGE_PT_custom_grid,
IMAGE_PT_overlay,
IMAGE_PT_overlay_uv_edit,
IMAGE_PT_overlay_uv_edit_geometry,

View File

@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 28
#define BLENDER_FILE_SUBVERSION 29
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file

View File

@ -3394,7 +3394,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
SpaceImage *sima = (SpaceImage *)sl;
sima->flag &= ~(SI_FLAG_UNUSED_0 | SI_FLAG_UNUSED_1 | SI_FLAG_UNUSED_3 |
SI_FLAG_UNUSED_6 | SI_FLAG_UNUSED_7 | SI_FLAG_UNUSED_8 |
SI_FLAG_UNUSED_17 | SI_FLAG_UNUSED_18 | SI_FLAG_UNUSED_23 |
SI_FLAG_UNUSED_17 | SI_CUSTOM_GRID | SI_FLAG_UNUSED_23 |
SI_FLAG_UNUSED_24);
break;
}

View File

@ -1579,28 +1579,25 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
/**
* Versioning code until next subversion bump goes here.
*
* \note Be sure to check when bumping the version:
* - "versioning_userdef.c", #blo_do_versions_userdef
* - "versioning_userdef.c", #do_versions_theme
*
* \note Keep this message at the bottom of the function.
*/
{
/* Keep this block, even when empty. */
if (!MAIN_VERSION_ATLEAST(bmain, 300, 29)) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SEQ) {
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;
LISTBASE_FOREACH (ARegion *, region, regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
region->v2d.max[1] = MAXSEQ;
switch (sl->spacetype) {
case SPACE_SEQ: {
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;
LISTBASE_FOREACH (ARegion *, region, regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
region->v2d.max[1] = MAXSEQ;
}
}
break;
}
case SPACE_IMAGE: {
SpaceImage *sima = (SpaceImage *)sl;
sima->custom_grid_subdiv = 10;
break;
}
}
}
@ -1613,4 +1610,17 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
/**
* Versioning code until next subversion bump goes here.
*
* \note Be sure to check when bumping the version:
* - "versioning_userdef.c", #blo_do_versions_userdef
* - "versioning_userdef.c", #do_versions_theme
*
* \note Keep this message at the bottom of the function.
*/
{
/* Keep this block, even when empty. */
}
}

View File

@ -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),
CUSTOM_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;
}
@ -68,15 +71,21 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata)
shd->grid_flag = 0;
}
if (sima->flag & SI_CUSTOM_GRID) {
shd->grid_flag |= CUSTOM_GRID;
}
shd->grid_distance = 1.0f;
copy_v3_fl3(shd->grid_size, 1.0f, 1.0f, 1.0f);
if (sima->mode == SI_MODE_UV) {
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);
}
const int grid_size = SI_GRID_STEPS_LEN;
shd->zoom_factor = ED_space_image_zoom_level(v2d, grid_size);
ED_space_image_grid_steps(sima, shd->grid_steps, grid_size);
return;
}
@ -248,6 +257,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);

View File

@ -139,9 +139,10 @@ typedef struct OVERLAY_ShadingData {
/** Grid */
float grid_axes[3], grid_distance;
float zplane_axes[3], grid_size[3];
float grid_steps[8];
float grid_steps[SI_GRID_STEPS_LEN];
float inv_viewport_size[2];
float grid_line_size;
float zoom_factor; /* Only for UV editor */
int grid_flag;
int zpos_flag;
int zneg_flag;

View File

@ -15,8 +15,9 @@ uniform float lineKernel = 0.0;
uniform sampler2D depthBuffer;
uniform int gridFlag;
uniform float zoomFactor;
#define STEPS_LEN 8
#define STEPS_LEN 8 /* Match: #SI_GRID_STEPS_LEN */
uniform float gridSteps[STEPS_LEN] = float[](0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0, 10000.0);
#define AXIS_X (1 << 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) /* UV/Image Image editor */
#define CUSTOM_GRID (1 << 12) /* UV Editor only */
#define M_1_SQRTPI 0.5641895835477563 /* 1/sqrt(pi) */
@ -122,9 +125,17 @@ 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 &&
/* Grid begins to appear when the length of one grid unit is at least
* (256/grid_size) pixels Value of grid_size defined in `overlay_grid.c`. */
(gridFlag & CUSTOM_GRID) == 0) {
grid_res = zoomFactor;
}
/* from biggest to smallest */
vec4 scale;
#if 0

View File

@ -41,6 +41,16 @@ struct SpaceImage;
struct bContext;
struct wmOperator;
struct wmWindowManager;
struct View2D;
/* image_draw.c */
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[SI_GRID_STEPS_LEN],
const int grid_dimension);
float ED_space_image_increment_snap_value(const int grid_dimesnions,
const float grid_steps[SI_GRID_STEPS_LEN],
const float zoom_factor);
/* image_edit.c, exported for transform */
struct Image *ED_space_image(struct SpaceImage *sima);

View File

@ -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,62 @@ 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[SI_GRID_STEPS_LEN],
const int grid_dimension)
{
if (sima->flag & SI_CUSTOM_GRID) {
for (int step = 0; step < SI_GRID_STEPS_LEN; step++) {
grid_steps[step] = powf(1, step) * (1.0f / ((float)sima->custom_grid_subdiv));
}
}
else {
for (int step = 0; step < SI_GRID_STEPS_LEN; step++) {
grid_steps[step] = powf(grid_dimension, step) *
(1.0f / (powf(grid_dimension, SI_GRID_STEPS_LEN)));
}
}
}
/**
* 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[SI_GRID_STEPS_LEN],
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 < SI_GRID_STEPS_LEN; 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];
}

View File

@ -126,6 +126,8 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED(
simage->tile_grid_shape[0] = 1;
simage->tile_grid_shape[1] = 1;
simage->custom_grid_subdiv = 10;
/* tool header */
region = MEM_callocN(sizeof(ARegion), "tool header for image");

View File

@ -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,16 @@ 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;
int grid_size = SI_GRID_STEPS_LEN;
float zoom_factor = ED_space_image_zoom_level(v2d, grid_size);
float grid_steps[SI_GRID_STEPS_LEN];
ED_space_image_grid_steps(sima, grid_steps, grid_size);
/* Snapping value based on what type of grid is used (adaptive-subdividing or custom-grid). */
r_snap[0] = ED_space_image_increment_snap_value(grid_size, grid_steps, zoom_factor);
r_snap[1] = r_snap[0] / 2.0f;
}
else if (t->spacetype == SPACE_CLIP) {
r_snap[0] = 0.125f;

View File

@ -590,6 +590,11 @@ static void initSnappingMode(TransInfo *t)
t->tsnap.project = 0;
t->tsnap.mode = ts->snap_uv_mode;
if ((t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_uv_flag & SCE_SNAP_ABS_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 +1507,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 (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) {
return false;
}

View File

@ -1463,14 +1463,15 @@ 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_mode;
char snap_node_mode;
char snap_uv_mode;
char snap_flag;
/** UV equivalent of `snap_flag`, limited to: #SCE_SNAP_ABS_GRID. */
char snap_uv_flag;
char snap_target;
char snap_transform_mode_flag;

View File

@ -1200,6 +1200,12 @@ typedef struct SpaceImage {
float uv_opacity;
int tile_grid_shape[2];
/**
* UV editor custom-grid. Value of `N` will produce `NxN` grid.
* Use when #SI_CUSTOM_GRID is set.
*/
int custom_grid_subdiv;
char _pad3[4];
MaskSpaceInfo mask_info;
SpaceImageOverlay overlay;
@ -1255,6 +1261,7 @@ 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),
SI_LIVE_UNWRAP = (1 << 11),
SI_USE_ALPHA = (1 << 12),
@ -1266,7 +1273,7 @@ typedef enum eSpaceImage_Flag {
SI_FULLWINDOW = (1 << 16),
SI_FLAG_UNUSED_17 = (1 << 17),
SI_FLAG_UNUSED_18 = (1 << 18), /* cleared */
SI_CUSTOM_GRID = (1 << 18),
/**
* This means that the image is drawn until it reaches the view edge,
@ -1292,6 +1299,9 @@ typedef enum eSpaceImageOverlay_Flag {
SI_OVERLAY_SHOW_OVERLAYS = (1 << 0),
} eSpaceImageOverlay_Flag;
/** Keep in sync with `STEPS_LEN` in `grid_frag.glsl`. */
#define SI_GRID_STEPS_LEN 8
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -3162,6 +3162,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_uv_flag", SCE_SNAP_ABS_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);

View File

@ -3481,6 +3481,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_custom_grid", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_CUSTOM_GRID);
RNA_def_property_boolean_default(prop, true);
RNA_def_property_ui_text(prop, "Custom Grid", "Use a grid with a user-defined number of steps");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
prop = RNA_def_property(srna, "custom_grid_subdivisions", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "custom_grid_subdiv");
RNA_def_property_range(prop, 1, 5000);
RNA_def_property_ui_text(
prop, "Dynamic Grid Size", "Number of grid units in UV space that 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);

View File

@ -30,11 +30,8 @@ namespace blender::nodes {
static void cmp_node_gamma_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_input<decl::Float>("Gamma")
.default_value(1.0f)
.min(0.001f)
.max(10.0f)
.subtype(PROP_UNSIGNED);
b.add_input<decl::Float>("Gamma").default_value(1.0f).min(0.001f).max(10.0f).subtype(
PROP_UNSIGNED);
b.add_output<decl::Color>("Image");
}