通用图元(线/箭头四翼头/球/胶囊/盒)瞬时+持久双缓冲聚合; 物理 pass 画刚体速度/重力/接触反力;角色 collider 与力学 线归位(引擎只画资产、脚本画行为);线宽按设备上限钳制
211 lines
11 KiB
C++
211 lines
11 KiB
C++
//
|
||
// 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 <Engine/Physics/CharacterController.h>
|
||
#include <Engine/ECS/Components.h> // BodyRecord 快照存 CompoundChild(完整类型)
|
||
|
||
#include <Jolt/Jolt.h>
|
||
#include <Jolt/Physics/PhysicsSystem.h>
|
||
#include <Jolt/Physics/Body/BodyID.h>
|
||
#include <Jolt/Physics/Constraints/Constraint.h> // JPH::Ref<Constraint>
|
||
#include <Jolt/Core/JobSystemThreadPool.h>
|
||
#include <Jolt/Core/TempAllocator.h>
|
||
|
||
#include <glm/glm.hpp>
|
||
#include <glm/gtc/quaternion.hpp>
|
||
#include <mutex>
|
||
#include <unordered_map>
|
||
#include <unordered_set>
|
||
#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;
|
||
|
||
// ---- 刚体驱动(游戏逻辑/编辑器调试;按实体位模式寻址)----
|
||
// Jolt 力在每步模拟后清零:游戏逻辑每帧持续施加 = 正确用法;
|
||
// 冲量一次性改变动量(自动唤醒休眠体);速度直写会打断自然
|
||
// 模拟(Unity 同语义)。body 不存在或类型不符时静默忽略:
|
||
// 力/扭矩/冲量仅动态体,速度直写动态+运动学体
|
||
void AddForce(u32 entityBits, const glm::vec3& force);
|
||
void AddImpulse(u32 entityBits, const glm::vec3& impulse);
|
||
void AddTorque(u32 entityBits, const glm::vec3& torque);
|
||
void SetLinearVelocity(u32 entityBits, const glm::vec3& velocity);
|
||
void SetAngularVelocity(u32 entityBits, const glm::vec3& angularVelocity);
|
||
// 无 body / 静态体返回零向量
|
||
glm::vec3 GetLinearVelocity(u32 entityBits) const;
|
||
|
||
// ---- 角色控制器(游戏逻辑门面;实体无 CharacterControllerComponent
|
||
// 或未就绪时静默忽略)----
|
||
// 速度直写喂意图(水平移动);垂直分量由 Update 自动积分重力
|
||
void SetCharacterVelocity(u32 entityBits, const glm::vec3& velocity);
|
||
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)
|
||
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; // 球/Capsule/Cylinder 半径(collider 决定语义)
|
||
f32 halfHeight = 0.0f; // Capsule/Cylinder 圆柱段半高
|
||
std::vector<ECS::RigidBodyComponent::CompoundChild> compound; // 子形状快照
|
||
f32 mass = 0.0f;
|
||
bool isTrigger = false; // 触发器标记(变更 → 重建 body)
|
||
glm::vec3 lastPos{0.0f}; // 静态/运动体编辑器拖动检测
|
||
glm::quat lastRot{1.0f, 0.0f, 0.0f, 0.0f}; // 同上(quat 直比,无欧拉往返)
|
||
// 加速度箭头用:上一步速度(定步长差分)与平滑后的加速度
|
||
glm::vec3 prevVel{0.0f};
|
||
glm::vec3 lastAccel{0.0f};
|
||
};
|
||
|
||
void SyncScene(); // 对账:孤儿清理 / 创建 / 参数同步
|
||
void Writeback(); // 动态体 → TransformComponent(标脏)+ lastPos/lastRot 对账基线
|
||
void UpdateAccels(f32 dt); // 速度差分 → BodyRecord.lastAccel(step 率)
|
||
void CollectDebugData(); // 按 m_DebugBits 收集调试图元(渲染帧率)
|
||
void CreateBody(u32 entityBits, ECS::RigidBodyComponent& rb,
|
||
ECS::TransformComponent& tr);
|
||
// 约束对账:owner 孤儿清理 / body 重建传染失效 / 建缺失与变更重建
|
||
// invalidated = 本帧 SyncScene 中被销毁或重建 body 的实体位模式
|
||
void SyncConstraints(const std::unordered_set<u32>& invalidated);
|
||
void CreateConstraint(u32 ownerBits, u32 targetBits,
|
||
const ECS::JointComponent& jc);
|
||
void RemoveConstraint(u32 ownerBits);
|
||
// 角色对账:孤儿清理 / 参数变更重建 / 编辑器拖动传送。
|
||
// 角色非刚体(不进 PhysicsSystem),无 body 清理负担
|
||
void SyncCharacters();
|
||
// 每物理步(PhysicsSystem::Update 之后,Jolt 官方建议序):
|
||
// 重力积分 + 扫掠 → 写回 TransformComponent(标脏)
|
||
void UpdateCharacters(f32 dt);
|
||
// Mesh 碰撞体:实体视觉网格 → Jolt 形状(Static=MeshShape /
|
||
// Dynamic=ConvexHull)。无 MeshRenderer 或构建失败返回 null(回退 Box)
|
||
JPH::RefConst<JPH::Shape> BuildMeshShape(u32 entityBits,
|
||
ECS::RigidBodyComponent& rb,
|
||
ECS::TransformComponent& tr);
|
||
// Compound 碰撞体:子形状列表 → Jolt StaticCompoundShape。
|
||
// 空 children 或构建失败返回 null(回退 Box)
|
||
JPH::RefConst<JPH::Shape> BuildCompoundShape(ECS::RigidBodyComponent& rb,
|
||
ECS::TransformComponent& tr);
|
||
void RemoveBody(u32 entityBits);
|
||
const BodyRecord* FindRecord(u32 entityBits) const; // 刚体驱动寻址
|
||
CharacterController* FindCharacter(u32 entityBits); // 角色门面寻址
|
||
void QueueEvent(CollisionEvent&& ev); // 回调线程 append(多线程并发,加锁)
|
||
bool TryBitsByBodyId(const JPH::BodyID& id, u32& outBits) const;
|
||
|
||
// ---- 约束(JointComponent → Jolt TwoBodyConstraint;owner 实体寻址)----
|
||
// target 惰性解析 UUID;body 重建/销毁 → 引用它的约束失效重建
|
||
struct ConstraintRecord {
|
||
JPH::Ref<JPH::Constraint> constraint;
|
||
ECS::JointComponent snapshot; // 变更检测 → 移除重建
|
||
u32 targetBits = 0;
|
||
};
|
||
std::unordered_map<u32, ConstraintRecord> m_Constraints;
|
||
|
||
// ---- 角色(entityBits → CharacterVirtual 封装)----
|
||
struct CharacterEntry {
|
||
std::unique_ptr<CharacterController> cc;
|
||
glm::vec3 lastPos{0.0f}; // 编辑器拖动检测
|
||
};
|
||
std::unordered_map<u32, CharacterEntry> m_Characters;
|
||
|
||
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
|
||
|
||
// BodyID 序号 → entity 位模式(O(1) 反查;ContactListener 回调用)
|
||
// 与 m_Bodies 同生共死:CreateBody 同插,RemoveBody/孤儿清理同 erase。
|
||
// 主线程在 SyncScene 写、Step 内 Update 阶段不写 → 回调线程只读,无需加锁
|
||
std::unordered_map<u32, u32> m_BodyToEntity;
|
||
|
||
// ---- 碰撞事件收集(多线程回调入队,主线程消费)----
|
||
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
|