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