Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 73 additions & 72 deletions include/public/Core/Scene.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,84 +4,85 @@
#include "SceneBase.hpp"

namespace Sleak {
/// Concrete scene type games subclass. Adds an opt-out switch for the
/// fixed-timestep update on top of SceneBase's lifecycle.
///
/// This is the class your levels, menus, and screens derive from.
/// Override the hooks you need and call the base implementation from
/// each one: OnLoad() and OnUnload() for assets, OnActivate() and
/// OnDeactivate() for entering and leaving, Begin() to build the object
/// graph, and Update() / FixedUpdate() / LateUpdate() for per-frame
/// work.
///
/// The scene owns every object handed to AddObject(), including lights
/// and cameras, and destroys all of them on unload. It also owns a
/// LightManager and a Physics::PhysicsWorld, reachable through
/// GetLightManager() and GetPhysicsWorld(), and registers added objects
/// with both automatically.
///
/// Call SetFixedUpdateEnabled(false) on scenes that do not simulate
/// anything, such as menus, to skip the fixed-timestep pass entirely.
///
/// @code{.cpp}
/// class WorldScene : public Sleak::Scene {
/// public:
/// WorldScene() : Sleak::Scene("WorldScene") {}
///
/// void Begin() override {
/// auto* cube = Sleak::GameObject::CreateCube(
/// Sleak::Math::Vector3D(0.0f, 0.0f, 0.0f));
/// AddObject(cube);
///
/// auto* sun = new Sleak::DirectionalLight("Sun");
/// sun->SetDirection(Sleak::Math::Vector3D(-0.5f, -0.8f, -0.3f));
/// sun->SetIntensity(4.0f);
/// AddObject(sun);
///
/// auto* cam = new Sleak::Camera("MainCamera");
/// AddObject(cam);
/// SetActiveCamera(cam);
///
/// Sleak::Scene::Begin(); // activates everything added above
/// }
///
/// void Update(float deltaTime) override {
/// Sleak::Scene::Update(deltaTime);
/// }
/// };
/// @endcode
///
/// @see SceneBase, GameBase, GameObject, LightManager
/// @ingroup core
class ENGINE_API Scene : public SceneBase {
public:
explicit Scene(const std::string& name)
: SceneBase(name), bEnableFixedUpdate(true) {}
~Scene() override = default;
/// Concrete scene type games subclass. Adds an opt-out switch for the
/// fixed-timestep update on top of SceneBase's lifecycle.
///
/// This is the class your levels, menus, and screens derive from.
/// Override the hooks you need and call the base implementation from
/// each one: OnLoad() and OnUnload() for assets, OnActivate() and
/// OnDeactivate() for entering and leaving, Begin() to build the object
/// graph, and Update() / FixedUpdate() / LateUpdate() for per-frame
/// work.
///
/// The scene owns every object handed to AddObject(), including lights
/// and cameras, and destroys all of them on unload. It also owns a
/// LightManager and a Physics::PhysicsWorld, reachable through
/// GetLightManager() and GetPhysicsWorld(), and registers added objects
/// with both automatically.
///
/// Call SetFixedUpdateEnabled(false) on scenes that do not simulate
/// anything, such as menus, to skip the fixed-timestep pass entirely.
/// That pass is also where physics steps, so it stops physics too.
///
/// @code{.cpp}
/// class WorldScene : public Sleak::Scene {
/// public:
/// WorldScene() : Sleak::Scene("WorldScene") {}
///
/// void Begin() override {
/// auto* cube = Sleak::GameObject::CreateCube(
/// Sleak::Math::Vector3D(0.0f, 0.0f, 0.0f));
/// AddObject(cube);
///
/// auto* sun = new Sleak::DirectionalLight("Sun");
/// sun->SetDirection(Sleak::Math::Vector3D(-0.5f, -0.8f, -0.3f));
/// sun->SetIntensity(4.0f);
/// AddObject(sun);
///
/// auto* cam = new Sleak::Camera("MainCamera");
/// AddObject(cam);
/// SetActiveCamera(cam);
///
/// Sleak::Scene::Begin(); // activates everything added above
/// }
///
/// void Update(float deltaTime) override {
/// Sleak::Scene::Update(deltaTime);
/// }
/// };
/// @endcode
///
/// @see SceneBase, GameBase, GameObject, LightManager
/// @ingroup core
class ENGINE_API Scene : public SceneBase {
public:
explicit Scene(const std::string& name)
: SceneBase(name), bEnableFixedUpdate(true) {}
~Scene() override = default;

// Resource lifecycle
void OnLoad() override;
void OnUnload() override;
// Resource lifecycle
void OnLoad() override;
void OnUnload() override;

// Activation lifecycle
void OnActivate() override;
void OnDeactivate() override;
// Activation lifecycle
void OnActivate() override;
void OnDeactivate() override;

// Initialization
bool Initialize() override;
void Begin() override;
// Initialization
bool Initialize() override;
void Begin() override;

// Update loops
void Update(float deltaTime) override;
void FixedUpdate(float fixedDeltaTime) override;
void LateUpdate(float deltaTime) override;
// Update loops
void Update(float deltaTime) override;
void FixedUpdate(float fixedDeltaTime) override;
void LateUpdate(float deltaTime) override;

void SetFixedUpdateEnabled(bool enabled) { bEnableFixedUpdate = enabled; }
bool IsFixedUpdateEnabled() const { return bEnableFixedUpdate; }
void SetFixedUpdateEnabled(bool enabled) { bEnableFixedUpdate = enabled; }
bool IsFixedUpdateEnabled() const { return bEnableFixedUpdate; }

private:
bool bEnableFixedUpdate;
};
private:
bool bEnableFixedUpdate;
};
}

#endif // _SCENE_HPP_
29 changes: 27 additions & 2 deletions include/public/Core/SceneBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,11 @@ namespace Sleak {
virtual void Begin() = 0;

// Update loops
/// Advances all active, root-level objects by deltaTime, then steps lighting and physics.
/// Advances all active, root-level objects by deltaTime, then runs
/// as many FixedUpdate() steps as the accumulated time allows.
virtual void Update(float deltaTime) = 0;
/// Advances all active, root-level objects on the fixed timestep.
/// Advances all active, root-level objects on the fixed timestep, then
/// steps physics.
virtual void FixedUpdate(float fixedDeltaTime);
/// Advances all active, root-level objects after the main Update pass.
virtual void LateUpdate(float deltaTime);
Expand Down Expand Up @@ -140,6 +142,21 @@ namespace Sleak {
List<GameObject*> FindObjectsByTag(const std::string& tag);
size_t GetObjectCount() const { return Objects.GetSize(); }

/// Length of one FixedUpdate() step in seconds; defaults to 1/60.
float GetFixedTimestep() const { return m_fixedTimestep; }
void SetFixedTimestep(float seconds);
/// Most FixedUpdate() steps one frame may run; leftover time is
/// dropped.
int GetMaxFixedSteps() const { return m_maxFixedSteps; }
void SetMaxFixedSteps(int steps) {
m_maxFixedSteps = steps > 0 ? steps : 1;
}
/// How far the current frame sits between the last fixed step and
/// the next one, in [0, 1). Use it to interpolate rendered poses.
float GetFixedStepAlpha() const {
return static_cast<float>(m_fixedAccumulator / m_fixedTimestep);
}

Camera* GetActiveCamera() const { return m_activeCamera; }
void SetActiveCamera(Camera* cam) { m_activeCamera = cam; }

Expand Down Expand Up @@ -170,6 +187,14 @@ namespace Sleak {
Physics::PhysicsWorld* m_physicsWorld = nullptr;
Skybox* m_skybox = nullptr;

double m_fixedAccumulator = 0.0;
float m_fixedTimestep = 1.0f / 60.0f;
int m_maxFixedSteps = 5;

/// Adds deltaTime to the accumulator and runs the FixedUpdate() steps
/// it covers.
void RunFixedSteps(float deltaTime);

/// Actually deletes objects queued by DestroyObject().
void ProcessPendingDestroy();
/// Destroys every object still owned by the scene, e.g. during Unload().
Expand Down
10 changes: 7 additions & 3 deletions include/public/Core/Timer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ namespace Sleak {
/// Seconds since construction or the last Reset().
float Elapsed() const;

private:
std::chrono::time_point<std::chrono::high_resolution_clock> startPoint;
/// Same as Elapsed() in double precision, for subtracting two
/// readings that are hours into a session.
double ElapsedSeconds() const;

private:
std::chrono::time_point<std::chrono::steady_clock> startPoint;
};
};

#endif
#endif
20 changes: 11 additions & 9 deletions include/public/Physics/PhysicsWorld.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,15 @@ namespace Sleak {
float distance = 0.0f;
};

/// Owns the broadphase tree and every registered collider; drives collision detection and resolution each step.
/// Owns the broadphase tree and every registered collider; drives
/// collision detection and resolution each step.
///
/// Each Scene creates one PhysicsWorld and steps it from its
/// update, so you normally reach it with
/// SceneBase::GetPhysicsWorld() rather than constructing one. You
/// also rarely call RegisterCollider() by hand: adding a
/// GameObject that carries a ColliderComponent registers it (and
/// its children) automatically.
/// FixedUpdate() on the scene's fixed timestep, so you normally reach
/// it with SceneBase::GetPhysicsWorld() rather than constructing one.
/// You also rarely call RegisterCollider() by hand: adding a GameObject
/// that carries a ColliderComponent registers it (and its children)
/// automatically.
///
/// Step() integrates every dynamic RigidbodyComponent using that
/// body's own gravity setting, refreshes the DynamicAABBTree
Expand Down Expand Up @@ -88,9 +89,10 @@ namespace Sleak {
PhysicsWorld() = default;
~PhysicsWorld() = default;

/// Advances the simulation by dt: integrates each dynamic rigidbody's
/// velocity, including its own gravity setting, into position, then
/// updates the broadphase and resolves collisions.
/// Advances the simulation by dt: integrates each dynamic
/// rigidbody's velocity, including its own gravity setting, into
/// position, then updates the broadphase and resolves collisions.
/// Ignores dt <= 0.
void Step(float dt);

void RegisterCollider(ColliderComponent* collider);
Expand Down
24 changes: 8 additions & 16 deletions src/Core/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,6 @@ namespace Sleak {
int Application::Run(GameBase* game) {
Game = game;

float lastTime = FrameTimer.Elapsed();

if(CoreWindow && CoreWindow->InitializeWindow()) {

if(renderer && renderer->Initialize()) {
Expand All @@ -150,9 +148,7 @@ namespace Sleak {
auto context = renderer->GetContext();
auto queue = RenderEngine::RenderCommandQueue::GetInstance();

float lastTime = FrameTimer.Elapsed();
float accumulator = 0.0f;
const float fixedTimestep = 1.0f / 60.0f;
constexpr double kMaxDeltaTime = 0.25;

// Initialize and begin the game (and scene)
if (Game) {
Expand Down Expand Up @@ -184,10 +180,13 @@ namespace Sleak {
m_benchmark->ToggleRecording();
}

// Start after init so load time is not the first frame delta
double lastTime = FrameTimer.ElapsedSeconds();

while(!CoreWindow->ShouldClose()) {

float currentTime = FrameTimer.Elapsed();
DeltaTime = currentTime - lastTime;
double currentTime = FrameTimer.ElapsedSeconds();
DeltaTime = static_cast<float>(
std::clamp(currentTime - lastTime, 0.0, kMaxDeltaTime));
lastTime = currentTime;

#if defined(_DEBUG) && defined(COUNT_FRAME)
Expand Down Expand Up @@ -218,14 +217,7 @@ namespace Sleak {
if (Game && Game->GetActiveScene()) {
auto* activeScene = Game->GetActiveScene();

// Fixed timestep updates (physics, etc.)
accumulator += DeltaTime;
while (accumulator >= fixedTimestep) {
activeScene->FixedUpdate(fixedTimestep);
accumulator -= fixedTimestep;
}

// Per-frame update
// Per-frame update; the scene runs its fixed steps inside
activeScene->Update(DeltaTime);

// Late update (after all updates, e.g. camera follow)
Expand Down
15 changes: 8 additions & 7 deletions src/Core/Timer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ namespace Sleak {
Reset();
}

void Timer::Reset() {
startPoint = std::chrono::high_resolution_clock::now();
}
void Timer::Reset() { startPoint = std::chrono::steady_clock::now(); }

// Outputs elaosed time as seconds
float Timer::Elapsed() const {
auto now = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> elapsed = now - startPoint;
return static_cast<float>(ElapsedSeconds());
}

double Timer::ElapsedSeconds() const {
std::chrono::duration<double> elapsed =
std::chrono::steady_clock::now() - startPoint;
return elapsed.count();
}
};
}; // namespace Sleak
13 changes: 8 additions & 5 deletions src/Physics/PhysicsWorld.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#include <Physics/PhysicsWorld.hpp>
#include <Physics/ColliderComponent.hpp>
#include <Physics/RigidbodyComponent.hpp>
#include <Core/GameObject.hpp>
#include <Camera/Camera.hpp>
#include <ECS/Components/TransformComponent.hpp>
#include <Core/GameObject.hpp>
#include <Core/Logger.hpp>
#include <ECS/Components/TransformComponent.hpp>
#include <Physics/ColliderComponent.hpp>
#include <Physics/PhysicsWorld.hpp>
#include <Physics/RigidbodyComponent.hpp>
#include <algorithm>
#include <cmath>

namespace Sleak {
namespace Physics {
Expand Down Expand Up @@ -39,6 +40,8 @@ void PhysicsWorld::UnregisterCollider(ColliderComponent* collider) {
}

void PhysicsWorld::Step(float dt) {
if (!(dt > 0.0f) || !std::isfinite(dt)) return;

// We need wasGrounded BEFORE clearing, so gravity doesn't apply while standing
for (auto* collider : m_colliders) {
if (auto* owner = collider->GetOwner()) {
Expand Down
Loading
Loading