Files
test/source/blender/draw/intern/draw_debug.cc
Clément Foucault 65ad36f5fd DRWManager: New implementation.
This is a new implementation of the draw manager using modern
rendering practices and GPU driven culling.

This only ports features that are not considered deprecated or to be
removed.

The old DRW API is kept working along side this new one, and does not
interfeer with it. However this needed some more hacking inside the
draw_view_lib.glsl. At least the create info are well separated.

The reviewer might start by looking at `draw_pass_test.cc` to see the
API in usage.

Important files are `draw_pass.hh`, `draw_command.hh`,
`draw_command_shared.hh`.

In a nutshell (for a developper used to old DRW API):
- `DRWShadingGroups` are replaced by `Pass<T>::Sub`.
- Contrary to DRWShadingGroups, all commands recorded inside a pass or
   sub-pass (even binds / push_constant / uniforms) will be executed in order.
- All memory is managed per object (except for Sub-Pass which are managed
   by their parent pass) and not from draw manager pools. So passes "can"
   potentially be recorded once and submitted multiple time (but this is
   not really encouraged for now). The only implicit link is between resource
   lifetime and `ResourceHandles`
- Sub passes can be any level deep.
- IMPORTANT: All state propagate from sub pass to subpass. There is no
   state stack concept anymore. Ensure the correct render state is set before
   drawing anything using `Pass::state_set()`.
- The drawcalls now needs a `ResourceHandle` instead of an `Object *`.
   This is to remove any implicit dependency between `Pass` and `Manager`.
   This was a huge problem in old implementation since the manager did not
   know what to pull from the object. Now it is explicitly requested by the
   engine.
- The pases need to be submitted to a `draw::Manager` instance which can
   be retrieved using `DRW_manager_get()` (for now).

Internally:
- All object data are stored in contiguous storage buffers. Removing a lot
   of complexity in the pass submission.
- Draw calls are sorted and visibility tested on GPU. Making more modern
   culling and better instancing usage possible in the future.
- Unit Tests have been added for regression testing and avoid most API
   breakage.
- `draw::View` now contains culling data for all objects in the scene
   allowing caching for multiple views.
- Bounding box and sphere final setup is moved to GPU.
- Some global resources locations have been hardcoded to reduce complexity.

What is missing:
- ~~Workaround for lack of gl_BaseInstanceARB.~~ Done
- ~~Object Uniform Attributes.~~ Done (Not in this patch)
- Workaround for hardware supporting a maximum of 8 SSBO.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D15817
2022-09-02 18:45:14 +02:00

733 lines
20 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2018 Blender Foundation. */
/** \file
* \ingroup draw
*
* \brief Simple API to draw debug shapes in the viewport.
*/
#include "BKE_object.h"
#include "BLI_link_utils.h"
#include "GPU_batch.h"
#include "GPU_capabilities.h"
#include "GPU_debug.h"
#include "draw_debug.h"
#include "draw_debug.hh"
#include "draw_manager.h"
#include "draw_shader.h"
#include "draw_shader_shared.h"
#include <iomanip>
#ifdef DEBUG
# define DRAW_DEBUG
#else
/* Uncomment to forcibly enable debug draw in release mode. */
//#define DRAW_DEBUG
#endif
namespace blender::draw {
/* -------------------------------------------------------------------- */
/** \name Init and state
* \{ */
DebugDraw::DebugDraw()
{
constexpr int circle_resolution = 16;
for (auto axis : IndexRange(3)) {
for (auto edge : IndexRange(circle_resolution)) {
for (auto vert : IndexRange(2)) {
const float angle = (2 * M_PI) * (edge + vert) / float(circle_resolution);
float point[3] = {cosf(angle), sinf(angle), 0.0f};
sphere_verts_.append(
float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3]));
}
}
}
constexpr int point_resolution = 4;
for (auto axis : IndexRange(3)) {
for (auto edge : IndexRange(point_resolution)) {
for (auto vert : IndexRange(2)) {
const float angle = (2 * M_PI) * (edge + vert) / float(point_resolution);
float point[3] = {cosf(angle), sinf(angle), 0.0f};
point_verts_.append(
float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3]));
}
}
}
};
void DebugDraw::init()
{
cpu_print_buf_.command.vertex_len = 0;
cpu_print_buf_.command.vertex_first = 0;
cpu_print_buf_.command.instance_len = 1;
cpu_print_buf_.command.instance_first_array = 0;
cpu_draw_buf_.command.vertex_len = 0;
cpu_draw_buf_.command.vertex_first = 0;
cpu_draw_buf_.command.instance_len = 1;
cpu_draw_buf_.command.instance_first_array = 0;
gpu_print_buf_.command.vertex_len = 0;
gpu_print_buf_.command.vertex_first = 0;
gpu_print_buf_.command.instance_len = 1;
gpu_print_buf_.command.instance_first_array = 0;
gpu_print_buf_used = false;
gpu_draw_buf_.command.vertex_len = 0;
gpu_draw_buf_.command.vertex_first = 0;
gpu_draw_buf_.command.instance_len = 1;
gpu_draw_buf_.command.instance_first_array = 0;
gpu_draw_buf_used = false;
modelmat_reset();
}
void DebugDraw::modelmat_reset()
{
model_mat_ = float4x4::identity();
}
void DebugDraw::modelmat_set(const float modelmat[4][4])
{
model_mat_ = modelmat;
}
GPUStorageBuf *DebugDraw::gpu_draw_buf_get()
{
BLI_assert(GPU_shader_storage_buffer_objects_support());
if (!gpu_draw_buf_used) {
gpu_draw_buf_used = true;
gpu_draw_buf_.push_update();
}
return gpu_draw_buf_;
}
GPUStorageBuf *DebugDraw::gpu_print_buf_get()
{
BLI_assert(GPU_shader_storage_buffer_objects_support());
if (!gpu_print_buf_used) {
gpu_print_buf_used = true;
gpu_print_buf_.push_update();
}
return gpu_print_buf_;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Draw functions
* \{ */
void DebugDraw::draw_line(float3 v1, float3 v2, float4 color)
{
draw_line(v1, v2, color_pack(color));
}
void DebugDraw::draw_polygon(Span<float3> poly_verts, float4 color)
{
BLI_assert(!poly_verts.is_empty());
uint col = color_pack(color);
float3 v0 = model_mat_ * poly_verts.last();
for (auto vert : poly_verts) {
float3 v1 = model_mat_ * vert;
draw_line(v0, v1, col);
v0 = v1;
}
}
void DebugDraw::draw_matrix(const float4x4 m4)
{
float3 v0 = float3(0.0f, 0.0f, 0.0f);
float3 v1 = float3(1.0f, 0.0f, 0.0f);
float3 v2 = float3(0.0f, 1.0f, 0.0f);
float3 v3 = float3(0.0f, 0.0f, 1.0f);
mul_project_m4_v3(m4.ptr(), v0);
mul_project_m4_v3(m4.ptr(), v1);
mul_project_m4_v3(m4.ptr(), v2);
mul_project_m4_v3(m4.ptr(), v3);
draw_line(v0, v1, float4(1.0f, 0.0f, 0.0f, 1.0f));
draw_line(v0, v2, float4(0.0f, 1.0f, 0.0f, 1.0f));
draw_line(v0, v3, float4(0.0f, 0.0f, 1.0f, 1.0f));
}
void DebugDraw::draw_bbox(const BoundBox &bbox, const float4 color)
{
uint col = color_pack(color);
draw_line(bbox.vec[0], bbox.vec[1], col);
draw_line(bbox.vec[1], bbox.vec[2], col);
draw_line(bbox.vec[2], bbox.vec[3], col);
draw_line(bbox.vec[3], bbox.vec[0], col);
draw_line(bbox.vec[4], bbox.vec[5], col);
draw_line(bbox.vec[5], bbox.vec[6], col);
draw_line(bbox.vec[6], bbox.vec[7], col);
draw_line(bbox.vec[7], bbox.vec[4], col);
draw_line(bbox.vec[0], bbox.vec[4], col);
draw_line(bbox.vec[1], bbox.vec[5], col);
draw_line(bbox.vec[2], bbox.vec[6], col);
draw_line(bbox.vec[3], bbox.vec[7], col);
}
void DebugDraw::draw_matrix_as_bbox(float4x4 mat, const float4 color)
{
BoundBox bb;
const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f};
BKE_boundbox_init_from_minmax(&bb, min, max);
for (auto i : IndexRange(8)) {
mul_project_m4_v3(mat.ptr(), bb.vec[i]);
}
draw_bbox(bb, color);
}
void DebugDraw::draw_sphere(const float3 center, float radius, const float4 color)
{
uint col = color_pack(color);
for (auto i : IndexRange(sphere_verts_.size() / 2)) {
float3 v0 = sphere_verts_[i * 2] * radius + center;
float3 v1 = sphere_verts_[i * 2 + 1] * radius + center;
draw_line(v0, v1, col);
}
}
void DebugDraw::draw_point(const float3 center, float radius, const float4 color)
{
uint col = color_pack(color);
for (auto i : IndexRange(point_verts_.size() / 2)) {
float3 v0 = point_verts_[i * 2] * radius + center;
float3 v1 = point_verts_[i * 2 + 1] * radius + center;
draw_line(v0, v1, col);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Print functions
* \{ */
template<> void DebugDraw::print_value<uint>(const uint &value)
{
print_value_uint(value, false, false, true);
}
template<> void DebugDraw::print_value<int>(const int &value)
{
print_value_uint(uint(abs(value)), false, (value < 0), false);
}
template<> void DebugDraw::print_value<bool>(const bool &value)
{
print_string(value ? "true " : "false");
}
template<> void DebugDraw::print_value<float>(const float &val)
{
std::stringstream ss;
ss << std::setw(12) << std::to_string(val);
print_string(ss.str());
}
template<> void DebugDraw::print_value<double>(const double &val)
{
print_value(float(val));
}
template<> void DebugDraw::print_value_hex<uint>(const uint &value)
{
print_value_uint(value, true, false, false);
}
template<> void DebugDraw::print_value_hex<int>(const int &value)
{
print_value_uint(uint(value), true, false, false);
}
template<> void DebugDraw::print_value_hex<float>(const float &value)
{
print_value_uint(*reinterpret_cast<const uint *>(&value), true, false, false);
}
template<> void DebugDraw::print_value_hex<double>(const double &val)
{
print_value_hex(float(val));
}
template<> void DebugDraw::print_value_binary<uint>(const uint &value)
{
print_value_binary(value);
}
template<> void DebugDraw::print_value_binary<int>(const int &value)
{
print_value_binary(uint(value));
}
template<> void DebugDraw::print_value_binary<float>(const float &value)
{
print_value_binary(*reinterpret_cast<const uint *>(&value));
}
template<> void DebugDraw::print_value_binary<double>(const double &val)
{
print_value_binary(float(val));
}
template<> void DebugDraw::print_value<float2>(const float2 &value)
{
print_no_endl("float2(", value[0], ", ", value[1], ")");
}
template<> void DebugDraw::print_value<float3>(const float3 &value)
{
print_no_endl("float3(", value[0], ", ", value[1], ", ", value[1], ")");
}
template<> void DebugDraw::print_value<float4>(const float4 &value)
{
print_no_endl("float4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
}
template<> void DebugDraw::print_value<int2>(const int2 &value)
{
print_no_endl("int2(", value[0], ", ", value[1], ")");
}
template<> void DebugDraw::print_value<int3>(const int3 &value)
{
print_no_endl("int3(", value[0], ", ", value[1], ", ", value[1], ")");
}
template<> void DebugDraw::print_value<int4>(const int4 &value)
{
print_no_endl("int4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
}
template<> void DebugDraw::print_value<uint2>(const uint2 &value)
{
print_no_endl("uint2(", value[0], ", ", value[1], ")");
}
template<> void DebugDraw::print_value<uint3>(const uint3 &value)
{
print_no_endl("uint3(", value[0], ", ", value[1], ", ", value[1], ")");
}
template<> void DebugDraw::print_value<uint4>(const uint4 &value)
{
print_no_endl("uint4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Internals
*
* IMPORTANT: All of these are copied from the shader libs (common_debug_draw_lib.glsl &
* common_debug_print_lib.glsl). They need to be kept in sync to write the same data.
* \{ */
void DebugDraw::draw_line(float3 v1, float3 v2, uint color)
{
DebugDrawBuf &buf = cpu_draw_buf_;
uint index = buf.command.vertex_len;
if (index + 2 < DRW_DEBUG_DRAW_VERT_MAX) {
buf.verts[index + 0] = vert_pack(model_mat_ * v1, color);
buf.verts[index + 1] = vert_pack(model_mat_ * v2, color);
buf.command.vertex_len += 2;
}
}
/* Keep in sync with drw_debug_color_pack(). */
uint DebugDraw::color_pack(float4 color)
{
color = math::clamp(color, 0.0f, 1.0f);
uint result = 0;
result |= uint(color.x * 255.0f) << 0u;
result |= uint(color.y * 255.0f) << 8u;
result |= uint(color.z * 255.0f) << 16u;
result |= uint(color.w * 255.0f) << 24u;
return result;
}
DRWDebugVert DebugDraw::vert_pack(float3 pos, uint color)
{
DRWDebugVert vert;
vert.pos0 = *reinterpret_cast<uint32_t *>(&pos.x);
vert.pos1 = *reinterpret_cast<uint32_t *>(&pos.y);
vert.pos2 = *reinterpret_cast<uint32_t *>(&pos.z);
vert.color = color;
return vert;
}
void DebugDraw::print_newline()
{
print_col_ = 0u;
print_row_ = ++cpu_print_buf_.command.instance_first_array;
}
void DebugDraw::print_string_start(uint len)
{
/* Break before word. */
if (print_col_ + len > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) {
print_newline();
}
}
/* Copied from gpu_shader_dependency. */
void DebugDraw::print_string(std::string str)
{
size_t len_before_pad = str.length();
/* Pad string to uint size to avoid out of bound reads. */
while (str.length() % 4 != 0) {
str += " ";
}
print_string_start(len_before_pad);
for (size_t i = 0; i < len_before_pad; i += 4) {
union {
uint8_t chars[4];
uint32_t word;
};
chars[0] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 0);
chars[1] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 1);
chars[2] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 2);
chars[3] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 3);
if (i + 4 > len_before_pad) {
chars[len_before_pad - i] = '\0';
}
print_char4(word);
}
}
/* Keep in sync with shader. */
void DebugDraw::print_char4(uint data)
{
/* Convert into char stream. */
for (; data != 0u; data >>= 8u) {
uint char1 = data & 0xFFu;
/* Check for null terminator. */
if (char1 == 0x00) {
break;
}
/* NOTE: Do not skip the header manually like in GPU. */
uint cursor = cpu_print_buf_.command.vertex_len++;
if (cursor < DRW_DEBUG_PRINT_MAX) {
/* For future usage. (i.e: Color) */
uint flags = 0u;
uint col = print_col_++;
uint print_header = (flags << 24u) | (print_row_ << 16u) | (col << 8u);
cpu_print_buf_.char_array[cursor] = print_header | char1;
/* Break word. */
if (print_col_ > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) {
print_newline();
}
}
}
}
void DebugDraw::print_append_char(uint char1, uint &char4)
{
char4 = (char4 << 8u) | char1;
}
void DebugDraw::print_append_digit(uint digit, uint &char4)
{
const uint char_A = 0x41u;
const uint char_0 = 0x30u;
bool is_hexadecimal = digit > 9u;
char4 = (char4 << 8u) | (is_hexadecimal ? (char_A + digit - 10u) : (char_0 + digit));
}
void DebugDraw::print_append_space(uint &char4)
{
char4 = (char4 << 8u) | 0x20u;
}
void DebugDraw::print_value_binary(uint value)
{
print_string("0b");
print_string_start(10u * 4u);
uint digits[10] = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u};
uint digit = 0u;
for (uint i = 0u; i < 32u; i++) {
print_append_digit(((value >> i) & 1u), digits[digit / 4u]);
digit++;
if ((i % 4u) == 3u) {
print_append_space(digits[digit / 4u]);
digit++;
}
}
/* Numbers are written from right to left. So we need to reverse the order. */
for (int j = 9; j >= 0; j--) {
print_char4(digits[j]);
}
}
void DebugDraw::print_value_uint(uint value,
const bool hex,
bool is_negative,
const bool is_unsigned)
{
print_string_start(3u * 4u);
const uint blank_value = hex ? 0x30303030u : 0x20202020u;
const uint prefix = hex ? 0x78302020u : 0x20202020u;
uint digits[3] = {blank_value, blank_value, prefix};
const uint base = hex ? 16u : 10u;
uint digit = 0u;
/* Add `u` suffix. */
if (is_unsigned) {
print_append_char('u', digits[digit / 4u]);
digit++;
}
/* Number's digits. */
for (; value != 0u || digit == uint(is_unsigned); value /= base) {
print_append_digit(value % base, digits[digit / 4u]);
digit++;
}
/* Add negative sign. */
if (is_negative) {
print_append_char('-', digits[digit / 4u]);
digit++;
}
/* Need to pad to uint alignment because we are issuing chars in "reverse". */
for (uint i = digit % 4u; i < 4u && i > 0u; i++) {
print_append_space(digits[digit / 4u]);
digit++;
}
/* Numbers are written from right to left. So we need to reverse the order. */
for (int j = 2; j >= 0; j--) {
print_char4(digits[j]);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Display
* \{ */
void DebugDraw::display_lines()
{
if (cpu_draw_buf_.command.vertex_len == 0 && gpu_draw_buf_used == false) {
return;
}
GPU_debug_group_begin("Lines");
cpu_draw_buf_.push_update();
float4x4 persmat;
const DRWView *view = DRW_view_get_active();
DRW_view_persmat_get(view, persmat.ptr(), false);
drw_state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
GPUBatch *batch = drw_cache_procedural_lines_get();
GPUShader *shader = DRW_shader_debug_draw_display_get();
GPU_batch_set_shader(batch, shader);
int slot = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_VERTS);
GPU_shader_uniform_mat4(shader, "persmat", persmat.ptr());
if (gpu_draw_buf_used) {
GPU_debug_group_begin("GPU");
GPU_storagebuf_bind(gpu_draw_buf_, slot);
GPU_batch_draw_indirect(batch, gpu_draw_buf_, 0);
GPU_storagebuf_unbind(gpu_draw_buf_);
GPU_debug_group_end();
}
GPU_debug_group_begin("CPU");
GPU_storagebuf_bind(cpu_draw_buf_, slot);
GPU_batch_draw_indirect(batch, cpu_draw_buf_, 0);
GPU_storagebuf_unbind(cpu_draw_buf_);
GPU_debug_group_end();
GPU_debug_group_end();
}
void DebugDraw::display_prints()
{
if (cpu_print_buf_.command.vertex_len == 0 && gpu_print_buf_used == false) {
return;
}
GPU_debug_group_begin("Prints");
cpu_print_buf_.push_update();
drw_state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_PROGRAM_POINT_SIZE);
GPUBatch *batch = drw_cache_procedural_points_get();
GPUShader *shader = DRW_shader_debug_print_display_get();
GPU_batch_set_shader(batch, shader);
int slot = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT);
if (gpu_print_buf_used) {
GPU_debug_group_begin("GPU");
GPU_storagebuf_bind(gpu_print_buf_, slot);
GPU_batch_draw_indirect(batch, gpu_print_buf_, 0);
GPU_storagebuf_unbind(gpu_print_buf_);
GPU_debug_group_end();
}
GPU_debug_group_begin("CPU");
GPU_storagebuf_bind(cpu_print_buf_, slot);
GPU_batch_draw_indirect(batch, cpu_print_buf_, 0);
GPU_storagebuf_unbind(cpu_print_buf_);
GPU_debug_group_end();
GPU_debug_group_end();
}
void DebugDraw::display_to_view()
{
GPU_debug_group_begin("DebugDraw");
display_lines();
/* Print 3D shapes before text to avoid overlaps. */
display_prints();
/* Init again so we don't draw the same thing twice. */
init();
GPU_debug_group_end();
}
} // namespace blender::draw
blender::draw::DebugDraw *DRW_debug_get()
{
if (!GPU_shader_storage_buffer_objects_support()) {
return nullptr;
}
return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name C-API private
* \{ */
void drw_debug_draw()
{
#ifdef DRAW_DEBUG
if (!GPU_shader_storage_buffer_objects_support() || DST.debug == nullptr) {
return;
}
/* TODO(@fclem): Convenience for now. Will have to move to #DRWManager. */
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->display_to_view();
#endif
}
/**
* NOTE: Init is once per draw manager cycle.
*/
void drw_debug_init()
{
/* Module should not be used in release builds. */
/* TODO(@fclem): Hide the functions declarations without using `ifdefs` everywhere. */
#ifdef DRAW_DEBUG
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
/* TODO(@fclem): Convenience for now. Will have to move to #DRWManager. */
if (DST.debug == nullptr) {
DST.debug = reinterpret_cast<DRWDebugModule *>(new blender::draw::DebugDraw());
}
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->init();
#endif
}
void drw_debug_module_free(DRWDebugModule *module)
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
if (module != nullptr) {
delete reinterpret_cast<blender::draw::DebugDraw *>(module);
}
}
GPUStorageBuf *drw_debug_gpu_draw_buf_get()
{
return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->gpu_draw_buf_get();
}
GPUStorageBuf *drw_debug_gpu_print_buf_get()
{
return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->gpu_print_buf_get();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name C-API public
* \{ */
void DRW_debug_modelmat_reset()
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_reset();
}
void DRW_debug_modelmat(const float modelmat[4][4])
{
#ifdef DRAW_DEBUG
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_set(modelmat);
#endif
}
void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4])
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_line(v1, v2, color);
}
void DRW_debug_polygon_v3(const float (*v)[3], int vert_len, const float color[4])
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_polygon(
blender::Span<float3>((float3 *)v, vert_len), color);
}
void DRW_debug_m4(const float m[4][4])
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix(m);
}
void DRW_debug_m4_as_bbox(const float m[4][4], bool invert, const float color[4])
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
blender::float4x4 m4 = m;
if (invert) {
m4 = m4.inverted();
}
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix_as_bbox(m4, color);
}
void DRW_debug_bbox(const BoundBox *bbox, const float color[4])
{
#ifdef DRAW_DEBUG
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_bbox(*bbox, color);
#endif
}
void DRW_debug_sphere(const float center[3], float radius, const float color[4])
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_sphere(center, radius, color);
}
/** \} */