Overlay-Next Camera

Overlay-Next version of Camera.

Rel #102179

#109059 was used as a reference.

Pull Request: https://projects.blender.org/blender/blender/pulls/124122
This commit is contained in:
Laurynas Duburas
2024-07-30 20:00:30 +02:00
committed by Clément Foucault
parent 4d85c03815
commit d4ce2dfe60
9 changed files with 899 additions and 66 deletions

View File

@@ -292,6 +292,7 @@ set(SRC
engines/overlay/overlay_next_background.hh
engines/overlay/overlay_next_bounds.hh
engines/overlay/overlay_next_empty.hh
engines/overlay/overlay_next_camera.hh
engines/overlay/overlay_next_grid.hh
engines/overlay/overlay_next_instance.hh
engines/overlay/overlay_next_lattice.hh

View File

@@ -0,0 +1,555 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "BKE_camera.h"
#include "DEG_depsgraph_query.hh"
#include "BKE_movieclip.h"
#include "BKE_tracking.h"
#include "DNA_camera_types.h"
#include "ED_view3d.hh"
#include "draw_manager_text.hh"
#include "overlay_next_empty.hh"
#include "overlay_next_private.hh"
static float camera_offaxis_shiftx_get(const Scene *scene,
const Object *ob,
float corner_x,
bool right_eye)
{
const Camera *cam = static_cast<const Camera *>(ob->data);
if (cam->stereo.convergence_mode == CAM_S3D_OFFAXIS) {
const char *viewnames[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
const float shiftx = BKE_camera_multiview_shift_x(&scene->r, ob, viewnames[right_eye]);
const float delta_shiftx = shiftx - cam->shiftx;
const float width = corner_x * 2.0f;
return delta_shiftx * width;
}
return 0.0;
}
namespace blender::draw::overlay {
struct CameraInstanceData : public ExtraInstanceData {
public:
float &volume_start = color_[2];
float &volume_end = color_[3];
float &depth = color_[3];
float &focus = color_[3];
float4x4 &matrix = object_to_world_;
float &dist_color_id = matrix[0][3];
float &corner_x = matrix[0][3];
float &corner_y = matrix[1][3];
float &center_x = matrix[2][3];
float &clip_start = matrix[2][3];
float &mist_start = matrix[2][3];
float &center_y = matrix[3][3];
float &clip_end = matrix[3][3];
float &mist_end = matrix[3][3];
CameraInstanceData(const CameraInstanceData &data)
: CameraInstanceData(data.object_to_world_, data.color_)
{
}
CameraInstanceData(const float4x4 &p_matrix, const float4 &color)
: ExtraInstanceData(p_matrix, color, 1.0f){};
};
class Cameras {
using CameraInstanceBuf = ShapeInstanceBuf<ExtraInstanceData>;
private:
PassSimple ps_ = {"Cameras"};
struct CallBuffers {
const SelectionType selection_type_;
CameraInstanceBuf distances_buf = {selection_type_, "camera_distances_buf"};
CameraInstanceBuf frame_buf = {selection_type_, "camera_frame_buf"};
CameraInstanceBuf tria_buf = {selection_type_, "camera_tria_buf"};
CameraInstanceBuf tria_wire_buf = {selection_type_, "camera_tria_wire_buf"};
CameraInstanceBuf volume_buf = {selection_type_, "camera_volume_buf"};
CameraInstanceBuf volume_wire_buf = {selection_type_, "camera_volume_wire_buf"};
CameraInstanceBuf sphere_solid_buf = {selection_type_, "camera_sphere_solid_buf"};
LineInstanceBuf stereo_connect_lines = {selection_type_, "camera_dashed_lines_buf"};
LineInstanceBuf tracking_path = {selection_type_, "camera_tracking_path_buf"};
Empties::CallBuffers empties{selection_type_};
} call_buffers_;
static void view3d_reconstruction(const select::ID select_id,
const Scene *scene,
const View3D *v3d,
const float4 &color,
const ObjectRef &ob_ref,
Resources &res,
CallBuffers &call_buffers)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const bool is_select = DRW_state_is_select();
Object *ob = ob_ref.object;
MovieClip *clip = BKE_object_movieclip_get((Scene *)scene, ob, false);
if (clip == nullptr) {
return;
}
const bool is_solid_bundle = (v3d->bundle_drawtype == OB_EMPTY_SPHERE) &&
((v3d->shading.type != OB_SOLID) || !XRAY_FLAG_ENABLED(v3d));
MovieTracking *tracking = &clip->tracking;
/* Index must start in 1, to mimic BKE_tracking_track_get_for_selection_index. */
int track_index = 1;
float4 bundle_color_custom;
float *bundle_color_solid = G_draw.block.color_bundle_solid;
float *bundle_color_unselected = G_draw.block.color_wire;
uchar4 text_color_selected, text_color_unselected;
/* Color Management: Exception here as texts are drawn in sRGB space directly. */
UI_GetThemeColor4ubv(TH_SELECT, text_color_selected);
UI_GetThemeColor4ubv(TH_TEXT, text_color_unselected);
float4x4 camera_mat;
BKE_tracking_get_camera_object_matrix(ob, camera_mat.ptr());
const float4x4 object_to_world{ob->object_to_world().ptr()};
LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &tracking->objects) {
float4x4 tracking_object_mat;
if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
tracking_object_mat = camera_mat;
}
else {
const int framenr = BKE_movieclip_remap_scene_to_clip_frame(
clip, DEG_get_ctime(draw_ctx->depsgraph));
float4x4 object_mat;
BKE_tracking_camera_get_reconstructed_interpolate(
tracking, tracking_object, framenr, object_mat.ptr());
tracking_object_mat = object_to_world * math::invert(object_mat);
}
LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
if ((track->flag & TRACK_HAS_BUNDLE) == 0) {
continue;
}
bool is_selected = TRACK_SELECTED(track);
float4x4 bundle_mat = math::translate(tracking_object_mat, float3{track->bundle_pos});
const float *bundle_color;
if (track->flag & TRACK_CUSTOMCOLOR) {
/* Meh, hardcoded srgb transform here. */
/* TODO: change the actual DNA color to be linear. */
srgb_to_linearrgb_v3_v3(bundle_color_custom, track->color);
bundle_color_custom[3] = 1.0;
bundle_color = bundle_color_custom;
}
else if (is_solid_bundle) {
bundle_color = bundle_color_solid;
}
else if (is_selected) {
bundle_color = color;
}
else {
bundle_color = bundle_color_unselected;
}
const select::ID track_select_id = is_select ? res.select_id(ob_ref, track_index++ << 16) :
select_id;
if (is_solid_bundle) {
if (is_selected) {
Empties::object_sync(track_select_id,
bundle_mat,
v3d->bundle_size,
v3d->bundle_drawtype,
color,
call_buffers.empties);
}
call_buffers.sphere_solid_buf.append(
ExtraInstanceData{bundle_mat, {float3{bundle_color}, 1.0f}, v3d->bundle_size},
track_select_id);
}
else {
Empties::object_sync(track_select_id,
bundle_mat,
v3d->bundle_size,
v3d->bundle_drawtype,
bundle_color,
call_buffers.empties);
}
if ((v3d->flag2 & V3D_SHOW_BUNDLENAME) && !is_select) {
DRWTextStore *dt = DRW_text_cache_ensure();
DRW_text_cache_add(dt,
bundle_mat[3],
track->name,
strlen(track->name),
10,
0,
DRW_TEXT_CACHE_GLOBALSPACE | DRW_TEXT_CACHE_STRING_PTR,
is_selected ? text_color_selected : text_color_unselected);
}
}
if ((v3d->flag2 & V3D_SHOW_CAMERAPATH) && (tracking_object->flag & TRACKING_OBJECT_CAMERA) &&
!is_select)
{
const MovieTrackingReconstruction *reconstruction = &tracking_object->reconstruction;
if (reconstruction->camnr) {
const MovieReconstructedCamera *camera = reconstruction->cameras;
float3 v0, v1;
for (int a = 0; a < reconstruction->camnr; a++, camera++) {
v0 = v1;
v1 = math::transform_point(camera_mat, float3(camera->mat[3]));
if (a > 0) {
/* This one is suboptimal (gl_lines instead of gl_line_strip)
* but we keep this for simplicity */
call_buffers.tracking_path.append(v0, v1, TH_CAMERA_PATH, select_id);
}
}
}
}
}
}
/**
* Draw the stereo 3d support elements (cameras, plane, volume).
* They are only visible when not looking through the camera:
*/
static void stereoscopy_extra(const CameraInstanceData &instdata,
const select::ID select_id,
const Scene *scene,
const View3D *v3d,
Resources &res,
Object *ob,
CallBuffers &call_buffers)
{
CameraInstanceData stereodata = instdata;
const Camera *cam = static_cast<const Camera *>(ob->data);
const bool is_select = DRW_state_is_select();
const char *viewnames[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
const bool is_stereo3d_cameras = (v3d->stereo3d_flag & V3D_S3D_DISPCAMERAS) != 0;
const bool is_stereo3d_plane = (v3d->stereo3d_flag & V3D_S3D_DISPPLANE) != 0;
const bool is_stereo3d_volume = (v3d->stereo3d_flag & V3D_S3D_DISPVOLUME) != 0;
if (!is_stereo3d_cameras) {
/* Draw single camera. */
call_buffers.frame_buf.append(instdata, select_id);
}
for (const int eye : IndexRange(2)) {
ob = BKE_camera_multiview_render(scene, ob, viewnames[eye]);
BKE_camera_multiview_model_matrix(&scene->r, ob, viewnames[eye], stereodata.matrix.ptr());
stereodata.corner_x = instdata.corner_x;
stereodata.corner_y = instdata.corner_y;
stereodata.center_x = instdata.center_x +
camera_offaxis_shiftx_get(scene, ob, instdata.corner_x, eye);
stereodata.center_y = instdata.center_y;
stereodata.depth = instdata.depth;
if (is_stereo3d_cameras) {
call_buffers.frame_buf.append(stereodata, select_id);
/* Connecting line between cameras. */
call_buffers.stereo_connect_lines.append(stereodata.matrix.location(),
instdata.object_to_world_.location(),
res.theme_settings.color_wire,
select_id);
}
if (is_stereo3d_volume && !is_select) {
float r = (eye == 1) ? 2.0f : 1.0f;
stereodata.volume_start = -cam->clip_start;
stereodata.volume_end = -cam->clip_end;
/* Encode eye + intensity and alpha (see shader) */
copy_v2_fl2(stereodata.color_, r + 0.15f, 1.0f);
call_buffers.volume_wire_buf.append(stereodata, select_id);
if (v3d->stereo3d_volume_alpha > 0.0f) {
/* Encode eye + intensity and alpha (see shader) */
copy_v2_fl2(stereodata.color_, r + 0.999f, v3d->stereo3d_volume_alpha);
call_buffers.volume_buf.append(stereodata, select_id);
}
/* restore */
copy_v3_v3(stereodata.color_, instdata.color_);
}
}
if (is_stereo3d_plane && !is_select) {
if (cam->stereo.convergence_mode == CAM_S3D_TOE) {
/* There is no real convergence plane but we highlight the center
* point where the views are pointing at. */
// zero_v3(stereodata.mat[0]); /* We reconstruct from Z and Y */
// zero_v3(stereodata.mat[1]); /* Y doesn't change */
stereodata.matrix.z_axis() = float3(0.0f);
stereodata.matrix.location() = float3(0.0f);
for (int i : IndexRange(2)) {
float4x4 mat;
/* Need normalized version here. */
BKE_camera_multiview_model_matrix(&scene->r, ob, viewnames[i], mat.ptr());
stereodata.matrix.z_axis() += mat.z_axis();
stereodata.matrix.location() += mat.location() * 0.5f;
}
stereodata.matrix.z_axis() = math::normalize(stereodata.matrix.z_axis());
stereodata.matrix.x_axis() = math::cross(stereodata.matrix.y_axis(),
stereodata.matrix.z_axis());
}
else if (cam->stereo.convergence_mode == CAM_S3D_PARALLEL) {
/* Show plane at the given distance between the views even if it makes no sense. */
stereodata.matrix.location() = float3(0.0f);
for (int i : IndexRange(2)) {
float4x4 mat;
BKE_camera_multiview_model_matrix_scaled(&scene->r, ob, viewnames[i], mat.ptr());
stereodata.matrix.location() += mat.location() * 0.5f;
}
}
else if (cam->stereo.convergence_mode == CAM_S3D_OFFAXIS) {
/* Nothing to do. Everything is already setup. */
}
stereodata.volume_start = -cam->stereo.convergence_distance;
stereodata.volume_end = -cam->stereo.convergence_distance;
/* Encode eye + intensity and alpha (see shader) */
copy_v2_fl2(stereodata.color_, 0.1f, 1.0f);
call_buffers.volume_wire_buf.append(stereodata, select_id);
if (v3d->stereo3d_convergence_alpha > 0.0f) {
/* Encode eye + intensity and alpha (see shader) */
copy_v2_fl2(stereodata.color_, 0.0f, v3d->stereo3d_convergence_alpha);
call_buffers.volume_buf.append(stereodata, select_id);
}
}
}
public:
Cameras(const SelectionType selection_type) : call_buffers_{selection_type} {};
void begin_sync()
{
call_buffers_.distances_buf.clear();
call_buffers_.frame_buf.clear();
call_buffers_.tria_buf.clear();
call_buffers_.tria_wire_buf.clear();
call_buffers_.volume_buf.clear();
call_buffers_.volume_wire_buf.clear();
call_buffers_.sphere_solid_buf.clear();
call_buffers_.stereo_connect_lines.clear();
call_buffers_.tracking_path.clear();
Empties::begin_sync(call_buffers_.empties);
}
void object_sync(const ObjectRef &ob_ref, Resources &res, State &state)
{
Object *ob = ob_ref.object;
const select::ID select_id = res.select_id(ob_ref);
CameraInstanceData data(ob->object_to_world(), res.object_wire_color(ob_ref, state));
const View3D *v3d = state.v3d;
const Scene *scene = state.scene;
const RegionView3D *rv3d = state.rv3d;
const Camera *cam = static_cast<Camera *>(ob->data);
const Object *camera_object = DEG_get_evaluated_object(state.depsgraph, v3d->camera);
const bool is_select = DRW_state_is_select();
const bool is_active = (ob == camera_object);
const bool is_camera_view = (is_active && (rv3d->persp == RV3D_CAMOB));
const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0;
const bool is_stereo3d_view = (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D);
const bool is_stereo3d_display_extra = is_active && is_multiview && (!is_camera_view) &&
((v3d->stereo3d_flag) != 0);
const bool is_selection_camera_stereo = is_select && is_camera_view && is_multiview &&
is_stereo3d_view;
float3 scale = math::to_scale(data.matrix);
/* BKE_camera_multiview_model_matrix already accounts for scale, don't do it here. */
if (is_selection_camera_stereo) {
scale = float3(1.0f);
}
else if (ELEM(0.0f, scale.x, scale.y, scale.z)) {
/* Avoid division by 0. */
return;
}
float4x3 vecs;
float2 aspect_ratio;
float2 shift;
float drawsize;
BKE_camera_view_frame_ex(scene,
cam,
cam->drawsize,
is_camera_view,
1.0f / scale,
aspect_ratio,
shift,
&drawsize,
vecs.ptr());
/* Apply scale to simplify the rest of the drawing. */
for (int i = 0; i < 4; i++) {
vecs[i] *= scale;
/* Project to z=-1 plane. Makes positioning / scaling easier. (see shader) */
mul_v2_fl(vecs[i], 1.0f / std::abs(vecs[i].z));
}
/* Frame coords */
const float2 center = (vecs[0].xy() + vecs[2].xy()) * 0.5f;
const float2 corner = vecs[0].xy() - center.xy();
data.corner_x = corner.x;
data.corner_y = corner.y;
data.center_x = center.x;
data.center_y = center.y;
data.depth = vecs[0].z;
if (is_camera_view) {
/* TODO(Miguel Pozo) */
if (!DRW_state_is_image_render()) {
/* Only draw the frame. */
if (is_multiview) {
float4x4 mat;
const bool is_right = v3d->multiview_eye == STEREO_RIGHT_ID;
const char *view_name = is_right ? STEREO_RIGHT_NAME : STEREO_LEFT_NAME;
BKE_camera_multiview_model_matrix(&scene->r, ob, view_name, mat.ptr());
data.center_x += camera_offaxis_shiftx_get(scene, ob, data.corner_x, is_right);
for (int i : IndexRange(4)) {
/* Partial copy to avoid overriding packed data. */
copy_v3_v3(data.matrix[i], mat[i].xyz());
}
}
data.depth *= -1.0f; /* Hides the back of the camera wires (see shader). */
call_buffers_.frame_buf.append(data, select_id);
}
}
else {
/* Stereo cameras, volumes, plane drawing. */
if (is_stereo3d_display_extra) {
stereoscopy_extra(data, select_id, scene, v3d, res, ob, call_buffers_);
}
else {
call_buffers_.frame_buf.append(data, select_id);
}
}
if (!is_camera_view) {
/* Triangle. */
float tria_size = 0.7f * drawsize / fabsf(data.depth);
float tria_margin = 0.1f * drawsize / fabsf(data.depth);
data.center_x = center.x;
data.center_y = center.y + data.corner_y + tria_margin + tria_size;
data.corner_x = data.corner_y = -tria_size;
(is_active ? call_buffers_.tria_buf : call_buffers_.tria_wire_buf).append(data, select_id);
}
if (cam->flag & CAM_SHOWLIMITS) {
/* Scale focus point. */
data.matrix.x_axis() *= cam->drawsize;
data.matrix.y_axis() *= cam->drawsize;
data.dist_color_id = (is_active) ? 3 : 2;
data.focus = -BKE_camera_object_dof_distance(ob);
data.clip_start = cam->clip_start;
data.clip_end = cam->clip_end;
call_buffers_.distances_buf.append(data, select_id);
}
if (cam->flag & CAM_SHOWMIST) {
World *world = scene->world;
if (world) {
data.dist_color_id = (is_active) ? 1 : 0;
data.focus = 1.0f; /* Disable */
data.mist_start = world->miststa;
data.mist_end = world->miststa + world->mistdist;
call_buffers_.distances_buf.append(data, select_id);
}
}
/* Motion Tracking. */
if ((v3d->flag2 & V3D_SHOW_RECONSTRUCTION) != 0) {
view3d_reconstruction(
select_id, scene, v3d, res.object_wire_color(ob_ref, state), ob_ref, res, call_buffers_);
}
// TODO: /* Background images. */
// if (look_through && (cam->flag & CAM_SHOW_BG_IMAGE) &&
// !BLI_listbase_is_empty(&cam->bg_images))
// {
// OVERLAY_image_camera_cache_populate(vedata, ob);
// }
}
void end_sync(Resources &res, ShapeCache &shapes, const State &state)
{
ps_.init();
res.select_bind(ps_);
{
PassSimple::Sub &sub_pass = ps_.sub("volume");
sub_pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA |
DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK | state.clipping_state);
sub_pass.shader_set(res.shaders.extra_shape.get());
sub_pass.bind_ubo("globalsBlock", &res.globals_buf);
call_buffers_.volume_buf.end_sync(sub_pass, shapes.camera_volume.get());
}
{
PassSimple::Sub &sub_pass = ps_.sub("volume_wire");
sub_pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA |
DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK | state.clipping_state);
sub_pass.shader_set(res.shaders.extra_shape.get());
sub_pass.bind_ubo("globalsBlock", &res.globals_buf);
call_buffers_.volume_wire_buf.end_sync(sub_pass, shapes.camera_volume_wire.get());
}
{
PassSimple::Sub &sub_pass = ps_.sub("camera_shapes");
sub_pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
DRW_STATE_DEPTH_LESS_EQUAL | state.clipping_state);
sub_pass.shader_set(res.shaders.extra_shape.get());
sub_pass.bind_ubo("globalsBlock", &res.globals_buf);
call_buffers_.distances_buf.end_sync(sub_pass, shapes.camera_distances.get());
call_buffers_.frame_buf.end_sync(sub_pass, shapes.camera_frame.get());
call_buffers_.tria_buf.end_sync(sub_pass, shapes.camera_tria.get());
call_buffers_.tria_wire_buf.end_sync(sub_pass, shapes.camera_tria_wire.get());
call_buffers_.sphere_solid_buf.end_sync(sub_pass, shapes.sphere_low_detail.get());
}
{
PassSimple::Sub &sub_pass = ps_.sub("camera_extra_wire");
sub_pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
DRW_STATE_DEPTH_LESS_EQUAL | state.clipping_state);
sub_pass.shader_set(res.shaders.extra_wire.get());
sub_pass.bind_ubo("globalsBlock", &res.globals_buf);
call_buffers_.stereo_connect_lines.end_sync(sub_pass);
call_buffers_.tracking_path.end_sync(sub_pass);
}
{
PassSimple::Sub &sub_pass = ps_.sub("empties");
Empties::end_sync(res, shapes, state, sub_pass, call_buffers_.empties);
}
}
void draw(Framebuffer &framebuffer, Manager &manager, View &view)
{
GPU_framebuffer_bind(framebuffer);
manager.submit(ps_, view);
}
};
} // namespace blender::draw::overlay

View File

@@ -13,13 +13,11 @@
namespace blender::draw::overlay {
class Empties {
friend class Cameras;
using EmptyInstanceBuf = ShapeInstanceBuf<ExtraInstanceData>;
private:
const SelectionType selection_type_;
PassSimple empty_ps_ = {"Empties"};
PassSimple empty_in_front_ps_ = {"Empties_In_front"};
PassSimple ps_ = {"Empties"};
struct CallBuffers {
const SelectionType selection_type_;
@@ -31,97 +29,110 @@ class Empties {
EmptyInstanceBuf cone_buf = {selection_type_, "cone_buf"};
EmptyInstanceBuf arrows_buf = {selection_type_, "arrows_buf"};
EmptyInstanceBuf image_buf = {selection_type_, "image_buf"};
} call_buffers_[2] = {{selection_type_}, {selection_type_}};
} call_buffers_;
public:
Empties(const SelectionType selection_type) : selection_type_(selection_type){};
Empties(const SelectionType selection_type) : call_buffers_{selection_type} {};
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();
}
begin_sync(call_buffers_);
}
static void begin_sync(CallBuffers &call_buffers)
{
call_buffers.plain_axes_buf.clear();
call_buffers.single_arrow_buf.clear();
call_buffers.cube_buf.clear();
call_buffers.circle_buf.clear();
call_buffers.sphere_buf.clear();
call_buffers.cone_buf.clear();
call_buffers.arrows_buf.clear();
call_buffers.image_buf.clear();
}
void object_sync(const ObjectRef &ob_ref, Resources &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(ob_ref.object->object_to_world(), color, ob_ref.object->empty_drawsize);
const float4 color = res.object_wire_color(ob_ref, state);
const select::ID select_id = res.select_id(ob_ref);
object_sync(select_id,
ob_ref.object->object_to_world(),
ob_ref.object->empty_drawsize,
ob_ref.object->empty_drawtype,
color,
call_buffers_);
}
switch (ob_ref.object->empty_drawtype) {
static void object_sync(const select::ID select_id,
const float4x4 &matrix,
const float draw_size,
const char empty_drawtype,
const float4 &color,
CallBuffers &call_buffers)
{
ExtraInstanceData data(matrix, color, draw_size);
switch (empty_drawtype) {
case OB_PLAINAXES:
call_bufs.plain_axes_buf.append(data, select_id);
call_buffers.plain_axes_buf.append(data, select_id);
break;
case OB_SINGLE_ARROW:
call_bufs.single_arrow_buf.append(data, select_id);
call_buffers.single_arrow_buf.append(data, select_id);
break;
case OB_CUBE:
call_bufs.cube_buf.append(data, select_id);
call_buffers.cube_buf.append(data, select_id);
break;
case OB_CIRCLE:
call_bufs.circle_buf.append(data, select_id);
call_buffers.circle_buf.append(data, select_id);
break;
case OB_EMPTY_SPHERE:
call_bufs.sphere_buf.append(data, select_id);
call_buffers.sphere_buf.append(data, select_id);
break;
case OB_EMPTY_CONE:
call_bufs.cone_buf.append(data, select_id);
call_buffers.cone_buf.append(data, select_id);
break;
case OB_ARROWS:
call_bufs.arrows_buf.append(data, select_id);
call_buffers.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);
call_buffers.image_buf.append(data, select_id);
break;
}
}
void end_sync(Resources &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.get());
pass.bind_ubo("globalsBlock", &res.globals_buf);
res.select_bind(pass);
call_bufs.plain_axes_buf.end_sync(pass, shapes.plain_axes.get());
call_bufs.single_arrow_buf.end_sync(pass, shapes.single_arrow.get());
call_bufs.cube_buf.end_sync(pass, shapes.cube.get());
call_bufs.circle_buf.end_sync(pass, shapes.circle.get());
call_bufs.sphere_buf.end_sync(pass, shapes.empty_sphere.get());
call_bufs.cone_buf.end_sync(pass, shapes.empty_cone.get());
call_bufs.arrows_buf.end_sync(pass, shapes.arrows.get());
call_bufs.image_buf.end_sync(pass, shapes.quad_wire.get());
};
init_pass(empty_ps_, call_buffers_[0]);
init_pass(empty_in_front_ps_, call_buffers_[1]);
ps_.init();
res.select_bind(ps_);
end_sync(res, shapes, state, ps_, call_buffers_);
}
void draw(Resources &res, Manager &manager, View &view)
static void end_sync(Resources &res,
ShapeCache &shapes,
const State &state,
PassSimple::Sub &ps,
CallBuffers &call_buffers)
{
GPU_framebuffer_bind(res.overlay_line_fb);
manager.submit(empty_ps_, view);
ps.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
state.clipping_state);
ps.shader_set(res.shaders.extra_shape.get());
ps.bind_ubo("globalsBlock", &res.globals_buf);
call_buffers.plain_axes_buf.end_sync(ps, shapes.plain_axes.get());
call_buffers.single_arrow_buf.end_sync(ps, shapes.single_arrow.get());
call_buffers.cube_buf.end_sync(ps, shapes.cube.get());
call_buffers.circle_buf.end_sync(ps, shapes.circle.get());
call_buffers.sphere_buf.end_sync(ps, shapes.empty_sphere.get());
call_buffers.cone_buf.end_sync(ps, shapes.empty_cone.get());
call_buffers.arrows_buf.end_sync(ps, shapes.arrows.get());
call_buffers.image_buf.end_sync(ps, shapes.quad_wire.get());
}
void draw_in_front(Resources &res, Manager &manager, View &view)
void draw(Framebuffer &framebuffer, Manager &manager, View &view)
{
GPU_framebuffer_bind(res.overlay_line_in_front_fb);
manager.submit(empty_in_front_ps_, view);
GPU_framebuffer_bind(framebuffer);
manager.submit(ps_, view);
}
};

View File

@@ -78,11 +78,12 @@ void Instance::begin_sync()
background.begin_sync(resources, state);
prepass.begin_sync(resources, state);
empties.begin_sync();
lattices.begin_sync(resources, state);
auto begin_sync_layer = [&](OverlayLayer &layer) {
layer.bounds.begin_sync();
layer.cameras.begin_sync();
layer.empties.begin_sync();
layer.lights.begin_sync();
};
begin_sync_layer(regular);
@@ -138,7 +139,10 @@ void Instance::object_sync(ObjectRef &ob_ref, Manager &manager)
if (!state.hide_overlays) {
switch (ob_ref.object->type) {
case OB_EMPTY:
empties.object_sync(ob_ref, resources, state);
layer.empties.object_sync(ob_ref, resources, state);
break;
case OB_CAMERA:
layer.cameras.object_sync(ob_ref, resources, state);
break;
case OB_ARMATURE:
break;
@@ -161,7 +165,7 @@ void Instance::object_sync(ObjectRef &ob_ref, Manager &manager)
speakers.object_sync(ob_ref, resources, state);
break;
}
regular.bounds.object_sync(ob_ref, resources, state);
layer.bounds.object_sync(ob_ref, resources, state);
}
}
@@ -171,13 +175,14 @@ void Instance::end_sync()
auto end_sync_layer = [&](OverlayLayer &layer) {
layer.bounds.end_sync(resources, shapes, state);
layer.cameras.end_sync(resources, shapes, state);
layer.empties.end_sync(resources, shapes, state);
layer.lights.end_sync(resources, shapes, state);
};
end_sync_layer(regular);
end_sync_layer(infront);
metaballs.end_sync(resources, shapes, state);
empties.end_sync(resources, shapes, state);
speakers.end_sync(resources, shapes, state);
}
@@ -243,15 +248,15 @@ void Instance::draw(Manager &manager)
background.draw(resources, manager);
regular.bounds.draw(resources.overlay_line_fb, manager, view);
regular.cameras.draw(resources.overlay_line_fb, manager, view);
regular.empties.draw(resources.overlay_line_fb, manager, view);
regular.lights.draw(resources.overlay_line_fb, manager, view);
empties.draw(resources, manager, view);
lattices.draw(resources, manager, view);
metaballs.draw(resources, manager, view);
speakers.draw(resources, manager, view);
grid.draw(resources, manager, view);
empties.draw_in_front(resources, manager, view);
/* TODO(: Breaks selection on M1 Max. */
// lattices.draw_in_front(resources, manager, view);
metaballs.draw_in_front(resources, manager, view);

View File

@@ -12,6 +12,7 @@
#include "overlay_next_background.hh"
#include "overlay_next_bounds.hh"
#include "overlay_next_camera.hh"
#include "overlay_next_empty.hh"
#include "overlay_next_grid.hh"
#include "overlay_next_lattice.hh"
@@ -47,11 +48,12 @@ class Instance {
struct OverlayLayer {
const SelectionType selection_type_;
Bounds bounds = {selection_type_};
Cameras cameras = {selection_type_};
Empties empties = {selection_type_};
Lights lights = {selection_type_};
} regular{selection_type_}, infront{selection_type_};
Metaballs metaballs = {selection_type_};
Empties empties = {selection_type_};
Lattices lattices;
Speakers speakers = {selection_type_};
Grid grid;

View File

@@ -87,6 +87,16 @@ class ShapeCache {
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;
BatchPtr light_icon_outer_lines;
@@ -134,6 +144,7 @@ class ShaderModule {
ShaderPtr depth_mesh;
ShaderPtr extra_shape;
ShaderPtr extra_wire_object;
ShaderPtr extra_wire;
ShaderPtr extra_ground_line;
ShaderPtr lattice_points;
ShaderPtr lattice_wire;
@@ -290,4 +301,49 @@ template<typename InstanceDataT> struct ShapeInstanceBuf : private select::Selec
}
};
struct LineInstanceBuf : private select::SelectBuf {
StorageVectorBuffer<PointData> data_buf;
int color_id = 0;
LineInstanceBuf(const SelectionType selection_type, const char *name = nullptr)
: select::SelectBuf(selection_type), data_buf(name){};
void clear()
{
this->select_clear();
data_buf.clear();
color_id = 0;
}
void append(const float3 &start, const float3 &end, const float4 &color, select::ID select_id)
{
this->select_append(select_id);
data_buf.append({float4{start}, color});
data_buf.append({float4{end}, color});
}
void append(const float3 &start, const float3 &end, const int color_id, select::ID select_id)
{
this->color_id = color_id;
this->select_append(select_id);
data_buf.append({float4{start}, float4{}});
data_buf.append({float4{end}, float4{}});
}
void end_sync(PassSimple::Sub &pass)
{
if (data_buf.is_empty()) {
return;
}
this->select_bind(pass);
data_buf.push_update();
pass.bind_ssbo("data_buf", &data_buf);
if (color_id) {
pass.push_constant("colorid", color_id);
}
pass.draw_procedural(GPU_PRIM_LINES, 1, data_buf.size());
}
};
} // namespace blender::draw::overlay

View File

@@ -82,6 +82,20 @@ ShaderModule::ShaderModule(const SelectionType selection_type, const bool clippi
info.vertex_inputs_.pop_last();
});
extra_wire = selectable_shader("overlay_extra_wire", [](gpu::shader::ShaderCreateInfo &info) {
info.typedef_source("overlay_shader_shared.h");
info.storage_buf(0, Qualifier::READ, "PointData", "data_buf[]");
info.push_constant(gpu::shader::Type::INT, "colorid");
info.define("pos", "data_buf[gl_InstanceID * 2 + gl_VertexID].pos_.xyz");
info.define("color", "data_buf[gl_InstanceID * 2 + gl_VertexID].color_");
info.additional_infos_.clear();
info.additional_info(
"draw_view", "draw_modelmat_new", "draw_resource_handle_new", "draw_globals");
info.vertex_inputs_.pop_last();
info.vertex_inputs_.pop_last();
info.vertex_inputs_.pop_last();
});
extra_wire_object = selectable_shader(
"overlay_extra_wire", [](gpu::shader::ShaderCreateInfo &info) {
info.define("OBJECT_WIRE");

View File

@@ -16,6 +16,12 @@ struct Vertex {
int vclass;
};
struct VertShaded {
float3 pos;
int v_class;
float3 nor;
};
/* Caller gets ownership of the #gpu::VertBuf. */
static gpu::VertBuf *vbo_from_vector(const Vector<Vertex> &vector)
{
@@ -31,6 +37,21 @@ static gpu::VertBuf *vbo_from_vector(const Vector<Vertex> &vector)
return vbo;
}
static gpu::VertBuf *vbo_from_vector(Vector<VertShaded> &vector)
{
static GPUVertFormat format = {0};
if (format.attr_len == 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);
GPU_vertformat_attr_add(&format, "nor", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
}
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(format);
GPU_vertbuf_data_alloc(*vbo, vector.size());
vbo->data<VertShaded>().copy_from(vector);
return vbo;
}
enum VertexClass {
VCLASS_NONE = 0,
@@ -74,6 +95,26 @@ static constexpr std::array<uint, 24> bone_box_wire = {
0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7,
};
static const std::array<uint3, 12> bone_box_solid_tris{
uint3{0, 2, 1}, /* bottom */
{0, 3, 2},
{0, 1, 5}, /* sides */
{0, 5, 4},
{1, 2, 6},
{1, 6, 5},
{2, 3, 7},
{2, 7, 6},
{3, 0, 4},
{3, 4, 7},
{4, 5, 6}, /* top */
{4, 6, 7},
};
static void append_line_loop(
Vector<Vertex> &dest, Span<float2> verts, float z, int flag, bool dashed = false)
{
@@ -106,11 +147,14 @@ static float light_distance_z_get(char axis, const bool start)
}
/* A single ring of vertices. */
static Vector<float2> ring_vertices(const float radius, const int segments)
static Vector<float2> ring_vertices(const float radius,
const int segments,
const bool half = false)
{
Vector<float2> verts;
for (int i : IndexRange(segments)) {
float angle = (2 * math::numbers::pi * i) / segments;
const float full = (half ? 1.0f : 2.0f) * math::numbers::pi;
for (const int angle_i : IndexRange(segments + (half ? 1 : 0))) {
const float angle = (full * angle_i) / segments;
verts.append(radius * float2(math::cos(angle), math::sin(angle)));
}
return verts;
@@ -154,6 +198,60 @@ static void light_append_direction_line(Vector<Vertex> &verts)
append_line_loop(verts, diamond, zend, VCLASS_LIGHT_DIST | VCLASS_SCREENSPACE);
}
static VertShaded sphere_lat_lon_vert(const float2 &lat_pt, const float2 &lon_pt)
{
const float x = lon_pt.y * lat_pt.x;
const float y = lon_pt.x;
const float z = lon_pt.y * lat_pt.y;
return VertShaded{{x, y, z}, VCLASS_EMPTY_SCALED, {x, y, z}};
}
static void append_sphere(Vector<VertShaded> &dest, const eDRWLevelOfDetail level_of_detail)
{
/* Sphere shape resolution */
/* Low */
constexpr int drw_sphere_shape_latitude_low = 32;
constexpr int drw_sphere_shape_longitude_low = 24;
/* Medium */
constexpr int drw_sphere_shape_latitude_medium = 64;
constexpr int drw_sphere_shape_longitude_medium = 48;
/* High */
constexpr int drw_sphere_shape_latitude_high = 80;
constexpr int drw_sphere_shape_longitude_high = 60;
BLI_assert(level_of_detail >= DRW_LOD_LOW && level_of_detail < DRW_LOD_MAX);
const std::array<Vector<float2>, DRW_LOD_MAX> latitude_rings = {
ring_vertices(1.0f, drw_sphere_shape_latitude_low),
ring_vertices(1.0f, drw_sphere_shape_latitude_medium),
ring_vertices(1.0f, drw_sphere_shape_latitude_high)};
const std::array<Vector<float2>, DRW_LOD_MAX> longitude_half_rings = {
ring_vertices(1.0f, drw_sphere_shape_longitude_low, true),
ring_vertices(1.0f, drw_sphere_shape_longitude_medium, true),
ring_vertices(1.0f, drw_sphere_shape_longitude_high, true)};
const Vector<float2> &latitude_ring = latitude_rings[level_of_detail];
const Vector<float2> &longitude_half_ring = longitude_half_rings[level_of_detail];
for (const int i : latitude_ring.index_range()) {
const float2 lat_pt = latitude_ring[i];
const float2 next_lat_pt = latitude_ring[(i + 1) % latitude_ring.size()];
for (const int j : IndexRange(longitude_half_ring.size() - 1)) {
const float2 lon_pt = longitude_half_ring[j];
const float2 next_lon_pt = longitude_half_ring[j + 1];
if (j != 0) { /* Pole */
dest.append(sphere_lat_lon_vert(next_lat_pt, next_lon_pt));
dest.append(sphere_lat_lon_vert(next_lat_pt, lon_pt));
dest.append(sphere_lat_lon_vert(lat_pt, lon_pt));
}
if (j != longitude_half_ring.index_range().last(1)) { /* Pole */
dest.append(sphere_lat_lon_vert(lat_pt, next_lon_pt));
dest.append(sphere_lat_lon_vert(next_lat_pt, next_lon_pt));
dest.append(sphere_lat_lon_vert(lat_pt, lon_pt));
}
}
}
}
ShapeCache::ShapeCache()
{
/* quad_wire */
@@ -455,6 +553,90 @@ ShapeCache::ShapeCache()
speaker = BatchPtr(
GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO));
}
/* camera distances */
{
const Vector<float2> diamond = ring_vertices(1.5f, 5);
const Vector<float2> cross = {{1.0f, 0.0f}, {-1.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, -1.0f}};
Vector<Vertex> verts;
verts.append({{0.0f, 0.0f, 0.0f}, VCLASS_CAMERA_DIST});
verts.append({{0.0f, 0.0f, 1.0f}, VCLASS_CAMERA_DIST});
append_line_loop(verts, diamond, 0.0f, VCLASS_CAMERA_DIST | VCLASS_SCREENSPACE);
append_line_loop(verts, diamond, 1.0f, VCLASS_CAMERA_DIST | VCLASS_SCREENSPACE);
/* Focus cross */
for (const float2 &point : cross) {
verts.append({{point.x, point.y, 2.0f}, VCLASS_CAMERA_DIST});
}
camera_distances = BatchPtr(
GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO));
}
/* camera frame */
{
const Vector<float2> rect{{-1.0f, -1.0f}, {-1.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, -1.0f}};
Vector<Vertex> verts;
/* Frame */
append_line_loop(verts, rect, 1.0f, VCLASS_CAMERA_FRAME);
/* Wires to origin. */
for (const float2 &point : rect) {
verts.append({{point.x, point.y, 1.0f}, VCLASS_CAMERA_FRAME});
verts.append({{point.x, point.y, 0.0f}, VCLASS_CAMERA_FRAME});
}
camera_frame = BatchPtr(
GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO));
}
/* camera tria */
{
const Vector<float2> triangle = {{-1.0f, 1.0f}, {1.0f, 1.0f}, {0.0f, 0.0f}};
Vector<Vertex> verts(2 * 3);
/* Wire */
append_line_loop(verts, triangle, 1.0f, VCLASS_CAMERA_FRAME);
camera_tria_wire = BatchPtr(
GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO));
verts.clear();
/* Triangle */
for (const float2 &point : triangle) {
verts.append({{point.x, point.y, 1.0f}, VCLASS_CAMERA_FRAME});
}
camera_tria = BatchPtr(
GPU_batch_create_ex(GPU_PRIM_TRIS, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO));
}
/* camera volume */
{
Vector<Vertex> verts;
for (const uint3 &tri : bone_box_solid_tris) {
for (const int i : IndexRange(tri.type_length)) {
const int v = tri[i];
const float x = bone_box_verts[v][2];
const float y = bone_box_verts[v][0];
const float z = bone_box_verts[v][1];
verts.append({{x, y, z}, VCLASS_CAMERA_FRAME | VCLASS_CAMERA_VOLUME});
}
}
camera_volume = BatchPtr(
GPU_batch_create_ex(GPU_PRIM_TRIS, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO));
}
/* camera volume wire */
{
Vector<Vertex> verts(bone_box_wire.size());
for (int i : bone_box_wire) {
const float x = bone_box_verts[i][2];
const float y = bone_box_verts[i][0];
const float z = bone_box_verts[i][1];
verts.append({{x, y, z}, VCLASS_CAMERA_FRAME | VCLASS_CAMERA_VOLUME});
}
camera_volume_wire = BatchPtr(
GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO));
}
/* spheres */
{
Vector<VertShaded> verts;
append_sphere(verts, DRW_LOD_LOW);
sphere_low_detail = BatchPtr(
GPU_batch_create_ex(GPU_PRIM_TRIS, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO));
}
/* ground line */
{
const Vector<float2> ring = ring_vertices(1.35f, diamond_nsegments);

View File

@@ -18,6 +18,7 @@ typedef enum OVERLAY_GridBits OVERLAY_GridBits;
typedef struct OVERLAY_GridData OVERLAY_GridData;
typedef struct ThemeColorData ThemeColorData;
typedef struct ExtraInstanceData ExtraInstanceData;
typedef struct PointData PointData;
#endif
/* TODO(fclem): Should eventually become OVERLAY_BackgroundType.
@@ -231,6 +232,12 @@ struct ExtraInstanceData {
};
BLI_STATIC_ASSERT_ALIGN(ExtraInstanceData, 16)
struct PointData {
float4 pos_;
float4 color_;
};
BLI_STATIC_ASSERT_ALIGN(PointData, 16)
#ifndef GPU_SHADER
# ifdef __cplusplus
}