aboutsummaryrefslogtreecommitdiff
path: root/src/crepe/facade/SDLContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/crepe/facade/SDLContext.cpp')
-rw-r--r--src/crepe/facade/SDLContext.cpp458
1 files changed, 360 insertions, 98 deletions
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index 8d8a87d..6c93fb2 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -1,48 +1,50 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_blendmode.h>
+#include <SDL2/SDL_hints.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 <stdexcept>
#include "../api/Camera.h"
+#include "../api/Color.h"
#include "../api/Config.h"
#include "../api/Sprite.h"
-#include "../api/Texture.h"
-#include "../util/Log.h"
+#include "../util/dbg.h"
+#include "api/Text.h"
+#include "api/Transform.h"
+#include "facade/Font.h"
+#include "manager/Mediator.h"
+#include "util/AbsolutePosition.h"
#include "SDLContext.h"
-#include "api/Color.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();
-
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);
+ 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()));
}
@@ -51,8 +53,8 @@ SDLContext::SDLContext() {
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()));
+ throw runtime_error(format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError())
+ );
}
this->game_renderer
@@ -62,6 +64,12 @@ SDLContext::SDLContext() {
if (!(IMG_Init(img_flags) & img_flags)) {
throw runtime_error("SDLContext: SDL_image could not initialize!");
}
+
+ if (TTF_Init() == -1) {
+ throw runtime_error(format("SDL_ttf initialization failed: {}", TTF_GetError()));
+ }
+
+ mediator.sdl_context = *this;
}
SDLContext::~SDLContext() {
@@ -73,78 +81,187 @@ SDLContext::~SDLContext() {
// 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.
+ TTF_Quit();
IMG_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;
}
-void SDLContext::clear_screen() {
- SDL_SetRenderDrawColor(this->game_renderer.get(), 0, 0, 0, 255);
- SDL_RenderClear(this->game_renderer.get());
+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::present_screen() { SDL_RenderPresent(this->game_renderer.get()); }
-SDL_Rect SDLContext::get_src_rect(const Sprite & sprite) const {
- return SDL_Rect{
- .x = sprite.mask.x,
- .y = sprite.mask.y,
- .w = sprite.mask.w,
- .h = sprite.mask.h,
- };
+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_Rect SDLContext::get_dst_rect(const Sprite & sprite, const vec2 & pos, const Camera & cam,
- const vec2 & cam_pos, const double & img_scale) const {
- int width = sprite.height * sprite.aspect_ratio;
- int height = sprite.height;
+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;
+ vec2 screen_pos = ctx.pos;
+
+ 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;
- width *= img_scale * cam.zoom;
- height *= img_scale * cam.zoom;
+ if (ctx.sprite.data.world_space) {
+ screen_pos = (screen_pos - cam_aux_data.cam_pos + cam_aux_data.zoomed_viewport / 2)
+ * cam_aux_data.render_scale
+ - size / 2 + cam_aux_data.bar_size;
+ } else {
+ screen_pos
+ = (screen_pos + cam_aux_data.zoomed_viewport / 2) * cam_aux_data.render_scale
+ - size / 2 + cam_aux_data.bar_size;
+ }
- return SDL_Rect{
- .x = static_cast<int>((pos.x - cam_pos.x + (cam.viewport_size.x / 2) - width / 2)),
- .y = static_cast<int>((pos.y - cam_pos.y + (cam.viewport_size.y / 2) - height / 2)),
- .w = width,
- .h = height,
+ return SDL_FRect {
+ .x = screen_pos.x,
+ .y = screen_pos.y,
+ .w = size.x,
+ .h = size.y,
};
}
void SDLContext::draw(const RenderContext & ctx) {
-
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");
+ const Sprite::Data & data = ctx.sprite.data;
SDL_RendererFlip render_flip
- = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * ctx.sprite.flip.flip_x)
- | (SDL_FLIP_VERTICAL * ctx.sprite.flip.flip_y));
+ = (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_Rect srcrect = this->get_src_rect(ctx.sprite);
- SDL_Rect dstrect
- = this->get_dst_rect(ctx.sprite, ctx.pos, ctx.cam, ctx.cam_pos, ctx.scale);
+ SDL_FRect dstrect = this->get_dst_rect(SDLContext::DestinationRectangleData {
+ .sprite = ctx.sprite,
+ .texture = ctx.texture,
+ .pos = ctx.pos,
+ .img_scale = ctx.scale,
+ });
- this->set_color_texture(ctx.sprite.sprite_image, ctx.sprite.color);
- SDL_RenderCopyEx(this->game_renderer.get(), ctx.sprite.sprite_image.texture.get(),
- &srcrect, &dstrect, ctx.angle, NULL, render_flip);
+ 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::set_camera(const Camera & cam) {
+void SDLContext::draw_text(const RenderText & data) {
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
+
+ const Text & text = data.text;
+ const Font & font = data.font;
+ vec2 absoluut_pos = AbsolutePosition::get_position(data.transform, data.text.offset);
+ std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> font_surface;
+ std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> font_texture;
+ SDL_Color color {
+ .r = text.data.text_color.r,
+ .g = text.data.text_color.g,
+ .b = text.data.text_color.b,
+ .a = text.data.text_color.a,
+ };
+ SDL_Surface * tmp_font_surface
+ = TTF_RenderText_Solid(font.get_font(), text.text.c_str(), color);
+ if (tmp_font_surface == NULL) {
+ throw runtime_error(format("draw_text: font surface error: {}", SDL_GetError()));
+ }
+ font_surface = {tmp_font_surface, [](SDL_Surface * surface) { SDL_FreeSurface(surface); }};
+
+ SDL_Texture * tmp_font_texture
+ = SDL_CreateTextureFromSurface(this->game_renderer.get(), font_surface.get());
+ if (tmp_font_texture == NULL) {
+ throw runtime_error(format("draw_text: font texture error: {}", SDL_GetError()));
+ }
+ font_texture
+ = {tmp_font_texture, [](SDL_Texture * texture) { SDL_DestroyTexture(texture); }};
+
+ vec2 size = text.dimensions * cam_aux_data.render_scale * data.transform.scale;
+ vec2 screen_pos = absoluut_pos;
+ if (text.data.world_space) {
+ screen_pos = (screen_pos - cam_aux_data.cam_pos + (cam_aux_data.zoomed_viewport) / 2)
+ * cam_aux_data.render_scale
+ - size / 2 + cam_aux_data.bar_size;
+ } else {
+ screen_pos
+ = (screen_pos + (cam_aux_data.zoomed_viewport) / 2) * cam_aux_data.render_scale
+ - size / 2 + cam_aux_data.bar_size;
+ }
+
+ SDL_FRect dstrect {
+ .x = screen_pos.x,
+ .y = screen_pos.y,
+ .w = size.x,
+ .h = size.y,
+ };
+
+ SDL_RenderCopyExF(
+ this->game_renderer.get(), font_texture.get(), NULL, &dstrect, data.transform.rotation,
+ NULL, SDL_FLIP_NONE
+ );
+}
+
+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);
@@ -152,46 +269,55 @@ void SDLContext::set_camera(const Camera & cam) {
SDL_SetWindowSize(this->game_window.get(), cam.screen.x, cam.screen.y);
}
- double screen_aspect = cam.screen.x / cam.screen.y;
- double viewport_aspect = cam.viewport_size.x / cam.viewport_size.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;
- SDL_Rect view;
// calculate black bars
if (screen_aspect > viewport_aspect) {
- // letterboxing
- view.h = cam.screen.y / cam.zoom;
- view.w = cam.screen.y * viewport_aspect;
- view.x = (cam.screen.x - view.w) / 2;
- view.y = 0;
- } else {
// pillarboxing
- view.h = cam.screen.x / viewport_aspect;
- view.w = cam.screen.x / cam.zoom;
- view.x = 0;
- view.y = (cam.screen.y - view.h) / 2;
+ 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;
}
- // set drawing area
- SDL_RenderSetViewport(this->game_renderer.get(), &view);
-
- SDL_RenderSetLogicalSize(this->game_renderer.get(), static_cast<int>(cam.viewport_size.x),
- static_cast<int>(cam.viewport_size.y));
- // set bg color
- SDL_SetRenderDrawColor(this->game_renderer.get(), cam.bg_color.r, cam.bg_color.g,
- cam.bg_color.b, cam.bg_color.a);
+ 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 = static_cast<int>(cam.viewport_size.x),
- .h = static_cast<int>(cam.viewport_size.y),
+ .w = cam.screen.x,
+ .h = cam.screen.y,
};
+
// fill bg color
SDL_RenderFillRect(this->game_renderer.get(), &bg);
}
-uint64_t SDLContext::get_ticks() const { return SDL_GetTicks64(); }
-
std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>>
SDLContext::texture_from_path(const std::string & path) {
@@ -216,16 +342,152 @@ SDLContext::texture_from_path(const std::string & path) {
return img_texture;
}
-
-ivec2 SDLContext::get_size(const Texture & ctx){
+ivec2 SDLContext::get_size(const Texture & ctx) {
ivec2 size;
- SDL_QueryTexture(ctx.texture.get(), NULL, NULL, &size.x, &size.y);
+ SDL_QueryTexture(ctx.get_img(), NULL, NULL, &size.x, &size.y);
return size;
}
-void SDLContext::delay(int ms) const { SDL_Delay(ms); }
+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.texture.get(), color.r, color.g, color.b);
- SDL_SetTextureAlphaMod(texture.texture.get(), color.a);
+ 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);
}