feat: Jolt 物理系统——刚体/碰撞体/物理调试图元 + 引擎时间单源
- PhysicsWorld:Jolt 集成,Dynamic/Kinematic/Static 刚体,姿态写回 Transform(Unity FixedUpdate 语义,吃 scaled dt,暂停自动冻结) - 碰撞体:Box(默认/手动 halfExtents)+ Mesh 碰撞体(Static→精确 MeshShape / Dynamic·Kinematic→ConvexHull,数据源 = Mesh ctor 缓存 的 CPU 位置副本,退化三角剔除,失败回退 Box 并告警) - PhysicsDebugPass:物理调试图元每帧上传绘制(debug_line shader) - Time:引擎时间单源(BeginFrame 落定 FrameTime,scaled/unscaled 分离) - JoltPhysics 以 submodule 接入(官方 url)
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// Time:帧时间系统(单源采集 → 帧契约 → 静态门面)。
|
||||
//
|
||||
// 数据流(镜像 FrameSceneData 的落帧模式):
|
||||
// Application::Run 帧首采集真实 dt(steady_clock 单源,钳制卡顿)→
|
||||
// Time::BeginFrame 落定当帧 FrameTime(业务当帧只读)→ 三轨消费:
|
||||
// 游戏轨 Dt() = unscaledDt × timeScale(OnUpdate/动画/粒子)
|
||||
// 真实轨 UnscaledDt() (编辑器相机/UI/FPS,暂停不受影响)
|
||||
// 物理轨 accumulator (吃 Dt(),scale=0 天然冻结,编辑器对账仍跑)
|
||||
//
|
||||
// 对齐 UE/Unity:
|
||||
// Time::Delta/UnscaledDelta ≈ Unity Time.deltaTime / unscaledDeltaTime
|
||||
// Time::SetTimeScale(0) ≈ Unity timeScale=0 / UE GlobalTimeDilation=0
|
||||
// FrameTime.elapsed ≈ Unity Time.time(scaled 累计)
|
||||
// Time::Realtime() ≈ Unity realtimeSinceStartup
|
||||
// TimerManager(UE SetTimer 语义)留二期,有真实消费场景再上。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_TIME_H
|
||||
#define HELLOVULKAN_TIME_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
// 帧时间契约:Run 帧首落定,当帧只读
|
||||
struct FrameTime {
|
||||
f64 unscaledDt = 0.0; // 真实帧时长(秒,f64 防 long-run 误差)
|
||||
f32 timeScale = 1.0f; // 全局缩放(0 = 暂停)
|
||||
u64 frameCount = 0;
|
||||
f64 elapsed = 0.0; // 游戏累计(scaled)
|
||||
f64 unscaledElapsed = 0.0; // 真实累计
|
||||
|
||||
f32 Dt() const { return static_cast<f32>(unscaledDt) * timeScale; }
|
||||
f32 UnscaledDt() const { return static_cast<f32>(unscaledDt); }
|
||||
f32 Milliseconds() const { return Dt() * 1000.0f; }
|
||||
f32 UnscaledMilliseconds() const { return UnscaledDt() * 1000.0f; }
|
||||
};
|
||||
|
||||
// 静态门面:任意深度代码免穿引用拿 Application(Unity Time 风格)
|
||||
class Time {
|
||||
public:
|
||||
// ---- 消费端 ----
|
||||
static const FrameTime& Current();
|
||||
static f32 Delta(); // scaled dt(当帧)
|
||||
static f32 UnscaledDelta();
|
||||
static f64 Elapsed(); // scaled 累计(秒)
|
||||
static f64 Realtime(); // 进程启动至今(真实秒,性能测量统一入口)
|
||||
static f32 TimeScale();
|
||||
static u64 FrameCount();
|
||||
|
||||
// ---- 控制意图(下一帧生效)----
|
||||
static void SetTimeScale(f32 scale); // 0 = 暂停;负值钳为 0
|
||||
|
||||
// ---- 框架内部:Application::Run 帧首调用 ----
|
||||
static void BeginFrame(f64 unscaledDt, u64 frameCount);
|
||||
|
||||
private:
|
||||
static FrameTime s_Current;
|
||||
static f32 s_PendingScale;
|
||||
static const std::chrono::steady_clock::time_point s_Startup;
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_TIME_H
|
||||
@@ -134,6 +134,45 @@ namespace Engine::ECS {
|
||||
bool castShadow = false; // 方向光可投影(引擎取第一个 castShadow 方向光为 CSM 光源)
|
||||
};
|
||||
|
||||
// 刚体(Jolt 后端)。纯数据 POD——头文件零 Jolt 依赖,
|
||||
// bodyId 存 JPH::BodyID 的裸位模式,翻译在 PhysicsWorld。
|
||||
// 生命周期:PhysicsWorld::SyncScene 每帧对账(创建/销毁/参数同步),
|
||||
// 动态体的 Transform 由物理写回(Writeback 标脏),静态/运动学的
|
||||
// Transform 由编辑器驱动同步进 body。
|
||||
// 扩展路线:软体/布料 = SoftBodyComponent(Jolt SoftBodySharedSettings),
|
||||
// 角色 = CharacterControllerComponent(CharacterVirtual),载具 = VehicleComponent
|
||||
// (VehicleConstraint)——都走 PhysicsWorld 门面,不复用本组件。
|
||||
struct RigidBodyComponent {
|
||||
enum class BodyType : u8 { Static = 0, Dynamic = 1, Kinematic = 2 };
|
||||
// Mesh:用实体 MeshRenderer 的视觉网格建碰撞(Unity MeshCollider
|
||||
// 语义)——Static → Jolt MeshShape(精确三角网格,不可移动);
|
||||
// Dynamic/Kinematic → ConvexHullShape(凸包近似,MeshShape 太贵
|
||||
// 不能动)。数据源 Mesh 的 CPU 位置副本,无 MeshRenderer 回退 Box
|
||||
enum class ColliderType : u8 { Box = 0, Sphere = 1, Mesh = 2 };
|
||||
|
||||
BodyType type = BodyType::Dynamic; // Unity 语义:加 Rigidbody 即受物理驱动
|
||||
ColliderType collider = ColliderType::Box;
|
||||
|
||||
// 碰撞体尺寸(× Transform 缩放,Unity BoxCollider.size 语义)——
|
||||
// 程序化模型顶点烘焙尺寸,不能从 scale 反推
|
||||
glm::vec3 boxHalfExtents{0.5f, 0.5f, 0.5f};
|
||||
f32 sphereRadius = 0.5f;
|
||||
|
||||
// 动态体参数
|
||||
f32 mass = 1.0f;
|
||||
f32 gravityScale = 1.0f;
|
||||
f32 friction = 0.5f;
|
||||
f32 restitution = 0.2f;
|
||||
|
||||
// 传感器(Unity isTrigger 语义):只产生 Added/Removed 事件,无碰撞解算
|
||||
bool isSensor = false;
|
||||
|
||||
// 运行时(PhysicsWorld 维护,勿手改)
|
||||
u32 bodyId = 0; // JPH::BodyID 裸位
|
||||
bool bodyValid = false;
|
||||
glm::vec3 velocity{0.0f}; // 只读调试视图:线速度(m/s)
|
||||
};
|
||||
|
||||
} // namespace Engine::ECS
|
||||
|
||||
#endif //HELLOVULKAN_COMPONENTS_H
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// PhysicsDebugDraw:Jolt 调试图元收集器(JPH::DebugRendererSimple 派生)。
|
||||
//
|
||||
// 职责只有"收集":Jolt 各调试源(BodyManager::Draw 的形状线框/速度箭头、
|
||||
// ConstraintManager::DrawConstraints、PhysicsWorld 手绘的重力/加速度箭头)
|
||||
// 触发的 DrawLine/DrawTriangle 回调全部收进顶点数组;渲染侧
|
||||
// (PhysicsDebugPass)每帧上传到 ring VB 绘制。收集器不理解 Vulkan,
|
||||
// 渲染 pass 不理解 Jolt——图元契约见 PhysicsDebugTypes.h。
|
||||
//
|
||||
// 生命周期:PhysicsWorld 持有并安装为 JPH::DebugRenderer::sInstance;
|
||||
// DebugRendererSimple 的构造即 DebugRenderer::Initialize()(预烘焙
|
||||
// box/sphere/cylinder 等通用几何 batch),NextFrame() 每帧释放不再
|
||||
// 引用的缓存几何。
|
||||
//
|
||||
// 扩展性(物理调试的唯一图元出口,新调试源零新通道):
|
||||
// 软体布料 → DrawSettings 的 mDrawSoftBody* 开关原生生效
|
||||
// 角色控制器 → CharacterVirtual::DebugDraw 走同一 sInstance
|
||||
// 载具控制器 → VehicleConstraint::Draw 走同一 sInstance
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_PHYSICSDEBUGDRAW_H
|
||||
#define HELLOVULKAN_PHYSICSDEBUGDRAW_H
|
||||
|
||||
#include <Engine/Physics/PhysicsDebugTypes.h>
|
||||
|
||||
#ifdef JPH_DEBUG_RENDERER
|
||||
|
||||
#include <Jolt/Jolt.h>
|
||||
#include <Jolt/Renderer/DebugRendererSimple.h>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class PhysicsDebugDraw final : public JPH::DebugRendererSimple {
|
||||
public:
|
||||
// 析构交给基类:~DebugRenderer 自己断言并清 sInstance(JPH_ASSERT(sInstance == this)),
|
||||
// 派生类手动清会让基类断言看到 nullptr 而失败
|
||||
|
||||
PhysicsDebugFrame& Frame() { return m_Frame; }
|
||||
|
||||
// 每次收集前调用:清空上一帧图元(容量保留,稳态零堆分配)
|
||||
void BeginCollect() { m_Frame.Clear(); }
|
||||
|
||||
// ---- DebugRenderer 回调(基类其余绘制原语全部路由到这里)----
|
||||
void DrawLine(JPH::RVec3Arg inFrom, JPH::RVec3Arg inTo,
|
||||
JPH::ColorArg inColor) override;
|
||||
void DrawTriangle(JPH::RVec3Arg inV1, JPH::RVec3Arg inV2,
|
||||
JPH::RVec3Arg inV3, JPH::ColorArg inColor,
|
||||
JPH::DebugRenderer::ECastShadow inCastShadow) override;
|
||||
void DrawText3D(JPH::RVec3Arg, const JPH::string_view&,
|
||||
JPH::ColorArg, float) override {
|
||||
// 3D 文本暂不支持(遮挡 UI 噪音,需要时再挂 ImGui 调试层)
|
||||
}
|
||||
|
||||
private:
|
||||
static glm::vec3 ToGlm(JPH::RVec3Arg v)
|
||||
{
|
||||
return {static_cast<f32>(v.GetX()), static_cast<f32>(v.GetY()),
|
||||
static_cast<f32>(v.GetZ())};
|
||||
}
|
||||
static glm::vec4 ToGlm(JPH::ColorArg c)
|
||||
{
|
||||
constexpr f32 kInv = 1.0f / 255.0f;
|
||||
return {c.r * kInv, c.g * kInv, c.b * kInv, c.a * kInv};
|
||||
}
|
||||
|
||||
PhysicsDebugFrame m_Frame;
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#else // !JPH_DEBUG_RENDERER
|
||||
|
||||
// Distribution 构建:Jolt 调试渲染被整体裁剪(DEBUG_RENDERER_IN_* 均关),
|
||||
// 收集器退化为空实现——所有消费方照常编译,零绘制零开销
|
||||
namespace Engine {
|
||||
|
||||
class PhysicsDebugDraw {
|
||||
public:
|
||||
PhysicsDebugFrame& Frame() { return m_Frame; }
|
||||
void BeginCollect() { m_Frame.Clear(); }
|
||||
void NextFrame() {}
|
||||
|
||||
private:
|
||||
PhysicsDebugFrame m_Frame;
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif // JPH_DEBUG_RENDERER
|
||||
|
||||
#endif //HELLOVULKAN_PHYSICSDEBUGDRAW_H
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// PhysicsDebugTypes:物理调试可视化的数据契约(Jolt 零依赖)。
|
||||
//
|
||||
// PhysicsDebugDraw(收集器,Physics 层)生产、PhysicsDebugPass(渲染)
|
||||
// 消费;渲染侧只 include 本头文件,不接触任何 Jolt 类型——分层约束
|
||||
// 与 RigidBodyComponent 的"零 Jolt 依赖"一致。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_PHYSICSDEBUGTYPES_H
|
||||
#define HELLOVULKAN_PHYSICSDEBUGTYPES_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
// 图元顶点(28B):世界空间位置 + 线性 HDR 颜色(可 >1,过 tonemap
|
||||
// 依旧醒目)。线段/三角形共用同一顶点格式,各走一条动态 VB
|
||||
struct PhysicsDebugVertex {
|
||||
glm::vec3 pos;
|
||||
glm::vec4 color;
|
||||
};
|
||||
|
||||
// 单帧收集结果:PhysicsWorld 每个 Step 调用尾按 debugBits 重新收集
|
||||
//(渲染帧率,收集间隔内数据保持稳定,多渲染帧复用同一份图元)
|
||||
struct PhysicsDebugFrame {
|
||||
std::vector<PhysicsDebugVertex> lines; // 线段顶点(成对)
|
||||
std::vector<PhysicsDebugVertex> triangles; // 三角形顶点(三张一副)
|
||||
|
||||
void Clear() { lines.clear(); triangles.clear(); }
|
||||
bool Empty() const { return lines.empty() && triangles.empty(); }
|
||||
};
|
||||
|
||||
// 可视化位掩码(ImGui 经 PhysicsWorld 门面读写;0 = 全关)
|
||||
enum PhysicsDebugBits : u32 {
|
||||
kPhyDbgColliders = 1 << 0, // collider 线框(Jolt BodyManager::Draw)
|
||||
kPhyDbgConstraints = 1 << 1, // 约束(关节/软体约束,接入后生效)
|
||||
kPhyDbgGravity = 1 << 2, // 重力箭头(手绘,Jolt 无现成 API)
|
||||
kPhyDbgVelocity = 1 << 3, // 速度箭头(Jolt DrawSettings.mDrawVelocity)
|
||||
kPhyDbgAccel = 1 << 4, // 加速度箭头(手绘,速度差分/定步长)
|
||||
kPhyDbgDefault = kPhyDbgColliders | kPhyDbgGravity |
|
||||
kPhyDbgVelocity | kPhyDbgAccel,
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_PHYSICSDEBUGTYPES_H
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// PhysicsEvents:碰撞事件数据契约(Jolt 零依赖)。
|
||||
//
|
||||
// PhysicsWorld 内部的 ContactListenerImpl 在步进中收集、帧尾入队;
|
||||
// 消费方(App/游戏逻辑)只 include 本头文件,不接触任何 Jolt 类型
|
||||
// ——分层约束与 RigidBodyComponent 的"零 Jolt 依赖"一致。
|
||||
//
|
||||
// 事件语义 = 最近一次物理步进(Step 开头清空队列,消费方每帧读取)。
|
||||
// a/b 存实体位模式(entt::to_integral 的裸值),消费方用
|
||||
// static_cast<entt::entity>(bits) 还原,并自行 registry.valid() 校验
|
||||
// ——实体可能在事件产生后、消费前被删除。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_PHYSICSEVENTS_H
|
||||
#define HELLOVULKAN_PHYSICSEVENTS_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
struct CollisionEvent {
|
||||
enum class Kind : u8 { Added = 0, Persisted = 1, Removed = 2 };
|
||||
|
||||
u32 a = 0; // 实体位模式(Body1;Jolt 保证 BodyID 1 < 2)
|
||||
u32 b = 0; // 实体位模式(Body2)
|
||||
Kind kind = Kind::Added;
|
||||
glm::vec3 point{0.0f}; // 世界空间首个接触点(Removed 时无效)
|
||||
glm::vec3 normal{0.0f}; // 世界空间法线(body2 沿此方向推出;Removed 时无效)
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_PHYSICSEVENTS_H
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// PhysicsRay:射线查询数据契约(Jolt 零依赖)。
|
||||
//
|
||||
// PhysicsWorld::RayCast(BodyInterface/NarrowPhaseQuery 门面)生产;
|
||||
// 消费方(游戏查询/编辑器拾取)只 include 本头文件,不接触任何 Jolt
|
||||
// 类型——分层约束与 PhysicsEvents.h 一致。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_PHYSICSRAY_H
|
||||
#define HELLOVULKAN_PHYSICSRAY_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
// 射线命中结果(最近命中单结果;编辑器拾取/点击选择够用,
|
||||
// 穿透列表走 CastRayCollector 扩展,本契约保持最小)
|
||||
struct RayHit {
|
||||
u32 entityBits = 0; // 命中实体的位模式(消费方自行 valid 校验)
|
||||
glm::vec3 point{0.0f}; // 世界空间命中点
|
||||
glm::vec3 normal{0.0f}; // 世界空间表面法线(命中子形状)
|
||||
f32 distance = 0.0f; // 命中距离(origin 起算)
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_PHYSICSRAY_H
|
||||
@@ -0,0 +1,140 @@
|
||||
//
|
||||
// PhysicsWorld:物理世界门面(Jolt 后端)。
|
||||
//
|
||||
// 镜像 SceneRenderer 的单向数据流:
|
||||
// SyncScene(读 ECS 对账 body)→ PhysicsSystem::Update → Writeback(动态体 → Transform 标脏)
|
||||
// 定步长 accumulator(1/60,最多 4 子步,积压丢弃防螺旋死亡)。
|
||||
//
|
||||
// 分层约束:
|
||||
// - 组件层(Components.h)零 Jolt 依赖:bodyId 存 JPH::BodyID 裸位模式
|
||||
// - App/编辑器只经 Application::GetPhysics() 访问开关,不接触 Jolt 头
|
||||
//
|
||||
// 可扩展路线(本门面是唯一入口,feature 各自分文件演进,禁止互相耦合):
|
||||
// 软体/布料 → Jolt SoftBodySharedSettings/SoftBodyCreationSettings(原生支持),
|
||||
// 扩展为 SyncSoftBodies/WritebackSoft(网格引用走 AssetRef)
|
||||
// 角色控制器 → CharacterVirtual(运动学扫掠,非刚体,独立 Controller 类持有)
|
||||
// 载具控制器 → VehicleConstraint(挂动态刚体 chassis 上,独立 Controller 类)
|
||||
// 碰撞事件 → ContactListenerImpl(本类嵌套,已实现)+ GetEvents 帧尾队列
|
||||
// 射线查询 → BodyInterface::RayCast 门面(编辑器拾取/游戏查询)
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_PHYSICSWORLD_H
|
||||
#define HELLOVULKAN_PHYSICSWORLD_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/Physics/PhysicsDebugDraw.h>
|
||||
#include <Engine/Physics/PhysicsEvents.h>
|
||||
#include <Engine/Physics/PhysicsRay.h>
|
||||
|
||||
#include <Jolt/Jolt.h>
|
||||
#include <Jolt/Physics/PhysicsSystem.h>
|
||||
#include <Jolt/Physics/Body/BodyID.h>
|
||||
#include <Jolt/Core/JobSystemThreadPool.h>
|
||||
#include <Jolt/Core/TempAllocator.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
namespace ECS { class Scene; struct RigidBodyComponent; struct TransformComponent; }
|
||||
namespace Assets { class AssetManager; }
|
||||
|
||||
class PhysicsWorld {
|
||||
public:
|
||||
// assets:Mesh 碰撞体解析 modelRef(MeshShape/ConvexHull 数据源)
|
||||
explicit PhysicsWorld(ECS::Scene& scene, Assets::AssetManager* assets);
|
||||
~PhysicsWorld();
|
||||
|
||||
PhysicsWorld(const PhysicsWorld&) = delete;
|
||||
PhysicsWorld& operator=(const PhysicsWorld&) = delete;
|
||||
|
||||
// 帧循环调用(OnUpdate 之前,Unity FixedUpdate→Update 语义)
|
||||
void Step(f32 dt);
|
||||
|
||||
void SetEnabled(bool enabled) { m_Enabled = enabled; }
|
||||
bool IsEnabled() const { return m_Enabled; }
|
||||
|
||||
// ---- 物理调试可视化(ImGui 开关;位掩码见 PhysicsDebugBits)----
|
||||
// Step 尾部按位收集图元(渲染帧率),Renderer 每帧上传绘制
|
||||
void SetDebugDrawBits(u32 bits) { m_DebugBits = bits; }
|
||||
u32 GetDebugDrawBits() const { return m_DebugBits; }
|
||||
PhysicsDebugFrame& GetDebugFrame() { return m_DebugDraw->Frame(); }
|
||||
|
||||
// ---- 碰撞事件(语义 = 最近一次步进;Step 开头清空)----
|
||||
// 主线程在 Step 之外读取,与多线程回调互不重叠,无需加锁
|
||||
const std::vector<CollisionEvent>& GetEvents() const {
|
||||
return m_PendingEvents;
|
||||
}
|
||||
|
||||
// ---- 射线查询(游戏查询/编辑器拾取;主线程调用,暂停时也可用)----
|
||||
// dir 会被内部归一化;maxDist 限制射程。命中返回 true 并填 outHit
|
||||
bool RayCast(const glm::vec3& origin, const glm::vec3& dir,
|
||||
f32 maxDist, RayHit& outHit) const;
|
||||
|
||||
private:
|
||||
// 创建时缓存的参数(变更检测 → 重建 body)
|
||||
struct BodyRecord {
|
||||
JPH::BodyID id{};
|
||||
u8 type = 0; // RigidBodyComponent::BodyType
|
||||
u8 collider = 0; // RigidBodyComponent::ColliderType
|
||||
glm::vec3 colliderSize{0.0f}; // 碰撞体尺寸缓存(box 半尺寸)
|
||||
f32 radius = 0.0f; // 球半径缓存
|
||||
f32 mass = 0.0f;
|
||||
bool isSensor = false; // 传感器标记(变更 → 重建 body)
|
||||
glm::vec3 lastPos{0.0f}; // 静态/运动体编辑器拖动检测
|
||||
glm::vec3 lastRot{0.0f};
|
||||
// 加速度箭头用:上一步速度(定步长差分)与平滑后的加速度
|
||||
glm::vec3 prevVel{0.0f};
|
||||
glm::vec3 lastAccel{0.0f};
|
||||
};
|
||||
|
||||
void SyncScene(); // 对账:孤儿清理 / 创建 / 参数同步
|
||||
void Writeback() const; // 动态体 → TransformComponent(标脏)
|
||||
void UpdateAccels(f32 dt); // 速度差分 → BodyRecord.lastAccel(step 率)
|
||||
void CollectDebugData(); // 按 m_DebugBits 收集调试图元(渲染帧率)
|
||||
void CreateBody(u32 entityBits, ECS::RigidBodyComponent& rb,
|
||||
ECS::TransformComponent& tr);
|
||||
// Mesh 碰撞体:实体视觉网格 → Jolt 形状(Static=MeshShape /
|
||||
// Dynamic=ConvexHull)。无 MeshRenderer 或构建失败返回 null(回退 Box)
|
||||
JPH::RefConst<JPH::Shape> BuildMeshShape(u32 entityBits,
|
||||
ECS::RigidBodyComponent& rb,
|
||||
ECS::TransformComponent& tr);
|
||||
void RemoveBody(u32 entityBits);
|
||||
void QueueEvent(CollisionEvent&& ev); // 回调线程 append(多线程并发,加锁)
|
||||
bool TryBitsByBodyId(const JPH::BodyID& id, u32& outBits) const;
|
||||
|
||||
ECS::Scene& m_Scene;
|
||||
Assets::AssetManager* m_Assets = nullptr; // Mesh 碰撞体解析 modelRef
|
||||
bool m_Enabled = true;
|
||||
f32 m_Accumulator = 0.0f;
|
||||
|
||||
// 层配置实现类在 cpp;接口必须比 PhysicsSystem 活得久
|
||||
Scope<JPH::BroadPhaseLayerInterface> m_BPLayerInterface;
|
||||
Scope<JPH::ObjectVsBroadPhaseLayerFilter> m_ObjectVsBPLayerFilter;
|
||||
Scope<JPH::ObjectLayerPairFilter> m_ObjectVsObjectLayerFilter;
|
||||
|
||||
Scope<JPH::TempAllocator> m_TempAllocator;
|
||||
Scope<JPH::JobSystem> m_JobSystem;
|
||||
Scope<JPH::PhysicsSystem> m_System;
|
||||
|
||||
std::unordered_map<u32, BodyRecord> m_Bodies; // entity 位模式 → body
|
||||
|
||||
// ---- 碰撞事件收集(多线程回调入队,主线程消费)----
|
||||
class ContactListenerImpl; // cpp 定义(JPH::ContactListener 派生)
|
||||
Scope<ContactListenerImpl> m_ContactListener;
|
||||
std::mutex m_EventMutex; // 保护 m_PendingEvents(回调并发)
|
||||
std::vector<CollisionEvent> m_PendingEvents; // 最近一次步进的事件
|
||||
|
||||
// ---- 物理调试可视化 ----
|
||||
// 堆上延迟构造:DebugRendererSimple 构造即预烘焙通用几何(走 JPH
|
||||
// 分配器),必须等 ctor 内 call_once 注册完分配器,见 PhysicsWorld.cpp
|
||||
Scope<PhysicsDebugDraw> m_DebugDraw; // Jolt 全局调试渲染后端(收集器)
|
||||
u32 m_DebugBits = 0; // PhysicsDebugBits(默认关,StatsPanel 开关)
|
||||
};
|
||||
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_PHYSICSWORLD_H
|
||||
@@ -67,7 +67,16 @@ namespace Engine
|
||||
};
|
||||
const Bounds& GetBounds() const { return m_Bounds; }
|
||||
|
||||
// ---- CPU 位置副本(物理 MeshShape/ConvexHull 数据源)----
|
||||
// GPU 上传后完整 Vertex 即弃,但物理碰撞仍需三角形:构造时抽
|
||||
// 位置-only(12B/顶点,约为完整 Vertex 的 1/5)+ LOD0 索引。
|
||||
const std::vector<glm::vec3>& GetCpuPositions() const { return m_CpuPositions; }
|
||||
const std::vector<u32>& GetCpuIndices() const { return m_CpuIndices; }
|
||||
|
||||
private:
|
||||
// 物理碰撞数据源(MeshShape 三角网格 / ConvexHull 点云)
|
||||
std::vector<glm::vec3> m_CpuPositions;
|
||||
std::vector<u32> m_CpuIndices; // LOD0(原始精度)
|
||||
// 同一 index buffer 内连续排布的各级索引区间
|
||||
struct LodRange {
|
||||
u32 firstIndex = 0;
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// PhysicsDebugPass:物理调试图元叠加(collider 线框/约束/重力/速度/
|
||||
// 加速度箭头)。
|
||||
//
|
||||
// 画在 HDRColor(Forward 之后、TAA 之前):调试图元进入场景主链路——
|
||||
// TAA 时域累积抗锯齿、Bloom/Tonemap 正常消费,kDebugTransparent 诊断
|
||||
// 模式下也可见。深度只测不写(被不透明几何正确遮挡),无混合直写
|
||||
// (fullscreen pass 状态泄漏教训)。
|
||||
//
|
||||
// 数据源 = PhysicsWorld 每帧收集的 PhysicsDebugFrame(Application 把
|
||||
// PhysicsWorld 的帧缓冲指针注入 Renderer)。空帧零绘制;debug 开关
|
||||
// 全关时 PhysicsWorld 不收集 → 顶点数为 0 → 本 pass 自然零开销。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_PHYSICSDEBUGPASS_H
|
||||
#define HELLOVULKAN_PHYSICSDEBUGPASS_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/Renderer/Passes/RenderPass.h>
|
||||
#include <Engine/Renderer/SceneTypes.h>
|
||||
#include <Engine/Vulkan/UniformBuffer.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace Engine {
|
||||
struct PhysicsDebugFrame; // PhysicsDebugTypes.h(Jolt 零依赖契约)
|
||||
|
||||
class Device;
|
||||
class ShaderManager;
|
||||
class DescriptorManager;
|
||||
|
||||
namespace Passes {
|
||||
|
||||
class PhysicsDebugPass final : public RenderPass {
|
||||
public:
|
||||
// 顶点预算(每帧槽位,按顶点数):物理调试几十个 body 的
|
||||
// 线框 + 箭头远用不满;溢出截断并告警一次
|
||||
static constexpr u32 kMaxLineVerts = 1 << 16;
|
||||
static constexpr u32 kMaxTriVerts = 1 << 16;
|
||||
|
||||
PhysicsDebugPass(Device& device, MemoryAllocator& allocator,
|
||||
ShaderManager& shaders,
|
||||
DescriptorManager& descriptors,
|
||||
FrameSceneData& frame,
|
||||
RG::TextureHandle hdrColor,
|
||||
RG::TextureHandle depth,
|
||||
UniformBuffer& lineVB, UniformBuffer& triVB);
|
||||
~PhysicsDebugPass();
|
||||
|
||||
PhysicsDebugPass(const PhysicsDebugPass&) = delete;
|
||||
PhysicsDebugPass& operator=(const PhysicsDebugPass&) = delete;
|
||||
|
||||
void Register(RG::RenderGraph& rg) override;
|
||||
|
||||
// 每帧 Renderer::Render 里(该槽位 fence 等待后)上传当帧图元
|
||||
void PrepareFrame(u32 frameSlot, const PhysicsDebugFrame& src);
|
||||
|
||||
private:
|
||||
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_TriVB;
|
||||
|
||||
vk::PipelineLayout m_Layout; // 无描述符,push = viewProj 64B
|
||||
vk::ShaderEXT m_Vert{}, m_Frag{};
|
||||
|
||||
std::array<u32, kMaxFramesInFlight> m_LineCounts{};
|
||||
std::array<u32, kMaxFramesInFlight> m_TriCounts{};
|
||||
bool m_TruncationWarned = false;
|
||||
};
|
||||
|
||||
} // namespace Passes
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_PHYSICSDEBUGPASS_H
|
||||
Reference in New Issue
Block a user