Threaded ticks
TODO make movable objects use velocity so rendering still looks smooth when things move
This commit is contained in:
@@ -22,7 +22,7 @@ CPMAddPackage(
|
||||
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-26.zip
|
||||
)
|
||||
|
||||
#set(CMAKE_CXX_FLAGS "-O3 -Wall -Wextra")
|
||||
set(CMAKE_CXX_FLAGS "-O3 -Wall -Wextra")
|
||||
|
||||
file(GLOB_RECURSE HEADERS "include/*.h" "include/*.hpp")
|
||||
file(GLOB_RECURSE SOURCES "src/*.c" "src/*.cpp")
|
||||
|
@@ -10,8 +10,18 @@ namespace Globals {
|
||||
inline Scene* CurrentScene = nullptr;
|
||||
inline DemoGameWindow* Window = nullptr;
|
||||
|
||||
inline float DeltaTime() { return Window->GetDeltaTime(); }
|
||||
inline void ChangeScene(Scene* scene) { if (CurrentScene != nullptr) CurrentScene->Unload(); CurrentScene = scene; if (CurrentScene != nullptr) CurrentScene->Init(); }
|
||||
inline void ChangeScene(Scene* scene)
|
||||
{
|
||||
if (CurrentScene != nullptr)
|
||||
CurrentScene->Unload();
|
||||
|
||||
CurrentScene = scene;
|
||||
|
||||
if (CurrentScene != nullptr) {
|
||||
CurrentScene->Init();
|
||||
CurrentScene->Run();
|
||||
}
|
||||
}
|
||||
inline bool ChangeScene (const std::string& scene_name)
|
||||
{
|
||||
Scene* scene = nullptr;
|
||||
|
9
include/Engine/Logger.h
Normal file
9
include/Engine/Logger.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include <jlog/Logger.hpp>
|
||||
|
||||
namespace Engine::Logger {
|
||||
inline jlog::GenericLogger Info {"Engine::Info", jlog::GlobalLogFile, Colors::Gray, Colors::Gray, Colors::Gray, Colors::Gray};
|
||||
inline jlog::GenericLogger Fatal {"Engine::Fatal", jlog::GlobalLogFile, Colors::Reds::Crimson, Colors::Gray, Colors::Gray, Colors::Reds::Crimson, Colors::White};
|
||||
inline jlog::GenericLogger Debug {"Engine::Debug", jlog::GlobalLogFile, Colors::Purples::Purple, Colors::Gray, Colors::Gray, Colors::Purples::Purple, Colors::White};
|
||||
|
||||
}
|
24
include/Engine/types/ConcurrentWorker.h
Normal file
24
include/Engine/types/ConcurrentWorker.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <atomic>
|
||||
#include <sstream>
|
||||
|
||||
namespace Engine {
|
||||
class ConcurrentWorker;
|
||||
}
|
||||
|
||||
class Engine::ConcurrentWorker {
|
||||
private:
|
||||
std::atomic<bool> work_complete{ false };
|
||||
std::thread worker_thread;
|
||||
public:
|
||||
[[nodiscard]] bool Done();
|
||||
public:
|
||||
~ConcurrentWorker();
|
||||
|
||||
template <typename Function, typename... Args>
|
||||
explicit ConcurrentWorker(Function&& func, Args&&... args) : worker_thread([this, func = std::forward<Function>(func),
|
||||
...args = std::forward<Args>(args)]() mutable { func(std::forward<Args>(args) ...); work_complete = true; })
|
||||
{ worker_thread.detach(); }
|
||||
};
|
@@ -5,6 +5,9 @@
|
||||
#include <Engine/types/entity/Entity.h>
|
||||
#include <Engine/types/entity/Hud.h>
|
||||
#include <JGL/types/RenderTarget.h>
|
||||
#include <Engine/types/ConcurrentWorker.h>
|
||||
#include <rewindow/types/window.h>
|
||||
#include <thread>
|
||||
|
||||
namespace Engine {
|
||||
class Camera;
|
||||
@@ -12,13 +15,23 @@ namespace Engine {
|
||||
}
|
||||
|
||||
class Engine::Scene {
|
||||
private:
|
||||
bool destroying = false;
|
||||
bool last_rendered_to_window = false;
|
||||
float render_target_frame_delta = 0;
|
||||
protected:
|
||||
// 25ms.
|
||||
u8 tick_rate = 40;
|
||||
bool should_tick = true;
|
||||
ConcurrentWorker* update_worker;
|
||||
std::string name;
|
||||
Camera* active_camera = nullptr;
|
||||
Entity* entity_list = nullptr;
|
||||
|
||||
std::vector<InstancedTexture*> instanced_textures{};
|
||||
std::vector<InstancedTexture*> instanced_alpha_masks{};
|
||||
protected:
|
||||
virtual void Update();
|
||||
virtual void Render();
|
||||
public:
|
||||
[[nodiscard]] Entity* GetEntityList() { return entity_list; }
|
||||
[[nodiscard]] size_t EntityCount() const;
|
||||
@@ -26,16 +39,22 @@ public:
|
||||
[[nodiscard]] Camera* GetActiveCamera() const;
|
||||
[[nodiscard]] InstancedTexture* GetInstancedTexture(const InstancedSprite* user);
|
||||
[[nodiscard]] InstancedTexture* GetInstancedAlphaMask(const InstancedSprite* user);
|
||||
[[nodiscard]] bool ShouldTick() const;
|
||||
[[nodiscard]] float TickDeltaTime() const;
|
||||
[[nodiscard]] float FrameDeltaTime() const;
|
||||
public:
|
||||
void ShouldTick(bool state);
|
||||
void SetActiveCamera(Camera* camera) { active_camera = camera; };
|
||||
void DestroyInstancedTexture(const InstancedTexture* itx);
|
||||
void DestroyInstancedAlphaMask(const InstancedTexture* alpha_mask);
|
||||
|
||||
virtual void Unload();
|
||||
virtual void Init() {}
|
||||
virtual void Update();
|
||||
virtual void Render(JGL::RenderTarget* render_target = nullptr);
|
||||
virtual void Init() {};
|
||||
void Run();
|
||||
|
||||
void UpdateLoop();
|
||||
void Render(JGL::RenderTarget* render_target);
|
||||
void Render(ReWindow::RWindow* window);
|
||||
public:
|
||||
explicit Scene(const std::string& name) : name(name) { entity_list = new Entity(); }
|
||||
explicit Scene(const std::string& name, const u8 tick_rate = 40) : tick_rate(tick_rate), name(name), entity_list(new Entity()) {}
|
||||
virtual ~Scene();
|
||||
};
|
||||
|
@@ -11,7 +11,6 @@ public:
|
||||
explicit LoadingScreen(const std::string& name) : Scene(name) {}
|
||||
|
||||
void Init() final;
|
||||
void Update() final;
|
||||
void Render(JGL::RenderTarget* render_target = nullptr) final;
|
||||
void Render() final;
|
||||
~LoadingScreen() final;
|
||||
};
|
||||
|
@@ -17,11 +17,8 @@ void Engine::DemoGameWindow::Display() {
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
// These are checked separately so that if Update() changes the scene we don't crash.
|
||||
if (Globals::CurrentScene)
|
||||
Globals::CurrentScene->Update();
|
||||
if (Globals::CurrentScene)
|
||||
Globals::CurrentScene->Render();
|
||||
Globals::CurrentScene->Render(this);
|
||||
|
||||
Engine::DemoGameWindow::GLSwapBuffers();
|
||||
}
|
||||
|
14
src/Engine/types/ConcurrentWorker.cpp
Normal file
14
src/Engine/types/ConcurrentWorker.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include <Engine/types/ConcurrentWorker.h>
|
||||
#include <Engine/Logger.h>
|
||||
|
||||
using namespace Engine;
|
||||
ConcurrentWorker::~ConcurrentWorker() {
|
||||
if (!work_complete) {
|
||||
std::stringstream thread_id; thread_id << worker_thread.get_id();
|
||||
Logger::Fatal("ConcurrentWorker was destroyed for thread " + thread_id.str() + " but the work wasn't finished?");
|
||||
}
|
||||
}
|
||||
|
||||
bool ConcurrentWorker::Done() {
|
||||
return work_complete;
|
||||
}
|
@@ -1,8 +1,11 @@
|
||||
#include <Engine/Globals.h>
|
||||
#include <Engine/types/Scene.h>
|
||||
#include <Engine/types/entity/Camera.h>
|
||||
#include <Engine/types/entity/InstancedSprite.h>
|
||||
#include <Engine/Logger.h>
|
||||
#include <JGL/JGL.h>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
using namespace Engine;
|
||||
size_t Scene::EntityCount() const {
|
||||
@@ -10,45 +13,78 @@ size_t Scene::EntityCount() const {
|
||||
}
|
||||
|
||||
void Scene::Update() {
|
||||
auto target_tick_time = std::chrono::microseconds(1000000 / tick_rate);
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
entity_list->UpdateChildren();
|
||||
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
|
||||
|
||||
if (elapsed < target_tick_time)
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(target_tick_time - elapsed));
|
||||
}
|
||||
|
||||
void Scene::Render(RenderTarget* render_target) {
|
||||
// The Camera and the HUD are ignored here because they're special cases.
|
||||
void Scene::UpdateLoop() {
|
||||
while (!destroying)
|
||||
if (should_tick)
|
||||
Update();
|
||||
else {
|
||||
auto target_tick_time = std::chrono::microseconds(1000000 / tick_rate);
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(target_tick_time));
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::Render() {
|
||||
std::vector<Renderable*> display_list{};
|
||||
for (auto* e : Entity::GetAllDescendents(entity_list))
|
||||
if (auto* r = dynamic_cast<Renderable*>(e))
|
||||
for (auto *e: Entity::GetAllDescendents(entity_list))
|
||||
if (auto *r = dynamic_cast<Renderable *>(e))
|
||||
if (r->ShouldRender())
|
||||
display_list.push_back(r);
|
||||
|
||||
std::sort(display_list.begin(), display_list.end(), [](Renderable* a, Renderable* b) {
|
||||
std::sort(display_list.begin(), display_list.end(), [](Renderable *a, Renderable *b) {
|
||||
return a->GetLayer() > b->GetLayer();
|
||||
});
|
||||
|
||||
J2D::Begin( render_target, true);
|
||||
|
||||
if (active_camera)
|
||||
active_camera->Render();
|
||||
|
||||
for (auto& r : display_list)
|
||||
for (auto &r: display_list)
|
||||
if (r->ShouldRender())
|
||||
r->Render();
|
||||
|
||||
J2D::End();
|
||||
}
|
||||
|
||||
void Scene::Render(JGL::RenderTarget* render_target) {
|
||||
if (!render_target)
|
||||
Logger::Fatal("Rendering the current scene to a RenderTarget that is nullptr?");
|
||||
|
||||
J2D::Begin(render_target, true);
|
||||
Render();
|
||||
J2D::End();
|
||||
last_rendered_to_window = false;
|
||||
}
|
||||
|
||||
void Scene::Render(ReWindow::RWindow* window) {
|
||||
if (!window)
|
||||
Logger::Fatal("Rendering the current scene to a ReWindow that is nullptr?");
|
||||
|
||||
J2D::Begin(nullptr, true);
|
||||
Render();
|
||||
J2D::End();
|
||||
last_rendered_to_window = true;
|
||||
}
|
||||
|
||||
Scene::~Scene() {
|
||||
for (auto* itx : instanced_textures)
|
||||
delete itx;
|
||||
|
||||
for (auto* a : instanced_alpha_masks)
|
||||
delete a;
|
||||
|
||||
delete entity_list;;
|
||||
delete active_camera;
|
||||
Scene::Unload();
|
||||
}
|
||||
|
||||
void Scene::Unload() {
|
||||
destroying = true;
|
||||
while (!update_worker->Done()) {}
|
||||
delete update_worker;
|
||||
|
||||
for (auto* itx : instanced_textures)
|
||||
delete itx;
|
||||
|
||||
@@ -104,3 +140,25 @@ void Scene::DestroyInstancedAlphaMask(const Engine::InstancedTexture* alpha_mask
|
||||
delete *it, instanced_alpha_masks.erase(it);
|
||||
}
|
||||
|
||||
void Scene::Run() {
|
||||
update_worker = new ConcurrentWorker([this] { UpdateLoop(); });
|
||||
}
|
||||
|
||||
bool Scene::ShouldTick() const {
|
||||
return should_tick;
|
||||
}
|
||||
|
||||
void Scene::ShouldTick(bool state) {
|
||||
should_tick = state;
|
||||
}
|
||||
|
||||
float Scene::TickDeltaTime() const {
|
||||
return (1000000.f / tick_rate) / 1000000.f;
|
||||
}
|
||||
|
||||
float Scene::FrameDeltaTime() const {
|
||||
if (last_rendered_to_window && Globals::Window)
|
||||
return Globals::Window->GetDeltaTime();
|
||||
return render_target_frame_delta;
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
#include <J3ML/J3ML.hpp>
|
||||
#include <Engine/types/entity/Entity.h>
|
||||
#include <Engine/Globals.h>
|
||||
|
||||
void Engine::Entity::AppendChild(Entity* entity) {
|
||||
if (entity->parent)
|
||||
@@ -17,7 +16,11 @@ Engine::Entity::~Entity() {
|
||||
|
||||
void Engine::Entity::UpdateChildren() {
|
||||
for (auto& e : children) {
|
||||
if (!e)
|
||||
continue;
|
||||
|
||||
e->Update();
|
||||
|
||||
if (!e->children.empty())
|
||||
e->UpdateChildren();
|
||||
}
|
||||
|
@@ -3,16 +3,16 @@
|
||||
|
||||
using namespace J3ML;
|
||||
void Engine::Movable::MoveX(float speed) {
|
||||
position.x = position.x + (speed * Globals::Window->GetDeltaTime());
|
||||
position.x = position.x + (speed * Globals::CurrentScene->TickDeltaTime());
|
||||
}
|
||||
|
||||
void Engine::Movable::MoveY(float speed) {
|
||||
position.y = position.y + (speed * Globals::DeltaTime());
|
||||
position.y = position.y + (speed * Globals::CurrentScene->TickDeltaTime());
|
||||
}
|
||||
|
||||
void Engine::Movable::Move(float angle_rad, float speed) {
|
||||
position.x = position.x + (speed * Globals::DeltaTime()) * Math::Cos(angle_rad);
|
||||
position.y = position.y + (speed * Globals::DeltaTime()) * Math::Sin(angle_rad);
|
||||
position.x = position.x + (speed * Globals::CurrentScene->TickDeltaTime()) * Math::Cos(angle_rad);
|
||||
position.y = position.y + (speed * Globals::CurrentScene->TickDeltaTime()) * Math::Sin(angle_rad);
|
||||
}
|
||||
|
||||
void Engine::Movable::MoveForward(float speed) {
|
||||
@@ -21,18 +21,18 @@ void Engine::Movable::MoveForward(float speed) {
|
||||
|
||||
void Engine::Movable::MoveBackward(float speed) {
|
||||
speed = -speed;
|
||||
position.x = position.x + (speed * Globals::DeltaTime()) * Math::Cos(face_angle);
|
||||
position.y = position.y + (speed * Globals::DeltaTime()) * Math::Sin(face_angle);
|
||||
position.x = position.x + (speed * Globals::CurrentScene->TickDeltaTime()) * Math::Cos(face_angle);
|
||||
position.y = position.y + (speed * Globals::CurrentScene->TickDeltaTime()) * Math::Sin(face_angle);
|
||||
}
|
||||
|
||||
void Engine::Movable::MoveLeft(float speed) {
|
||||
position.x = position.x + (speed * Globals::DeltaTime()) * -Math::Sin(face_angle);
|
||||
position.y = position.y + (speed * Globals::DeltaTime()) * Math::Cos(face_angle);
|
||||
position.x = position.x + (speed * Globals::CurrentScene->TickDeltaTime()) * -Math::Sin(face_angle);
|
||||
position.y = position.y + (speed * Globals::CurrentScene->TickDeltaTime()) * Math::Cos(face_angle);
|
||||
}
|
||||
|
||||
void Engine::Movable::MoveRight(float speed) {
|
||||
position.x = position.x + (speed * Globals::DeltaTime()) * Math::Sin(face_angle);
|
||||
position.y = position.y + (speed * Globals::DeltaTime()) * -Math::Cos(face_angle);
|
||||
position.x = position.x + (speed * Globals::CurrentScene->TickDeltaTime()) * Math::Sin(face_angle);
|
||||
position.y = position.y + (speed * Globals::CurrentScene->TickDeltaTime()) * -Math::Cos(face_angle);
|
||||
}
|
||||
|
||||
void Engine::Movable::SetRotation(float new_face_angle) {
|
||||
@@ -45,7 +45,7 @@ void Engine::Movable::SetRotation(float new_face_angle) {
|
||||
}
|
||||
|
||||
void Engine::Movable::Rotate(float speed) {
|
||||
SetRotation(face_angle + speed * Globals::DeltaTime());
|
||||
SetRotation(face_angle + speed * Globals::CurrentScene->TickDeltaTime());
|
||||
}
|
||||
|
||||
float Engine::Movable::GetRotation() const {
|
||||
|
@@ -2,6 +2,7 @@
|
||||
#include <Engine/Globals.h>
|
||||
|
||||
void Game::Box::Update() {
|
||||
|
||||
if (Globals::Window->IsKeyDown(Keys::W))
|
||||
MoveY(-500);
|
||||
if (Globals::Window->IsKeyDown(Keys::S))
|
||||
|
@@ -4,7 +4,9 @@
|
||||
|
||||
void ControllableBox::Init() {
|
||||
auto* hud = new Game::DemoGameHud();
|
||||
auto *b = new Game::Box({0, 0});
|
||||
entity_list->AppendChild(b);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
auto *b = new Game::Box({0, 0});
|
||||
entity_list->AppendChild(b);
|
||||
}
|
||||
entity_list->AppendChild(hud);
|
||||
}
|
||||
|
@@ -6,26 +6,22 @@ void LoadingScreen::Init() {
|
||||
RedactedSoftware = new JGL::Texture("assets/sprites/Re3D.png");
|
||||
}
|
||||
|
||||
void LoadingScreen::Update() {
|
||||
angle += (elapsed * 4) * Globals::DeltaTime();
|
||||
elapsed += Globals::DeltaTime();
|
||||
void LoadingScreen::Render() {
|
||||
angle += (elapsed * 4) * Globals::CurrentScene->FrameDeltaTime();
|
||||
elapsed += Globals::CurrentScene->FrameDeltaTime();
|
||||
|
||||
// Pretend we're actually loading something idk.
|
||||
if (elapsed >= 2.75)
|
||||
Globals::ChangeScene("Scene0");
|
||||
}
|
||||
|
||||
void LoadingScreen::Render(RenderTarget* render_target) {
|
||||
auto text_length = JGL::Fonts::Jupiteroid.MeasureString("Loading ....", 24);
|
||||
std::string text = "Loading";
|
||||
int dots = static_cast<int>(floor(elapsed / 0.35)) % 4;
|
||||
for (int i = 0; i < dots; ++i)
|
||||
text += ".";
|
||||
|
||||
J2D::Begin(render_target, true);
|
||||
J2D::DrawSprite(RedactedSoftware, {Globals::Window->GetSize().x - RedactedSoftware->GetDimensions().x, Globals::Window->GetSize().y - RedactedSoftware->GetDimensions().y}, angle, {0.5, 0.5});
|
||||
J2D::DrawString(Colors::Whites::Ivory, text, (Globals::Window->GetSize().x - text_length.x) - RedactedSoftware->GetDimensions().x , Globals::Window->GetSize().y - text_length.y * 1.125, 1, 24);
|
||||
J2D::End();
|
||||
J2D::DrawSprite(RedactedSoftware, {Globals::Window->GetSize().x - RedactedSoftware->GetDimensions().x, Globals::Window->GetSize().y - RedactedSoftware->GetDimensions().y}, angle, {0.5, 0.5});
|
||||
J2D::DrawString(Colors::Whites::Ivory, text, (Globals::Window->GetSize().x - text_length.x) - RedactedSoftware->GetDimensions().x , Globals::Window->GetSize().y - text_length.y * 1.125, 1, 24);
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user