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
8
9#pragma once
10
11#include "overlay_next_image.hh"
13
14namespace blender::draw::overlay {
15
16class Empties {
17 friend class Cameras;
18 using EmptyInstanceBuf = ShapeInstanceBuf<ExtraInstanceData>;
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
struct Image Image
@ OB_DRAW_IN_FRONT
@ OB_EMPTY_IMAGE_DEPTH_DEFAULT
@ OB_EMPTY_IMAGE_DEPTH_FRONT
@ OB_EMPTY_IMAGE_DEPTH_BACK
@ 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_USE_ALPHA_BLEND
@ SPACE_VIEW3D
static AppView * view
static void View(GHOST_IWindow *window, bool stereo, int eye=0)
void GPU_framebuffer_bind(GPUFrameBuffer *framebuffer)
int GPU_texture_original_height(const GPUTexture *texture)
int GPU_texture_original_width(const GPUTexture *texture)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a color
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(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
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
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