feat(physics): add runtime physics scaffolding

This commit is contained in:
2026-04-15 11:58:27 +08:00
parent d17ddffdef
commit 3317e47009
31 changed files with 2120 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
#pragma once
#include <XCEngine/Components/ColliderComponent.h>
namespace XCEngine {
namespace Components {
class BoxColliderComponent : public ColliderComponent {
public:
std::string GetName() const override { return "BoxCollider"; }
const Math::Vector3& GetSize() const { return m_size; }
void SetSize(const Math::Vector3& value);
void Serialize(std::ostream& os) const override;
void Deserialize(std::istream& is) override;
private:
Math::Vector3 m_size = Math::Vector3::One();
};
} // namespace Components
} // namespace XCEngine

View File

@@ -0,0 +1,31 @@
#pragma once
#include <XCEngine/Components/ColliderComponent.h>
namespace XCEngine {
namespace Components {
class CapsuleColliderComponent : public ColliderComponent {
public:
std::string GetName() const override { return "CapsuleCollider"; }
float GetRadius() const { return m_radius; }
void SetRadius(float value);
float GetHeight() const { return m_height; }
void SetHeight(float value);
ColliderAxis GetAxis() const { return m_axis; }
void SetAxis(ColliderAxis value);
void Serialize(std::ostream& os) const override;
void Deserialize(std::istream& is) override;
private:
float m_radius = 0.5f;
float m_height = 2.0f;
ColliderAxis m_axis = ColliderAxis::Y;
};
} // namespace Components
} // namespace XCEngine

View File

@@ -0,0 +1,46 @@
#pragma once
#include <XCEngine/Components/Component.h>
#include <XCEngine/Core/Math/Vector3.h>
#include <cstdint>
namespace XCEngine {
namespace Components {
enum class ColliderAxis : uint8_t {
X = 0,
Y = 1,
Z = 2
};
class ColliderComponent : public Component {
public:
bool IsTrigger() const { return m_isTrigger; }
void SetTrigger(bool value) { m_isTrigger = value; }
const Math::Vector3& GetCenter() const { return m_center; }
void SetCenter(const Math::Vector3& value);
float GetStaticFriction() const { return m_staticFriction; }
void SetStaticFriction(float value);
float GetDynamicFriction() const { return m_dynamicFriction; }
void SetDynamicFriction(float value);
float GetRestitution() const { return m_restitution; }
void SetRestitution(float value);
protected:
void SerializeBase(std::ostream& os) const;
bool DeserializeBaseField(const std::string& key, const std::string& value);
private:
bool m_isTrigger = false;
Math::Vector3 m_center = Math::Vector3::Zero();
float m_staticFriction = 0.6f;
float m_dynamicFriction = 0.6f;
float m_restitution = 0.0f;
};
} // namespace Components
} // namespace XCEngine

View File

@@ -53,6 +53,7 @@ public:
component->m_gameObject = this;
T* ptr = component.get();
m_components.emplace_back(std::move(component));
NotifyComponentAdded(ptr);
return ptr;
}
@@ -120,6 +121,7 @@ public:
for (auto it = m_components.begin(); it != m_components.end(); ++it) {
if (T* casted = dynamic_cast<T*>(it->get())) {
NotifyComponentRemoving(casted);
m_components.erase(it);
return;
}
@@ -139,6 +141,7 @@ public:
return false;
}
NotifyComponentRemoving(component);
m_components.erase(it);
return true;
}
@@ -213,6 +216,8 @@ public:
void Deserialize(std::istream& is);
private:
void NotifyComponentAdded(Component* component);
void NotifyComponentRemoving(Component* component);
void NotifyComponentsBecameActive();
void NotifyComponentsBecameInactive();
void PropagateActiveInHierarchyChange(bool oldParentActiveInHierarchy, bool newParentActiveInHierarchy);

View File

@@ -0,0 +1,44 @@
#pragma once
#include <XCEngine/Components/Component.h>
#include <XCEngine/Physics/PhysicsTypes.h>
namespace XCEngine {
namespace Components {
class RigidbodyComponent : public Component {
public:
std::string GetName() const override { return "Rigidbody"; }
Physics::PhysicsBodyType GetBodyType() const { return m_bodyType; }
void SetBodyType(Physics::PhysicsBodyType value) { m_bodyType = value; }
float GetMass() const { return m_mass; }
void SetMass(float value);
float GetLinearDamping() const { return m_linearDamping; }
void SetLinearDamping(float value);
float GetAngularDamping() const { return m_angularDamping; }
void SetAngularDamping(float value);
bool GetUseGravity() const { return m_useGravity; }
void SetUseGravity(bool value) { m_useGravity = value; }
bool GetEnableCCD() const { return m_enableCCD; }
void SetEnableCCD(bool value) { m_enableCCD = value; }
void Serialize(std::ostream& os) const override;
void Deserialize(std::istream& is) override;
private:
Physics::PhysicsBodyType m_bodyType = Physics::PhysicsBodyType::Dynamic;
float m_mass = 1.0f;
float m_linearDamping = 0.0f;
float m_angularDamping = 0.05f;
bool m_useGravity = true;
bool m_enableCCD = false;
};
} // namespace Components
} // namespace XCEngine

View File

@@ -0,0 +1,23 @@
#pragma once
#include <XCEngine/Components/ColliderComponent.h>
namespace XCEngine {
namespace Components {
class SphereColliderComponent : public ColliderComponent {
public:
std::string GetName() const override { return "SphereCollider"; }
float GetRadius() const { return m_radius; }
void SetRadius(float value);
void Serialize(std::ostream& os) const override;
void Deserialize(std::istream& is) override;
private:
float m_radius = 0.5f;
};
} // namespace Components
} // namespace XCEngine

View File

@@ -0,0 +1,28 @@
#pragma once
#include <XCEngine/Core/Math/Vector3.h>
#include <cstdint>
namespace XCEngine {
namespace Components {
class GameObject;
class Scene;
} // namespace Components
namespace Physics {
enum class PhysicsBodyType : uint8_t {
Static = 0,
Dynamic,
Kinematic
};
struct PhysicsWorldCreateInfo {
Components::Scene* scene = nullptr;
Math::Vector3 gravity = Math::Vector3(0.0f, -9.81f, 0.0f);
};
} // namespace Physics
} // namespace XCEngine

View File

@@ -0,0 +1,56 @@
#pragma once
#include <XCEngine/Physics/PhysicsTypes.h>
#include <cstddef>
#include <cstdint>
#include <memory>
namespace XCEngine {
namespace Components {
class Component;
} // namespace Components
} // namespace XCEngine
namespace XCEngine {
namespace Physics {
class PhysXWorldBackend;
class PhysicsWorld {
public:
PhysicsWorld();
~PhysicsWorld();
static bool IsPhysXAvailable();
bool Initialize(const PhysicsWorldCreateInfo& createInfo);
void Shutdown();
void Step(float fixedDeltaTime);
bool IsInitialized() const { return m_initialized; }
const PhysicsWorldCreateInfo& GetCreateInfo() const { return m_createInfo; }
size_t GetTrackedRigidbodyCount() const { return m_trackedRigidbodyCount; }
size_t GetTrackedColliderCount() const { return m_trackedColliderCount; }
private:
void AttachSceneEventHandlers(Components::Scene* scene);
void DetachSceneEventHandlers();
void RebuildTrackedSceneState();
void TrackGameObjectComponents(Components::GameObject* gameObject, int delta);
void TrackComponent(Components::Component* component, int delta);
PhysicsWorldCreateInfo m_createInfo;
bool m_initialized = false;
uint64_t m_componentAddedSubscriptionId = 0;
uint64_t m_componentRemovedSubscriptionId = 0;
uint64_t m_gameObjectDestroyedSubscriptionId = 0;
size_t m_trackedRigidbodyCount = 0;
size_t m_trackedColliderCount = 0;
std::unique_ptr<PhysXWorldBackend> m_backend;
};
} // namespace Physics
} // namespace XCEngine

View File

@@ -0,0 +1,17 @@
#pragma once
#include <XCEngine/Physics/PhysicsTypes.h>
namespace XCEngine {
namespace Physics {
struct RaycastHit {
Components::GameObject* gameObject = nullptr;
Math::Vector3 point = Math::Vector3::Zero();
Math::Vector3 normal = Math::Vector3::Zero();
float distance = 0.0f;
bool isTrigger = false;
};
} // namespace Physics
} // namespace XCEngine

View File

@@ -71,6 +71,8 @@ public:
Core::Event<GameObject*>& OnGameObjectCreated() { return m_onGameObjectCreated; }
Core::Event<GameObject*>& OnGameObjectDestroyed() { return m_onGameObjectDestroyed; }
Core::Event<GameObject*, Component*>& OnComponentAdded() { return m_onComponentAdded; }
Core::Event<GameObject*, Component*>& OnComponentRemoved() { return m_onComponentRemoved; }
private:
GameObject* FindInChildren(GameObject* parent, const std::string& name) const;
@@ -108,6 +110,8 @@ private:
Core::Event<GameObject*> m_onGameObjectCreated;
Core::Event<GameObject*> m_onGameObjectDestroyed;
Core::Event<GameObject*, Component*> m_onComponentAdded;
Core::Event<GameObject*, Component*> m_onComponentRemoved;
friend class GameObject;
friend class SceneManager;

View File

@@ -16,6 +16,12 @@ struct UISystemFrameResult;
} // namespace Runtime
} // namespace UI
namespace Physics {
class PhysicsWorld;
} // namespace Physics
} // namespace XCEngine
namespace XCEngine {
@@ -50,9 +56,14 @@ public:
bool IsRunning() const { return m_running; }
Scene* GetScene() const { return m_scene; }
Physics::PhysicsWorld* GetPhysicsWorld() const { return m_physicsWorld.get(); }
private:
void CreatePhysicsWorldForScene(Scene* scene);
void DestroyPhysicsWorld();
std::unique_ptr<UI::Runtime::UISceneRuntimeContext> m_uiRuntime;
std::unique_ptr<Physics::PhysicsWorld> m_physicsWorld;
Scene* m_scene = nullptr;
bool m_running = false;
};