Blender V5.0
overlay_empty.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
11#include "DRW_render.hh"
12#include "overlay_base.hh"
13#include "overlay_image.hh"
14
15namespace blender::draw::overlay {
16
21 friend class Cameras;
22 using EmptyInstanceBuf = ShapeInstanceBuf<ExtraInstanceData>;
23
24 private:
25 /* Images added by Image > Background. Both added in preset view (like Top, Front, ..) and in
26 * custom view. Object property "In Front" unchecked. */
27 PassSortable images_back_ps_ = {"images_back_ps_"};
28 /* All Empty images from cases of `images_ps_`, `images_blend_ps_`, `images_back_ps_`
29 * with object property "In Front" checked. */
30 PassSortable images_front_ps_ = {"images_front_ps_"};
31
32 /* Images added by Empty > Image and Image > Reference with unchecked image "Opacity".
33 * Object property "In Front" unchecked. */
34 PassMain images_ps_ = {"images_ps_"};
35 /* Images added by Empty > Image and Image > Reference with image "Opacity" checked.
36 * Object property "In Front" unchecked. */
37 PassSortable images_blend_ps_ = {"images_blend_ps_"};
38
39 PassSimple ps_ = {"Empties"};
40
41 struct CallBuffers {
42 const SelectionType selection_type_;
43 EmptyInstanceBuf plain_axes_buf = {selection_type_, "plain_axes_buf"};
44 EmptyInstanceBuf single_arrow_buf = {selection_type_, "single_arrow_buf"};
45 EmptyInstanceBuf cube_buf = {selection_type_, "cube_buf"};
46 EmptyInstanceBuf circle_buf = {selection_type_, "circle_buf"};
47 EmptyInstanceBuf sphere_buf = {selection_type_, "sphere_buf"};
48 EmptyInstanceBuf cone_buf = {selection_type_, "cone_buf"};
49 EmptyInstanceBuf arrows_buf = {selection_type_, "arrows_buf"};
50 EmptyInstanceBuf image_buf = {selection_type_, "image_buf"};
51 } call_buffers_;
52
53 View::OffsetData offset_data_;
54 float4x4 depth_bias_winmat_;
55
56 public:
57 Empties(const SelectionType selection_type) : call_buffers_{selection_type} {};
58
59 void begin_sync(Resources &res, const State &state) final
60 {
61 enabled_ = state.is_space_v3d() && state.show_extras();
62
63 if (!enabled_) {
64 return;
65 }
66
67 offset_data_ = state.offset_data_get();
68
69 auto init_pass = [&](PassMain &pass, DRWState draw_state) {
70 pass.init();
71 pass.state_set(draw_state, state.clipping_plane_count);
72 pass.shader_set(res.shaders->image_plane_depth_bias.get());
73 pass.push_constant("depth_bias_winmat", &depth_bias_winmat_);
74 pass.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
75 pass.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
76 res.select_bind(pass);
77 };
78
79 auto init_sortable = [&](PassSortable &pass, DRWState draw_state) {
80 pass.init();
81 PassMain::Sub &sub = pass.sub("ResourceBind", -FLT_MAX);
82 sub.state_set(draw_state, state.clipping_plane_count);
83 res.select_bind(pass, sub);
84 };
85
86 DRWState draw_state;
87
89 init_pass(images_ps_, draw_state);
90
92 init_sortable(images_back_ps_, draw_state);
93 init_sortable(images_blend_ps_, draw_state);
94
96 init_sortable(images_front_ps_, draw_state);
97
98 begin_sync(call_buffers_);
99 }
100
101 static void begin_sync(CallBuffers &call_buffers)
102 {
103 call_buffers.plain_axes_buf.clear();
104 call_buffers.single_arrow_buf.clear();
105 call_buffers.cube_buf.clear();
106 call_buffers.circle_buf.clear();
107 call_buffers.sphere_buf.clear();
108 call_buffers.cone_buf.clear();
109 call_buffers.arrows_buf.clear();
110 call_buffers.image_buf.clear();
111 }
112
113 void object_sync(Manager &manager,
114 const ObjectRef &ob_ref,
115 Resources &res,
116 const State &state) final
117 {
118 if (!enabled_) {
119 return;
120 }
121
122 const float4 color = res.object_wire_color(ob_ref, state);
123 const select::ID select_id = res.select_id(ob_ref);
124 if (ob_ref.object->empty_drawtype == OB_EMPTY_IMAGE) {
125 image_sync(ob_ref, select_id, manager, res, state, call_buffers_.image_buf);
126 return;
127 }
128 object_sync(select_id,
129 ob_ref.object->object_to_world(),
130 ob_ref.object->empty_drawsize,
131 ob_ref.object->empty_drawtype,
132 color,
133 call_buffers_);
134 }
135
136 static void object_sync(const select::ID select_id,
137 const float4x4 &matrix,
138 const float draw_size,
139 const char empty_drawtype,
140 const float4 &color,
141 CallBuffers &call_buffers)
142 {
143 ExtraInstanceData data(matrix, color, draw_size);
144
145 switch (empty_drawtype) {
146 case OB_PLAINAXES:
147 call_buffers.plain_axes_buf.append(data, select_id);
148 break;
149 case OB_SINGLE_ARROW:
150 call_buffers.single_arrow_buf.append(data, select_id);
151 break;
152 case OB_CUBE:
153 call_buffers.cube_buf.append(data, select_id);
154 break;
155 case OB_CIRCLE:
156 call_buffers.circle_buf.append(data, select_id);
157 break;
158 case OB_EMPTY_SPHERE:
159 call_buffers.sphere_buf.append(data, select_id);
160 break;
161 case OB_EMPTY_CONE:
162 call_buffers.cone_buf.append(data, select_id);
163 break;
164 case OB_ARROWS:
165 call_buffers.arrows_buf.append(data, select_id);
166 break;
167 }
168 }
169
170 void end_sync(Resources &res, const State &state) final
171 {
172 if (!enabled_) {
173 return;
174 }
175
176 ps_.init();
177 res.select_bind(ps_);
178 end_sync(res, state, ps_, call_buffers_);
179 }
180
181 static void end_sync(Resources &res,
182 const State &state,
183 PassSimple::Sub &ps,
184 CallBuffers &call_buffers)
185 {
187 state.clipping_plane_count);
191
192 call_buffers.plain_axes_buf.end_sync(ps, res.shapes.plain_axes.get());
193 call_buffers.single_arrow_buf.end_sync(ps, res.shapes.single_arrow.get());
194 call_buffers.cube_buf.end_sync(ps, res.shapes.cube.get());
195 call_buffers.circle_buf.end_sync(ps, res.shapes.circle.get());
196 call_buffers.sphere_buf.end_sync(ps, res.shapes.empty_sphere.get());
197 call_buffers.cone_buf.end_sync(ps, res.shapes.empty_cone.get());
198 call_buffers.arrows_buf.end_sync(ps, res.shapes.arrows.get());
199 call_buffers.image_buf.end_sync(ps, res.shapes.quad_wire.get());
200 }
201
202 void pre_draw(Manager &manager, View &view) final
203 {
204 if (!enabled_) {
205 return;
206 }
207
208 manager.generate_commands(images_back_ps_, view);
209 manager.generate_commands(images_ps_, view);
210 manager.generate_commands(images_blend_ps_, view);
211 manager.generate_commands(images_front_ps_, view);
212
213 depth_bias_winmat_ = offset_data_.winmat_polygon_offset(view.winmat(), -1.0f);
214 }
215
216 void draw_line(Framebuffer &framebuffer, Manager &manager, View &view) final
217 {
218 if (!enabled_) {
219 return;
220 }
221
222 GPU_framebuffer_bind(framebuffer);
223 manager.submit(ps_, view);
224 }
225
226 void draw_background_images(Framebuffer &framebuffer, Manager &manager, View &view)
227 {
228 if (!enabled_) {
229 return;
230 }
231
232 GPU_framebuffer_bind(framebuffer);
233 manager.submit_only(images_back_ps_, view);
234 }
235
236 void draw_images(Framebuffer &framebuffer, Manager &manager, View &view)
237 {
238 if (!enabled_) {
239 return;
240 }
241
242 GPU_framebuffer_bind(framebuffer);
243
244 manager.submit_only(images_ps_, view);
245 manager.submit_only(images_blend_ps_, view);
246 }
247
248 void draw_in_front_images(Framebuffer &framebuffer, Manager &manager, View &view)
249 {
250 if (!enabled_) {
251 return;
252 }
253
254 GPU_framebuffer_bind(framebuffer);
255
256 manager.submit_only(images_front_ps_, view);
257 }
258
259 private:
260 void image_sync(const ObjectRef &ob_ref,
261 select::ID select_id,
262 Manager &manager,
263 Resources &res,
264 const State &state,
265 EmptyInstanceBuf &empty_image_buf)
266 {
267 Object *ob = ob_ref.object;
268 gpu::Texture *tex = nullptr;
269 ::Image *ima = static_cast<::Image *>(ob_ref.object->data);
270 float4x4 mat;
271
272 const bool show_frame = BKE_object_empty_image_frame_is_visible_in_view3d(ob, state.rv3d);
273 const bool show_image = show_frame &&
275 const bool use_alpha_blend = (ob_ref.object->empty_image_flag &
277 const bool use_alpha_premult = ima && (ima->alpha_mode == IMA_ALPHA_PREMUL);
278
279 if (!show_frame) {
280 return;
281 }
282
283 {
284 /* Calling #BKE_image_get_size may free the texture. Get the size from 'tex' instead,
285 * see: #59347 */
286 int2 size = int2(0);
287 if (ima != nullptr) {
288 ImageUser iuser = *ob->iuser;
289 Images::stereo_setup(state.scene, state.v3d, ima, &iuser);
290 tex = BKE_image_get_gpu_texture(ima, &iuser);
291 if (tex) {
293 }
294 }
295 CLAMP_MIN(size.x, 1);
296 CLAMP_MIN(size.y, 1);
297
298 float2 image_aspect;
299 calc_image_aspect(ima, size, image_aspect);
300
301 mat = ob->object_to_world();
302 mat.x_axis() *= image_aspect.x * 0.5f * ob->empty_drawsize;
303 mat.y_axis() *= image_aspect.y * 0.5f * ob->empty_drawsize;
304 mat.location() += (mat.x_axis() * (ob->ima_ofs[0] * 2.0f + 1.0f) +
305 mat.y_axis() * (ob->ima_ofs[1] * 2.0f + 1.0f));
306 }
307
308 if (show_frame) {
309 const float4 color = res.object_wire_color(ob_ref, state);
310 empty_image_buf.append(ExtraInstanceData(mat, color, 1.0f), select_id);
311 }
312
313 if (show_image && tex && ((ob->color[3] > 0.0f) || !use_alpha_blend)) {
314 /* Use the actual depth if we are doing depth tests to determine the distance to the
315 * object. */
316 char depth_mode = state.is_depth_only_drawing ? char(OB_EMPTY_IMAGE_DEPTH_DEFAULT) :
317 ob->empty_image_depth;
318 PassMain::Sub &pass = create_subpass(state, *ob, use_alpha_blend, mat, res);
319 pass.bind_texture("img_tx", tex);
320 pass.push_constant("img_premultiplied", use_alpha_premult);
321 pass.push_constant("img_alpha_blend", use_alpha_blend);
322 pass.push_constant("is_camera_background", false);
323 pass.push_constant("depth_set", depth_mode != OB_EMPTY_IMAGE_DEPTH_DEFAULT);
324 pass.push_constant("ucolor", float4(ob->color));
325 ResourceHandleRange res_handle = manager.resource_handle(mat);
326 pass.draw(res.shapes.quad_solid.get(), res_handle, select_id.get());
327 }
328 }
329
330 PassMain::Sub &create_subpass(const State &state,
331 const Object &ob,
332 const bool use_alpha_blend,
333 const float4x4 &mat,
334 Resources &res)
335 {
336 const bool in_front = state.use_in_front && (ob.dtx & OB_DRAW_IN_FRONT);
337 if (in_front) {
338 return create_subpass(state, mat, res, images_front_ps_, true);
339 }
340 const char depth_mode = state.is_depth_only_drawing ? char(OB_EMPTY_IMAGE_DEPTH_DEFAULT) :
341 ob.empty_image_depth;
342 switch (depth_mode) {
344 return create_subpass(state, mat, res, images_back_ps_, false);
346 return create_subpass(state, mat, res, images_front_ps_, true);
348 default:
349 return use_alpha_blend ? create_subpass(state, mat, res, images_blend_ps_, true) :
350 images_ps_;
351 }
352 }
353
354 PassMain::Sub &create_subpass(const State &state,
355 const float4x4 &mat,
356 Resources &res,
357 PassSortable &parent,
358 bool depth_bias)
359 {
360 const float3 tmp = state.camera_position - mat.location();
361 const float z = -math::dot(state.camera_forward, tmp);
362 PassMain::Sub &sub = parent.sub("Sub", z);
363 if (depth_bias) {
364 sub.shader_set(res.shaders->image_plane_depth_bias.get());
365 sub.push_constant("depth_bias_winmat", &depth_bias_winmat_);
366 }
367 else {
368 sub.shader_set(res.shaders->image_plane.get());
369 }
370 sub.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
371 sub.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
372 return sub;
373 };
374
375 static void calc_image_aspect(::Image *ima, const int2 &size, float2 &r_image_aspect)
376 {
377 /* if no image, make it a 1x1 empty square, honor scale & offset */
378 const float2 ima_dim = ima ? float2(size.x, size.y) : float2(1.0f);
379
380 /* Get the image aspect even if the buffer is invalid */
381 float2 sca(1.0f);
382 if (ima) {
383 if (ima->aspx > ima->aspy) {
384 sca.y = ima->aspy / ima->aspx;
385 }
386 else if (ima->aspx < ima->aspy) {
387 sca.x = ima->aspx / ima->aspy;
388 }
389 }
390
391 const float2 scale_inv(ima_dim.x * sca.x, ima_dim.y * sca.y);
392 r_image_aspect = (scale_inv.x > scale_inv.y) ? float2(1.0f, scale_inv.y / scale_inv.x) :
393 float2(scale_inv.x / scale_inv.y, 1.0f);
394 }
395};
396
397} // namespace blender::draw::overlay
blender::gpu::Texture * BKE_image_get_gpu_texture(Image *image, ImageUser *iuser)
Definition image_gpu.cc:496
bool BKE_object_empty_image_frame_is_visible_in_view3d(const Object *ob, const RegionView3D *rv3d)
bool BKE_object_empty_image_data_is_visible_in_view3d(const Object *ob, const RegionView3D *rv3d)
#define CLAMP_MIN(a, b)
@ IMA_ALPHA_PREMUL
struct Image Image
@ OB_EMPTY_IMAGE_USE_ALPHA_BLEND
@ OB_DRAW_IN_FRONT
@ OB_EMPTY_CONE
@ OB_SINGLE_ARROW
@ OB_PLAINAXES
@ OB_ARROWS
@ OB_CIRCLE
@ OB_CUBE
@ OB_EMPTY_IMAGE
@ OB_EMPTY_SPHERE
struct Object Object
@ OB_EMPTY_IMAGE_DEPTH_DEFAULT
@ OB_EMPTY_IMAGE_DEPTH_FRONT
@ OB_EMPTY_IMAGE_DEPTH_BACK
static AppView * view
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
int GPU_texture_original_height(const blender::gpu::Texture *texture)
int GPU_texture_original_width(const blender::gpu::Texture *texture)
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
void submit_only(PassMain &pass, View &view)
ResourceHandleRange resource_handle(const ObjectRef &ref, float inflate_bounds=0.0f)
void shader_set(gpu::Shader *shader)
PassBase< DrawCommandBufType > & sub(const char *name)
Definition draw_pass.hh:690
void state_set(DRWState state, int clip_plane_count=0)
void bind_ubo(const char *name, gpu::UniformBuf *buffer)
void push_constant(const char *name, const float &data)
detail::PassBase< command::DrawMultiBuf > Sub
Definition draw_pass.hh:499
void pre_draw(Manager &manager, View &view) final
static void object_sync(const select::ID select_id, const float4x4 &matrix, const float draw_size, const char empty_drawtype, const float4 &color, CallBuffers &call_buffers)
void draw_in_front_images(Framebuffer &framebuffer, Manager &manager, View &view)
void draw_images(Framebuffer &framebuffer, Manager &manager, View &view)
void begin_sync(Resources &res, const State &state) final
void end_sync(Resources &res, const State &state) final
Empties(const SelectionType selection_type)
void draw_line(Framebuffer &framebuffer, Manager &manager, View &view) final
static void begin_sync(CallBuffers &call_buffers)
void object_sync(Manager &manager, const ObjectRef &ob_ref, Resources &res, const State &state) final
static void end_sync(Resources &res, const State &state, PassSimple::Sub &ps, CallBuffers &call_buffers)
void draw_background_images(Framebuffer &framebuffer, Manager &manager, View &view)
static void stereo_setup(const Scene *scene, const View3D *v3d, ::Image *ima, ImageUser *iuser)
blender::gpu::Shader * get()
#define DRW_CLIPPING_UBO_SLOT
#define OVERLAY_GLOBALS_SLOT
DRWState
Definition draw_state.hh:25
@ DRW_STATE_DEPTH_LESS
Definition draw_state.hh:37
@ DRW_STATE_WRITE_DEPTH
Definition draw_state.hh:29
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
@ DRW_STATE_DEPTH_LESS_EQUAL
Definition draw_state.hh:38
@ DRW_STATE_BLEND_ALPHA_PREMUL
Definition draw_state.hh:57
static ulong state[N]
select::SelectionType SelectionType
detail::Pass< command::DrawCommandBuf > PassSimple
detail::Pass< command::DrawMultiBuf > PassMain
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< float, 3 > float3
#define FLT_MAX
Definition stdcycles.h:14
char alpha_mode
ImageUser * iuser
char empty_image_flag
float empty_drawsize
float color[4]
float ima_ofs[2]
const float4 & object_wire_color(const ObjectRef &ob_ref, ThemeColorID theme_id) const
draw::UniformArrayBuffer< float4, 6 > clip_planes_buf
draw::UniformBuffer< UniformData > globals_buf
float x
float y