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