Blender V5.0
overlay_particle.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
8
9#pragma once
10
11#include "BKE_material.hh"
12#include "BKE_pointcache.h"
14#include "DNA_material_types.h"
15#include "DNA_particle_types.h"
16#include "ED_particle.hh"
17
18#include "draw_cache.hh"
19#include "draw_cache_impl.hh"
20#include "overlay_base.hh"
21
22namespace blender::draw::overlay {
23
29 private:
30 PassMain particle_ps_ = {"particle_ps_"};
31 PassMain::Sub *dot_ps_ = nullptr;
32 PassMain::Sub *shape_ps_ = nullptr;
33 PassMain::Sub *hair_ps_ = nullptr;
34
35 PassSimple edit_particle_ps_ = {"edit_particle_ps_"};
36 PassSimple::Sub *edit_vert_ps_ = nullptr;
37 PassSimple::Sub *edit_edge_ps_ = nullptr;
38
39 bool show_weight_ = false;
40 bool show_point_inner_ = false;
41 bool show_point_tip_ = false;
42
43 public:
44 void begin_sync(Resources &res, const State &state) final
45 {
46 enabled_ = state.is_space_v3d() && !state.skip_particles;
47
48 if (!enabled_) {
49 return;
50 }
51
52 const bool is_transform = (G.moving & G_TRANSFORM_OBJ) != 0;
53
54 const ParticleEditSettings *edit_settings = PE_settings(const_cast<Scene *>(state.scene));
55 if (edit_settings) {
56 show_weight_ = (edit_settings->brushtype == PE_BRUSH_WEIGHT);
57 show_point_inner_ = edit_settings->selectmode == SCE_SELECT_POINT;
58 show_point_tip_ = ELEM(edit_settings->selectmode, SCE_SELECT_POINT, SCE_SELECT_END);
59 }
60
61 {
62 auto &pass = particle_ps_;
63 pass.init();
64 pass.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
65 pass.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
67 state.clipping_plane_count);
68 res.select_bind(pass);
69 {
70 auto &sub = pass.sub("Dots");
71 sub.shader_set(res.shaders->particle_dot.get());
72 sub.bind_texture("weight_tx", res.weight_ramp_tx);
73 dot_ps_ = &sub;
74 }
75 {
76 auto &sub = pass.sub("Shapes");
77 sub.shader_set(res.shaders->particle_shape.get());
78 sub.bind_texture("weight_tx", res.weight_ramp_tx);
79 shape_ps_ = &sub;
80 }
81 {
82 auto &sub = pass.sub("Hair");
83 sub.shader_set(res.shaders->particle_hair.get());
84 sub.push_constant("color_type", state.v3d->shading.wire_color_type);
85 sub.push_constant("is_transform", is_transform);
86 hair_ps_ = &sub;
87 }
88 }
89
90 {
91 auto &pass = edit_particle_ps_;
92 pass.init();
93 pass.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
94 pass.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
96 state.clipping_plane_count);
97 res.select_bind(pass);
98 {
99 auto &sub = pass.sub("Dots");
100 sub.shader_set(res.shaders->particle_edit_vert.get());
101 sub.bind_texture("weight_tx", res.weight_ramp_tx);
102 sub.push_constant("use_weight", false);
103 sub.push_constant("use_grease_pencil", false);
104 edit_vert_ps_ = &sub;
105 }
106 {
107 auto &sub = pass.sub("Edges");
108 sub.shader_set(res.shaders->particle_edit_edge.get());
109 sub.bind_texture("weight_tx", res.weight_ramp_tx);
110 sub.push_constant("use_weight", show_weight_);
111 sub.push_constant("use_grease_pencil", false);
112 edit_edge_ps_ = &sub;
113 }
114 }
115 }
116
118 const ObjectRef &ob_ref,
119 Resources & /*res*/,
120 const State &state) final
121 {
122 if (!enabled_) {
123 return;
124 }
125
126 /* Usually the edit structure is created by Particle Edit Mode Toggle
127 * operator, but sometimes it's invoked after tagging hair as outdated
128 * (for example, when toggling edit mode). That makes it impossible to
129 * create edit structure for until after next dependency graph evaluation.
130 *
131 * Ideally, the edit structure will be created here already via some
132 * dependency graph callback or so, but currently trying to make it nicer
133 * only causes bad level calls and breaks design from the past.
134 */
135 Object *object_eval = ob_ref.object;
136 Object *object_orig = DEG_get_original(object_eval);
137 Scene *scene_orig = const_cast<Scene *>(DEG_get_original(state.scene));
138 PTCacheEdit *edit = PE_create_current(state.depsgraph, scene_orig, object_orig);
139 if (edit == nullptr) {
140 /* Happens when trying to edit particles in EMITTER mode without having them cached. */
141 return;
142 }
143
144 auto find_active_evaluated_psys =
145 [&](ListBaseWrapper<ParticleSystem> particle_systems_orig,
146 ListBaseWrapper<ParticleSystem> particle_systems_eval) -> ParticleSystem * {
147 int psys_index = 0;
148 for (ParticleSystem *psys_orig : particle_systems_orig) {
149 if (PE_get_current_from_psys(psys_orig) == edit) {
150 return particle_systems_eval.get(psys_index);
151 }
152 psys_index++;
153 }
154 return nullptr;
155 };
156
157 ParticleSystem *psys = find_active_evaluated_psys(&object_orig->particlesystem,
158 &object_eval->particlesystem);
159 if (psys == nullptr) {
160 printf("Error getting evaluated particle system for edit.\n");
161 return;
162 }
163
164 Object *ob = ob_ref.object;
165
166 ResourceHandleRange handle = manager.resource_handle_for_psys(ob_ref,
167 ob_ref.particles_matrix());
168
169 {
170 gpu::Batch *geom = DRW_cache_particles_get_edit_strands(ob, psys, edit, show_weight_);
171 edit_edge_ps_->draw(geom, handle);
172 }
173 if (show_point_inner_) {
174 gpu::Batch *geom = DRW_cache_particles_get_edit_inner_points(ob, psys, edit);
175 edit_vert_ps_->draw(geom, handle);
176 }
177 if (show_point_tip_) {
178 gpu::Batch *geom = DRW_cache_particles_get_edit_tip_points(ob, psys, edit);
179 edit_vert_ps_->draw(geom, handle);
180 }
181 }
182
183 void object_sync(Manager &manager,
184 const ObjectRef &ob_ref,
185 Resources &res,
186 const State &state) final
187 {
188 if (!enabled_) {
189 return;
190 }
191
192 Object *ob = ob_ref.object;
193
194 ResourceHandleRange handle = {};
195
198 continue;
199 }
200
201 if (!handle.is_valid()) {
202 handle = manager.resource_handle_for_psys(ob_ref, ob_ref.particles_matrix());
203 }
204
205 const ParticleSettings *part = psys->part;
206
207 auto set_color = [&](PassMain::Sub &sub) {
208 /* NOTE(fclem): Is color even useful in our modern context? */
210 sub.push_constant("ucolor", float4(ma ? float3(&ma->r) : float3(0.6f), part->draw_size));
211 };
212
213 blender::gpu::Batch *geom = nullptr;
214 const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
215 switch (draw_as) {
216 case PART_DRAW_PATH:
217 if ((state.is_wireframe_mode == false) && (part->draw_as == PART_DRAW_REND)) {
218 /* Render engine should have rendered it already. */
219 break;
220 }
221 geom = DRW_cache_particles_get_hair(ob, psys, nullptr);
222 hair_ps_->push_constant("use_coloring", true); /* TODO */
223 hair_ps_->draw(geom, handle, res.select_id(ob_ref).get());
224 break;
225 case PART_DRAW_NOT:
226 /* Nothing to draw. */
227 break;
228 case PART_DRAW_OB:
229 case PART_DRAW_GR:
230 /* Instances are realized by Depsgraph and rendered as a regular object instance. */
231 break;
232 default:
233 /* Eventually, would be good to assert. But there are many other draw type that could be
234 * set and they need to revert to PART_DRAW_DOT. */
235 // BLI_assert_unreachable();
236 case PART_DRAW_DOT:
237 geom = DRW_cache_particles_get_dots(ob, psys);
238 set_color(*dot_ps_);
239 dot_ps_->draw(geom, handle, res.select_id(ob_ref).get());
240 break;
241 case PART_DRAW_AXIS:
242 geom = DRW_cache_particles_get_dots(ob, psys);
243 set_color(*shape_ps_);
244 shape_ps_->push_constant("shape_type", int(PART_SHAPE_AXIS));
245 shape_ps_->draw_expand(geom, GPU_PRIM_LINES, 3, 1, handle, res.select_id(ob_ref).get());
246 break;
247 case PART_DRAW_CIRC:
248 geom = DRW_cache_particles_get_dots(ob, psys);
249 set_color(*shape_ps_);
250 shape_ps_->push_constant("shape_type", int(PART_SHAPE_CIRCLE));
251 shape_ps_->draw_expand(geom,
254 1,
255 handle,
256 res.select_id(ob_ref).get());
257 break;
258 case PART_DRAW_CROSS:
259 geom = DRW_cache_particles_get_dots(ob, psys);
260 set_color(*shape_ps_);
261 shape_ps_->push_constant("shape_type", int(PART_SHAPE_CROSS));
262 shape_ps_->draw_expand(geom, GPU_PRIM_LINES, 3, 1, handle, res.select_id(ob_ref).get());
263 break;
264 }
265 }
266 }
267
268 void pre_draw(Manager &manager, View &view) final
269 {
270 if (!enabled_) {
271 return;
272 }
273
274 manager.generate_commands(particle_ps_, view);
275 }
276
277 void draw_line(Framebuffer &framebuffer, Manager &manager, View &view) final
278 {
279 if (!enabled_) {
280 return;
281 }
282
283 GPU_framebuffer_bind(framebuffer);
284 manager.submit_only(particle_ps_, view);
285 }
286
287 void draw(Framebuffer &framebuffer, Manager &manager, View &view) final
288 {
289 if (!enabled_) {
290 return;
291 }
292
293 GPU_framebuffer_bind(framebuffer);
294 manager.submit(edit_particle_ps_, view);
295 }
296};
297} // namespace blender::draw::overlay
@ G_TRANSFORM_OBJ
General operations, lookup, etc. for materials.
Material * BKE_object_material_get_eval(Object *ob, short act)
#define ELEM(...)
T * DEG_get_original(T *id)
@ PART_DRAW_CIRC
@ PART_DRAW_CROSS
@ PART_DRAW_AXIS
@ PART_DRAW_PATH
@ PART_DRAW_NOT
@ PART_DRAW_GR
@ PART_DRAW_OB
@ PART_DRAW_REND
@ PART_DRAW_DOT
@ SCE_SELECT_POINT
@ SCE_SELECT_END
@ PE_BRUSH_WEIGHT
PTCacheEdit * PE_get_current_from_psys(ParticleSystem *psys)
ParticleEditSettings * PE_settings(Scene *scene)
PTCacheEdit * PE_create_current(Depsgraph *depsgraph, Scene *scene, Object *ob)
static AppView * view
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
@ GPU_PRIM_LINES
detail::PassBase< command::DrawMultiBuf > Sub
Definition draw_pass.hh:499
void object_sync(Manager &manager, const ObjectRef &ob_ref, Resources &res, const State &state) final
void edit_object_sync(Manager &manager, const ObjectRef &ob_ref, Resources &, const State &state) final
void draw(Framebuffer &framebuffer, Manager &manager, View &view) final
void begin_sync(Resources &res, const State &state) final
void pre_draw(Manager &manager, View &view) final
void draw_line(Framebuffer &framebuffer, Manager &manager, View &view) final
bool DRW_object_is_visible_psys_in_active_context(const Object *object, const ParticleSystem *psys)
#define DRW_CLIPPING_UBO_SLOT
#define OVERLAY_GLOBALS_SLOT
@ 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
#define printf(...)
static ulong state[N]
#define G(x, y, z)
gpu::Batch * DRW_cache_particles_get_edit_inner_points(Object *object, ParticleSystem *psys, PTCacheEdit *edit)
detail::Pass< command::DrawCommandBuf > PassSimple
detail::Pass< command::DrawMultiBuf > PassMain
gpu::Batch * DRW_cache_particles_get_dots(Object *object, ParticleSystem *psys)
gpu::Batch * DRW_cache_particles_get_edit_tip_points(Object *object, ParticleSystem *psys, PTCacheEdit *edit)
gpu::Batch * DRW_cache_particles_get_hair(Object *object, ParticleSystem *psys, ModifierData *md)
gpu::Batch * DRW_cache_particles_get_edit_strands(Object *object, ParticleSystem *psys, PTCacheEdit *edit, bool use_weight)
VecBase< float, 4 > float4
ListBaseWrapperTemplate< ListBase, T > ListBaseWrapper
VecBase< float, 3 > float3
@ PART_SHAPE_CIRCLE
#define PARTICLE_SHAPE_CIRCLE_RESOLUTION
ListBase particlesystem