Compare commits

..

No commits in common. "2feb486f20d6b285e970a20b59495f51cff38665" and "e37eb0b03ff3ff39c2e18a773d480efea3298598" have entirely different histories.

8 changed files with 95 additions and 311 deletions

View File

@ -7,11 +7,13 @@ module;
export module core.engine; export module core.engine;
import config; import config;
import core.render_server; import core.renderer;
import wrappers.sdl; import wrappers.sdl;
export namespace core export namespace core
{ {
using Window = sdl::Window;
class Engine class Engine
{ {
// Whether this class is currently instantiated (to prevent multiple instances) // Whether this class is currently instantiated (to prevent multiple instances)
@ -20,13 +22,13 @@ export namespace core
// If this is set to false, the application will exit // If this is set to false, the application will exit
bool keep_running_ = true; bool keep_running_ = true;
sdl::Window window_; Window window_;
RenderServer render_server_; Renderer renderer_;
// Private constructor // Private constructor
Engine(sdl::Window&& window, sdl::Renderer&& renderer) Engine(Window&& window, Renderer&& renderer)
: window_{std::move(window)}, : window_{std::move(window)},
render_server_{std::move(renderer)} renderer_{std::move(renderer)}
{ {
instantiated_ = true; instantiated_ = true;
} }
@ -60,7 +62,7 @@ export namespace core
return std::unique_ptr<Engine>{ return std::unique_ptr<Engine>{
new Engine{ new Engine{
std::move(sdl_window), std::move(sdl_window),
std::move(sdl_renderer) Renderer{std::move(sdl_renderer)}
} }
}; };
} }
@ -70,20 +72,20 @@ export namespace core
return keep_running_; return keep_running_;
} }
sdl::Window& get_window() Window& get_window()
{ {
return window_; return window_;
} }
RenderServer& get_render_server() Renderer& get_renderer()
{ {
return render_server_; return renderer_;
} }
// Handles an SDL event. Returns true if the event has been handled. // Handles an SDL event. Returns true if the event has been handled.
bool handle_event(const sdl::Event* event) bool handle_event(const sdl::Event* event)
{ {
if (event->type == sdl::EventType::Quit) { if (event->type == SDL_EVENT_QUIT) {
// Exit the application // Exit the application
keep_running_ = false; keep_running_ = false;
return true; return true;

View File

@ -1,81 +0,0 @@
module;
#include <string>
#include <utility>
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<TextureID, sdl::Texture, TextureLoader>;
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);
}
};
}

47
src/core/renderer.cppm Normal file
View File

@ -0,0 +1,47 @@
module;
#include <utility>
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);
}
};
}

View File

@ -1,122 +0,0 @@
module;
#include <cassert>
#include <map>
#include <optional>
#include <string>
#include <vector>
export module core.resource_manager;
export namespace core
{
template <typename ResourceLoaderType, typename ResourceType>
concept IsResourceLoader = requires(ResourceLoaderType loader, const std::string& name)
{
{ loader.load_resource(name) } -> std::same_as<ResourceType>;
};
template <
typename ResourceIDType,
typename ResourceType,
IsResourceLoader<ResourceType> ResourceLoaderType
>
class ResourceManager
{
// Resource loader
ResourceLoaderType& resource_loader_;
// Registry of loaded resources, array index is resource ID
std::vector<ResourceType> resource_registry_;
// Mapping of all loaded resources from (file) name to resource ID
std::map<std::string, ResourceIDType> 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<ResourceIDType>(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<ResourceIDType> 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];
}
};
}

View File

@ -1,35 +0,0 @@
module;
#include <string>
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);
}
};
}

View File

@ -2,13 +2,12 @@ module;
#include <cassert> #include <cassert>
#include <memory> #include <memory>
#include <vector>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
export module game.game; export module game.game;
import core.engine; import core.engine;
import core.render_server; import core.renderer;
import game.sprite; import game.sprite;
import wrappers.sdl; import wrappers.sdl;
import wrappers.sdl_image; import wrappers.sdl_image;
@ -23,18 +22,21 @@ export namespace game
// Reference to the engine // Reference to the engine
core::Engine& engine_; core::Engine& engine_;
// Sprites for testing // Sprite for testing
Sprite player_sprite_; std::unique_ptr<Sprite> sprite_{nullptr};
std::vector<Sprite> sprites_;
// Private constructor // Private constructor
explicit Game(core::Engine& engine) explicit Game(core::Engine& engine)
: engine_(engine), : engine_(engine)
player_sprite_{ {
engine_.get_render_server().load_texture("assets/neocat.png"), // TODO: Texture should be a reference/pointer to an object managed by a ResourceManager or similar.
sdl::FRect{0, 0, 100, 100} auto texture = sdl_image::LoadTexture(
} engine_.get_renderer().get_sdl_renderer(),
{} "assets/neocat.png"
);
sprite_ = std::make_unique<Sprite>(std::move(texture), 100, 100);
}
public: public:
Game() = delete; Game() = delete;
@ -60,28 +62,15 @@ export namespace game
} }
// Handles an SDL event. Returns true if the event has been handled. // Handles an SDL event. Returns true if the event has been handled.
bool handle_event(const sdl::Event* event) bool handle_event(const sdl::Event* event) const
{ {
if (event->type == sdl::EventType::MouseMotion) { if (event->type == SDL_EVENT_MOUSE_MOTION) {
player_sprite_.move( sprite_->move(
event->motion.x - 50, event->motion.x - 50,
event->motion.y - 50 event->motion.y - 50
); );
return true; return true;
} }
if (event->type == sdl::EventType::MouseButtonUp) {
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; return false;
} }
@ -91,15 +80,10 @@ export namespace game
void render() const void render() const
{ {
const auto& render_server = engine_.get_render_server(); const auto& renderer = engine_.get_renderer();
render_server.start_frame(); renderer.start_frame();
sprite_->draw(renderer);
for (const Sprite& sprite : sprites_) { renderer.finish_frame();
sprite.draw(render_server);
}
player_sprite_.draw(render_server);
render_server.finish_frame();
} }
void shutdown() void shutdown()

View File

@ -1,10 +1,11 @@
module; module;
#include <utility>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
export module game.sprite; export module game.sprite;
import core.render_server; import core.renderer;
import wrappers.sdl; import wrappers.sdl;
// TODO: Move this to a different namespace (core, drawing, ...?) // TODO: Move this to a different namespace (core, drawing, ...?)
@ -12,14 +13,21 @@ export namespace game
{ {
class Sprite class Sprite
{ {
core::TextureID texture_id_; // TODO: Texture should be a reference/pointer to an object managed by a ResourceManager or similar.
sdl::Texture texture_;
sdl::FRect dest_rect_{0, 0, 0, 0}; sdl::FRect dest_rect_{0, 0, 0, 0};
public: public:
explicit Sprite(const core::TextureID texture_id, const sdl::FRect dest_rect) explicit Sprite(
: texture_id_{texture_id}, sdl::Texture&& texture,
dest_rect_{dest_rect} const int width,
{} const int height
)
: texture_{std::move(texture)}
{
dest_rect_.w = static_cast<float>(width);
dest_rect_.h = static_cast<float>(height);
}
void move(const float x, const float y) void move(const float x, const float y)
{ {
@ -27,9 +35,9 @@ export namespace game
dest_rect_.y = y; dest_rect_.y = y;
} }
void draw(const core::RenderServer& render_server) const void draw(const core::Renderer& renderer) const
{ {
render_server.render_texture(texture_id_, nullptr, &dest_rect_); renderer.render_texture(texture_, nullptr, &dest_rect_);
} }
}; };
} }

View File

@ -8,23 +8,4 @@ export namespace sdl
{ {
// Simple alias for SDL_Event union // Simple alias for SDL_Event union
using Event = SDL_Event; using Event = SDL_Event;
// Alias for EventType enum
using EventType_t = SDL_EventType;
/**
* Wrapper for the SDL_EventType enum.
*
* We're using a namespace here to emulate an enum-like interface, without having to copy the entire enum.
* More constants can be added on demand.
*/
namespace EventType
{
constexpr EventType_t Quit = SDL_EVENT_QUIT;
constexpr EventType_t KeyDown = SDL_EVENT_KEY_DOWN;
constexpr EventType_t KeyUp = SDL_EVENT_KEY_UP;
constexpr EventType_t MouseMotion = SDL_EVENT_MOUSE_MOTION;
constexpr EventType_t MouseButtonUp = SDL_EVENT_MOUSE_BUTTON_UP;
constexpr EventType_t MouseButtonDown = SDL_EVENT_MOUSE_BUTTON_DOWN;
}
} }