We got GameObjects / Components and shit
This commit is contained in:
@@ -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<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();
|
||||
|
||||
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<float>(newTime - prevTime).count();
|
||||
const auto frameStart = clock::now();
|
||||
float frameTimeSec = std::chrono::duration<float>(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<float>::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<float>(now - prevTime).count();
|
||||
if (dt > frameTime) {
|
||||
SDL_Delay(static_cast<std::uint32_t>(dt - frameTime));
|
||||
}
|
||||
const auto targetEnd = frameStart + std::chrono::duration_cast<clock::duration>(
|
||||
std::chrono::duration<float>(dt)
|
||||
);
|
||||
std::this_thread::sleep_until(targetEnd);
|
||||
}
|
||||
}
|
||||
gfxDevice.waitIdle();
|
||||
}
|
||||
|
||||
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.
|
||||
// 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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; }
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user