diff --git a/source/blender/draw/engines/overlay/overlay_next_instance.cc b/source/blender/draw/engines/overlay/overlay_next_instance.cc index cc2e8f498d9..6d8a64457dc 100644 --- a/source/blender/draw/engines/overlay/overlay_next_instance.cc +++ b/source/blender/draw/engines/overlay/overlay_next_instance.cc @@ -156,6 +156,7 @@ void Instance::object_sync(ObjectRef &ob_ref, Manager &manager) const bool in_edit_mode = object_is_edit_mode(ob_ref.object); const bool in_paint_mode = object_is_paint_mode(ob_ref.object); const bool in_sculpt_mode = object_is_sculpt_mode(ob_ref); + const bool in_particle_edit_mode = object_is_particle_edit_mode(ob_ref); const bool in_edit_paint_mode = object_is_edit_paint_mode( ob_ref, in_edit_mode, in_paint_mode, in_sculpt_mode); const bool needs_prepass = object_needs_prepass(ob_ref, in_paint_mode); @@ -166,6 +167,10 @@ void Instance::object_sync(ObjectRef &ob_ref, Manager &manager) layer.prepass.object_sync(manager, ob_ref, resources, state); } + if (in_particle_edit_mode) { + layer.particles.edit_object_sync(manager, ob_ref, resources, state); + } + if (in_paint_mode) { switch (ob_ref.object->type) { case OB_MESH: @@ -437,6 +442,7 @@ void Instance::draw(Manager &manager) layer.facing.draw(framebuffer, manager, view); layer.fade.draw(framebuffer, manager, view); layer.paints.draw(framebuffer, manager, view); + layer.particles.draw_no_line(framebuffer, manager, view); }; auto draw_layer = [&](OverlayLayer &layer, Framebuffer &framebuffer) { @@ -537,6 +543,11 @@ bool Instance::object_is_sculpt_mode(const ObjectRef &ob_ref) return false; } +bool Instance::object_is_particle_edit_mode(const ObjectRef &ob_ref) +{ + return (ob_ref.object->mode == OB_MODE_PARTICLE_EDIT) && (state.ctx_mode == CTX_MODE_PARTICLE); +} + bool Instance::object_is_sculpt_mode(const Object *object) { if (object->sculpt && (object->sculpt->mode_type == OB_MODE_SCULPT)) { diff --git a/source/blender/draw/engines/overlay/overlay_next_instance.hh b/source/blender/draw/engines/overlay/overlay_next_instance.hh index fb1b60b4727..ff042fece5e 100644 --- a/source/blender/draw/engines/overlay/overlay_next_instance.hh +++ b/source/blender/draw/engines/overlay/overlay_next_instance.hh @@ -119,6 +119,7 @@ class Instance { bool object_is_selected(const ObjectRef &ob_ref); bool object_is_edit_mode(const Object *object); bool object_is_paint_mode(const Object *object); + bool object_is_particle_edit_mode(const ObjectRef &ob_ref); /* Checks for both curve sculpt and regular sculpt mode. */ bool object_is_sculpt_mode(const ObjectRef &ob_ref); /* Checks only for sculpt mode. */ diff --git a/source/blender/draw/engines/overlay/overlay_next_particle.hh b/source/blender/draw/engines/overlay/overlay_next_particle.hh index c68134bd44a..d47f3234e66 100644 --- a/source/blender/draw/engines/overlay/overlay_next_particle.hh +++ b/source/blender/draw/engines/overlay/overlay_next_particle.hh @@ -13,6 +13,8 @@ #include "BKE_pointcache.h" +#include "ED_particle.hh" + #include "overlay_next_private.hh" namespace blender::draw::overlay { @@ -24,6 +26,14 @@ class Particles { PassMain::Sub *shape_ps_ = nullptr; PassMain::Sub *hair_ps_ = nullptr; + PassSimple edit_particle_ps_ = {"edit_particle_ps_"}; + PassSimple::Sub *edit_vert_ps_ = nullptr; + PassSimple::Sub *edit_edge_ps_ = nullptr; + + bool show_weight_ = false; + bool show_point_inner_ = false; + bool show_point_tip_ = false; + bool enabled_ = false; public: @@ -37,6 +47,13 @@ class Particles { const bool is_transform = (G.moving & G_TRANSFORM_OBJ) != 0; + const ParticleEditSettings *edit_settings = PE_settings(const_cast(state.scene)); + if (edit_settings) { + show_weight_ = (edit_settings->brushtype == PE_BRUSH_WEIGHT); + show_point_inner_ = edit_settings->selectmode == SCE_SELECT_POINT; + show_point_tip_ = ELEM(edit_settings->selectmode, SCE_SELECT_POINT, SCE_SELECT_END); + } + { auto &pass = particle_ps_; pass.init(); @@ -66,6 +83,32 @@ class Particles { hair_ps_ = ⊂ } } + + { + auto &pass = edit_particle_ps_; + pass.init(); + pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL, + state.clipping_plane_count); + res.select_bind(pass); + { + auto &sub = pass.sub("Dots"); + sub.shader_set(res.shaders.particle_edit_vert.get()); + sub.bind_ubo("globalsBlock", &res.globals_buf); + sub.bind_texture("weightTex", res.weight_ramp_tx); + sub.push_constant("useWeight", show_weight_); + sub.push_constant("useGreasePencil", false); + edit_vert_ps_ = ⊂ + } + { + auto &sub = pass.sub("Edges"); + sub.shader_set(res.shaders.particle_edit_edge.get()); + sub.bind_ubo("globalsBlock", &res.globals_buf); + sub.bind_texture("weightTex", res.weight_ramp_tx); + sub.push_constant("useWeight", false); + sub.push_constant("useGreasePencil", false); + edit_edge_ps_ = ⊂ + } + } } /* Particle data are stored in world space. If an object is instanced, the associated particle @@ -90,6 +133,71 @@ class Particles { return dupli_mat; } + void edit_object_sync(Manager &manager, + const ObjectRef &ob_ref, + Resources & /*res*/, + const State &state) + { + if (!enabled_) { + return; + } + + /* Usually the edit structure is created by Particle Edit Mode Toggle + * operator, but sometimes it's invoked after tagging hair as outdated + * (for example, when toggling edit mode). That makes it impossible to + * create edit structure for until after next dependency graph evaluation. + * + * Ideally, the edit structure will be created here already via some + * dependency graph callback or so, but currently trying to make it nicer + * only causes bad level calls and breaks design from the past. + */ + Object *object_eval = ob_ref.object; + Object *object_orig = DEG_get_original_object(object_eval); + Scene *scene_orig = (Scene *)DEG_get_original_id(const_cast(&state.scene->id)); + PTCacheEdit *edit = PE_create_current(state.depsgraph, scene_orig, object_orig); + if (edit == nullptr) { + /* Happens when trying to edit particles in EMITTER mode without having them cached. */ + return; + } + + auto find_active_evaluated_psys = + [&](ListBaseWrapper particle_systems_orig, + ListBaseWrapper particle_systems_eval) -> ParticleSystem * { + int psys_index = 0; + for (ParticleSystem *psys_orig : particle_systems_orig) { + if (PE_get_current_from_psys(psys_orig) == edit) { + return particle_systems_eval.get(psys_index); + } + psys_index++; + } + return nullptr; + }; + + ParticleSystem *psys = find_active_evaluated_psys(&object_orig->particlesystem, + &object_eval->particlesystem); + if (psys == nullptr) { + printf("Error getting evaluated particle system for edit.\n"); + return; + } + + Object *ob = ob_ref.object; + + ResourceHandle handle = manager.resource_handle_for_psys(ob_ref, dupli_matrix_get(ob_ref)); + + { + gpu::Batch *geom = DRW_cache_particles_get_edit_strands(ob, psys, edit, show_weight_); + edit_edge_ps_->draw(geom, handle); + } + if (show_point_inner_) { + gpu::Batch *geom = DRW_cache_particles_get_edit_inner_points(ob, psys, edit); + edit_vert_ps_->draw(geom, handle); + } + if (show_point_tip_) { + gpu::Batch *geom = DRW_cache_particles_get_edit_tip_points(ob, psys, edit); + edit_vert_ps_->draw(geom, handle); + } + } + void object_sync(Manager &manager, const ObjectRef &ob_ref, Resources &res, const State &state) { if (!enabled_) { @@ -100,7 +208,7 @@ class Particles { ResourceHandle handle = {0}; - LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) { + for (ParticleSystem *psys : ListBaseWrapper(&ob->particlesystem)) { if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { continue; } @@ -181,5 +289,15 @@ class Particles { GPU_framebuffer_bind(framebuffer); manager.submit(particle_ps_, view); } + + void draw_no_line(Framebuffer &framebuffer, Manager &manager, View &view) + { + if (!enabled_) { + return; + } + + GPU_framebuffer_bind(framebuffer); + manager.submit(edit_particle_ps_, view); + } }; } // namespace blender::draw::overlay diff --git a/source/blender/draw/engines/overlay/overlay_next_private.hh b/source/blender/draw/engines/overlay/overlay_next_private.hh index 20ad09c3484..92938eb5448 100644 --- a/source/blender/draw/engines/overlay/overlay_next_private.hh +++ b/source/blender/draw/engines/overlay/overlay_next_private.hh @@ -231,6 +231,8 @@ class ShaderModule { ShaderPtr outline_prepass_pointcloud; ShaderPtr outline_prepass_gpencil; ShaderPtr outline_detect = shader("overlay_outline_detect"); + ShaderPtr particle_edit_vert; + ShaderPtr particle_edit_edge; ShaderPtr paint_region_edge; ShaderPtr paint_region_face; ShaderPtr paint_region_vert; diff --git a/source/blender/draw/engines/overlay/overlay_next_shader.cc b/source/blender/draw/engines/overlay/overlay_next_shader.cc index 2d749eba28d..60e67977d49 100644 --- a/source/blender/draw/engines/overlay/overlay_next_shader.cc +++ b/source/blender/draw/engines/overlay/overlay_next_shader.cc @@ -255,6 +255,13 @@ ShaderModule::ShaderModule(const SelectionType selection_type, const bool clippi info.additional_info("draw_gpencil_new", "draw_object_infos_new"); }); + particle_edit_vert = shader( + "overlay_edit_particle_point", + [](gpu::shader::ShaderCreateInfo &info) { shader_patch_common(info); }); + particle_edit_edge = shader( + "overlay_edit_particle_strand", + [](gpu::shader::ShaderCreateInfo &info) { shader_patch_common(info); }); + paint_region_edge = shader("overlay_paint_wire", [](gpu::shader::ShaderCreateInfo &info) { shader_patch_common(info); });