add basic ah physicsworld
This commit is contained in:
@@ -10,6 +10,9 @@ set(SRC_FILES
|
||||
"src/Components/Spinner.cpp"
|
||||
"src/Components/OrbitAndSpin.cpp"
|
||||
|
||||
"src/Components/Physics/Rigidbody.cpp"
|
||||
"src/Components/Physics/BoxCollider.cpp"
|
||||
|
||||
"src/Graphics/BindlessSetManager.cpp"
|
||||
"src/Graphics/Camera.cpp"
|
||||
"src/Graphics/ComputePipeline.cpp"
|
||||
@@ -45,11 +48,15 @@ set(SRC_FILES
|
||||
"src/Scene/Scene.cpp"
|
||||
"src/Scene/SceneManager.cpp"
|
||||
|
||||
|
||||
"src/FS/AssetFS.cpp"
|
||||
"src/FS/Manifest.cpp"
|
||||
|
||||
"src/Util/DeltaTime.cpp"
|
||||
|
||||
"src/Physics/PhysicsWorld.cpp"
|
||||
"src/Physics/SimplePhysicsWorld.cpp"
|
||||
"src/Physics/PhysicsSceneBridge.cpp"
|
||||
src/Components/Physics/BoxCollider.cpp
|
||||
)
|
||||
|
||||
add_library(destrum ${SRC_FILES})
|
||||
|
||||
Binary file not shown.
@@ -34,6 +34,7 @@ public:
|
||||
virtual void customUpdate(float dt) = 0;
|
||||
virtual void customDraw() = 0;
|
||||
virtual void customCleanup() = 0;
|
||||
virtual void customFixedUpdate(float dt) = 0;
|
||||
|
||||
virtual void onWindowResize(int newWidth, int newHeight) {};
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef BOXCOLLIDER_H
|
||||
#define BOXCOLLIDER_H
|
||||
|
||||
#include <destrum/Components/Physics/Collider.h>
|
||||
|
||||
class BoxCollider final : public Collider {
|
||||
public:
|
||||
explicit BoxCollider(GameObject& owner, const glm::vec3& halfExtents = glm::vec3{0.5f})
|
||||
: Collider(owner)
|
||||
, m_HalfExtents(halfExtents) {}
|
||||
|
||||
void Update() override {}
|
||||
|
||||
|
||||
[[nodiscard]] PhysicsShapeDesc BuildPhysicsShape() const override {
|
||||
return PhysicsShapeDesc::Box(m_HalfExtents, m_CenterOffset, m_IsTrigger);
|
||||
}
|
||||
|
||||
[[nodiscard]] const glm::vec3& GetHalfExtents() const {
|
||||
return m_HalfExtents;
|
||||
}
|
||||
|
||||
void SetHalfExtents(const glm::vec3& halfExtents) {
|
||||
m_HalfExtents = halfExtents;
|
||||
}
|
||||
|
||||
private:
|
||||
glm::vec3 m_HalfExtents{0.5f};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <destrum/Components/Collider.h>
|
||||
|
||||
class CapsuleCollider final : public Collider {
|
||||
public:
|
||||
explicit CapsuleCollider(GameObject& owner, float radius = 0.5f, float height = 2.0f)
|
||||
: Collider(owner)
|
||||
, m_Radius(radius)
|
||||
, m_Height(height) {}
|
||||
|
||||
[[nodiscard]] PhysicsShapeDesc BuildPhysicsShape() const override {
|
||||
return PhysicsShapeDesc::Capsule(m_Radius, m_Height, m_CenterOffset, m_IsTrigger);
|
||||
}
|
||||
|
||||
[[nodiscard]] float GetRadius() const {
|
||||
return m_Radius;
|
||||
}
|
||||
|
||||
void SetRadius(float radius) {
|
||||
m_Radius = std::max(radius, 0.0f);
|
||||
}
|
||||
|
||||
[[nodiscard]] float GetHeight() const {
|
||||
return m_Height;
|
||||
}
|
||||
|
||||
void SetHeight(float height) {
|
||||
m_Height = std::max(height, m_Radius * 2.0f);
|
||||
}
|
||||
|
||||
private:
|
||||
float m_Radius{0.5f};
|
||||
float m_Height{2.0f};
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <destrum/ObjectModel/Component.h>
|
||||
#include <destrum/Physics/PhysicsTypes.h>
|
||||
|
||||
class Collider : public Component {
|
||||
public:
|
||||
explicit Collider(GameObject& owner)
|
||||
: Component(owner) {}
|
||||
|
||||
~Collider() override = default;
|
||||
|
||||
[[nodiscard]] virtual PhysicsShapeDesc BuildPhysicsShape() const = 0;
|
||||
|
||||
[[nodiscard]] bool IsTrigger() const { return m_IsTrigger; }
|
||||
void SetTrigger(bool trigger) { m_IsTrigger = trigger; }
|
||||
|
||||
[[nodiscard]] const glm::vec3& GetCenterOffset() const { return m_CenterOffset; }
|
||||
void SetCenterOffset(const glm::vec3& offset) { m_CenterOffset = offset; }
|
||||
|
||||
protected:
|
||||
bool m_IsTrigger{false};
|
||||
glm::vec3 m_CenterOffset{0.0f};
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <destrum/ObjectModel/Component.h>
|
||||
#include <destrum/Physics/PhysicsTypes.h>
|
||||
|
||||
class PhysicsWorld;
|
||||
|
||||
class Rigidbody final : public Component {
|
||||
public:
|
||||
explicit Rigidbody(GameObject& owner)
|
||||
: Component(owner) {}
|
||||
|
||||
~Rigidbody() override = default;
|
||||
|
||||
void Update() override {}
|
||||
|
||||
void AttachPhysicsBody(PhysicsWorld* world, PhysicsBodyHandle body);
|
||||
void DetachPhysicsBody();
|
||||
|
||||
[[nodiscard]] PhysicsBodyHandle GetBody() const { return m_Body; }
|
||||
[[nodiscard]] bool HasPhysicsBody() const { return m_Body.IsValid() && m_World != nullptr; }
|
||||
|
||||
void AddForce(const glm::vec3& force);
|
||||
void AddImpulse(const glm::vec3& impulse);
|
||||
|
||||
void SetLinearVelocity(const glm::vec3& velocity);
|
||||
[[nodiscard]] glm::vec3 GetLinearVelocity() const;
|
||||
|
||||
[[nodiscard]] RigidbodyType GetType() const { return m_Type; }
|
||||
void SetType(RigidbodyType type) { m_Type = type; }
|
||||
|
||||
[[nodiscard]] float GetMass() const { return m_Mass; }
|
||||
void SetMass(float mass) { m_Mass = mass > 0.0f ? mass : 0.0001f; }
|
||||
|
||||
[[nodiscard]] float GetFriction() const { return m_Friction; }
|
||||
void SetFriction(float friction) { m_Friction = friction; }
|
||||
|
||||
[[nodiscard]] float GetRestitution() const { return m_Restitution; }
|
||||
void SetRestitution(float restitution) { m_Restitution = restitution; }
|
||||
|
||||
[[nodiscard]] bool UsesGravity() const { return m_UseGravity; }
|
||||
void SetUseGravity(bool useGravity) { m_UseGravity = useGravity; }
|
||||
|
||||
[[nodiscard]] bool AllowsSleep() const { return m_AllowSleep; }
|
||||
void SetAllowSleep(bool allowSleep) { m_AllowSleep = allowSleep; }
|
||||
|
||||
[[nodiscard]] PhysicsWorld* GetPhysicsWorld() const { return m_World; }
|
||||
|
||||
private:
|
||||
PhysicsWorld* m_World{nullptr};
|
||||
PhysicsBodyHandle m_Body{};
|
||||
|
||||
RigidbodyType m_Type{RigidbodyType::Dynamic};
|
||||
|
||||
float m_Mass{1.0f};
|
||||
float m_Friction{0.5f};
|
||||
float m_Restitution{0.0f};
|
||||
|
||||
bool m_UseGravity{true};
|
||||
bool m_AllowSleep{true};
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <destrum/Components/Collider.h>
|
||||
|
||||
class SphereCollider final : public Collider {
|
||||
public:
|
||||
explicit SphereCollider(GameObject& owner, float radius = 0.5f)
|
||||
: Collider(owner)
|
||||
, m_Radius(radius) {}
|
||||
|
||||
[[nodiscard]] PhysicsShapeDesc BuildPhysicsShape() const override {
|
||||
return PhysicsShapeDesc::Sphere(m_Radius, m_CenterOffset, m_IsTrigger);
|
||||
}
|
||||
|
||||
[[nodiscard]] float GetRadius() const {
|
||||
return m_Radius;
|
||||
}
|
||||
|
||||
void SetRadius(float radius) {
|
||||
m_Radius = std::max(radius, 0.0f);
|
||||
}
|
||||
|
||||
private:
|
||||
float m_Radius{0.5f};
|
||||
};
|
||||
@@ -47,13 +47,14 @@ public:
|
||||
GameObject& operator=(const GameObject& other) = delete;
|
||||
GameObject& operator=(GameObject&& other) = delete;
|
||||
|
||||
template <typename Component, typename... Args>
|
||||
requires std::constructible_from<Component, GameObject&, Args...>
|
||||
Component *AddComponent(Args&&... args) {
|
||||
template <typename TComponent, typename... Args>
|
||||
requires std::derived_from<TComponent, Component> &&
|
||||
std::constructible_from<TComponent, GameObject&, Args&&...>
|
||||
TComponent* AddComponent(Args&&... args) {
|
||||
auto& addedComponent = m_Components.emplace_back(
|
||||
std::make_unique<Component>(*this, std::forward<Args>(args)...));
|
||||
std::make_unique<TComponent>(*this, std::forward<Args>(args)...));
|
||||
|
||||
return reinterpret_cast<Component*>(addedComponent.get());
|
||||
return static_cast<TComponent*>(addedComponent.get());
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <destrum/Physics/PhysicsWorld.h>
|
||||
#include <destrum/Physics/SimplePhysicsWorld.h>
|
||||
|
||||
class GameObject;
|
||||
|
||||
// Small helper you can store inside Scene.
|
||||
//
|
||||
// Example:
|
||||
// class Scene {
|
||||
// public:
|
||||
// PhysicsSceneBridge& GetPhysics() { return m_Physics; }
|
||||
// private:
|
||||
// PhysicsSceneBridge m_Physics{std::make_unique<SimplePhysicsWorld>()};
|
||||
// };
|
||||
class PhysicsSceneBridge final {
|
||||
public:
|
||||
explicit PhysicsSceneBridge(std::unique_ptr<PhysicsWorld> world);
|
||||
|
||||
[[nodiscard]] PhysicsWorld& GetWorld() { return *m_World; }
|
||||
[[nodiscard]] const PhysicsWorld& GetWorld() const { return *m_World; }
|
||||
|
||||
void RegisterGameObject(GameObject& object);
|
||||
void UnregisterGameObject(GameObject& object);
|
||||
|
||||
void FixedUpdate(float fixedDt);
|
||||
|
||||
private:
|
||||
std::unique_ptr<PhysicsWorld> m_World;
|
||||
};
|
||||
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
class GameObject;
|
||||
|
||||
enum class RigidbodyType {
|
||||
Static,
|
||||
Dynamic,
|
||||
Kinematic
|
||||
};
|
||||
|
||||
enum class PhysicsShapeType {
|
||||
None,
|
||||
Box,
|
||||
Sphere,
|
||||
Capsule
|
||||
};
|
||||
|
||||
struct PhysicsBodyHandle {
|
||||
std::uint32_t id{std::numeric_limits<std::uint32_t>::max()};
|
||||
|
||||
[[nodiscard]] bool IsValid() const {
|
||||
return id != std::numeric_limits<std::uint32_t>::max();
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
id = std::numeric_limits<std::uint32_t>::max();
|
||||
}
|
||||
|
||||
friend bool operator==(const PhysicsBodyHandle& a, const PhysicsBodyHandle& b) {
|
||||
return a.id == b.id;
|
||||
}
|
||||
|
||||
friend bool operator!=(const PhysicsBodyHandle& a, const PhysicsBodyHandle& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
struct PhysicsTransform {
|
||||
glm::vec3 position{0.0f};
|
||||
glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f};
|
||||
};
|
||||
|
||||
struct PhysicsMaterialDesc {
|
||||
float friction{0.5f};
|
||||
float restitution{0.0f};
|
||||
};
|
||||
|
||||
struct PhysicsShapeDesc {
|
||||
PhysicsShapeType type{PhysicsShapeType::None};
|
||||
|
||||
// Box
|
||||
glm::vec3 halfExtents{0.5f};
|
||||
|
||||
// Sphere / capsule
|
||||
float radius{0.5f};
|
||||
|
||||
// Capsule full height, including the two hemispheres.
|
||||
float height{2.0f};
|
||||
|
||||
// Local offset from the rigidbody transform.
|
||||
glm::vec3 centerOffset{0.0f};
|
||||
|
||||
bool isTrigger{false};
|
||||
|
||||
[[nodiscard]] static PhysicsShapeDesc Box(const glm::vec3& halfExtents,
|
||||
const glm::vec3& centerOffset = glm::vec3{0.0f},
|
||||
bool isTrigger = false) {
|
||||
PhysicsShapeDesc desc{};
|
||||
desc.type = PhysicsShapeType::Box;
|
||||
desc.halfExtents = halfExtents;
|
||||
desc.centerOffset = centerOffset;
|
||||
desc.isTrigger = isTrigger;
|
||||
return desc;
|
||||
}
|
||||
|
||||
[[nodiscard]] static PhysicsShapeDesc Sphere(float radius,
|
||||
const glm::vec3& centerOffset = glm::vec3{0.0f},
|
||||
bool isTrigger = false) {
|
||||
PhysicsShapeDesc desc{};
|
||||
desc.type = PhysicsShapeType::Sphere;
|
||||
desc.radius = radius;
|
||||
desc.centerOffset = centerOffset;
|
||||
desc.isTrigger = isTrigger;
|
||||
return desc;
|
||||
}
|
||||
|
||||
[[nodiscard]] static PhysicsShapeDesc Capsule(float radius,
|
||||
float height,
|
||||
const glm::vec3& centerOffset = glm::vec3{0.0f},
|
||||
bool isTrigger = false) {
|
||||
PhysicsShapeDesc desc{};
|
||||
desc.type = PhysicsShapeType::Capsule;
|
||||
desc.radius = radius;
|
||||
desc.height = height;
|
||||
desc.centerOffset = centerOffset;
|
||||
desc.isTrigger = isTrigger;
|
||||
return desc;
|
||||
}
|
||||
};
|
||||
|
||||
struct PhysicsBodyDesc {
|
||||
GameObject* owner{nullptr};
|
||||
|
||||
PhysicsTransform transform{};
|
||||
PhysicsShapeDesc shape{};
|
||||
PhysicsMaterialDesc material{};
|
||||
|
||||
RigidbodyType type{RigidbodyType::Dynamic};
|
||||
|
||||
float mass{1.0f};
|
||||
bool useGravity{true};
|
||||
bool allowSleep{true};
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <destrum/Physics/PhysicsTypes.h>
|
||||
#include <destrum/Components/Physics/Rigidbody.h>
|
||||
|
||||
class GameObject;
|
||||
|
||||
struct PhysicsRaycastHit {
|
||||
GameObject* object{nullptr};
|
||||
PhysicsBodyHandle body{};
|
||||
glm::vec3 point{0.0f};
|
||||
glm::vec3 normal{0.0f, 1.0f, 0.0f};
|
||||
float distance{0.0f};
|
||||
};
|
||||
|
||||
class PhysicsWorld {
|
||||
public:
|
||||
virtual ~PhysicsWorld() = default;
|
||||
|
||||
void RegisterRigidbody(Rigidbody& rigidbody);
|
||||
void UnregisterRigidbody(Rigidbody& rigidbody);
|
||||
|
||||
virtual void Step(float fixedDt) = 0;
|
||||
|
||||
// - Kinematic: Transform -> physics body before Step()
|
||||
// - Dynamic: physics body -> Transform after Step()
|
||||
void SyncKinematicBodiesToPhysics();
|
||||
void SyncDynamicBodiesToTransforms();
|
||||
|
||||
virtual PhysicsBodyHandle CreateBody(const PhysicsBodyDesc& desc) = 0;
|
||||
virtual void DestroyBody(PhysicsBodyHandle body) = 0;
|
||||
|
||||
virtual void SetBodyTransform(PhysicsBodyHandle body, const PhysicsTransform& transform) = 0;
|
||||
virtual PhysicsTransform GetBodyTransform(PhysicsBodyHandle body) const = 0;
|
||||
|
||||
virtual void SetLinearVelocity(PhysicsBodyHandle body, const glm::vec3& velocity) = 0;
|
||||
virtual glm::vec3 GetLinearVelocity(PhysicsBodyHandle body) const = 0;
|
||||
|
||||
virtual void AddForce(PhysicsBodyHandle body, const glm::vec3& force) = 0;
|
||||
virtual void AddImpulse(PhysicsBodyHandle body, const glm::vec3& impulse) = 0;
|
||||
|
||||
virtual bool Raycast(const glm::vec3& origin,
|
||||
const glm::vec3& direction,
|
||||
float maxDistance,
|
||||
PhysicsRaycastHit& hit) const = 0;
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include <destrum/Physics/PhysicsWorld.h>
|
||||
|
||||
class Rigidbody;
|
||||
|
||||
// Simple CPU placeholder backend.
|
||||
//
|
||||
// This is NOT a replacement for Jolt/PhysX/Bullet.
|
||||
// It exists so your component/scene/render sync can be implemented and tested
|
||||
// before the real backend is added.
|
||||
//
|
||||
// It supports:
|
||||
// - static, dynamic, kinematic bodies
|
||||
// - gravity
|
||||
// - force/impulse integration
|
||||
// - very simple sphere/box/capsule raycasts using bounding spheres
|
||||
//
|
||||
// It does NOT support:
|
||||
// - collision response
|
||||
// - contacts
|
||||
// - friction
|
||||
// - constraints
|
||||
// - real character controllers
|
||||
class SimplePhysicsWorld final : public PhysicsWorld {
|
||||
public:
|
||||
explicit SimplePhysicsWorld(const glm::vec3& gravity = glm::vec3{0.0f, -9.81f, 0.0f});
|
||||
|
||||
void Step(float fixedDt) override;
|
||||
|
||||
PhysicsBodyHandle CreateBody(const PhysicsBodyDesc& desc) override;
|
||||
void DestroyBody(PhysicsBodyHandle body) override;
|
||||
|
||||
void SetBodyTransform(PhysicsBodyHandle body, const PhysicsTransform& transform) override;
|
||||
PhysicsTransform GetBodyTransform(PhysicsBodyHandle body) const override;
|
||||
|
||||
void SetLinearVelocity(PhysicsBodyHandle body, const glm::vec3& velocity) override;
|
||||
glm::vec3 GetLinearVelocity(PhysicsBodyHandle body) const override;
|
||||
|
||||
void AddForce(PhysicsBodyHandle body, const glm::vec3& force) override;
|
||||
void AddImpulse(PhysicsBodyHandle body, const glm::vec3& impulse) override;
|
||||
|
||||
bool Raycast(const glm::vec3& origin,
|
||||
const glm::vec3& direction,
|
||||
float maxDistance,
|
||||
PhysicsRaycastHit& hit) const override;
|
||||
|
||||
void SyncKinematicBodiesToPhysics();
|
||||
void SyncDynamicBodiesToTransforms();
|
||||
|
||||
[[nodiscard]] const glm::vec3& GetGravity() const { return m_Gravity; }
|
||||
void SetGravity(const glm::vec3& gravity) { m_Gravity = gravity; }
|
||||
|
||||
private:
|
||||
struct BodyRecord {
|
||||
bool alive{false};
|
||||
|
||||
PhysicsBodyHandle handle{};
|
||||
PhysicsBodyDesc desc{};
|
||||
|
||||
PhysicsTransform previousTransform{};
|
||||
PhysicsTransform currentTransform{};
|
||||
|
||||
glm::vec3 linearVelocity{0.0f};
|
||||
glm::vec3 accumulatedForce{0.0f};
|
||||
|
||||
Rigidbody* rigidbody{nullptr};
|
||||
};
|
||||
|
||||
[[nodiscard]] BodyRecord* FindBody(PhysicsBodyHandle body);
|
||||
[[nodiscard]] const BodyRecord* FindBody(PhysicsBodyHandle body) const;
|
||||
|
||||
[[nodiscard]] float GetApproxBoundingRadius(const BodyRecord& body) const;
|
||||
[[nodiscard]] static bool RaySphere(const glm::vec3& origin,
|
||||
const glm::vec3& dirNormalized,
|
||||
const glm::vec3& center,
|
||||
float radius,
|
||||
float maxDistance,
|
||||
float& outDistance);
|
||||
|
||||
glm::vec3 m_Gravity{0.0f, -9.81f, 0.0f};
|
||||
|
||||
std::vector<BodyRecord> m_Bodies;
|
||||
std::uint32_t m_NextId{0};
|
||||
};
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <destrum/Event.h>
|
||||
#include <destrum/Scene/SceneManager.h>
|
||||
|
||||
#include "destrum/Physics/PhysicsSceneBridge.h"
|
||||
|
||||
class GameObject;
|
||||
|
||||
class Scene final {
|
||||
@@ -19,7 +21,7 @@ public:
|
||||
void Load();
|
||||
|
||||
void Update();
|
||||
void FixedUpdate();
|
||||
void FixedUpdate(float dt);
|
||||
void LateUpdate();
|
||||
void Render(const RenderContext& ctx) const;
|
||||
void RenderImgui();
|
||||
@@ -62,9 +64,13 @@ public:
|
||||
|
||||
Event<> OnSceneLoaded;
|
||||
|
||||
PhysicsSceneBridge& GetPhysics() { return m_Physics; }
|
||||
private:
|
||||
explicit Scene(const std::string& name);
|
||||
|
||||
PhysicsSceneBridge m_Physics{std::make_unique<SimplePhysicsWorld>()};
|
||||
|
||||
|
||||
std::string m_name;
|
||||
std::vector<std::shared_ptr<GameObject>> m_objects{};
|
||||
std::vector<std::shared_ptr<GameObject>> m_pendingAdditions{};
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
Scene& GetCurrentScene() const { return *m_scenes[m_ActiveSceneIndex]; }
|
||||
|
||||
void Update();
|
||||
void FixedUpdate();
|
||||
void FixedUpdate(float dt);
|
||||
void LateUpdate();
|
||||
|
||||
void Render(const RenderContext& ctx);
|
||||
|
||||
+53
-35
@@ -12,10 +12,12 @@
|
||||
|
||||
#include <Jolt/Jolt.h>
|
||||
|
||||
App::App() {
|
||||
App::App()
|
||||
{
|
||||
}
|
||||
|
||||
void App::init(const AppParams ¶ms) {
|
||||
void App::init(const AppParams& params)
|
||||
{
|
||||
m_params = params;
|
||||
|
||||
AssetFS::GetInstance().Init(params.exeDir);
|
||||
@@ -33,7 +35,8 @@ void App::init(const AppParams ¶ms) {
|
||||
SDL_WINDOW_VULKAN);
|
||||
|
||||
SDL_SetWindowResizable(window, SDL_TRUE);
|
||||
if (!window) {
|
||||
if (!window)
|
||||
{
|
||||
spdlog::error("Failed to create window. SDL Error: {}", SDL_GetError());
|
||||
std::exit(1);
|
||||
}
|
||||
@@ -48,7 +51,8 @@ void App::init(const AppParams ¶ms) {
|
||||
customInit();
|
||||
}
|
||||
|
||||
void App::run() {
|
||||
void App::run()
|
||||
{
|
||||
Time::GetInstance().Update(); // initialize delta timing
|
||||
|
||||
const float fixedDt = static_cast<float>(Time::GetInstance().FixedDeltaTime());
|
||||
@@ -56,14 +60,16 @@ void App::run() {
|
||||
float accumulator = 0.0f;
|
||||
|
||||
isRunning = true;
|
||||
while (isRunning) {
|
||||
while (isRunning)
|
||||
{
|
||||
// ---- Update timing ---
|
||||
Time::GetInstance().Update();
|
||||
float dt = static_cast<float>(Time::GetInstance().DeltaTime());
|
||||
|
||||
if (dt > 0.25f) dt = 0.25f;
|
||||
|
||||
if (dt > 0.0f) {
|
||||
if (dt > 0.0f)
|
||||
{
|
||||
float newFPS = 1.0f / dt;
|
||||
avgFPS = std::lerp(avgFPS, newFPS, 0.1f);
|
||||
}
|
||||
@@ -75,16 +81,21 @@ void App::run() {
|
||||
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
imguiPass.handleEvent(event);
|
||||
if (event.type == SDL_QUIT) {
|
||||
if (event.type == SDL_QUIT)
|
||||
{
|
||||
isRunning = false;
|
||||
break;
|
||||
}
|
||||
if (event.type == SDL_WINDOWEVENT) {
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_WINDOWEVENT_RESIZED: {
|
||||
if (event.type == SDL_WINDOWEVENT)
|
||||
{
|
||||
switch (event.window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
{
|
||||
resizePending = true;
|
||||
lastResizeTime = std::chrono::steady_clock::now();
|
||||
break;
|
||||
@@ -92,22 +103,24 @@ void App::run() {
|
||||
}
|
||||
}
|
||||
const bool mouseEvent =
|
||||
event.type == SDL_MOUSEBUTTONDOWN ||
|
||||
event.type == SDL_MOUSEBUTTONUP ||
|
||||
event.type == SDL_MOUSEMOTION ||
|
||||
event.type == SDL_MOUSEWHEEL;
|
||||
event.type == SDL_MOUSEBUTTONDOWN ||
|
||||
event.type == SDL_MOUSEBUTTONUP ||
|
||||
event.type == SDL_MOUSEMOTION ||
|
||||
event.type == SDL_MOUSEWHEEL;
|
||||
|
||||
const bool keyboardEvent =
|
||||
event.type == SDL_KEYDOWN ||
|
||||
event.type == SDL_KEYUP ||
|
||||
event.type == SDL_TEXTINPUT;
|
||||
event.type == SDL_KEYDOWN ||
|
||||
event.type == SDL_KEYUP ||
|
||||
event.type == SDL_TEXTINPUT;
|
||||
|
||||
const bool capturedByImgui =
|
||||
(mouseEvent && imguiPass.wantsMouse()) ||
|
||||
(keyboardEvent && imguiPass.wantsKeyboard());
|
||||
(mouseEvent && imguiPass.wantsMouse()) ||
|
||||
(keyboardEvent && imguiPass.wantsKeyboard());
|
||||
|
||||
if (!capturedByImgui) {
|
||||
if (InputManager::GetInstance().ProcessEvent(event)) {
|
||||
if (!capturedByImgui)
|
||||
{
|
||||
if (InputManager::GetInstance().ProcessEvent(event))
|
||||
{
|
||||
isRunning = false;
|
||||
}
|
||||
}
|
||||
@@ -123,13 +136,12 @@ void App::run() {
|
||||
|
||||
imguiPass.endFrame();
|
||||
int steps = 0;
|
||||
while (accumulator >= fixedDt && steps < maxSteps) {
|
||||
while (accumulator >= fixedDt && steps < maxSteps)
|
||||
{
|
||||
// physics.Update(fixedDt);
|
||||
|
||||
SDL_SetWindowTitle(
|
||||
window,
|
||||
fmt::format("{} - FPS: {:.2f}", m_params.windowTitle, avgFPS).c_str()
|
||||
);
|
||||
customFixedUpdate(fixedDt);
|
||||
// physics.Step(fixedDt);
|
||||
// physics.SyncTransforms();
|
||||
|
||||
accumulator -= fixedDt;
|
||||
steps++;
|
||||
@@ -138,11 +150,13 @@ void App::run() {
|
||||
|
||||
const float alpha = accumulator / fixedDt;
|
||||
|
||||
if (gfxDevice.needsSwapchainRecreate() || resizePending) {
|
||||
if (gfxDevice.needsSwapchainRecreate() || resizePending)
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (resizePending &&
|
||||
now - lastResizeTime < std::chrono::milliseconds(100)) {
|
||||
now - lastResizeTime < std::chrono::milliseconds(100))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -150,7 +164,8 @@ void App::run() {
|
||||
int h = 0;
|
||||
SDL_Vulkan_GetDrawableSize(window, &w, &h);
|
||||
|
||||
if (w == 0 || h == 0) {
|
||||
if (w == 0 || h == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -166,9 +181,11 @@ void App::run() {
|
||||
customDraw();
|
||||
|
||||
|
||||
if (frameLimit) {
|
||||
if (frameLimit)
|
||||
{
|
||||
auto sleepTime = Time::GetInstance().SleepDuration();
|
||||
if (sleepTime.count() > 0) {
|
||||
if (sleepTime.count() > 0)
|
||||
{
|
||||
std::this_thread::sleep_for(sleepTime);
|
||||
}
|
||||
}
|
||||
@@ -177,7 +194,8 @@ void App::run() {
|
||||
gfxDevice.waitIdle();
|
||||
}
|
||||
|
||||
void App::cleanup() {
|
||||
void App::cleanup()
|
||||
{
|
||||
spdlog::info("Cleaning up");
|
||||
customCleanup();
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
#include <destrum/Components/Physics/BoxCollider.h>
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <destrum/Components/Physics/Rigidbody.h>
|
||||
|
||||
#include <destrum/Physics/PhysicsWorld.h>
|
||||
|
||||
void Rigidbody::AttachPhysicsBody(PhysicsWorld* world, PhysicsBodyHandle body) {
|
||||
m_World = world;
|
||||
m_Body = body;
|
||||
}
|
||||
|
||||
void Rigidbody::DetachPhysicsBody() {
|
||||
m_World = nullptr;
|
||||
m_Body.Reset();
|
||||
}
|
||||
|
||||
void Rigidbody::AddForce(const glm::vec3& force) {
|
||||
if (!HasPhysicsBody()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_World->AddForce(m_Body, force);
|
||||
}
|
||||
|
||||
void Rigidbody::AddImpulse(const glm::vec3& impulse) {
|
||||
if (!HasPhysicsBody()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_World->AddImpulse(m_Body, impulse);
|
||||
}
|
||||
|
||||
void Rigidbody::SetLinearVelocity(const glm::vec3& velocity) {
|
||||
if (!HasPhysicsBody()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_World->SetLinearVelocity(m_Body, velocity);
|
||||
}
|
||||
|
||||
glm::vec3 Rigidbody::GetLinearVelocity() const {
|
||||
if (!HasPhysicsBody()) {
|
||||
return glm::vec3{0.0f};
|
||||
}
|
||||
|
||||
return m_World->GetLinearVelocity(m_Body);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <destrum/Physics/PhysicsSceneBridge.h>
|
||||
|
||||
#include <destrum/Components/Physics/Rigidbody.h>
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
|
||||
PhysicsSceneBridge::PhysicsSceneBridge(std::unique_ptr<PhysicsWorld> world)
|
||||
: m_World(std::move(world)) {
|
||||
}
|
||||
|
||||
void PhysicsSceneBridge::RegisterGameObject(GameObject& object) {
|
||||
if (auto* rb = object.GetComponent<Rigidbody>()) {
|
||||
if (!rb->HasPhysicsBody()) {
|
||||
m_World->RegisterRigidbody(*rb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsSceneBridge::UnregisterGameObject(GameObject& object) {
|
||||
if (auto* rb = object.GetComponent<Rigidbody>()) {
|
||||
if (rb->HasPhysicsBody()) {
|
||||
m_World->UnregisterRigidbody(*rb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsSceneBridge::FixedUpdate(float fixedDt) {
|
||||
if (auto* simple = dynamic_cast<SimplePhysicsWorld*>(m_World.get())) {
|
||||
simple->SyncKinematicBodiesToPhysics();
|
||||
simple->Step(fixedDt);
|
||||
simple->SyncDynamicBodiesToTransforms();
|
||||
return;
|
||||
}
|
||||
|
||||
m_World->Step(fixedDt);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#include <destrum/Physics/PhysicsWorld.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <destrum/Components/Physics/Rigidbody.h>
|
||||
#include <destrum/Components/Physics/Collider.h>
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/ObjectModel/Transform.h>
|
||||
#include <destrum/Components/Physics/Rigidbody.h>
|
||||
|
||||
void PhysicsWorld::RegisterRigidbody(Rigidbody& rigidbody) {
|
||||
GameObject* owner = rigidbody.GetGameObject();
|
||||
Transform& transform = owner->GetTransform();
|
||||
|
||||
auto* collider = owner->GetComponent<Collider>();
|
||||
if (!collider) {
|
||||
throw std::runtime_error("Rigidbody requires a Collider on the same GameObject for now.");
|
||||
}
|
||||
|
||||
PhysicsBodyDesc desc{};
|
||||
desc.owner = owner;
|
||||
desc.transform.position = transform.GetWorldPosition();
|
||||
desc.transform.rotation = transform.GetWorldRotation();
|
||||
desc.shape = collider->BuildPhysicsShape();
|
||||
|
||||
desc.type = rigidbody.GetType();
|
||||
desc.mass = rigidbody.GetMass();
|
||||
desc.useGravity = rigidbody.UsesGravity();
|
||||
desc.allowSleep = rigidbody.AllowsSleep();
|
||||
desc.material.friction = rigidbody.GetFriction();
|
||||
desc.material.restitution = rigidbody.GetRestitution();
|
||||
|
||||
PhysicsBodyHandle handle = CreateBody(desc);
|
||||
rigidbody.AttachPhysicsBody(this, handle);
|
||||
}
|
||||
|
||||
void PhysicsWorld::UnregisterRigidbody(Rigidbody& rigidbody) {
|
||||
PhysicsBodyHandle handle = rigidbody.GetBody();
|
||||
|
||||
if (handle.IsValid()) {
|
||||
DestroyBody(handle);
|
||||
}
|
||||
|
||||
rigidbody.DetachPhysicsBody();
|
||||
}
|
||||
|
||||
void PhysicsWorld::SyncKinematicBodiesToPhysics() {
|
||||
// Backend-independent sync is intentionally not possible here because
|
||||
// PhysicsWorld does not own the list of active rigidbodies.
|
||||
//
|
||||
// Use SimplePhysicsWorld as a reference implementation.
|
||||
//
|
||||
// If you use Jolt/PhysX/Bullet later, keep a body table in the backend:
|
||||
// handle -> { GameObject*, Rigidbody* }.
|
||||
}
|
||||
|
||||
void PhysicsWorld::SyncDynamicBodiesToTransforms() {
|
||||
// Backend-independent sync is intentionally not possible here because
|
||||
// PhysicsWorld does not own the list of active rigidbodies.
|
||||
//
|
||||
// Use SimplePhysicsWorld as a reference implementation.
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
#include <destrum/Physics/SimplePhysicsWorld.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include <destrum/Components/Physics/Rigidbody.h>
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/ObjectModel/Transform.h>
|
||||
|
||||
SimplePhysicsWorld::SimplePhysicsWorld(const glm::vec3& gravity)
|
||||
: m_Gravity(gravity) {
|
||||
}
|
||||
|
||||
PhysicsBodyHandle SimplePhysicsWorld::CreateBody(const PhysicsBodyDesc& desc) {
|
||||
BodyRecord record{};
|
||||
record.alive = true;
|
||||
record.handle = PhysicsBodyHandle{m_NextId++};
|
||||
record.desc = desc;
|
||||
record.previousTransform = desc.transform;
|
||||
record.currentTransform = desc.transform;
|
||||
|
||||
if (desc.owner) {
|
||||
record.rigidbody = desc.owner->GetComponent<Rigidbody>();
|
||||
}
|
||||
|
||||
m_Bodies.emplace_back(record);
|
||||
return record.handle;
|
||||
}
|
||||
|
||||
void SimplePhysicsWorld::DestroyBody(PhysicsBodyHandle body) {
|
||||
if (BodyRecord* record = FindBody(body)) {
|
||||
record->alive = false;
|
||||
record->rigidbody = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SimplePhysicsWorld::SetBodyTransform(PhysicsBodyHandle body, const PhysicsTransform& transform) {
|
||||
BodyRecord* record = FindBody(body);
|
||||
if (!record) {
|
||||
return;
|
||||
}
|
||||
|
||||
record->previousTransform = record->currentTransform;
|
||||
record->currentTransform = transform;
|
||||
}
|
||||
|
||||
PhysicsTransform SimplePhysicsWorld::GetBodyTransform(PhysicsBodyHandle body) const {
|
||||
const BodyRecord* record = FindBody(body);
|
||||
if (!record) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return record->currentTransform;
|
||||
}
|
||||
|
||||
void SimplePhysicsWorld::SetLinearVelocity(PhysicsBodyHandle body, const glm::vec3& velocity) {
|
||||
BodyRecord* record = FindBody(body);
|
||||
if (!record) {
|
||||
return;
|
||||
}
|
||||
|
||||
record->linearVelocity = velocity;
|
||||
}
|
||||
|
||||
glm::vec3 SimplePhysicsWorld::GetLinearVelocity(PhysicsBodyHandle body) const {
|
||||
const BodyRecord* record = FindBody(body);
|
||||
if (!record) {
|
||||
return glm::vec3{0.0f};
|
||||
}
|
||||
|
||||
return record->linearVelocity;
|
||||
}
|
||||
|
||||
void SimplePhysicsWorld::AddForce(PhysicsBodyHandle body, const glm::vec3& force) {
|
||||
BodyRecord* record = FindBody(body);
|
||||
if (!record || record->desc.type != RigidbodyType::Dynamic) {
|
||||
return;
|
||||
}
|
||||
|
||||
record->accumulatedForce += force;
|
||||
}
|
||||
|
||||
void SimplePhysicsWorld::AddImpulse(PhysicsBodyHandle body, const glm::vec3& impulse) {
|
||||
BodyRecord* record = FindBody(body);
|
||||
if (!record || record->desc.type != RigidbodyType::Dynamic) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float invMass = record->desc.mass > 0.0f ? 1.0f / record->desc.mass : 0.0f;
|
||||
record->linearVelocity += impulse * invMass;
|
||||
}
|
||||
|
||||
void SimplePhysicsWorld::Step(float fixedDt) {
|
||||
if (fixedDt <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (BodyRecord& body : m_Bodies) {
|
||||
if (!body.alive) {
|
||||
continue;
|
||||
}
|
||||
|
||||
body.previousTransform = body.currentTransform;
|
||||
|
||||
if (body.desc.type != RigidbodyType::Dynamic) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float invMass = body.desc.mass > 0.0f ? 1.0f / body.desc.mass : 0.0f;
|
||||
|
||||
glm::vec3 acceleration{0.0f};
|
||||
|
||||
if (body.desc.useGravity) {
|
||||
acceleration += m_Gravity;
|
||||
}
|
||||
|
||||
acceleration += body.accumulatedForce * invMass;
|
||||
|
||||
// Semi-implicit Euler.
|
||||
body.linearVelocity += acceleration * fixedDt;
|
||||
body.currentTransform.position += body.linearVelocity * fixedDt;
|
||||
|
||||
body.accumulatedForce = glm::vec3{0.0f};
|
||||
}
|
||||
|
||||
// Compact dead records occasionally.
|
||||
m_Bodies.erase(
|
||||
std::remove_if(m_Bodies.begin(), m_Bodies.end(),
|
||||
[](const BodyRecord& body) { return !body.alive; }),
|
||||
m_Bodies.end());
|
||||
}
|
||||
|
||||
void SimplePhysicsWorld::SyncKinematicBodiesToPhysics() {
|
||||
for (BodyRecord& body : m_Bodies) {
|
||||
if (!body.alive || body.desc.type != RigidbodyType::Kinematic || !body.desc.owner) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Transform& transform = body.desc.owner->GetTransform();
|
||||
|
||||
body.previousTransform = body.currentTransform;
|
||||
body.currentTransform.position = transform.GetWorldPosition();
|
||||
body.currentTransform.rotation = transform.GetWorldRotation();
|
||||
}
|
||||
}
|
||||
|
||||
void SimplePhysicsWorld::SyncDynamicBodiesToTransforms() {
|
||||
for (BodyRecord& body : m_Bodies) {
|
||||
if (!body.alive || body.desc.type != RigidbodyType::Dynamic || !body.desc.owner) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Transform& transform = body.desc.owner->GetTransform();
|
||||
transform.SetWorldPosition(body.currentTransform.position);
|
||||
transform.SetWorldRotation(body.currentTransform.rotation);
|
||||
}
|
||||
}
|
||||
|
||||
bool SimplePhysicsWorld::Raycast(const glm::vec3& origin,
|
||||
const glm::vec3& direction,
|
||||
float maxDistance,
|
||||
PhysicsRaycastHit& hit) const {
|
||||
if (maxDistance <= 0.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const float len = glm::length(direction);
|
||||
if (len <= 0.00001f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const glm::vec3 dir = direction / len;
|
||||
|
||||
bool found = false;
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
for (const BodyRecord& body : m_Bodies) {
|
||||
if (!body.alive || body.desc.shape.type == PhysicsShapeType::None) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float distance = 0.0f;
|
||||
const float radius = GetApproxBoundingRadius(body);
|
||||
const glm::vec3 center = body.currentTransform.position + body.desc.shape.centerOffset;
|
||||
|
||||
if (RaySphere(origin, dir, center, radius, maxDistance, distance)) {
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
found = true;
|
||||
|
||||
hit.object = body.desc.owner;
|
||||
hit.body = body.handle;
|
||||
hit.distance = distance;
|
||||
hit.point = origin + dir * distance;
|
||||
|
||||
const glm::vec3 n = hit.point - center;
|
||||
hit.normal = glm::length(n) > 0.00001f ? glm::normalize(n) : glm::vec3{0.0f, 1.0f, 0.0f};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
SimplePhysicsWorld::BodyRecord* SimplePhysicsWorld::FindBody(PhysicsBodyHandle body) {
|
||||
for (BodyRecord& record : m_Bodies) {
|
||||
if (record.alive && record.handle == body) {
|
||||
return &record;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const SimplePhysicsWorld::BodyRecord* SimplePhysicsWorld::FindBody(PhysicsBodyHandle body) const {
|
||||
for (const BodyRecord& record : m_Bodies) {
|
||||
if (record.alive && record.handle == body) {
|
||||
return &record;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
float SimplePhysicsWorld::GetApproxBoundingRadius(const BodyRecord& body) const {
|
||||
const PhysicsShapeDesc& shape = body.desc.shape;
|
||||
|
||||
switch (shape.type) {
|
||||
case PhysicsShapeType::Box:
|
||||
return glm::length(shape.halfExtents);
|
||||
|
||||
case PhysicsShapeType::Sphere:
|
||||
return shape.radius;
|
||||
|
||||
case PhysicsShapeType::Capsule:
|
||||
return shape.height * 0.5f;
|
||||
|
||||
case PhysicsShapeType::None:
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool SimplePhysicsWorld::RaySphere(const glm::vec3& origin,
|
||||
const glm::vec3& dirNormalized,
|
||||
const glm::vec3& center,
|
||||
float radius,
|
||||
float maxDistance,
|
||||
float& outDistance) {
|
||||
const glm::vec3 oc = origin - center;
|
||||
|
||||
const float a = glm::dot(dirNormalized, dirNormalized);
|
||||
const float b = 2.0f * glm::dot(oc, dirNormalized);
|
||||
const float c = glm::dot(oc, oc) - radius * radius;
|
||||
|
||||
const float discriminant = b * b - 4.0f * a * c;
|
||||
if (discriminant < 0.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const float sqrtDisc = std::sqrt(discriminant);
|
||||
const float t0 = (-b - sqrtDisc) / (2.0f * a);
|
||||
const float t1 = (-b + sqrtDisc) / (2.0f * a);
|
||||
|
||||
float t = t0;
|
||||
if (t < 0.0f) {
|
||||
t = t1;
|
||||
}
|
||||
|
||||
if (t < 0.0f || t > maxDistance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outDistance = t;
|
||||
return true;
|
||||
}
|
||||
@@ -57,12 +57,13 @@ void Scene::Update() {
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::FixedUpdate() {
|
||||
void Scene::FixedUpdate(float dt) {
|
||||
for (const auto& object: m_objects) {
|
||||
if (object->IsActiveInHierarchy()) {
|
||||
object->FixedUpdate();
|
||||
}
|
||||
}
|
||||
m_Physics.FixedUpdate(dt);
|
||||
}
|
||||
|
||||
void Scene::LateUpdate() {
|
||||
|
||||
@@ -8,8 +8,8 @@ void SceneManager::Update() {
|
||||
m_scenes[m_ActiveSceneIndex]->Update();
|
||||
}
|
||||
|
||||
void SceneManager::FixedUpdate() {
|
||||
m_scenes[m_ActiveSceneIndex]->FixedUpdate();
|
||||
void SceneManager::FixedUpdate(float dt) {
|
||||
m_scenes[m_ActiveSceneIndex]->FixedUpdate(dt);
|
||||
}
|
||||
|
||||
void SceneManager::LateUpdate() {
|
||||
|
||||
Reference in New Issue
Block a user