Blender V4.3
overlay_next_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
9#pragma once
10
12
13#include "draw_common.hh"
14
15namespace blender::draw::overlay {
16
17class Outline {
18 private:
19 /* Simple render pass that renders an object ID pass. */
20 PassMain outline_prepass_ps_ = {"Prepass"};
21 PassMain::Sub *prepass_curves_ps_ = nullptr;
22 PassMain::Sub *prepass_pointcloud_ps_ = nullptr;
23 PassMain::Sub *prepass_gpencil_ps_ = nullptr;
24 PassMain::Sub *prepass_mesh_ps_ = nullptr;
25 PassMain::Sub *prepass_volume_ps_ = nullptr;
26 PassMain::Sub *prepass_wire_ps_ = nullptr;
27 /* Detect edges inside the ID pass and output color for each of them. */
28 PassSimple outline_resolve_ps_ = {"Resolve"};
29
30 TextureFromPool object_id_tx_ = {"outline_ob_id_tx"};
31 TextureFromPool tmp_depth_tx_ = {"outline_depth_tx"};
32
33 Framebuffer prepass_fb_ = {"outline.prepass_fb"};
34
35 bool enabled = false;
36
37 overlay::GreasePencil::ViewParameters grease_pencil_view;
38
39 public:
40 void begin_sync(Resources &res, const State &state)
41 {
42 enabled = (state.v3d_flag & V3D_SELECT_OUTLINE);
43 if (!enabled) {
44 return;
45 }
46
47 {
48 /* TODO(fclem): This is against design. We should not sync depending on view position.
49 * Eventually, we should do this in a compute shader prepass. */
50 float4x4 viewinv;
51 DRW_view_viewmat_get(nullptr, viewinv.ptr(), true);
52 grease_pencil_view = {DRW_view_is_persp_get(nullptr), viewinv};
53 }
54
55 const float outline_width = UI_GetThemeValuef(TH_OUTLINE_WIDTH);
56 const bool do_smooth_lines = (U.gpu_flag & USER_GPU_FLAG_OVERLAY_SMOOTH_WIRE) != 0;
57 const bool do_expand = (U.pixelsize > 1.0) || (outline_width > 2.0f);
58 const bool is_transform = (G.moving & G_TRANSFORM_OBJ) != 0;
59
60 {
61 auto &pass = outline_prepass_ps_;
62 pass.init();
63 pass.framebuffer_set(&prepass_fb_);
64 pass.clear_color_depth_stencil(float4(0.0f), 1.0f, 0x0);
66 state.clipping_plane_count);
67 {
68 auto &sub = pass.sub("Curves");
69 sub.shader_set(res.shaders.outline_prepass_curves.get());
70 sub.push_constant("isTransform", is_transform);
71 sub.bind_ubo("globalsBlock", &res.globals_buf);
72 prepass_curves_ps_ = ⊂
73 }
74 {
75 auto &sub = pass.sub("PointCloud");
77 sub.push_constant("isTransform", is_transform);
78 sub.bind_ubo("globalsBlock", &res.globals_buf);
79 prepass_pointcloud_ps_ = ⊂
80 }
81 {
82 auto &sub = pass.sub("GreasePencil");
84 sub.push_constant("isTransform", is_transform);
85 sub.bind_ubo("globalsBlock", &res.globals_buf);
86 prepass_gpencil_ps_ = ⊂
87 }
88 {
89 auto &sub = pass.sub("Mesh");
91 sub.push_constant("isTransform", is_transform);
92 sub.bind_ubo("globalsBlock", &res.globals_buf);
93 prepass_mesh_ps_ = ⊂
94 }
95 {
96 auto &sub = pass.sub("Volume");
98 sub.push_constant("isTransform", is_transform);
99 sub.bind_ubo("globalsBlock", &res.globals_buf);
100 prepass_volume_ps_ = ⊂
101 }
102 {
103 auto &sub = pass.sub("Wire");
105 sub.push_constant("isTransform", is_transform);
106 sub.bind_ubo("globalsBlock", &res.globals_buf);
107 prepass_wire_ps_ = ⊂
108 }
109 }
110 {
111 auto &pass = outline_resolve_ps_;
112 pass.init();
113 pass.framebuffer_set(&res.overlay_line_only_fb);
115 pass.shader_set(res.shaders.outline_detect.get());
116 /* Don't occlude the outline if in xray mode as it causes too much flickering. */
117 pass.push_constant("alphaOcclu", state.xray_enabled ? 1.0f : 0.35f);
118 pass.push_constant("doThickOutlines", do_expand);
119 pass.push_constant("doAntiAliasing", do_smooth_lines);
120 pass.push_constant("isXrayWires", state.xray_enabled_and_not_wire);
121 pass.bind_texture("outlineId", &object_id_tx_);
122 pass.bind_texture("sceneDepth", &res.depth_tx);
123 pass.bind_texture("outlineDepth", &tmp_depth_tx_);
124 pass.bind_ubo("globalsBlock", &res.globals_buf);
125 pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
126 }
127 }
128
129 void object_sync(Manager &manager, const ObjectRef &ob_ref, const State &state)
130 {
131 if (!enabled) {
132 return;
133 }
134
135 /* Outlines of bounding boxes are not drawn. */
136 if (ob_ref.object->dt == OB_BOUNDBOX) {
137 return;
138 }
139
140 gpu::Batch *geom;
141 switch (ob_ref.object->type) {
143 /* TODO ? */
144 break;
145 case OB_CURVES:
146 geom = curves_sub_pass_setup(*prepass_curves_ps_, state.scene, ob_ref.object);
147 prepass_curves_ps_->draw(geom, manager.unique_handle(ob_ref));
148 break;
149 case OB_GREASE_PENCIL:
150 GreasePencil::draw_grease_pencil(*prepass_gpencil_ps_,
151 grease_pencil_view,
152 state.scene,
153 ob_ref.object,
154 manager.unique_handle(ob_ref));
155 break;
156 case OB_MESH:
157 if (!state.xray_enabled_and_not_wire) {
158 geom = DRW_cache_mesh_surface_get(ob_ref.object);
159 prepass_mesh_ps_->draw(geom, manager.unique_handle(ob_ref));
160 }
161 {
162 /* TODO(fclem): This is against design. We should not sync depending on view position.
163 * Eventually, add a bounding box display pass with some special culling phase. */
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 = 0;
168 bool is_flat_object_viewed_from_side = ((state.rv3d->persp == RV3D_ORTHO) &&
169 DRW_object_is_flat(ob_ref.object, &flat_axis) &&
171 flat_axis));
172 if (state.xray_enabled_and_not_wire || is_flat_object_viewed_from_side) {
173 geom = DRW_cache_mesh_edge_detection_get(ob_ref.object, nullptr);
174 prepass_wire_ps_->draw_expand(
175 geom, GPU_PRIM_LINES, 1, 1, manager.unique_handle(ob_ref));
176 }
177 }
178 break;
179 case OB_POINTCLOUD:
180 /* Looks bad in wireframe mode. Could be relaxed if we draw a wireframe of some sort in
181 * the future. */
182 if (!state.is_wireframe_mode) {
183 geom = point_cloud_sub_pass_setup(*prepass_pointcloud_ps_, ob_ref.object);
184 prepass_pointcloud_ps_->draw(geom, manager.unique_handle(ob_ref));
185 }
186 break;
187 case OB_VOLUME:
189 prepass_volume_ps_->draw(geom, manager.unique_handle(ob_ref));
190 break;
191 default:
192 break;
193 }
194 }
195
196 void draw(Resources &res, Manager &manager, View &view)
197 {
198 if (!enabled) {
199 return;
200 }
201
202 GPU_debug_group_begin("Outline");
203
204 int2 render_size = int2(res.depth_tx.size());
205
207 tmp_depth_tx_.acquire(render_size, GPU_DEPTH24_STENCIL8, usage);
208 object_id_tx_.acquire(render_size, GPU_R16UI, usage);
209
210 prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(tmp_depth_tx_),
211 GPU_ATTACHMENT_TEXTURE(object_id_tx_));
212
213 manager.submit(outline_prepass_ps_, view);
214 manager.submit(outline_resolve_ps_, view);
215
216 tmp_depth_tx_.release();
217 object_id_tx_.release();
218
220 }
221};
222
223} // namespace blender::draw::overlay
@ G_TRANSFORM_OBJ
@ OB_BOUNDBOX
@ OB_GREASE_PENCIL
@ OB_MESH
@ OB_POINTCLOUD
@ OB_VOLUME
@ OB_GPENCIL_LEGACY
@ OB_CURVES
@ USER_GPU_FLAG_OVERLAY_SMOOTH_WIRE
@ RV3D_ORTHO
@ V3D_SELECT_OUTLINE
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)
@ GPU_PRIM_LINES
@ GPU_PRIM_TRIS
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_ATTACHMENT
@ GPU_R16UI
@ GPU_DEPTH24_STENCIL8
@ TH_OUTLINE_WIDTH
float UI_GetThemeValuef(int colorid)
unsigned int U
Definition btGjkEpa3.h:78
void ensure(GPUAttachment depth=GPU_ATTACHMENT_NONE, GPUAttachment color1=GPU_ATTACHMENT_NONE, GPUAttachment color2=GPU_ATTACHMENT_NONE, GPUAttachment color3=GPU_ATTACHMENT_NONE, GPUAttachment color4=GPU_ATTACHMENT_NONE, GPUAttachment color5=GPU_ATTACHMENT_NONE, GPUAttachment color6=GPU_ATTACHMENT_NONE, GPUAttachment color7=GPU_ATTACHMENT_NONE, GPUAttachment color8=GPU_ATTACHMENT_NONE)
ResourceHandle unique_handle(const ObjectRef &ref)
void submit(PassSimple &pass, View &view)
void acquire(int2 extent, eGPUTextureFormat format, eGPUTextureUsage usage=GPU_TEXTURE_USAGE_GENERAL)
int3 size(int miplvl=0) const
void draw(gpu::Batch *batch, uint instance_len=-1, uint vertex_len=-1, uint vertex_first=-1, ResourceHandle handle={0}, uint custom_id=0)
Definition draw_pass.hh:760
PassBase< DrawCommandBufType > & sub(const char *name)
Definition draw_pass.hh:616
void draw_expand(gpu::Batch *batch, GPUPrimType primitive_type, uint primitive_len, uint instance_len, uint vertex_len=-1, uint vertex_first=-1, ResourceHandle handle={0}, uint custom_id=0)
Definition draw_pass.hh:793
void shader_set(GPUShader *shader)
Definition draw_pass.hh:971
void begin_sync(Resources &res, const State &state)
void draw(Resources &res, Manager &manager, View &view)
void object_sync(Manager &manager, const ObjectRef &ob_ref, const State &state)
blender::gpu::Batch * DRW_cache_mesh_surface_get(Object *ob)
blender::gpu::Batch * DRW_cache_mesh_edge_detection_get(Object *ob, bool *r_is_manifold)
bool DRW_object_is_flat(Object *ob, int *r_axis)
bool DRW_object_axis_orthogonal_to_view(Object *ob, int axis)
bool DRW_view_is_persp_get(const DRWView *view)
void DRW_view_viewmat_get(const DRWView *view, float mat[4][4], bool inverse)
@ 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)
gpu::Batch * point_cloud_sub_pass_setup(PassMain::Sub &sub_ps, Object *object, GPUMaterial *gpu_material=nullptr)
gpu::Batch * curves_sub_pass_setup(PassMain::Sub &ps, const Scene *scene, Object *ob, GPUMaterial *gpu_material=nullptr)
blender::gpu::Batch * DRW_cache_volume_selection_surface_get(Object *ob)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
const c_style_mat & ptr() const