ADD: Integrate SceneManager into Application, derive scene for LoadingScreen, closes #17
All checks were successful
Tests / test (push) Successful in 2m34s

This commit is contained in:
Sebastian Böckelmann 2026-04-21 17:33:48 +02:00
parent 8ea01b7850
commit d84995bd29
17 changed files with 277 additions and 62 deletions

View File

@ -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

BIN
assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -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<SceneManager>();
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();

View File

@ -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> keyboard;
std::unique_ptr<Mouse> mouse;
std::unique_ptr<StateManager> stateManager;
std::shared_ptr<GameState> gameState;
std::unique_ptr<SceneManager> sceneManager;
private:
bool running = true;
std::unique_ptr<Window> window;
static Application* instance;
std::vector<Layer*> layers;
void updateTime();
float lastFrame;
protected:
void pushLayer(Layer* layer);
};

View File

@ -21,12 +21,16 @@ namespace LayoutEngine {
std::vector<ComputedLayout> 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) {

View File

@ -7,7 +7,8 @@
enum class SizeUnit {
Pixels,
Percent
Percent,
Vmin
};
struct SizeValue {

View File

@ -17,5 +17,6 @@ void Scene::onRender() {
}
void Scene::addLayer(std::unique_ptr<Layer> layer) {
layer->onAttach();
layers.push_back(std::move(layer));
}

View File

@ -8,13 +8,19 @@
#include <vector>
#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<AssetRequest> getRequiredAssets() {return {};}
void onUpdate();
void onRender();

View File

@ -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<Scene> next) {
nextScene = std::move(next);
std::vector<AssetRequest> 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;

View File

@ -0,0 +1,29 @@
//
// Created by sebastian on 21.04.26.
//
#ifndef SCENEMANAGER_H
#define SCENEMANAGER_H
#include <memory>
#include "../../renderer/loader/async/AssetLoader.h""
class Scene;
class SceneManager {
public:
void switchTo(std::unique_ptr<Scene> next);
void update();
void render();
~SceneManager();
private:
std::unique_ptr<Scene> currentScene;
std::unique_ptr<Scene> nextScene;
AssetLoader assetLoader;
bool isTransitioning = false;
};
#endif //SCENEMANAGER_H

View File

@ -0,0 +1,21 @@
//
// Created by sebastian on 21.04.26.
//
#include "SplashScreen.h"
#include "SplashScreenLayer.h"
std::vector<AssetRequest> SplashScreen::getRequiredAssets() {
std::vector<AssetRequest> requests;
requests.push_back(TextureRequest("logo", "assets/logo.png"));
return requests;
}
void SplashScreen::onEnter() {
addLayer(std::make_unique<SplashScreenLayer>());
}
void SplashScreen::onExit() {
Scene::onExit();
}

View File

@ -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<AssetRequest> getRequiredAssets() override;
void onEnter() override;
void onExit() override;
};
#endif //SPLASHSCREEN_H

View File

@ -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<GUIRenderer>(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<UiContainer>();
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<UiImage>(std::make_unique<UiImage>(AssetManager::getTexture("logo")->getTextureID(), logoLayoutStyle));
}
void SplashScreenLayer::onDetach() {
Layer::onDetach();
}
SplashScreenLayer::~SplashScreenLayer() = default;

View File

@ -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<UiContainer> rootContainer;
std::unique_ptr<GUIRenderer> guiRenderer;
};
#endif //SPLASHSCREENLAYER_H

View File

@ -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});
pendingQueue.push(assetRequest);
++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});
++total;
std::visit([]<typename T>(const T& req) -> void {
using Type = std::decay_t<T>;
if constexpr (std::is_same_v<Type, TextureRequest>) {
spdlog::debug("Scheduled Texture Request: {} -> {}", req.name, req.path);
} else if constexpr (std::is_same_v<Type, ModelRequest>) {
spdlog::debug("Scheduled Model Request: {} -> {}", req.name, req.objPath);
} else if constexpr (std::is_same_v<Type, StagedModelRequest>) {
spdlog::debug("Scheduled Staged Model Request: {} -> {}", req.name, req.assetPath);
}
pendingCV.notify_one();
}, 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;

View File

@ -5,6 +5,7 @@
#ifndef ASSETLOADER_H
#define ASSETLOADER_H
#include <condition_variable>
#include <format>
#include <queue>
#include <string>
#include <variant>
@ -39,6 +40,27 @@ struct StagedModelRequest {
using AssetRequest = std::variant<TextureRequest, ModelRequest, StagedModelRequest>;
using IntermediateAsset = std::variant<RawTextureData, RawModelData, RawStagedModelData>;
inline std::string assetRequestToString(const AssetRequest& request) {
return std::visit([]<typename T>(const T& req) -> std::string {
using Type = std::decay_t<T>;
if constexpr (std::is_same_v<Type, TextureRequest>) {
return std::format("TextureRequest{{name='{}', path='{}'}}", req.name, req.path);
} else if constexpr (std::is_same_v<Type, ModelRequest>) {
return std::format("ModelRequest{{name='{}', objPath='{}'}}", req.name, req.objPath);
} else if constexpr (std::is_same_v<Type, StagedModelRequest>) {
return std::format("StagedModelRequest{{name='{}', assetPath='{}'}}", req.name, req.assetPath);
}
}, request);
}
template <>
struct std::formatter<AssetRequest> {
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();

View File

@ -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<SplashScreen>());
}