Blender V5.0
overlay_outline.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#pragma once
10
11#include "overlay_base.hh"
13
14#include "draw_common.hh"
15
16#include "DNA_userdef_types.h"
17
18namespace blender::draw::overlay {
19
25 private:
26 /* Simple render pass that renders an object ID pass. */
27 PassMain outline_prepass_ps_ = {"Prepass"};
28 PassMain::Sub *prepass_curves_ps_ = nullptr;
29 PassMain::Sub *prepass_pointcloud_ps_ = nullptr;
30 PassMain::Sub *prepass_gpencil_ps_ = nullptr;
31 PassMain::Sub *prepass_mesh_ps_ = nullptr;
32 PassMain::Sub *prepass_volume_ps_ = nullptr;
33 PassMain::Sub *prepass_wire_ps_ = nullptr;
34 /* Detect edges inside the ID pass and output color for each of them. */
35 PassSimple outline_resolve_ps_ = {"Resolve"};
36
37 TextureFromPool object_id_tx_ = {"outline_ob_id_tx"};
38 TextureFromPool tmp_depth_tx_ = {"outline_depth_tx"};
39
40 Framebuffer prepass_fb_ = {"outline.prepass_fb"};
41
42 Vector<FlatObjectRef> flat_objects_;
43
44 PassMain outline_prepass_flat_ps_ = {"PrepassFlat"};
45
46 public:
47 void begin_sync(Resources &res, const State &state) final
48 {
49 enabled_ = !res.is_selection();
50 enabled_ &= state.v3d && (state.v3d_flag & V3D_SELECT_OUTLINE);
51
52 flat_objects_.clear();
53
54 if (!enabled_) {
55 return;
56 }
57
58 const float outline_width = UI_GetThemeValuef(TH_OUTLINE_WIDTH);
59 const bool do_smooth_lines = (U.gpu_flag & USER_GPU_FLAG_OVERLAY_SMOOTH_WIRE) != 0;
60 const bool do_expand = (U.pixelsize > 1.0) || (outline_width > 2.0f);
61 const bool is_transform = (G.moving & G_TRANSFORM_OBJ) != 0;
62
63 {
64 auto &pass = outline_prepass_ps_;
65 pass.init();
66 pass.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
67 pass.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
68 pass.framebuffer_set(&prepass_fb_);
69 pass.clear_color_depth_stencil(float4(0.0f), 1.0f, 0x0);
71 state.clipping_plane_count);
72 {
73 auto &sub = pass.sub("Curves");
74 sub.shader_set(res.shaders->outline_prepass_curves.get());
75 sub.push_constant("is_transform", is_transform);
76 prepass_curves_ps_ = &sub;
77 }
78 {
79 auto &sub = pass.sub("PointCloud");
80 sub.shader_set(res.shaders->outline_prepass_pointcloud.get());
81 sub.push_constant("is_transform", is_transform);
82 prepass_pointcloud_ps_ = &sub;
83 }
84 {
85 auto &sub = pass.sub("GreasePencil");
86 sub.shader_set(res.shaders->outline_prepass_gpencil.get());
87 sub.push_constant("is_transform", is_transform);
88 prepass_gpencil_ps_ = &sub;
89 }
90 {
91 auto &sub = pass.sub("Mesh");
92 sub.shader_set(res.shaders->outline_prepass_mesh.get());
93 sub.push_constant("is_transform", is_transform);
94 prepass_mesh_ps_ = &sub;
95 }
96 {
97 auto &sub = pass.sub("Volume");
98 sub.shader_set(res.shaders->outline_prepass_mesh.get());
99 sub.push_constant("is_transform", is_transform);
100 prepass_volume_ps_ = &sub;
101 }
102 {
103 auto &sub = pass.sub("Wire");
104 sub.shader_set(res.shaders->outline_prepass_wire.get());
105 sub.push_constant("is_transform", is_transform);
106 prepass_wire_ps_ = &sub;
107 }
108 }
109 {
110 auto &pass = outline_resolve_ps_;
111 pass.init();
113 pass.shader_set(res.shaders->outline_detect.get());
114 /* Don't occlude the outline if in xray mode as it causes too much flickering. */
115 pass.push_constant("alpha_occlu", state.xray_enabled ? 1.0f : 0.35f);
116 pass.push_constant("do_thick_outlines", do_expand);
117 pass.push_constant("do_anti_aliasing", do_smooth_lines);
118 pass.push_constant("is_xray_wires", state.xray_enabled_and_not_wire);
119 pass.bind_texture("outline_id_tx", &object_id_tx_);
120 pass.bind_texture("scene_depth_tx", &res.depth_tx);
121 pass.bind_texture("outline_depth_tx", &tmp_depth_tx_);
122 pass.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
123 pass.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
124 pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
125 }
126 }
127
128 void object_sync(Manager &manager,
129 const ObjectRef &ob_ref,
130 Resources &res,
131 const State &state) final
132 {
133 if (!enabled_) {
134 return;
135 }
136
137 /* Outlines of bounding boxes are not drawn. */
138 if (ob_ref.object->dt == OB_BOUNDBOX) {
139 return;
140 }
141
142 gpu::Batch *geom;
143 switch (ob_ref.object->type) {
144 case OB_CURVES: {
145 const char *error = nullptr;
146 /* The error string will always have been printed by the engine already.
147 * No need to display it twice. */
148 geom = curves_sub_pass_setup(*prepass_curves_ps_, state.scene, ob_ref.object, error);
149 prepass_curves_ps_->draw(geom, manager.unique_handle(ob_ref));
150 break;
151 }
152 case OB_GREASE_PENCIL:
153 GreasePencil::draw_grease_pencil(
154 res, *prepass_gpencil_ps_, state.scene, ob_ref.object, manager.unique_handle(ob_ref));
155 break;
156 case OB_MESH:
157 if (state.xray_enabled_and_not_wire) {
158 geom = DRW_cache_mesh_edge_detection_get(ob_ref.object, nullptr);
159 prepass_wire_ps_->draw_expand(geom, GPU_PRIM_LINES, 1, 1, manager.unique_handle(ob_ref));
160 }
161 else {
162 geom = DRW_cache_mesh_surface_get(ob_ref.object);
163 prepass_mesh_ps_->draw(geom, manager.unique_handle(ob_ref));
164
165 /* Display flat object as a line when view is orthogonal to them.
166 * This fixes only the biggest case which is a plane in ortho view. */
167 int flat_axis = FlatObjectRef::flat_axis_index_get(ob_ref.object);
168 if (flat_axis != -1) {
169 geom = DRW_cache_mesh_edge_detection_get(ob_ref.object, nullptr);
170 flat_objects_.append({geom, manager.unique_handle(ob_ref), flat_axis});
171 }
172 }
173 break;
174 case OB_POINTCLOUD:
175 /* Looks bad in wireframe mode. Could be relaxed if we draw a wireframe of some sort in
176 * the future. */
177 if (!state.is_wireframe_mode) {
178 geom = pointcloud_sub_pass_setup(*prepass_pointcloud_ps_, ob_ref.object);
179 prepass_pointcloud_ps_->draw(geom, manager.unique_handle(ob_ref));
180 }
181 break;
182 case OB_VOLUME:
183 geom = DRW_cache_volume_selection_surface_get(ob_ref.object);
184 /* TODO(fclem): Get rid of these check and enforce correct API on the batch cache. */
185 if (geom) {
186 prepass_volume_ps_->draw(geom, manager.unique_handle(ob_ref));
187 }
188 break;
189 default:
190 break;
191 }
192 }
193
194 /* Flat objects outline workaround need to generate passes for each redraw. */
196 {
197 outline_prepass_flat_ps_.init();
198
199 if (!enabled_) {
200 return;
201 }
202
203 if (!view.is_persp()) {
204 const bool is_transform = (G.moving & G_TRANSFORM_OBJ) != 0;
205 /* Note: We need a dedicated pass since we have to populated it for each redraw. */
206 auto &pass = outline_prepass_flat_ps_;
207 pass.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
208 pass.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
209 pass.framebuffer_set(&prepass_fb_);
211 state.clipping_plane_count);
212 pass.shader_set(res.shaders->outline_prepass_wire.get());
213 pass.push_constant("is_transform", is_transform);
214
215 for (FlatObjectRef flag_ob_ref : flat_objects_) {
216 flag_ob_ref.if_flat_axis_orthogonal_to_view(
217 manager, view, [&](gpu::Batch *geom, ResourceIndex resource_index) {
218 pass.draw_expand(geom, GPU_PRIM_LINES, 1, 1, resource_index);
219 });
220 }
221 }
222 }
223
224 void pre_draw(Manager &manager, View &view) final
225 {
226 if (!enabled_) {
227 return;
228 }
229
230 manager.generate_commands(outline_prepass_ps_, view);
231 manager.generate_commands(outline_prepass_flat_ps_, view);
232 }
233
234 /* TODO(fclem): Remove dependency on Resources. */
235 void draw_line_only_ex(Framebuffer &framebuffer, Resources &res, Manager &manager, View &view)
236 {
237 if (!enabled_) {
238 return;
239 }
240
241 GPU_debug_group_begin("Outline");
242
243 int2 render_size = int2(res.depth_tx.size());
244
246 tmp_depth_tx_.acquire(render_size, gpu::TextureFormat::SFLOAT_32_DEPTH_UINT_8, usage);
247 object_id_tx_.acquire(render_size, gpu::TextureFormat::UINT_16, usage);
248
249 prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(tmp_depth_tx_),
250 GPU_ATTACHMENT_TEXTURE(object_id_tx_));
251
252 manager.submit_only(outline_prepass_ps_, view);
253 manager.submit_only(outline_prepass_flat_ps_, view);
254
255 GPU_framebuffer_bind(framebuffer);
256 manager.submit(outline_resolve_ps_, view);
257
258 tmp_depth_tx_.release();
259 object_id_tx_.release();
260
262 }
263};
264
265} // namespace blender::draw::overlay
@ G_TRANSFORM_OBJ
@ OB_BOUNDBOX
@ OB_GREASE_PENCIL
@ OB_MESH
@ OB_POINTCLOUD
@ OB_VOLUME
@ OB_CURVES
@ USER_GPU_FLAG_OVERLAY_SMOOTH_WIRE
@ V3D_SELECT_OUTLINE
static AppView * view
void GPU_debug_group_end()
Definition gpu_debug.cc:33
void GPU_debug_group_begin(const char *name)
Definition gpu_debug.cc:22
#define GPU_ATTACHMENT_TEXTURE(_texture)
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
@ GPU_PRIM_LINES
@ GPU_PRIM_TRIS
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_ATTACHMENT
@ TH_OUTLINE_WIDTH
float UI_GetThemeValuef(int colorid)
#define U
void submit_only(PassMain &pass, View &view)
void submit(PassSimple &pass, View &view)
int3 size(int miplvl=0) const
detail::PassBase< command::DrawMultiBuf > Sub
Definition draw_pass.hh:499
void object_sync(Manager &manager, const ObjectRef &ob_ref, Resources &res, const State &state) final
void flat_objects_pass_sync(Manager &manager, View &view, Resources &res, const State &state)
void pre_draw(Manager &manager, View &view) final
void begin_sync(Resources &res, const State &state) final
void draw_line_only_ex(Framebuffer &framebuffer, Resources &res, Manager &manager, View &view)
blender::gpu::Shader * get()
#define DRW_CLIPPING_UBO_SLOT
#define OVERLAY_GLOBALS_SLOT
@ 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]
#define G(x, y, z)
static void error(const char *str)
detail::Pass< command::DrawCommandBuf > PassSimple
detail::Pass< command::DrawMultiBuf > PassMain
gpu::Batch * curves_sub_pass_setup(PassMain::Sub &ps, const Scene *scene, Object *ob, const char *&r_error, GPUMaterial *gpu_material=nullptr)
gpu::Batch * pointcloud_sub_pass_setup(PassMain::Sub &sub_ps, Object *object, GPUMaterial *gpu_material=nullptr)
gpu::Batch * DRW_cache_mesh_edge_detection_get(Object *ob, bool *r_is_manifold)
gpu::Batch * DRW_cache_volume_selection_surface_get(Object *ob)
gpu::Batch * DRW_cache_mesh_surface_get(Object *ob)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
static int flat_axis_index_get(const Object *ob)
draw::UniformArrayBuffer< float4, 6 > clip_planes_buf
draw::UniformBuffer< UniformData > globals_buf