SFMLOrthogonalLayer.hpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /*********************************************************************
  2. Matt Marchant 2016
  3. http://trederia.blogspot.com
  4. tmxlite - Zlib license.
  5. This software is provided 'as-is', without any express or
  6. implied warranty. In no event will the authors be held
  7. liable for any damages arising from the use of this software.
  8. Permission is granted to anyone to use this software for any purpose,
  9. including commercial applications, and to alter it and redistribute
  10. it freely, subject to the following restrictions:
  11. 1. The origin of this software must not be misrepresented;
  12. you must not claim that you wrote the original software.
  13. If you use this software in a product, an acknowledgment
  14. in the product documentation would be appreciated but
  15. is not required.
  16. 2. Altered source versions must be plainly marked as such,
  17. and must not be misrepresented as being the original software.
  18. 3. This notice may not be removed or altered from any
  19. source distribution.
  20. *********************************************************************/
  21. /*
  22. Creates an SFML drawable from an Orthogonal tmx map layer.
  23. This is an example of drawing with SFML - not all features,
  24. such as tile flipping, are implemented. For a more detailed
  25. implementation, including artifact prevention, see:
  26. https://github.com/fallahn/xygine/blob/master/xygine/src/components/ComponentTileMapLayer.cpp
  27. */
  28. #ifndef SFML_ORTHO_HPP_
  29. #define SFML_ORTHO_HPP_
  30. #include <tmxlite/Map.hpp>
  31. #include <tmxlite/TileLayer.hpp>
  32. #include <SFML/Graphics.hpp>
  33. #include <memory>
  34. #include <vector>
  35. #include <array>
  36. #include <map>
  37. #include <string>
  38. #include <limits>
  39. #include <iostream>
  40. #include <sstream>
  41. #include <cmath>
  42. #include "Log.hpp"
  43. class MapLayer final : public sf::Drawable, public sf::Transformable
  44. {
  45. public:
  46. MapLayer() = default;
  47. MapLayer(const tmx::Map& map, std::size_t idx)
  48. {
  49. create(map, idx);
  50. }
  51. void create(const tmx::Map& map, std::size_t idx)
  52. {
  53. const auto& layers = map.getLayers();
  54. if (map.getOrientation() == tmx::Orientation::Orthogonal &&
  55. idx < layers.size() && layers[idx]->getType() == tmx::Layer::Type::Tile)
  56. {
  57. //round the chunk size to the nearest tile
  58. const auto tileSize = map.getTileSize();
  59. m_chunkSize.x = std::floor(m_chunkSize.x / tileSize.x) * tileSize.x;
  60. m_chunkSize.y = std::floor(m_chunkSize.y / tileSize.y) * tileSize.y;
  61. const auto& layer = *dynamic_cast<const tmx::TileLayer*>(layers[idx].get());
  62. createChunks(map, layer);
  63. auto mapSize = map.getBounds();
  64. m_globalBounds.width = mapSize.width;
  65. m_globalBounds.height = mapSize.height;
  66. }
  67. else
  68. {
  69. std::cout << "Not a valid othogonal layer, nothing will be drawn." << std::endl;
  70. }
  71. }
  72. ~MapLayer() = default;
  73. MapLayer(const MapLayer&) = delete;
  74. MapLayer& operator = (const MapLayer&) = delete;
  75. const sf::FloatRect& getGlobalBounds() const { return m_globalBounds; }
  76. private:
  77. sf::Vector2f m_chunkSize = sf::Vector2f(256.f, 256.f);
  78. sf::Vector2u m_chunkCount;
  79. sf::FloatRect m_globalBounds;
  80. using TextureResource = std::map<std::string, std::unique_ptr<sf::Texture>>;
  81. TextureResource m_textureResource;
  82. class Chunk final : public sf::Transformable, public sf::Drawable
  83. {
  84. public:
  85. using Ptr = std::unique_ptr<Chunk>;
  86. using Tile = std::array<sf::Vertex, 4u>;
  87. Chunk(const tmx::TileLayer& layer, std::vector<const tmx::Tileset*> tilesets,
  88. const sf::Vector2f& position, const sf::Vector2f& tileCount, std::size_t rowSize, TextureResource& tr)
  89. {
  90. auto opacity = static_cast<sf::Uint8>(layer.getOpacity() / 1.f * 255.f);
  91. sf::Color vertColour = sf::Color::White;
  92. vertColour.a = opacity;
  93. auto offset = layer.getOffset();
  94. sf::Vector2f layerOffset(sf::Vector2i(offset.x, offset.y));
  95. const auto& tileIDs = layer.getTiles();
  96. //go through the tiles and create the appropriate arrays
  97. for (const auto ts : tilesets)
  98. {
  99. bool chunkArrayCreated = false;
  100. auto tileSize = ts->getTileSize();
  101. sf::Vector2u tsTileCount;
  102. std::size_t xPos = static_cast<std::size_t>(position.x / tileSize.x);
  103. std::size_t yPos = static_cast<std::size_t>(position.y / tileSize.y);
  104. for (auto y = yPos; y < yPos + tileCount.y; ++y)
  105. {
  106. for (auto x = xPos; x < xPos + tileCount.x; ++x)
  107. {
  108. auto idx = (y * rowSize + x);
  109. if (idx < tileIDs.size() && tileIDs[idx].ID >= ts->getFirstGID()
  110. && tileIDs[idx].ID < (ts->getFirstGID() + ts->getTileCount()))
  111. {
  112. //ID must belong to this set - so add a tile
  113. if (!chunkArrayCreated)
  114. {
  115. m_chunkArrays.emplace_back(std::make_unique<ChunkArray>(*tr.find(ts->getImagePath())->second));
  116. auto texSize = m_chunkArrays.back()->getTextureSize();
  117. tsTileCount.x = texSize.x / tileSize.x;
  118. tsTileCount.y = texSize.y / tileSize.y;
  119. chunkArrayCreated = true;
  120. }
  121. auto& ca = m_chunkArrays.back();
  122. sf::Vector2f tileOffset(x * tileSize.x, y * tileSize.y);
  123. auto idIndex = tileIDs[idx].ID - ts->getFirstGID();
  124. sf::Vector2f tileIndex(idIndex % tsTileCount.x, idIndex / tsTileCount.x);
  125. tileIndex.x *= tileSize.x;
  126. tileIndex.y *= tileSize.y;
  127. Tile tile =
  128. {
  129. sf::Vertex(tileOffset, vertColour, tileIndex),
  130. sf::Vertex(tileOffset + sf::Vector2f(tileSize.x, 0.f), vertColour, tileIndex + sf::Vector2f(tileSize.x, 0.f)),
  131. sf::Vertex(tileOffset + sf::Vector2f(tileSize.x, tileSize.y), vertColour, tileIndex + sf::Vector2f(tileSize.x, tileSize.y)),
  132. sf::Vertex(tileOffset + sf::Vector2f(0.f, tileSize.y), vertColour, tileIndex + sf::Vector2f(0.f, tileSize.y))
  133. };
  134. //if(idIndex != 1 && idIndex != 3) { EXCLUDE CERTAIN TILES HERE
  135. ca->addTile(tile);
  136. //}
  137. }
  138. }
  139. }
  140. }
  141. setPosition(0, 0);
  142. }
  143. ~Chunk() = default;
  144. Chunk(const Chunk&) = delete;
  145. Chunk& operator = (const Chunk&) = delete;
  146. bool empty() const { return m_chunkArrays.empty(); }
  147. private:
  148. class ChunkArray final : public sf::Drawable
  149. {
  150. public:
  151. using Ptr = std::unique_ptr<ChunkArray>;
  152. explicit ChunkArray(const sf::Texture& t)
  153. : m_texture(t) {}
  154. ~ChunkArray() = default;
  155. ChunkArray(const ChunkArray&) = delete;
  156. ChunkArray& operator = (const ChunkArray&) = delete;
  157. void addTile(const Chunk::Tile& tile)
  158. {
  159. for (const auto& v : tile)
  160. {
  161. m_vertices.push_back(v);
  162. }
  163. }
  164. sf::Vector2u getTextureSize() const { return m_texture.getSize(); }
  165. private:
  166. const sf::Texture& m_texture;
  167. std::vector<sf::Vertex> m_vertices;
  168. void draw(sf::RenderTarget& rt, sf::RenderStates states) const override
  169. {
  170. states.texture = &m_texture;
  171. rt.draw(m_vertices.data(), m_vertices.size(), sf::Quads, states);
  172. }
  173. };
  174. std::vector<ChunkArray::Ptr> m_chunkArrays;
  175. void draw(sf::RenderTarget& rt, sf::RenderStates states) const override
  176. {
  177. states.transform *= getTransform();
  178. for (const auto& a : m_chunkArrays)
  179. {
  180. rt.draw(*a, states);
  181. }
  182. }
  183. };
  184. std::vector<Chunk::Ptr> m_chunks;
  185. mutable std::vector<const Chunk*> m_visibleChunks;
  186. void createChunks(const tmx::Map& map, const tmx::TileLayer& layer)
  187. {
  188. //look up all the tile sets and load the textures
  189. const auto& tileSets = map.getTilesets();
  190. const auto& layerIDs = layer.getTiles();
  191. std::uint32_t maxID = std::numeric_limits<std::uint32_t>::max();
  192. std::vector<const tmx::Tileset*> usedTileSets;
  193. for (auto i = tileSets.rbegin(); i != tileSets.rend(); ++i)
  194. {
  195. for (const auto& tile : layerIDs)
  196. {
  197. if (tile.ID >= i->getFirstGID() && tile.ID < maxID)
  198. {
  199. usedTileSets.push_back(&(*i));
  200. break;
  201. }
  202. }
  203. maxID = i->getFirstGID();
  204. }
  205. sf::Image fallback;
  206. fallback.create(2, 2, sf::Color::Magenta);
  207. for (const auto ts : usedTileSets)
  208. {
  209. const auto& path = ts->getImagePath();
  210. std::unique_ptr<sf::Texture> newTexture = std::make_unique<sf::Texture>();
  211. sf::Image img;
  212. if (!img.loadFromFile(path))
  213. {
  214. newTexture->loadFromImage(fallback);
  215. }
  216. else
  217. {
  218. if (ts->hasTransparency())
  219. {
  220. auto transparency = ts->getTransparencyColour();
  221. img.createMaskFromColor({ transparency.r, transparency.g, transparency.b, transparency.a });
  222. }
  223. newTexture->loadFromImage(img);
  224. }
  225. m_textureResource.insert(std::make_pair(path, std::move(newTexture)));
  226. }
  227. //calculate the number of chunks in the layer
  228. //and create each one
  229. const auto bounds = map.getBounds();
  230. m_chunkCount.x = static_cast<sf::Uint32>(std::ceil(bounds.width / m_chunkSize.x));
  231. m_chunkCount.y = static_cast<sf::Uint32>(std::ceil(bounds.height / m_chunkSize.y));
  232. sf::Vector2f tileCount(m_chunkSize.x / map.getTileSize().x, m_chunkSize.y / map.getTileSize().y);
  233. for (auto y = 0u; y < m_chunkCount.y; ++y)
  234. {
  235. for (auto x = 0u; x < m_chunkCount.x; ++x)
  236. {
  237. m_chunks.emplace_back(std::make_unique<Chunk>(layer, usedTileSets,
  238. sf::Vector2f(x * m_chunkSize.x, y * m_chunkSize.y), tileCount, map.getTileCount().x, m_textureResource));
  239. }
  240. }
  241. }
  242. public:
  243. void updateVisibility(const sf::View& view) const
  244. {
  245. sf::Vector2f viewCorner = view.getCenter();
  246. viewCorner -= view.getSize();
  247. int posX = static_cast<int>(std::floor(viewCorner.x / m_chunkSize.x));
  248. int posY = static_cast<int>(std::floor(viewCorner.y / m_chunkSize.y));
  249. std::vector<const Chunk*> visible;
  250. for (auto y = posY; y < posY + 2 * 720 / m_chunkSize.y; ++y)
  251. {
  252. for (auto x = posX; x < posX + 2 * 1280 / m_chunkSize.x; ++x)
  253. {
  254. auto idx = y * int(m_chunkCount.x) + x;
  255. if (idx >= 0 && idx < (int)m_chunks.size() && !m_chunks[idx]->empty())
  256. {
  257. visible.push_back(m_chunks[idx].get());
  258. }
  259. }
  260. }
  261. std::swap(m_visibleChunks, visible);
  262. }
  263. private:
  264. void draw(sf::RenderTarget& rt, sf::RenderStates states) const override
  265. {
  266. //calc view coverage and draw nearest chunks
  267. //updateVisibility(rt.getView());
  268. states.transform *= getTransform();
  269. if(m_enabled)
  270. for (const auto& c : m_visibleChunks)
  271. {
  272. rt.draw(*c, states);
  273. }
  274. }
  275. public:
  276. void whack() {
  277. setPosition(rand() % 40 - 20, rand() % 40 - 20);
  278. }
  279. bool m_enabled;
  280. };
  281. #endif //SFML_ORTHO_HPP_