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;
const Scene *scene;
const View3D *v3d;
const RegionView3D *rv3d;
const Base *active_base;
View3DOverlay overlay;
float pixelsize;
enum eContextObjectMode ctx_mode;
enum eObjectMode object_mode;
bool clear_in_front;
bool use_in_front;
bool is_wireframe_mode;
bool hide_overlays;
bool xray_enabled;
bool xray_enabled_and_not_wire;
float xray_opacity;
short v3d_flag; /* TODO: move to #View3DOverlay. */
short v3d_gridflag; /* TODO: move to #View3DOverlay. */
int cfra;
DRWState clipping_state;
};
using blender::draw::Framebuffer;
using blender::draw::StorageVectorBuffer;
using blender::draw::Texture;
using blender::draw::TextureFromPool;
using blender::draw::TextureRef;
template<typename SelectEngineT> struct Resources : public SelectEngineT::SelectMap {
ShaderModule<SelectEngineT> &shaders;
Framebuffer overlay_fb = {"overlay_fb"};
Framebuffer overlay_in_front_fb = {"overlay_in_front_fb"};
Framebuffer overlay_color_only_fb = {"overlay_color_only_fb"};
Framebuffer overlay_line_fb = {"overlay_line_fb"};
Framebuffer overlay_line_in_front_fb = {"overlay_line_in_front_fb"};
TextureFromPool line_tx = {"line_tx"};
TextureFromPool depth_in_front_alloc_tx = {"overlay_depth_in_front_tx"};
TextureFromPool color_overlay_alloc_tx = {"overlay_color_overlay_alloc_tx"};
TextureFromPool color_render_alloc_tx = {"overlay_color_render_alloc_tx"};
/** TODO(fclem): Copy of G_data.block that should become theme colors only and managed by the
* engine. */
GlobalsUboStorage theme_settings;
/* References, not owned. */
GPUUniformBuf *globals_buf;
TextureRef depth_tx;
TextureRef depth_in_front_tx;
TextureRef color_overlay_tx;
TextureRef color_render_tx;
Resources(ShaderModule<SelectEngineT> &shader_module) : shaders(shader_module){};
[[nodiscard]] ThemeColorID object_wire_theme_id(const ObjectRef &ob_ref,
const State &state) const
{
const bool is_edit = (state.object_mode & OB_MODE_EDIT) &&
(ob_ref.object->mode & OB_MODE_EDIT);
const bool active = (state.active_base != nullptr) &&
((ob_ref.dupli_parent != nullptr) ?
(state.active_base->object == ob_ref.dupli_parent) :
(state.active_base->object == ob_ref.object));
const bool is_selected = ((ob_ref.object->base_flag & BASE_SELECTED) != 0);
/* Object in edit mode. */
if (is_edit) {
return TH_WIRE_EDIT;
}
/* Transformed object during operators. */
if (((G.moving & G_TRANSFORM_OBJ) != 0) && is_selected) {
return TH_TRANSFORM;
}
/* Sets the 'theme_id' or fallback to wire */
if ((ob_ref.object->base_flag & BASE_SELECTED) != 0) {
return (active) ? TH_ACTIVE : TH_SELECT;
}
switch (ob_ref.object->type) {
case OB_LAMP:
return TH_LIGHT;
case OB_SPEAKER:
return TH_SPEAKER;
case OB_CAMERA:
return TH_CAMERA;
case OB_LIGHTPROBE:
/* TODO: add light-probe color. Use empty color for now. */
case OB_EMPTY:
return TH_EMPTY;
default:
return (is_edit) ? TH_WIRE_EDIT : TH_WIRE;
}
}
[[nodiscard]] const float4 &object_wire_color(const ObjectRef &ob_ref,
ThemeColorID theme_id) const
{
if (UNLIKELY(ob_ref.object->base_flag & BASE_FROM_SET)) {
return theme_settings.color_wire;
}
switch (theme_id) {
case TH_WIRE_EDIT:
return theme_settings.color_wire_edit;
case TH_ACTIVE:
return theme_settings.color_active;
case TH_SELECT:
return theme_settings.color_select;
case TH_TRANSFORM:
return theme_settings.color_transform;
case TH_SPEAKER:
return theme_settings.color_speaker;
case TH_CAMERA:
return theme_settings.color_camera;
case TH_EMPTY:
return theme_settings.color_empty;
case TH_LIGHT:
return theme_settings.color_light;
default:
return theme_settings.color_wire;
}
}
[[nodiscard]] const float4 &object_wire_color(const ObjectRef &ob_ref, const State &state) const
{
ThemeColorID theme_id = object_wire_theme_id(ob_ref, state);
return object_wire_color(ob_ref, theme_id);
}
};
} // namespace blender::draw::overlay
typedef struct OVERLAY_FramebufferList {
struct GPUFrameBuffer *overlay_default_fb;
struct GPUFrameBuffer *overlay_line_fb;
@ -436,10 +576,6 @@ typedef struct OVERLAY_StorageList {
struct OVERLAY_PrivateData *pd;
} OVERLAY_StorageList;
typedef struct OVERLAY_Instance {
GPUUniformBuf *grid_ubo;
} OVERLAY_Instance;
typedef struct OVERLAY_Data {
void *engine_type;
OVERLAY_FramebufferList *fbl;
@ -447,7 +583,7 @@ typedef struct OVERLAY_Data {
OVERLAY_PassList *psl;
OVERLAY_StorageList *stl;
OVERLAY_Instance *instance;
void *instance;
} OVERLAY_Data;
typedef struct OVERLAY_DupliData {
@ -460,7 +596,7 @@ typedef struct OVERLAY_DupliData {
short base_flag;
} OVERLAY_DupliData;
typedef struct BoneInstanceData {
struct BoneInstanceData {
/* Keep sync with bone instance vertex format (OVERLAY_InstanceFormats) */
union {
float mat[4][4];
@ -477,7 +613,12 @@ typedef struct BoneInstanceData {
float _pad03[3], amax_b;
};
};
} BoneInstanceData;
BoneInstanceData() = default;
/* Constructor used by metaball overlays and expected to be used for drawing
* metaball_wire_sphere with armature wire shader that produces wide-lines. */
BoneInstanceData(Object *ob, const float *pos, const float radius, const float color[4]);
};
typedef struct OVERLAY_InstanceFormats {
struct GPUVertFormat *instance_pos;
@ -797,7 +938,3 @@ GPUShader *OVERLAY_shader_xray_fade(void);
OVERLAY_InstanceFormats *OVERLAY_shader_instance_formats_get(void);
void OVERLAY_shader_free(void);
#ifdef __cplusplus
}
#endif

View File

@ -11,7 +11,10 @@
#include "UI_resources.h"
#include "gpu_shader_create_info.hh"
#include "overlay_private.hh"
#include "overlay_shader.hh"
struct OVERLAY_Shaders {
GPUShader *antialiasing;
@ -1209,3 +1212,20 @@ void OVERLAY_shader_free(void)
MEM_SAFE_FREE(*format);
}
}
namespace blender::draw::overlay {
template<>
ShaderModule<select::Instance> *ShaderModule<select::Instance>::g_shader_module = nullptr;
template<>
ShaderModule<select::InstanceDummy> *ShaderModule<select::InstanceDummy>::g_shader_module =
nullptr;
void shader_module_free()
{
ShaderModule<select::Instance>::module_free();
ShaderModule<select::InstanceDummy>::module_free();
}
} // namespace blender::draw::overlay

View File

@ -0,0 +1,149 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "GPU_shader.h"
#include "gpu_shader_create_info.hh"
#include "../select/select_instance.hh"
namespace blender::draw::overlay {
/**
* Shader module. Shared between instances.
*/
template<typename SelectEngineT, bool ClippingEnabled = false> class ShaderModule {
private:
/** Shared shader module across all engine instances. */
static ShaderModule *g_shader_module;
/** TODO: Support clipping. This global state should be set by the overlay::Instance and switch
* to the shader variations that use clipping. */
// bool clipping_enabled = false;
class Shader {
protected:
GPUShader *shader_ = nullptr;
public:
Shader() = default;
Shader(const char *create_info_name)
: shader_(GPU_shader_create_from_info_name(create_info_name)){};
~Shader()
{
DRW_SHADER_FREE_SAFE(shader_);
}
operator GPUShader *()
{
return shader_;
}
};
/**
* Special class for any shader that needs to have clipped and selection variations.
*/
class ShaderGeometry : public Shader {
public:
ShaderGeometry(const char *create_info_name)
{
/* TODO: This is what it should be like with all variations defined with create infos. */
// std::string create_info_name = base_create_info;
// create_info_name += SelectEngineT::shader_suffix;
// create_info_name += ClippingEnabled ? "_clipped" : "";
// this->shader_ = GPU_shader_create_from_info_name(create_info_name.c_str());
/* WORKAROUND: ... but for now, we have to patch the create info used by the old engine. */
gpu::shader::ShaderCreateInfo info =
*reinterpret_cast<const gpu::shader::ShaderCreateInfo *>(
GPU_shader_create_info_get(create_info_name));
info.define(SelectEngineT::shader_define);
this->shader_ = GPU_shader_create_from_info(
reinterpret_cast<const GPUShaderCreateInfo *>(&info));
}
/* WORKAROUND: Create a shader using a patch function to patch the create info. */
ShaderGeometry(const char *create_info_name,
std::function<void(gpu::shader::ShaderCreateInfo &info)> patch)
{
gpu::shader::ShaderCreateInfo info =
*reinterpret_cast<const gpu::shader::ShaderCreateInfo *>(
GPU_shader_create_info_get(create_info_name));
patch(info);
SelectEngineT::SelectShader::patch(info);
this->shader_ = GPU_shader_create_from_info(
reinterpret_cast<const GPUShaderCreateInfo *>(&info));
}
};
public:
/** ShaderGeometry */
ShaderGeometry armature_sphere_outline = {
"overlay_armature_sphere_outline", [](gpu::shader::ShaderCreateInfo &info) {
using namespace blender::gpu::shader;
info.storage_buf(0, Qualifier::READ, "mat4", "data_buf[]");
info.define("inst_obmat", "data_buf[gl_InstanceID]");
info.vertex_inputs_.pop_last();
}};
ShaderGeometry depth_mesh = {"overlay_depth_only", [](gpu::shader::ShaderCreateInfo &info) {
using namespace blender::gpu::shader;
info.additional_infos_.clear();
info.additional_info(
"draw_view", "draw_modelmat_new", "draw_resource_handle_new");
}};
ShaderGeometry extra_shape = {
"overlay_extra", [](gpu::shader::ShaderCreateInfo &info) {
using namespace blender::gpu::shader;
info.storage_buf(0, Qualifier::READ, "ExtraInstanceData", "data_buf[]");
info.define("color", "data_buf[gl_InstanceID].color_");
info.define("inst_obmat", "data_buf[gl_InstanceID].object_to_world_");
info.vertex_inputs_.pop_last();
info.vertex_inputs_.pop_last();
}};
/** Shader */
Shader grid = {"overlay_grid"};
Shader background_fill = {"overlay_background"};
Shader background_clip_bound = {"overlay_clipbound"};
/** Module */
/** Only to be used by Instance constructor. */
static ShaderModule &module_get()
{
if (g_shader_module == nullptr) {
/* TODO(@fclem) thread-safety. */
g_shader_module = new ShaderModule();
}
return *g_shader_module;
}
static void module_free()
{
if (g_shader_module != nullptr) {
/* TODO(@fclem) thread-safety. */
delete g_shader_module;
g_shader_module = nullptr;
}
}
};
void shader_module_free();
} // namespace blender::draw::overlay

View File

@ -1,6 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef GPU_SHADER
# pragma once
# include "GPU_shader_shared_utils.h"
# include "DNA_action_types.h"
@ -12,6 +14,8 @@ extern "C" {
typedef enum OVERLAY_GridBits OVERLAY_GridBits;
# endif
typedef struct OVERLAY_GridData OVERLAY_GridData;
typedef struct ThemeColorData ThemeColorData;
typedef struct ExtraInstanceData ExtraInstanceData;
#endif
/* TODO(fclem): Should eventually become OVERLAY_BackgroundType.
@ -84,6 +88,125 @@ BLI_STATIC_ASSERT(MOTIONPATH_VERT_SEL == (1u << 0), "Ensure value is sync");
BLI_STATIC_ASSERT(MOTIONPATH_VERT_KEY == (1u << 1), "Ensure value is sync");
#endif
struct ThemeColorData {
float4 color_wire;
float4 color_wire_edit;
float4 color_active;
float4 color_select;
float4 color_library_select;
float4 color_library;
float4 color_transform;
float4 color_light;
float4 color_speaker;
float4 color_camera;
float4 color_camera_path;
float4 color_empty;
float4 color_vertex;
float4 color_vertex_select;
float4 color_vertex_unreferenced;
float4 color_vertex_missing_data;
float4 color_edit_mesh_active;
float4 color_edge_select;
float4 color_edge_seam;
float4 color_edge_sharp;
float4 color_edge_crease;
float4 color_edge_bweight;
float4 color_edge_face_select;
float4 color_edge_freestyle;
float4 color_face;
float4 color_face_select;
float4 color_face_freestyle;
float4 color_gpencil_vertex;
float4 color_gpencil_vertex_select;
float4 color_normal;
float4 color_vnormal;
float4 color_lnormal;
float4 color_facedot;
float4 color_skinroot;
float4 color_deselect;
float4 color_outline;
float4 color_light_no_alpha;
float4 color_background;
float4 color_background_gradient;
float4 color_checker_primary;
float4 color_checker_secondary;
float4 color_clipping_border;
float4 color_edit_mesh_middle;
float4 color_handle_free;
float4 color_handle_auto;
float4 color_handle_vect;
float4 color_handle_align;
float4 color_handle_autoclamp;
float4 color_handle_sel_free;
float4 color_handle_sel_auto;
float4 color_handle_sel_vect;
float4 color_handle_sel_align;
float4 color_handle_sel_autoclamp;
float4 color_nurb_uline;
float4 color_nurb_vline;
float4 color_nurb_sel_uline;
float4 color_nurb_sel_vline;
float4 color_active_spline;
float4 color_bone_pose;
float4 color_bone_pose_active;
float4 color_bone_pose_active_unsel;
float4 color_bone_pose_constraint;
float4 color_bone_pose_ik;
float4 color_bone_pose_spline_ik;
float4 color_bone_pose_target;
float4 color_bone_solid;
float4 color_bone_locked;
float4 color_bone_active;
float4 color_bone_active_unsel;
float4 color_bone_select;
float4 color_bone_ik_line;
float4 color_bone_ik_line_no_target;
float4 color_bone_ik_line_spline;
float4 color_text;
float4 color_text_hi;
float4 color_bundle_solid;
float4 color_mball_radius;
float4 color_mball_radius_select;
float4 color_mball_stiffness;
float4 color_mball_stiffness_select;
float4 color_current_frame;
float4 color_grid;
float4 color_grid_emphasis;
float4 color_grid_axis_x;
float4 color_grid_axis_y;
float4 color_grid_axis_z;
float4 color_face_back;
float4 color_face_front;
float4 color_uv_shadow;
};
BLI_STATIC_ASSERT_ALIGN(ThemeColorData, 16)
struct ExtraInstanceData {
float4 color_;
float4x4 object_to_world_;
#if !defined(GPU_SHADER) && defined(__cplusplus)
ExtraInstanceData(const float4x4 &object_to_world, float4 &color, float draw_size)
{
this->color_ = color;
this->object_to_world_ = object_to_world;
this->object_to_world_[3][3] = draw_size;
};
#endif
};
BLI_STATIC_ASSERT_ALIGN(ExtraInstanceData, 16)
#ifndef GPU_SHADER
# ifdef __cplusplus
}

View File

@ -0,0 +1,380 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "BLI_vector.hh"
#include "GPU_batch.h"
namespace blender::draw::overlay {
/**
* Buffer containing instances of a certain shape.
*/
template<typename SelectEngineT, typename InstanceDataT>
struct ShapeInstanceBuf : private SelectEngineT::SelectBuf {
using SelectID = typename SelectEngineT::ID;
StorageVectorBuffer<InstanceDataT> data_buf;
ShapeInstanceBuf(const char *name = nullptr) : data_buf(name){};
void clear()
{
this->select_clear();
data_buf.clear();
}
void append(const InstanceDataT &data, SelectID select_id)
{
this->select_append(select_id);
data_buf.append(data);
}
void end_sync(PassSimple &pass, GPUBatch *shape)
{
if (data_buf.size() == 0) {
return;
}
this->select_bind(pass);
data_buf.push_update();
pass.bind_ssbo("data_buf", &data_buf);
pass.draw(shape, data_buf.size());
}
};
/**
* Contains all overlay generic geometry batches.
*/
class ShapeCache {
private:
/* Needs to be first for other lambdas to access. */
GPUVertFormat format = []() {
GPUVertFormat format = {0};
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
GPU_vertformat_attr_add(&format, "vclass", GPU_COMP_I32, 1, GPU_FETCH_INT);
return format;
}();
/* Matches Vertex Format. */
struct Vertex {
float3 pos;
int vclass;
};
struct AutoFreeGPUBatch {
GPUBatch *batch;
AutoFreeGPUBatch(GPUBatch *_batch) : batch(_batch){};
~AutoFreeGPUBatch()
{
GPU_BATCH_DISCARD_SAFE(batch);
}
operator GPUBatch *()
{
return batch;
}
};
/* Caller gets ownership of the #GPUVertBuf. */
GPUVertBuf *vbo_from_vector(Vector<Vertex> &vector)
{
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, vector.size());
Vertex *vbo_data = (Vertex *)GPU_vertbuf_get_data(vbo);
/* Copy data to VBO using a wrapper span. Could use memcpy if that's too slow. */
MutableSpan<Vertex> span(vbo_data, vector.size());
span.copy_from(vector);
return vbo;
}
enum VertexClass {
VCLASS_LIGHT_AREA_SHAPE = 1 << 0,
VCLASS_LIGHT_SPOT_SHAPE = 1 << 1,
VCLASS_LIGHT_SPOT_BLEND = 1 << 2,
VCLASS_LIGHT_SPOT_CONE = 1 << 3,
VCLASS_LIGHT_DIST = 1 << 4,
VCLASS_CAMERA_FRAME = 1 << 5,
VCLASS_CAMERA_DIST = 1 << 6,
VCLASS_CAMERA_VOLUME = 1 << 7,
VCLASS_SCREENSPACE = 1 << 8,
VCLASS_SCREENALIGNED = 1 << 9,
VCLASS_EMPTY_SCALED = 1 << 10,
VCLASS_EMPTY_AXES = 1 << 11,
VCLASS_EMPTY_AXES_NAME = 1 << 12,
VCLASS_EMPTY_AXES_SHADOW = 1 << 13,
VCLASS_EMPTY_SIZE = 1 << 14,
};
static constexpr float bone_box_verts[8][3] = {
{1.0f, 0.0f, 1.0f},
{1.0f, 0.0f, -1.0f},
{-1.0f, 0.0f, -1.0f},
{-1.0f, 0.0f, 1.0f},
{1.0f, 1.0f, 1.0f},
{1.0f, 1.0f, -1.0f},
{-1.0f, 1.0f, -1.0f},
{-1.0f, 1.0f, 1.0f},
};
static constexpr std::array<uint, 24> bone_box_wire = {
0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7,
};
/* A single ring of vertices. */
static Vector<float2> ring_vertices(const float radius, const int segments)
{
Vector<float2> verts;
for (int i : IndexRange(segments)) {
float angle = (2 * M_PI * i) / segments;
verts.append(radius * float2(math::cos(angle), math::sin(angle)));
}
return verts;
}
/* Returns lines segment geometry forming 3 circles, one on each axis. */
static Vector<Vertex> sphere_axes_circles(const float radius,
const VertexClass vclass,
const int segments)
{
Vector<float2> ring = ring_vertices(radius, segments);
Vector<Vertex> verts;
for (int axis : IndexRange(3)) {
for (int i : IndexRange(segments)) {
for (int j : IndexRange(2)) {
float2 cv = ring[(i + j) % segments];
if (axis == 0) {
verts.append({{cv[0], cv[1], 0.0f}, vclass});
}
else if (axis == 1) {
verts.append({{cv[0], 0.0f, cv[1]}, vclass});
}
else {
verts.append({{0.0f, cv[0], cv[1]}, vclass});
}
}
}
}
return verts;
}
public:
AutoFreeGPUBatch quad_wire = [this]() {
Vector<Vertex> verts;
verts.append({{-1.0f, -1.0f, 0.0f}, VCLASS_EMPTY_SCALED});
verts.append({{-1.0f, +1.0f, 0.0f}, VCLASS_EMPTY_SCALED});
verts.append({{-1.0f, +1.0f, 0.0f}, VCLASS_EMPTY_SCALED});
verts.append({{+1.0f, +1.0f, 0.0f}, VCLASS_EMPTY_SCALED});
verts.append({{+1.0f, +1.0f, 0.0f}, VCLASS_EMPTY_SCALED});
verts.append({{+1.0f, -1.0f, 0.0f}, VCLASS_EMPTY_SCALED});
verts.append({{+1.0f, -1.0f, 0.0f}, VCLASS_EMPTY_SCALED});
verts.append({{-1.0f, -1.0f, 0.0f}, VCLASS_EMPTY_SCALED});
return GPU_batch_create_ex(
GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO);
}();
AutoFreeGPUBatch plain_axes = [this]() {
Vector<Vertex> verts;
verts.append({{0.0f, -1.0f, 0.0f}, VCLASS_EMPTY_SCALED});
verts.append({{0.0f, +1.0f, 0.0f}, VCLASS_EMPTY_SCALED});
verts.append({{-1.0f, 0.0f, 0.0f}, VCLASS_EMPTY_SCALED});
verts.append({{+1.0f, 0.0f, 0.0f}, VCLASS_EMPTY_SCALED});
verts.append({{0.0f, 0.0f, -1.0f}, VCLASS_EMPTY_SCALED});
verts.append({{0.0f, 0.0f, +1.0f}, VCLASS_EMPTY_SCALED});
return GPU_batch_create_ex(
GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO);
}();
AutoFreeGPUBatch single_arrow = [this]() {
Vector<Vertex> verts;
float p[3][3] = {{0}};
p[0][2] = 1.0f;
p[1][0] = 0.035f;
p[1][1] = 0.035f;
p[2][0] = -0.035f;
p[2][1] = 0.035f;
p[1][2] = p[2][2] = 0.75f;
for (int sides : IndexRange(4)) {
if (sides % 2 == 1) {
p[1][0] = -p[1][0];
p[2][1] = -p[2][1];
}
else {
p[1][1] = -p[1][1];
p[2][0] = -p[2][0];
}
for (int i = 0, a = 1; i < 2; i++, a++) {
verts.append({{p[i][0], p[i][1], p[i][2]}, VCLASS_EMPTY_SCALED});
verts.append({{p[a][0], p[a][1], p[a][2]}, VCLASS_EMPTY_SCALED});
}
}
verts.append({{0.0f, 0.0f, 0.0}, VCLASS_EMPTY_SCALED});
verts.append({{0.0f, 0.0f, 0.75f}, VCLASS_EMPTY_SCALED});
return GPU_batch_create_ex(
GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO);
}();
AutoFreeGPUBatch cube = [this]() {
Vector<Vertex> verts;
for (auto index : bone_box_wire) {
float x = bone_box_verts[index][0];
float y = bone_box_verts[index][1] * 2.0 - 1.0f;
float z = bone_box_verts[index][2];
verts.append({{x, y, z}, VCLASS_EMPTY_SCALED});
}
return GPU_batch_create_ex(
GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO);
}();
AutoFreeGPUBatch circle = [this]() {
constexpr int resolution = 64;
Vector<float2> ring = ring_vertices(1.0f, resolution);
Vector<Vertex> verts;
for (int a : IndexRange(resolution + 1)) {
float2 cv = ring[a % resolution];
verts.append({{cv.x, 0.0f, cv.y}, VCLASS_EMPTY_SCALED});
}
return GPU_batch_create_ex(
GPU_PRIM_LINE_STRIP, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO);
}();
AutoFreeGPUBatch empty_sphere = [this]() {
Vector<Vertex> verts = sphere_axes_circles(1.0f, VCLASS_EMPTY_SCALED, 32);
return GPU_batch_create_ex(
GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO);
}();
AutoFreeGPUBatch empty_cone = [this]() {
/* A single ring of vertices. */
constexpr int resolution = 8;
Vector<float2> ring = ring_vertices(1.0f, resolution);
Vector<Vertex> verts;
for (int i : IndexRange(resolution)) {
float2 cv = ring[i % resolution];
/* Cone sides. */
verts.append({{cv[0], 0.0f, cv[1]}, VCLASS_EMPTY_SCALED});
verts.append({{0.0f, 2.0f, 0.0f}, VCLASS_EMPTY_SCALED});
/* Base ring. */
for (int j : IndexRange(2)) {
float2 cv = ring[(i + j) % resolution];
verts.append({{cv[0], 0.0f, cv[1]}, VCLASS_EMPTY_SCALED});
}
}
return GPU_batch_create_ex(
GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO);
}();
AutoFreeGPUBatch arrows = [this]() {
float2 x_axis_name_scale = {0.0215f, 0.025f};
Vector<float2> x_axis_name = {
float2(0.9f, 1.0f) * x_axis_name_scale,
float2(-1.0f, -1.0f) * x_axis_name_scale,
float2(-0.9f, 1.0f) * x_axis_name_scale,
float2(1.0f, -1.0f) * x_axis_name_scale,
};
float2 y_axis_name_scale = {0.0175f, 0.025f};
Vector<float2> y_axis_name = {
float2(-1.0f, 1.0f) * y_axis_name_scale,
float2(0.0f, -0.1f) * y_axis_name_scale,
float2(1.0f, 1.0f) * y_axis_name_scale,
float2(0.0f, -0.1f) * y_axis_name_scale,
float2(0.0f, -0.1f) * y_axis_name_scale,
float2(0.0f, -1.0f) * y_axis_name_scale,
};
float2 z_axis_name_scale = {0.02f, 0.025f};
Vector<float2> z_axis_name = {
float2(-0.95f, 1.00f) * z_axis_name_scale,
float2(0.95f, 1.00f) * z_axis_name_scale,
float2(0.95f, 1.00f) * z_axis_name_scale,
float2(0.95f, 0.90f) * z_axis_name_scale,
float2(0.95f, 0.90f) * z_axis_name_scale,
float2(-1.00f, -0.90f) * z_axis_name_scale,
float2(-1.00f, -0.90f) * z_axis_name_scale,
float2(-1.00f, -1.00f) * z_axis_name_scale,
float2(-1.00f, -1.00f) * z_axis_name_scale,
float2(1.00f, -1.00f) * z_axis_name_scale,
};
float2 axis_marker_scale = {0.007f, 0.007f};
Vector<float2> axis_marker = {
#if 0 /* square */
float2(-1.0f, 1.0f) * axis_marker_scale,
float2(1.0f, 1.0f) * axis_marker_scale,
float2(1.0f, 1.0f) * axis_marker_scale,
float2(1.0f, -1.0f) * axis_marker_scale,
float2(1.0f, -1.0f) * axis_marker_scale,
float2(-1.0f, -1.0f) * axis_marker_scale,
float2(-1.0f, -1.0f) * axis_marker_scale,
float2(-1.0f, 1.0f) * axis_marker_scale,
#else /* diamond */
float2(-1.0f, 0.0f) * axis_marker_scale,
float2(0.0f, 1.0f) * axis_marker_scale,
float2(0.0f, 1.0f) * axis_marker_scale,
float2(1.0f, 0.0f) * axis_marker_scale,
float2(1.0f, 0.0f) * axis_marker_scale,
float2(0.0f, -1.0f) * axis_marker_scale,
float2(0.0f, -1.0f) * axis_marker_scale,
float2(-1.0f, 0.0f) * axis_marker_scale,
#endif
};
Vector<Vertex> verts;
for (int axis : IndexRange(3)) {
/* Vertex layout is XY screen position and axis in Z.
* Fractional part of Z is a positive offset at axis unit position. */
int flag = VCLASS_EMPTY_AXES | VCLASS_SCREENALIGNED;
/* Center to axis line. */
verts.append({{0.0f, 0.0f, 0.0f}, 0});
verts.append({{0.0f, 0.0f, float(axis)}, flag});
/* Axis end marker. */
constexpr int marker_fill_layer = 6;
for (int j = 1; j < marker_fill_layer + 1; j++) {
for (float2 axis_marker_vert : axis_marker) {
verts.append({{axis_marker_vert * ((4.0f * j) / marker_fill_layer), float(axis)}, flag});
}
}
/* Axis name. */
Vector<float2> *axis_names[3] = {&x_axis_name, &y_axis_name, &z_axis_name};
for (float2 axis_name_vert : *(axis_names[axis])) {
int flag = VCLASS_EMPTY_AXES | VCLASS_EMPTY_AXES_NAME | VCLASS_SCREENALIGNED;
verts.append({{axis_name_vert * 4.0f, axis + 0.25f}, flag});
}
}
return GPU_batch_create_ex(
GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO);
}();
AutoFreeGPUBatch metaball_wire_circle = [this]() {
/* A single ring of vertices. */
constexpr int resolution = 64;
constexpr float radius = 1.0f;
Vector<float2> ring = ring_vertices(radius, resolution);
Vector<Vertex> verts;
for (int i : IndexRange(resolution + 1)) {
float2 cv = ring[i % resolution];
verts.append({{cv[0], cv[1], 0.0f}, VCLASS_SCREENALIGNED});
}
return GPU_batch_create_ex(
GPU_PRIM_LINE_STRIP, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO);
}();
};
} // namespace blender::draw::overlay

View File

@ -13,6 +13,7 @@ GPU_SHADER_INTERFACE_INFO(overlay_extra_iface, "")
GPU_SHADER_CREATE_INFO(overlay_extra)
.do_static_compilation(true)
.typedef_source("overlay_shader_shared.h")
.vertex_in(0, Type::VEC3, "pos")
.vertex_in(1, Type::INT, "vclass")
/* Instance attributes. */

View File

@ -1,12 +1,15 @@
#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(select_lib.glsl)
/* Sphere radius */
const float rad = 0.05;
void main()
{
select_id_set(in_select_buf[gl_InstanceID]);
vec4 bone_color, state_color;
mat4 model_mat = extract_matrix_packed_data(inst_obmat, state_color, bone_color);

View File

@ -1,8 +1,10 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(select_lib.glsl)
void main()
{
lineOutput = pack_line_data(gl_FragCoord.xy, edgeStart, edgePos);
fragColor = vec4(finalColor.rgb, finalColor.a * alpha);
select_id_output(select_id);
}

View File

@ -1,6 +1,10 @@
#pragma BLENDER_REQUIRE(select_lib.glsl)
void main()
{
/* No color output, only depth (line below is implicit). */
// gl_FragDepth = gl_FragCoord.z;
/* This is optimized to noop in the non select case. */
select_id_output(select_id);
}

View File

@ -1,11 +1,14 @@
#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(select_lib.glsl)
void main()
{
GPU_INTEL_VERTEX_SHADER_WORKAROUND
select_id_set(drw_CustomID);
vec3 world_pos = point_object_to_world(pos);
gl_Position = point_world_to_ndc(world_pos);

View File

@ -1,8 +1,11 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(select_lib.glsl)
void main()
{
fragColor = finalColor;
lineOutput = pack_line_data(gl_FragCoord.xy, edgeStart, edgePos);
select_id_output(select_id);
}

View File

@ -1,6 +1,7 @@
#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(select_lib.glsl)
#define lamp_area_size inst_data.xy
#define lamp_clip_sta inst_data.z
@ -41,6 +42,8 @@
void main()
{
select_id_set(in_select_buf[gl_InstanceID]);
/* Extract data packed inside the unused mat4 members. */
vec4 inst_data = vec4(inst_obmat[0][3], inst_obmat[1][3], inst_obmat[2][3], inst_obmat[3][3]);
float inst_color_data = color.a;

View File

@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*/
#pragma once
#define SELECT_DATA 4
#define SELECT_ID_IN 5
#define SELECT_ID_OUT 6

View File

@ -162,7 +162,7 @@ static void select_cache_init(void *vedata)
}
else {
pd->shgrp_face_unif = DRW_shgroup_create(sh->select_id_uniform, psl->select_id_face_pass);
DRW_shgroup_uniform_int_copy(pd->shgrp_face_unif, "id", 0);
DRW_shgroup_uniform_int_copy(pd->shgrp_face_unif, "select_id", 0);
DRW_shgroup_uniform_float_copy(pd->shgrp_face_unif, "retopologyOffset", retopology_offset);
}

View File

@ -7,6 +7,10 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* select_engine.c */
extern DrawEngineType draw_engine_select_type;
@ -22,3 +26,11 @@ struct SELECTID_Context *DRW_select_engine_context_get(void);
struct GPUFrameBuffer *DRW_engine_select_framebuffer_get(void);
struct GPUTexture *DRW_engine_select_texture_get(void);
/* select_instance.cc */
extern DrawEngineType draw_engine_select_next_type;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,117 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup select
*/
#include "DRW_render.h"
#include "GPU_capabilities.h"
#include "select_engine.h"
#include "../overlay/overlay_instance.hh"
#include "select_instance.hh"
using namespace blender::draw;
/* -------------------------------------------------------------------- */
/** \name Select-Next Engine
* \{ */
using Instance = overlay::Instance<select::Instance>;
typedef struct SELECT_NextData {
void *engine_type;
DRWViewportEmptyList *fbl;
DRWViewportEmptyList *txl;
DRWViewportEmptyList *psl;
DRWViewportEmptyList *stl;
Instance *instance;
} SELECT_NextData;
static void SELECT_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 SELECT_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 SELECT_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 SELECT_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 SELECT_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());
}
static void SELECT_next_instance_free(void *instance_)
{
auto *instance = (Instance *)instance_;
if (instance != nullptr) {
delete instance;
}
}
static const DrawEngineDataSize SELECT_next_data_size = DRW_VIEWPORT_DATA_SIZE(SELECT_NextData);
DrawEngineType draw_engine_select_next_type = {
NULL,
NULL,
N_("Select-Next"),
&SELECT_next_data_size,
&SELECT_next_engine_init,
NULL,
&SELECT_next_instance_free,
&SELECT_next_cache_init,
&SELECT_next_cache_populate,
&SELECT_next_cache_finish,
&SELECT_next_draw_scene,
NULL,
NULL,
NULL,
NULL,
};
/** \} */

View File

@ -0,0 +1,263 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation. */
/** \file
* \ingroup draw_engine
*
*/
#pragma once
#include "DRW_gpu_wrapper.hh"
#include "GPU_select.h"
#include "../intern/gpu_select_private.h"
#include "draw_manager.hh"
#include "draw_pass.hh"
#include "gpu_shader_create_info.hh"
#include "select_defines.h"
#include "select_shader_shared.hh"
namespace blender::draw::select {
/**
* Dummy implementation of the select engine types to avoid any overhead.
* Bypass any selection logic.
*/
struct InstanceDummy {
/* Add type safety to selection ID. Only the select engine should provide them. */
class ID {
public:
constexpr uint32_t get() const
{
return 0;
}
};
struct SelectShader {
static void patch(gpu::shader::ShaderCreateInfo &){};
};
struct SelectBuf {
void select_clear(){};
void select_append(ID){};
void select_bind(PassSimple &){};
};
struct SelectMap {
[[nodiscard]] const ID select_id(const ObjectRef &, uint = 0)
{
return {};
}
void begin_sync(){};
void select_bind(PassSimple &){};
void select_bind(PassMain &){};
void end_sync(){};
void read_result(){};
};
};
/**
* This is an implementation of the Select engine specialized for selecting object.
* Should plug seamlessly inside the overlay engine logic.
*/
struct Instance {
/* Add type safety to selection ID. Only the select engine should provide them. */
class ID {
public:
uint32_t value;
uint32_t get() const
{
return value;
}
};
struct SelectShader {
static void patch(gpu::shader::ShaderCreateInfo &info)
{
info.define("SELECT_ENABLE");
/* Replace additional info. */
for (StringRefNull &str : info.additional_infos_) {
if (str == "draw_modelmat_new") {
str = "draw_modelmat_new_with_custom_id";
}
}
info.additional_info("select_id_patch");
};
};
/**
* Add a dedicated selection id buffer to a pass.
* To be used when not using a #PassMain which can pass the select ID via CustomID.
*/
struct SelectBuf {
StorageVectorBuffer<uint32_t> select_buf = {"select_buf"};
void select_clear()
{
select_buf.clear();
}
void select_append(ID select_id)
{
select_buf.append(select_id.get());
}
void select_bind(PassSimple &pass)
{
select_buf.push_update();
pass.bind_ssbo(SELECT_ID_IN, &select_buf);
}
};
/**
* Generate selection IDs from objects and keep record of the mapping between them.
* The id's are contiguous so that we can create a destination buffer.
*/
struct SelectMap {
/** Mapping between internal IDs and `object->runtime.select_id`. */
Vector<uint> select_id_map;
#ifdef DEBUG
/** Debug map containing a copy of the object name. */
Vector<std::string> map_names;
#endif
/** Stores the result of the whole selection drawing. Content depends on selection mode. */
StorageArrayBuffer<uint> select_output_buf = {"select_output_buf"};
/** Dummy buffer. Might be better to remove, but simplify the shader create info patching. */
StorageArrayBuffer<uint, 4, true> dummy_select_buf = {"dummy_select_buf"};
/** Uniform buffer to bind to all passes to pass information about the selection state. */
UniformBuffer<SelectInfoData> info_buf;
/** Will remove the depth test state from any pass drawing objects with select id. */
bool disable_depth_test;
/* TODO(fclem): The sub_object_id id should eventually become some enum or take a sub-object
* reference directly. This would isolate the selection logic to this class. */
[[nodiscard]] const ID select_id(const ObjectRef &ob_ref, uint sub_object_id = 0)
{
uint object_id = ob_ref.object->runtime.select_id;
uint id = select_id_map.append_and_get_index(object_id | sub_object_id);
#ifdef DEBUG
map_names.append(ob_ref.object->id.name);
#endif
return {id};
}
/* Load an invalid index that will not write to the output (not selectable). */
[[nodiscard]] const ID select_invalid_id()
{
return {uint32_t(-1)};
}
void begin_sync()
{
switch (gpu_select_next_get_mode()) {
case GPU_SELECT_ALL:
info_buf.mode = SelectType::SELECT_ALL;
disable_depth_test = true;
break;
/* Not sure if these 2 NEAREST are mapped to the right algorithm. */
case GPU_SELECT_NEAREST_FIRST_PASS:
case GPU_SELECT_NEAREST_SECOND_PASS:
case GPU_SELECT_PICK_ALL:
info_buf.mode = SelectType::SELECT_PICK_ALL;
info_buf.cursor = int2(gpu_select_next_get_pick_area_center());
disable_depth_test = true;
break;
case GPU_SELECT_PICK_NEAREST:
info_buf.mode = SelectType::SELECT_PICK_NEAREST;
info_buf.cursor = int2(gpu_select_next_get_pick_area_center());
disable_depth_test = true;
break;
}
info_buf.push_update();
select_id_map.clear();
#ifdef DEBUG
map_names.clear();
#endif
}
/** IMPORTANT: Changes the draw state. Need to be called after the pass own state_set. */
void select_bind(PassSimple &pass)
{
if (disable_depth_test) {
/* TODO: clipping state. */
pass.state_set(DRW_STATE_WRITE_COLOR);
}
pass.bind_ubo(SELECT_DATA, &info_buf);
pass.bind_ssbo(SELECT_ID_OUT, &select_output_buf);
}
/** IMPORTANT: Changes the draw state. Need to be called after the pass own state_set. */
void select_bind(PassMain &pass)
{
pass.use_custom_ids = true;
if (disable_depth_test) {
/* TODO: clipping state. */
pass.state_set(DRW_STATE_WRITE_COLOR);
}
pass.bind_ubo(SELECT_DATA, &info_buf);
/* IMPORTANT: This binds a dummy buffer `in_select_buf` but it is not supposed to be used. */
pass.bind_ssbo(SELECT_ID_IN, &dummy_select_buf);
pass.bind_ssbo(SELECT_ID_OUT, &select_output_buf);
}
void end_sync()
{
select_output_buf.resize(ceil_to_multiple_u(select_id_map.size(), 4));
select_output_buf.push_update();
if (info_buf.mode == SelectType::SELECT_ALL) {
/* This mode uses atomicOr and store result as a bitmap. Clear to 0 (no selection). */
GPU_storagebuf_clear(select_output_buf, 0);
}
else {
/* Other modes use atomicMin. Clear to UINT_MAX. */
GPU_storagebuf_clear(select_output_buf, 0xFFFFFFFFu);
}
}
void read_result()
{
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
select_output_buf.read();
Vector<GPUSelectResult> result;
/* Convert raw data from GPU to #GPUSelectResult. */
switch (info_buf.mode) {
case SelectType::SELECT_ALL:
for (auto i : IndexRange(select_id_map.size())) {
if (((select_output_buf[i / 32] >> (i % 32)) & 1) != 0) {
result.append({select_id_map[i], 0xFFFFu});
}
}
break;
case SelectType::SELECT_PICK_ALL:
case SelectType::SELECT_PICK_NEAREST:
for (auto i : IndexRange(select_id_map.size())) {
if (select_output_buf[i] != 0xFFFFFFFFu) {
/* NOTE: For `SELECT_PICK_NEAREST`, `select_output_buf` also contains the screen
* distance to cursor in the lowest bits. */
result.append({select_id_map[i], select_output_buf[i]});
}
}
break;
}
gpu_select_next_set_result(result.data(), result.size());
}
};
};
} // namespace blender::draw::select

View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef GPU_SHADER
# pragma once
# include "GPU_shader_shared_utils.h"
namespace blender::draw::select {
#endif
/* Matches eV3DSelectMode */
enum SelectType : uint32_t {
SELECT_ALL = 0u,
SELECT_PICK_ALL = 1u,
SELECT_PICK_NEAREST = 2u,
};
struct SelectInfoData {
int2 cursor;
SelectType mode;
uint _pad0;
};
BLI_STATIC_ASSERT_ALIGN(SelectInfoData, 16)
#ifndef GPU_SHADER
} // namespace blender::draw::select
#endif

View File

@ -1,12 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
#include "select_defines.h"
/* -------------------------------------------------------------------- */
/** \name Select ID for Edit Mesh Selection
* \{ */
GPU_SHADER_INTERFACE_INFO(select_id_iface, "").flat(Type::INT, "id");
GPU_SHADER_INTERFACE_INFO(select_id_iface, "").flat(Type::INT, "select_id");
GPU_SHADER_CREATE_INFO(select_id_flat)
.push_constant(Type::FLOAT, "sizeVertex")
@ -24,7 +25,7 @@ GPU_SHADER_CREATE_INFO(select_id_flat)
GPU_SHADER_CREATE_INFO(select_id_uniform)
.define("UNIFORM_ID")
.push_constant(Type::FLOAT, "sizeVertex")
.push_constant(Type::INT, "id")
.push_constant(Type::INT, "select_id")
.push_constant(Type::FLOAT, "retopologyOffset")
.vertex_in(0, Type::VEC3, "pos")
.fragment_out(0, Type::UINT, "fragColor")
@ -42,4 +43,17 @@ GPU_SHADER_CREATE_INFO(select_id_uniform_clipped)
.additional_info("select_id_uniform")
.additional_info("drw_clipped")
.do_static_compilation(true);
/* Used to patch overlay shaders. */
GPU_SHADER_CREATE_INFO(select_id_patch)
.typedef_source("select_shader_shared.hh")
.vertex_out(select_id_iface)
/* Need to make sure the depth & stencil comparison runs before the fragment shader. */
.early_fragment_test(true)
.uniform_buf(SELECT_DATA, "SelectInfoData", "select_info_buf")
/* Select IDs for instanced draw-calls not using #PassMain. */
.storage_buf(SELECT_ID_IN, Qualifier::READ, "int", "in_select_buf[]")
/* Stores the result of the whole selection drawing. Content depends on selection mode. */
.storage_buf(SELECT_ID_OUT, Qualifier::READ_WRITE, "uint", "out_select_buf[]");
/** \} */

View File

@ -1,4 +1,4 @@
void main()
{
fragColor = floatBitsToUint(intBitsToFloat(id));
fragColor = floatBitsToUint(intBitsToFloat(select_id));
}

View File

@ -4,7 +4,7 @@
void main()
{
#ifndef UNIFORM_ID
id = offset + index;
select_id = offset + index;
#endif
vec3 world_pos = point_object_to_world(pos);

View File

@ -0,0 +1,49 @@
#ifndef SELECT_ENABLE
/* Avoid requesting the select_id when not in selection mode. */
# define select_id_set(select_id)
# define select_id_output(select_id)
#elif defined(GPU_VERTEX_SHADER)
void select_id_set(int id)
{
/* Declared in the create info. */
select_id = id;
}
#elif defined(GPU_FRAGMENT_SHADER)
void select_id_output(int id)
{
if (id == -1) {
/* Invalid index */
return;
}
if (select_info_buf.mode == SELECT_ALL) {
/* Set the bit of the select id in the bitmap. */
atomicOr(out_select_buf[id / 32u], 1u << (uint(id) % 32u));
}
else if (select_info_buf.mode == SELECT_PICK_ALL) {
/* Stores the nearest depth for this select id. */
atomicMin(out_select_buf[id], floatBitsToUint(gl_FragCoord.z));
}
else if (select_info_buf.mode == SELECT_PICK_NEAREST) {
/* Stores the nearest depth with the distance to the cursor. */
/* Distance function to the cursor. Currently a simple pixel ring distance. */
ivec2 coord = abs(ivec2(gl_FragCoord.xy) - select_info_buf.cursor);
uint dist = uint(max(coord.x, coord.y));
uint depth = uint(gl_FragCoord.z * float(0x00FFFFFFu));
/* Reject hits outside of valid range. */
if (dist < 0xFFu) {
/* Packed values to ensure the atomicMin is performed on the whole result. */
atomicMin(out_select_buf[id], (depth << 8u) | dist);
}
}
}
#endif

View File

@ -1201,7 +1201,8 @@ static void drw_engines_enable_from_engine(const RenderEngineType *engine_type,
static void drw_engines_enable_overlays(void)
{
use_drw_engine(&draw_engine_overlay_type);
use_drw_engine((U.experimental.enable_overlay_next) ? &draw_engine_overlay_next_type :
&draw_engine_overlay_type);
}
/**
* Use for select and depth-drawing.
@ -1220,7 +1221,8 @@ static void drw_engine_enable_image_editor(void)
use_drw_engine(&draw_engine_image_type);
}
use_drw_engine(&draw_engine_overlay_type);
use_drw_engine((U.experimental.enable_overlay_next) ? &draw_engine_overlay_next_type :
&draw_engine_overlay_type);
}
static void drw_engines_enable_editors(void)
@ -1238,7 +1240,8 @@ static void drw_engines_enable_editors(void)
SpaceNode *snode = (SpaceNode *)space_data;
if ((snode->flag & SNODE_BACKDRAW) != 0) {
use_drw_engine(&draw_engine_image_type);
use_drw_engine(&draw_engine_overlay_type);
use_drw_engine((U.experimental.enable_overlay_next) ? &draw_engine_overlay_next_type :
&draw_engine_overlay_type);
}
}
}
@ -2450,7 +2453,10 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
DST.options.is_material_select = do_material_sub_selection;
drw_task_graph_init();
/* Get list of enabled engines */
if (use_obedit) {
if (U.experimental.enable_overlay_next) {
use_drw_engine(&draw_engine_select_next_type);
}
else if (use_obedit) {
drw_engines_enable_overlays();
}
else if (!draw_surface) {
@ -2559,6 +2565,9 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
draw_select_framebuffer_depth_only_setup(viewport_size);
GPU_framebuffer_bind(g_select_buffer.framebuffer_depth_only);
GPU_framebuffer_clear_depth(g_select_buffer.framebuffer_depth_only, 1.0f);
/* WORKAROUND: Needed for Select-Next for keeping the same codeflow as Overlay-Next. */
BLI_assert(DRW_viewport_texture_list_get()->depth == NULL);
DRW_viewport_texture_list_get()->depth = g_select_buffer.texture_depth;
/* Start Drawing */
DRW_state_reset();
@ -2571,11 +2580,15 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
if (!select_pass_fn(DRW_SELECT_PASS_PRE, select_pass_user_data)) {
break;
}
DRW_state_lock(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_TEST_ENABLED);
if (!U.experimental.enable_overlay_next) {
DRW_state_lock(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_TEST_ENABLED);
}
drw_engines_draw_scene();
DRW_state_lock(0);
if (!U.experimental.enable_overlay_next) {
DRW_state_lock(0);
}
if (!select_pass_fn(DRW_SELECT_PASS_POST, select_pass_user_data)) {
break;
@ -2584,6 +2597,9 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
DRW_smoke_exit(DST.vmempool);
/* WORKAROUND: Do not leave ownership to the viewport list. */
DRW_viewport_texture_list_get()->depth = NULL;
DRW_state_reset();
drw_engines_disable();
@ -3020,6 +3036,8 @@ void DRW_engines_register(void)
DRW_engine_register(&draw_engine_gpencil_type);
DRW_engine_register(&draw_engine_overlay_type);
DRW_engine_register(&draw_engine_overlay_next_type);
DRW_engine_register(&draw_engine_select_next_type);
DRW_engine_register(&draw_engine_select_type);
DRW_engine_register(&draw_engine_basic_type);
DRW_engine_register(&draw_engine_compositor_type);

View File

@ -1285,11 +1285,11 @@ bool ED_view3d_distance_set_from_location(struct RegionView3D *rv3d,
*/
float ED_scene_grid_scale(const struct Scene *scene, const char **r_grid_unit);
float ED_view3d_grid_scale(const struct Scene *scene,
struct View3D *v3d,
const struct View3D *v3d,
const char **r_grid_unit);
void ED_view3d_grid_steps(const struct Scene *scene,
struct View3D *v3d,
struct RegionView3D *rv3d,
const struct View3D *v3d,
const struct RegionView3D *rv3d,
float r_grid_steps[8]);
/**
* Simulates the grid scale that is actually viewed.

View File

@ -850,15 +850,15 @@ float ED_scene_grid_scale(const Scene *scene, const char **r_grid_unit)
return 1.0f;
}
float ED_view3d_grid_scale(const Scene *scene, View3D *v3d, const char **r_grid_unit)
float ED_view3d_grid_scale(const Scene *scene, const View3D *v3d, const char **r_grid_unit)
{
return v3d->grid * ED_scene_grid_scale(scene, r_grid_unit);
}
#define STEPS_LEN 8
void ED_view3d_grid_steps(const Scene *scene,
View3D *v3d,
RegionView3D *rv3d,
const View3D *v3d,
const RegionView3D *rv3d,
float r_grid_steps[STEPS_LEN])
{
const void *usys;

View File

@ -486,7 +486,7 @@ static bool drw_select_loop_pass(eDRWSelectStage stage, void *user_data)
bool continue_pass = false;
struct DrawSelectLoopUserData *data = user_data;
if (stage == DRW_SELECT_PASS_PRE) {
GPU_select_begin(
GPU_select_begin_next(
data->buffer, data->buffer_len, data->rect, data->gpu_select_mode, data->hits);
/* always run POST after PRE. */
continue_pass = true;
@ -597,7 +597,7 @@ int view3d_opengl_select_ex(ViewContext *vc,
/* Re-use cache (rect must be smaller than the cached)
* other context is assumed to be unchanged */
if (GPU_select_is_cached()) {
GPU_select_begin(buffer, buffer_len, &rect, gpu_select_mode, 0);
GPU_select_begin_next(buffer, buffer_len, &rect, gpu_select_mode, 0);
GPU_select_cache_load_id();
hits = GPU_select_end();
goto finally;

View File

@ -29,6 +29,7 @@ set(INC
# For *_info.hh includes.
../compositor/realtime_compositor
../draw/engines/eevee_next
../draw/engines/select
../draw/engines/workbench
../draw/intern
@ -76,6 +77,7 @@ set(SRC
intern/gpu_platform.cc
intern/gpu_query.cc
intern/gpu_select.c
intern/gpu_select_next.cc
intern/gpu_select_pick.c
intern/gpu_select_sample_query.cc
intern/gpu_shader.cc

View File

@ -50,6 +50,15 @@ void GPU_select_begin(GPUSelectResult *buffer,
const struct rcti *input,
eGPUSelectMode mode,
int oldhits);
/**
* Initialize and provide buffer for results.
* Uses the new Select-Next engine if enabled.
*/
void GPU_select_begin_next(GPUSelectResult *buffer,
const uint buffer_len,
const struct rcti *input,
eGPUSelectMode mode,
int oldhits);
/**
* Loads a new selection id and ends previous query, if any.
* In second pass of selection it also returns

View File

@ -10,6 +10,8 @@
#include <stdlib.h>
#include <string.h>
#include "DNA_userdef_types.h"
#include "GPU_select.h"
#include "BLI_rect.h"
@ -30,6 +32,8 @@ typedef enum eGPUSelectAlgo {
/** Read depth buffer for every drawing pass and extract depths, `gpu_select_pick.c`
* Only sets 4th component (ID) correctly. */
ALGO_GL_PICK = 2,
/** Use Select-Next draw engine. */
ALGO_SELECT_NEXT = 3,
} eGPUSelectAlgo;
typedef struct GPUSelectState {
@ -60,11 +64,12 @@ static GPUSelectState g_select_state = {0};
/** \name Public API
* \{ */
void GPU_select_begin(GPUSelectResult *buffer,
const uint buffer_len,
const rcti *input,
eGPUSelectMode mode,
int oldhits)
static void gpu_select_begin_ex(GPUSelectResult *buffer,
const uint buffer_len,
const rcti *input,
eGPUSelectMode mode,
int oldhits,
bool use_select_next)
{
if (mode == GPU_SELECT_NEAREST_SECOND_PASS) {
/* In the case hits was '-1',
@ -76,7 +81,10 @@ void GPU_select_begin(GPUSelectResult *buffer,
g_select_state.select_is_active = true;
g_select_state.mode = mode;
if (ELEM(g_select_state.mode, GPU_SELECT_PICK_ALL, GPU_SELECT_PICK_NEAREST)) {
if (use_select_next) {
g_select_state.algorithm = ALGO_SELECT_NEXT;
}
else if (ELEM(g_select_state.mode, GPU_SELECT_PICK_ALL, GPU_SELECT_PICK_NEAREST)) {
g_select_state.algorithm = ALGO_GL_PICK;
}
else {
@ -89,6 +97,7 @@ void GPU_select_begin(GPUSelectResult *buffer,
g_select_state.use_cache_needs_init = false;
switch (g_select_state.algorithm) {
case ALGO_SELECT_NEXT:
case ALGO_GL_QUERY: {
g_select_state.use_cache = false;
break;
@ -102,6 +111,10 @@ void GPU_select_begin(GPUSelectResult *buffer,
}
switch (g_select_state.algorithm) {
case ALGO_SELECT_NEXT: {
gpu_select_next_begin(buffer, buffer_len, input, mode);
break;
}
case ALGO_GL_QUERY: {
gpu_select_query_begin(buffer, buffer_len, input, mode, oldhits);
break;
@ -114,6 +127,25 @@ void GPU_select_begin(GPUSelectResult *buffer,
}
}
void GPU_select_begin_next(GPUSelectResult *buffer,
const uint buffer_len,
const rcti *input,
eGPUSelectMode mode,
int oldhits)
{
gpu_select_begin_ex(
buffer, buffer_len, input, mode, oldhits, U.experimental.enable_overlay_next);
}
void GPU_select_begin(GPUSelectResult *buffer,
const uint buffer_len,
const rcti *input,
eGPUSelectMode mode,
int oldhits)
{
gpu_select_begin_ex(buffer, buffer_len, input, mode, oldhits, false);
}
bool GPU_select_load_id(uint id)
{
/* if no selection mode active, ignore */
@ -122,6 +154,11 @@ bool GPU_select_load_id(uint id)
}
switch (g_select_state.algorithm) {
case ALGO_SELECT_NEXT:
/* This shouldn't use this pipeline. */
BLI_assert_unreachable();
return 0;
case ALGO_GL_QUERY: {
return gpu_select_query_load_id(id);
}
@ -137,6 +174,10 @@ uint GPU_select_end(void)
uint hits = 0;
switch (g_select_state.algorithm) {
case ALGO_SELECT_NEXT: {
hits = gpu_select_next_end();
break;
}
case ALGO_GL_QUERY: {
hits = gpu_select_query_end();
break;

View File

@ -0,0 +1,91 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2017 Blender Foundation. All rights reserved. */
/** \file
* \ingroup gpu
*
* Glue to make the new Select-Next engine work with the old GPU select API.
*/
#include <float.h>
#include "BLI_rect.h"
#include "BLI_span.hh"
#include "GPU_select.h"
#include "gpu_select_private.h"
struct GPUSelectNextState {
/** Result buffer set on initialization. */
GPUSelectResult *buffer;
uint buffer_len;
/** Area of the viewport to render / select from. */
rcti rect;
/** Number of hits. Set to -1 if it overflows buffer_len. */
uint hits;
/** Mode of operation. */
eGPUSelectMode mode;
};
static GPUSelectNextState g_state = {};
void gpu_select_next_begin(GPUSelectResult *buffer,
uint buffer_len,
const rcti *input,
eGPUSelectMode mode)
{
g_state.buffer = buffer;
g_state.rect = *input;
g_state.buffer_len = buffer_len;
g_state.mode = mode;
}
int gpu_select_next_get_pick_area_center()
{
BLI_assert(BLI_rcti_size_x(&g_state.rect) == BLI_rcti_size_y(&g_state.rect));
return BLI_rcti_size_x(&g_state.rect) / 2;
}
eGPUSelectMode gpu_select_next_get_mode()
{
return g_state.mode;
}
void gpu_select_next_set_result(GPUSelectResult *hit_buf, uint hit_len)
{
if (hit_len > g_state.buffer_len) {
g_state.hits = -1;
return;
}
blender::MutableSpan<GPUSelectResult> result(g_state.buffer, g_state.buffer_len);
blender::Span<GPUSelectResult> hits(hit_buf, hit_len);
/* TODO(fclem): There might be some conversion to do to align to the other APIs output. */
switch (g_state.mode) {
case eGPUSelectMode::GPU_SELECT_ALL:
result.take_front(hit_len).copy_from(hits);
break;
case eGPUSelectMode::GPU_SELECT_NEAREST_FIRST_PASS:
result.take_front(hit_len).copy_from(hits);
break;
case eGPUSelectMode::GPU_SELECT_NEAREST_SECOND_PASS:
result.take_front(hit_len).copy_from(hits);
break;
case eGPUSelectMode::GPU_SELECT_PICK_ALL:
result.take_front(hit_len).copy_from(hits);
break;
case eGPUSelectMode::GPU_SELECT_PICK_NEAREST:
result.take_front(hit_len).copy_from(hits);
break;
}
g_state.hits = hit_len;
}
uint gpu_select_next_end()
{
return g_state.hits;
}

View File

@ -37,6 +37,19 @@ void gpu_select_query_begin(
bool gpu_select_query_load_id(uint id);
uint gpu_select_query_end(void);
/* gpu_select_next */
void gpu_select_next_begin(GPUSelectResult *buffer,
uint buffer_len,
const rcti *input,
eGPUSelectMode mode);
uint gpu_select_next_end(void);
/* Return a single offset since picking uses squared viewport. */
int gpu_select_next_get_pick_area_center(void);
eGPUSelectMode gpu_select_next_get_mode(void);
void gpu_select_next_set_result(GPUSelectResult *buffer, uint buffer_len);
#define SELECT_ID_NONE ((uint)0xffffffff)
#ifdef __cplusplus

View File

@ -670,9 +670,10 @@ typedef struct UserDef_Experimental {
char use_override_templates;
char enable_eevee_next;
char use_sculpt_texture_paint;
char enable_overlay_next;
char enable_workbench_next;
char use_new_volume_nodes;
char _pad[6];
char _pad[5];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;

View File

@ -6635,6 +6635,11 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
"pop-over");
RNA_def_property_update(prop, 0, "rna_userdef_ui_update");
prop = RNA_def_property(srna, "enable_overlay_next", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "enable_overlay_next", 1);
RNA_def_property_ui_text(
prop, "Overlay Next", "Enable the new Overlay codebase, requires restart");
prop = RNA_def_property(srna, "use_all_linked_data_direct", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_ui_text(
prop,