From 07adbf48e0781cd8c95983c1871a84b6160ee5bf Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 14 Nov 2024 13:57:13 +0100 Subject: implement asset + more WIP audio system --- src/crepe/api/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/crepe/api/CMakeLists.txt') diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 85696c4..93a1fac 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources(crepe PUBLIC - # AudioSource.cpp + AudioSource.cpp BehaviorScript.cpp Script.cpp GameObject.cpp @@ -23,7 +23,7 @@ target_sources(crepe PUBLIC ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES - # AudioSource.h + AudioSource.h BehaviorScript.h Config.h Script.h -- cgit v1.2.3 From 431b0bd7c6c502b42bb5be5488371d8c475e7024 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 14 Nov 2024 14:06:18 +0100 Subject: move some shit around --- src/crepe/Asset.cpp | 50 ------------------------------ src/crepe/Asset.h | 43 -------------------------- src/crepe/CMakeLists.txt | 2 -- src/crepe/api/Asset.cpp | 50 ++++++++++++++++++++++++++++++ src/crepe/api/Asset.h | 43 ++++++++++++++++++++++++++ src/crepe/api/AssetManager.cpp | 17 ---------- src/crepe/api/AssetManager.h | 65 --------------------------------------- src/crepe/api/AssetManager.hpp | 24 --------------- src/crepe/api/AudioSource.h | 5 ++- src/crepe/api/CMakeLists.txt | 8 +++-- src/crepe/api/ResourceManager.cpp | 17 ++++++++++ src/crepe/api/ResourceManager.h | 65 +++++++++++++++++++++++++++++++++++++++ src/crepe/api/ResourceManager.hpp | 24 +++++++++++++++ src/crepe/facade/Sound.cpp | 2 +- src/test/AssetTest.cpp | 2 +- 15 files changed, 208 insertions(+), 209 deletions(-) delete mode 100644 src/crepe/Asset.cpp delete mode 100644 src/crepe/Asset.h create mode 100644 src/crepe/api/Asset.cpp create mode 100644 src/crepe/api/Asset.h delete mode 100644 src/crepe/api/AssetManager.cpp delete mode 100644 src/crepe/api/AssetManager.h delete mode 100644 src/crepe/api/AssetManager.hpp create mode 100644 src/crepe/api/ResourceManager.cpp create mode 100644 src/crepe/api/ResourceManager.h create mode 100644 src/crepe/api/ResourceManager.hpp (limited to 'src/crepe/api/CMakeLists.txt') diff --git a/src/crepe/Asset.cpp b/src/crepe/Asset.cpp deleted file mode 100644 index 8692c6c..0000000 --- a/src/crepe/Asset.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include - -#include "Asset.h" -#include "api/Config.h" - -using namespace crepe; -using namespace std; - -Asset::Asset(const string & src) : src(find_asset(src)) { } -Asset::Asset(const char * src) : src(find_asset(src)) { } - -const string & Asset::get_path() const noexcept { return this->src; } - -string Asset::find_asset(const string & src) const { - auto & cfg = Config::get_instance(); - auto & root_pattern = cfg.asset.root_pattern; - - // if root_pattern is empty, find_asset must return all paths as-is - if (root_pattern.empty()) return src; - - // absolute paths do not need to be resolved, only canonicalized - filesystem::path path = src; - if (path.is_absolute()) - return filesystem::canonical(path); - - // find directory matching root_pattern - filesystem::path root = this->whereami(); - while (1) { - if (filesystem::exists(root / root_pattern)) - break; - if (!root.has_parent_path()) - throw runtime_error(format("Asset: Cannot find root pattern ({})", root_pattern)); - root = root.parent_path(); - } - - // join path to root (base directory) and canonicalize - return filesystem::canonical(root / path); -} - -string Asset::whereami() const noexcept { - string path; - size_t path_length = wai_getExecutablePath(NULL, 0, NULL); - path.resize(path_length + 1); // wai writes null byte - wai_getExecutablePath(path.data(), path_length, NULL); - path.resize(path_length); - return path; -} - diff --git a/src/crepe/Asset.h b/src/crepe/Asset.h deleted file mode 100644 index f6e6782..0000000 --- a/src/crepe/Asset.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include - -namespace crepe { - -/** - * \brief Asset location helper - * - * This class is used to locate game asset files, and should *always* be used - * instead of reading file paths directly. - */ -class Asset { -public: - /** - * \param src Unique identifier to asset - */ - Asset(const std::string & src); - /** - * \param src Unique identifier to asset - */ - Asset(const char * src); - -public: - /** - * \brief Get the path to this asset - * \return path to this asset - */ - const std::string & get_path() const noexcept; - -private: - //! path to asset - const std::string src; - -private: - std::string find_asset(const std::string & src) const; - /** - * \returns The path to the current executable - */ - std::string whereami() const noexcept; -}; - -} // namespace crepe diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index 52a781e..05f86d7 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -1,5 +1,4 @@ target_sources(crepe PUBLIC - Asset.cpp Particle.cpp ComponentManager.cpp Component.cpp @@ -8,7 +7,6 @@ target_sources(crepe PUBLIC ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES - Asset.h ComponentManager.h ComponentManager.hpp Component.h diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp new file mode 100644 index 0000000..8692c6c --- /dev/null +++ b/src/crepe/api/Asset.cpp @@ -0,0 +1,50 @@ +#include +#include +#include + +#include "Asset.h" +#include "api/Config.h" + +using namespace crepe; +using namespace std; + +Asset::Asset(const string & src) : src(find_asset(src)) { } +Asset::Asset(const char * src) : src(find_asset(src)) { } + +const string & Asset::get_path() const noexcept { return this->src; } + +string Asset::find_asset(const string & src) const { + auto & cfg = Config::get_instance(); + auto & root_pattern = cfg.asset.root_pattern; + + // if root_pattern is empty, find_asset must return all paths as-is + if (root_pattern.empty()) return src; + + // absolute paths do not need to be resolved, only canonicalized + filesystem::path path = src; + if (path.is_absolute()) + return filesystem::canonical(path); + + // find directory matching root_pattern + filesystem::path root = this->whereami(); + while (1) { + if (filesystem::exists(root / root_pattern)) + break; + if (!root.has_parent_path()) + throw runtime_error(format("Asset: Cannot find root pattern ({})", root_pattern)); + root = root.parent_path(); + } + + // join path to root (base directory) and canonicalize + return filesystem::canonical(root / path); +} + +string Asset::whereami() const noexcept { + string path; + size_t path_length = wai_getExecutablePath(NULL, 0, NULL); + path.resize(path_length + 1); // wai writes null byte + wai_getExecutablePath(path.data(), path_length, NULL); + path.resize(path_length); + return path; +} + diff --git a/src/crepe/api/Asset.h b/src/crepe/api/Asset.h new file mode 100644 index 0000000..f6e6782 --- /dev/null +++ b/src/crepe/api/Asset.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +namespace crepe { + +/** + * \brief Asset location helper + * + * This class is used to locate game asset files, and should *always* be used + * instead of reading file paths directly. + */ +class Asset { +public: + /** + * \param src Unique identifier to asset + */ + Asset(const std::string & src); + /** + * \param src Unique identifier to asset + */ + Asset(const char * src); + +public: + /** + * \brief Get the path to this asset + * \return path to this asset + */ + const std::string & get_path() const noexcept; + +private: + //! path to asset + const std::string src; + +private: + std::string find_asset(const std::string & src) const; + /** + * \returns The path to the current executable + */ + std::string whereami() const noexcept; +}; + +} // namespace crepe diff --git a/src/crepe/api/AssetManager.cpp b/src/crepe/api/AssetManager.cpp deleted file mode 100644 index 3925758..0000000 --- a/src/crepe/api/AssetManager.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "util/Log.h" - -#include "AssetManager.h" - -using namespace crepe; - -AssetManager & AssetManager::get_instance() { - static AssetManager instance; - return instance; -} - -AssetManager::~AssetManager() { - dbg_trace(); - this->asset_cache.clear(); -} - -AssetManager::AssetManager() { dbg_trace(); } diff --git a/src/crepe/api/AssetManager.h b/src/crepe/api/AssetManager.h deleted file mode 100644 index 86a9902..0000000 --- a/src/crepe/api/AssetManager.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace crepe { - -/** - * \brief The AssetManager is responsible for storing and managing assets over - * multiple scenes. - * - * The AssetManager ensures that assets are loaded once and can be accessed - * across different scenes. It caches assets to avoid reloading them every time - * a scene is loaded. Assets are retained in memory until the AssetManager is - * destroyed, at which point the cached assets are cleared. - */ -class AssetManager { - -private: - //! A cache that holds all the assets, accessible by their file path, over multiple scenes. - std::unordered_map asset_cache; - -private: - AssetManager(); - virtual ~AssetManager(); - -public: - AssetManager(const AssetManager &) = delete; - AssetManager(AssetManager &&) = delete; - AssetManager & operator=(const AssetManager &) = delete; - AssetManager & operator=(AssetManager &&) = delete; - - /** - * \brief Retrieves the singleton instance of the AssetManager. - * - * \return A reference to the single instance of the AssetManager. - */ - static AssetManager & get_instance(); - -public: - /** - * \brief Caches an asset by loading it from the given file path. - * - * \param file_path The path to the asset file to load. - * \param reload If true, the asset will be reloaded from the file, even if - * it is already cached. - * \tparam T The type of asset to cache (e.g., texture, sound, etc.). - * - * \return A shared pointer to the cached asset. - * - * This template function caches the asset at the given file path. If the - * asset is already cached and `reload` is false, the existing cached version - * will be returned. Otherwise, the asset will be reloaded and added to the - * cache. - */ - template - std::shared_ptr cache(const std::string & file_path, - bool reload = false); -}; - -} // namespace crepe - -#include "AssetManager.hpp" diff --git a/src/crepe/api/AssetManager.hpp b/src/crepe/api/AssetManager.hpp deleted file mode 100644 index 977b4e1..0000000 --- a/src/crepe/api/AssetManager.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "AssetManager.h" - -namespace crepe { - -template -std::shared_ptr AssetManager::cache(const std::string & file_path, - bool reload) { - auto it = asset_cache.find(file_path); - - if (!reload && it != asset_cache.end()) { - return std::any_cast>(it->second); - } - - std::shared_ptr new_asset - = std::make_shared(file_path.c_str()); - - asset_cache[file_path] = new_asset; - - return new_asset; -} - -} // namespace crepe diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 0748267..8a78927 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -1,11 +1,10 @@ #pragma once -#include - -#include "../Asset.h" #include "../Component.h" #include "../types.h" +#include "Asset.h" + namespace crepe { //! Audio source component diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 93a1fac..70f1527 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -8,7 +8,7 @@ target_sources(crepe PUBLIC Transform.cpp Color.cpp Texture.cpp - AssetManager.cpp + ResourceManager.cpp Sprite.cpp SaveManager.cpp Config.cpp @@ -20,6 +20,7 @@ target_sources(crepe PUBLIC Animator.cpp LoopManager.cpp LoopTimer.cpp + Asset.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -35,8 +36,8 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Vector2.h Color.h Texture.h - AssetManager.h - AssetManager.hpp + ResourceManager.h + ResourceManager.hpp SaveManager.h Scene.h Metadata.h @@ -46,4 +47,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Animator.h LoopManager.h LoopTimer.h + Asset.h ) diff --git a/src/crepe/api/ResourceManager.cpp b/src/crepe/api/ResourceManager.cpp new file mode 100644 index 0000000..470e511 --- /dev/null +++ b/src/crepe/api/ResourceManager.cpp @@ -0,0 +1,17 @@ +#include "util/Log.h" + +#include "ResourceManager.h" + +using namespace crepe; + +ResourceManager & ResourceManager::get_instance() { + static ResourceManager instance; + return instance; +} + +ResourceManager::~ResourceManager() { + dbg_trace(); + this->asset_cache.clear(); +} + +ResourceManager::ResourceManager() { dbg_trace(); } diff --git a/src/crepe/api/ResourceManager.h b/src/crepe/api/ResourceManager.h new file mode 100644 index 0000000..7a45493 --- /dev/null +++ b/src/crepe/api/ResourceManager.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include +#include + +namespace crepe { + +/** + * \brief The ResourceManager is responsible for storing and managing assets over + * multiple scenes. + * + * The ResourceManager ensures that assets are loaded once and can be accessed + * across different scenes. It caches assets to avoid reloading them every time + * a scene is loaded. Assets are retained in memory until the ResourceManager is + * destroyed, at which point the cached assets are cleared. + */ +class ResourceManager { + +private: + //! A cache that holds all the assets, accessible by their file path, over multiple scenes. + std::unordered_map asset_cache; + +private: + ResourceManager(); + virtual ~ResourceManager(); + +public: + ResourceManager(const ResourceManager &) = delete; + ResourceManager(ResourceManager &&) = delete; + ResourceManager & operator=(const ResourceManager &) = delete; + ResourceManager & operator=(ResourceManager &&) = delete; + + /** + * \brief Retrieves the singleton instance of the ResourceManager. + * + * \return A reference to the single instance of the ResourceManager. + */ + static ResourceManager & get_instance(); + +public: + /** + * \brief Caches an asset by loading it from the given file path. + * + * \param file_path The path to the asset file to load. + * \param reload If true, the asset will be reloaded from the file, even if + * it is already cached. + * \tparam T The type of asset to cache (e.g., texture, sound, etc.). + * + * \return A shared pointer to the cached asset. + * + * This template function caches the asset at the given file path. If the + * asset is already cached and `reload` is false, the existing cached version + * will be returned. Otherwise, the asset will be reloaded and added to the + * cache. + */ + template + std::shared_ptr cache(const std::string & file_path, + bool reload = false); +}; + +} // namespace crepe + +#include "ResourceManager.hpp" diff --git a/src/crepe/api/ResourceManager.hpp b/src/crepe/api/ResourceManager.hpp new file mode 100644 index 0000000..9cd4bcb --- /dev/null +++ b/src/crepe/api/ResourceManager.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "ResourceManager.h" + +namespace crepe { + +template +std::shared_ptr ResourceManager::cache(const std::string & file_path, + bool reload) { + auto it = asset_cache.find(file_path); + + if (!reload && it != asset_cache.end()) { + return std::any_cast>(it->second); + } + + std::shared_ptr new_asset + = std::make_shared(file_path.c_str()); + + asset_cache[file_path] = new_asset; + + return new_asset; +} + +} // namespace crepe diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index b7bfeab..726f11f 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -1,6 +1,6 @@ #include -#include "../Asset.h" +#include "../api/Asset.h" #include "../util/Log.h" #include "Sound.h" diff --git a/src/test/AssetTest.cpp b/src/test/AssetTest.cpp index c3ff158..324a3f1 100644 --- a/src/test/AssetTest.cpp +++ b/src/test/AssetTest.cpp @@ -1,7 +1,7 @@ #include "api/Config.h" #include -#include +#include using namespace std; using namespace crepe; -- cgit v1.2.3 From add8724446fdeae1aaec9b07544cf7a5475a9bfe Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 14 Nov 2024 19:57:45 +0100 Subject: ResourceManager working + tested --- src/crepe/Resource.h | 3 +-- src/crepe/api/Asset.cpp | 4 ++++ src/crepe/api/Asset.h | 7 +++++++ src/crepe/api/CMakeLists.txt | 1 - src/crepe/api/ResourceManager.cpp | 27 +++++++++++++++++++++------ src/crepe/api/ResourceManager.h | 4 ---- src/crepe/api/ResourceManager.hpp | 27 --------------------------- src/crepe/facade/Sound.cpp | 5 +++++ src/crepe/facade/Sound.h | 2 +- src/crepe/util/OptionalRef.hpp | 2 +- src/test/OptionalRefTest.cpp | 24 +++++++++++++++++++++++- src/test/ResourceManagerTest.cpp | 36 +++++++++++++++++++++++++++++++++--- src/test/main.cpp | 15 +++++++++++++-- 13 files changed, 109 insertions(+), 48 deletions(-) delete mode 100644 src/crepe/api/ResourceManager.hpp (limited to 'src/crepe/api/CMakeLists.txt') diff --git a/src/crepe/Resource.h b/src/crepe/Resource.h index 95b4d06..a0c8859 100644 --- a/src/crepe/Resource.h +++ b/src/crepe/Resource.h @@ -1,7 +1,5 @@ #pragma once -#include - namespace crepe { class ResourceManager; @@ -14,6 +12,7 @@ class Asset; class Resource { public: Resource(const Asset & src); + virtual ~Resource() = default; private: /** diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp index 1887814..5271cf7 100644 --- a/src/crepe/api/Asset.cpp +++ b/src/crepe/api/Asset.cpp @@ -48,6 +48,10 @@ string Asset::whereami() const noexcept { return path; } +bool Asset::operator==(const Asset & other) const noexcept { + return this->src == other.src; +} + size_t std::hash::operator()(const Asset & asset) const noexcept { return std::hash{}(asset.get_path()); }; diff --git a/src/crepe/api/Asset.h b/src/crepe/api/Asset.h index 0f6b0b3..05dccba 100644 --- a/src/crepe/api/Asset.h +++ b/src/crepe/api/Asset.h @@ -29,6 +29,13 @@ public: */ const std::string & get_path() const noexcept; + /** + * \brief Comparison operator + * \param other Possibly different instance of \c Asset to test equality against + * \return True if \c this and \c other are equal + */ + bool operator == (const Asset & other) const noexcept; + private: //! path to asset const std::string src; diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 70f1527..b452f37 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -37,7 +37,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Color.h Texture.h ResourceManager.h - ResourceManager.hpp SaveManager.h Scene.h Metadata.h diff --git a/src/crepe/api/ResourceManager.cpp b/src/crepe/api/ResourceManager.cpp index 6eb4afd..7877ed9 100644 --- a/src/crepe/api/ResourceManager.cpp +++ b/src/crepe/api/ResourceManager.cpp @@ -1,11 +1,11 @@ +#include + #include "util/Log.h" #include "ResourceManager.h" -// default resource cache functions -#include "../facade/Sound.h" - using namespace crepe; +using namespace std; ResourceManager & ResourceManager::get_instance() { static ResourceManager instance; @@ -19,8 +19,23 @@ void ResourceManager::clear() { this->resources.clear(); } -template <> -Sound & ResourceManager::cache(const Asset & asset) { - return this->cache(asset); +template +T & ResourceManager::cache(const Asset & asset) { + dbg_trace(); + static_assert(is_base_of::value, "cache must recieve a derivative class of Resource"); + + if (!this->resources.contains(asset)) + this->resources[asset] = make_unique(asset); + + Resource * resource = this->resources.at(asset).get(); + T * concrete_resource = dynamic_cast(resource); + + if (concrete_resource == nullptr) + throw runtime_error(format("ResourceManager: mismatch between requested type and actual type of resource ({})", asset.get_path())); + + return *concrete_resource; } +#include "../facade/Sound.h" +template Sound & ResourceManager::cache(const Asset &); + diff --git a/src/crepe/api/ResourceManager.h b/src/crepe/api/ResourceManager.h index 468af16..efdd5c5 100644 --- a/src/crepe/api/ResourceManager.h +++ b/src/crepe/api/ResourceManager.h @@ -65,9 +65,5 @@ public: void clear(); }; -template <> -Sound & ResourceManager::cache(const Asset & asset); - } // namespace crepe -#include "ResourceManager.hpp" diff --git a/src/crepe/api/ResourceManager.hpp b/src/crepe/api/ResourceManager.hpp deleted file mode 100644 index 62cac20..0000000 --- a/src/crepe/api/ResourceManager.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include - -#include "ResourceManager.h" - -namespace crepe { - -template -T & ResourceManager::cache(const Asset & asset) { - using namespace std; - static_assert(is_base_of::value, "cache must recieve a derivative class of Resource"); - - if (!this->resources.contains(asset)) - this->resources[asset] = make_unique(asset); - - Resource * resource = this->resources.at(asset).get(); - T * concrete_resource = dynamic_cast(resource); - - if (concrete_resource == nullptr) - throw runtime_error(format("ResourceManager: mismatch between requested type and actual type of resource ({})", asset.get_path())); - - return *concrete_resource; -} - -} // namespace crepe diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index 4eefcda..b589759 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -11,6 +11,7 @@ Sound::Sound(const Asset & src) : Resource(src) { this->sample.load(src.get_path().c_str()); dbg_trace(); } +Sound::~Sound() { dbg_trace(); } void Sound::play() { SoundContext & ctx = this->context.get(); @@ -52,3 +53,7 @@ void Sound::set_looping(bool looping) { ctx.engine.setLooping(this->handle, this->looping); } +void Sound::set_context(SoundContext & ctx) { + this->context = ctx; +} + diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h index 6f8462a..94b1996 100644 --- a/src/crepe/facade/Sound.h +++ b/src/crepe/facade/Sound.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -20,6 +19,7 @@ class SoundContext; class Sound : public Resource { public: Sound(const Asset & src); + ~Sound(); // dbg_trace /** * \brief Pause this sample * diff --git a/src/crepe/util/OptionalRef.hpp b/src/crepe/util/OptionalRef.hpp index e603a25..7b201b0 100644 --- a/src/crepe/util/OptionalRef.hpp +++ b/src/crepe/util/OptionalRef.hpp @@ -60,7 +60,7 @@ OptionalRef & OptionalRef::operator=(T & ref) { template OptionalRef::operator bool() const noexcept { - return this->ref == nullptr; + return this->ref != nullptr; } } diff --git a/src/test/OptionalRefTest.cpp b/src/test/OptionalRefTest.cpp index 65bd816..219ccca 100644 --- a/src/test/OptionalRefTest.cpp +++ b/src/test/OptionalRefTest.cpp @@ -9,8 +9,30 @@ using namespace testing; TEST(OptionalRefTest, Explicit) { string value = "foo"; OptionalRef ref; + EXPECT_FALSE(ref); + ASSERT_THROW(ref.get(), runtime_error); + + ref.set(value); + EXPECT_TRUE(ref); + ASSERT_NO_THROW(ref.get()); + + ref.clear(); + EXPECT_FALSE(ref); + ASSERT_THROW(ref.get(), runtime_error); +} + +TEST(OptionalRefTest, Implicit) { + string value = "foo"; + OptionalRef ref = value; + EXPECT_TRUE(ref); + ASSERT_NO_THROW(ref.get()); - EXPECT_FALSE(bool(ref)); + ref.clear(); + EXPECT_FALSE(ref); ASSERT_THROW(ref.get(), runtime_error); + + ref = value; + EXPECT_TRUE(ref); + ASSERT_NO_THROW(ref.get()); } diff --git a/src/test/ResourceManagerTest.cpp b/src/test/ResourceManagerTest.cpp index 42b6b5d..5d1ae7a 100644 --- a/src/test/ResourceManagerTest.cpp +++ b/src/test/ResourceManagerTest.cpp @@ -1,5 +1,7 @@ #include +#include +#include #include using namespace std; @@ -8,14 +10,42 @@ using namespace testing; class ResourceManagerTest : public Test { public: - ResourceManager & manager = ResourceManager::get_instance(); + ResourceManager & resman = ResourceManager::get_instance(); + Config & cfg = Config::get_instance(); void SetUp() override { - this->manager.clear(); + cfg.log.level = Log::Level::TRACE; + resman.clear(); } }; TEST_F(ResourceManagerTest, Main) { - Sound & sound = this->manager.cache("mwe/audio/sfx1.wav"); + Asset path1 = "mwe/audio/sfx1.wav"; + Asset path2 = "mwe/audio/sfx1.wav"; + ASSERT_EQ(path1, path2); + + Sound * ptr1 = nullptr; + Sound * ptr2 = nullptr; + { + Log::logf(Log::Level::DEBUG, "Get first sound (constructor call)"); + Sound & sound = resman.cache(path1); + ptr1 = &sound; + } + { + Log::logf(Log::Level::DEBUG, "Get same sound (NO constructor call)"); + Sound & sound = resman.cache(path2); + ptr2 = &sound; + } + EXPECT_EQ(ptr1, ptr2); + + Log::logf(Log::Level::DEBUG, "Clear cache (destructor call)"); + resman.clear(); + + Log::logf(Log::Level::DEBUG, "Get first sound again (constructor call)"); + Sound & sound = resman.cache(path1); + + // NOTE: there is no way (that I know of) to ensure the above statement + // allocates the new Sound instance in a different location than the first, + // so this test was verified using the above print statements. } diff --git a/src/test/main.cpp b/src/test/main.cpp index 241015d..19a8d6e 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -5,11 +5,22 @@ using namespace crepe; using namespace testing; +class GlobalConfigReset : public EmptyTestEventListener { +public: + Config & cfg = Config::get_instance(); + + // This function is called before each test + void OnTestStart(const TestInfo &) override { + cfg.log.level = Log::Level::WARNING; + } +}; + int main(int argc, char ** argv) { InitGoogleTest(&argc, argv); - auto & cfg = Config::get_instance(); - cfg.log.level = Log::Level::ERROR; + UnitTest & ut = *UnitTest::GetInstance(); + ut.listeners().Append(new GlobalConfigReset); return RUN_ALL_TESTS(); } + -- cgit v1.2.3 From a685e5f743786cc6499e7ce8973bb78a83d101f7 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Tue, 26 Nov 2024 20:03:41 +0100 Subject: big WIP --- src/crepe/CMakeLists.txt | 2 + src/crepe/ResourceManager.cpp | 36 ++++++++++++ src/crepe/ResourceManager.h | 76 +++++++++++++++++++++++++ src/crepe/api/AudioSource.h | 1 + src/crepe/api/CMakeLists.txt | 2 - src/crepe/api/Config.h | 27 +-------- src/crepe/api/ResourceManager.cpp | 41 -------------- src/crepe/api/ResourceManager.h | 69 ----------------------- src/crepe/system/AudioSystem.cpp | 2 +- src/crepe/system/AudioSystem.h | 4 +- src/crepe/util/Log.cpp | 2 +- src/crepe/util/Log.h | 16 ++++++ src/test/EventTest.cpp | 1 - src/test/ResourceManagerTest.cpp | 115 +++++++++++++++++++++++++++----------- src/test/main.cpp | 12 ++-- 15 files changed, 224 insertions(+), 182 deletions(-) create mode 100644 src/crepe/ResourceManager.cpp create mode 100644 src/crepe/ResourceManager.h delete mode 100644 src/crepe/api/ResourceManager.cpp delete mode 100644 src/crepe/api/ResourceManager.h (limited to 'src/crepe/api/CMakeLists.txt') diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index df15b8f..0313dfa 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -3,6 +3,7 @@ target_sources(crepe PUBLIC ComponentManager.cpp Component.cpp Collider.cpp + ResourceManager.cpp Resource.cpp ) @@ -13,6 +14,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Collider.h ValueBroker.h ValueBroker.hpp + ResourceManager.h Resource.h ) diff --git a/src/crepe/ResourceManager.cpp b/src/crepe/ResourceManager.cpp new file mode 100644 index 0000000..111b9e0 --- /dev/null +++ b/src/crepe/ResourceManager.cpp @@ -0,0 +1,36 @@ +#include + +#include "util/Log.h" + +#include "ResourceManager.h" + +using namespace crepe; +using namespace std; + +ResourceManager::~ResourceManager() { dbg_trace(); } +ResourceManager::ResourceManager() { dbg_trace(); } + +void ResourceManager::clear() { + this->resources.clear(); +} + +// template +// T & ResourceManager::cache(const Asset & asset) { +// dbg_trace(); +// static_assert(is_base_of::value, "cache must recieve a derivative class of Resource"); +// +// if (!this->resources.contains(asset)) +// this->resources[asset] = make_unique(asset); +// +// Resource * resource = this->resources.at(asset).get(); +// T * concrete_resource = dynamic_cast(resource); +// +// if (concrete_resource == nullptr) +// throw runtime_error(format("ResourceManager: mismatch between requested type and actual type of resource ({})", asset.get_path())); +// +// return *concrete_resource; +// } +// +// #include "facade/Sound.h" +// template Sound & ResourceManager::cache(const Asset &); + diff --git a/src/crepe/ResourceManager.h b/src/crepe/ResourceManager.h new file mode 100644 index 0000000..26a86a8 --- /dev/null +++ b/src/crepe/ResourceManager.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include + +#include "api/Asset.h" + +#include "Component.h" +#include "Resource.h" + +namespace crepe { + + +/** + * \brief The ResourceManager is responsible for storing and managing assets over + * multiple scenes. + * + * The ResourceManager ensures that assets are loaded once and can be accessed + * across different scenes. It caches assets to avoid reloading them every time + * a scene is loaded. Assets are retained in memory until the ResourceManager is + * destroyed, at which point the cached assets are cleared. + */ +class ResourceManager { +public: + ResourceManager(); // dbg_trace + virtual ~ResourceManager(); // dbg_trace + +private: + template + Resource & get_internal(const Component & component, const Asset & asset); + + template + const Asset & get_source(const Component & component) const; + + //! A cache that holds all the assets, accessible by their file path, over multiple scenes. + std::unordered_map> resources; + +public: + /** + * \brief Caches an asset by loading it from the given file path. + * + * \param file_path The path to the asset file to load. + * \param reload If true, the asset will be reloaded from the file, even if + * it is already cached. + * \tparam T The type of asset to cache (e.g., texture, sound, etc.). + * + * \return A reference to the resource + * + * This template function caches the asset at the given file path. If the + * asset is already cached, the existing instance will be returned. + * Otherwise, the concrete resource will be instantiated and added to the + * cache. + */ + template + void cache(const Asset & asset, bool persistent = false); + + template + void cache(const Component & component, bool persistent = false); + + // void resman.cache(Asset, Lifetime); + // void resman.cache(Component, Asset, Lifetime); + + template + Resource & get(const Component & component); + + //! Clear the resource cache + void clear(); +}; + +class Sound; +class AudioSource; +template <> +Sound & ResourceManager::get(const AudioSource & component); + +} // namespace crepe + diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 1264790..0950129 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -3,6 +3,7 @@ #include "../Component.h" #include "../types.h" +#include "GameObject.h" #include "Asset.h" namespace crepe { diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index a2e21fa..ad82924 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -7,7 +7,6 @@ target_sources(crepe PUBLIC Transform.cpp Color.cpp Texture.cpp - ResourceManager.cpp Sprite.cpp SaveManager.cpp Config.cpp @@ -39,7 +38,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Vector2.hpp Color.h Texture.h - ResourceManager.h SaveManager.h Scene.h Metadata.h diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index 0c9d116..5bd6913 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -11,35 +11,12 @@ namespace crepe { * modified *before* execution is handed over from the game programmer to the engine (i.e. the * main loop is started). */ -class Config final { -public: +struct Config final { //! Retrieve handle to global Config instance static Config & get_instance(); -private: - Config() = default; - ~Config() = default; - Config(const Config &) = default; - Config(Config &&) = default; - Config & operator=(const Config &) = default; - Config & operator=(Config &&) = default; - -public: //! Logging-related settings - struct { - /** - * \brief Log level - * - * Only messages with equal or higher priority than this value will be logged. - */ - Log::Level level = Log::Level::INFO; - /** - * \brief Colored log output - * - * Enables log coloring using ANSI escape codes. - */ - bool color = true; - } log; + Log::Config log; //! Save manager struct { diff --git a/src/crepe/api/ResourceManager.cpp b/src/crepe/api/ResourceManager.cpp deleted file mode 100644 index 7877ed9..0000000 --- a/src/crepe/api/ResourceManager.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include - -#include "util/Log.h" - -#include "ResourceManager.h" - -using namespace crepe; -using namespace std; - -ResourceManager & ResourceManager::get_instance() { - static ResourceManager instance; - return instance; -} - -ResourceManager::~ResourceManager() { dbg_trace(); } -ResourceManager::ResourceManager() { dbg_trace(); } - -void ResourceManager::clear() { - this->resources.clear(); -} - -template -T & ResourceManager::cache(const Asset & asset) { - dbg_trace(); - static_assert(is_base_of::value, "cache must recieve a derivative class of Resource"); - - if (!this->resources.contains(asset)) - this->resources[asset] = make_unique(asset); - - Resource * resource = this->resources.at(asset).get(); - T * concrete_resource = dynamic_cast(resource); - - if (concrete_resource == nullptr) - throw runtime_error(format("ResourceManager: mismatch between requested type and actual type of resource ({})", asset.get_path())); - - return *concrete_resource; -} - -#include "../facade/Sound.h" -template Sound & ResourceManager::cache(const Asset &); - diff --git a/src/crepe/api/ResourceManager.h b/src/crepe/api/ResourceManager.h deleted file mode 100644 index efdd5c5..0000000 --- a/src/crepe/api/ResourceManager.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include -#include - -#include "Asset.h" -#include "Resource.h" - -namespace crepe { - -class Sound; - -/** - * \brief The ResourceManager is responsible for storing and managing assets over - * multiple scenes. - * - * The ResourceManager ensures that assets are loaded once and can be accessed - * across different scenes. It caches assets to avoid reloading them every time - * a scene is loaded. Assets are retained in memory until the ResourceManager is - * destroyed, at which point the cached assets are cleared. - */ -class ResourceManager { - -private: - //! A cache that holds all the assets, accessible by their file path, over multiple scenes. - std::unordered_map> resources; - -private: - ResourceManager(); // dbg_trace - virtual ~ResourceManager(); // dbg_trace - - ResourceManager(const ResourceManager &) = delete; - ResourceManager(ResourceManager &&) = delete; - ResourceManager & operator=(const ResourceManager &) = delete; - ResourceManager & operator=(ResourceManager &&) = delete; - -public: - /** - * \brief Retrieves the singleton instance of the ResourceManager. - * - * \return A reference to the single instance of the ResourceManager. - */ - static ResourceManager & get_instance(); - -public: - /** - * \brief Caches an asset by loading it from the given file path. - * - * \param file_path The path to the asset file to load. - * \param reload If true, the asset will be reloaded from the file, even if - * it is already cached. - * \tparam T The type of asset to cache (e.g., texture, sound, etc.). - * - * \return A reference to the resource - * - * This template function caches the asset at the given file path. If the - * asset is already cached, the existing instance will be returned. - * Otherwise, the concrete resource will be instantiated and added to the - * cache. - */ - template - T & cache(const Asset & asset); - - //! Clear the resource cache - void clear(); -}; - -} // namespace crepe - diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index b7ac1f2..6c30b85 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -47,7 +47,7 @@ void AudioSystem::update() { * using the same underlying instance of Sound (esp. w/ * play/pause/retrigger behavior). */ - Sound & sound = this->resman.cache(component.source); + // Sound & sound = this->resource_manager.get(component); // TODO: lots of state diffing } diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index d0b4f9a..d3d5aeb 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -1,7 +1,7 @@ #pragma once #include "../facade/SoundContext.h" -#include "../api/ResourceManager.h" +#include "../ResourceManager.h" #include "System.h" @@ -14,7 +14,7 @@ public: private: SoundContext context {}; - ResourceManager & resman = ResourceManager::get_instance(); + ResourceManager resource_manager {}; }; } // namespace crepe diff --git a/src/crepe/util/Log.cpp b/src/crepe/util/Log.cpp index 84d80a8..bc86c7e 100644 --- a/src/crepe/util/Log.cpp +++ b/src/crepe/util/Log.cpp @@ -25,7 +25,7 @@ string Log::prefix(const Level & level) { } void Log::log(const Level & level, const string & msg) { - auto & cfg = Config::get_instance(); + auto & cfg = crepe::Config::get_instance(); if (level < cfg.log.level) return; string out = Log::prefix(level) + msg; diff --git a/src/crepe/util/Log.h b/src/crepe/util/Log.h index fc0bb3a..914145a 100644 --- a/src/crepe/util/Log.h +++ b/src/crepe/util/Log.h @@ -77,6 +77,22 @@ private: * \return Colored message severity prefix string */ static std::string prefix(const Level & level); + +public: + struct Config { + /** + * \brief Log level + * + * Only messages with equal or higher priority than this value will be logged. + */ + Level level = INFO; + /** + * \brief Colored log output + * + * Enables log coloring using ANSI escape codes. + */ + bool color = true; + }; }; } // namespace crepe diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index b0e6c9c..a21a851 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -37,7 +37,6 @@ public: TEST_F(EventManagerTest, EventSubscription) { EventHandler key_handler = [](const KeyPressEvent & e) { - std::cout << "Key Event Triggered" << std::endl; return true; }; diff --git a/src/test/ResourceManagerTest.cpp b/src/test/ResourceManagerTest.cpp index 3fc9ebd..f57c419 100644 --- a/src/test/ResourceManagerTest.cpp +++ b/src/test/ResourceManagerTest.cpp @@ -1,52 +1,101 @@ #include +#define private public +#define protected public + #include -#include -#include +#include +#include +#include +#include using namespace std; using namespace crepe; using namespace testing; +class TestComponent : public Component { +public: + TestComponent(game_object_id_t id, const Asset & asset) + : Component(id), + source(asset) {} + const Asset source; +}; + +class TestResource : public Resource { +public: + static unsigned instances; + +public: + const unsigned instance; + TestResource(const Asset & src) + : Resource(src), + instance(this->instances++) { } +}; +unsigned TestResource::instances = 0; + +template <> +TestResource & ResourceManager::get(const TestComponent & component) { + return this->get_internal(component, component.source); +} + class ResourceManagerTest : public Test { public: - ResourceManager & resman = ResourceManager::get_instance(); - Config & cfg = Config::get_instance(); + ResourceManager resource_manager{}; + + static constexpr const char * ASSET_LOCATION = "asset/texture/img.png"; + + TestComponent a{0, ASSET_LOCATION}; + TestComponent b{1, ASSET_LOCATION}; +private: void SetUp() override { - resman.clear(); + TestResource::instances = 0; } }; -TEST_F(ResourceManagerTest, Main) { - // NOTE: there is no way (that I know of) to ensure the last cache call - // allocates the new Sound instance in a different location than the first, - // so this test should be verified manually using these print statements and - // debug trace messages. - cfg.log.level = Log::Level::TRACE; - - Asset path1 = "mwe/audio/sfx1.wav"; - Asset path2 = "mwe/audio/sfx1.wav"; - ASSERT_EQ(path1, path2); - - Sound * ptr1 = nullptr; - Sound * ptr2 = nullptr; - { - Log::logf(Log::Level::DEBUG, "Get first sound (constructor call)"); - Sound & sound = resman.cache(path1); - ptr1 = &sound; - } - { - Log::logf(Log::Level::DEBUG, "Get same sound (NO constructor call)"); - Sound & sound = resman.cache(path2); - ptr2 = &sound; - } - EXPECT_EQ(ptr1, ptr2); +TEST_F(ResourceManagerTest, Uncached) { + TestResource & res_1 = resource_manager.get(a); // 1 + TestResource & res_2 = resource_manager.get(a); // 1 + TestResource & res_3 = resource_manager.get(b); // 2 + TestResource & res_4 = resource_manager.get(b); // 2 + + ASSERT_EQ(res_1, res_2); + ASSERT_EQ(res_3, res_4); + EXPECT_NE(res_1, res_3); + + EXPECT_EQ(TestResource::instances, 2); +} + +// TODO: per GameObject / Component +TEST_F(ResourceManagerTest, PerComponent) { + resource_manager.cache(a); + resource_manager.cache(b); + + TestResource & res_1 = resource_manager.get(a); // 1 + TestResource & res_2 = resource_manager.get(a); // 1 + TestResource & res_3 = resource_manager.get(b); // 2 + TestResource & res_4 = resource_manager.get(b); // 2 + + ASSERT_EQ(res_1, res_2); + ASSERT_EQ(res_3, res_4); + EXPECT_NE(res_1, res_3); + + EXPECT_EQ(TestResource::instances, 2); +} + +TEST_F(ResourceManagerTest, PerAsset) { + resource_manager.cache(ASSET_LOCATION); + EXPECT_EQ(TestResource::instances, 1); + + TestResource & res_1 = resource_manager.get(a); // 1 + TestResource & res_2 = resource_manager.get(a); // 1 + TestResource & res_3 = resource_manager.get(b); // 1 + TestResource & res_4 = resource_manager.get(b); // 1 - Log::logf(Log::Level::DEBUG, "Clear cache (destructor call)"); - resman.clear(); + EXPECT_EQ(res_1, res_2); + EXPECT_EQ(res_2, res_3); + EXPECT_EQ(res_3, res_4); - Log::logf(Log::Level::DEBUG, "Get first sound again (constructor call)"); - Sound & sound = resman.cache(path1); + EXPECT_EQ(TestResource::instances, 1); } diff --git a/src/test/main.cpp b/src/test/main.cpp index e03a989..54f74fd 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -1,8 +1,4 @@ #include - -#define protected public -#define private public - #include using namespace crepe; @@ -11,12 +7,14 @@ using namespace testing; class GlobalConfigReset : public EmptyTestEventListener { public: Config & cfg = Config::get_instance(); - Config cfg_default = Config(); // This function is called before each test void OnTestStart(const TestInfo &) override { - cfg = cfg_default; - cfg.log.level = Log::Level::WARNING; + cfg = { + .log = { + .level = Log::Level::WARNING, + }, + }; } }; -- cgit v1.2.3