aboutsummaryrefslogtreecommitdiff
path: root/src/crepe/facade
diff options
context:
space:
mode:
Diffstat (limited to 'src/crepe/facade')
-rw-r--r--src/crepe/facade/CMakeLists.txt6
-rw-r--r--src/crepe/facade/DB.cpp30
-rw-r--r--src/crepe/facade/DB.h17
-rw-r--r--src/crepe/facade/EventData.h54
-rw-r--r--src/crepe/facade/Font.cpp21
-rw-r--r--src/crepe/facade/Font.h42
-rw-r--r--src/crepe/facade/FontFacade.cpp43
-rw-r--r--src/crepe/facade/FontFacade.h34
-rw-r--r--src/crepe/facade/SDLContext.cpp457
-rw-r--r--src/crepe/facade/SDLContext.h360
-rw-r--r--src/crepe/facade/Sound.cpp48
-rw-r--r--src/crepe/facade/Sound.h71
-rw-r--r--src/crepe/facade/SoundContext.cpp25
-rw-r--r--src/crepe/facade/SoundContext.h58
-rw-r--r--src/crepe/facade/SoundHandle.h12
-rw-r--r--src/crepe/facade/Texture.cpp28
-rw-r--r--src/crepe/facade/Texture.h69
17 files changed, 1041 insertions, 334 deletions
diff --git a/src/crepe/facade/CMakeLists.txt b/src/crepe/facade/CMakeLists.txt
index 4cc53bc..243ae46 100644
--- a/src/crepe/facade/CMakeLists.txt
+++ b/src/crepe/facade/CMakeLists.txt
@@ -1,14 +1,20 @@
target_sources(crepe PUBLIC
Sound.cpp
+ Texture.cpp
SoundContext.cpp
SDLContext.cpp
DB.cpp
+ FontFacade.cpp
+ Font.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
Sound.h
+ Texture.h
SoundContext.h
SDLContext.h
DB.h
+ FontFacade.h
+ Font.h
)
diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp
index 80047a6..ae2d4bc 100644
--- a/src/crepe/facade/DB.cpp
+++ b/src/crepe/facade/DB.cpp
@@ -1,6 +1,5 @@
#include <cstring>
-#include "Exception.h"
#include "util/Log.h"
#include "DB.h"
@@ -15,19 +14,13 @@ DB::DB(const string & path) {
// init database struct
libdb::DB * db;
if ((ret = libdb::db_create(&db, NULL, 0)) != 0)
- throw Exception("db_create: {}", libdb::db_strerror(ret));
+ throw runtime_error(format("db_create: {}", libdb::db_strerror(ret)));
this->db = {db, [](libdb::DB * db) { db->close(db, 0); }};
// load or create database file
- ret = this->db->open(this->db.get(), NULL, path.c_str(), NULL,
- libdb::DB_BTREE, DB_CREATE, 0);
- if (ret != 0) throw Exception("db->open: {}", libdb::db_strerror(ret));
-
- // create cursor
- libdb::DBC * cursor;
- ret = this->db->cursor(this->db.get(), NULL, &cursor, 0);
- if (ret != 0) throw Exception("db->cursor: {}", libdb::db_strerror(ret));
- this->cursor = {cursor, [](libdb::DBC * cursor) { cursor->close(cursor); }};
+ const char * file = path.empty() ? NULL : path.c_str();
+ ret = this->db->open(this->db.get(), NULL, file, NULL, libdb::DB_BTREE, DB_CREATE, 0);
+ if (ret != 0) throw runtime_error(format("db->open: {}", libdb::db_strerror(ret)));
}
libdb::DBT DB::to_thing(const string & thing) const noexcept {
@@ -43,22 +36,25 @@ string DB::get(const string & key) {
libdb::DBT db_val;
memset(&db_val, 0, sizeof(libdb::DBT));
- int ret = this->cursor->get(this->cursor.get(), &db_key, &db_val, DB_FIRST);
- if (ret != 0) throw Exception("cursor->get: {}", libdb::db_strerror(ret));
- return {static_cast<char *>(db_val.data), db_val.size};
+ int ret = this->db->get(this->db.get(), NULL, &db_key, &db_val, 0);
+ if (ret == 0) return {static_cast<char *>(db_val.data), db_val.size};
+
+ string err = format("db->get: {}", libdb::db_strerror(ret));
+ if (ret == DB_NOTFOUND) throw out_of_range(err);
+ else throw runtime_error(err);
}
void DB::set(const string & key, const string & value) {
libdb::DBT db_key = this->to_thing(key);
libdb::DBT db_val = this->to_thing(value);
int ret = this->db->put(this->db.get(), NULL, &db_key, &db_val, 0);
- if (ret != 0) throw Exception("cursor->get: {}", libdb::db_strerror(ret));
+ if (ret != 0) throw runtime_error(format("db->get: {}", libdb::db_strerror(ret)));
}
-bool DB::has(const std::string & key) noexcept {
+bool DB::has(const std::string & key) {
try {
this->get(key);
- } catch (...) {
+ } catch (std::out_of_range &) {
return false;
}
return true;
diff --git a/src/crepe/facade/DB.h b/src/crepe/facade/DB.h
index 7c757a2..84cdf19 100644
--- a/src/crepe/facade/DB.h
+++ b/src/crepe/facade/DB.h
@@ -15,15 +15,17 @@ namespace crepe {
/**
* \brief Berkeley DB facade
*
- * Berkeley DB is a simple key-value database that stores arbitrary data as
- * both key and value. This facade uses STL strings as keys/values.
+ * Berkeley DB is a simple key-value database that stores arbitrary data as both key and value.
+ * This facade uses STL strings as keys/values.
*/
class DB {
public:
/**
* \param path The path of the database (created if nonexistant)
+ *
+ * \note If \p path is empty, the database is entirely in-memory
*/
- DB(const std::string & path);
+ DB(const std::string & path = "");
virtual ~DB() = default;
public:
@@ -34,7 +36,8 @@ public:
*
* \return The value
*
- * \throws Exception if value is not found in DB or other error occurs
+ * \throws std::out_of_range if value is not found in DB
+ * \throws std::runtime_error if other error occurs
*/
std::string get(const std::string & key);
/**
@@ -43,7 +46,7 @@ public:
* \param key The value key
* \param value The value to store
*
- * \throws Exception if an error occurs
+ * \throws std::runtime_error if an error occurs
*/
void set(const std::string & key, const std::string & value);
/**
@@ -53,13 +56,11 @@ public:
*
* \returns True if the key exists, or false if it does not
*/
- bool has(const std::string & key) noexcept;
+ bool has(const std::string & key);
private:
//! RAII wrapper around \c DB struct
std::unique_ptr<libdb::DB, std::function<void(libdb::DB *)>> db;
- //! RAII wrapper around \c DBC struct
- std::unique_ptr<libdb::DBC, std::function<void(libdb::DBC *)>> cursor;
private:
/**
diff --git a/src/crepe/facade/EventData.h b/src/crepe/facade/EventData.h
new file mode 100644
index 0000000..a7526b4
--- /dev/null
+++ b/src/crepe/facade/EventData.h
@@ -0,0 +1,54 @@
+#pragma once
+#include "../api/KeyCodes.h"
+#include "../types.h"
+namespace crepe {
+//! EventType enum for passing eventType
+enum EventType {
+ NONE = 0,
+ MOUSE_DOWN,
+ MOUSE_UP,
+ MOUSE_MOVE,
+ MOUSE_WHEEL,
+ KEY_UP,
+ KEY_DOWN,
+ SHUTDOWN,
+ WINDOW_MINIMIZE,
+ WINDOW_MAXIMIZE,
+ WINDOW_FOCUS_GAIN,
+ WINDOW_FOCUS_LOST,
+ WINDOW_MOVE,
+ WINDOW_RESIZE,
+ WINDOW_EXPOSE,
+};
+
+//! Struct for storing key data.
+struct KeyData {
+ Keycode key = Keycode::NONE;
+ bool key_repeat = false;
+};
+
+//! Struct for storing mouse data.
+struct MouseData {
+ MouseButton mouse_button = MouseButton::NONE;
+ ivec2 mouse_position = {-1, -1};
+ int scroll_direction = -1;
+ float scroll_delta = INFINITY;
+ ivec2 rel_mouse_move = {-1, -1};
+};
+
+//! Struct for storing window data.
+struct WindowData {
+ ivec2 move_delta;
+ ivec2 resize_dimension;
+};
+
+//! EventData struct for passing event data from facade
+struct EventData {
+ EventType event_type = EventType::NONE;
+ union {
+ KeyData key_data;
+ MouseData mouse_data;
+ WindowData window_data;
+ } data;
+};
+} // namespace crepe
diff --git a/src/crepe/facade/Font.cpp b/src/crepe/facade/Font.cpp
new file mode 100644
index 0000000..771002f
--- /dev/null
+++ b/src/crepe/facade/Font.cpp
@@ -0,0 +1,21 @@
+#include <SDL2/SDL_ttf.h>
+
+#include "../api/Asset.h"
+#include "../api/Config.h"
+
+#include "Font.h"
+
+using namespace std;
+using namespace crepe;
+
+Font::Font(const Asset & src, Mediator & mediator) : Resource(src, mediator) {
+ const Config & config = Config::get_instance();
+ const std::string FONT_PATH = src.get_path();
+ TTF_Font * loaded_font = TTF_OpenFont(FONT_PATH.c_str(), config.font.size);
+ if (loaded_font == NULL) {
+ throw runtime_error(format("Font: {} (path: {})", TTF_GetError(), FONT_PATH));
+ }
+ this->font = {loaded_font, [](TTF_Font * close_font) { TTF_CloseFont(close_font); }};
+}
+
+TTF_Font * Font::get_font() const { return this->font.get(); }
diff --git a/src/crepe/facade/Font.h b/src/crepe/facade/Font.h
new file mode 100644
index 0000000..b208d96
--- /dev/null
+++ b/src/crepe/facade/Font.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <SDL2/SDL_ttf.h>
+#include <memory>
+
+#include "../Resource.h"
+#include "../api/Config.h"
+
+namespace crepe {
+
+class Asset;
+/**
+ * \brief Resource for managing font creation and destruction
+ *
+ * This class is a wrapper around an SDL_ttf font instance, encapsulating font loading and usage.
+ * It loads a font from an Asset and manages its lifecycle. The font is automatically unloaded
+ * when this object is destroyed.
+ */
+class Font : public Resource {
+
+public:
+ /**
+ * \param src The Asset containing the font file path and metadata to load the font.
+ * \param mediator The Mediator object used for managing the SDL context or related systems.
+ */
+ Font(const Asset & src, Mediator & mediator);
+ /**
+ * \brief Gets the underlying TTF_Font resource.
+ *
+ * This function returns the raw pointer to the SDL_ttf TTF_Font object that represents
+ * the loaded font. This can be used with SDL_ttf functions to render text.
+ *
+ * \return The raw TTF_Font object wrapped in a unique pointer.
+ */
+ TTF_Font * get_font() const;
+
+private:
+ //! The SDL_ttf font object with custom deleter.
+ std::unique_ptr<TTF_Font, std::function<void(TTF_Font *)>> font = nullptr;
+};
+
+} // namespace crepe
diff --git a/src/crepe/facade/FontFacade.cpp b/src/crepe/facade/FontFacade.cpp
new file mode 100644
index 0000000..87f95ab
--- /dev/null
+++ b/src/crepe/facade/FontFacade.cpp
@@ -0,0 +1,43 @@
+#include <fontconfig/fontconfig.h>
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+#include "FontFacade.h"
+
+using namespace std;
+using namespace crepe;
+
+FontFacade::FontFacade() {
+ if (!FcInit()) throw runtime_error("Failed to initialize Fontconfig.");
+}
+
+FontFacade::~FontFacade() { FcFini(); }
+
+Asset FontFacade::get_font_asset(const string & font_family) {
+ FcPattern * raw_pattern
+ = FcNameParse(reinterpret_cast<const FcChar8 *>(font_family.c_str()));
+ if (raw_pattern == NULL) throw runtime_error("Failed to create font pattern.");
+
+ unique_ptr<FcPattern, function<void(FcPattern *)>> pattern{
+ raw_pattern, [](FcPattern * p) { FcPatternDestroy(p); }};
+
+ FcConfig * config = FcConfigGetCurrent();
+ if (config == NULL) throw runtime_error("Failed to get current Fontconfig configuration.");
+
+ FcResult result;
+ FcPattern * raw_matched_pattern = FcFontMatch(config, pattern.get(), &result);
+ if (raw_matched_pattern == NULL) throw runtime_error("No matching font found.");
+
+ unique_ptr<FcPattern, function<void(FcPattern *)>> matched_pattern
+ = {raw_matched_pattern, [](FcPattern * p) { FcPatternDestroy(p); }};
+
+ FcChar8 * file_path = nullptr;
+ FcResult res = FcPatternGetString(matched_pattern.get(), FC_FILE, 0, &file_path);
+ if (res != FcResultMatch || file_path == NULL)
+ throw runtime_error("Failed to get font file path.");
+
+ string font_file_path = reinterpret_cast<const char *>(file_path);
+ return Asset(font_file_path);
+}
diff --git a/src/crepe/facade/FontFacade.h b/src/crepe/facade/FontFacade.h
new file mode 100644
index 0000000..9761070
--- /dev/null
+++ b/src/crepe/facade/FontFacade.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <memory>
+
+#include "../api/Asset.h"
+
+namespace crepe {
+
+/**
+ *
+ * \brief Font facade class for converting font family names to absolute file paths
+ *
+ */
+class FontFacade {
+public:
+ FontFacade();
+ ~FontFacade();
+ FontFacade(const FontFacade & other) = delete;
+ FontFacade & operator=(const FontFacade & other) = delete;
+ FontFacade(FontFacade && other) noexcept = delete;
+ FontFacade & operator=(FontFacade && other) noexcept = delete;
+ /**
+ *
+ * \brief Facade function to convert a font_family into an asset.
+ *
+ * This function uses the FontConfig library to convert a font family name (Arial, Inter, Helvetica) and converts it to the font source path.
+ * This function returns a default font path if the font_family name doesnt exist or cant be found
+ * \param font_family Name of the font family name.
+ * \return Asset with filepath to the corresponding font.
+ */
+ Asset get_font_asset(const std::string & font_family);
+};
+
+} // namespace crepe
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index 5527803..fffbe34 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -1,74 +1,69 @@
#include <SDL2/SDL.h>
+#include <SDL2/SDL_blendmode.h>
#include <SDL2/SDL_image.h>
+#include <SDL2/SDL_keycode.h>
+#include <SDL2/SDL_pixels.h>
#include <SDL2/SDL_rect.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_surface.h>
+#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL_video.h>
+#include <array>
#include <cmath>
#include <cstddef>
+#include <cstdint>
#include <functional>
-#include <iostream>
#include <memory>
-#include <string>
-#include <utility>
+#include <stdexcept>
+#include "../api/Camera.h"
+#include "../api/Color.h"
+#include "../api/Config.h"
#include "../api/Sprite.h"
-#include "../api/Texture.h"
-#include "../api/Transform.h"
#include "../util/Log.h"
-#include "Exception.h"
+#include "manager/Mediator.h"
#include "SDLContext.h"
+#include "Texture.h"
+#include "types.h"
using namespace crepe;
+using namespace std;
-SDLContext & SDLContext::get_instance() {
- static SDLContext instance;
- return instance;
-}
-
-SDLContext::SDLContext() {
+SDLContext::SDLContext(Mediator & mediator) {
dbg_trace();
- // FIXME: read window defaults from config manager
-
- if (SDL_Init(SDL_INIT_VIDEO) < 0) {
- // FIXME: throw exception
- std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError()
- << std::endl;
- return;
+ if (TTF_Init() == -1) {
+ throw runtime_error(format("SDL_ttf initialization failed: {}", TTF_GetError()));
}
- SDL_Window * tmp_window = SDL_CreateWindow(
- "Crepe Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
- this->viewport.w, this->viewport.h, 0);
+ if (SDL_Init(SDL_INIT_VIDEO) != 0) {
+ throw runtime_error(format("SDLContext: SDL_Init error: {}", SDL_GetError()));
+ }
+
+ auto & cfg = Config::get_instance().window_settings;
+ SDL_Window * tmp_window
+ = SDL_CreateWindow(cfg.window_title.c_str(), SDL_WINDOWPOS_CENTERED,
+ SDL_WINDOWPOS_CENTERED, cfg.default_size.x, cfg.default_size.y, 0);
if (!tmp_window) {
- // FIXME: throw exception
- std::cerr << "Window could not be created! SDL_Error: "
- << SDL_GetError() << std::endl;
- return;
+ throw runtime_error(format("SDLContext: SDL_Window error: {}", SDL_GetError()));
}
- this->game_window
- = {tmp_window, [](SDL_Window * window) { SDL_DestroyWindow(window); }};
+ this->game_window = {tmp_window, [](SDL_Window * window) { SDL_DestroyWindow(window); }};
- SDL_Renderer * tmp_renderer = SDL_CreateRenderer(
- this->game_window.get(), -1, SDL_RENDERER_ACCELERATED);
+ SDL_Renderer * tmp_renderer
+ = SDL_CreateRenderer(this->game_window.get(), -1, SDL_RENDERER_ACCELERATED);
if (!tmp_renderer) {
- // FIXME: throw exception
- std::cerr << "Renderer could not be created! SDL_Error: "
- << SDL_GetError() << std::endl;
- SDL_DestroyWindow(this->game_window.get());
- return;
+ throw runtime_error(
+ format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError()));
}
- this->game_renderer = {tmp_renderer, [](SDL_Renderer * renderer) {
- SDL_DestroyRenderer(renderer);
- }};
+ this->game_renderer
+ = {tmp_renderer, [](SDL_Renderer * renderer) { SDL_DestroyRenderer(renderer); }};
int img_flags = IMG_INIT_PNG;
if (!(IMG_Init(img_flags) & img_flags)) {
- // FIXME: throw exception
- std::cout << "SDL_image could not initialize! SDL_image Error: "
- << IMG_GetError() << std::endl;
+ throw runtime_error("SDLContext: SDL_image could not initialize!");
}
+
+ mediator.sdl_context = *this;
}
SDLContext::~SDLContext() {
@@ -81,114 +76,344 @@ SDLContext::~SDLContext() {
// thread that SDL_Init() was called on? This has caused problems for me
// before.
IMG_Quit();
+ TTF_Quit();
SDL_Quit();
}
-void SDLContext::handle_events(bool & running) {
- //TODO: wouter i need events
- /*
- SDL_Event event;
- SDL_PollEvent(&event);
- switch (event.type) {
- case SDL_QUIT:
- running = false;
- break;
- case SDL_KEYDOWN:
- triggerEvent(KeyPressedEvent(getCustomKey(event.key.keysym.sym)));
- break;
- case SDL_MOUSEBUTTONDOWN:
- int x, y;
- SDL_GetMouseState(&x, &y);
- triggerEvent(MousePressedEvent(x, y));
- break;
+Keycode SDLContext::sdl_to_keycode(SDL_Scancode sdl_key) {
+ if (!lookup_table.contains(sdl_key)) return Keycode::NONE;
+ return lookup_table.at(sdl_key);
+}
+
+const keyboard_state_t & SDLContext::get_keyboard_state() {
+ SDL_PumpEvents();
+ const Uint8 * current_state = SDL_GetKeyboardState(nullptr);
+
+ for (int i = 0; i < SDL_NUM_SCANCODES; ++i) {
+
+ Keycode key = sdl_to_keycode(static_cast<SDL_Scancode>(i));
+ if (key != Keycode::NONE) {
+ this->keyboard_state[key] = current_state[i] != 0;
+ }
+ }
+ return this->keyboard_state;
+}
+
+MouseButton SDLContext::sdl_to_mousebutton(Uint8 sdl_button) {
+ static const std::array<MouseButton, 5> MOUSE_BUTTON_LOOKUP_TABLE = [] {
+ std::array<MouseButton, 5> table{};
+ table.fill(MouseButton::NONE);
+
+ table[SDL_BUTTON_LEFT] = MouseButton::LEFT_MOUSE;
+ table[SDL_BUTTON_RIGHT] = MouseButton::RIGHT_MOUSE;
+ table[SDL_BUTTON_MIDDLE] = MouseButton::MIDDLE_MOUSE;
+ table[SDL_BUTTON_X1] = MouseButton::X1_MOUSE;
+ table[SDL_BUTTON_X2] = MouseButton::X2_MOUSE;
+
+ return table;
+ }();
+
+ if (sdl_button >= MOUSE_BUTTON_LOOKUP_TABLE.size()) {
+ // Return NONE for invalid or unmapped button
+ return MouseButton::NONE;
}
- */
+
+ return MOUSE_BUTTON_LOOKUP_TABLE[sdl_button];
}
void SDLContext::clear_screen() { SDL_RenderClear(this->game_renderer.get()); }
void SDLContext::present_screen() {
+ SDL_SetRenderDrawColor(this->game_renderer.get(), 0, 0, 0, 255);
+ SDL_RenderFillRectF(this->game_renderer.get(), &black_bars[0]);
+ SDL_RenderFillRectF(this->game_renderer.get(), &black_bars[1]);
SDL_RenderPresent(this->game_renderer.get());
}
-void SDLContext::draw(const Sprite & sprite, const Transform & transform,
- const Camera & cam) {
+SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const {
- SDL_RendererFlip render_flip
- = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x)
- | (SDL_FLIP_VERTICAL * sprite.flip.flip_y));
-
- double adjusted_x = (transform.position.x - cam.x) * cam.zoom;
- double adjusted_y = (transform.position.y - cam.y) * cam.zoom;
- double adjusted_w = sprite.sprite_rect.w * transform.scale * cam.zoom;
- double adjusted_h = sprite.sprite_rect.h * transform.scale * cam.zoom;
-
- SDL_Rect srcrect = {
- .x = sprite.sprite_rect.x,
- .y = sprite.sprite_rect.y,
- .w = sprite.sprite_rect.w,
- .h = sprite.sprite_rect.h,
- };
+ const Sprite::Data & data = ctx.sprite.data;
- SDL_Rect dstrect = {
- .x = static_cast<int>(adjusted_x),
- .y = static_cast<int>(adjusted_y),
- .w = static_cast<int>(adjusted_w),
- .h = static_cast<int>(adjusted_h),
- };
+ float aspect_ratio
+ = (ctx.sprite.aspect_ratio == 0) ? ctx.texture.get_ratio() : ctx.sprite.aspect_ratio;
+
+ vec2 size = data.size;
+ if (data.size.x == 0 && data.size.y != 0) {
+ size.x = data.size.y * aspect_ratio;
+ }
+ if (data.size.y == 0 && data.size.x != 0) {
+ size.y = data.size.x / aspect_ratio;
+ }
+ size *= cam_aux_data.render_scale * ctx.img_scale * data.scale_offset;
- SDL_RenderCopyEx(this->game_renderer.get(),
- sprite.sprite_image->texture.get(), &srcrect,
+ vec2 screen_pos = (ctx.pos + data.position_offset - cam_aux_data.cam_pos
+ + (cam_aux_data.zoomed_viewport) / 2)
+ * cam_aux_data.render_scale
+ - size / 2 + cam_aux_data.bar_size;
- &dstrect, transform.rotation, NULL, render_flip);
+ return SDL_FRect{
+ .x = screen_pos.x,
+ .y = screen_pos.y,
+ .w = size.x,
+ .h = size.y,
+ };
}
-void SDLContext::camera(const Camera & cam) {
- this->viewport.w = static_cast<int>(cam.aspect_width);
- this->viewport.h = static_cast<int>(cam.aspect_height);
- this->viewport.x = static_cast<int>(cam.x) - (SCREEN_WIDTH / 2);
- this->viewport.y = static_cast<int>(cam.y) - (SCREEN_HEIGHT / 2);
+void SDLContext::draw(const RenderContext & ctx) {
+ const Sprite::Data & data = ctx.sprite.data;
+ SDL_RendererFlip render_flip
+ = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * data.flip.flip_x)
+ | (SDL_FLIP_VERTICAL * data.flip.flip_y));
+
+ SDL_Rect srcrect;
+ SDL_Rect * srcrect_ptr = NULL;
+ if (ctx.sprite.mask.w != 0 || ctx.sprite.mask.h != 0) {
+ srcrect.w = ctx.sprite.mask.w;
+ srcrect.h = ctx.sprite.mask.h;
+ srcrect.x = ctx.sprite.mask.x;
+ srcrect.y = ctx.sprite.mask.y;
+ srcrect_ptr = &srcrect;
+ }
+
+ SDL_FRect dstrect = this->get_dst_rect(SDLContext::DestinationRectangleData{
+ .sprite = ctx.sprite,
+ .texture = ctx.texture,
+ .pos = ctx.pos,
+ .img_scale = ctx.scale,
+ });
- SDL_SetRenderDrawColor(this->game_renderer.get(), cam.bg_color.r,
- cam.bg_color.g, cam.bg_color.b, cam.bg_color.a);
+ double angle = ctx.angle + data.angle_offset;
+
+ this->set_color_texture(ctx.texture, ctx.sprite.data.color);
+ SDL_RenderCopyExF(this->game_renderer.get(), ctx.texture.get_img(), srcrect_ptr, &dstrect,
+ angle, NULL, render_flip);
}
-uint64_t SDLContext::get_ticks() const { return SDL_GetTicks64(); }
+void SDLContext::update_camera_view(const Camera & cam, const vec2 & new_pos) {
+
+ const Camera::Data & cam_data = cam.data;
+ // resize window
+ int w, h;
+ SDL_GetWindowSize(this->game_window.get(), &w, &h);
+ if (w != cam.screen.x || h != cam.screen.y) {
+ SDL_SetWindowSize(this->game_window.get(), cam.screen.x, cam.screen.y);
+ }
+
+ vec2 & zoomed_viewport = this->cam_aux_data.zoomed_viewport;
+ vec2 & bar_size = this->cam_aux_data.bar_size;
+ vec2 & render_scale = this->cam_aux_data.render_scale;
+ this->cam_aux_data.cam_pos = new_pos;
+
+ zoomed_viewport = cam.viewport_size * cam_data.zoom;
+ float screen_aspect = static_cast<float>(cam.screen.x) / cam.screen.y;
+ float viewport_aspect = zoomed_viewport.x / zoomed_viewport.y;
+
+ // calculate black bars
+ if (screen_aspect > viewport_aspect) {
+ // pillarboxing
+ float scale = cam.screen.y / zoomed_viewport.y;
+ float adj_width = zoomed_viewport.x * scale;
+ float bar_width = (cam.screen.x - adj_width) / 2;
+ this->black_bars[0] = {0, 0, bar_width, (float) cam.screen.y};
+ this->black_bars[1] = {(cam.screen.x - bar_width), 0, bar_width, (float) cam.screen.y};
+
+ bar_size = {bar_width, 0};
+ render_scale.x = render_scale.y = scale;
+ } else {
+ // letterboxing
+ float scale = cam.screen.x / (cam.viewport_size.x * cam_data.zoom);
+ float adj_height = cam.viewport_size.y * scale;
+ float bar_height = (cam.screen.y - adj_height) / 2;
+ this->black_bars[0] = {0, 0, (float) cam.screen.x, bar_height};
+ this->black_bars[1]
+ = {0, (cam.screen.y - bar_height), (float) cam.screen.x, bar_height};
+
+ bar_size = {0, bar_height};
+ render_scale.x = render_scale.y = scale;
+ }
+
+ SDL_SetRenderDrawColor(this->game_renderer.get(), cam_data.bg_color.r, cam_data.bg_color.g,
+ cam_data.bg_color.b, cam_data.bg_color.a);
+
+ SDL_Rect bg = {
+ .x = 0,
+ .y = 0,
+ .w = cam.screen.x,
+ .h = cam.screen.y,
+ };
+
+ // fill bg color
+ SDL_RenderFillRect(this->game_renderer.get(), &bg);
+}
std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>>
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");
- }
+ if (tmp == nullptr)
+ throw runtime_error(format("SDLContext: IMG_Load error: {}", SDL_GetError()));
- std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>>
- img_surface;
- img_surface
- = {tmp, [](SDL_Surface * surface) { SDL_FreeSurface(surface); }};
+ std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> img_surface;
+ img_surface = {tmp, [](SDL_Surface * surface) { SDL_FreeSurface(surface); }};
- SDL_Texture * tmp_texture = SDL_CreateTextureFromSurface(
- this->game_renderer.get(), img_surface.get());
+ SDL_Texture * tmp_texture
+ = SDL_CreateTextureFromSurface(this->game_renderer.get(), img_surface.get());
if (tmp_texture == nullptr) {
- throw Exception("Texture cannot be load from {}", path);
+ throw runtime_error(format("SDLContext: Texture cannot be load from {}", path));
}
- std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>>
- img_texture;
- img_texture = {tmp_texture,
- [](SDL_Texture * texture) { SDL_DestroyTexture(texture); }};
+ std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> img_texture;
+ img_texture = {tmp_texture, [](SDL_Texture * texture) { SDL_DestroyTexture(texture); }};
+ SDL_SetTextureBlendMode(img_texture.get(), SDL_BLENDMODE_BLEND);
return img_texture;
}
-int SDLContext::get_width(const Texture & ctx) const {
- int w;
- SDL_QueryTexture(ctx.texture.get(), NULL, NULL, &w, NULL);
- return w;
+
+ivec2 SDLContext::get_size(const Texture & ctx) {
+ ivec2 size;
+ SDL_QueryTexture(ctx.get_img(), NULL, NULL, &size.x, &size.y);
+ return size;
}
-int SDLContext::get_height(const Texture & ctx) const {
- int h;
- SDL_QueryTexture(ctx.texture.get(), NULL, NULL, NULL, &h);
- return h;
+
+std::vector<EventData> SDLContext::get_events() {
+ std::vector<EventData> event_list;
+ SDL_Event event;
+ const CameraAuxiliaryData & cam = this->cam_aux_data;
+ while (SDL_PollEvent(&event)) {
+ ivec2 mouse_pos;
+ mouse_pos.x = (event.button.x - cam.bar_size.x) / cam.render_scale.x;
+ mouse_pos.y = (event.button.y - cam.bar_size.y) / cam.render_scale.y;
+ switch (event.type) {
+ case SDL_QUIT:
+ event_list.push_back({.event_type = EventType::SHUTDOWN});
+ break;
+ case SDL_KEYDOWN:
+ event_list.push_back(EventData{
+ .event_type = EventType::KEY_DOWN,
+ .data = {
+ .key_data = {
+ .key = this->sdl_to_keycode(event.key.keysym.scancode),
+ .key_repeat = event.key.repeat != 0,
+ },
+ },
+ });
+ break;
+
+ case SDL_KEYUP:
+ event_list.push_back(EventData{
+ .event_type = EventType::KEY_UP,
+ .data = {
+ .key_data = {
+ .key = this->sdl_to_keycode(event.key.keysym.scancode),
+ .key_repeat = event.key.repeat != 0,
+ },
+ },
+ });
+ break;
+
+ case SDL_MOUSEBUTTONDOWN:
+ event_list.push_back(EventData{
+ .event_type = EventType::MOUSE_DOWN,
+ .data = {
+ .mouse_data = {
+ .mouse_button = this->sdl_to_mousebutton(event.button.button),
+ .mouse_position = mouse_pos,
+ },
+ },
+ });
+ break;
+ case SDL_MOUSEBUTTONUP:
+ event_list.push_back(EventData{
+ .event_type = EventType::MOUSE_UP,
+ .data = {
+ .mouse_data = {
+ .mouse_button = this->sdl_to_mousebutton(event.button.button),
+ .mouse_position = mouse_pos,
+ },
+ },
+ });
+ break;
+
+ case SDL_MOUSEMOTION:
+ event_list.push_back(EventData{
+ .event_type = EventType::MOUSE_MOVE,
+ .data = {
+ .mouse_data = {
+ .mouse_position = mouse_pos,
+ .rel_mouse_move = {event.motion.xrel, event.motion.yrel},
+ },
+ },
+ });
+ break;
+
+ case SDL_MOUSEWHEEL:
+ event_list.push_back(EventData{
+ .event_type = EventType::MOUSE_WHEEL,
+ .data = {
+ .mouse_data = {
+ .mouse_position = mouse_pos,
+ .scroll_direction = event.wheel.y < 0 ? -1 : 1,
+ .scroll_delta = event.wheel.preciseY,
+ },
+ },
+ });
+ break;
+ case SDL_WINDOWEVENT:
+ this->handle_window_event(event.window, event_list);
+ break;
+ }
+ }
+
+ return event_list;
+}
+
+void SDLContext::handle_window_event(const SDL_WindowEvent & window_event,
+ std::vector<EventData> & event_list) {
+ switch (window_event.event) {
+ case SDL_WINDOWEVENT_EXPOSED:
+ event_list.push_back(EventData{EventType::WINDOW_EXPOSE});
+ break;
+ case SDL_WINDOWEVENT_RESIZED:
+ event_list.push_back(EventData{
+ .event_type = EventType::WINDOW_RESIZE,
+ .data = {
+ .window_data = {
+ .resize_dimension = {window_event.data1, window_event.data2}
+ },
+ },
+ });
+ break;
+ case SDL_WINDOWEVENT_MOVED:
+ event_list.push_back(EventData{
+ .event_type = EventType::WINDOW_MOVE,
+ .data = {
+ .window_data = {
+ .move_delta = {window_event.data1, window_event.data2}
+ },
+ },
+ });
+ break;
+
+ case SDL_WINDOWEVENT_MINIMIZED:
+ event_list.push_back(EventData{EventType::WINDOW_MINIMIZE});
+ break;
+ case SDL_WINDOWEVENT_MAXIMIZED:
+ event_list.push_back(EventData{EventType::WINDOW_MAXIMIZE});
+ break;
+ case SDL_WINDOWEVENT_FOCUS_GAINED:
+ event_list.push_back(EventData{EventType::WINDOW_FOCUS_GAIN});
+ break;
+ case SDL_WINDOWEVENT_FOCUS_LOST:
+ event_list.push_back(EventData{EventType::WINDOW_FOCUS_LOST});
+ break;
+ }
+}
+
+void SDLContext::set_color_texture(const Texture & texture, const Color & color) {
+ SDL_SetTextureColorMod(texture.get_img(), color.r, color.g, color.b);
+ SDL_SetTextureAlphaMod(texture.get_img(), color.a);
+}
+
+Asset SDLContext::get_font_from_name(const std::string & font_family) {
+ return this->font_facade.get_font_asset(font_family);
}
-void SDLContext::delay(int ms) const { SDL_Delay(ms); }
diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h
index 536dec5..b687f87 100644
--- a/src/crepe/facade/SDLContext.h
+++ b/src/crepe/facade/SDLContext.h
@@ -1,38 +1,75 @@
#pragma once
+#include <SDL2/SDL.h>
#include <SDL2/SDL_keycode.h>
+#include <SDL2/SDL_rect.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_video.h>
+#include <cmath>
#include <functional>
#include <memory>
#include <string>
+#include <unordered_map>
-#include "../api/Sprite.h"
-#include "../api/Transform.h"
+#include "../types.h"
#include "api/Camera.h"
+#include "api/Color.h"
+#include "api/KeyCodes.h"
+#include "api/Sprite.h"
+#include "api/Transform.h"
+#include "types.h"
-// FIXME: this needs to be removed
-const int SCREEN_WIDTH = 640;
-const int SCREEN_HEIGHT = 480;
+#include "EventData.h"
+#include "FontFacade.h"
namespace crepe {
-
-// TODO: SDL_Keycode is defined in a header not distributed with crepe, which
-// means this typedef is unusable when crepe is packaged. Wouter will fix this
-// later.
-typedef SDL_Keycode CREPE_KEYCODES;
-
class Texture;
-class LoopManager;
+class Mediator;
/**
- * \class SDLContext
* \brief Facade for the SDL library
- *
- * SDLContext is a singleton that handles the SDL window and renderer, provides methods
- * for event handling, and rendering to the screen. It is never used directly by the user
+ *
+ * SDLContext is a singleton that handles the SDL window and renderer, provides methods for
+ * event handling, and rendering to the screen. It is never used directly by the user
*/
class SDLContext {
+public:
+ //! data that the camera component cannot hold
+ struct CameraAuxiliaryData {
+
+ //! zoomed in viewport in game_units
+ vec2 zoomed_viewport;
+
+ /**
+ * \brief scaling factor
+ *
+ * depending on the black bars type will the scaling be different.
+ * - letterboxing --> scaling on the y-as
+ * - pillarboxing --> scaling on the x-as
+ */
+ vec2 render_scale;
+
+ /**
+ * \brief size of calculated black bars
+ *
+ * depending on the black bars type will the size be different
+ * - lettorboxing --> {0, bar_height}
+ * - pillarboxing --> {bar_width , 0}
+ */
+ vec2 bar_size;
+
+ //! Calculated camera position
+ vec2 cam_pos;
+ };
+
+ //! rendering data needed to render on screen
+ struct RenderContext {
+ const Sprite & sprite;
+ const Texture & texture;
+ const vec2 & pos;
+ const double & angle;
+ const double & scale;
+ };
public:
/**
@@ -41,61 +78,93 @@ public:
*/
static SDLContext & get_instance();
+public:
SDLContext(const SDLContext &) = delete;
SDLContext(SDLContext &&) = delete;
SDLContext & operator=(const SDLContext &) = delete;
SDLContext & operator=(SDLContext &&) = delete;
-private:
- //! will only use handle_events
- friend class LoopManager;
+public:
/**
- * \brief Handles SDL events such as window close and input.
- * \param running Reference to a boolean flag that controls the main loop.
+ * \brief Constructs an SDLContext instance.
+ * Initializes SDL, creates a window and renderer.
*/
- void handle_events(bool & running);
+ SDLContext(Mediator & mediator);
-private:
- //! Will only use get_ticks
- friend class AnimatorSystem;
- //! Will only use delay
- friend class LoopTimer;
/**
- * \brief Gets the current SDL ticks since the program started.
- * \return Current ticks in milliseconds as a constant uint64_t.
+ * \brief Destroys the SDLContext instance.
+ * Cleans up SDL resources, including the window and renderer.
*/
- uint64_t get_ticks() const;
+ ~SDLContext();
+
+public:
/**
- * \brief Pauses the execution for a specified duration.
+ * \brief Retrieves a list of all events from the SDL context.
*
- * This function uses SDL's delay function to halt the program execution
- * for a given number of milliseconds, allowing for frame rate control
- * or other timing-related functionality.
+ * This method retrieves all the events from the SDL context that are currently
+ * available. It is primarily used by the InputSystem to process various
+ * input events such as mouse clicks, mouse movements, and keyboard presses.
*
- * \param ms Duration of the delay in milliseconds.
+ * \return Events that occurred since last call to `get_events()`
*/
- void delay(int ms) const;
-
-private:
+ std::vector<EventData> get_events();
/**
- * \brief Constructs an SDLContext instance.
- * Initializes SDL, creates a window and renderer.
+ * \brief Fills event_list with triggered window events
+ *
+ * This method checks if any window events are triggered and adds them to the event_list.
+ *
*/
- SDLContext();
-
+ void handle_window_event(const SDL_WindowEvent & window_event,
+ std::vector<EventData> & event_list);
/**
- * \brief Destroys the SDLContext instance.
- * Cleans up SDL resources, including the window and renderer.
+ * \brief Converts an SDL scan code to the custom Keycode type.
+ *
+ * This method maps an SDL scan code to the corresponding `Keycode` enum value,
+ * which is used internally by the system to identify the keys.
+ *
+ * \param sdl_key The SDL scan code to convert.
+ * \return The corresponding `Keycode` value or `Keycode::NONE` if the key is unrecognized.
*/
- ~SDLContext();
+ Keycode sdl_to_keycode(SDL_Scancode sdl_key);
-private:
- //! Will use the funtions: texture_from_path, get_width,get_height.
- friend class Texture;
+ /**
+ * \brief Converts an SDL mouse button code to the custom MouseButton type.
+ *
+ * This method maps an SDL mouse button code to the corresponding `MouseButton`
+ * enum value, which is used internally by the system to identify mouse buttons.
+ *
+ * \param sdl_button The SDL mouse button code to convert.
+ * \return The corresponding `MouseButton` value or `MouseButton::NONE` if the key is unrecognized
+ */
+ MouseButton sdl_to_mousebutton(Uint8 sdl_button);
+ /**
+ * \brief Gets the current state of the keyboard.
+ *
+ * Updates the internal keyboard state by checking the current key states using
+ * SDL's `SDL_GetKeyboardState()`, and returns a reference to the `keyboard_state_t`.
+ *
+ * \return A constant reference to the `keyboard_state_t`, which holds the state
+ * of each key (true = pressed, false = not pressed).
+ */
+ const keyboard_state_t & get_keyboard_state();
- //! Will use the funtions: texture_from_path, get_width,get_height.
- friend class Animator;
+public:
+ /**
+ * \brief Gets the current SDL ticks since the program started.
+ * \return Current ticks in milliseconds as a constant uint64_t.
+ */
+ uint64_t get_ticks() const;
+ /**
+ * \brief Pauses the execution for a specified duration.
+ *
+ * This function uses SDL's delay function to halt the program execution for a given number
+ * of milliseconds, allowing for frame rate control or other timing-related functionality.
+ *
+ * \param ms Duration of the delay in milliseconds.
+ */
+ void delay(int ms) const;
+public:
/**
* \brief Loads a texture from a file path.
* \param path Path to the image file.
@@ -104,31 +173,18 @@ private:
std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>>
texture_from_path(const std::string & path);
/**
- * \brief Gets the width of a texture.
+ * \brief Gets the size of a texture.
* \param texture Reference to the Texture object.
- * \return Width of the texture as an integer.
+ * \return Width and height of the texture as an integer in pixels.
*/
- int get_width(const Texture &) const;
-
- /**
- * \brief Gets the height of a texture.
- * \param texture Reference to the Texture object.
- * \return Height of the texture as an integer.
- */
- int get_height(const Texture &) const;
-
-private:
- //! Will use draw,clear_screen, present_screen, camera.
- friend class RenderSystem;
+ ivec2 get_size(const Texture & ctx);
+public:
/**
* \brief Draws a sprite to the screen using the specified transform and camera.
- * \param sprite Reference to the Sprite to draw.
- * \param transform Reference to the Transform for positioning.
- * \param camera Reference to the Camera for view adjustments.
+ * \param RenderContext Reference to rendering data to draw
*/
- void draw(const Sprite & sprite, const Transform & transform,
- const Camera & camera);
+ void draw(const RenderContext & ctx);
//! Clears the screen, preparing for a new frame.
void clear_screen();
@@ -137,21 +193,173 @@ private:
void present_screen();
/**
- * \brief Sets the current camera for rendering.
- * \param camera Reference to the Camera object.
+ * \brief calculates camera view settings. such as black_bars, zoomed_viewport, scaling and
+ * adjusting window size.
+ *
+ * \note only supports windowed mode.
+ * \param camera Reference to the current Camera object in the scene.
+ * \param new_pos new camera position from transform and offset
+ */
+ void update_camera_view(const Camera & camera, const vec2 & new_pos);
+
+public:
+ //! the data needed to construct a sdl dst rectangle
+ struct DestinationRectangleData {
+ const Sprite & sprite;
+ const Texture & texture;
+ const vec2 & pos;
+ const double & img_scale;
+ };
+
+ /**
+ * \brief calculates the sqaure size of the image for destination
+ *
+ * \param data needed to calculate a destination rectangle
+ * \return sdl rectangle to draw a dst image to draw on the screen
+ */
+ SDL_FRect get_dst_rect(const DestinationRectangleData & data) const;
+ /**
+ * \brief Set an additional color value multiplied into render copy operations.
+ *
+ * \param texture the given texture to adjust
+ * \param color the color data for the texture
*/
- void camera(const Camera & camera);
+ void set_color_texture(const Texture & texture, const Color & color);
private:
//! sdl Window
std::unique_ptr<SDL_Window, std::function<void(SDL_Window *)>> game_window;
//! renderer for the crepe engine
- std::unique_ptr<SDL_Renderer, std::function<void(SDL_Renderer *)>>
- game_renderer;
+ std::unique_ptr<SDL_Renderer, std::function<void(SDL_Renderer *)>> game_renderer;
- //! viewport for the camera window
- SDL_Rect viewport = {0, 0, 640, 480};
+ //! black bars rectangle to draw
+ SDL_FRect black_bars[2] = {};
+
+ /**
+ * \cam_aux_data extra data that the component cannot hold.
+ *
+ * - this is defined in this class because get_events() needs this information aswell
+ */
+ CameraAuxiliaryData cam_aux_data;
+
+private:
+ //! instance of the font_facade
+ FontFacade font_facade{};
+
+public:
+ /**
+ * \brief Function to Get asset from font_family
+ *
+ * This function uses the FontFacade function to convert a font_family to an asset.
+ *
+ * \param font_family name of the font style that needs to be used (will return an asset with default font path of the font_family doesnt exist)
+ *
+ * \return asset with the font style absolute path
+ */
+ Asset get_font_from_name(const std::string & font_family);
+ //! variable to store the state of each key (true = pressed, false = not pressed)
+ keyboard_state_t keyboard_state;
+ //! lookup table for converting SDL_SCANCODES to Keycodes
+ const std::unordered_map<SDL_Scancode, Keycode> lookup_table
+ = {{SDL_SCANCODE_SPACE, Keycode::SPACE},
+ {SDL_SCANCODE_APOSTROPHE, Keycode::APOSTROPHE},
+ {SDL_SCANCODE_COMMA, Keycode::COMMA},
+ {SDL_SCANCODE_MINUS, Keycode::MINUS},
+ {SDL_SCANCODE_PERIOD, Keycode::PERIOD},
+ {SDL_SCANCODE_SLASH, Keycode::SLASH},
+ {SDL_SCANCODE_0, Keycode::D0},
+ {SDL_SCANCODE_1, Keycode::D1},
+ {SDL_SCANCODE_2, Keycode::D2},
+ {SDL_SCANCODE_3, Keycode::D3},
+ {SDL_SCANCODE_4, Keycode::D4},
+ {SDL_SCANCODE_5, Keycode::D5},
+ {SDL_SCANCODE_6, Keycode::D6},
+ {SDL_SCANCODE_7, Keycode::D7},
+ {SDL_SCANCODE_8, Keycode::D8},
+ {SDL_SCANCODE_9, Keycode::D9},
+ {SDL_SCANCODE_SEMICOLON, Keycode::SEMICOLON},
+ {SDL_SCANCODE_EQUALS, Keycode::EQUAL},
+ {SDL_SCANCODE_A, Keycode::A},
+ {SDL_SCANCODE_B, Keycode::B},
+ {SDL_SCANCODE_C, Keycode::C},
+ {SDL_SCANCODE_D, Keycode::D},
+ {SDL_SCANCODE_E, Keycode::E},
+ {SDL_SCANCODE_F, Keycode::F},
+ {SDL_SCANCODE_G, Keycode::G},
+ {SDL_SCANCODE_H, Keycode::H},
+ {SDL_SCANCODE_I, Keycode::I},
+ {SDL_SCANCODE_J, Keycode::J},
+ {SDL_SCANCODE_K, Keycode::K},
+ {SDL_SCANCODE_L, Keycode::L},
+ {SDL_SCANCODE_M, Keycode::M},
+ {SDL_SCANCODE_N, Keycode::N},
+ {SDL_SCANCODE_O, Keycode::O},
+ {SDL_SCANCODE_P, Keycode::P},
+ {SDL_SCANCODE_Q, Keycode::Q},
+ {SDL_SCANCODE_R, Keycode::R},
+ {SDL_SCANCODE_S, Keycode::S},
+ {SDL_SCANCODE_T, Keycode::T},
+ {SDL_SCANCODE_U, Keycode::U},
+ {SDL_SCANCODE_V, Keycode::V},
+ {SDL_SCANCODE_W, Keycode::W},
+ {SDL_SCANCODE_X, Keycode::X},
+ {SDL_SCANCODE_Y, Keycode::Y},
+ {SDL_SCANCODE_Z, Keycode::Z},
+ {SDL_SCANCODE_LEFTBRACKET, Keycode::LEFT_BRACKET},
+ {SDL_SCANCODE_BACKSLASH, Keycode::BACKSLASH},
+ {SDL_SCANCODE_RIGHTBRACKET, Keycode::RIGHT_BRACKET},
+ {SDL_SCANCODE_GRAVE, Keycode::GRAVE_ACCENT},
+ {SDL_SCANCODE_ESCAPE, Keycode::ESCAPE},
+ {SDL_SCANCODE_RETURN, Keycode::ENTER},
+ {SDL_SCANCODE_TAB, Keycode::TAB},
+ {SDL_SCANCODE_BACKSPACE, Keycode::BACKSPACE},
+ {SDL_SCANCODE_INSERT, Keycode::INSERT},
+ {SDL_SCANCODE_DELETE, Keycode::DELETE},
+ {SDL_SCANCODE_RIGHT, Keycode::RIGHT},
+ {SDL_SCANCODE_LEFT, Keycode::LEFT},
+ {SDL_SCANCODE_DOWN, Keycode::DOWN},
+ {SDL_SCANCODE_UP, Keycode::UP},
+ {SDL_SCANCODE_PAGEUP, Keycode::PAGE_UP},
+ {SDL_SCANCODE_PAGEDOWN, Keycode::PAGE_DOWN},
+ {SDL_SCANCODE_HOME, Keycode::HOME},
+ {SDL_SCANCODE_END, Keycode::END},
+ {SDL_SCANCODE_CAPSLOCK, Keycode::CAPS_LOCK},
+ {SDL_SCANCODE_SCROLLLOCK, Keycode::SCROLL_LOCK},
+ {SDL_SCANCODE_NUMLOCKCLEAR, Keycode::NUM_LOCK},
+ {SDL_SCANCODE_PRINTSCREEN, Keycode::PRINT_SCREEN},
+ {SDL_SCANCODE_PAUSE, Keycode::PAUSE},
+ {SDL_SCANCODE_F1, Keycode::F1},
+ {SDL_SCANCODE_F2, Keycode::F2},
+ {SDL_SCANCODE_F3, Keycode::F3},
+ {SDL_SCANCODE_F4, Keycode::F4},
+ {SDL_SCANCODE_F5, Keycode::F5},
+ {SDL_SCANCODE_F6, Keycode::F6},
+ {SDL_SCANCODE_F7, Keycode::F7},
+ {SDL_SCANCODE_F8, Keycode::F8},
+ {SDL_SCANCODE_F9, Keycode::F9},
+ {SDL_SCANCODE_F10, Keycode::F10},
+ {SDL_SCANCODE_F11, Keycode::F11},
+ {SDL_SCANCODE_F12, Keycode::F12},
+ {SDL_SCANCODE_KP_0, Keycode::KP0},
+ {SDL_SCANCODE_KP_1, Keycode::KP1},
+ {SDL_SCANCODE_KP_2, Keycode::KP2},
+ {SDL_SCANCODE_KP_3, Keycode::KP3},
+ {SDL_SCANCODE_KP_4, Keycode::KP4},
+ {SDL_SCANCODE_KP_5, Keycode::KP5},
+ {SDL_SCANCODE_KP_6, Keycode::KP6},
+ {SDL_SCANCODE_KP_7, Keycode::KP7},
+ {SDL_SCANCODE_KP_8, Keycode::KP8},
+ {SDL_SCANCODE_KP_9, Keycode::KP9},
+ {SDL_SCANCODE_LSHIFT, Keycode::LEFT_SHIFT},
+ {SDL_SCANCODE_LCTRL, Keycode::LEFT_CONTROL},
+ {SDL_SCANCODE_LALT, Keycode::LEFT_ALT},
+ {SDL_SCANCODE_LGUI, Keycode::LEFT_SUPER},
+ {SDL_SCANCODE_RSHIFT, Keycode::RIGHT_SHIFT},
+ {SDL_SCANCODE_RCTRL, Keycode::RIGHT_CONTROL},
+ {SDL_SCANCODE_RALT, Keycode::RIGHT_ALT},
+ {SDL_SCANCODE_RGUI, Keycode::RIGHT_SUPER},
+ {SDL_SCANCODE_MENU, Keycode::MENU}};
};
} // namespace crepe
diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp
index b589759..97e455e 100644
--- a/src/crepe/facade/Sound.cpp
+++ b/src/crepe/facade/Sound.cpp
@@ -2,58 +2,12 @@
#include "../util/Log.h"
#include "Sound.h"
-#include "SoundContext.h"
using namespace crepe;
using namespace std;
-Sound::Sound(const Asset & src) : Resource(src) {
+Sound::Sound(const Asset & src, Mediator & mediator) : Resource(src, mediator) {
this->sample.load(src.get_path().c_str());
dbg_trace();
}
Sound::~Sound() { dbg_trace(); }
-
-void Sound::play() {
- SoundContext & ctx = this->context.get();
- 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 = this->context.get();
- if (ctx.engine.getPause(this->handle)) return;
- ctx.engine.setPause(this->handle, true);
-}
-
-void Sound::rewind() {
- SoundContext & ctx = this->context.get();
- if (!ctx.engine.isValidVoiceHandle(this->handle)) return;
- ctx.engine.seek(this->handle, 0);
-}
-
-void Sound::set_volume(float volume) {
- this->volume = volume;
-
- SoundContext & ctx = this->context.get();
- 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 = 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 94b1996..4a5d692 100644
--- a/src/crepe/facade/Sound.h
+++ b/src/crepe/facade/Sound.h
@@ -3,86 +3,29 @@
#include <soloud/soloud.h>
#include <soloud/soloud_wav.h>
-#include "../util/OptionalRef.h"
#include "../Resource.h"
namespace crepe {
class SoundContext;
+class Mediator;
/**
* \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.
+ * 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 : public Resource {
public:
- Sound(const Asset & src);
+ Sound(const Asset & src, Mediator & mediator);
~Sound(); // dbg_trace
- /**
- * \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:
- void set_context(SoundContext & ctx);
private:
+ //! Deserialized resource (soloud)
SoLoud::Wav sample;
- SoLoud::handle handle;
- OptionalRef<SoundContext> context;
-
- float volume = 1.0f;
- bool looping = false;
+ //! SoundContext uses \c sample
+ friend class SoundContext;
};
} // namespace crepe
diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp
index b65dfb2..b1f8cb3 100644
--- a/src/crepe/facade/SoundContext.cpp
+++ b/src/crepe/facade/SoundContext.cpp
@@ -6,10 +6,31 @@ using namespace crepe;
SoundContext::SoundContext() {
dbg_trace();
- engine.init();
+ this->engine.init();
+ this->engine.setMaxActiveVoiceCount(this->config.audio.voices);
}
SoundContext::~SoundContext() {
dbg_trace();
- engine.deinit();
+ this->engine.deinit();
+}
+
+SoundHandle SoundContext::play(Sound & resource) {
+ SoLoud::handle real_handle = this->engine.play(resource.sample, 1.0f);
+ SoundHandle handle = this->next_handle;
+ this->registry[handle] = real_handle;
+ this->next_handle++;
+ return handle;
+}
+
+void SoundContext::stop(const SoundHandle & handle) {
+ this->engine.stop(this->registry[handle]);
+}
+
+void SoundContext::set_volume(const SoundHandle & handle, float volume) {
+ this->engine.setVolume(this->registry[handle], volume);
+}
+
+void SoundContext::set_loop(const SoundHandle & handle, bool loop) {
+ this->engine.setLooping(this->registry[handle], loop);
}
diff --git a/src/crepe/facade/SoundContext.h b/src/crepe/facade/SoundContext.h
index d22ff7a..d986c59 100644
--- a/src/crepe/facade/SoundContext.h
+++ b/src/crepe/facade/SoundContext.h
@@ -2,15 +2,18 @@
#include <soloud/soloud.h>
+#include "../api/Config.h"
+
#include "Sound.h"
+#include "SoundHandle.h"
namespace crepe {
/**
* \brief Sound engine facade
*
- * This class is a wrapper around a \c SoLoud::Soloud instance, which provides
- * the methods for playing \c Sound instances. It is part of the sound facade.
+ * This class is a wrapper around a \c SoLoud::Soloud instance, which provides the methods for
+ * playing \c Sound instances. It is part of the sound facade.
*/
class SoundContext {
public:
@@ -22,10 +25,57 @@ public:
SoundContext & operator=(const SoundContext &) = delete;
SoundContext & operator=(SoundContext &&) = delete;
+ /**
+ * \brief Play a sample
+ *
+ * Plays a Sound from the beginning of the sample and returns a handle to control it later.
+ *
+ * \param resource Sound instance to play
+ *
+ * \returns Handle to control this voice
+ */
+ virtual SoundHandle play(Sound & resource);
+ /**
+ * \brief Stop a voice immediately if it is still playing
+ *
+ * \note This function does nothing if the handle is invalid or if the sound is already
+ * stopped / finished playing.
+ *
+ * \param handle Voice handle returned by SoundContext::play
+ */
+ virtual void stop(const SoundHandle & handle);
+ /**
+ * \brief Change the volume of a voice
+ *
+ * \note This function does nothing if the handle is invalid or if the sound is already
+ * stopped / finished playing.
+ *
+ * \param handle Voice handle returned by SoundContext::play
+ * \param volume New gain value (0=silent, 1=default)
+ */
+ virtual void set_volume(const SoundHandle & handle, float volume);
+ /**
+ * \brief Set the looping behavior of a voice
+ *
+ * \note This function does nothing if the handle is invalid or if the sound is already
+ * stopped / finished playing.
+ *
+ * \param handle Voice handle returned by SoundContext::play
+ * \param loop Looping behavior (false=oneshot, true=loop)
+ */
+ virtual void set_loop(const SoundHandle & handle, bool loop);
+
private:
+ //! Abstracted class
SoLoud::Soloud engine;
- //! Sound directly calls methods on \c engine
- friend class Sound;
+
+ //! Config reference
+ Config & config = Config::get_instance();
+
+ //! Sound handle registry
+ std::unordered_map<SoundHandle, SoLoud::handle> registry;
+ //! Unique handle counter
+ SoundHandle next_handle = 0;
};
} // namespace crepe
diff --git a/src/crepe/facade/SoundHandle.h b/src/crepe/facade/SoundHandle.h
new file mode 100644
index 0000000..b7925fc
--- /dev/null
+++ b/src/crepe/facade/SoundHandle.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <cstddef>
+
+namespace crepe {
+
+/**
+ * \brief Voice handle returned by
+ */
+typedef size_t SoundHandle;
+
+} // namespace crepe
diff --git a/src/crepe/facade/Texture.cpp b/src/crepe/facade/Texture.cpp
new file mode 100644
index 0000000..b63403d
--- /dev/null
+++ b/src/crepe/facade/Texture.cpp
@@ -0,0 +1,28 @@
+#include "../util/Log.h"
+#include "facade/SDLContext.h"
+#include "manager/Mediator.h"
+
+#include "Resource.h"
+#include "Texture.h"
+#include "types.h"
+
+using namespace crepe;
+using namespace std;
+
+Texture::Texture(const Asset & src, Mediator & mediator) : Resource(src, mediator) {
+ dbg_trace();
+ SDLContext & ctx = mediator.sdl_context;
+ this->texture = ctx.texture_from_path(src.get_path());
+ this->size = ctx.get_size(*this);
+ this->aspect_ratio = static_cast<float>(this->size.x) / this->size.y;
+}
+
+Texture::~Texture() {
+ dbg_trace();
+ this->texture.reset();
+}
+
+const ivec2 & Texture::get_size() const noexcept { return this->size; }
+const float & Texture::get_ratio() const noexcept { return this->aspect_ratio; }
+
+SDL_Texture * Texture::get_img() const noexcept { return this->texture.get(); }
diff --git a/src/crepe/facade/Texture.h b/src/crepe/facade/Texture.h
new file mode 100644
index 0000000..cdacac4
--- /dev/null
+++ b/src/crepe/facade/Texture.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <SDL2/SDL_render.h>
+#include <memory>
+
+#include "../Resource.h"
+
+#include "types.h"
+
+namespace crepe {
+
+class Mediator;
+class Asset;
+
+/**
+ * \class Texture
+ * \brief Manages texture loading and properties.
+ *
+ * The Texture class is responsible for loading an image from a source and providing access to
+ * its dimensions. Textures can be used for rendering.
+ */
+class Texture : public Resource {
+
+public:
+ /**
+ * \brief Constructs a Texture from an Asset resource.
+ * \param src Asset with texture data to load.
+ * \param mediator use the SDLContext reference to load the image
+ */
+ Texture(const Asset & src, Mediator & mediator);
+
+ /**
+ * \brief Destroys the Texture instance
+ */
+ ~Texture();
+
+ /**
+ * \brief get width and height of image in pixels
+ * \return pixel size width and height
+ *
+ */
+ const ivec2 & get_size() const noexcept;
+
+ /**
+ * \brief aspect_ratio of image
+ * \return ratio
+ *
+ */
+ const float & get_ratio() const noexcept;
+
+ /**
+ * \brief get the image texture
+ * \return SDL_Texture
+ *
+ */
+ SDL_Texture * get_img() const noexcept;
+
+private:
+ //! The texture of the class from the library
+ std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> texture;
+
+ // texture size in pixel
+ ivec2 size;
+
+ //! ratio of image
+ float aspect_ratio;
+};
+
+} // namespace crepe