ADD: AssetManager

This commit is contained in:
sebastian 2026-07-30 18:33:15 +02:00
parent eeb7c9c00f
commit 96d2e89d48
23 changed files with 256 additions and 37 deletions

View File

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

12
assets/models/chess.mtl Normal file
View File

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

View File

@ -6,11 +6,11 @@
#define COLORRACE_MESHCOMPONENT_H
#include <memory>
#include "renderer/model/RawModel.h"
#include "loader/models/Model.h"
namespace engine {
struct MeshComponent {
std::shared_ptr<engine::RawModel> raw_model;
std::shared_ptr<Model> model;
};
}
#endif //COLORRACE_MESHCOMPONENT_H

View File

@ -6,6 +6,7 @@
#include <iostream>
#include <ostream>
#include <utility>
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) {
layer->onAttach();
m_layers.push_back(std::move(layer));

View File

@ -8,13 +8,16 @@
#include <vector>
#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<AssetManager>()) {}
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<AssetRequest> getRequiredAssets() const { return {}; }
protected:
std::vector<std::unique_ptr<Layer>> m_layers;
void addLayer(std::unique_ptr<Layer> layer);
engine::EntityRegistry entityRegistry;
std::unique_ptr<AssetManager> assetManager;
};
}

View File

@ -5,20 +5,32 @@
#include "SceneManager.h"
#include <iostream>
#include <ostream>
#include "spdlog/spdlog.h"
void engine::SceneManager::switchTo(std::unique_ptr<Scene> scene, std::function<void()> onSceneLoaded) {
onSceneLoaded = std::move(onSceneLoaded);
nextScene = std::move(scene);
//Todo: Integrate Asset Loader
std::vector<AssetRequest> 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();
}

View File

@ -7,6 +7,7 @@
#include <memory>
#include "Scene.h"
#include "loader/AssetLoader.h"
namespace engine {
@ -24,6 +25,7 @@ namespace engine {
std::unique_ptr<Scene> nextScene;
bool isTransitioning = false;
std::function<void()> onSceneLoaded;
AssetLoader assetLoader;
};

View File

@ -4,8 +4,13 @@
#include "AssetLoader.h"
#include <iostream>
#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::LoadedAsset> engine::AssetLoader::processUploadQueue(int maxPerFrame) {
std::vector<LoadedAsset> result;
auto uploadQueue = determineUploadQueue(maxPerFrame);
std::cout << "Upload Queue: " << uploadQueue.size() << std::endl;
for (const auto& asset : uploadQueue) {
std::visit([&result]<typename T0>(T0& rawData) -> void {
using T = std::decay_t<T0>;
if constexpr (std::is_same_v<T, RawTextureData>) {
Texture2D texture = TextureUploader::upload(rawData);
result.emplace_back(LoadedTexture{rawData.name, std::make_shared<Texture2D>(std::move(texture))});
} else if constexpr (std::is_same_v<T, RawModelBundle>) {
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<T>,
"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::IntermediateAsset> engine::AssetLoader::determineUploadQueue(int maxPerFrame) {
std::vector<IntermediateAsset> 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;
}

View File

@ -14,6 +14,7 @@
#include <vector>
#include "assets/AssetRequests.h"
#include "assets/LoadedAssets.h"
#include "assets/RawAssetData.h"
namespace engine {
@ -28,20 +29,24 @@ namespace engine {
}
};
template<typename>
inline constexpr bool always_false = false;
class AssetLoader {
public:
void scheduleAsset(const AssetRequest& request);
void start();
void stop();
void processUploadQueue(int maxPerFrame);
std::vector<LoadedAsset> 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<IntermediateAsset> determineUploadQueue(int maxPerFrame);
std::queue<AssetRequest> pendingQueue;
std::mutex pendingMutex;

View File

@ -0,0 +1,63 @@
//
// Created by sebastian on 30.07.26.
//
#include "AssetManager.h"
#include <iostream>
#include <ostream>
std::shared_ptr<engine::Model> 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<decltype(loaded)>;
if constexpr (std::is_same_v<T, LoadedModel>) {
insertModel(loaded.name, loaded.model);
} else if constexpr (std::is_same_v<T, LoadedTexture>) {
insertTexture(loaded.name, loaded.texture);
}
}, loadedAsset);
}
void engine::AssetManager::insertModel(const std::string &name, std::shared_ptr<Model> 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::Texture2D> 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<Texture2D> 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);
}

View File

@ -0,0 +1,39 @@
//
// Created by sebastian on 30.07.26.
//
#ifndef COLORRACE_ASSETMANAGER_H
#define COLORRACE_ASSETMANAGER_H
#include <string>
#include <unordered_map>
#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<Model> 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> model);
void unloadModel(const std::string& name);
std::shared_ptr<Texture2D> getTexture(const std::string& name) const;
bool hasTexture(const std::string& name) const;
void insertTexture(const std::string& name, std::shared_ptr<Texture2D> texture);
void unloadTexture(const std::string& name);
private:
std::unordered_map<std::string, std::shared_ptr<Texture2D>> m_textures;
std::unordered_map<std::string, std::shared_ptr<Model>> m_models;
};
}
#endif //COLORRACE_ASSETMANAGER_H

View File

@ -9,6 +9,8 @@
#include <string>
#include <variant>
#include "AssetTypes.h"
namespace engine {
struct TextureRequest
@ -23,6 +25,7 @@ namespace engine {
{
std::string name;
std::string path;
ModelSplitPolicy splitPolicy = ModelSplitPolicy::Combined;
};

View File

@ -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<RawMaterialData> materials;
std::vector<RawTextureData> textures;
};
using IntermediateAsset =
std::variant<
RawTextureData,
RawModelData
RawModelBundle
>;

View File

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

View File

@ -8,15 +8,10 @@
namespace engine {
struct RawModelBundle {
RawModelData model;
std::vector<RawMaterialData> materials;
std::vector<RawTextureData> textures;
};
class ModelImporter {
public:
static RawModelBundle importFromFile(const std::string& objPath);
static RawModelBundle importFromFile(const std::string& objPath, ModelSplitPolicy policy);
};
}

View File

@ -35,6 +35,8 @@ std::vector<engine::LoadedAsset> engine::ModelUploader::upload(const engine::Raw
std::make_shared<Model>(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<R
for (auto section : object.subModels) {
auto& target = merged[section.materialName];
target.materialName = section.materialName;
target.materialName = target.name = section.materialName;
unsigned int offset = static_cast<unsigned int>(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());

View File

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

View File

@ -19,6 +19,6 @@ void engine::MeshRenderSystem::update(engine::EntityRegistry &registry, engine::
auto& transform = transforms.get(entity);
renderQueue.submit(engine::RenderCommand(mesh.raw_model, transform.computeModelMatrix()));
renderQueue.submit(engine::RenderCommand(mesh.model, transform.computeModelMatrix()));
}
}

View File

@ -9,11 +9,12 @@
#include <ostream>
#include "glm/glm.hpp"
#include "loader/models/Model.h"
#include "model/RawModel.h"
namespace engine {
struct RenderCommand {
std::shared_ptr<engine::RawModel> mesh;
std::shared_ptr<engine::Model> mesh;
glm::mat4 transformationMatrix;
};

View File

@ -6,7 +6,6 @@
#define COLORRACE_VAO_H
#pragma once
#include <glad/glad.h>
#include <memory>
#include <vector>
#include "Vbo.h"

View File

@ -18,16 +18,6 @@ void GameLayer::onAttach() {
m_renderState.backfaceCulling = false;
m_renderer = std::make_unique<engine::MeshRenderer>();
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<TransformComponent>(entity, transform);
m_entityRegistry.addComponent<MeshComponent>(entity, mesh);
}
void GameLayer::onDetach() {

View File

@ -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<engine::Camera>()) {}
@ -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<engine::TransformComponent>(entity, transform);
entityRegistry.addComponent<engine::MeshComponent>(entity, mesh);
}
}
void GameScene::onExit() {
Scene::onExit();
}
std::vector<engine::AssetRequest> GameScene::getRequiredAssets() const {
std::vector<engine::AssetRequest> requests;
requests.emplace_back(engine::ModelRequest{"pawn", "assets/models/chess.obj", engine::ModelSplitPolicy::Combined});
return requests;
};

View File

@ -12,6 +12,7 @@ public:
GameScene();
void onEnter() override;
void onExit() override;
std::vector<engine::AssetRequest> getRequiredAssets() const override;
private:
std::shared_ptr<engine::Camera> m_camera;