Index: source/graphics/LOSTexture.cpp =================================================================== --- source/graphics/LOSTexture.cpp +++ source/graphics/LOSTexture.cpp @@ -20,7 +20,6 @@ #include "LOSTexture.h" #include "graphics/ShaderManager.h" -#include "graphics/Terrain.h" #include "lib/bits.h" #include "lib/config2.h" #include "ps/CLogger.h" @@ -31,12 +30,11 @@ #include "renderer/TimeManager.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpRangeManager.h" -#include "simulation2/components/ICmpTerrain.h" #include "simulation2/helpers/Los.h" /* -The LOS bitmap is computed with one value per map vertex, based on +The LOS bitmap is computed with one value per LOS vertex, based on CCmpRangeManager's visibility information. The bitmap is then blurred using an NxN filter (in particular a @@ -243,11 +241,11 @@ void CLOSTexture::ConstructTexture(int unit) { - CmpPtr cmpTerrain(m_Simulation, SYSTEM_ENTITY); - if (!cmpTerrain) + CmpPtr cmpRangeManager(m_Simulation, SYSTEM_ENTITY); + if (!cmpRangeManager) return; - m_MapSize = cmpTerrain->GetVerticesPerSide(); + m_MapSize = cmpRangeManager->GetVerticesPerSide(); m_TextureSize = (GLsizei)round_up_to_pow2(round_up((size_t)m_MapSize + g_BlurSize - 1, g_SubTextureAlignment)); @@ -294,7 +292,7 @@ // world pos ((mapsize-1)*cellsize, y, (mapsize-1)*cellsize) (i.e. last vertex) // onto texcoord ((mapsize-0.5) / texsize, (mapsize-0.5) / texsize) (i.e. middle of last texel) - float s = (m_MapSize-1) / (float)(m_TextureSize * (m_MapSize-1) * TERRAIN_TILE_SIZE); + float s = (m_MapSize-1) / (float)(m_TextureSize * (m_MapSize-1) * LOS_TILE_SIZE); float t = 0.5f / m_TextureSize; m_TextureMatrix.SetZero(); m_TextureMatrix._11 = s; @@ -320,8 +318,8 @@ // If the map was resized, delete and regenerate the texture if (m_Texture) { - CmpPtr cmpTerrain(m_Simulation, SYSTEM_ENTITY); - if (cmpTerrain && m_MapSize != (ssize_t)cmpTerrain->GetVerticesPerSide()) + CmpPtr cmpRangeManager(m_Simulation, SYSTEM_ENTITY); + if (!cmpRangeManager || m_MapSize != cmpRangeManager->GetVerticesPerSide()) DeleteTexture(); } Index: source/simulation2/components/CCmpProjectileManager.cpp =================================================================== --- source/simulation2/components/CCmpProjectileManager.cpp +++ source/simulation2/components/CCmpProjectileManager.cpp @@ -362,8 +362,8 @@ const CFrustum& frustum, bool culling, const CLosQuerier& los, bool losRevealAll) const { // Don't display objects outside the visible area - ssize_t posi = (ssize_t)(0.5f + position.X / TERRAIN_TILE_SIZE); - ssize_t posj = (ssize_t)(0.5f + position.Z / TERRAIN_TILE_SIZE); + ssize_t posi = (ssize_t)(0.5f + position.X / LOS_TILE_SIZE); + ssize_t posj = (ssize_t)(0.5f + position.Z / LOS_TILE_SIZE); if (!losRevealAll && !los.IsVisible(posi, posj)) return; Index: source/simulation2/components/CCmpRallyPointRenderer.cpp =================================================================== --- source/simulation2/components/CCmpRallyPointRenderer.cpp +++ source/simulation2/components/CCmpRallyPointRenderer.cpp @@ -816,10 +816,10 @@ // Go through the path node list, comparing each node's visibility with the previous one. If it changes, end the current segment and start // a new one at the next point. - const float terrainSize = static_cast(TERRAIN_TILE_SIZE); + const float cellSize = static_cast(LOS_TILE_SIZE); bool lastVisible = losQuerier.IsExplored( - (fixed::FromFloat(m_Path[index][0].X / terrainSize)).ToInt_RoundToNearest(), - (fixed::FromFloat(m_Path[index][0].Y / terrainSize)).ToInt_RoundToNearest() + (fixed::FromFloat(m_Path[index][0].X / cellSize)).ToInt_RoundToNearest(), + (fixed::FromFloat(m_Path[index][0].Y / cellSize)).ToInt_RoundToNearest() ); // Starting node index of the current segment size_t curSegmentStartIndex = 0; @@ -827,8 +827,8 @@ for (size_t k = 1; k < m_Path[index].size(); ++k) { // Grab tile indices for this coord - int i = (fixed::FromFloat(m_Path[index][k].X / terrainSize)).ToInt_RoundToNearest(); - int j = (fixed::FromFloat(m_Path[index][k].Y / terrainSize)).ToInt_RoundToNearest(); + int i = (fixed::FromFloat(m_Path[index][k].X / cellSize)).ToInt_RoundToNearest(); + int j = (fixed::FromFloat(m_Path[index][k].Y / cellSize)).ToInt_RoundToNearest(); bool nodeVisible = losQuerier.IsExplored(i, j); if (nodeVisible != lastVisible) Index: source/simulation2/components/CCmpRangeManager.cpp =================================================================== --- source/simulation2/components/CCmpRangeManager.cpp +++ source/simulation2/components/CCmpRangeManager.cpp @@ -44,8 +44,8 @@ #include "ps/Profile.h" #include "renderer/Scene.h" -#define LOS_TILES_RATIO 8 -#define DEBUG_RANGE_MANAGER_BOUNDS 0 +constexpr int LOS_REGION_RATIO = 8; +constexpr int DEBUG_RANGE_MANAGER_BOUNDS = 0; /** @@ -373,18 +373,18 @@ // LOS state: static const player_id_t MAX_LOS_PLAYER_ID = 16; - using LosTile = std::pair; + using LosRegion = std::pair; std::array m_LosRevealAll; bool m_LosCircular; - i32 m_TerrainVerticesPerSide; + i32 m_LosVerticesPerSide; // Cache for visibility tracking - i32 m_LosTilesPerSide; + i32 m_LosRegionsPerSide; bool m_GlobalVisibilityUpdate; std::array m_GlobalPlayerVisibilityUpdate; Grid m_DirtyVisibility; - Grid> m_LosTiles; + Grid> m_LosRegions; // List of entities that must be updated, regardless of the status of their tile std::vector m_ModifiedEntities; @@ -439,7 +439,7 @@ m_GlobalVisibilityUpdate = true; m_LosCircular = false; - m_TerrainVerticesPerSide = 0; + m_LosVerticesPerSide = 0; } virtual void Deinit() @@ -460,14 +460,14 @@ SerializeArray()(serialize, "los reveal all", m_LosRevealAll); serialize.Bool("los circular", m_LosCircular); - serialize.NumberI32_Unbounded("terrain verts per side", m_TerrainVerticesPerSide); + serialize.NumberI32_Unbounded("los verts per side", m_LosVerticesPerSide); serialize.Bool("global visibility update", m_GlobalVisibilityUpdate); SerializeArray()(serialize, "global player visibility update", m_GlobalPlayerVisibilityUpdate); SerializedGridCompressed()(serialize, "dirty visibility", m_DirtyVisibility); SerializeVector()(serialize, "modified entities", m_ModifiedEntities); - // We don't serialize m_Subdivision, m_LosPlayerCounts or m_LosTiles + // We don't serialize m_Subdivision, m_LosPlayerCounts or m_LosRegions // since they can be recomputed from the entity data when deserializing; // m_LosState must be serialized since it depends on the history of exploration @@ -562,12 +562,12 @@ SharingLosMove(it->second.visionSharing, it->second.visionRange, from, to); else LosMove(it->second.owner, it->second.visionRange, from, to); - LosTile oldLosTile = PosToLosTilesHelper(it->second.x, it->second.z); - LosTile newLosTile = PosToLosTilesHelper(msgData.x, msgData.z); - if (oldLosTile != newLosTile) + LosRegion oldLosRegion = PosToLosRegionsHelper(it->second.x, it->second.z); + LosRegion newLosRegion = PosToLosRegionsHelper(msgData.x, msgData.z); + if (oldLosRegion != newLosRegion) { - RemoveFromTile(oldLosTile, ent); - AddToTile(newLosTile, ent); + RemoveFromTile(oldLosRegion, ent); + AddToTile(newLosRegion, ent); } } else @@ -578,7 +578,7 @@ SharingLosAdd(it->second.visionSharing, it->second.visionRange, to); else LosAdd(it->second.owner, it->second.visionRange, to); - AddToTile(PosToLosTilesHelper(msgData.x, msgData.z), ent); + AddToTile(PosToLosRegionsHelper(msgData.x, msgData.z), ent); } it->second.SetFlag(true); @@ -595,7 +595,7 @@ SharingLosRemove(it->second.visionSharing, it->second.visionRange, from); else LosRemove(it->second.owner, it->second.visionRange, from); - RemoveFromTile(PosToLosTilesHelper(it->second.x, it->second.z), ent); + RemoveFromTile(PosToLosRegionsHelper(it->second.x, it->second.z), ent); } it->second.SetFlag(false); @@ -655,7 +655,7 @@ if (it->second.HasFlag()) { m_Subdivision.Remove(ent, CFixedVector2D(it->second.x, it->second.z), it->second.size); - RemoveFromTile(PosToLosTilesHelper(it->second.x, it->second.z), ent); + RemoveFromTile(PosToLosRegionsHelper(it->second.x, it->second.z), ent); } // This will be called after Ownership's OnDestroy, so ownership will be set @@ -761,13 +761,15 @@ } } - virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, ssize_t vertices) + virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1) { + // Don't support rectangular looking maps. + ENSURE(x1-x0 == z1-z0); m_WorldX0 = x0; m_WorldZ0 = z0; m_WorldX1 = x1; m_WorldZ1 = z1; - m_TerrainVerticesPerSide = (i32)vertices; + m_LosVerticesPerSide = ((x1 - x0) / LOS_TILE_SIZE).ToInt_RoundToZero() + 1; ResetDerivedData(); } @@ -784,7 +786,7 @@ std::array, MAX_LOS_PLAYER_ID> oldPlayerCounts = m_LosPlayerCounts; Grid oldStateRevealed = m_LosStateRevealed; FastSpatialSubdivision oldSubdivision = m_Subdivision; - Grid > oldLosTiles = m_LosTiles; + Grid > oldLosRegions = m_LosRegions; m_Deserializing = true; ResetDerivedData(); @@ -818,7 +820,7 @@ debug_warn(L"inconsistent revealed"); if (oldSubdivision != m_Subdivision) debug_warn(L"inconsistent subdivs"); - if (oldLosTiles != m_LosTiles) + if (oldLosRegions != m_LosRegions) debug_warn(L"inconsistent los tiles"); } @@ -833,7 +835,7 @@ ENSURE(m_WorldX0.IsZero() && m_WorldZ0.IsZero()); // don't bother implementing non-zero offsets yet ResetSubdivisions(m_WorldX1, m_WorldZ1); - m_LosTilesPerSide = (m_TerrainVerticesPerSide - 1)/LOS_TILES_RATIO; + m_LosRegionsPerSide = m_LosVerticesPerSide / LOS_REGION_RATIO; for (size_t player_id = 0; player_id < m_LosPlayerCounts.size(); ++player_id) m_LosPlayerCounts[player_id].clear(); @@ -844,24 +846,24 @@ if (m_Deserializing) { // recalc current exploration stats. - for (i32 j = 0; j < m_TerrainVerticesPerSide; j++) - for (i32 i = 0; i < m_TerrainVerticesPerSide; i++) + for (i32 j = 0; j < m_LosVerticesPerSide; j++) + for (i32 i = 0; i < m_LosVerticesPerSide; i++) if (!LosIsOffWorld(i, j)) for (u8 k = 1; k < MAX_LOS_PLAYER_ID+1; ++k) m_ExploredVertices.at(k) += ((m_LosState.get(i, j) & ((u32)LosState::EXPLORED << (2*(k-1)))) > 0); } else - m_LosState.resize(m_TerrainVerticesPerSide, m_TerrainVerticesPerSide); + m_LosState.resize(m_LosVerticesPerSide, m_LosVerticesPerSide); - m_LosStateRevealed.resize(m_TerrainVerticesPerSide, m_TerrainVerticesPerSide); + m_LosStateRevealed.resize(m_LosVerticesPerSide, m_LosVerticesPerSide); if (!m_Deserializing) { - m_DirtyVisibility.resize(m_LosTilesPerSide, m_LosTilesPerSide); + m_DirtyVisibility.resize(m_LosRegionsPerSide, m_LosRegionsPerSide); } - ENSURE(m_DirtyVisibility.width() == m_LosTilesPerSide); - ENSURE(m_DirtyVisibility.height() == m_LosTilesPerSide); + ENSURE(m_DirtyVisibility.width() == m_LosRegionsPerSide); + ENSURE(m_DirtyVisibility.height() == m_LosRegionsPerSide); - m_LosTiles.resize(m_LosTilesPerSide, m_LosTilesPerSide); + m_LosRegions.resize(m_LosRegionsPerSide, m_LosRegionsPerSide); for (EntityMap::const_iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it) if (it->second.HasFlag()) @@ -870,15 +872,15 @@ SharingLosAdd(it->second.visionSharing, it->second.visionRange, CFixedVector2D(it->second.x, it->second.z)); else LosAdd(it->second.owner, it->second.visionRange, CFixedVector2D(it->second.x, it->second.z)); - AddToTile(PosToLosTilesHelper(it->second.x, it->second.z), it->first); + AddToTile(PosToLosRegionsHelper(it->second.x, it->second.z), it->first); if (it->second.HasFlag()) RevealShore(it->second.owner, true); } m_TotalInworldVertices = 0; - for (ssize_t j = 0; j < m_TerrainVerticesPerSide; ++j) - for (ssize_t i = 0; i < m_TerrainVerticesPerSide; ++i) + for (ssize_t j = 0; j < m_LosVerticesPerSide; ++j) + for (ssize_t i = 0; i < m_LosVerticesPerSide; ++i) { if (LosIsOffWorld(i,j)) m_LosStateRevealed.get(i, j) = 0; @@ -1590,9 +1592,9 @@ virtual CLosQuerier GetLosQuerier(player_id_t player) const { if (GetLosRevealAll(player)) - return CLosQuerier(0xFFFFFFFFu, m_LosStateRevealed, m_TerrainVerticesPerSide); + return CLosQuerier(0xFFFFFFFFu, m_LosStateRevealed, m_LosVerticesPerSide); else - return CLosQuerier(GetSharedLosMask(player), m_LosState, m_TerrainVerticesPerSide); + return CLosQuerier(GetSharedLosMask(player), m_LosState, m_LosVerticesPerSide); } virtual void ActivateScriptedVisibility(entity_id_t ent, bool status) @@ -1617,8 +1619,8 @@ return LosVisibility::HIDDEN; CFixedVector2D pos = cmpPosition->GetPosition2D(); - int i = (pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest(); - int j = (pos.Y / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest(); + int i = (pos.X / LOS_TILE_SIZE).ToInt_RoundToNearest(); + int j = (pos.Y / LOS_TILE_SIZE).ToInt_RoundToNearest(); // Reveal flag makes all positioned entities visible and all mirages useless if (GetLosRevealAll(player)) @@ -1629,7 +1631,7 @@ } // Get visible regions - CLosQuerier los(GetSharedLosMask(player), m_LosState, m_TerrainVerticesPerSide); + CLosQuerier los(GetSharedLosMask(player), m_LosState, m_LosVerticesPerSide); CmpPtr cmpVisibility(ent); @@ -1723,7 +1725,7 @@ CFixedVector2D pos = cmpPosition->GetPosition2D(); - if (IsVisibilityDirty(m_DirtyVisibility[PosToLosTilesHelper(pos.X, pos.Y)], player)) + if (IsVisibilityDirty(m_DirtyVisibility[PosToLosRegionsHelper(pos.X, pos.Y)], player)) return ComputeLosVisibility(ent, player); if (std::find(m_ModifiedEntities.begin(), m_ModifiedEntities.end(), entId) != m_ModifiedEntities.end()) @@ -1744,8 +1746,8 @@ virtual LosVisibility GetLosVisibilityPosition(entity_pos_t x, entity_pos_t z, player_id_t player) const { - int i = (x / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest(); - int j = (z / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest(); + int i = (x / LOS_TILE_SIZE).ToInt_RoundToNearest(); + int j = (z / LOS_TILE_SIZE).ToInt_RoundToNearest(); // Reveal flag makes all positioned entities visible and all mirages useless if (GetLosRevealAll(player)) @@ -1757,7 +1759,7 @@ } // Get visible regions - CLosQuerier los(GetSharedLosMask(player), m_LosState, m_TerrainVerticesPerSide); + CLosQuerier los(GetSharedLosMask(player), m_LosState, m_LosVerticesPerSide); if (los.IsVisible(i,j)) return LosVisibility::VISIBLE; @@ -1766,47 +1768,55 @@ return LosVisibility::HIDDEN; } - LosTile PosToLosTilesHelper(u16 x, u16 z) const + size_t GetVerticesPerSide() const { - return LosTile{ Clamp(x/LOS_TILES_RATIO, 0, m_LosTilesPerSide - 1), Clamp(z/LOS_TILES_RATIO, 0, m_LosTilesPerSide - 1) }; + return m_LosVerticesPerSide; } - LosTile PosToLosTilesHelper(entity_pos_t x, entity_pos_t z) const + LosRegion LosVertexToLosRegionsHelper(u16 x, u16 z) const { - i32 i = Clamp( - (x/(entity_pos_t::FromInt(TERRAIN_TILE_SIZE * LOS_TILES_RATIO))).ToInt_RoundToZero(), + return LosRegion { + Clamp(x/LOS_REGION_RATIO, 0, m_LosRegionsPerSide - 1), + Clamp(z/LOS_REGION_RATIO, 0, m_LosRegionsPerSide - 1) + }; + } + + LosRegion PosToLosRegionsHelper(entity_pos_t x, entity_pos_t z) const + { + u16 i = Clamp( + ((x/LOS_TILE_SIZE)/LOS_REGION_RATIO).ToInt_RoundToZero(), 0, - m_LosTilesPerSide - 1); - i32 j = Clamp( - (z/(entity_pos_t::FromInt(TERRAIN_TILE_SIZE * LOS_TILES_RATIO))).ToInt_RoundToZero(), + m_LosRegionsPerSide - 1); + u16 j = Clamp( + ((z/LOS_TILE_SIZE)/LOS_REGION_RATIO).ToInt_RoundToZero(), 0, - m_LosTilesPerSide - 1); + m_LosRegionsPerSide - 1); return std::make_pair(i, j); } - void AddToTile(LosTile tile, entity_id_t ent) + void AddToTile(LosRegion tile, entity_id_t ent) { - m_LosTiles[tile].insert(ent); + m_LosRegions[tile].insert(ent); } - void RemoveFromTile(LosTile tile, entity_id_t ent) + void RemoveFromTile(LosRegion tile, entity_id_t ent) { - std::set::const_iterator tileIt = m_LosTiles[tile].find(ent); - if (tileIt != m_LosTiles[tile].end()) - m_LosTiles[tile].erase(tileIt); + std::set::const_iterator tileIt = m_LosRegions[tile].find(ent); + if (tileIt != m_LosRegions[tile].end()) + m_LosRegions[tile].erase(tileIt); } void UpdateVisibilityData() { PROFILE("UpdateVisibilityData"); - for (u16 i = 0; i < m_LosTilesPerSide; ++i) - for (u16 j = 0; j < m_LosTilesPerSide; ++j) + for (u16 i = 0; i < m_LosRegionsPerSide; ++i) + for (u16 j = 0; j < m_LosRegionsPerSide; ++j) { - LosTile pos{i, j}; + LosRegion pos{i, j}; for (player_id_t player = 1; player < MAX_LOS_PLAYER_ID + 1; ++player) if (IsVisibilityDirty(m_DirtyVisibility[pos], player) || m_GlobalPlayerVisibilityUpdate[player-1] == 1 || m_GlobalVisibilityUpdate) - for (const entity_id_t& ent : m_LosTiles[pos]) + for (const entity_id_t& ent : m_LosRegions[pos]) UpdateVisibility(ent, player); m_DirtyVisibility[pos] = 0; @@ -1928,8 +1938,8 @@ void ExploreAllTiles(player_id_t p) { - for (u16 j = 0; j < m_TerrainVerticesPerSide; ++j) - for (u16 i = 0; i < m_TerrainVerticesPerSide; ++i) + for (u16 j = 0; j < m_LosVerticesPerSide; ++j) + for (u16 i = 0; i < m_LosVerticesPerSide; ++i) { if (LosIsOffWorld(i,j)) continue; @@ -1949,40 +1959,30 @@ const Grid& grid = cmpTerritoryManager->GetTerritoryGrid(); // Territory data is stored per territory-tile (typically a multiple of terrain-tiles). - // LOS data is stored per terrain-tile vertex. + // LOS data is stored per los tile. + auto scale = [](i32 coord) { return coord * LOS_TILE_SIZE / (ICmpTerritoryManager::NAVCELLS_PER_TERRITORY_TILE*Pathfinding::NAVCELLS_PER_TILE); }; // For each territory-tile, if it is owned by a valid player then update the LOS // for every vertex inside/around that tile, to mark them as explored. - - // Currently this code doesn't support territory-tiles smaller than terrain-tiles - // (it will get scale==0 and break), or a non-integer multiple, so check that first - cassert(ICmpTerritoryManager::NAVCELLS_PER_TERRITORY_TILE >= Pathfinding::NAVCELLS_PER_TILE); - cassert(ICmpTerritoryManager::NAVCELLS_PER_TERRITORY_TILE % Pathfinding::NAVCELLS_PER_TILE == 0); - - int scale = ICmpTerritoryManager::NAVCELLS_PER_TERRITORY_TILE / Pathfinding::NAVCELLS_PER_TILE; - - ENSURE(grid.m_W*scale == m_TerrainVerticesPerSide-1 && grid.m_H*scale == m_TerrainVerticesPerSide-1); - - for (u16 j = 0; j < grid.m_H; ++j) - for (u16 i = 0; i < grid.m_W; ++i) + for (u16 j = 0; j < m_LosVerticesPerSide; ++j) + for (u16 i = 0; i < m_LosVerticesPerSide; ++i) { - u8 p = grid.get(i, j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK; + // This does slightly redundant work is the los grid is smaller than the territory grid + // (but it's unlikely to matter much). + u8 p = grid.get(scale(i), scale(j)) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK; if (p > 0 && p <= MAX_LOS_PLAYER_ID) { u32& explored = m_ExploredVertices.at(p); - for (int tj = j * scale; tj <= (j+1) * scale; ++tj) - for (int ti = i * scale; ti <= (i+1) * scale; ++ti) - { - if (LosIsOffWorld(ti, tj)) - continue; - u32& losState = m_LosState.get(ti, tj); - if (!(losState & ((u32)LosState::EXPLORED << (2*(p-1))))) - { - ++explored; - losState |= ((u32)LosState::EXPLORED << (2*(p-1))); - } - } + if (LosIsOffWorld(i, j)) + continue; + + u32& losState = m_LosState.get(i, j); + if (!(losState & ((u32)LosState::EXPLORED << (2*(p-1))))) + { + ++explored; + losState |= ((u32)LosState::EXPLORED << (2*(p-1))); + } } } @@ -2011,10 +2011,10 @@ continue; CFixedVector2D pos = cmpPosition->GetPosition2D(); - int i = (pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest(); - int j = (pos.Y / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest(); + int i = (pos.X / LOS_TILE_SIZE).ToInt_RoundToNearest(); + int j = (pos.Y / LOS_TILE_SIZE).ToInt_RoundToNearest(); - CLosQuerier los(GetSharedLosMask(p), m_LosState, m_TerrainVerticesPerSide); + CLosQuerier los(GetSharedLosMask(p), m_LosState, m_LosVerticesPerSide); if (!los.IsExplored(i,j) || los.IsVisible(i,j)) continue; @@ -2041,7 +2041,7 @@ CmpPtr cmpPathfinder(GetSystemEntity()); const Grid& shoreGrid = cmpPathfinder->ComputeShoreGrid(true); - ENSURE(shoreGrid.m_W == m_TerrainVerticesPerSide-1 && shoreGrid.m_H == m_TerrainVerticesPerSide-1); + ENSURE(shoreGrid.m_W == m_LosVerticesPerSide-1 && shoreGrid.m_H == m_LosVerticesPerSide-1); Grid& counts = m_LosPlayerCounts.at(p); ENSURE(!counts.blank()); @@ -2071,10 +2071,10 @@ { // With a circular map, vertex is off-world if hypot(i - size/2, j - size/2) >= size/2: - ssize_t dist2 = (i - m_TerrainVerticesPerSide/2)*(i - m_TerrainVerticesPerSide/2) - + (j - m_TerrainVerticesPerSide/2)*(j - m_TerrainVerticesPerSide/2); + ssize_t dist2 = (i - m_LosVerticesPerSide/2)*(i - m_LosVerticesPerSide/2) + + (j - m_LosVerticesPerSide/2)*(j - m_LosVerticesPerSide/2); - ssize_t r = m_TerrainVerticesPerSide / 2 - MAP_EDGE_TILES + 1; + ssize_t r = m_LosVerticesPerSide / 2 - MAP_EDGE_TILES + 1; // subtract a bit from the radius to ensure nice // SoD blurring around the edges of the map @@ -2085,8 +2085,8 @@ // With a square map, the outermost edge of the map should be off-world, // so the SoD texture blends out nicely return i < MAP_EDGE_TILES || j < MAP_EDGE_TILES || - i >= m_TerrainVerticesPerSide - MAP_EDGE_TILES || - j >= m_TerrainVerticesPerSide - MAP_EDGE_TILES; + i >= m_LosVerticesPerSide - MAP_EDGE_TILES || + j >= m_LosVerticesPerSide - MAP_EDGE_TILES; } } @@ -2150,20 +2150,20 @@ // Mark the LoS tiles around the updated vertex // 1: left-up, 2: right-up, 3: left-down, 4: right-down - LosTile n1 = PosToLosTilesHelper(i-1, j-1); - LosTile n2 = PosToLosTilesHelper(i-1, j); - LosTile n3 = PosToLosTilesHelper(i, j-1); - LosTile n4 = PosToLosTilesHelper(i, j); + LosRegion n1 = LosVertexToLosRegionsHelper(i-1, j-1); + LosRegion n2 = LosVertexToLosRegionsHelper(i-1, j); + LosRegion n3 = LosVertexToLosRegionsHelper(i, j-1); + LosRegion n4 = LosVertexToLosRegionsHelper(i, j); u16 sharedDirtyVisibilityMask = m_SharedDirtyVisibilityMasks[owner]; if (j > 0 && i > 0) m_DirtyVisibility[n1] |= sharedDirtyVisibilityMask; - if (n2 != n1 && j > 0 && i < m_TerrainVerticesPerSide) + if (n2 != n1 && j > 0 && i < m_LosVerticesPerSide) m_DirtyVisibility[n2] |= sharedDirtyVisibilityMask; - if (n3 != n1 && j < m_TerrainVerticesPerSide && i > 0) + if (n3 != n1 && j < m_LosVerticesPerSide && i > 0) m_DirtyVisibility[n3] |= sharedDirtyVisibilityMask; - if (n4 != n1 && j < m_TerrainVerticesPerSide && i < m_TerrainVerticesPerSide) + if (n4 != n1 && j < m_LosVerticesPerSide && i < m_LosVerticesPerSide) m_DirtyVisibility[n4] |= sharedDirtyVisibilityMask; } @@ -2175,7 +2175,7 @@ template void LosUpdateHelper(u8 owner, entity_pos_t visionRange, CFixedVector2D pos) { - if (m_TerrainVerticesPerSide == 0) // do nothing if not initialised yet + if (m_LosVerticesPerSide == 0) // do nothing if not initialised yet return; PROFILE("LosUpdateHelper"); @@ -2184,7 +2184,7 @@ // Lazy initialisation of counts: if (counts.blank()) - counts.resize(m_TerrainVerticesPerSide, m_TerrainVerticesPerSide); + counts.resize(m_LosVerticesPerSide, m_LosVerticesPerSide); // Compute the circular region as a series of strips. // Rather than quantise pos to vertexes, we do more precise sub-tile computations @@ -2197,15 +2197,15 @@ // Compute top/bottom coordinates, and clamp to exclude the 1-tile border around the map // (so that we never render the sharp edge of the map) - i32 j0 = ((pos.Y - visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToInfinity(); - i32 j1 = ((pos.Y + visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(); + i32 j0 = ((pos.Y - visionRange)/LOS_TILE_SIZE).ToInt_RoundToInfinity(); + i32 j1 = ((pos.Y + visionRange)/LOS_TILE_SIZE).ToInt_RoundToNegInfinity(); i32 j0clamp = std::max(j0, 1); - i32 j1clamp = std::min(j1, m_TerrainVerticesPerSide-2); + i32 j1clamp = std::min(j1, m_LosVerticesPerSide-2); // Translate world coordinates into fractional tile-space coordinates - entity_pos_t x = pos.X / (int)TERRAIN_TILE_SIZE; - entity_pos_t y = pos.Y / (int)TERRAIN_TILE_SIZE; - entity_pos_t r = visionRange / (int)TERRAIN_TILE_SIZE; + entity_pos_t x = pos.X / LOS_TILE_SIZE; + entity_pos_t y = pos.Y / LOS_TILE_SIZE; + entity_pos_t r = visionRange / LOS_TILE_SIZE; entity_pos_t r2 = r.Square(); // Compute the integers on either side of x @@ -2247,7 +2247,7 @@ // Clamp the strip to exclude the 1-tile border, // then add or remove the strip as requested i32 i0clamp = std::max(i0, 1); - i32 i1clamp = std::min(i1, m_TerrainVerticesPerSide-2); + i32 i1clamp = std::min(i1, m_LosVerticesPerSide-2); if (adding) LosAddStripHelper(owner, i0clamp, i1clamp, j, counts); else @@ -2262,7 +2262,7 @@ */ void LosUpdateHelperIncremental(u8 owner, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to) { - if (m_TerrainVerticesPerSide == 0) // do nothing if not initialised yet + if (m_LosVerticesPerSide == 0) // do nothing if not initialised yet return; PROFILE("LosUpdateHelperIncremental"); @@ -2271,7 +2271,7 @@ // Lazy initialisation of counts: if (counts.blank()) - counts.resize(m_TerrainVerticesPerSide, m_TerrainVerticesPerSide); + counts.resize(m_LosVerticesPerSide, m_LosVerticesPerSide); // See comments in LosUpdateHelper. // This does exactly the same, except computing the strips for @@ -2280,18 +2280,18 @@ // so we can compute the difference between the removed/added strips // and only have to touch tiles that have a net change.) - i32 j0_from = ((from.Y - visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToInfinity(); - i32 j1_from = ((from.Y + visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(); - i32 j0_to = ((to.Y - visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToInfinity(); - i32 j1_to = ((to.Y + visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(); + i32 j0_from = ((from.Y - visionRange)/LOS_TILE_SIZE).ToInt_RoundToInfinity(); + i32 j1_from = ((from.Y + visionRange)/LOS_TILE_SIZE).ToInt_RoundToNegInfinity(); + i32 j0_to = ((to.Y - visionRange)/LOS_TILE_SIZE).ToInt_RoundToInfinity(); + i32 j1_to = ((to.Y + visionRange)/LOS_TILE_SIZE).ToInt_RoundToNegInfinity(); i32 j0clamp = std::max(std::min(j0_from, j0_to), 1); - i32 j1clamp = std::min(std::max(j1_from, j1_to), m_TerrainVerticesPerSide-2); + i32 j1clamp = std::min(std::max(j1_from, j1_to), m_LosVerticesPerSide-2); - entity_pos_t x_from = from.X / (int)TERRAIN_TILE_SIZE; - entity_pos_t y_from = from.Y / (int)TERRAIN_TILE_SIZE; - entity_pos_t x_to = to.X / (int)TERRAIN_TILE_SIZE; - entity_pos_t y_to = to.Y / (int)TERRAIN_TILE_SIZE; - entity_pos_t r = visionRange / (int)TERRAIN_TILE_SIZE; + entity_pos_t x_from = from.X / LOS_TILE_SIZE; + entity_pos_t y_from = from.Y / LOS_TILE_SIZE; + entity_pos_t x_to = to.X / LOS_TILE_SIZE; + entity_pos_t y_to = to.Y / LOS_TILE_SIZE; + entity_pos_t r = visionRange / LOS_TILE_SIZE; entity_pos_t r2 = r.Square(); i32 xfloor_from = (x_from - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity(); @@ -2349,9 +2349,9 @@ if (!(i0_to == i0_from && i1_to == i1_from)) { i32 i0clamp_from = std::max(i0_from, 1); - i32 i1clamp_from = std::min(i1_from, m_TerrainVerticesPerSide-2); + i32 i1clamp_from = std::min(i1_from, m_LosVerticesPerSide-2); i32 i0clamp_to = std::max(i0_to, 1); - i32 i1clamp_to = std::min(i1_to, m_TerrainVerticesPerSide-2); + i32 i1clamp_to = std::min(i1_to, m_LosVerticesPerSide-2); // Check whether one strip is negative width, // and we can just add/remove the entire other strip @@ -2455,8 +2455,8 @@ u32 exploredVertices = 0; std::vector::const_iterator playerIt; - for (i32 j = 0; j < m_TerrainVerticesPerSide; j++) - for (i32 i = 0; i < m_TerrainVerticesPerSide; i++) + for (i32 j = 0; j < m_LosVerticesPerSide; j++) + for (i32 i = 0; i < m_LosVerticesPerSide; i++) { if (LosIsOffWorld(i, j)) continue; @@ -2474,6 +2474,3 @@ }; REGISTER_COMPONENT_TYPE(RangeManager) - -#undef LOS_TILES_RATIO -#undef DEBUG_RANGE_MANAGER_BOUNDS Index: source/simulation2/components/CCmpTerrain.cpp =================================================================== --- source/simulation2/components/CCmpTerrain.cpp +++ source/simulation2/components/CCmpTerrain.cpp @@ -139,8 +139,7 @@ { cmpRangeManager->SetBounds(entity_pos_t::Zero(), entity_pos_t::Zero(), entity_pos_t::FromInt(tiles*(int)TERRAIN_TILE_SIZE), - entity_pos_t::FromInt(tiles*(int)TERRAIN_TILE_SIZE), - vertices); + entity_pos_t::FromInt(tiles*(int)TERRAIN_TILE_SIZE)); } if (ReloadWater && CRenderer::IsInitialised()) Index: source/simulation2/components/ICmpRangeManager.h =================================================================== --- source/simulation2/components/ICmpRangeManager.h +++ source/simulation2/components/ICmpRangeManager.h @@ -105,9 +105,8 @@ * Set the bounds of the world. * Entities should not be outside the bounds (else efficiency will suffer). * @param x0,z0,x1,z1 Coordinates of the corners of the world - * @param vertices Number of terrain vertices per side */ - virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, ssize_t vertices) = 0; + virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1) = 0; /** * Execute a passive query. @@ -352,6 +351,10 @@ */ virtual u8 GetUnionPercentMapExplored(const std::vector& players) const = 0; + /** + * @return The number of LOS vertices. + */ + virtual size_t GetVerticesPerSide() const = 0; /** * Perform some internal consistency checks for testing/debugging. Index: source/simulation2/components/tests/test_RangeManager.h =================================================================== --- source/simulation2/components/tests/test_RangeManager.h +++ source/simulation2/components/tests/test_RangeManager.h @@ -141,7 +141,7 @@ // This tests that the incremental computation produces the correct result // in various edge cases - cmp->SetBounds(entity_pos_t::FromInt(0), entity_pos_t::FromInt(0), entity_pos_t::FromInt(512), entity_pos_t::FromInt(512), 512/TERRAIN_TILE_SIZE + 1); + cmp->SetBounds(entity_pos_t::FromInt(0), entity_pos_t::FromInt(0), entity_pos_t::FromInt(512), entity_pos_t::FromInt(512)); cmp->Verify(); { CMessageCreate msg(100); cmp->HandleMessage(msg, false); } cmp->Verify(); Index: source/simulation2/helpers/Los.h =================================================================== --- source/simulation2/helpers/Los.h +++ source/simulation2/helpers/Los.h @@ -22,6 +22,13 @@ // since files must include "Los.h" explicitly, and that's only done in .cpp files. #include "Grid.h" +/** + * Computing LOS data at a very high resolution is not necessary and quite slow. + * This is the size, in meters, of every LOS tile. + * (Note that this also means it is the minimal meaningful resolution of any vision range change). + */ +static constexpr i32 LOS_TILE_SIZE = 4; + enum class LosState : u8 { UNEXPLORED = 0,