diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e0b006..c457e82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,6 +134,8 @@ add_library(Engine STATIC engine/src/loader/models/Model.h engine/src/loader/models/MeshSection.cpp engine/src/loader/models/MeshSection.h + engine/src/loader/assets/AssetManager.cpp + engine/src/loader/assets/AssetManager.h ) target_include_directories(Engine PUBLIC engine/src) target_link_libraries(Engine PUBLIC OpenGL::GL glfw glad glm::glm imgui spdlog::spdlog tinyobjloader stb_image) diff --git a/assets/models/chess.mtl b/assets/models/chess.mtl new file mode 100644 index 0000000..6c4cb69 --- /dev/null +++ b/assets/models/chess.mtl @@ -0,0 +1,12 @@ +# Blender 5.2.0 LTS MTL File: 'chess.blend' +# www.blender.org + +newmtl Material.002 +Ns 735.817688 +Ka 1.000000 1.000000 1.000000 +Kd 0.800000 0.800000 0.800000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 diff --git a/engine/src/ecs/standardComponents/MeshComponent.h b/engine/src/ecs/standardComponents/MeshComponent.h index be320e4..039f5d3 100644 --- a/engine/src/ecs/standardComponents/MeshComponent.h +++ b/engine/src/ecs/standardComponents/MeshComponent.h @@ -6,11 +6,11 @@ #define COLORRACE_MESHCOMPONENT_H #include -#include "renderer/model/RawModel.h" +#include "loader/models/Model.h" namespace engine { struct MeshComponent { - std::shared_ptr raw_model; + std::shared_ptr model; }; } #endif //COLORRACE_MESHCOMPONENT_H diff --git a/engine/src/layer/Scene.cpp b/engine/src/layer/Scene.cpp index 74a406d..aac9938 100644 --- a/engine/src/layer/Scene.cpp +++ b/engine/src/layer/Scene.cpp @@ -6,6 +6,7 @@ #include #include +#include void engine::Scene::onUpdate() { for (const auto& layer : m_layers) { @@ -19,6 +20,11 @@ void engine::Scene::onRender() { } } +void engine::Scene::onRegisterLoadedAsset(LoadedAsset loadedAsset) { + std::cout << "Loaded asset: " << std::endl; + assetManager->insertAsset(std::move(loadedAsset)); +} + void engine::Scene::addLayer(std::unique_ptr layer) { layer->onAttach(); m_layers.push_back(std::move(layer)); diff --git a/engine/src/layer/Scene.h b/engine/src/layer/Scene.h index b850cf8..71a27be 100644 --- a/engine/src/layer/Scene.h +++ b/engine/src/layer/Scene.h @@ -8,13 +8,16 @@ #include #include "Layer.h" +#include "loader/assets/AssetManager.h" +#include "loader/assets/AssetRequests.h" +#include "loader/assets/LoadedAssets.h" #include "renderer/entities/Camera.h" namespace engine { class Scene { public: - explicit Scene() {} + explicit Scene(): assetManager(std::make_unique()) {} virtual ~Scene() = default; virtual void onEnter() {} virtual void onExit() { @@ -25,12 +28,17 @@ namespace engine { void onUpdate(); void onRender(); + + void onRegisterLoadedAsset(LoadedAsset loadedAsset); + + virtual std::vector getRequiredAssets() const { return {}; } protected: std::vector> m_layers; void addLayer(std::unique_ptr layer); engine::EntityRegistry entityRegistry; + std::unique_ptr assetManager; }; } diff --git a/engine/src/layer/SceneManager.cpp b/engine/src/layer/SceneManager.cpp index 57ed4e9..2e6b522 100644 --- a/engine/src/layer/SceneManager.cpp +++ b/engine/src/layer/SceneManager.cpp @@ -5,20 +5,32 @@ #include "SceneManager.h" #include -#include + +#include "spdlog/spdlog.h" void engine::SceneManager::switchTo(std::unique_ptr scene, std::function onSceneLoaded) { onSceneLoaded = std::move(onSceneLoaded); nextScene = std::move(scene); - //Todo: Integrate Asset Loader + std::vector requestedAssets = nextScene->getRequiredAssets(); + spdlog::debug("Requested Assets: {}", requestedAssets.size()); + + for (AssetRequest & request : requestedAssets) { + assetLoader.scheduleAsset(request); + } + + assetLoader.start(); } void engine::SceneManager::update() { if (nextScene) { - //Todo: AssetLoader should process AssetQueue + auto uploadedAssets = assetLoader.processUploadQueue(2); + std::cout << "Uploaded Assets: " << uploadedAssets.size() << std::endl; + for (const auto& asset : uploadedAssets) { + nextScene->onRegisterLoadedAsset(asset); + } - if (getTransitionProgress() == 1.0) { // Todo: Replace with AssetLoader.getProgress.done() + if (assetLoader.getProgress().isDone()) { if (currentScene) { currentScene->onExit(); } @@ -27,7 +39,8 @@ void engine::SceneManager::update() { nextScene = nullptr; currentScene->onEnter(); - // Todo: Stop AssetLoader + assetLoader.stop(); + if (onSceneLoaded) { onSceneLoaded(); onSceneLoaded = nullptr; @@ -47,5 +60,5 @@ void engine::SceneManager::render() const { } float engine::SceneManager::getTransitionProgress() const { - return 1.0f; + return assetLoader.getProgress().fraction(); } diff --git a/engine/src/layer/SceneManager.h b/engine/src/layer/SceneManager.h index 0e7ec4b..aa3fa66 100644 --- a/engine/src/layer/SceneManager.h +++ b/engine/src/layer/SceneManager.h @@ -7,6 +7,7 @@ #include #include "Scene.h" +#include "loader/AssetLoader.h" namespace engine { @@ -24,6 +25,7 @@ namespace engine { std::unique_ptr nextScene; bool isTransitioning = false; std::function onSceneLoaded; + AssetLoader assetLoader; }; diff --git a/engine/src/loader/AssetLoader.cpp b/engine/src/loader/AssetLoader.cpp index efbde9e..75d29aa 100644 --- a/engine/src/loader/AssetLoader.cpp +++ b/engine/src/loader/AssetLoader.cpp @@ -4,8 +4,13 @@ #include "AssetLoader.h" +#include + +#include "models/ModelImporter.h" +#include "models/ModelUploader.h" #include "spdlog/spdlog.h" #include "textures/TextureImporter.h" +#include "textures/TextureUploader.h" void engine::AssetLoader::scheduleAsset(const AssetRequest &request) { { @@ -39,6 +44,39 @@ void engine::AssetLoader::stop() { } } + + +std::vector engine::AssetLoader::processUploadQueue(int maxPerFrame) { + std::vector result; + auto uploadQueue = determineUploadQueue(maxPerFrame); + std::cout << "Upload Queue: " << uploadQueue.size() << std::endl; + for (const auto& asset : uploadQueue) { + std::visit([&result](T0& rawData) -> void { + using T = std::decay_t; + if constexpr (std::is_same_v) { + Texture2D texture = TextureUploader::upload(rawData); + result.emplace_back(LoadedTexture{rawData.name, std::make_shared(std::move(texture))}); + } else if constexpr (std::is_same_v) { + std::cout << "Uploading model " << rawData.model.name << std::endl; + auto modelAssets = ModelUploader::upload(rawData, rawData.model.splitPolicy); + std::cout << "Moddel Assets" << modelAssets.size() << std::endl; + result.insert(result.end(), modelAssets.begin(), modelAssets.end()); + std::cout << "Result size" << result.size() << std::endl; + } else { + static_assert( + always_false, + "Unhandled asset type in AssetLoader::processUploadQueue" + ); + } + }, asset); + ++loaded; + } + + std::cout << "Loaded Assets: " << result.size() << std::endl; + + return result; +} + engine::LoadingProgress engine::AssetLoader::getProgress() const { return {total.load(), loaded.load()}; } @@ -83,7 +121,20 @@ engine::RawTextureData engine::AssetLoader::processTextureRequest(const TextureR return textureData; } -engine::RawModelData engine::AssetLoader::processModelRequest(const ModelRequest &request) { +engine::RawModelBundle engine::AssetLoader::processModelRequest(const ModelRequest &request) { spdlog::debug("Processing Model Request: {} -> {}", request.name, request.path); - + RawModelBundle modelData = ModelImporter::importFromFile(request.path, request.splitPolicy); + modelData.model.name = request.name; + return modelData; +} + +std::vector engine::AssetLoader::determineUploadQueue(int maxPerFrame) { + std::vector result; + std::lock_guard lock(readyMutex); + for (int i = 0; i < maxPerFrame && !readyQueue.empty(); ++i) { + result.push_back(std::move(readyQueue.front())); + readyQueue.pop(); + } + + return result; } diff --git a/engine/src/loader/AssetLoader.h b/engine/src/loader/AssetLoader.h index bb04377..d0d57a0 100644 --- a/engine/src/loader/AssetLoader.h +++ b/engine/src/loader/AssetLoader.h @@ -14,6 +14,7 @@ #include #include "assets/AssetRequests.h" +#include "assets/LoadedAssets.h" #include "assets/RawAssetData.h" namespace engine { @@ -28,20 +29,24 @@ namespace engine { } }; + template +inline constexpr bool always_false = false; + class AssetLoader { public: void scheduleAsset(const AssetRequest& request); void start(); void stop(); - void processUploadQueue(int maxPerFrame); + std::vector processUploadQueue(int maxPerFrame); LoadingProgress getProgress() const; private: void loadingThreadFunc(); static RawTextureData processTextureRequest(const TextureRequest& request); - static RawModelData processModelRequest(const ModelRequest& request); + static RawModelBundle processModelRequest(const ModelRequest& request); + std::vector determineUploadQueue(int maxPerFrame); std::queue pendingQueue; std::mutex pendingMutex; diff --git a/engine/src/loader/assets/AssetManager.cpp b/engine/src/loader/assets/AssetManager.cpp new file mode 100644 index 0000000..aa32ae8 --- /dev/null +++ b/engine/src/loader/assets/AssetManager.cpp @@ -0,0 +1,63 @@ +// +// Created by sebastian on 30.07.26. +// + +#include "AssetManager.h" + +#include +#include + +std::shared_ptr engine::AssetManager::getModel(const std::string &name) const { + auto it = m_models.find(name); + if (it == m_models.end()) { + throw std::runtime_error("Model not found: " + name); + } + return it->second; +} + +bool engine::AssetManager::hasModel(const std::string &name) const { + return m_models.contains(name); +} + +void engine::AssetManager::insertAsset(LoadedAsset loadedAsset) { + std::cout << "Inserting asset: " << std::endl; + std::visit([this](const auto& loaded) { + using T = std::decay_t; + + if constexpr (std::is_same_v) { + insertModel(loaded.name, loaded.model); + } else if constexpr (std::is_same_v) { + insertTexture(loaded.name, loaded.texture); + } + }, loadedAsset); +} + +void engine::AssetManager::insertModel(const std::string &name, std::shared_ptr model) { + std::cout << "Inserting model " << name << std::endl; + m_models[name] = std::move(model); +} + +void engine::AssetManager::unloadModel(const std::string &name) { + m_models.erase(name); +} + +std::shared_ptr engine::AssetManager::getTexture(const std::string &name) const { + auto it = m_textures.find(name); + if (it == m_textures.end()) { + throw std::runtime_error("Texture not found: " + name); + } + return it->second; +} + +bool engine::AssetManager::hasTexture(const std::string &name) const { + return m_textures.contains(name); +} + +void engine::AssetManager::insertTexture(const std::string &name, std::shared_ptr texture) { + std::cout << "Inserting texture " << name << std::endl; + m_textures[name] = std::move(texture); +} + +void engine::AssetManager::unloadTexture(const std::string &name) { + m_textures.erase(name); +} diff --git a/engine/src/loader/assets/AssetManager.h b/engine/src/loader/assets/AssetManager.h new file mode 100644 index 0000000..e291eec --- /dev/null +++ b/engine/src/loader/assets/AssetManager.h @@ -0,0 +1,39 @@ +// +// Created by sebastian on 30.07.26. +// + +#ifndef COLORRACE_ASSETMANAGER_H +#define COLORRACE_ASSETMANAGER_H +#include +#include + +#include "LoadedAssets.h" +#include "loader/models/Model.h" +#include "loader/textures/Texture2D.h" + + +namespace engine { + class AssetManager { + public: + AssetManager() = default; + ~AssetManager() = default; + + std::shared_ptr getModel(const std::string& name) const; + bool hasModel(const std::string& name) const; + void insertAsset(LoadedAsset loadedAsset); + void insertModel(const std::string& name, std::shared_ptr model); + void unloadModel(const std::string& name); + + std::shared_ptr getTexture(const std::string& name) const; + bool hasTexture(const std::string& name) const; + void insertTexture(const std::string& name, std::shared_ptr texture); + void unloadTexture(const std::string& name); + + + private: + std::unordered_map> m_textures; + std::unordered_map> m_models; + }; +} + +#endif //COLORRACE_ASSETMANAGER_H diff --git a/engine/src/loader/assets/AssetRequests.h b/engine/src/loader/assets/AssetRequests.h index 212706e..f05d291 100644 --- a/engine/src/loader/assets/AssetRequests.h +++ b/engine/src/loader/assets/AssetRequests.h @@ -9,6 +9,8 @@ #include #include +#include "AssetTypes.h" + namespace engine { struct TextureRequest @@ -23,6 +25,7 @@ namespace engine { { std::string name; std::string path; + ModelSplitPolicy splitPolicy = ModelSplitPolicy::Combined; }; diff --git a/engine/src/loader/assets/RawAssetData.h b/engine/src/loader/assets/RawAssetData.h index 0178127..83d18cf 100644 --- a/engine/src/loader/assets/RawAssetData.h +++ b/engine/src/loader/assets/RawAssetData.h @@ -16,6 +16,7 @@ namespace engine { + struct RawTextureData { std::string name; @@ -69,11 +70,17 @@ namespace engine { ModelSplitPolicy splitPolicy; }; + struct RawModelBundle { + RawModelData model; + std::vector materials; + std::vector textures; + }; + using IntermediateAsset = std::variant< RawTextureData, - RawModelData + RawModelBundle >; diff --git a/engine/src/loader/models/ModelImporter.cpp b/engine/src/loader/models/ModelImporter.cpp index 472ae1c..788aaa2 100644 --- a/engine/src/loader/models/ModelImporter.cpp +++ b/engine/src/loader/models/ModelImporter.cpp @@ -11,7 +11,7 @@ #include "loader/textures/TextureImporter.h" -engine::RawModelBundle engine::ModelImporter::importFromFile(const std::string& objPath) { +engine::RawModelBundle engine::ModelImporter::importFromFile(const std::string& objPath, ModelSplitPolicy policy) { tinyobj::ObjReaderConfig config; config.mtl_search_path = std::filesystem::path(objPath).parent_path().string(); config.triangulate = true; // GL braucht Dreiecke, keine n-Ecke @@ -99,6 +99,6 @@ engine::RawModelBundle engine::ModelImporter::importFromFile(const std::string& } bundle.model.objects.push_back(std::move(object)); } - + bundle.model.splitPolicy = policy; return bundle; } diff --git a/engine/src/loader/models/ModelImporter.h b/engine/src/loader/models/ModelImporter.h index 1700f69..4acae1d 100644 --- a/engine/src/loader/models/ModelImporter.h +++ b/engine/src/loader/models/ModelImporter.h @@ -8,15 +8,10 @@ namespace engine { - struct RawModelBundle { - RawModelData model; - std::vector materials; - std::vector textures; - }; class ModelImporter { public: - static RawModelBundle importFromFile(const std::string& objPath); + static RawModelBundle importFromFile(const std::string& objPath, ModelSplitPolicy policy); }; } diff --git a/engine/src/loader/models/ModelUploader.cpp b/engine/src/loader/models/ModelUploader.cpp index f382b3d..2232dd5 100644 --- a/engine/src/loader/models/ModelUploader.cpp +++ b/engine/src/loader/models/ModelUploader.cpp @@ -35,6 +35,8 @@ std::vector engine::ModelUploader::upload(const engine::Raw std::make_shared(buildCombinedByMaterial(bundle.model.objects, materials)) }); break; + default: + throw std::runtime_error("Invalid ModelSplitPolicy"); } return results; } @@ -127,7 +129,7 @@ engine::Model engine::ModelUploader::buildCombinedByMaterial(const std::vector(target.vertices.size() / 3); target.vertices.insert(target.vertices.end(), section.vertices.begin(), section.vertices.end()); target.normals.insert(target.normals.end(), section.normals.begin(), section.normals.end()); diff --git a/engine/src/loader/textures/Texture2D.h b/engine/src/loader/textures/Texture2D.h index a26748d..e80b78c 100644 --- a/engine/src/loader/textures/Texture2D.h +++ b/engine/src/loader/textures/Texture2D.h @@ -4,7 +4,7 @@ #ifndef COLORRACE_TEXTURE2D_H #define COLORRACE_TEXTURE2D_H -#include "../../../../cmake-build-debug/_deps/glfw-src/src/internal.h" +#include "glad/glad.h" namespace engine { diff --git a/engine/src/renderer/MeshRenderSystem.cpp b/engine/src/renderer/MeshRenderSystem.cpp index b48339a..44bb1d1 100644 --- a/engine/src/renderer/MeshRenderSystem.cpp +++ b/engine/src/renderer/MeshRenderSystem.cpp @@ -19,6 +19,6 @@ void engine::MeshRenderSystem::update(engine::EntityRegistry ®istry, engine:: auto& transform = transforms.get(entity); - renderQueue.submit(engine::RenderCommand(mesh.raw_model, transform.computeModelMatrix())); + renderQueue.submit(engine::RenderCommand(mesh.model, transform.computeModelMatrix())); } } diff --git a/engine/src/renderer/RenderQueue.h b/engine/src/renderer/RenderQueue.h index 9dcd77f..d6a84eb 100644 --- a/engine/src/renderer/RenderQueue.h +++ b/engine/src/renderer/RenderQueue.h @@ -9,11 +9,12 @@ #include #include "glm/glm.hpp" +#include "loader/models/Model.h" #include "model/RawModel.h" namespace engine { struct RenderCommand { - std::shared_ptr mesh; + std::shared_ptr mesh; glm::mat4 transformationMatrix; }; diff --git a/engine/src/utils/openglWrapper/openglObjects/Vao.h b/engine/src/utils/openglWrapper/openglObjects/Vao.h index 44fc509..5617a7a 100644 --- a/engine/src/utils/openglWrapper/openglObjects/Vao.h +++ b/engine/src/utils/openglWrapper/openglObjects/Vao.h @@ -6,7 +6,6 @@ #define COLORRACE_VAO_H #pragma once -#include #include #include #include "Vbo.h" diff --git a/game/src/GameLayer.cpp b/game/src/GameLayer.cpp index 5dab343..96248e0 100644 --- a/game/src/GameLayer.cpp +++ b/game/src/GameLayer.cpp @@ -18,16 +18,6 @@ void GameLayer::onAttach() { m_renderState.backfaceCulling = false; m_renderer = std::make_unique(); - - Entity entity = m_entityRegistry.createEntity(); - - const auto cubeModel = CubeFactory::createCube(); - - const auto transform = TransformComponent({0,0,0}, {0,0,0}, {1,1,1}); - const auto mesh = MeshComponent(cubeModel); - - m_entityRegistry.addComponent(entity, transform); - m_entityRegistry.addComponent(entity, mesh); } void GameLayer::onDetach() { diff --git a/game/src/GameScene.cpp b/game/src/GameScene.cpp index db22ed8..bc29b71 100644 --- a/game/src/GameScene.cpp +++ b/game/src/GameScene.cpp @@ -5,6 +5,8 @@ #include "GameScene.h" #include "GameLayer.h" +#include "ecs/standardComponents/MeshComponent.h" +#include "ecs/standardComponents/TransformComponent.h" GameScene::GameScene() : Scene(), m_camera(std::make_unique()) {} @@ -14,8 +16,26 @@ void GameScene::onEnter() { m_camera->setPosition({0.0f, 0.0f, 5.0f}); m_camera->setYaw(0.0f); m_camera->setPitch(0.0f); + + if (assetManager->hasModel("pawn")) { + engine::Entity entity = entityRegistry.createEntity(); + + const auto pawnModel = assetManager->getModel("pawn"); + + const auto transform = engine::TransformComponent({0,0,0}, {0,0,0}, {1,1,1}); + const auto mesh = engine::MeshComponent(pawnModel); + + entityRegistry.addComponent(entity, transform); + entityRegistry.addComponent(entity, mesh); + } } void GameScene::onExit() { Scene::onExit(); } + +std::vector GameScene::getRequiredAssets() const { + std::vector requests; + requests.emplace_back(engine::ModelRequest{"pawn", "assets/models/chess.obj", engine::ModelSplitPolicy::Combined}); + return requests; +}; diff --git a/game/src/GameScene.h b/game/src/GameScene.h index 619de8b..2fef1ab 100644 --- a/game/src/GameScene.h +++ b/game/src/GameScene.h @@ -12,6 +12,7 @@ public: GameScene(); void onEnter() override; void onExit() override; + std::vector getRequiredAssets() const override; private: std::shared_ptr m_camera;