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