We got GameObjects / Components and shit

This commit is contained in:
2026-01-10 06:09:50 +01:00
parent 10b00b0525
commit 0bfc5e0705
35 changed files with 1546 additions and 331 deletions

View File

@@ -28,16 +28,18 @@ public:
void run();
void cleanup();
virtual void customInit() = 0;
virtual void customUpdate(float dt) = 0;
virtual void customDraw() = 0;
virtual void customCleanup() = 0;
virtual void onWindowResize(int newWidth, int newHeight) {};
protected:
SDL_Window* window{nullptr};
AppParams m_params{};
CPUMesh testMesh{};
MeshID testMeshID;
MaterialID testMaterialID;
GfxDevice gfxDevice;
GameRenderer renderer;
Camera camera{glm::vec3(0.f, 0.f, -5.f), glm::vec3(0, 1, 0)};
@@ -47,10 +49,9 @@ protected:
bool isRunning{false};
bool gamePaused{false};
bool frameLimit{true};
bool frameLimit{false};
float frameTime{0.f};
float avgFPS{0.f};
};

View File

@@ -0,0 +1,26 @@
#ifndef MESHRENDERERCOMPONENT_H
#define MESHRENDERERCOMPONENT_H
#include <destrum/ObjectModel/Component.h>
#include <destrum/Graphics/ids.h>
class MeshRendererComponent final: public Component {
public:
explicit MeshRendererComponent(GameObject &parent);
void Start() override;
void Update() override;
void Render(const RenderContext& ctx) override;
void SetMeshID(MeshID id) { meshID = id; }
MeshID GetMeshID() const { return meshID; }
void SetMaterialID(MaterialID id) { materialID = id; }
MaterialID GetMaterialID() const { return materialID; }
private:
MeshID meshID{NULL_MESH_ID};
MaterialID materialID{NULL_MATERIAL_ID};
};
#endif //MESHRENDERERCOMPONENT_H

View File

@@ -0,0 +1,29 @@
#ifndef ROTATOR_H
#define ROTATOR_H
#include <destrum/ObjectModel/Component.h>
#include <destrum/ObjectModel/Transform.h>
class Rotator final : public Component {
public:
explicit Rotator(GameObject& parent, float distance, float speed);
Rotator(const Rotator& other) = delete;
Rotator(Rotator&& other) noexcept = delete;
Rotator& operator=(const Rotator& other) = delete;
Rotator& operator=(Rotator&& other) noexcept = delete;
void Update() override;
void SetDistance(float distance) { m_Distance = distance; }
void SetSpeed(float speed) { m_Speed = speed; }
void SetRotatePosition(const glm::vec3& position) { GetTransform().SetLocalPosition(position); }
private:
float m_Distance{};
float m_Speed{};
float m_CurrentAngle{};
glm::vec3 m_OriginalPosition{};
};
#endif //ROTATOR_H

View File

@@ -0,0 +1,104 @@
#ifndef EVENT_H
#define EVENT_H
#include <functional>
#include <unordered_set>
class EventListener;
class BaseEvent {
public:
BaseEvent() = default;
virtual ~BaseEvent() = default;
virtual void RemoveListener(EventListener* listener) = 0;
};
class EventListener {
template <typename... EventArgs>
friend class Event;
public:
virtual ~EventListener() {
for (auto* event: m_Events)
event->RemoveListener(this);
}
EventListener(EventListener&&) = delete;
EventListener(const EventListener&) = delete;
EventListener& operator=(EventListener&&) = delete;
EventListener& operator=(const EventListener&) = delete;
protected:
EventListener() = default;
private:
void AddEvent(BaseEvent* event) { m_Events.insert(event); }
void RemoveEvent(BaseEvent* event) { m_Events.erase(event); }
std::unordered_set<BaseEvent*> m_Events{};
};
template <typename... EventArgs>
class Event final: public BaseEvent {
using EventFunction = std::pair<void*, std::function<void(EventArgs...)>>;
public:
Event() = default;
~Event() override {
for (auto* eventListener: m_EventListeners)
eventListener->RemoveEvent(this);
}
Event(Event&&) = delete;
Event(const Event&) = delete;
Event& operator=(Event&&) = delete;
Event& operator=(const Event&) = delete;
//This is to allow any member function of a EventListener to be bound as a event callback
template <typename ObjectType>
requires std::derived_from<ObjectType, EventListener>
void AddListener(ObjectType* object, void (ObjectType::*memberFunction)(EventArgs...)) {
auto* listener = static_cast<EventListener*>(object);
listener->AddEvent(this);
m_EventListeners.insert(listener);
m_FunctionBinds.emplace_back(
listener, [object, memberFunction] (EventArgs... args) { (object->*memberFunction)(args...); });
}
template <typename Function>
void AddListener(Function function) {
m_FunctionBinds.emplace_back(nullptr, [function] (EventArgs... args) { function(args...); });
}
template <typename... Args>
void Invoke(Args&&... args) {
m_Invoking = true;
for (auto&& listenerFunction: m_FunctionBinds)
listenerFunction.second(args...);
m_Invoking = false;
}
void RemoveListener(EventListener* listener) override {
m_EventListeners.erase(listener);
for (auto it = m_FunctionBinds.begin(); it != m_FunctionBinds.end();) {
if (it->first == static_cast<void*>(listener)) {
it = m_FunctionBinds.erase(it);
} else {
++it;
}
}
}
private:
bool m_Invoking{false};
std::vector<EventFunction> m_FunctionBinds{};
std::unordered_set<EventListener*> m_EventListeners{};
};
#endif //EVENT_H

View File

@@ -27,6 +27,8 @@ public:
const std::vector<MeshDrawCommand>& drawCommands,
const std::vector<std::size_t>& sortedDrawCommands);
void cleanup(VkDevice device);
private:
VkPipelineLayout m_pipelineLayout;

View File

@@ -0,0 +1,12 @@
#ifndef RENDERCONTEXT_H
#define RENDERCONTEXT_H
#include <destrum/Graphics/Renderer.h>
struct RenderContext {
GameRenderer& renderer;
const Camera& camera;
const GameRenderer::SceneData& sceneData;
};
#endif //RENDERCONTEXT_H

View File

@@ -30,7 +30,7 @@ public:
void endDrawing();
void draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData);
void cleanup();
void cleanup(VkDevice device);
void drawMesh(MeshID id, const glm::mat4& transform, MaterialID materialId);
const GPUImage& getDrawImage(const GfxDevice& gfx_device) const;

View File

@@ -95,7 +95,7 @@ static std::vector<CPUMesh::Vertex> vertices = {
static std::vector<uint32_t> indices = {
0, 1, 2, 2, 3, 0, // Front (+Z)
4, 7, 6, 6, 5, 4, // Back (-Z)
4, 5, 6, 6, 7, 4, // Back (-Z) <-- fixed
8, 9,10, 10,11, 8, // Right (+X)
12,13,14, 14,15,12, // Left (-X)
16,17,18, 18,19,16, // Top (+Y)

View File

@@ -17,7 +17,7 @@ public:
void Init();
void BeginFrame();
void ProcessEvent(const SDL_Event& e);
bool ProcessEvent(const SDL_Event& e);
void EndFrame() {}
bool IsKeyDown(SDL_Scancode sc) const;

View File

@@ -0,0 +1,48 @@
#ifndef COMPONENT_H
#define COMPONENT_H
#include <destrum/Graphics/RenderContext.h>
#include <destrum/ObjectModel/Object.h>
class GameObject;
class Transform;
class Component: public Object {
public:
~Component() override = default;
Component(const Component& other) = delete;
Component(Component&& other) noexcept = delete;
Component& operator=(const Component& other) = delete;
Component& operator=(Component&& other) noexcept = delete;
[[nodiscard]] bool isEnabled() const { return m_IsEnabled; }
[[nodiscard]] GameObject *GetGameObject() const { return m_ParentGameObjectPtr; }
[[nodiscard]] Transform& GetTransform() const;
void Destroy() override;
virtual void Start();
virtual void SetEnabled(bool enabled);
virtual void Update() = 0;
virtual void LateUpdate();
virtual void FixedUpdate();
virtual void ImGuiInspector(); //Specifically for the inspector
virtual void ImGuiRender();
virtual void Render(const RenderContext& ctx);
bool HasStarted{false};
protected:
explicit Component(GameObject& pParent, const std::string& name = "Component");
private:
GameObject* m_ParentGameObjectPtr{};
bool m_IsEnabled{true};
};
#endif //COMPONENT_H

View File

@@ -0,0 +1,121 @@
#pragma once
#include <memory>
#include <vector>
#include <destrum/ObjectModel/Component.h>
#include <destrum/ObjectModel/Object.h>
#include <destrum/ObjectModel/Transform.h>
class GameObject final: public Object {
public:
friend class Scene;
void Update();
void LateUpdate();
void FixedUpdate();
void Render(const RenderContext& ctx) const;
void Destroy() override;
void CleanupComponents();
void SetActive(bool active) {
if (active == m_Active) {
return;
}
m_Active = active;
SetActiveDirty();
}
[[nodiscard]] std::vector<std::unique_ptr<Component>>& GetComponents() { return m_Components; }
[[nodiscard]] Transform& GetTransform() { return m_TransformPtr; }
[[nodiscard]] bool IsActiveInHierarchy();
[[nodiscard]] bool IsActive() const { return m_Active; }
explicit GameObject(const std::string& name = "GameObject");
~GameObject() override;
GameObject(const GameObject& other) = delete;
GameObject(GameObject&& other) = delete;
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) {
auto& addedComponent = m_Components.emplace_back(
std::make_unique<Component>(*this, std::forward<Args>(args)...));
return reinterpret_cast<Component*>(addedComponent.get());
}
template <typename Component>
[[nodiscard]] Component *GetComponent() {
for (const auto& component: m_Components) {
if (auto casted = dynamic_cast<Component*>(component.get())) {
return casted;
}
}
return nullptr;
}
template <typename Component>
Component *DestroyComponent() {
for (const auto& component: m_Components) {
if (auto casted = dynamic_cast<Component*>(component.get())) {
casted->Destroy();
return casted;
}
}
return nullptr;
}
template <typename Component>
[[nodiscard]] bool HasComponent() {
for (const auto& component: m_Components) {
if (auto casted = dynamic_cast<Component*>(component.get())) {
return true;
}
}
return false;
}
template <typename Component>
Component *GetComponentInChildren() {
return GetComponentInChildrenRecursive<Component>(&GetTransform());
}
private:
template <typename Component>
static Component *GetComponentInChildrenRecursive(Transform* transform) {
if (!transform) return nullptr;
GameObject* owner = transform->GetOwner();
if (owner) {
if (Component* comp = owner->GetComponent<Component>()) {
return comp;
}
}
for (Transform* child: transform->GetChildren()) {
if (Component* found = GetComponentInChildrenRecursive<Component>(child)) {
return found;
}
}
return nullptr;
}
void SetActiveDirty();
bool m_Active{true};
Scene* m_Scene{};
Transform m_TransformPtr{this};
std::vector<std::unique_ptr<Component>> m_Components{};
bool m_ActiveDirty{true};
bool m_ActiveInHierarchy{true}; //Derived
void UpdateActiveState();
};

View File

@@ -0,0 +1,31 @@
#ifndef OBJECT_H
#define OBJECT_H
#include <string>
class Object {
public:
Object(const Object& other) = delete;
Object(Object&& other) noexcept = delete;
Object& operator=(const Object& other) = delete;
Object& operator=(Object&& other) noexcept = delete;
[[nodiscard]] const std::string& GetName() const { return m_Name; }
[[nodiscard]] bool IsBeingDestroyed() const { return m_BeingDestroyed; }
void SetName(const std::string& newName){ m_Name = newName; }
virtual ~Object();
virtual void Destroy();
protected:
explicit Object(std::string name = "Object");
private:
bool m_BeingDestroyed{false};
std::string m_Name{};
};
#endif //OBJECT_H

View File

@@ -0,0 +1,96 @@
#pragma once
#include <vector>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
class GameObject;
class Transform final {
public:
explicit Transform(GameObject* owner);
~Transform();
Transform(const Transform&) = delete;
Transform(Transform&&) noexcept = delete;
Transform& operator=(const Transform&) = delete;
Transform& operator=(Transform&&) noexcept = delete;
[[nodiscard]] const glm::vec3& GetWorldPosition();
[[nodiscard]] const glm::quat& GetWorldRotation();
[[nodiscard]] const glm::vec3& GetWorldScale();
[[nodiscard]] const glm::mat4& GetWorldMatrix();
void Move(const glm::vec3& move);
void Move(double x, double y, double z);
[[nodiscard]] const glm::vec3& GetLocalPosition() const { return m_LocalPosition; }
[[nodiscard]] const glm::quat& GetLocalRotation() const { return m_LocalRotation; }
[[nodiscard]] const glm::vec3& GetLocalScale() const { return m_LocalScale; }
void SetLocalPosition(const glm::vec3& position);
void SetLocalPosition(float x, float y, float z);
void SetLocalRotation(float x, float y, float z);
void SetLocalRotation(const glm::vec3& rotation);
void SetLocalRotation(const glm::quat& rotation);
void SetLocalScale(float x, float y, float z);
void SetLocalScale(const glm::vec3& scale);
void SetWorldPosition(const glm::vec3& position);
void SetWorldPosition(float x, float y, float z);
void SetWorldRotation(const glm::quat& rotation);
void SetWorldRotation(const glm::vec3& rotation);
void SetWorldRotation(float x, float y, float z);
void SetWorldScale(double x, double y, double z);
void SetWorldScale(const glm::vec3& scale);
[[nodiscard]] Transform* GetParent() const { return m_Parent; }
void SetParent(Transform* parent, bool useWorldPosition = true);
[[nodiscard]] bool IsChild(Transform* child) const;
[[nodiscard]] int GetChildCount() const { return static_cast<int>(m_Children.size()); }
[[nodiscard]] const std::vector<Transform*>& GetChildren() const;
[[nodiscard]] GameObject* GetOwner() const;
private:
void AddChild(Transform* transform);
void RemoveChild(Transform* transform);
void UpdateWorldPosition();
void UpdateWorldRotation();
void UpdateWorldScale();
void UpdateWorldMatrix();
void SetPositionDirty();
void SetRotationDirty();
void SetScaleDirty();
glm::vec3 m_LocalPosition{};
glm::quat m_LocalRotation{ glm::mat4{ 1.0f }};
glm::vec3 m_LocalScale{ 1, 1, 1 };
bool m_PositionDirty{true};
bool m_RotationDirty{true};
bool m_ScaleDirty{true};
bool m_MatrixDirty{true};
glm::vec3 m_WorldPosition{};
glm::quat m_WorldRotation{ glm::mat4{ 1.0f }};
glm::vec3 m_WorldScale{ 1, 1, 1 };
glm::mat4 m_WorldMatrix{};
Transform* m_Parent{};
std::vector<Transform*> m_Children{};
GameObject* m_Owner{};
};

View File

@@ -0,0 +1,84 @@
#ifndef SCENE_H
#define SCENE_H
#include <functional>
#include <destrum/Event.h>
#include <destrum/Scene/SceneManager.h>
class GameObject;
class Scene final {
friend Scene& SceneManager::CreateScene(const std::string& name);
public:
void Add(std::shared_ptr<GameObject> object);
void Remove(const std::shared_ptr<GameObject>& object);
void RemoveAll();
void Load();
void Update();
void FixedUpdate();
void LateUpdate();
void Render(const RenderContext& ctx) const;
void RenderImgui();
void CleanupDestroyedGameObjects();
void Unload();
void DestroyGameObjects();
[[nodiscard]] bool IsBeingUnloaded() const { return m_BeingUnloaded; }
void SetRegisterBindings(std::function<void()> registerBindings) {
m_registerBindings = std::move(registerBindings);
}
void SetUnregisterBindings(std::function<void()> unregisterBindings) {
m_unregisterBindings = std::move(unregisterBindings);
}
void UnloadBindings() {
if (m_unregisterBindings) {
m_unregisterBindings();
}
}
void LoadBindings() {
OnSceneLoaded.Invoke();
if (m_registerBindings) {
m_registerBindings();
}
}
[[nodiscard]] const std::string& GetName() const { return m_name; }
[[nodiscard]] unsigned int GetId() const { return m_idCounter++; }
~Scene();
Scene(const Scene& other) = delete;
Scene(Scene&& other) = delete;
Scene& operator=(const Scene& other) = delete;
Scene& operator=(Scene&& other) = delete;
Event<> OnSceneLoaded;
private:
explicit Scene(const std::string& name);
std::string m_name;
std::vector<std::shared_ptr<GameObject>> m_objects{};
std::vector<std::shared_ptr<GameObject>> m_pendingAdditions{};
bool m_BeingUnloaded{false};
static unsigned int m_idCounter;
bool m_renderImgui{false};
//Imgui vars
bool m_ShowDemoWindow{false};
std::function<void()> m_registerBindings;
std::function<void()> m_unregisterBindings;
};
#endif // SCENE_H

View File

@@ -0,0 +1,56 @@
#ifndef SCENEMANAGER_H
#define SCENEMANAGER_H
#include <memory>
#include <string>
#include <vector>
#include <destrum/Singleton.h>
#include <destrum/Graphics/RenderContext.h>
class Scene;
class SceneManager final: public Singleton<SceneManager> {
public:
Scene& CreateScene(const std::string& name);
//TODO: Verry bad fix
Scene& GetCurrentScene() const { return *m_scenes[m_ActiveSceneIndex]; }
void Update();
void FixedUpdate();
void LateUpdate();
void Render(const RenderContext& ctx);
void RenderImgui();
void HandleGameObjectDestroy();
void DestroyGameObjects();
void UnloadAllScenes();
void HandleSceneDestroy();
void HandleScene();
void Destroy();
void SwitchScene(int index);
int GetActiveSceneId() const { return m_ActiveSceneIndex; }
[[nodiscard]] const std::vector<std::shared_ptr<Scene>>& GetScenes() const { return m_scenes; }
int GetSceneCount() const {
return static_cast<int>(m_scenes.size());
}
private:
friend class Singleton<SceneManager>;
SceneManager() = default;
int m_ActiveSceneIndex{0};
std::vector<std::shared_ptr<Scene>> m_scenes;
};
#endif //SCENEMANAGER_H