diff options
Diffstat (limited to 'src')
49 files changed, 1308 insertions, 9 deletions
| diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0090188..09c60bd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,14 +3,37 @@ cmake_minimum_required(VERSION 3.28)  set(CMAKE_C_STANDARD 11)  set(CMAKE_CXX_STANDARD 20)  set(CMAKE_EXPORT_COMPILE_COMMANDS 1) - -# enable debug features  set(CMAKE_BUILD_TYPE Debug) -add_compile_definitions(DEBUG)  project(crepe C CXX) -add_executable(main) +find_package(SDL2 REQUIRED) +find_package(SoLoud REQUIRED) +find_package(GTest REQUIRED) + +add_library(crepe SHARED) +add_executable(test_main EXCLUDE_FROM_ALL) + +target_include_directories(crepe +	PUBLIC SYSTEM INTERFACE . +) + +target_link_libraries(crepe +	PRIVATE soloud +	PRIVATE SDL2 +)  add_subdirectory(crepe) +add_subdirectory(test) +add_subdirectory(example) + +install( +	TARGETS crepe +	FILE_SET HEADERS DESTINATION include/crepe +) + +target_link_libraries(test_main +	PRIVATE gtest_main +	PUBLIC crepe +) diff --git a/src/crepe/Asset.cpp b/src/crepe/Asset.cpp new file mode 100644 index 0000000..15ddc27 --- /dev/null +++ b/src/crepe/Asset.cpp @@ -0,0 +1,14 @@ +#include <filesystem> + +#include "Asset.h" + +using namespace crepe; + +Asset::Asset(const std::string & src) { +	this->src = std::filesystem::canonical(src); +	this->file = std::ifstream(this->src, std::ios::in | std::ios::binary); +} + +const std::istream & Asset::read() { return this->file; } + +const char * Asset::canonical() { return this->src.c_str(); } diff --git a/src/crepe/Asset.h b/src/crepe/Asset.h new file mode 100644 index 0000000..0cb5834 --- /dev/null +++ b/src/crepe/Asset.h @@ -0,0 +1,33 @@ +#pragma once + +#include <fstream> +#include <iostream> +#include <string> + +namespace crepe { + +/** + * \brief Asset location helper + * + * This class is used to locate and canonicalize paths to game asset files, and + * should *always* be used when retrieving files from disk. + */ +class Asset { +public: +	/** +	 * \param src  Unique identifier to asset +	 */ +	Asset(const std::string & src); + +public: +	//! Get an input stream to the contents of this resource +	const std::istream & read(); +	//! Get the canonical path to this resource +	const char * canonical(); + +private: +	std::string src; +	std::ifstream file; +}; + +} // namespace crepe diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index 392b7d7..cfa51fb 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -1,3 +1,23 @@ -target_sources(main PUBLIC -	main.cpp +target_sources(crepe PUBLIC +	Asset.cpp +	Sound.cpp +	SoundContext.cpp +	ComponentManager.cpp +	Component.cpp +	ScriptSystem.cpp  ) + +target_sources(crepe PUBLIC FILE_SET HEADERS FILES +	Asset.h +	Sound.h +	SoundContext.h +	ComponentManager.h +	ComponentManager.hpp +	Component.h +	System.h +	ScriptSystem.h +) + +add_subdirectory(api) +add_subdirectory(util) + diff --git a/src/crepe/Component.cpp b/src/crepe/Component.cpp new file mode 100644 index 0000000..bce90f1 --- /dev/null +++ b/src/crepe/Component.cpp @@ -0,0 +1,3 @@ +#include "Component.h" + +using namespace crepe; diff --git a/src/crepe/Component.h b/src/crepe/Component.h new file mode 100644 index 0000000..9cda177 --- /dev/null +++ b/src/crepe/Component.h @@ -0,0 +1,16 @@ +#pragma once + +namespace crepe { + +class Component { +protected: +	Component() = default; + +public: +	virtual ~Component() = default; + +public: +	bool active = true; +}; + +} // namespace crepe diff --git a/src/crepe/ComponentManager.cpp b/src/crepe/ComponentManager.cpp new file mode 100644 index 0000000..8bde33a --- /dev/null +++ b/src/crepe/ComponentManager.cpp @@ -0,0 +1,29 @@ +#include "ComponentManager.h" +#include "util/log.h" + +using namespace crepe; + +ComponentManager & ComponentManager::get_instance() { +	static ComponentManager instance; +	return instance; +} + +void ComponentManager::delete_all_components_of_id(uint32_t id) { +	// Loop through all the types (in the unordered_map<>) +	for (auto & [type, componentArray] : components) { +		// Make sure that the id (that we are looking for) is within the boundaries of the vector<> +		if (id < componentArray.size()) { +			// Clear the components at this specific id +			componentArray[id].clear(); +		} +	} +} + +void ComponentManager::delete_all_components() { +	// Clear the whole unordered_map<> +	this->components.clear(); +} + +ComponentManager::ComponentManager() { dbg_trace(); } + +ComponentManager::~ComponentManager() { dbg_trace(); } diff --git a/src/crepe/ComponentManager.h b/src/crepe/ComponentManager.h new file mode 100644 index 0000000..38f32e4 --- /dev/null +++ b/src/crepe/ComponentManager.h @@ -0,0 +1,63 @@ +#pragma once + +#include <cstdint> +#include <memory> +#include <typeindex> +#include <unordered_map> +#include <utility> +#include <vector> + +#include "Component.h" + +namespace crepe { + +class ComponentManager { +public: +	// Singleton +	static ComponentManager & get_instance(); +	ComponentManager(const ComponentManager &) = delete; +	ComponentManager(ComponentManager &&) = delete; +	ComponentManager & operator=(const ComponentManager &) = delete; +	ComponentManager & operator=(ComponentManager &&) = delete; + +public: +	//! Add a component of a specific type +	template <typename T, typename... Args> +	T & add_component(uint32_t id, Args &&... args); +	//! Deletes all components of a specific type and id +	template <typename T> +	void delete_components_by_id(uint32_t id); +	//! Deletes all components of a specific type +	template <typename T> +	void delete_components(); +	//! Deletes all components of a specific id +	void delete_all_components_of_id(uint32_t id); +	//! Deletes all components +	void delete_all_components(); + +	//! Get a vector<> of all components at specific type and id +	template <typename T> +	std::vector<std::reference_wrapper<T>> +	get_components_by_id(uint32_t id) const; +	//! Get a vector<> of all components of a specific type +	template <typename T> +	std::vector<std::reference_wrapper<T>> get_components_by_type() const; + +private: +	ComponentManager(); +	virtual ~ComponentManager(); + +	/* +	 * The std::unordered_map<std::type_index, std::vector<std::vector<std::unique_ptr<Component>>>> below might seem a bit strange, let me explain this structure: +	 * The std::unordered_map<> has a key and value. The key is a std::type_index and the value is a std::vector. So, a new std::vector will be created for each new std::type_index. +	 * The first std::vector<> stores another vector<>. This first vector<> is to bind the entity's id to a component. +	 * The second std::vector<> stores unique_ptrs. Each component can be gathered via an unique_ptr. This second vector<> allows multiple components of the same std::type_index for one entity (id). +	 */ +	std::unordered_map<std::type_index, +					   std::vector<std::vector<std::unique_ptr<Component>>>> +		components; +}; + +} // namespace crepe + +#include "ComponentManager.hpp" diff --git a/src/crepe/ComponentManager.hpp b/src/crepe/ComponentManager.hpp new file mode 100644 index 0000000..2377a94 --- /dev/null +++ b/src/crepe/ComponentManager.hpp @@ -0,0 +1,147 @@ +#pragma once + +#include <type_traits> + +#include "ComponentManager.h" + +namespace crepe { + +template <class T, typename... Args> +T & ComponentManager::add_component(uint32_t id, Args &&... args) { +	using namespace std; + +	static_assert(is_base_of<Component, T>::value, +				  "add_component must recieve a derivative class of Component"); + +	// Determine the type of T (this is used as the key of the unordered_map<>) +	type_index type = typeid(T); + +	// Check if this component type is already in the unordered_map<> +	if (components.find(type) == components.end()) { +		//If not, create a new (empty) vector<> of vector<unique_ptr<Component>> +		components[type] = vector<vector<unique_ptr<Component>>>(); +	} + +	// Resize the vector<> if the id is greater than the current size +	if (id >= components[type].size()) { +		// Initialize new slots to nullptr (resize does automatically init to nullptr) +		components[type].resize(id + 1); +	} + +	// Create a new component of type T (arguments directly forwarded). The +	// constructor must be called by ComponentManager. +	T * instance = new T(forward<Args>(args)...); +	// store its unique_ptr in the vector<> +	components[type][id].push_back(unique_ptr<T>(instance)); + +	return *instance; +} + +template <typename T> +void ComponentManager::delete_components_by_id(uint32_t id) { +	using namespace std; + +	// Determine the type of T (this is used as the key of the unordered_map<>) +	type_index type = typeid(T); + +	// Find the type (in the unordered_map<>) +	if (components.find(type) != components.end()) { +		// Get the correct vector<> +		vector<vector<unique_ptr<Component>>> & component_array +			= components[type]; + +		// Make sure that the id (that we are looking for) is within the boundaries of the vector<> +		if (id < component_array.size()) { +			// Clear the whole vector<> of this specific type and id +			component_array[id].clear(); +		} +	} +} + +template <typename T> +void ComponentManager::delete_components() { +	// Determine the type of T (this is used as the key of the unordered_map<>) +	std::type_index type = typeid(T); + +	if (components.find(type) == components.end()) return; + +	components[type].clear(); +} + +template <typename T> +std::vector<std::reference_wrapper<T>> +ComponentManager::get_components_by_id(uint32_t id) const { +	using namespace std; + +	// Determine the type of T (this is used as the key of the unordered_map<>) +	type_index type = typeid(T); + +	// Create an empty vector<> +	vector<reference_wrapper<T>> component_vector; + +	if (components.find(type) == components.end()) return component_vector; + +	// Get the correct vector<> +	const vector<vector<unique_ptr<Component>>> & component_array +		= components.at(type); + +	// Make sure that the id (that we are looking for) is within the boundaries of the vector<> +	if (id >= component_array.size()) return component_vector; + +	// Loop trough the whole vector<> +	for (const unique_ptr<Component> & component_ptr : component_array[id]) { +		// Cast the unique_ptr to a raw pointer +		T * casted_component = static_cast<T *>(component_ptr.get()); + +		if (casted_component == nullptr) continue; + +		// Add the dereferenced raw pointer to the vector<> +		component_vector.push_back(*casted_component); +	} + +	return component_vector; +} + +template <typename T> +std::vector<std::reference_wrapper<T>> +ComponentManager::get_components_by_type() const { +	using namespace std; + +	// Determine the type of T (this is used as the key of the unordered_map<>) +	type_index type = typeid(T); + +	// Create an empty vector<> +	vector<reference_wrapper<T>> component_vector; +	// Set the id to 0 (the id will also be stored in the returned vector<>) +	// uint32_t id = 0; + +	// Find the type (in the unordered_map<>) +	if (components.find(type) == components.end()) return component_vector; + +	// Get the correct vector<> +	const vector<vector<unique_ptr<Component>>> & component_array +		= components.at(type); + +	// Loop through the whole vector<> +	for (const vector<unique_ptr<Component>> & component : component_array) { +		// Loop trough the whole vector<> +		for (const unique_ptr<Component> & component_ptr : component) { +			// Cast the unique_ptr to a raw pointer +			T * casted_component = static_cast<T *>(component_ptr.get()); + +			// Ensure that the cast was successful +			if (casted_component == nullptr) continue; + +			// Pair the dereferenced raw pointer and the id and add it to the vector<> +			component_vector.emplace_back(ref(*casted_component)); +		} + +		// Increase the id (the id will also be stored in the returned vector<>) +		//++id; +	} + +	// Return the vector<> +	return component_vector; +} + +} // namespace crepe diff --git a/src/crepe/ScriptSystem.cpp b/src/crepe/ScriptSystem.cpp new file mode 100644 index 0000000..5d882be --- /dev/null +++ b/src/crepe/ScriptSystem.cpp @@ -0,0 +1,45 @@ +#include <forward_list> +#include <functional> +#include <vector> + +#include "ComponentManager.h" +#include "ScriptSystem.h" +#include "api/BehaviorScript.h" +#include "api/Script.h" +#include "util/log.h" + +using namespace std; +using namespace crepe; +using namespace crepe::api; + +ScriptSystem::ScriptSystem() { dbg_trace(); } +ScriptSystem::~ScriptSystem() { dbg_trace(); } + +ScriptSystem & ScriptSystem::get_instance() { +	static ScriptSystem instance; +	return instance; +} + +void ScriptSystem::update() { +	using namespace std; +	dbg_trace(); + +	forward_list<Script *> scripts = this->get_scripts(); +	for (Script * script : scripts) script->update(); +} + +forward_list<Script *> ScriptSystem::get_scripts() { +	forward_list<Script *> scripts = {}; +	ComponentManager & mgr = ComponentManager::get_instance(); +	vector<reference_wrapper<BehaviorScript>> behavior_scripts +		= mgr.get_components_by_type<BehaviorScript>(); + +	for (auto behavior_script_ref : behavior_scripts) { +		BehaviorScript & behavior_script = behavior_script_ref.get(); +		Script * script = behavior_script.script.get(); +		if (script == nullptr) continue; +		scripts.push_front(script); +	} + +	return scripts; +} diff --git a/src/crepe/ScriptSystem.h b/src/crepe/ScriptSystem.h new file mode 100644 index 0000000..1f472a0 --- /dev/null +++ b/src/crepe/ScriptSystem.h @@ -0,0 +1,26 @@ +#pragma once + +#include <forward_list> + +#include "System.h" + +namespace crepe::api { +class Script; +} + +namespace crepe { + +class ScriptSystem : public System { +public: +	static ScriptSystem & get_instance(); +	void update(); + +private: +	ScriptSystem(); +	~ScriptSystem(); + +private: +	std::forward_list<api::Script *> get_scripts(); +}; + +} // namespace crepe diff --git a/src/crepe/Sound.cpp b/src/crepe/Sound.cpp new file mode 100644 index 0000000..64fa281 --- /dev/null +++ b/src/crepe/Sound.cpp @@ -0,0 +1,60 @@ +#include "util/log.h" + +#include "Sound.h" +#include "SoundContext.h" + +using namespace crepe; + +Sound::Sound(std::unique_ptr<Asset> res) { +	dbg_trace(); +	this->load(std::move(res)); +} + +Sound::Sound(const char * src) { +	dbg_trace(); +	this->load(std::make_unique<Asset>(src)); +} + +void Sound::load(std::unique_ptr<Asset> res) { +	this->sample.load(res->canonical()); +} + +void Sound::play() { +	SoundContext & ctx = SoundContext::get_instance(); +	if (ctx.engine.getPause(this->handle)) { +		// resume if paused +		ctx.engine.setPause(this->handle, false); +	} else { +		// or start new sound +		this->handle = ctx.engine.play(this->sample, this->volume); +		ctx.engine.setLooping(this->handle, this->looping); +	} +} + +void Sound::pause() { +	SoundContext & ctx = SoundContext::get_instance(); +	if (ctx.engine.getPause(this->handle)) return; +	ctx.engine.setPause(this->handle, true); +} + +void Sound::rewind() { +	SoundContext & ctx = SoundContext::get_instance(); +	if (!ctx.engine.isValidVoiceHandle(this->handle)) return; +	ctx.engine.seek(this->handle, 0); +} + +void Sound::set_volume(float volume) { +	this->volume = volume; + +	SoundContext & ctx = SoundContext::get_instance(); +	if (!ctx.engine.isValidVoiceHandle(this->handle)) return; +	ctx.engine.setVolume(this->handle, this->volume); +} + +void Sound::set_looping(bool looping) { +	this->looping = looping; + +	SoundContext & ctx = SoundContext::get_instance(); +	if (!ctx.engine.isValidVoiceHandle(this->handle)) return; +	ctx.engine.setLooping(this->handle, this->looping); +} diff --git a/src/crepe/Sound.h b/src/crepe/Sound.h new file mode 100644 index 0000000..b7cfbb8 --- /dev/null +++ b/src/crepe/Sound.h @@ -0,0 +1,82 @@ +#pragma once + +#include <soloud/soloud.h> +#include <soloud/soloud_wav.h> + +#include <memory> + +#include "Asset.h" + +namespace crepe { + +class Sound { +public: +	/** +	 * \brief Pause this sample +	 * +	 * Pauses this sound if it is playing, or does nothing if it is already +	 * paused. The playhead position is saved, such that calling \c play() after +	 * this function makes the sound resume. +	 */ +	void pause(); +	/** +	 * \brief Play this sample +	 * +	 * Resume playback if this sound is paused, or start from the beginning of +	 * the sample. +	 * +	 * \note This class only saves a reference to the most recent 'voice' of this +	 * sound. Calling \c play() while the sound is already playing causes +	 * multiple instances of the sample to play simultaniously. The sample +	 * started last is the one that is controlled afterwards. +	 */ +	void play(); +	/** +	 * \brief Reset playhead position +	 *  +	 * Resets the playhead position so that calling \c play() after this function +	 * makes it play from the start of the sample. If the sound is not paused +	 * before calling this function, this function will stop playback. +	 */ +	void rewind(); +	/** +	 * \brief Set playback volume / gain +	 * +	 * \param volume  Volume (0 = muted, 1 = full volume) +	 */ +	void set_volume(float volume); +	/** +	 * \brief Get playback volume / gain +	 * +	 * \return Volume +	 */ +	float get_volume() const { return this->volume; } +	/** +	 * \brief Set looping behavior for this sample +	 * +	 * \param looping  Looping behavior (false = one-shot, true = loop) +	 */ +	void set_looping(bool looping); +	/** +	 * \brief Get looping behavior +	 * +	 * \return true if looping, false if one-shot +	 */ +	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); + +private: +	SoLoud::Wav sample; +	SoLoud::handle handle; + +	float volume = 1.0f; +	bool looping = false; +}; + +} // namespace crepe diff --git a/src/crepe/SoundContext.cpp b/src/crepe/SoundContext.cpp new file mode 100644 index 0000000..72047d2 --- /dev/null +++ b/src/crepe/SoundContext.cpp @@ -0,0 +1,20 @@ +#include "util/log.h" + +#include "SoundContext.h" + +using namespace crepe; + +SoundContext & SoundContext::get_instance() { +	static SoundContext instance; +	return instance; +} + +SoundContext::SoundContext() { +	dbg_trace(); +	engine.init(); +} + +SoundContext::~SoundContext() { +	dbg_trace(); +	engine.deinit(); +} diff --git a/src/crepe/SoundContext.h b/src/crepe/SoundContext.h new file mode 100644 index 0000000..d3123d2 --- /dev/null +++ b/src/crepe/SoundContext.h @@ -0,0 +1,26 @@ +#pragma once + +#include <soloud/soloud.h> + +#include "Sound.h" + +namespace crepe { + +class SoundContext { +private: +	SoundContext(); +	virtual ~SoundContext(); + +	// singleton +	static SoundContext & get_instance(); +	SoundContext(const SoundContext &) = delete; +	SoundContext(SoundContext &&) = delete; +	SoundContext & operator=(const SoundContext &) = delete; +	SoundContext & operator=(SoundContext &&) = delete; + +private: +	SoLoud::Soloud engine; +	friend class Sound; +}; + +} // namespace crepe diff --git a/src/crepe/System.h b/src/crepe/System.h new file mode 100644 index 0000000..ecbb7f5 --- /dev/null +++ b/src/crepe/System.h @@ -0,0 +1,22 @@ +#pragma once + +namespace crepe { + +class System { +public: +	static System & get_instance(); +	virtual void update() = 0; + +protected: +	System() {}; +	virtual ~System() {}; + +private: +	// singleton +	System(const System &) = delete; +	System(System &&) = delete; +	System & operator=(const System &) = delete; +	System & operator=(System &&) = delete; +}; + +} // namespace crepe diff --git a/src/crepe/api/AudioSource.cpp b/src/crepe/api/AudioSource.cpp new file mode 100644 index 0000000..bb067dc --- /dev/null +++ b/src/crepe/api/AudioSource.cpp @@ -0,0 +1,23 @@ +#include <memory> + +#include "AudioSource.h" + +#include "../Sound.h" + +using namespace crepe::api; + +AudioSource::AudioSource(std::unique_ptr<Asset> audio_clip) { +	this->sound = std::make_unique<crepe::Sound>(std::move(audio_clip)); +} + +void AudioSource::play() { return this->play(false); } + +void AudioSource::play(bool looping) { +	this->sound->set_looping(looping); +	this->sound->play(); +} + +void AudioSource::stop() { +	this->sound->pause(); +	this->sound->rewind(); +} diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h new file mode 100644 index 0000000..7980212 --- /dev/null +++ b/src/crepe/api/AudioSource.h @@ -0,0 +1,41 @@ +#pragma once + +#include <memory> + +#include "../Asset.h" +#include "../Component.h" + +namespace crepe { +class Sound; +} + +namespace crepe::api { + +//! Audio source component +class AudioSource : Component { +public: +	AudioSource(std::unique_ptr<Asset> audio_clip); +	virtual ~AudioSource() = default; + +public: +	//! Start or resume this audio source +	void play(); +	void play(bool looping); +	//! Stop this audio source +	void stop(); + +public: +	//! Sample file location +	std::unique_ptr<Asset> audio_clip; +	//! TODO: ????? +	bool play_on_awake; +	//! Repeat the current audio clip during playback +	bool loop; +	//! Normalized volume (0.0 - 1.0) +	float volume; + +private: +	std::unique_ptr<crepe::Sound> sound; +}; + +} // namespace crepe::api diff --git a/src/crepe/api/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp new file mode 100644 index 0000000..84bfd4c --- /dev/null +++ b/src/crepe/api/BehaviorScript.cpp @@ -0,0 +1,7 @@ +#include "../util/log.h" + +#include "BehaviorScript.h" + +using namespace crepe::api; + +BehaviorScript::BehaviorScript() { dbg_trace(); } diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h new file mode 100644 index 0000000..25d3cc5 --- /dev/null +++ b/src/crepe/api/BehaviorScript.h @@ -0,0 +1,36 @@ +#pragma once + +#include <memory> + +#include "../Component.h" +#include "Script.h" + +namespace crepe { +class ScriptSystem; +class ComponentManager; +} // namespace crepe + +namespace crepe::api { + +class Script; + +class BehaviorScript : public Component { +protected: +	friend class crepe::ComponentManager; +	BehaviorScript(); + +public: +	virtual ~BehaviorScript() = default; + +public: +	template <class T> +	BehaviorScript & set_script(); + +protected: +	friend class crepe::ScriptSystem; +	std::unique_ptr<Script> script = nullptr; +}; + +} // namespace crepe::api + +#include "BehaviorScript.hpp" diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp new file mode 100644 index 0000000..a6bd81c --- /dev/null +++ b/src/crepe/api/BehaviorScript.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include <type_traits> + +#include "../util/log.h" +#include "BehaviorScript.h" + +namespace crepe::api { + +template <class T> +BehaviorScript & BehaviorScript::set_script() { +	static_assert(std::is_base_of<Script, T>::value); +	dbg_trace(); +	Script * s = new T(); +	this->script = std::unique_ptr<Script>(s); +	return *this; +} + +} // namespace crepe::api diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt new file mode 100644 index 0000000..ae92f3d --- /dev/null +++ b/src/crepe/api/CMakeLists.txt @@ -0,0 +1,21 @@ +target_sources(crepe PUBLIC +	# AudioSource.cpp +	BehaviorScript.cpp +	Script.cpp +	GameObject.cpp +	Collider.cpp +	Rigidbody.cpp +	Sprite.cpp +) + +target_sources(crepe PUBLIC FILE_SET HEADERS FILES +	# AudioSource.h +	BehaviorScript.h +	Script.h +	GameObject.h +	GameObject.hpp +	Collider.h +	Rigidbody.h +	Sprite.h +) + diff --git a/src/crepe/api/Collider.cpp b/src/crepe/api/Collider.cpp new file mode 100644 index 0000000..6370a42 --- /dev/null +++ b/src/crepe/api/Collider.cpp @@ -0,0 +1,5 @@ +#include "Collider.h" + +using namespace crepe::api; + +Collider::Collider(int size) : size(size) {} diff --git a/src/crepe/api/Collider.h b/src/crepe/api/Collider.h new file mode 100644 index 0000000..72d8e77 --- /dev/null +++ b/src/crepe/api/Collider.h @@ -0,0 +1,14 @@ +#pragma once + +#include "../Component.h" + +namespace crepe::api { + +class Collider : public Component { +public: +	Collider(int size); + +	int size; +}; + +} // namespace crepe::api diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp new file mode 100644 index 0000000..b167187 --- /dev/null +++ b/src/crepe/api/GameObject.cpp @@ -0,0 +1,7 @@ +#include "GameObject.h" + +using namespace crepe::api; +using namespace std; + +GameObject::GameObject(uint32_t id, string name, string tag, int layer) +	: id(id), name(name), tag(tag), active(true), layer(layer) {} diff --git a/src/crepe/api/GameObject.h b/src/crepe/api/GameObject.h new file mode 100644 index 0000000..57508c5 --- /dev/null +++ b/src/crepe/api/GameObject.h @@ -0,0 +1,24 @@ +#pragma once + +#include <cstdint> +#include <string> + +namespace crepe::api { + +class GameObject { +public: +	GameObject(uint32_t id, std::string name, std::string tag, int layer); + +	template <typename T, typename... Args> +	T & add_component(Args &&... args); + +	uint32_t id; +	std::string name; +	std::string tag; +	bool active; +	int layer; +}; + +} // namespace crepe::api + +#include "GameObject.hpp" diff --git a/src/crepe/api/GameObject.hpp b/src/crepe/api/GameObject.hpp new file mode 100644 index 0000000..8295ea3 --- /dev/null +++ b/src/crepe/api/GameObject.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "../ComponentManager.h" + +#include "GameObject.h" + +namespace crepe::api { + +template <typename T, typename... Args> +T & GameObject::add_component(Args &&... args) { +	auto & mgr = ComponentManager::get_instance(); +	return mgr.add_component<T>(id, std::forward<Args>(args)...); +} + +} // namespace crepe::api diff --git a/src/crepe/api/Rigidbody.cpp b/src/crepe/api/Rigidbody.cpp new file mode 100644 index 0000000..98d1d60 --- /dev/null +++ b/src/crepe/api/Rigidbody.cpp @@ -0,0 +1,6 @@ +#include "Rigidbody.h" + +using namespace crepe::api; + +Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) +	: mass(mass), gravity_scale(gravityScale), body_type(bodyType) {} diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h new file mode 100644 index 0000000..6cae579 --- /dev/null +++ b/src/crepe/api/Rigidbody.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../Component.h" + +namespace crepe::api { + +class Rigidbody : public Component { +public: +	Rigidbody(int mass, int gravityScale, int bodyType); + +	int mass; +	int gravity_scale; +	int body_type; +}; + +} // namespace crepe::api diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp new file mode 100644 index 0000000..5016ed0 --- /dev/null +++ b/src/crepe/api/Script.cpp @@ -0,0 +1,3 @@ +#include "Script.h" + +using namespace crepe::api; diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h new file mode 100644 index 0000000..0036d1f --- /dev/null +++ b/src/crepe/api/Script.h @@ -0,0 +1,21 @@ +#pragma once + +namespace crepe { +class ScriptSystem; +} + +namespace crepe::api { + +class Script { +	friend class crepe::ScriptSystem; + +protected: +	virtual void init() {} +	virtual void update() {} +	// NOTE: additional *events* (like unity's OnDisable and OnEnable) should be +	// implemented as member methods in derivative user script classes and +	// registered in init(), otherwise this class will balloon in size with each +	// added event. +}; + +} // namespace crepe::api diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp new file mode 100644 index 0000000..aa7dbb0 --- /dev/null +++ b/src/crepe/api/Sprite.cpp @@ -0,0 +1,8 @@ +#include <string> + +#include "Sprite.h" + +using namespace crepe::api; +using namespace std; + +Sprite::Sprite(string path) : path(path) {} diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h new file mode 100644 index 0000000..580f825 --- /dev/null +++ b/src/crepe/api/Sprite.h @@ -0,0 +1,16 @@ +#pragma once + +#include <string> + +#include "../Component.h" + +namespace crepe::api { + +class Sprite : public Component { +public: +	Sprite(std::string path); + +	std::string path; +}; + +} // namespace crepe::api diff --git a/src/crepe/main.cpp b/src/crepe/main.cpp deleted file mode 100644 index 8e9a184..0000000 --- a/src/crepe/main.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include <stdio.h> - -int main() { printf("Hello World!\n"); } diff --git a/src/crepe/util/CMakeLists.txt b/src/crepe/util/CMakeLists.txt new file mode 100644 index 0000000..e2cffaf --- /dev/null +++ b/src/crepe/util/CMakeLists.txt @@ -0,0 +1,11 @@ +target_sources(crepe PUBLIC +	log.cpp +	fmt.cpp +) + +target_sources(crepe PUBLIC FILE_SET HEADERS FILES +	color.h +	log.h +	fmt.h +) + diff --git a/src/crepe/util/color.h b/src/crepe/util/color.h new file mode 100644 index 0000000..066c9d3 --- /dev/null +++ b/src/crepe/util/color.h @@ -0,0 +1,41 @@ +#pragma once + +namespace crepe::util::color { + +constexpr const char * RESET = "\e[0m"; + +constexpr const char * FG_BLACK = "\e[30m"; +constexpr const char * FG_RED = "\e[31m"; +constexpr const char * FG_GREEN = "\e[32m"; +constexpr const char * FG_YELLOW = "\e[33m"; +constexpr const char * FG_BLUE = "\e[34m"; +constexpr const char * FG_MAGENTA = "\e[35m"; +constexpr const char * FG_CYAN = "\e[36m"; +constexpr const char * FG_WHITE = "\e[37m"; +constexpr const char * BG_BLACK = "\e[40m"; +constexpr const char * BG_RED = "\e[41m"; +constexpr const char * BG_GREEN = "\e[42m"; +constexpr const char * BG_YELLOW = "\e[43m"; +constexpr const char * BG_BLUE = "\e[44m"; +constexpr const char * BG_MAGENTA = "\e[45m"; +constexpr const char * BG_CYAN = "\e[46m"; +constexpr const char * BG_WHITE = "\e[47m"; + +constexpr const char * FG_BLACK_BRIGHT = "\e[90m"; +constexpr const char * FG_RED_BRIGHT = "\e[91m"; +constexpr const char * FG_GREEN_BRIGHT = "\e[92m"; +constexpr const char * FG_YELLOW_BRIGHT = "\e[93m"; +constexpr const char * FG_BLUE_BRIGHT = "\e[94m"; +constexpr const char * FG_MAGENTA_BRIGHT = "\e[95m"; +constexpr const char * FG_CYAN_BRIGHT = "\e[96m"; +constexpr const char * FG_WHITE_BRIGHT = "\e[97m"; +constexpr const char * BG_BLACK_BRIGHT = "\e[100m"; +constexpr const char * BG_RED_BRIGHT = "\e[101m"; +constexpr const char * BG_GREEN_BRIGHT = "\e[102m"; +constexpr const char * BG_YELLOW_BRIGHT = "\e[103m"; +constexpr const char * BG_BLUE_BRIGHT = "\e[104m"; +constexpr const char * BG_MAGENTA_BRIGHT = "\e[105m"; +constexpr const char * BG_CYAN_BRIGHT = "\e[106m"; +constexpr const char * BG_WHITE_BRIGHT = "\e[107m"; + +} // namespace crepe::util::color diff --git a/src/crepe/util/fmt.cpp b/src/crepe/util/fmt.cpp new file mode 100644 index 0000000..8ef1164 --- /dev/null +++ b/src/crepe/util/fmt.cpp @@ -0,0 +1,33 @@ +#include <cstdarg> +#include <cstdio> +#include <string> + +#include "fmt.h" + +using namespace std; + +string crepe::util::va_stringf(va_list args, const char * fmt) { +	va_list args_copy; +	va_copy(args_copy, args); + +	size_t sz = vsnprintf(NULL, 0, fmt, args_copy) + 1; +	char * msg = (char *) malloc(sz); +	va_end(args_copy); + +	vsnprintf(msg, sz, fmt, args); + +	string out = msg; +	free(msg); + +	va_end(args); + +	return out; +} + +string crepe::util::stringf(const char * fmt, ...) { +	va_list args; +	va_start(args, fmt); +	string out = va_stringf(args, fmt); +	va_end(args); +	return out; +} diff --git a/src/crepe/util/fmt.h b/src/crepe/util/fmt.h new file mode 100644 index 0000000..44c426f --- /dev/null +++ b/src/crepe/util/fmt.h @@ -0,0 +1,10 @@ +#pragma once + +#include <string> + +namespace crepe::util { + +std::string va_stringf(va_list args, const char * fmt); +std::string stringf(const char * fmt, ...); + +} // namespace crepe::util diff --git a/src/crepe/util/log.cpp b/src/crepe/util/log.cpp new file mode 100644 index 0000000..0c2ce1e --- /dev/null +++ b/src/crepe/util/log.cpp @@ -0,0 +1,40 @@ +#include <cstdarg> +#include <cstdio> +#include <cstdlib> +#include <string> + +#include "fmt.h" +#include "log.h" + +using namespace crepe::util; + +static const char * const LOG_PREFIX[] = { +	[log_level::DEBUG] = "[DBG] ", +	[log_level::INFO] = "[INFO] ", +	[log_level::WARNING] = "[WARN] ", +	[log_level::ERROR] = "[ERR] ", +}; + +static void log(enum log_level level, const std::string & msg) { +	using namespace std; +	string out = string(LOG_PREFIX[level]) + msg; +	if (!out.ends_with("\n")) out += "\n"; + +	// TODO: also log to file or smth +	printf("%s", out.c_str()); +	fflush(stdout); +} + +void crepe::util::logf(const char * fmt, ...) { +	va_list args; +	va_start(args, fmt); +	log(log_level::DEBUG, va_stringf(args, fmt)); +	va_end(args); +} + +void crepe::util::logf(log_level level, const char * fmt, ...) { +	va_list args; +	va_start(args, fmt); +	log(level, va_stringf(args, fmt)); +	va_end(args); +} diff --git a/src/crepe/util/log.h b/src/crepe/util/log.h new file mode 100644 index 0000000..3e36f0a --- /dev/null +++ b/src/crepe/util/log.h @@ -0,0 +1,36 @@ +#pragma once + +// allow user to disable debug macros +#ifndef CREPE_DISABLE_MACROS + +#include "color.h" + +// utility macros +#define _crepe_logf_here(fmt, ...) \ +	crepe::util::logf(util::log_level::DEBUG, "%s%s (%s:%d)%s" fmt "\n", \ +					  crepe::util::color::FG_WHITE, __PRETTY_FUNCTION__, \ +					  __FILE_NAME__, __LINE__, crepe::util::color::RESET, \ +					  __VA_ARGS__) + +// very illegal global function-style macros +// NOLINTBEGIN +#define dbg_logf(fmt, ...) _crepe_logf_here(": " fmt, __VA_ARGS__) +#define dbg_log(str) _crepe_logf_here("%s: " str, "") +#define dbg_trace() _crepe_logf_here("%s", "") +// NOLINTEND + +#endif + +namespace crepe::util { + +enum log_level { +	DEBUG, +	INFO, +	WARNING, +	ERROR, +}; + +void logf(const char * fmt, ...); +void logf(enum log_level level, const char * fmt, ...); + +} // namespace crepe::util diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt new file mode 100644 index 0000000..53f584f --- /dev/null +++ b/src/example/CMakeLists.txt @@ -0,0 +1,22 @@ +# all examples +add_custom_target(examples) + +# add_example(target_name [SOURCES...]) +function(add_example target_name) +	# if SOURCES is not specified +	if(NOT ARGV1) +		# A .cpp file with target_name exists, and should be used +		set(sources ${target_name}.cpp) +	else() +		set(sources ${ARGV}) +	endif() + +	add_executable(${target_name} EXCLUDE_FROM_ALL ${sources}) +	target_link_libraries(${target_name} PUBLIC crepe) +	add_dependencies(examples ${target_name}) +endfunction() + +add_example(audio_internal) +add_example(components_internal) +add_example(script) + diff --git a/src/example/audio_internal.cpp b/src/example/audio_internal.cpp new file mode 100644 index 0000000..1199e2d --- /dev/null +++ b/src/example/audio_internal.cpp @@ -0,0 +1,45 @@ +/** \file + *  + * Standalone example for usage of the internal \c Sound class. + */ + +#include <crepe/Sound.h> +#include <crepe/util/log.h> + +#include <chrono> +#include <thread> + +using namespace crepe; +using namespace std; +using namespace std::chrono_literals; +using std::make_unique; + +int main() { +	dbg_trace(); + +	auto bgm = Sound("../mwe/audio/bgm.ogg"); +	auto sfx1 = Sound("../mwe/audio/sfx1.wav"); +	auto sfx2 = Sound("../mwe/audio/sfx2.wav"); +	auto sfx3 = Sound("../mwe/audio/sfx3.wav"); + +	bgm.play(); + +	// play each sample sequentially +	this_thread::sleep_for(500ms); +	sfx1.play(); +	this_thread::sleep_for(500ms); +	sfx2.play(); +	bgm.pause(); +	this_thread::sleep_for(500ms); +	sfx3.play(); +	bgm.play(); +	this_thread::sleep_for(500ms); + +	// play all samples simultaniously +	sfx1.play(); +	sfx2.play(); +	sfx3.play(); +	this_thread::sleep_for(1000ms); + +	return 0; +} diff --git a/src/example/components_internal.cpp b/src/example/components_internal.cpp new file mode 100644 index 0000000..e2169e1 --- /dev/null +++ b/src/example/components_internal.cpp @@ -0,0 +1,63 @@ +/** \file + *  + * Standalone example for usage of the internal ECS + */ + +#include <cassert> +#include <chrono> + +#include <crepe/Component.h> +#include <crepe/ComponentManager.h> + +#include <crepe/api/Collider.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Rigidbody.h> +#include <crepe/api/Sprite.h> + +#include <crepe/util/log.h> + +using namespace crepe::api; +using namespace crepe; +using namespace std; + +#define OBJ_COUNT 100000 + +int main() { +	dbg_trace(); + +	auto & mgr = ComponentManager::get_instance(); + +	auto start_adding = chrono::high_resolution_clock::now(); + +	GameObject * game_object[OBJ_COUNT]; + +	for (int i = 0; i < OBJ_COUNT; ++i) { +		game_object[i] = new GameObject(i, "Name", "Tag", 0); + +		game_object[i]->add_component<Sprite>("test"); +		game_object[i]->add_component<Rigidbody>(0, 0, i); +		game_object[i]->add_component<Collider>(i); +	} + +	auto stop_adding = chrono::high_resolution_clock::now(); + +	auto sprites = mgr.get_components_by_type<Sprite>(); +	for (auto sprite : sprites) { +		assert(sprite.get().path == "test"); +	} + +	auto stop_looping = chrono::high_resolution_clock::now(); + +	for (int i = 0; i < OBJ_COUNT; ++i) { +		delete game_object[i]; +	} + +	auto add_time = chrono::duration_cast<chrono::microseconds>(stop_adding +																- start_adding); +	auto loop_time = chrono::duration_cast<chrono::microseconds>(stop_looping +																 - stop_adding); +	printf("add time:  %ldus\n", add_time.count()); +	printf("loop time: %ldus\n", loop_time.count()); + +	return 0; +} diff --git a/src/example/script.cpp b/src/example/script.cpp new file mode 100644 index 0000000..6e5563c --- /dev/null +++ b/src/example/script.cpp @@ -0,0 +1,32 @@ +/** \file + *  + * Standalone example for usage of the script component and system + */ + +#include <crepe/ComponentManager.h> +#include <crepe/ScriptSystem.h> +#include <crepe/util/log.h> + +#include <crepe/api/BehaviorScript.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Script.h> + +using namespace crepe; +using namespace crepe::api; +using namespace std; + +class MyScript : public Script { +	void update() { dbg_log("MY SCRIPT UPDATE"); } +}; + +int main() { +	dbg_trace(); + +	auto obj = GameObject(0, "name", "tag", 0); +	obj.add_component<BehaviorScript>().set_script<MyScript>(); + +	auto & sys = ScriptSystem::get_instance(); +	sys.update(); // -> MyScript::update + +	return 0; +} diff --git a/src/makefile b/src/makefile new file mode 100644 index 0000000..c1ef601 --- /dev/null +++ b/src/makefile @@ -0,0 +1,6 @@ +.PHONY: FORCE + +FMT += $(shell git ls-files '*.c' '*.cpp' '*.h' '*.hpp') +format: FORCE +	clang-tidy -p build/compile_commands.json --fix-errors $(FMT) + diff --git a/src/readme.md b/src/readme.md new file mode 100644 index 0000000..a8ffc51 --- /dev/null +++ b/src/readme.md @@ -0,0 +1,34 @@ +# engine source + +This folder contains the crêpe engine source files, unit tests, and some toy +examples. The only target built by default by the CMakeLists.txt in this folder +is the crêpe shared library object. + +Examples (using Ninja): + +``` +$ cmake -B build -G Ninja +$ ninja -C build +``` + +Unit tests can be built by explicitly specifying the target `test_main` when +running the build command: + +``` +$ ninja -C build test_main +``` + +Each source file in the example/ folder corresponds to a CMake target as well +(all examples can be built at once by specifying the `examples` target): + +``` +$ ninja -C build audio_internal components_internal +``` + +For installing crêpe system-wide after building (install must be run with +elevated privileges): + +``` +# ninja -C build install +``` + diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt new file mode 100644 index 0000000..0d316d6 --- /dev/null +++ b/src/test/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(test_main PUBLIC +	dummy.cpp +	# audio.cpp +) + diff --git a/src/test/audio.cpp b/src/test/audio.cpp new file mode 100644 index 0000000..d6ff689 --- /dev/null +++ b/src/test/audio.cpp @@ -0,0 +1,10 @@ +#include <gtest/gtest.h> + +using namespace std; +using namespace std::chrono_literals; + +// using namespace crepe; + +// TODO: mock internal audio class + +TEST(audio, play) { ASSERT_TRUE(true); } diff --git a/src/test/dummy.cpp b/src/test/dummy.cpp new file mode 100644 index 0000000..a00a9c6 --- /dev/null +++ b/src/test/dummy.cpp @@ -0,0 +1,3 @@ +#include <gtest/gtest.h> + +TEST(dummy, foo) { ASSERT_TRUE(1); } |