Pushing what I have.

This commit is contained in:
2025-01-06 17:06:54 -05:00
parent cf1537c8a8
commit 45ccc88794
19 changed files with 283 additions and 105 deletions

View File

@@ -14,7 +14,7 @@ include (cmake/CPM.cmake)
CPMAddPackage(
NAME JGL
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-45.zip
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-46.zip
)
CPMAddPackage(

View File

@@ -2,6 +2,7 @@
#include <vector>
#include <Engine/types/InstancedTexture.h>
#include <Engine/types/entity/Entity.h>
#include <Engine/types/entity/Hud.h>
#include <JGL/types/RenderTarget.h>
@@ -10,45 +11,31 @@ namespace Engine {
class Scene;
}
using namespace JGL;
class Engine::Scene {
protected:
std::string name;
Hud* heads_up_display = nullptr;
Camera* active_camera = nullptr;
Entity* entity_list = nullptr;
std::vector<InstancedTexture*> instanced_textures{};
std::vector<InstancedTexture*> instanced_alpha_masks{};
std::vector<RenderTarget*> layers{};
std::vector<Entity*> entity_list{};
protected:
[[nodiscard]] std::vector<Entity*> GetFlatEntityList(const std::vector<Entity*>& entity_list) const;
public:
[[nodiscard]] bool EntityListContains(const Entity* entity) const;
[[nodiscard]] Entity* GetEntityList() { return entity_list; }
[[nodiscard]] size_t EntityCount() const;
[[nodiscard]] std::string GetName() const;
[[nodiscard]] Camera* GetActiveCamera() const;
[[nodiscard]] InstancedTexture* GetInstancedTexture(const InstancedSprite* user);
[[nodiscard]] InstancedTexture* GetInstancedAlphaMask(const InstancedSprite* user);
public:
void SetActiveCamera(Camera* camera) { active_camera = camera; };
void DestroyInstancedTexture(const InstancedTexture* itx);
void DestroyInstancedAlphaMask(const InstancedTexture* alpha_mask);
public:
void AppendEntity(Entity* entity);
// Removes and deallocates.
void DestroyEntity(Entity* entity);
// Only removes from the list.
void RemoveEntity(Entity* entity);
virtual void Unload();
virtual void Init() {}
virtual void Update();
virtual void Render(RenderTarget* render_target = nullptr);
virtual void Render(JGL::RenderTarget* render_target = nullptr);
public:
explicit Scene(const std::string& name) : name(name) {}
explicit Scene(const std::string& name) : name(name) { entity_list = new Entity(); }
virtual ~Scene();
};

View File

@@ -0,0 +1,32 @@
#pragma once
#include <Engine/types/entity/Entity.h>
#include <Engine/types/entity/mixins/Living.h>
#include <Engine/types/entity/mixins/Renderable.h>
namespace Engine {
class BoxCollider;
class Movable;
class Track;
}
/* I would expect you to set up such that the direct children would be made to perfectly
* follow the track. The children of those children would be offset by the parents
* movement every frame. This way, by default, Things could move on a train on the track. - Redacted. */
class Engine::Track : public Entity, public Renderable, public Living {
private:
std::vector<Vector2> nodes{};
std::vector<unsigned int> child_node_position{};
void TranslateChild(Entity* entity);
protected:
Vector2 GetNextNode(const Movable* movable);
public:
// Returns whether the track forms a perfect loop *the first node and last node are the same*
[[nodiscard]] bool Loops() const { return !nodes.empty() && nodes.front() == nodes.back(); }
/// @returns The nearest node to a given entity.
Vector2 ClosestNodeTo(const Movable* movable) const;
Vector2 ClosestNodeTo(const BoxCollider* box_collider) const;
void Render() override;
void Update() override;
};

View File

@@ -9,6 +9,8 @@ namespace Engine {
class Engine::Camera : public Entity, public Movable, public Renderable {
public:
void ShouldRender(bool state) override;
[[nodiscard]] bool ShouldRender() const override;
void Render() override {};
public:
explicit Camera(const Vector2& position) : Renderable(0), Movable(position) {}

View File

@@ -15,14 +15,17 @@ protected:
// Epoch micro-seconds.
long creation_time = 0.0f;
std::vector<Entity*> children{};
void UpdateChildren();
public:
void UpdateChildren();
// Parent child structure.
[[nodiscard]] bool AppendChild(Entity* entity);
void AppendChild(Entity* entity);
void DestroyChild(Entity* entity);
void RemoveChild(Entity* entity);
public:
[[nodiscard]] std::vector<Entity*> GetChildren() const;
[[nodiscard]] static std::vector<Entity*> GetAllDescendents(const Entity* entity);
[[nodiscard]] bool HasChildren() const { return !children.empty(); }
[[nodiscard]] bool HasParent() const { return parent; }
[[nodiscard]] Entity* GetParent() const;
// Microseconds.

View File

@@ -1,12 +1,14 @@
#pragma once
#include <Engine/types/entity/Entity.h>
#include <Engine/types/entity/mixins/Renderable.h>
namespace Engine {
class Hud;
}
class Engine::Hud : public Engine::Renderable {
class Engine::Hud : public Entity, public Renderable {
public:
Hud() : Renderable(0) {};
/// You want these to be rendered last.
explicit Hud(const unsigned int layer = std::numeric_limits<unsigned int>::min()) : Renderable(layer) {};
~Hud() override = default;
};

View File

@@ -0,0 +1,51 @@
#pragma once
#include <vector>
#include <functional>
#include <Engine/types/entity/Entity.h>
#include <Engine/types/entity/mixins/BoxCollider.h>
#include <Engine/types/entity/mixins/Movable.h>
#include <Engine/types/entity/mixins/Renderable.h>
#include <Engine/types/entity/mixins/Living.h>
namespace Engine {
class Trigger;
class DynamicTrigger;
}
// Inheribable trigger.
class Engine::Trigger : public Entity, public Living, public BoxCollider, public Movable, public Renderable {
protected:
Vector2 size;
public:
[[nodiscard]] AABB2D GetBounds() const override;
void Render() override;
void SetSize(const Vector2& new_size) { size = new_size; }
public:
explicit Trigger(const Vector2& position, const Vector2& size, bool enabled = true) :
size(size), Entity(), Living(enabled), BoxCollider(enabled), Movable(position), Renderable(0, false) {}
~Trigger() override = default;
};
class Engine::DynamicTrigger final : public Trigger {
private:
void RunActions();
std::function<bool()> condition = nullptr;
std::vector<std::function<void()>> actions{};
public:
void Update() final;
void SetCondition(const std::function<bool()>& condition);
void AddAction(const std::function<void()>& action);
void ClearActions();
public:
explicit DynamicTrigger(const Vector2& position, const Vector2& size, bool enabled = true) : Trigger(position, size, enabled) {};
DynamicTrigger(const Vector2& position, const Vector2& size, bool enabled, const std::function<bool()>& condition, const std::vector<std::function<void()>>& actions)
: condition(condition), actions(actions), Trigger(position, size, enabled) {}
DynamicTrigger(const Vector2& position, const Vector2& size, bool enabled, const std::function<bool()>& condition, const std::function<void()>& action) :
Trigger(position, size, enabled) , condition(condition) { actions.push_back(action); }
~DynamicTrigger() final = default;
};

View File

@@ -9,9 +9,9 @@ class Engine::BoxCollider {
private:
bool enabled = false;
public:
[[nodiscard]] virtual AABB2D GetBounds() = 0;
[[nodiscard]] bool BoxCollides(BoxCollider* rhs);
[[nodiscard]] bool BoxCollides(const Vector2& rhs);
[[nodiscard]] virtual AABB2D GetBounds() const = 0;
[[nodiscard]] bool BoxCollides(BoxCollider* rhs) const;
[[nodiscard]] bool BoxCollides(const Vector2& rhs) const;
public:
void EnableBoxCollision() { enabled = true; }
void DisableBoxCollision() { enabled = false; }

View File

@@ -0,0 +1,16 @@
#pragma once
namespace Engine {
class Living;
}
class Engine::Living {
protected:
bool alive = true;
public:
[[nodiscard]] bool IsAlive() { return alive; }
void SetAlive(bool state) { alive = state; }
public:
explicit Living(bool alive = true) : alive(alive) {}
virtual ~Living() = default;
};

View File

@@ -1,21 +1,23 @@
#pragma once
#include <Engine/types/entity/Entity.h>
namespace Engine {
class Renderable;
}
class Engine::Renderable {
protected:
// Higher numbers are farther to the back.
private:
unsigned int layer = 0;
bool should_render = true;
public:
[[nodiscard]] unsigned int GetLayer() const { return layer; }
void SetLayer(unsigned int new_layer) { layer = new_layer; }
[[nodiscard]] virtual bool ShouldRender() const { return should_render; };
public:
void SetLayer(unsigned int new_layer) { layer = new_layer; }
virtual void ShouldRender(bool state) { should_render = state; }
// *must* be overridden.
virtual void Render() = 0;
public:
explicit Renderable(unsigned int layer) : layer(layer) {}
// The default layer is 1 because zero would almost always be the HUD / score / some kind of overlay.
explicit Renderable(unsigned int layer = 1, bool should_render = true) : should_render(should_render), layer(layer) {}
virtual ~Renderable() = default;
};

View File

@@ -10,5 +10,5 @@ class Game::Box final : public Engine::InstancedSprite {
public:
void Update() final;
public:
explicit Box(const Vector2& position, unsigned int depth = 0, float rad_rotation = 0.0f) : Engine::InstancedSprite(position, depth, "assets/sprites/Re3D.png", rad_rotation, Colors::White, nullptr, "assets/sprites/alpha_mask.png") {}
explicit Box(const Vector2& position, unsigned int layer = 1, float rad_rotation = 0.0f) : Engine::InstancedSprite(position, layer, "assets/sprites/Re3D.png", rad_rotation, Colors::White, nullptr, "assets/sprites/alpha_mask.png") {}
};

View File

@@ -6,7 +6,7 @@ class LoadingScreen final : public Engine::Scene {
protected:
float elapsed = 0.0f;
float angle = 0.0f;
Texture* RedactedSoftware = nullptr;
JGL::Texture* RedactedSoftware = nullptr;
public:
explicit LoadingScreen(const std::string& name) : Scene(name) {}

View File

@@ -5,36 +5,21 @@
#include <algorithm>
using namespace Engine;
bool Scene::EntityListContains(const Entity* entity) const {
auto flat = GetFlatEntityList(entity_list);
for (auto* e : flat)
if (e == entity)
return true;
return false;
}
size_t Scene::EntityCount() const {
return GetFlatEntityList(entity_list).size();
return Entity::GetAllDescendents(entity_list).size();
}
void Scene::Update() {
if (active_camera)
active_camera->Update();
for (auto& e : entity_list)
if (auto* c = dynamic_cast<Camera*>(e); c == nullptr)
e->Update();
entity_list->UpdateChildren();
}
void Scene::Render(RenderTarget* render_target) {
// The Camera and the HUD are ignored here because they're special cases.
std::vector<Renderable*> display_list{};
for (auto* e : GetFlatEntityList(entity_list))
for (auto* e : Entity::GetAllDescendents(entity_list))
if (auto* r = dynamic_cast<Renderable*>(e))
if (auto* c = dynamic_cast<Camera*>(r); c == nullptr)
if (auto* h = dynamic_cast<Hud*>(c); h == nullptr)
display_list.push_back(r);
if (r->ShouldRender())
display_list.push_back(r);
std::sort(display_list.begin(), display_list.end(), [](Renderable* a, Renderable* b) {
return a->GetLayer() > b->GetLayer();
@@ -46,50 +31,32 @@ void Scene::Render(RenderTarget* render_target) {
active_camera->Render();
for (auto& r : display_list)
r->Render();
if (heads_up_display)
heads_up_display->Render();
if (r->ShouldRender())
r->Render();
J2D::End();
}
Scene::~Scene() {
for (auto* e : entity_list)
delete e;
for (auto* itx : instanced_textures)
delete itx;
for (auto* a : instanced_alpha_masks)
delete a;
delete heads_up_display;
delete entity_list;;
delete active_camera;
}
void Scene::Unload() {
for (auto* e : entity_list)
delete e;
for (auto* itx : instanced_textures)
delete itx;
delete heads_up_display;
}
for (auto* a : instanced_alpha_masks)
delete a;
void Scene::AppendEntity(Entity* entity) {
if (!EntityListContains(entity))
entity_list.push_back(entity);
}
void Scene::DestroyEntity(Entity *entity) {
auto it = std::find(entity_list.begin(), entity_list.end(), entity);
if (it != entity_list.end())
delete *it, entity_list.erase(it);
}
void Scene::RemoveEntity(Entity* entity) {
auto it = std::find(entity_list.begin(), entity_list.end(), entity);
if (it != entity_list.end())
entity_list.erase(it);
delete entity_list;
delete active_camera;
}
std::string Scene::GetName() const {
@@ -100,18 +67,6 @@ Camera* Scene::GetActiveCamera() const {
return active_camera;
}
std::vector<Entity*> Scene::GetFlatEntityList(const std::vector<Entity*>& ent_list) const {
std::vector<Entity*> result{};
for (auto* e : ent_list) {
auto children = GetFlatEntityList(e->GetChildren());
for (auto* c : children)
result.push_back(c);
result.push_back(e);
}
return result;
}
InstancedTexture* Scene::GetInstancedTexture(const InstancedSprite* user) {
for (auto* itx : instanced_textures)
if (itx->InUseBy(user))

View File

@@ -0,0 +1,62 @@
#include <Engine/types/Track.h>
#include <Engine/types/entity/mixins/Movable.h>
#include <Engine/types/entity/mixins/BoxCollider.h>
#include <JGL/JGL.h>
using namespace Engine;
void Track::Render() {
if (!nodes.empty())
J2D::DrawLines(Colors::Purples::DarkViolet, nodes.data(), nodes.size());
}
Vector2 Track::ClosestNodeTo(const Movable* movable) const {
if (nodes.empty())
throw std::runtime_error("Getting the closest node to a given movable but there is no nodes?");
Vector2 result;
float minimum = std::numeric_limits<float>::max();
for (const auto& n : nodes) {
float distance_sq = Vector2::DistanceSq(n, movable->GetPosition());
if (distance_sq < minimum)
minimum = distance_sq, result = n;
}
return result;
}
Vector2 Track::ClosestNodeTo(const BoxCollider* box_collider) const {
if (nodes.empty())
throw std::runtime_error("Getting the closest node to a given box_collider but there is no nodes?");
Vector2 result;
float minimum = std::numeric_limits<float>::max();
auto aabb_minimum = box_collider->GetBounds().minPoint;
auto aabb_maximum = box_collider->GetBounds().maxPoint;
for (const auto& n : nodes) {
Vector2 closest_aabb_point_to_current_node = { std::max(aabb_minimum.x, std::min(n.x, aabb_maximum.x)), std::max(aabb_minimum.y, std::min(n.y, aabb_maximum.y)) };
float distance_squared = Vector2::DistanceSq(n, closest_aabb_point_to_current_node);
if (distance_squared < minimum)
minimum = distance_squared, result = n;
}
return result;
}
void Track::Update() {
}
void Track::TranslateChild(Entity* entity) {
/*
if (auto* m = dynamic_cast<Movable*>(entity))
m->Move()
auto m_pos = movable->GetPosition();
*/
}
Vector2 Track::GetNextNode(const Movable* movable) {
auto m_pos = movable->GetPosition();
}

View File

@@ -0,0 +1,11 @@
#include <Engine/types/entity/Camera.h>
#include <Engine/Globals.h>
void Engine::Camera::ShouldRender(bool state) {
if (state)
Globals::CurrentScene->SetActiveCamera(this);
}
bool Engine::Camera::ShouldRender() const {
return Globals::CurrentScene->GetActiveCamera() == this;
}

View File

@@ -2,13 +2,12 @@
#include <Engine/types/entity/Entity.h>
#include <Engine/Globals.h>
bool Engine::Entity::AppendChild(Entity* entity) {
if (!Globals::CurrentScene->EntityListContains(entity)) {
entity->parent = this;
children.push_back(entity);
return true;
}
return false;
void Engine::Entity::AppendChild(Entity* entity) {
if (entity->parent)
entity->parent->RemoveChild(entity);
entity->parent = this;
children.push_back(entity);
}
Engine::Entity::~Entity() {
@@ -34,6 +33,7 @@ void Engine::Entity::RemoveChild(Entity* entity) {
auto it = std::find(children.begin(), children.end(), entity);
if (it != children.end())
children.erase(it);
entity->parent = nullptr;
}
long Engine::Entity::GetCreationTime() const {
@@ -51,3 +51,16 @@ Engine::Entity* Engine::Entity::GetParent() const {
std::vector<Engine::Entity*> Engine::Entity::GetChildren() const {
return children;
}
std::vector<Engine::Entity*> Engine::Entity::GetAllDescendents(const Engine::Entity* entity) {
std::vector<Engine::Entity*> result = entity->GetChildren();
if (result.empty())
return result;
for (auto* c : entity->GetChildren()) {
auto descendants = Entity::GetAllDescendents(c);
result.insert(result.end(), descendants.begin(), descendants.end());
}
return result;
}

View File

@@ -0,0 +1,42 @@
#include <Engine/types/entity/Trigger.h>
#include <JGL/JGL.h>
using namespace Engine;
void Trigger::Render() {
if (ShouldRender()) {
auto size = GetBounds().maxPoint - GetBounds().minPoint;
J2D::OutlineRect(Colors::Red, position, size);
}
}
AABB2D Trigger::GetBounds() const {
return { position, position + size };
}
void DynamicTrigger::AddAction(const std::function<void()>& action) {
actions.push_back(action);
}
void DynamicTrigger::Update() {
bool success = false;
if (condition)
success = condition();
if (success)
RunActions();
}
void DynamicTrigger::RunActions() {
for (const auto& a : actions)
a();
}
void DynamicTrigger::ClearActions() {
actions = {};
}
void DynamicTrigger::SetCondition(const std::function<bool()> & c) {
condition = c;
}

View File

@@ -1,7 +1,7 @@
#include <Engine/types/entity/mixins/BoxCollider.h>
using namespace Engine;
bool BoxCollider::BoxCollides(BoxCollider* rhs) {
bool BoxCollider::BoxCollides(BoxCollider* rhs) const {
if (GetBounds().Intersects(rhs->GetBounds()))
return true;
if (GetBounds().Contains(rhs->GetBounds()))
@@ -9,6 +9,6 @@ bool BoxCollider::BoxCollides(BoxCollider* rhs) {
return false;
}
bool BoxCollider::BoxCollides(const Vector2& rhs) {
bool BoxCollider::BoxCollides(const Vector2& rhs) const {
return GetBounds().Contains(rhs);
}

View File

@@ -5,6 +5,6 @@
void ControllableBox::Init() {
auto* hud = new Game::DemoGameHud();
auto *b = new Game::Box({0, 0});
AppendEntity(b);
heads_up_display = hud;
entity_list->AppendChild(b);
entity_list->AppendChild(hud);
}