Blender V5.0
overlay_sculpt.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 "BKE_attribute.hh"
12#include "BKE_curves.hh"
13#include "BKE_mesh.hh"
14#include "BKE_paint.hh"
15#include "BKE_paint_bvh.hh"
16#include "BKE_subdiv_ccg.hh"
18
19#include "DRW_render.hh"
20#include "bmesh.hh"
21
22#include "draw_cache_impl.hh"
23#include "draw_sculpt.hh"
24
25#include "overlay_base.hh"
26
27namespace blender::draw::overlay {
28
35
36 private:
37 PassSimple sculpt_mask_ = {"SculptMaskAndFaceSet"};
38 PassSimple::Sub *mesh_ps_ = nullptr;
39 PassSimple::Sub *curves_ps_ = nullptr;
40
41 PassSimple sculpt_curve_cage_ = {"SculptCage"};
42
43 bool show_curves_cage_ = false;
44 bool show_face_set_ = false;
45 bool show_mask_ = false;
46
47 public:
48 void begin_sync(Resources &res, const State &state) final
49 {
50 show_curves_cage_ = state.show_sculpt_curves_cage();
51 show_face_set_ = state.show_sculpt_face_sets();
52 show_mask_ = state.show_sculpt_mask();
53
54 enabled_ = state.is_space_v3d() && !state.is_wire() && !res.is_selection() &&
55 !state.is_depth_only_drawing &&
57 (show_curves_cage_ || show_face_set_ || show_mask_);
58
59 if (!enabled_) {
60 /* Not used. But release the data. */
61 sculpt_mask_.init();
62 sculpt_curve_cage_.init();
63 return;
64 }
65
66 float curve_cage_opacity = show_curves_cage_ ? state.overlay.sculpt_curves_cage_opacity : 0.0f;
67 float face_set_opacity = show_face_set_ ? state.overlay.sculpt_mode_face_sets_opacity : 0.0f;
68 float mask_opacity = show_mask_ ? state.overlay.sculpt_mode_mask_opacity : 0.0f;
69
70 {
71 sculpt_mask_.init();
72 sculpt_mask_.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
73 sculpt_mask_.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
74 {
75 auto &sub = sculpt_mask_.sub("Mesh");
77 state.clipping_plane_count);
78 sub.shader_set(res.shaders->sculpt_mesh.get());
79 sub.push_constant("mask_opacity", mask_opacity);
80 sub.push_constant("face_sets_opacity", face_set_opacity);
81 mesh_ps_ = ⊂
82 }
83 {
84 auto &sub = sculpt_mask_.sub("Curves");
86 state.clipping_plane_count);
87 sub.shader_set(res.shaders->sculpt_curves.get());
88 sub.push_constant("selection_opacity", mask_opacity);
89 curves_ps_ = ⊂
90 }
91 }
92 {
93 auto &pass = sculpt_curve_cage_;
94 pass.init();
96 state.clipping_plane_count);
97 pass.shader_set(res.shaders->sculpt_curves_cage.get());
98 pass.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
99 pass.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
100 pass.push_constant("opacity", curve_cage_opacity);
101 }
102 }
103
104 void object_sync(Manager &manager,
105 const ObjectRef &ob_ref,
106 Resources & /*res*/,
107 const State &state) final
108 {
109 if (!enabled_) {
110 return;
111 }
112
113 switch (ob_ref.object->type) {
114 case OB_MESH:
115 mesh_sync(manager, ob_ref, state);
116 break;
117 case OB_CURVES:
118 curves_sync(manager, ob_ref, state);
119 break;
120 }
121 }
122
123 void curves_sync(Manager &manager, const ObjectRef &ob_ref, const State &state)
124 {
126
127 /* As an optimization, draw nothing if everything is selected. */
128 if (show_mask_ && !everything_selected(curves)) {
129 /* Retrieve the location of the texture. */
130 bool is_point_domain;
131 bool is_valid;
133 &curves, ".selection", is_point_domain, is_valid);
134 if (is_valid) {
135 /* Evaluate curves and their attributes if necessary. */
136 const char *error = nullptr;
137 /* The error string will always have been printed by the engine already.
138 * No need to display it twice. */
139 gpu::Batch *geometry = curves_sub_pass_setup(
140 *curves_ps_, state.scene, ob_ref.object, error);
141 if (select_attr_buf.get()) {
142 ResourceHandleRange handle = manager.unique_handle(ob_ref);
143
144 curves_ps_->push_constant("is_point_domain", is_point_domain);
145 curves_ps_->bind_texture("selection_tx", select_attr_buf);
146 curves_ps_->draw(geometry, handle);
147 }
148 }
149 }
150
151 if (show_curves_cage_) {
152 ResourceHandleRange handle = manager.unique_handle(ob_ref);
153
154 blender::gpu::Batch *geometry = DRW_curves_batch_cache_get_sculpt_curves_cage(&curves);
155 sculpt_curve_cage_.draw(geometry, handle);
156 }
157 }
158
159 void mesh_sync(Manager &manager, const ObjectRef &ob_ref, const State &state)
160 {
161 if (!show_face_set_ && !show_mask_) {
162 /* Nothing to display. */
163 return;
164 }
165
166 const SculptSession *sculpt_session = ob_ref.object->sculpt;
167 if (sculpt_session == nullptr) {
168 return;
169 }
170
172 if (!pbvh) {
173 /* It is possible to have SculptSession without pbvh::Tree. This happens, for example, when
174 * toggling object mode to sculpt then to edit mode. */
175 return;
176 }
177
178 /* Using the original object/geometry is necessary because we skip depsgraph updates in sculpt
179 * mode to improve performance. This means the evaluated mesh doesn't have the latest face set,
180 * visibility, and mask data. */
181 Object *object_orig = DEG_get_original(ob_ref.object);
182 if (!object_orig) {
184 return;
185 }
186
187 switch (pbvh->type()) {
189 const Mesh &mesh = DRW_object_get_data_for_drawing<Mesh>(*object_orig);
190 if (!mesh.attributes().contains(".sculpt_face_set") &&
191 !mesh.attributes().contains(".sculpt_mask"))
192 {
193 return;
194 }
195 break;
196 }
198 const SubdivCCG &subdiv_ccg = *sculpt_session->subdiv_ccg;
199 const Mesh &base_mesh = DRW_object_get_data_for_drawing<Mesh>(*object_orig);
200 if (subdiv_ccg.masks.is_empty() && !base_mesh.attributes().contains(".sculpt_face_set")) {
201 return;
202 }
203 break;
204 }
206 const BMesh &bm = *sculpt_session->bm;
207 if (!CustomData_has_layer_named(&bm.pdata, CD_PROP_FLOAT, ".sculpt_face_set") &&
208 !CustomData_has_layer_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask"))
209 {
210 return;
211 }
212 break;
213 }
214 }
215
216 const bool use_pbvh = BKE_sculptsession_use_pbvh_draw(ob_ref.object, state.rv3d);
217 if (use_pbvh) {
218 ResourceHandleRange handle = manager.unique_handle_for_sculpt(ob_ref);
219
220 SculptBatchFeature sculpt_batch_features_ = (show_face_set_ ? SCULPT_BATCH_FACE_SET :
222 (show_mask_ ? SCULPT_BATCH_MASK :
224
225 for (SculptBatch &batch : sculpt_batches_get(ob_ref.object, sculpt_batch_features_)) {
226 mesh_ps_->draw(batch.batch, handle);
227 }
228 }
229 else {
230 ResourceHandleRange handle = manager.unique_handle(ob_ref);
231
233 gpu::Batch *sculpt_overlays = DRW_mesh_batch_cache_get_sculpt_overlays(mesh);
234 mesh_ps_->draw(sculpt_overlays, handle);
235 }
236 }
237
238 void draw_line(Framebuffer &framebuffer, Manager &manager, View &view) final
239 {
240 if (!enabled_) {
241 return;
242 }
243 GPU_framebuffer_bind(framebuffer);
244 manager.submit(sculpt_curve_cage_, view);
245 }
246
247 void draw_on_render(gpu::FrameBuffer *framebuffer, Manager &manager, View &view) final
248 {
249 if (!enabled_) {
250 return;
251 }
252 GPU_framebuffer_bind(framebuffer);
253 manager.submit(sculpt_mask_, view);
254 }
255
256 private:
257 bool everything_selected(const ::Curves &curves_id)
258 {
259 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
260 const VArray<bool> selection = *curves.attributes().lookup_or_default<bool>(
261 ".selection", bke::AttrDomain::Point, true);
262 return selection.is_single() && selection.get_internal_single();
263 }
264};
265
266} // namespace blender::draw::overlay
Low-level operations for curves.
bool CustomData_has_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
bool BKE_sculptsession_use_pbvh_draw(const Object *ob, const RegionView3D *rv3d)
Definition paint.cc:3068
A BVH for high poly meshes.
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define ELEM(...)
T * DEG_get_original(T *id)
@ CD_PROP_FLOAT
@ OB_MODE_SCULPT
@ OB_MODE_SCULPT_CURVES
@ OB_MESH
@ OB_CURVES
T & DRW_object_get_data_for_drawing(const Object &object)
static AppView * view
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
BMesh * bm
bool is_empty() const
Definition BLI_array.hh:264
AttributeSet attributes
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
AttributeAccessor attributes() const
ResourceHandleRange unique_handle_for_sculpt(const ObjectRef &ref)
ResourceHandleRange unique_handle(const ObjectRef &ref)
detail::PassBase< command::DrawCommandBuf > Sub
Definition draw_pass.hh:499
void object_sync(Manager &manager, const ObjectRef &ob_ref, Resources &, const State &state) final
void mesh_sync(Manager &manager, const ObjectRef &ob_ref, const State &state)
void draw_line(Framebuffer &framebuffer, Manager &manager, View &view) final
void begin_sync(Resources &res, const State &state) final
void curves_sync(Manager &manager, const ObjectRef &ob_ref, const State &state)
void draw_on_render(gpu::FrameBuffer *framebuffer, Manager &manager, View &view) final
#define DRW_CLIPPING_UBO_SLOT
#define OVERLAY_GLOBALS_SLOT
@ DRW_STATE_BLEND_ALPHA
Definition draw_state.hh:55
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
@ DRW_STATE_DEPTH_LESS_EQUAL
Definition draw_state.hh:38
@ DRW_STATE_BLEND_MUL
Definition draw_state.hh:60
struct @021025263243242147216143265077100330027142264337::@225245033123204053237120173316075113304004012000 batch
static ulong state[N]
static void error(const char *str)
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:3052
detail::Pass< command::DrawCommandBuf > PassSimple
gpu::Batch * curves_sub_pass_setup(PassMain::Sub &ps, const Scene *scene, Object *ob, const char *&r_error, GPUMaterial *gpu_material=nullptr)
Vector< SculptBatch > sculpt_batches_get(const Object *ob, SculptBatchFeature features)
blender::gpu::Batch * DRW_curves_batch_cache_get_sculpt_curves_cage(Curves *curves)
blender::gpu::Batch * DRW_mesh_batch_cache_get_sculpt_overlays(Mesh &mesh)
blender::gpu::VertBufPtr & DRW_curves_texture_for_evaluated_attribute(Curves *curves, StringRef name, bool &r_is_point_domain, bool &r_valid_attribute)
std::unique_ptr< gpu::VertBuf, gpu::VertBufDeleter > VertBufPtr
struct SculptSession * sculpt
SubdivCCG * subdiv_ccg
Definition BKE_paint.hh:395
blender::Array< float > masks