Blender V4.3
overlay_next_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
9#pragma once
10
11#include "BKE_image.hh"
12#include "BKE_paint.hh"
13
15
16#include "draw_cache_impl.hh"
17
19
20namespace blender::draw::overlay {
21
22class Paints {
23
24 private:
25 /* Draw selection state on top of the mesh to communicate which areas can be painted on. */
26 PassSimple paint_region_ps_ = {"paint_region_ps_"};
27 PassSimple::Sub *paint_region_edge_ps_ = nullptr;
28 PassSimple::Sub *paint_region_face_ps_ = nullptr;
29 PassSimple::Sub *paint_region_vert_ps_ = nullptr;
30
31 PassSimple weight_ps_ = {"weight_ps_"};
32 /* Black and white mask overlayed on top of mesh to preview painting influence. */
33 PassSimple paint_mask_ps_ = {"paint_mask_ps_"};
34
35 bool show_weight_ = false;
36 bool show_wires_ = false;
37 bool show_paint_mask_ = false;
38
39 bool enabled_ = false;
40
41 public:
42 void begin_sync(Resources &res, const State &state)
43 {
44 enabled_ =
45 (state.space_type == SPACE_VIEW3D) && (res.selection_type == SelectionType::DISABLED) &&
47
48 /* Init in any case to release the data. */
49 paint_region_ps_.init();
50 weight_ps_.init();
51 paint_mask_ps_.init();
52
53 if (!enabled_) {
54 return;
55 }
56
57 show_weight_ = state.ctx_mode == CTX_MODE_PAINT_WEIGHT;
58 show_wires_ = state.overlay.paint_flag & V3D_OVERLAY_PAINT_WIRE;
59
60 {
61 auto &pass = paint_region_ps_;
62 {
63 auto &sub = pass.sub("Face");
66 state.clipping_plane_count);
67 sub.shader_set(res.shaders.paint_region_face.get());
68 sub.bind_ubo("globalsBlock", &res.globals_buf);
69 sub.push_constant("ucolor", float4(1.0, 1.0, 1.0, 0.2));
70 paint_region_face_ps_ = ⊂
71 }
72 {
73 auto &sub = pass.sub("Edge");
76 state.clipping_plane_count);
77 sub.shader_set(res.shaders.paint_region_edge.get());
78 sub.bind_ubo("globalsBlock", &res.globals_buf);
79 paint_region_edge_ps_ = ⊂
80 }
81 {
82 auto &sub = pass.sub("Vert");
84 state.clipping_plane_count);
85 sub.shader_set(res.shaders.paint_region_vert.get());
86 sub.bind_ubo("globalsBlock", &res.globals_buf);
87 paint_region_vert_ps_ = ⊂
88 }
89 }
90
91 if (state.ctx_mode == CTX_MODE_PAINT_WEIGHT) {
92 /* Support masked transparency in Workbench.
93 * EEVEE can't be supported since depth won't match. */
94 const eDrawType shading_type = eDrawType(state.v3d->shading.type);
95 const bool masked_transparency_support = (shading_type <= OB_SOLID) ||
97 const bool shadeless = shading_type == OB_WIRE;
98 const bool draw_contours = state.overlay.wpaint_flag & V3D_OVERLAY_WPAINT_CONTOURS;
99
100 auto &pass = weight_ps_;
102 (masked_transparency_support ?
105 state.clipping_plane_count);
106 pass.shader_set(shadeless ? res.shaders.paint_weight.get() :
108 pass.bind_ubo("globalsBlock", &res.globals_buf);
109 pass.bind_texture("colorramp", &res.weight_ramp_tx);
110 pass.push_constant("drawContours", draw_contours);
111 pass.push_constant("opacity", state.overlay.weight_paint_mode_opacity);
112 if (!shadeless) {
113 /* Arbitrary light to give a hint of the geometry behind the weights. */
114 pass.push_constant("light_dir", math::normalize(float3(0.0f, 0.5f, 0.86602f)));
115 }
116 }
117
118 if (state.ctx_mode == CTX_MODE_PAINT_TEXTURE) {
119 const ImagePaintSettings &paint_settings = state.scene->toolsettings->imapaint;
120 show_paint_mask_ = paint_settings.stencil &&
121 (paint_settings.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL);
122
123 if (show_paint_mask_) {
124 const bool mask_premult = (paint_settings.stencil->alpha_mode == IMA_ALPHA_PREMUL);
125 const bool mask_inverted = (paint_settings.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV);
126 GPUTexture *mask_texture = BKE_image_get_gpu_texture(paint_settings.stencil, nullptr);
127
128 auto &pass = paint_mask_ps_;
130 state.clipping_plane_count);
131 pass.shader_set(res.shaders.paint_texture.get());
132 pass.bind_ubo("globalsBlock", &res.globals_buf);
133 pass.bind_texture("maskImage", mask_texture);
134 pass.push_constant("maskPremult", mask_premult);
135 pass.push_constant("maskInvertStencil", mask_inverted);
136 pass.push_constant("maskColor", float3(paint_settings.stencil_col));
137 pass.push_constant("opacity", state.overlay.texture_paint_mode_opacity);
138 }
139 }
140 }
141
142 void object_sync(Manager &manager, const ObjectRef &ob_ref, const State &state)
143 {
144 if (!enabled_) {
145 return;
146 }
147
148 if (ob_ref.object->type != OB_MESH) {
149 /* Only meshes are supported for now. */
150 return;
151 }
152
153 switch (state.ctx_mode) {
155 if (ob_ref.object->mode != OB_MODE_WEIGHT_PAINT) {
156 /* Not matching context mode. */
157 return;
158 }
159 break;
161 if (ob_ref.object->mode != OB_MODE_VERTEX_PAINT) {
162 /* Not matching context mode. */
163 return;
164 }
165 break;
167 if (ob_ref.object->mode != OB_MODE_TEXTURE_PAINT) {
168 /* Not matching context mode. */
169 return;
170 }
171 break;
172 default:
173 /* Not in paint mode. */
174 return;
175 }
176
177 switch (state.ctx_mode) {
179 gpu::Batch *geom = DRW_cache_mesh_surface_weights_get(ob_ref.object);
180 weight_ps_.draw(geom, manager.unique_handle(ob_ref));
181 break;
182 }
184 /* Drawing of vertex paint color is done by the render engine (i.e. workbench). */
185 break;
186 }
188 if (show_paint_mask_) {
189 gpu::Batch *geom = DRW_cache_mesh_surface_texpaint_single_get(ob_ref.object);
190 paint_mask_ps_.draw(geom, manager.unique_handle(ob_ref));
191 }
192 break;
193 }
194 default:
196 return;
197 }
198
199 /* Selection Display. */
200 {
201 /* NOTE(fclem): Why do we need original mesh here, only to get the flag? */
202 const Mesh &mesh_orig = *static_cast<Mesh *>(DEG_get_original_object(ob_ref.object)->data);
203 const bool use_face_selection = (mesh_orig.editflag & ME_EDIT_PAINT_FACE_SEL);
204 const bool use_vert_selection = (mesh_orig.editflag & ME_EDIT_PAINT_VERT_SEL);
205 /* Texture paint mode only draws the face selection without wires or vertices as we don't
206 * draw on the geometry data directly. */
207 const bool in_texture_paint_mode = state.ctx_mode == CTX_MODE_PAINT_TEXTURE;
208
209 if ((use_face_selection || show_wires_) && !in_texture_paint_mode) {
210 gpu::Batch *geom = DRW_cache_mesh_surface_edges_get(ob_ref.object);
211 paint_region_edge_ps_->push_constant("useSelect", use_face_selection);
212 paint_region_edge_ps_->draw(geom, manager.unique_handle(ob_ref));
213 }
214 if (use_face_selection) {
215 gpu::Batch *geom = DRW_cache_mesh_surface_get(ob_ref.object);
216 paint_region_face_ps_->draw(geom, manager.unique_handle(ob_ref));
217 }
218 if (use_vert_selection && !in_texture_paint_mode) {
219 gpu::Batch *geom = DRW_cache_mesh_all_verts_get(ob_ref.object);
220 paint_region_vert_ps_->draw(geom, manager.unique_handle(ob_ref));
221 }
222 }
223 }
224
225 void draw(GPUFrameBuffer *framebuffer, Manager &manager, View &view)
226 {
227 if (!enabled_) {
228 return;
229 }
230 GPU_framebuffer_bind(framebuffer);
231 manager.submit(weight_ps_, view);
232 manager.submit(paint_mask_ps_, view);
233 /* TODO(fclem): Draw this onto the line frame-buffer to get wide-line and anti-aliasing.
234 * Just need to make sure the shaders output line data. */
235 manager.submit(paint_region_ps_, view);
236 }
237};
238
239} // namespace blender::draw::overlay
@ CTX_MODE_PAINT_TEXTURE
@ CTX_MODE_PAINT_VERTEX
@ CTX_MODE_PAINT_WEIGHT
GPUTexture * BKE_image_get_gpu_texture(Image *image, ImageUser *iuser)
Definition image_gpu.cc:476
bool BKE_scene_uses_blender_workbench(const Scene *scene)
Definition scene.cc:2777
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define ELEM(...)
Object * DEG_get_original_object(Object *object)
@ 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
@ SPACE_VIEW3D
@ V3D_OVERLAY_PAINT_WIRE
@ V3D_OVERLAY_WPAINT_CONTOURS
void GPU_framebuffer_bind(GPUFrameBuffer *framebuffer)
ResourceHandle unique_handle(const ObjectRef &ref)
void submit(PassSimple &pass, View &view)
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 state_set(DRWState state, int clip_plane_count=0)
Definition draw_pass.hh:954
void push_constant(const char *name, const float &data)
void begin_sync(Resources &res, const State &state)
void draw(GPUFrameBuffer *framebuffer, Manager &manager, View &view)
void object_sync(Manager &manager, const ObjectRef &ob_ref, const State &state)
blender::gpu::Batch * DRW_cache_mesh_surface_weights_get(Object *ob)
blender::gpu::Batch * DRW_cache_mesh_surface_edges_get(Object *ob)
blender::gpu::Batch * DRW_cache_mesh_surface_get(Object *ob)
blender::gpu::Batch * DRW_cache_mesh_all_verts_get(Object *ob)
blender::gpu::Batch * DRW_cache_mesh_surface_texpaint_single_get(Object *ob)
@ 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]
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< float, 4 > float4
VecBase< float, 3 > float3
struct Image * stencil
char alpha_mode
char editflag