Blender V5.0
overlay_paint.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_image.hh"
12#include "BKE_paint.hh"
13#include "BKE_scene.hh"
14
16
17#include "draw_cache.hh"
18#include "draw_cache_impl.hh"
19
20#include "overlay_base.hh"
21
22namespace blender::draw::overlay {
23
28class Paints : Overlay {
29
30 private:
31 /* Draw selection state on top of the mesh to communicate which areas can be painted on. */
32 PassSimple paint_region_ps_ = {"paint_region_ps_"};
33 PassSimple::Sub *paint_region_edge_ps_ = nullptr;
34 PassSimple::Sub *paint_region_face_ps_ = nullptr;
35 PassSimple::Sub *paint_region_vert_ps_ = nullptr;
36
37 PassSimple weight_ps_ = {"weight_ps_"};
38 /* Used when there's not a valid pre-pass (depth <=). */
39 PassSimple::Sub *weight_opaque_ps_ = nullptr;
40 /* Used when there's a valid pre-pass (depth ==). */
41 PassSimple::Sub *weight_masked_transparency_ps_ = nullptr;
42 /* Black and white mask overlayed on top of mesh to preview painting influence. */
43 PassSimple paint_mask_ps_ = {"paint_mask_ps_"};
44
45 bool show_weight_ = false;
46 bool show_wires_ = false;
47 bool show_paint_mask_ = false;
48 bool masked_transparency_support_ = false;
49
50 public:
51 void begin_sync(Resources &res, const State &state) final
52 {
53 enabled_ =
54 state.is_space_v3d() && !res.is_selection() &&
56
57 /* Init in any case to release the data. */
58 paint_region_ps_.init();
59 weight_ps_.init();
60 paint_mask_ps_.init();
61
62 if (!enabled_) {
63 return;
64 }
65
66 show_weight_ = state.ctx_mode == CTX_MODE_PAINT_WEIGHT;
67 show_wires_ = state.overlay.paint_flag & V3D_OVERLAY_PAINT_WIRE;
68
69 {
70 auto &pass = paint_region_ps_;
71 pass.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
72 pass.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
73 {
74 auto &sub = pass.sub("Face");
77 state.clipping_plane_count);
78 sub.shader_set(res.shaders->paint_region_face.get());
79 sub.push_constant("ucolor", float4(1.0, 1.0, 1.0, 0.2));
80 paint_region_face_ps_ = &sub;
81 }
82 {
83 auto &sub = pass.sub("Edge");
86 state.clipping_plane_count);
87 sub.shader_set(res.shaders->paint_region_edge.get());
88 paint_region_edge_ps_ = &sub;
89 }
90 {
91 auto &sub = pass.sub("Vert");
93 state.clipping_plane_count);
94 sub.shader_set(res.shaders->paint_region_vert.get());
95 paint_region_vert_ps_ = &sub;
96 }
97 }
98
99 if (state.ctx_mode == CTX_MODE_PAINT_WEIGHT) {
100 /* Support masked transparency in Workbench.
101 * EEVEE can't be supported since depth won't match. */
102 const eDrawType shading_type = eDrawType(state.v3d->shading.type);
103 masked_transparency_support_ = ((shading_type == OB_SOLID) ||
104 (shading_type >= OB_SOLID &&
106 !state.xray_enabled;
107 const bool shadeless = shading_type == OB_WIRE;
108 const bool draw_contours = state.overlay.wpaint_flag & V3D_OVERLAY_WPAINT_CONTOURS;
109
110 auto &pass = weight_ps_;
111 pass.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
112 pass.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
113 auto weight_subpass = [&](const char *name, DRWState drw_state) {
114 auto &sub = pass.sub(name);
115 sub.state_set(drw_state, state.clipping_plane_count);
116 sub.shader_set(shadeless ? res.shaders->paint_weight.get() :
117 res.shaders->paint_weight_fake_shading.get());
118 sub.bind_texture("colorramp", &res.weight_ramp_tx);
119 sub.push_constant("draw_contours", draw_contours);
120 sub.push_constant("opacity", state.overlay.weight_paint_mode_opacity);
121 if (!shadeless) {
122 /* Arbitrary light to give a hint of the geometry behind the weights. */
123 sub.push_constant("light_dir", math::normalize(float3(0.0f, 0.5f, 0.86602f)));
124 }
125 return &sub;
126 };
127 weight_opaque_ps_ = weight_subpass(
129 weight_masked_transparency_ps_ = weight_subpass(
130 "Masked Transparency",
132 }
133
134 if (state.ctx_mode == CTX_MODE_PAINT_TEXTURE) {
135 const ImagePaintSettings &paint_settings = state.scene->toolsettings->imapaint;
136 show_paint_mask_ = paint_settings.stencil &&
137 (paint_settings.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL);
138
139 if (show_paint_mask_) {
140 const bool mask_premult = (paint_settings.stencil->alpha_mode == IMA_ALPHA_PREMUL);
141 const bool mask_inverted = (paint_settings.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV);
142 gpu::Texture *mask_texture = BKE_image_get_gpu_texture(paint_settings.stencil, nullptr);
143
144 auto &pass = paint_mask_ps_;
146 state.clipping_plane_count);
147 pass.shader_set(res.shaders->paint_texture.get());
148 pass.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
149 pass.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
150 pass.bind_texture("mask_image", mask_texture);
151 pass.push_constant("maskPremult", mask_premult);
152 pass.push_constant("mask_invert_stencil", mask_inverted);
153 pass.push_constant("mask_color", float3(paint_settings.stencil_col));
154 pass.push_constant("opacity", state.overlay.texture_paint_mode_opacity);
155 }
156 }
157 }
158
159 void object_sync(Manager &manager,
160 const ObjectRef &ob_ref,
161 Resources & /*res*/,
162 const State &state) final
163 {
164 if (!enabled_) {
165 return;
166 }
167
168 if (ob_ref.object->type != OB_MESH) {
169 /* Only meshes are supported for now. */
170 return;
171 }
172
173 switch (state.ctx_mode) {
175 if (ob_ref.object->mode != OB_MODE_WEIGHT_PAINT) {
176 /* Not matching context mode. */
177 return;
178 }
179 break;
181 if (ob_ref.object->mode != OB_MODE_VERTEX_PAINT) {
182 /* Not matching context mode. */
183 return;
184 }
185 break;
187 if (ob_ref.object->mode != OB_MODE_TEXTURE_PAINT) {
188 /* Not matching context mode. */
189 return;
190 }
191 break;
192 default:
193 /* Not in paint mode. */
194 return;
195 }
196
197 switch (state.ctx_mode) {
199 gpu::Batch *geom = DRW_cache_mesh_surface_weights_get(ob_ref.object);
200 if (masked_transparency_support_ && ob_ref.object->dt >= OB_SOLID) {
201 weight_masked_transparency_ps_->draw(geom, manager.unique_handle(ob_ref));
202 }
203 else {
204 weight_opaque_ps_->draw(geom, manager.unique_handle(ob_ref));
205 }
206 break;
207 }
209 /* Drawing of vertex paint color is done by the render engine (i.e. workbench). */
210 break;
211 }
213 if (show_paint_mask_) {
214 gpu::Batch *geom = DRW_cache_mesh_surface_texpaint_single_get(ob_ref.object);
215 paint_mask_ps_.draw(geom, manager.unique_handle(ob_ref));
216 }
217 break;
218 }
219 default:
221 return;
222 }
223
224 /* Selection Display. */
225 {
226 /* NOTE(fclem): Why do we need original mesh here, only to get the flag? */
228 *DEG_get_original(ob_ref.object));
229 const bool use_face_selection = (mesh_orig.editflag & ME_EDIT_PAINT_FACE_SEL);
230 const bool use_vert_selection = (mesh_orig.editflag & ME_EDIT_PAINT_VERT_SEL);
231 /* Texture paint mode only draws the face selection without wires or vertices as we don't
232 * draw on the geometry data directly. */
233 const bool in_texture_paint_mode = state.ctx_mode == CTX_MODE_PAINT_TEXTURE;
234
235 if ((use_face_selection || show_wires_) && !in_texture_paint_mode) {
236 gpu::Batch *geom = DRW_cache_mesh_paint_overlay_edges_get(ob_ref.object);
237 paint_region_edge_ps_->push_constant("use_select", use_face_selection);
238 paint_region_edge_ps_->draw(geom, manager.unique_handle(ob_ref));
239 }
240 if (use_face_selection) {
241 gpu::Batch *geom = DRW_cache_mesh_paint_overlay_surface_get(ob_ref.object);
242 paint_region_face_ps_->draw(geom, manager.unique_handle(ob_ref));
243 }
244 if (use_vert_selection && !in_texture_paint_mode) {
245 gpu::Batch *geom = DRW_cache_mesh_paint_overlay_verts_get(ob_ref.object);
246 paint_region_vert_ps_->draw(geom, manager.unique_handle(ob_ref));
247 }
248 }
249 }
250
251 void draw(Framebuffer &framebuffer, Manager &manager, View &view) final
252 {
253 if (!enabled_) {
254 return;
255 }
256 GPU_framebuffer_bind(framebuffer);
257 manager.submit(weight_ps_, view);
258 manager.submit(paint_mask_ps_, view);
259 /* TODO(fclem): Draw this onto the line frame-buffer to get wide-line and anti-aliasing.
260 * Just need to make sure the shaders output line data. */
261 manager.submit(paint_region_ps_, view);
262 }
263};
264
265} // namespace blender::draw::overlay
@ CTX_MODE_PAINT_TEXTURE
@ CTX_MODE_PAINT_VERTEX
@ CTX_MODE_PAINT_WEIGHT
blender::gpu::Texture * BKE_image_get_gpu_texture(Image *image, ImageUser *iuser)
Definition image_gpu.cc:496
bool BKE_scene_uses_blender_workbench(const Scene *scene)
Definition scene.cc:2829
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define ELEM(...)
T * DEG_get_original(T *id)
@ IMA_ALPHA_PREMUL
@ ME_EDIT_PAINT_VERT_SEL
@ ME_EDIT_PAINT_FACE_SEL
eDrawType
@ OB_WIRE
@ OB_SOLID
@ OB_MODE_WEIGHT_PAINT
@ OB_MODE_TEXTURE_PAINT
@ OB_MODE_VERTEX_PAINT
@ OB_MESH
@ IMAGEPAINT_PROJECT_LAYER_STENCIL_INV
@ IMAGEPAINT_PROJECT_LAYER_STENCIL
@ V3D_OVERLAY_WPAINT_CONTOURS
@ V3D_OVERLAY_PAINT_WIRE
T & DRW_object_get_data_for_drawing(const Object &object)
static AppView * view
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
detail::PassBase< command::DrawCommandBuf > Sub
Definition draw_pass.hh:499
void draw(Framebuffer &framebuffer, Manager &manager, View &view) final
void begin_sync(Resources &res, const State &state) final
void object_sync(Manager &manager, const ObjectRef &ob_ref, Resources &, const State &state) final
#define DRW_CLIPPING_UBO_SLOT
#define OVERLAY_GLOBALS_SLOT
DRWState
Definition draw_state.hh:25
@ DRW_STATE_BLEND_ALPHA
Definition draw_state.hh:55
@ DRW_STATE_DEPTH_EQUAL
Definition draw_state.hh:39
@ 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
static ulong state[N]
detail::Pass< command::DrawCommandBuf > PassSimple
gpu::Batch * DRW_cache_mesh_paint_overlay_edges_get(Object *ob)
gpu::Batch * DRW_cache_mesh_paint_overlay_verts_get(Object *ob)
gpu::Batch * DRW_cache_mesh_surface_weights_get(Object *ob)
gpu::Batch * DRW_cache_mesh_paint_overlay_surface_get(Object *ob)
gpu::Batch * DRW_cache_mesh_surface_texpaint_single_get(Object *ob)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< float, 4 > float4
VecBase< float, 3 > float3
const char * name
struct Image * stencil
char alpha_mode
char editflag