WIP: Overlay Next: Shapes and Extras #109059

Draft
Miguel Pozo wants to merge 78 commits from pragma37/blender:pull-overlay-next-extras into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
24 changed files with 3229 additions and 373 deletions

View File

@ -2426,8 +2426,8 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
({"property": "use_experimental_compositors"}, ("blender/blender/issues/88150", "#88150")),
({"property": "enable_eevee_next"}, ("blender/blender/issues/93220", "#93220")),
({"property": "enable_workbench_next"}, ("blender/blender/issues/101619", "#101619")),
({"property": "use_grease_pencil_version3"}, ("blender/blender/projects/40", "Grease Pencil 3.0")),
({"property": "enable_overlay_next"}, ("blender/blender/issues/102179", "#102179")),
({"property": "use_grease_pencil_version3"}, ("blender/blender/projects/40", "Grease Pencil 3.0")),

Unwanted change

Unwanted change
),
)

View File

@ -0,0 +1,139 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "BKE_mball.h"
#include "overlay_next_private.hh"
namespace blender::draw::overlay {
class BoundPassesBase : public OverlayPasses {
ExtraInstanceBuf cube = extra_buf("cube", shapes.empty_cube);
ExtraInstanceBuf sphere = extra_buf("sphere", shapes.empty_sphere);
ExtraInstanceBuf cone = extra_buf("cone", shapes.empty_cone);
ExtraInstanceBuf cylinder = extra_buf("cylinder", shapes.empty_cylinder);
ExtraInstanceBuf capsule_body = extra_buf("capsule_body", shapes.empty_capsule_body);
ExtraInstanceBuf capsule_cap = extra_buf("capsule_cap", shapes.empty_capsule_cap);
public:
BoundPassesBase(const char *name,
SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors,
bool in_front)
: OverlayPasses(name, selection_type, shapes, theme_colors, in_front){};
void bounds_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res,
const State &state,
char boundtype,
bool around_origin)
{
ExtraInstanceData data(ob_ref.object, res.object_wire_color(ob_ref, state));
Object *ob = ob_ref.object;
if (ObjectType(ob->type) == OB_MBALL && !BKE_mball_is_basis(ob)) {
return;
}
const BoundBox *bb = BKE_object_boundbox_get(ob);
BoundBox bb_local;
if (bb == nullptr) {
const float3 min = float3(-1.0f);
const float3 max = float3(1.0f);
BKE_boundbox_init_from_minmax(&bb_local, min, max);
bb = &bb_local;
}
float3 center = float3(0);
if (!around_origin) {
BKE_boundbox_calc_center_aabb(bb, center);
}
float3 size;
BKE_boundbox_calc_size_aabb(bb, size);
if (boundtype == OB_BOUND_BOX) {

Use switch

Use `switch`
float4x4 mat = math::from_scale<float4x4>(size);
mat.location() = center;
cube.append(data.with_matrix(data.matrix * mat), select_id);
}
else if (boundtype == OB_BOUND_SPHERE) {
size = float3(std::max({size.x, size.y, size.z}));
float4x4 mat = math::from_scale<float4x4>(size);
mat.location() = center;
sphere.append(data.with_matrix(data.matrix * mat), select_id);
}
else if (boundtype == OB_BOUND_CYLINDER) {
size.x = size.y = std::max(size.x, size.y);
float4x4 mat = math::from_scale<float4x4>(size);
mat.location() = center;
cylinder.append(data.with_matrix(data.matrix * mat), select_id);
}
else if (boundtype == OB_BOUND_CONE) {
size.x = size.y = std::max(size.x, size.y);
float4x4 mat = math::from_scale<float4x4>(size);
mat.location() = center;
/* Cone batch has base at 0 and is pointing towards +Y. */
std::swap(mat[1], mat[2]);
mat.location().z -= size.z;
cone.append(data.with_matrix(data.matrix * mat), select_id);
}
else if (boundtype == OB_BOUND_CAPSULE) {
size.x = size.y = std::max(size.x, size.y);
float4x4 mat = math::from_scale<float4x4>(float3(size.x));
mat.location() = center;
mat.location().z = center.z + std::max(0.0f, size.z - size.x);
capsule_cap.append(data.with_matrix(data.matrix * mat), select_id);
mat.location().z = center.z - std::max(0.0f, size.z - size.x);
mat.z_axis() *= -1.0f;
capsule_cap.append(data.with_matrix(data.matrix * mat), select_id);
mat.z_axis().z = std::max(0.0f, (size.z - size.x) * 2.0f);
capsule_body.append(data.with_matrix(data.matrix * mat), select_id);
}
else {
BLI_assert_unreachable();
}
}
};
class BoundPasses : public BoundPassesBase {
public:
BoundPasses(SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors,
bool in_front)
: BoundPassesBase("Bounds", selection_type, shapes, theme_colors, in_front){};
virtual void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res,
const State &state) final override
{
Object *ob = ob_ref.object;
const bool from_dupli = ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI);
const bool has_bounds = !ELEM(
ob->type, OB_LAMP, OB_CAMERA, OB_EMPTY, OB_SPEAKER, OB_LIGHTPROBE);
const bool draw_bounds = ob->dt == OB_BOUNDBOX || ob->dtx & OB_DRAWBOUNDOX;
if (from_dupli || !has_bounds || !draw_bounds) {
return;
}
bounds_sync(ob_ref, select_id, res, state, ob->boundtype, false);
}
};
using Bound = OverlayType<BoundPasses>;
} // namespace blender::draw::overlay

View File

@ -0,0 +1,501 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "BKE_camera.h"
#include "BKE_movieclip.h"
#include "BKE_tracking.h"
#include "DEG_depsgraph_query.h"
#include "DNA_camera_types.h"
#include "ED_view3d.h"
#include "overlay_next_empty.hh"
namespace blender::draw::overlay {
struct CameraInstanceData : public ExtraInstanceData {
CameraInstanceData(const ExtraInstanceData &data) : ExtraInstanceData(data){};
CameraInstanceData() : ExtraInstanceData(){};
/* Pack render data inside matrix and color. */
float &volume_start()
{
return color[2];
}
float &volume_end()
{
return color[3];
}
float &depth()
{
return color[3];
}
float &focus()
{
return color[3];
}
float &dist_color_id()
{
return matrix[0][3];
}
float &corner_x()
{
return matrix[0][3];
}
float &corner_y()
{
return matrix[1][3];
}
float &center_x()
{
return matrix[2][3];
}
float &clip_start()
{
return matrix[2][3];
}
float &mist_start()
{
return matrix[2][3];
}
float &center_y()
{
return matrix[3][3];
}
float &clip_end()
{
return matrix[3][3];
}
float &mist_end()
{
return matrix[3][3];
}
};
class CameraPasses : public EmptyPassesBase {
ExtraInstanceBuf volume = extra_buf("camera_volume", shapes.camera_volume, BLEND_CULL_BACK);
ExtraInstanceBuf volume_wire = extra_buf(
"camera_volume_wire", shapes.camera_volume_wire, BLEND_CULL_BACK);
ExtraInstanceBuf frame = extra_buf("camera_frame", shapes.camera_frame);
ExtraInstanceBuf distances = extra_buf("camera_distances", shapes.camera_distances);
ExtraInstanceBuf tria_wire = extra_buf("camera_tria_wire", shapes.camera_tria_wire);
ExtraInstanceBuf tria = extra_buf("camera_tria", shapes.camera_tria);
LineInstanceBuf path = line_buf("camera_path", theme_colors.color_camera_path);
ExtraInstanceBuf sphere_solid = extra_buf("sphere_solid", shapes.sphere_solid);
LineInstanceBuf relation_line = line_buf("relation_line", theme_colors.color_wire);
public:
CameraPasses(SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors,
bool in_front)
: EmptyPassesBase("Cameras", selection_type, shapes, theme_colors, in_front){};
void view3d_reconstruction_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res,
const State &state,
CameraInstanceData data)
{
const Scene *scene = state.scene;
const View3D *v3d = state.v3d;
Object *ob = ob_ref.object;
const bool is_select = state.selection_type != SelectionType::DISABLED;
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;
float4x4 camera_mat;
BKE_tracking_get_camera_object_matrix(ob, camera_mat.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(state.depsgraph));
float4x4 object_mat;
BKE_tracking_camera_get_reconstructed_interpolate(
tracking, tracking_object, framenr, object_mat.ptr());
tracking_object_mat = data.matrix * 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);
CameraInstanceData bundle_data = data.with_size(v3d->bundle_size);
bundle_data.matrix = math::translate(bundle_data.matrix, float3(track->bundle_pos));
if (track->flag & TRACK_CUSTOMCOLOR) {
/* Hardcoded srgb transform. */
/* TODO: change the actual DNA color to be linear. */
srgb_to_linearrgb_v3_v3(bundle_data.color, track->color);
}
else if (is_solid_bundle) {
bundle_data.color = res.theme_settings.color_bundle_solid;
}
else if (!is_selected) {
bundle_data.color = res.theme_settings.color_wire;
}
select::ID track_select_id = select_id;
if (is_select) {
track_select_id = res.select_id(ob_ref, track_index++);
}
if (is_solid_bundle) {
if (is_selected) {
empty_buf(v3d->bundle_drawtype)
.append(bundle_data.with_color(data.color), track_select_id);
}
sphere_solid.append(bundle_data, track_select_id);
}
else {
empty_buf(v3d->bundle_drawtype).append(bundle_data, track_select_id);
}
if ((v3d->flag2 & V3D_SHOW_BUNDLENAME) && !is_select) {
#if 0
/* TODO(Miguel Pozo): */
uchar text_color_selected[4], text_color_unselected[4];
/* 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);
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);
#endif
}
}
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 = float3(0);
float3 v1 = float3(0);
for (int i = 0; i < reconstruction->camnr; i++, camera++) {
v0 = v1;
v1 = math::transform_point(camera_mat, float3(camera->mat[3]));
if (i > 0) {
path.append({v0, v1}, select_id);
}
}
}
}
}
}
float offaxis_shiftx_get(const Scene *scene, Object *ob, CameraInstanceData data, bool right_eye)
{
::Camera *cam = static_cast<::Camera *>(ob->data);
if (cam->stereo.convergence_mode != CAM_S3D_OFFAXIS) {
return 0.0;
}
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 = data.corner_x() * 2.0f;
return delta_shiftx * width;
}
/**
* Draw the stereo 3d support elements (cameras, plane, volume).
* They are only visible when not looking through the camera:
*/
void stereoscopy_extra_sync(const ObjectRef &ob_ref,
const select::ID select_id,
const State &state,
CameraInstanceData data)
{
CameraInstanceData stereodata = data;
const View3D *v3d = state.v3d;
const Scene *scene = state.scene;
Object *ob = ob_ref.object;
::Camera *cam = static_cast<::Camera *>(ob->data);
const bool is_select = state.selection_type != SelectionType::DISABLED;
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. */
frame.append(data, select_id);
}
for (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() = data.corner_x();
stereodata.corner_y() = data.corner_y();
stereodata.center_x() = data.center_x() + offaxis_shiftx_get(scene, ob, data, eye);
stereodata.center_y() = data.center_y();
stereodata.depth() = data.depth();
if (is_stereo3d_cameras) {
frame.append(stereodata, select_id);
/* Connecting line between cameras. */
relation_line.append({stereodata.matrix.location(), data.matrix.location()}, 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) */
// stereodata.color.xy() = {r + 0.15f, 1.0f};
copy_v2_v2(stereodata.color, float2(r + 0.15f, 1.0f));
frame.append(stereodata, select_id);
if (v3d->stereo3d_volume_alpha > 0.0f) {
/* Encode eye + intensity and alpha (see shader) */
// stereodata.color.xy() = {r + 0.999f, v3d->stereo3d_volume_alpha};
copy_v2_v2(stereodata.color, float2(r + 0.999f, v3d->stereo3d_convergence_alpha));
volume.append(stereodata, select_id);
}
/* restore */
stereodata.color = data.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.matrix[0]); /* We reconstruct from Z and Y */
// zero_v3(stereodata.matrix[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) */
// stereodata.color.xy() = {0.1f, 1.0f};
copy_v2_v2(stereodata.color, float2(0.1f, 1.0f));
frame.append(stereodata, select_id);
if (v3d->stereo3d_convergence_alpha > 0.0f) {
/* Encode eye + intensity and alpha (see shader) */
// stereodata.color.xy() = {0.0f, v3d->stereo3d_convergence_alpha};
copy_v2_v2(stereodata.color, float2(0.0f, v3d->stereo3d_convergence_alpha));
volume.append(stereodata, select_id);
}
}
}
virtual void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res,
const State &state) final override
{
Object *ob = ob_ref.object;
BLI_assert(ob->type == OB_CAMERA);
CameraInstanceData data(ExtraInstanceData(ob, res.object_wire_color(ob_ref, state)));
const View3D *v3d = state.v3d;
const Scene *scene = state.scene;
const RegionView3D *rv3d = state.rv3d;
::Camera *cam = static_cast<::Camera *>(ob->data);
Object *camera_object = DEG_get_evaluated_object(state.depsgraph, v3d->camera);
const bool is_select = state.selection_type != SelectionType::DISABLED;
const bool is_active = (ob == camera_object);
const bool look_through = (is_active && (rv3d->persp == RV3D_CAMOB));

is_camera_view

is_camera_view
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 && (!look_through) &&
((v3d->stereo3d_flag) != 0);
const bool is_selection_camera_stereo = is_select && look_through && 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;
}
data.matrix.view<3, 3>() = math::normalize(data.matrix.view<3, 3>());
float3 scale_inv = 1.0f / scale;
float4x3 vecs;
float2 aspect_ratio;
float2 shift;
float drawsize;
BKE_camera_view_frame_ex(scene,
cam,
cam->drawsize,
look_through,
scale_inv,
aspect_ratio,
shift,
&drawsize,
vecs.ptr());
/* Apply scale to simplify the rest of the drawing. */
for (int i : IndexRange(4)) {
vecs[i] *= scale;
/* Project to z=-1 plane. Makes positioning / scaling easier. (see shader) */
// vecs[i].xy() *= 1.0f / std::abs(vecs[i].z);
mul_v2_fl(vecs[i], 1.0f / std::abs(vecs[i].z));
}
/* Frame coords */
float2 center = (vecs[0].xy() + vecs[2].xy()) / 2.0f;
float2 corner = vecs[0].xy() - center;
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 (look_through) {
/* 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() += offaxis_shiftx_get(scene, ob, data, is_right);
for (int i : IndexRange(4)) {
/* Partial copy to avoid overriding packed data. */
// data.matrix[i].xyz() = mat[i].xyz();
copy_v3_v3(data.matrix[i], mat[i].xyz());
}
}
data.depth() *= -1.0f; /* Hides the back of the camera wires (see shader). */
frame.append(data, select_id);
}
}
else {
/* Stereo cameras, volumes, plane drawing. */
if (is_stereo3d_display_extra) {
stereoscopy_extra_sync(ob_ref, select_id, state, data);
}
else {
frame.append(data, select_id);
}
}
if (!look_through) {
/* Triangle. */
float tria_size = 0.7f * drawsize / std::abs(data.depth());
float tria_margin = 0.1f * drawsize / std::abs(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;
ExtraInstanceBuf &buf = is_active ? tria : 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;
distances.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;
distances.append(data, select_id);
}
}
/* Motion Tracking. */
if (v3d->flag2 & V3D_SHOW_RECONSTRUCTION) {
view3d_reconstruction_sync(ob_ref, select_id, res, state, data);
}
#if 0
/* TODO(Miguel Pozo): */
/* 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);
}
#endif
}
};
using Camera = OverlayType<CameraPasses>;
} // namespace blender::draw::overlay

View File

@ -0,0 +1,62 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "overlay_next_bounds.hh"
#include "DNA_rigidbody_types.h"
namespace blender::draw::overlay {
class CollisionPasses : public BoundPassesBase {

I would just merge that with the BoundPasses and avoid the base class. This would avoid splitting the drawing buffers.

I would just merge that with the `BoundPasses` and avoid the base class. This would avoid splitting the drawing buffers.
public:
CollisionPasses(SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors,
bool in_front)
: BoundPassesBase("Collisions", selection_type, shapes, theme_colors, in_front){};
virtual void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res,
const State &state) final override
{
Object *ob = ob_ref.object;
const bool from_dupli = ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI);
if (from_dupli || ob->rigidbody_object == nullptr) {
return;
}
int bound_type;
switch (ob->rigidbody_object->shape) {
case RB_SHAPE_BOX:
bound_type = OB_BOUND_BOX;
break;
case RB_SHAPE_SPHERE:
bound_type = OB_BOUND_SPHERE;
break;
case RB_SHAPE_CONE:
bound_type = OB_BOUND_CONE;
break;
case RB_SHAPE_CYLINDER:
bound_type = OB_BOUND_CYLINDER;
break;
case RB_SHAPE_CAPSULE:
bound_type = OB_BOUND_CAPSULE;
break;
default:
BLI_assert_unreachable();
}
bounds_sync(ob_ref, select_id, res, state, bound_type, true);
}
};
using Collision = OverlayType<CollisionPasses>;
} // namespace blender::draw::overlay

View File

@ -9,121 +9,80 @@
#pragma once
#include "overlay_next_private.hh"
namespace blender::draw::overlay {
class Empties {
using EmptyInstanceBuf = ShapeInstanceBuf<ExtraInstanceData>;
class EmptyPassesBase : public OverlayPasses {
private:
const SelectionType selection_type_;
PassSimple empty_ps_ = {"Empties"};
PassSimple empty_in_front_ps_ = {"Empties_In_front"};
struct CallBuffers {
const SelectionType selection_type_;
EmptyInstanceBuf plain_axes_buf = {selection_type_, "plain_axes_buf"};
EmptyInstanceBuf single_arrow_buf = {selection_type_, "single_arrow_buf"};
EmptyInstanceBuf cube_buf = {selection_type_, "cube_buf"};
EmptyInstanceBuf circle_buf = {selection_type_, "circle_buf"};
EmptyInstanceBuf sphere_buf = {selection_type_, "sphere_buf"};
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_}};
ExtraInstanceBuf plain_axes = extra_buf("plain_axes", shapes.plain_axes);
ExtraInstanceBuf single_arrow = extra_buf("single_arrow", shapes.single_arrow);
ExtraInstanceBuf arrows = extra_buf("arrows", shapes.arrows);
ExtraInstanceBuf image = extra_buf("image", shapes.quad_wire);
ExtraInstanceBuf circle = extra_buf("circle", shapes.circle);
ExtraInstanceBuf cube = extra_buf("cube", shapes.empty_cube);
ExtraInstanceBuf sphere = extra_buf("sphere", shapes.empty_sphere);
ExtraInstanceBuf cone = extra_buf("cone", shapes.empty_cone);
public:
Empties(const SelectionType selection_type) : selection_type_(selection_type){};
EmptyPassesBase(const char *name,
SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors,
bool in_front)
: OverlayPasses(name, selection_type, shapes, theme_colors, in_front){};
void begin_sync()
protected:
ExtraInstanceBuf &empty_buf(int empty_drawtype)
{
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, 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(
float4x4(ob_ref.object->object_to_world), color, ob_ref.object->empty_drawsize);
const select::ID select_id = res.select_id(ob_ref);
switch (ob_ref.object->empty_drawtype) {
switch (empty_drawtype) {
case OB_PLAINAXES:
call_bufs.plain_axes_buf.append(data, select_id);
break;
return plain_axes;
case OB_SINGLE_ARROW:
call_bufs.single_arrow_buf.append(data, select_id);
break;
return single_arrow;
case OB_CUBE:
call_bufs.cube_buf.append(data, select_id);
break;
return cube;
case OB_CIRCLE:
call_bufs.circle_buf.append(data, select_id);
break;
return circle;
case OB_EMPTY_SPHERE:
call_bufs.sphere_buf.append(data, select_id);
break;
return sphere;
case OB_EMPTY_CONE:
call_bufs.cone_buf.append(data, select_id);
break;
return cone;
case OB_ARROWS:
call_bufs.arrows_buf.append(data, select_id);
break;
return arrows;
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;
return image;
default:
BLI_assert_unreachable();
return plain_axes;
}
}
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]);
}
void draw(Resources &res, Manager &manager, View &view)
{
GPU_framebuffer_bind(res.overlay_line_fb);
manager.submit(empty_ps_, view);
}
void draw_in_front(Resources &res, Manager &manager, View &view)
{
GPU_framebuffer_bind(res.overlay_line_in_front_fb);
manager.submit(empty_in_front_ps_, view);
}
};
class EmptyPasses : public EmptyPassesBase {
public:
EmptyPasses(SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors,
bool in_front)
: EmptyPassesBase("Empties", selection_type, shapes, theme_colors, in_front){};
virtual void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res,
const State &state) final override
{
Object *ob = ob_ref.object;
BLI_assert(ob->type == OB_EMPTY);
ExtraInstanceData data(ob, res.object_wire_color(ob_ref, state));
empty_buf(ob->empty_drawtype).append(data, select_id);
if (ob->empty_drawtype == OB_EMPTY_IMAGE) {
/** TODO:
* See OVERLAY_image_empty_cache_populate() for the image. */
}
}
};
using Empty = OverlayType<EmptyPasses>;
} // namespace blender::draw::overlay

View File

@ -0,0 +1,133 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "BKE_anim_path.h"
#include "DNA_curve_types.h"
#include "DNA_object_force_types.h"
#include "overlay_next_private.hh"
namespace blender::draw::overlay {
class ForceFieldPasses : public OverlayPasses {
ExtraInstanceBuf field_wind = extra_buf("field_wind", shapes.field_wind);
ExtraInstanceBuf field_force = extra_buf("field_force", shapes.field_force);
ExtraInstanceBuf field_vortex = extra_buf("field_vortex", shapes.field_vortex);
ExtraInstanceBuf field_curve = extra_buf("field_curve", shapes.field_curve);
ExtraInstanceBuf field_tube_limit = extra_buf("field_tube_limit", shapes.field_tube_limit);
ExtraInstanceBuf field_cone_limit = extra_buf("field_cone_limit", shapes.field_cone_limit);
ExtraInstanceBuf field_sphere_limit = extra_buf("field_sphere_limit", shapes.field_sphere_limit);
public:
ForceFieldPasses(SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors,
bool in_front)
: OverlayPasses("Force Fields", selection_type, shapes, theme_colors, in_front){};
virtual void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res,
const State &state) final override
{
Object *ob = ob_ref.object;
if (!ob->pd || !ob->pd->forcefield) {
return;
}
ExtraInstanceData data(ob_ref.object, res.object_wire_color(ob_ref, state));
/* Pack render data into object matrix. */
float &size_x = data.matrix[0].w;
float &size_y = data.matrix[1].w;
float &size_z = data.matrix[2].w;
PartDeflect *pd = ob->pd;
Curve *cu = (ob->type == OB_CURVES_LEGACY) ? static_cast<Curve *>(ob->data) : nullptr;
data.color = res.object_background_blend_color(ob_ref, state);
size_x = size_y = size_z = ob->empty_drawsize;
switch (pd->forcefield) {
case PFIELD_FORCE:
field_force.append(data, select_id);
break;
case PFIELD_WIND:
size_z = pd->f_strength;
field_wind.append(data, select_id);
break;
case PFIELD_VORTEX:
size_y = (pd->f_strength < 0.0f) ? -size_y : size_y;
field_vortex.append(data, select_id);
break;
case PFIELD_GUIDE:
if (cu && (cu->flag & CU_PATH) && ob->runtime.curve_cache->anim_path_accum_length) {
float4x4 matrix = data.matrix;
float4 position;
size_x = size_y = size_z = pd->f_strength;
BKE_where_on_path(ob, 0.0f, position, nullptr, nullptr, nullptr, nullptr);
matrix.location() = data.matrix.location() + position.xyz();
field_curve.append(data.with_matrix(matrix), select_id);
BKE_where_on_path(ob, 1.0f, position, nullptr, nullptr, nullptr, nullptr);
matrix.location() = data.matrix.location() + position.xyz();
field_sphere_limit.append(data.with_matrix(matrix), select_id);
}
break;
}
if (pd->falloff == PFIELD_FALL_TUBE) {
if (pd->flag & (PFIELD_USEMAX | PFIELD_USEMAXR)) {
size_z = (pd->flag & PFIELD_USEMAX) ? pd->maxdist : 0.0f;
size_x = (pd->flag & PFIELD_USEMAXR) ? pd->maxrad : 1.0f;
size_y = size_x;
field_tube_limit.append(data, select_id);
}
if (pd->flag & (PFIELD_USEMIN | PFIELD_USEMINR)) {
size_z = (pd->flag & PFIELD_USEMIN) ? pd->mindist : 0.0f;
size_x = (pd->flag & PFIELD_USEMINR) ? pd->minrad : 1.0f;
size_y = size_x;
field_tube_limit.append(data, select_id);
}
}
else if (pd->falloff == PFIELD_FALL_CONE) {
if (pd->flag & (PFIELD_USEMAX | PFIELD_USEMAXR)) {
float radius = DEG2RADF((pd->flag & PFIELD_USEMAXR) ? pd->maxrad : 1.0f);
float distance = (pd->flag & PFIELD_USEMAX) ? pd->maxdist : 0.0f;
size_x = distance * math::sin(radius);
size_z = distance * math::cos(radius);
size_y = size_x;
field_cone_limit.append(data, select_id);
}
if (pd->flag & (PFIELD_USEMIN | PFIELD_USEMINR)) {
float radius = DEG2RADF((pd->flag & PFIELD_USEMINR) ? pd->minrad : 1.0f);
float distance = (pd->flag & PFIELD_USEMIN) ? pd->mindist : 0.0f;
size_x = distance * math::sin(radius);
size_z = distance * math::cos(radius);
size_y = size_x;
field_cone_limit.append(data, select_id);
}
}
else if (pd->falloff == PFIELD_FALL_SPHERE) {
if (pd->flag & PFIELD_USEMAX) {
size_x = size_y = size_z = pd->maxdist;
field_sphere_limit.append(data, select_id);
}
if (pd->flag & PFIELD_USEMIN) {
size_x = size_y = size_z = pd->mindist;
field_sphere_limit.append(data, select_id);
}
}
}
};
using ForceField = OverlayType<ForceFieldPasses>;
} // namespace blender::draw::overlay

View File

@ -12,6 +12,14 @@
#include "overlay_next_instance.hh"
#include "overlay_next_bounds.hh"
#include "overlay_next_camera.hh"
#include "overlay_next_collision.hh"
#include "overlay_next_empty.hh"
#include "overlay_next_force_field.hh"
#include "overlay_next_light.hh"
#include "overlay_next_light_probe.hh"
namespace blender::draw::overlay {
void Instance::init()
@ -22,6 +30,7 @@ void Instance::init()
BKE_view_layer_synced_ensure(ctx->scene, ctx->view_layer);
state.depsgraph = ctx->depsgraph;
state.manager = DRW_manager_get();
state.view_layer = ctx->view_layer;
state.scene = ctx->scene;
state.v3d = ctx->v3d;
@ -30,6 +39,7 @@ void Instance::init()
state.object_mode = ctx->object_mode;
state.pixelsize = U.pixelsize;
state.selection_type = selection_type_;
state.ctx_mode = CTX_data_mode_enum_ex(ctx->object_edit, ctx->obact, ctx->object_mode);
state.space_type = state.v3d != nullptr ? SPACE_VIEW3D : eSpace_Type(ctx->space_data->spacetype);
if (state.v3d != nullptr) {
@ -75,9 +85,18 @@ void Instance::begin_sync()
background.begin_sync(resources, state);
prepass.begin_sync(resources, state);
empties.begin_sync();
metaballs.begin_sync();
grid.begin_sync(resources, state, view);
bounds.begin_sync(resources, state);
cameras.begin_sync(resources, state);
collisions.begin_sync(resources, state);
empties.begin_sync(resources, state);
force_fields.begin_sync(resources, state);
lights.begin_sync(resources, state);
light_probes.begin_sync(resources, state);
object_centers.begin_sync(resources, state);
object_relations.begin_sync(resources, state);
speakers.begin_sync(resources, state);
}
void Instance::object_sync(ObjectRef &ob_ref, Manager &manager)
@ -85,6 +104,12 @@ 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 || state.hide_overlays) {
return;
}
const select::ID select_id = resources.select_id(ob_ref);

I would not put the select_id creation there. It would create id for object that might not be drawn. Also it is further for the code that actually uses it.

I would not put the select_id creation there. It would create id for object that might not be drawn. Also it is further for the code that actually uses it.
if (needs_prepass) {
switch (ob_ref.object->type) {
case OB_MESH:
@ -92,7 +117,7 @@ void Instance::object_sync(ObjectRef &ob_ref, Manager &manager)
case OB_CURVES:
case OB_FONT:
case OB_CURVES_LEGACY:
prepass.object_sync(manager, ob_ref, resources);
prepass.object_sync(manager, ob_ref, select_id, resources);
break;
}
}
@ -121,19 +146,36 @@ 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);
break;
case OB_ARMATURE:
break;
case OB_MBALL:
if (!in_edit_mode) {
metaballs.object_sync(ob_ref, resources, state);
}
case OB_CAMERA:
cameras.object_sync(ob_ref, select_id, resources, state);
break;
case OB_EMPTY:
empties.object_sync(ob_ref, select_id, resources, state);
break;
case OB_GPENCIL_LEGACY:
break;
case OB_LAMP:
lights.object_sync(ob_ref, select_id, resources, state);
break;
case OB_LIGHTPROBE:
light_probes.object_sync(ob_ref, select_id, resources, state);
break;
case OB_MBALL:
if (!in_edit_mode) {
metaballs.object_sync(ob_ref, select_id, resources, state);
}
break;
case OB_SPEAKER:
speakers.object_sync(ob_ref, select_id, resources, state);
break;
}
bounds.object_sync(ob_ref, select_id, resources, state);
collisions.object_sync(ob_ref, select_id, resources, state);
force_fields.object_sync(ob_ref, select_id, resources, state);
object_centers.object_sync(ob_ref, select_id, resources, state);
object_relations.object_sync(ob_ref, select_id, resources, state);
}
}
@ -142,7 +184,16 @@ void Instance::end_sync()
resources.end_sync();
metaballs.end_sync(resources, shapes, state);
empties.end_sync(resources, shapes, state);
bounds.end_sync(resources, state);
cameras.end_sync(resources, state);
collisions.end_sync(resources, state);
empties.end_sync(resources, state);
force_fields.end_sync(resources, state);
lights.end_sync(resources, state);
light_probes.end_sync(resources, state);
object_centers.end_sync(resources, state);
object_relations.end_sync(resources, state);
speakers.end_sync(resources, state);
}
void Instance::draw(Manager &manager)
@ -157,7 +208,7 @@ void Instance::draw(Manager &manager)
const DRWView *view_legacy = DRW_view_default_get();
View view("OverlayView", view_legacy);
/* TODO: Better semantics using a switch? */
/* 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);
@ -207,13 +258,31 @@ void Instance::draw(Manager &manager)
background.draw(resources, manager);
empties.draw(resources, manager, view);
metaballs.draw(resources, manager, view);
bounds.draw(resources, manager, view);
cameras.draw(resources, manager, view);
collisions.draw(resources, manager, view);
empties.draw(resources, manager, view);
force_fields.draw(resources, manager, view);
lights.draw(resources, manager, view);
light_probes.draw(resources, manager, view);
object_centers.draw(resources, manager, view);
object_relations.draw(resources, manager, view);
speakers.draw(resources, manager, view);
grid.draw(resources, manager, view);
empties.draw_in_front(resources, manager, view);
metaballs.draw_in_front(resources, manager, view);
bounds.draw_in_front(resources, manager, view);
cameras.draw_in_front(resources, manager, view);
collisions.draw_in_front(resources, manager, view);
empties.draw_in_front(resources, manager, view);
force_fields.draw_in_front(resources, manager, view);
lights.draw_in_front(resources, manager, view);
light_probes.draw_in_front(resources, manager, view);
object_centers.draw_in_front(resources, manager, view);
object_relations.draw_in_front(resources, manager, view);
speakers.draw_in_front(resources, manager, view);
// anti_aliasing.draw(resources, manager, view);

View File

@ -11,11 +11,23 @@
#include "overlay_next_private.hh"
#include "overlay_next_background.hh"
#include "overlay_next_empty.hh"
#include "overlay_next_grid.hh"
#include "overlay_next_metaball.hh"
#include "overlay_next_object_center.hh"
#include "overlay_next_object_relation.hh"
#include "overlay_next_prepass.hh"
#include "overlay_next_bounds.hh"
#include "overlay_next_camera.hh"
#include "overlay_next_collision.hh"
#include "overlay_next_empty.hh"
#include "overlay_next_force_field.hh"
#include "overlay_next_light.hh"
#include "overlay_next_light_probe.hh"
#include "overlay_next_object_center.hh"
#include "overlay_next_object_relation.hh"
#include "overlay_next_speaker.hh"
namespace blender::draw::overlay {
/**
@ -40,8 +52,17 @@ class Instance {
Background background;
Prepass prepass;
Metaballs metaballs = {selection_type_};
Empties empties = {selection_type_};
Grid grid;
Bound bounds = {selection_type_, shapes, G_draw.block};
Camera cameras = {selection_type_, shapes, G_draw.block};
Collision collisions = {selection_type_, shapes, G_draw.block};
Empty empties = {selection_type_, shapes, G_draw.block};
ForceField force_fields = {selection_type_, shapes, G_draw.block};
Light lights = {selection_type_, shapes, G_draw.block};
LightProbe light_probes = {selection_type_, shapes, G_draw.block};
ObjectCenter object_centers = {selection_type_, shapes, G_draw.block};
ObjectRelation object_relations = {selection_type_, shapes, G_draw.block};
Speaker speakers = {selection_type_, shapes, G_draw.block};
Instance(const SelectionType selection_type) : selection_type_(selection_type){};

View File

@ -0,0 +1,152 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "overlay_next_private.hh"
namespace blender::draw::overlay {
using GroundLineInstanceBuf = BatchInstanceBuf<float4>;
class LightPasses : public OverlayPasses {
GroundLineInstanceBuf groundline = {"groundline", shapes.groundline.get(), selection_type};
ExtraInstanceBuf icon_inner = extra_buf("light_icon_inner", shapes.light_icon_inner);
ExtraInstanceBuf icon_outer = extra_buf("light_icon_outer", shapes.light_icon_outer);
ExtraInstanceBuf icon_sun_rays = extra_buf("light_icon_sun_rays", shapes.light_icon_sun_rays);
ExtraInstanceBuf point = extra_buf("light_point", shapes.light_point);
ExtraInstanceBuf sun = extra_buf("light_sun", shapes.light_sun);
ExtraInstanceBuf spot = extra_buf("light_spot", shapes.light_spot);
ExtraInstanceBuf spot_cone_back = extra_buf(
"light_spot_cone_back", shapes.light_spot_cone, BLEND_CULL_BACK);
ExtraInstanceBuf spot_cone_front = extra_buf(
"light_spot_cone_front", shapes.light_spot_cone, BLEND_CULL_FRONT);
ExtraInstanceBuf area_disk = extra_buf("light_area_disk", shapes.light_area_disk);
ExtraInstanceBuf area_square = extra_buf("light_area_square", shapes.light_area_square);
public:
LightPasses(SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors,
bool in_front)
: OverlayPasses("Lights", selection_type, shapes, theme_colors, in_front){};
virtual void begin_sync(Resources &res, const State &state) final override
{
OverlayPasses::begin_sync(res, state);
groundline.clear();
}
virtual void end_sync(Resources &res, const State &state) final override
{
OverlayPasses::end_sync(res, state);
if (groundline.data_buf.is_empty()) {
return;
}
PassSimple::Sub &ps = ps_.sub("GroundLine");
ps.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_BLEND_ADD |
state.clipping_state);
ps.shader_set(res.shaders.extra_groundline.get());
/* TODO: Fixed index. */
ps.bind_ubo("globalsBlock", &res.globals_buf);
groundline.end_sync(ps);
}
virtual void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res,
const State &state) final override
{
Object *ob = ob_ref.object;
BLI_assert(ob->type == OB_LAMP);
ExtraInstanceData data(ob_ref.object, res.object_wire_color(ob_ref, state));
/* Pack render data into object matrix. */
float4x4 &matrix = data.matrix;
float &area_size_x = matrix[0].w;
float &area_size_y = matrix[1].w;
float &spot_cosine = matrix[0].w;
float &spot_blend = matrix[1].w;
float &clip_start = matrix[2].w;
float &clip_end = matrix[3].w;
Light *la = static_cast<Light *>(ob->data);
/* FIXME / TODO: clip_end has no meaning nowadays.
* In EEVEE, Only clip_start is used shadow-mapping.
* Clip end is computed automatically based on light power.
* For now, always use the custom distance as clip_end. */
clip_end = la->att_dist;
clip_start = la->clipsta;
/* Remove the alpha. */
data.color = float4(data.color.xyz(), 1.0f);
float4 light_color = data.color;
if (state.overlay.flag & V3D_OVERLAY_SHOW_LIGHT_COLORS) {
light_color = float4(la->r, la->g, la->b, 1.0f);
}
groundline.append(float4(data.matrix.location()), select_id);
icon_inner.append(data.with_color(light_color), select_id);
icon_outer.append(data, select_id);
if (la->type == LA_LOCAL) {
area_size_x = area_size_y = la->radius;
point.append(data, select_id);
}
else if (la->type == LA_SUN) {
sun.append(data, select_id);
icon_sun_rays.append(data.with_color(light_color), select_id);
}
else if (la->type == LA_SPOT) {
/* Previous implementation was using the clip-end distance as cone size.
* We cannot do this anymore so we use a fixed size of 10. (see #72871) */
matrix = math::scale(matrix, float3(10.0f));
/* For cycles and EEVEE the spot attenuation is:
* `y = (1/sqrt(1 + x^2) - a)/((1 - a) b)`
* x being the tangent of the angle between the light direction and the
* generatrix of the cone. We solve the case where spot attenuation y = 1
* and y = 0 root for y = 1 is sqrt(1/c^2 - 1) root for y = 0 is
* sqrt(1/a^2 - 1) and use that to position the blend circle. */
float a = cosf(la->spotsize * 0.5f);
float b = la->spotblend;
float c = a * b - a - b;
float a2 = a * a;
float c2 = c * c;
/* Optimized version or root1 / root0 */
spot_blend = sqrtf((a2 - a2 * c2) / (c2 - a2 * c2));
spot_cosine = a;
/* HACK: We pack the area size in alpha color. This is decoded by the shader. */
data.color.w = -max_ff(la->radius, FLT_MIN);
spot.append(data, select_id);
if ((la->mode & LA_SHOW_CONE) && !DRW_state_is_select()) {
spot_cone_front.append(data.with_color({0.0f, 0.0f, 0.0f, 0.5f}), select_id);
spot_cone_back.append(data.with_color({1.0f, 1.0f, 1.0f, 0.3f}), select_id);
}
}
else if (la->type == LA_AREA) {
ExtraInstanceBuf &buf = ELEM(la->area_shape, LA_AREA_SQUARE, LA_AREA_RECT) ? area_square :
area_disk;
bool uniform_scale = !ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE);
area_size_x = la->area_size;
area_size_y = uniform_scale ? la->area_size : la->area_sizey;
buf.append(data, select_id);
}
else {
BLI_assert_unreachable();
}
}
};
using Light = OverlayType<LightPasses>;
} // namespace blender::draw::overlay

View File

@ -0,0 +1,198 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "DNA_lightprobe_types.h"
#include "overlay_next_empty.hh"
#include "overlay_next_light.hh"
namespace blender::draw::overlay {
class DotsGridPass {
PassMain ps_ = {"Probe Grid"};
public:
void begin_sync(Resources &res, const State &state)
{
ps_.init();
ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_PROGRAM_POINT_SIZE | state.clipping_state);
ps_.shader_set(res.shaders.extra_grid.get());
/* TODO(Miguel Pozo): Selection doesn't work. */
res.select_bind(ps_);
/* TODO: Fixed index. */
ps_.bind_ubo("globalsBlock", &res.globals_buf);
ps_.bind_texture("depthBuffer", &res.depth_tx);
}
void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
int3 grid_resolution,
int theme_id,
Resources & /*res*/,
const State &state)
{
float4x4 matrix = float4x4(ob_ref.object->object_to_world);
/* Pack render data into object matrix. */
matrix[0][3] = grid_resolution.x;
matrix[1][3] = grid_resolution.y;
matrix[2][3] = grid_resolution.z;
matrix[3][3] = theme_id;
uint cell_count = grid_resolution.x * grid_resolution.y * grid_resolution.z;
ResourceHandle res_handle = state.manager->resource_handle(matrix);
ps_.draw_procedural(GPU_PRIM_POINTS, 1, cell_count, 0, res_handle, select_id.get());
}
void draw(Manager &manager, View &view, Framebuffer &fb)
{
fb.bind();
manager.submit(ps_, view);
}
};
class LightProbePasses : public OverlayPasses {
DotsGridPass dots_grid;
GroundLineInstanceBuf groundline = {"groundline", shapes.groundline.get(), selection_type};
ExtraInstanceBuf probe_cube = extra_buf("probe_cube", shapes.probe_cube);
ExtraInstanceBuf probe_grid = extra_buf("probe_grid", shapes.probe_grid);
ExtraInstanceBuf probe_planar = extra_buf("probe_planar", shapes.probe_planar);
ExtraInstanceBuf single_arrow = extra_buf("single_arrow", shapes.single_arrow);
ExtraInstanceBuf quad = extra_buf("quad", shapes.quad);
ExtraInstanceBuf cube = extra_buf("cube", shapes.empty_cube);
ExtraInstanceBuf sphere = extra_buf("sphere", shapes.empty_sphere);
public:
LightProbePasses(SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors,
bool in_front)
: OverlayPasses("Lights", selection_type, shapes, theme_colors, in_front){};
virtual void begin_sync(Resources &res, const State &state) final override
{
OverlayPasses::begin_sync(res, state);
dots_grid.begin_sync(res, state);
groundline.clear();
}
virtual void end_sync(Resources &res, const State &state) final override
{
OverlayPasses::end_sync(res, state);
if (groundline.data_buf.is_empty()) {
return;
}
PassSimple::Sub &ps = ps_.sub("GroundLine");
ps.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_BLEND_ADD |
state.clipping_state);
ps.shader_set(res.shaders.extra_groundline.get());
/* TODO: Fixed index. */
ps.bind_ubo("globalsBlock", &res.globals_buf);
groundline.end_sync(ps);
}
virtual void draw(Manager &manager, View &view, Framebuffer &fb) final override
{
OverlayPasses::draw(manager, view, fb);
dots_grid.draw(manager, view, fb);
}
virtual void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res,
const State &state) final override
{
Object *ob = ob_ref.object;
BLI_assert(ob->type == OB_LIGHTPROBE);
ExtraInstanceData data(ob, res.object_wire_color(ob_ref, state));
const ::LightProbe *probe = (::LightProbe *)ob_ref.object->data;
const bool show_clipping = (probe->flag & LIGHTPROBE_FLAG_SHOW_CLIP_DIST) != 0;
const bool show_parallax = (probe->flag & LIGHTPROBE_FLAG_SHOW_PARALLAX) != 0;
const bool show_influence = (probe->flag & LIGHTPROBE_FLAG_SHOW_INFLUENCE) != 0;
const bool show_data = (ob_ref.object->base_flag & BASE_SELECTED) ||
res.selection_type != SelectionType::DISABLED;
if (probe->type == LIGHTPROBE_TYPE_CUBE) {
/* Pack render data into object matrix. */
data.matrix[2][3] = show_clipping ? probe->clipsta : -1.0;
data.matrix[3][3] = show_clipping ? probe->clipend : -1.0;
probe_cube.append(data, select_id);
groundline.append(float4(data.matrix.location()), select_id);
if (show_influence) {
float influence_start = probe->distinf * (1.0f - probe->falloff);
float influence_end = probe->distinf;
ExtraInstanceBuf &buf = (probe->attenuation_type == LIGHTPROBE_SHAPE_BOX) ? cube : sphere;
buf.append(data.with_size(influence_start), select_id);
buf.append(data.with_size(influence_end), select_id);
}
if (show_parallax) {
float radius = (probe->flag & LIGHTPROBE_FLAG_CUSTOM_PARALLAX) ? probe->distpar :
probe->distinf;
ExtraInstanceBuf &buf = (probe->parallax_type == LIGHTPROBE_SHAPE_BOX) ? cube : sphere;
buf.append(data.with_size(radius), select_id);
}
}
else if (probe->type == LIGHTPROBE_TYPE_GRID) {
/* Pack render data into object matrix. */
data.matrix[2][3] = show_clipping ? probe->clipsta : -1.0;
data.matrix[3][3] = show_clipping ? probe->clipend : -1.0;
probe_grid.append(data, select_id);
if (show_influence) {
float influence_start = 1.0f + probe->distinf * (1.0f - probe->falloff);
float influence_end = 1.0f + probe->distinf;
cube.append(data.with_size(influence_start), select_id);
cube.append(data.with_size(influence_end), select_id);
}
/* Data dots */
if (show_data) {
int3 resolution = int3(&probe->grid_resolution_x);
int theme_id = res.object_wire_theme_id(ob_ref, state) == TH_ACTIVE ? 1 : 2;
dots_grid.object_sync(ob_ref, select_id, resolution, theme_id, res, state);
}
}
else if (probe->type == LIGHTPROBE_TYPE_PLANAR) {
probe_planar.append(data, select_id);
if (res.selection_type != SelectionType::DISABLED &&
(probe->flag & LIGHTPROBE_FLAG_SHOW_DATA)) {
quad.append(data, select_id);
}
if (show_influence) {
data.matrix.z_axis() = math::normalize(data.matrix.z_axis()) * probe->distinf;
cube.append(data, select_id);
data.matrix.z_axis() *= 1.0f - probe->falloff;
cube.append(data, select_id);
}
data.matrix.z_axis() = float3(0);
cube.append(data, select_id);
data.matrix = float4x4(ob_ref.object->object_to_world);
data.matrix.view<3, 3>() = math::normalize(data.matrix.view<3, 3>());
single_arrow.append(data.with_size(ob_ref.object->empty_drawsize), select_id);
}
}
};
using LightProbe = OverlayType<LightProbePasses>;
} // namespace blender::draw::overlay

View File

@ -19,16 +19,16 @@ class Metaballs {
using SphereOutlineInstanceBuf = ShapeInstanceBuf<BoneInstanceData>;
private:
const SelectionType selection_type_;
PassSimple metaball_ps_ = {"MetaBalls"};
PassSimple metaball_in_front_ps_ = {"MetaBalls_In_front"};
SphereOutlineInstanceBuf circle_buf_ = {selection_type_, "metaball_data_buf"};
SphereOutlineInstanceBuf circle_in_front_buf_ = {selection_type_, "metaball_data_buf"};
SphereOutlineInstanceBuf circle_buf_;
SphereOutlineInstanceBuf circle_in_front_buf_;
public:
Metaballs(const SelectionType selection_type) : selection_type_(selection_type){};
Metaballs(const SelectionType selection_type)
: circle_buf_(selection_type, "metaball_data_buf"),
circle_in_front_buf_(selection_type, "metaball_in_front_data_buf"){};
void begin_sync()
{
@ -64,7 +64,10 @@ class Metaballs {
}
}
void object_sync(const ObjectRef &ob_ref, Resources &res, const State &state)
void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res,
const State &state)
{
SphereOutlineInstanceBuf &circle_buf = (ob_ref.object->dtx & OB_DRAW_IN_FRONT) != 0 ?
circle_in_front_buf_ :
@ -72,7 +75,6 @@ class Metaballs {
MetaBall *mb = static_cast<MetaBall *>(ob_ref.object->data);
const float4 &color = res.object_wire_color(ob_ref, state);
const select::ID select_id = res.select_id(ob_ref);
LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) {
/* Draw radius only. */

View File

@ -0,0 +1,84 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "overlay_next_private.hh"
namespace blender::draw::overlay {
class ObjectCenterPasses : public OverlayPasses {
ExtraInstanceBuf origin_xform = extra_buf("origin_xform", shapes.plain_axes, DEFAULT_ALWAYS);
ExtraInstanceBuf arrows = extra_buf("arrows", shapes.arrows);
PointInstanceBuf active = point_buf("active", theme_colors.color_active);
PointInstanceBuf selected = point_buf("selected", theme_colors.color_select);
PointInstanceBuf deselected = point_buf("deselected", theme_colors.color_deselect);
PointInstanceBuf selected_lib = point_buf("selected_lib", theme_colors.color_library_select);
PointInstanceBuf deselected_lib = point_buf("deselected_lib", theme_colors.color_library);
public:
ObjectCenterPasses(SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors,
bool in_front)
: OverlayPasses("Centers", selection_type, shapes, theme_colors, in_front){};
virtual void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res,
const State &state) override
{
Object *ob = ob_ref.object;
ExtraInstanceData data(ob_ref.object, res.object_wire_color(ob_ref, state));
const bool is_select_mode = state.selection_type != SelectionType::DISABLED;
const bool draw_xform = state.object_mode == OB_MODE_OBJECT &&
(state.scene->toolsettings->transform_flag & SCE_XFORM_DATA_ORIGIN) &&
(ob->base_flag & BASE_SELECTED) && !is_select_mode;
if (draw_xform) {
/* TODO(Miguel Pozo): What's this? */
const float4 color_xform = {0.15f, 0.15f, 0.15f, 0.7f};
origin_xform.append(data.with_color(color_xform), select_id);
}
const bool from_dupli = ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI);
if (!from_dupli && ob->dtx & OB_AXIS) {
arrows.append(data, select_id);
}
const bool is_paint_mode = state.object_mode & (OB_MODE_ALL_PAINT | OB_MODE_ALL_PAINT_GPENCIL |
OB_MODE_SCULPT_CURVES);
const bool hide_obcenters = state.overlay.flag & V3D_OVERLAY_HIDE_OBJECT_ORIGINS;
if (is_paint_mode || from_dupli || hide_obcenters) {
return;
}
float4 center = float4(ob->object_to_world[3]);
const bool is_library = ID_REAL_USERS(&ob->id) > 1 || ID_IS_LINKED(ob);
/* TODO(Miguel Pozo): Handle const. */
BKE_view_layer_synced_ensure(state.scene, (ViewLayer *)state.view_layer);
if (ob == BKE_view_layer_active_object_get(state.view_layer)) {
active.append(center, select_id);
}
else if (ob->base_flag & BASE_SELECTED) {
(is_library ? selected_lib : selected).append(center, select_id);
}
else if (state.v3d_flag & V3D_DRAW_CENTERS) {
(is_library ? deselected_lib : deselected).append(center, select_id);
}
}
};
using ObjectCenter = OverlayType<ObjectCenterPasses>;
} // namespace blender::draw::overlay

View File

@ -0,0 +1,151 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "overlay_next_private.hh"
#include "BKE_constraint.h"
#include "DNA_constraint_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_modifier_types.h"
#include "DNA_rigidbody_types.h"
namespace blender::draw::overlay {
class ObjectRelationPasses : public OverlayPasses {
public:
ObjectRelationPasses(SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors,
bool in_front)
: OverlayPasses("Object Relations", selection_type, shapes, theme_colors, in_front){};
PointInstanceBuf relation_point = point_buf("relation_point", theme_colors.color_wire);
LineInstanceBuf relation_line = line_buf("relation_line", theme_colors.color_wire);
LineInstanceBuf constraint_line = line_buf("constraint_line", theme_colors.color_grid_axis_z);
virtual void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources & /*res*/,
const State &state) override
{
Object *ob = ob_ref.object;
const bool is_select_mode = state.selection_type != SelectionType::DISABLED;
const bool from_dupli = ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI);
const bool hide_relations = state.v3d_flag & V3D_HIDE_HELPLINES;
if (is_select_mode || from_dupli || hide_relations) {
return;
}
const float4x4 ob_mat = float4x4(ob->object_to_world);
const float3 &ob_pos = ob_mat.location();
/* Parent lines. */
if (ob->parent && (DRW_object_visibility_in_active_context(ob->parent) & OB_VISIBLE_SELF)) {
float3 parent_pos = float3(ob->runtime.parent_display_origin);
relation_line.append({parent_pos, ob_pos}, select_id);
}
/* Hook lines. */
for (ModifierData *md : ListBaseWrapper<ModifierData>(&ob->modifiers)) {
if (md->type == eModifierType_Hook) {
HookModifierData *hmd = reinterpret_cast<HookModifierData *>(md);
float3 center = math::transform_point(ob_mat, float3(hmd->cent));
if (hmd->object) {
relation_line.append({hmd->object->object_to_world[3], center}, select_id);
}
relation_point.append(float4(center), select_id);
}
}
for (GpencilModifierData *md :
ListBaseWrapper<GpencilModifierData>(ob->greasepencil_modifiers)) {
if (md->type == eGpencilModifierType_Hook) {
HookGpencilModifierData *hmd = reinterpret_cast<HookGpencilModifierData *>(md);
float3 center = math::transform_point(ob_mat, float3(hmd->cent));
if (hmd->object) {
relation_line.append({hmd->object->object_to_world[3], center}, select_id);
}
relation_point.append(float4(center), select_id);
}
}
/* Rigidbody constraint lines. */
if (ob->rigidbody_constraint) {
Object *rbc_ob1 = ob->rigidbody_constraint->ob1;
Object *rbc_ob2 = ob->rigidbody_constraint->ob2;
if (rbc_ob1 && (DRW_object_visibility_in_active_context(rbc_ob1) & OB_VISIBLE_SELF)) {
relation_line.append({rbc_ob1->object_to_world[3], ob_pos}, select_id);
}
if (rbc_ob2 && (DRW_object_visibility_in_active_context(rbc_ob2) & OB_VISIBLE_SELF)) {
relation_line.append({rbc_ob2->object_to_world[3], ob_pos}, select_id);
}
}
/* Constraint lines */
if (!BLI_listbase_is_empty(&ob->constraints)) {
/** TODO(Miguel Pozo): Remove casting. */
Depsgraph *depsgraph = (Depsgraph *)state.depsgraph;
Scene *scene = (Scene *)state.scene;
bConstraintOb *con_ob = BKE_constraints_make_evalob(
depsgraph, scene, ob, nullptr, CONSTRAINT_OBTYPE_OBJECT);
for (bConstraint *con : ListBaseWrapper<bConstraint>(&ob->constraints)) {
if (ELEM(con->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_OBJECTSOLVER)) {
/* Special case for object solver and follow track constraints because they don't fill
* constraint targets properly (design limitation -- scene is needed for their target
* but it can't be accessed from get_targets callback) */
Object *cam_ob = nullptr;
if (con->type == CONSTRAINT_TYPE_FOLLOWTRACK) {
bFollowTrackConstraint *data = (bFollowTrackConstraint *)con->data;
cam_ob = data->camera ? data->camera : scene->camera;
}
else if (con->type == CONSTRAINT_TYPE_OBJECTSOLVER) {
bObjectSolverConstraint *data = (bObjectSolverConstraint *)con->data;
cam_ob = data->camera ? data->camera : scene->camera;
}
if (cam_ob) {
constraint_line.append({cam_ob->object_to_world[3], ob_pos}, select_id);
}
}
else {
const bConstraintTypeInfo *type_info = BKE_constraint_typeinfo_get(con);
ListBase targets = {nullptr, nullptr};
if ((con->ui_expand_flag & (1 << 0)) && BKE_constraint_targets_get(con, &targets)) {
BKE_constraint_custom_object_space_init(con_ob, con);
for (bConstraintTarget *target : ListBaseWrapper<bConstraintTarget>(&targets)) {
/* calculate target's position */
float3 target_pos = float3(0.0f);
if (target->flag & CONSTRAINT_TAR_CUSTOM_SPACE) {
target_pos = con_ob->space_obj_world_matrix[3];
}
else if (type_info->get_target_matrix) {
type_info->get_target_matrix(
depsgraph, con, con_ob, target, DEG_get_ctime(depsgraph));
target_pos = target->matrix[3];
}
constraint_line.append({target_pos, ob_pos}, select_id);
}
BKE_constraint_targets_flush(con, &targets, true);
}
}
}
/* NOTE: Don't use #BKE_constraints_clear_evalob here as that will reset `ob->constinv`. */
MEM_freeN(con_ob);
}
}
};
using ObjectRelation = OverlayType<ObjectRelationPasses>;
} // namespace blender::draw::overlay

View File

@ -33,7 +33,10 @@ class Prepass {
init_pass(prepass_in_front_ps_);
}
void object_sync(Manager &manager, const ObjectRef &ob_ref, Resources &res)
void object_sync(Manager &manager,
const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res)
{
PassMain &pass = (ob_ref.object->dtx & OB_DRAW_IN_FRONT) != 0 ? prepass_in_front_ps_ :
prepass_ps_;
@ -43,7 +46,7 @@ class Prepass {
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());
pass.draw(geom, res_handle, select_id.get());
}
}

View File

@ -33,6 +33,7 @@ using blender::draw::TextureRef;
struct State {
Depsgraph *depsgraph;
Manager *manager;
const ViewLayer *view_layer;
const Scene *scene;
const View3D *v3d;
@ -40,6 +41,7 @@ struct State {
const Base *active_base;
View3DOverlay overlay;
float pixelsize;
SelectionType selection_type;
enum eSpace_Type space_type;
enum eContextObjectMode ctx_mode;
enum eObjectMode object_mode;
@ -56,46 +58,88 @@ struct State {
DRWState clipping_state;
};
struct BatchDeleter {
void operator()(GPUBatch *shader)
{
GPU_BATCH_DISCARD_SAFE(shader);
}
};
using BatchPtr = std::unique_ptr<GPUBatch, BatchDeleter>;
/**
* Contains all overlay generic geometry batches.
*/
class ShapeCache {
private:
struct BatchDeleter {
void operator()(GPUBatch *shader)
{
GPU_BATCH_DISCARD_SAFE(shader);
}
};
using BatchPtr = std::unique_ptr<GPUBatch, BatchDeleter>;
public:
BatchPtr quad_wire;
// GPUBatch *drw_normal_arrow; /* Single Arrow? */
BatchPtr plain_axes;
BatchPtr single_arrow;
BatchPtr cube;
BatchPtr arrows;
BatchPtr quad_wire;
BatchPtr circle;
BatchPtr empty_cube;
BatchPtr empty_sphere;
BatchPtr empty_cone;
BatchPtr arrows;
BatchPtr empty_cylinder;
BatchPtr empty_capsule_body;
BatchPtr empty_capsule_cap;
BatchPtr sphere_solid;
BatchPtr quad;
BatchPtr grid;
BatchPtr metaball_wire_circle;
BatchPtr speaker;
BatchPtr groundline;
BatchPtr light_icon_inner;
BatchPtr light_icon_outer;
BatchPtr light_icon_sun_rays;
BatchPtr light_point;
BatchPtr light_sun;
BatchPtr light_spot;
BatchPtr light_spot_cone;
BatchPtr light_area_disk;
BatchPtr light_area_square;
BatchPtr probe_cube;
BatchPtr probe_grid;
BatchPtr probe_planar;
BatchPtr camera_volume;
BatchPtr camera_volume_wire;
BatchPtr camera_frame;
BatchPtr camera_tria_wire;
BatchPtr camera_tria;
BatchPtr camera_distances;
BatchPtr field_wind;
BatchPtr field_force;
BatchPtr field_vortex;
BatchPtr field_curve;
BatchPtr field_tube_limit;
BatchPtr field_cone_limit;
BatchPtr field_sphere_limit;
ShapeCache();
};
struct ShaderDeleter {
void operator()(GPUShader *shader)
{
DRW_SHADER_FREE_SAFE(shader);
}
};
using ShaderPtr = std::unique_ptr<GPUShader, ShaderDeleter>;
/**
* Shader module. Shared between instances.
*/
class ShaderModule {
private:
struct ShaderDeleter {
void operator()(GPUShader *shader)
{
DRW_SHADER_FREE_SAFE(shader);
}
};
using ShaderPtr = std::unique_ptr<GPUShader, ShaderDeleter>;
/** Shared shader module across all engine instances. */
static ShaderModule *g_shader_modules[2 /*Selection Instance*/][2 /*Clipping Enabled*/];
@ -115,6 +159,16 @@ class ShaderModule {
ShaderPtr depth_mesh;
ShaderPtr extra_shape;
ShaderPtr extra_groundline;
ShaderPtr extra_point;
ShaderPtr extra_line;
ShaderPtr extra_grid;
/* TODO */
ShaderPtr extra_wire;
ShaderPtr extra_wire_object;
ShaderPtr extra_loose_point;
ShaderModule(const SelectionType selection_type, const bool clipping_enabled);
/** Module */
@ -230,12 +284,25 @@ struct Resources : public select::SelectMap {
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);
}
};
/**
* Buffer containing instances of a certain shape.
*/
template<typename InstanceDataT> struct ShapeInstanceBuf : private select::SelectBuf {
template<typename InstanceDataT> struct ShapeInstanceBuf : protected select::SelectBuf {
StorageVectorBuffer<InstanceDataT> data_buf;
@ -254,7 +321,7 @@ template<typename InstanceDataT> struct ShapeInstanceBuf : private select::Selec
data_buf.append(data);
}
void end_sync(PassSimple &pass, GPUBatch *shape)
void end_sync(PassSimple::Sub &pass, GPUBatch *shape)
{
if (data_buf.size() == 0) {
return;
@ -266,4 +333,241 @@ template<typename InstanceDataT> struct ShapeInstanceBuf : private select::Selec
}
};
template<typename T> struct BatchInstanceBuf : public ShapeInstanceBuf<T> {
GPUBatch *batch;
BatchInstanceBuf(const char *name, GPUBatch *batch, const SelectionType selection_type)
: ShapeInstanceBuf<T>(selection_type, name), batch(batch){};
BatchInstanceBuf(const char *name,
GPUBatch *batch,
const SelectionType selection_type,
Vector<BatchInstanceBuf<T> *> &pass_vector)
: BatchInstanceBuf(name, batch, selection_type)
{
pass_vector.append(this);
};
void end_sync(PassSimple::Sub &pass)
{
ShapeInstanceBuf<T>::end_sync(pass, batch);
}
};
using ExtraInstanceBuf = BatchInstanceBuf<ExtraInstanceData>;
struct PointInstanceBuf : public ShapeInstanceBuf<float4> {
float4 color;
PointInstanceBuf(const char *name, float4 color, const SelectionType selection_type)
: ShapeInstanceBuf<float4>(selection_type, name), color(color){};
PointInstanceBuf(const char *name,
float4 color,
const SelectionType selection_type,
Vector<PointInstanceBuf *> &pass_vector)
: PointInstanceBuf(name, color, selection_type)
{
pass_vector.append(this);
};
void end_sync(PassSimple::Sub &pass)
{
if (data_buf.size() == 0) {
return;
}
this->select_bind(pass);
data_buf.push_update();
pass.bind_ssbo("data_buf", &data_buf);
pass.push_constant("ucolor", color);
pass.draw_procedural(GPU_PRIM_POINTS, data_buf.size(), 1);
}
};
struct LineInstanceBuf : public ShapeInstanceBuf<LineInstanceData> {
float4 color;
LineInstanceBuf(const char *name, float4 color, const SelectionType selection_type)
: ShapeInstanceBuf<LineInstanceData>(selection_type, name), color(color){};
LineInstanceBuf(const char *name,
float4 color,
const SelectionType selection_type,
Vector<LineInstanceBuf *> &pass_vector)
: LineInstanceBuf(name, color, selection_type)
{
pass_vector.append(this);
};
void end_sync(PassSimple::Sub &pass)
{
if (data_buf.size() == 0) {
return;
}
this->select_bind(pass);
data_buf.push_update();
pass.bind_ssbo("data_buf", &data_buf);
pass.push_constant("ucolor", color);
pass.draw_procedural(GPU_PRIM_LINES, data_buf.size(), 2);
}
};
class OverlayPasses {

This class is too generic of a name. It should be GeometryPass since it is instanciated twice in OverlayType.

This class is too generic of a name. It should be `GeometryPass` since it is instanciated twice in `OverlayType`.
protected:
const SelectionType selection_type;
const ShapeCache &shapes;
const GlobalsUboStorage &theme_colors;
PassSimple ps_;
enum ExtraType { DEFAULT, DEFAULT_ALWAYS, BLEND_CULL_FRONT, BLEND_CULL_BACK, MAX };

Document each values. Not sure what's the difference between DEFAULT and DEFAULT_ALWAYS.

Document each values. Not sure what's the difference between `DEFAULT` and `DEFAULT_ALWAYS`.
Vector<ExtraInstanceBuf *> extra_buffers_[ExtraType::MAX] = {{}};
ExtraInstanceBuf extra_buf(const char *name,
const BatchPtr &shape_ptr,
ExtraType pass_type = DEFAULT)
{
Vector<ExtraInstanceBuf *> &vector = extra_buffers_[pass_type];
return {name, shape_ptr.get(), selection_type, vector};
};
Vector<PointInstanceBuf *> point_buffers_ = {};
PointInstanceBuf point_buf(const char *name, float4 color)
{
return {name, color, selection_type, point_buffers_};
}
Vector<LineInstanceBuf *> line_buffers_ = {};
LineInstanceBuf line_buf(const char *name, float4 color)
{
return {name, color, selection_type, line_buffers_};
}
public:
OverlayPasses(const char *name,
SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors,
bool in_front)
: selection_type(selection_type),
shapes(shapes),
theme_colors(theme_colors),
ps_(in_front ? (name + std::string(" In Front")).c_str() : name){};
virtual void begin_sync(Resources &res, const State & /*state*/)
{
ps_.init();
res.select_bind(ps_);
for (Vector<ExtraInstanceBuf *> &vector : extra_buffers_) {
for (ExtraInstanceBuf *buf : vector) {
buf->clear();
}
}
for (PointInstanceBuf *buf : point_buffers_) {
buf->clear();
}
for (LineInstanceBuf *buf : line_buffers_) {
buf->clear();
}
};
virtual void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources & /*res*/,
const State &state) = 0;
virtual void end_sync(Resources &res, const State &state)
{
auto sub_pass =
[&](const char *name, ShaderPtr &shader, DRWState drw_state) -> PassSimple::Sub * {
PassSimple::Sub &ps = ps_.sub(name);
ps.state_set(drw_state);
ps.shader_set(shader.get());
/* TODO: Fixed index. */
ps.bind_ubo("globalsBlock", &res.globals_buf);
return &ps;
};
auto sub_pass_iter = [&](const char *name, auto iter, ShaderPtr &shader, DRWState drw_state) {
if (iter.is_empty()) {
return;
}
PassSimple::Sub *ps = sub_pass(name, shader, drw_state);
for (auto *buf : iter) {
buf->end_sync(*ps);
}
};
DRWState state_base = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | state.clipping_state;
DRWState state_default = state_base | DRW_STATE_DEPTH_LESS_EQUAL;
DRWState state_blend = state_default | DRW_STATE_BLEND_ALPHA;
DRWState state_point = state_blend | DRW_STATE_PROGRAM_POINT_SIZE;
sub_pass_iter("Default", extra_buffers_[DEFAULT], res.shaders.extra_shape, state_default);
sub_pass_iter("Default Always",
extra_buffers_[DEFAULT_ALWAYS],
res.shaders.extra_shape,
state_base | DRW_STATE_DEPTH_ALWAYS);
sub_pass_iter("Blend Cull Back",
extra_buffers_[BLEND_CULL_BACK],
res.shaders.extra_shape,
state_blend | DRW_STATE_CULL_BACK);
sub_pass_iter("Blend Cull Front",
extra_buffers_[BLEND_CULL_FRONT],
res.shaders.extra_shape,
state_blend | DRW_STATE_CULL_FRONT);
sub_pass_iter("Point", point_buffers_, res.shaders.extra_point, state_point);
sub_pass_iter("Line", line_buffers_, res.shaders.extra_line, state_blend);
};
virtual void draw(Manager &manager, View &view, Framebuffer &fb)
{
fb.bind();
manager.submit(ps_, view);
}
};
template<typename T> class OverlayType {
T passes_;
T passes_in_front_;
public:
OverlayType(const SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors)
: passes_(selection_type, shapes, theme_colors, false),
passes_in_front_(selection_type, shapes, theme_colors, true){};
void begin_sync(Resources &res, const State &state)
{
passes_.begin_sync(res, state);
passes_in_front_.begin_sync(res, state);
}
void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res,
const State &state)
{
T &passes = ob_ref.object->dtx & OB_DRAW_IN_FRONT ? passes_in_front_ : passes_;
passes.object_sync(ob_ref, select_id, res, state);
}
void end_sync(Resources &res, const State &state)
{
passes_.end_sync(res, state);
passes_in_front_.end_sync(res, state);
}
void draw(Resources &res, Manager &manager, View &view)
{
passes_.draw(manager, view, res.overlay_line_fb);
}
void draw_in_front(Resources &res, Manager &manager, View &view)
{
passes_in_front_.draw(manager, view, res.overlay_line_in_front_fb);
}
};
} // namespace blender::draw::overlay

View File

@ -12,19 +12,20 @@ namespace blender::draw::overlay {
ShaderModule *ShaderModule::g_shader_modules[2][2] = {{nullptr}};
ShaderModule::ShaderPtr ShaderModule::selectable_shader(const char *create_info_name)
ShaderPtr ShaderModule::selectable_shader(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 += clipping_enabled_ ? "_clipped" : "";
// create_info_name += ClippingEnabled ? "_clipped" : "";
// this->shader_ = GPU_shader_create_from_info_name(create_info_name.c_str());
UNUSED_VARS(clipping_enabled_);
/* WORKAROUND: ... but for now, we have to patch the create info used by the old engine. */
gpu::shader::ShaderCreateInfo info = *reinterpret_cast<const gpu::shader::ShaderCreateInfo *>(
GPU_shader_create_info_get(create_info_name));
info.define("OVERLAY_NEXT");
if (selection_type_ != SelectionType::DISABLED) {
info.define("SELECT_ENABLE");
}
@ -33,7 +34,7 @@ ShaderModule::ShaderPtr ShaderModule::selectable_shader(const char *create_info_
GPU_shader_create_from_info(reinterpret_cast<const GPUShaderCreateInfo *>(&info)));
}
ShaderModule::ShaderPtr ShaderModule::selectable_shader(
ShaderPtr ShaderModule::selectable_shader(
const char *create_info_name, std::function<void(gpu::shader::ShaderCreateInfo &info)> patch)
{
gpu::shader::ShaderCreateInfo info = *reinterpret_cast<const gpu::shader::ShaderCreateInfo *>(
@ -41,6 +42,8 @@ ShaderModule::ShaderPtr ShaderModule::selectable_shader(
patch(info);
info.define("OVERLAY_NEXT");
if (selection_type_ != SelectionType::DISABLED) {
info.define("SELECT_ENABLE");
/* Replace additional info. */
@ -75,11 +78,44 @@ ShaderModule::ShaderModule(const SelectionType selection_type, const bool clippi
extra_shape = selectable_shader("overlay_extra", [](gpu::shader::ShaderCreateInfo &info) {
info.storage_buf(0, Qualifier::READ, "ExtraInstanceData", "data_buf[]");

I would rename ExtraInstanceData to ShapeInstanceData so that we can use ShapeInstanceBuf instead of the less meaningful ExtraInstanceBuf.

I would rename `ExtraInstanceData` to `ShapeInstanceData` so that we can use `ShapeInstanceBuf` instead of the less meaningful `ExtraInstanceBuf`.
info.define("color", "data_buf[gl_InstanceID].color_");
info.define("inst_obmat", "data_buf[gl_InstanceID].object_to_world_");
info.define("color", "data_buf[gl_InstanceID]._color");
info.define("inst_obmat", "data_buf[gl_InstanceID].matrix");
info.vertex_inputs_.pop_last();
info.vertex_inputs_.pop_last();
});
extra_groundline = selectable_shader(
"overlay_extra_groundline", [](gpu::shader::ShaderCreateInfo &info) {
info.storage_buf(0, Qualifier::READ, "float4", "data_buf[]");
info.define("inst_pos", "data_buf[gl_InstanceID].xyz");
info.vertex_inputs_.pop_last();
/* Use the same vertex layout for all shapes. */
info.vertex_in(1, gpu::shader::Type::INT, "vclass");
});
extra_point = selectable_shader("overlay_extra_point", [](gpu::shader::ShaderCreateInfo &info) {
info.storage_buf(0, Qualifier::READ, "float4", "data_buf[]");
info.define("pos", "data_buf[gl_InstanceID].xyz");
info.vertex_inputs_.pop_last();
});
extra_line = selectable_shader("overlay_extra_wire", [](gpu::shader::ShaderCreateInfo &info) {
info.typedef_source("overlay_shader_shared.h");
info.storage_buf(0, Qualifier::READ, "LineInstanceData", "data_buf[]");
info.push_constant(gpu::shader::Type::VEC4, "ucolor");
info.define("pos",
"(gl_VertexID == 0 ? data_buf[gl_InstanceID].start.xyz : "
"data_buf[gl_InstanceID].end.xyz)");
info.define("color", "ucolor");
info.define("colorid", "0");
info.vertex_inputs_.pop_last();
info.vertex_inputs_.pop_last();
info.vertex_inputs_.pop_last();
});
extra_grid = selectable_shader("overlay_extra_grid", [](gpu::shader::ShaderCreateInfo &info) {
info.additional_info("draw_modelmat_new", "draw_resource_handle_new");
});
}
ShaderModule &ShaderModule::module_get(SelectionType selection_type, bool clipping_enabled)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "overlay_next_private.hh"
namespace blender::draw::overlay {
class SpeakerPasses : public OverlayPasses {
ExtraInstanceBuf speaker = extra_buf("speaker", shapes.speaker);
public:
SpeakerPasses(SelectionType selection_type,
const ShapeCache &shapes,
const GlobalsUboStorage &theme_colors,
bool in_front)
: OverlayPasses("Speakers", selection_type, shapes, theme_colors, in_front){};
virtual void object_sync(const ObjectRef &ob_ref,
const select::ID select_id,
Resources &res,
const State &state) final override
{
if (ob_ref.object->type == OB_SPEAKER) {
ExtraInstanceData data(ob_ref.object, res.object_wire_color(ob_ref, state));
speaker.append(data, select_id);
}
}
};
using Speaker = OverlayType<SpeakerPasses>;
} // namespace blender::draw::overlay

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 LineInstanceData LineInstanceData;
#endif
/* TODO(fclem): Should eventually become OVERLAY_BackgroundType.
@ -195,20 +196,65 @@ struct ThemeColorData {
BLI_STATIC_ASSERT_ALIGN(ThemeColorData, 16)
struct ExtraInstanceData {
float4 color_;
float4x4 object_to_world_;
#ifdef GPU_SHADER
/** NOTE: The `color` token is reserved for its macro alias. */
float4 _color;
#else
float4 color;
#endif
float4x4 matrix;
#if !defined(GPU_SHADER) && defined(__cplusplus)
ExtraInstanceData(const float4x4 &object_to_world, float4 &color, float draw_size)
ExtraInstanceData() = default;
ExtraInstanceData(const float4x4 &object_to_world, const float4 &color, float draw_size)
{
this->color_ = color;
this->object_to_world_ = object_to_world;
this->object_to_world_[3][3] = draw_size;
this->color = color;
this->matrix = object_to_world;
this->matrix[3][3] = draw_size;
};
ExtraInstanceData(const Object *object, const float4 &color)
: ExtraInstanceData(float4x4(object->object_to_world),
color,
object->type == OB_EMPTY ? object->empty_drawsize : 1.0f){};
ExtraInstanceData with_matrix(const float4x4 &matrix) const
{
ExtraInstanceData copy = *this;
copy.matrix = matrix;
copy.matrix[3][3] = this->matrix[3][3];
return copy;
}
ExtraInstanceData with_color(const float4 &color) const
{
ExtraInstanceData copy = *this;
copy.color = color;
copy.matrix[3][3] = this->matrix[3][3];
return copy;
}
ExtraInstanceData with_size(const float &draw_size) const
{
ExtraInstanceData copy = *this;
copy.matrix[3][3] = draw_size;
return copy;
}
#endif
};
BLI_STATIC_ASSERT_ALIGN(ExtraInstanceData, 16)
struct LineInstanceData {
float4 start;
float4 end;
#if !defined(GPU_SHADER) && defined(__cplusplus)
LineInstanceData(float3 start, float3 end) : start(start), end(end){};
#endif
};
BLI_STATIC_ASSERT_ALIGN(LineInstanceData, 16)
#ifndef GPU_SHADER
# ifdef __cplusplus
}

View File

@ -24,12 +24,18 @@ vec4 color_from_id(float color_id)
void main()
{
mat4 model_mat = gridModelMatrix;
#ifdef OVERLAY_NEXT
mat4 grid_matrix = ModelMatrix;
#else
mat4 grid_matrix = gridModelMatrix;
#endif
mat4 model_mat = grid_matrix;
model_mat[0][3] = model_mat[1][3] = model_mat[2][3] = 0.0;
model_mat[3][3] = 1.0;
float color_id = gridModelMatrix[3].w;
float color_id = grid_matrix[3].w;
ivec3 grid_resolution = ivec3(gridModelMatrix[0].w, gridModelMatrix[1].w, gridModelMatrix[2].w);
ivec3 grid_resolution = ivec3(grid_matrix[0].w, grid_matrix[1].w, grid_matrix[2].w);
vec3 ls_cell_location;
/* Keep in sync with update_irradiance_probe */

View File

@ -4,7 +4,11 @@
void main()
{
#ifdef OVERLAY_NEXT
vec3 world_pos = pos;
#else
vec3 world_pos = point_object_to_world(pos);
#endif
gl_Position = point_world_to_ndc(world_pos);
gl_PointSize = sizeObjectCenter;

View File

@ -9,7 +9,11 @@ vec2 screen_position(vec4 p)
void main()
{
#ifdef OVERLAY_NEXT
vec3 world_pos = pos;
#else
vec3 world_pos = point_object_to_world(pos);
#endif
gl_Position = point_world_to_ndc(world_pos);
#ifdef SELECT_EDGES

View File

@ -69,7 +69,7 @@ struct SelectBuf {
}
}
void select_bind(PassSimple &pass)
void select_bind(PassSimple::Sub &pass)
{
if (selection_type != SelectionType::DISABLED) {
select_buf.push_update();

View File

@ -615,13 +615,14 @@ void DrawMultiBuf::bind(RecordingState &state,
group.start = resource_id_count_;
resource_id_count_ += group.len * view_len;
int batch_inst_len;
int batch_vert_len, batch_vert_first, batch_base_index, batch_inst_len;
/* Now that GPUBatches are guaranteed to be finished, extract their parameters. */
GPU_batch_draw_parameter_get(group.gpu_batch,
&group.vertex_len,
&group.vertex_first,
&group.base_index,
&batch_inst_len);
GPU_batch_draw_parameter_get(
group.gpu_batch, &batch_vert_len, &batch_vert_first, &batch_base_index, &batch_inst_len);
group.vertex_len = group.vertex_len == -1 ? batch_vert_len : group.vertex_len;
group.vertex_first = group.vertex_first == -1 ? batch_vert_first : group.vertex_first;
group.base_index = batch_base_index;
/* Instancing attributes are not supported using the new pipeline since we use the base
* instance to set the correct resource_id. Workaround is a storage_buf + gl_InstanceID. */