From 02387b8fe3880bb58078abd46328788e7a998da3 Mon Sep 17 00:00:00 2001 From: Lynix Date: Fri, 25 Nov 2022 19:28:22 +0100 Subject: [PATCH] Graphics: Add Tilemap --- examples/Physics2DDemo/main.cpp | 28 ++- include/Nazara/Graphics.hpp | 1 + include/Nazara/Graphics/Tilemap.hpp | 94 +++++++ include/Nazara/Graphics/Tilemap.inl | 376 ++++++++++++++++++++++++++++ src/Nazara/Graphics/Tilemap.cpp | 161 ++++++++++++ 5 files changed, 653 insertions(+), 7 deletions(-) create mode 100644 include/Nazara/Graphics/Tilemap.hpp create mode 100644 include/Nazara/Graphics/Tilemap.inl create mode 100644 src/Nazara/Graphics/Tilemap.cpp diff --git a/examples/Physics2DDemo/main.cpp b/examples/Physics2DDemo/main.cpp index 7cf46e6e3..25be96f3c 100644 --- a/examples/Physics2DDemo/main.cpp +++ b/examples/Physics2DDemo/main.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -93,13 +92,28 @@ int main() entt::entity groundEntity = registry.create(); { - std::shared_ptr sprite = std::make_shared(Nz::Graphics::Instance()->GetDefaultMaterials().basicDefault); - sprite->SetSize({ 800.f, 20.f }); - sprite->SetOrigin({ 400.f, 10.f, 0.f }); + std::shared_ptr tilemap = std::make_shared(Nz::Vector2ui(40, 20), Nz::Vector2f(64.f, 64.f), 18); + tilemap->SetOrigin(Nz::Vector2f(0.5f, 0.5f)); + tilemap->EnableIsometricMode(true); + for (std::size_t i = 0; i < 18; ++i) + { + std::shared_ptr material = Nz::Graphics::Instance()->GetDefaultMaterials().basicTransparent->Clone(); + material->SetTextureProperty("BaseColorMap", Nz::Texture::LoadFromFile(resourceDir / "tiles" / (std::to_string(i + 1) + ".png"), texParams)); - registry.emplace(groundEntity).SetPosition(1920.f / 2.f, 50.f); - registry.emplace(groundEntity).AttachRenderable(sprite, 1); - auto& rigidBody = registry.emplace(groundEntity, physSytem.CreateRigidBody(0.f, std::make_shared(Nz::Vector2f(800.f, 20.f)))); + tilemap->SetMaterial(i, material); + } + + for (unsigned int y = 0; y < 20; ++y) + { + for (unsigned int x = 0; x < 40; ++x) + { + tilemap->EnableTile({ x, y }, Nz::Rectf{ 0.f, 0.f, 1.f, 1.f }, Nz::Color::White, (y == 0) ? 1 : 4); + } + } + + registry.emplace(groundEntity).SetPosition(1920.f / 2.f, 0.f); + registry.emplace(groundEntity).AttachRenderable(tilemap, 1); + auto& rigidBody = registry.emplace(groundEntity, physSytem.CreateRigidBody(0.f, std::make_shared(tilemap->GetSize()))); rigidBody.SetElasticity(0.99f); } diff --git a/include/Nazara/Graphics.hpp b/include/Nazara/Graphics.hpp index a51bd4d8a..665ec0de4 100644 --- a/include/Nazara/Graphics.hpp +++ b/include/Nazara/Graphics.hpp @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include diff --git a/include/Nazara/Graphics/Tilemap.hpp b/include/Nazara/Graphics/Tilemap.hpp new file mode 100644 index 000000000..3488cc7f0 --- /dev/null +++ b/include/Nazara/Graphics/Tilemap.hpp @@ -0,0 +1,94 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_GRAPHICS_TILEMAP_HPP +#define NAZARA_GRAPHICS_TILEMAP_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class MaterialInstance; + + class NAZARA_GRAPHICS_API Tilemap : public InstancedRenderable + { + public: + struct Tile; + + Tilemap(const Vector2ui& mapSize, const Vector2f& tileSize, std::size_t materialCount = 1); + Tilemap(const Tilemap&) = delete; + Tilemap(Tilemap&&) noexcept = default; + ~Tilemap() = default; + + void BuildElement(ElementRendererRegistry& registry, const ElementData& elementData, std::size_t passIndex, std::vector& elements) const override; + + inline void DisableTile(const Vector2ui& tilePos); + inline void DisableTiles(); + inline void DisableTiles(const Vector2ui* tilesPos, std::size_t tileCount); + + inline void EnableIsometricMode(bool isometric); + + inline void EnableTile(const Vector2ui& tilePos, const Rectf& coords, const Color& color = Color::White, std::size_t materialIndex = 0U); + inline void EnableTile(const Vector2ui& tilePos, const Rectui& rect, const Color& color = Color::White, std::size_t materialIndex = 0U); + inline void EnableTiles(const Rectf& coords, const Color& color = Color::White, std::size_t materialIndex = 0U); + inline void EnableTiles(const Rectui& rect, const Color& color = Color::White, std::size_t materialIndex = 0U); + inline void EnableTiles(const Vector2ui* tilesPos, std::size_t tileCount, const Rectf& coords, const Color& color = Color::White, std::size_t materialIndex = 0U); + inline void EnableTiles(const Vector2ui* tilesPos, std::size_t tileCount, const Rectui& rect, const Color& color = Color::White, std::size_t materialIndex = 0U); + + inline const Vector2ui& GetMapSize() const; + const std::shared_ptr& GetMaterial(std::size_t i) const override; + std::size_t GetMaterialCount() const override; + inline Vector2f GetSize() const; + inline const Tile& GetTile(const Vector2ui& tilePos) const; + inline const Vector2f& GetTileSize() const; + + inline bool IsIsometricModeEnabled() const; + + void SetMaterial(std::size_t matIndex, std::shared_ptr material); + inline void SetOrigin(const Vector3f& origin); + + struct Tile + { + std::size_t layerIndex = 0U; + Color color = Color::White; + Rectf textureCoords = Rectf::Zero(); + bool enabled = false; + }; + + Tilemap& operator=(const Tilemap&) = delete; + Tilemap& operator=(Tilemap&&) noexcept = default; + + private: + Vector3ui GetTextureSize(std::size_t matIndex) const; + inline void InvalidateVertices(); + void UpdateVertices() const; + + struct Layer + { + std::set tiles; + std::shared_ptr material; + }; + + mutable std::vector m_vertices; + std::vector m_tiles; + std::vector m_layers; + Vector2ui m_mapSize; + Vector2f m_tileSize; + Vector3f m_origin; + bool m_isometricModeEnabled; + mutable bool m_shouldRebuildVertices; + }; +} + +#include + +#endif // NAZARA_GRAPHICS_TILEMAP_HPP diff --git a/include/Nazara/Graphics/Tilemap.inl b/include/Nazara/Graphics/Tilemap.inl new file mode 100644 index 000000000..82845b068 --- /dev/null +++ b/include/Nazara/Graphics/Tilemap.inl @@ -0,0 +1,376 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + /*! + * \brief Disable the tile at position tilePos, disabling rendering at this location + * + * \param tilePos Position of the tile to disable + * + * \see DisableTiles + */ + inline void Tilemap::DisableTile(const Vector2ui& tilePos) + { + NazaraAssert(tilePos.x < m_mapSize.x&& tilePos.y < m_mapSize.y, "Tile position is out of bounds"); + + std::size_t tileIndex = tilePos.y * m_mapSize.x + tilePos.x; + Tile& tile = m_tiles[tileIndex]; + tile.enabled = false; + + m_layers[tile.layerIndex].tiles.erase(tileIndex); + + InvalidateVertices(); + } + + /*! + * \brief Disable all tiles + */ + inline void Tilemap::DisableTiles() + { + for (Tile& tile : m_tiles) + tile.enabled = false; + + for (Layer& layer : m_layers) + layer.tiles.clear(); + + InvalidateVertices(); + } + + /*! + * \brief Disable tileCount tiles at positions contained at tilesPos location, disabling rendering at those locations + * + * This is equivalent to calling tileCount times DisableTile with the positions contained at tilesPos + * + * \param tilesPos Pointer to a valid array of at least tileCount positions + * \param tileCount Number of tiles to disable + * + * \remark if tileCount is zero, this is a no-op and the value of tilesPos is not used + * + * \see DisableTile + */ + inline void Tilemap::DisableTiles(const Vector2ui* tilesPos, std::size_t tileCount) + { + NazaraAssert(tilesPos || tileCount == 0, "Invalid tile position array with a non-zero tileCount"); + + UInt32 invalidatedLayers = 0; + + for (std::size_t i = 0; i < tileCount; ++i) + { + NazaraAssert(tilesPos->x < m_mapSize.x&& tilesPos->y < m_mapSize.y, "Tile position is out of bounds"); + + std::size_t tileIndex = tilesPos->y * m_mapSize.x + tilesPos->x; + Tile& tile = m_tiles[tileIndex]; + tile.enabled = false; + + m_layers[tile.layerIndex].tiles.erase(tileIndex); + + invalidatedLayers |= 1U << tile.layerIndex; + + tilesPos++; + } + + if (tileCount > 0) + InvalidateVertices(); + } + + /*! + * \brief Enable/Disable isometric mode + * + * If enabled, every odd line will overlap by half the tile size with the upper line + * + * \param isometric Should the isometric mode be enabled for this Tilemap + * + * \see IsIsometricModeEnabled + */ + inline void Tilemap::EnableIsometricMode(bool isometric) + { + m_isometricModeEnabled = isometric; + + InvalidateVertices(); + } + + /*! + * \brief Enable and sets the tile at position tilePos + * + * Setup the tile at position tilePos using color, normalized coordinates coords and materialIndex + * + * \param tilePos Position of the tile to enable + * \param coords Normalized coordinates ([0..1]) used to specify which region of the material textures will be used + * \param color The multiplicative color applied to the tile + * \param materialIndex The material which will be used for rendering this tile + * + * \see EnableTiles + */ + inline void Tilemap::EnableTile(const Vector2ui& tilePos, const Rectf& coords, const Color& color, std::size_t materialIndex) + { + NazaraAssert(tilePos.x < m_mapSize.x&& tilePos.y < m_mapSize.y, "Tile position is out of bounds"); + NazaraAssert(materialIndex < m_layers.size(), "Material out of bounds"); + + UInt32 invalidatedLayers = 1U << materialIndex; + + std::size_t tileIndex = tilePos.y * m_mapSize.x + tilePos.x; + Tile& tile = m_tiles[tilePos.y * m_mapSize.x + tilePos.x]; + + if (!tile.enabled) + m_layers[materialIndex].tiles.insert(tileIndex); + else if (materialIndex != tile.layerIndex) + { + m_layers[tile.layerIndex].tiles.erase(tileIndex); + m_layers[materialIndex].tiles.insert(tileIndex); + + invalidatedLayers |= 1U << tile.layerIndex; + } + + tile.enabled = true; + tile.color = color; + tile.textureCoords = coords; + tile.layerIndex = materialIndex; + + InvalidateVertices(); + } + + /*! + * \brief Enable and sets the tile at position tilePos + * + * Setup the tile at position tilePos using color, unnormalized coordinates rect and materialIndex + * + * \param tilePos Position of the tile to enable + * \param coords Unnormalized coordinates ([0..size]) used to specify which region of the material textures will be used + * \param color The multiplicative color applied to the tile + * \param materialIndex The material which will be used for rendering this tile + * + * \remark The material at [materialIndex] must have a valid diffuse map before using this function, + * as the size of the material diffuse map is used to compute normalized texture coordinates before returning. + * + * \see EnableTiles + */ + inline void Tilemap::EnableTile(const Vector2ui& tilePos, const Rectui& rect, const Color& color, std::size_t materialIndex) + { + NazaraAssert(materialIndex < m_layers.size(), "Material out of bounds"); + + Vector2ui textureSize(GetTextureSize(materialIndex)); + float invWidth = 1.f / textureSize.x; + float invHeight = 1.f / textureSize.y; + + Rectf unnormalizedCoords(invWidth * rect.x, invHeight * rect.y, invWidth * rect.width, invHeight * rect.height); + EnableTile(tilePos, unnormalizedCoords, color, materialIndex); + } + + /*! + * \brief Enable and sets all the tiles + * + * Setup all tiles using color, normalized coordinates coords and materialIndex + * + * \param coords Normalized coordinates ([0..1]) used to specify which region of the material textures will be used + * \param color The multiplicative color applied to the tile + * \param materialIndex The material which will be used for rendering this tile + * + * \remark The material at [materialIndex] must have a valid diffuse map before using this function, + * as the size of the material diffuse map is used to compute normalized texture coordinates before returning. + * + * \see EnableTile + */ + inline void Tilemap::EnableTiles(const Rectf& coords, const Color& color, std::size_t materialIndex) + { + NazaraAssert(materialIndex < m_layers.size(), "Material out of bounds"); + + for (Layer& layer : m_layers) + layer.tiles.clear(); + + std::size_t tileIndex = 0; + for (Tile& tile : m_tiles) + { + tile.enabled = true; + tile.color = color; + tile.textureCoords = coords; + tile.layerIndex = materialIndex; + + m_layers[materialIndex].tiles.insert(tileIndex++); + } + + InvalidateVertices(); + } + + /*! + * \brief Enable and sets all the tiles + * + * Setup all tiles using color, unnormalized coordinates coords and materialIndex + * + * \param coords Unnormalized coordinates ([0..size]) used to specify which region of the material textures will be used + * \param color The multiplicative color applied to the tile + * \param materialIndex The material which will be used for rendering this tile + * + * \remark The material at [materialIndex] must have a valid diffuse map before using this function, + * as the size of the material diffuse map is used to compute normalized texture coordinates before returning. + * + * \see EnableTile + */ + inline void Tilemap::EnableTiles(const Rectui& rect, const Color& color, std::size_t materialIndex) + { + NazaraAssert(materialIndex < m_layers.size(), "Material out of bounds"); + + Vector2ui textureSize(GetTextureSize(materialIndex)); + float invWidth = 1.f / textureSize.x; + float invHeight = 1.f / textureSize.y; + + Rectf unnormalizedCoords(invWidth * rect.x, invHeight * rect.y, invWidth * rect.width, invHeight * rect.height); + EnableTiles(unnormalizedCoords, color, materialIndex); + } + + /*! + * \brief Enable and sets tileCount tiles at positions contained at tilesPos location, enabling rendering at those locations + * + * Setup all tiles using color, normalized coordinates coords and materialIndex + * + * \param tilesPos Pointer to a valid array of at least tileCount positions + * \param tileCount Number of tiles to enable + * \param coords Normalized coordinates ([0..1]) used to specify which region of the material textures will be used + * \param color The multiplicative color applied to the tile + * \param materialIndex The material which will be used for rendering this tile + * + * \see EnableTile + */ + inline void Tilemap::EnableTiles(const Vector2ui* tilesPos, std::size_t tileCount, const Rectf& coords, const Color& color, std::size_t materialIndex) + { + NazaraAssert(tilesPos || tileCount == 0, "Invalid tile position array with a non-zero tileCount"); + NazaraAssert(materialIndex < m_layers.size(), "Material out of bounds"); + + UInt32 invalidatedLayers = 1U << materialIndex; + + for (std::size_t i = 0; i < tileCount; ++i) + { + NazaraAssert(tilesPos->x < m_mapSize.x&& tilesPos->y < m_mapSize.y, "Tile position is out of bounds"); + + std::size_t tileIndex = tilesPos->y * m_mapSize.x + tilesPos->x; + Tile& tile = m_tiles[tileIndex]; + + if (!tile.enabled) + m_layers[materialIndex].tiles.insert(tileIndex); + else if (materialIndex != tile.layerIndex) + { + m_layers[tile.layerIndex].tiles.erase(tileIndex); + m_layers[materialIndex].tiles.insert(tileIndex); + + invalidatedLayers |= 1U << tile.layerIndex; + } + + tile.enabled = true; + tile.color = color; + tile.textureCoords = coords; + tile.layerIndex = materialIndex; + tilesPos++; + } + + if (tileCount > 0) + InvalidateVertices(); + } + + /*! + * \brief Enable and sets tileCount tiles at positions contained at tilesPos location, enabling rendering at those locations + * + * Setup all tiles using color, unnormalized coordinates coords and materialIndex + * + * \param tilesPos Pointer to a valid array of at least tileCount positions + * \param tileCount Number of tiles to enable + * \param coords Unnormalized coordinates ([0..size]) used to specify which region of the material textures will be used + * \param color The multiplicative color applied to the tile + * \param materialIndex The material which will be used for rendering this tile + * + * \remark The material at [materialIndex] must have a valid diffuse map before using this function, + * as the size of the material diffuse map is used to compute normalized texture coordinates before returning. + * + * \see EnableTile + */ + inline void Tilemap::EnableTiles(const Vector2ui* tilesPos, std::size_t tileCount, const Rectui& rect, const Color& color, std::size_t materialIndex) + { + NazaraAssert(materialIndex < m_layers.size(), "Material out of bounds"); + + Vector2ui textureSize(GetTextureSize(materialIndex)); + float invWidth = 1.f / textureSize.x; + float invHeight = 1.f / textureSize.y; + + Rectf unnormalizedCoords(invWidth * rect.x, invHeight * rect.y, invWidth * rect.width, invHeight * rect.height); + EnableTiles(tilesPos, tileCount, unnormalizedCoords, color, materialIndex); + } + + /*! + * \brief Gets the tilemap size (i.e. number of tiles in each dimension) + * \return Number of tiles in each dimension + * + * \see GetSize + * \see GetTileSize + */ + inline const Vector2ui& Tilemap::GetMapSize() const + { + return m_mapSize; + } + + /*! + * \brief Returns the size of the tilemap in units (which is equivalent to GetMapSize() * GetTileSize()) + * \return Maximum size in units occupied by this tilemap + * + * \see GetMapSize + * \see GetTileSize + */ + inline Vector2f Tilemap::GetSize() const + { + return Vector2f(m_mapSize) * m_tileSize; + } + + /*! + * \brief Returns informations about a particular tile + * + * \param tilePos Position of the tile to get (enabled or not) + * + * \return Maximum size in units occupied by this tilemap + */ + inline const Tilemap::Tile& Tilemap::GetTile(const Vector2ui& tilePos) const + { + NazaraAssert(tilePos.x < m_mapSize.x&& tilePos.y < m_mapSize.y, "Tile position is out of bounds"); + + return m_tiles[tilePos.y * m_mapSize.x + tilePos.x]; + } + + /*! + * \brief Gets the tile size (i.e. number of units occupied by a tile in each dimension) + * \return Tile size in each dimension + * + * \see GetMapSize + * \see GetSize + */ + inline const Vector2f& Tilemap::GetTileSize() const + { + return m_tileSize; + } + + /*! + * \brief Gets the actual state of the isometric mode + * \return True if the isometric mode is enabled + * + * \see EnableIsometricMode + */ + inline bool Tilemap::IsIsometricModeEnabled() const + { + return m_isometricModeEnabled; + } + + inline void Tilemap::SetOrigin(const Vector3f& origin) + { + m_origin = origin; + + InvalidateVertices(); + } + + inline void Tilemap::InvalidateVertices() + { + m_shouldRebuildVertices = true; + OnElementInvalidated(this); + } +} + +#include diff --git a/src/Nazara/Graphics/Tilemap.cpp b/src/Nazara/Graphics/Tilemap.cpp new file mode 100644 index 000000000..4bc9d6b1e --- /dev/null +++ b/src/Nazara/Graphics/Tilemap.cpp @@ -0,0 +1,161 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +namespace Nz +{ + /*! + * \brief Constructs a Tilemap object, containing mapSize tileSize-sized tiles + * + * \param mapSize Number of tiles in each dimension, must be + * \param tileSize Size of each tile of the Tilemap + * \param materialCount The maximum number of differents Material this Tilemap will use + * + * \remark When constructed, a Tilemap has no tile active and will not be rendered + * To use it, you have to enable some tiles. + * + * \remark The default material is used for every material requested + */ + Tilemap::Tilemap(const Vector2ui& mapSize, const Vector2f& tileSize, std::size_t materialCount) : + m_tiles(mapSize.x* mapSize.y), + m_layers(materialCount), + m_mapSize(mapSize), + m_tileSize(tileSize), + m_isometricModeEnabled(false), + m_shouldRebuildVertices(false) + { + NazaraAssert(m_tiles.size() != 0U, "Invalid map size"); + NazaraAssert(m_tileSize.x > 0 && m_tileSize.y > 0, "Invalid tile size"); + NazaraAssert(m_layers.size() != 0U, "Invalid material count"); + + std::shared_ptr defaultMaterialInstance = Graphics::Instance()->GetDefaultMaterials().basicDefault; + for (auto& layer : m_layers) + layer.material = defaultMaterialInstance; + } + + void Tilemap::BuildElement(ElementRendererRegistry& registry, const ElementData& elementData, std::size_t passIndex, std::vector& elements) const + { + if (m_shouldRebuildVertices) + UpdateVertices(); + + const std::shared_ptr& vertexDeclaration = VertexDeclaration::Get(VertexLayout::XYZ_Color_UV); + + RenderPipelineInfo::VertexBufferData vertexBufferData = { + 0, + vertexDeclaration + }; + + const auto& whiteTexture = Graphics::Instance()->GetDefaultTextures().whiteTextures[UnderlyingCast(ImageType::E2D)]; + + const VertexStruct_XYZ_Color_UV* vertices = reinterpret_cast(m_vertices.data()); + for (std::size_t layerIndex = 0; layerIndex < m_layers.size(); ++layerIndex) + { + const auto& layer = m_layers[layerIndex]; + if (layer.tiles.empty()) + continue; + + const auto& materialPipeline = layer.material->GetPipeline(passIndex); + if (!materialPipeline) + return; + + MaterialPassFlags passFlags = layer.material->GetPassFlags(passIndex); + + const auto& renderPipeline = materialPipeline->GetRenderPipeline(&vertexBufferData, 1); + + std::size_t spriteCount = layer.tiles.size(); + elements.emplace_back(registry.AllocateElement(GetRenderLayer(), layer.material, passFlags, renderPipeline, *elementData.worldInstance, vertexDeclaration, whiteTexture, spriteCount, vertices, *elementData.scissorBox)); + + vertices += 4 * spriteCount; + } + } + + const std::shared_ptr& Tilemap::GetMaterial(std::size_t i) const + { + assert(i < m_layers.size()); + return m_layers[i].material; + } + + std::size_t Tilemap::GetMaterialCount() const + { + return m_layers.size(); + } + + void Tilemap::SetMaterial(std::size_t matIndex, std::shared_ptr material) + { + NazaraAssert(matIndex < m_layers.size(), "Material index out of bounds"); + NazaraAssert(material, "invalid material"); + + OnMaterialInvalidated(this, matIndex, material); + m_layers[matIndex].material = std::move(material); + } + + Vector3ui Tilemap::GetTextureSize(std::size_t matIndex) const + { + assert(matIndex < m_layers.size()); + + //TODO: Cache index in registry? + if (const std::shared_ptr* textureOpt = m_layers[matIndex].material->GetTextureProperty("BaseColorMap")) + { + // Material should always have textures but we're better safe than sorry + if (const std::shared_ptr& texture = *textureOpt) + return texture->GetSize(); + } + + // Couldn't get material pass or texture + return Vector3ui::Unit(); //< prevents division by zero + } + + void Tilemap::UpdateVertices() const + { + std::size_t spriteCount = 0; + for (const Layer& layer : m_layers) + spriteCount += layer.tiles.size(); + + m_vertices.resize(spriteCount * 4); + VertexStruct_XYZ_Color_UV* vertexPtr = reinterpret_cast(m_vertices.data()); + + Vector3f originShift = m_origin * GetSize(); + + float topCorner = m_tileSize.y * (m_mapSize.y - 1); + + for (const Layer& layer : m_layers) + { + for (std::size_t tileIndex : layer.tiles) + { + const Tile& tile = m_tiles[tileIndex]; + NazaraAssert(tile.enabled, "Tile specified for rendering is not enabled"); + + std::size_t x = tileIndex % m_mapSize.x; + std::size_t y = tileIndex / m_mapSize.x; + + Vector3f tileLeftBottom; + if (m_isometricModeEnabled) + tileLeftBottom.Set(x * m_tileSize.x + m_tileSize.x / 2.f * (y % 2), topCorner - y / 2.f * m_tileSize.y, 0.f); + else + tileLeftBottom.Set(x * m_tileSize.x, topCorner - y * m_tileSize.y, 0.f); + + std::array cornerExtent; + cornerExtent[UnderlyingCast(RectCorner::LeftBottom)] = Vector2f(0.f, 0.f); + cornerExtent[UnderlyingCast(RectCorner::RightBottom)] = Vector2f(1.f, 0.f); + cornerExtent[UnderlyingCast(RectCorner::LeftTop)] = Vector2f(0.f, 1.f); + cornerExtent[UnderlyingCast(RectCorner::RightTop)] = Vector2f(1.f, 1.f); + + for (RectCorner corner : { RectCorner::LeftBottom, RectCorner::RightBottom, RectCorner::LeftTop, RectCorner::RightTop }) + { + vertexPtr->color = tile.color; + vertexPtr->position = tileLeftBottom + Vector3f(m_tileSize * cornerExtent[UnderlyingCast(corner)], 0.f) - originShift; + vertexPtr->uv = tile.textureCoords.GetCorner(corner); + ++vertexPtr; + } + } + } + + m_shouldRebuildVertices = false; + } +}