#ifndef SFML_ORTHO_HPP_ #define SFML_ORTHO_HPP_ #include #include #include #include #include #include #include #include #include #include #include class MapLayer final : public sf::Drawable { public: MapLayer() {} MapLayer(const tmx::Map& map, std::size_t idx) { const auto& layers = map.getLayers(); if (map.getOrientation() == tmx::Orientation::Isometric && idx < layers.size() && layers[idx]->getType() == tmx::Layer::Type::Tile) { //round the chunk size to the nearest tile const auto tileSize = map.getTileSize(); m_chunkSize.x = std::floor(m_chunkSize.x / tileSize.x) * tileSize.x; m_chunkSize.y = std::floor(m_chunkSize.y / tileSize.y) * tileSize.y; const auto& layer = *dynamic_cast(layers[idx].get()); createChunks(map, layer); auto mapSize = map.getBounds(); m_globalBounds.width = mapSize.width; m_globalBounds.height = mapSize.height; } else { std::cout << "Not a valid othogonal layer, nothing will be drawn." << std::endl; } } ~MapLayer() = default; MapLayer(const MapLayer&) = delete; MapLayer& operator = (const MapLayer&) = delete; const sf::FloatRect& getGlobalBounds() const { return m_globalBounds; } private: sf::Vector2f m_chunkSize = sf::Vector2f(1024.f, 1024.f); sf::Vector2u m_chunkCount; sf::FloatRect m_globalBounds; using TextureResource = std::map>; TextureResource m_textureResource; class Chunk final : public sf::Transformable, public sf::Drawable { public: using Ptr = std::unique_ptr; using Tile = std::array; Chunk(const tmx::TileLayer& layer, std::vector tilesets, const sf::Vector2f& position, const sf::Vector2f& tileCount, std::size_t rowSize, TextureResource& tr) { auto opacity = static_cast(layer.getOpacity() / 1.f * 255.f); sf::Color vertColour = sf::Color::White; vertColour.a = opacity; auto offset = layer.getOffset(); sf::Vector2f layerOffset(offset.x, offset.y); const auto& tileIDs = layer.getTiles(); //go through the tiles and create the appropriate arrays for (const auto ts : tilesets) { bool chunkArrayCreated = false; auto tileSize = ts->getTileSize(); sf::Vector2u tsTileCount; std::size_t xPos = static_cast(position.x / tileSize.x); std::size_t yPos = static_cast(position.y / tileSize.y); for (auto y = yPos; y < yPos + tileCount.y; ++y) { for (auto x = xPos; x < xPos + tileCount.x; ++x) { auto idx = (y * rowSize + x); if (idx < tileIDs.size() && tileIDs[idx].ID >= ts->getFirstGID() && tileIDs[idx].ID < (ts->getFirstGID() + ts->getTileCount())) { //ID must belong to this set - so add a tile if (!chunkArrayCreated) { m_chunkArrays.emplace_back(std::make_unique(*tr.find(ts->getImagePath())->second)); auto texSize = m_chunkArrays.back()->getTextureSize(); tsTileCount.x = texSize.x / tileSize.x; tsTileCount.y = texSize.y / tileSize.y; chunkArrayCreated = true; std::cout << "chunk array created!\n"; } auto& ca = m_chunkArrays.back(); /**/ //sf::Vector2f tileOffset; //tileOffset.x = sf::Vector2f tileOffset(-static_cast(x * (tileSize.x / 2u)), static_cast(x * (tileSize.y / 2u))); tileOffset.x -= static_cast(y * (tileSize.x / 2u)); tileOffset.y -= static_cast(y * (tileSize.y / 2u)); tileOffset.x -= static_cast(tileSize.x / 2u); tileOffset.y += static_cast(tileSize.y / 2u); //sf::Vector2f tileOffset((xPos-yPos) * tileSize.x * 0.5f, (xPos+yPos) * tileSize.y * 0.5f); //sf::Vector2f tileOffset; //tileOffset.x = (x - y) * (tileSize.x * tileSize.y) + 16 * (tileSize.x * tileSize.y); //tileOffset.y = (x + y) * (tileSize.x * tileSize.y) * 0.5; //pos.x = (float)((x - y) * m_TileSize + m_Width * m_TileSize); //pos.y = (float)((x + y) * m_TileSize * 0.5); /* */ //sf::Vector2f tileOffset(x * (tileSize.x), x * (tileSize.y * 0.5)); //tileOffset.x -= static_cast(y * (tileSize.x / 2u)); //tileOffset.y -= static_cast(y * (tileSize.y / 2u)); //tileOffset.x -= static_cast(tileSize.x / 2u); //tileOffset.y += static_cast(tileSize.y / 2u); auto idIndex = tileIDs[idx].ID - ts->getFirstGID(); sf::Vector2f tileIndex(idIndex % tsTileCount.x, idIndex / tsTileCount.x); tileIndex.x *= tileSize.x; tileIndex.y *= tileSize.y; Tile tile = { sf::Vertex(tileOffset, vertColour, tileIndex), sf::Vertex(tileOffset + sf::Vector2f(tileSize.x, 0.f), vertColour, tileIndex + sf::Vector2f(tileSize.x, 0.f)), sf::Vertex(tileOffset + sf::Vector2f(tileSize.x, tileSize.y), vertColour, tileIndex + sf::Vector2f(tileSize.x, tileSize.y)), sf::Vertex(tileOffset + sf::Vector2f(0.f, tileSize.y), vertColour, tileIndex + sf::Vector2f(0.f, tileSize.y)) }; ca->addTile(tile); std::cout << "TileSize: " << tileSize.x << "," << tileSize.y << "\n"; std::cout << "TileOffset: " << tileOffset.x << " , " << tileOffset.y << "\n"; } } } } setPosition(position); //std::cout << "Position: " << getPosition << "\n;"; } ~Chunk() = default; Chunk(const Chunk&) = delete; Chunk& operator = (const Chunk&) = delete; bool empty() const { return m_chunkArrays.empty(); } private: class ChunkArray final : public sf::Drawable { public: using Ptr = std::unique_ptr; explicit ChunkArray(const sf::Texture& t) : m_texture(t) {} ~ChunkArray() = default; ChunkArray(const ChunkArray&) = delete; ChunkArray& operator = (const ChunkArray&) = delete; void addTile(const Chunk::Tile& tile) { for (const auto& v : tile) { m_vertices.push_back(v); } } sf::Vector2u getTextureSize() const { return m_texture.getSize(); } private: const sf::Texture& m_texture; std::vector m_vertices; void draw(sf::RenderTarget& rt, sf::RenderStates states) const override { states.texture = &m_texture; rt.draw(m_vertices.data(), m_vertices.size(), sf::Quads, states); } }; std::vector m_chunkArrays; void draw(sf::RenderTarget& rt, sf::RenderStates states) const override { states.transform *= getTransform(); for (const auto& a : m_chunkArrays) { rt.draw(*a, states); } } }; std::vector m_chunks; mutable std::vector m_visibleChunks; void createChunks(const tmx::Map& map, const tmx::TileLayer& layer) { //look up all the tile sets and load the textures const auto& tileSets = map.getTilesets(); const auto& layerIDs = layer.getTiles(); std::uint32_t maxID = std::numeric_limits::max(); std::vector usedTileSets; for (auto i = tileSets.rbegin(); i != tileSets.rend(); ++i) { for (const auto& tile : layerIDs) { if (tile.ID >= i->getFirstGID() && tile.ID < maxID) { usedTileSets.push_back(&(*i)); break; } } maxID = i->getFirstGID(); } sf::Image fallback; fallback.create(2, 2, sf::Color::Magenta); for (const auto ts : usedTileSets) { const auto& path = ts->getImagePath(); std::unique_ptr newTexture = std::make_unique(); sf::Image img; if (!img.loadFromFile(path)) { newTexture->loadFromImage(fallback); } else { if (ts->hasTransparency()) { auto transparency = ts->getTransparencyColour(); img.createMaskFromColor({ transparency.r, transparency.g, transparency.b, transparency.a }); } newTexture->loadFromImage(img); } m_textureResource.insert(std::make_pair(path, std::move(newTexture))); } //calculate the number of chunks in the layer //and create each one const auto bounds = map.getBounds(); m_chunkCount.x = static_cast(std::ceil(bounds.width / m_chunkSize.x)); m_chunkCount.y = static_cast(std::ceil(bounds.height / m_chunkSize.y)); sf::Vector2f tileCount(m_chunkSize.x / map.getTileSize().x, m_chunkSize.y / map.getTileSize().y); for (auto y = 0u; y < m_chunkCount.y; ++y) { for (auto x = 0u; x < m_chunkCount.x; ++x) { m_chunks.emplace_back(std::make_unique(layer, usedTileSets, sf::Vector2f(x * m_chunkSize.x, y * m_chunkSize.y), tileCount, map.getTileCount().x, m_textureResource)); } } } void updateVisibility(const sf::View& view) const { sf::Vector2f viewCorner = view.getCenter(); viewCorner -= view.getSize() / 2.f; int posX = static_cast(std::floor(viewCorner.x / m_chunkSize.x)); int posY = static_cast(std::floor(viewCorner.y / m_chunkSize.y)); std::vector visible; for (auto y = posY; y < posY + 2; ++y) { for (auto x = posX; x < posX + 2; ++x) { auto idx = y * int(m_chunkCount.x) + x; if (idx >= 0 && idx < m_chunks.size() && !m_chunks[idx]->empty()) { visible.push_back(m_chunks[idx].get()); } } } std::swap(m_visibleChunks, visible); } void draw(sf::RenderTarget& rt, sf::RenderStates states) const override { //calc view coverage and draw nearest chunks updateVisibility(rt.getView()); for (const auto& c : m_visibleChunks) { rt.draw(*c, states); } } }; #endif //SFML_ORTHO_HPP_