diff --git a/CMakeLists.txt b/CMakeLists.txt index e4f447e..af4121c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,9 @@ project(rla_iipp) # Defines the project name. include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) # Includes the contents of the conanbuildinfo.cmake file. conan_basic_setup() # Prepares the CMakeList.txt for Conan. +set(CMAKE_CXX_STANDARD 20) # c++ 20 + # $source_files is a space-delimited list of filenames. -add_executable(rla_iipp src/main.cpp src/engine/engine.hpp src/engine/game/game.hpp src/engine/rendering/renderer.hpp src/game/visualizer.hpp src/game/visualizer.cpp src/engine/rendering/sdl/sdlrenderer.cpp src/engine/rendering/sdl/sdlrenderer.hpp src/engine/rendering/sdl/sdlrenderer.cpp src/engine/rendering/sdl/sdlrenderer.hpp src/engine/engine.cpp src/engine/engine.hpp src/engine/rendering/sdl/sdltexturemanager.cpp src/engine/rendering/sdl/sdltexturemanager.hpp) # Specifies the executable to build. +add_executable(rla_iipp src/main.cpp src/engine/engine.hpp src/engine/game/game.hpp src/engine/rendering/renderer.hpp src/game/visualizer.hpp src/game/visualizer.cpp src/engine/rendering/sdl/sdlrenderer.cpp src/engine/rendering/sdl/sdlrenderer.hpp src/engine/rendering/sdl/sdlrenderer.cpp src/engine/rendering/sdl/sdlrenderer.hpp src/engine/engine.cpp src/engine/engine.hpp src/engine/rendering/sdl/sdltexturemanager.cpp src/engine/rendering/sdl/sdltexturemanager.hpp src/engine/resources/resourcemanager.hpp src/engine/rendering/sdl/sdlfontmanager.cpp src/engine/rendering/sdl/sdlfontmanager.hpp) # Specifies the executable to build. target_link_libraries(rla_iipp ${CONAN_LIBS}) # Specifies what libraries to link, using Conan. file(COPY assets DESTINATION ${CMAKE_BINARY_DIR}/bin) \ No newline at end of file diff --git a/assets/fonts/Consolas-Regular.ttf b/assets/fonts/Consolas-Regular.ttf new file mode 100644 index 0000000..cd4980c Binary files /dev/null and b/assets/fonts/Consolas-Regular.ttf differ diff --git a/assets/fonts/lazy.ttf b/assets/fonts/lazy.ttf new file mode 100755 index 0000000..eb1000b Binary files /dev/null and b/assets/fonts/lazy.ttf differ diff --git a/assets/sprites/character.bmp b/assets/sprites/character.bmp new file mode 100644 index 0000000..35b9c6a Binary files /dev/null and b/assets/sprites/character.bmp differ diff --git a/conanfile.txt b/conanfile.txt index 00c6c16..bdee208 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -2,6 +2,6 @@ sdl/2.0.16 sdl_ttf/2.0.15 sdl_image/2.0.5 -lua/5.4.3 +fmt/8.0.1 [generators] cmake \ No newline at end of file diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 5769cfd..7557117 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -6,17 +6,16 @@ #include #include #include -#include +#include + using namespace std::chrono_literals; void Engine::start_loop() { const auto one_second_milli = std::chrono::milliseconds(1s); if(game == nullptr) { - printf("game is null\n"); - return; + throw MissingGameException(); } if(renderer == nullptr) { - printf("renderer is null\n"); - return; + throw MissingRendererException(); } RendererParams renderParams = game->get_renderer_params(); renderer->initialize(renderParams); @@ -49,10 +48,18 @@ Engine::render_fps(std::chrono::time_point end, double frames = 1; std::chrono::duration dur = end - lastframe; if(SHOW_FPS) { - printf("fps: %f\n", frames/dur.count()); + //printf("fps: %f\n", frames/dur.count()); + this->renderer->draw_text(DEFAULT_FONT, fmt::format("{:.2f}", frames/dur.count()), 0, 0); } return end; } Engine::~Engine() = default; +const char *MissingGameException::what() const noexcept { + return MISSING_GAME.c_str(); +} + +const char *MissingRendererException::what() const noexcept { + return MISSING_RENDERER.c_str(); +} diff --git a/src/engine/engine.hpp b/src/engine/engine.hpp index df87efa..1aca33d 100644 --- a/src/engine/engine.hpp +++ b/src/engine/engine.hpp @@ -4,12 +4,21 @@ #ifndef RLA_IIPP_ENGINE_HPP #define RLA_IIPP_ENGINE_HPP +#define FMT_HEADER_ONLY #include #include #include "game/game.hpp" +#include #include "rendering/renderer.hpp" +static const std::string MISSING_GAME = "Game pointer is null"; +static const std::string MISSING_RENDERER = "Renderer pointer is null"; static const bool SHOW_FPS = true; static const std::string RESOURCE_DIR = "assets"; +static const TextRenderDetails DEFAULT_FONT = TextRenderDetails( + "fonts/Consolas-Regular.ttf", + 12, + COLOR_WHITE + ); class Engine { public: Engine(std::unique_ptr game, std::unique_ptr renderer); @@ -23,5 +32,15 @@ private: std::unique_ptr renderer = nullptr; }; +class MissingGameException : public std::exception { +public: + const char *what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW override; +}; + +class MissingRendererException : public std::exception { +public: + const char *what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW override; +}; + #endif //RLA_IIPP_ENGINE_HPP diff --git a/src/engine/rendering/renderer.hpp b/src/engine/rendering/renderer.hpp index c10b35d..85aa035 100644 --- a/src/engine/rendering/renderer.hpp +++ b/src/engine/rendering/renderer.hpp @@ -5,6 +5,18 @@ #ifndef RLA_IIPP_RENDERER_HPP #define RLA_IIPP_RENDERER_HPP #include +#include + +struct SpriteSheet { + std::string path; + int sprite_width; + int sprite_height; +}; + +struct Sprite { + SpriteSheet *sprite_sheet; + int index; +}; struct Color { public: @@ -15,11 +27,22 @@ public: Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) : r(r), g(g), b(b), a(a) {} }; +#define COLOR_BLACK Color(0, 0, 0, 255) +#define COLOR_WHITE Color(255, 255, 255, 255) + +struct TextRenderDetails { + std::string font_path; + int size; + Color color; + + TextRenderDetails(const std::string &fontPath, int size, const Color &color) : font_path( + fontPath), size(size), color(color) {} +}; struct RendererParams { std::string title; - unsigned int width; - unsigned int height; + int width; + int height; }; class Renderer { @@ -27,7 +50,8 @@ public: virtual void initialize(RendererParams params)=0; virtual void flush() = 0; virtual void draw_point(int x, int y, Color color) = 0; - virtual void draw_text(std::string text,int x, int y)=0; + virtual void draw_text(TextRenderDetails details, std::string text,int x, int y)=0; + virtual void draw_sprite(Sprite sprite, int x, int y)=0; }; #endif //RLA_IIPP_RENDERER_HPP diff --git a/src/engine/rendering/sdl/sdlfontmanager.cpp b/src/engine/rendering/sdl/sdlfontmanager.cpp new file mode 100644 index 0000000..72a8192 --- /dev/null +++ b/src/engine/rendering/sdl/sdlfontmanager.cpp @@ -0,0 +1,19 @@ +// +// Created by m on 12/5/21. +// + +#include "sdlfontmanager.hpp" + +std::string SdlFontManager::get_key(SdlFontArgs args) { + return fmt::format("{}_{}", args.size, get_resource_path(args.font_path)); +} + +TTF_Font *SdlFontManager::load_resource(SdlFontArgs args) { + std::string path = get_resource_path(args.font_path); + TTF_Font *font = TTF_OpenFont(path.c_str(), args.size); + if(font == nullptr) { + printf("Could not load font: %s\n", TTF_GetError()); + return nullptr; + } + return font; +} diff --git a/src/engine/rendering/sdl/sdlfontmanager.hpp b/src/engine/rendering/sdl/sdlfontmanager.hpp new file mode 100644 index 0000000..b222310 --- /dev/null +++ b/src/engine/rendering/sdl/sdlfontmanager.hpp @@ -0,0 +1,28 @@ +// +// Created by m on 12/5/21. +// + +#ifndef RLA_IIPP_SDLFONTMANAGER_HPP +#define RLA_IIPP_SDLFONTMANAGER_HPP + + +#include +#include +#include +#include "../../resources/resourcemanager.hpp" + +struct SdlFontArgs { + int size; + std::string font_path; + + SdlFontArgs(int size, std::string fontPath) : size(size), font_path(std::move(fontPath)) {} +}; + +class SdlFontManager : public ResourceManager { + std::string get_key(SdlFontArgs args) override; + + TTF_Font *load_resource(SdlFontArgs args) override; +}; + + +#endif //RLA_IIPP_SDLFONTMANAGER_HPP diff --git a/src/engine/rendering/sdl/sdlrenderer.cpp b/src/engine/rendering/sdl/sdlrenderer.cpp index c33daa1..47c18c5 100644 --- a/src/engine/rendering/sdl/sdlrenderer.cpp +++ b/src/engine/rendering/sdl/sdlrenderer.cpp @@ -11,7 +11,11 @@ void SdlRenderer::initialize(RendererParams params) { } auto img_flags = IMG_INIT_PNG; if(!(IMG_Init(img_flags) & img_flags)) { - printf("Failed to init image loading: %s", IMG_GetError()); + printf("Failed to init image loading: %s\n", IMG_GetError()); + exit(EXIT_FAILURE); + } + if(TTF_Init() < 0) { + printf("Failed to initialize font loading: %s\n", TTF_GetError()); exit(EXIT_FAILURE); } SDL_Window* sdl_window = SDL_CreateWindow(params.title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, params.width, params.height, SDL_WINDOW_SHOWN); @@ -24,7 +28,9 @@ void SdlRenderer::initialize(RendererParams params) { SDL_Renderer* sdl_renderer = SDL_CreateRenderer(this->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); this->renderer = sdl_renderer; - this->texture_manager = std::make_unique(); + this->texture_manager = std::make_unique(this->renderer); + this->font_manager = std::make_unique(); + this->renderer_params = params; SDL_RenderClear(this->renderer); } @@ -32,7 +38,7 @@ void SdlRenderer::initialize(RendererParams params) { void SdlRenderer::flush() { SDL_RenderPresent(this->renderer); - SDL_SetRenderDrawColor(this->renderer, 0, 0, 0, 255); + SDL_SetRenderDrawColor(this->renderer, COLOR_BLACK.r, COLOR_BLACK.g, COLOR_BLACK.b, COLOR_BLACK.a); SDL_RenderClear(this->renderer); } @@ -41,8 +47,10 @@ void SdlRenderer::draw_point(int x, int y, Color color) { SDL_RenderDrawPoint(this->renderer, x, y); } -void SdlRenderer::draw_text(std::string text, int x, int y) { - +void SdlRenderer::draw_text(TextRenderDetails details, std::string text, int x, int y) { + TTF_Font *font = this->font_manager->fetch_resource(SdlFontArgs(details.size, details.font_path)); + SDL_Texture *texture = this->texture_manager->load_text_as_texture(text, font, details.color); + render_texture(x, y, texture, nullptr); } SdlRenderer::~SdlRenderer() { @@ -54,5 +62,37 @@ SdlRenderer::~SdlRenderer() { // exit subsystems IMG_Quit(); + TTF_Quit(); SDL_Quit(); } + +void SdlRenderer::render_texture(int x, int y, SDL_Texture *texture, SDL_Rect *src) { + int w, h; + if(src == nullptr) { + SDL_QueryTexture(texture, nullptr, nullptr, &w, &h); + } + else { + w = src->w; + h = src->h; + } + if(x+w >= renderer_params.width) { + x = renderer_params.width - w; + } + if(y+h >= renderer_params.height) { + y = renderer_params.height - h; + } + SDL_Rect rect = {x,y,w,h}; + SDL_RenderCopy(this->renderer, texture, src, &rect); +} + +void SdlRenderer::draw_sprite(Sprite sprite, int x, int y) { + SpriteSheet *sprite_sheet = sprite.sprite_sheet; + SDL_Texture *texture = this->texture_manager->fetch_resource(sprite_sheet->path); + int w, h; + SDL_QueryTexture(texture, nullptr, nullptr, &w, &h); + int l = (sprite_sheet->sprite_width * sprite.index) / w; + int src_y = l*sprite_sheet->sprite_height; + int src_x = (sprite_sheet->sprite_width * sprite.index) % w; + auto source = SDL_Rect{src_x, src_y, sprite_sheet->sprite_width, sprite_sheet->sprite_height}; + render_texture(x, y, texture, &source); +} diff --git a/src/engine/rendering/sdl/sdlrenderer.hpp b/src/engine/rendering/sdl/sdlrenderer.hpp index 409efc3..7347b0a 100644 --- a/src/engine/rendering/sdl/sdlrenderer.hpp +++ b/src/engine/rendering/sdl/sdlrenderer.hpp @@ -6,27 +6,29 @@ #define RLA_IIPP_SDLRENDERER_HPP #include "../renderer.hpp" #include "sdltexturemanager.hpp" +#include "sdlfontmanager.hpp" #include #include #include class SdlRenderer : public Renderer { - void initialize(RendererParams params) override; - - void flush() override; - - void draw_point(int x, int y, Color color) override; - public: ~SdlRenderer(); + void draw_text(TextRenderDetails details, std::string text, int x, int y) override; + void initialize(RendererParams params) override; + void flush() override; + void draw_point(int x, int y, Color color) override; -public: - void draw_text(std::string text, int x, int y) override; + void draw_sprite(Sprite sprite, int x, int y) override; private: + RendererParams renderer_params; SDL_Window* window = nullptr; SDL_Renderer* renderer = nullptr; std::unique_ptr texture_manager = nullptr; + std::unique_ptr font_manager = nullptr; + + void render_texture(int x, int y, SDL_Texture *texture, SDL_Rect *src); }; diff --git a/src/engine/rendering/sdl/sdltexturemanager.cpp b/src/engine/rendering/sdl/sdltexturemanager.cpp index 0eff93b..66fe717 100644 --- a/src/engine/rendering/sdl/sdltexturemanager.cpp +++ b/src/engine/rendering/sdl/sdltexturemanager.cpp @@ -4,27 +4,6 @@ #include "sdltexturemanager.hpp" -SdlTextureManager::SdlTextureManager() { - this->texture_map = std::unordered_map(); -} - -SDL_Texture *SdlTextureManager::load_texture(const std::string &path, SDL_Renderer *renderer) { - std::string full_path = RESOURCE_DIR + "/" + path; - if(this->texture_map.find(full_path) == this->texture_map.end()) { - SDL_Surface* surface = load_surface(full_path); - if(surface == nullptr) { - return nullptr; - } - SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); - if(texture == nullptr) { - printf("failed ot load image into texture: %s\n", IMG_GetError()); - } - this->texture_map[path] = texture; - SDL_FreeSurface(surface); - } - return this->texture_map[path]; -} - SDL_Surface *SdlTextureManager::load_surface(const std::string& path) { SDL_Surface* surface = IMG_Load(path.c_str()); if(surface == nullptr) { @@ -34,7 +13,32 @@ SDL_Surface *SdlTextureManager::load_surface(const std::string& path) { } SdlTextureManager::~SdlTextureManager() { - std::for_each(this->texture_map.begin(), this->texture_map.end(), [](const std::pair& pair) { + std::for_each(this->resource_map.begin(), this->resource_map.end(), [](const std::pair& pair) { SDL_DestroyTexture(pair.second); }); } + +SDL_Texture *SdlTextureManager::load_resource(std::string args) { + std::string full_path = get_resource_path(args); + SDL_Surface* surface = load_surface(full_path); + if(surface == nullptr) { + return nullptr; + } + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_FreeSurface(surface); + return texture; +} + +std::string SdlTextureManager::get_key(std::string args) { + return get_resource_path(args); +} + +SDL_Texture *SdlTextureManager::load_text_as_texture(const std::string& text, TTF_Font *font, Color color) { + SDL_Surface *surface = TTF_RenderText_Solid(font, text.c_str(), {color.r, color.g, color.b}); + if(surface == nullptr) { + return nullptr; + } + SDL_Texture *texture = SDL_CreateTextureFromSurface(this->renderer, surface); + SDL_FreeSurface(surface); + return texture; +} diff --git a/src/engine/rendering/sdl/sdltexturemanager.hpp b/src/engine/rendering/sdl/sdltexturemanager.hpp index 6134de2..3e3ae50 100644 --- a/src/engine/rendering/sdl/sdltexturemanager.hpp +++ b/src/engine/rendering/sdl/sdltexturemanager.hpp @@ -8,18 +8,24 @@ #include #include #include "../../engine.hpp" +#include "../../resources/resourcemanager.hpp" #include #include +#include + +class SdlTextureManager : public ResourceManager { +protected: + SDL_Texture *load_resource(std::string args) override; + std::string get_key(std::string args) override; -class SdlTextureManager { public: - SDL_Texture *load_texture(const std::string &path, SDL_Renderer *renderer); - SdlTextureManager(); + explicit SdlTextureManager(SDL_Renderer *renderer) : renderer(renderer) {} + SDL_Texture *load_text_as_texture(const std::string& text, TTF_Font *font, Color color); + ~SdlTextureManager(); - virtual ~SdlTextureManager(); private: - std::unordered_map texture_map; + SDL_Renderer *renderer = nullptr; static SDL_Surface* load_surface(const std::string& path); }; diff --git a/src/engine/resources/resourcemanager.hpp b/src/engine/resources/resourcemanager.hpp new file mode 100644 index 0000000..f1527af --- /dev/null +++ b/src/engine/resources/resourcemanager.hpp @@ -0,0 +1,32 @@ +// +// Created by m on 12/4/21. +// + +#ifndef RLA_IIPP_RESOURCEMANAGER_HPP +#define RLA_IIPP_RESOURCEMANAGER_HPP +#include +#include "../engine.hpp" + +template +class ResourceManager { +public: + virtual T* fetch_resource(A args) { + K key = get_key(args); + if(this->resource_map.find(key) == this->resource_map.end()) { + T* resource = this->load_resource(args); + this->resource_map[key] = resource; + } + return this->resource_map[key]; + } + +protected: + std::unordered_map resource_map; + virtual K get_key(A args)=0; + virtual T* load_resource(A args)=0; + std::string get_resource_path(std::string path) { + return fmt::format("{}/{}", RESOURCE_DIR, path); + } +}; + + +#endif //RLA_IIPP_RESOURCEMANAGER_HPP diff --git a/src/game/visualizer.cpp b/src/game/visualizer.cpp index 9363e7f..6bad2ea 100644 --- a/src/game/visualizer.cpp +++ b/src/game/visualizer.cpp @@ -3,10 +3,6 @@ // #include "visualizer.hpp" -#define TITLE "test" -#define WIN_WIDTH 800 -#define WIN_HEIGHT 600 -#define TARGET_FPS 60 bool Visualizer::update() { this->x += 1; if(this->x > WIN_WIDTH) { @@ -25,7 +21,11 @@ unsigned int Visualizer::get_framerate() { } void Visualizer::render(Renderer *renderer) { - Color color = Color(255, 255, 255, 255); + Color color = COLOR_WHITE; + SpriteSheet sheet = SpriteSheet{"sprites/character.bmp", 48, 48}; + auto sprite = Sprite{&sheet, 1}; + RendererParams window_params = get_renderer_params(); + renderer->draw_sprite(sprite, window_params.width/2, window_params.height/2); for(int y=0;ydraw_point(this->x, y, color); } diff --git a/src/game/visualizer.hpp b/src/game/visualizer.hpp index 1d907a2..01d5eeb 100644 --- a/src/game/visualizer.hpp +++ b/src/game/visualizer.hpp @@ -2,9 +2,13 @@ // Created by m on 12/3/21. // #include "../engine/game/game.hpp" +#include "../engine/rendering/renderer.hpp" #ifndef RLA_IIPP_VISUALIZER_HPP #define RLA_IIPP_VISUALIZER_HPP - +#define TITLE "test" +#define WIN_WIDTH 800 +#define WIN_HEIGHT 600 +#define TARGET_FPS 61 class Visualizer : public Game { public: