We got GameObjects / Components and shit
This commit is contained in:
@@ -2,6 +2,10 @@ add_subdirectory(third_party)
|
|||||||
|
|
||||||
set(SRC_FILES
|
set(SRC_FILES
|
||||||
"src/App.cpp"
|
"src/App.cpp"
|
||||||
|
"src/Event.cpp"
|
||||||
|
|
||||||
|
"src/Components/MeshRendererComponent.cpp"
|
||||||
|
"src/Components/Rotator.cpp"
|
||||||
|
|
||||||
"src/Graphics/BindlessSetManager.cpp"
|
"src/Graphics/BindlessSetManager.cpp"
|
||||||
"src/Graphics/Camera.cpp"
|
"src/Graphics/Camera.cpp"
|
||||||
@@ -24,6 +28,14 @@ set(SRC_FILES
|
|||||||
|
|
||||||
"src/Input/InputManager.cpp"
|
"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"
|
"src/FS/AssetFS.cpp"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -28,16 +28,18 @@ public:
|
|||||||
void run();
|
void run();
|
||||||
void cleanup();
|
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:
|
protected:
|
||||||
SDL_Window* window{nullptr};
|
SDL_Window* window{nullptr};
|
||||||
AppParams m_params{};
|
AppParams m_params{};
|
||||||
|
|
||||||
CPUMesh testMesh{};
|
|
||||||
MeshID testMeshID;
|
|
||||||
MaterialID testMaterialID;
|
|
||||||
|
|
||||||
GfxDevice gfxDevice;
|
GfxDevice gfxDevice;
|
||||||
GameRenderer renderer;
|
|
||||||
|
|
||||||
Camera camera{glm::vec3(0.f, 0.f, -5.f), glm::vec3(0, 1, 0)};
|
Camera camera{glm::vec3(0.f, 0.f, -5.f), glm::vec3(0, 1, 0)};
|
||||||
|
|
||||||
@@ -47,10 +49,9 @@ protected:
|
|||||||
bool isRunning{false};
|
bool isRunning{false};
|
||||||
bool gamePaused{false};
|
bool gamePaused{false};
|
||||||
|
|
||||||
bool frameLimit{true};
|
bool frameLimit{false};
|
||||||
float frameTime{0.f};
|
float frameTime{0.f};
|
||||||
float avgFPS{0.f};
|
float avgFPS{0.f};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
26
destrum/include/destrum/Components/MeshRendererComponent.h
Normal file
26
destrum/include/destrum/Components/MeshRendererComponent.h
Normal 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
|
||||||
29
destrum/include/destrum/Components/Rotator.h
Normal file
29
destrum/include/destrum/Components/Rotator.h
Normal 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
|
||||||
104
destrum/include/destrum/Event.h
Normal file
104
destrum/include/destrum/Event.h
Normal 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
|
||||||
@@ -27,6 +27,8 @@ public:
|
|||||||
const std::vector<MeshDrawCommand>& drawCommands,
|
const std::vector<MeshDrawCommand>& drawCommands,
|
||||||
const std::vector<std::size_t>& sortedDrawCommands);
|
const std::vector<std::size_t>& sortedDrawCommands);
|
||||||
|
|
||||||
|
void cleanup(VkDevice device);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VkPipelineLayout m_pipelineLayout;
|
VkPipelineLayout m_pipelineLayout;
|
||||||
|
|||||||
12
destrum/include/destrum/Graphics/RenderContext.h
Normal file
12
destrum/include/destrum/Graphics/RenderContext.h
Normal 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
|
||||||
@@ -30,7 +30,7 @@ public:
|
|||||||
void endDrawing();
|
void endDrawing();
|
||||||
|
|
||||||
void draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData);
|
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);
|
void drawMesh(MeshID id, const glm::mat4& transform, MaterialID materialId);
|
||||||
const GPUImage& getDrawImage(const GfxDevice& gfx_device) const;
|
const GPUImage& getDrawImage(const GfxDevice& gfx_device) const;
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ static std::vector<CPUMesh::Vertex> vertices = {
|
|||||||
|
|
||||||
static std::vector<uint32_t> indices = {
|
static std::vector<uint32_t> indices = {
|
||||||
0, 1, 2, 2, 3, 0, // Front (+Z)
|
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)
|
8, 9,10, 10,11, 8, // Right (+X)
|
||||||
12,13,14, 14,15,12, // Left (-X)
|
12,13,14, 14,15,12, // Left (-X)
|
||||||
16,17,18, 18,19,16, // Top (+Y)
|
16,17,18, 18,19,16, // Top (+Y)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public:
|
|||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
void BeginFrame();
|
void BeginFrame();
|
||||||
void ProcessEvent(const SDL_Event& e);
|
bool ProcessEvent(const SDL_Event& e);
|
||||||
void EndFrame() {}
|
void EndFrame() {}
|
||||||
|
|
||||||
bool IsKeyDown(SDL_Scancode sc) const;
|
bool IsKeyDown(SDL_Scancode sc) const;
|
||||||
|
|||||||
48
destrum/include/destrum/ObjectModel/Component.h
Normal file
48
destrum/include/destrum/ObjectModel/Component.h
Normal 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
|
||||||
121
destrum/include/destrum/ObjectModel/GameObject.h
Normal file
121
destrum/include/destrum/ObjectModel/GameObject.h
Normal 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();
|
||||||
|
};
|
||||||
31
destrum/include/destrum/ObjectModel/Object.h
Normal file
31
destrum/include/destrum/ObjectModel/Object.h
Normal 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
|
||||||
96
destrum/include/destrum/ObjectModel/Transform.h
Normal file
96
destrum/include/destrum/ObjectModel/Transform.h
Normal 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{};
|
||||||
|
};
|
||||||
84
destrum/include/destrum/Scene/Scene.h
Normal file
84
destrum/include/destrum/Scene/Scene.h
Normal 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
|
||||||
56
destrum/include/destrum/Scene/SceneManager.h
Normal file
56
destrum/include/destrum/Scene/SceneManager.h
Normal 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
|
||||||
@@ -7,8 +7,7 @@
|
|||||||
#include "glm/gtx/transform.hpp"
|
#include "glm/gtx/transform.hpp"
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
App::App(): renderer{meshCache, materialCache} {
|
App::App() {}
|
||||||
}
|
|
||||||
|
|
||||||
void App::init(const AppParams& params) {
|
void App::init(const AppParams& params) {
|
||||||
m_params = params;
|
m_params = params;
|
||||||
@@ -34,132 +33,122 @@ void App::init(const AppParams& params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gfxDevice.init(window, params.appName, false);
|
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<float>(params.renderSize.x) / static_cast<float>(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();
|
InputManager::GetInstance().Init();
|
||||||
|
|
||||||
|
customInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::run() {
|
void App::run() {
|
||||||
const float FPS = 30.f;
|
using clock = std::chrono::steady_clock;
|
||||||
const float dt = 1.f / FPS;
|
|
||||||
|
|
||||||
auto prevTime = std::chrono::high_resolution_clock::now();
|
const float targetHz = 60.0f;
|
||||||
float accumulator = dt; // so that we get at least 1 update before render
|
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;
|
isRunning = true;
|
||||||
while (isRunning) {
|
while (isRunning) {
|
||||||
const auto newTime = std::chrono::high_resolution_clock::now();
|
const auto frameStart = clock::now();
|
||||||
frameTime = std::chrono::duration<float>(newTime - prevTime).count();
|
float frameTimeSec = std::chrono::duration<float>(frameStart - prevTime).count();
|
||||||
|
prevTime = frameStart;
|
||||||
|
|
||||||
if (frameTime > 0.07f && frameTime < 5.f) {
|
if (frameTimeSec > 0.07f && frameTimeSec < 5.f) {
|
||||||
// if >=5.f - debugging?
|
spdlog::warn("Frame drop detected, time: {:.4f}s", frameTimeSec);
|
||||||
spdlog::warn("Frame drop detected, time: {:.4f}s", frameTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
accumulator += frameTime;
|
if (frameTimeSec > maxFrameTime) frameTimeSec = maxFrameTime;
|
||||||
prevTime = newTime;
|
if (frameTimeSec < 0.0f) frameTimeSec = 0.0f;
|
||||||
|
|
||||||
float newFPS = 1.f / frameTime;
|
accumulator += frameTimeSec;
|
||||||
if (newFPS == std::numeric_limits<float>::infinity()) {
|
|
||||||
// can happen when frameTime == 0
|
if (frameTimeSec > 0.0f) {
|
||||||
newFPS = 0;
|
const float newFPS = 1.0f / frameTimeSec;
|
||||||
}
|
|
||||||
avgFPS = std::lerp(avgFPS, newFPS, 0.1f);
|
avgFPS = std::lerp(avgFPS, newFPS, 0.1f);
|
||||||
|
|
||||||
if (accumulator > 10 * dt) {
|
|
||||||
// game stopped for debug
|
|
||||||
accumulator = dt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (accumulator >= dt) {
|
|
||||||
InputManager::GetInstance().BeginFrame();
|
InputManager::GetInstance().BeginFrame();
|
||||||
|
camera.Update(dt);
|
||||||
|
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
if (event.type == SDL_QUIT) {
|
if (event.type == SDL_QUIT) {
|
||||||
isRunning = false;
|
isRunning = false;
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
if (event.type == SDL_WINDOWEVENT) {
|
if (event.type == SDL_WINDOWEVENT) {
|
||||||
switch (event.window.event) {
|
switch (event.window.event) {
|
||||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||||
/* fallthrough */
|
|
||||||
case SDL_WINDOWEVENT_RESIZED:
|
case SDL_WINDOWEVENT_RESIZED:
|
||||||
m_params.windowSize = {event.window.data1, event.window.data2};
|
m_params.windowSize = { event.window.data1, event.window.data2 };
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InputManager::GetInstance().ProcessEvent(event);
|
if (InputManager::GetInstance().ProcessEvent(event)) {
|
||||||
|
isRunning = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (!isRunning) break;
|
||||||
|
customUpdate(dt);
|
||||||
|
|
||||||
camera.Update(dt);
|
// ---- Swapchain resize check once per frame ----
|
||||||
if (gfxDevice.needsSwapchainRecreate()) {
|
if (gfxDevice.needsSwapchainRecreate()) {
|
||||||
spdlog::info("Recreating swapchain to size: {}x{}", m_params.windowSize.x, m_params.windowSize.y);
|
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);
|
gfxDevice.recreateSwapchain(m_params.windowSize.x, m_params.windowSize.y);
|
||||||
renderer.resize(gfxDevice, { m_params.windowSize.x, m_params.windowSize.y });
|
onWindowResize(m_params.windowSize.x, m_params.windowSize.y);
|
||||||
float aspectRatio = float(m_params.windowSize.x) / float(m_params.windowSize.y);
|
|
||||||
camera.setAspectRatio(aspectRatio);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- 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;
|
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()) {
|
if (!gfxDevice.needsSwapchainRecreate()) {
|
||||||
glm::mat4 objMatrix = glm::mat4(1.f);
|
// glm::mat4 objMatrix = glm::translate(glm::mat4(1.f), glm::vec3(0.f, -3.0f, 0.f));
|
||||||
objMatrix = glm::translate(objMatrix, glm::vec3(0.f, -3.0f, 0.f));
|
//
|
||||||
renderer.beginDrawing(gfxDevice);
|
// renderer.beginDrawing(gfxDevice);
|
||||||
renderer.drawMesh(testMeshID, glm::mat4(1.f), testMaterialID);
|
// renderer.drawMesh(testMeshID, glm::mat4(1.f), testMaterialID);
|
||||||
renderer.drawMesh(testMeshID, objMatrix, 0);
|
// renderer.drawMesh(testMeshID, objMatrix, 0);
|
||||||
renderer.endDrawing();
|
// renderer.endDrawing();
|
||||||
|
//
|
||||||
|
// const auto cmd = gfxDevice.beginFrame();
|
||||||
auto cmd = gfxDevice.beginFrame();
|
// const auto& drawImage = renderer.getDrawImage(gfxDevice);
|
||||||
const auto& drawImage = renderer.getDrawImage(gfxDevice);
|
//
|
||||||
renderer.draw(cmd, gfxDevice, camera, GameRenderer::SceneData{
|
// renderer.draw(cmd, gfxDevice, camera, GameRenderer::SceneData{
|
||||||
camera, glm::vec3(0.1f), 0.5f, glm::vec3(0.5f), 0.01f
|
// 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}},
|
// gfxDevice.endFrame(cmd, drawImage, {
|
||||||
.drawImageBlitRect = glm::ivec4{}}
|
// .clearColor = {{0.f, 0.f, 0.5f, 1.f}},
|
||||||
);
|
// .drawImageBlitRect = glm::ivec4{}
|
||||||
|
// });
|
||||||
|
customDraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Frame cap (if you still want it) ----
|
||||||
if (frameLimit) {
|
if (frameLimit) {
|
||||||
// Delay to not overload the CPU
|
const auto targetEnd = frameStart + std::chrono::duration_cast<clock::duration>(
|
||||||
const auto now = std::chrono::high_resolution_clock::now();
|
std::chrono::duration<float>(dt)
|
||||||
const auto frameTime = std::chrono::duration<float>(now - prevTime).count();
|
);
|
||||||
if (dt > frameTime) {
|
std::this_thread::sleep_until(targetEnd);
|
||||||
SDL_Delay(static_cast<std::uint32_t>(dt - frameTime));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
gfxDevice.waitIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::cleanup() {
|
void App::cleanup() {
|
||||||
|
|||||||
19
destrum/src/Components/MeshRendererComponent.cpp
Normal file
19
destrum/src/Components/MeshRendererComponent.cpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include <destrum/Components/MeshRendererComponent.h>
|
||||||
|
#include <destrum/ObjectModel/Transform.h>
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
destrum/src/Components/Rotator.cpp
Normal file
17
destrum/src/Components/Rotator.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include <destrum/Components/Rotator.h>
|
||||||
|
|
||||||
|
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<float>(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));
|
||||||
|
|
||||||
|
}
|
||||||
1
destrum/src/Event.cpp
Normal file
1
destrum/src/Event.cpp
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include <destrum/Event.h>
|
||||||
@@ -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.
|
// 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).
|
// 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.
|
// If your NormalizeAxis maps triggers weirdly, swap to raw event value approach.
|
||||||
const float lt = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
|
const float lt = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
|
||||||
const float rt = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
|
const float rt = input.GetPadAxis(pad0, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
|
||||||
const float vertical = (rt - lt);
|
const float vertical = (rt - lt);
|
||||||
padMove += m_up * vertical;
|
padMove += m_up * vertical;
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ void Camera::Update(float deltaTime) {
|
|||||||
|
|
||||||
const float padLookSpeed = 2.2f; // radians/sec at full deflection
|
const float padLookSpeed = 2.2f; // radians/sec at full deflection
|
||||||
m_yaw += rx * padLookSpeed * deltaTime;
|
m_yaw += rx * padLookSpeed * deltaTime;
|
||||||
m_pitch -= ry * padLookSpeed * deltaTime;
|
m_pitch += ry * padLookSpeed * deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clamp pitch again after modifications
|
// Clamp pitch again after modifications
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync)
|
|||||||
.set_required_features_12(features12)
|
.set_required_features_12(features12)
|
||||||
.set_required_features_13(features13)
|
.set_required_features_13(features13)
|
||||||
.set_surface(surface)
|
.set_surface(surface)
|
||||||
|
.prefer_gpu_device_type(vkb::PreferredDeviceType::discrete)
|
||||||
.select()
|
.select()
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
@@ -177,7 +178,7 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E
|
|||||||
auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
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);
|
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL);
|
||||||
swapchainLayout = VK_IMAGE_LAYOUT_GENERAL;
|
swapchainLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
|
|
||||||
|
|||||||
@@ -127,3 +127,9 @@ void MeshPipeline::draw(VkCommandBuffer cmd,
|
|||||||
vkCmdDrawIndexed(cmd, mesh.numIndices, 1, 0, 0, 0);
|
vkCmdDrawIndexed(cmd, mesh.numIndices, 1, 0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MeshPipeline::cleanup(VkDevice device) {
|
||||||
|
vkDestroyPipelineLayout(device, m_pipelineLayout, nullptr);
|
||||||
|
m_pipeline.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
void GameRenderer::drawMesh(MeshID id, const glm::mat4& transform, MaterialID materialId) {
|
||||||
|
|||||||
@@ -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) {
|
switch (e.type) {
|
||||||
|
case SDL_QUIT: {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
case SDL_KEYDOWN: {
|
case SDL_KEYDOWN: {
|
||||||
if (e.key.repeat) break;
|
if (e.key.repeat) break;
|
||||||
SDL_Scancode sc = e.key.keysym.scancode;
|
SDL_Scancode sc = e.key.keysym.scancode;
|
||||||
@@ -196,6 +199,7 @@ void InputManager::ProcessEvent(const SDL_Event& e) {
|
|||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputManager::IsKeyDown(SDL_Scancode sc) const { return m_keysDown.count(sc) != 0; }
|
bool InputManager::IsKeyDown(SDL_Scancode sc) const { return m_keysDown.count(sc) != 0; }
|
||||||
|
|||||||
44
destrum/src/ObjectModel/Component.cpp
Normal file
44
destrum/src/ObjectModel/Component.cpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#include <destrum/ObjectModel/Component.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <destrum/ObjectModel/GameObject.h>
|
||||||
|
// #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) {
|
||||||
|
|
||||||
|
}
|
||||||
96
destrum/src/ObjectModel/GameObject.cpp
Normal file
96
destrum/src/ObjectModel/GameObject.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#include <destrum/ObjectModel/GameObject.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#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>& component) {
|
||||||
|
return component->IsBeingDestroyed();
|
||||||
|
});
|
||||||
|
}
|
||||||
22
destrum/src/ObjectModel/Object.cpp
Normal file
22
destrum/src/ObjectModel/Object.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#include <destrum/ObjectModel/Object.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#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)) {
|
||||||
|
}
|
||||||
253
destrum/src/ObjectModel/Transform.cpp
Normal file
253
destrum/src/ObjectModel/Transform.cpp
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
#include <destrum/ObjectModel/Transform.h>
|
||||||
|
#include <destrum/ObjectModel/GameObject.h>
|
||||||
|
|
||||||
|
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*>& Transform::GetChildren() const {
|
||||||
|
// std::vector<Transform*> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
149
destrum/src/Scene/Scene.cpp
Normal file
149
destrum/src/Scene/Scene.cpp
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
#include <destrum/Scene/Scene.h>
|
||||||
|
#include <destrum/ObjectModel/GameObject.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <SDL_scancode.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
|
// #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<GameObject> object) {
|
||||||
|
// m_objects.emplace_back(std::move(object));
|
||||||
|
m_pendingAdditions.emplace_back(std::move(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::Remove(const std::shared_ptr<GameObject>& 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<float>(width / 2), 0,
|
||||||
|
// static_cast<float>(width / 2), static_cast<float>(height), SDL_Color(255, 0, 0, 255) // Red vertical line
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// Renderer::GetInstance().RenderLine(
|
||||||
|
// 0, static_cast<float>(height / 2), static_cast<float>(width), static_cast<float>(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>& 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
83
destrum/src/Scene/SceneManager.cpp
Normal file
83
destrum/src/Scene/SceneManager.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#include <destrum/Scene/SceneManager.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <destrum/Scene/Scene.h>
|
||||||
|
|
||||||
|
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<int>(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<Scene>(new Scene(name));
|
||||||
|
m_scenes.push_back(scene);
|
||||||
|
return *scene;
|
||||||
|
}
|
||||||
@@ -2,12 +2,28 @@
|
|||||||
#define LIGHTKEEPER_H
|
#define LIGHTKEEPER_H
|
||||||
|
|
||||||
#include <destrum/App.h>
|
#include <destrum/App.h>
|
||||||
|
#include <destrum/Scene/SceneManager.h>
|
||||||
|
|
||||||
class LightKeeper : public App {
|
class LightKeeper final : public App {
|
||||||
public:
|
public:
|
||||||
LightKeeper();
|
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
|
#endif //LIGHTKEEPER_H
|
||||||
|
|||||||
@@ -1,4 +1,108 @@
|
|||||||
#include "Lightkeeper.h"
|
#include "Lightkeeper.h"
|
||||||
|
|
||||||
LightKeeper::LightKeeper() {
|
#include <destrum/FS/AssetFS.h>
|
||||||
|
#include "glm/gtx/transform.hpp"
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include <destrum/Scene/Scene.h>
|
||||||
|
|
||||||
|
#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<float>(m_params.renderSize.x) / static_cast<float>(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<GameObject>("TestCube");
|
||||||
|
auto meshComp = testCube->AddComponent<MeshRendererComponent>();
|
||||||
|
meshComp->SetMeshID(testMeshID);
|
||||||
|
meshComp->SetMaterialID(testMaterialID);
|
||||||
|
|
||||||
|
testCube->AddComponent<Rotator>(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<float>(newWidth) / static_cast<float>(newHeight);
|
||||||
|
camera.setAspectRatio(aspectRatio);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ int main(int argc, char* argv[]) {
|
|||||||
.exeDir = exeDir,
|
.exeDir = exeDir,
|
||||||
});
|
});
|
||||||
app.run();
|
app.run();
|
||||||
|
app.cleanup();
|
||||||
|
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -1,213 +0,0 @@
|
|||||||
#include <SDL.h>
|
|
||||||
#include <SDL_vulkan.h>
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <VkBootstrap.h>
|
|
||||||
#include <vk_mem_alloc.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include <volk.h>
|
|
||||||
|
|
||||||
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<VkImage> swapchainImages;
|
|
||||||
std::vector<VkImageView> swapchainImageViews;
|
|
||||||
VkCommandPool commandPool;
|
|
||||||
FrameData frames[MAX_FRAMES_IN_FLIGHT];
|
|
||||||
std::vector<VkFence> 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;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user