新增调试绘制体系:DebugDraw 服务 + 物理调试 pass + 脚本侧力学线
通用图元(线/箭头四翼头/球/胶囊/盒)瞬时+持久双缓冲聚合; 物理 pass 画刚体速度/重力/接触反力;角色 collider 与力学 线归位(引擎只画资产、脚本画行为);线宽按设备上限钳制
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// DebugDraw:引擎级通用调试图元服务(Unity Debug.DrawLine / UE
|
||||
// DrawDebugHelpers 的对应物)。
|
||||
//
|
||||
// 分层定位:全局静态 API,任何层(脚本/物理/动画/AI)调用即收集;
|
||||
// Renderer 每帧取走缓冲,PhysicsDebugPass 统一上传叠加渲染。引擎侧
|
||||
// 只写这一次,之后一切新可视化 = 调用方加几行,不再碰引擎代码。
|
||||
//
|
||||
// 语义对齐 Unity/UE:
|
||||
// duration <= 0 → 只画当前帧(调用方每帧重调,Unity DrawLine 同)
|
||||
// duration > 0 → 存活 duration 秒(内部持久列表,期间无需重调;
|
||||
// 固定步长系统用 2×步长 即可跨渲染帧无缝续接)
|
||||
// depthTest=false → 透视遮挡直显(UE DrawDebugLine bPersistent 位)
|
||||
// width → 线宽(需 wideLines 特性,设备不支持时钳回 1.0)
|
||||
//
|
||||
// 线程模型:主线程单写单读(脚本 OnUpdate 收集 → Renderer 上传 →
|
||||
// EndFrame 翻页)。
|
||||
// 预算:kMaxVerts 截断 + 一次告警(调试图元打满是滥用信号)。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_DEBUGDRAW_H
|
||||
#define HELLOVULKAN_DEBUGDRAW_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Engine::Debug {
|
||||
|
||||
// 顶点(28B):与 PhysicsDebugVertex 同布局——渲染 pass 复用同一
|
||||
// 顶点输入态(pos vec3 + color vec4)
|
||||
struct Vertex {
|
||||
glm::vec3 pos;
|
||||
glm::vec4 color;
|
||||
};
|
||||
|
||||
// 渲染批次:同 (拓扑, 线宽, 深度测试) 的连续顶点段。调用方按
|
||||
// 参数自动分组,draw 侧每批一次状态切换(动态状态零管线对象)
|
||||
struct Batch {
|
||||
bool tris; // true = triangleList / false = lineList
|
||||
f32 width;
|
||||
bool depthTest;
|
||||
u32 first; // 顶点偏移(顶点数,非字节)
|
||||
u32 count;
|
||||
};
|
||||
|
||||
// 单帧收集结果(Renderer 每帧消费,勿手动修改)
|
||||
struct DebugFrame {
|
||||
std::vector<Vertex> verts;
|
||||
std::vector<Batch> batches;
|
||||
|
||||
void Clear() { verts.clear(); batches.clear(); }
|
||||
bool Empty() const { return batches.empty(); }
|
||||
};
|
||||
|
||||
// 顶点预算(每渲染帧):调试线框足够用,溢出截断 + 告警一次
|
||||
inline constexpr u32 kMaxVerts = 1 << 16;
|
||||
|
||||
// ---- 全局开关 ----
|
||||
void SetEnabled(bool on);
|
||||
bool IsEnabled();
|
||||
void Clear(); // 清空瞬时 + 持久图元(PIE 停止/场景切换时调用)
|
||||
|
||||
// ---- 线类(全部世界空间)----
|
||||
void Line(const glm::vec3& a, const glm::vec3& b,
|
||||
const glm::vec4& color,
|
||||
f32 width = 1.0f, f32 duration = 0.0f,
|
||||
bool depthTest = true);
|
||||
|
||||
// Ray:from 出发沿单位方向 dirNorm 画 length 长(Unity DrawRay 同形)
|
||||
void Ray(const glm::vec3& from, const glm::vec3& dirNorm, f32 length,
|
||||
const glm::vec4& color,
|
||||
f32 width = 1.0f, f32 duration = 0.0f, bool depthTest = true);
|
||||
|
||||
// 箭头:主干 + 两翼头(正交基自动构建,零向量方向安全退化成点)
|
||||
void Arrow(const glm::vec3& from, const glm::vec3& to,
|
||||
const glm::vec4& color, f32 headSize = 0.12f,
|
||||
f32 width = 1.0f, f32 duration = 0.0f,
|
||||
bool depthTest = true);
|
||||
|
||||
// 十字标记(Unity Debug.DrawCross 同位)
|
||||
void Cross(const glm::vec3& pos, f32 size, const glm::vec4& color,
|
||||
f32 width = 1.0f, f32 duration = 0.0f,
|
||||
bool depthTest = true);
|
||||
|
||||
// ---- 形状线框 ----
|
||||
|
||||
// 三大圆经线圈(UE DrawDebugSphere 同形)
|
||||
void WireSphere(const glm::vec3& center, f32 radius,
|
||||
const glm::vec4& color, int segments = 16,
|
||||
f32 width = 1.0f, f32 duration = 0.0f,
|
||||
bool depthTest = true);
|
||||
|
||||
// 朝向 normal 的平面圆
|
||||
void WireCircle(const glm::vec3& center, f32 radius,
|
||||
const glm::vec3& normal, const glm::vec4& color,
|
||||
int segments = 24,
|
||||
f32 width = 1.0f, f32 duration = 0.0f,
|
||||
bool depthTest = true);
|
||||
|
||||
// 轴对齐 / 旋转盒(rot 单位四元数 = AABB;UE DrawDebugBox 同形)
|
||||
void WireBox(const glm::vec3& center, const glm::vec3& halfExtents,
|
||||
const glm::vec4& color,
|
||||
const glm::quat& rot = glm::quat(1.0f, 0.0f, 0.0f, 0.0f),
|
||||
f32 width = 1.0f, f32 duration = 0.0f,
|
||||
bool depthTest = true);
|
||||
|
||||
// 胶囊线框:轴向 local +Y(Jolt 惯例),halfHeight = 圆柱段半长
|
||||
//(总高 = 2×(halfHeight+radius),与引擎 CharacterController 参数一致)
|
||||
void WireCapsule(const glm::vec3& center, f32 radius, f32 halfHeight,
|
||||
const glm::vec4& color,
|
||||
const glm::quat& rot = glm::quat(1.0f, 0.0f, 0.0f, 0.0f),
|
||||
f32 width = 1.0f, f32 duration = 0.0f,
|
||||
bool depthTest = true);
|
||||
|
||||
// ---- 便捷色:0xRRGGBB → 线性 HDR vec4(可 >1 过 tonemap 依旧醒目)----
|
||||
constexpr glm::vec4 Rgb(u32 hex, f32 alpha = 1.0f)
|
||||
{
|
||||
constexpr f32 kInv = 1.0f / 255.0f;
|
||||
return {(static_cast<f32>((hex >> 16) & 0xFF)) * kInv,
|
||||
(static_cast<f32>((hex >> 8) & 0xFF)) * kInv,
|
||||
(static_cast<f32>(hex & 0xFF)) * kInv,
|
||||
alpha};
|
||||
}
|
||||
|
||||
// ---- Renderer 消费端(业务代码勿调用)----
|
||||
|
||||
// 取当前帧缓冲(禁用/空 = Empty)。Renderer 上传窗口调用
|
||||
const DebugFrame& Frame();
|
||||
|
||||
// 翻页:清瞬时缓冲 + 清过期持久项。Renderer 上传完成后调用
|
||||
void EndFrame();
|
||||
|
||||
} // namespace Engine::Debug
|
||||
|
||||
#endif //HELLOVULKAN_DEBUGDRAW_H
|
||||
@@ -99,6 +99,9 @@ namespace Engine {
|
||||
void Jump(u32 entityBits, f32 speed); // 垂直速度置 speed
|
||||
bool IsCharacterGrounded(u32 entityBits) const;
|
||||
glm::vec3 GetCharacterVelocity(u32 entityBits) const;
|
||||
// 角色脚底位置(物理权威世界坐标):调试图元/特效锚点用它,
|
||||
// 而不是 Transform().GetPosition()——后者在有父级时是本地坐标
|
||||
glm::vec3 GetCharacterPosition(u32 entityBits) const;
|
||||
|
||||
private:
|
||||
// 创建时缓存的参数(变更检测 → 重建 body)
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
//
|
||||
// PhysicsDebugPass:物理调试图元叠加(collider 线框/约束/重力/速度/
|
||||
// 加速度箭头)。
|
||||
// PhysicsDebugPass:调试图元叠加 pass(双源)。
|
||||
// 物理源 = PhysicsWorld 收集的 PhysicsDebugFrame(collider 线框/
|
||||
// 约束,Application 注入帧缓冲指针)
|
||||
// 通用源 = Engine::Debug 调试服务(脚本/任意层的 Line/Arrow/Wire*,
|
||||
// Unity Debug.DrawLine 同位;批次自带线宽/深度测试/拓扑)
|
||||
//
|
||||
// 画在 HDRColor(Forward 之后、TAA 之前):调试图元进入场景主链路——
|
||||
// TAA 时域累积抗锯齿、Bloom/Tonemap 正常消费,kDebugTransparent 诊断
|
||||
// 模式下也可见。深度只测不写(被不透明几何正确遮挡),无混合直写
|
||||
// (fullscreen pass 状态泄漏教训)。
|
||||
//
|
||||
// 数据源 = PhysicsWorld 每帧收集的 PhysicsDebugFrame(Application 把
|
||||
// PhysicsWorld 的帧缓冲指针注入 Renderer)。空帧零绘制;debug 开关
|
||||
// 全关时 PhysicsWorld 不收集 → 顶点数为 0 → 本 pass 自然零开销。
|
||||
// 空帧零绘制:两源顶点数均 0 时提前短路。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_PHYSICSDEBUGPASS_H
|
||||
#define HELLOVULKAN_PHYSICSDEBUGPASS_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/Debug/DebugDraw.h>
|
||||
#include <Engine/Renderer/Passes/RenderPass.h>
|
||||
#include <Engine/Renderer/SceneTypes.h>
|
||||
#include <Engine/Vulkan/UniformBuffer.h>
|
||||
#include <Engine/Vulkan/ShaderManager.h>
|
||||
#include <vector>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace Engine {
|
||||
@@ -44,7 +47,8 @@ namespace Engine {
|
||||
FrameSceneData& frame,
|
||||
RG::TextureHandle hdrColor,
|
||||
RG::TextureHandle depth,
|
||||
UniformBuffer& lineVB, UniformBuffer& triVB);
|
||||
UniformBuffer& lineVB, UniformBuffer& triVB,
|
||||
UniformBuffer& debugVB);
|
||||
~PhysicsDebugPass();
|
||||
|
||||
PhysicsDebugPass(const PhysicsDebugPass&) = delete;
|
||||
@@ -52,23 +56,36 @@ namespace Engine {
|
||||
|
||||
void Register(RG::RenderGraph& rg) override;
|
||||
|
||||
// 每帧 Renderer::Render 里(该槽位 fence 等待后)上传当帧图元
|
||||
void PrepareFrame(u32 frameSlot, const PhysicsDebugFrame& src);
|
||||
// 每帧 Renderer::Render 里(该槽位 fence 等待后)上传当帧
|
||||
// 图元。dbg 为 nullptr = 通用调试源禁用/空
|
||||
void PrepareFrame(u32 frameSlot, const PhysicsDebugFrame& src,
|
||||
const Debug::DebugFrame* dbg);
|
||||
|
||||
private:
|
||||
// 一个可绘制段:独立拓扑/线宽/深度测试(动态状态逐批设置)
|
||||
struct Span {
|
||||
bool tris;
|
||||
f32 width;
|
||||
bool depthTest;
|
||||
u32 first; // 顶点偏移(对应 VB 内顶点序号)
|
||||
u32 count;
|
||||
};
|
||||
|
||||
Device& m_Device;
|
||||
DescriptorManager& m_Descriptors;
|
||||
FrameSceneData& m_Frame;
|
||||
RG::TextureHandle m_HDRColor;
|
||||
RG::TextureHandle m_Depth;
|
||||
UniformBuffer& m_LineVB; // per-slot ring(Renderer 持有生命周期)
|
||||
UniformBuffer& m_LineVB; // per-slot ring(物理源,Renderer 持有)
|
||||
UniformBuffer& m_TriVB;
|
||||
UniformBuffer& m_DebugVB; // 通用调试源 ring(Renderer 持有)
|
||||
|
||||
vk::PipelineLayout m_Layout; // 无描述符,push = viewProj 64B
|
||||
ShaderManager::ShaderRef m_Vert, m_Frag; // 析构自动销毁
|
||||
|
||||
std::array<u32, kMaxFramesInFlight> m_LineCounts{};
|
||||
std::array<u32, kMaxFramesInFlight> m_TriCounts{};
|
||||
std::array<std::vector<Span>, kMaxFramesInFlight> m_Spans;
|
||||
bool m_TruncationWarned = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ public:
|
||||
// 各向异性过滤:纹理采样器清晰度(掠射角材质锐利度)的硬件开关
|
||||
bool IsSamplerAnisotropySupported() const { return m_SamplerAnisotropySupported; }
|
||||
float MaxSamplerAnisotropy() const { return m_MaxSamplerAnisotropy; }
|
||||
bool IsWideLinesSupported() const { return m_WideLinesSupported; }
|
||||
float MaxLineWidth() const { return m_MaxLineWidth; }
|
||||
|
||||
private:
|
||||
void CreateInstance();
|
||||
@@ -81,6 +83,8 @@ private:
|
||||
bool m_DrawIndirectCountSupported = false;
|
||||
bool m_SamplerAnisotropySupported = false;
|
||||
float m_MaxSamplerAnisotropy = 1.0f;
|
||||
bool m_WideLinesSupported = false;
|
||||
float m_MaxLineWidth = 1.0f; // 设备线宽上限(像素,limits.lineWidthRange[1])
|
||||
|
||||
std::vector<const char*> m_ValidationLayers = { "VK_LAYER_KHRONOS_validation" };
|
||||
std::vector<const char*> m_DeviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
|
||||
|
||||
Reference in New Issue
Block a user