diff --git a/CMakeLists.txt b/CMakeLists.txt index 96c84ce..aeeb4d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -313,6 +313,12 @@ if(BUILD_GAME) src/engine/renderer/loader/async/AssetLoadingProgressEvent.h src/engine/core/scenes/Scene.cpp src/engine/core/scenes/Scene.h + src/engine/core/scenes/SceneManager.cpp + src/engine/core/scenes/SceneManager.h + src/engine/core/scenes/SplashScreen.cpp + src/engine/core/scenes/SplashScreen.h + src/engine/core/scenes/SplashScreenLayer.cpp + src/engine/core/scenes/SplashScreenLayer.h ) target_compile_options(Dicewars_Siedler PRIVATE -Wall diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..71ed491 Binary files /dev/null and b/assets/logo.png differ diff --git a/src/engine/core/Application.cpp b/src/engine/core/Application.cpp index 2981f32..400c097 100644 --- a/src/engine/core/Application.cpp +++ b/src/engine/core/Application.cpp @@ -5,10 +5,10 @@ #include "Application.h" #include "EngineTime.h" -#include "../layer/Layer.h" #include "../platform/glfw/InputManager.h" #include "inputsOutputs/stateControl/StateRegistry.h" - +#include "../core/scenes/SceneManager.h" +#include "../core/scenes/Scene.h" Application* Application::instance = nullptr; void Application::updateTime() { @@ -20,14 +20,10 @@ void Application::updateTime() { // printf("Frametime: %f\n", EngineTime::deltaTime); } -void Application::pushLayer(Layer* layer) { - layers.push_back(layer); - layer->onAttach(); -} - Application::Application() { instance = this; + sceneManager = std::make_unique(); WindowProps window_props = WindowProps(); window_props.Width = 1280; @@ -50,11 +46,9 @@ void Application::run() { { window->OnUpdate(); updateTime(); - for (Layer* layer : layers) - layer->onUpdate(); - for (Layer* layer : layers) - layer->onRender(); + sceneManager->update(); + sceneManager->render(); InputManager::update(); diff --git a/src/engine/core/Application.h b/src/engine/core/Application.h index 28be5b5..627e651 100644 --- a/src/engine/core/Application.h +++ b/src/engine/core/Application.h @@ -15,7 +15,8 @@ #include "inputsOutputs/stateControl/states/State.h" -class Layer; + +class SceneManager; class Application { @@ -31,22 +32,17 @@ public: std::unique_ptr keyboard; std::unique_ptr mouse; std::unique_ptr stateManager; - std::shared_ptr gameState; + std::unique_ptr sceneManager; private: bool running = true; std::unique_ptr window; - static Application* instance; - std::vector layers; void updateTime(); float lastFrame; - -protected: - void pushLayer(Layer* layer); }; diff --git a/src/engine/core/gui/uiComponent/layout/LayoutEngine.h b/src/engine/core/gui/uiComponent/layout/LayoutEngine.h index 8892779..986e319 100644 --- a/src/engine/core/gui/uiComponent/layout/LayoutEngine.h +++ b/src/engine/core/gui/uiComponent/layout/LayoutEngine.h @@ -21,12 +21,16 @@ namespace LayoutEngine { std::vector children; }; - inline float resolveDim(const SizeValue size, float parentSize, float axisLength) { + inline float resolveDim(const SizeValue size, float parentSize, float axisLength, float screenWidth, float screenHeight) { switch (size.unit) { case SizeUnit::Percent: return size.value * parentSize; case SizeUnit::Pixels: return size.value / axisLength; // has to be normalized! + case SizeUnit::Vmin: { + float vmin = std::min(screenWidth, screenHeight); + return (size.value * vmin) / axisLength; // normalisiert + } default: return 0.0f; } @@ -34,10 +38,10 @@ namespace LayoutEngine { inline Dimensions computeSelfDimensions(const NodeLayout &node, const Dimensions &parent, float screenWidth, float screenHeight) { Dimensions d = {}; - d.x = parent.x + resolveDim(node.layoutStyle.margin.left, parent.width, screenWidth); - d.y = parent.y + resolveDim(node.layoutStyle.margin.top, parent.height, screenHeight); - d.width = resolveDim(node.layoutStyle.width, parent.width, screenWidth); - d.height = resolveDim(node.layoutStyle.height, parent.height, screenHeight); + d.x = parent.x + resolveDim(node.layoutStyle.margin.left, parent.width, screenWidth, screenWidth, screenHeight); + d.y = parent.y + resolveDim(node.layoutStyle.margin.top, parent.height, screenHeight, screenWidth, screenHeight); + d.width = resolveDim(node.layoutStyle.width, parent.width, screenWidth, screenWidth, screenHeight); + d.height = resolveDim(node.layoutStyle.height, parent.height, screenHeight, screenWidth, screenHeight); return d; } @@ -81,10 +85,10 @@ namespace LayoutEngine { result.children.resize(node.children.size()); //Padding auflösen - float padLeft = resolveDim(node.layoutStyle.padding.left, result.self.width, screenWidth); - float padRight = resolveDim(node.layoutStyle.padding.right, result.self.width, screenWidth); - float padTop = resolveDim(node.layoutStyle.padding.top, result.self.height, screenHeight); - float padBottom = resolveDim(node.layoutStyle.padding.bottom, result.self.height, screenHeight); + float padLeft = resolveDim(node.layoutStyle.padding.left, result.self.width, screenWidth, screenWidth, screenHeight); + float padRight = resolveDim(node.layoutStyle.padding.right, result.self.width, screenWidth, screenWidth, screenHeight); + float padTop = resolveDim(node.layoutStyle.padding.top, result.self.height, screenHeight, screenWidth, screenHeight); + float padBottom = resolveDim(node.layoutStyle.padding.bottom, result.self.height, screenHeight, screenWidth, screenHeight); // Available space for children Dimensions innerArea = { @@ -105,8 +109,8 @@ namespace LayoutEngine { const float mainSize = isColumn ? childLayout.self.height : childLayout.self.width; const float marginMain = isColumn - ? resolveDim(child.layoutStyle.margin.top, parent.height, screenHeight) + resolveDim(child.layoutStyle.margin.bottom, parent.height, screenHeight) - : resolveDim(child.layoutStyle.margin.left, parent.width, screenWidth) + resolveDim(child.layoutStyle.margin.right, parent.width, screenWidth); + ? resolveDim(child.layoutStyle.margin.top, parent.height, screenHeight, screenWidth, screenHeight) + resolveDim(child.layoutStyle.margin.bottom, parent.height, screenHeight, screenWidth, screenHeight) + : resolveDim(child.layoutStyle.margin.left, parent.width, screenWidth, screenWidth, screenHeight) + resolveDim(child.layoutStyle.margin.right, parent.width, screenWidth, screenWidth, screenHeight); totalMainSize += mainSize + marginMain; } @@ -128,15 +132,15 @@ namespace LayoutEngine { cursor += gap; if (isColumn) { - float marginTop = resolveDim(child.layoutStyle.margin.top, innerArea.height, screenHeight); - float marginBottom = resolveDim(child.layoutStyle.margin.bottom, innerArea.height, screenHeight); + float marginTop = resolveDim(child.layoutStyle.margin.top, innerArea.height, screenHeight, screenWidth, screenHeight); + float marginBottom = resolveDim(child.layoutStyle.margin.bottom, innerArea.height, screenHeight, screenWidth, screenHeight); offsetLayout(childLayout, 0.0f, cursor); cursor += childLayout.self.height + marginTop + marginBottom; if (i + 1 < node.children.size()) cursor += node.layoutStyle.gap; } else { - float marginLeft = resolveDim(child.layoutStyle.margin.left, innerArea.width, screenWidth); - float marginRight = resolveDim(child.layoutStyle.margin.right, innerArea.width, screenWidth); + float marginLeft = resolveDim(child.layoutStyle.margin.left, innerArea.width, screenWidth, screenWidth, screenHeight); + float marginRight = resolveDim(child.layoutStyle.margin.right, innerArea.width, screenWidth, screenWidth, screenHeight); offsetLayout(childLayout, cursor, 0.0f); cursor += childLayout.self.width + marginLeft + marginRight; if (i + 1 < node.children.size()) @@ -149,7 +153,7 @@ namespace LayoutEngine { auto& childLayout = result.children[i]; if (isColumn) { - float marginLeft = resolveDim(child.layoutStyle.margin.left, innerArea.width, screenWidth); + float marginLeft = resolveDim(child.layoutStyle.margin.left, innerArea.width, screenWidth, screenWidth, screenHeight); float remainingCross = innerArea.width - childLayout.self.width; float oldX = childLayout.self.x; switch (node.layoutStyle.alignItems) { @@ -163,7 +167,7 @@ namespace LayoutEngine { } offsetChildLayouts(childLayout, childLayout.self.x - oldX, 0.0f); } else { - float marginTop = resolveDim(child.layoutStyle.margin.top, innerArea.height, screenHeight); + float marginTop = resolveDim(child.layoutStyle.margin.top, innerArea.height, screenHeight, screenWidth, screenHeight); float remainingCross = innerArea.height - childLayout.self.height; float oldY = childLayout.self.y; switch (node.layoutStyle.alignItems) { diff --git a/src/engine/core/gui/uiComponent/layout/LayoutStyle.h b/src/engine/core/gui/uiComponent/layout/LayoutStyle.h index 7bb9da6..83d88f2 100644 --- a/src/engine/core/gui/uiComponent/layout/LayoutStyle.h +++ b/src/engine/core/gui/uiComponent/layout/LayoutStyle.h @@ -7,7 +7,8 @@ enum class SizeUnit { Pixels, - Percent + Percent, + Vmin }; struct SizeValue { diff --git a/src/engine/core/scenes/Scene.cpp b/src/engine/core/scenes/Scene.cpp index 7ace207..4dc2bff 100644 --- a/src/engine/core/scenes/Scene.cpp +++ b/src/engine/core/scenes/Scene.cpp @@ -17,5 +17,6 @@ void Scene::onRender() { } void Scene::addLayer(std::unique_ptr layer) { + layer->onAttach(); layers.push_back(std::move(layer)); } diff --git a/src/engine/core/scenes/Scene.h b/src/engine/core/scenes/Scene.h index 072805e..4334417 100644 --- a/src/engine/core/scenes/Scene.h +++ b/src/engine/core/scenes/Scene.h @@ -8,13 +8,19 @@ #include #include "../../layer/Layer.h" +#include "../../renderer/loader/async/AssetLoader.h" class Scene { public: virtual ~Scene() = default; virtual void onEnter() {} //Layer registrieren - virtual void onExit() {} //Aufräumen, Daten unloaden + virtual void onExit() { + for (const auto& layer : layers) { + layer->onDetach(); + } + } //Aufräumen, Daten unloaden + virtual std::vector getRequiredAssets() {return {};} void onUpdate(); void onRender(); diff --git a/src/engine/core/scenes/SceneManager.cpp b/src/engine/core/scenes/SceneManager.cpp new file mode 100644 index 0000000..61ed7f3 --- /dev/null +++ b/src/engine/core/scenes/SceneManager.cpp @@ -0,0 +1,47 @@ +// +// Created by sebastian on 21.04.26. +// + +#include "SceneManager.h" + +#include "Scene.h" +#include "spdlog/spdlog.h" + +void SceneManager::switchTo(std::unique_ptr next) { + nextScene = std::move(next); + std::vector requestedAssets = nextScene->getRequiredAssets(); + spdlog::debug("Required assets: {}", requestedAssets.size()); + for (AssetRequest& request : requestedAssets) { + assetLoader.scheduleAsset(request); + } + + assetLoader.start(); +} + +void SceneManager::update() { + if (nextScene) { + assetLoader.processUploadQueue(2); + + if (assetLoader.getProgress().isDone()) { + if (currentScene) { + currentScene->onExit(); + } + currentScene = std::move(nextScene); + nextScene = nullptr; + currentScene->onEnter(); + assetLoader.stop(); + } + } + + if (currentScene) { + currentScene->onUpdate(); + } +} + +void SceneManager::render() { + if (currentScene) { + currentScene->onRender(); + } +} + +SceneManager::~SceneManager() = default; diff --git a/src/engine/core/scenes/SceneManager.h b/src/engine/core/scenes/SceneManager.h new file mode 100644 index 0000000..2621d5c --- /dev/null +++ b/src/engine/core/scenes/SceneManager.h @@ -0,0 +1,29 @@ +// +// Created by sebastian on 21.04.26. +// + +#ifndef SCENEMANAGER_H +#define SCENEMANAGER_H +#include + +#include "../../renderer/loader/async/AssetLoader.h"" +class Scene; + +class SceneManager { +public: + void switchTo(std::unique_ptr next); + void update(); + void render(); + ~SceneManager(); +private: + std::unique_ptr currentScene; + std::unique_ptr nextScene; + + AssetLoader assetLoader; + + bool isTransitioning = false; +}; + + + +#endif //SCENEMANAGER_H diff --git a/src/engine/core/scenes/SplashScreen.cpp b/src/engine/core/scenes/SplashScreen.cpp new file mode 100644 index 0000000..837e89c --- /dev/null +++ b/src/engine/core/scenes/SplashScreen.cpp @@ -0,0 +1,21 @@ +// +// Created by sebastian on 21.04.26. +// + +#include "SplashScreen.h" + +#include "SplashScreenLayer.h" + +std::vector SplashScreen::getRequiredAssets() { + std::vector requests; + requests.push_back(TextureRequest("logo", "assets/logo.png")); + return requests; +} + +void SplashScreen::onEnter() { + addLayer(std::make_unique()); +} + +void SplashScreen::onExit() { + Scene::onExit(); +} diff --git a/src/engine/core/scenes/SplashScreen.h b/src/engine/core/scenes/SplashScreen.h new file mode 100644 index 0000000..8f0b7d6 --- /dev/null +++ b/src/engine/core/scenes/SplashScreen.h @@ -0,0 +1,20 @@ +// +// Created by sebastian on 21.04.26. +// + +#ifndef SPLASHSCREEN_H +#define SPLASHSCREEN_H +#include "Scene.h" + + +class SplashScreen : public Scene { +public: + std::vector getRequiredAssets() override; + void onEnter() override; + void onExit() override; + +}; + + + +#endif //SPLASHSCREEN_H diff --git a/src/engine/core/scenes/SplashScreenLayer.cpp b/src/engine/core/scenes/SplashScreenLayer.cpp new file mode 100644 index 0000000..03c8b31 --- /dev/null +++ b/src/engine/core/scenes/SplashScreenLayer.cpp @@ -0,0 +1,49 @@ +// +// Created by sebastian on 21.04.26. +// + +#include "SplashScreenLayer.h" + +#include "../../renderer/loader/AssetManager.h" +#include "../../renderer/GUIRenderer.h" +#include "../gui/uiComponent/UiImage.h" +#include "../gui/uiMain/UiContainer.h" +#include "spdlog/spdlog.h" + +SplashScreenLayer::SplashScreenLayer() { + guiRenderer = std::make_unique(Loader::instance()); +} + +void SplashScreenLayer::onRender() { + UiRenderBundle renderBundle; + if (rootContainer) { + rootContainer->collectRenderData(renderBundle); + } + + auto guis = renderBundle.getGUITextures(); + guiRenderer->render(guis); +} + +void SplashScreenLayer::onUpdate() { + Dimensions rootParent {0.0, 0.0, 1.0, 1.0f}; + rootContainer->uiPositioner.compute(rootParent); +} + +void SplashScreenLayer::onAttach() { + rootContainer = std::make_unique(); + + auto logoLayoutStyle = LayoutStyle(); + logoLayoutStyle.width = SizeValue(0.5f, SizeUnit::Vmin); + logoLayoutStyle.height = SizeValue(0.5f, SizeUnit::Vmin); + + rootContainer->getLayoutStyle().justifyContent = JustifyContent::Center; + rootContainer->getLayoutStyle().alignItems = AlignItems::Center; + + rootContainer->addChild(std::make_unique(AssetManager::getTexture("logo")->getTextureID(), logoLayoutStyle)); +} + +void SplashScreenLayer::onDetach() { + Layer::onDetach(); +} + +SplashScreenLayer::~SplashScreenLayer() = default; diff --git a/src/engine/core/scenes/SplashScreenLayer.h b/src/engine/core/scenes/SplashScreenLayer.h new file mode 100644 index 0000000..23bf4a9 --- /dev/null +++ b/src/engine/core/scenes/SplashScreenLayer.h @@ -0,0 +1,26 @@ +// +// Created by sebastian on 21.04.26. +// + +#ifndef SPLASHSCREENLAYER_H +#define SPLASHSCREENLAYER_H +#include "../../layer/Layer.h" + +#include "../../renderer/GUIRenderer.h" +#include "../../core/gui/uiMain/UiContainer.h" +class SplashScreenLayer: public Layer { +public: + SplashScreenLayer(); + virtual void onRender() override; + virtual void onUpdate() override; + virtual void onAttach() override; + virtual void onDetach() override; + ~SplashScreenLayer() override; +private: + std::unique_ptr rootContainer; + std::unique_ptr guiRenderer; +}; + + + +#endif //SPLASHSCREENLAYER_H diff --git a/src/engine/renderer/loader/async/AssetLoader.cpp b/src/engine/renderer/loader/async/AssetLoader.cpp index d1dc4c2..e14ecc1 100644 --- a/src/engine/renderer/loader/async/AssetLoader.cpp +++ b/src/engine/renderer/loader/async/AssetLoader.cpp @@ -12,34 +12,28 @@ #include "../OBJLoader.h" #include "../TextureLoader.h" #include "../../../core/events/EventBus.h" +#include "spdlog/spdlog.h" namespace fs = std::filesystem; -void AssetLoader::scheduleTexture(const std::string &name, const std::string &path) { - { - std::lock_guard lock(pendingMutex); - pendingQueue.push(TextureRequest{name, path}); - ++total; - } - pendingCV.notify_one(); -} -void AssetLoader::scheduleModel(const std::string &name, const std::string &objPath) { +void AssetLoader::scheduleAsset(const AssetRequest &assetRequest) { { std::lock_guard lock(pendingMutex); - pendingQueue.push(ModelRequest{name, objPath}); - ++total; - } - pendingCV.notify_one(); -} - -void AssetLoader::scheduleAsset(const std::string &name, const std::string &assetPath) { - { - std::lock_guard lock(pendingMutex); - pendingQueue.push(StagedModelRequest{name, assetPath}); + pendingQueue.push(assetRequest); ++total; } pendingCV.notify_one(); + std::visit([](const T& req) -> void { + using Type = std::decay_t; + if constexpr (std::is_same_v) { + spdlog::debug("Scheduled Texture Request: {} -> {}", req.name, req.path); + } else if constexpr (std::is_same_v) { + spdlog::debug("Scheduled Model Request: {} -> {}", req.name, req.objPath); + } else if constexpr (std::is_same_v) { + spdlog::debug("Scheduled Staged Model Request: {} -> {}", req.name, req.assetPath); + } + }, assetRequest); } void AssetLoader::start() { @@ -97,6 +91,7 @@ void AssetLoader::loadingThreadFunc() { } RawTextureData AssetLoader::processTextureRequest(const TextureRequest &request) { + spdlog::debug("Processing Texture Request: {} -> {}", request.name, request.path); RawTextureData textureData = TextureLoader::loadRawTextureData(request.path, request.flipVertically); textureData.name = request.name; return textureData; diff --git a/src/engine/renderer/loader/async/AssetLoader.h b/src/engine/renderer/loader/async/AssetLoader.h index a7448e0..93abc2d 100644 --- a/src/engine/renderer/loader/async/AssetLoader.h +++ b/src/engine/renderer/loader/async/AssetLoader.h @@ -5,6 +5,7 @@ #ifndef ASSETLOADER_H #define ASSETLOADER_H #include +#include #include #include #include @@ -39,6 +40,27 @@ struct StagedModelRequest { using AssetRequest = std::variant; using IntermediateAsset = std::variant; +inline std::string assetRequestToString(const AssetRequest& request) { + return std::visit([](const T& req) -> std::string { + using Type = std::decay_t; + if constexpr (std::is_same_v) { + return std::format("TextureRequest{{name='{}', path='{}'}}", req.name, req.path); + } else if constexpr (std::is_same_v) { + return std::format("ModelRequest{{name='{}', objPath='{}'}}", req.name, req.objPath); + } else if constexpr (std::is_same_v) { + return std::format("StagedModelRequest{{name='{}', assetPath='{}'}}", req.name, req.assetPath); + } + }, request); +} + +template <> +struct std::formatter { + constexpr auto parse(std::format_parse_context& ctx) { return ctx.begin(); } + auto format(const AssetRequest& request, std::format_context& ctx) const { + return std::format_to(ctx.out(), "{}", assetRequestToString(request)); + } +}; + struct LoadingProgress { int total; int loaded; @@ -52,9 +74,7 @@ struct LoadingProgress { class AssetLoader { public: - void scheduleTexture(const std::string& name, const std::string& path); - void scheduleModel(const std::string& name, const std::string& objPath); - void scheduleAsset(const std::string& name, const std::string& assetPath); + void scheduleAsset(const AssetRequest& assetRequest); void start(); void stop(); diff --git a/src/game/DicewarsApp.cpp b/src/game/DicewarsApp.cpp index c851281..330359f 100644 --- a/src/game/DicewarsApp.cpp +++ b/src/game/DicewarsApp.cpp @@ -4,9 +4,9 @@ #include "DicewarsApp.h" -#include "GameLayer.h" -#include "UILayer.h" +#include "../engine/core/scenes/SceneManager.h" +#include "../engine/core/scenes/SplashScreen.h" DicewarsApp::DicewarsApp() { - + sceneManager->switchTo(std::make_unique()); }