From a24fc6bbc1ae3c0ee5a26c5b964af219215de692 Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Sun, 18 Sep 2022 17:03:40 +1200 Subject: UV: extend custom grid sizes to set each axis separately For example, allows a custom UV grid size of 4 x 12. TODO: Fix snapping with custom UV grid sizes. Manifest Tasks: T78391 Differential Revision: https://developer.blender.org/D16000 --- release/scripts/startup/bl_ui/space_image.py | 9 +- source/blender/blenloader/intern/versioning_300.cc | 21 +++-- .../blender/draw/engines/overlay/overlay_grid.cc | 4 +- .../engines/overlay/shaders/overlay_grid_frag.glsl | 99 +++++++++------------- source/blender/editors/include/ED_image.h | 3 +- source/blender/editors/space_image/image_draw.c | 19 +++-- source/blender/editors/space_image/space_image.c | 3 +- source/blender/editors/transform/transform.c | 8 +- source/blender/makesdna/DNA_space_types.h | 5 +- source/blender/makesrna/intern/rna_space.c | 14 ++- 10 files changed, 103 insertions(+), 82 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index da752b5fe00..b624d024843 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -1537,9 +1537,12 @@ class IMAGE_PT_overlay_guides(Panel): row = col.row(align=True) sub = row.row(align=True) sub.prop(uvedit, "use_custom_grid", text="") - sub = sub.row(align=True) - sub.active = uvedit.use_custom_grid - sub.prop(uvedit, "custom_grid_subdivisions", text="") + if uvedit.use_custom_grid: + row = layout.row() + row.use_property_split = True + row.use_property_decorate = False + sub = sub.row(align=True) + sub.prop(uvedit, "custom_grid_subdivisions", text="") row = layout.row() row.use_property_split = True diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index e789cd66632..8f1f2fa2c17 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -2401,11 +2401,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } break; } - case SPACE_IMAGE: { - SpaceImage *sima = (SpaceImage *)sl; - sima->custom_grid_subdiv = 10; - break; - } } } } @@ -3426,5 +3421,21 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Custom grids in UV Editor have separate X and Y divisions. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + switch (sl->spacetype) { + case SPACE_IMAGE: { + SpaceImage *sima = (SpaceImage *)sl; + sima->custom_grid_subdiv[0] = 10; + sima->custom_grid_subdiv[1] = 10; + break; + } + } + } + } + } } } diff --git a/source/blender/draw/engines/overlay/overlay_grid.cc b/source/blender/draw/engines/overlay/overlay_grid.cc index 71abcf91223..4a4d9ee6c19 100644 --- a/source/blender/draw/engines/overlay/overlay_grid.cc +++ b/source/blender/draw/engines/overlay/overlay_grid.cc @@ -31,6 +31,7 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata) float *zplane_axes = pd->grid.zplane_axes; float grid_steps[SI_GRID_STEPS_LEN] = { 0.001f, 0.01f, 0.1f, 1.0f, 10.0f, 100.0f, 1000.0f, 10000.0f}; + float grid_steps_y[SI_GRID_STEPS_LEN] = {0.0f}; /* When zero, use value from grid_steps. */ OVERLAY_GridBits grid_flag = OVERLAY_GridBits(0), zneg_flag = OVERLAY_GridBits(0), zpos_flag = OVERLAY_GridBits(0); grid->line_size = max_ff(0.0f, U.pixelsize - 1.0f) * 0.5f; @@ -68,7 +69,7 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata) } grid->zoom_factor = ED_space_image_zoom_level(v2d, SI_GRID_STEPS_LEN); - ED_space_image_grid_steps(sima, grid_steps, SI_GRID_STEPS_LEN); + ED_space_image_grid_steps(sima, grid_steps, grid_steps_y, SI_GRID_STEPS_LEN); } else { /* SPACE_VIEW3D */ @@ -197,6 +198,7 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata) /* Convert to UBO alignment. */ for (int i = 0; i < SI_GRID_STEPS_LEN; i++) { grid->steps[i][0] = grid_steps[i]; + grid->steps[i][1] = (grid_steps_y[i] != 0.0f) ? grid_steps_y[i] : grid_steps[i]; } pd->grid.grid_flag = grid_flag; pd->grid.zneg_flag = zneg_flag; diff --git a/source/blender/draw/engines/overlay/shaders/overlay_grid_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_grid_frag.glsl index 54a4231590e..2c81966fe50 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_grid_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_grid_frag.glsl @@ -1,6 +1,6 @@ /** * Infinite grid: - * Draw antialiazed grid and axes of different sizes with smooth blending between Level of details. + * Draw antialiased grid and axes of different sizes with smooth blending between levels of detail. * We draw multiple triangles to avoid float precision issues due to perspective interpolation. **/ @@ -8,29 +8,33 @@ #pragma BLENDER_REQUIRE(common_math_lib.glsl) /** - * We want to know how much a pixel is covered by a line. - * We replace the square pixel with acircle of the same area and try to find the intersection area. - * The area we search is the circular segment. https://en.wikipedia.org/wiki/Circular_segment - * The formula for the area uses inverse trig function and is quite complexe. Instead, - * we approximate it by using the smoothstep function and a 1.05 factor to the disc radius. + * We want to know how much of a pixel is covered by a line. + * Here, we imagine the square pixel is a circle with the same area and try to find the + * intersection area. The overlap area is a circular segment. + * https://en.wikipedia.org/wiki/Circular_segment The formula for the area uses inverse trig + * function and is quite complex. Instead, we approximate it by using the smoothstep function and + * a 1.05 factor to the disc radius. + * + * For an alternate approach, see: + * https://developer.nvidia.com/gpugems/gpugems2/part-iii-high-quality-rendering/chapter-22-fast-prefiltered-lines */ #define M_1_SQRTPI 0.5641895835477563 /* 1/sqrt(pi) */ #define DISC_RADIUS (M_1_SQRTPI * 1.05) -#define GRID_LINE_SMOOTH_START (0.5 - DISC_RADIUS) -#define GRID_LINE_SMOOTH_END (0.5 + DISC_RADIUS) +#define GRID_LINE_SMOOTH_START (0.5 + DISC_RADIUS) +#define GRID_LINE_SMOOTH_END (0.5 - DISC_RADIUS) #define GRID_LINE_STEP(dist) smoothstep(GRID_LINE_SMOOTH_START, GRID_LINE_SMOOTH_END, dist) -float get_grid(vec2 co, vec2 fwidthCos, float grid_scale) +float get_grid(vec2 co, vec2 fwidthCos, vec2 grid_scale) { - float half_size = grid_scale / 2.0; + vec2 half_size = grid_scale / 2.0; /* Triangular wave pattern, amplitude is [0, half_size]. */ - vec2 grid_domain = abs(mod(co + half_size, vec2(grid_scale)) - half_size); + vec2 grid_domain = abs(mod(co + half_size, grid_scale) - half_size); /* Modulate by the absolute rate of change of the coordinates * (make line have the same width under perspective). */ grid_domain /= fwidthCos; /* Collapse waves. */ float line_dist = min(grid_domain.x, grid_domain.y); - return 1.0 - GRID_LINE_STEP(line_dist - grid_buf.line_size); + return GRID_LINE_STEP(line_dist - grid_buf.line_size); } vec3 get_axes(vec3 co, vec3 fwidthCos, float line_size) @@ -39,7 +43,7 @@ vec3 get_axes(vec3 co, vec3 fwidthCos, float line_size) /* Modulate by the absolute rate of change of the coordinates * (make line have the same width under perspective). */ axes_domain /= fwidthCos; - return 1.0 - GRID_LINE_STEP(axes_domain - (line_size + grid_buf.line_size)); + return GRID_LINE_STEP(axes_domain - (line_size + grid_buf.line_size)); } #define linearstep(p0, p1, v) (clamp(((v) - (p0)) / abs((p1) - (p0)), 0.0, 1.0)) @@ -106,49 +110,30 @@ void main() grid_res = grid_buf.zoom_factor; } - /* From biggest to smallest. */ - vec4 scale; -#define grid_step(a) grid_buf.steps[a].x -#if 0 /* Inefficient. */ - int step_id = 0; - scale[0] = 0.0; - scale[1] = grid_step(0); - while (scale[1] < grid_res && step_id != STEPS_LEN - 1) { - scale[0] = scale[1]; - scale[1] = grid_step(++step_id); - } - scale[2] = grid_step(min(step_id + 1, STEPS_LEN - 1)); - scale[3] = grid_step(min(step_id + 2, STEPS_LEN - 1)); -#else - /* For more efficiency, unroll the loop above. */ - if (grid_step(0) > grid_res) { - scale = vec4(0.0, grid_step(0), grid_step(1), grid_step(2)); - } - else if (grid_step(1) > grid_res) { - scale = vec4(grid_step(0), grid_step(1), grid_step(2), grid_step(3)); - } - else if (grid_step(2) > grid_res) { - scale = vec4(grid_step(1), grid_step(2), grid_step(3), grid_step(4)); - } - else if (grid_step(3) > grid_res) { - scale = vec4(grid_step(2), grid_step(3), grid_step(4), grid_step(5)); - } - else if (grid_step(4) > grid_res) { - scale = vec4(grid_step(3), grid_step(4), grid_step(5), grid_step(6)); - } - else if (grid_step(5) > grid_res) { - scale = vec4(grid_step(4), grid_step(5), grid_step(6), grid_step(7)); - } - else if (grid_step(6) > grid_res) { - scale = vec4(grid_step(5), grid_step(6), grid_step(7), grid_step(7)); - } - else { - scale = vec4(grid_step(6), grid_step(7), grid_step(7), grid_step(7)); +/** Keep in sync with `SI_GRID_STEPS_LEN` in `DNA_space_types.h`. */ +#define STEPS_LEN 8 + int step_id_x = STEPS_LEN - 1; + int step_id_y = STEPS_LEN - 1; + + /* Loop backwards a compile-time-constant number of steps. */ + for (int i = STEPS_LEN - 2; i >= 0; --i) { + step_id_x = (grid_res < grid_buf.steps[i].x) ? i : step_id_x; /* Branchless. */ + step_id_y = (grid_res < grid_buf.steps[i].y) ? i : step_id_y; } -#endif -#undef grid_step - float blend = 1.0 - linearstep(scale[0], scale[1], grid_res); + /* From biggest to smallest. */ + float scale0x = step_id_x > 0 ? grid_buf.steps[step_id_x - 1].x : 0.0; + float scaleAx = grid_buf.steps[step_id_x].x; + float scaleBx = grid_buf.steps[min(step_id_x + 1, STEPS_LEN - 1)].x; + float scaleCx = grid_buf.steps[min(step_id_x + 2, STEPS_LEN - 1)].x; + + float scale0y = step_id_y > 0 ? grid_buf.steps[step_id_y - 1].y : 0.0; + float scaleAy = grid_buf.steps[step_id_y].y; + float scaleBy = grid_buf.steps[min(step_id_y + 1, STEPS_LEN - 1)].y; + float scaleCy = grid_buf.steps[min(step_id_y + 2, STEPS_LEN - 1)].y; + + /* Subtract from 1.0 to fix blending when `scale0x == scaleAx`. */ + float blend = 1.0 - linearstep(scale0x + scale0y, scaleAx + scaleAy, grid_res + grid_res); blend = blend * blend * blend; vec2 grid_pos, grid_fwidth; @@ -165,9 +150,9 @@ void main() grid_fwidth = fwidthPos.xy; } - float gridA = get_grid(grid_pos, grid_fwidth, scale[1]); - float gridB = get_grid(grid_pos, grid_fwidth, scale[2]); - float gridC = get_grid(grid_pos, grid_fwidth, scale[3]); + float gridA = get_grid(grid_pos, grid_fwidth, vec2(scaleAx, scaleAy)); + float gridB = get_grid(grid_pos, grid_fwidth, vec2(scaleBx, scaleBy)); + float gridC = get_grid(grid_pos, grid_fwidth, vec2(scaleCx, scaleCy)); out_color = colorGrid; out_color.a *= gridA * blend; diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index ef75277b1ea..da303f3552b 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -32,7 +32,8 @@ struct wmWindowManager; float ED_space_image_zoom_level(const struct View2D *v2d, int grid_dimension); void ED_space_image_grid_steps(struct SpaceImage *sima, - float grid_steps[SI_GRID_STEPS_LEN], + float grid_steps_x[SI_GRID_STEPS_LEN], + float grid_steps_y[SI_GRID_STEPS_LEN], int grid_dimension); /** * Calculate the increment snapping value for UV/image editor based on the zoom factor diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 37a6e6dfcb1..8a934396229 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -585,18 +585,19 @@ float ED_space_image_zoom_level(const View2D *v2d, const int grid_dimension) } void ED_space_image_grid_steps(SpaceImage *sima, - float grid_steps[SI_GRID_STEPS_LEN], + float grid_steps_x[SI_GRID_STEPS_LEN], + float grid_steps_y[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)); + const int flag = sima->flag; + for (int step = 0; step < SI_GRID_STEPS_LEN; step++) { + if (flag & SI_CUSTOM_GRID) { + grid_steps_x[step] = 1.0f / sima->custom_grid_subdiv[0]; + grid_steps_y[step] = 1.0f / sima->custom_grid_subdiv[1]; } - } - 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))); + else { + grid_steps_x[step] = powf(grid_dimension, step - SI_GRID_STEPS_LEN); + grid_steps_y[step] = powf(grid_dimension, step - SI_GRID_STEPS_LEN); } } } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 096ae4fd328..2b65267644a 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -112,7 +112,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; + simage->custom_grid_subdiv[0] = 10; + simage->custom_grid_subdiv[1] = 10; /* header */ region = MEM_callocN(sizeof(ARegion), "header for image"); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index eb46da3579a..95aa48efd84 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1719,11 +1719,17 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2]) 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]; + float grid_steps_y[SI_GRID_STEPS_LEN]; - ED_space_image_grid_steps(sima, grid_steps, grid_size); + ED_space_image_grid_steps(sima, grid_steps, grid_steps_y, 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; + + /* TODO: Implement snapping for custom grid sizes with `grid_steps[0] != grid_steps_y[0]`. + * r_snap_y[0] = ED_space_image_increment_snap_value(grid_size, grid_steps_y, zoom_factor); + * r_snap_y[1] = r_snap_y[0] / 2.0f; + */ } else if (t->spacetype == SPACE_CLIP) { r_snap[0] = 0.125f; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index a974330a2ac..44d4cef53a6 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1236,11 +1236,10 @@ typedef struct SpaceImage { int tile_grid_shape[2]; /** - * UV editor custom-grid. Value of `N` will produce `NxN` grid. + * UV editor custom-grid. Value of `{M,N}` will produce `MxN` grid. * Use when #SI_CUSTOM_GRID is set. */ - int custom_grid_subdiv; - char _pad3[4]; + int custom_grid_subdiv[2]; MaskSpaceInfo mask_info; SpaceImageOverlay overlay; diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index bf25e3214f8..e2a3fcc367e 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -1886,6 +1886,15 @@ static void rna_SpaceUVEditor_tile_grid_shape_set(PointerRNA *ptr, const int *va } } +static void rna_SpaceUVEditor_custom_grid_subdiv_set(PointerRNA *ptr, const int *values) +{ + SpaceImage *data = (SpaceImage *)(ptr->data); + + for (int i = 0; i < 2; i++) { + data->custom_grid_subdiv[i] = CLAMPIS(values[i], 1, 5000); + } +} + /* Space Text Editor */ static void rna_SpaceTextEditor_word_wrap_set(PointerRNA *ptr, bool value) @@ -3611,9 +3620,12 @@ static void rna_def_space_image_uv(BlenderRNA *brna) 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); + prop = RNA_def_property(srna, "custom_grid_subdivisions", PROP_INT, PROP_XYZ); RNA_def_property_int_sdna(prop, NULL, "custom_grid_subdiv"); + RNA_def_property_array(prop, 2); + RNA_def_property_int_default(prop, 10); RNA_def_property_range(prop, 1, 5000); + RNA_def_property_int_funcs(prop, NULL, "rna_SpaceUVEditor_custom_grid_subdiv_set", NULL); 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); -- cgit v1.2.3