From d9e7fd70197174c03b6ec97cdb1ce2dcedf279b8 Mon Sep 17 00:00:00 2001 From: Thraix Date: Thu, 13 Apr 2023 21:00:36 +0200 Subject: [PATCH] Add Asset system - Add Abstract Asset class which defines Assets - Add AssetManager class to keep track of all the Asset - Add AssetFile class to cache the asset without loading it - Add UUID class to uniquely identify assets - Add MetaFile class to load meta asset files --- CopiumEngine/CopiumEngine.vcxproj | 17 ++ CopiumEngine/CopiumEngine.vcxproj.filters | 47 +++++ CopiumEngine/assets/fox.meta | 3 + CopiumEngine/assets/fox2.meta | 3 + CopiumEngine/src/copium/asset/Asset.cpp | 41 ++++ CopiumEngine/src/copium/asset/Asset.h | 30 +++ CopiumEngine/src/copium/asset/AssetFile.cpp | 50 +++++ CopiumEngine/src/copium/asset/AssetFile.h | 28 +++ .../src/copium/asset/AssetManager.cpp | 163 ++++++++++++++++ CopiumEngine/src/copium/asset/AssetManager.h | 72 +++++++ CopiumEngine/src/copium/asset/AssetMeta.h | 26 +++ CopiumEngine/src/copium/core/Application.cpp | 13 +- CopiumEngine/src/copium/core/Application.h | 6 +- CopiumEngine/src/copium/core/Vulkan.cpp | 10 +- CopiumEngine/src/copium/main.cpp | 6 +- CopiumEngine/src/copium/renderer/Renderer.cpp | 12 +- CopiumEngine/src/copium/renderer/Renderer.h | 3 +- CopiumEngine/src/copium/sampler/Texture2D.cpp | 13 +- CopiumEngine/src/copium/sampler/Texture2D.h | 5 +- CopiumEngine/src/copium/util/Common.h | 24 ++- CopiumEngine/src/copium/util/MetaFile.cpp | 179 ++++++++++++++++++ CopiumEngine/src/copium/util/MetaFile.h | 56 ++++++ .../src/copium/util/RuntimeException.cpp | 13 ++ .../src/copium/util/RuntimeException.h | 16 ++ CopiumEngine/src/copium/util/StringUtil.cpp | 37 ++++ CopiumEngine/src/copium/util/StringUtil.h | 22 +++ CopiumEngine/src/copium/util/UUID.cpp | 93 +++++++++ CopiumEngine/src/copium/util/UUID.h | 35 ++++ .../src/copium/util/VulkanException.h | 16 +- 29 files changed, 1002 insertions(+), 37 deletions(-) create mode 100644 CopiumEngine/assets/fox.meta create mode 100644 CopiumEngine/assets/fox2.meta create mode 100644 CopiumEngine/src/copium/asset/Asset.cpp create mode 100644 CopiumEngine/src/copium/asset/Asset.h create mode 100644 CopiumEngine/src/copium/asset/AssetFile.cpp create mode 100644 CopiumEngine/src/copium/asset/AssetFile.h create mode 100644 CopiumEngine/src/copium/asset/AssetManager.cpp create mode 100644 CopiumEngine/src/copium/asset/AssetManager.h create mode 100644 CopiumEngine/src/copium/asset/AssetMeta.h create mode 100644 CopiumEngine/src/copium/util/MetaFile.cpp create mode 100644 CopiumEngine/src/copium/util/MetaFile.h create mode 100644 CopiumEngine/src/copium/util/RuntimeException.cpp create mode 100644 CopiumEngine/src/copium/util/RuntimeException.h create mode 100644 CopiumEngine/src/copium/util/StringUtil.cpp create mode 100644 CopiumEngine/src/copium/util/StringUtil.h create mode 100644 CopiumEngine/src/copium/util/UUID.cpp create mode 100644 CopiumEngine/src/copium/util/UUID.h diff --git a/CopiumEngine/CopiumEngine.vcxproj b/CopiumEngine/CopiumEngine.vcxproj index 1aa92ae..0c0deab 100644 --- a/CopiumEngine/CopiumEngine.vcxproj +++ b/CopiumEngine/CopiumEngine.vcxproj @@ -166,6 +166,9 @@ + + + @@ -184,6 +187,7 @@ + @@ -198,14 +202,21 @@ + + + + + + + @@ -224,6 +235,7 @@ + @@ -239,14 +251,19 @@ + + + + + diff --git a/CopiumEngine/CopiumEngine.vcxproj.filters b/CopiumEngine/CopiumEngine.vcxproj.filters index 502c68b..dce9c0c 100644 --- a/CopiumEngine/CopiumEngine.vcxproj.filters +++ b/CopiumEngine/CopiumEngine.vcxproj.filters @@ -129,6 +129,27 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -248,6 +269,30 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -256,5 +301,7 @@ + + \ No newline at end of file diff --git a/CopiumEngine/assets/fox.meta b/CopiumEngine/assets/fox.meta new file mode 100644 index 0000000..efe3944 --- /dev/null +++ b/CopiumEngine/assets/fox.meta @@ -0,0 +1,3 @@ +[Texture2D] +filepath=res/textures/texture.png +uuid=f49a5284-d666-0982-95ca-cf68cc3d4f45 diff --git a/CopiumEngine/assets/fox2.meta b/CopiumEngine/assets/fox2.meta new file mode 100644 index 0000000..d0215b7 --- /dev/null +++ b/CopiumEngine/assets/fox2.meta @@ -0,0 +1,3 @@ +[Texture2D] +filepath=res/textures/texture2.png +uuid=0964e525-22c3-4d25-d5c6-a162965f6e8d diff --git a/CopiumEngine/src/copium/asset/Asset.cpp b/CopiumEngine/src/copium/asset/Asset.cpp new file mode 100644 index 0000000..67b0d01 --- /dev/null +++ b/CopiumEngine/src/copium/asset/Asset.cpp @@ -0,0 +1,41 @@ +#include "copium/asset/Asset.h" + +namespace Copium +{ + Asset::Asset(AssetType type) + { + metaData.type = type; + } + + Asset::~Asset() = default; + + AssetHandle Asset::GetHandle() const + { + return metaData.handle; + } + + AssetType Asset::GetType() const + { + return metaData.type; + } + + const std::string& Asset::GetName() const + { + return metaData.name; + } + + UUID Asset::GetUUID() const + { + return metaData.uuid; + } + + bool Asset::isRuntime() const + { + return metaData.isRuntime; + } + + Asset::operator AssetHandle() const + { + return metaData.handle; + } +} \ No newline at end of file diff --git a/CopiumEngine/src/copium/asset/Asset.h b/CopiumEngine/src/copium/asset/Asset.h new file mode 100644 index 0000000..1c0ac7e --- /dev/null +++ b/CopiumEngine/src/copium/asset/Asset.h @@ -0,0 +1,30 @@ +#pragma once + +#include "copium/asset/AssetMeta.h" +#include "copium/util/MetaFile.h" +#include "copium/util/UUID.h" + +#include + +namespace Copium +{ + + class Asset + { + friend class AssetManager; + public: + Asset(AssetType type); + virtual ~Asset(); + + AssetHandle GetHandle() const; + AssetType GetType() const; + const std::string& GetName() const; + UUID GetUUID() const; + bool isRuntime() const; + + operator AssetHandle() const; + + private: + AssetMeta metaData; + }; +} diff --git a/CopiumEngine/src/copium/asset/AssetFile.cpp b/CopiumEngine/src/copium/asset/AssetFile.cpp new file mode 100644 index 0000000..ea3200a --- /dev/null +++ b/CopiumEngine/src/copium/asset/AssetFile.cpp @@ -0,0 +1,50 @@ +#include "copium/asset/AssetFile.h" + +#include "copium/util/FileSystem.h" + +namespace Copium +{ + AssetFile::AssetFile(const std::string& path) + : path{path} + { + Load(); + } + + bool AssetFile::NeedReload() const + { + return dateModified < FileSystem::DateModified(path); + } + + void AssetFile::Load() + { + const std::vector> strToType{{"Texture2D", AssetType::Texture2D}}; + MetaFile metaFile{path}; + for (auto&& [str, type] : strToType) + { + if (!metaFile.HasMetaClass(str)) + continue; + + Load(metaFile, str, type); + return; + } + CP_ABORT("Load : Unknown Asset type"); + } + + const std::string& AssetFile::GetPath() const + { + return path; + } + + UUID AssetFile::GetUUID() const + { + return uuid; + } + + void AssetFile::Load(const MetaFile& metaFile, const std::string& className, AssetType assetType) + { + const MetaFileClass& metaClass = metaFile.GetMetaClass(className); + uuid = UUID{metaClass.GetValue("uuid")}; + type = assetType; + dateModified = FileSystem::DateModified(path); + } +} \ No newline at end of file diff --git a/CopiumEngine/src/copium/asset/AssetFile.h b/CopiumEngine/src/copium/asset/AssetFile.h new file mode 100644 index 0000000..804115a --- /dev/null +++ b/CopiumEngine/src/copium/asset/AssetFile.h @@ -0,0 +1,28 @@ +#pragma once + +#include "copium/asset/AssetMeta.h" +#include "copium/util/MetaFile.h" +#include "copium/util/UUID.h" + +namespace Copium +{ + class AssetFile + { + private: + std::string path; + AssetType type; + UUID uuid; + int64_t dateModified; + + public: + AssetFile(const std::string& path); + + bool NeedReload() const; + void Load(); + + const std::string& GetPath() const; + UUID GetUUID() const; + private: + void Load(const MetaFile& metaFile, const std::string& className, AssetType assetType); + }; +} \ No newline at end of file diff --git a/CopiumEngine/src/copium/asset/AssetManager.cpp b/CopiumEngine/src/copium/asset/AssetManager.cpp new file mode 100644 index 0000000..83f2c5f --- /dev/null +++ b/CopiumEngine/src/copium/asset/AssetManager.cpp @@ -0,0 +1,163 @@ +#include "copium/asset/AssetManager.h" + +#include "copium/sampler/Texture2D.h" +#include "copium/util/Common.h" +#include "copium/util/MetaFile.h" + +#include +#include + +namespace Copium +{ + std::vector AssetManager::assetDirs; + std::map> AssetManager::assets; + std::map AssetManager::pathToAssetCache; + std::map AssetManager::nameToAssetCache; + std::vector AssetManager::cachedAssetFiles; + AssetHandle AssetManager::assetHandle = 1; + AssetHandle AssetManager::runtimeAssetHandle = (1 << 31) + 1; + + void AssetManager::RegisterAssetDir(std::string assetDir) + { + if (assetDir.back() == '/') + assetDir.pop_back(); + assetDirs.emplace_back(assetDir); + for (std::filesystem::recursive_directory_iterator it(assetDir), end; it != end; ++it) + { + if (std::filesystem::is_directory(it->path())) + continue; + std::filesystem::path assetDirPath{assetDir}; + cachedAssetFiles.emplace_back(assetDir + "/" + std::filesystem::absolute(it->path()).string().substr(std::filesystem::absolute(assetDirPath).string().size()).c_str()); + } + UUID uuid{}; + CP_INFO(uuid.ToString().c_str()); + } + + void AssetManager::UnregisterAssetDir(std::string assetDir) + { + if (assetDir.back() == '/') + assetDir.pop_back(); + + for (auto it = assetDirs.begin(); it != assetDirs.end(); ++it) + { + if (*it == assetDir) + { + assetDirs.erase(it); + return; + } + } + } + + Asset& AssetManager::GetAsset(AssetHandle handle) + { + auto it = assets.find(handle); + CP_ASSERT(it != assets.end(), "GetAsset : Asset not loaded"); + return *it->second.get(); + } + + Asset& AssetManager::LoadAsset(const std::string& assetPath) + { + CP_DEBUG("LoadAsset : Loading Asset: %s", assetPath.c_str()); + + for (auto& dir : assetDirs) + { + std::string path = dir + "/" + assetPath; + auto it = pathToAssetCache.find(path); + if (it != pathToAssetCache.end()) + return *assets.find(it->second)->second.get(); + + std::ifstream file{path}; + if (!file.good()) + continue; + + MetaFile metaFile{path}; + if (metaFile.HasMetaClass("Texture2D")) + { + return CreateAsset(metaFile, "Texture2D"); + } + CP_ABORT("LoadAsset : Unknown Asset type: %s/%s", dir.c_str(), assetPath.c_str()); + } + CP_ABORT("LoadAsset : Unknown Asset: %s", assetPath.c_str()); + } + + + Asset& AssetManager::LoadAsset(const UUID& uuid) + { + CP_DEBUG("LoadAsset : Loading uuid Asset: %s", uuid.ToString().c_str()); + for (auto&& assetFile : cachedAssetFiles) + { + if (assetFile.GetUUID() != uuid) + continue; + + if (assetFile.NeedReload()) + assetFile.Load(); + + if (assetFile.GetUUID() != uuid) + continue; + + CP_DEBUG("LoadAsset : Loading Asset: %s", assetFile.GetPath().c_str()); + auto it = pathToAssetCache.find(assetFile.GetPath()); + if (it != pathToAssetCache.end()) + return *assets.find(it->second)->second.get(); + + MetaFile metaFile{assetFile.GetPath()}; + if (metaFile.HasMetaClass("Texture2D")) + { + return CreateAsset(metaFile, "Texture2D"); + } + CP_ABORT("LoadAsset : Unknown Asset type: %s", assetFile.GetPath().c_str()); + } + CP_ABORT("LoadAsset : Asset not found with uuid=%s", uuid.ToString().c_str()); + // TODO: Reload the assetCache to see if a new file has appeared with that uuid + } + + void AssetManager::UnloadAsset(AssetHandle handle) + { + auto it = assets.find(handle); + CP_ASSERT(it != assets.end(), "UnloadAsset : Asset not loaded"); + + if (it->second->isRuntime()) + nameToAssetCache.erase(it->second->GetName()); + else + pathToAssetCache.erase(it->second->GetName()); + assets.erase(it); + } + + void AssetManager::Cleanup() + { + if (assets.empty()) + return; + CP_WARN("Cleanup : Cleaning up %d loaded assets", assets.size()); + assets.clear(); + nameToAssetCache.clear(); + pathToAssetCache.clear(); + } + + Asset& AssetManager::RegisterRuntimeAsset(const std::string& name, std::unique_ptr&& asset) + { + auto it = nameToAssetCache.find(name); + CP_ASSERT(it == nameToAssetCache.end(), "RegistedRuntimeAsset : Asset already exists: %s", name); + + AssetHandle handle = runtimeAssetHandle++; + Asset* asset2 = assets.emplace(handle, std::move(asset)).first->second.get(); + asset2->metaData.handle = handle; + asset2->metaData.name = name; + asset2->metaData.uuid = UUID(); + asset2->metaData.isRuntime = true; + nameToAssetCache.emplace(name, handle); + return *asset2; + } + + template + Asset& AssetManager::CreateAsset(const MetaFile& metaFile, const std::string& metaFileClass) + { + AssetHandle handle = assetHandle++; + pathToAssetCache.emplace(metaFile.GetFilePath(), handle); + Asset& asset = *assets.emplace(handle, std::make_unique(metaFile)).first->second.get(); + asset.metaData.handle = handle; + asset.metaData.name = metaFile.GetFilePath(); + asset.metaData.uuid = UUID{metaFile.GetMetaClass(metaFileClass).GetValue("uuid")}; + asset.metaData.isRuntime = false; + return asset; + } +} \ No newline at end of file diff --git a/CopiumEngine/src/copium/asset/AssetManager.h b/CopiumEngine/src/copium/asset/AssetManager.h new file mode 100644 index 0000000..6c7cdda --- /dev/null +++ b/CopiumEngine/src/copium/asset/AssetManager.h @@ -0,0 +1,72 @@ +#pragma once + +#include "copium/asset/Asset.h" +#include "copium/asset/AssetFile.h" +#include "copium/util/Common.h" + +#include +#include + +namespace Copium +{ + class AssetManager + { + CP_STATIC_CLASS(AssetManager); + private: + static std::vector assetDirs; + static std::map> assets; + + static std::map pathToAssetCache; + static std::map nameToAssetCache; + static AssetHandle assetHandle; + static AssetHandle runtimeAssetHandle; + + static std::vector cachedAssetFiles; // TODO: Make a set? + public: + static void RegisterAssetDir(std::string assetDir); + static void UnregisterAssetDir(std::string assetDir); + static Asset& GetAsset(AssetHandle handle); + static Asset& LoadAsset(const std::string& assetPath); + static Asset& LoadAsset(const UUID& uuid); + static void UnloadAsset(AssetHandle handle); + static Asset& RegisterRuntimeAsset(const std::string& name, std::unique_ptr&& asset); + static void Cleanup(); + + template + static AssetT& LoadAsset(const std::string& assetPath) + { + AssetT* asset = dynamic_cast(&LoadAsset(assetPath)); + CP_ASSERT(asset, "LoadAsset : Invalid Asset cast"); + return *asset; + } + + template + static AssetT& LoadAsset(const UUID& uuid) + { + AssetT* asset = dynamic_cast(&LoadAsset(uuid)); + CP_ASSERT(asset, "LoadAsset : Invalid Asset cast"); + return *asset; + } + + template + static AssetT& GetAsset(AssetHandle handle) + { + Asset& asset = GetAsset(handle); + AssetT* assetT = dynamic_cast(&asset); + CP_ASSERT(assetT, "GetAsset : Invalid Asset cast"); + return *assetT; + } + + template + static AssetT& RegisterRuntimeAsset(const std::string& name, std::unique_ptr&& assetT) + { + AssetT* ptr = assetT.release(); + Asset& asset = RegisterRuntimeAsset(name, std::unique_ptr((Asset*)ptr)); + return *(AssetT*)&asset; + } + + private: + template + static Asset& CreateAsset(const MetaFile& metaFile, const std::string& metaFileClass); + }; +} diff --git a/CopiumEngine/src/copium/asset/AssetMeta.h b/CopiumEngine/src/copium/asset/AssetMeta.h new file mode 100644 index 0000000..3715893 --- /dev/null +++ b/CopiumEngine/src/copium/asset/AssetMeta.h @@ -0,0 +1,26 @@ +#pragma once + +#include "copium/util/UUID.h" + +#include +#include + +namespace Copium +{ + enum class AssetType + { + Pipeline, + Texture2D, + Sound, + }; + using AssetHandle = uint64_t; + + struct AssetMeta + { + AssetHandle handle; + AssetType type; + std::string name; + UUID uuid; + bool isRuntime; + }; +} \ No newline at end of file diff --git a/CopiumEngine/src/copium/core/Application.cpp b/CopiumEngine/src/copium/core/Application.cpp index 59f6af4..43255d8 100644 --- a/CopiumEngine/src/copium/core/Application.cpp +++ b/CopiumEngine/src/copium/core/Application.cpp @@ -3,6 +3,7 @@ #include "copium/core/Vulkan.h" #include "copium/mesh/Vertex.h" #include "copium/mesh/VertexPassthrough.h" +#include "copium/asset/AssetManager.h" #include @@ -49,6 +50,8 @@ namespace Copium Application::~Application() { vkDeviceWaitIdle(Vulkan::GetDevice()); + AssetManager::UnloadAsset(texture2D); + AssetManager::UnloadAsset(texture2D2); } bool Application::Update() @@ -81,8 +84,8 @@ namespace Copium void Application::InitializeTextureSampler() { - texture2D = std::make_unique("res/textures/texture.png"); - texture2D2 = std::make_unique("res/textures/texture2.png"); + texture2D = AssetManager::LoadAsset("fox.meta"); + texture2D2 = AssetManager::LoadAsset("fox2.meta"); } void Application::InitializeDescriptorSets() @@ -90,7 +93,7 @@ namespace Copium descriptorPool = std::make_unique(); descriptorSet = graphicsPipeline->CreateDescriptorSet(*descriptorPool, 0); - descriptorSet->SetSampler(*texture2D, 1); + descriptorSet->SetSampler(AssetManager::GetAsset(texture2D), 1); descriptorSetPassthrough = graphicsPipelinePassthrough->CreateDescriptorSet(*descriptorPool, 0); descriptorSetPassthrough->SetSampler(framebuffer->GetColorAttachment(), 0); @@ -144,8 +147,8 @@ namespace Copium renderer->Quad(glm::vec2{-1 + x * 0.2 + 0.05, -1 + y * 0.2 + 0.05}, glm::vec2{0.1, 0.1}, glm::vec3{x * 0.1, y * 0.1, 1.0}); } } - renderer->Quad(glm::vec2{-0.9, -0.4}, glm::vec2{0.8, 0.8}, *texture2D); - renderer->Quad(glm::vec2{ 0.1, -0.4}, glm::vec2{0.8, 0.8}, *texture2D2); + renderer->Quad(glm::vec2{-0.9, -0.4}, glm::vec2{0.8, 0.8}, AssetManager::GetAsset(texture2D)); + renderer->Quad(glm::vec2{ 0.1, -0.4}, glm::vec2{0.8, 0.8}, AssetManager::GetAsset(texture2D2)); renderer->End(); framebuffer->Unbind(*commandBuffer); diff --git a/CopiumEngine/src/copium/core/Application.h b/CopiumEngine/src/copium/core/Application.h index 2ac290d..c39c606 100644 --- a/CopiumEngine/src/copium/core/Application.h +++ b/CopiumEngine/src/copium/core/Application.h @@ -1,12 +1,12 @@ #pragma once +#include "copium/asset/AssetMeta.h" #include "copium/buffer/Framebuffer.h" #include "copium/mesh/Mesh.h" #include "copium/pipeline/DescriptorPool.h" #include "copium/pipeline/DescriptorSet.h" #include "copium/pipeline/Pipeline.h" #include "copium/renderer/Renderer.h" -#include "copium/sampler/Texture2D.h" namespace Copium { @@ -16,8 +16,8 @@ namespace Copium private: std::unique_ptr renderer; std::unique_ptr framebuffer; - std::unique_ptr texture2D; - std::unique_ptr texture2D2; + AssetHandle texture2D; + AssetHandle texture2D2; std::unique_ptr descriptorPool; std::unique_ptr descriptorSet; std::unique_ptr descriptorSetPassthrough; diff --git a/CopiumEngine/src/copium/core/Vulkan.cpp b/CopiumEngine/src/copium/core/Vulkan.cpp index 16fb8aa..0717147 100644 --- a/CopiumEngine/src/copium/core/Vulkan.cpp +++ b/CopiumEngine/src/copium/core/Vulkan.cpp @@ -1,5 +1,7 @@ #include "copium/core/Vulkan.h" +#include "copium/asset/AssetManager.h" + namespace Copium { std::unique_ptr Vulkan::instance; @@ -10,13 +12,19 @@ namespace Copium void Vulkan::Initialize() { instance = std::make_unique("Copium Engine"); - window = std::make_unique( "Copium Engine", 1920, 1080, Window::Mode::Windowed); + window = std::make_unique("Copium Engine", 1920, 1080, Window::Mode::Windowed); device = std::make_unique(); swapChain = std::make_unique(); + + // TODO: Make the working directory always be relative to the assets folder + // By looking at where the executable is, since that should always be in the bin folder (it currently isn't though) + AssetManager::RegisterAssetDir("assets/"); } void Vulkan::Destroy() { + AssetManager::UnregisterAssetDir("assets/"); + AssetManager::Cleanup(); swapChain.reset(); device.reset(); window.reset(); diff --git a/CopiumEngine/src/copium/main.cpp b/CopiumEngine/src/copium/main.cpp index f33daf9..ec0d302 100644 --- a/CopiumEngine/src/copium/main.cpp +++ b/CopiumEngine/src/copium/main.cpp @@ -5,9 +5,13 @@ #include -int main() +int main(int argc, char** argv) { CP_ASSERT(glfwInit() == GLFW_TRUE, "main : Failed to initialize the glfw context"); + for (int i = 0; i < argc; i++) + { + CP_INFO(argv[i]); + } Copium::Vulkan::Initialize(); { diff --git a/CopiumEngine/src/copium/renderer/Renderer.cpp b/CopiumEngine/src/copium/renderer/Renderer.cpp index 3e54677..da19dd7 100644 --- a/CopiumEngine/src/copium/renderer/Renderer.cpp +++ b/CopiumEngine/src/copium/renderer/Renderer.cpp @@ -1,5 +1,6 @@ #include "copium/renderer/Renderer.h" +#include "copium/asset/AssetManager.h" #include "copium/core/Vulkan.h" #include "copium/pipeline/PipelineCreator.h" #include "copium/renderer/RendererVertex.h" @@ -14,13 +15,18 @@ namespace Copium Renderer::Renderer(VkRenderPass renderPass) : descriptorPool{}, ibo{MAX_NUM_INDICES}, - emptyTexture{{0, 0, 0, 255}, 1, 1}, - samplers{MAX_NUM_TEXTURES, &emptyTexture} + emptyTexture{AssetManager::RegisterRuntimeAsset("empty", std::make_unique(std::vector{0, 0, 0, 255}, 1, 1))}, + samplers{MAX_NUM_TEXTURES, &AssetManager::GetAsset(emptyTexture)} { InitializeIndexBuffer(); InitializeGraphicsPipeline(renderPass); } + Renderer::~Renderer() + { + AssetManager::UnloadAsset(emptyTexture); + } + void Renderer::Quad(const glm::vec2& pos, const glm::vec2& size, const glm::vec3& color) { AllocateQuad(); @@ -144,7 +150,7 @@ namespace Copium void Renderer::NextBatch() { batchIndex++; - std::fill(samplers.begin(), samplers.end(), &emptyTexture); + std::fill(samplers.begin(), samplers.end(), &AssetManager::GetAsset(emptyTexture)); if (batchIndex >= batches.size()) { batches.emplace_back(std::make_unique(*graphicsPipeline, descriptorPool, MAX_NUM_VERTICES, samplers)); diff --git a/CopiumEngine/src/copium/renderer/Renderer.h b/CopiumEngine/src/copium/renderer/Renderer.h index 5565c3c..5209fea 100644 --- a/CopiumEngine/src/copium/renderer/Renderer.h +++ b/CopiumEngine/src/copium/renderer/Renderer.h @@ -19,7 +19,7 @@ namespace Copium private: DescriptorPool descriptorPool; IndexBuffer ibo; - Texture2D emptyTexture; + AssetHandle emptyTexture; std::unique_ptr graphicsPipeline; std::vector> batches; @@ -32,6 +32,7 @@ namespace Copium void* mappedVertexBuffer; public: Renderer(VkRenderPass renderPass); + ~Renderer(); void Quad(const glm::vec2& from, const glm::vec2& to, const glm::vec3& color = glm::vec3{1, 1, 1}); void Quad(const glm::vec2& from, const glm::vec2& to, const Sampler& sampler, const glm::vec2& texCoord1 = glm::vec2{0, 0}, const glm::vec2& texCoord2 = glm::vec2{1, 1}); diff --git a/CopiumEngine/src/copium/sampler/Texture2D.cpp b/CopiumEngine/src/copium/sampler/Texture2D.cpp index 0893dfa..c204be2 100644 --- a/CopiumEngine/src/copium/sampler/Texture2D.cpp +++ b/CopiumEngine/src/copium/sampler/Texture2D.cpp @@ -5,17 +5,20 @@ #define STB_IMAGE_IMPLEMENTATION #include +#include + namespace Copium { - Texture2D::Texture2D(const std::string& filename) - : Sampler{} + Texture2D::Texture2D(const MetaFile& metaFile) + : Sampler{}, Asset{AssetType::Texture2D} { - CP_DEBUG("Texture2D : Loading texture file: %s", filename.c_str()); - InitializeTextureImageFromFile(filename); + const std::string& filepath = metaFile.GetMetaClass("Texture2D").GetValue("filepath"); + CP_DEBUG("Texture2D : Loading texture file: %s", filepath.c_str()); + InitializeTextureImageFromFile(filepath); } Texture2D::Texture2D(const std::vector& rgbaData, int width, int height) - : Sampler{} + : Sampler{}, Asset{AssetType::Texture2D} { CP_ASSERT(rgbaData.size() == width * height * 4, "rgbaData has invalid size, should be equal to width * height * 4 (%d) actually is %d", width * height * 4, rgbaData.size()); InitializeTextureImageFromData((void*)rgbaData.data(), width, height); diff --git a/CopiumEngine/src/copium/sampler/Texture2D.h b/CopiumEngine/src/copium/sampler/Texture2D.h index 5d00aca..5f3dcf4 100644 --- a/CopiumEngine/src/copium/sampler/Texture2D.h +++ b/CopiumEngine/src/copium/sampler/Texture2D.h @@ -1,5 +1,6 @@ #pragma once +#include "copium/asset/Asset.h" #include "copium/buffer/CommandBufferScoped.h" #include "copium/sampler/Image.h" #include "copium/sampler/Sampler.h" @@ -9,7 +10,7 @@ namespace Copium { - class Texture2D final : public Sampler + class Texture2D final : public Sampler, public Asset { CP_DELETE_COPY_AND_MOVE_CTOR(Texture2D); private: @@ -17,7 +18,7 @@ namespace Copium VkDeviceMemory imageMemory; VkImageView imageView; public: - Texture2D(const std::string& filename); + Texture2D(const MetaFile& metaFile); Texture2D(const std::vector& rgbaData, int width, int height); ~Texture2D() override; diff --git a/CopiumEngine/src/copium/util/Common.h b/CopiumEngine/src/copium/util/Common.h index 0769fd8..b07a82d 100644 --- a/CopiumEngine/src/copium/util/Common.h +++ b/CopiumEngine/src/copium/util/Common.h @@ -1,5 +1,6 @@ #pragma once +#include "copium/util/RuntimeException.h" #include "copium/util/VulkanException.h" #include @@ -11,30 +12,31 @@ #define CP_TERM_CLEAR "\033[0m" #define CP_DEBUG(format, ...) std::cout << CP_TERM_GRAY << "[DBG] " << Copium::String::Format(format, __VA_ARGS__) << CP_TERM_CLEAR << std::endl -#define CP_INFO(format, ...) std::cout << "[INF] " << Copium::String::Format(format, __VA_ARGS__) << std::endl +#define CP_INFO(format, ...) std::cout << "[INF] " << Copium::String::Format(format, __VA_ARGS__) << std::endl #define CP_WARN(format, ...) std::cout << CP_TERM_YELLOW << "[WRN] " << Copium::String::Format(format, __VA_ARGS__) << CP_TERM_CLEAR << std::endl #define CP_ERR(format, ...) std::cout << CP_TERM_RED << "[ERR] " << Copium::String::Format(format, __VA_ARGS__) << CP_TERM_CLEAR << std::endl // Continue traces, will not print the [XXX] tag before the log #define CP_DEBUG_CONT(format, ...) std::cout << CP_TERM_GRAY << " " << Copium::String::Format(format, __VA_ARGS__) << CP_TERM_CLEAR << std::endl -#define CP_INFO_CONT(format, ...) std::cout << " " << Copium::String::Format(format, __VA_ARGS__) << std::endl +#define CP_INFO_CONT(format, ...) std::cout << " " << Copium::String::Format(format, __VA_ARGS__) << std::endl #define CP_WARN_CONT(format, ...) std::cout << CP_TERM_YELLOW << " " << Copium::String::Format(format, __VA_ARGS__) << CP_TERM_CLEAR << std::endl #define CP_ERR_CONT(format, ...) std::cout << CP_TERM_RED << " " << Copium::String::Format(format, __VA_ARGS__) << CP_TERM_CLEAR << std::endl -#define CP_UNIMPLEMENTED() CP_WARN("%s is unimplemented", __FUNCTION__) #define CP_ABORT(format, ...) \ do \ { \ - CP_ERR(format, __VA_ARGS__); \ - throw std::runtime_error(Copium::String::Format(format, __VA_ARGS__)); \ + CP_ERR("Aborted at %s:%d", __FILE__, __LINE__); \ + CP_ERR_CONT(format, __VA_ARGS__); \ + throw Copium::RuntimeException(Copium::String::Format(format, __VA_ARGS__)); \ } while(false) #define CP_ASSERT(Function, format, ...) \ do \ { \ if(!(Function)) \ { \ - CP_ERR(format, __VA_ARGS__); \ - throw std::runtime_error(Copium::String::Format(format, __VA_ARGS__)); \ + CP_ERR("Assertion failed at %s:%d", __FILE__, __LINE__); \ + CP_ERR_CONT(format, __VA_ARGS__); \ + throw Copium::RuntimeException(Copium::String::Format(format, __VA_ARGS__)); \ } \ } while(false) #define CP_VK_ASSERT(Function, format, ...) \ @@ -42,11 +44,15 @@ { \ if(Function != VK_SUCCESS) \ { \ - CP_ERR(format, __VA_ARGS__); \ - throw VulkanException(Copium::String::Format(format, __VA_ARGS__)); \ + CP_ERR("Assertion failed at %s:%d", __FILE__, __LINE__); \ + CP_ERR_CONT(format, __VA_ARGS__); \ + throw Copium::VulkanException(Copium::String::Format(format, __VA_ARGS__)); \ } \ } while(false) +#define CP_UNIMPLEMENTED() CP_WARN("%s is unimplemented", __FUNCTION__) +#define CP_ABORT_UNIMPLEMENTED() CP_ABORT("%s is unimplemented", __FUNCTION__) + #define CP_STATIC_CLASS(ClassName)\ ClassName() = delete #define CP_DELETE_COPY_AND_MOVE_CTOR(ClassName) \ diff --git a/CopiumEngine/src/copium/util/MetaFile.cpp b/CopiumEngine/src/copium/util/MetaFile.cpp new file mode 100644 index 0000000..ffd1cf4 --- /dev/null +++ b/CopiumEngine/src/copium/util/MetaFile.cpp @@ -0,0 +1,179 @@ +#include "copium/util/MetaFile.h" + +#include "copium/util/Common.h" +#include "copium/util/StringUtil.h" + +#include + +namespace Copium +{ + std::string MetaFileClass::GetValue(const std::string& key, const std::string& val) const + { + auto it = values.find(key); + if(it != values.end()) + return it->second; + return val; + } + + bool MetaFileClass::HasValue(const std::string& key) const + { + return values.find(key) != values.end(); + } + + const std::string& MetaFileClass::GetValue(const std::string& key) const + { + auto it = values.find(key); + CP_ASSERT(it != values.end(), "GetValue : Value does not exist: %s", key.c_str()); + return it->second; + } + + const std::map& MetaFileClass::GetValues() const + { + return values; + } + + void MetaFileClass::AddValue(const std::string& key, const std::string& val) + { + values.emplace(key, val); + } + + std::ostream& operator<<(std::ostream& stream, const MetaFileClass& file) + { + for(auto value : file.GetValues()) + { + stream << value.first << "=" << value.second << std::endl; + } + return stream; + } + + MetaFile::MetaFile() {} + MetaFile::MetaFile(const std::string& filepath) + : filepath{filepath} + { + std::ifstream stream(filepath); + + CP_ASSERT(stream.is_open(), "MetaFile : Could not find meta file: %s", filepath.c_str()); + LoadMetaFile(stream); + } + + MetaFile::MetaFile(std::istream& stream) + { + LoadMetaFile(stream); + } + + bool MetaFile::HasMetaClass(const std::string& className) const + { + return classes.find(className) != classes.end(); + } + + MetaFileClass& MetaFile::GetMetaClass(const std::string& className) + { + auto it = classes.find(className); + CP_ASSERT(it != classes.end(), "GetMetaClass : class does not exist: %s", className.c_str()); + return it->second; + } + + const MetaFileClass& MetaFile::GetMetaClass(const std::string& className) const + { + auto it = classes.find(className); + CP_ASSERT(it != classes.end(), "GetMetaClass : class does not exist: ", className.c_str()); + return it->second; + } + + const std::string& MetaFile::GetFilePath() const + { + return filepath; + } + + void MetaFile::AddMetaClass(const std::string& name, const MetaFileClass& metaClass) + { + classes[name] = metaClass; + } + + std::ostream& operator<<(std::ostream& stream, const MetaFile& file) + { + for(auto metaClass : file.classes) + { + stream << "[" << metaClass.first << "]" << std::endl; + stream << metaClass.second; + } + return stream; + } + + std::istream& operator>>(std::istream& stream, MetaFile& file) + { + file.classes.clear(); + file.LoadMetaFile(stream); + return stream; + } + + void MetaFile::LoadMetaFile(std::istream& stream) + { + std::string currentClass = ""; + auto metaClassIt = classes.end(); + std::string line; + while(std::getline(stream, line)) + { + std::string_view trimmedLine = StringUtil::Trim(line); + if(trimmedLine.empty()) + continue; + + if(trimmedLine == "---") + { + return; + } + + if(trimmedLine.front() == '[' && trimmedLine.back() == ']' ) + { + currentClass = StringUtil::Trim(line); + currentClass = currentClass.substr(1, currentClass.size() - 2); + metaClassIt = classes.find(currentClass); + CP_ASSERT(metaClassIt == classes.end(), "LoadMetaFile : Meta file contains two of the same class: %s", currentClass.c_str()); + metaClassIt = classes.emplace(currentClass, MetaFileClass{}).first; + continue; + } + + size_t pos = line.find("="); + if(pos == std::string::npos) + { + CP_WARN("LoadMetaFile : Meta file line does not contain \'=\'"); + continue; + } + + std::string_view key = StringUtil::Trim(std::string_view(line.c_str(), pos)); + std::string_view value = StringUtil::Trim(std::string_view(line.c_str() + pos + 1)); + if(key.length() == 0) + { + CP_WARN("LoadMetaFile : MetaFile key is empty"); + continue; + } + + CP_ASSERT(metaClassIt != classes.end(), "LoadMetaFile : No meta file header specified: ", filepath.c_str()); + auto res = metaClassIt->second.values.emplace(key, value); + if(!res.second) + { + CP_WARN("LoadMetaFile : Meta file key is defined twice: %s", std::string(key).c_str()); + } + } + } + + std::vector MetaFile::ReadList(const std::string& file) + { + std::vector metaFiles; + std::ifstream stream{file}; + if(stream) + { + MetaFile meta; + while(!stream.eof()) + { + MetaFile meta{}; + stream >> meta; + if(meta.classes.empty()) + continue; + metaFiles.emplace_back(meta); + } + return metaFiles; + } + return {}; + } +} diff --git a/CopiumEngine/src/copium/util/MetaFile.h b/CopiumEngine/src/copium/util/MetaFile.h new file mode 100644 index 0000000..05ce603 --- /dev/null +++ b/CopiumEngine/src/copium/util/MetaFile.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include + +namespace Copium +{ + class MetaFileClass + { + friend class MetaFile; + private: + std::map values; + + public: + MetaFileClass() {} + MetaFileClass(const std::map& values) + : values{values} + {} + std::string GetValue(const std::string& key, const std::string& val) const; + bool HasValue(const std::string& key) const; + const std::string& GetValue(const std::string& key) const; + const std::map& GetValues() const; + + void AddValue(const std::string& key, const std::string& val); + + friend std::ostream& operator<<(std::ostream& stream, const MetaFileClass& file); + }; + + class MetaFile + { + private: + std::string filepath; + std::map classes; // map> + + public: + MetaFile(); + MetaFile(const std::string& filepath); + MetaFile(std::istream& stream); + + bool HasMetaClass(const std::string& className) const; + MetaFileClass& GetMetaClass(const std::string& className); + const MetaFileClass& GetMetaClass(const std::string& className) const; + const std::string& GetFilePath() const; + + void AddMetaClass(const std::string& name, const MetaFileClass& metaClass); + + friend std::ostream& operator<<(std::ostream& stream, const MetaFile& file); + friend std::istream& operator>>(std::istream& stream, MetaFile& file); + + static std::vector ReadList(const std::string& file); + private: + void LoadMetaFile(std::istream& stream); + }; +} diff --git a/CopiumEngine/src/copium/util/RuntimeException.cpp b/CopiumEngine/src/copium/util/RuntimeException.cpp new file mode 100644 index 0000000..640521a --- /dev/null +++ b/CopiumEngine/src/copium/util/RuntimeException.cpp @@ -0,0 +1,13 @@ +#include "copium/util/RuntimeException.h" + +namespace Copium +{ + RuntimeException::RuntimeException(const std::string& str) + : errorMessage{str} + {} + + const std::string& RuntimeException::GetErrorMessage() const + { + return errorMessage; + } +} \ No newline at end of file diff --git a/CopiumEngine/src/copium/util/RuntimeException.h b/CopiumEngine/src/copium/util/RuntimeException.h new file mode 100644 index 0000000..a9ef0bd --- /dev/null +++ b/CopiumEngine/src/copium/util/RuntimeException.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace Copium +{ + class RuntimeException + { + private: + std::string errorMessage; + public: + RuntimeException(const std::string& str); + + const std::string& GetErrorMessage() const; + }; +} \ No newline at end of file diff --git a/CopiumEngine/src/copium/util/StringUtil.cpp b/CopiumEngine/src/copium/util/StringUtil.cpp new file mode 100644 index 0000000..e863f17 --- /dev/null +++ b/CopiumEngine/src/copium/util/StringUtil.cpp @@ -0,0 +1,37 @@ +#include "copium/util/StringUtil.h" + +namespace Copium +{ + size_t StringUtil::GetTrimStartPos(const std::string_view& str) + { + size_t pos = 0; + while(pos < str.size() && (str[pos] == ' ' || str[pos] == '\t')) + pos++; + + return pos; + } + + size_t StringUtil::GetTrimEndPos(const std::string_view& str) + { + if(str.empty()) + return 0; + size_t pos = str.size() - 1; + while(pos > 0 && (str[pos] == ' ' || str[pos] == '\t')) + pos--; + return pos; + } + + std::string_view StringUtil::Trim(const std::string_view& str) + { + size_t start = GetTrimStartPos(str); + size_t end = GetTrimEndPos(str); + if(start == str.size() || start > end) + return ""; + return std::string_view(str.data() + start, end - start + 1); + } + + std::string_view StringUtil::Trim(const std::string& str) + { + return Trim(std::string_view(str)); + } +} \ No newline at end of file diff --git a/CopiumEngine/src/copium/util/StringUtil.h b/CopiumEngine/src/copium/util/StringUtil.h new file mode 100644 index 0000000..bc95a73 --- /dev/null +++ b/CopiumEngine/src/copium/util/StringUtil.h @@ -0,0 +1,22 @@ +#pragma once + +#include "copium/util/Common.h" + +#include +#include + +namespace Copium +{ + + class StringUtil + { + CP_STATIC_CLASS(StringUtil); + + public: + static std::string_view Trim(const std::string& str); + static std::string_view Trim(const std::string_view& str); + private: + static size_t GetTrimStartPos(const std::string_view& str); + static size_t GetTrimEndPos(const std::string_view& str); + }; +} diff --git a/CopiumEngine/src/copium/util/UUID.cpp b/CopiumEngine/src/copium/util/UUID.cpp new file mode 100644 index 0000000..31099d2 --- /dev/null +++ b/CopiumEngine/src/copium/util/UUID.cpp @@ -0,0 +1,93 @@ +#include "copium/util/UUID.h" + +#include "copium/util/Common.h" + +namespace Copium +{ + + std::random_device UUID::randomDevice{}; + std::mt19937 UUID::randomGenerator{randomDevice()}; + std::uniform_int_distribution UUID::randomDistribution{std::numeric_limits::min(), std::numeric_limits::max()}; + + UUID::UUID() + : msb{randomDistribution(randomGenerator)}, lsb{randomDistribution(randomGenerator)} + {} + + UUID::UUID(uint64_t msb, uint64_t lsb) + : msb{msb}, lsb{lsb} + {} + + UUID::UUID(const std::string& uuidString) + : msb{0}, lsb{0} + { + CP_ASSERT(uuidString.size() == 36, "UUID : Invalid UUID string size: %s", uuidString.c_str()); + for (int i = 0; i < 18; i++) + { + if (i == 8 || i == 13) // skip "-" + continue; + msb <<= 4; + msb |= HexToDec(uuidString[i]); + } + for (int i = 19; i < 36; i++) + { + if (i == 23) // skip "-" + continue; + lsb <<= 4; + lsb |= HexToDec(uuidString[i]); + } + } + + std::string UUID::ToString() const + { + std::string string; + string.reserve(36); + for (int i = 0; i < 16; i++) + { + if (i == 8 || i == 12) string.push_back('-'); + string.push_back(DecToHex((msb >> (60 - i * 4)) & 0xf)); + } + string.push_back('-'); + for (int i = 0; i < 16; i++) + { + if (i == 4) string.push_back('-'); + string.push_back(DecToHex((lsb >> (60 - i * 4)) & 0xf)); + } + return string; + } + + bool UUID::operator==(const UUID& rhs) + { + return msb == rhs.msb && lsb == rhs.lsb; + } + + bool UUID::operator!=(const UUID& rhs) + { + return !(*this == rhs); + } + + bool UUID::operator<(const UUID& rhs) + { + if (msb != rhs.msb) + return msb < rhs.msb; + return lsb < rhs.lsb; + } + + std::ostream& operator<<(std::ostream& os, const UUID& uuid) + { + return os << uuid.ToString(); + } + + uint8_t UUID::HexToDec(char c) const + { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + CP_ABORT("HexToDec : Invalid char value: %c (%d)", c, (int)c); + } + + char UUID::DecToHex(uint8_t nibble) const + { + if (nibble >= 0 && nibble <= 9) return '0' + nibble; + if (nibble >= 10 && nibble <= 15) return 'a' + nibble - 10; + CP_ABORT("DecToHex : Invalid nibble value: %d", (int)nibble); + } +} diff --git a/CopiumEngine/src/copium/util/UUID.h b/CopiumEngine/src/copium/util/UUID.h new file mode 100644 index 0000000..56850d9 --- /dev/null +++ b/CopiumEngine/src/copium/util/UUID.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +namespace Copium +{ + class UUID + { + private: + uint64_t msb; + uint64_t lsb; + static std::random_device randomDevice; + static std::mt19937 randomGenerator; + static std::uniform_int_distribution randomDistribution; + public: + UUID(); + UUID(uint64_t msb, uint64_t lsb); + // Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + UUID(const std::string& uuidString); + + std::string ToString() const; + + bool operator==(const UUID& rhs); + bool operator!=(const UUID& rhs); + bool operator<(const UUID& rhs); + + friend std::ostream& operator<<(std::ostream& os, const UUID& uuid); + + private: + uint8_t HexToDec(char c) const; + char DecToHex(uint8_t byte) const; + }; +} diff --git a/CopiumEngine/src/copium/util/VulkanException.h b/CopiumEngine/src/copium/util/VulkanException.h index 018874c..d85e5be 100644 --- a/CopiumEngine/src/copium/util/VulkanException.h +++ b/CopiumEngine/src/copium/util/VulkanException.h @@ -1,14 +1,16 @@ #pragma once +#include "copium/util/RuntimeException.h" + #include namespace Copium { - class VulkanException : public std::runtime_error - { - public: - VulkanException(const std::string& str) - : runtime_error{str.c_str()} - {} - }; + class VulkanException : public RuntimeException + { + public: + VulkanException(const std::string& str) + : RuntimeException{str} + {} + }; }