From 31afd1095a4b4f5992bf528af2f58d870829ee3c Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Fri, 9 May 2025 14:28:40 -0400 Subject: [PATCH 1/2] WIP prototyping --- .../OvCore/include/OvCore/Assets/Asset.h | 18 +++++++++++ .../OvCore/include/OvCore/Assets/AssetRef.h | 27 ++++++++++++++++ .../include/OvCore/Assets/AssetRegistry.h | 31 +++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 Sources/Overload/OvCore/include/OvCore/Assets/Asset.h create mode 100644 Sources/Overload/OvCore/include/OvCore/Assets/AssetRef.h create mode 100644 Sources/Overload/OvCore/include/OvCore/Assets/AssetRegistry.h diff --git a/Sources/Overload/OvCore/include/OvCore/Assets/Asset.h b/Sources/Overload/OvCore/include/OvCore/Assets/Asset.h new file mode 100644 index 000000000..8c64f2e9d --- /dev/null +++ b/Sources/Overload/OvCore/include/OvCore/Assets/Asset.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace OvCore::Assets +{ + using AssetID = uint32_t; + + class Asset + { + public: + AssetID id; + + virtual bool IsLoaded() const = 0; + virtual void Load() = 0; + virtual void Offload() = 0; + }; +} diff --git a/Sources/Overload/OvCore/include/OvCore/Assets/AssetRef.h b/Sources/Overload/OvCore/include/OvCore/Assets/AssetRef.h new file mode 100644 index 000000000..888998941 --- /dev/null +++ b/Sources/Overload/OvCore/include/OvCore/Assets/AssetRef.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace OvCore::Assets +{ + template + concept AssetType = std::is_base_of::value; + + /** + * A simple asset reference. Doesn't need the underlying asset to be loaded. + */ + struct GenericAssetRef + { + AssetID id; + }; + + /** + * Provide utility functions to manage the underlying asset. + */ + template + class AssetRef : public GenericAssetRef + { + + }; +} diff --git a/Sources/Overload/OvCore/include/OvCore/Assets/AssetRegistry.h b/Sources/Overload/OvCore/include/OvCore/Assets/AssetRegistry.h new file mode 100644 index 000000000..e643ebc4c --- /dev/null +++ b/Sources/Overload/OvCore/include/OvCore/Assets/AssetRegistry.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include +#include + +namespace OvCore::Assets +{ + class AssetRegistry + { + public: + /** + * Discover assets in the given path and register them in the asset registry. + * @note This is quite expensive, and should probably be called only once at the beginning of the application. + * @param p_path + * @param p_recursive + */ + uint32_t DiscoverAssets(const std::filesystem::path& p_path, bool p_recursive = true); + + /** + * Register a single asset in the asset registry. + * @param p_path + */ + GenericAssetRef RegisterAssetAtPath(const std::filesystem::path& p_path); + + private: + std::unordered_map m_assets; + }; +} From 4fa4a056400bf88bd3ecb15ccae8f3ce1f24d519 Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Fri, 9 May 2025 17:29:15 -0400 Subject: [PATCH 2/2] Working on the POC --- .../OvCore/include/OvCore/Assets/Asset.h | 170 +++++++++++++++++- .../OvCore/include/OvCore/Assets/AssetRef.h | 19 ++ .../include/OvCore/Assets/AssetRegistry.h | 17 ++ 3 files changed, 201 insertions(+), 5 deletions(-) diff --git a/Sources/Overload/OvCore/include/OvCore/Assets/Asset.h b/Sources/Overload/OvCore/include/OvCore/Assets/Asset.h index 8c64f2e9d..e6bc7c2ff 100644 --- a/Sources/Overload/OvCore/include/OvCore/Assets/Asset.h +++ b/Sources/Overload/OvCore/include/OvCore/Assets/Asset.h @@ -1,18 +1,178 @@ #pragma once +#include #include +#include namespace OvCore::Assets { - using AssetID = uint32_t; + using GUID = uint32_t; - class Asset + GUID GenerateGUID() + { + static GUID id = 0; + return ++id; + } + + using AssetID = GUID; + + /** + * Can be seen as a reference to something on the disk (File). + */ + class File { public: - AssetID id; + File(const std::string_view p_path) : m_path(p_path) {} + bool Exists() const { return std::filesystem::exists(m_path); } + void MoveTo(const std::string_view p_newPath) { m_path = p_newPath; } + void Delete() { std::filesystem::remove(m_path); } + void SaveToDisk() { _Write(_Serialize()); } + void LoadFromDisk() { _Deserialize(_Read()); } + const std::string& GetPath() const { return m_path; } - virtual bool IsLoaded() const = 0; + protected: + std::string _Serialize() { return std::string{}; } + void _Deserialize(const std::string& p_data) { /*...*/ } + + private: + void _Write(const std::string& p_content) {}; // Write to disk + std::string _Read() {}; // Read from disk + + protected: + std::string m_path; + }; + + /** + * An asset is a file that can be loaded and used in the engine, and is identified by a GUID. + */ + class AAssetBase : public File + { + public: + // Make sure only the asset registry can create assets + AAssetBase(const std::string_view p_path) : + m_id(GenerateGUID()), + File(p_path) + { + } + + void Reload() + { + Unload(); + Load(); + } + + // Synchronous loading virtual void Load() = 0; - virtual void Offload() = 0; + virtual void Unload() = 0; + virtual bool IsLoaded() const = 0; + + private: + AssetID m_id; + std::string m_path; + }; + + class Mock + { + public: + Mock(int x, int y) {} + }; + + // Is this even needed? + class MockFactory + { + public: + Mock&& Create(int x, int y) + { + return Mock(x, y); + } + + Mock&& CreateFromPath(std::string p_path) + { + // Load file, parse, etc... + return Mock(0, 0); + } + }; + + template + class Asset : public AAssetBase + { + public: + Asset(const std::string_view p_path) : AAssetBase(p_path) {} + + // Template specialized for each asset type + void Load() override; + + void Unload() override + { + m_underlying.reset(); + } + + bool IsLoaded() const override + { + return m_underlying.has_value(); + } + + private: + std::optional m_underlying; // Ideally this stays hidden, no getter. }; + + // Template specialization for Mock + void Asset::Load() + { + auto factory = MockFactory{}; + m_underlying = std::make_optional(factory.Create(1, 2)); + } } + +#include +#include + +namespace +{ + // Example use case + int main() + { + using namespace OvCore::Assets; + using namespace OvCore::Global; + + // Somewhere at init + MockFactory factory; + ServiceLocator::Provide(factory); + + // This should be done in the asset registry + { + const auto asset = Asset{ "path/to/asset.ovmock" }; + } + + // The asset registry should only return a reference to the asset + const AssetRef mockAssetRef; // Let's assume this is returned by the asset registry + + // Check if the asset reference is valid (not expired) + if (mockAssetRef.IsValid()) // same as `if (mockAssetRef)` with operator bool. This only checks that the reference is valid, not that the asset is loaded. + { + // -> to access the underlying MockAsset& + Asset& asset = mockAssetRef.Get(); // Shouldn't fail since IsValid() returned true + + // If the asset is not loaded, this will load it. + // A fallback strategy could be added for asynchronous loading. + // e.g. while the asset is loading, use a placeholder asset. + Mock& mock = asset.Get(); + + // TODO: Consider using some sort of monad to handle the double indirection. + } + + // Should print "Is loaded: false" + std::cout << std::format("Is loaded: {}", asset.IsLoaded()) << std::endl; + + if (auto& mockInstance = mockAssetRef->GetUnderlying()) + { + + } + + // Constructor a mock instance + auto mockInstance = OvCore::Global::ServiceLocator::Get().Create(1, 2); + + + return 0; + } +} \ No newline at end of file diff --git a/Sources/Overload/OvCore/include/OvCore/Assets/AssetRef.h b/Sources/Overload/OvCore/include/OvCore/Assets/AssetRef.h index 888998941..34f49c00a 100644 --- a/Sources/Overload/OvCore/include/OvCore/Assets/AssetRef.h +++ b/Sources/Overload/OvCore/include/OvCore/Assets/AssetRef.h @@ -1,7 +1,9 @@ #pragma once #include +#include #include +#include namespace OvCore::Assets { @@ -22,6 +24,23 @@ namespace OvCore::Assets template class AssetRef : public GenericAssetRef { + public: + AssetID id; + + /** + * Returns the underlying asset. + */ + std::optional Get() + { + auto& assetRegistry = OvCore::Global::ServiceLocator::Get(); + return assetRegistry.GetAsset(id); + return static_cast(m_assets[id]); + } + bool IsExpired() const + { + auto& assetRegistry = OvCore::Global::ServiceLocator::Get(); + return assetRegistry. + } }; } diff --git a/Sources/Overload/OvCore/include/OvCore/Assets/AssetRegistry.h b/Sources/Overload/OvCore/include/OvCore/Assets/AssetRegistry.h index e643ebc4c..df2340a5f 100644 --- a/Sources/Overload/OvCore/include/OvCore/Assets/AssetRegistry.h +++ b/Sources/Overload/OvCore/include/OvCore/Assets/AssetRegistry.h @@ -25,6 +25,23 @@ namespace OvCore::Assets */ GenericAssetRef RegisterAssetAtPath(const std::filesystem::path& p_path); + /** + * Returns a reference to the asset with the given ID. + * @param p_id + */ + Asset& GetAsset(uint32_t p_id) + { + auto it = m_assets.find(p_id); + if (it != m_assets.end()) + { + return it->second; + } + else + { + throw std::runtime_error("Asset not found"); + } + } + private: std::unordered_map m_assets; };