Overlay-Next: Outline
Straightforward port. Only the dupli object optimization and the resource handle deduplication are left as todos. Marked areas that are against design to be fixed. Rel #102179 Pull Request: https://projects.blender.org/blender/blender/pulls/126152
This commit is contained in:
committed by
Clément Foucault
parent
1c70e07af5
commit
c42380c633
@@ -205,6 +205,13 @@ template<typename MatT, typename RotationT>
|
||||
[[nodiscard]] MatT from_loc_rot(const typename MatT::loc_type &location,
|
||||
const RotationT &rotation);
|
||||
|
||||
/**
|
||||
* Create a transform matrix with translation and scale applied in this order.
|
||||
*/
|
||||
template<typename MatT, int ScaleDim>
|
||||
[[nodiscard]] MatT from_loc_scale(const typename MatT::loc_type &location,
|
||||
const VecBase<typename MatT::base_type, ScaleDim> &scale);
|
||||
|
||||
/**
|
||||
* Create a transform matrix with translation, rotation and scale applied in this order.
|
||||
*/
|
||||
@@ -1409,6 +1416,15 @@ template<typename MatT, typename RotationT>
|
||||
return mat;
|
||||
}
|
||||
|
||||
template<typename MatT, int ScaleDim>
|
||||
[[nodiscard]] MatT from_loc_scale(const typename MatT::loc_type &location,
|
||||
const VecBase<typename MatT::base_type, ScaleDim> &scale)
|
||||
{
|
||||
MatT mat = MatT(from_scale<MatT>(scale));
|
||||
mat.location() = location;
|
||||
return mat;
|
||||
}
|
||||
|
||||
template<typename MatT, typename VectorT>
|
||||
[[nodiscard]] MatT from_orthonormal_axes(const VectorT forward, const VectorT up)
|
||||
{
|
||||
|
||||
@@ -296,6 +296,7 @@ set(SRC
|
||||
engines/overlay/overlay_next_camera.hh
|
||||
engines/overlay/overlay_next_facing.hh
|
||||
engines/overlay/overlay_next_force_field.hh
|
||||
engines/overlay/overlay_next_grease_pencil.hh
|
||||
engines/overlay/overlay_next_grid.hh
|
||||
engines/overlay/overlay_next_instance.hh
|
||||
engines/overlay/overlay_next_lattice.hh
|
||||
@@ -303,6 +304,7 @@ set(SRC
|
||||
engines/overlay/overlay_next_lightprobe.hh
|
||||
engines/overlay/overlay_next_mesh.hh
|
||||
engines/overlay/overlay_next_metaball.hh
|
||||
engines/overlay/overlay_next_outline.hh
|
||||
engines/overlay/overlay_next_prepass.hh
|
||||
engines/overlay/overlay_next_private.hh
|
||||
engines/overlay/overlay_next_relation.hh
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup overlay
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_bounds.hh"
|
||||
#include "BLI_math_matrix.hh"
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_object.hh"
|
||||
|
||||
#include "ED_grease_pencil.hh"
|
||||
|
||||
#include "overlay_next_private.hh"
|
||||
|
||||
namespace blender::draw::overlay {
|
||||
|
||||
class GreasePencil {
|
||||
public:
|
||||
struct ViewParameters {
|
||||
bool is_perspective;
|
||||
union {
|
||||
/* Z axis if ortho or position if perspective. */
|
||||
float3 z_axis;
|
||||
float3 location;
|
||||
};
|
||||
|
||||
ViewParameters() = default;
|
||||
|
||||
ViewParameters(bool is_perspective, const float4x4 &viewinv)
|
||||
{
|
||||
if (is_perspective) {
|
||||
location = viewinv.location();
|
||||
}
|
||||
else {
|
||||
z_axis = viewinv.z_axis();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
static void draw_grease_pencil(PassMain::Sub &pass,
|
||||
const ViewParameters &view,
|
||||
const Scene *scene,
|
||||
Object *ob,
|
||||
ResourceHandle res_handle)
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::ed::greasepencil;
|
||||
::GreasePencil &grease_pencil = *static_cast<::GreasePencil *>(ob->data);
|
||||
|
||||
float4 plane = (grease_pencil.flag & GREASE_PENCIL_STROKE_ORDER_3D) ?
|
||||
float4(0.0f) :
|
||||
depth_plane_get(ob, view);
|
||||
pass.push_constant("gpDepthPlane", plane);
|
||||
|
||||
int t_offset = 0;
|
||||
const Vector<DrawingInfo> drawings = retrieve_visible_drawings(*scene, grease_pencil, true);
|
||||
for (const DrawingInfo info : drawings) {
|
||||
const bool is_stroke_order_3d = (grease_pencil.flag & GREASE_PENCIL_STROKE_ORDER_3D) != 0;
|
||||
|
||||
const float object_scale = mat4_to_scale(ob->object_to_world().ptr());
|
||||
const float thickness_scale = bke::greasepencil::LEGACY_RADIUS_CONVERSION_FACTOR;
|
||||
|
||||
gpu::VertBuf *position_tx = draw::DRW_cache_grease_pencil_position_buffer_get(scene, ob);
|
||||
gpu::VertBuf *color_tx = draw::DRW_cache_grease_pencil_color_buffer_get(scene, ob);
|
||||
|
||||
pass.push_constant("gpStrokeOrder3d", is_stroke_order_3d);
|
||||
pass.push_constant("gpThicknessScale", object_scale);
|
||||
pass.push_constant("gpThicknessOffset", 0.0f);
|
||||
pass.push_constant("gpThicknessWorldScale", thickness_scale);
|
||||
pass.bind_texture("gp_pos_tx", position_tx);
|
||||
pass.bind_texture("gp_col_tx", color_tx);
|
||||
|
||||
const bke::CurvesGeometry &curves = info.drawing.strokes();
|
||||
const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve();
|
||||
const bke::AttributeAccessor attributes = curves.attributes();
|
||||
const VArray<int> stroke_materials = *attributes.lookup_or_default<int>(
|
||||
"material_index", bke::AttrDomain::Curve, 0);
|
||||
const VArray<bool> cyclic = *attributes.lookup_or_default<bool>(
|
||||
"cyclic", bke::AttrDomain::Curve, false);
|
||||
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask visible_strokes = ed::greasepencil::retrieve_visible_strokes(
|
||||
*ob, info.drawing, memory);
|
||||
|
||||
visible_strokes.foreach_index([&](const int stroke_i) {
|
||||
const IndexRange points = points_by_curve[stroke_i];
|
||||
const int material_index = stroke_materials[stroke_i];
|
||||
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, material_index + 1);
|
||||
|
||||
const bool hide_onion = info.onion_id != 0;
|
||||
const bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
|
||||
|
||||
const int num_stroke_triangles = (points.size() >= 3) ? (points.size() - 2) : 0;
|
||||
const int num_stroke_vertices = (points.size() +
|
||||
int(cyclic[stroke_i] && (points.size() >= 3)));
|
||||
|
||||
if (hide_material || hide_onion) {
|
||||
t_offset += num_stroke_triangles;
|
||||
t_offset += num_stroke_vertices * 2;
|
||||
return;
|
||||
}
|
||||
|
||||
blender::gpu::Batch *geom = draw::DRW_cache_grease_pencil_get(scene, ob);
|
||||
|
||||
const bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0;
|
||||
const bool show_fill = (points.size() >= 3) &&
|
||||
(gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
|
||||
|
||||
if (show_fill) {
|
||||
int v_first = t_offset * 3;
|
||||
int v_count = num_stroke_triangles * 3;
|
||||
pass.draw(geom, 1, v_count, v_first, res_handle);
|
||||
}
|
||||
|
||||
t_offset += num_stroke_triangles;
|
||||
|
||||
if (show_stroke) {
|
||||
int v_first = t_offset * 3;
|
||||
int v_count = num_stroke_vertices * 2 * 3;
|
||||
pass.draw(geom, 1, v_count, v_first, res_handle);
|
||||
}
|
||||
t_offset += num_stroke_vertices * 2;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/* Returns the normal plane in NDC space. */
|
||||
static float4 depth_plane_get(const Object *ob, const ViewParameters &view)
|
||||
{
|
||||
using namespace blender::math;
|
||||
|
||||
/* Find the normal most likely to represent the grease pencil object. */
|
||||
/* TODO: This does not work quite well if you use
|
||||
* strokes not aligned with the object axes. Maybe we could try to
|
||||
* compute the minimum axis of all strokes. But this would be more
|
||||
* computationally heavy and should go into the GPData evaluation. */
|
||||
const std::optional<blender::Bounds<float3>> bounds = BKE_object_boundbox_get(ob).value_or(
|
||||
blender::Bounds(float3(0)));
|
||||
|
||||
float3 center = midpoint(bounds->min, bounds->max);
|
||||
float3 size = (bounds->max - bounds->min) * 0.5f;
|
||||
/* Avoid division by 0.0 later. */
|
||||
size += 1e-8f;
|
||||
/* Convert Bbox unit space to object space. */
|
||||
float4x4 bbox_to_object = from_loc_scale<float4x4>(center, size);
|
||||
float4x4 bbox_to_world = ob->object_to_world() * bbox_to_object;
|
||||
|
||||
float3 bbox_center = bbox_to_world.location();
|
||||
float3 view_vector = (view.is_perspective) ? view.location - bbox_center : view.z_axis;
|
||||
|
||||
float3x3 world_to_bbox = invert(float3x3(bbox_to_world));
|
||||
|
||||
/* Normalize the vector in BBox space. */
|
||||
float3 local_plane_direction = normalize(transform_direction(world_to_bbox, view_vector));
|
||||
/* `bbox_to_world_normal` is a "normal" matrix. It transforms BBox space normals to world. */
|
||||
float3x3 bbox_to_world_normal = transpose(world_to_bbox);
|
||||
float3 plane_direction = normalize(
|
||||
transform_direction(bbox_to_world_normal, local_plane_direction));
|
||||
|
||||
return float4(plane_direction, -dot(plane_direction, bbox_center));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::draw::overlay
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#include "ED_view3d.hh"
|
||||
|
||||
#include "BKE_paint.hh"
|
||||
|
||||
#include "draw_debug.hh"
|
||||
|
||||
#include "overlay_next_instance.hh"
|
||||
@@ -85,6 +87,7 @@ void Instance::begin_sync()
|
||||
resources.begin_sync();
|
||||
|
||||
background.begin_sync(resources, state);
|
||||
outline.begin_sync(resources, state);
|
||||
|
||||
auto begin_sync_layer = [&](OverlayLayer &layer) {
|
||||
layer.bounds.begin_sync();
|
||||
@@ -112,7 +115,9 @@ void Instance::begin_sync()
|
||||
void Instance::object_sync(ObjectRef &ob_ref, Manager &manager)
|
||||
{
|
||||
const bool in_edit_mode = object_is_edit_mode(ob_ref.object);
|
||||
const bool needs_prepass = true; /* TODO */
|
||||
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 needs_prepass = !state.xray_enabled; /* TODO */
|
||||
|
||||
OverlayLayer &layer = (state.use_in_front && ob_ref.object->dtx & OB_DRAW_IN_FRONT) ? infront :
|
||||
regular;
|
||||
@@ -189,6 +194,22 @@ void Instance::object_sync(ObjectRef &ob_ref, Manager &manager)
|
||||
layer.force_fields.object_sync(ob_ref, resources, state);
|
||||
layer.bounds.object_sync(ob_ref, resources, state);
|
||||
layer.relations.object_sync(ob_ref, resources, state);
|
||||
|
||||
if (object_is_selected(ob_ref)) {
|
||||
if (in_edit_mode || in_paint_mode || in_sculpt_mode) {
|
||||
/* Disable outlines for objects in sculpt, paint or edit mode. */
|
||||
}
|
||||
else if ((ob_ref.object->base_flag & BASE_FROM_DUPLI) &&
|
||||
(object_is_edit_mode(ob_ref.dupli_parent) ||
|
||||
object_is_sculpt_mode(ob_ref.dupli_parent) ||
|
||||
object_is_paint_mode(ob_ref.dupli_parent)))
|
||||
{
|
||||
/* Disable outlines for objects instanced by an object in sculpt, paint or edit mode. */
|
||||
}
|
||||
else {
|
||||
outline.object_sync(manager, ob_ref, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,12 +294,15 @@ void Instance::draw(Manager &manager)
|
||||
GPU_ATTACHMENT_TEXTURE(resources.overlay_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(resources.line_tx));
|
||||
resources.overlay_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_in_front_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx));
|
||||
GPU_ATTACHMENT_TEXTURE(resources.overlay_tx));
|
||||
resources.overlay_line_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_in_front_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(resources.overlay_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(resources.line_tx));
|
||||
}
|
||||
|
||||
resources.overlay_line_only_fb.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(resources.overlay_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(resources.line_tx));
|
||||
resources.overlay_color_only_fb.ensure(GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(resources.overlay_tx));
|
||||
resources.overlay_output_fb.ensure(GPU_ATTACHMENT_NONE,
|
||||
@@ -291,6 +315,8 @@ void Instance::draw(Manager &manager)
|
||||
regular.prepass.draw(resources.overlay_line_fb, manager, view);
|
||||
infront.prepass.draw(resources.overlay_line_in_front_fb, manager, view);
|
||||
|
||||
outline.draw(resources, manager, view);
|
||||
|
||||
auto overlay_fb_draw = [&](OverlayLayer &layer, Framebuffer &framebuffer) {
|
||||
regular.facing.draw(framebuffer, manager, view);
|
||||
};
|
||||
@@ -338,11 +364,46 @@ void Instance::draw(Manager &manager)
|
||||
resources.read_result();
|
||||
}
|
||||
|
||||
bool Instance::object_is_edit_mode(const Object *ob)
|
||||
bool Instance::object_is_selected(const ObjectRef &ob_ref)
|
||||
{
|
||||
if (DRW_object_is_in_edit_mode(ob)) {
|
||||
return (ob_ref.object->base_flag & BASE_SELECTED);
|
||||
}
|
||||
|
||||
bool Instance::object_is_paint_mode(const Object *object)
|
||||
{
|
||||
if (object->type == OB_GREASE_PENCIL && state.object_mode & OB_MODE_WEIGHT_GPENCIL_LEGACY) {
|
||||
return true;
|
||||
}
|
||||
return (object == state.active_base->object) && (state.object_mode & OB_MODE_ALL_PAINT);
|
||||
}
|
||||
|
||||
bool Instance::object_is_sculpt_mode(const ObjectRef &ob_ref)
|
||||
{
|
||||
if (state.object_mode == OB_MODE_SCULPT_CURVES) {
|
||||
const Object *active_object = state.active_base->object;
|
||||
const bool is_active_object = ob_ref.object == active_object;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Instance::object_is_sculpt_mode(const Object *object)
|
||||
{
|
||||
if (object->sculpt && (object->sculpt->mode_type == OB_MODE_SCULPT)) {
|
||||
return object == state.active_base->object;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Instance::object_is_edit_mode(const Object *object)
|
||||
{
|
||||
if (DRW_object_is_in_edit_mode(object)) {
|
||||
/* Also check for context mode as the object mode is not 100% reliable. (see T72490) */
|
||||
switch (ob->type) {
|
||||
switch (object->type) {
|
||||
case OB_MESH:
|
||||
return state.ctx_mode == CTX_MODE_EDIT_MESH;
|
||||
case OB_ARMATURE:
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
#include "overlay_next_empty.hh"
|
||||
#include "overlay_next_facing.hh"
|
||||
#include "overlay_next_force_field.hh"
|
||||
#include "overlay_next_grease_pencil.hh"
|
||||
#include "overlay_next_grid.hh"
|
||||
#include "overlay_next_lattice.hh"
|
||||
#include "overlay_next_light.hh"
|
||||
#include "overlay_next_lightprobe.hh"
|
||||
#include "overlay_next_mesh.hh"
|
||||
#include "overlay_next_metaball.hh"
|
||||
#include "overlay_next_outline.hh"
|
||||
#include "overlay_next_prepass.hh"
|
||||
#include "overlay_next_relation.hh"
|
||||
#include "overlay_next_speaker.hh"
|
||||
@@ -49,6 +51,7 @@ class Instance {
|
||||
|
||||
/** Overlay types. */
|
||||
Background background;
|
||||
Outline outline;
|
||||
|
||||
struct OverlayLayer {
|
||||
const SelectionType selection_type_;
|
||||
@@ -57,6 +60,7 @@ class Instance {
|
||||
Empties empties = {selection_type_};
|
||||
Facing facing = {selection_type_};
|
||||
ForceFields force_fields = {selection_type_};
|
||||
GreasePencil grease_pencil;
|
||||
Lattices lattices;
|
||||
Lights lights = {selection_type_};
|
||||
LightProbes light_probes = {selection_type_};
|
||||
@@ -85,7 +89,13 @@ class Instance {
|
||||
void draw(Manager &manager);
|
||||
|
||||
private:
|
||||
bool object_is_edit_mode(const Object *ob);
|
||||
bool object_is_selected(const ObjectRef &ob_ref);
|
||||
bool object_is_edit_mode(const Object *object);
|
||||
bool object_is_paint_mode(const Object *object);
|
||||
/* Checks for both curve sculpt and regular sculpt mode. */
|
||||
bool object_is_sculpt_mode(const ObjectRef &ob_ref);
|
||||
/* Checks only for sculpt mode. */
|
||||
bool object_is_sculpt_mode(const Object *object);
|
||||
};
|
||||
|
||||
} // namespace blender::draw::overlay
|
||||
|
||||
211
source/blender/draw/engines/overlay/overlay_next_outline.hh
Normal file
211
source/blender/draw/engines/overlay/overlay_next_outline.hh
Normal file
@@ -0,0 +1,211 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup overlay
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "overlay_next_private.hh"
|
||||
|
||||
#include "draw_common.hh"
|
||||
|
||||
namespace blender::draw::overlay {
|
||||
|
||||
class Outline {
|
||||
private:
|
||||
/* Simple render pass that renders an object ID pass. */
|
||||
PassMain outline_prepass_ps_ = {"Prepass"};
|
||||
PassMain::Sub *prepass_curves_ps_ = nullptr;
|
||||
PassMain::Sub *prepass_pointcloud_ps_ = nullptr;
|
||||
PassMain::Sub *prepass_gpencil_ps_ = nullptr;
|
||||
PassMain::Sub *prepass_mesh_ps_ = nullptr;
|
||||
PassMain::Sub *prepass_wire_ps_ = nullptr;
|
||||
/* Detect edges inside the ID pass and output color for each of them. */
|
||||
PassSimple outline_resolve_ps_ = {"Resolve"};
|
||||
|
||||
TextureFromPool object_id_tx_ = {"outline_ob_id_tx"};
|
||||
TextureFromPool tmp_depth_tx_ = {"outline_depth_tx"};
|
||||
|
||||
Framebuffer prepass_fb_ = {"outline.prepass_fb"};
|
||||
|
||||
bool enabled = false;
|
||||
|
||||
overlay::GreasePencil::ViewParameters grease_pencil_view;
|
||||
|
||||
public:
|
||||
void begin_sync(Resources &res, const State &state)
|
||||
{
|
||||
enabled = (state.v3d_flag & V3D_SELECT_OUTLINE);
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
/* TODO(fclem): This is against design. We should not sync depending on view position.
|
||||
* Eventually, we should do this in a compute shader prepass. */
|
||||
float4x4 viewinv;
|
||||
DRW_view_viewmat_get(nullptr, viewinv.ptr(), true);
|
||||
grease_pencil_view = {DRW_view_is_persp_get(nullptr), viewinv};
|
||||
}
|
||||
|
||||
const float outline_width = UI_GetThemeValuef(TH_OUTLINE_WIDTH);
|
||||
const bool do_smooth_lines = (U.gpu_flag & USER_GPU_FLAG_OVERLAY_SMOOTH_WIRE) != 0;
|
||||
const bool do_expand = (U.pixelsize > 1.0) || (outline_width > 2.0f);
|
||||
const bool is_transform = (G.moving & G_TRANSFORM_OBJ) != 0;
|
||||
|
||||
{
|
||||
auto &pass = outline_prepass_ps_;
|
||||
pass.init();
|
||||
pass.framebuffer_set(&prepass_fb_);
|
||||
pass.clear_color_depth_stencil(float4(0.0f), 1.0f, 0x0);
|
||||
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
|
||||
state.clipping_state);
|
||||
{
|
||||
auto &sub = pass.sub("Curves");
|
||||
sub.shader_set(res.shaders.outline_prepass_curves.get());
|
||||
sub.push_constant("isTransform", is_transform);
|
||||
sub.bind_ubo("globalsBlock", &res.globals_buf);
|
||||
prepass_curves_ps_ = ⊂
|
||||
}
|
||||
{
|
||||
auto &sub = pass.sub("PointCloud");
|
||||
sub.shader_set(res.shaders.outline_prepass_pointcloud.get());
|
||||
sub.push_constant("isTransform", is_transform);
|
||||
sub.bind_ubo("globalsBlock", &res.globals_buf);
|
||||
prepass_pointcloud_ps_ = ⊂
|
||||
}
|
||||
{
|
||||
auto &sub = pass.sub("GreasePencil");
|
||||
sub.shader_set(res.shaders.outline_prepass_gpencil.get());
|
||||
sub.push_constant("isTransform", is_transform);
|
||||
sub.bind_ubo("globalsBlock", &res.globals_buf);
|
||||
prepass_gpencil_ps_ = ⊂
|
||||
}
|
||||
{
|
||||
auto &sub = pass.sub("Mesh");
|
||||
sub.shader_set(res.shaders.outline_prepass_mesh.get());
|
||||
sub.push_constant("isTransform", is_transform);
|
||||
sub.bind_ubo("globalsBlock", &res.globals_buf);
|
||||
prepass_mesh_ps_ = ⊂
|
||||
}
|
||||
{
|
||||
auto &sub = pass.sub("Wire");
|
||||
sub.shader_set(res.shaders.outline_prepass_wire.get());
|
||||
sub.push_constant("isTransform", is_transform);
|
||||
sub.bind_ubo("globalsBlock", &res.globals_buf);
|
||||
prepass_wire_ps_ = ⊂
|
||||
}
|
||||
}
|
||||
{
|
||||
auto &pass = outline_resolve_ps_;
|
||||
pass.init();
|
||||
pass.framebuffer_set(&res.overlay_line_only_fb);
|
||||
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL);
|
||||
pass.shader_set(res.shaders.outline_detect.get());
|
||||
/* Don't occlude the outline if in xray mode as it causes too much flickering. */
|
||||
pass.push_constant("alphaOcclu", state.xray_enabled ? 1.0f : 0.35f);
|
||||
pass.push_constant("doThickOutlines", do_expand);
|
||||
pass.push_constant("doAntiAliasing", do_smooth_lines);
|
||||
pass.bind_texture("outlineId", &object_id_tx_);
|
||||
pass.bind_texture("sceneDepth", &res.depth_tx);
|
||||
pass.bind_texture("outlineDepth", &tmp_depth_tx_);
|
||||
pass.bind_ubo("globalsBlock", &res.globals_buf);
|
||||
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
void object_sync(Manager &manager, const ObjectRef &ob_ref, const State &state)
|
||||
{
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Outlines of bounding boxes are not drawn. */
|
||||
if (ob_ref.object->dt == OB_BOUNDBOX) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO(fclem): Non-mandatory handle creation and reuse with other overlays. */
|
||||
ResourceHandle res_handle = manager.resource_handle(ob_ref);
|
||||
|
||||
gpu::Batch *geom;
|
||||
switch (ob_ref.object->type) {
|
||||
case OB_GPENCIL_LEGACY:
|
||||
/* TODO ? */
|
||||
break;
|
||||
case OB_CURVES:
|
||||
geom = curves_sub_pass_setup(*prepass_curves_ps_, state.scene, ob_ref.object);
|
||||
prepass_curves_ps_->draw(geom, res_handle);
|
||||
break;
|
||||
case OB_GREASE_PENCIL:
|
||||
GreasePencil::draw_grease_pencil(
|
||||
*prepass_gpencil_ps_, grease_pencil_view, state.scene, ob_ref.object, res_handle);
|
||||
break;
|
||||
case OB_MESH:
|
||||
geom = DRW_cache_mesh_surface_get(ob_ref.object);
|
||||
prepass_mesh_ps_->draw(geom, res_handle);
|
||||
{
|
||||
/* TODO(fclem): This is against design. We should not sync depending on view position.
|
||||
* Eventually, add a bounding box display pass with some special culling phase. */
|
||||
|
||||
/* Display flat object as a line when view is orthogonal to them.
|
||||
* This fixes only the biggest case which is a plane in ortho view. */
|
||||
int flat_axis = 0;
|
||||
bool is_flat_object_viewed_from_side = ((state.rv3d->persp == RV3D_ORTHO) &&
|
||||
DRW_object_is_flat(ob_ref.object, &flat_axis) &&
|
||||
DRW_object_axis_orthogonal_to_view(ob_ref.object,
|
||||
flat_axis));
|
||||
if (is_flat_object_viewed_from_side) {
|
||||
geom = DRW_cache_mesh_edge_detection_get(ob_ref.object, nullptr);
|
||||
prepass_wire_ps_->draw(geom, res_handle);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OB_POINTCLOUD:
|
||||
/* Looks bad in wireframe mode. Could be relaxed if we draw a wireframe of some sort in
|
||||
* the future. */
|
||||
if (!state.is_wireframe_mode) {
|
||||
geom = point_cloud_sub_pass_setup(*prepass_pointcloud_ps_, ob_ref.object);
|
||||
prepass_pointcloud_ps_->draw(geom, res_handle);
|
||||
}
|
||||
break;
|
||||
case OB_VOLUME:
|
||||
geom = DRW_cache_volume_selection_surface_get(ob_ref.object);
|
||||
outline_prepass_ps_.draw(geom, res_handle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void draw(Resources &res, Manager &manager, View &view)
|
||||
{
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPU_debug_group_begin("Outline");
|
||||
|
||||
int2 render_size = int2(res.depth_tx.size());
|
||||
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
|
||||
tmp_depth_tx_.acquire(render_size, GPU_DEPTH24_STENCIL8, usage);
|
||||
object_id_tx_.acquire(render_size, GPU_R16UI, usage);
|
||||
|
||||
prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(tmp_depth_tx_),
|
||||
GPU_ATTACHMENT_TEXTURE(object_id_tx_));
|
||||
|
||||
manager.submit(outline_prepass_ps_, view);
|
||||
manager.submit(outline_resolve_ps_, view);
|
||||
|
||||
tmp_depth_tx_.release();
|
||||
object_id_tx_.release();
|
||||
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::draw::overlay
|
||||
@@ -181,6 +181,12 @@ class ShaderModule {
|
||||
ShaderPtr mesh_face_normal, mesh_face_normal_subdiv;
|
||||
ShaderPtr mesh_loop_normal, mesh_loop_normal_subdiv;
|
||||
ShaderPtr mesh_vert_normal;
|
||||
ShaderPtr outline_prepass_mesh;
|
||||
ShaderPtr outline_prepass_wire;
|
||||
ShaderPtr outline_prepass_curves;
|
||||
ShaderPtr outline_prepass_pointcloud;
|
||||
ShaderPtr outline_prepass_gpencil;
|
||||
ShaderPtr outline_detect = shader("overlay_outline_detect");
|
||||
|
||||
/** Selectable Shaders */
|
||||
ShaderPtr armature_sphere_outline;
|
||||
@@ -217,11 +223,20 @@ class ShaderModule {
|
||||
struct Resources : public select::SelectMap {
|
||||
ShaderModule &shaders;
|
||||
|
||||
Framebuffer overlay_fb = {"overlay_fb"};
|
||||
Framebuffer overlay_in_front_fb = {"overlay_in_front_fb"};
|
||||
/* Overlay Color. */
|
||||
Framebuffer overlay_color_only_fb = {"overlay_color_only_fb"};
|
||||
/* Overlay Color, Line Data. */
|
||||
Framebuffer overlay_line_only_fb = {"overlay_line_only_fb"};
|
||||
/* Depth, Overlay Color. */
|
||||
Framebuffer overlay_fb = {"overlay_fb"};
|
||||
/* Depth, Overlay Color, Line Data. */
|
||||
Framebuffer overlay_line_fb = {"overlay_line_fb"};
|
||||
/* Depth In-Front, Overlay Color. */
|
||||
Framebuffer overlay_in_front_fb = {"overlay_in_front_fb"};
|
||||
/* Depth In-Front, Overlay Color, Line Data. */
|
||||
Framebuffer overlay_line_in_front_fb = {"overlay_line_in_front_fb"};
|
||||
|
||||
/* Output Color. */
|
||||
Framebuffer overlay_output_fb = {"overlay_output_fb"};
|
||||
|
||||
/* Target containing line direction and data for line expansion and anti-aliasing. */
|
||||
|
||||
@@ -162,6 +162,33 @@ ShaderModule::ShaderModule(const SelectionType selection_type, const bool clippi
|
||||
info.storage_buf(0, Qualifier::READ, "uint", "vnor[]", Frequency::GEOMETRY);
|
||||
});
|
||||
|
||||
outline_prepass_mesh = shader(
|
||||
"overlay_outline_prepass_mesh", [](gpu::shader::ShaderCreateInfo &info) {
|
||||
shader_patch_common(info);
|
||||
info.additional_info("draw_object_infos_new", "overlay_outline_prepass");
|
||||
});
|
||||
outline_prepass_wire = shader(
|
||||
"overlay_outline_prepass_wire", [](gpu::shader::ShaderCreateInfo &info) {
|
||||
shader_patch_common(info);
|
||||
info.additional_info("draw_object_infos_new", "overlay_outline_prepass");
|
||||
});
|
||||
outline_prepass_curves = shader(
|
||||
"overlay_outline_prepass_curves", [](gpu::shader::ShaderCreateInfo &info) {
|
||||
shader_patch_common(info);
|
||||
info.additional_info("draw_hair_new", "draw_object_infos_new", "overlay_outline_prepass");
|
||||
});
|
||||
outline_prepass_pointcloud = shader(
|
||||
"overlay_outline_prepass_pointcloud", [](gpu::shader::ShaderCreateInfo &info) {
|
||||
shader_patch_common(info);
|
||||
info.additional_info(
|
||||
"draw_pointcloud_new", "draw_object_infos_new", "overlay_outline_prepass");
|
||||
});
|
||||
outline_prepass_gpencil = shader(
|
||||
"overlay_outline_prepass_gpencil", [](gpu::shader::ShaderCreateInfo &info) {
|
||||
shader_patch_common(info);
|
||||
info.additional_info("draw_gpencil_new", "draw_object_infos_new");
|
||||
});
|
||||
|
||||
/** Selectable Shaders */
|
||||
|
||||
armature_sphere_outline = selectable_shader(
|
||||
|
||||
@@ -16,13 +16,13 @@ GPU_SHADER_CREATE_INFO(overlay_outline_prepass)
|
||||
/* Using uint because 16bit uint can contain more ids than int. */
|
||||
.fragment_out(0, Type::UINT, "out_object_id")
|
||||
.fragment_source("overlay_outline_prepass_frag.glsl")
|
||||
.additional_info("draw_resource_handle", "draw_globals");
|
||||
.additional_info("draw_globals");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(overlay_outline_prepass_mesh)
|
||||
.do_static_compilation(true)
|
||||
.vertex_in(0, Type::VEC3, "pos")
|
||||
.vertex_source("overlay_outline_prepass_vert.glsl")
|
||||
.additional_info("draw_mesh", "overlay_outline_prepass")
|
||||
.additional_info("draw_mesh", "draw_resource_handle", "overlay_outline_prepass")
|
||||
.additional_info("draw_object_infos");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(overlay_outline_prepass_mesh_clipped)
|
||||
@@ -34,21 +34,20 @@ GPU_SHADER_INTERFACE_INFO(overlay_outline_prepass_wire_iface, "vert").flat(Type:
|
||||
GPU_SHADER_CREATE_INFO(overlay_outline_prepass_curves)
|
||||
.do_static_compilation(true)
|
||||
.vertex_source("overlay_outline_prepass_curves_vert.glsl")
|
||||
.additional_info("draw_hair", "overlay_outline_prepass")
|
||||
.additional_info("draw_hair", "draw_resource_handle", "overlay_outline_prepass")
|
||||
.additional_info("draw_object_infos");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(overlay_outline_prepass_curves_clipped)
|
||||
.do_static_compilation(true)
|
||||
.additional_info("overlay_outline_prepass_curves", "drw_clipped");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(overlay_outline_prepass_wire_common)
|
||||
.vertex_in(0, Type::VEC3, "pos")
|
||||
.additional_info("draw_mesh", "overlay_outline_prepass")
|
||||
.additional_info("draw_object_infos");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(overlay_outline_prepass_wire)
|
||||
.do_static_compilation(true)
|
||||
.additional_info("overlay_outline_prepass_wire_common")
|
||||
.additional_info("overlay_outline_prepass",
|
||||
"draw_object_infos",
|
||||
"draw_mesh",
|
||||
"draw_resource_handle")
|
||||
.vertex_in(0, Type::VEC3, "pos")
|
||||
.define("USE_GEOM")
|
||||
.vertex_out(overlay_outline_prepass_wire_iface)
|
||||
.geometry_layout(PrimitiveIn::LINES_ADJACENCY, PrimitiveOut::LINE_STRIP, 2)
|
||||
@@ -59,7 +58,11 @@ GPU_SHADER_CREATE_INFO(overlay_outline_prepass_wire)
|
||||
GPU_SHADER_CREATE_INFO(overlay_outline_prepass_wire_no_geom)
|
||||
.metal_backend_only(true)
|
||||
.do_static_compilation(true)
|
||||
.additional_info("overlay_outline_prepass_wire_common")
|
||||
.vertex_in(0, Type::VEC3, "pos")
|
||||
.additional_info("overlay_outline_prepass",
|
||||
"draw_object_infos",
|
||||
"draw_mesh",
|
||||
"draw_resource_handle")
|
||||
.vertex_source("overlay_outline_prepass_vert_no_geom.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(overlay_outline_prepass_wire_clipped)
|
||||
@@ -96,7 +99,7 @@ GPU_SHADER_CREATE_INFO(overlay_outline_prepass_gpencil_clipped)
|
||||
GPU_SHADER_CREATE_INFO(overlay_outline_prepass_pointcloud)
|
||||
.do_static_compilation(true)
|
||||
.vertex_source("overlay_outline_prepass_pointcloud_vert.glsl")
|
||||
.additional_info("draw_pointcloud", "overlay_outline_prepass")
|
||||
.additional_info("draw_pointcloud", "draw_resource_handle", "overlay_outline_prepass")
|
||||
.additional_info("draw_object_infos");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(overlay_outline_prepass_pointcloud_clipped)
|
||||
|
||||
@@ -5,11 +5,17 @@
|
||||
#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
|
||||
uint outline_colorid_get(void)
|
||||
{
|
||||
#ifdef OBINFO_NEW
|
||||
eObjectInfoFlag ob_flag = eObjectInfoFlag(floatBitsToUint(drw_infos[resource_id].infos.w));
|
||||
bool is_active = flag_test(ob_flag, OBJECT_ACTIVE);
|
||||
#else
|
||||
int flag = int(abs(ObjectInfo.w));
|
||||
bool is_active = (flag & DRW_BASE_ACTIVE) != 0;
|
||||
#endif
|
||||
|
||||
if (isTransform) {
|
||||
return 0u; /* colorTransform */
|
||||
|
||||
@@ -8,8 +8,13 @@
|
||||
|
||||
uint outline_colorid_get(void)
|
||||
{
|
||||
#ifdef OBINFO_NEW
|
||||
eObjectInfoFlag ob_flag = eObjectInfoFlag(floatBitsToUint(drw_infos[resource_id].infos.w));
|
||||
bool is_active = flag_test(ob_flag, OBJECT_ACTIVE);
|
||||
#else
|
||||
int flag = int(abs(ObjectInfo.w));
|
||||
bool is_active = (flag & DRW_BASE_ACTIVE) != 0;
|
||||
#endif
|
||||
|
||||
if (isTransform) {
|
||||
return 0u; /* colorTransform */
|
||||
|
||||
@@ -5,11 +5,17 @@
|
||||
#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_pointcloud_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
|
||||
uint outline_colorid_get(void)
|
||||
{
|
||||
#ifdef OBINFO_NEW
|
||||
eObjectInfoFlag ob_flag = eObjectInfoFlag(floatBitsToUint(drw_infos[resource_id].infos.w));
|
||||
bool is_active = flag_test(ob_flag, OBJECT_ACTIVE);
|
||||
#else
|
||||
int flag = int(abs(ObjectInfo.w));
|
||||
bool is_active = (flag & DRW_BASE_ACTIVE) != 0;
|
||||
#endif
|
||||
|
||||
if (isTransform) {
|
||||
return 0u; /* colorTransform */
|
||||
|
||||
@@ -4,11 +4,17 @@
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
|
||||
uint outline_colorid_get(void)
|
||||
{
|
||||
#ifdef OBINFO_NEW
|
||||
eObjectInfoFlag ob_flag = eObjectInfoFlag(floatBitsToUint(drw_infos[resource_id].infos.w));
|
||||
bool is_active = flag_test(ob_flag, OBJECT_ACTIVE);
|
||||
#else
|
||||
int flag = int(abs(ObjectInfo.w));
|
||||
bool is_active = (flag & DRW_BASE_ACTIVE) != 0;
|
||||
#endif
|
||||
|
||||
if (isTransform) {
|
||||
return 0u; /* colorTransform */
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma USE_SSBO_VERTEX_FETCH(LineList, 2)
|
||||
#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
|
||||
#define DISCARD_VERTEX \
|
||||
gl_Position = vec4(0.0); \
|
||||
@@ -12,8 +13,13 @@
|
||||
|
||||
uint outline_colorid_get(void)
|
||||
{
|
||||
#ifdef OBINFO_NEW
|
||||
eObjectInfoFlag ob_flag = eObjectInfoFlag(floatBitsToUint(drw_infos[resource_id].infos.w));
|
||||
bool is_active = flag_test(ob_flag, OBJECT_ACTIVE);
|
||||
#else
|
||||
int flag = int(abs(ObjectInfo.w));
|
||||
bool is_active = (flag & DRW_BASE_ACTIVE) != 0;
|
||||
#endif
|
||||
|
||||
if (isTransform) {
|
||||
return 0u; /* colorTransform */
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
|
||||
bool is_edge_sharpness_visible(float wd)
|
||||
{
|
||||
@@ -12,10 +13,17 @@ bool is_edge_sharpness_visible(float wd)
|
||||
|
||||
void wire_color_get(out vec3 rim_col, out vec3 wire_col)
|
||||
{
|
||||
#ifdef OBINFO_NEW
|
||||
eObjectInfoFlag ob_flag = eObjectInfoFlag(floatBitsToUint(drw_infos[resource_id].infos.w));
|
||||
bool is_selected = flag_test(ob_flag, OBJECT_SELECTED);
|
||||
bool is_from_set = flag_test(ob_flag, OBJECT_FROM_SET);
|
||||
bool is_active = flag_test(ob_flag, OBJECT_ACTIVE);
|
||||
#else
|
||||
int flag = int(abs(ObjectInfo.w));
|
||||
bool is_selected = (flag & DRW_BASE_SELECTED) != 0;
|
||||
bool is_from_set = (flag & DRW_BASE_FROM_SET) != 0;
|
||||
bool is_active = (flag & DRW_BASE_ACTIVE) != 0;
|
||||
#endif
|
||||
|
||||
if (is_from_set) {
|
||||
rim_col = colorWire.rgb;
|
||||
|
||||
@@ -171,8 +171,11 @@ GPU_SHADER_CREATE_INFO(draw_gpencil_new)
|
||||
.sampler(0, ImageType::FLOAT_BUFFER, "gp_pos_tx")
|
||||
.sampler(1, ImageType::FLOAT_BUFFER, "gp_col_tx")
|
||||
/* Per Object */
|
||||
.define("gpThicknessScale", "1.0") /* TODO(fclem): Replace with object info. */
|
||||
.push_constant(Type::FLOAT, "gpThicknessScale") /* TODO(fclem): Replace with object info. */
|
||||
.push_constant(Type::FLOAT, "gpThicknessWorldScale") /* TODO(fclem): Same as above. */
|
||||
.define("gpThicknessIsScreenSpace", "(gpThicknessWorldScale < 0.0)")
|
||||
/* Per Layer */
|
||||
.push_constant(Type::FLOAT, "gpThicknessOffset")
|
||||
.additional_info("draw_modelmat_new",
|
||||
"draw_resource_id_varying",
|
||||
"draw_view",
|
||||
|
||||
Reference in New Issue
Block a user