Blender V4.3
overlay_next_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
9#pragma once
10
12#include "DNA_particle_types.h"
13
14#include "BKE_pointcache.h"
15
16#include "ED_particle.hh"
17
19
20namespace blender::draw::overlay {
21
22class Particles {
23 private:
24 PassMain particle_ps_ = {"particle_ps_"};
25 PassMain::Sub *dot_ps_ = nullptr;
26 PassMain::Sub *shape_ps_ = nullptr;
27 PassMain::Sub *hair_ps_ = nullptr;
28
29 PassSimple edit_particle_ps_ = {"edit_particle_ps_"};
30 PassSimple::Sub *edit_vert_ps_ = nullptr;
31 PassSimple::Sub *edit_edge_ps_ = nullptr;
32
33 bool show_weight_ = false;
34 bool show_point_inner_ = false;
35 bool show_point_tip_ = false;
36
37 bool enabled_ = false;
38
39 public:
40 void begin_sync(Resources &res, const State &state)
41 {
42 enabled_ = state.space_type == SPACE_VIEW3D;
43
44 if (!enabled_) {
45 return;
46 }
47
48 const bool is_transform = (G.moving & G_TRANSFORM_OBJ) != 0;
49
50 const ParticleEditSettings *edit_settings = PE_settings(const_cast<Scene *>(state.scene));
51 if (edit_settings) {
52 show_weight_ = (edit_settings->brushtype == PE_BRUSH_WEIGHT);
53 show_point_inner_ = edit_settings->selectmode == SCE_SELECT_POINT;
54 show_point_tip_ = ELEM(edit_settings->selectmode, SCE_SELECT_POINT, SCE_SELECT_END);
55 }
56
57 {
58 auto &pass = particle_ps_;
59 pass.init();
61 state.clipping_plane_count);
62 res.select_bind(pass);
63 {
64 auto &sub = pass.sub("Dots");
65 sub.shader_set(res.shaders.particle_dot.get());
66 sub.bind_ubo("globalsBlock", &res.globals_buf);
67 sub.bind_texture("weightTex", res.weight_ramp_tx);
68 dot_ps_ = &sub;
69 }
70 {
71 auto &sub = pass.sub("Shapes");
72 sub.shader_set(res.shaders.particle_shape.get());
73 sub.bind_ubo("globalsBlock", &res.globals_buf);
74 sub.bind_texture("weightTex", res.weight_ramp_tx);
75 shape_ps_ = &sub;
76 }
77 {
78 auto &sub = pass.sub("Hair");
79 sub.shader_set(res.shaders.particle_hair.get());
80 sub.bind_ubo("globalsBlock", &res.globals_buf);
81 sub.push_constant("colorType", state.v3d->shading.wire_color_type);
82 sub.push_constant("isTransform", is_transform);
83 hair_ps_ = &sub;
84 }
85 }
86
87 {
88 auto &pass = edit_particle_ps_;
89 pass.init();
91 state.clipping_plane_count);
92 res.select_bind(pass);
93 {
94 auto &sub = pass.sub("Dots");
95 sub.shader_set(res.shaders.particle_edit_vert.get());
96 sub.bind_ubo("globalsBlock", &res.globals_buf);
97 sub.bind_texture("weightTex", res.weight_ramp_tx);
98 sub.push_constant("useWeight", show_weight_);
99 sub.push_constant("useGreasePencil", false);
100 edit_vert_ps_ = &sub;
101 }
102 {
103 auto &sub = pass.sub("Edges");
105 sub.bind_ubo("globalsBlock", &res.globals_buf);
106 sub.bind_texture("weightTex", res.weight_ramp_tx);
107 sub.push_constant("useWeight", false);
108 sub.push_constant("useGreasePencil", false);
109 edit_edge_ps_ = &sub;
110 }
111 }
112 }
113
114 /* Particle data are stored in world space. If an object is instanced, the associated particle
115 * systems need to be offset appropriately. */
116 static float4x4 dupli_matrix_get(const ObjectRef &ob_ref)
117 {
118 float4x4 dupli_mat = float4x4::identity();
119
120 if ((ob_ref.dupli_parent != nullptr) && (ob_ref.dupli_object != nullptr)) {
121 if (ob_ref.dupli_object->type & OB_DUPLICOLLECTION) {
122 Collection *collection = ob_ref.dupli_parent->instance_collection;
123 if (collection != nullptr) {
124 dupli_mat[3] -= float4(float3(collection->instance_offset), 0.0f);
125 }
126 dupli_mat = ob_ref.dupli_parent->object_to_world() * dupli_mat;
127 }
128 else {
129 dupli_mat = ob_ref.object->object_to_world() *
130 math::invert(ob_ref.dupli_object->ob->object_to_world());
131 }
132 }
133 return dupli_mat;
134 }
135
137 const ObjectRef &ob_ref,
138 Resources & /*res*/,
139 const State &state)
140 {
141 if (!enabled_) {
142 return;
143 }
144
145 /* Usually the edit structure is created by Particle Edit Mode Toggle
146 * operator, but sometimes it's invoked after tagging hair as outdated
147 * (for example, when toggling edit mode). That makes it impossible to
148 * create edit structure for until after next dependency graph evaluation.
149 *
150 * Ideally, the edit structure will be created here already via some
151 * dependency graph callback or so, but currently trying to make it nicer
152 * only causes bad level calls and breaks design from the past.
153 */
154 Object *object_eval = ob_ref.object;
155 Object *object_orig = DEG_get_original_object(object_eval);
156 Scene *scene_orig = (Scene *)DEG_get_original_id(const_cast<ID *>(&state.scene->id));
157 PTCacheEdit *edit = PE_create_current(state.depsgraph, scene_orig, object_orig);
158 if (edit == nullptr) {
159 /* Happens when trying to edit particles in EMITTER mode without having them cached. */
160 return;
161 }
162
163 auto find_active_evaluated_psys =
164 [&](ListBaseWrapper<ParticleSystem> particle_systems_orig,
165 ListBaseWrapper<ParticleSystem> particle_systems_eval) -> ParticleSystem * {
166 int psys_index = 0;
167 for (ParticleSystem *psys_orig : particle_systems_orig) {
168 if (PE_get_current_from_psys(psys_orig) == edit) {
169 return particle_systems_eval.get(psys_index);
170 }
171 psys_index++;
172 }
173 return nullptr;
174 };
175
176 ParticleSystem *psys = find_active_evaluated_psys(&object_orig->particlesystem,
177 &object_eval->particlesystem);
178 if (psys == nullptr) {
179 printf("Error getting evaluated particle system for edit.\n");
180 return;
181 }
182
183 Object *ob = ob_ref.object;
184
185 ResourceHandle handle = manager.resource_handle_for_psys(ob_ref, dupli_matrix_get(ob_ref));
186
187 {
188 gpu::Batch *geom = DRW_cache_particles_get_edit_strands(ob, psys, edit, show_weight_);
189 edit_edge_ps_->draw(geom, handle);
190 }
191 if (show_point_inner_) {
192 gpu::Batch *geom = DRW_cache_particles_get_edit_inner_points(ob, psys, edit);
193 edit_vert_ps_->draw(geom, handle);
194 }
195 if (show_point_tip_) {
196 gpu::Batch *geom = DRW_cache_particles_get_edit_tip_points(ob, psys, edit);
197 edit_vert_ps_->draw(geom, handle);
198 }
199 }
200
201 void object_sync(Manager &manager, const ObjectRef &ob_ref, Resources &res, const State &state)
202 {
203 if (!enabled_) {
204 return;
205 }
206
207 Object *ob = ob_ref.object;
208
209 ResourceHandle handle = {0};
210
213 continue;
214 }
215
216 if (handle.raw == 0u) {
217 handle = manager.resource_handle_for_psys(ob_ref, dupli_matrix_get(ob_ref));
218 }
219
220 const ParticleSettings *part = psys->part;
221
222 auto set_color = [&](PassMain::Sub &sub) {
223 /* NOTE(fclem): Is color even useful in our modern context? */
224 Material *ma = BKE_object_material_get_eval(ob, part->omat);
225 sub.push_constant("ucolor", float4(ma ? float3(&ma->r) : float3(0.6f), part->draw_size));
226 };
227
228 blender::gpu::Batch *geom = nullptr;
229 const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
230 switch (draw_as) {
231 case PART_DRAW_PATH:
232 if ((state.is_wireframe_mode == false) && (part->draw_as == PART_DRAW_REND)) {
233 /* Render engine should have rendered it already. */
234 break;
235 }
236 geom = DRW_cache_particles_get_hair(ob, psys, nullptr);
237 hair_ps_->push_constant("useColoring", true); /* TODO */
238 hair_ps_->draw(geom, handle, res.select_id(ob_ref).get());
239 break;
240 case PART_DRAW_NOT:
241 /* Nothing to draw. */
242 break;
243 case PART_DRAW_OB:
244 case PART_DRAW_GR:
245 /* Instances are realized by Depsgraph and rendered as a regular object instance. */
246 break;
247 default:
248 /* Eventually, would be good to assert. But there are many other draw type that could be
249 * set and they need to revert to PART_DRAW_DOT. */
250 // BLI_assert_unreachable();
251 case PART_DRAW_DOT:
252 geom = DRW_cache_particles_get_dots(ob, psys);
253 set_color(*dot_ps_);
254 dot_ps_->draw(geom, handle, res.select_id(ob_ref).get());
255 break;
256 case PART_DRAW_AXIS:
257 geom = DRW_cache_particles_get_dots(ob, psys);
258 set_color(*shape_ps_);
259 shape_ps_->push_constant("shape_type", PART_SHAPE_AXIS);
260 shape_ps_->draw_expand(geom, GPU_PRIM_LINES, 3, 1, handle, res.select_id(ob_ref).get());
261 break;
262 case PART_DRAW_CIRC:
263 geom = DRW_cache_particles_get_dots(ob, psys);
264 set_color(*shape_ps_);
265 shape_ps_->push_constant("shape_type", PART_SHAPE_CIRCLE);
266 shape_ps_->draw_expand(geom,
269 1,
270 handle,
271 res.select_id(ob_ref).get());
272 break;
273 case PART_DRAW_CROSS:
274 geom = DRW_cache_particles_get_dots(ob, psys);
275 set_color(*shape_ps_);
276 shape_ps_->push_constant("shape_type", PART_SHAPE_CROSS);
277 shape_ps_->draw_expand(geom, GPU_PRIM_LINES, 3, 1, handle, res.select_id(ob_ref).get());
278 break;
279 }
280 }
281 }
282
283 void draw(Framebuffer &framebuffer, Manager &manager, View &view)
284 {
285 if (!enabled_) {
286 return;
287 }
288
289 GPU_framebuffer_bind(framebuffer);
290 manager.submit(particle_ps_, view);
291 }
292
293 void draw_no_line(Framebuffer &framebuffer, Manager &manager, View &view)
294 {
295 if (!enabled_) {
296 return;
297 }
298
299 GPU_framebuffer_bind(framebuffer);
300 manager.submit(edit_particle_ps_, view);
301 }
302};
303} // namespace blender::draw::overlay
@ G_TRANSFORM_OBJ
struct Material * BKE_object_material_get_eval(struct Object *ob, short act)
#define ELEM(...)
ID * DEG_get_original_id(ID *id)
Object * DEG_get_original_object(Object *object)
Object groups, one object can be in many groups at once.
@ OB_DUPLICOLLECTION
@ 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
@ SPACE_VIEW3D
PTCacheEdit * PE_get_current_from_psys(ParticleSystem *psys)
ParticleEditSettings * PE_settings(Scene *scene)
PTCacheEdit * PE_create_current(Depsgraph *depsgraph, Scene *scene, Object *ob)
void GPU_framebuffer_bind(GPUFrameBuffer *framebuffer)
@ GPU_PRIM_LINES
static bNodeSocket * set_color(bNode *node, COLLADAFW::Color col)
ResourceHandle resource_handle_for_psys(const ObjectRef &ref, const float4x4 &model_matrix)
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 push_constant(const char *name, const float &data)
void draw_expand(gpu::Batch *batch, GPUPrimType primitive_type, uint primitive_len, uint instance_len, uint vertex_len=-1, uint vertex_first=-1, ResourceHandle handle={0}, uint custom_id=0)
Definition draw_pass.hh:793
void shader_set(GPUShader *shader)
Definition draw_pass.hh:971
void edit_object_sync(Manager &manager, const ObjectRef &ob_ref, Resources &, const State &state)
void draw(Framebuffer &framebuffer, Manager &manager, View &view)
void draw_no_line(Framebuffer &framebuffer, Manager &manager, View &view)
void begin_sync(Resources &res, const State &state)
static float4x4 dupli_matrix_get(const ObjectRef &ob_ref)
void object_sync(Manager &manager, const ObjectRef &ob_ref, Resources &res, const State &state)
#define printf
blender::gpu::Batch * DRW_cache_particles_get_dots(Object *object, ParticleSystem *psys)
blender::gpu::Batch * DRW_cache_particles_get_hair(Object *object, ParticleSystem *psys, ModifierData *md)
blender::gpu::Batch * DRW_cache_particles_get_edit_strands(Object *object, ParticleSystem *psys, PTCacheEdit *edit, bool use_weight)
blender::gpu::Batch * DRW_cache_particles_get_edit_tip_points(Object *object, ParticleSystem *psys, PTCacheEdit *edit)
blender::gpu::Batch * DRW_cache_particles_get_edit_inner_points(Object *object, ParticleSystem *psys, PTCacheEdit *edit)
bool DRW_object_is_visible_psys_in_active_context(const Object *object, const ParticleSystem *psys)
@ 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]
#define G(x, y, z)
CartesianBasis invert(const CartesianBasis &basis)
VecBase< float, 4 > float4
ListBaseWrapperTemplate< ListBase, T > ListBaseWrapper
VecBase< float, 3 > float3
#define PART_SHAPE_AXIS
#define PART_SHAPE_CROSS
#define PART_SHAPE_CIRCLE
#define PARTICLE_SHAPE_CIRCLE_RESOLUTION
Definition DNA_ID.h:413
ListBase particlesystem
struct Collection * instance_collection
DupliObject * dupli_object
void select_bind(PassSimple &pass)
const ID select_id(const ObjectRef &ob_ref, uint sub_object_id=0)