module; #include #include #include #include export module rutile.core.drawing.tile_set; import rutile.core.render_server; import wrappers.sdl; export namespace rutile { using TileSetID = unsigned int; using TileID = unsigned int; struct TileDefinition { sdl::FRect atlas_src_rect; }; class TileSet { // Texture ID of tileset atlas texture TextureID texture_id_; // Size of each tile in pixels // TODO: Do we need this? // int tile_size_; // Tile definitions: Maps tile ID to a struct describing the tile, e.g. the texture source boundaries, // collision data, etc. std::vector tile_definitions_; public: TileSet() = delete; explicit TileSet( const TextureID texture_id, std::vector tile_definitions ) : texture_id_{texture_id}, tile_definitions_{std::move(tile_definitions)} {} static TileSet create_from_texture( RenderServer& render_server, const std::string& filename, const int tile_size, const std::vector& tile_atlas_coords ) { const TextureID texture_id = render_server.load_texture(filename); const sdl::Texture& texture = render_server.get_texture(texture_id); assert(tile_size > 0); // TODO: These probably shouldn't be asserts... assert(texture.get_width() % tile_size == 0); assert(texture.get_height() % tile_size == 0); std::vector tile_definitions; // TODO: Should we automatically create tile 0 (no tile) instead of listing it in tile_atlas_coords? for (const auto& [x, y] : tile_atlas_coords) { const TileDefinition tile{ { static_cast(x * tile_size), static_cast(y * tile_size), static_cast(tile_size), static_cast(tile_size), } }; // TODO: These probably shouldn't be asserts... assert(tile.atlas_src_rect.x + tile.atlas_src_rect.w <= texture.get_width()); assert(tile.atlas_src_rect.y + tile.atlas_src_rect.h <= texture.get_height()); tile_definitions.push_back(tile); } return TileSet(texture_id, tile_definitions); } constexpr TextureID get_texture_id() const { return texture_id_; } constexpr const TileDefinition& get_tile_definition(const TileID tile_id) const { return tile_definitions_.at(tile_id); } }; }