diff options
Diffstat (limited to 'src/crepe/api')
-rw-r--r-- | src/crepe/api/Asset.cpp | 58 | ||||
-rw-r--r-- | src/crepe/api/Asset.h | 60 | ||||
-rw-r--r-- | src/crepe/api/AssetManager.cpp | 17 | ||||
-rw-r--r-- | src/crepe/api/AudioSource.cpp | 20 | ||||
-rw-r--r-- | src/crepe/api/AudioSource.h | 60 | ||||
-rw-r--r-- | src/crepe/api/CMakeLists.txt | 11 | ||||
-rw-r--r-- | src/crepe/api/Config.h | 14 | ||||
-rw-r--r-- | src/crepe/api/ResourceManager.cpp | 41 | ||||
-rw-r--r-- | src/crepe/api/ResourceManager.h | 69 | ||||
-rw-r--r-- | src/crepe/api/Texture.cpp | 2 |
10 files changed, 329 insertions, 23 deletions
diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp new file mode 100644 index 0000000..5271cf7 --- /dev/null +++ b/src/crepe/api/Asset.cpp @@ -0,0 +1,58 @@ +#include <filesystem> +#include <stdexcept> +#include <whereami.h> + +#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; +} + +bool Asset::operator==(const Asset & other) const noexcept { + return this->src == other.src; +} + +size_t std::hash<const Asset>::operator()(const Asset & asset) const noexcept { + return std::hash<string>{}(asset.get_path()); +}; + diff --git a/src/crepe/api/Asset.h b/src/crepe/api/Asset.h new file mode 100644 index 0000000..05dccba --- /dev/null +++ b/src/crepe/api/Asset.h @@ -0,0 +1,60 @@ +#pragma once + +#include <string> +#include <unordered_map> + +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; + + /** + * \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; + +private: + std::string find_asset(const std::string & src) const; + /** + * \returns The path to the current executable + */ + std::string whereami() const noexcept; +}; + +} // namespace crepe + +namespace std { + +template<> struct hash<const crepe::Asset> { + size_t operator()(const crepe::Asset & asset) const noexcept; +}; + +} + 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/AudioSource.cpp b/src/crepe/api/AudioSource.cpp new file mode 100644 index 0000000..4baac9a --- /dev/null +++ b/src/crepe/api/AudioSource.cpp @@ -0,0 +1,20 @@ +#include "AudioSource.h" + +using namespace crepe; +using namespace std; + +AudioSource::AudioSource(game_object_id_t id, const Asset & src) : + Component(id), + source(src) +{ } + +void AudioSource::play(bool looping) { + this->loop = looping; + this->playing = true; +} + +void AudioSource::stop() { + this->playing = false; + this->rewind = true; +} + diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h new file mode 100644 index 0000000..1264790 --- /dev/null +++ b/src/crepe/api/AudioSource.h @@ -0,0 +1,60 @@ +#pragma once + +#include "../Component.h" +#include "../types.h" + +#include "Asset.h" + +namespace crepe { + +class AudioSystem; + +//! Audio source component +class AudioSource : public Component { + //! AudioSource components are handled by AudioSystem + friend class AudioSystem; + +protected: + AudioSource(game_object_id_t id, const Asset & source); + //! Only ComponentManager can create components + friend class ComponentManager; +public: + // But std::unique_ptr needs to be able to destoy this component again + virtual ~AudioSource() = default; + +public: + //! Start or resume this audio source + void play(bool looping = false); + //! Stop this audio source + void stop(); + +public: + //! Play when this component becomes active + bool play_on_awake = false; + //! Repeat the current audio clip during playback + bool loop = false; + //! Normalized volume (0.0 - 1.0) + float volume = 1.0; + +private: + //! This audio source's clip + const Asset source; + + //! If this source is playing audio + bool playing = false; + //! Rewind the sample location + bool rewind = false; + +private: + //! Value of \c active after last system update + bool last_active = false; + //! Value of \c playing after last system update + bool last_playing = false; + //! Value of \c volume after last system update + float last_volume = 1.0; + //! Value of \c loop after last system update + bool last_loop = false; +}; + +} // namespace crepe + diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index f9b370f..cca0e8b 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 GameObject.cpp Rigidbody.cpp @@ -7,7 +7,7 @@ target_sources(crepe PUBLIC Transform.cpp Color.cpp Texture.cpp - AssetManager.cpp + ResourceManager.cpp Sprite.cpp SaveManager.cpp Config.cpp @@ -19,10 +19,11 @@ target_sources(crepe PUBLIC Animator.cpp LoopManager.cpp LoopTimer.cpp + Asset.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES - # AudioSource.h + AudioSource.h BehaviorScript.h Config.h Script.h @@ -34,8 +35,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Vector2.h Color.h Texture.h - AssetManager.h - AssetManager.hpp + ResourceManager.h SaveManager.h Scene.h Metadata.h @@ -45,4 +45,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Animator.h LoopManager.h LoopTimer.h + Asset.h ) diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index 3ab877a..13eabd1 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -62,6 +62,20 @@ public: */ double gravity = 1; } physics; + + //! Asset loading options + struct { + /** + * \brief Pattern to match for Asset base directory + * + * All non-absolute paths resolved using \c Asset will be made relative to + * the first parent directory relative to the calling executable where + * appending this pattern results in a path that exists. If this string is + * empty, path resolution is disabled, and Asset will return all paths + * as-is. + */ + std::string root_pattern = ".crepe-root"; + } asset; }; } // namespace crepe diff --git a/src/crepe/api/ResourceManager.cpp b/src/crepe/api/ResourceManager.cpp new file mode 100644 index 0000000..7877ed9 --- /dev/null +++ b/src/crepe/api/ResourceManager.cpp @@ -0,0 +1,41 @@ +#include <stdexcept> + +#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 <typename T> +T & ResourceManager::cache(const Asset & asset) { + dbg_trace(); + static_assert(is_base_of<Resource, T>::value, "cache must recieve a derivative class of Resource"); + + if (!this->resources.contains(asset)) + this->resources[asset] = make_unique<T>(asset); + + Resource * resource = this->resources.at(asset).get(); + T * concrete_resource = dynamic_cast<T *>(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 new file mode 100644 index 0000000..efdd5c5 --- /dev/null +++ b/src/crepe/api/ResourceManager.h @@ -0,0 +1,69 @@ +#pragma once + +#include <memory> +#include <unordered_map> + +#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<const Asset, std::unique_ptr<Resource>> 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 <typename T> + T & cache(const Asset & asset); + + //! Clear the resource cache + void clear(); +}; + +} // namespace crepe + diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp index de0d0ea..6a1e4d8 100644 --- a/src/crepe/api/Texture.cpp +++ b/src/crepe/api/Texture.cpp @@ -26,7 +26,7 @@ Texture::~Texture() { void Texture::load(unique_ptr<Asset> res) { SDLContext & ctx = SDLContext::get_instance(); - this->texture = std::move(ctx.texture_from_path(res->get_canonical())); + this->texture = std::move(ctx.texture_from_path(res->get_path())); } int Texture::get_width() const { |