diff options
Diffstat (limited to 'src/crepe/facade/SDLContext.cpp')
-rw-r--r-- | src/crepe/facade/SDLContext.cpp | 388 |
1 files changed, 308 insertions, 80 deletions
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 83e91f8..ad9f1f0 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -1,22 +1,30 @@ #include <SDL2/SDL.h> +#include <SDL2/SDL_blendmode.h> #include <SDL2/SDL_image.h> +#include <SDL2/SDL_keycode.h> #include <SDL2/SDL_rect.h> #include <SDL2/SDL_render.h> #include <SDL2/SDL_surface.h> #include <SDL2/SDL_video.h> +#include <array> #include <cmath> #include <cstddef> +#include <cstdint> #include <functional> #include <iostream> #include <memory> -#include <string> +#include <stdexcept> +#include "../api/Camera.h" +#include "../api/Config.h" #include "../api/Sprite.h" #include "../api/Texture.h" -#include "../api/Transform.h" +#include "../manager/EventManager.h" #include "../util/Log.h" #include "SDLContext.h" +#include "api/Color.h" +#include "types.h" using namespace crepe; using namespace std; @@ -28,31 +36,25 @@ SDLContext & SDLContext::get_instance() { SDLContext::SDLContext() { 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 (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("Crepe Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - this->viewport.w, this->viewport.h, 0); + = 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); }}; 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 @@ -60,9 +62,7 @@ SDLContext::SDLContext() { 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!"); } } @@ -79,67 +79,233 @@ SDLContext::~SDLContext() { 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_Keycode sdl_key) { + static const std::array<Keycode, SDL_NUM_SCANCODES> LOOKUP_TABLE = [] { + std::array<Keycode, SDL_NUM_SCANCODES> table{}; + table.fill(Keycode::NONE); + + table[SDL_SCANCODE_SPACE] = Keycode::SPACE; + table[SDL_SCANCODE_APOSTROPHE] = Keycode::APOSTROPHE; + table[SDL_SCANCODE_COMMA] = Keycode::COMMA; + table[SDL_SCANCODE_MINUS] = Keycode::MINUS; + table[SDL_SCANCODE_PERIOD] = Keycode::PERIOD; + table[SDL_SCANCODE_SLASH] = Keycode::SLASH; + table[SDL_SCANCODE_0] = Keycode::D0; + table[SDL_SCANCODE_1] = Keycode::D1; + table[SDL_SCANCODE_2] = Keycode::D2; + table[SDL_SCANCODE_3] = Keycode::D3; + table[SDL_SCANCODE_4] = Keycode::D4; + table[SDL_SCANCODE_5] = Keycode::D5; + table[SDL_SCANCODE_6] = Keycode::D6; + table[SDL_SCANCODE_7] = Keycode::D7; + table[SDL_SCANCODE_8] = Keycode::D8; + table[SDL_SCANCODE_9] = Keycode::D9; + table[SDL_SCANCODE_SEMICOLON] = Keycode::SEMICOLON; + table[SDL_SCANCODE_EQUALS] = Keycode::EQUAL; + table[SDL_SCANCODE_A] = Keycode::A; + table[SDL_SCANCODE_B] = Keycode::B; + table[SDL_SCANCODE_C] = Keycode::C; + table[SDL_SCANCODE_D] = Keycode::D; + table[SDL_SCANCODE_E] = Keycode::E; + table[SDL_SCANCODE_F] = Keycode::F; + table[SDL_SCANCODE_G] = Keycode::G; + table[SDL_SCANCODE_H] = Keycode::H; + table[SDL_SCANCODE_I] = Keycode::I; + table[SDL_SCANCODE_J] = Keycode::J; + table[SDL_SCANCODE_K] = Keycode::K; + table[SDL_SCANCODE_L] = Keycode::L; + table[SDL_SCANCODE_M] = Keycode::M; + table[SDL_SCANCODE_N] = Keycode::N; + table[SDL_SCANCODE_O] = Keycode::O; + table[SDL_SCANCODE_P] = Keycode::P; + table[SDL_SCANCODE_Q] = Keycode::Q; + table[SDL_SCANCODE_R] = Keycode::R; + table[SDL_SCANCODE_S] = Keycode::S; + table[SDL_SCANCODE_T] = Keycode::T; + table[SDL_SCANCODE_U] = Keycode::U; + table[SDL_SCANCODE_V] = Keycode::V; + table[SDL_SCANCODE_W] = Keycode::W; + table[SDL_SCANCODE_X] = Keycode::X; + table[SDL_SCANCODE_Y] = Keycode::Y; + table[SDL_SCANCODE_Z] = Keycode::Z; + table[SDL_SCANCODE_LEFTBRACKET] = Keycode::LEFT_BRACKET; + table[SDL_SCANCODE_BACKSLASH] = Keycode::BACKSLASH; + table[SDL_SCANCODE_RIGHTBRACKET] = Keycode::RIGHT_BRACKET; + table[SDL_SCANCODE_GRAVE] = Keycode::GRAVE_ACCENT; + table[SDL_SCANCODE_ESCAPE] = Keycode::ESCAPE; + table[SDL_SCANCODE_RETURN] = Keycode::ENTER; + table[SDL_SCANCODE_TAB] = Keycode::TAB; + table[SDL_SCANCODE_BACKSPACE] = Keycode::BACKSPACE; + table[SDL_SCANCODE_INSERT] = Keycode::INSERT; + table[SDL_SCANCODE_DELETE] = Keycode::DELETE; + table[SDL_SCANCODE_RIGHT] = Keycode::RIGHT; + table[SDL_SCANCODE_LEFT] = Keycode::LEFT; + table[SDL_SCANCODE_DOWN] = Keycode::DOWN; + table[SDL_SCANCODE_UP] = Keycode::UP; + table[SDL_SCANCODE_PAGEUP] = Keycode::PAGE_UP; + table[SDL_SCANCODE_PAGEDOWN] = Keycode::PAGE_DOWN; + table[SDL_SCANCODE_HOME] = Keycode::HOME; + table[SDL_SCANCODE_END] = Keycode::END; + table[SDL_SCANCODE_CAPSLOCK] = Keycode::CAPS_LOCK; + table[SDL_SCANCODE_SCROLLLOCK] = Keycode::SCROLL_LOCK; + table[SDL_SCANCODE_NUMLOCKCLEAR] = Keycode::NUM_LOCK; + table[SDL_SCANCODE_PRINTSCREEN] = Keycode::PRINT_SCREEN; + table[SDL_SCANCODE_PAUSE] = Keycode::PAUSE; + table[SDL_SCANCODE_F1] = Keycode::F1; + table[SDL_SCANCODE_F2] = Keycode::F2; + table[SDL_SCANCODE_F3] = Keycode::F3; + table[SDL_SCANCODE_F4] = Keycode::F4; + table[SDL_SCANCODE_F5] = Keycode::F5; + table[SDL_SCANCODE_F6] = Keycode::F6; + table[SDL_SCANCODE_F7] = Keycode::F7; + table[SDL_SCANCODE_F8] = Keycode::F8; + table[SDL_SCANCODE_F9] = Keycode::F9; + table[SDL_SCANCODE_F10] = Keycode::F10; + table[SDL_SCANCODE_F11] = Keycode::F11; + table[SDL_SCANCODE_F12] = Keycode::F12; + table[SDL_SCANCODE_KP_0] = Keycode::KP0; + table[SDL_SCANCODE_KP_1] = Keycode::KP1; + table[SDL_SCANCODE_KP_2] = Keycode::KP2; + table[SDL_SCANCODE_KP_3] = Keycode::KP3; + table[SDL_SCANCODE_KP_4] = Keycode::KP4; + table[SDL_SCANCODE_KP_5] = Keycode::KP5; + table[SDL_SCANCODE_KP_6] = Keycode::KP6; + table[SDL_SCANCODE_KP_7] = Keycode::KP7; + table[SDL_SCANCODE_KP_8] = Keycode::KP8; + table[SDL_SCANCODE_KP_9] = Keycode::KP9; + table[SDL_SCANCODE_LSHIFT] = Keycode::LEFT_SHIFT; + table[SDL_SCANCODE_LCTRL] = Keycode::LEFT_CONTROL; + table[SDL_SCANCODE_LALT] = Keycode::LEFT_ALT; + table[SDL_SCANCODE_LGUI] = Keycode::LEFT_SUPER; + table[SDL_SCANCODE_RSHIFT] = Keycode::RIGHT_SHIFT; + table[SDL_SCANCODE_RCTRL] = Keycode::RIGHT_CONTROL; + table[SDL_SCANCODE_RALT] = Keycode::RIGHT_ALT; + table[SDL_SCANCODE_RGUI] = Keycode::RIGHT_SUPER; + table[SDL_SCANCODE_MENU] = Keycode::MENU; + + return table; + }(); + + if (sdl_key < 0 || sdl_key >= SDL_NUM_SCANCODES) { + return Keycode::NONE; } - */ + + return LOOKUP_TABLE[sdl_key]; } -void SDLContext::clear_screen() { SDL_RenderClear(this->game_renderer.get()); } -void SDLContext::present_screen() { SDL_RenderPresent(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); -void SDLContext::draw(const Sprite & sprite, const Transform & transform, const Camera & cam) { + 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; - 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, + 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_SetRenderDrawColor(this->game_renderer.get(), 0, 0, 0, 255); + SDL_RenderClear(this->game_renderer.get()); +} +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, }; +} +SDL_Rect SDLContext::get_dst_rect(const Sprite & sprite, const vec2 & pos, const Camera & cam, + const vec2 & cam_pos, const double & img_scale) const { - 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), + int width = sprite.height * sprite.aspect_ratio; + int height = sprite.height; + + width *= img_scale * cam.zoom; + height *= img_scale * cam.zoom; + + 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, }; +} - SDL_RenderCopyEx(this->game_renderer.get(), sprite.sprite_image->texture.get(), &srcrect, - &dstrect, transform.rotation, NULL, render_flip); +void SDLContext::draw(const RenderContext & ctx) { + + SDL_RendererFlip render_flip + = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * ctx.sprite.flip.flip_x) + | (SDL_FLIP_VERTICAL * ctx.sprite.flip.flip_y)); + + 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); + + 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); } -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::set_camera(const Camera & cam) { + + // 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); + } + + double screen_aspect = cam.screen.x / cam.screen.y; + double viewport_aspect = cam.viewport_size.x / cam.viewport_size.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; + } + // 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_Rect bg = { + .x = 0, + .y = 0, + .w = static_cast<int>(cam.viewport_size.x), + .h = static_cast<int>(cam.viewport_size.y), + }; + // fill bg color + SDL_RenderFillRect(this->game_renderer.get(), &bg); } uint64_t SDLContext::get_ticks() const { return SDL_GetTicks64(); } @@ -148,9 +314,8 @@ 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); }}; @@ -159,22 +324,85 @@ SDLContext::texture_from_path(const std::string & path) { = SDL_CreateTextureFromSurface(this->game_renderer.get(), img_surface.get()); if (tmp_texture == nullptr) { - throw runtime_error(format("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); }}; + 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; -} -int SDLContext::get_height(const Texture & ctx) const { - int h; - SDL_QueryTexture(ctx.texture.get(), NULL, NULL, NULL, &h); - return h; + +ivec2 SDLContext::get_size(const Texture & ctx) { + ivec2 size; + SDL_QueryTexture(ctx.texture.get(), NULL, NULL, &size.x, &size.y); + return size; } + void SDLContext::delay(int ms) const { SDL_Delay(ms); } + +std::vector<SDLContext::EventData> SDLContext::get_events() { + std::vector<SDLContext::EventData> event_list; + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + event_list.push_back(EventData{ + .event_type = SDLContext::EventType::SHUTDOWN, + }); + break; + case SDL_KEYDOWN: + event_list.push_back(EventData{ + .event_type = SDLContext::EventType::KEYDOWN, + .key = sdl_to_keycode(event.key.keysym.scancode), + .key_repeat = (event.key.repeat != 0), + }); + break; + case SDL_KEYUP: + event_list.push_back(EventData{ + .event_type = SDLContext::EventType::KEYUP, + .key = sdl_to_keycode(event.key.keysym.scancode), + }); + break; + case SDL_MOUSEBUTTONDOWN: + event_list.push_back(EventData{ + .event_type = SDLContext::EventType::MOUSEDOWN, + .mouse_button = sdl_to_mousebutton(event.button.button), + .mouse_position = {event.button.x, event.button.y}, + }); + break; + case SDL_MOUSEBUTTONUP: { + int x, y; + SDL_GetMouseState(&x, &y); + event_list.push_back(EventData{ + .event_type = SDLContext::EventType::MOUSEUP, + .mouse_button = sdl_to_mousebutton(event.button.button), + .mouse_position = {event.button.x, event.button.y}, + }); + } break; + + case SDL_MOUSEMOTION: { + event_list.push_back( + EventData{.event_type = SDLContext::EventType::MOUSEMOVE, + .mouse_position = {event.motion.x, event.motion.y}, + .rel_mouse_move = {event.motion.xrel, event.motion.yrel}}); + } break; + + case SDL_MOUSEWHEEL: { + event_list.push_back(EventData{ + .event_type = SDLContext::EventType::MOUSEWHEEL, + .mouse_position = {event.motion.x, event.motion.y}, + // TODO: why is this needed? + .scroll_direction = event.wheel.y < 0 ? -1 : 1, + .scroll_delta = event.wheel.preciseY, + }); + } break; + } + } + return event_list; +} +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); +} |