diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index e977b84c220..7909f047f77 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -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")), ), ) diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 3309364c074..b1200530b05 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -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 diff --git a/source/blender/draw/engines/overlay/overlay_armature.cc b/source/blender/draw/engines/overlay/overlay_armature.cc index 20a9cde5310..d57311528dd 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.cc +++ b/source/blender/draw/engines/overlay/overlay_armature.cc @@ -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) diff --git a/source/blender/draw/engines/overlay/overlay_background.hh b/source/blender/draw/engines/overlay/overlay_background.hh new file mode 100644 index 00000000000..c0834371f61 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_background.hh @@ -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 class Background { + using ResourcesT = Resources; + + 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 diff --git a/source/blender/draw/engines/overlay/overlay_empty.hh b/source/blender/draw/engines/overlay/overlay_empty.hh new file mode 100644 index 00000000000..89f01b8b1ee --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_empty.hh @@ -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 class Empties { + using SelectID = typename SelectEngineT::ID; + using ResourcesT = Resources; + using EmptyInstanceBuf = ShapeInstanceBuf; + + 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 diff --git a/source/blender/draw/engines/overlay/overlay_engine.cc b/source/blender/draw/engines/overlay/overlay_engine.cc index 0fcfc76e5ca..22fc6e01bc0 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.cc +++ b/source/blender/draw/engines/overlay/overlay_engine.cc @@ -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( - 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(vedata); + + if (ved->instance == nullptr) { + ved->instance = new Instance(); + } + + reinterpret_cast(ved->instance)->init(); +} + +static void OVERLAY_next_cache_init(void *vedata) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast(reinterpret_cast(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(reinterpret_cast(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(reinterpret_cast(vedata)->instance)->end_sync(); +} + +static void OVERLAY_next_draw_scene(void *vedata) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + + reinterpret_cast(reinterpret_cast(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 diff --git a/source/blender/draw/engines/overlay/overlay_engine.h b/source/blender/draw/engines/overlay/overlay_engine.h index a33ea17dc1e..caf380c87af 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.h +++ b/source/blender/draw/engines/overlay/overlay_engine.h @@ -12,6 +12,7 @@ extern "C" { #endif extern DrawEngineType draw_engine_overlay_type; +extern DrawEngineType draw_engine_overlay_next_type; #ifdef __cplusplus } diff --git a/source/blender/draw/engines/overlay/overlay_grid.cc b/source/blender/draw/engines/overlay/overlay_grid.cc index b38825723f4..6a688a02f4e 100644 --- a/source/blender/draw/engines/overlay/overlay_grid.cc +++ b/source/blender/draw/engines/overlay/overlay_grid.cc @@ -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(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); diff --git a/source/blender/draw/engines/overlay/overlay_grid.hh b/source/blender/draw/engines/overlay/overlay_grid.hh new file mode 100644 index 00000000000..e00fc35f3a0 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_grid.hh @@ -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 class Grid { + using ResourcesT = Resources; + + private: + UniformBuffer 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 diff --git a/source/blender/draw/engines/overlay/overlay_instance.cc b/source/blender/draw/engines/overlay/overlay_instance.cc new file mode 100644 index 00000000000..01429aae1e2 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_instance.cc @@ -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 void Instance::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 void Instance::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 void Instance::object_sync(ObjectRef &ob_ref, Manager &manager) +{ + const bool in_edit_mode = object_is_edit_mode(ob_ref.object); + const bool needs_prepass = true; /* TODO */ + + 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 void Instance::end_sync() +{ + resources.end_sync(); + + metaballs.end_sync(resources, shapes, state); + empties.end_sync(resources, shapes, state); +} + +template void Instance::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::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); + +} // 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); +} diff --git a/source/blender/draw/engines/overlay/overlay_instance.hh b/source/blender/draw/engines/overlay/overlay_instance.hh new file mode 100644 index 00000000000..7fa290fdd79 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_instance.hh @@ -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 resources = {overlay::ShaderModule::module_get()}; + State state; + + /** Overlay types. */ + Background background; + Prepass prepass; + Metaballs metaballs; + Empties empties; + Grid 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::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); + +} // namespace blender::draw::overlay diff --git a/source/blender/draw/engines/overlay/overlay_metaball.hh b/source/blender/draw/engines/overlay/overlay_metaball.hh new file mode 100644 index 00000000000..4d6f2882091 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_metaball.hh @@ -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 class Metaballs { + using SelectID = typename SelectEngineT::ID; + using ResourcesT = Resources; + using SphereOutlineInstanceBuf = ShapeInstanceBuf; + + 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(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(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 diff --git a/source/blender/draw/engines/overlay/overlay_prepass.hh b/source/blender/draw/engines/overlay/overlay_prepass.hh new file mode 100644 index 00000000000..09c8977e106 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_prepass.hh @@ -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 class Prepass { + using SelectID = typename SelectEngineT::ID; + using ResourcesT = Resources; + + 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 diff --git a/source/blender/draw/engines/overlay/overlay_private.hh b/source/blender/draw/engines/overlay/overlay_private.hh index 6c6e5cc7fbd..f3040d0958a 100644 --- a/source/blender/draw/engines/overlay/overlay_private.hh +++ b/source/blender/draw/engines/overlay/overlay_private.hh @@ -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 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 struct Resources : public SelectEngineT::SelectMap { + ShaderModule &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 &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 diff --git a/source/blender/draw/engines/overlay/overlay_shader.cc b/source/blender/draw/engines/overlay/overlay_shader.cc index f549a66a537..d29cb7ba400 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.cc +++ b/source/blender/draw/engines/overlay/overlay_shader.cc @@ -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 *ShaderModule::g_shader_module = nullptr; + +template<> +ShaderModule *ShaderModule::g_shader_module = + nullptr; + +void shader_module_free() +{ + ShaderModule::module_free(); + ShaderModule::module_free(); +} + +} // namespace blender::draw::overlay diff --git a/source/blender/draw/engines/overlay/overlay_shader.hh b/source/blender/draw/engines/overlay/overlay_shader.hh new file mode 100644 index 00000000000..89eaf99a8b0 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_shader.hh @@ -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 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( + GPU_shader_create_info_get(create_info_name)); + + info.define(SelectEngineT::shader_define); + + this->shader_ = GPU_shader_create_from_info( + reinterpret_cast(&info)); + } + + /* WORKAROUND: Create a shader using a patch function to patch the create info. */ + ShaderGeometry(const char *create_info_name, + std::function patch) + { + gpu::shader::ShaderCreateInfo info = + *reinterpret_cast( + GPU_shader_create_info_get(create_info_name)); + + patch(info); + SelectEngineT::SelectShader::patch(info); + + this->shader_ = GPU_shader_create_from_info( + reinterpret_cast(&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 diff --git a/source/blender/draw/engines/overlay/overlay_shader_shared.h b/source/blender/draw/engines/overlay/overlay_shader_shared.h index a04858a4a67..c38de0a1057 100644 --- a/source/blender/draw/engines/overlay/overlay_shader_shared.h +++ b/source/blender/draw/engines/overlay/overlay_shader_shared.h @@ -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 } diff --git a/source/blender/draw/engines/overlay/overlay_shape.hh b/source/blender/draw/engines/overlay/overlay_shape.hh new file mode 100644 index 00000000000..34de3c9858a --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_shape.hh @@ -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 +struct ShapeInstanceBuf : private SelectEngineT::SelectBuf { + using SelectID = typename SelectEngineT::ID; + + StorageVectorBuffer 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 &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 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 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 ring_vertices(const float radius, const int segments) + { + Vector 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 sphere_axes_circles(const float radius, + const VertexClass vclass, + const int segments) + { + Vector ring = ring_vertices(radius, segments); + + Vector 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 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 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 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 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 ring = ring_vertices(1.0f, resolution); + + Vector 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 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 ring = ring_vertices(1.0f, resolution); + + Vector 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 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 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 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 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 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 *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 ring = ring_vertices(radius, resolution); + + Vector 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 \ No newline at end of file diff --git a/source/blender/draw/engines/overlay/shaders/infos/overlay_extra_info.hh b/source/blender/draw/engines/overlay/shaders/infos/overlay_extra_info.hh index 9e6b6e5f737..aff132cd414 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/overlay_extra_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/overlay_extra_info.hh @@ -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. */ diff --git a/source/blender/draw/engines/overlay/shaders/overlay_armature_sphere_solid_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_armature_sphere_solid_vert.glsl index 3d2dfc018bb..e786e38acc8 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_armature_sphere_solid_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_armature_sphere_solid_vert.glsl @@ -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); diff --git a/source/blender/draw/engines/overlay/shaders/overlay_armature_wire_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_armature_wire_frag.glsl index 2c454a8becd..5bf9f9491b9 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_armature_wire_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_armature_wire_frag.glsl @@ -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); } diff --git a/source/blender/draw/engines/overlay/shaders/overlay_depth_only_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_depth_only_frag.glsl index 59efdd8d538..412dedff320 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_depth_only_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_depth_only_frag.glsl @@ -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); } diff --git a/source/blender/draw/engines/overlay/shaders/overlay_depth_only_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_depth_only_vert.glsl index d403890f44e..2a3a8e7b560 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_depth_only_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_depth_only_vert.glsl @@ -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); diff --git a/source/blender/draw/engines/overlay/shaders/overlay_extra_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_extra_frag.glsl index bf29b2b057e..4d4d9db5909 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_extra_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_extra_frag.glsl @@ -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); } diff --git a/source/blender/draw/engines/overlay/shaders/overlay_extra_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_extra_vert.glsl index 7b1e29525e9..c2e54f031f8 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_extra_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_extra_vert.glsl @@ -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; diff --git a/source/blender/draw/engines/select/select_defines.h b/source/blender/draw/engines/select/select_defines.h new file mode 100644 index 00000000000..7f77b77be1d --- /dev/null +++ b/source/blender/draw/engines/select/select_defines.h @@ -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 diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c index afe36903cfa..741257a0526 100644 --- a/source/blender/draw/engines/select/select_engine.c +++ b/source/blender/draw/engines/select/select_engine.c @@ -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); } diff --git a/source/blender/draw/engines/select/select_engine.h b/source/blender/draw/engines/select/select_engine.h index 2151d605578..4bd6afd1cd1 100644 --- a/source/blender/draw/engines/select/select_engine.h +++ b/source/blender/draw/engines/select/select_engine.h @@ -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 diff --git a/source/blender/draw/engines/select/select_instance.cc b/source/blender/draw/engines/select/select_instance.cc new file mode 100644 index 00000000000..6d422a1d190 --- /dev/null +++ b/source/blender/draw/engines/select/select_instance.cc @@ -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; + +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(vedata); + + if (ved->instance == nullptr) { + ved->instance = new Instance(); + } + + reinterpret_cast(ved->instance)->init(); +} + +static void SELECT_next_cache_init(void *vedata) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast(reinterpret_cast(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(reinterpret_cast(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(reinterpret_cast(vedata)->instance)->end_sync(); +} + +static void SELECT_next_draw_scene(void *vedata) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + + reinterpret_cast(reinterpret_cast(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, +}; + +/** \} */ \ No newline at end of file diff --git a/source/blender/draw/engines/select/select_instance.hh b/source/blender/draw/engines/select/select_instance.hh new file mode 100644 index 00000000000..d96ebd7722a --- /dev/null +++ b/source/blender/draw/engines/select/select_instance.hh @@ -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 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 select_id_map; +#ifdef DEBUG + /** Debug map containing a copy of the object name. */ + Vector map_names; +#endif + /** Stores the result of the whole selection drawing. Content depends on selection mode. */ + StorageArrayBuffer select_output_buf = {"select_output_buf"}; + /** Dummy buffer. Might be better to remove, but simplify the shader create info patching. */ + StorageArrayBuffer dummy_select_buf = {"dummy_select_buf"}; + /** Uniform buffer to bind to all passes to pass information about the selection state. */ + UniformBuffer 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 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 \ No newline at end of file diff --git a/source/blender/draw/engines/select/select_shader_shared.hh b/source/blender/draw/engines/select/select_shader_shared.hh new file mode 100644 index 00000000000..0538561d39b --- /dev/null +++ b/source/blender/draw/engines/select/select_shader_shared.hh @@ -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 diff --git a/source/blender/draw/engines/select/shaders/infos/select_id_info.hh b/source/blender/draw/engines/select/shaders/infos/select_id_info.hh index c8779927024..5e1809b2acd 100644 --- a/source/blender/draw/engines/select/shaders/infos/select_id_info.hh +++ b/source/blender/draw/engines/select/shaders/infos/select_id_info.hh @@ -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[]"); + /** \} */ diff --git a/source/blender/draw/engines/select/shaders/select_id_frag.glsl b/source/blender/draw/engines/select/shaders/select_id_frag.glsl index aecf57b4766..a4ba7ad35e5 100644 --- a/source/blender/draw/engines/select/shaders/select_id_frag.glsl +++ b/source/blender/draw/engines/select/shaders/select_id_frag.glsl @@ -1,4 +1,4 @@ void main() { - fragColor = floatBitsToUint(intBitsToFloat(id)); + fragColor = floatBitsToUint(intBitsToFloat(select_id)); } diff --git a/source/blender/draw/engines/select/shaders/select_id_vert.glsl b/source/blender/draw/engines/select/shaders/select_id_vert.glsl index b0885fd526b..ff26d82afa0 100644 --- a/source/blender/draw/engines/select/shaders/select_id_vert.glsl +++ b/source/blender/draw/engines/select/shaders/select_id_vert.glsl @@ -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); diff --git a/source/blender/draw/engines/select/shaders/select_lib.glsl b/source/blender/draw/engines/select/shaders/select_lib.glsl new file mode 100644 index 00000000000..fc17c6edef8 --- /dev/null +++ b/source/blender/draw/engines/select/shaders/select_lib.glsl @@ -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 diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 9ea93637b01..575ab96a2df 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -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); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 9e0c1404761..6ee8779bfb8 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -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. diff --git a/source/blender/editors/space_view3d/view3d_draw.cc b/source/blender/editors/space_view3d/view3d_draw.cc index 00573d41598..df1b5e3e5cf 100644 --- a/source/blender/editors/space_view3d/view3d_draw.cc +++ b/source/blender/editors/space_view3d/view3d_draw.cc @@ -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; diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index bd17c673dc8..633515cf09b 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -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; diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 4305778d21f..e1bcf7b43ea 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -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 diff --git a/source/blender/gpu/GPU_select.h b/source/blender/gpu/GPU_select.h index 7a7496430af..34358d4539e 100644 --- a/source/blender/gpu/GPU_select.h +++ b/source/blender/gpu/GPU_select.h @@ -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 diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c index 1037801d782..98a990175d9 100644 --- a/source/blender/gpu/intern/gpu_select.c +++ b/source/blender/gpu/intern/gpu_select.c @@ -10,6 +10,8 @@ #include #include +#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; diff --git a/source/blender/gpu/intern/gpu_select_next.cc b/source/blender/gpu/intern/gpu_select_next.cc new file mode 100644 index 00000000000..17724fa4fec --- /dev/null +++ b/source/blender/gpu/intern/gpu_select_next.cc @@ -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 + +#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 result(g_state.buffer, g_state.buffer_len); + blender::Span 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; +} diff --git a/source/blender/gpu/intern/gpu_select_private.h b/source/blender/gpu/intern/gpu_select_private.h index 47484727f8c..0873cbfad1e 100644 --- a/source/blender/gpu/intern/gpu_select_private.h +++ b/source/blender/gpu/intern/gpu_select_private.h @@ -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 diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 70aed0eab5b..1412d3d5ae1 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -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; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index af14bfe70a1..efd3ecc0202 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -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,