tornavis/source/blender/draw/intern/draw_debug.cc

715 lines
19 KiB
C++

/* SPDX-FileCopyrightText: 2018 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*
* \brief Simple API to draw debug shapes in the viewport.
*/
#include "BKE_object.hh"
#include "BLI_link_utils.h"
#include "BLI_math_matrix.hh"
#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>
#include <sstream>
#if defined(DEBUG) || defined(WITH_DRAW_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;
print_col_ = 0;
print_row_ = 0;
modelmat_reset();
}
void DebugDraw::modelmat_reset()
{
model_mat_ = float4x4::identity();
}
void DebugDraw::modelmat_set(const float modelmat[4][4])
{
model_mat_ = float4x4_view(modelmat);
}
GPUStorageBuf *DebugDraw::gpu_draw_buf_get()
{
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()
{
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> face_verts, float4 color)
{
BLI_assert(!face_verts.is_empty());
uint col = color_pack(color);
float3 v0 = math::transform_point(model_mat_, face_verts.last());
for (auto vert : face_verts) {
float3 v1 = math::transform_point(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 libraries (`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(math::transform_point(model_mat_, v1), color);
buf.verts[index + 1] = vert_pack(math::transform_point(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.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);
GPU_shader_uniform_mat4(shader, "persmat", persmat.ptr());
if (gpu_draw_buf_used) {
GPU_debug_group_begin("GPU");
GPU_storagebuf_bind(gpu_draw_buf_, DRW_DEBUG_DRAW_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_, DRW_DEBUG_DRAW_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);
float f_viewport[4];
GPU_viewport_size_get_f(f_viewport);
GPU_shader_uniform_2fv(shader, "viewport_size", &f_viewport[2]);
if (gpu_print_buf_used) {
GPU_debug_group_begin("GPU");
GPU_storagebuf_bind(gpu_print_buf_, DRW_DEBUG_PRINT_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_, DRW_DEBUG_PRINT_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
/* -------------------------------------------------------------------- */
/** \name DebugDraw Access
* \{ */
blender::draw::DebugDraw *DRW_debug_get()
{
return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name C-API private
* \{ */
void drw_debug_draw()
{
#ifdef DRAW_DEBUG
if (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
/* 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 (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()
{
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_reset();
}
void DRW_debug_modelmat(const float modelmat[4][4])
{
#ifdef DRAW_DEBUG
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_set(modelmat);
#else
UNUSED_VARS(modelmat);
#endif
}
void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4])
{
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])
{
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])
{
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix(float4x4(m));
}
void DRW_debug_m4_as_bbox(const float m[4][4], bool invert, const float color[4])
{
blender::float4x4 m4(m);
if (invert) {
m4 = blender::math::invert(m4);
}
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
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_bbox(*bbox, color);
#else
UNUSED_VARS(bbox, color);
#endif
}
void DRW_debug_sphere(const float center[3], float radius, const float color[4])
{
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_sphere(center, radius, color);
}
/** \} */