ADD: debug draw

This commit is contained in:
2025-12-24 23:26:06 +09:00
parent f02086bc32
commit c5e656d585
12 changed files with 1413 additions and 0 deletions

10
shaders/debug_lines.frag Normal file
View File

@@ -0,0 +1,10 @@
#version 450
layout(location = 0) in vec4 inColor;
layout(location = 0) out vec4 outColor;
void main()
{
outColor = inColor;
}

31
shaders/debug_lines.vert Normal file
View File

@@ -0,0 +1,31 @@
#version 450
#extension GL_EXT_buffer_reference : require
layout(location = 0) out vec4 outColor;
struct DebugVertex
{
vec3 position;
float _pad0;
vec4 color;
};
layout(buffer_reference, std430) readonly buffer DebugVertexBuffer
{
DebugVertex vertices[];
};
layout(push_constant) uniform DebugPush
{
mat4 viewproj;
DebugVertexBuffer vertexBuffer;
} pc;
void main()
{
DebugVertex v = pc.vertexBuffer.vertices[gl_VertexIndex];
gl_Position = pc.viewproj * vec4(v.position, 1.0);
outColor = v.color;
}

View File

@@ -17,6 +17,8 @@ add_executable (vulkan_engine
core/ui/imgui_system.cpp
core/picking/picking_system.h
core/picking/picking_system.cpp
core/debug_draw/debug_draw.h
core/debug_draw/debug_draw.cpp
core/game_api.h
core/game_api.cpp
# core/device
@@ -93,6 +95,8 @@ add_executable (vulkan_engine
render/passes/imgui_pass.cpp
render/passes/tonemap.h
render/passes/tonemap.cpp
render/passes/debug_draw.h
render/passes/debug_draw.cpp
# render graph
render/graph/types.h
render/graph/graph.h

View File

@@ -34,6 +34,7 @@ class RayTracingManager;
class TextureCache;
class IBLManager;
class InputSystem;
class DebugDrawSystem;
struct ShadowSettings
{
@@ -142,6 +143,7 @@ public:
RenderGraph* renderGraph = nullptr; // render graph (built per-frame)
SDL_Window* window = nullptr; // SDL window handle
InputSystem* input = nullptr; // input system (engine-owned)
DebugDrawSystem* debug_draw = nullptr; // debug 3D draw collector (engine-owned)
// Frequently used values
VkExtent2D drawExtent{};

View File

@@ -0,0 +1,617 @@
#include "debug_draw.h"
#include <algorithm>
#include <cmath>
namespace
{
static float clamp_nonnegative_finite(float v, float fallback = 0.0f)
{
if (!std::isfinite(v)) return fallback;
return std::max(0.0f, v);
}
static float ttl_from_seconds(float seconds)
{
if (!std::isfinite(seconds) || seconds <= 0.0f)
{
return -1.0f; // one-frame
}
return seconds;
}
static int clamp_segments(int segments)
{
if (segments < 3) return 3;
if (segments > 256) return 256;
return segments;
}
static glm::vec3 safe_normalize(const glm::vec3 &v, const glm::vec3 &fallback)
{
const float len2 = glm::dot(v, v);
if (!std::isfinite(len2) || len2 <= 1.0e-12f) return fallback;
return v * (1.0f / std::sqrt(len2));
}
static void basis_from_normal(const glm::vec3 &n, glm::vec3 &out_u, glm::vec3 &out_v)
{
const glm::vec3 nn = safe_normalize(n, glm::vec3(0.0f, 1.0f, 0.0f));
const glm::vec3 a = (std::abs(nn.y) < 0.999f) ? glm::vec3(0.0f, 1.0f, 0.0f) : glm::vec3(1.0f, 0.0f, 0.0f);
out_u = safe_normalize(glm::cross(nn, a), glm::vec3(1.0f, 0.0f, 0.0f));
out_v = safe_normalize(glm::cross(nn, out_u), glm::vec3(0.0f, 0.0f, 1.0f));
}
static void push_line(std::vector<DebugDrawVertex> &dst,
const glm::vec3 &a,
const glm::vec3 &b,
const glm::vec4 &color)
{
DebugDrawVertex v0{};
v0.position = a;
v0.color = color;
DebugDrawVertex v1{};
v1.position = b;
v1.color = color;
dst.push_back(v0);
dst.push_back(v1);
}
static bool layer_enabled(uint32_t layer_mask, DebugDrawLayer layer)
{
return (layer_mask & static_cast<uint32_t>(layer)) != 0u;
}
template <typename CmdT>
static std::vector<DebugDrawVertex> *select_bucket(const CmdT &cmd,
const DebugDrawSystem::Settings &settings,
std::vector<DebugDrawVertex> &depth_vertices,
std::vector<DebugDrawVertex> &overlay_vertices)
{
if (!layer_enabled(settings.layer_mask, cmd.layer))
{
return nullptr;
}
if (cmd.depth == DebugDepth::DepthTested)
{
if (!settings.show_depth_tested) return nullptr;
return &depth_vertices;
}
if (!settings.show_overlay) return nullptr;
return &overlay_vertices;
}
static void emit_aabb(std::vector<DebugDrawVertex> &dst,
const glm::vec3 &center_local,
const glm::vec3 &half_extents,
const glm::vec4 &color)
{
glm::vec3 e = half_extents;
e.x = std::max(0.0f, e.x);
e.y = std::max(0.0f, e.y);
e.z = std::max(0.0f, e.z);
const glm::vec3 c = center_local;
const glm::vec3 corners[8] = {
c + glm::vec3(-e.x, -e.y, -e.z),
c + glm::vec3(+e.x, -e.y, -e.z),
c + glm::vec3(-e.x, +e.y, -e.z),
c + glm::vec3(+e.x, +e.y, -e.z),
c + glm::vec3(-e.x, -e.y, +e.z),
c + glm::vec3(+e.x, -e.y, +e.z),
c + glm::vec3(-e.x, +e.y, +e.z),
c + glm::vec3(+e.x, +e.y, +e.z),
};
constexpr uint8_t edges[12][2] = {
{0, 1}, {1, 3}, {3, 2}, {2, 0},
{4, 5}, {5, 7}, {7, 6}, {6, 4},
{0, 4}, {1, 5}, {2, 6}, {3, 7},
};
for (auto &eidx : edges)
{
push_line(dst, corners[eidx[0]], corners[eidx[1]], color);
}
}
static void emit_obb(std::vector<DebugDrawVertex> &dst,
const std::array<glm::vec3, 8> &corners_local,
const glm::vec4 &color)
{
constexpr uint8_t edges[12][2] = {
{0, 1}, {1, 3}, {3, 2}, {2, 0},
{4, 5}, {5, 7}, {7, 6}, {6, 4},
{0, 4}, {1, 5}, {2, 6}, {3, 7},
};
for (auto &eidx : edges)
{
push_line(dst, corners_local[eidx[0]], corners_local[eidx[1]], color);
}
}
static void emit_circle(std::vector<DebugDrawVertex> &dst,
const glm::vec3 &center_local,
const glm::vec3 &normal,
float radius,
int segments,
const glm::vec4 &color)
{
radius = clamp_nonnegative_finite(radius, 0.0f);
if (radius <= 0.0f) return;
glm::vec3 u{}, v{};
basis_from_normal(normal, u, v);
const int seg = clamp_segments(segments);
const float two_pi = 6.2831853071795864769f;
glm::vec3 prev{};
for (int i = 0; i <= seg; ++i)
{
const float t = (static_cast<float>(i) / static_cast<float>(seg)) * two_pi;
const glm::vec3 p = center_local + (u * std::cos(t) + v * std::sin(t)) * radius;
if (i > 0)
{
push_line(dst, prev, p, color);
}
prev = p;
}
}
static void emit_sphere(std::vector<DebugDrawVertex> &dst,
const glm::vec3 &center_local,
float radius,
int segments,
const glm::vec4 &color)
{
radius = clamp_nonnegative_finite(radius, 0.0f);
if (radius <= 0.0f) return;
// 3 great circles
emit_circle(dst, center_local, glm::vec3(0.0f, 0.0f, 1.0f), radius, segments, color); // XY
emit_circle(dst, center_local, glm::vec3(0.0f, 1.0f, 0.0f), radius, segments, color); // XZ
emit_circle(dst, center_local, glm::vec3(1.0f, 0.0f, 0.0f), radius, segments, color); // YZ
}
static void emit_cone(std::vector<DebugDrawVertex> &dst,
const glm::vec3 &apex_local,
const glm::vec3 &direction_local,
float length,
float angle_degrees,
int segments,
const glm::vec4 &color)
{
length = clamp_nonnegative_finite(length, 0.0f);
if (length <= 0.0f) return;
float angle = angle_degrees;
if (!std::isfinite(angle)) angle = 0.0f;
angle = std::clamp(angle, 0.0f, 89.9f);
const float radius = length * std::tan(glm::radians(angle));
const glm::vec3 dir = safe_normalize(direction_local, glm::vec3(0.0f, -1.0f, 0.0f));
const glm::vec3 base_center = apex_local + dir * length;
// Axis
push_line(dst, apex_local, base_center, color);
// Base circle + spokes
glm::vec3 u{}, v{};
basis_from_normal(dir, u, v);
const int seg = clamp_segments(segments);
const float two_pi = 6.2831853071795864769f;
glm::vec3 first{};
glm::vec3 prev{};
for (int i = 0; i < seg; ++i)
{
const float t = (static_cast<float>(i) / static_cast<float>(seg)) * two_pi;
const glm::vec3 p = base_center + (u * std::cos(t) + v * std::sin(t)) * radius;
if (i == 0) first = p;
if (i > 0)
{
push_line(dst, prev, p, color);
}
push_line(dst, apex_local, p, color);
prev = p;
}
push_line(dst, prev, first, color);
}
static void emit_capsule(std::vector<DebugDrawVertex> &dst,
const glm::vec3 &p0_local,
const glm::vec3 &p1_local,
float radius,
int segments,
const glm::vec4 &color)
{
radius = clamp_nonnegative_finite(radius, 0.0f);
if (radius <= 0.0f) return;
const glm::vec3 axis = p1_local - p0_local;
const float axis_len2 = glm::dot(axis, axis);
if (!std::isfinite(axis_len2) || axis_len2 <= 1.0e-10f)
{
emit_sphere(dst, p0_local, radius, segments, color);
return;
}
const float axis_len = std::sqrt(axis_len2);
const glm::vec3 u = axis * (1.0f / axis_len);
// Basis around the capsule axis
const glm::vec3 a = (std::abs(u.y) < 0.999f) ? glm::vec3(0.0f, 1.0f, 0.0f) : glm::vec3(1.0f, 0.0f, 0.0f);
const glm::vec3 v = safe_normalize(glm::cross(u, a), glm::vec3(1.0f, 0.0f, 0.0f));
const glm::vec3 w = safe_normalize(glm::cross(u, v), glm::vec3(0.0f, 0.0f, 1.0f));
const int seg = clamp_segments(segments);
const float two_pi = 6.2831853071795864769f;
// Rings + side lines
glm::vec3 prev0{}, prev1{}, first0{}, first1{};
for (int i = 0; i < seg; ++i)
{
const float t = (static_cast<float>(i) / static_cast<float>(seg)) * two_pi;
const glm::vec3 offset = (v * std::cos(t) + w * std::sin(t)) * radius;
const glm::vec3 a0 = p0_local + offset;
const glm::vec3 a1 = p1_local + offset;
if (i == 0)
{
first0 = a0;
first1 = a1;
}
else
{
push_line(dst, prev0, a0, color);
push_line(dst, prev1, a1, color);
}
push_line(dst, a0, a1, color);
prev0 = a0;
prev1 = a1;
}
push_line(dst, prev0, first0, color);
push_line(dst, prev1, first1, color);
// Endcap arcs (2 meridians per end)
const int half_seg = std::max(3, seg / 2);
for (int i = 0; i < half_seg; ++i)
{
const float t0 = (static_cast<float>(i) / static_cast<float>(half_seg)) * 3.14159265358979323846f;
const float t1 = (static_cast<float>(i + 1) / static_cast<float>(half_seg)) * 3.14159265358979323846f;
// p0 hemisphere faces -u
const glm::vec3 p0_v0 = p0_local + (v * std::cos(t0) - u * std::sin(t0)) * radius;
const glm::vec3 p0_v1 = p0_local + (v * std::cos(t1) - u * std::sin(t1)) * radius;
const glm::vec3 p0_w0 = p0_local + (w * std::cos(t0) - u * std::sin(t0)) * radius;
const glm::vec3 p0_w1 = p0_local + (w * std::cos(t1) - u * std::sin(t1)) * radius;
push_line(dst, p0_v0, p0_v1, color);
push_line(dst, p0_w0, p0_w1, color);
// p1 hemisphere faces +u
const glm::vec3 p1_v0 = p1_local + (v * std::cos(t0) + u * std::sin(t0)) * radius;
const glm::vec3 p1_v1 = p1_local + (v * std::cos(t1) + u * std::sin(t1)) * radius;
const glm::vec3 p1_w0 = p1_local + (w * std::cos(t0) + u * std::sin(t0)) * radius;
const glm::vec3 p1_w1 = p1_local + (w * std::cos(t1) + u * std::sin(t1)) * radius;
push_line(dst, p1_v0, p1_v1, color);
push_line(dst, p1_w0, p1_w1, color);
}
}
}
size_t DebugDrawSystem::command_count() const
{
return _lines.size() + _aabbs.size() + _spheres.size() + _capsules.size() + _circles.size() + _cones.size() + _obbs.size();
}
void DebugDrawSystem::clear()
{
_lines.clear();
_aabbs.clear();
_spheres.clear();
_capsules.clear();
_circles.clear();
_cones.clear();
_obbs.clear();
}
template <typename T>
void DebugDrawSystem::prune_list(std::vector<T> &cmds, float dt_seconds)
{
if (cmds.empty())
{
return;
}
if (!std::isfinite(dt_seconds) || dt_seconds < 0.0f)
{
dt_seconds = 0.0f;
}
size_t dst = 0;
for (size_t i = 0; i < cmds.size(); ++i)
{
T cmd = cmds[i];
// one-frame commands are removed on begin_frame()
if (cmd.ttl_seconds < 0.0f)
{
continue;
}
if (dt_seconds > 0.0f)
{
cmd.ttl_seconds -= dt_seconds;
}
if (cmd.ttl_seconds <= 0.0f)
{
continue;
}
cmds[dst++] = cmd;
}
cmds.resize(dst);
}
void DebugDrawSystem::begin_frame(float dt_seconds)
{
prune_list(_lines, dt_seconds);
prune_list(_aabbs, dt_seconds);
prune_list(_spheres, dt_seconds);
prune_list(_capsules, dt_seconds);
prune_list(_circles, dt_seconds);
prune_list(_cones, dt_seconds);
prune_list(_obbs, dt_seconds);
}
void DebugDrawSystem::add_line(const WorldVec3 &a_world,
const WorldVec3 &b_world,
const glm::vec4 &color,
float seconds,
DebugDepth depth,
DebugDrawLayer layer)
{
CmdLine cmd{};
cmd.a_world = a_world;
cmd.b_world = b_world;
cmd.color = color;
cmd.depth = depth;
cmd.layer = layer;
cmd.ttl_seconds = ttl_from_seconds(seconds);
_lines.push_back(cmd);
}
void DebugDrawSystem::add_ray(const WorldVec3 &origin_world,
const glm::dvec3 &dir_world,
double length,
const glm::vec4 &color,
float seconds,
DebugDepth depth,
DebugDrawLayer layer)
{
if (!std::isfinite(length) || length <= 0.0)
{
return;
}
glm::dvec3 d = dir_world;
const double len2 = glm::dot(d, d);
if (!std::isfinite(len2) || len2 <= 1.0e-18)
{
d = glm::dvec3(0.0, 1.0, 0.0);
}
else
{
d *= 1.0 / std::sqrt(len2);
}
add_line(origin_world,
origin_world + d * length,
color,
seconds,
depth,
layer);
}
void DebugDrawSystem::add_aabb(const WorldVec3 &center_world,
const glm::vec3 &half_extents,
const glm::vec4 &color,
float seconds,
DebugDepth depth,
DebugDrawLayer layer)
{
CmdAabb cmd{};
cmd.center_world = center_world;
cmd.half_extents = half_extents;
cmd.color = color;
cmd.depth = depth;
cmd.layer = layer;
cmd.ttl_seconds = ttl_from_seconds(seconds);
_aabbs.push_back(cmd);
}
void DebugDrawSystem::add_sphere(const WorldVec3 &center_world,
float radius,
const glm::vec4 &color,
float seconds,
DebugDepth depth,
DebugDrawLayer layer)
{
CmdSphere cmd{};
cmd.center_world = center_world;
cmd.radius = radius;
cmd.color = color;
cmd.depth = depth;
cmd.layer = layer;
cmd.ttl_seconds = ttl_from_seconds(seconds);
_spheres.push_back(cmd);
}
void DebugDrawSystem::add_capsule(const WorldVec3 &p0_world,
const WorldVec3 &p1_world,
float radius,
const glm::vec4 &color,
float seconds,
DebugDepth depth,
DebugDrawLayer layer)
{
CmdCapsule cmd{};
cmd.p0_world = p0_world;
cmd.p1_world = p1_world;
cmd.radius = radius;
cmd.color = color;
cmd.depth = depth;
cmd.layer = layer;
cmd.ttl_seconds = ttl_from_seconds(seconds);
_capsules.push_back(cmd);
}
void DebugDrawSystem::add_circle(const WorldVec3 &center_world,
const glm::dvec3 &normal_world,
float radius,
const glm::vec4 &color,
float seconds,
DebugDepth depth,
DebugDrawLayer layer)
{
CmdCircle cmd{};
cmd.center_world = center_world;
cmd.normal_world = normal_world;
cmd.radius = radius;
cmd.color = color;
cmd.depth = depth;
cmd.layer = layer;
cmd.ttl_seconds = ttl_from_seconds(seconds);
_circles.push_back(cmd);
}
void DebugDrawSystem::add_cone(const WorldVec3 &apex_world,
const glm::dvec3 &direction_world,
float length,
float angle_degrees,
const glm::vec4 &color,
float seconds,
DebugDepth depth,
DebugDrawLayer layer)
{
CmdCone cmd{};
cmd.apex_world = apex_world;
cmd.direction_world = direction_world;
cmd.length = length;
cmd.angle_degrees = angle_degrees;
cmd.color = color;
cmd.depth = depth;
cmd.layer = layer;
cmd.ttl_seconds = ttl_from_seconds(seconds);
_cones.push_back(cmd);
}
void DebugDrawSystem::add_obb_corners(const std::array<WorldVec3, 8> &corners_world,
const glm::vec4 &color,
float seconds,
DebugDepth depth,
DebugDrawLayer layer)
{
CmdObb cmd{};
cmd.corners_world = corners_world;
cmd.color = color;
cmd.depth = depth;
cmd.layer = layer;
cmd.ttl_seconds = ttl_from_seconds(seconds);
_obbs.push_back(cmd);
}
DebugDrawSystem::LineVertexLists DebugDrawSystem::build_line_vertices(const WorldVec3 &origin_world) const
{
LineVertexLists out{};
if (!_settings.enabled)
{
return out;
}
std::vector<DebugDrawVertex> depth_vertices;
std::vector<DebugDrawVertex> overlay_vertices;
const int seg = clamp_segments(_settings.segments);
for (const CmdLine &cmd : _lines)
{
std::vector<DebugDrawVertex> *dst = select_bucket(cmd, _settings, depth_vertices, overlay_vertices);
if (!dst) continue;
const glm::vec3 a = world_to_local(cmd.a_world, origin_world);
const glm::vec3 b = world_to_local(cmd.b_world, origin_world);
push_line(*dst, a, b, cmd.color);
}
for (const CmdAabb &cmd : _aabbs)
{
std::vector<DebugDrawVertex> *dst = select_bucket(cmd, _settings, depth_vertices, overlay_vertices);
if (!dst) continue;
const glm::vec3 c = world_to_local(cmd.center_world, origin_world);
emit_aabb(*dst, c, cmd.half_extents, cmd.color);
}
for (const CmdSphere &cmd : _spheres)
{
std::vector<DebugDrawVertex> *dst = select_bucket(cmd, _settings, depth_vertices, overlay_vertices);
if (!dst) continue;
const glm::vec3 c = world_to_local(cmd.center_world, origin_world);
emit_sphere(*dst, c, cmd.radius, seg, cmd.color);
}
for (const CmdCapsule &cmd : _capsules)
{
std::vector<DebugDrawVertex> *dst = select_bucket(cmd, _settings, depth_vertices, overlay_vertices);
if (!dst) continue;
const glm::vec3 p0 = world_to_local(cmd.p0_world, origin_world);
const glm::vec3 p1 = world_to_local(cmd.p1_world, origin_world);
emit_capsule(*dst, p0, p1, cmd.radius, seg, cmd.color);
}
for (const CmdCircle &cmd : _circles)
{
std::vector<DebugDrawVertex> *dst = select_bucket(cmd, _settings, depth_vertices, overlay_vertices);
if (!dst) continue;
const glm::vec3 c = world_to_local(cmd.center_world, origin_world);
const glm::vec3 n = glm::vec3(cmd.normal_world);
emit_circle(*dst, c, n, cmd.radius, seg, cmd.color);
}
for (const CmdCone &cmd : _cones)
{
std::vector<DebugDrawVertex> *dst = select_bucket(cmd, _settings, depth_vertices, overlay_vertices);
if (!dst) continue;
const glm::vec3 apex = world_to_local(cmd.apex_world, origin_world);
const glm::vec3 dir = glm::vec3(cmd.direction_world);
emit_cone(*dst, apex, dir, cmd.length, cmd.angle_degrees, seg, cmd.color);
}
for (const CmdObb &cmd : _obbs)
{
std::vector<DebugDrawVertex> *dst = select_bucket(cmd, _settings, depth_vertices, overlay_vertices);
if (!dst) continue;
std::array<glm::vec3, 8> corners_local{};
for (size_t i = 0; i < 8; ++i)
{
corners_local[i] = world_to_local(cmd.corners_world[i], origin_world);
}
emit_obb(*dst, corners_local, cmd.color);
}
out.depth_vertex_count = static_cast<uint32_t>(depth_vertices.size());
out.overlay_vertex_count = static_cast<uint32_t>(overlay_vertices.size());
out.vertices.reserve(depth_vertices.size() + overlay_vertices.size());
out.vertices.insert(out.vertices.end(), depth_vertices.begin(), depth_vertices.end());
out.vertices.insert(out.vertices.end(), overlay_vertices.begin(), overlay_vertices.end());
return out;
}

View File

@@ -0,0 +1,191 @@
#pragma once
#include <core/world.h>
#include <glm/glm.hpp>
#include <array>
#include <cstdint>
#include <vector>
enum class DebugDepth : uint8_t
{
DepthTested = 0,
AlwaysOnTop = 1,
};
enum class DebugDrawLayer : uint32_t
{
Physics = 1u << 0u,
Picking = 1u << 1u,
Lights = 1u << 2u,
Particles = 1u << 3u,
Volumetrics = 1u << 4u,
Misc = 1u << 5u,
};
struct DebugDrawVertex
{
glm::vec3 position{0.0f};
float _pad0{0.0f};
glm::vec4 color{1.0f};
};
static_assert(sizeof(DebugDrawVertex) == 32);
class DebugDrawSystem
{
public:
struct Settings
{
bool enabled = false;
bool show_depth_tested = true;
bool show_overlay = true;
uint32_t layer_mask = static_cast<uint32_t>(DebugDrawLayer::Physics)
| static_cast<uint32_t>(DebugDrawLayer::Picking)
| static_cast<uint32_t>(DebugDrawLayer::Lights)
| static_cast<uint32_t>(DebugDrawLayer::Particles)
| static_cast<uint32_t>(DebugDrawLayer::Volumetrics)
| static_cast<uint32_t>(DebugDrawLayer::Misc);
int segments = 32;
};
struct LineVertexLists
{
std::vector<DebugDrawVertex> vertices;
uint32_t depth_vertex_count = 0;
uint32_t overlay_vertex_count = 0;
};
Settings &settings() { return _settings; }
const Settings &settings() const { return _settings; }
void clear();
// Called once per frame before new submissions to expire one-frame and timed commands.
void begin_frame(float dt_seconds);
void add_line(const WorldVec3 &a_world,
const WorldVec3 &b_world,
const glm::vec4 &color,
float seconds = 0.0f,
DebugDepth depth = DebugDepth::DepthTested,
DebugDrawLayer layer = DebugDrawLayer::Physics);
void add_ray(const WorldVec3 &origin_world,
const glm::dvec3 &dir_world,
double length,
const glm::vec4 &color,
float seconds = 0.0f,
DebugDepth depth = DebugDepth::DepthTested,
DebugDrawLayer layer = DebugDrawLayer::Physics);
void add_aabb(const WorldVec3 &center_world,
const glm::vec3 &half_extents,
const glm::vec4 &color,
float seconds = 0.0f,
DebugDepth depth = DebugDepth::DepthTested,
DebugDrawLayer layer = DebugDrawLayer::Physics);
void add_sphere(const WorldVec3 &center_world,
float radius,
const glm::vec4 &color,
float seconds = 0.0f,
DebugDepth depth = DebugDepth::DepthTested,
DebugDrawLayer layer = DebugDrawLayer::Physics);
void add_capsule(const WorldVec3 &p0_world,
const WorldVec3 &p1_world,
float radius,
const glm::vec4 &color,
float seconds = 0.0f,
DebugDepth depth = DebugDepth::DepthTested,
DebugDrawLayer layer = DebugDrawLayer::Physics);
void add_circle(const WorldVec3 &center_world,
const glm::dvec3 &normal_world,
float radius,
const glm::vec4 &color,
float seconds = 0.0f,
DebugDepth depth = DebugDepth::DepthTested,
DebugDrawLayer layer = DebugDrawLayer::Misc);
void add_cone(const WorldVec3 &apex_world,
const glm::dvec3 &direction_world,
float length,
float angle_degrees,
const glm::vec4 &color,
float seconds = 0.0f,
DebugDepth depth = DebugDepth::DepthTested,
DebugDrawLayer layer = DebugDrawLayer::Misc);
void add_obb_corners(const std::array<WorldVec3, 8> &corners_world,
const glm::vec4 &color,
float seconds = 0.0f,
DebugDepth depth = DebugDepth::DepthTested,
DebugDrawLayer layer = DebugDrawLayer::Misc);
// Expand currently queued commands into render-local line vertices.
LineVertexLists build_line_vertices(const WorldVec3 &origin_world) const;
size_t command_count() const;
private:
struct CmdBase
{
DebugDepth depth{DebugDepth::DepthTested};
DebugDrawLayer layer{DebugDrawLayer::Misc};
glm::vec4 color{1.0f};
float ttl_seconds = -1.0f; // <0 = one-frame
};
struct CmdLine : CmdBase
{
WorldVec3 a_world{0.0, 0.0, 0.0};
WorldVec3 b_world{0.0, 0.0, 0.0};
};
struct CmdAabb : CmdBase
{
WorldVec3 center_world{0.0, 0.0, 0.0};
glm::vec3 half_extents{0.5f, 0.5f, 0.5f};
};
struct CmdSphere : CmdBase
{
WorldVec3 center_world{0.0, 0.0, 0.0};
float radius{1.0f};
};
struct CmdCapsule : CmdBase
{
WorldVec3 p0_world{0.0, 0.0, 0.0};
WorldVec3 p1_world{0.0, 0.0, 0.0};
float radius{0.5f};
};
struct CmdCircle : CmdBase
{
WorldVec3 center_world{0.0, 0.0, 0.0};
glm::dvec3 normal_world{0.0, 1.0, 0.0};
float radius{1.0f};
};
struct CmdCone : CmdBase
{
WorldVec3 apex_world{0.0, 0.0, 0.0};
glm::dvec3 direction_world{0.0, -1.0, 0.0};
float length{1.0f};
float angle_degrees{15.0f};
};
struct CmdObb : CmdBase
{
std::array<WorldVec3, 8> corners_world{};
};
template <typename T>
static void prune_list(std::vector<T> &cmds, float dt_seconds);
Settings _settings{};
std::vector<CmdLine> _lines;
std::vector<CmdAabb> _aabbs;
std::vector<CmdSphere> _spheres;
std::vector<CmdCapsule> _capsules;
std::vector<CmdCircle> _circles;
std::vector<CmdCone> _cones;
std::vector<CmdObb> _obbs;
};

View File

@@ -42,6 +42,7 @@
#include "vk_mem_alloc.h"
#include "core/ui/imgui_system.h"
#include "core/picking/picking_system.h"
#include "core/debug_draw/debug_draw.h"
#include "render/passes/geometry.h"
#include "render/passes/imgui_pass.h"
#include "render/passes/lighting.h"
@@ -50,7 +51,9 @@
#include "render/passes/transparent.h"
#include "render/passes/fxaa.h"
#include "render/passes/tonemap.h"
#include "render/passes/debug_draw.h"
#include "render/passes/shadow.h"
#include "scene/mesh_bvh.h"
#include "device/resource.h"
#include "device/images.h"
#include "context.h"
@@ -65,6 +68,11 @@ VulkanEngine *loadedEngine = nullptr;
VulkanEngine::~VulkanEngine() = default;
void DebugDrawDeleter::operator()(DebugDrawSystem *p) const
{
delete p;
}
static VkExtent2D clamp_nonzero_extent(VkExtent2D extent)
{
if (extent.width == 0) extent.width = 1;
@@ -243,6 +251,10 @@ void VulkanEngine::init()
_context = std::make_shared<EngineContext>();
_input = std::make_unique<InputSystem>();
_context->input = _input.get();
_debugDraw.reset(new DebugDrawSystem());
_context->debug_draw = _debugDraw.get();
_context->device = _deviceManager;
_context->resources = _resourceManager;
_context->descriptors = std::make_shared<DescriptorAllocatorGrowable>(); {
@@ -921,6 +933,14 @@ void VulkanEngine::cleanup()
_picking->cleanup();
_picking.reset();
}
if (_debugDraw)
{
if (_context)
{
_context->debug_draw = nullptr;
}
_debugDraw.reset();
}
// Flush all frame deletion queues first while VMA allocator is still alive
for (int i = 0; i < FRAME_OVERLAP; i++)
@@ -1030,6 +1050,11 @@ void VulkanEngine::draw()
_sceneManager->update_scene();
if (_debugDraw && _sceneManager)
{
_debugDraw->begin_frame(_sceneManager->getDeltaTime());
}
// Update IBL based on camera position and user-defined reflection volumes.
if (_iblManager && _sceneManager)
{
@@ -1304,6 +1329,183 @@ void VulkanEngine::draw()
}
imguiPass = _renderPassManager->getImGuiPass();
// Emit per-frame debug primitives (lights/particles/volumes/picking BVH) into the debug draw system.
if (_context && _context->debug_draw && _context->debug_draw->settings().enabled && _sceneManager)
{
DebugDrawSystem *dd = _context->debug_draw;
SceneManager *scene = _sceneManager.get();
const WorldVec3 origin_world = scene ? scene->get_world_origin() : WorldVec3{0.0, 0.0, 0.0};
const uint32_t layer_mask = dd->settings().layer_mask;
auto layer_on = [layer_mask](DebugDrawLayer layer) {
return (layer_mask & static_cast<uint32_t>(layer)) != 0u;
};
// Picking: BVH root bounds + picked surface bounds (if available)
if (layer_on(DebugDrawLayer::Picking) && _picking && _picking->debug_draw_bvh())
{
const auto &pick = _picking->last_pick();
if (pick.valid && pick.mesh)
{
const glm::mat4 &M = pick.worldTransform;
auto obb_from_local_aabb = [&](const glm::vec3 &center_local, const glm::vec3 &half_extents) {
const glm::vec3 c = center_local;
const glm::vec3 e = glm::max(half_extents, glm::vec3(0.0f));
const glm::vec3 corners_local[8] = {
c + glm::vec3(-e.x, -e.y, -e.z),
c + glm::vec3(+e.x, -e.y, -e.z),
c + glm::vec3(-e.x, +e.y, -e.z),
c + glm::vec3(+e.x, +e.y, -e.z),
c + glm::vec3(-e.x, -e.y, +e.z),
c + glm::vec3(+e.x, -e.y, +e.z),
c + glm::vec3(-e.x, +e.y, +e.z),
c + glm::vec3(+e.x, +e.y, +e.z),
};
std::array<WorldVec3, 8> corners_world{};
for (int i = 0; i < 8; ++i)
{
const glm::vec3 p_local = glm::vec3(M * glm::vec4(corners_local[i], 1.0f));
corners_world[i] = local_to_world(p_local, origin_world);
}
return corners_world;
};
if (pick.surfaceIndex < pick.mesh->surfaces.size())
{
const Bounds &b = pick.mesh->surfaces[pick.surfaceIndex].bounds;
dd->add_obb_corners(obb_from_local_aabb(b.origin, b.extents),
glm::vec4(1.0f, 1.0f, 0.0f, 0.75f),
0.0f,
DebugDepth::AlwaysOnTop,
DebugDrawLayer::Picking);
}
if (pick.mesh->bvh && !pick.mesh->bvh->nodes.empty())
{
const auto &root = pick.mesh->bvh->nodes[0];
const glm::vec3 bmin(root.bounds.min.x, root.bounds.min.y, root.bounds.min.z);
const glm::vec3 bmax(root.bounds.max.x, root.bounds.max.y, root.bounds.max.z);
const glm::vec3 c = (bmin + bmax) * 0.5f;
const glm::vec3 e = (bmax - bmin) * 0.5f;
dd->add_obb_corners(obb_from_local_aabb(c, e),
glm::vec4(0.0f, 1.0f, 1.0f, 0.75f),
0.0f,
DebugDepth::AlwaysOnTop,
DebugDrawLayer::Picking);
}
}
}
// Lights: spheres + spot cones
if (scene && layer_on(DebugDrawLayer::Lights))
{
for (const auto &pl : scene->getPointLights())
{
dd->add_sphere(pl.position_world,
pl.radius,
glm::vec4(pl.color, 0.35f),
0.0f,
DebugDepth::AlwaysOnTop,
DebugDrawLayer::Lights);
}
for (const auto &sl : scene->getSpotLights())
{
dd->add_cone(sl.position_world,
glm::dvec3(sl.direction),
sl.radius,
sl.outer_angle_deg,
glm::vec4(sl.color, 0.35f),
0.0f,
DebugDepth::AlwaysOnTop,
DebugDrawLayer::Lights);
dd->add_sphere(sl.position_world,
0.15f,
glm::vec4(sl.color, 0.9f),
0.0f,
DebugDepth::AlwaysOnTop,
DebugDrawLayer::Lights);
}
}
// Particles: emitter + spawn radius + emission cone
if (layer_on(DebugDrawLayer::Particles))
{
if (auto *particles = _renderPassManager->getPass<ParticlePass>())
{
for (const auto &sys : particles->systems())
{
if (!sys.enabled || sys.count == 0) continue;
const WorldVec3 emitter_world = local_to_world(sys.params.emitter_pos_local, origin_world);
glm::vec4 c = sys.params.color;
c.a = 0.5f;
dd->add_sphere(emitter_world,
std::max(0.05f, sys.params.spawn_radius * 0.5f),
c,
0.0f,
DebugDepth::AlwaysOnTop,
DebugDrawLayer::Particles);
dd->add_circle(emitter_world,
glm::dvec3(sys.params.emitter_dir_local),
sys.params.spawn_radius,
glm::vec4(1.0f, 0.6f, 0.1f, 0.35f),
0.0f,
DebugDepth::AlwaysOnTop,
DebugDrawLayer::Particles);
dd->add_cone(emitter_world,
glm::dvec3(sys.params.emitter_dir_local),
std::max(0.5f, sys.params.spawn_radius * 3.0f),
sys.params.cone_angle_degrees,
glm::vec4(1.0f, 0.6f, 0.1f, 0.35f),
0.0f,
DebugDepth::AlwaysOnTop,
DebugDrawLayer::Particles);
}
}
}
// Volumetrics: volume AABB + wind vector
if (scene && _context->enableVolumetrics && layer_on(DebugDrawLayer::Volumetrics))
{
const glm::vec3 cam_local = scene->get_camera_local_position();
for (uint32_t i = 0; i < EngineContext::MAX_VOXEL_VOLUMES; ++i)
{
const auto &vs = _context->voxelVolumes[i];
if (!vs.enabled) continue;
glm::vec3 center_local = vs.volumeCenterLocal;
if (vs.followCameraXZ)
{
center_local.x += cam_local.x;
center_local.z += cam_local.z;
}
const WorldVec3 center_world = local_to_world(center_local, origin_world);
dd->add_aabb(center_world,
vs.volumeHalfExtents,
glm::vec4(0.4f, 0.8f, 1.0f, 0.35f),
0.0f,
DebugDepth::AlwaysOnTop,
DebugDrawLayer::Volumetrics);
const float wind_len = glm::length(vs.windVelocityLocal);
if (std::isfinite(wind_len) && wind_len > 1.0e-4f)
{
dd->add_ray(center_world,
glm::dvec3(vs.windVelocityLocal),
std::clamp(wind_len, 0.5f, 25.0f),
glm::vec4(0.2f, 1.0f, 0.2f, 0.9f),
0.0f,
DebugDepth::AlwaysOnTop,
DebugDrawLayer::Volumetrics);
}
}
}
}
// Optional Tonemap pass: sample HDR draw -> LDR intermediate
if (auto *tonemap = _renderPassManager->getPass<TonemapPass>())
{
@@ -1314,11 +1516,23 @@ void VulkanEngine::draw()
{
finalColor = fxaa->register_graph(_renderGraph.get(), finalColor);
}
// Debug lines after tonemap/FXAA so they don't trigger bloom.
if (auto *debugDraw = _renderPassManager->getPass<DebugDrawPass>())
{
debugDraw->register_graph(_renderGraph.get(), finalColor, hDepth, true /*LDR*/);
}
}
else
{
// If tonemapping is disabled, present whichever HDR buffer we ended up with.
finalColor = hdrTarget;
// Debug lines onto HDR target when tonemapping is disabled.
if (auto *debugDraw = _renderPassManager->getPass<DebugDrawPass>())
{
debugDraw->register_graph(_renderGraph.get(), finalColor, hDepth, false /*HDR*/);
}
}
}

View File

@@ -41,6 +41,12 @@
#include "core/picking/picking_system.h"
class InputSystem;
class DebugDrawSystem;
struct DebugDrawDeleter
{
void operator()(DebugDrawSystem *p) const;
};
// Number of frames-in-flight. Affects per-frame command buffers, fences,
// semaphores, and transient descriptor pools in FrameResources.
@@ -87,6 +93,7 @@ public:
std::unique_ptr<SceneManager> _sceneManager;
std::unique_ptr<PickingSystem> _picking;
std::unique_ptr<InputSystem> _input;
std::unique_ptr<DebugDrawSystem, DebugDrawDeleter> _debugDraw;
std::unique_ptr<PipelineManager> _pipelineManager;
std::unique_ptr<AssetManager> _assetManager;
std::unique_ptr<AsyncAssetLoader> _asyncLoader;

View File

@@ -6,6 +6,7 @@
#include "engine.h"
#include "core/picking/picking_system.h"
#include "core/debug_draw/debug_draw.h"
#include "SDL2/SDL.h"
#include "SDL2/SDL_vulkan.h"
@@ -1413,6 +1414,68 @@ namespace
{
ImGui::TextUnformatted("Picking system not available");
}
// Debug draw settings (engine-owned collector + render pass)
if (eng->_context && eng->_context->debug_draw)
{
DebugDrawSystem *dd = eng->_context->debug_draw;
auto &s = dd->settings();
bool enabled = s.enabled;
if (ImGui::Checkbox("Enable debug draw", &enabled))
{
s.enabled = enabled;
}
if (s.enabled)
{
ImGui::SameLine();
ImGui::Text("Commands: %zu", dd->command_count());
int seg = s.segments;
if (ImGui::SliderInt("Circle segments", &seg, 3, 128))
{
s.segments = seg;
}
bool depth_tested = s.show_depth_tested;
bool overlay = s.show_overlay;
if (ImGui::Checkbox("Depth-tested", &depth_tested))
{
s.show_depth_tested = depth_tested;
}
ImGui::SameLine();
if (ImGui::Checkbox("Overlay", &overlay))
{
s.show_overlay = overlay;
}
auto layer_checkbox = [&s](const char *label, DebugDrawLayer layer) {
const uint32_t bit = static_cast<uint32_t>(layer);
bool on = (s.layer_mask & bit) != 0u;
if (ImGui::Checkbox(label, &on))
{
if (on) s.layer_mask |= bit;
else s.layer_mask &= ~bit;
}
};
ImGui::TextUnformatted("Layers");
layer_checkbox("Physics##dd_layer_physics", DebugDrawLayer::Physics);
ImGui::SameLine();
layer_checkbox("Picking##dd_layer_picking", DebugDrawLayer::Picking);
ImGui::SameLine();
layer_checkbox("Lights##dd_layer_lights", DebugDrawLayer::Lights);
layer_checkbox("Particles##dd_layer_particles", DebugDrawLayer::Particles);
ImGui::SameLine();
layer_checkbox("Volumetrics##dd_layer_volumetrics", DebugDrawLayer::Volumetrics);
ImGui::SameLine();
layer_checkbox("Misc##dd_layer_misc", DebugDrawLayer::Misc);
}
}
else
{
ImGui::TextUnformatted("Debug draw system not available");
}
ImGui::Separator();
// Spawn glTF instances (runtime)

View File

@@ -0,0 +1,234 @@
#include "debug_draw.h"
#include <core/assets/manager.h>
#include <core/context.h>
#include <core/debug_draw/debug_draw.h>
#include <core/device/device.h>
#include <core/device/resource.h>
#include <core/device/swapchain.h>
#include <core/frame/resources.h>
#include <core/pipeline/manager.h>
#include <render/graph/graph.h>
#include <render/graph/resources.h>
#include <scene/vk_scene.h>
#include <cstring>
namespace
{
struct DebugDrawPushConstants
{
glm::mat4 viewproj;
VkDeviceAddress vertex_buffer;
};
static_assert(offsetof(DebugDrawPushConstants, vertex_buffer) == 64);
static_assert(sizeof(DebugDrawPushConstants) >= 72);
static_assert((sizeof(DebugDrawPushConstants) % 4) == 0);
constexpr const char *k_debug_vert = "debug_lines.vert.spv";
constexpr const char *k_debug_frag = "debug_lines.frag.spv";
constexpr const char *k_ldr_depth = "debug_lines.ldr.depth";
constexpr const char *k_ldr_overlay = "debug_lines.ldr.overlay";
constexpr const char *k_hdr_depth = "debug_lines.hdr.depth";
constexpr const char *k_hdr_overlay = "debug_lines.hdr.overlay";
}
void DebugDrawPass::init(EngineContext *context)
{
_context = context;
if (!_context || !_context->getAssets() || !_context->pipelines || !_context->getSwapchain())
{
return;
}
const VkFormat ldrFormat = _context->getSwapchain()->swapchainImageFormat();
const VkFormat hdrFormat = _context->getSwapchain()->drawImage().imageFormat;
const VkFormat depthFormat = _context->getSwapchain()->depthImage().imageFormat;
GraphicsPipelineCreateInfo base{};
base.vertexShaderPath = _context->getAssets()->shaderPath(k_debug_vert);
base.fragmentShaderPath = _context->getAssets()->shaderPath(k_debug_frag);
base.setLayouts = {};
VkPushConstantRange pcr{};
pcr.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
pcr.offset = 0;
pcr.size = sizeof(DebugDrawPushConstants);
base.pushConstants = {pcr};
auto make_cfg = [depthFormat](VkFormat colorFormat, bool depth_test) {
return [colorFormat, depthFormat, depth_test](PipelineBuilder &b) {
b.set_input_topology(VK_PRIMITIVE_TOPOLOGY_LINE_LIST);
b.set_polygon_mode(VK_POLYGON_MODE_FILL);
b.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
b.set_multisampling_none();
b.enable_blending_alphablend();
if (depth_test)
{
b.enable_depthtest(false, VK_COMPARE_OP_GREATER_OR_EQUAL);
}
else
{
b.disable_depthtest();
}
b.set_color_attachment_format(colorFormat);
b.set_depth_format(depthFormat);
};
};
GraphicsPipelineCreateInfo ldrDepth = base;
ldrDepth.configure = make_cfg(ldrFormat, true);
_context->pipelines->createGraphicsPipeline(k_ldr_depth, ldrDepth);
GraphicsPipelineCreateInfo ldrOverlay = base;
ldrOverlay.configure = make_cfg(ldrFormat, false);
_context->pipelines->createGraphicsPipeline(k_ldr_overlay, ldrOverlay);
GraphicsPipelineCreateInfo hdrDepth = base;
hdrDepth.configure = make_cfg(hdrFormat, true);
_context->pipelines->createGraphicsPipeline(k_hdr_depth, hdrDepth);
GraphicsPipelineCreateInfo hdrOverlay = base;
hdrOverlay.configure = make_cfg(hdrFormat, false);
_context->pipelines->createGraphicsPipeline(k_hdr_overlay, hdrOverlay);
}
void DebugDrawPass::cleanup()
{
_deletionQueue.flush();
}
void DebugDrawPass::execute(VkCommandBuffer)
{
// Executed via render graph.
}
void DebugDrawPass::register_graph(RenderGraph *graph,
RGImageHandle target_color,
RGImageHandle depth,
bool is_ldr_target)
{
if (!graph || !_context || !target_color.valid())
{
return;
}
if (!_context->debug_draw || !_context->debug_draw->settings().enabled)
{
return;
}
// Always declare depth so depth-tested lines can be drawn when present.
// Pipelines are compatible even if the overlay variant disables depth testing.
graph->add_pass(
"DebugDraw",
RGPassType::Graphics,
[target_color, depth](RGPassBuilder &builder, EngineContext *) {
builder.write_color(target_color);
if (depth.valid())
{
builder.write_depth(depth, false /*load existing depth*/);
}
},
[this, is_ldr_target](VkCommandBuffer cmd, const RGPassResources &res, EngineContext *ctx) {
(void)res;
draw_debug(cmd, ctx, res, is_ldr_target);
});
}
void DebugDrawPass::draw_debug(VkCommandBuffer cmd,
EngineContext *ctx,
const RGPassResources &,
bool is_ldr_target)
{
EngineContext *ctxLocal = ctx ? ctx : _context;
if (!ctxLocal || !ctxLocal->currentFrame || !ctxLocal->getDevice() || !ctxLocal->getResources() || !ctxLocal->pipelines)
{
return;
}
DebugDrawSystem *dd = ctxLocal->debug_draw;
if (!dd || !dd->settings().enabled)
{
return;
}
WorldVec3 origin_world{0.0, 0.0, 0.0};
if (ctxLocal->scene)
{
origin_world = ctxLocal->scene->get_world_origin();
}
DebugDrawSystem::LineVertexLists lists = dd->build_line_vertices(origin_world);
if (lists.vertices.empty())
{
return;
}
const VkDeviceSize bytes = static_cast<VkDeviceSize>(lists.vertices.size() * sizeof(DebugDrawVertex));
if (bytes == 0)
{
return;
}
ResourceManager *rm = ctxLocal->getResources();
DeviceManager *dm = ctxLocal->getDevice();
AllocatedBuffer vb = rm->create_buffer(
static_cast<size_t>(bytes),
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
VMA_MEMORY_USAGE_CPU_TO_GPU);
if (vb.buffer == VK_NULL_HANDLE || vb.allocation == VK_NULL_HANDLE || vb.info.pMappedData == nullptr)
{
return;
}
std::memcpy(vb.info.pMappedData, lists.vertices.data(), static_cast<size_t>(bytes));
vmaFlushAllocation(dm->allocator(), vb.allocation, 0, bytes);
ctxLocal->currentFrame->_deletionQueue.push_function([rm, vb]() {
rm->destroy_buffer(vb);
});
VkBufferDeviceAddressInfo addrInfo{.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO};
addrInfo.buffer = vb.buffer;
VkDeviceAddress addr = vkGetBufferDeviceAddress(dm->device(), &addrInfo);
DebugDrawPushConstants pc{};
pc.viewproj = ctxLocal->getSceneData().viewproj;
pc.vertex_buffer = addr;
VkExtent2D extent = ctxLocal->getDrawExtent();
VkViewport vp{0.f, 0.f, (float)extent.width, (float)extent.height, 0.f, 1.f};
VkRect2D sc{{0, 0}, extent};
vkCmdSetViewport(cmd, 0, 1, &vp);
vkCmdSetScissor(cmd, 0, 1, &sc);
const char *depthPipeName = is_ldr_target ? k_ldr_depth : k_hdr_depth;
const char *overlayPipeName = is_ldr_target ? k_ldr_overlay : k_hdr_overlay;
if (lists.depth_vertex_count > 0)
{
VkPipeline pipeline = VK_NULL_HANDLE;
VkPipelineLayout layout = VK_NULL_HANDLE;
if (ctxLocal->pipelines->getGraphics(depthPipeName, pipeline, layout))
{
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdPushConstants(cmd, layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(pc), &pc);
vkCmdDraw(cmd, lists.depth_vertex_count, 1, 0, 0);
}
}
if (lists.overlay_vertex_count > 0)
{
VkPipeline pipeline = VK_NULL_HANDLE;
VkPipelineLayout layout = VK_NULL_HANDLE;
if (ctxLocal->pipelines->getGraphics(overlayPipeName, pipeline, layout))
{
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdPushConstants(cmd, layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(pc), &pc);
vkCmdDraw(cmd, lists.overlay_vertex_count, 1, lists.depth_vertex_count, 0);
}
}
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include <render/renderpass.h>
#include <render/graph/types.h>
class EngineContext;
class RenderGraph;
class RGPassResources;
// Debug line rendering pass (wire primitives generated by DebugDrawSystem).
// Draws onto either the final LDR target (after tonemap/FXAA) or the HDR draw target
// when tonemapping is disabled.
class DebugDrawPass final : public IRenderPass
{
public:
void init(EngineContext *context) override;
void cleanup() override;
void execute(VkCommandBuffer) override;
const char *getName() const override { return "DebugDraw"; }
void register_graph(RenderGraph *graph,
RGImageHandle target_color,
RGImageHandle depth,
bool is_ldr_target);
private:
void draw_debug(VkCommandBuffer cmd,
EngineContext *ctx,
const RGPassResources &res,
bool is_ldr_target);
EngineContext *_context = nullptr;
DeletionQueue _deletionQueue;
};

View File

@@ -8,6 +8,7 @@
#include "passes/clouds.h"
#include "passes/particles.h"
#include "passes/fxaa.h"
#include "passes/debug_draw.h"
#include "passes/transparent.h"
#include "passes/tonemap.h"
#include "passes/shadow.h"
@@ -53,6 +54,10 @@ void RenderPassManager::init(EngineContext *context)
fxaaPass->init(context);
addPass(std::move(fxaaPass));
auto debugDrawPass = std::make_unique<DebugDrawPass>();
debugDrawPass->init(context);
addPass(std::move(debugDrawPass));
auto transparentPass = std::make_unique<TransparentPass>();
transparentPass->init(context);
addPass(std::move(transparentPass));