aboutsummaryrefslogtreecommitdiff
path: root/src/crepe/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/crepe/api')
-rw-r--r--src/crepe/api/AssetManager.cpp17
-rw-r--r--src/crepe/api/AssetManager.h35
-rw-r--r--src/crepe/api/AssetManager.hpp24
-rw-r--r--src/crepe/api/AudioSource.cpp5
-rw-r--r--src/crepe/api/AudioSource.h4
-rw-r--r--src/crepe/api/BehaviorScript.cpp10
-rw-r--r--src/crepe/api/BehaviorScript.h30
-rw-r--r--src/crepe/api/BehaviorScript.hpp22
-rw-r--r--src/crepe/api/CMakeLists.txt23
-rw-r--r--src/crepe/api/CircleCollider.h13
-rw-r--r--src/crepe/api/Color.cpp34
-rw-r--r--src/crepe/api/Color.h37
-rw-r--r--src/crepe/api/Config.h40
-rw-r--r--src/crepe/api/Force.cpp21
-rw-r--r--src/crepe/api/Force.h17
-rw-r--r--src/crepe/api/GameObject.cpp7
-rw-r--r--src/crepe/api/GameObject.h24
-rw-r--r--src/crepe/api/GameObject.hpp15
-rw-r--r--src/crepe/api/ParticleEmitter.cpp37
-rw-r--r--src/crepe/api/ParticleEmitter.h42
-rw-r--r--src/crepe/api/Point.h11
-rw-r--r--src/crepe/api/Rigidbody.cpp8
-rw-r--r--src/crepe/api/Rigidbody.h30
-rw-r--r--src/crepe/api/Script.cpp3
-rw-r--r--src/crepe/api/Script.h38
-rw-r--r--src/crepe/api/Script.hpp25
-rw-r--r--src/crepe/api/Sprite.cpp20
-rw-r--r--src/crepe/api/Sprite.h32
-rw-r--r--src/crepe/api/Texture.cpp32
-rw-r--r--src/crepe/api/Texture.h33
-rw-r--r--src/crepe/api/Transform.cpp15
-rw-r--r--src/crepe/api/Transform.h27
32 files changed, 710 insertions, 21 deletions
diff --git a/src/crepe/api/AssetManager.cpp b/src/crepe/api/AssetManager.cpp
new file mode 100644
index 0000000..560df6c
--- /dev/null
+++ b/src/crepe/api/AssetManager.cpp
@@ -0,0 +1,17 @@
+#include "util/log.h"
+
+#include "AssetManager.h"
+
+using namespace crepe::api;
+
+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
new file mode 100644
index 0000000..3e72a49
--- /dev/null
+++ b/src/crepe/api/AssetManager.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <any>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+namespace crepe::api {
+
+class AssetManager {
+
+private:
+ std::unordered_map<std::string, std::any> asset_cache;
+
+private:
+ AssetManager();
+ virtual ~AssetManager();
+
+public:
+ AssetManager(const AssetManager &) = delete;
+ AssetManager(AssetManager &&) = delete;
+ AssetManager & operator=(const AssetManager &) = delete;
+ AssetManager & operator=(AssetManager &&) = delete;
+
+ static AssetManager & get_instance();
+
+public:
+ template <typename asset>
+ std::shared_ptr<asset> cache(const std::string & file_path,
+ bool reload = false);
+};
+
+} // namespace crepe::api
+
+#include "AssetManager.hpp"
diff --git a/src/crepe/api/AssetManager.hpp b/src/crepe/api/AssetManager.hpp
new file mode 100644
index 0000000..468724c
--- /dev/null
+++ b/src/crepe/api/AssetManager.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "AssetManager.h"
+
+namespace crepe::api {
+
+template <typename asset>
+std::shared_ptr<asset> 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<std::shared_ptr<asset>>(it->second);
+ }
+
+ std::shared_ptr<asset> new_asset
+ = std::make_shared<asset>(file_path.c_str());
+
+ asset_cache[file_path] = new_asset;
+
+ return new_asset;
+}
+
+} // namespace crepe::api
diff --git a/src/crepe/api/AudioSource.cpp b/src/crepe/api/AudioSource.cpp
index b512d27..35b8d83 100644
--- a/src/crepe/api/AudioSource.cpp
+++ b/src/crepe/api/AudioSource.cpp
@@ -1,7 +1,8 @@
-#include "AudioSource.h"
+#include <memory>
#include "../Sound.h"
-#include <memory>
+
+#include "AudioSource.h"
using namespace crepe::api;
diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h
index 2d26cda..7980212 100644
--- a/src/crepe/api/AudioSource.h
+++ b/src/crepe/api/AudioSource.h
@@ -2,8 +2,8 @@
#include <memory>
-#include "Asset.h"
-#include "Component.h"
+#include "../Asset.h"
+#include "../Component.h"
namespace crepe {
class Sound;
diff --git a/src/crepe/api/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp
index 2dd933e..e69de29 100644
--- a/src/crepe/api/BehaviorScript.cpp
+++ b/src/crepe/api/BehaviorScript.cpp
@@ -1,10 +0,0 @@
-#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
index e9542c1..6133cc8 100644
--- a/src/crepe/api/BehaviorScript.h
+++ b/src/crepe/api/BehaviorScript.h
@@ -1,17 +1,35 @@
#pragma once
-#include "../Script.h"
+#include <memory>
+
#include "../Component.h"
+namespace crepe {
+class ScriptSystem;
+class ComponentManager;
+} // namespace crepe
+
namespace crepe::api {
-class BehaviorScript : public Script, public Component {
- // only allow ComponentManager to instantiate scripts
- friend class ComponentManager;
+class Script;
+
+class BehaviorScript : public Component {
+protected:
+ friend class crepe::ComponentManager;
+ using Component::Component;
+
+public:
+ virtual ~BehaviorScript() = default;
+
+public:
+ template <class T>
+ BehaviorScript & set_script();
protected:
- BehaviorScript();
+ 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..2a3502f
--- /dev/null
+++ b/src/crepe/api/BehaviorScript.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <type_traits>
+
+#include "../util/log.h"
+
+#include "BehaviorScript.h"
+#include "Script.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();
+ s->parent = this;
+ 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
index 86623de..0bb1263 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -1,10 +1,31 @@
target_sources(crepe PUBLIC
# AudioSource.cpp
BehaviorScript.cpp
+ Script.cpp
+ GameObject.cpp
+ Rigidbody.cpp
+ Force.cpp
+ ParticleEmitter.cpp
+ Transform.cpp
+ Color.cpp
+ Texture.cpp
+ AssetManager.cpp
+ Sprite.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
# AudioSource.h
BehaviorScript.h
+ Config.h
+ Script.h
+ Script.hpp
+ GameObject.h
+ GameObject.hpp
+ Rigidbody.h
+ Sprite.h
+ Point.h
+ Color.h
+ Texture.h
+ AssetManager.h
+ AssetManager.hpp
)
-
diff --git a/src/crepe/api/CircleCollider.h b/src/crepe/api/CircleCollider.h
new file mode 100644
index 0000000..762574b
--- /dev/null
+++ b/src/crepe/api/CircleCollider.h
@@ -0,0 +1,13 @@
+#pragma once
+#include "../Collider.h"
+
+namespace crepe::api {
+
+class CircleCollider : public Collider {
+public:
+ CircleCollider(uint32_t game_object_id, int radius)
+ : Collider(game_object_id), radius(radius) {}
+ int radius;
+};
+
+} // namespace crepe::api
diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp
new file mode 100644
index 0000000..fb5bd1a
--- /dev/null
+++ b/src/crepe/api/Color.cpp
@@ -0,0 +1,34 @@
+#include "Color.h"
+
+using namespace crepe::api;
+
+Color Color::white = Color(255, 255, 255, 0);
+Color Color::red = Color(255, 0, 0, 0);
+Color Color::green = Color(0, 255, 0, 0);
+Color Color::blue = Color(0, 0, 255, 0);
+Color Color::black = Color(0, 0, 0, 0);
+Color Color::cyan = Color(0, 255, 255, 0);
+Color Color::yellow = Color(255, 255, 0, 0);
+Color Color::magenta = Color(255, 0, 255, 0);
+
+// FIXME: do we really need double precision for color values?
+Color::Color(double red, double green, double blue, double alpha) {
+ this->a = alpha;
+ this->r = red;
+ this->g = green;
+ this->b = blue;
+};
+
+const Color & Color::get_white() { return Color::white; };
+
+const Color & Color::get_red() { return Color::red; };
+const Color & Color::get_green() { return Color::green; };
+const Color & Color::get_blue() { return Color::blue; };
+
+const Color & Color::get_black() { return Color::black; };
+
+const Color & Color::get_cyan() { return Color::cyan; };
+
+const Color & Color::get_yellow() { return Color::yellow; };
+
+const Color & Color::get_magenta() { return Color::magenta; };
diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h
new file mode 100644
index 0000000..e818de4
--- /dev/null
+++ b/src/crepe/api/Color.h
@@ -0,0 +1,37 @@
+#pragma once
+
+namespace crepe::api {
+
+class Color {
+
+ // FIXME: can't these colors be defined as a `static constexpr const Color`
+ // instead?
+
+public:
+ Color(double red, double green, double blue, double alpha);
+ static const Color & get_white();
+ static const Color & get_red();
+ static const Color & get_green();
+ static const Color & get_blue();
+ static const Color & get_cyan();
+ static const Color & get_magenta();
+ static const Color & get_yellow();
+ static const Color & get_black();
+
+private:
+ double r;
+ double g;
+ double b;
+ double a;
+
+ static Color white;
+ static Color red;
+ static Color green;
+ static Color blue;
+ static Color cyan;
+ static Color magenta;
+ static Color yellow;
+ static Color black;
+};
+
+} // namespace crepe::api
diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h
new file mode 100644
index 0000000..8a7f268
--- /dev/null
+++ b/src/crepe/api/Config.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "../util/log.h"
+
+namespace crepe::api {
+
+class Config {
+private:
+ Config() = default;
+
+public:
+ ~Config() = default;
+
+public:
+ //! Retrieve handle to global Config instance
+ static Config & get_instance() {
+ static Config instance;
+ return instance;
+ }
+
+public:
+ //! Logging-related settings
+ struct {
+ /**
+ * \brief Log level
+ *
+ * Only messages with equal or higher priority than this value will be
+ * logged.
+ */
+ util::LogLevel level = util::LogLevel::INFO;
+ /**
+ * \brief Colored log output
+ *
+ * Enables log coloring using ANSI escape codes.
+ */
+ bool color = true;
+ } log;
+};
+
+} // namespace crepe::api
diff --git a/src/crepe/api/Force.cpp b/src/crepe/api/Force.cpp
new file mode 100644
index 0000000..e359adc
--- /dev/null
+++ b/src/crepe/api/Force.cpp
@@ -0,0 +1,21 @@
+#include <cmath>
+
+#include "Force.h"
+
+namespace crepe::api {
+
+Force::Force(uint32_t game_object_id, uint32_t magnitude, uint32_t direction)
+ : Component(game_object_id) {
+ // TODO: A standard angle unit should be established for the entire engine
+ // and assumed to be the default everywhere. Only conversion functions should
+ // explicitly contain the unit (i.e. `deg_to_rad()` & `rad_to_deg()`)
+
+ // Convert direction from degrees to radians
+ float radian_direction = static_cast<float>(direction) * (M_PI / 180.0f);
+ force_x = static_cast<int32_t>(
+ std::round(magnitude * std::cos(radian_direction)));
+ force_y = static_cast<int32_t>(
+ std::round(magnitude * std::sin(radian_direction)));
+}
+
+} // namespace crepe::api
diff --git a/src/crepe/api/Force.h b/src/crepe/api/Force.h
new file mode 100644
index 0000000..8da9a00
--- /dev/null
+++ b/src/crepe/api/Force.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <cstdint>
+
+#include "../Component.h"
+
+namespace crepe::api {
+
+class Force : public Component {
+public:
+ Force(uint32_t game_object_id, uint32_t magnitude, uint32_t direction);
+
+ int32_t force_x;
+ int32_t force_y;
+};
+
+} // 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..3c7e867
--- /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>(this->id, std::forward<Args>(args)...);
+}
+
+} // namespace crepe::api
diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp
new file mode 100644
index 0000000..0b3a9ee
--- /dev/null
+++ b/src/crepe/api/ParticleEmitter.cpp
@@ -0,0 +1,37 @@
+#include <ctime>
+#include <iostream>
+
+#include "Particle.h"
+#include "ParticleEmitter.h"
+
+using namespace crepe;
+
+ParticleEmitter::ParticleEmitter(uint32_t game_object_id,
+ uint32_t max_particles, uint32_t emission_rate,
+ uint32_t speed, uint32_t speed_offset,
+ uint32_t angle, uint32_t angleOffset,
+ float begin_lifespan, float end_lifespan)
+ : Component(game_object_id), max_particles(max_particles),
+ emission_rate(emission_rate), speed(speed), speed_offset(speed_offset),
+ position{0, 0}, begin_lifespan(begin_lifespan),
+ end_lifespan(end_lifespan) {
+ std::srand(
+ static_cast<uint32_t>(std::time(nullptr))); // initialize random seed
+ std::cout << "Create emitter" << std::endl;
+ // FIXME: Why do these expressions start with `360 +`, only to be `% 360`'d
+ // right after? This does not make any sense to me.
+ min_angle = (360 + angle - (angleOffset % 360)) % 360;
+ max_angle = (360 + angle + (angleOffset % 360)) % 360;
+ position.x = 400; // FIXME: what are these magic values?
+ position.y = 400;
+ for (size_t i = 0; i < max_particles; i++) {
+ this->particles.emplace_back();
+ }
+}
+
+ParticleEmitter::~ParticleEmitter() {
+ std::vector<Particle>::iterator it = this->particles.begin();
+ while (it != this->particles.end()) {
+ it = this->particles.erase(it);
+ }
+}
diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h
new file mode 100644
index 0000000..2e2e95b
--- /dev/null
+++ b/src/crepe/api/ParticleEmitter.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#include "Component.h"
+#include "Particle.h"
+
+namespace crepe {
+
+class ParticleEmitter : public Component {
+public:
+ ParticleEmitter(uint32_t game_object_id, uint32_t max_particles,
+ uint32_t emission_rate, uint32_t speed,
+ uint32_t speed_offset, uint32_t angle, uint32_t angleOffset,
+ float begin_lifespan, float end_lifespan);
+ ~ParticleEmitter();
+
+ //! position of the emitter
+ Position position;
+ //! maximum number of particles
+ uint32_t max_particles;
+ //! rate of particle emission
+ uint32_t emission_rate;
+ //! base speed of the particles
+ uint32_t speed;
+ //! offset for random speed variation
+ uint32_t speed_offset;
+ //! min angle of particle emission
+ uint32_t min_angle;
+ //! max angle of particle emission
+ uint32_t max_angle;
+ //! begin Lifespan of particle (only visual)
+ float begin_lifespan;
+ //! begin Lifespan of particle
+ float end_lifespan;
+
+ //! collection of particles
+ std::vector<Particle> particles;
+};
+
+} // namespace crepe
diff --git a/src/crepe/api/Point.h b/src/crepe/api/Point.h
new file mode 100644
index 0000000..b47b7e6
--- /dev/null
+++ b/src/crepe/api/Point.h
@@ -0,0 +1,11 @@
+#pragma once
+
+namespace crepe::api {
+
+class Point {
+public:
+ double x;
+ double y;
+};
+
+} // namespace crepe::api
diff --git a/src/crepe/api/Rigidbody.cpp b/src/crepe/api/Rigidbody.cpp
new file mode 100644
index 0000000..ebf9fb9
--- /dev/null
+++ b/src/crepe/api/Rigidbody.cpp
@@ -0,0 +1,8 @@
+#include "Rigidbody.h"
+
+using namespace crepe::api;
+
+Rigidbody::Rigidbody(uint32_t game_object_id, int mass, int gravity_scale,
+ BodyType bodyType)
+ : Component(game_object_id), mass(mass), gravity_scale(gravity_scale),
+ body_type(bodyType) {}
diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h
new file mode 100644
index 0000000..6079a76
--- /dev/null
+++ b/src/crepe/api/Rigidbody.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <cstdint>
+
+#include "../Component.h"
+
+namespace crepe::api {
+
+// FIXME: can't this enum be defined inside the class declaration of Rigidbody?
+enum class BodyType {
+ //! Does not move (e.g. walls, ground ...)
+ STATIC,
+ //! Moves and responds to forces (e.g. player, physics objects ...)
+ DYNAMIC,
+ //! Moves but does not respond to forces (e.g. moving platforms ...)
+ KINEMATIC,
+};
+
+class Rigidbody : public Component {
+public:
+ Rigidbody(uint32_t game_object_id, int mass, int gravity_scale,
+ BodyType body_type);
+ int32_t velocity_x;
+ int32_t velocity_y;
+ int mass;
+ int gravity_scale;
+ BodyType 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..59e6ec0
--- /dev/null
+++ b/src/crepe/api/Script.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <vector>
+
+namespace crepe {
+class ScriptSystem;
+}
+
+namespace crepe::api {
+
+class BehaviorScript;
+
+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.
+
+protected:
+ template <typename T>
+ T & get_component();
+
+ template <typename T>
+ std::vector<std::reference_wrapper<T>> get_components();
+
+private:
+ friend class crepe::api::BehaviorScript;
+ BehaviorScript * parent = nullptr;
+};
+
+} // namespace crepe::api
+
+#include "Script.hpp"
diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp
new file mode 100644
index 0000000..8004fe3
--- /dev/null
+++ b/src/crepe/api/Script.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "../ComponentManager.h"
+
+#include "BehaviorScript.h"
+#include "Script.h"
+
+namespace crepe::api {
+
+template <typename T>
+T & Script::get_component() {
+ std::vector<std::reference_wrapper<T>> all_components
+ = this->get_components<T>();
+ if (all_components.size() < 1) throw nullptr; // TODO
+
+ return all_components.back().get();
+}
+
+template <typename T>
+std::vector<std::reference_wrapper<T>> Script::get_components() {
+ ComponentManager & mgr = ComponentManager::get_instance();
+ return mgr.get_components_by_id<T>(this->parent->game_object_id);
+}
+
+} // namespace crepe::api
diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp
new file mode 100644
index 0000000..806f147
--- /dev/null
+++ b/src/crepe/api/Sprite.cpp
@@ -0,0 +1,20 @@
+#include <cstdint>
+#include <memory>
+
+#include "api/Texture.h"
+#include "util/log.h"
+
+#include "Component.h"
+#include "Sprite.h"
+
+using namespace std;
+using namespace crepe;
+using namespace crepe::api;
+
+Sprite::Sprite(uint32_t id, shared_ptr<Texture> image, const Color & color,
+ const FlipSettings & flip)
+ : Component(id), color(color), flip(flip), sprite_image(image) {
+ dbg_trace();
+}
+
+Sprite::~Sprite() { dbg_trace(); }
diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h
new file mode 100644
index 0000000..b06125e
--- /dev/null
+++ b/src/crepe/api/Sprite.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <SDL2/SDL_rect.h>
+#include <cstdint>
+#include <memory>
+
+#include "api/Color.h"
+#include "api/Texture.h"
+
+#include "Component.h"
+
+namespace crepe::api {
+
+struct FlipSettings {
+ bool flip_x = 1;
+ bool flip_y = 1;
+};
+
+class Sprite : public Component {
+
+public:
+ Sprite(uint32_t game_id, std::shared_ptr<Texture> image,
+ const Color & color, const FlipSettings & flip);
+ ~Sprite();
+ std::shared_ptr<Texture> sprite_image;
+ Color color;
+ FlipSettings flip;
+ uint8_t sorting_in_layer;
+ uint8_t order_in_layer;
+};
+
+} // namespace crepe::api
diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp
new file mode 100644
index 0000000..481ef7c
--- /dev/null
+++ b/src/crepe/api/Texture.cpp
@@ -0,0 +1,32 @@
+#include <SDL2/SDL_render.h>
+
+#include "util/log.h"
+
+#include "Asset.h"
+#include "SDLContext.h"
+#include "Texture.h"
+
+using namespace crepe::api;
+using namespace std;
+
+Texture::Texture(unique_ptr<Asset> res) {
+ dbg_trace();
+ this->load(std::move(res));
+}
+
+Texture::Texture(const char * src) {
+ dbg_trace();
+ this->load(make_unique<Asset>(src));
+}
+
+Texture::~Texture() {
+ dbg_trace();
+ if (this->texture != nullptr) {
+ SDL_DestroyTexture(this->texture);
+ }
+}
+
+void Texture::load(unique_ptr<Asset> res) {
+ SDLContext & ctx = SDLContext::get_instance();
+ this->texture = ctx.texture_from_path(res->canonical());
+}
diff --git a/src/crepe/api/Texture.h b/src/crepe/api/Texture.h
new file mode 100644
index 0000000..f8481e3
--- /dev/null
+++ b/src/crepe/api/Texture.h
@@ -0,0 +1,33 @@
+#pragma once
+
+// FIXME: this header can't be included because this is an API header, and SDL2
+// development headers won't be bundled with crepe. Why is this facade in the
+// API namespace?
+#include <SDL2/SDL_render.h>
+#include <memory>
+
+#include "Asset.h"
+
+namespace crepe {
+class SDLContext;
+}
+
+namespace crepe::api {
+
+class Texture {
+
+public:
+ Texture(const char * src);
+ Texture(std::unique_ptr<Asset> res);
+ ~Texture();
+
+private:
+ void load(std::unique_ptr<Asset> res);
+
+private:
+ SDL_Texture * texture = nullptr;
+
+ friend class crepe::SDLContext;
+};
+
+} // namespace crepe::api
diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp
new file mode 100644
index 0000000..3b218bc
--- /dev/null
+++ b/src/crepe/api/Transform.cpp
@@ -0,0 +1,15 @@
+#include <cstdint>
+
+#include "api/Point.h"
+#include "util/log.h"
+
+#include "Component.h"
+#include "Transform.h"
+
+using namespace crepe::api;
+
+Transform::Transform(uint32_t game_id, const Point & point, double rot, double scale)
+ : Component(game_id), position(point), rotation(rot), scale(scale) {
+ dbg_trace();
+}
+Transform::~Transform() { dbg_trace(); }
diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h
new file mode 100644
index 0000000..c451c16
--- /dev/null
+++ b/src/crepe/api/Transform.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <cstdint>
+
+#include "api/Point.h"
+
+#include "Component.h"
+
+namespace crepe::api {
+
+class Transform : public Component {
+ // FIXME: What's the difference between the `Point` and `Position`
+ // classes/structs? How about we replace both with a universal `Vec2` that
+ // works similar (or the same) as those found in GLSL?
+
+public:
+ Transform(uint32_t id, const Point &, double, double);
+ ~Transform();
+ //! Translation (shift)
+ Point position;
+ //! Rotation, in radians
+ double rotation;
+ //! Multiplication factor
+ double scale;
+};
+
+} // namespace crepe::api