From adbb732ba2f0f7ffe00b7c4fdf60de8234f9e615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20FOUCAULT?= Date: Mon, 9 Sep 2024 15:39:28 +0200 Subject: [PATCH] Overlay-Next: Sculpt Rel #102179 Pull Request: https://projects.blender.org/blender/blender/pulls/127261 --- source/blender/draw/CMakeLists.txt | 1 + .../engines/overlay/overlay_next_instance.cc | 20 +- .../engines/overlay/overlay_next_instance.hh | 2 + .../engines/overlay/overlay_next_private.hh | 8 + .../engines/overlay/overlay_next_sculpt.hh | 260 ++++++++++++++++++ .../engines/overlay/overlay_next_shader.cc | 11 + .../infos/overlay_sculpt_curves_info.hh | 2 +- .../shaders/overlay_sculpt_mask_frag.glsl | 2 +- 8 files changed, 303 insertions(+), 3 deletions(-) create mode 100644 source/blender/draw/engines/overlay/overlay_next_sculpt.hh diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 3be75267420..9052b468852 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -311,6 +311,7 @@ set(SRC engines/overlay/overlay_next_prepass.hh engines/overlay/overlay_next_private.hh engines/overlay/overlay_next_relation.hh + engines/overlay/overlay_next_sculpt.hh engines/overlay/overlay_next_speaker.hh engines/overlay/overlay_next_wireframe.hh engines/overlay/overlay_next_xray_fade.hh diff --git a/source/blender/draw/engines/overlay/overlay_next_instance.cc b/source/blender/draw/engines/overlay/overlay_next_instance.cc index 0e0898d98d1..bae54fe015b 100644 --- a/source/blender/draw/engines/overlay/overlay_next_instance.cc +++ b/source/blender/draw/engines/overlay/overlay_next_instance.cc @@ -116,6 +116,7 @@ void Instance::begin_sync() layer.prepass.begin_sync(resources, state); layer.relations.begin_sync(); layer.speakers.begin_sync(); + layer.sculpts.begin_sync(resources, state); layer.wireframe.begin_sync(resources, state); }; begin_sync_layer(regular); @@ -142,6 +143,10 @@ void Instance::object_sync(ObjectRef &ob_ref, Manager &manager) layer.prepass.object_sync(manager, ob_ref, resources, state); } + if (in_sculpt_mode) { + layer.sculpts.object_sync(manager, ob_ref, state); + } + if (in_edit_mode && !state.hide_overlays) { switch (ob_ref.object->type) { case OB_MESH: @@ -264,6 +269,9 @@ void Instance::draw(Manager &manager) resources.color_overlay_tx.wrap(DRW_viewport_texture_list_get()->color_overlay); resources.color_render_tx.wrap(DRW_viewport_texture_list_get()->color); + resources.render_fb = DRW_viewport_framebuffer_list_get()->default_fb; + resources.render_in_front_fb = DRW_viewport_framebuffer_list_get()->in_front_fb; + int2 render_size = int2(resources.depth_tx.size()); const DRWView *view_legacy = DRW_view_default_get(); @@ -331,6 +339,9 @@ void Instance::draw(Manager &manager) resources.overlay_output_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx)); + regular.sculpts.draw_on_render(resources.render_fb, manager, view); + infront.sculpts.draw_on_render(resources.render_in_front_fb, manager, view); + GPU_framebuffer_bind(resources.overlay_line_fb); float4 clear_color(0.0f); if (state.xray_enabled) { @@ -374,6 +385,7 @@ void Instance::draw(Manager &manager) layer.fluids.draw(framebuffer, manager, view); layer.particles.draw(framebuffer, manager, view); layer.armatures.draw(framebuffer, manager, view); + layer.sculpts.draw(framebuffer, manager, view); layer.meshes.draw(framebuffer, manager, view); }; @@ -435,7 +447,13 @@ bool Instance::object_is_sculpt_mode(const ObjectRef &ob_ref) bool is_geonode_preview = ob_ref.dupli_object && ob_ref.dupli_object->preview_base_geometry; bool is_active_dupli_parent = ob_ref.dupli_parent == active_object; return is_active_object || (is_active_dupli_parent && is_geonode_preview); - }; + } + + if (state.object_mode == OB_MODE_SCULPT) { + const Object *active_object = state.active_base->object; + const bool is_active_object = ob_ref.object == active_object; + return is_active_object; + } return false; } diff --git a/source/blender/draw/engines/overlay/overlay_next_instance.hh b/source/blender/draw/engines/overlay/overlay_next_instance.hh index b4b0c70d1f7..e400fc23307 100644 --- a/source/blender/draw/engines/overlay/overlay_next_instance.hh +++ b/source/blender/draw/engines/overlay/overlay_next_instance.hh @@ -31,6 +31,7 @@ #include "overlay_next_particle.hh" #include "overlay_next_prepass.hh" #include "overlay_next_relation.hh" +#include "overlay_next_sculpt.hh" #include "overlay_next_speaker.hh" #include "overlay_next_wireframe.hh" #include "overlay_next_xray_fade.hh" @@ -80,6 +81,7 @@ class Instance { Particles particles; Prepass prepass = {selection_type_}; Relations relations = {selection_type_}; + Sculpts sculpts = {selection_type_}; Speakers speakers = {selection_type_}; Wireframe wireframe; } regular{selection_type_}, infront{selection_type_}; diff --git a/source/blender/draw/engines/overlay/overlay_next_private.hh b/source/blender/draw/engines/overlay/overlay_next_private.hh index da86367aaeb..2f2cab8c6d3 100644 --- a/source/blender/draw/engines/overlay/overlay_next_private.hh +++ b/source/blender/draw/engines/overlay/overlay_next_private.hh @@ -220,6 +220,9 @@ class ShaderModule { ShaderPtr outline_prepass_pointcloud; ShaderPtr outline_prepass_gpencil; ShaderPtr outline_detect = shader("overlay_outline_detect"); + ShaderPtr sculpt_mesh; + ShaderPtr sculpt_curves; + ShaderPtr sculpt_curves_cage; ShaderPtr xray_fade; /** Selectable Shaders */ @@ -298,6 +301,11 @@ struct Resources : public select::SelectMap { /* Output Color. */ Framebuffer overlay_output_fb = {"overlay_output_fb"}; + /* Render Framebuffers. Only used for multiplicative blending on top of the render. */ + /* TODO(fclem): Remove the usage of these somehow. This is against design. */ + GPUFrameBuffer *render_fb = nullptr; + GPUFrameBuffer *render_in_front_fb = nullptr; + /* Target containing line direction and data for line expansion and anti-aliasing. */ TextureFromPool line_tx = {"line_tx"}; /* Target containing overlay color before anti-aliasing. */ diff --git a/source/blender/draw/engines/overlay/overlay_next_sculpt.hh b/source/blender/draw/engines/overlay/overlay_next_sculpt.hh new file mode 100644 index 00000000000..dea523f509a --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_next_sculpt.hh @@ -0,0 +1,260 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup overlay + */ + +#pragma once + +#include "BKE_attribute.hh" +#include "BKE_mesh.hh" +#include "BKE_paint.hh" +#include "BKE_pbvh_api.hh" +#include "BKE_subdiv_ccg.hh" + +#include "draw_cache_impl.hh" + +#include "overlay_next_private.hh" + +namespace blender::draw::overlay { + +class Sculpts { + + private: + const SelectionType selection_type_; + + PassSimple sculpt_mask_ = {"SculptMaskAndFaceSet"}; + PassSimple::Sub *mesh_ps_ = nullptr; + PassSimple::Sub *curves_ps_ = nullptr; + + PassSimple sculpt_curve_cage_ = {"SculptCage"}; + + bool show_curves_cage_ = false; + bool show_face_set_ = false; + bool show_mask_ = false; + + bool enabled_ = false; + + public: + Sculpts(const SelectionType selection_type_) : selection_type_(selection_type_) {} + + void begin_sync(Resources &res, const State &state) + { + const int sculpt_overlay_flags = V3D_OVERLAY_SCULPT_SHOW_FACE_SETS | + V3D_OVERLAY_SCULPT_SHOW_MASK | V3D_OVERLAY_SCULPT_CURVES_CAGE; + + enabled_ = (state.space_type == SPACE_VIEW3D) && !state.xray_enabled && + (selection_type_ == SelectionType::DISABLED) && + ELEM(state.object_mode, OB_MODE_SCULPT_CURVES, OB_MODE_SCULPT) && + (state.overlay.flag & sculpt_overlay_flags); + + if (!enabled_) { + /* Not used. But release the data. */ + sculpt_mask_.init(); + sculpt_curve_cage_.init(); + return; + } + + show_curves_cage_ = state.overlay.flag & V3D_OVERLAY_SCULPT_CURVES_CAGE; + show_face_set_ = state.overlay.flag & V3D_OVERLAY_SCULPT_SHOW_FACE_SETS; + show_mask_ = state.overlay.flag & V3D_OVERLAY_SCULPT_SHOW_MASK; + + float curve_cage_opacity = show_curves_cage_ ? state.overlay.sculpt_curves_cage_opacity : 0.0f; + float face_set_opacity = show_face_set_ ? state.overlay.sculpt_mode_face_sets_opacity : 0.0f; + float mask_opacity = show_mask_ ? state.overlay.sculpt_mode_mask_opacity : 0.0f; + + { + sculpt_mask_.init(); + sculpt_mask_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | + DRW_STATE_BLEND_MUL, + state.clipping_plane_count); + { + auto &sub = sculpt_mask_.sub("Mesh"); + sub.shader_set(res.shaders.sculpt_mesh.get()); + sub.bind_ubo("globalsBlock", &res.globals_buf); + sub.push_constant("maskOpacity", mask_opacity); + sub.push_constant("faceSetsOpacity", face_set_opacity); + mesh_ps_ = ⊂ + } + { + auto &sub = sculpt_mask_.sub("Curves"); + sub.shader_set(res.shaders.sculpt_curves.get()); + sub.bind_ubo("globalsBlock", &res.globals_buf); + sub.push_constant("selection_opacity", mask_opacity); + curves_ps_ = ⊂ + } + } + { + auto &pass = sculpt_curve_cage_; + pass.init(); + pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA, + state.clipping_plane_count); + pass.shader_set(res.shaders.sculpt_curves_cage.get()); + pass.bind_ubo("globalsBlock", &res.globals_buf); + pass.push_constant("opacity", curve_cage_opacity); + } + } + + void object_sync(Manager &manager, const ObjectRef &ob_ref, const State &state) + { + if (!enabled_) { + return; + } + + switch (ob_ref.object->type) { + case OB_MESH: + mesh_sync(manager, ob_ref, state); + break; + case OB_CURVES: + curves_sync(manager, ob_ref, state); + break; + } + } + + void curves_sync(Manager &manager, const ObjectRef &ob_ref, const State &state) + { + ::Curves *curves = static_cast<::Curves *>(ob_ref.object->data); + + /* As an optimization, draw nothing if everything is selected. */ + if (show_mask_ && !everything_selected(*curves)) { + /* Retrieve the location of the texture. */ + bool is_point_domain; + gpu::VertBuf **select_attr_buf = DRW_curves_texture_for_evaluated_attribute( + curves, ".selection", &is_point_domain); + if (select_attr_buf) { + /* Evaluate curves and their attributes if necessary. */ + gpu::Batch *geometry = curves_sub_pass_setup(*curves_ps_, state.scene, ob_ref.object); + if (*select_attr_buf) { + ResourceHandle handle = manager.resource_handle(ob_ref); + + curves_ps_->push_constant("is_point_domain", is_point_domain); + curves_ps_->bind_texture("selection_tx", *select_attr_buf); + curves_ps_->draw(geometry, handle); + } + } + } + + if (show_curves_cage_) { + ResourceHandle handle = manager.resource_handle(ob_ref); + + blender::gpu::Batch *geometry = DRW_curves_batch_cache_get_sculpt_curves_cage(curves); + sculpt_curve_cage_.draw(geometry, handle); + } + } + + void mesh_sync(Manager &manager, const ObjectRef &ob_ref, const State &state) + { + if (!show_face_set_ && !show_mask_) { + /* Nothing to display. */ + return; + } + + const SculptSession *sculpt_session = ob_ref.object->sculpt; + if (sculpt_session == nullptr) { + return; + } + + bke::pbvh::Tree *pbvh = bke::object::pbvh_get(*ob_ref.object); + if (!pbvh) { + /* It is possible to have SculptSession without pbvh::Tree. This happens, for example, when + * toggling object mode to sculpt then to edit mode. */ + return; + } + + /* Using the original object/geometry is necessary because we skip depsgraph updates in sculpt + * mode to improve performance. This means the evaluated mesh doesn't have the latest face set, + * visibility, and mask data. */ + Object *object_orig = reinterpret_cast(DEG_get_original_id(&ob_ref.object->id)); + if (!object_orig) { + BLI_assert_unreachable(); + return; + } + + switch (pbvh->type()) { + case blender::bke::pbvh::Type::Mesh: { + const Mesh &mesh = *static_cast(object_orig->data); + if (!mesh.attributes().contains(".sculpt_face_set") && + !mesh.attributes().contains(".sculpt_mask")) + { + return; + } + break; + } + case blender::bke::pbvh::Type::Grids: { + const SubdivCCG &subdiv_ccg = *sculpt_session->subdiv_ccg; + const Mesh &base_mesh = *static_cast(object_orig->data); + if (!BKE_subdiv_ccg_key_top_level(subdiv_ccg).has_mask && + !base_mesh.attributes().contains(".sculpt_face_set")) + { + return; + } + break; + } + case blender::bke::pbvh::Type::BMesh: { + const BMesh &bm = *sculpt_session->bm; + if (!CustomData_has_layer_named(&bm.pdata, CD_PROP_FLOAT, ".sculpt_face_set") && + !CustomData_has_layer_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask")) + { + return; + } + break; + } + } + + const bool use_pbvh = BKE_sculptsession_use_pbvh_draw(ob_ref.object, state.rv3d); + if (use_pbvh) { + /* TODO(fclem): Deduplicate with other engine. */ + const blender::Bounds bounds = bke::pbvh::bounds_get(*ob_ref.object->sculpt->pbvh); + const float3 center = math::midpoint(bounds.min, bounds.max); + const float3 half_extent = bounds.max - center; + ResourceHandle handle = manager.resource_handle(ob_ref, nullptr, ¢er, &half_extent); + + SculptBatchFeature sculpt_batch_features_ = (show_face_set_ ? SCULPT_BATCH_FACE_SET : + SCULPT_BATCH_DEFAULT) | + (show_mask_ ? SCULPT_BATCH_MASK : + SCULPT_BATCH_DEFAULT); + + for (SculptBatch &batch : sculpt_batches_get(ob_ref.object, sculpt_batch_features_)) { + mesh_ps_->draw(batch.batch, handle); + } + } + else { + ResourceHandle handle = manager.resource_handle(ob_ref); + + Mesh &mesh = *static_cast(ob_ref.object->data); + gpu::Batch *sculpt_overlays = DRW_mesh_batch_cache_get_sculpt_overlays(mesh); + mesh_ps_->draw(sculpt_overlays, handle); + } + } + + void draw(GPUFrameBuffer *framebuffer, Manager &manager, View &view) + { + if (!enabled_) { + return; + } + GPU_framebuffer_bind(framebuffer); + manager.submit(sculpt_curve_cage_, view); + } + + void draw_on_render(GPUFrameBuffer *framebuffer, Manager &manager, View &view) + { + if (!enabled_) { + return; + } + GPU_framebuffer_bind(framebuffer); + manager.submit(sculpt_mask_, view); + } + + private: + bool everything_selected(const ::Curves &curves_id) + { + const bke::CurvesGeometry &curves = curves_id.geometry.wrap(); + const VArray selection = *curves.attributes().lookup_or_default( + ".selection", bke::AttrDomain::Point, true); + return selection.is_single() && selection.get_internal_single(); + } +}; + +} // namespace blender::draw::overlay diff --git a/source/blender/draw/engines/overlay/overlay_next_shader.cc b/source/blender/draw/engines/overlay/overlay_next_shader.cc index 82afef75e91..5e300a3b021 100644 --- a/source/blender/draw/engines/overlay/overlay_next_shader.cc +++ b/source/blender/draw/engines/overlay/overlay_next_shader.cc @@ -236,6 +236,17 @@ ShaderModule::ShaderModule(const SelectionType selection_type, const bool clippi info.additional_info("draw_gpencil_new", "draw_object_infos_new"); }); + sculpt_mesh = shader("overlay_sculpt_mask", + [](gpu::shader::ShaderCreateInfo &info) { shader_patch_common(info); }); + sculpt_curves = shader("overlay_sculpt_curves_selection", + [](gpu::shader::ShaderCreateInfo &info) { + shader_patch_common(info); + info.additional_info("draw_hair_new"); + }); + sculpt_curves_cage = shader( + "overlay_sculpt_curves_cage", + [](gpu::shader::ShaderCreateInfo &info) { shader_patch_common(info); }); + xray_fade = shader("overlay_xray_fade", [](gpu::shader::ShaderCreateInfo &info) { info.sampler(2, ImageType::DEPTH_2D, "xrayDepthTexInfront"); }); diff --git a/source/blender/draw/engines/overlay/shaders/infos/overlay_sculpt_curves_info.hh b/source/blender/draw/engines/overlay/shaders/infos/overlay_sculpt_curves_info.hh index 0a436064d7b..55758b3c410 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/overlay_sculpt_curves_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/overlay_sculpt_curves_info.hh @@ -11,7 +11,7 @@ GPU_SHADER_CREATE_INFO(overlay_sculpt_curves_selection) .do_static_compilation(true) .push_constant(Type::BOOL, "is_point_domain") .push_constant(Type::FLOAT, "selection_opacity") - .sampler(0, ImageType::FLOAT_BUFFER, "selection_tx") + .sampler(1, ImageType::FLOAT_BUFFER, "selection_tx") .vertex_out(overlay_sculpt_curves_selection_iface) .vertex_source("overlay_sculpt_curves_selection_vert.glsl") .fragment_source("overlay_sculpt_curves_selection_frag.glsl") diff --git a/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_frag.glsl index 56de1df4e6b..fe3b8e9be0a 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_frag.glsl @@ -4,5 +4,5 @@ void main() { - fragColor = vec4(faceset_color * vec3(mask_color), 1.0); + fragColor = vec4(faceset_color * mask_color, 1.0); }