Blender V4.3
overlay_next_grid.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#pragma once
10
12
13#include "DNA_camera_types.h"
14#include "DNA_space_types.h"
15
16#include "ED_image.hh"
17#include "ED_view3d.hh"
18
19#include "draw_shader_shared.hh"
21
22namespace blender::draw::overlay {
23
24class Grid {
25 private:
27 StorageVectorBuffer<float4> tile_pos_buf_;
28
29 PassSimple grid_ps_ = {"grid_ps_"};
30
31 float3 grid_axes_ = float3(0.0f);
32 float3 zplane_axes_ = float3(0.0f);
33 OVERLAY_GridBits grid_flag_ = OVERLAY_GridBits(0);
34 OVERLAY_GridBits zneg_flag_ = OVERLAY_GridBits(0);
35 OVERLAY_GridBits zpos_flag_ = OVERLAY_GridBits(0);
36
37 bool enabled_ = false;
38
39 public:
40 void begin_sync(Resources &res, ShapeCache &shapes, const State &state, const View &view)
41 {
42 enabled_ = init(state, view);
43 if (!enabled_) {
44 grid_ps_.init();
45 return;
46 }
47
48 data_.push_update();
49
50 grid_ps_.init();
52 if (state.space_type == SPACE_IMAGE) {
53 /* Add quad background. */
54 auto &sub = grid_ps_.sub("grid_background");
55 sub.shader_set(res.shaders.grid_background.get());
56 const float4 color_back = math::interpolate(
58 sub.push_constant("ucolor", color_back);
59 sub.push_constant("tile_scale", float3(data_.size));
60 sub.bind_texture("depthBuffer", &res.depth_tx);
61 sub.draw(shapes.quad_solid.get());
62 }
63 {
64 auto &sub = grid_ps_.sub("grid");
65 sub.shader_set(res.shaders.grid.get());
66 sub.bind_ubo("grid_buf", &data_);
67 sub.bind_ubo("globalsBlock", &res.globals_buf);
68 sub.bind_texture("depth_tx", &res.depth_tx);
69 if (zneg_flag_ & SHOW_AXIS_Z) {
70 sub.push_constant("grid_flag", zneg_flag_);
71 sub.push_constant("plane_axes", zplane_axes_);
72 sub.draw(shapes.grid.get());
73 }
74 if (grid_flag_) {
75 sub.push_constant("grid_flag", grid_flag_);
76 sub.push_constant("plane_axes", grid_axes_);
77 sub.draw(shapes.grid.get());
78 }
79 if (zpos_flag_ & SHOW_AXIS_Z) {
80 sub.push_constant("grid_flag", zpos_flag_);
81 sub.push_constant("plane_axes", zplane_axes_);
82 sub.draw(shapes.grid.get());
83 }
84 }
85 if (state.space_type == SPACE_IMAGE) {
86 float4 theme_color;
87 UI_GetThemeColorShade4fv(TH_BACK, 60, theme_color);
88 srgb_to_linearrgb_v4(theme_color, theme_color);
89
90 /* Add wire border. */
91 auto &sub = grid_ps_.sub("wire_border");
92 sub.shader_set(res.shaders.grid_image.get());
93 sub.push_constant("ucolor", theme_color);
94 tile_pos_buf_.clear();
95 for (const int x : IndexRange(data_.size[0])) {
96 for (const int y : IndexRange(data_.size[1])) {
97 tile_pos_buf_.append(float4(x, y, 0.0f, 0.0f));
98 }
99 }
100 tile_pos_buf_.push_update();
101 sub.bind_ssbo("tile_pos_buf", &tile_pos_buf_);
102 sub.draw(shapes.quad_wire.get(), tile_pos_buf_.size());
103 }
104 }
105
106 void draw(Framebuffer &framebuffer, Manager &manager, View &view)
107 {
108 if (!enabled_) {
109 return;
110 }
111
112 GPU_framebuffer_bind(framebuffer);
113 manager.submit(grid_ps_, view);
114 }
115
116 private:
117 bool init(const State &state, const View &view)
118 {
119 data_.line_size = max_ff(0.0f, U.pixelsize - 1.0f) * 0.5f;
120 /* Default, nothing is drawn. */
121 grid_flag_ = zneg_flag_ = zpos_flag_ = OVERLAY_GridBits(0);
122
123 return (state.space_type == SPACE_IMAGE) ? init_2d(state) : init_3d(state, view);
124 }
125
126 void copy_steps_to_data(Span<float> grid_steps_x, Span<float> grid_steps_y)
127 {
128 /* Convert to UBO alignment. */
129 for (const int i : IndexRange(SI_GRID_STEPS_LEN)) {
130 data_.steps[i][0] = grid_steps_x[i];
131 data_.steps[i][1] = grid_steps_y[i];
132 }
133 }
134
135 bool init_2d(const State &state)
136 {
137 if (state.hide_overlays) {
138 return false;
139 }
140 SpaceImage *sima = (SpaceImage *)state.space_data;
141 const View2D *v2d = &state.region->v2d;
142 std::array<float, SI_GRID_STEPS_LEN> grid_steps_x = {
143 0.001f, 0.01f, 0.1f, 1.0f, 10.0f, 100.0f, 1000.0f, 10000.0f};
144 std::array<float, SI_GRID_STEPS_LEN> grid_steps_y = {0.0f};
145
146 /* Only UV Edit mode has the various Overlay options for now. */
147 const bool is_uv_edit = sima->mode == SI_MODE_UV;
148
149 const bool background_enabled = is_uv_edit ? (!state.hide_overlays &&
150 (sima->overlay.flag &
152 true;
153 if (background_enabled) {
154 grid_flag_ = GRID_BACK | PLANE_IMAGE;
155 if (sima->flag & SI_GRID_OVER_IMAGE) {
156 grid_flag_ = PLANE_IMAGE;
157 }
158 }
159
160 const bool draw_grid = is_uv_edit || !ED_space_image_has_buffer(sima);
161 if (background_enabled && draw_grid) {
162 grid_flag_ |= SHOW_GRID;
163 if (is_uv_edit) {
164 if (sima->grid_shape_source != SI_GRID_SHAPE_DYNAMIC) {
165 grid_flag_ |= CUSTOM_GRID;
166 }
167 }
168 }
169
170 data_.distance = 1.0f;
171 data_.size = float4(1.0f);
172 if (is_uv_edit) {
173 data_.size[0] = float(sima->tile_grid_shape[0]);
174 data_.size[1] = float(sima->tile_grid_shape[1]);
175 }
176
177 data_.zoom_factor = ED_space_image_zoom_level(v2d, SI_GRID_STEPS_LEN);
178 ED_space_image_grid_steps(sima, grid_steps_x.data(), grid_steps_y.data(), SI_GRID_STEPS_LEN);
179 copy_steps_to_data(grid_steps_x, grid_steps_y);
180 return true;
181 }
182
183 bool init_3d(const State &state, const View &view)
184 {
185 const View3D *v3d = state.v3d;
186 const RegionView3D *rv3d = state.rv3d;
187
188 const bool show_axis_x = (state.v3d_gridflag & V3D_SHOW_X) != 0;
189 const bool show_axis_y = (state.v3d_gridflag & V3D_SHOW_Y) != 0;
190 const bool show_axis_z = (state.v3d_gridflag & V3D_SHOW_Z) != 0;
191 const bool show_floor = (state.v3d_gridflag & V3D_SHOW_FLOOR) != 0;
192 const bool show_ortho_grid = (state.v3d_gridflag & V3D_SHOW_ORTHO_GRID) != 0;
193 const bool show_any = show_axis_x || show_axis_y || show_axis_z || show_floor ||
194 show_ortho_grid;
195
196 if (state.hide_overlays || !show_any) {
197 return false;
198 }
199
200 std::array<float, SI_GRID_STEPS_LEN> grid_steps = {
201 0.001f, 0.01f, 0.1f, 1.0f, 10.0f, 100.0f, 1000.0f, 10000.0f};
202
203 /* If perspective view or non-axis aligned view. */
204 if (view.is_persp() || rv3d->view == RV3D_VIEW_USER) {
205 if (show_axis_x) {
206 grid_flag_ |= PLANE_XY | SHOW_AXIS_X;
207 }
208 if (show_axis_y) {
209 grid_flag_ |= PLANE_XY | SHOW_AXIS_Y;
210 }
211 if (show_floor) {
212 grid_flag_ |= PLANE_XY | SHOW_GRID;
213 }
214 }
215 else {
216 if (show_ortho_grid && ELEM(rv3d->view, RV3D_VIEW_RIGHT, RV3D_VIEW_LEFT)) {
218 }
219 else if (show_ortho_grid && ELEM(rv3d->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) {
221 }
222 else if (show_ortho_grid && ELEM(rv3d->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK)) {
224 }
225 }
226
227 grid_axes_[0] = float((grid_flag_ & (PLANE_XZ | PLANE_XY)) != 0);
228 grid_axes_[1] = float((grid_flag_ & (PLANE_YZ | PLANE_XY)) != 0);
229 grid_axes_[2] = float((grid_flag_ & (PLANE_YZ | PLANE_XZ)) != 0);
230
231 /* Z axis if needed */
232 if (((rv3d->view == RV3D_VIEW_USER) || (rv3d->persp != RV3D_ORTHO)) && show_axis_z) {
233 zpos_flag_ = SHOW_AXIS_Z;
234
235 float3 zvec = -float3(view.viewinv()[2]);
236 float3 campos = float3(view.viewinv()[3]);
237
238 /* z axis : chose the most facing plane */
239 if (fabsf(zvec[0]) < fabsf(zvec[1])) {
240 zpos_flag_ |= PLANE_XZ;
241 }
242 else {
243 zpos_flag_ |= PLANE_YZ;
244 }
245 zneg_flag_ = zpos_flag_;
246
247 /* Perspective: If camera is below floor plane, we switch clipping.
248 * Orthographic: If eye vector is looking up, we switch clipping. */
249 if ((view.is_persp() && (campos[2] > 0.0f)) || (!view.is_persp() && (zvec[2] < 0.0f))) {
250 zpos_flag_ |= CLIP_ZPOS;
251 zneg_flag_ |= CLIP_ZNEG;
252 }
253 else {
254 zpos_flag_ |= CLIP_ZNEG;
255 zneg_flag_ |= CLIP_ZPOS;
256 }
257
258 zplane_axes_[0] = float((zpos_flag_ & (PLANE_XZ | PLANE_XY)) != 0);
259 zplane_axes_[1] = float((zpos_flag_ & (PLANE_YZ | PLANE_XY)) != 0);
260 zplane_axes_[2] = float((zpos_flag_ & (PLANE_YZ | PLANE_XZ)) != 0);
261 }
262 else {
263 zneg_flag_ = zpos_flag_ = CLIP_ZNEG | CLIP_ZPOS;
264 }
265
266 float dist;
267 if (rv3d->persp == RV3D_CAMOB && v3d->camera && v3d->camera->type == OB_CAMERA) {
268 Object *camera_object = DEG_get_evaluated_object(state.depsgraph, v3d->camera);
269 dist = ((Camera *)(camera_object->data))->clip_end;
270 grid_flag_ |= GRID_CAMERA;
271 zneg_flag_ |= GRID_CAMERA;
272 zpos_flag_ |= GRID_CAMERA;
273 }
274 else {
275 dist = v3d->clip_end;
276 }
277
278 if (view.is_persp()) {
279 data_.size = float4(dist);
280 }
281 else {
282 float viewdist = 1.0f / min_ff(fabsf(view.winmat()[0][0]), fabsf(view.winmat()[1][1]));
283 data_.size = float4(viewdist * dist);
284 }
285
286 data_.distance = dist / 2.0f;
287
288 ED_view3d_grid_steps(state.scene, v3d, rv3d, grid_steps.data());
289
290 if ((v3d->flag & (V3D_XR_SESSION_SURFACE | V3D_XR_SESSION_MIRROR)) != 0) {
291 /* The calculations for the grid parameters assume that the view matrix has no scale
292 * component, which may not be correct if the user is "shrunk" or "enlarged" by zooming in or
293 * out. Therefore, we need to compensate the values here. */
294 /* Assumption is uniform scaling (all column vectors are of same length). */
295 float viewinvscale = len_v3(view.viewinv()[0]);
296 data_.distance *= viewinvscale;
297 }
298 copy_steps_to_data(grid_steps, grid_steps);
299 return true;
300 }
301};
302
303} // namespace blender::draw::overlay
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE void srgb_to_linearrgb_v4(float linear[4], const float srgb[4])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
#define ELEM(...)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ OB_CAMERA
@ SI_GRID_OVER_IMAGE
@ SI_OVERLAY_SHOW_GRID_BACKGROUND
@ SPACE_IMAGE
#define SI_GRID_STEPS_LEN
@ SI_GRID_SHAPE_DYNAMIC
@ SI_MODE_UV
@ V3D_SHOW_FLOOR
@ V3D_SHOW_Z
@ V3D_SHOW_X
@ V3D_SHOW_Y
@ V3D_SHOW_ORTHO_GRID
@ RV3D_CAMOB
@ RV3D_ORTHO
@ V3D_XR_SESSION_SURFACE
@ V3D_XR_SESSION_MIRROR
@ RV3D_VIEW_FRONT
@ RV3D_VIEW_BOTTOM
@ RV3D_VIEW_LEFT
@ RV3D_VIEW_RIGHT
@ RV3D_VIEW_TOP
@ RV3D_VIEW_BACK
@ RV3D_VIEW_USER
float ED_space_image_zoom_level(const View2D *v2d, int grid_dimension)
bool ED_space_image_has_buffer(SpaceImage *sima)
void ED_space_image_grid_steps(SpaceImage *sima, float grid_steps_x[SI_GRID_STEPS_LEN], float grid_steps_y[SI_GRID_STEPS_LEN], int grid_dimension)
void ED_view3d_grid_steps(const Scene *scene, const View3D *v3d, const RegionView3D *rv3d, float r_grid_steps[8])
void GPU_framebuffer_bind(GPUFrameBuffer *framebuffer)
@ TH_BACK
void UI_GetThemeColorShade4fv(int colorid, int offset, float col[4])
void init()
unsigned int U
Definition btGjkEpa3.h:78
void submit(PassSimple &pass, View &view)
PassBase< DrawCommandBufType > & sub(const char *name)
Definition draw_pass.hh:616
void state_set(DRWState state, int clip_plane_count=0)
Definition draw_pass.hh:954
void shader_set(GPUShader *shader)
Definition draw_pass.hh:971
void begin_sync(Resources &res, ShapeCache &shapes, const State &state, const View &view)
void draw(Framebuffer &framebuffer, Manager &manager, View &view)
#define fabsf(x)
@ DRW_STATE_BLEND_ALPHA
Definition draw_state.hh:55
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
draw_view in_light_buf[] float
static ulong state[N]
T interpolate(const T &a, const T &b, const FactorT &t)
VecBase< float, 4 > float4
VecBase< float, 3 > float3
struct Object * camera