// Copyright (C) 2015 Jérôme Leclercq // 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 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 */ inline TileMap::TileMap(const Nz::Vector2ui& mapSize, const Nz::Vector2f& tileSize, std::size_t materialCount) : m_tiles(mapSize.x * mapSize.y), m_layers(materialCount), m_mapSize(mapSize), m_tileSize(tileSize), m_isometricModeEnabled(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"); for (Layer& layer : m_layers) layer.material = Material::GetDefault(); InvalidateBoundingVolume(); } /*! * \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); InvalidateInstanceData(1U << tile.layerIndex); } /*! * \brief Disable all tiles */ inline void TileMap::DisableTiles() { for (Tile& tile : m_tiles) tile.enabled = false; for (Layer& layer : m_layers) layer.tiles.clear(); InvalidateInstanceData(0xFFFFFFFF); } /*! * \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) InvalidateInstanceData(invalidatedLayers); } /*! * \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; InvalidateInstanceData(0xFFFFFFFF); } /*! * \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[materialIndex].tiles.erase(tileIndex); m_layers[tile.layerIndex].tiles.insert(tileIndex); invalidatedLayers |= 1U << tile.layerIndex; } tile.enabled = true; tile.color = color; tile.textureCoords = coords; tile.layerIndex = materialIndex; InvalidateInstanceData(invalidatedLayers); } /*! * \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"); NazaraAssert(m_layers[materialIndex].material->HasDiffuseMap(), "Material has no diffuse map"); Texture* diffuseMap = m_layers[materialIndex].material->GetDiffuseMap(); float invWidth = 1.f / diffuseMap->GetWidth(); float invHeight = 1.f / diffuseMap->GetHeight(); 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++); } InvalidateInstanceData(0xFFFFFFFF); } /*! * \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"); Texture* diffuseMap = m_layers[materialIndex].material->GetDiffuseMap(); float invWidth = 1.f / diffuseMap->GetWidth(); float invHeight = 1.f / diffuseMap->GetHeight(); 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[materialIndex].tiles.erase(tileIndex); m_layers[tile.layerIndex].tiles.insert(tileIndex); invalidatedLayers |= 1U << tile.layerIndex; } tile.enabled = true; tile.color = color; tile.textureCoords = coords; tile.layerIndex = materialIndex; tilesPos++; } if (tileCount > 0) InvalidateInstanceData(invalidatedLayers); } /*! * \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"); NazaraAssert(m_layers[materialIndex].material->HasDiffuseMap(), "Material has no diffuse map"); Texture* diffuseMap = m_layers[materialIndex].material->GetDiffuseMap(); float invWidth = 1.f / diffuseMap->GetWidth(); float invHeight = 1.f / diffuseMap->GetHeight(); Rectf unnormalizedCoords(invWidth * rect.x, invHeight * rect.y, invWidth * rect.width, invHeight * rect.height); EnableTiles(tilesPos, tileCount, unnormalizedCoords, color, materialIndex); } /*! * \brief Gets the material at position index used by the TileMap * * \param index Index of the material to query * * \return Material at index */ inline const MaterialRef& TileMap::GetMaterial(std::size_t index) const { NazaraAssert(index < m_layers.size(), "Material out of bounds"); return m_layers[index].material; } /*! * \brief Gets the maximum material count this TileMap can use * \return Material count */ inline std::size_t TileMap::GetMaterialCount() const { return m_layers.size(); } /*! * \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; } /*! * \brief Sets a material of the TileMap * * \param index Index of the material to change * \param material Material for the TileMap */ inline void TileMap::SetMaterial(std::size_t index, MaterialRef material) { NazaraAssert(index < m_layers.size(), "Material out of bounds"); m_layers[index].material = std::move(material); } /*! * \brief Sets the current TileMap with the content of the other one * \return A reference to this * * \param TileMap The other TileMap */ inline TileMap& TileMap::operator=(const TileMap& tileMap) { InstancedRenderable::operator=(tileMap); m_layers = tileMap.m_layers; m_mapSize = tileMap.m_mapSize; m_tiles = tileMap.m_tiles; m_tileSize = tileMap.m_tileSize; // We do not copy final vertices because it's highly probable that our parameters are modified and they must be regenerated InvalidateBoundingVolume(); InvalidateInstanceData(0xFFFFFFFF); return *this; } /*! * \brief Creates a new TileMap from the arguments * \return A reference to the newly created TileMap * * \param args Arguments for the TileMap */ template TileMapRef TileMap::New(Args&&... args) { std::unique_ptr object(new TileMap(std::forward(args)...)); object->SetPersistent(false); return object.release(); } } #include