Files
test/source/blender/draw/engines/overlay/overlay_private.hh
hogan.mastanduno dde9d21b91 OpenXR: VR Advanced Locomotion Phase 1
Includes the following changes to the existing Locomotion system for VR Scene Inspection:
 * new VR Navigation Preferences and VR Session Settings
 * changes to XR raycast logic and its visualization
 * new XR vignette that appears when moving
 * snap turning

Pull Request: https://projects.blender.org/blender/blender/pulls/144241
2025-10-01 22:16:12 +02:00

1198 lines
43 KiB
C++

/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "BKE_context.hh"
#include "BKE_movieclip.h"
#include "BKE_object.hh"
#include "BLI_function_ref.hh"
#include "DNA_space_types.h"
#include "DNA_world_types.h"
#include "GPU_matrix.hh"
#include "DRW_gpu_wrapper.hh"
#include "DRW_render.hh"
#include "UI_resources.hh"
#include "draw_manager.hh"
#include "draw_pass.hh"
#include "draw_view_data.hh"
#include "gpu_shader_create_info.hh"
#include "../select/select_instance.hh"
#include "overlay_shader_shared.hh"
#include "draw_common.hh"
template<> struct blender::gpu::AttrType<VertexClass> {
static constexpr VertAttrType type = VertAttrType::SINT_32;
};
template<> struct blender::gpu::AttrType<StickBoneFlag> {
static constexpr VertAttrType type = VertAttrType::SINT_32;
};
namespace blender::draw::overlay {
struct BoneInstanceData {
/* Keep sync with bone instance vertex format (OVERLAY_InstanceFormats) */
union {
float4x4 mat44;
float mat[4][4];
struct {
float _pad0[3], color_hint_a;
float _pad1[3], color_hint_b;
float _pad2[3], color_a;
float _pad3[3], color_b;
};
struct {
float _pad00[3], amin_a;
float _pad01[3], amin_b;
float _pad02[3], amax_a;
float _pad03[3], amax_b;
};
};
BoneInstanceData() = default;
/**
* Constructor used by meta-ball overlays and expected to be used for drawing
* meta-ball edit circles with armature wire shader that produces wide-lines.
*/
BoneInstanceData(const float4x4 &ob_mat,
const float3 &pos,
const float radius,
const float color[4])
{
mat44[0] = ob_mat[0] * radius;
mat44[1] = ob_mat[1] * radius;
mat44[2] = ob_mat[2] * radius;
mat44[3] = float4(blender::math::transform_point(ob_mat, pos), 0.0f);
set_color(color);
}
BoneInstanceData(const float4x4 &bone_mat, const float4 &bone_color, const float4 &hint_color)
: mat44(bone_mat)
{
set_color(bone_color);
set_hint_color(hint_color);
};
BoneInstanceData(const float4x4 &bone_mat, const float4 &bone_color) : mat44(bone_mat)
{
set_color(bone_color);
};
void set_color(const float4 &bone_color)
{
/* Encoded color into 2 floats to be able to use the matrix to color the custom bones. */
color_a = encode_2f_to_float(bone_color[0], bone_color[1]);
color_b = encode_2f_to_float(bone_color[2], bone_color[3]);
}
void set_hint_color(const float4 &hint_color)
{
/* Encoded color into 2 floats to be able to use the matrix to color the custom bones. */
color_hint_a = encode_2f_to_float(hint_color[0], hint_color[1]);
color_hint_b = encode_2f_to_float(hint_color[2], hint_color[3]);
}
private:
/* Encode 2 units float with byte precision into a float. */
float encode_2f_to_float(float a, float b) const
{
/* NOTE: `b` can go up to 2. Needed to encode wire size. */
return float(int(clamp_f(a, 0.0f, 1.0f) * 255) | (int(clamp_f(b, 0.0f, 2.0f) * 255) << 8));
}
};
using SelectionType = select::SelectionType;
using blender::draw::Framebuffer;
using blender::draw::StorageVectorBuffer;
using blender::draw::Texture;
using blender::draw::TextureFromPool;
using blender::draw::TextureRef;
struct State {
Depsgraph *depsgraph = nullptr;
const ViewLayer *view_layer = nullptr;
const Scene *scene = nullptr;
const View3D *v3d = nullptr;
const SpaceLink *space_data = nullptr;
const ARegion *region = nullptr;
const RegionView3D *rv3d = nullptr;
DRWTextStore *dt = nullptr;
View3DOverlay overlay = {};
eSpace_Type space_type = SPACE_EMPTY;
eContextObjectMode ctx_mode = CTX_MODE_EDIT_MESH;
eObjectMode object_mode = OB_MODE_OBJECT;
const Object *object_active = nullptr;
bool clear_in_front = false;
bool use_in_front = false;
bool is_wireframe_mode = false;
/** Whether we are rendering for an image (viewport render). */
bool is_viewport_image_render = false;
/** Whether we are rendering for an image. */
bool is_image_render = false;
/** True if rendering only to query the depth. Can be for auto-depth rotation. */
bool is_depth_only_drawing = false;
/** Skip drawing particle systems. Prevents self-occlusion issues in Particle Edit mode. */
bool skip_particles = false;
/** When drag-dropping material onto objects to assignment. */
bool is_material_select = false;
/** Whether we should render the background or leave it transparent. */
bool draw_background = false;
/** True if the render engine outputs satisfactory depth information to the depth buffer. */
bool is_render_depth_available = false;
/** Whether we should render a vignette over the scene. */
bool vignette_enabled = false;
/** Should text draw in this mode? */
bool show_text = false;
bool hide_overlays = false;
bool xray_enabled = false;
bool xray_enabled_and_not_wire = false;
/** Can be true even if X-ray Alpha is 1.0. */
bool xray_flag_enabled = false;
/** Brings the active pose armature in front of all objects. */
bool do_pose_xray = false;
/** Add a veil on top of all surfaces to make the active pose armature pop out. */
bool do_pose_fade_geom = false;
float xray_opacity = 0.0f;
short v3d_flag = 0; /* TODO: move to #View3DOverlay. */
short v3d_gridflag = 0; /* TODO: move to #View3DOverlay. */
int cfra = 0;
float3 camera_position = float3(0.0f);
float3 camera_forward = float3(0.0f);
int clipping_plane_count = 0;
/** Active Image properties. Only valid image space only. */
bool is_image_valid = false;
int2 image_size = int2(0);
float2 image_uv_aspect = float2(0.0f);
float2 image_aspect = float2(0.0f);
View::OffsetData offset_data_get() const
{
if (rv3d == nullptr) {
return View::OffsetData();
}
return View::OffsetData(*rv3d);
}
/* Factor to use for wireframe offset.
* Result of GPU_polygon_offset_calc for the current view.
* Only valid at draw time, so use push constant reference instead of copy. */
float ndc_offset_factor = 0.0f;
/** Convenience functions. */
/** Scene geometry is solid. Occlude overlays behind scene geometry. */
bool is_solid() const
{
return xray_opacity == 1.0f;
}
/** Scene geometry is semi-transparent. Fade overlays behind scene geometry (see #XrayFade). */
bool is_xray() const
{
return (xray_opacity < 1.0f) && (xray_opacity > 0.0f);
}
/** Scene geometry is fully transparent. Scene geometry does not occlude overlays. */
bool is_wire() const
{
return xray_opacity == 0.0f;
}
bool is_space_v3d() const
{
return this->space_type == SPACE_VIEW3D;
}
bool is_space_image() const
{
return this->space_type == SPACE_IMAGE;
}
bool is_space_node() const
{
return this->space_type == SPACE_NODE;
}
bool show_extras() const
{
return (this->overlay.flag & V3D_OVERLAY_HIDE_OBJECT_XTRAS) == 0;
}
bool show_face_orientation() const
{
return (this->overlay.flag & V3D_OVERLAY_FACE_ORIENTATION);
}
bool show_bone_selection() const
{
return (this->overlay.flag & V3D_OVERLAY_BONE_SELECT);
}
bool show_wireframes() const
{
return (this->overlay.flag & V3D_OVERLAY_WIREFRAMES);
}
bool show_motion_paths() const
{
return (this->overlay.flag & V3D_OVERLAY_HIDE_MOTION_PATHS) == 0;
}
bool show_bones() const
{
return (this->overlay.flag & V3D_OVERLAY_HIDE_BONES) == 0;
}
bool show_object_origins() const
{
return (this->overlay.flag & V3D_OVERLAY_HIDE_OBJECT_ORIGINS) == 0;
}
bool show_fade_inactive() const
{
return (this->overlay.flag & V3D_OVERLAY_FADE_INACTIVE);
}
bool show_attribute_viewer() const
{
return (this->overlay.flag & V3D_OVERLAY_VIEWER_ATTRIBUTE);
}
bool show_attribute_viewer_text() const
{
return (this->overlay.flag & V3D_OVERLAY_VIEWER_ATTRIBUTE_TEXT);
}
bool show_sculpt_mask() const
{
return (this->overlay.flag & V3D_OVERLAY_SCULPT_SHOW_MASK);
}
bool show_sculpt_face_sets() const
{
return (this->overlay.flag & V3D_OVERLAY_SCULPT_SHOW_FACE_SETS);
}
bool show_sculpt_curves_cage() const
{
return (this->overlay.flag & V3D_OVERLAY_SCULPT_CURVES_CAGE);
}
bool show_light_colors() const
{
return (this->overlay.flag & V3D_OVERLAY_SHOW_LIGHT_COLORS);
}
};
/* Matches Vertex Format. */
struct Vertex {
float3 pos;
VertexClass vclass;
GPU_VERTEX_FORMAT_FUNC(Vertex, pos, vclass);
};
struct VertexBone {
float3 pos;
StickBoneFlag vclass;
GPU_VERTEX_FORMAT_FUNC(VertexBone, pos, vclass);
};
struct VertexWithColor {
float3 pos;
float3 color;
GPU_VERTEX_FORMAT_FUNC(VertexWithColor, pos, color);
};
struct VertShaded {
float3 pos;
VertexClass vclass;
float3 nor;
GPU_VERTEX_FORMAT_FUNC(VertShaded, pos, vclass, nor);
};
/* TODO(fclem): Might be good to remove for simplicity. */
struct VertexTriple {
float2 pos0;
float2 pos1;
float2 pos2;
GPU_VERTEX_FORMAT_FUNC(VertexTriple, pos0, pos1, pos2);
};
/**
* Contains all overlay generic geometry batches.
*/
class ShapeCache {
private:
struct BatchDeleter {
void operator()(gpu::Batch *shader)
{
GPU_BATCH_DISCARD_SAFE(shader);
}
};
using BatchPtr = std::unique_ptr<gpu::Batch, BatchDeleter>;
public:
BatchPtr bone_box;
BatchPtr bone_box_wire;
BatchPtr bone_envelope;
BatchPtr bone_envelope_wire;
BatchPtr bone_octahedron;
BatchPtr bone_octahedron_wire;
BatchPtr bone_sphere;
BatchPtr bone_sphere_wire;
BatchPtr bone_stick;
BatchPtr bone_degrees_of_freedom;
BatchPtr bone_degrees_of_freedom_wire;
BatchPtr grid;
BatchPtr cube_solid;
BatchPtr cursor_circle;
BatchPtr cursor_lines;
BatchPtr quad_wire;
BatchPtr quad_solid;
BatchPtr plain_axes;
BatchPtr single_arrow;
BatchPtr cube;
BatchPtr circle;
BatchPtr empty_sphere;
BatchPtr empty_cone;
BatchPtr cylinder;
BatchPtr capsule_body;
BatchPtr capsule_cap;
BatchPtr arrows;
BatchPtr metaball_wire_circle;
BatchPtr speaker;
BatchPtr camera_distances;
BatchPtr camera_frame;
BatchPtr camera_tria_wire;
BatchPtr camera_tria;
BatchPtr camera_volume;
BatchPtr camera_volume_wire;
BatchPtr sphere_low_detail;
BatchPtr ground_line;
/* Batch drawing a quad with coordinate [0..1] at 0.75 depth. */
BatchPtr image_quad;
BatchPtr light_icon_outer_lines;
BatchPtr light_icon_inner_lines;
BatchPtr light_icon_sun_rays;
BatchPtr light_point_lines;
BatchPtr light_sun_lines;
BatchPtr light_spot_lines;
BatchPtr light_area_disk_lines;
BatchPtr light_area_square_lines;
BatchPtr light_spot_volume;
BatchPtr field_force;
BatchPtr field_wind;
BatchPtr field_vortex;
BatchPtr field_curve;
BatchPtr field_sphere_limit;
BatchPtr field_tube_limit;
BatchPtr field_cone_limit;
BatchPtr lightprobe_cube;
BatchPtr lightprobe_planar;
BatchPtr lightprobe_grid;
ShapeCache();
private:
/* Caller gets ownership of the #gpu::VertBuf. */
template<typename T> gpu::VertBuf *vbo_from_vector(const Vector<T> &vector)
{
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(T::format());
GPU_vertbuf_data_alloc(*vbo, vector.size());
vbo->data<T>().copy_from(vector);
return vbo;
}
};
using StaticShader = gpu::StaticShader;
/**
* Shader module. Shared between instances.
*/
class ShaderModule {
private:
/* Allow StaticShaderCache access to the constructor. */
friend gpu::StaticShaderCache<ShaderModule>;
using StaticCache =
gpu::StaticShaderCache<ShaderModule>[2 /* Selection Instance. */][2 /* Clipping Enabled. */];
static StaticCache &get_static_cache()
{
/** Shared shader module across all engine instances. */
static StaticCache static_cache;
return static_cache;
}
const SelectionType selection_type_;
/** TODO: Support clipping. This global state should be set by the overlay::Instance and switch
* to the shader variations that use clipping. */
const bool clipping_enabled_;
public:
/** Shaders */
StaticShader anti_aliasing = {"overlay_antialiasing"};
StaticShader armature_degrees_of_freedom = shader_clippable("overlay_armature_dof");
StaticShader attribute_viewer_mesh = shader_clippable("overlay_viewer_attribute_mesh");
StaticShader attribute_viewer_pointcloud = shader_clippable(
"overlay_viewer_attribute_pointcloud");
StaticShader attribute_viewer_curve = shader_clippable("overlay_viewer_attribute_curve");
StaticShader attribute_viewer_curves = shader_clippable("overlay_viewer_attribute_curves");
StaticShader background_fill = {"overlay_background"};
StaticShader background_clip_bound = {"overlay_clipbound"};
StaticShader curve_edit_points = shader_clippable("overlay_edit_curves_point");
StaticShader curve_edit_line = shader_clippable("overlay_edit_particle_strand");
StaticShader curve_edit_handles = shader_clippable("overlay_edit_curves_handle");
StaticShader facing = shader_clippable("overlay_facing");
StaticShader grid = {"overlay_grid_next"};
StaticShader grid_background = {"overlay_grid_background"};
StaticShader grid_grease_pencil = shader_clippable("overlay_gpencil_canvas");
StaticShader grid_image = {"overlay_grid_image"};
StaticShader lattice_points = shader_clippable("overlay_edit_lattice_point");
StaticShader lattice_wire = shader_clippable("overlay_edit_lattice_wire");
StaticShader legacy_curve_edit_handles = shader_clippable("overlay_edit_curve_handle");
StaticShader legacy_curve_edit_normals = shader_clippable("overlay_edit_curve_normals");
StaticShader legacy_curve_edit_points = shader_clippable("overlay_edit_curve_point");
StaticShader legacy_curve_edit_wires = shader_clippable("overlay_edit_curve_wire");
StaticShader light_spot_cone = shader_clippable("overlay_extra_spot_cone");
StaticShader mesh_analysis = shader_clippable("overlay_edit_mesh_analysis");
StaticShader mesh_edit_depth = shader_clippable("overlay_edit_mesh_depth");
StaticShader mesh_edit_edge = shader_clippable("overlay_edit_mesh_edge");
StaticShader mesh_edit_face = shader_clippable("overlay_edit_mesh_face");
StaticShader mesh_edit_facedot = shader_clippable("overlay_edit_mesh_facedot");
StaticShader mesh_edit_vert = shader_clippable("overlay_edit_mesh_vert");
StaticShader mesh_edit_skin_root = shader_clippable("overlay_edit_mesh_skin_root");
StaticShader mesh_face_normal = shader_clippable("overlay_mesh_face_normal");
StaticShader mesh_face_normal_subdiv = shader_clippable("overlay_mesh_face_normal_subdiv");
StaticShader mesh_loop_normal = shader_clippable("overlay_mesh_loop_normal");
StaticShader mesh_loop_normal_subdiv = shader_clippable("overlay_mesh_loop_normal_subdiv");
StaticShader mesh_vert_normal = shader_clippable("overlay_mesh_vert_normal");
StaticShader mesh_vert_normal_subdiv = shader_clippable("overlay_mesh_vert_normal_subdiv");
StaticShader motion_path_line = shader_clippable("overlay_motion_path_line");
StaticShader motion_path_vert = shader_clippable("overlay_motion_path_point");
StaticShader outline_detect = {"overlay_outline_detect"};
StaticShader outline_prepass_curves = shader_clippable("overlay_outline_prepass_curves");
StaticShader outline_prepass_gpencil = shader_clippable("overlay_outline_prepass_gpencil");
StaticShader outline_prepass_mesh = shader_clippable("overlay_outline_prepass_mesh");
StaticShader outline_prepass_pointcloud = shader_clippable("overlay_outline_prepass_pointcloud");
StaticShader outline_prepass_wire = shader_clippable("overlay_outline_prepass_wire");
StaticShader paint_region_edge = shader_clippable("overlay_paint_wire");
StaticShader paint_region_face = shader_clippable("overlay_paint_face");
StaticShader paint_region_vert = shader_clippable("overlay_paint_point");
StaticShader paint_texture = shader_clippable("overlay_paint_texture");
StaticShader paint_weight = shader_clippable("overlay_paint_weight");
/* TODO(fclem): Specialization constant. */
StaticShader paint_weight_fake_shading = shader_clippable("overlay_paint_weight_fake_shading");
StaticShader particle_edit_vert = shader_clippable("overlay_edit_particle_point");
StaticShader particle_edit_edge = shader_clippable("overlay_edit_particle_strand");
StaticShader pointcloud_points = shader_clippable("overlay_edit_pointcloud");
StaticShader sculpt_curves = shader_clippable("overlay_sculpt_curves_selection");
StaticShader sculpt_curves_cage = shader_clippable("overlay_sculpt_curves_cage");
StaticShader sculpt_mesh = shader_clippable("overlay_sculpt_mask");
StaticShader uniform_color = shader_clippable("overlay_uniform_color");
StaticShader uv_analysis_stretch_angle = {"overlay_edit_uv_stretching_angle"};
StaticShader uv_analysis_stretch_area = {"overlay_edit_uv_stretching_area"};
StaticShader uv_brush_stencil = {"overlay_edit_uv_stencil_image"};
StaticShader uv_edit_edge = {"overlay_edit_uv_edges"};
StaticShader uv_edit_face = {"overlay_edit_uv_faces"};
StaticShader uv_edit_facedot = {"overlay_edit_uv_face_dots"};
StaticShader uv_edit_vert = {"overlay_edit_uv_verts"};
StaticShader uv_image_borders = {"overlay_edit_uv_tiled_image_borders"};
StaticShader uv_paint_mask = {"overlay_edit_uv_mask_image"};
StaticShader uv_wireframe = {"overlay_wireframe_uv"};
StaticShader xray_fade = {"overlay_xray_fade"};
/** Selectable Shaders */
StaticShader armature_envelope_fill = shader_selectable("overlay_armature_envelope_solid");
StaticShader armature_envelope_outline = shader_selectable("overlay_armature_envelope_outline");
StaticShader armature_shape_outline = shader_selectable("overlay_armature_shape_outline");
StaticShader armature_shape_fill = shader_selectable("overlay_armature_shape_solid");
StaticShader armature_shape_wire = shader_selectable("overlay_armature_shape_wire");
StaticShader armature_shape_wire_strip = shader_selectable("overlay_armature_shape_wire_strip");
StaticShader armature_sphere_outline = shader_selectable("overlay_armature_sphere_outline");
StaticShader armature_sphere_fill = shader_selectable("overlay_armature_sphere_solid");
StaticShader armature_stick = shader_selectable("overlay_armature_stick");
StaticShader armature_wire = shader_selectable("overlay_armature_wire");
StaticShader depth_curves = shader_selectable("overlay_depth_curves");
StaticShader depth_grease_pencil = shader_selectable("overlay_depth_gpencil");
StaticShader depth_mesh = shader_selectable("overlay_depth_mesh");
StaticShader depth_mesh_conservative = shader_selectable("overlay_depth_mesh_conservative");
StaticShader depth_pointcloud = shader_selectable("overlay_depth_pointcloud");
StaticShader extra_shape = shader_selectable("overlay_extra");
StaticShader extra_point = shader_selectable("overlay_extra_point");
StaticShader extra_wire = shader_selectable("overlay_extra_wire");
StaticShader extra_wire_object = shader_selectable("overlay_extra_wire_object");
StaticShader extra_loose_points = shader_selectable("overlay_extra_loose_point");
StaticShader extra_grid = shader_selectable("overlay_extra_grid");
StaticShader extra_ground_line = shader_selectable("overlay_extra_groundline");
StaticShader image_plane = shader_selectable("overlay_image");
StaticShader image_plane_depth_bias = shader_selectable("overlay_image_depth_bias");
StaticShader particle_dot = shader_selectable("overlay_particle_dot");
StaticShader particle_shape = shader_selectable("overlay_particle_shape");
StaticShader particle_hair = shader_selectable("overlay_particle_hair");
StaticShader wireframe_mesh = shader_selectable("overlay_wireframe");
/* Draw objects without edges for the wireframe overlay. */
StaticShader wireframe_points = shader_selectable("overlay_wireframe_points");
StaticShader wireframe_curve = shader_selectable("overlay_wireframe_curve");
StaticShader fluid_grid_lines_flags = shader_selectable_no_clip(
"overlay_volume_gridlines_flags");
StaticShader fluid_grid_lines_flat = shader_selectable_no_clip("overlay_volume_gridlines_flat");
StaticShader fluid_grid_lines_range = shader_selectable_no_clip(
"overlay_volume_gridlines_range");
StaticShader fluid_velocity_streamline = shader_selectable_no_clip(
"overlay_volume_velocity_streamline");
StaticShader fluid_velocity_mac = shader_selectable_no_clip("overlay_volume_velocity_mac");
StaticShader fluid_velocity_needle = shader_selectable_no_clip("overlay_volume_velocity_needle");
/** Module */
/** Only to be used by Instance constructor. */
static ShaderModule &module_get(SelectionType selection_type, bool clipping_enabled);
static void module_free();
private:
ShaderModule(const SelectionType selection_type, const bool clipping_enabled)
: selection_type_(selection_type), clipping_enabled_(clipping_enabled){};
StaticShader shader_clippable(const char *create_info_name);
StaticShader shader_selectable(const char *create_info_name);
StaticShader shader_selectable_no_clip(const char *create_info_name);
};
struct GreasePencilDepthPlane {
/* Plane data to reference as push constant.
* Will be computed just before drawing. */
float4 plane;
/* Center and size of the bounding box of the Grease Pencil object. */
Bounds<float3> bounds;
/* Grease-pencil object resource handle. */
ResourceHandleRange handle;
};
struct Resources : public select::SelectMap {
ShaderModule *shaders = nullptr;
/* Overlay Color. */
Framebuffer overlay_color_only_fb = {"overlay_color_only_fb"};
/* Overlay Color, Line Data. */
Framebuffer overlay_line_only_fb = {"overlay_line_only_fb"};
/* Depth, Overlay Color. */
Framebuffer overlay_fb = {"overlay_fb"};
/* Depth, Overlay Color, Line Data. */
Framebuffer overlay_line_fb = {"overlay_line_fb"};
/* Depth In-Front, Overlay Color. */
Framebuffer overlay_in_front_fb = {"overlay_in_front_fb"};
/* Depth In-Front, Overlay Color, Line Data. */
Framebuffer overlay_line_in_front_fb = {"overlay_line_in_front_fb"};
/* Output Color. */
Framebuffer overlay_output_color_only_fb = {"overlay_output_color_only_fb"};
/* Depth, Output Color. */
Framebuffer overlay_output_fb = {"overlay_output_fb"};
/* Render Frame-buffers. Only used for multiplicative blending on top of the render. */
/* TODO(fclem): Remove the usage of these somehow. This is against design. */
gpu::FrameBuffer *render_fb = nullptr;
gpu::FrameBuffer *render_in_front_fb = nullptr;
/* Target containing line direction and data for line expansion and anti-aliasing. */
TextureFromPool line_tx = {"line_tx"};
/* Target containing overlay color before anti-aliasing. */
TextureFromPool overlay_tx = {"overlay_tx"};
/* Target containing depth of overlays when xray is enabled. */
TextureFromPool xray_depth_tx = {"xray_depth_tx"};
TextureFromPool xray_depth_in_front_tx = {"xray_depth_in_front_tx"};
/* Texture that are usually allocated inside. These are fallback when they aren't.
* They are then wrapped inside the #TextureRefs below. */
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"};
/* 1px texture containing only maximum depth. To be used for fulfilling bindings when depth
* texture is not available or not needed. */
Texture dummy_depth_tx = {"dummy_depth_tx"};
/* Global vector for all grease pencil depth planes.
* Managed by the grease pencil overlay module.
* This is to avoid passing the grease pencil overlay class to other overlay and
* keep draw_grease_pencil as a static function.
* Memory is reference, so we have to use a container with fixed memory. */
detail::SubPassVector<GreasePencilDepthPlane, 16> depth_planes;
int64_t depth_planes_count = 0;
draw::UniformBuffer<UniformData> globals_buf;
UniformData &theme = globals_buf;
draw::UniformArrayBuffer<float4, 6> clip_planes_buf;
/* Wrappers around #DefaultTextureList members. */
TextureRef depth_in_front_tx;
TextureRef color_overlay_tx;
TextureRef color_render_tx;
/**
* Scene depth buffer that can also be used as render target for overlays.
*
* Can only be bound as a texture if either:
* - the current frame-buffer has no depth buffer attached.
* - `state.xray_enabled` is true.
*/
TextureRef depth_tx;
/**
* Depth target.
* Can either be default depth buffer texture from #DefaultTextureList
* or `xray_depth_tx` if X-ray is enabled.
*/
TextureRef depth_target_tx;
TextureRef depth_target_in_front_tx;
/** Copy of the settings the current texture was generated with. Used to detect updates. */
bool weight_ramp_custom = false;
ColorBand weight_ramp_copy = {};
/** Baked color ramp texture from theme and user settings. Maps weight [0..1] to color. */
Texture weight_ramp_tx = {"weight_ramp"};
Vector<MovieClip *> bg_movie_clips;
const ShapeCache &shapes;
Resources(const SelectionType selection_type_, const ShapeCache &shapes_)
: select::SelectMap(selection_type_), shapes(shapes_){};
~Resources()
{
free_movieclips_textures();
}
void update_theme_settings(const DRWContext *ctx, const State &state);
void update_clip_planes(const State &state);
void init(bool clipping_enabled)
{
shaders = &overlay::ShaderModule::module_get(selection_type, clipping_enabled);
shaders->anti_aliasing.ensure_compile_async();
shaders->armature_degrees_of_freedom.ensure_compile_async();
shaders->armature_envelope_fill.ensure_compile_async();
shaders->armature_envelope_outline.ensure_compile_async();
shaders->armature_shape_fill.ensure_compile_async();
shaders->armature_shape_outline.ensure_compile_async();
shaders->armature_shape_wire_strip.ensure_compile_async();
shaders->armature_shape_wire.ensure_compile_async();
shaders->armature_sphere_fill.ensure_compile_async();
shaders->armature_sphere_outline.ensure_compile_async();
shaders->armature_stick.ensure_compile_async();
shaders->armature_wire.ensure_compile_async();
shaders->attribute_viewer_curve.ensure_compile_async();
shaders->attribute_viewer_curves.ensure_compile_async();
shaders->attribute_viewer_mesh.ensure_compile_async();
shaders->attribute_viewer_pointcloud.ensure_compile_async();
shaders->background_fill.ensure_compile_async();
shaders->curve_edit_handles.ensure_compile_async();
shaders->curve_edit_line.ensure_compile_async();
shaders->curve_edit_points.ensure_compile_async();
shaders->depth_curves.ensure_compile_async();
shaders->depth_grease_pencil.ensure_compile_async();
shaders->depth_mesh.ensure_compile_async();
shaders->depth_pointcloud.ensure_compile_async();
shaders->extra_grid.ensure_compile_async();
shaders->extra_ground_line.ensure_compile_async();
shaders->extra_loose_points.ensure_compile_async();
shaders->extra_point.ensure_compile_async();
shaders->extra_shape.ensure_compile_async();
shaders->extra_wire_object.ensure_compile_async();
shaders->extra_wire.ensure_compile_async();
shaders->fluid_grid_lines_flags.ensure_compile_async();
shaders->fluid_grid_lines_flat.ensure_compile_async();
shaders->fluid_grid_lines_range.ensure_compile_async();
shaders->fluid_velocity_mac.ensure_compile_async();
shaders->fluid_velocity_needle.ensure_compile_async();
shaders->fluid_velocity_streamline.ensure_compile_async();
shaders->grid.ensure_compile_async();
shaders->image_plane_depth_bias.ensure_compile_async();
shaders->lattice_points.ensure_compile_async();
shaders->lattice_wire.ensure_compile_async();
shaders->legacy_curve_edit_handles.ensure_compile_async();
shaders->legacy_curve_edit_points.ensure_compile_async();
shaders->legacy_curve_edit_wires.ensure_compile_async();
shaders->light_spot_cone.ensure_compile_async();
shaders->mesh_analysis.ensure_compile_async();
shaders->mesh_edit_depth.ensure_compile_async();
shaders->mesh_edit_edge.ensure_compile_async();
shaders->mesh_edit_face.ensure_compile_async();
shaders->mesh_edit_facedot.ensure_compile_async();
shaders->mesh_edit_skin_root.ensure_compile_async();
shaders->mesh_edit_vert.ensure_compile_async();
shaders->motion_path_line.ensure_compile_async();
shaders->motion_path_vert.ensure_compile_async();
shaders->outline_detect.ensure_compile_async();
shaders->outline_prepass_curves.ensure_compile_async();
shaders->outline_prepass_gpencil.ensure_compile_async();
shaders->outline_prepass_mesh.ensure_compile_async();
shaders->outline_prepass_pointcloud.ensure_compile_async();
shaders->outline_prepass_wire.ensure_compile_async();
shaders->paint_weight_fake_shading.ensure_compile_async();
shaders->particle_dot.ensure_compile_async();
shaders->particle_edit_edge.ensure_compile_async();
shaders->particle_edit_vert.ensure_compile_async();
shaders->particle_hair.ensure_compile_async();
shaders->particle_shape.ensure_compile_async();
shaders->pointcloud_points.ensure_compile_async();
shaders->uniform_color.ensure_compile_async();
shaders->wireframe_curve.ensure_compile_async();
shaders->wireframe_mesh.ensure_compile_async();
shaders->wireframe_points.ensure_compile_async();
}
void begin_sync(int clipping_plane_count)
{
SelectMap::begin_sync(clipping_plane_count);
free_movieclips_textures();
}
void acquire(const DRWContext *draw_ctx, const State &state)
{
DefaultTextureList &viewport_textures = *draw_ctx->viewport_texture_list_get();
DefaultFramebufferList &viewport_framebuffers = *draw_ctx->viewport_framebuffer_list_get();
this->depth_tx.wrap(viewport_textures.depth);
this->depth_in_front_tx.wrap(viewport_textures.depth_in_front);
this->color_overlay_tx.wrap(viewport_textures.color_overlay);
this->color_render_tx.wrap(viewport_textures.color);
this->render_fb = viewport_framebuffers.default_fb;
this->render_in_front_fb = viewport_framebuffers.in_front_fb;
int2 render_size = int2(this->depth_tx.size());
if (state.xray_enabled) {
/* For X-ray we render the scene to a separate depth buffer. */
this->xray_depth_tx.acquire(render_size, gpu::TextureFormat::SFLOAT_32_DEPTH_UINT_8);
this->depth_target_tx.wrap(this->xray_depth_tx);
/* TODO(fclem): Remove mandatory allocation. */
this->xray_depth_in_front_tx.acquire(render_size,
gpu::TextureFormat::SFLOAT_32_DEPTH_UINT_8);
this->depth_target_in_front_tx.wrap(this->xray_depth_in_front_tx);
}
else {
/* TODO(fclem): Remove mandatory allocation. */
if (!this->depth_in_front_tx.is_valid()) {
this->depth_in_front_alloc_tx.acquire(render_size,
gpu::TextureFormat::SFLOAT_32_DEPTH_UINT_8);
this->depth_in_front_tx.wrap(this->depth_in_front_alloc_tx);
}
this->depth_target_tx.wrap(this->depth_tx);
this->depth_target_in_front_tx.wrap(this->depth_in_front_tx);
}
/* TODO: Better semantics using a switch? */
if (!this->color_overlay_tx.is_valid()) {
/* Likely to be the selection case. Allocate dummy texture and bind only depth buffer. */
this->color_overlay_alloc_tx.acquire(int2(1, 1), gpu::TextureFormat::SRGBA_8_8_8_8);
this->color_render_alloc_tx.acquire(int2(1, 1), gpu::TextureFormat::SRGBA_8_8_8_8);
this->color_overlay_tx.wrap(this->color_overlay_alloc_tx);
this->color_render_tx.wrap(this->color_render_alloc_tx);
this->line_tx.acquire(int2(1, 1), gpu::TextureFormat::UNORM_8_8_8_8);
this->overlay_tx.acquire(int2(1, 1), gpu::TextureFormat::SRGBA_8_8_8_8);
this->overlay_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->depth_target_tx));
this->overlay_line_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->depth_target_tx));
this->overlay_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->depth_target_tx));
this->overlay_line_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->depth_target_tx));
}
else {
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE |
GPU_TEXTURE_USAGE_ATTACHMENT;
this->line_tx.acquire(render_size, gpu::TextureFormat::UNORM_8_8_8_8, usage);
this->overlay_tx.acquire(render_size, gpu::TextureFormat::SRGBA_8_8_8_8, usage);
this->overlay_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->depth_target_tx),
GPU_ATTACHMENT_TEXTURE(this->overlay_tx));
this->overlay_line_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->depth_target_tx),
GPU_ATTACHMENT_TEXTURE(this->overlay_tx),
GPU_ATTACHMENT_TEXTURE(this->line_tx));
this->overlay_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->depth_target_in_front_tx),
GPU_ATTACHMENT_TEXTURE(this->overlay_tx));
this->overlay_line_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->depth_target_in_front_tx),
GPU_ATTACHMENT_TEXTURE(this->overlay_tx),
GPU_ATTACHMENT_TEXTURE(this->line_tx));
}
this->overlay_line_only_fb.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(this->overlay_tx),
GPU_ATTACHMENT_TEXTURE(this->line_tx));
this->overlay_color_only_fb.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(this->overlay_tx));
this->overlay_output_color_only_fb.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(this->color_overlay_tx));
this->overlay_output_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->depth_tx),
GPU_ATTACHMENT_TEXTURE(this->color_overlay_tx));
}
void release()
{
this->line_tx.release();
this->overlay_tx.release();
this->xray_depth_tx.release();
this->xray_depth_in_front_tx.release();
this->depth_in_front_alloc_tx.release();
this->color_overlay_alloc_tx.release();
this->color_render_alloc_tx.release();
free_movieclips_textures();
}
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 = ob_ref.is_active(state.object_active);
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 fall back 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;
}
}
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.colors.wire;
}
switch (theme_id) {
case TH_WIRE_EDIT:
return theme.colors.wire_edit;
case TH_ACTIVE:
return theme.colors.active_object;
case TH_SELECT:
return theme.colors.object_select;
case TH_TRANSFORM:
return theme.colors.transform;
case TH_SPEAKER:
return theme.colors.speaker;
case TH_CAMERA:
return theme.colors.camera;
case TH_EMPTY:
return theme.colors.empty;
case TH_LIGHT:
return theme.colors.light;
default:
return theme.colors.wire;
}
}
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);
}
float4 background_blend_color(ThemeColorID theme_id) const
{
float4 color;
UI_GetThemeColorBlendShade4fv(theme_id, TH_BACK, 0.5, 0, color);
return color;
}
float4 object_background_blend_color(const ObjectRef &ob_ref, const State &state) const
{
ThemeColorID theme_id = object_wire_theme_id(ob_ref, state);
return background_blend_color(theme_id);
}
float4 background_color_get(const State &state)
{
if (state.v3d->shading.background_type == V3D_SHADING_BACKGROUND_WORLD) {
if (state.scene->world) {
return float4(float3(&state.scene->world->horr), 0.0f);
}
}
else if (state.v3d->shading.background_type == V3D_SHADING_BACKGROUND_VIEWPORT) {
return state.v3d->shading.background_color;
}
float4 color;
UI_GetThemeColor3fv(TH_BACK, color);
return color;
}
void free_movieclips_textures()
{
/* Free Movie clip textures after rendering */
for (MovieClip *clip : bg_movie_clips) {
BKE_movieclip_free_gputexture(clip);
}
bg_movie_clips.clear();
}
static float vertex_size_get()
{
/* M_SQRT2 to be at least the same size of the old square */
return max_ff(1.0f, UI_GetThemeValuef(TH_VERTEX_SIZE) * float(M_SQRT2) / 2.0f);
}
/** Convenience functions. */
/* Returns true if drawing for any selection mode. */
bool is_selection() const
{
return this->selection_type != SelectionType::DISABLED;
}
};
/* List of flat objects draw-calls.
* In order to not loose selection display of flat objects view from the side,
* we store them in a list and add them to the pass just in time if their flat side is
* perpendicular to the view. */
/* Reference to a flat object.
* Allow deferred rendering condition of flat object for special purpose. */
struct FlatObjectRef {
gpu::Batch *geom;
ResourceHandleRange handle;
int flattened_axis_id;
/* Returns flat axis index if only one axis is flat. Returns -1 otherwise. */
static int flat_axis_index_get(const Object *ob)
{
BLI_assert(ELEM(ob->type,
OB_MESH,
OB_CURVES_LEGACY,
OB_SURF,
OB_FONT,
OB_CURVES,
OB_POINTCLOUD,
OB_VOLUME));
float dim[3];
BKE_object_dimensions_get(ob, dim);
if (dim[0] == 0.0f) {
return 0;
}
if (dim[1] == 0.0f) {
return 1;
}
if (dim[2] == 0.0f) {
return 2;
}
return -1;
}
using Callback = FunctionRef<void(gpu::Batch *geom, ResourceHandleRange handle)>;
/* Execute callback for every handles that is orthogonal to the view.
* Note: Only works in orthogonal view. */
void if_flat_axis_orthogonal_to_view(Manager &manager, const View &view, Callback callback) const
{
const float4x4 &object_to_world =
manager.matrix_buf.current().get_or_resize(handle.resource_index()).model;
float3 view_forward = view.forward();
float3 axis_not_flat_a = (flattened_axis_id == 0) ? object_to_world.y_axis() :
object_to_world.x_axis();
float3 axis_not_flat_b = (flattened_axis_id == 1) ? object_to_world.z_axis() :
object_to_world.y_axis();
float3 axis_flat = math::cross(axis_not_flat_a, axis_not_flat_b);
if (math::abs(math::dot(view_forward, axis_flat)) < 1e-3f) {
callback(geom, handle);
}
}
};
/**
* Buffer containing instances of a certain shape.
*/
template<typename InstanceDataT> struct ShapeInstanceBuf : private select::SelectBuf {
StorageVectorBuffer<InstanceDataT> data_buf;
ShapeInstanceBuf(const SelectionType selection_type, const char *name = nullptr)
: select::SelectBuf(selection_type), data_buf(name){};
void clear()
{
this->select_clear();
data_buf.clear();
}
void append(const InstanceDataT &data, select::ID select_id)
{
this->select_append(select_id);
data_buf.append(data);
}
void end_sync(PassSimple::Sub &pass, gpu::Batch *shape)
{
if (data_buf.is_empty()) {
return;
}
this->select_bind(pass);
data_buf.push_update();
pass.bind_ssbo("data_buf", &data_buf);
pass.draw(shape, data_buf.size());
}
void end_sync(PassSimple::Sub &pass,
gpu::Batch *shape,
GPUPrimType primitive_type,
uint primitive_len)
{
if (data_buf.is_empty()) {
return;
}
this->select_bind(pass);
data_buf.push_update();
pass.bind_ssbo("data_buf", &data_buf);
pass.draw_expand(shape, primitive_type, primitive_len, data_buf.size());
}
};
struct VertexPrimitiveBuf {
protected:
select::SelectBuf select_buf;
StorageVectorBuffer<VertexData> data_buf;
int color_id = 0;
VertexPrimitiveBuf(const SelectionType selection_type, const char *name = nullptr)
: select_buf(selection_type), data_buf(name){};
void append(const float3 &position, const float4 &color)
{
data_buf.append({float4(position, 0.0f), color});
}
void end_sync(PassSimple::Sub &pass, GPUPrimType primitive)
{
if (data_buf.is_empty()) {
return;
}
select_buf.select_bind(pass);
data_buf.push_update();
pass.bind_ssbo("data_buf", &data_buf);
pass.push_constant("colorid", color_id);
pass.draw_procedural(primitive, 1, data_buf.size());
}
public:
void clear()
{
select_buf.select_clear();
data_buf.clear();
color_id = 0;
}
};
struct PointPrimitiveBuf : public VertexPrimitiveBuf {
public:
PointPrimitiveBuf(const SelectionType selection_type, const char *name = nullptr)
: VertexPrimitiveBuf(selection_type, name)
{
}
void append(const float3 &position,
const float4 &color,
select::ID select_id = select::SelectMap::select_invalid_id())
{
select_buf.select_append(select_id);
VertexPrimitiveBuf::append(position, color);
}
void append(const float3 &position, const int color_id, select::ID select_id)
{
this->color_id = color_id;
append(position, float4(), select_id);
}
void end_sync(PassSimple::Sub &pass)
{
VertexPrimitiveBuf::end_sync(pass, GPU_PRIM_POINTS);
}
};
struct LinePrimitiveBuf : public VertexPrimitiveBuf {
public:
LinePrimitiveBuf(const SelectionType selection_type, const char *name = nullptr)
: VertexPrimitiveBuf(selection_type, name)
{
}
void append(const float3 &start,
const float3 &end,
const float4 &color,
select::ID select_id = select::SelectMap::select_invalid_id())
{
select_buf.select_append(select_id);
VertexPrimitiveBuf::append(start, color);
VertexPrimitiveBuf::append(end, color);
}
void append(const float3 &start,
const float3 &end,
const int color_id,
select::ID select_id = select::SelectMap::select_invalid_id())
{
this->color_id = color_id;
append(start, end, float4(), select_id);
}
void end_sync(PassSimple::Sub &pass)
{
VertexPrimitiveBuf::end_sync(pass, GPU_PRIM_LINES);
}
};
/* Consider instance any object form a set or a dupli system.
* This hides some overlay to avoid making the viewport unreadable. */
static inline bool is_from_dupli_or_set(const Object *ob)
{
return ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI);
}
/* Consider instance any object form a set or a dupli system.
* This hides some overlay to avoid making the viewport unreadable. */
static inline bool is_from_dupli_or_set(const ObjectRef &ob_ref)
{
return is_from_dupli_or_set(ob_ref.object);
}
} // namespace blender::draw::overlay