ADD: AssetManager
This commit is contained in:
parent
eeb7c9c00f
commit
96d2e89d48
@ -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
12
assets/models/chess.mtl
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
63
engine/src/loader/assets/AssetManager.cpp
Normal file
63
engine/src/loader/assets/AssetManager.cpp
Normal 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);
|
||||
}
|
||||
39
engine/src/loader/assets/AssetManager.h
Normal file
39
engine/src/loader/assets/AssetManager.h
Normal 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
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
>;
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
#define COLORRACE_VAO_H
|
||||
|
||||
#pragma once
|
||||
#include <glad/glad.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "Vbo.h"
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user