diff options
Diffstat (limited to 'src/crepe')
27 files changed, 590 insertions, 74 deletions
diff --git a/src/crepe/Asset.cpp b/src/crepe/Asset.cpp deleted file mode 100644 index 9c41ecb..0000000 --- a/src/crepe/Asset.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include <filesystem> - -#include "Asset.h" - -using namespace crepe; -using namespace std; - -// FIXME: restore this -// src(std::filesystem::canonical(src)) -Asset::Asset(const std::string & src) : src(src) { -	this->file = std::ifstream(this->src, std::ios::in | std::ios::binary); -} - -istream & Asset::get_stream() { return this->file; } - -const string & Asset::get_canonical() const { return this->src; } diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index 3b05742..df15b8f 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -1,19 +1,19 @@  target_sources(crepe PUBLIC -	Asset.cpp  	Particle.cpp  	ComponentManager.cpp  	Component.cpp  	Collider.cpp +	Resource.cpp  )  target_sources(crepe PUBLIC FILE_SET HEADERS FILES -	Asset.h  	ComponentManager.h  	ComponentManager.hpp  	Component.h  	Collider.h  	ValueBroker.h  	ValueBroker.hpp +	Resource.h  )  add_subdirectory(api) diff --git a/src/crepe/Component.h b/src/crepe/Component.h index 5279fb3..2e4ef7d 100644 --- a/src/crepe/Component.h +++ b/src/crepe/Component.h @@ -16,7 +16,12 @@ class Component {  public:  	//! Whether the component is active  	bool active = true; -	//! The id of the GameObject this component belongs to +	/** +	 * \brief The id of the GameObject this component belongs to +	 * +	 * \note Only systems are supposed to use this member, but since friend +	 * relations aren't inherited this needs to be public. +	 */  	const game_object_id_t game_object_id;  protected: @@ -24,7 +29,7 @@ protected:  	 * \param id The id of the GameObject this component belongs to  	 */  	Component(game_object_id_t id); -	//! Only the ComponentManager can create components +	//! Only ComponentManager can create components  	friend class ComponentManager;  public: @@ -40,6 +45,8 @@ public:  	 * \return The maximum number of instances for this component  	 */  	virtual int get_instances_max() const { return -1; } +	//! Only ComponentManager needs to know the max instance count +	friend class ComponentManager;  };  } // namespace crepe diff --git a/src/crepe/Resource.cpp b/src/crepe/Resource.cpp new file mode 100644 index 0000000..e254695 --- /dev/null +++ b/src/crepe/Resource.cpp @@ -0,0 +1,6 @@ +#include "Resource.h" + +using namespace crepe; + +Resource::Resource(const Asset & asset) { } + diff --git a/src/crepe/Resource.h b/src/crepe/Resource.h new file mode 100644 index 0000000..a0c8859 --- /dev/null +++ b/src/crepe/Resource.h @@ -0,0 +1,26 @@ +#pragma once + +namespace crepe { + +class ResourceManager; +class Asset; + +/** + * Resource is an interface class used to represent a (deserialized) game + * resource (e.g. textures, sounds). + */ +class Resource { +public: +	Resource(const Asset & src); +	virtual ~Resource() = default; + +private: +	/** +	 * The resource manager uses \c clone to create new instances of the concrete +	 * resource class. This may be used to inherit references to classes that +	 * would otherwise need to be implemented as singletons. +	 */ +	friend class ResourceManager; +}; + +} // namespace crepe 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 { diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 83e91f8..f2daada 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -149,7 +149,7 @@ SDLContext::texture_from_path(const std::string & path) {  	SDL_Surface * tmp = IMG_Load(path.c_str());  	if (tmp == nullptr) { -		tmp = IMG_Load("../asset/texture/ERROR.png"); +		tmp = IMG_Load("asset/texture/ERROR.png");  	}  	std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> img_surface; diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index 7aa89a9..b589759 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -1,3 +1,4 @@ +#include "../api/Asset.h"  #include "../util/Log.h"  #include "Sound.h" @@ -6,20 +7,14 @@  using namespace crepe;  using namespace std; -Sound::Sound(unique_ptr<Asset> res) { +Sound::Sound(const Asset & src) : Resource(src) { +	this->sample.load(src.get_path().c_str());  	dbg_trace(); -	this->load(std::move(res));  } - -Sound::Sound(const char * src) { -	dbg_trace(); -	this->load(make_unique<Asset>(src)); -} - -void Sound::load(unique_ptr<Asset> res) { this->sample.load(res->get_canonical().c_str()); } +Sound::~Sound() { dbg_trace(); }  void Sound::play() { -	SoundContext & ctx = SoundContext::get_instance(); +	SoundContext & ctx = this->context.get();  	if (ctx.engine.getPause(this->handle)) {  		// resume if paused  		ctx.engine.setPause(this->handle, false); @@ -31,13 +26,13 @@ void Sound::play() {  }  void Sound::pause() { -	SoundContext & ctx = SoundContext::get_instance(); +	SoundContext & ctx = this->context.get();  	if (ctx.engine.getPause(this->handle)) return;  	ctx.engine.setPause(this->handle, true);  }  void Sound::rewind() { -	SoundContext & ctx = SoundContext::get_instance(); +	SoundContext & ctx = this->context.get();  	if (!ctx.engine.isValidVoiceHandle(this->handle)) return;  	ctx.engine.seek(this->handle, 0);  } @@ -45,7 +40,7 @@ void Sound::rewind() {  void Sound::set_volume(float volume) {  	this->volume = volume; -	SoundContext & ctx = SoundContext::get_instance(); +	SoundContext & ctx = this->context.get();  	if (!ctx.engine.isValidVoiceHandle(this->handle)) return;  	ctx.engine.setVolume(this->handle, this->volume);  } @@ -53,7 +48,12 @@ void Sound::set_volume(float volume) {  void Sound::set_looping(bool looping) {  	this->looping = looping; -	SoundContext & ctx = SoundContext::get_instance(); +	SoundContext & ctx = this->context.get();  	if (!ctx.engine.isValidVoiceHandle(this->handle)) return;  	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 32b6478..a84aa8c 100644 --- a/src/crepe/facade/Sound.h +++ b/src/crepe/facade/Sound.h @@ -1,21 +1,25 @@  #pragma once -#include <memory>  #include <soloud/soloud.h>  #include <soloud/soloud_wav.h> -#include "../Asset.h" +#include "../util/OptionalRef.h" +#include "../Resource.h"  namespace crepe { +class SoundContext; +  /**   * \brief Sound resource facade   *   * This class is a wrapper around a \c SoLoud::Wav instance, which holds a   * single sample. It is part of the sound facade.   */ -class Sound { +class Sound : public Resource {  public: +	Sound(const Asset & src); +	~Sound(); // dbg_trace  	/**  	 * \brief Pause this sample  	 * @@ -67,15 +71,12 @@ public:  	bool get_looping() const { return this->looping; }  public: -	Sound(const char * src); -	Sound(std::unique_ptr<Asset> res); - -private: -	void load(std::unique_ptr<Asset> res); +	void set_context(SoundContext & ctx);  private:  	SoLoud::Wav sample;  	SoLoud::handle handle; +	OptionalRef<SoundContext> context;  	float volume = 1.0f;  	bool looping = false; diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp index deb2b62..b65dfb2 100644 --- a/src/crepe/facade/SoundContext.cpp +++ b/src/crepe/facade/SoundContext.cpp @@ -4,11 +4,6 @@  using namespace crepe; -SoundContext & SoundContext::get_instance() { -	static SoundContext instance; -	return instance; -} -  SoundContext::SoundContext() {  	dbg_trace();  	engine.init(); diff --git a/src/crepe/facade/SoundContext.h b/src/crepe/facade/SoundContext.h index d703c16..d22ff7a 100644 --- a/src/crepe/facade/SoundContext.h +++ b/src/crepe/facade/SoundContext.h @@ -13,18 +13,18 @@ namespace crepe {   * the methods for playing \c Sound instances. It is part of the sound facade.   */  class SoundContext { -private: -	// singleton +public:  	SoundContext();  	virtual ~SoundContext(); +  	SoundContext(const SoundContext &) = delete;  	SoundContext(SoundContext &&) = delete;  	SoundContext & operator=(const SoundContext &) = delete;  	SoundContext & operator=(SoundContext &&) = delete;  private: -	static SoundContext & get_instance();  	SoLoud::Soloud engine; +	//! Sound directly calls methods on \c engine  	friend class Sound;  }; diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp new file mode 100644 index 0000000..c8dae9d --- /dev/null +++ b/src/crepe/system/AudioSystem.cpp @@ -0,0 +1,56 @@ +#include "AudioSystem.h" +#include "ComponentManager.h" + +#include "../api/AudioSource.h" + +using namespace crepe; +using namespace std; + +void AudioSystem::update() { +	ComponentManager & mgr = this->component_manager; +	vector<reference_wrapper<AudioSource>> components = mgr.get_components_by_type<AudioSource>(); + +	for (auto component_ref : components) { +		AudioSource & component = component_ref.get(); +		if (!component.active) continue; + +		/** +		 * How this is supposed to work: +		 * - Get an instance of Sound for this resource/component combo (Sound +		 *   instance is supposed to be unique per component, even if they use the +		 *   same underlying asset). +		 * OR +		 * - Use the same instance of Sound if this is what the cache returns +		 *   (= what the game programmer's wishes to do). +		 * +		 * NOT supposed to happen but still the case: +		 * - Below function call causes assets to be cached unintentionally +		 * - Cached assets are deleted at the end of a scene (i think?) +		 * - I'm not sure if the ResourceManager is even supposed to have a public +		 *   `.clear()` method since the control over resource lifetime is +		 *   explicitly handed over to the game programmer by using ResourceManager +		 *   to cache/uncache. I believe the proper methods are supposed to be: +		 * +		 *   - get()       get a reference to resource (used here) +		 *   - clear()     clears NON-cached assets +		 *   - cache()     marks asset as "do not delete at end of scene" +		 *   - uncache()   undoes the above +		 * +		 *   I think somewhere in the above function calls a unique identifier for +		 *   the Asset/GameObject should be given to make sure the unique instance +		 *   shit works as intended. The resource manager is also used for things +		 *   other than sounds. +		 * +		 * Also need to check: +		 * - Is it an issue if there are multiple AudioSource components playing +		 *   the same sample (= identical Asset), while they are all triggered +		 *   using the same underlying instance of Sound (esp. w/ +		 *   play/pause/retrigger behavior). +		 */ +		Sound & sound = this->resman.cache<Sound>(component.source); +		sound.set_context(this->context); + +		// TODO: lots of state diffing +	} +} + diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h new file mode 100644 index 0000000..d0b4f9a --- /dev/null +++ b/src/crepe/system/AudioSystem.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../facade/SoundContext.h" +#include "../api/ResourceManager.h" + +#include "System.h" + +namespace crepe { + +class AudioSystem : public System { +public: +	using System::System; +	void update() override; + +private: +	SoundContext context {}; +	ResourceManager & resman = ResourceManager::get_instance(); +}; + +} // namespace crepe + diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt index d658b25..f507b90 100644 --- a/src/crepe/system/CMakeLists.txt +++ b/src/crepe/system/CMakeLists.txt @@ -5,6 +5,7 @@ target_sources(crepe PUBLIC  	PhysicsSystem.cpp  	CollisionSystem.cpp  	RenderSystem.cpp +	AudioSystem.cpp  	AnimatorSystem.cpp  ) @@ -14,5 +15,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	PhysicsSystem.h  	CollisionSystem.h  	RenderSystem.h +	AudioSystem.h  	AnimatorSystem.h  ) diff --git a/src/crepe/system/System.h b/src/crepe/system/System.h index 28ea20e..36f7edc 100644 --- a/src/crepe/system/System.h +++ b/src/crepe/system/System.h @@ -1,5 +1,7 @@  #pragma once +#include "../ComponentManager.h" +  namespace crepe {  class ComponentManager; diff --git a/src/crepe/util/CMakeLists.txt b/src/crepe/util/CMakeLists.txt index 4be738a..94ed906 100644 --- a/src/crepe/util/CMakeLists.txt +++ b/src/crepe/util/CMakeLists.txt @@ -9,5 +9,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	Log.hpp  	Proxy.h  	Proxy.hpp +	OptionalRef.h +	OptionalRef.hpp  ) diff --git a/src/crepe/util/OptionalRef.h b/src/crepe/util/OptionalRef.h new file mode 100644 index 0000000..1ad3a6d --- /dev/null +++ b/src/crepe/util/OptionalRef.h @@ -0,0 +1,41 @@ +#pragma once + +namespace crepe { + +/** + * \brief Optional reference utility + * + * This class doesn't need to know the full definition of \c T to be used. + * + * \tparam T Value type + */ +template <typename T> +class OptionalRef { +public: +	OptionalRef() = default; +	OptionalRef(T &); +  OptionalRef<T> & operator=(T &); +	explicit operator bool() const noexcept; + +	void set(T &) noexcept; +	T & get() const; +	void clear() noexcept; + +  OptionalRef(const OptionalRef<T> &); +  OptionalRef(OptionalRef<T> &&); +  OptionalRef<T> & operator=(const OptionalRef<T> &); +  OptionalRef<T> & operator=(OptionalRef<T> &&); + +private: +	/** +	 * \brief Reference to the value of type \c T +	 * +	 * \note This raw pointer is *not* managed, and only used as a reference! +	 */ +	T * ref = nullptr; +}; + +} + +#include "OptionalRef.hpp" + diff --git a/src/crepe/util/OptionalRef.hpp b/src/crepe/util/OptionalRef.hpp new file mode 100644 index 0000000..7b201b0 --- /dev/null +++ b/src/crepe/util/OptionalRef.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include <stdexcept> + +#include "OptionalRef.h" + +namespace crepe { + +template <typename T> +OptionalRef<T>::OptionalRef(T & ref) { +	this->set(ref); +} + +template <typename T> +OptionalRef<T>::OptionalRef(const OptionalRef<T> & other) { +	this->ref = other.ref; +} + +template <typename T> +OptionalRef<T>::OptionalRef(OptionalRef<T> && other) { +	this->ref = other.ref; +	other.clear(); +} + +template <typename T> +OptionalRef<T> & OptionalRef<T>::operator=(const OptionalRef<T> & other) { +	this->ref = other.ref; +	return *this; +} + +template <typename T> +OptionalRef<T> & OptionalRef<T>::operator=(OptionalRef<T> && other) { +	this->ref = other.ref; +	other.clear(); +	return *this; +} + +template <typename T> +T & OptionalRef<T>::get() const { +	if (this->ref == nullptr) +		throw std::runtime_error("OptionalRef: attempt to dereference nullptr"); +	return *this->ref; +} + +template <typename T> +void OptionalRef<T>::set(T & ref) noexcept { +	this->ref = &ref; +} + +template <typename T> +void OptionalRef<T>::clear() noexcept { +	this->ref = nullptr; +} + +template <typename T> +OptionalRef<T> & OptionalRef<T>::operator=(T & ref) { +	this->set(ref); +	return *this; +} + +template <typename T> +OptionalRef<T>::operator bool() const noexcept { +	return this->ref != nullptr; +} + +} +  |