Overlay-Next: Initial implementation #107045

Closed
Clément Foucault wants to merge 28 commits from fclem/blender:overlay-next into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
46 changed files with 2633 additions and 53 deletions

View File

@ -2392,6 +2392,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
({"property": "use_full_frame_compositor"}, ("blender/blender/issues/88150", "#88150")),
({"property": "enable_eevee_next"}, ("blender/blender/issues/93220", "#93220")),
({"property": "enable_workbench_next"}, ("blender/blender/issues/101619", "#101619")),
({"property": "enable_overlay_next"}, ("blender/blender/issues/102179", "#102179")),
),
)

View File

@ -191,6 +191,7 @@ set(SRC
engines/gpencil/gpencil_shader_fx.c
engines/select/select_draw_utils.c
engines/select/select_engine.c
engines/select/select_instance.cc
engines/overlay/overlay_antialiasing.cc
engines/overlay/overlay_armature.cc
engines/overlay/overlay_background.cc
@ -206,6 +207,7 @@ set(SRC
engines/overlay/overlay_gpencil.cc
engines/overlay/overlay_grid.cc
engines/overlay/overlay_image.cc
engines/overlay/overlay_instance.cc
engines/overlay/overlay_lattice.cc
engines/overlay/overlay_metaball.cc
engines/overlay/overlay_mode_transfer.cc
@ -311,6 +313,7 @@ set(SRC
engines/select/select_engine.h
engines/select/select_private.h
engines/overlay/overlay_engine.h
engines/overlay/overlay_instance.hh
engines/overlay/overlay_private.hh
)
@ -598,6 +601,9 @@ set(GLSL_SRC
engines/select/shaders/select_id_vert.glsl
engines/select/shaders/select_id_frag.glsl
engines/select/shaders/select_lib.glsl
engines/select/select_shader_shared.hh
engines/basic/shaders/basic_conservative_depth_geom.glsl
engines/basic/shaders/basic_depth_vert.glsl

View File

@ -618,9 +618,6 @@ static void drw_shgroup_bone_envelope(ArmatureDrawContext *ctx,
/* Custom (geometry) */
extern "C" void drw_batch_cache_validate(Object *custom);
extern "C" void drw_batch_cache_generate_requested_delayed(Object *custom);
BLI_INLINE DRWCallBuffer *custom_bone_instance_shgroup(ArmatureDrawContext *ctx,
DRWShadingGroup *grp,
GPUBatch *custom_geom)

View File

@ -0,0 +1,104 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "DEG_depsgraph_query.h"
#include "DNA_camera_types.h"
#include "DNA_space_types.h"
#include "ED_view3d.h"
#include "UI_resources.h"
#include "draw_cache.h"
#include "draw_pass.hh"
#include "overlay_private.hh"
#include "overlay_shader_shared.h"
namespace blender::draw::overlay {
template<typename SelectEngineT> class Background {
using ResourcesT = Resources<SelectEngineT>;
private:
PassSimple bg_ps_ = {"Background"};
public:
void begin_sync(ResourcesT &res, const State &state)
{
DRWState pass_state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_BACKGROUND;
float4 color_override(0.0f, 0.0f, 0.0f, 0.0f);
int background_type;
if (DRW_state_is_opengl_render() && !DRW_state_draw_background()) {
background_type = BG_SOLID;
color_override[3] = 1.0f;
}
/*
else if (pd->space_type == SPACE_IMAGE) {
background_type = BG_SOLID_CHECKER;
}
else if (pd->space_type == SPACE_NODE) {
background_type = BG_MASK;
pass_state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_MUL;
}
*/
else if (!DRW_state_draw_background()) {
background_type = BG_CHECKER;
}
else if (state.v3d->shading.background_type == V3D_SHADING_BACKGROUND_WORLD &&
state.scene->world) {
background_type = BG_SOLID;
/* TODO(fclem): this is a scene referred linear color. we should convert
* it to display linear here. */
color_override = float4(UNPACK3(&state.scene->world->horr), 1.0f);
}
else if (state.v3d->shading.background_type == V3D_SHADING_BACKGROUND_VIEWPORT &&
state.v3d->shading.type <= OB_SOLID) {
background_type = BG_SOLID;
color_override = float4(UNPACK3(state.v3d->shading.background_color), 1.0f);
}
else {
switch (UI_GetThemeValue(TH_BACKGROUND_TYPE)) {
case TH_BACKGROUND_GRADIENT_LINEAR:
background_type = BG_GRADIENT;
break;
case TH_BACKGROUND_GRADIENT_RADIAL:
background_type = BG_RADIAL;
break;
default:
case TH_BACKGROUND_SINGLE_COLOR:
background_type = BG_SOLID;
break;
}
}
bg_ps_.init();
bg_ps_.state_set(pass_state);
bg_ps_.shader_set(res.shaders.background_fill);
bg_ps_.bind_ubo("globalsBlock", &res.globals_buf);
bg_ps_.bind_texture("colorBuffer", &res.color_render_tx);
bg_ps_.bind_texture("depthBuffer", &res.depth_tx);
bg_ps_.push_constant("colorOverride", color_override);
bg_ps_.push_constant("bgType", background_type);
bg_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
if (state.clipping_state != 0 && state.rv3d != nullptr && state.rv3d->clipbb != nullptr) {
bg_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA | DRW_STATE_CULL_BACK);
bg_ps_.shader_set(res.shaders.background_clip_bound);
bg_ps_.push_constant("ucolor", res.theme_settings.color_clipping_border);
bg_ps_.push_constant("boundbox", &state.rv3d->clipbb->vec[0][0], 8);
bg_ps_.draw(DRW_cache_cube_get());
}
}
void draw(ResourcesT &res, Manager &manager)
{
GPU_framebuffer_bind(res.overlay_color_only_fb);
manager.submit(bg_ps_);
}
};
} // namespace blender::draw::overlay

View File

@ -0,0 +1,128 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "DNA_object_types.h"
#include "draw_pass.hh"
#include "draw_shader_shared.h"
#include "overlay_private.hh"
#include "overlay_shape.hh"
namespace blender::draw::overlay {
template<typename SelectEngineT> class Empties {
using SelectID = typename SelectEngineT::ID;
using ResourcesT = Resources<SelectEngineT>;
using EmptyInstanceBuf = ShapeInstanceBuf<SelectEngineT, ExtraInstanceData>;
private:
PassSimple empty_ps_ = {"Empties"};
PassSimple empty_in_front_ps_ = {"Empties_In_front"};
struct CallBuffers {
EmptyInstanceBuf plain_axes_buf = {"plain_axes_buf"};
EmptyInstanceBuf single_arrow_buf = {"single_arrow_buf"};
EmptyInstanceBuf cube_buf = {"cube_buf"};
EmptyInstanceBuf circle_buf = {"circle_buf"};
EmptyInstanceBuf sphere_buf = {"sphere_buf"};
EmptyInstanceBuf cone_buf = {"cone_buf"};
EmptyInstanceBuf arrows_buf = {"arrows_buf"};
EmptyInstanceBuf image_buf = {"image_buf"};
} call_buffers_[2];
public:
void begin_sync()
{
for (int i = 0; i < 2; i++) {
call_buffers_[i].plain_axes_buf.clear();
call_buffers_[i].single_arrow_buf.clear();
call_buffers_[i].cube_buf.clear();
call_buffers_[i].circle_buf.clear();
call_buffers_[i].sphere_buf.clear();
call_buffers_[i].cone_buf.clear();
call_buffers_[i].arrows_buf.clear();
call_buffers_[i].image_buf.clear();
}
}
void object_sync(const ObjectRef &ob_ref, ResourcesT &res, const State &state)
{
CallBuffers &call_bufs = call_buffers_[int((ob_ref.object->dtx & OB_DRAW_IN_FRONT) != 0)];
float4 color = res.object_wire_color(ob_ref, state);
ExtraInstanceData data(
float4x4(ob_ref.object->object_to_world), color, ob_ref.object->empty_drawsize);
const SelectID select_id = res.select_id(ob_ref);
switch (ob_ref.object->empty_drawtype) {
case OB_PLAINAXES:
call_bufs.plain_axes_buf.append(data, select_id);
break;
case OB_SINGLE_ARROW:
call_bufs.single_arrow_buf.append(data, select_id);
break;
case OB_CUBE:
call_bufs.cube_buf.append(data, select_id);
break;
case OB_CIRCLE:
call_bufs.circle_buf.append(data, select_id);
break;
case OB_EMPTY_SPHERE:
call_bufs.sphere_buf.append(data, select_id);
break;
case OB_EMPTY_CONE:
call_bufs.cone_buf.append(data, select_id);
break;
case OB_ARROWS:
call_bufs.arrows_buf.append(data, select_id);
break;
case OB_EMPTY_IMAGE:
/* This only show the frame. See OVERLAY_image_empty_cache_populate() for the image. */
call_bufs.image_buf.append(data, select_id);
break;
}
}
void end_sync(ResourcesT &res, ShapeCache &shapes, const State &state)
{
auto init_pass = [&](PassSimple &pass, CallBuffers &call_bufs) {
pass.init();
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
state.clipping_state);
pass.shader_set(res.shaders.extra_shape);
pass.bind_ubo("globalsBlock", &res.globals_buf);
res.select_bind(pass);
call_bufs.plain_axes_buf.end_sync(pass, shapes.plain_axes);
call_bufs.single_arrow_buf.end_sync(pass, shapes.single_arrow);
call_bufs.cube_buf.end_sync(pass, shapes.cube);
call_bufs.circle_buf.end_sync(pass, shapes.circle);
call_bufs.sphere_buf.end_sync(pass, shapes.empty_sphere);
call_bufs.cone_buf.end_sync(pass, shapes.empty_cone);
call_bufs.arrows_buf.end_sync(pass, shapes.arrows);
call_bufs.image_buf.end_sync(pass, shapes.quad_wire);
};
init_pass(empty_ps_, call_buffers_[0]);
init_pass(empty_in_front_ps_, call_buffers_[1]);
}
void draw(ResourcesT &res, Manager &manager, View &view)
{
GPU_framebuffer_bind(res.overlay_line_fb);
manager.submit(empty_ps_, view);
}
void draw_in_front(ResourcesT &res, Manager &manager, View &view)
{
GPU_framebuffer_bind(res.overlay_line_in_front_fb);
manager.submit(empty_in_front_ps_, view);
}
};
} // namespace blender::draw::overlay

View File

@ -20,11 +20,20 @@
#include "BKE_object.h"
#include "BKE_paint.h"
#include "GPU_capabilities.h"
#include "DNA_space_types.h"
#include "draw_manager.hh"
#include "overlay_instance.hh"
#include "overlay_engine.h"
#include "overlay_private.hh"
using namespace blender::draw;
using Instance = blender::draw::overlay::Instance<>;
/* -------------------------------------------------------------------- */
/** \name Engine Callbacks
* \{ */
@ -46,8 +55,7 @@ static void OVERLAY_engine_init(void *vedata)
/* Allocate instance. */
if (data->instance == nullptr) {
data->instance = static_cast<OVERLAY_Instance *>(
MEM_callocN(sizeof(*data->instance), __func__));
data->instance = new Instance();
}
OVERLAY_PrivateData *pd = stl->pd;
@ -729,13 +737,75 @@ static void OVERLAY_draw_scene(void *vedata)
static void OVERLAY_engine_free()
{
OVERLAY_shader_free();
overlay::shader_module_free();
}
static void OVERLAY_instance_free(void *instance_)
{
OVERLAY_Instance *instance = (OVERLAY_Instance *)instance_;
DRW_UBO_FREE_SAFE(instance->grid_ubo);
MEM_freeN(instance);
auto *instance = (Instance *)instance_;
if (instance != nullptr) {
delete instance;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Engine Instance
* \{ */
static void OVERLAY_next_engine_init(void *vedata)
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
OVERLAY_Data *ved = reinterpret_cast<OVERLAY_Data *>(vedata);
if (ved->instance == nullptr) {
ved->instance = new Instance();
}
reinterpret_cast<Instance *>(ved->instance)->init();
}
static void OVERLAY_next_cache_init(void *vedata)
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
reinterpret_cast<Instance *>(reinterpret_cast<OVERLAY_Data *>(vedata)->instance)->begin_sync();
}
static void OVERLAY_next_cache_populate(void *vedata, Object *object)
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
ObjectRef ref;
ref.object = object;
ref.dupli_object = DRW_object_get_dupli(object);
ref.dupli_parent = DRW_object_get_dupli_parent(object);
reinterpret_cast<Instance *>(reinterpret_cast<OVERLAY_Data *>(vedata)->instance)
->object_sync(ref, *DRW_manager_get());
}
static void OVERLAY_next_cache_finish(void *vedata)
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
reinterpret_cast<Instance *>(reinterpret_cast<OVERLAY_Data *>(vedata)->instance)->end_sync();
}
static void OVERLAY_next_draw_scene(void *vedata)
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
reinterpret_cast<Instance *>(reinterpret_cast<OVERLAY_Data *>(vedata)->instance)
->draw(*DRW_manager_get());
}
/** \} */
@ -764,6 +834,24 @@ DrawEngineType draw_engine_overlay_type = {
nullptr,
};
DrawEngineType draw_engine_overlay_next_type = {
nullptr,
nullptr,
N_("Overlay"),
&overlay_data_size,
&OVERLAY_next_engine_init,
nullptr,
&OVERLAY_instance_free,
&OVERLAY_next_cache_init,
&OVERLAY_next_cache_populate,
&OVERLAY_next_cache_finish,
&OVERLAY_next_draw_scene,
nullptr,
nullptr,
nullptr,
nullptr,
};
/** \} */
#undef SELECT_ENGINE

View File

@ -12,6 +12,7 @@ extern "C" {
#endif
extern DrawEngineType draw_engine_overlay_type;
extern DrawEngineType draw_engine_overlay_next_type;
#ifdef __cplusplus
}

View File

@ -17,8 +17,13 @@
#include "UI_resources.h"
#include "overlay_instance.hh"
#include "overlay_private.hh"
using namespace blender::draw;
using Instance = blender::draw::overlay::Instance<>;
BLI_STATIC_ASSERT(SI_GRID_STEPS_LEN == OVERLAY_GRID_STEPS_LEN, "")
void OVERLAY_grid_init(OVERLAY_Data *vedata)
@ -225,10 +230,11 @@ void OVERLAY_grid_cache_init(OVERLAY_Data *ved)
return;
}
if (ved->instance->grid_ubo == nullptr) {
ved->instance->grid_ubo = GPU_uniformbuf_create(sizeof(OVERLAY_GridData));
GPUUniformBuf *&grid_ubo = reinterpret_cast<Instance *>(ved->instance)->grid_ubo;
if (grid_ubo == nullptr) {
grid_ubo = GPU_uniformbuf_create(sizeof(OVERLAY_GridData));
}
GPU_uniformbuf_update(ved->instance->grid_ubo, &pd->grid_data);
GPU_uniformbuf_update(grid_ubo, &pd->grid_data);
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA;
DRW_PASS_CREATE(psl->grid_ps, state);
@ -258,7 +264,7 @@ void OVERLAY_grid_cache_init(OVERLAY_Data *ved)
/* Create 3 quads to render ordered transparency Z axis */
grp = DRW_shgroup_create(sh, psl->grid_ps);
DRW_shgroup_uniform_block(grp, "grid_buf", ved->instance->grid_ubo);
DRW_shgroup_uniform_block(grp, "grid_buf", grid_ubo);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &dtxl->depth);

View File

@ -0,0 +1,211 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "DEG_depsgraph_query.h"
#include "DNA_camera_types.h"
#include "DNA_space_types.h"
#include "ED_view3d.h"
#include "draw_cache.h"
#include "draw_pass.hh"
#include "overlay_private.hh"
#include "overlay_shader_shared.h"
namespace blender::draw::overlay {
template<typename SelectEngineT> class Grid {
using ResourcesT = Resources<SelectEngineT>;
private:
UniformBuffer<OVERLAY_GridData> data_;
PassSimple grid_ps_ = {"grid_ps_"};
float3 grid_axes_ = float3(0.0f);
float3 zplane_axes_ = float3(0.0f);
OVERLAY_GridBits grid_flag_ = OVERLAY_GridBits(0);
OVERLAY_GridBits zneg_flag_ = OVERLAY_GridBits(0);
OVERLAY_GridBits zpos_flag_ = OVERLAY_GridBits(0);
bool enabled_ = false;
public:
void update_ubo(const State &state, const View &view)
{
float grid_steps[SI_GRID_STEPS_LEN] = {
0.001f, 0.01f, 0.1f, 1.0f, 10.0f, 100.0f, 1000.0f, 10000.0f};
float grid_steps_y[SI_GRID_STEPS_LEN] = {0.0f}; /* When zero, use value from grid_steps. */
data_.line_size = max_ff(0.0f, U.pixelsize - 1.0f) * 0.5f;
/* Default, nothing is drawn. */
grid_flag_ = zneg_flag_ = zpos_flag_ = OVERLAY_GridBits(0);
const View3D *v3d = state.v3d;
const RegionView3D *rv3d = state.rv3d;
const bool show_axis_x = (state.v3d_gridflag & V3D_SHOW_X) != 0;
const bool show_axis_y = (state.v3d_gridflag & V3D_SHOW_Y) != 0;
const bool show_axis_z = (state.v3d_gridflag & V3D_SHOW_Z) != 0;
const bool show_floor = (state.v3d_gridflag & V3D_SHOW_FLOOR) != 0;
const bool show_ortho_grid = (state.v3d_gridflag & V3D_SHOW_ORTHO_GRID) != 0;
const bool show_any = show_axis_x || show_axis_y || show_axis_z || show_floor ||
show_ortho_grid;
enabled_ = !state.hide_overlays && show_any;
if (!enabled_) {
return;
}
/* If perspective view or non-axis aligned view. */
if (view.is_persp() || rv3d->view == RV3D_VIEW_USER) {
if (show_axis_x) {
grid_flag_ |= PLANE_XY | SHOW_AXIS_X;
}
if (show_axis_y) {
grid_flag_ |= PLANE_XY | SHOW_AXIS_Y;
}
if (show_floor) {
grid_flag_ |= PLANE_XY | SHOW_GRID;
}
}
else {
if (show_ortho_grid && ELEM(rv3d->view, RV3D_VIEW_RIGHT, RV3D_VIEW_LEFT)) {
grid_flag_ = PLANE_YZ | SHOW_AXIS_Y | SHOW_AXIS_Z | SHOW_GRID | GRID_BACK;
}
else if (show_ortho_grid && ELEM(rv3d->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) {
grid_flag_ = PLANE_XY | SHOW_AXIS_X | SHOW_AXIS_Y | SHOW_GRID | GRID_BACK;
}
else if (show_ortho_grid && ELEM(rv3d->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK)) {
grid_flag_ = PLANE_XZ | SHOW_AXIS_X | SHOW_AXIS_Z | SHOW_GRID | GRID_BACK;
}
}
grid_axes_[0] = float((grid_flag_ & (PLANE_XZ | PLANE_XY)) != 0);
grid_axes_[1] = float((grid_flag_ & (PLANE_YZ | PLANE_XY)) != 0);
grid_axes_[2] = float((grid_flag_ & (PLANE_YZ | PLANE_XZ)) != 0);
/* Z axis if needed */
if (((rv3d->view == RV3D_VIEW_USER) || (rv3d->persp != RV3D_ORTHO)) && show_axis_z) {
zpos_flag_ = SHOW_AXIS_Z;
float3 zvec = -float3(view.viewinv()[2]);
float3 campos = float3(view.viewinv()[3]);
/* z axis : chose the most facing plane */
if (fabsf(zvec[0]) < fabsf(zvec[1])) {
zpos_flag_ |= PLANE_XZ;
}
else {
zpos_flag_ |= PLANE_YZ;
}
zneg_flag_ = zpos_flag_;
/* Perspective: If camera is below floor plane, we switch clipping.
* Orthographic: If eye vector is looking up, we switch clipping. */
if ((view.is_persp() && (campos[2] > 0.0f)) || (!view.is_persp() && (zvec[2] < 0.0f))) {
zpos_flag_ |= CLIP_ZPOS;
zneg_flag_ |= CLIP_ZNEG;
}
else {
zpos_flag_ |= CLIP_ZNEG;
zneg_flag_ |= CLIP_ZPOS;
}
zplane_axes_[0] = float((zpos_flag_ & (PLANE_XZ | PLANE_XY)) != 0);
zplane_axes_[1] = float((zpos_flag_ & (PLANE_YZ | PLANE_XY)) != 0);
zplane_axes_[2] = float((zpos_flag_ & (PLANE_YZ | PLANE_XZ)) != 0);
}
else {
zneg_flag_ = zpos_flag_ = CLIP_ZNEG | CLIP_ZPOS;
}
float dist;
if (rv3d->persp == RV3D_CAMOB && v3d->camera && v3d->camera->type == OB_CAMERA) {
Object *camera_object = DEG_get_evaluated_object(state.depsgraph, v3d->camera);
dist = ((Camera *)(camera_object->data))->clip_end;
grid_flag_ |= GRID_CAMERA;
zneg_flag_ |= GRID_CAMERA;
zpos_flag_ |= GRID_CAMERA;
}
else {
dist = v3d->clip_end;
}
if (view.is_persp()) {
data_.size = float4(dist);
}
else {
float viewdist = 1.0f / min_ff(fabsf(view.winmat()[0][0]), fabsf(view.winmat()[1][1]));
data_.size = float4(viewdist * dist);
}
data_.distance = dist / 2.0f;
ED_view3d_grid_steps(state.scene, v3d, rv3d, grid_steps);
if ((v3d->flag & (V3D_XR_SESSION_SURFACE | V3D_XR_SESSION_MIRROR)) != 0) {
/* The calculations for the grid parameters assume that the view matrix has no scale
* component, which may not be correct if the user is "shrunk" or "enlarged" by zooming in or
* out. Therefore, we need to compensate the values here. */
/* Assumption is uniform scaling (all column vectors are of same length). */
float viewinvscale = len_v3(view.viewinv()[0]);
data_.distance *= viewinvscale;
}
/* Convert to UBO alignment. */
for (int i = 0; i < SI_GRID_STEPS_LEN; i++) {
data_.steps[i][0] = grid_steps[i];
data_.steps[i][1] = (grid_steps_y[i] != 0.0f) ? grid_steps_y[i] : grid_steps[i];
}
data_.push_update();
}
void begin_sync(ResourcesT &res, const State &state, const View &view)
{
this->update_ubo(state, view);
if (!enabled_) {
return;
}
grid_ps_.init();
grid_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA);
grid_ps_.shader_set(res.shaders.grid);
grid_ps_.bind_ubo("grid_buf", &data_);
grid_ps_.bind_ubo("globalsBlock", &res.globals_buf);
grid_ps_.bind_texture("depth_tx", &res.depth_tx);
if (zneg_flag_ & SHOW_AXIS_Z) {
grid_ps_.push_constant("grid_flag", zneg_flag_);
grid_ps_.push_constant("plane_axes", zplane_axes_);
grid_ps_.draw(DRW_cache_grid_get());
}
if (grid_flag_) {
grid_ps_.push_constant("grid_flag", grid_flag_);
grid_ps_.push_constant("plane_axes", grid_axes_);
grid_ps_.draw(DRW_cache_grid_get());
}
if (zpos_flag_ & SHOW_AXIS_Z) {
grid_ps_.push_constant("grid_flag", zpos_flag_);
grid_ps_.push_constant("plane_axes", zplane_axes_);
grid_ps_.draw(DRW_cache_grid_get());
}
}
void draw(ResourcesT &res, Manager &manager, View &view)
{
if (!enabled_) {
return;
}
GPU_framebuffer_bind(res.overlay_color_only_fb);
manager.submit(grid_ps_, view);
}
};
} // namespace blender::draw::overlay

View File

@ -0,0 +1,250 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#include "draw_debug.hh"
#include "overlay_instance.hh"
namespace blender::draw::overlay {
template<typename T> void Instance<T>::init()
{
/* TODO(fclem): Remove DRW global usage. */
const DRWContextState *ctx = DRW_context_state_get();
/* Was needed by `object_wire_theme_id()` when doing the port. Not sure if needed nowadays. */
BKE_view_layer_synced_ensure(ctx->scene, ctx->view_layer);
state.depsgraph = ctx->depsgraph;
state.view_layer = ctx->view_layer;
state.scene = ctx->scene;
state.v3d = ctx->v3d;
state.rv3d = ctx->rv3d;
state.active_base = BKE_view_layer_active_base_get(ctx->view_layer);
state.object_mode = ctx->object_mode;
state.pixelsize = U.pixelsize;
state.ctx_mode = CTX_data_mode_enum_ex(ctx->object_edit, ctx->obact, ctx->object_mode);
state.clear_in_front = (state.v3d->shading.type != OB_SOLID);
state.use_in_front = (state.v3d->shading.type <= OB_SOLID) ||
BKE_scene_uses_blender_workbench(state.scene);
state.is_wireframe_mode = (state.v3d->shading.type == OB_WIRE);
state.hide_overlays = (state.v3d->flag2 & V3D_HIDE_OVERLAYS) != 0;
state.xray_enabled = XRAY_ACTIVE(state.v3d);
state.xray_enabled_and_not_wire = state.xray_enabled && (state.v3d->shading.type > OB_WIRE);
state.xray_opacity = XRAY_ALPHA(state.v3d);
state.cfra = DEG_get_ctime(state.depsgraph);
state.clipping_state = RV3D_CLIPPING_ENABLED(state.v3d, state.rv3d) ? DRW_STATE_CLIP_PLANES :
DRWState(0);
if (!state.hide_overlays) {
state.overlay = state.v3d->overlay;
state.v3d_flag = state.v3d->flag;
state.v3d_gridflag = state.v3d->gridflag;
}
else {
memset(&state.overlay, 0, sizeof(state.overlay));
state.v3d_flag = 0;
state.v3d_gridflag = 0;
state.overlay.flag = V3D_OVERLAY_HIDE_TEXT | V3D_OVERLAY_HIDE_MOTION_PATHS |
V3D_OVERLAY_HIDE_BONES | V3D_OVERLAY_HIDE_OBJECT_XTRAS |
V3D_OVERLAY_HIDE_OBJECT_ORIGINS;
state.overlay.wireframe_threshold = state.v3d->overlay.wireframe_threshold;
state.overlay.wireframe_opacity = state.v3d->overlay.wireframe_opacity;
}
/* TODO(fclem): Remove DRW global usage. */
resources.globals_buf = G_draw.block_ubo;
resources.theme_settings = G_draw.block;
}
template<typename T> void Instance<T>::begin_sync()
{
const DRWView *view_legacy = DRW_view_default_get();
View view("OverlayView", view_legacy);
resources.begin_sync();
background.begin_sync(resources, state);
prepass.begin_sync(resources, state);
empties.begin_sync();
metaballs.begin_sync();
grid.begin_sync(resources, state, view);
}
template<typename T> void Instance<T>::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 */
if (needs_prepass) {
switch (ob_ref.object->type) {
case OB_MESH:
case OB_SURF:
case OB_CURVES:
case OB_FONT:
case OB_CURVES_LEGACY:
prepass.object_sync(manager, ob_ref, resources);
break;
}
}
if (in_edit_mode && !state.hide_overlays) {
switch (ob_ref.object->type) {
case OB_MESH:
break;
case OB_ARMATURE:
break;
case OB_CURVES_LEGACY:
break;
case OB_SURF:
break;
case OB_LATTICE:
break;
case OB_MBALL:
metaballs.edit_object_sync(ob_ref, resources);
break;
case OB_FONT:
break;
case OB_CURVES:
break;
}
}
if (!state.hide_overlays) {
switch (ob_ref.object->type) {
case OB_EMPTY:
empties.object_sync(ob_ref, resources, state);
break;
case OB_ARMATURE:
break;
case OB_MBALL:
if (!in_edit_mode) {
metaballs.object_sync(ob_ref, resources, state);
}
break;
case OB_GPENCIL_LEGACY:
break;
}
}
}
template<typename T> void Instance<T>::end_sync()
{
resources.end_sync();
metaballs.end_sync(resources, shapes, state);
empties.end_sync(resources, shapes, state);
}
template<typename T> void Instance<T>::draw(Manager &manager)
{
resources.depth_tx.wrap(DRW_viewport_texture_list_get()->depth);
resources.depth_in_front_tx.wrap(DRW_viewport_texture_list_get()->depth_in_front);
resources.color_overlay_tx.wrap(DRW_viewport_texture_list_get()->color_overlay);
resources.color_render_tx.wrap(DRW_viewport_texture_list_get()->color);
int2 render_size = int2(resources.depth_tx.size());
const DRWView *view_legacy = DRW_view_default_get();
View view("OverlayView", view_legacy);
/* TODO: Better semantical switch? */
if (!resources.color_overlay_tx.is_valid()) {
/* Likely to be the selection case. Allocate dummy texture and bind only depth buffer. */
resources.line_tx.acquire(int2(1, 1), GPU_RGBA8);
resources.color_overlay_alloc_tx.acquire(int2(1, 1), GPU_SRGB8_A8);
resources.color_render_alloc_tx.acquire(int2(1, 1), GPU_SRGB8_A8);
resources.color_overlay_tx.wrap(resources.color_overlay_alloc_tx);
resources.color_render_tx.wrap(resources.color_render_alloc_tx);
resources.overlay_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
resources.overlay_line_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
/* Create it but shouldn't even be used. */
resources.overlay_color_only_fb.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx));
}
else {
resources.line_tx.acquire(render_size, GPU_RGBA8);
resources.overlay_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx),
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx));
resources.overlay_line_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx),
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx),
GPU_ATTACHMENT_TEXTURE(resources.line_tx));
resources.overlay_color_only_fb.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx));
}
/* TODO(fclem): Remove mandatory allocation. */
if (!resources.depth_in_front_tx.is_valid()) {
resources.depth_in_front_alloc_tx.acquire(render_size, GPU_DEPTH_COMPONENT24);
resources.depth_in_front_tx.wrap(resources.depth_in_front_alloc_tx);
}
resources.overlay_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_in_front_tx),
GPU_ATTACHMENT_TEXTURE(resources.color_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.line_tx));
GPU_framebuffer_bind(resources.overlay_color_only_fb);
float4 clear_color(0.0f);
GPU_framebuffer_clear_color(resources.overlay_color_only_fb, clear_color);
prepass.draw(resources, manager, view);
prepass.draw_in_front(resources, manager, view);
background.draw(resources, manager);
empties.draw(resources, manager, view);
metaballs.draw(resources, manager, view);
grid.draw(resources, manager, view);
empties.draw_in_front(resources, manager, view);
metaballs.draw_in_front(resources, manager, view);
// anti_aliasing.draw(resources, manager, view);
resources.line_tx.release();
resources.depth_in_front_alloc_tx.release();
resources.color_overlay_alloc_tx.release();
resources.color_render_alloc_tx.release();
resources.read_result();
}
/* Instantiation. */
template void Instance<>::init();
template void Instance<>::begin_sync();
template void Instance<>::object_sync(ObjectRef &ob_ref, Manager &manager);
template void Instance<>::end_sync();
template void Instance<>::draw(Manager &manager);
template void Instance<select::Instance>::init();
template void Instance<select::Instance>::begin_sync();
template void Instance<select::Instance>::object_sync(ObjectRef &ob_ref, Manager &manager);
template void Instance<select::Instance>::end_sync();
template void Instance<select::Instance>::draw(Manager &manager);
} // namespace blender::draw::overlay
/* TODO(fclem): Move elsewhere. */
BoneInstanceData::BoneInstanceData(Object *ob,
const float *pos,
const float radius,
const float color[4])
{
/* TODO(fclem): Use C++ math API. */
mul_v3_v3fl(this->mat[0], ob->object_to_world[0], radius);
mul_v3_v3fl(this->mat[1], ob->object_to_world[1], radius);
mul_v3_v3fl(this->mat[2], ob->object_to_world[2], radius);
mul_v3_m4v3(this->mat[3], ob->object_to_world, pos);
/* WATCH: Reminder, alpha is wire-size. */
OVERLAY_bone_instance_data_set_color(this, color);
}

View File

@ -0,0 +1,102 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "draw_manager.hh"
#include "overlay_background.hh"
#include "overlay_empty.hh"
#include "overlay_grid.hh"
#include "overlay_metaball.hh"
#include "overlay_prepass.hh"
#include "overlay_shape.hh"
#include "../select/select_instance.hh"
namespace blender::draw::overlay {
template<
/* Selection engine reuse most of the Overlay engine by creating selection IDs for each
* selectable component and using a special shaders for drawing.
* Making the select engine templated makes it easier to phase out any overhead of the
* selection for the regular non-selection case.*/
typename SelectEngineT = select::InstanceDummy>
class Instance {
public:
/* WORKAROUND: Legacy. Move to grid pass. */
GPUUniformBuf *grid_ubo = nullptr;
ShapeCache shapes;
/** Global types. */
Resources<SelectEngineT> resources = {overlay::ShaderModule<SelectEngineT>::module_get()};
State state;
/** Overlay types. */
Background<SelectEngineT> background;
Prepass<SelectEngineT> prepass;
Metaballs<SelectEngineT> metaballs;
Empties<SelectEngineT> empties;
Grid<SelectEngineT> grid;
~Instance()
{
DRW_UBO_FREE_SAFE(grid_ubo);
}
void init();
void begin_sync();
void object_sync(ObjectRef &ob_ref, Manager &manager);
void end_sync();
void draw(Manager &manager);
private:
bool object_is_edit_mode(const Object *ob)
{
if (DRW_object_is_in_edit_mode(ob)) {
/* Also check for context mode as the object mode is not 100% reliable. (see T72490) */
switch (ob->type) {
case OB_MESH:
return state.ctx_mode == CTX_MODE_EDIT_MESH;
case OB_ARMATURE:
return state.ctx_mode == CTX_MODE_EDIT_ARMATURE;
case OB_CURVES_LEGACY:
return state.ctx_mode == CTX_MODE_EDIT_CURVE;
case OB_SURF:
return state.ctx_mode == CTX_MODE_EDIT_SURFACE;
case OB_LATTICE:
return state.ctx_mode == CTX_MODE_EDIT_LATTICE;
case OB_MBALL:
return state.ctx_mode == CTX_MODE_EDIT_METABALL;
case OB_FONT:
return state.ctx_mode == CTX_MODE_EDIT_TEXT;
case OB_CURVES:
return state.ctx_mode == CTX_MODE_EDIT_CURVES;
case OB_POINTCLOUD:
case OB_VOLUME:
/* No edit mode yet. */
return false;
}
}
return false;
}
};
/* Instantiation. */
extern template void Instance<>::init();
extern template void Instance<>::begin_sync();
extern template void Instance<>::object_sync(ObjectRef &ob_ref, Manager &manager);
extern template void Instance<>::end_sync();
extern template void Instance<>::draw(Manager &manager);
extern template void Instance<select::Instance>::init();
extern template void Instance<select::Instance>::begin_sync();
extern template void Instance<select::Instance>::object_sync(ObjectRef &ob_ref, Manager &manager);
extern template void Instance<select::Instance>::end_sync();
extern template void Instance<select::Instance>::draw(Manager &manager);
} // namespace blender::draw::overlay

View File

@ -0,0 +1,116 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "DEG_depsgraph_query.h"
#include "DNA_space_types.h"
#include "ED_mball.h"
#include "ED_view3d.h"
#include "UI_resources.h"
#include "draw_cache.h"
#include "draw_pass.hh"
#include "overlay_private.hh"
#include "overlay_shader_shared.h"
namespace blender::draw::overlay {
template<typename SelectEngineT> class Metaballs {
using SelectID = typename SelectEngineT::ID;
using ResourcesT = Resources<SelectEngineT>;
using SphereOutlineInstanceBuf = ShapeInstanceBuf<SelectEngineT, BoneInstanceData>;
private:
PassSimple metaball_ps_ = {"MetaBalls"};
PassSimple metaball_in_front_ps_ = {"MetaBalls_In_front"};
SphereOutlineInstanceBuf circle_buf_ = {"metaball_data_buf"};
SphereOutlineInstanceBuf circle_in_front_buf_ = {"metaball_data_buf"};
public:
void begin_sync()
{
circle_buf_.clear();
circle_in_front_buf_.clear();
}
void edit_object_sync(const ObjectRef &ob_ref, ResourcesT &res)
{
SphereOutlineInstanceBuf &circle_buf = (ob_ref.object->dtx & OB_DRAW_IN_FRONT) != 0 ?
circle_in_front_buf_ :
circle_buf_;
MetaBall *mb = static_cast<MetaBall *>(ob_ref.object->data);
const float *color;
const float *col_radius = res.theme_settings.color_mball_radius;
const float *col_radius_select = res.theme_settings.color_mball_radius_select;
const float *col_stiffness = res.theme_settings.color_mball_stiffness;
const float *col_stiffness_select = res.theme_settings.color_mball_stiffness_select;
LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) {
const bool is_selected = (ml->flag & SELECT) != 0;
const bool is_scale_radius = (ml->flag & MB_SCALE_RAD) != 0;
float stiffness_radius = ml->rad * atanf(ml->s) / float(M_PI_2);
const SelectID radius_id = res.select_id(ob_ref, MBALLSEL_RADIUS);
color = (is_selected && is_scale_radius) ? col_radius_select : col_radius;
circle_buf.append({ob_ref.object, &ml->x, ml->rad, color}, radius_id);
const SelectID stiff_id = res.select_id(ob_ref, MBALLSEL_STIFF);
color = (is_selected && !is_scale_radius) ? col_stiffness_select : col_stiffness;
circle_buf.append({ob_ref.object, &ml->x, stiffness_radius, color}, stiff_id);
}
}
void object_sync(const ObjectRef &ob_ref, ResourcesT &res, const State &state)
{
SphereOutlineInstanceBuf &circle_buf = (ob_ref.object->dtx & OB_DRAW_IN_FRONT) != 0 ?
circle_in_front_buf_ :
circle_buf_;
MetaBall *mb = static_cast<MetaBall *>(ob_ref.object->data);
const float4 &color = res.object_wire_color(ob_ref, state);
const SelectID select_id = res.select_id(ob_ref);
LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) {
/* Draw radius only. */
circle_buf.append({ob_ref.object, &ml->x, ml->rad, color}, select_id);
}
}
void end_sync(ResourcesT &res, ShapeCache &shapes, const State &state)
{
auto init_pass = [&](PassSimple &pass, SphereOutlineInstanceBuf &call_buf) {
pass.init();
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
state.clipping_state);
/* NOTE: Use armature sphere outline shader to have perspective correct outline instead of
* just a circle facing the camera. */
pass.shader_set(res.shaders.armature_sphere_outline);
pass.bind_ubo("globalsBlock", &res.globals_buf);
res.select_bind(pass);
call_buf.end_sync(pass, shapes.metaball_wire_circle);
};
init_pass(metaball_ps_, circle_buf_);
init_pass(metaball_in_front_ps_, circle_in_front_buf_);
}
void draw(ResourcesT &res, Manager &manager, View &view)
{
GPU_framebuffer_bind(res.overlay_line_fb);
manager.submit(metaball_ps_, view);
}
void draw_in_front(ResourcesT &res, Manager &manager, View &view)
{
GPU_framebuffer_bind(res.overlay_line_in_front_fb);
manager.submit(metaball_in_front_ps_, view);
}
};
} // namespace blender::draw::overlay

View File

@ -0,0 +1,68 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*
* A depth pass that write surface depth when it is needed.
* It is also used for selecting non overlay-only objects.
*/
#pragma once
#include "draw_cache.h"
#include "overlay_private.hh"
namespace blender::draw::overlay {
template<typename SelectEngineT> class Prepass {
using SelectID = typename SelectEngineT::ID;
using ResourcesT = Resources<SelectEngineT>;
private:
PassMain prepass_ps_ = {"prepass"};
PassMain prepass_in_front_ps_ = {"prepass_in_front"};
public:
void begin_sync(ResourcesT &res, const State &state)
{
auto init_pass = [&](PassMain &pass) {
pass.init();
pass.state_set(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | state.clipping_state);
pass.shader_set(res.shaders.depth_mesh);
res.select_bind(pass);
};
init_pass(prepass_ps_);
init_pass(prepass_in_front_ps_);
}
void object_sync(Manager &manager, const ObjectRef &ob_ref, ResourcesT &res)
{
PassMain &pass = (ob_ref.object->dtx & OB_DRAW_IN_FRONT) != 0 ? prepass_in_front_ps_ :
prepass_ps_;
/* TODO(fclem) This function should contain what `basic_cache_populate` contained. */
GPUBatch *geom = DRW_cache_object_surface_get(ob_ref.object);
if (geom) {
ResourceHandle res_handle = manager.resource_handle(ob_ref);
pass.draw(geom, res_handle, res.select_id(ob_ref).get());
}
}
void draw(ResourcesT &res, Manager &manager, View &view)
{
/* Should be fine to use the line buffer since the prepass only writes to the depth buffer. */
GPU_framebuffer_bind(res.overlay_line_fb);
manager.submit(prepass_ps_, view);
}
void draw_in_front(ResourcesT &res, Manager &manager, View &view)
{
/* Should be fine to use the line buffer since the prepass only writes to the depth buffer. */
GPU_framebuffer_bind(res.overlay_line_in_front_fb);
manager.submit(prepass_in_front_ps_, view);
}
};
} // namespace blender::draw::overlay

View File

@ -7,13 +7,17 @@
#pragma once
#include "BKE_global.h"
#include "DRW_gpu_wrapper.hh"
#include "DRW_render.h"
#include "overlay_shader_shared.h"
#include "UI_resources.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "draw_handle.hh"
#include "overlay_shader.hh"
#include "overlay_shader_shared.h"
#ifdef __APPLE__
# define USE_GEOM_SHADER_WORKAROUND 1
@ -27,6 +31,142 @@ extern "C" {
/* Forward declarations */
struct ImBuf;
namespace blender::draw::overlay {
template<typename SelectEngineT> class Instance;
struct State {
Depsgraph *depsgraph;
const ViewLayer *view_layer;