Overlay-Next: Sculpt

Rel #102179

Pull Request: https://projects.blender.org/blender/blender/pulls/127261
This commit is contained in:
Clément FOUCAULT
2024-09-09 15:39:28 +02:00
committed by Clément Foucault
parent eee34de007
commit adbb732ba2
8 changed files with 303 additions and 3 deletions

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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_};

View File

@@ -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. */

View File

@@ -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<Object *>(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<const Mesh *>(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<const Mesh *>(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<float3> 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, &center, &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<Mesh *>(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<bool> selection = *curves.attributes().lookup_or_default<bool>(
".selection", bke::AttrDomain::Point, true);
return selection.is_single() && selection.get_internal_single();
}
};
} // namespace blender::draw::overlay

View File

@@ -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");
});

View File

@@ -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")

View File

@@ -4,5 +4,5 @@
void main()
{
fragColor = vec4(faceset_color * vec3(mask_color), 1.0);
fragColor = vec4(faceset_color * mask_color, 1.0);
}