From 0bfc5e0705599d3d5ce477fbf34695f9b5624dd0 Mon Sep 17 00:00:00 2001 From: Bram Date: Sat, 10 Jan 2026 06:09:50 +0100 Subject: [PATCH] We got GameObjects / Components and shit --- destrum/CMakeLists.txt | 12 + destrum/include/destrum/App.h | 15 +- .../Components/MeshRendererComponent.h | 26 ++ destrum/include/destrum/Components/Rotator.h | 29 ++ destrum/include/destrum/Event.h | 104 +++++++ .../destrum/Graphics/Pipelines/MeshPipeline.h | 2 + .../include/destrum/Graphics/RenderContext.h | 12 + destrum/include/destrum/Graphics/Renderer.h | 2 +- .../include/destrum/Graphics/Resources/Mesh.h | 2 +- destrum/include/destrum/Input/InputManager.h | 2 +- .../include/destrum/ObjectModel/Component.h | 48 ++++ .../include/destrum/ObjectModel/GameObject.h | 121 +++++++++ destrum/include/destrum/ObjectModel/Object.h | 31 +++ .../include/destrum/ObjectModel/Transform.h | 96 +++++++ destrum/include/destrum/Scene/Scene.h | 84 ++++++ destrum/include/destrum/Scene/SceneManager.h | 56 ++++ destrum/src/App.cpp | 189 ++++++------- .../src/Components/MeshRendererComponent.cpp | 19 ++ destrum/src/Components/Rotator.cpp | 17 ++ destrum/src/Event.cpp | 1 + destrum/src/Graphics/Camera.cpp | 6 +- destrum/src/Graphics/GfxDevice.cpp | 3 +- .../src/Graphics/Pipelines/MeshPipeline.cpp | 6 + destrum/src/Graphics/Renderer.cpp | 3 +- destrum/src/Input/InputManager.cpp | 6 +- destrum/src/ObjectModel/Component.cpp | 44 +++ destrum/src/ObjectModel/GameObject.cpp | 96 +++++++ destrum/src/ObjectModel/Object.cpp | 22 ++ destrum/src/ObjectModel/Transform.cpp | 253 ++++++++++++++++++ destrum/src/Scene/Scene.cpp | 149 +++++++++++ destrum/src/Scene/SceneManager.cpp | 83 ++++++ lightkeeper/include/Lightkeeper.h | 18 +- lightkeeper/src/Lightkeeper.cpp | 106 +++++++- lightkeeper/src/main.cpp | 1 + lightkeeper/src/main12.cpp | 213 --------------- 35 files changed, 1546 insertions(+), 331 deletions(-) create mode 100644 destrum/include/destrum/Components/MeshRendererComponent.h create mode 100644 destrum/include/destrum/Components/Rotator.h create mode 100644 destrum/include/destrum/Event.h create mode 100644 destrum/include/destrum/Graphics/RenderContext.h create mode 100644 destrum/include/destrum/ObjectModel/Component.h create mode 100644 destrum/include/destrum/ObjectModel/GameObject.h create mode 100644 destrum/include/destrum/ObjectModel/Object.h create mode 100644 destrum/include/destrum/ObjectModel/Transform.h create mode 100644 destrum/include/destrum/Scene/Scene.h create mode 100644 destrum/include/destrum/Scene/SceneManager.h create mode 100644 destrum/src/Components/MeshRendererComponent.cpp create mode 100644 destrum/src/Components/Rotator.cpp create mode 100644 destrum/src/Event.cpp create mode 100644 destrum/src/ObjectModel/Component.cpp create mode 100644 destrum/src/ObjectModel/GameObject.cpp create mode 100644 destrum/src/ObjectModel/Object.cpp create mode 100644 destrum/src/ObjectModel/Transform.cpp create mode 100644 destrum/src/Scene/Scene.cpp create mode 100644 destrum/src/Scene/SceneManager.cpp delete mode 100644 lightkeeper/src/main12.cpp diff --git a/destrum/CMakeLists.txt b/destrum/CMakeLists.txt index a95b7cb..a29070e 100644 --- a/destrum/CMakeLists.txt +++ b/destrum/CMakeLists.txt @@ -2,6 +2,10 @@ add_subdirectory(third_party) set(SRC_FILES "src/App.cpp" + "src/Event.cpp" + + "src/Components/MeshRendererComponent.cpp" + "src/Components/Rotator.cpp" "src/Graphics/BindlessSetManager.cpp" "src/Graphics/Camera.cpp" @@ -24,6 +28,14 @@ set(SRC_FILES "src/Input/InputManager.cpp" + "src/ObjectModel/Component.cpp" + "src/ObjectModel/GameObject.cpp" + "src/ObjectModel/Object.cpp" + "src/ObjectModel/Transform.cpp" + + "src/Scene/Scene.cpp" + "src/Scene/SceneManager.cpp" + "src/FS/AssetFS.cpp" ) diff --git a/destrum/include/destrum/App.h b/destrum/include/destrum/App.h index 82538f9..389c547 100644 --- a/destrum/include/destrum/App.h +++ b/destrum/include/destrum/App.h @@ -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}; - }; diff --git a/destrum/include/destrum/Components/MeshRendererComponent.h b/destrum/include/destrum/Components/MeshRendererComponent.h new file mode 100644 index 0000000..bf2e916 --- /dev/null +++ b/destrum/include/destrum/Components/MeshRendererComponent.h @@ -0,0 +1,26 @@ +#ifndef MESHRENDERERCOMPONENT_H +#define MESHRENDERERCOMPONENT_H + +#include + +#include + +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 diff --git a/destrum/include/destrum/Components/Rotator.h b/destrum/include/destrum/Components/Rotator.h new file mode 100644 index 0000000..fb6b3e3 --- /dev/null +++ b/destrum/include/destrum/Components/Rotator.h @@ -0,0 +1,29 @@ +#ifndef ROTATOR_H +#define ROTATOR_H + +#include +#include + +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 diff --git a/destrum/include/destrum/Event.h b/destrum/include/destrum/Event.h new file mode 100644 index 0000000..df98041 --- /dev/null +++ b/destrum/include/destrum/Event.h @@ -0,0 +1,104 @@ +#ifndef EVENT_H +#define EVENT_H +#include +#include + +class EventListener; + +class BaseEvent { +public: + BaseEvent() = default; + virtual ~BaseEvent() = default; + + virtual void RemoveListener(EventListener* listener) = 0; +}; + +class EventListener { + template + 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 m_Events{}; +}; + +template +class Event final: public BaseEvent { + using EventFunction = std::pair>; + +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 + requires std::derived_from + void AddListener(ObjectType* object, void (ObjectType::*memberFunction)(EventArgs...)) { + auto* listener = static_cast(object); + listener->AddEvent(this); + m_EventListeners.insert(listener); + + m_FunctionBinds.emplace_back( + listener, [object, memberFunction] (EventArgs... args) { (object->*memberFunction)(args...); }); + } + + template + void AddListener(Function function) { + m_FunctionBinds.emplace_back(nullptr, [function] (EventArgs... args) { function(args...); }); + } + + template + 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(listener)) { + it = m_FunctionBinds.erase(it); + } else { + ++it; + } + } + } + +private: + bool m_Invoking{false}; + std::vector m_FunctionBinds{}; + std::unordered_set m_EventListeners{}; +}; + + +#endif //EVENT_H diff --git a/destrum/include/destrum/Graphics/Pipelines/MeshPipeline.h b/destrum/include/destrum/Graphics/Pipelines/MeshPipeline.h index fefd765..b7e1011 100644 --- a/destrum/include/destrum/Graphics/Pipelines/MeshPipeline.h +++ b/destrum/include/destrum/Graphics/Pipelines/MeshPipeline.h @@ -27,6 +27,8 @@ public: const std::vector& drawCommands, const std::vector& sortedDrawCommands); + void cleanup(VkDevice device); + private: VkPipelineLayout m_pipelineLayout; diff --git a/destrum/include/destrum/Graphics/RenderContext.h b/destrum/include/destrum/Graphics/RenderContext.h new file mode 100644 index 0000000..eb800dd --- /dev/null +++ b/destrum/include/destrum/Graphics/RenderContext.h @@ -0,0 +1,12 @@ +#ifndef RENDERCONTEXT_H +#define RENDERCONTEXT_H + +#include + +struct RenderContext { + GameRenderer& renderer; + const Camera& camera; + const GameRenderer::SceneData& sceneData; +}; + +#endif //RENDERCONTEXT_H diff --git a/destrum/include/destrum/Graphics/Renderer.h b/destrum/include/destrum/Graphics/Renderer.h index ed681c4..f23c288 100644 --- a/destrum/include/destrum/Graphics/Renderer.h +++ b/destrum/include/destrum/Graphics/Renderer.h @@ -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; diff --git a/destrum/include/destrum/Graphics/Resources/Mesh.h b/destrum/include/destrum/Graphics/Resources/Mesh.h index 9d847a2..c4821a9 100644 --- a/destrum/include/destrum/Graphics/Resources/Mesh.h +++ b/destrum/include/destrum/Graphics/Resources/Mesh.h @@ -95,7 +95,7 @@ static std::vector vertices = { static std::vector 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) diff --git a/destrum/include/destrum/Input/InputManager.h b/destrum/include/destrum/Input/InputManager.h index 4195146..90534e7 100644 --- a/destrum/include/destrum/Input/InputManager.h +++ b/destrum/include/destrum/Input/InputManager.h @@ -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; diff --git a/destrum/include/destrum/ObjectModel/Component.h b/destrum/include/destrum/ObjectModel/Component.h new file mode 100644 index 0000000..0247e2a --- /dev/null +++ b/destrum/include/destrum/ObjectModel/Component.h @@ -0,0 +1,48 @@ +#ifndef COMPONENT_H +#define COMPONENT_H + +#include +#include + +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 diff --git a/destrum/include/destrum/ObjectModel/GameObject.h b/destrum/include/destrum/ObjectModel/GameObject.h new file mode 100644 index 0000000..495fc16 --- /dev/null +++ b/destrum/include/destrum/ObjectModel/GameObject.h @@ -0,0 +1,121 @@ +#pragma once +#include +#include + +#include +#include +#include + +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>& 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 + requires std::constructible_from + Component *AddComponent(Args&&... args) { + auto& addedComponent = m_Components.emplace_back( + std::make_unique(*this, std::forward(args)...)); + + return reinterpret_cast(addedComponent.get()); + } + + template + [[nodiscard]] Component *GetComponent() { + for (const auto& component: m_Components) { + if (auto casted = dynamic_cast(component.get())) { + return casted; + } + } + return nullptr; + } + + template + Component *DestroyComponent() { + for (const auto& component: m_Components) { + if (auto casted = dynamic_cast(component.get())) { + casted->Destroy(); + return casted; + } + } + return nullptr; + } + + template + [[nodiscard]] bool HasComponent() { + for (const auto& component: m_Components) { + if (auto casted = dynamic_cast(component.get())) { + return true; + } + } + return false; + } + + template + Component *GetComponentInChildren() { + return GetComponentInChildrenRecursive(&GetTransform()); + } + +private: + template + static Component *GetComponentInChildrenRecursive(Transform* transform) { + if (!transform) return nullptr; + GameObject* owner = transform->GetOwner(); + if (owner) { + if (Component* comp = owner->GetComponent()) { + return comp; + } + } + for (Transform* child: transform->GetChildren()) { + if (Component* found = GetComponentInChildrenRecursive(child)) { + return found; + } + } + return nullptr; + } + + void SetActiveDirty(); + + bool m_Active{true}; + Scene* m_Scene{}; + Transform m_TransformPtr{this}; + std::vector> m_Components{}; + + bool m_ActiveDirty{true}; + bool m_ActiveInHierarchy{true}; //Derived + void UpdateActiveState(); +}; diff --git a/destrum/include/destrum/ObjectModel/Object.h b/destrum/include/destrum/ObjectModel/Object.h new file mode 100644 index 0000000..ee3154d --- /dev/null +++ b/destrum/include/destrum/ObjectModel/Object.h @@ -0,0 +1,31 @@ +#ifndef OBJECT_H +#define OBJECT_H + +#include + +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 diff --git a/destrum/include/destrum/ObjectModel/Transform.h b/destrum/include/destrum/ObjectModel/Transform.h new file mode 100644 index 0000000..4361372 --- /dev/null +++ b/destrum/include/destrum/ObjectModel/Transform.h @@ -0,0 +1,96 @@ +#pragma once +#include +#include +#include + +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(m_Children.size()); } + [[nodiscard]] const std::vector& 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 m_Children{}; + + GameObject* m_Owner{}; +}; diff --git a/destrum/include/destrum/Scene/Scene.h b/destrum/include/destrum/Scene/Scene.h new file mode 100644 index 0000000..3574adc --- /dev/null +++ b/destrum/include/destrum/Scene/Scene.h @@ -0,0 +1,84 @@ +#ifndef SCENE_H +#define SCENE_H + +#include + +#include +#include + +class GameObject; + +class Scene final { + friend Scene& SceneManager::CreateScene(const std::string& name); + +public: + void Add(std::shared_ptr object); + void Remove(const std::shared_ptr& 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 registerBindings) { + m_registerBindings = std::move(registerBindings); + } + + void SetUnregisterBindings(std::function 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> m_objects{}; + std::vector> m_pendingAdditions{}; + bool m_BeingUnloaded{false}; + + static unsigned int m_idCounter; + + bool m_renderImgui{false}; + + //Imgui vars + bool m_ShowDemoWindow{false}; + + std::function m_registerBindings; + std::function m_unregisterBindings; +}; + +#endif // SCENE_H diff --git a/destrum/include/destrum/Scene/SceneManager.h b/destrum/include/destrum/Scene/SceneManager.h new file mode 100644 index 0000000..11e3e40 --- /dev/null +++ b/destrum/include/destrum/Scene/SceneManager.h @@ -0,0 +1,56 @@ +#ifndef SCENEMANAGER_H +#define SCENEMANAGER_H + +#include +#include +#include + +#include + +#include + +class Scene; + +class SceneManager final: public Singleton { +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>& GetScenes() const { return m_scenes; } + + int GetSceneCount() const { + return static_cast(m_scenes.size()); + } + +private: + friend class Singleton; + + SceneManager() = default; + + int m_ActiveSceneIndex{0}; + + std::vector> m_scenes; +}; + +#endif //SCENEMANAGER_H diff --git a/destrum/src/App.cpp b/destrum/src/App.cpp index a68e458..267c3aa 100644 --- a/destrum/src/App.cpp +++ b/destrum/src/App.cpp @@ -7,8 +7,7 @@ #include "glm/gtx/transform.hpp" #include "spdlog/spdlog.h" -App::App(): renderer{meshCache, materialCache} { -} +App::App() {} void App::init(const AppParams& params) { m_params = params; @@ -34,132 +33,122 @@ void App::init(const AppParams& params) { } gfxDevice.init(window, params.appName, false); - materialCache.init(gfxDevice); - renderer.init(gfxDevice, params.renderSize); - - //Read whole file - auto file = AssetFS::GetInstance().ReadBytes("engine://assetfstest.txt"); - std::string fileStr(file.begin(), file.end()); - spdlog::info("Read from assetfstest.txt: {}", fileStr); - testMesh.name = "Test Mesh"; - testMesh.vertices = vertices; - testMesh.indices = indices; - - testMeshID = meshCache.addMesh(gfxDevice, testMesh); - spdlog::info("TestMesh uploaded with id: {}", testMeshID); - - const auto testimgpath = AssetFS::GetInstance().GetFullPath("engine://textures/kobe.png"); - auto testimgID = gfxDevice.loadImageFromFile(testimgpath); - spdlog::info("Test image loaded with id: {}", testimgID); - testMaterialID = materialCache.addMaterial(gfxDevice, { - .baseColor = glm::vec3(1.f), - .diffuseTexture = testimgID, - }); - spdlog::info("Test material created with id: {}", testMaterialID); - - float aspectRatio = static_cast(params.renderSize.x) / static_cast(params.renderSize.y); - camera.setAspectRatio(aspectRatio); - - //Look 90 deg to the right - camera.SetRotation(glm::radians(glm::vec2(90.f, 0.f))); InputManager::GetInstance().Init(); + + customInit(); } void App::run() { - const float FPS = 30.f; - const float dt = 1.f / FPS; + using clock = std::chrono::steady_clock; - auto prevTime = std::chrono::high_resolution_clock::now(); - float accumulator = dt; // so that we get at least 1 update before render + const float targetHz = 60.0f; + const float dt = 1.0f / targetHz; + const float maxFrameTime = 0.25f; // clamp big hitches + const int maxSteps = 5; // prevent spiral of death + + auto prevTime = clock::now(); + float accumulator = 0.0f; isRunning = true; while (isRunning) { - const auto newTime = std::chrono::high_resolution_clock::now(); - frameTime = std::chrono::duration(newTime - prevTime).count(); + const auto frameStart = clock::now(); + float frameTimeSec = std::chrono::duration(frameStart - prevTime).count(); + prevTime = frameStart; - if (frameTime > 0.07f && frameTime < 5.f) { - // if >=5.f - debugging? - spdlog::warn("Frame drop detected, time: {:.4f}s", frameTime); + if (frameTimeSec > 0.07f && frameTimeSec < 5.f) { + spdlog::warn("Frame drop detected, time: {:.4f}s", frameTimeSec); } - accumulator += frameTime; - prevTime = newTime; + if (frameTimeSec > maxFrameTime) frameTimeSec = maxFrameTime; + if (frameTimeSec < 0.0f) frameTimeSec = 0.0f; - float newFPS = 1.f / frameTime; - if (newFPS == std::numeric_limits::infinity()) { - // can happen when frameTime == 0 - newFPS = 0; - } - avgFPS = std::lerp(avgFPS, newFPS, 0.1f); + accumulator += frameTimeSec; - if (accumulator > 10 * dt) { - // game stopped for debug - accumulator = dt; + if (frameTimeSec > 0.0f) { + const float newFPS = 1.0f / frameTimeSec; + avgFPS = std::lerp(avgFPS, newFPS, 0.1f); } - while (accumulator >= dt) { - InputManager::GetInstance().BeginFrame(); - SDL_Event event; - while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) { - isRunning = false; - return; - } - if (event.type == SDL_WINDOWEVENT) { - switch (event.window.event) { - case SDL_WINDOWEVENT_SIZE_CHANGED: - /* fallthrough */ - case SDL_WINDOWEVENT_RESIZED: - m_params.windowSize = {event.window.data1, event.window.data2}; - break; - } - } - InputManager::GetInstance().ProcessEvent(event); - + InputManager::GetInstance().BeginFrame(); + camera.Update(dt); + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + isRunning = false; + break; } - - camera.Update(dt); - if (gfxDevice.needsSwapchainRecreate()) { - spdlog::info("Recreating swapchain to size: {}x{}", m_params.windowSize.x, m_params.windowSize.y); - gfxDevice.recreateSwapchain(m_params.windowSize.x, m_params.windowSize.y); - renderer.resize(gfxDevice, { m_params.windowSize.x, m_params.windowSize.y }); - float aspectRatio = float(m_params.windowSize.x) / float(m_params.windowSize.y); - camera.setAspectRatio(aspectRatio); + if (event.type == SDL_WINDOWEVENT) { + switch (event.window.event) { + case SDL_WINDOWEVENT_SIZE_CHANGED: + case SDL_WINDOWEVENT_RESIZED: + m_params.windowSize = { event.window.data1, event.window.data2 }; + break; + } } + if (InputManager::GetInstance().ProcessEvent(event)) { + isRunning = false; + } + } + if (!isRunning) break; + customUpdate(dt); + // ---- Swapchain resize check once per frame ---- + if (gfxDevice.needsSwapchainRecreate()) { + spdlog::info("Recreating swapchain to size: {}x{}", m_params.windowSize.x, m_params.windowSize.y); + gfxDevice.recreateSwapchain(m_params.windowSize.x, m_params.windowSize.y); + onWindowResize(m_params.windowSize.x, m_params.windowSize.y); + } + + // ---- Fixed updates (no event polling inside) ---- + int steps = 0; + while (accumulator >= dt && steps < maxSteps) { + //Set window title to fps + SDL_SetWindowTitle( + window, + fmt::format("{} - FPS: {:.2f}", m_params.windowTitle, avgFPS).c_str()); accumulator -= dt; + steps++; } + // If we hit the step cap, drop leftover time to recover smoothly + if (steps == maxSteps) accumulator = 0.0f; + // Optional interpolation factor for rendering + const float alpha = accumulator / dt; + + // ---- Render ---- if (!gfxDevice.needsSwapchainRecreate()) { - glm::mat4 objMatrix = glm::mat4(1.f); - objMatrix = glm::translate(objMatrix, glm::vec3(0.f, -3.0f, 0.f)); - renderer.beginDrawing(gfxDevice); - renderer.drawMesh(testMeshID, glm::mat4(1.f), testMaterialID); - renderer.drawMesh(testMeshID, objMatrix, 0); - renderer.endDrawing(); - - - auto cmd = gfxDevice.beginFrame(); - const auto& drawImage = renderer.getDrawImage(gfxDevice); - renderer.draw(cmd, gfxDevice, camera, GameRenderer::SceneData{ - camera, glm::vec3(0.1f), 0.5f, glm::vec3(0.5f), 0.01f - }); - gfxDevice.endFrame(cmd, drawImage, { - .clearColor = {{0.f, 0.f, 0.5f, 1.f}}, - .drawImageBlitRect = glm::ivec4{}} - ); + // glm::mat4 objMatrix = glm::translate(glm::mat4(1.f), glm::vec3(0.f, -3.0f, 0.f)); + // + // renderer.beginDrawing(gfxDevice); + // renderer.drawMesh(testMeshID, glm::mat4(1.f), testMaterialID); + // renderer.drawMesh(testMeshID, objMatrix, 0); + // renderer.endDrawing(); + // + // const auto cmd = gfxDevice.beginFrame(); + // const auto& drawImage = renderer.getDrawImage(gfxDevice); + // + // renderer.draw(cmd, gfxDevice, camera, GameRenderer::SceneData{ + // camera, glm::vec3(0.1f), 0.5f, glm::vec3(0.5f), 0.01f + // }); + // + // gfxDevice.endFrame(cmd, drawImage, { + // .clearColor = {{0.f, 0.f, 0.5f, 1.f}}, + // .drawImageBlitRect = glm::ivec4{} + // }); + customDraw(); } + + // ---- Frame cap (if you still want it) ---- if (frameLimit) { - // Delay to not overload the CPU - const auto now = std::chrono::high_resolution_clock::now(); - const auto frameTime = std::chrono::duration(now - prevTime).count(); - if (dt > frameTime) { - SDL_Delay(static_cast(dt - frameTime)); - } + const auto targetEnd = frameStart + std::chrono::duration_cast( + std::chrono::duration(dt) + ); + std::this_thread::sleep_until(targetEnd); } } + gfxDevice.waitIdle(); } void App::cleanup() { diff --git a/destrum/src/Components/MeshRendererComponent.cpp b/destrum/src/Components/MeshRendererComponent.cpp new file mode 100644 index 0000000..85130aa --- /dev/null +++ b/destrum/src/Components/MeshRendererComponent.cpp @@ -0,0 +1,19 @@ +#include +#include + + +MeshRendererComponent::MeshRendererComponent(GameObject& parent): Component(parent, "MeshRendererComponent") { +} + +void MeshRendererComponent::Start() { + Component::Start(); +} + +void MeshRendererComponent::Update() { +} + +void MeshRendererComponent::Render(const RenderContext& ctx) { + if (meshID != NULL_MESH_ID && materialID != NULL_MATERIAL_ID) { + ctx.renderer.drawMesh(meshID, GetTransform().GetWorldMatrix(), materialID); + } +} diff --git a/destrum/src/Components/Rotator.cpp b/destrum/src/Components/Rotator.cpp new file mode 100644 index 0000000..49d3a3a --- /dev/null +++ b/destrum/src/Components/Rotator.cpp @@ -0,0 +1,17 @@ +#include + +Rotator::Rotator(GameObject& parent, float distance, float speed): +Component(parent, "Rotator"), +m_Distance(distance), +m_Speed(speed), +m_CurrentAngle(0), +m_OriginalPosition(GetTransform().GetWorldPosition()) +{} + +void Rotator::Update() { + m_CurrentAngle += m_Speed * static_cast(0.001); + const float x = cos(m_CurrentAngle) * m_Distance; + const float y = sin(m_CurrentAngle) * m_Distance; + GetTransform().SetLocalPosition(m_OriginalPosition + glm::vec3(x, y, 0)); + +} diff --git a/destrum/src/Event.cpp b/destrum/src/Event.cpp new file mode 100644 index 0000000..3989bcd --- /dev/null +++ b/destrum/src/Event.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/destrum/src/Graphics/Camera.cpp b/destrum/src/Graphics/Camera.cpp index 33f31c0..6e912b5 100644 --- a/destrum/src/Graphics/Camera.cpp +++ b/destrum/src/Graphics/Camera.cpp @@ -65,8 +65,8 @@ void Camera::Update(float deltaTime) { // SDL controller triggers are axes too: 0..1-ish after normalization in our helper, but signless. // With our NormalizeAxis, triggers will sit near 0 until pressed (depending on mapping). // If your NormalizeAxis maps triggers weirdly, swap to raw event value approach. - const float lt = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_TRIGGERLEFT); - const float rt = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); + const float lt = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); + const float rt = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_TRIGGERLEFT); const float vertical = (rt - lt); padMove += m_up * vertical; @@ -96,7 +96,7 @@ void Camera::Update(float deltaTime) { const float padLookSpeed = 2.2f; // radians/sec at full deflection m_yaw += rx * padLookSpeed * deltaTime; - m_pitch -= ry * padLookSpeed * deltaTime; + m_pitch += ry * padLookSpeed * deltaTime; } // Clamp pitch again after modifications diff --git a/destrum/src/Graphics/GfxDevice.cpp b/destrum/src/Graphics/GfxDevice.cpp index 40eaf10..774fb85 100644 --- a/destrum/src/Graphics/GfxDevice.cpp +++ b/destrum/src/Graphics/GfxDevice.cpp @@ -68,6 +68,7 @@ void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) .set_required_features_12(features12) .set_required_features_13(features13) .set_surface(surface) + .prefer_gpu_device_type(vkb::PreferredDeviceType::discrete) .select() .value(); @@ -177,7 +178,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED; { - VkImageSubresourceRange clearRange =vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT); + const VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT); vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL); swapchainLayout = VK_IMAGE_LAYOUT_GENERAL; diff --git a/destrum/src/Graphics/Pipelines/MeshPipeline.cpp b/destrum/src/Graphics/Pipelines/MeshPipeline.cpp index b166322..7051af1 100644 --- a/destrum/src/Graphics/Pipelines/MeshPipeline.cpp +++ b/destrum/src/Graphics/Pipelines/MeshPipeline.cpp @@ -127,3 +127,9 @@ void MeshPipeline::draw(VkCommandBuffer cmd, vkCmdDrawIndexed(cmd, mesh.numIndices, 1, 0, 0, 0); } } + +void MeshPipeline::cleanup(VkDevice device) { + vkDestroyPipelineLayout(device, m_pipelineLayout, nullptr); + m_pipeline.reset(); +} + diff --git a/destrum/src/Graphics/Renderer.cpp b/destrum/src/Graphics/Renderer.cpp index 09f9bcb..bd03015 100644 --- a/destrum/src/Graphics/Renderer.cpp +++ b/destrum/src/Graphics/Renderer.cpp @@ -86,7 +86,8 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& } -void GameRenderer::cleanup() { +void GameRenderer::cleanup(VkDevice device) { + meshPipeline->cleanup(device); } void GameRenderer::drawMesh(MeshID id, const glm::mat4& transform, MaterialID materialId) { diff --git a/destrum/src/Input/InputManager.cpp b/destrum/src/Input/InputManager.cpp index 50632d0..1323765 100644 --- a/destrum/src/Input/InputManager.cpp +++ b/destrum/src/Input/InputManager.cpp @@ -97,8 +97,11 @@ void InputManager::RemoveController(SDL_JoystickID instanceId) { ); } -void InputManager::ProcessEvent(const SDL_Event& e) { +bool InputManager::ProcessEvent(const SDL_Event& e) { switch (e.type) { + case SDL_QUIT: { + return true; + } case SDL_KEYDOWN: { if (e.key.repeat) break; SDL_Scancode sc = e.key.keysym.scancode; @@ -196,6 +199,7 @@ void InputManager::ProcessEvent(const SDL_Event& e) { default: break; } + return false; } bool InputManager::IsKeyDown(SDL_Scancode sc) const { return m_keysDown.count(sc) != 0; } diff --git a/destrum/src/ObjectModel/Component.cpp b/destrum/src/ObjectModel/Component.cpp new file mode 100644 index 0000000..f2e6c3c --- /dev/null +++ b/destrum/src/ObjectModel/Component.cpp @@ -0,0 +1,44 @@ +#include + +#include +#include +// #include "imgui.h" + +Component::Component(GameObject& pParent, const std::string& name): Object(name), m_ParentGameObjectPtr(&pParent) { + // if (m_ParentGameObjectPtr == nullptr) { + // //TODO: Change pParent ot be a reference + // throw std::runtime_error("Component made with no GameObject??"); + // } +} + +Transform& Component::GetTransform() const { + return m_ParentGameObjectPtr->GetTransform(); +} + +void Component::Destroy() { + // const bool isBeingDestroyed = GetIsBeingDestroyed(); + Object::Destroy(); +} + +void Component::Start() { +} + +void Component::SetEnabled(bool enabled) { + m_IsEnabled = enabled; +} + +void Component::LateUpdate() { +} + +void Component::FixedUpdate() { +} + +void Component::ImGuiInspector() { +} + +void Component::ImGuiRender() { +} + +void Component::Render(const RenderContext& ctx) { + +} diff --git a/destrum/src/ObjectModel/GameObject.cpp b/destrum/src/ObjectModel/GameObject.cpp new file mode 100644 index 0000000..46dd39e --- /dev/null +++ b/destrum/src/ObjectModel/GameObject.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +#include "spdlog/spdlog.h" + +// #include "Managers/ResourceManager.h" + +GameObject::~GameObject() { + spdlog::debug("GameObject destroyed: {}", GetName()); +} + +void GameObject::SetActiveDirty() { + m_ActiveDirty = true; + for (const Transform* child : m_TransformPtr.GetChildren()) { + child->GetOwner()->SetActiveDirty(); + } +} + +void GameObject::UpdateActiveState() { + const auto* parentPtr = m_TransformPtr.GetParent(); + + if(parentPtr == nullptr) { + m_ActiveInHierarchy = m_Active; + } else { + m_ActiveInHierarchy = m_Active && parentPtr->GetOwner()->IsActiveInHierarchy(); + } + + m_ActiveDirty = false; +} + +bool GameObject::IsActiveInHierarchy() { + if (m_ActiveDirty) { + UpdateActiveState(); + } + + return m_ActiveInHierarchy; +} + +GameObject::GameObject(const std::string& name): Object(name) { +} + +void GameObject::Update() { + for (const auto& component: m_Components) { + if (!component->HasStarted) { + component->Start(); + component->HasStarted = true; + } + component->Update(); + } +} + +void GameObject::LateUpdate() { + for (const auto& component: m_Components) { + component->LateUpdate(); + } +} + +void GameObject::FixedUpdate() { + for (const auto& component: m_Components) { + component->FixedUpdate(); + } +} + +void GameObject::Render(const RenderContext& ctx) const { + for (const auto& component: m_Components) { + component->Render(ctx); + } +} + +// void GameObject::ImGuiRender() { +// for (const auto& component: m_Components) { +// component->ImGuiRender(); +// } +// } + +void GameObject::Destroy() { + Object::Destroy(); + + m_TransformPtr.SetParent(nullptr); + + for (const auto& component: m_Components) { + component->Destroy(); + } + + for (const auto child : m_TransformPtr.GetChildren()) { + child->GetOwner()->Destroy(); + } +} + +void GameObject::CleanupComponents() { + std::erase_if(m_Components, [](const std::unique_ptr& component) { + return component->IsBeingDestroyed(); + }); +} diff --git a/destrum/src/ObjectModel/Object.cpp b/destrum/src/ObjectModel/Object.cpp new file mode 100644 index 0000000..027b153 --- /dev/null +++ b/destrum/src/ObjectModel/Object.cpp @@ -0,0 +1,22 @@ +#include + +#include +#include + +#include "spdlog/spdlog.h" + +Object::~Object() { + if (!m_BeingDestroyed) { + assert(false && "Objects destructor called before destroy"); + } + spdlog::debug("Object Destroyed: {}", m_Name); +} + +void Object::Destroy() { + std::cout << "Marked Object for destruction: " << m_Name << std::endl; + spdlog::debug("Object Destroyed: {}", m_Name); + m_BeingDestroyed = true; +} + +Object::Object(std::string name): m_Name(std::move(name)) { +} diff --git a/destrum/src/ObjectModel/Transform.cpp b/destrum/src/ObjectModel/Transform.cpp new file mode 100644 index 0000000..ca35a76 --- /dev/null +++ b/destrum/src/ObjectModel/Transform.cpp @@ -0,0 +1,253 @@ +#include +#include + +Transform::Transform(GameObject* owner): m_Owner(owner) { +} + +Transform::~Transform() { + SetParent(nullptr); + + for (auto it = m_Children.begin(); it != m_Children.end();) { + Transform* child = *it; + it = m_Children.erase(it); + child->SetParent(nullptr); + } +} + +const glm::vec3& Transform::GetWorldPosition() { + if (m_PositionDirty) { + UpdateWorldPosition(); + } + return m_WorldPosition; +} + +const glm::quat& Transform::GetWorldRotation() { + if (m_RotationDirty) { + UpdateWorldRotation(); + } + return m_WorldRotation; +} + +const glm::vec3& Transform::GetWorldScale() { + if (m_ScaleDirty) { + UpdateWorldScale(); + } + return m_WorldScale; +} + +const glm::mat4& Transform::GetWorldMatrix() { + if (m_MatrixDirty) { + UpdateWorldMatrix(); + } + return m_WorldMatrix; +} + +void Transform::SetWorldPosition(const glm::vec3& position) { + if (m_Parent == nullptr) { + SetLocalPosition(position); + } else { + SetLocalPosition(position - m_Parent->GetWorldPosition()); + } +} + +void Transform::SetWorldPosition(float x, float y, float z) { + SetWorldPosition(glm::vec3(x, y, z)); +} + +void Transform::SetWorldRotation(const glm::quat& rotation) { + if(m_Parent == nullptr) { + SetLocalRotation(rotation); + } else { + SetLocalRotation(glm::inverse(m_Parent->GetWorldRotation()) * rotation); + } +} + +void Transform::SetWorldRotation(const glm::vec3& rotation) { + SetWorldRotation(glm::quat(glm::radians(rotation))); +} + +void Transform::SetWorldRotation(float x, float y, float z) { + SetWorldRotation(glm::vec3{x, y, z}); +} + +void Transform::SetWorldScale(double x, double y, double z) { + SetWorldScale(glm::vec3{x, y, z}); +} + +void Transform::SetWorldScale(const glm::vec3& scale) { + if (m_Parent == nullptr) { + SetLocalScale(scale); + } else { + SetLocalScale(scale - m_Parent->GetWorldScale()); + } +} + +void Transform::Move(const glm::vec3& move) { + SetLocalPosition(m_LocalPosition + move); +} + +void Transform::Move(double x, double y, double z) { + this->Move(glm::vec3(x, y, z)); +} + +void Transform::SetLocalPosition(const glm::vec3& position) { + m_LocalPosition = position; + SetPositionDirty(); +} + +void Transform::SetLocalPosition(float x, float y, float z) { + SetLocalPosition(glm::vec3{x, y, z}); +} + +void Transform::SetLocalRotation(float x, float y, float z) { + SetLocalRotation(glm::vec3{x, y, z}); +} + +void Transform::SetLocalRotation(const glm::vec3& rotation) { + SetLocalRotation(glm::quat(glm::radians(rotation))); +} + +void Transform::SetLocalRotation(const glm::quat& rotation) { + m_LocalRotation = rotation; + SetRotationDirty(); +} + +void Transform::SetLocalScale(float x, float y, float z) { + SetLocalScale({x, y, z}); +} + +void Transform::SetLocalScale(const glm::vec3& scale) { + m_LocalScale = scale; + SetScaleDirty(); +} + +void Transform::RemoveChild(Transform* transform) { + std::erase(m_Children, transform); +} + +void Transform::AddChild(Transform* transform) { + // if (transform == this or transform == nullptr) { + // return; + // } + // + // if (transform->m_Parent) { + // transform->m_Parent->RemoveChild(transform); + // } + // + // transform->SetParent(this); + m_Children.push_back(transform); + // + // transform->SetPositionDirty(); +} + +void Transform::SetParent(Transform* parent, bool useWorldPosition) { + if (parent == m_Parent or parent == this or IsChild(parent)) { + return; + } + + if (parent == nullptr) { + SetLocalPosition(GetWorldPosition()); + } else { + if (useWorldPosition) { + SetLocalPosition(GetWorldPosition() - parent->GetWorldPosition()); + } + SetPositionDirty(); + } + if (m_Parent) { + m_Parent->RemoveChild(this); + } + m_Parent = parent; + if (m_Parent) { + m_Parent->AddChild(this); + } +} + + +bool Transform::IsChild(Transform* child) const { + return std::ranges::find(m_Children, child) != m_Children.end(); +} + +const std::vector& Transform::GetChildren() const { + // std::vector validChildren; + // for (auto* child : m_Children) { + // if (child && !child->GetOwner()->IsBeingDestroyed()) { + // validChildren.push_back(child); + // } + // } + // return validChildren; + return m_Children; +} + +GameObject *Transform::GetOwner() const { + return m_Owner; +} + +void Transform::SetPositionDirty() { + m_PositionDirty = true; + m_MatrixDirty = true; + for (const auto child: m_Children) { + child->SetPositionDirty(); + } +} + +void Transform::SetRotationDirty() { + m_RotationDirty = true; + m_MatrixDirty = true; + + for(Transform* childPtr : m_Children) { + if(not childPtr->m_RotationDirty) { + childPtr->SetRotationDirty(); + } + } +} + +void Transform::SetScaleDirty() { + m_ScaleDirty = true; + m_MatrixDirty = true; + + for(Transform* childPtr : m_Children) { + if(not childPtr->m_ScaleDirty) { + childPtr->SetScaleDirty(); + } + } +} + +void Transform::UpdateWorldPosition() { + if (m_Parent) { + m_WorldPosition = m_Parent->GetWorldPosition() + m_LocalPosition; + } else { + m_WorldPosition = m_LocalPosition; + } + m_PositionDirty = false; +} + +void Transform::UpdateWorldRotation() { + if (m_Parent == nullptr) { + m_WorldRotation = m_LocalRotation; + } else { + m_WorldRotation = m_LocalRotation * m_Parent->GetWorldRotation(); + } + + m_RotationDirty = false; +} + +void Transform::UpdateWorldScale() { + if(m_Parent == nullptr) { + m_WorldScale = m_LocalScale; + } else { + m_WorldScale = m_LocalScale * m_Parent->GetWorldScale(); + } + + m_ScaleDirty = false; +} + +void Transform::UpdateWorldMatrix() { + const glm::mat4 trans = glm::translate(glm::mat4(1.0f), GetWorldPosition()); + const glm::mat4 rot = glm::mat4_cast(GetWorldRotation()); + const glm::mat4 scale = glm::scale(glm::mat4(1.0f), GetWorldScale()); + + m_WorldMatrix = trans * rot * scale; + m_MatrixDirty = false; +} + + diff --git a/destrum/src/Scene/Scene.cpp b/destrum/src/Scene/Scene.cpp new file mode 100644 index 0000000..2257de7 --- /dev/null +++ b/destrum/src/Scene/Scene.cpp @@ -0,0 +1,149 @@ +#include +#include + +#include +#include +#include + + +#include +#include + +// #include "ServiceLocator.h" +// #include "Input/InputManager.h" +// #include "Managers/Renderer.h" + + +unsigned int Scene::m_idCounter = 0; + +Scene::Scene(const std::string& name) : m_name(name) { +} + +Scene::~Scene() = default; + +void Scene::Add(std::shared_ptr object) { + // m_objects.emplace_back(std::move(object)); + m_pendingAdditions.emplace_back(std::move(object)); +} + +void Scene::Remove(const std::shared_ptr& object) { + std::erase(m_objects, object); +} + +void Scene::RemoveAll() { + m_objects.clear(); +} + +void Scene::Load() { + OnSceneLoaded.Invoke(); + if (m_registerBindings) { + m_registerBindings(); + } +} + +void Scene::Update() { + if (!m_pendingAdditions.empty()) { + for (auto& obj : m_pendingAdditions) { + m_objects.emplace_back(std::move(obj)); + } + m_pendingAdditions.clear(); + } + + + for (const auto& object: m_objects) { + if (object->IsActiveInHierarchy()) { + object->Update(); + } + } +} + +void Scene::FixedUpdate() { + for (const auto& object: m_objects) { + if (object->IsActiveInHierarchy()) { + object->FixedUpdate(); + } + } +} + +void Scene::LateUpdate() { + for (const auto& object: m_objects) { + if (object->IsActiveInHierarchy()) { + object->LateUpdate(); + } + } +} + +void Scene::Render(const RenderContext& ctx) const { + for (const auto& object: m_objects) { + if (object->IsActiveInHierarchy()) { + object->Render(ctx); + } + } + + // int width, height; + // SDL_GetWindowSize(Renderer::GetInstance().GetSDLWindow(), &width, &height); + // + // Renderer::GetInstance().RenderLine( + // static_cast(width / 2), 0, + // static_cast(width / 2), static_cast(height), SDL_Color(255, 0, 0, 255) // Red vertical line + // ); + // + // Renderer::GetInstance().RenderLine( + // 0, static_cast(height / 2), static_cast(width), static_cast(height / 2), SDL_Color(0, 255, 0, 255) // Green horizontal line + // ); +} + +void Scene::RenderImgui() { + +} + + +void Scene::CleanupDestroyedGameObjects() { + if (m_BeingUnloaded) { + //Scene is gone anyways, kill everything + m_objects.clear(); + return; + } + + for (const auto& gameObject: m_objects) { + //First check if a gameobjects components needs to be destroyed + gameObject->CleanupComponents(); + } + + // //Strange for loop since im deleting during looping over it + // for (auto it = m_objects.begin(); it != m_objects.end();) { + // if ((*it)->IsBeingDestroyed()) { + // it = m_objects.erase(it); + // } else { + // ++it; + // } + // } + + std::erase_if(m_objects, [] (const std::shared_ptr& gameObject) { + return gameObject->IsBeingDestroyed(); + }); +} + +void Scene::Unload() { + if (m_unregisterBindings) { + m_unregisterBindings(); + } + m_BeingUnloaded = true; +} + +void Scene::DestroyGameObjects() { + if (m_BeingUnloaded) { + + for (auto& obj : m_pendingAdditions) { + m_objects.emplace_back(std::move(obj)); + } + m_pendingAdditions.clear(); + + //Scene is gone anyways, kill everything + for (const auto& gameObject: m_objects) { + gameObject->Destroy(); + } + } else { + assert(true && "Scene is being cleared but not unloaded? Wierd"); + } +} diff --git a/destrum/src/Scene/SceneManager.cpp b/destrum/src/Scene/SceneManager.cpp new file mode 100644 index 0000000..b46b2ee --- /dev/null +++ b/destrum/src/Scene/SceneManager.cpp @@ -0,0 +1,83 @@ +#include + +#include + +#include + +void SceneManager::Update() { + m_scenes[m_ActiveSceneIndex]->Update(); +} + +void SceneManager::FixedUpdate() { + m_scenes[m_ActiveSceneIndex]->FixedUpdate(); +} + +void SceneManager::LateUpdate() { + m_scenes[m_ActiveSceneIndex]->LateUpdate(); +} + +void SceneManager::Render(const RenderContext& ctx) { + m_scenes[m_ActiveSceneIndex]->Render(ctx); +} + +void SceneManager::RenderImgui() { + m_scenes[m_ActiveSceneIndex]->RenderImgui(); +} + +void SceneManager::HandleGameObjectDestroy() { + for (const auto& scene : m_scenes) { + scene->CleanupDestroyedGameObjects(); + } +} + +void SceneManager::DestroyGameObjects() { + for (const auto& scene: m_scenes) { + scene->DestroyGameObjects(); + } +} + +void SceneManager::UnloadAllScenes() { + for (const auto& scene : m_scenes) { + scene->Unload(); + } +} + +void SceneManager::HandleSceneDestroy() { + for (auto it = m_scenes.begin(); it != m_scenes.end();) { + if ((*it)->IsBeingUnloaded()) { + it = m_scenes.erase(it); + } else { + ++it; + } + } +} + +void SceneManager::HandleScene() { + DestroyGameObjects(); + HandleGameObjectDestroy(); + HandleSceneDestroy(); +} + +void SceneManager::Destroy() { + UnloadAllScenes(); + DestroyGameObjects(); + HandleGameObjectDestroy(); + HandleSceneDestroy(); +} + +void SceneManager::SwitchScene(int index) { + // InputManager::GetInstance().RemoveAllBindings(); + + if (index < 0 || index >= static_cast(m_scenes.size())) { + throw std::out_of_range("Scene index out of range"); + } + m_scenes[m_ActiveSceneIndex]->UnloadBindings(); + m_ActiveSceneIndex = index; + m_scenes[m_ActiveSceneIndex]->LoadBindings(); +} + +Scene &SceneManager::CreateScene(const std::string &name) { + const auto &scene = std::shared_ptr(new Scene(name)); + m_scenes.push_back(scene); + return *scene; +} diff --git a/lightkeeper/include/Lightkeeper.h b/lightkeeper/include/Lightkeeper.h index 3191eea..b29d1a8 100644 --- a/lightkeeper/include/Lightkeeper.h +++ b/lightkeeper/include/Lightkeeper.h @@ -2,12 +2,28 @@ #define LIGHTKEEPER_H #include +#include -class LightKeeper : public App { +class LightKeeper final : public App { public: LightKeeper(); + virtual ~LightKeeper(); + void customInit() override; + void customUpdate(float dt) override; + void customDraw() override; + void customCleanup() override; + void onWindowResize(int newWidth, int newHeight) override; +private: + MeshCache meshCache; + MaterialCache materialCache; + GameRenderer renderer; + Camera camera{glm::vec3(0.f, 0.f, -5.f), glm::vec3(0, 1, 0)}; + + CPUMesh testMesh{}; + MeshID testMeshID; + MaterialID testMaterialID; }; #endif //LIGHTKEEPER_H diff --git a/lightkeeper/src/Lightkeeper.cpp b/lightkeeper/src/Lightkeeper.cpp index 406aacd..ab0c8fc 100644 --- a/lightkeeper/src/Lightkeeper.cpp +++ b/lightkeeper/src/Lightkeeper.cpp @@ -1,4 +1,108 @@ #include "Lightkeeper.h" -LightKeeper::LightKeeper() { +#include +#include "glm/gtx/transform.hpp" +#include "spdlog/spdlog.h" +#include + +#include "destrum/Components/MeshRendererComponent.h" +#include "destrum/Components/Rotator.h" +#include "destrum/ObjectModel/GameObject.h" + +LightKeeper::LightKeeper(): App(), renderer(meshCache, materialCache) { + +} + +LightKeeper::~LightKeeper() { +} + +void LightKeeper::customInit() { + materialCache.init(gfxDevice); + renderer.init(gfxDevice, m_params.renderSize); + + const float aspectRatio = static_cast(m_params.renderSize.x) / static_cast(m_params.renderSize.y); + camera.setAspectRatio(aspectRatio); + + auto file = AssetFS::GetInstance().ReadBytes("engine://assetfstest.txt"); + std::string fileStr(file.begin(), file.end()); + spdlog::info("Read from assetfstest.txt: {}", fileStr); + testMesh.name = "Test Mesh"; + testMesh.vertices = vertices; + testMesh.indices = indices; + + testMeshID = meshCache.addMesh(gfxDevice, testMesh); + spdlog::info("TestMesh uploaded with id: {}", testMeshID); + + const auto testimgpath = AssetFS::GetInstance().GetFullPath("engine://textures/kobe.png"); + auto testimgID = gfxDevice.loadImageFromFile(testimgpath); + spdlog::info("Test image loaded with id: {}", testimgID); + testMaterialID = materialCache.addMaterial(gfxDevice, { + .baseColor = glm::vec3(1.f), + .diffuseTexture = testimgID, + }); + spdlog::info("Test material created with id: {}", testMaterialID); + + camera.SetRotation(glm::radians(glm::vec2(90.f, 0.f))); + + auto& scene = SceneManager::GetInstance().CreateScene("Main"); + + auto testCube = std::make_shared("TestCube"); + auto meshComp = testCube->AddComponent(); + meshComp->SetMeshID(testMeshID); + meshComp->SetMaterialID(testMaterialID); + + testCube->AddComponent(10, 5); + + + scene.Add(testCube); +} + +void LightKeeper::customUpdate(float dt) { + camera.Update(dt); + SceneManager::GetInstance().Update(); +} + +void LightKeeper::customDraw() { + + renderer.beginDrawing(gfxDevice); + + const RenderContext ctx{ + .renderer = renderer, + .camera = camera, + .sceneData = { + .camera = camera, + .ambientColor = glm::vec3(0.1f), + .ambientIntensity = 0.5f, + .fogColor = glm::vec3(0.5f), + .fogDensity = 0.01f + } + }; + + SceneManager::GetInstance().Render(ctx); + renderer.endDrawing(); + + const auto cmd = gfxDevice.beginFrame(); + const auto& drawImage = renderer.getDrawImage(gfxDevice); + + renderer.draw( + cmd, gfxDevice, camera, GameRenderer::SceneData{ + camera, glm::vec3(0.1f), 0.5f, glm::vec3(0.5f), 0.01f + }); + + gfxDevice.endFrame( + cmd, drawImage, { + .clearColor = {{0.f, 0.f, 0.5f, 1.f}}, + .drawImageBlitRect = glm::ivec4{} + }); +} + +void LightKeeper::customCleanup() { + SceneManager::GetInstance().Destroy(); + renderer.cleanup(gfxDevice.getDevice().device); +} + +void LightKeeper::onWindowResize(int newWidth, int newHeight) { + renderer.resize(gfxDevice, glm::ivec2{newWidth, newHeight}); + const float aspectRatio = static_cast(newWidth) / static_cast(newHeight); + camera.setAspectRatio(aspectRatio); } diff --git a/lightkeeper/src/main.cpp b/lightkeeper/src/main.cpp index 36deab8..37f4eb1 100644 --- a/lightkeeper/src/main.cpp +++ b/lightkeeper/src/main.cpp @@ -23,6 +23,7 @@ int main(int argc, char* argv[]) { .exeDir = exeDir, }); app.run(); + app.cleanup(); SDL_Quit(); return 0; diff --git a/lightkeeper/src/main12.cpp b/lightkeeper/src/main12.cpp deleted file mode 100644 index 68e8154..0000000 --- a/lightkeeper/src/main12.cpp +++ /dev/null @@ -1,213 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -const int MAX_FRAMES_IN_FLIGHT = 2; - -struct FrameData { - VkSemaphore imageAvailable; - VkSemaphore renderFinished; - VkFence inFlight; - VkCommandBuffer commandBuffer; -}; - -SDL_Window* window = nullptr; -VkInstance instance; -vkb::PhysicalDevice physicalDevice; -VkDevice device; -vkb::Device vkbDevice; -VkQueue graphicsQueue; -VkQueue presentQueue; -VkSurfaceKHR surface; -VkSwapchainKHR swapchain; -std::vector swapchainImages; -std::vector swapchainImageViews; -VkCommandPool commandPool; -FrameData frames[MAX_FRAMES_IN_FLIGHT]; -std::vector imagesInFlight; -size_t currentFrame = 0; -VkFormat swapchainImageFormat; -VkExtent2D swapchainExtent; - -// --- Helper: allocate command buffer --- -VkCommandBuffer allocateCommandBuffer(VkDevice device, VkCommandPool pool) { - VkCommandBufferAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.commandPool = pool; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandBufferCount = 1; - - VkCommandBuffer cmd; - vkAllocateCommandBuffers(device, &allocInfo, &cmd); - return cmd; -} - -// --- Helper: record command buffer with dynamic rendering --- -void recordCommandBuffer(VkCommandBuffer cmd, VkImage image) { - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - vkBeginCommandBuffer(cmd, &beginInfo); - - VkRenderingAttachmentInfo colorAttachment{}; - colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; - colorAttachment.imageView = VK_NULL_HANDLE; // placeholder, normally swapchainImageViews[i] - colorAttachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkClearValue clearColor{ {{0.1f, 0.2f, 0.3f, 1.0f}} }; - colorAttachment.clearValue = clearColor; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - - VkRenderingInfo renderingInfo{}; - renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; - renderingInfo.renderArea.offset = {0, 0}; - renderingInfo.renderArea.extent = {800, 600}; - renderingInfo.layerCount = 1; - renderingInfo.colorAttachmentCount = 1; - renderingInfo.pColorAttachments = &colorAttachment; - - vkCmdBeginRendering(cmd, &renderingInfo); - // No draw calls, just clear - vkCmdEndRendering(cmd); - - vkEndCommandBuffer(cmd); -} - -int SDL_main(int argc, char* argv[]) { - // --- SDL Window --- - if (SDL_Init(SDL_INIT_VIDEO) != 0) - throw std::runtime_error("SDL_Init failed"); - - window = SDL_CreateWindow("Vulkan SDL Dynamic Rendering", - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, - 800, 600, - SDL_WINDOW_VULKAN); - - // --- Vulkan instance --- - volkInitialize(); - vkb::InstanceBuilder builder; - auto instRet = builder.set_app_name("SDL Dynamic Rendering") - .request_validation_layers(true) - .build(); - if (!instRet) throw std::runtime_error("Failed to create instance"); - instance = instRet.value().instance; - - if (!SDL_Vulkan_CreateSurface(window, instance, &surface)) - throw std::runtime_error("Failed to create Vulkan surface"); - - // --- Physical device & logical device --- - vkb::PhysicalDeviceSelector selector{instRet.value()}; - auto physRet = selector.set_minimum_version(1,2).set_surface(surface).require_dedicated_transfer_queue().select(); - if (!physRet) throw std::runtime_error("Failed to select physical device"); - physicalDevice = physRet.value(); - - vkb::DeviceBuilder deviceBuilder{physicalDevice}; - auto devRet = deviceBuilder.build(); - if (!devRet) throw std::runtime_error("Failed to create device"); - device = devRet.value().device; - vkbDevice = devRet.value(); - graphicsQueue = devRet.value().get_queue(vkb::QueueType::graphics).value(); - presentQueue = devRet.value().get_queue(vkb::QueueType::present).value(); - - // --- Swapchain --- - vkb::SwapchainBuilder swapchainBuilder{vkbDevice}; - auto swapRet = swapchainBuilder.set_desired_format(VkSurfaceFormatKHR{ - .format = VK_FORMAT_B8G8R8A8_SRGB, - .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, - }) - .set_desired_present_mode(VK_PRESENT_MODE_FIFO_KHR) - .set_desired_extent(800,600) - .build(); - if (!swapRet) throw std::runtime_error("Failed to create swapchain"); - - swapchain = swapRet.value().swapchain; - swapchainImages = swapRet.value().get_images().value(); - swapchainImageFormat = swapRet.value().image_format; - swapchainExtent = swapRet.value().extent; - - // --- Command pool --- - VkCommandPoolCreateInfo poolInfo{}; - poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - poolInfo.queueFamilyIndex = vkbDevice.get_queue_index(vkb::QueueType::graphics).value(); - poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool); - - // --- Frames --- - imagesInFlight.resize(swapchainImages.size(), VK_NULL_HANDLE); - for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - VkSemaphoreCreateInfo semInfo{}; - semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - vkCreateSemaphore(device, &semInfo, nullptr, &frames[i].imageAvailable); - vkCreateSemaphore(device, &semInfo, nullptr, &frames[i].renderFinished); - - VkFenceCreateInfo fenceInfo{}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - vkCreateFence(device, &fenceInfo, nullptr, &frames[i].inFlight); - - frames[i].commandBuffer = allocateCommandBuffer(device, commandPool); - } - - // --- Main loop --- - bool running = true; - while (running) { - SDL_Event event; - while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) running = false; - } - - FrameData& frame = frames[currentFrame]; - - vkWaitForFences(device, 1, &frame.inFlight, VK_TRUE, UINT64_MAX); - - uint32_t imageIndex; - vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, frame.imageAvailable, VK_NULL_HANDLE, &imageIndex); - - if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) - vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX); - imagesInFlight[imageIndex] = frame.inFlight; - - vkResetFences(device, 1, &frame.inFlight); - vkResetCommandBuffer(frame.commandBuffer, 0); - recordCommandBuffer(frame.commandBuffer, swapchainImages[imageIndex]); - - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - VkSemaphore waitSemaphores[] = { frame.imageAvailable }; - VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = waitSemaphores; - submitInfo.pWaitDstStageMask = waitStages; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &frame.commandBuffer; - VkSemaphore signalSemaphores[] = { frame.renderFinished }; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = signalSemaphores; - - vkQueueSubmit(graphicsQueue, 1, &submitInfo, frame.inFlight); - - VkPresentInfoKHR presentInfo{}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = signalSemaphores; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = &swapchain; - presentInfo.pImageIndices = &imageIndex; - - vkQueuePresentKHR(presentQueue, &presentInfo); - - currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; - } - - vkDeviceWaitIdle(device); - SDL_DestroyWindow(window); - SDL_Quit(); - return 0; -}