Blender V4.3
overlay_next_grease_pencil.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
11#include "BLI_bounds.hh"
12#include "BLI_math_matrix.hh"
13
14#include "BKE_curves.hh"
15#include "BKE_grease_pencil.hh"
16#include "BKE_object.hh"
17
18#include "ED_grease_pencil.hh"
19
21
22namespace blender::draw::overlay {
23
25 private:
26 PassSimple edit_grease_pencil_ps_ = {"GPencil Edit"};
27 PassSimple::Sub *edit_points_ = nullptr;
28 PassSimple::Sub *edit_lines_ = nullptr;
29
30 PassSimple grid_ps_ = {"GPencil Grid"};
31
32 bool show_points_ = false;
33 bool show_lines_ = false;
34 bool show_grid_ = false;
35 bool show_weight_ = false;
36
37 /* TODO(fclem): This is quite wasteful and expensive, prefer in shader Z modification like the
38 * retopology offset. */
39 View view_edit_cage = {"view_edit_cage"};
40 float view_dist = 0.0f;
41
42 bool enabled_ = false;
43
44 public:
45 void begin_sync(Resources &res, const State &state, const View &view)
46 {
47 enabled_ = state.space_type == SPACE_VIEW3D;
48
49 if (!enabled_) {
50 return;
51 }
52
53 view_dist = state.view_dist_get(view.winmat());
54
55 const View3D *v3d = state.v3d;
56 const ToolSettings *ts = state.scene->toolsettings;
57
58 const bke::AttrDomain selection_domain_edit = ED_grease_pencil_edit_selection_domain_get(ts);
59 const bool show_edit_point = selection_domain_edit == bke::AttrDomain::Point;
60 const bool show_lines = (v3d->gp_flag & V3D_GP_SHOW_EDIT_LINES);
61 const bool show_direction = (v3d->gp_flag & V3D_GP_SHOW_STROKE_DIRECTION);
62
63 show_points_ = show_lines_ = show_weight_ = false;
64
65 switch (state.object_mode) {
67 /* Draw mode. */
68 break;
70 /* Vertex paint mode. */
71 break;
72 case OB_MODE_EDIT:
73 /* Edit mode. */
74 show_points_ = show_edit_point;
75 show_lines_ = show_lines;
76 break;
78 /* Weight paint mode. */
79 show_points_ = true;
80 show_lines_ = show_lines;
81 show_weight_ = true;
82 break;
84 /* Sculpt mode. */
85 show_points_ = (selection_domain_edit == bke::AttrDomain::Point);
86 show_lines_ = show_lines && (ts->gpencil_selectmode_sculpt != 0);
87 break;
88 default:
89 /* Not a Grease Pencil mode. */
90 break;
91 }
92
93 edit_points_ = nullptr;
94 edit_lines_ = nullptr;
95
96 {
97 auto &pass = edit_grease_pencil_ps_;
98 pass.init();
101 state.clipping_plane_count);
102
103 if (show_points_) {
104 auto &sub = pass.sub("Points");
105 sub.shader_set(res.shaders.curve_edit_points.get());
106 sub.bind_ubo("globalsBlock", &res.globals_buf);
107 sub.bind_texture("weightTex", &res.weight_ramp_tx);
108 sub.push_constant("useWeight", show_weight_);
109 sub.push_constant("useGreasePencil", true);
110 sub.push_constant("doStrokeEndpoints", show_direction);
111 edit_points_ = ⊂
112 }
113
114 if (show_lines_) {
115 auto &sub = pass.sub("Lines");
116 sub.shader_set(res.shaders.curve_edit_line.get());
117 sub.bind_ubo("globalsBlock", &res.globals_buf);
118 sub.bind_texture("weightTex", &res.weight_ramp_tx);
119 sub.push_constant("useWeight", show_weight_);
120 sub.push_constant("useGreasePencil", true);
121 edit_lines_ = ⊂
122 }
123 }
124
125 const bool is_depth_projection_mode = ts->gpencil_v3d_align &
127 show_grid_ = (v3d->gp_flag & V3D_GP_SHOW_GRID) && !is_depth_projection_mode;
128
129 {
130 const bool grid_xray = (v3d->gp_flag & V3D_GP_SHOW_GRID_XRAY);
131 DRWState depth_write_state = (grid_xray) ? DRW_STATE_DEPTH_ALWAYS :
133 auto &pass = grid_ps_;
134 pass.init();
135 pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA | depth_write_state,
136 state.clipping_plane_count);
137 if (show_grid_) {
138 const float4 col_grid(0.5f, 0.5f, 0.5f, state.overlay.gpencil_grid_opacity);
139 pass.shader_set(res.shaders.grid_grease_pencil.get());
140 pass.bind_ubo("globalsBlock", &res.globals_buf);
141 pass.push_constant("color", col_grid);
142 }
143 }
144 }
145
147 const ObjectRef &ob_ref,
148 const State &state,
149 Resources & /*res*/)
150 {
151 if (!enabled_) {
152 return;
153 }
154
155 Object *ob = ob_ref.object;
156
157 if (show_points_) {
158 gpu::Batch *geom = show_weight_ ?
161 edit_points_->draw(geom, manager.unique_handle(ob_ref));
162 }
163 if (show_lines_) {
164 gpu::Batch *geom = show_weight_ ? DRW_cache_grease_pencil_weight_lines_get(state.scene, ob) :
166 edit_lines_->draw(geom, manager.unique_handle(ob_ref));
167 }
168 }
169
171 const ObjectRef &ob_ref,
172 const State &state,
173 Resources &res)
174 {
175 /* Reuse same logic as edit mode. */
176 edit_object_sync(manager, ob_ref, state, res);
177 }
178
180 const ObjectRef &ob_ref,
181 const State &state,
182 Resources &res)
183 {
184 /* Reuse same logic as edit mode. */
185 edit_object_sync(manager, ob_ref, state, res);
186 }
187
188 void object_sync(const ObjectRef &ob_ref, Resources & /*res*/, State &state)
189 {
190 if (!enabled_) {
191 return;
192 }
193
194 if (ob_ref.object != state.active_base->object) {
195 /* Only display for the active object. */
196 return;
197 }
198
199 if (show_grid_) {
200 const int grid_lines = 4;
201 const int line_count = grid_lines * 4 + 2;
202 const float4x4 grid_mat = grid_matrix_get(*ob_ref.object, state.scene);
203
204 grid_ps_.push_constant("xAxis", grid_mat.x_axis());
205 grid_ps_.push_constant("yAxis", grid_mat.y_axis());
206 grid_ps_.push_constant("origin", grid_mat.location());
207 grid_ps_.push_constant("halfLineCount", line_count / 2);
208 grid_ps_.draw_procedural(GPU_PRIM_LINES, 1, line_count * 2);
209 }
210 }
211
212 void draw(Framebuffer &framebuffer, Manager &manager, View &view)
213 {
214 if (!enabled_) {
215 return;
216 }
217
218 GPU_framebuffer_bind(framebuffer);
219 manager.submit(grid_ps_, view);
220 }
221
222 void draw_color_only(Framebuffer &framebuffer, Manager &manager, View &view)
223 {
224 if (!enabled_) {
225 return;
226 }
227
228 view_edit_cage.sync(view.viewmat(), winmat_polygon_offset(view.winmat(), view_dist, 0.5f));
229
230 GPU_framebuffer_bind(framebuffer);
231 manager.submit(edit_grease_pencil_ps_, view_edit_cage);
232 }
233
236 union {
237 /* Z axis if ortho or position if perspective. */
240 };
241
242 ViewParameters() = default;
243
244 ViewParameters(bool is_perspective, const float4x4 &viewinv)
245 {
246 if (is_perspective) {
247 location = viewinv.location();
248 }
249 else {
250 z_axis = viewinv.z_axis();
251 }
252 }
253 };
254
256 const ViewParameters &view,
257 const Scene *scene,
258 Object *ob,
259 ResourceHandle res_handle,
261 {
262 using namespace blender;
263 using namespace blender::ed::greasepencil;
264 ::GreasePencil &grease_pencil = *static_cast<::GreasePencil *>(ob->data);
265
266 float4 plane = (grease_pencil.flag & GREASE_PENCIL_STROKE_ORDER_3D) ?
267 float4(0.0f) :
268 depth_plane_get(ob, view);
269 pass.push_constant("gpDepthPlane", plane);
270
271 int t_offset = 0;
272 const Vector<DrawingInfo> drawings = retrieve_visible_drawings(*scene, grease_pencil, true);
273 for (const DrawingInfo info : drawings) {
274 const bool is_stroke_order_3d = (grease_pencil.flag & GREASE_PENCIL_STROKE_ORDER_3D) != 0;
275
276 const float object_scale = mat4_to_scale(ob->object_to_world().ptr());
277 const float thickness_scale = bke::greasepencil::LEGACY_RADIUS_CONVERSION_FACTOR;
278
281
282 pass.push_constant("gpStrokeOrder3d", is_stroke_order_3d);
283 pass.push_constant("gpThicknessScale", object_scale);
284 pass.push_constant("gpThicknessOffset", 0.0f);
285 pass.push_constant("gpThicknessWorldScale", thickness_scale);
286 pass.bind_texture("gp_pos_tx", position_tx);
287 pass.bind_texture("gp_col_tx", color_tx);
288
289 const bke::CurvesGeometry &curves = info.drawing.strokes();
290 const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve();
291 const bke::AttributeAccessor attributes = curves.attributes();
292 const VArray<int> stroke_materials = *attributes.lookup_or_default<int>(
293 "material_index", bke::AttrDomain::Curve, 0);
294 const VArray<bool> cyclic = *attributes.lookup_or_default<bool>(
295 "cyclic", bke::AttrDomain::Curve, false);
296
297 IndexMaskMemory memory;
299 *ob, info.drawing, memory);
300
301 visible_strokes.foreach_index([&](const int stroke_i) {
302 const IndexRange points = points_by_curve[stroke_i];
303 const int material_index = stroke_materials[stroke_i];
304 MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, material_index + 1);
305
306 const bool hide_onion = info.onion_id != 0;
307 const bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
308
309 const int num_stroke_triangles = (points.size() >= 3) ? (points.size() - 2) : 0;
310 const int num_stroke_vertices = (points.size() +
311 int(cyclic[stroke_i] && (points.size() >= 3)));
312
313 if (hide_material || hide_onion) {
314 t_offset += num_stroke_triangles;
315 t_offset += num_stroke_vertices * 2;
316 return;
317 }
318
319 blender::gpu::Batch *geom = draw::DRW_cache_grease_pencil_get(scene, ob);
320
321 const bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0;
322 const bool show_fill = (points.size() >= 3) &&
323 (gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
324
325 if (show_fill) {
326 int v_first = t_offset * 3;
327 int v_count = num_stroke_triangles * 3;
328 pass.draw(geom, 1, v_count, v_first, res_handle, select_id.get());
329 }
330
331 t_offset += num_stroke_triangles;
332
333 if (show_stroke) {
334 int v_first = t_offset * 3;
335 int v_count = num_stroke_vertices * 2 * 3;
336 pass.draw(geom, 1, v_count, v_first, res_handle, select_id.get());
337 }
338 t_offset += num_stroke_vertices * 2;
339 });
340 }
341 }
342
343 private:
344 /* Returns the normal plane in NDC space. */
345 static float4 depth_plane_get(const Object *ob, const ViewParameters &view)
346 {
347 using namespace blender::math;
348
349 /* Find the normal most likely to represent the grease pencil object. */
350 /* TODO: This does not work quite well if you use
351 * strokes not aligned with the object axes. Maybe we could try to
352 * compute the minimum axis of all strokes. But this would be more
353 * computationally heavy and should go into the GPData evaluation. */
354 const std::optional<blender::Bounds<float3>> bounds = BKE_object_boundbox_get(ob).value_or(
356
357 float3 center = midpoint(bounds->min, bounds->max);
358 float3 size = (bounds->max - bounds->min) * 0.5f;
359 /* Avoid division by 0.0 later. */
360 size += 1e-8f;
361 /* Convert Bbox unit space to object space. */
362 float4x4 bbox_to_object = from_loc_scale<float4x4>(center, size);
363 float4x4 bbox_to_world = ob->object_to_world() * bbox_to_object;
364
365 float3 bbox_center = bbox_to_world.location();
366 float3 view_vector = (view.is_perspective) ? view.location - bbox_center : view.z_axis;
367
368 float3x3 world_to_bbox = invert(float3x3(bbox_to_world));
369
370 /* Normalize the vector in BBox space. */
371 float3 local_plane_direction = normalize(transform_direction(world_to_bbox, view_vector));
372 /* `bbox_to_world_normal` is a "normal" matrix. It transforms BBox space normals to world. */
373 float3x3 bbox_to_world_normal = transpose(world_to_bbox);
374 float3 plane_direction = normalize(
375 transform_direction(bbox_to_world_normal, local_plane_direction));
376
377 return float4(plane_direction, -dot(plane_direction, bbox_center));
378 }
379
380 float4x4 grid_matrix_get(const Object &object, const Scene *scene)
381 {
382 const ToolSettings *ts = scene->toolsettings;
383
384 const ::GreasePencil &grease_pencil = *static_cast<::GreasePencil *>(object.data);
385 const blender::bke::greasepencil::Layer &layer = *grease_pencil.get_active_layer();
386
387 float4x4 mat = object.object_to_world();
389 mat = layer.to_world_space(object);
390 }
391 const View3DCursor *cursor = &scene->cursor;
392
393 /* Set the grid in the selected axis */
394 switch (ts->gp_sculpt.lock_axis) {
395 case GP_LOCKAXIS_X:
396 std::swap(mat[0], mat[2]);
397 break;
398 case GP_LOCKAXIS_Y:
399 std::swap(mat[1], mat[2]);
400 break;
401 case GP_LOCKAXIS_Z:
402 /* Default. */
403 break;
404 case GP_LOCKAXIS_CURSOR: {
405 mat = float4x4(cursor->matrix<float3x3>());
406 break;
407 }
408 case GP_LOCKAXIS_VIEW:
409 /* view aligned */
410 DRW_view_viewmat_get(nullptr, mat.ptr(), true);
411 break;
412 }
413
414 mat *= 2.0f;
415
417 mat.location() = cursor->location;
418 }
419 else {
420 mat.location() = layer.to_world_space(object).location();
421 }
422 return mat;
423 }
424};
425
426} // namespace blender::draw::overlay
Low-level operations for curves.
Low-level operations for grease pencil.
struct MaterialGPencilStyle * BKE_gpencil_material_settings(struct Object *ob, short act)
General operations, lookup, etc. for blender objects.
std::optional< blender::Bounds< blender::float3 > > BKE_object_boundbox_get(const Object *ob)
float mat4_to_scale(const float mat[4][4])
@ GREASE_PENCIL_STROKE_ORDER_3D
@ GP_MATERIAL_HIDE
@ GP_MATERIAL_STROKE_SHOW
@ GP_MATERIAL_FILL_SHOW
@ OB_MODE_VERTEX_GREASE_PENCIL
@ OB_MODE_EDIT
@ OB_MODE_PAINT_GREASE_PENCIL
@ OB_MODE_SCULPT_GREASE_PENCIL
@ OB_MODE_WEIGHT_GREASE_PENCIL
@ GP_LOCKAXIS_X
@ GP_LOCKAXIS_VIEW
@ GP_LOCKAXIS_Y
@ GP_LOCKAXIS_Z
@ GP_LOCKAXIS_CURSOR
@ GP_PROJECT_DEPTH_VIEW
@ GP_PROJECT_CURSOR
@ GP_PROJECT_DEPTH_STROKE
@ SPACE_VIEW3D
@ V3D_GP_SHOW_GRID_XRAY
@ V3D_GP_SHOW_STROKE_DIRECTION
@ V3D_GP_SHOW_EDIT_LINES
@ V3D_GP_SHOW_GRID
void GPU_framebuffer_bind(GPUFrameBuffer *framebuffer)
@ GPU_PRIM_LINES
btMatrix3x3 transpose() const
Return the transpose of the matrix.
SIMD_FORCE_INLINE btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition btVector3.h:303
ResourceHandle unique_handle(const ObjectRef &ref)
void submit(PassSimple &pass, View &view)
void sync(const float4x4 &view_mat, const float4x4 &win_mat, int view_id=0)
Definition draw_view.cc:20
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
void draw_procedural(GPUPrimType primitive, uint instance_len, uint vertex_len, uint vertex_first=-1, ResourceHandle handle={0}, uint custom_id=0)
Definition draw_pass.hh:833
PassBase< DrawCommandBufType > & sub(const char *name)
Definition draw_pass.hh:616
void push_constant(const char *name, const float &data)
void shader_set(GPUShader *shader)
Definition draw_pass.hh:971
void object_sync(const ObjectRef &ob_ref, Resources &, State &state)
void sculpt_object_sync(Manager &manager, const ObjectRef &ob_ref, const State &state, Resources &res)
static void draw_grease_pencil(PassMain::Sub &pass, const ViewParameters &view, const Scene *scene, Object *ob, ResourceHandle res_handle, select::ID select_id=select::SelectMap::select_invalid_id())
void paint_object_sync(Manager &manager, const ObjectRef &ob_ref, const State &state, Resources &res)
void draw(Framebuffer &framebuffer, Manager &manager, View &view)
void edit_object_sync(Manager &manager, const ObjectRef &ob_ref, const State &state, Resources &)
void draw_color_only(Framebuffer &framebuffer, Manager &manager, View &view)
void begin_sync(Resources &res, const State &state, const View &view)
void foreach_index(Fn &&fn) const
void DRW_view_viewmat_get(const DRWView *view, float mat[4][4], bool inverse)
DRWState
Definition draw_state.hh:25
@ DRW_STATE_BLEND_ALPHA
Definition draw_state.hh:55
@ 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_DEPTH_ALWAYS
Definition draw_state.hh:36
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
blender::bke::AttrDomain ED_grease_pencil_edit_selection_domain_get(const ToolSettings *tool_settings)
static ulong state[N]
constexpr float LEGACY_RADIUS_CONVERSION_FACTOR
static float4x4 winmat_polygon_offset(float4x4 winmat, float view_dist, float offset)
blender::gpu::Batch * DRW_cache_grease_pencil_get(const Scene *scene, Object *ob)
gpu::VertBuf * DRW_cache_grease_pencil_position_buffer_get(const Scene *scene, Object *ob)
blender::gpu::Batch * DRW_cache_grease_pencil_edit_lines_get(const Scene *scene, Object *ob)
blender::gpu::Batch * DRW_cache_grease_pencil_weight_lines_get(const Scene *scene, Object *ob)
blender::gpu::Batch * DRW_cache_grease_pencil_edit_points_get(const Scene *scene, Object *ob)
blender::gpu::Batch * DRW_cache_grease_pencil_weight_points_get(const Scene *scene, Object *ob)
gpu::VertBuf * DRW_cache_grease_pencil_color_buffer_get(const Scene *scene, Object *ob)
IndexMask retrieve_visible_strokes(Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory)
MatT from_loc_scale(const typename MatT::loc_type &location, const VecBase< typename MatT::base_type, ScaleDim > &scale)
CartesianBasis invert(const CartesianBasis &basis)
T midpoint(const T &a, const T &b)
VecBase< T, 3 > transform_direction(const MatBase< T, 3, 3 > &mat, const VecBase< T, 3 > &direction)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
char gpencil_selectmode_sculpt
struct GP_Sculpt_Settings gp_sculpt
const c_style_mat & ptr() const
ViewParameters(bool is_perspective, const float4x4 &viewinv)