From 772f4b665aab5c6346f3e72a130d4a10a3daaab3 Mon Sep 17 00:00:00 2001 From: binaryDiv Date: Wed, 26 Nov 2025 01:57:08 +0100 Subject: [PATCH] Implement ResourceManager and TextureLoader --- src/core/engine.cppm | 20 +++--- src/core/render_server.cppm | 81 ++++++++++++++++++++++ src/core/renderer.cppm | 47 ------------- src/core/resource_manager.cppm | 122 +++++++++++++++++++++++++++++++++ src/core/texture_loader.cppm | 35 ++++++++++ src/game/game.cppm | 54 ++++++++++----- src/game/sprite.cppm | 24 +++---- 7 files changed, 290 insertions(+), 93 deletions(-) create mode 100644 src/core/render_server.cppm delete mode 100644 src/core/renderer.cppm create mode 100644 src/core/resource_manager.cppm create mode 100644 src/core/texture_loader.cppm diff --git a/src/core/engine.cppm b/src/core/engine.cppm index 3d50323..fda4019 100644 --- a/src/core/engine.cppm +++ b/src/core/engine.cppm @@ -7,13 +7,11 @@ module; export module core.engine; import config; -import core.renderer; +import core.render_server; import wrappers.sdl; export namespace core { - using Window = sdl::Window; - class Engine { // Whether this class is currently instantiated (to prevent multiple instances) @@ -22,13 +20,13 @@ export namespace core // If this is set to false, the application will exit bool keep_running_ = true; - Window window_; - Renderer renderer_; + sdl::Window window_; + RenderServer render_server_; // Private constructor - Engine(Window&& window, Renderer&& renderer) + Engine(sdl::Window&& window, sdl::Renderer&& renderer) : window_{std::move(window)}, - renderer_{std::move(renderer)} + render_server_{std::move(renderer)} { instantiated_ = true; } @@ -62,7 +60,7 @@ export namespace core return std::unique_ptr{ new Engine{ std::move(sdl_window), - Renderer{std::move(sdl_renderer)} + std::move(sdl_renderer) } }; } @@ -72,14 +70,14 @@ export namespace core return keep_running_; } - Window& get_window() + sdl::Window& get_window() { return window_; } - Renderer& get_renderer() + RenderServer& get_render_server() { - return renderer_; + return render_server_; } // Handles an SDL event. Returns true if the event has been handled. diff --git a/src/core/render_server.cppm b/src/core/render_server.cppm new file mode 100644 index 0000000..c159769 --- /dev/null +++ b/src/core/render_server.cppm @@ -0,0 +1,81 @@ +module; + +#include +#include + +export module core.render_server; + +import core.resource_manager; +import core.texture_loader; +import wrappers.sdl; + +export namespace core +{ + using TextureID = unsigned int; + using TextureManager = ResourceManager; + + class RenderServer + { + sdl::Renderer renderer_; + TextureLoader texture_loader_; + TextureManager texture_manager_; + + public: + RenderServer() = delete; + + explicit RenderServer(sdl::Renderer&& renderer) + : renderer_{std::move(renderer)}, + texture_loader_{renderer_}, + texture_manager_{texture_loader_} + {} + + // No copy or move operations + RenderServer(const RenderServer&) = delete; + RenderServer& operator=(const RenderServer&) = delete; + RenderServer(RenderServer&&) = delete; + RenderServer& operator=(RenderServer&&) = delete; + + ~RenderServer() = default; + + constexpr sdl::Renderer& get_renderer() + { + return renderer_; + } + + /** + * Loads a texture from a file (if not loaded yet) and returns its texture ID. + */ + constexpr TextureID load_texture(const std::string& filename) + { + return texture_manager_.load_resource_by_name(filename); + } + + void start_frame() const + { + renderer_.clear(); + } + + void finish_frame() const + { + renderer_.present(); + } + + constexpr void render_texture( + const TextureID texture_id, + const sdl::FRect* src_rect, + const sdl::FRect* dest_rect + ) const + { + render_texture(texture_manager_.get_resource(texture_id), src_rect, dest_rect); + } + + void render_texture( + const sdl::Texture& texture, + const sdl::FRect* src_rect, + const sdl::FRect* dest_rect + ) const + { + renderer_.render_texture(texture, src_rect, dest_rect); + } + }; +} diff --git a/src/core/renderer.cppm b/src/core/renderer.cppm deleted file mode 100644 index 1704c2f..0000000 --- a/src/core/renderer.cppm +++ /dev/null @@ -1,47 +0,0 @@ -module; - -#include - -export module core.renderer; - -import wrappers.sdl; - -export namespace core -{ - // TODO: Rename this class to RenderServer or something to distinguish it from sdl::Renderer? - class Renderer - { - sdl::Renderer sdl_renderer_; - - public: - Renderer() = delete; - - explicit Renderer(sdl::Renderer&& sdl_renderer) - : sdl_renderer_{std::move(sdl_renderer)} - {} - - constexpr sdl::Renderer& get_sdl_renderer() - { - return sdl_renderer_; - } - - void start_frame() const - { - sdl_renderer_.clear(); - } - - void finish_frame() const - { - sdl_renderer_.present(); - } - - void render_texture( - const sdl::Texture& texture, - const sdl::FRect* src_rect, - const sdl::FRect* dest_rect - ) const - { - sdl_renderer_.render_texture(texture, src_rect, dest_rect); - } - }; -} diff --git a/src/core/resource_manager.cppm b/src/core/resource_manager.cppm new file mode 100644 index 0000000..2e947f5 --- /dev/null +++ b/src/core/resource_manager.cppm @@ -0,0 +1,122 @@ +module; + +#include +#include +#include +#include +#include + +export module core.resource_manager; + +export namespace core +{ + template + concept IsResourceLoader = requires(ResourceLoaderType loader, const std::string& name) + { + { loader.load_resource(name) } -> std::same_as; + }; + + template < + typename ResourceIDType, + typename ResourceType, + IsResourceLoader ResourceLoaderType + > + class ResourceManager + { + // Resource loader + ResourceLoaderType& resource_loader_; + + // Registry of loaded resources, array index is resource ID + std::vector resource_registry_; + + // Mapping of all loaded resources from (file) name to resource ID + std::map resource_name_map_; + + public: + ResourceManager() = delete; + + explicit ResourceManager(ResourceLoaderType& resource_loader) + : resource_loader_{resource_loader} + {} + + // No copy or move operations + ResourceManager(const ResourceManager&) = delete; + ResourceManager& operator=(const ResourceManager&) = delete; + ResourceManager(ResourceManager&&) = delete; + ResourceManager& operator=(ResourceManager&&) = delete; + + ~ResourceManager() = default; + + /** + * Adds a new resource to the registry and returns its ID. + */ + ResourceIDType add_resource(ResourceType&& resource) + { + // Add resource to end of vector + resource_registry_.push_back(std::move(resource)); + + // Return the index of the newly added resource + return static_cast(resource_registry_.size() - 1); + } + + /** + * Adds a new resource to the registry, associates the name with it and returns its ID. + */ + ResourceIDType add_resource(ResourceType&& resource, const std::string& name) + { + // Add resource + ResourceIDType resource_id = add_resource(std::move(resource)); + + // Associate name with resource ID for future access + resource_name_map_.emplace(name, resource_id); + + return resource_id; + } + + /** + * Gets the resource ID for a given resource name, or `std::nullopt` if the resource was not found. + */ + std::optional get_resource_id_by_name(const std::string& name) const + { + // Check if resource is already loaded + if ( + const auto search = resource_name_map_.find(name); + search != resource_name_map_.end() + ) { + // Return resource ID + return search->second; + } + + return std::nullopt; + } + + /** + * Loads a resource (e.g. from a file) and adds it to the registry if it isn't loaded yet. + * Returns the resource ID. + */ + ResourceIDType load_resource_by_name(const std::string& name) + { + // Check if resource is already loaded + if ( + auto resource_id = get_resource_id_by_name(name); + resource_id.has_value() + ) { + return resource_id.value(); + } + + // Load resource and add it to the registry + return add_resource(resource_loader_.load_resource(name), name); + } + + /** + * Returns a reference to the resource with the given ID. + * The reference is not guaranteed to be valid after adding new resources to the registry. + * Assumes that the resource ID is valid. + */ + const ResourceType& get_resource(const ResourceIDType resource_id) const + { + assert(resource_id < resource_registry_.size()); + return resource_registry_[resource_id]; + } + }; +} diff --git a/src/core/texture_loader.cppm b/src/core/texture_loader.cppm new file mode 100644 index 0000000..32548c4 --- /dev/null +++ b/src/core/texture_loader.cppm @@ -0,0 +1,35 @@ +module; + +#include + +export module core.texture_loader; + +import core.resource_manager; +import wrappers.sdl; +import wrappers.sdl_image; + +export namespace core +{ + class TextureLoader + { + sdl::Renderer& renderer_; + + public: + explicit TextureLoader(sdl::Renderer& renderer) + : renderer_{renderer} + {} + + // No copy or move operations (reference) + TextureLoader(const TextureLoader&) = delete; + TextureLoader& operator=(const TextureLoader&) = delete; + TextureLoader(TextureLoader&&) = delete; + TextureLoader& operator=(TextureLoader&&) = delete; + + ~TextureLoader() = default; + + sdl::Texture load_resource(const std::string& filename) const + { + return sdl_image::LoadTexture(renderer_, filename); + } + }; +} diff --git a/src/game/game.cppm b/src/game/game.cppm index 1d68ace..04f43f5 100644 --- a/src/game/game.cppm +++ b/src/game/game.cppm @@ -2,12 +2,13 @@ module; #include #include +#include #include export module game.game; import core.engine; -import core.renderer; +import core.render_server; import game.sprite; import wrappers.sdl; import wrappers.sdl_image; @@ -22,21 +23,18 @@ export namespace game // Reference to the engine core::Engine& engine_; - // Sprite for testing - std::unique_ptr sprite_{nullptr}; + // Sprites for testing + Sprite player_sprite_; + std::vector sprites_; // Private constructor explicit Game(core::Engine& engine) - : engine_(engine) - { - // TODO: Texture should be a reference/pointer to an object managed by a ResourceManager or similar. - auto texture = sdl_image::LoadTexture( - engine_.get_renderer().get_sdl_renderer(), - "assets/neocat.png" - ); - - sprite_ = std::make_unique(std::move(texture), 100, 100); - } + : engine_(engine), + player_sprite_{ + engine_.get_render_server().load_texture("assets/neocat.png"), + sdl::FRect{0, 0, 100, 100} + } + {} public: Game() = delete; @@ -62,15 +60,28 @@ export namespace game } // Handles an SDL event. Returns true if the event has been handled. - bool handle_event(const sdl::Event* event) const + bool handle_event(const sdl::Event* event) { if (event->type == SDL_EVENT_MOUSE_MOTION) { - sprite_->move( + player_sprite_.move( event->motion.x - 50, event->motion.y - 50 ); return true; } + + if (event->type == SDL_EVENT_MOUSE_BUTTON_UP) { + sprites_.emplace_back( + engine_.get_render_server().load_texture("assets/neofox.png"), + sdl::FRect{ + event->motion.x - 50, + event->motion.y - 50, + 100, 100 + } + ); + return true; + } + return false; } @@ -80,10 +91,15 @@ export namespace game void render() const { - const auto& renderer = engine_.get_renderer(); - renderer.start_frame(); - sprite_->draw(renderer); - renderer.finish_frame(); + const auto& render_server = engine_.get_render_server(); + render_server.start_frame(); + + for (const Sprite& sprite : sprites_) { + sprite.draw(render_server); + } + player_sprite_.draw(render_server); + + render_server.finish_frame(); } void shutdown() diff --git a/src/game/sprite.cppm b/src/game/sprite.cppm index 901ea28..cd297c3 100644 --- a/src/game/sprite.cppm +++ b/src/game/sprite.cppm @@ -1,11 +1,10 @@ module; -#include #include export module game.sprite; -import core.renderer; +import core.render_server; import wrappers.sdl; // TODO: Move this to a different namespace (core, drawing, ...?) @@ -13,21 +12,14 @@ export namespace game { class Sprite { - // TODO: Texture should be a reference/pointer to an object managed by a ResourceManager or similar. - sdl::Texture texture_; + core::TextureID texture_id_; sdl::FRect dest_rect_{0, 0, 0, 0}; public: - explicit Sprite( - sdl::Texture&& texture, - const int width, - const int height - ) - : texture_{std::move(texture)} - { - dest_rect_.w = static_cast(width); - dest_rect_.h = static_cast(height); - } + explicit Sprite(const core::TextureID texture_id, const sdl::FRect dest_rect) + : texture_id_{texture_id}, + dest_rect_{dest_rect} + {} void move(const float x, const float y) { @@ -35,9 +27,9 @@ export namespace game dest_rect_.y = y; } - void draw(const core::Renderer& renderer) const + void draw(const core::RenderServer& render_server) const { - renderer.render_texture(texture_, nullptr, &dest_rect_); + render_server.render_texture(texture_id_, nullptr, &dest_rect_); } }; }