diff --git a/src/core/renderer.cpp b/src/core/renderer.cpp new file mode 100644 index 0000000..d8c800d --- /dev/null +++ b/src/core/renderer.cpp @@ -0,0 +1,110 @@ +#include "renderer.h" +#include +#include +#include +#include +#include +#include + +namespace rogue { +RenderData::~RenderData() { + SDL_DestroyRenderer(this->renderer); // also frees textures + SDL_DestroyWindow(this->window); + Render::clear_render_data(); +} + +RenderDataSetup &RenderDataSetup::with_window(char const *name, Uint32 flags) { + assert(this->window == nullptr && "Cannot initialize window twice"); + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(0, &mode); + this->window = SDL_CreateWindow(name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, mode.w, mode.h, flags); + return *this; +} + +RenderDataSetup &RenderDataSetup::with_renderer(Uint32 flags) { + assert(this->renderer == nullptr && "Cannot initialize renderer twice"); + assert(this->window != nullptr && "Cannot initialize renderer before window"); + this->renderer = SDL_CreateRenderer(this->window, -1, flags); + return *this; +} + +RenderDataSetup &RenderDataSetup::with_resource_path(std::filesystem::path path) { + assert(!this->resource_base_path.has_value() && "Cannot set base resource path twice"); + this->resource_base_path = path; + return *this; +} + +RenderDataSetup &RenderDataSetup::with_sprite(std::filesystem::path sprite_path) { + assert(this->renderer != nullptr && "Cannot create sprites without a renderer"); + assert(this->resource_base_path.has_value() && "Resource base path has to be set before loading sprites"); + std::filesystem::path complete_path{this->resource_base_path.value()/sprite_path}; + SDL_Log("Adding sprite: %s", complete_path.c_str()); + assert(std::filesystem::exists(complete_path) && "Sprite path has to exist"); + if(SDL_Texture *texture{IMG_LoadTexture(this->renderer, complete_path.native().c_str())}) { + this->sprites.push_back(texture); + } else { + SDL_Log("Failed to load texture %s reason: %s", complete_path.c_str(), SDL_GetError()); + } + return *this; +} + +RenderData RenderDataSetup::build() { + assert(this->window != nullptr && "Cannot build RenderData without a window"); + assert(this->renderer != nullptr && "Cannot build RenderData without a renderer"); + assert(this->sprites.size() > 0 && "Cannot build RenderData with 0 sprites"); + RenderData data; + data.window=this->window; + data.renderer=this->renderer; + data.sprites = this->sprites; + return data; +} + +Tile Render::camera_center{0u, 0u}; +RenderData* Render::data{nullptr}; +unsigned Render::camera_width{10}; +Tile Render::camera_offset{0, 0}; +int Render::tile_screen_width{2}; +int Render::half_tile_screen_width{1}; + +#define assert_render_initialized() assert(Render::data != nullptr && __func__ && " provide_render_data has to be called before any other Render functions") + +void Render::clear_render_data() { + assert_render_initialized(); + Render::data = nullptr; +} + +void Render::provide_render_data(RenderData &data) { + Render::data = &data; +} + +void Render::clear(Tile camera_center) { + assert_render_initialized(); + SDL_SetRenderDrawColor(Render::data->renderer, 0u, 0u, 0u, 255u); + SDL_RenderClear(Render::data->renderer); + + int width, height; + SDL_GetWindowSize(Render::data->window, &width, &height); + Render::tile_screen_width = int(width / camera_width); + Render::half_tile_screen_width = Render::tile_screen_width >> 1; + + Render::camera_center = camera_center; + Render::camera_offset.x = -Render::camera_center.x * Render::tile_screen_width + (width >> 1); + Render::camera_offset.y = -Render::camera_center.y * Render::tile_screen_width + (height >> 1); +} + +void Render::draw(Tile world_space_tile, Sprite sprite) { + assert_render_initialized(); + SDL_Rect const rect{ + .x = (world_space_tile.x * Render::tile_screen_width) - Render::half_tile_screen_width + Render::camera_offset.x, + .y = (world_space_tile.y * Render::tile_screen_width) - Render::half_tile_screen_width + Render::camera_offset.y, + .w = Render::tile_screen_width, + .h = Render::tile_screen_width + }; + SDL_RenderCopy(Render::data->renderer, Render::data->sprites[sprite], 0 , &rect); +} + +void Render::present() { + assert_render_initialized(); + SDL_RenderPresent(Render::data->renderer); +} +} diff --git a/src/core/renderer.h b/src/core/renderer.h new file mode 100644 index 0000000..e2e287b --- /dev/null +++ b/src/core/renderer.h @@ -0,0 +1,61 @@ +#ifndef ROGUE_RENDERER_H +#define ROGUE_RENDERER_H + +#include "roguedefs.h" +#include +#include +#include +#include +#include +#include + +namespace rogue { +// Contains all data required to render the game world +class RenderData { + friend class RenderDataSetup; + friend class Render; +public: ~RenderData(); +private: RenderData() = default; + SDL_Window *window{nullptr}; + SDL_Renderer *renderer{nullptr}; + std::vector sprites{}; +}; + +// Global interface for rendering +class Render { +private: + friend class RenderData; + static void clear_render_data(); +public: + static void provide_render_data(RenderData &data); + + static void clear(Tile camera_center); + static void draw(Tile world_space_tile, Sprite sprite); + static void present(); +private: + static Tile camera_center; + static unsigned camera_width; + static Tile camera_offset; + static int tile_screen_width; + static int half_tile_screen_width; + static RenderData *data; +}; + +// Render configuration, builder for RenderData +class RenderDataSetup { +public: + RenderDataSetup() = default; + RenderDataSetup &with_window(char const *name, Uint32 flags); + RenderDataSetup &with_renderer(Uint32 flags); + RenderDataSetup &with_resource_path(std::filesystem::path base_path); + RenderDataSetup &with_sprite(std::filesystem::path sprite_path); + RenderData build(); +private: + std::optional resource_base_path; + SDL_Window *window{nullptr}; + SDL_Renderer *renderer{nullptr}; + std::vector sprites{}; +}; +} + +#endif // !ROGUE_RENDERER_H diff --git a/src/core/roguedefs.h b/src/core/roguedefs.h new file mode 100644 index 0000000..a750958 --- /dev/null +++ b/src/core/roguedefs.h @@ -0,0 +1,17 @@ +#ifndef ROGUEDEFS_H +#define ROGUEDEFS_H + +#include "SDL2/SDL_rect.h" + +enum class Directions : unsigned short { + NORTH = 0x1, + EAST = 0x2, + SOUTH = 0x4, + WEST = 0x8, +}; + +typedef SDL_Point Tile; +typedef SDL_Point Chunk; +typedef unsigned Sprite; + +#endif // !ROGUEDEFS_H