From e356737451df0278717fe2f6ba258bf1fbbe860d Mon Sep 17 00:00:00 2001 From: Laurynas Duburas Date: Tue, 30 Jul 2024 18:17:28 +0200 Subject: [PATCH] Overlay-Next: Object Bounds Overlay-Next version of bounds and collisions. Rel #102179 Pull Request: https://projects.blender.org/blender/blender/pulls/124654 --- source/blender/draw/CMakeLists.txt | 1 + .../engines/overlay/overlay_next_bounds.hh | 171 ++++++++++++++++++ .../engines/overlay/overlay_next_instance.cc | 9 +- .../engines/overlay/overlay_next_instance.hh | 3 +- .../engines/overlay/overlay_next_private.hh | 3 + .../engines/overlay/overlay_next_shape.cc | 58 +++++- 6 files changed, 238 insertions(+), 7 deletions(-) create mode 100644 source/blender/draw/engines/overlay/overlay_next_bounds.hh diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 23c98013575..44975e0c7c7 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -290,6 +290,7 @@ set(SRC engines/image/image_usage.hh engines/overlay/overlay_engine.h engines/overlay/overlay_next_background.hh + engines/overlay/overlay_next_bounds.hh engines/overlay/overlay_next_empty.hh engines/overlay/overlay_next_grid.hh engines/overlay/overlay_next_instance.hh diff --git a/source/blender/draw/engines/overlay/overlay_next_bounds.hh b/source/blender/draw/engines/overlay/overlay_next_bounds.hh new file mode 100644 index 00000000000..71de0f5c59e --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_next_bounds.hh @@ -0,0 +1,171 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup overlay + */ + +#pragma once + +#include "BKE_mball.hh" + +#include "BLI_bounds_types.hh" +#include "BLI_utildefines.h" + +#include "DNA_rigidbody_types.h" + +#include "overlay_next_private.hh" + +namespace blender::draw::overlay { +class Bounds { + using BoundsInstanceBuf = ShapeInstanceBuf; + + private: + PassSimple ps_ = {"Bounds"}; + + struct CallBuffers { + const SelectionType selection_type_; + + BoundsInstanceBuf box = {selection_type_, "bound_box"}; + BoundsInstanceBuf sphere = {selection_type_, "bound_sphere"}; + BoundsInstanceBuf cylinder = {selection_type_, "bound_cylinder"}; + BoundsInstanceBuf cone = {selection_type_, "bound_cone"}; + BoundsInstanceBuf capsule_body = {selection_type_, "bound_capsule_body"}; + BoundsInstanceBuf capsule_cap = {selection_type_, "bound_capsule_cap"}; + } call_buffers_; + + public: + Bounds(const SelectionType selection_type) : call_buffers_{selection_type} {} + + void begin_sync() + { + call_buffers_.box.clear(); + call_buffers_.sphere.clear(); + call_buffers_.cylinder.clear(); + call_buffers_.cone.clear(); + call_buffers_.capsule_body.clear(); + call_buffers_.capsule_cap.clear(); + } + + void object_sync(const ObjectRef &ob_ref, Resources &res, const State &state) + { + const Object *ob = ob_ref.object; + const bool from_dupli = (ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI)) != 0; + const bool has_bounds = !ELEM( + ob->type, OB_LAMP, OB_CAMERA, OB_EMPTY, OB_SPEAKER, OB_LIGHTPROBE); + const bool draw_bounds = has_bounds && ((ob->dt == OB_BOUNDBOX) || + ((ob->dtx & OB_DRAWBOUNDOX) && !from_dupli)); + const float4 color = res.object_wire_color(ob_ref, state); + + auto add_bounds = [&](const bool around_origin, const char bound_type) { + if (ob->type == OB_MBALL && !BKE_mball_is_basis(ob)) { + return; + } + const float4x4 object_mat{ob->object_to_world().ptr()}; + const blender::Bounds bounds = BKE_object_boundbox_get(ob).value_or( + blender::Bounds(float3(-1.0f), float3(1.0f))); + const float3 size = (bounds.max - bounds.min) * 0.5f; + const float3 center = around_origin ? float3(0) : math::midpoint(bounds.min, bounds.max); + const select::ID select_id = res.select_id(ob_ref); + + switch (bound_type) { + case OB_BOUND_BOX: { + float4x4 scale = math::from_scale(size); + scale.location() = center; + ExtraInstanceData data(object_mat * scale, color, 1.0f); + call_buffers_.box.append(data, select_id); + break; + } + case OB_BOUND_SPHERE: { + float4x4 scale = math::from_scale(float3{math::reduce_max(size)}); + scale.location() = center; + ExtraInstanceData data(object_mat * scale, color, 1.0f); + call_buffers_.sphere.append(data, select_id); + break; + } + case OB_BOUND_CYLINDER: { + float4x4 scale = math::from_scale( + float3{float2{math::max(size.x, size.y)}, size.z}); + scale.location() = center; + ExtraInstanceData data(object_mat * scale, color, 1.0f); + call_buffers_.cylinder.append(data, select_id); + break; + } + case OB_BOUND_CONE: { + float4x4 mat = math::from_scale( + float3{float2{math::max(size.x, size.y)}, size.z}); + 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; + ExtraInstanceData data(object_mat * mat, color, 1.0f); + call_buffers_.cone.append(data, select_id); + break; + } + case OB_BOUND_CAPSULE: { + float4x4 mat = math::from_scale(float3{math::max(size.x, size.y)}); + mat.location() = center; + mat.location().z = center.z + std::max(0.0f, size.z - size.x); + ExtraInstanceData data(object_mat * mat, color, 1.0f); + call_buffers_.capsule_cap.append(data, select_id); + mat.z_axis() *= -1; + mat.location().z = center.z - std::max(0.0f, size.z - size.x); + data.object_to_world_ = object_mat * mat; + call_buffers_.capsule_cap.append(data, select_id); + mat.z_axis().z = std::max(0.0f, size.z * 2.0f - size.x * 2.0f); + data.object_to_world_ = object_mat * mat; + call_buffers_.capsule_body.append(data, select_id); + break; + } + } + }; + + if (draw_bounds) { + add_bounds(false, ob->boundtype); + } + if (!from_dupli && ob->rigidbody_object != nullptr) { + switch (ob->rigidbody_object->shape) { + case RB_SHAPE_BOX: + add_bounds(true, OB_BOUND_BOX); + break; + case RB_SHAPE_SPHERE: + add_bounds(true, OB_BOUND_SPHERE); + break; + case RB_SHAPE_CONE: + add_bounds(true, OB_BOUND_CONE); + break; + case RB_SHAPE_CYLINDER: + add_bounds(true, OB_BOUND_CYLINDER); + break; + case RB_SHAPE_CAPSULE: + add_bounds(true, OB_BOUND_CAPSULE); + break; + }; + } + } + + void end_sync(Resources &res, ShapeCache &shapes, const State &state) + { + ps_.init(); + ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | + state.clipping_state); + ps_.shader_set(res.shaders.extra_shape.get()); + ps_.bind_ubo("globalsBlock", &res.globals_buf); + res.select_bind(ps_); + + call_buffers_.box.end_sync(ps_, shapes.cube.get()); + call_buffers_.sphere.end_sync(ps_, shapes.empty_sphere.get()); + call_buffers_.cylinder.end_sync(ps_, shapes.cylinder.get()); + call_buffers_.cone.end_sync(ps_, shapes.empty_cone.get()); + call_buffers_.capsule_body.end_sync(ps_, shapes.capsule_body.get()); + call_buffers_.capsule_cap.end_sync(ps_, shapes.capsule_cap.get()); + } + + void draw(Framebuffer &framebuffer, Manager &manager, View &view) + { + GPU_framebuffer_bind(framebuffer); + manager.submit(ps_, view); + } +}; +} // namespace blender::draw::overlay diff --git a/source/blender/draw/engines/overlay/overlay_next_instance.cc b/source/blender/draw/engines/overlay/overlay_next_instance.cc index 2302d829094..168ad8fbb49 100644 --- a/source/blender/draw/engines/overlay/overlay_next_instance.cc +++ b/source/blender/draw/engines/overlay/overlay_next_instance.cc @@ -81,7 +81,10 @@ void Instance::begin_sync() empties.begin_sync(); lattices.begin_sync(resources, state); - auto begin_sync_layer = [&](OverlayLayer &layer) { layer.lights.begin_sync(); }; + auto begin_sync_layer = [&](OverlayLayer &layer) { + layer.bounds.begin_sync(); + layer.lights.begin_sync(); + }; begin_sync_layer(regular); begin_sync_layer(infront); @@ -158,6 +161,7 @@ void Instance::object_sync(ObjectRef &ob_ref, Manager &manager) speakers.object_sync(ob_ref, resources, state); break; } + regular.bounds.object_sync(ob_ref, resources, state); } } @@ -166,6 +170,7 @@ void Instance::end_sync() resources.end_sync(); auto end_sync_layer = [&](OverlayLayer &layer) { + layer.bounds.end_sync(resources, shapes, state); layer.lights.end_sync(resources, shapes, state); }; end_sync_layer(regular); @@ -237,7 +242,7 @@ void Instance::draw(Manager &manager) prepass.draw_in_front(resources, manager, view); background.draw(resources, manager); - + regular.bounds.draw(resources.overlay_line_fb, manager, view); regular.lights.draw(resources.overlay_line_fb, manager, view); empties.draw(resources, manager, view); lattices.draw(resources, manager, view); diff --git a/source/blender/draw/engines/overlay/overlay_next_instance.hh b/source/blender/draw/engines/overlay/overlay_next_instance.hh index cd67511e07b..2a5efff48ec 100644 --- a/source/blender/draw/engines/overlay/overlay_next_instance.hh +++ b/source/blender/draw/engines/overlay/overlay_next_instance.hh @@ -11,6 +11,7 @@ #include "overlay_next_private.hh" #include "overlay_next_background.hh" +#include "overlay_next_bounds.hh" #include "overlay_next_empty.hh" #include "overlay_next_grid.hh" #include "overlay_next_lattice.hh" @@ -45,7 +46,7 @@ class Instance { struct OverlayLayer { const SelectionType selection_type_; - + Bounds bounds = {selection_type_}; Lights lights = {selection_type_}; } regular{selection_type_}, infront{selection_type_}; diff --git a/source/blender/draw/engines/overlay/overlay_next_private.hh b/source/blender/draw/engines/overlay/overlay_next_private.hh index d711e257b57..b84a5e8bbe9 100644 --- a/source/blender/draw/engines/overlay/overlay_next_private.hh +++ b/source/blender/draw/engines/overlay/overlay_next_private.hh @@ -79,6 +79,9 @@ class ShapeCache { BatchPtr circle; BatchPtr empty_sphere; BatchPtr empty_cone; + BatchPtr cylinder; + BatchPtr capsule_body; + BatchPtr capsule_cap; BatchPtr arrows; BatchPtr metaball_wire_circle; diff --git a/source/blender/draw/engines/overlay/overlay_next_shape.cc b/source/blender/draw/engines/overlay/overlay_next_shape.cc index 7c2be3281e5..1c1508fa473 100644 --- a/source/blender/draw/engines/overlay/overlay_next_shape.cc +++ b/source/blender/draw/engines/overlay/overlay_next_shape.cc @@ -17,7 +17,7 @@ struct Vertex { }; /* Caller gets ownership of the #gpu::VertBuf. */ -static gpu::VertBuf *vbo_from_vector(Vector &vector) +static gpu::VertBuf *vbo_from_vector(const Vector &vector) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -32,6 +32,8 @@ static gpu::VertBuf *vbo_from_vector(Vector &vector) } enum VertexClass { + VCLASS_NONE = 0, + VCLASS_LIGHT_AREA_SHAPE = 1 << 0, VCLASS_LIGHT_SPOT_SHAPE = 1 << 1, VCLASS_LIGHT_SPOT_BLEND = 1 << 2, @@ -76,9 +78,9 @@ static void append_line_loop( Vector &dest, Span verts, float z, int flag, bool dashed = false) { const int step = dashed ? 2 : 1; - for (int i : IndexRange(verts.size() / step)) { - for (int j : IndexRange(2)) { - float2 cv = verts[(i * step + j) % (verts.size())]; + for (const int i : IndexRange(verts.size() / step)) { + for (const int j : IndexRange(2)) { + const float2 &cv = verts[(i * step + j) % (verts.size())]; dest.append({{cv[0], cv[1], z}, flag}); } } @@ -267,6 +269,54 @@ ShapeCache::ShapeCache() empty_cone = BatchPtr( GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO)); } + /* cylinder */ + { + constexpr int n_segments = 12; + const Vector ring = ring_vertices(1.0f, n_segments); + Vector verts; + /* top ring */ + append_line_loop(verts, ring, 1.0f, VCLASS_EMPTY_SCALED); + /* bottom ring */ + append_line_loop(verts, ring, -1.0f, VCLASS_EMPTY_SCALED); + /* cylinder sides */ + for (const float2 &point : ring) { + verts.append({{point.x, point.y, 1.0f}, VCLASS_EMPTY_SCALED}); + verts.append({{point.x, point.y, -1.0f}, VCLASS_EMPTY_SCALED}); + } + cylinder = BatchPtr( + GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO)); + } + /* capsule body */ + { + const Vector diamond = ring_vertices(1.0f, 4); + Vector verts; + for (const float2 &point : diamond) { + verts.append({{point.x, point.y, 1.0f}, VCLASS_NONE}); + verts.append({{point.x, point.y, 0.0f}, VCLASS_NONE}); + } + capsule_body = BatchPtr( + GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO)); + } + /* capsule cap */ + { + constexpr int n_segments = 24; + const Vector ring = ring_vertices(1.0f, n_segments); + Vector verts; + /* Base circle */ + append_line_loop(verts, ring, 0.0f, VCLASS_NONE); + for (const int i : IndexRange(n_segments / 2)) { + const float2 &point = ring[i]; + const float2 &next_point = ring[i + 1]; + /* Y half circle */ + verts.append({{point.x, 0.0f, point.y}, VCLASS_NONE}); + verts.append({{next_point.x, 0.0f, next_point.y}, VCLASS_NONE}); + /* X half circle */ + verts.append({{0.0f, point.x, point.y}, VCLASS_NONE}); + verts.append({{0.0f, next_point.x, next_point.y}, VCLASS_NONE}); + } + capsule_cap = BatchPtr( + GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO)); + } /* arrows */ { float2 x_axis_name_scale = {0.0215f, 0.025f};