diff options
Diffstat (limited to 'src/crepe/facade')
-rw-r--r-- | src/crepe/facade/CMakeLists.txt | 20 | ||||
-rw-r--r-- | src/crepe/facade/DB.cpp | 61 | ||||
-rw-r--r-- | src/crepe/facade/DB.h | 75 | ||||
-rw-r--r-- | src/crepe/facade/EventData.h | 54 | ||||
-rw-r--r-- | src/crepe/facade/Font.cpp | 21 | ||||
-rw-r--r-- | src/crepe/facade/Font.h | 42 | ||||
-rw-r--r-- | src/crepe/facade/FontFacade.cpp | 43 | ||||
-rw-r--r-- | src/crepe/facade/FontFacade.h | 34 | ||||
-rw-r--r-- | src/crepe/facade/SDLContext.cpp | 419 | ||||
-rw-r--r-- | src/crepe/facade/SDLContext.h | 365 | ||||
-rw-r--r-- | src/crepe/facade/Sound.cpp | 13 | ||||
-rw-r--r-- | src/crepe/facade/Sound.h | 31 | ||||
-rw-r--r-- | src/crepe/facade/SoundContext.cpp | 36 | ||||
-rw-r--r-- | src/crepe/facade/SoundContext.h | 81 | ||||
-rw-r--r-- | src/crepe/facade/SoundHandle.h | 12 | ||||
-rw-r--r-- | src/crepe/facade/Texture.cpp | 29 | ||||
-rw-r--r-- | src/crepe/facade/Texture.h | 69 |
17 files changed, 1405 insertions, 0 deletions
diff --git a/src/crepe/facade/CMakeLists.txt b/src/crepe/facade/CMakeLists.txt new file mode 100644 index 0000000..243ae46 --- /dev/null +++ b/src/crepe/facade/CMakeLists.txt @@ -0,0 +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 new file mode 100644 index 0000000..7a3e473 --- /dev/null +++ b/src/crepe/facade/DB.cpp @@ -0,0 +1,61 @@ +#include <cstring> + +#include "util/dbg.h" + +#include "DB.h" + +using namespace std; +using namespace crepe; + +DB::DB(const string & path) { + dbg_trace(); + int ret; + + // init database struct + libdb::DB * db; + if ((ret = libdb::db_create(&db, NULL, 0)) != 0) + 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 + 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 { + libdb::DBT thang; + memset(&thang, 0, sizeof(libdb::DBT)); + thang.data = (void *) thing.data(); + thang.size = thing.size(); + return thang; +} + +string DB::get(const string & key) { + libdb::DBT db_key = this->to_thing(key); + libdb::DBT db_val; + memset(&db_val, 0, sizeof(libdb::DBT)); + + 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 runtime_error(format("db->get: {}", libdb::db_strerror(ret))); +} + +bool DB::has(const std::string & key) { + try { + this->get(key); + } catch (std::out_of_range &) { + return false; + } + return true; +} diff --git a/src/crepe/facade/DB.h b/src/crepe/facade/DB.h new file mode 100644 index 0000000..84cdf19 --- /dev/null +++ b/src/crepe/facade/DB.h @@ -0,0 +1,75 @@ +#pragma once + +#include <functional> +#include <memory> +#include <string> + +namespace libdb { +extern "C" { +#include <db.h> +} +} // namespace libdb + +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. + */ +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 = ""); + virtual ~DB() = default; + +public: + /** + * \brief Get a value from the database, or throw an exception + * + * \param key The value key + * + * \return The value + * + * \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); + /** + * \brief Set (create or overwrite) a value in the database + * + * \param key The value key + * \param value The value to store + * + * \throws std::runtime_error if an error occurs + */ + void set(const std::string & key, const std::string & value); + /** + * \brief Check if a key exists in the database + * + * \param key The value key + * + * \returns True if the key exists, or false if it does not + */ + bool has(const std::string & key); + +private: + //! RAII wrapper around \c DB struct + std::unique_ptr<libdb::DB, std::function<void(libdb::DB *)>> db; + +private: + /** + * \brief Convert an STL string to DBT (data base thang) + * + * \param thing Input data + * \return \c DBT with the same data as input \c thing + */ + libdb::DBT to_thing(const std::string & thing) const noexcept; +}; + +} // namespace crepe 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 new file mode 100644 index 0000000..9486ccd --- /dev/null +++ b/src/crepe/facade/SDLContext.cpp @@ -0,0 +1,419 @@ +#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 <memory> +#include <stdexcept> + +#include "../api/Camera.h" +#include "../api/Color.h" +#include "../api/Config.h" +#include "../api/Sprite.h" +#include "../util/dbg.h" +#include "manager/Mediator.h" + +#include "SDLContext.h" +#include "Texture.h" +#include "types.h" + +using namespace crepe; +using namespace std; + +SDLContext::SDLContext(Mediator & mediator) { + dbg_trace(); + if (TTF_Init() == -1) { + throw runtime_error(format("SDL_ttf initialization failed: {}", TTF_GetError())); + } + 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) { + throw runtime_error(format("SDLContext: SDL_Window error: {}", SDL_GetError())); + } + 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); + if (!tmp_renderer) { + throw runtime_error( + format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError())); + } + + this->game_renderer + = {tmp_renderer, [](SDL_Renderer * renderer) { SDL_DestroyRenderer(renderer); }}; + + int img_flags = IMG_INIT_PNG; + if (!(IMG_Init(img_flags) & img_flags)) { + throw runtime_error("SDLContext: SDL_image could not initialize!"); + } + + mediator.sdl_context = *this; +} + +SDLContext::~SDLContext() { + dbg_trace(); + + this->game_renderer.reset(); + this->game_window.reset(); + + // TODO: how are we going to ensure that these are called from the same + // thread that SDL_Init() was called on? This has caused problems for me + // before. + IMG_Quit(); + TTF_Quit(); + SDL_Quit(); +} + +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()); +} + +SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const { + + const Sprite::Data & data = ctx.sprite.data; + + 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; + + 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; + + return SDL_FRect{ + .x = screen_pos.x, + .y = screen_pos.y, + .w = size.x, + .h = size.y, + }; +} + +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, + }); + + 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); +} + +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) + 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); }}; + + SDL_Texture * tmp_texture + = SDL_CreateTextureFromSurface(this->game_renderer.get(), img_surface.get()); + + if (tmp_texture == nullptr) { + 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); }}; + + SDL_SetTextureBlendMode(img_texture.get(), SDL_BLENDMODE_BLEND); + return img_texture; +} + +ivec2 SDLContext::get_size(const Texture & ctx) { + ivec2 size; + SDL_QueryTexture(ctx.get_img(), NULL, NULL, &size.x, &size.y); + return size; +} + +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); +} diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h new file mode 100644 index 0000000..b687f87 --- /dev/null +++ b/src/crepe/facade/SDLContext.h @@ -0,0 +1,365 @@ +#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 "../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" + +#include "EventData.h" +#include "FontFacade.h" + +namespace crepe { +class Texture; +class Mediator; + +/** + * \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 + */ +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: + /** + * \brief Gets the singleton instance of SDLContext. + * \return Reference to the SDLContext instance. + */ + static SDLContext & get_instance(); + +public: + SDLContext(const SDLContext &) = delete; + SDLContext(SDLContext &&) = delete; + SDLContext & operator=(const SDLContext &) = delete; + SDLContext & operator=(SDLContext &&) = delete; + +public: + /** + * \brief Constructs an SDLContext instance. + * Initializes SDL, creates a window and renderer. + */ + SDLContext(Mediator & mediator); + + /** + * \brief Destroys the SDLContext instance. + * Cleans up SDL resources, including the window and renderer. + */ + ~SDLContext(); + +public: + /** + * \brief Retrieves a list of all events from the SDL context. + * + * 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. + * + * \return Events that occurred since last call to `get_events()` + */ + std::vector<EventData> get_events(); + /** + * \brief Fills event_list with triggered window events + * + * This method checks if any window events are triggered and adds them to the event_list. + * + */ + void handle_window_event(const SDL_WindowEvent & window_event, + std::vector<EventData> & event_list); + /** + * \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. + */ + Keycode sdl_to_keycode(SDL_Scancode sdl_key); + + /** + * \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(); + +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. + * \return Pointer to the created SDL_Texture. + */ + std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> + texture_from_path(const std::string & path); + /** + * \brief Gets the size of a texture. + * \param texture Reference to the Texture object. + * \return Width and height of the texture as an integer in pixels. + */ + ivec2 get_size(const Texture & ctx); + +public: + /** + * \brief Draws a sprite to the screen using the specified transform and camera. + * \param RenderContext Reference to rendering data to draw + */ + void draw(const RenderContext & ctx); + + //! Clears the screen, preparing for a new frame. + void clear_screen(); + + //! Presents the rendered frame to the screen. + void present_screen(); + + /** + * \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 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; + + //! 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 new file mode 100644 index 0000000..b1e6463 --- /dev/null +++ b/src/crepe/facade/Sound.cpp @@ -0,0 +1,13 @@ +#include "../api/Asset.h" +#include "../util/dbg.h" + +#include "Sound.h" + +using namespace crepe; +using namespace std; + +Sound::Sound(const Asset & src, Mediator & mediator) : Resource(src, mediator) { + this->sample.load(src.get_path().c_str()); + dbg_trace(); +} +Sound::~Sound() { dbg_trace(); } diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h new file mode 100644 index 0000000..4a5d692 --- /dev/null +++ b/src/crepe/facade/Sound.h @@ -0,0 +1,31 @@ +#pragma once + +#include <soloud/soloud.h> +#include <soloud/soloud_wav.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. + */ +class Sound : public Resource { +public: + Sound(const Asset & src, Mediator & mediator); + ~Sound(); // dbg_trace + +private: + //! Deserialized resource (soloud) + SoLoud::Wav sample; + //! SoundContext uses \c sample + friend class SoundContext; +}; + +} // namespace crepe diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp new file mode 100644 index 0000000..5091e07 --- /dev/null +++ b/src/crepe/facade/SoundContext.cpp @@ -0,0 +1,36 @@ +#include "../util/dbg.h" + +#include "SoundContext.h" + +using namespace crepe; + +SoundContext::SoundContext() { + dbg_trace(); + this->engine.init(); + this->engine.setMaxActiveVoiceCount(this->config.audio.voices); +} + +SoundContext::~SoundContext() { + dbg_trace(); + 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 new file mode 100644 index 0000000..d986c59 --- /dev/null +++ b/src/crepe/facade/SoundContext.h @@ -0,0 +1,81 @@ +#pragma once + +#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. + */ +class SoundContext { +public: + SoundContext(); + virtual ~SoundContext(); + + SoundContext(const SoundContext &) = delete; + SoundContext(SoundContext &&) = delete; + 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; + + //! 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..6fb22e1 --- /dev/null +++ b/src/crepe/facade/Texture.cpp @@ -0,0 +1,29 @@ +#include "../Resource.h" +#include "../facade/SDLContext.h" +#include "../manager/Mediator.h" +#include "../types.h" +#include "../util/dbg.h" + +#include "Texture.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 |