Index: source/graphics/CameraController.cpp =================================================================== --- source/graphics/CameraController.cpp +++ source/graphics/CameraController.cpp @@ -249,7 +249,7 @@ CmpPtr cmpPosition(*(g_Game->GetSimulation2()), m_FollowEntity); CmpPtr cmpRangeManager(*(g_Game->GetSimulation2()), SYSTEM_ENTITY); if (cmpPosition && cmpPosition->IsInWorld() && - cmpRangeManager && cmpRangeManager->GetLosVisibility(m_FollowEntity, g_Game->GetViewedPlayerID()) == ICmpRangeManager::VIS_VISIBLE) + cmpRangeManager && cmpRangeManager->GetLosVisibility(m_FollowEntity, g_Game->GetViewedPlayerID()) == LosVisibility::VISIBLE) { // Get the most recent interpolated position float frameOffset = g_Game->GetSimulation2()->GetLastFrameOffset(); Index: source/graphics/tests/test_LOSTexture.h =================================================================== --- source/graphics/tests/test_LOSTexture.h +++ source/graphics/tests/test_LOSTexture.h @@ -41,12 +41,16 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }; - std::vector inputDataVec(inputData, inputData+size*size); + Grid inputDataVec(size, size); - // LOS_MASK should be cmpRanageManager->GetSharedLosMask(1), + for (u8 i = 0; i < size; ++i) + for (u8 j = 0; j < size; ++j) + inputDataVec.set(i, j, inputData[i + j * size]); + + // LosState::MASK should be cmpRanageManager->GetSharedLosMask(1), // but that would mean adding a huge mock component for this and it - // should always be LOS_MASK for player 1 (as the other players are bit-shifted). - ICmpRangeManager::CLosQuerier los(ICmpRangeManager::LOS_MASK, inputDataVec, size); + // should always be LosState::MASK for player 1 (as the other players are bit-shifted). + ICmpRangeManager::CLosQuerier los((u32)LosState::MASK, inputDataVec, size); std::vector losData; size_t pitch; @@ -66,13 +70,12 @@ CLOSTexture tex(sim); const ssize_t size = 257; - std::vector inputDataVec; - inputDataVec.resize(size*size); + Grid inputDataVec(size, size); - // LOS_MASK should be cmpRanageManager->GetSharedLosMask(1), + // LosState::MASK should be cmpRanageManager->GetSharedLosMask(1), // but that would mean adding a huge mock component for this and it - // should always be LOS_MASK for player 1 (as the other players are bit-shifted). - ICmpRangeManager::CLosQuerier los(ICmpRangeManager::LOS_MASK, inputDataVec, size); + // should always be LosState::MASK for player 1 (as the other players are bit-shifted). + ICmpRangeManager::CLosQuerier los((u32)LosState::MASK, inputDataVec, size); size_t reps = 128; double t = timer_Time(); Index: source/gui/ObjectTypes/CMiniMap.cpp =================================================================== --- source/gui/ObjectTypes/CMiniMap.cpp +++ source/gui/ObjectTypes/CMiniMap.cpp @@ -544,8 +544,8 @@ ICmpMinimap* cmpMinimap = static_cast(it->second); if (cmpMinimap->GetRenderData(v.r, v.g, v.b, posX, posZ)) { - ICmpRangeManager::ELosVisibility vis = cmpRangeManager->GetLosVisibility(it->first, g_Game->GetSimulation2()->GetSimContext().GetCurrentDisplayedPlayer()); - if (vis != ICmpRangeManager::VIS_HIDDEN) + LosVisibility vis = cmpRangeManager->GetLosVisibility(it->first, g_Game->GetSimulation2()->GetSimContext().GetCurrentDisplayedPlayer()); + if (vis != LosVisibility::HIDDEN) { v.a = 255; v.x = posX.ToFloat() * sx; Index: source/simulation2/components/CCmpRangeManager.cpp =================================================================== --- source/simulation2/components/CCmpRangeManager.cpp +++ source/simulation2/components/CCmpRangeManager.cpp @@ -46,22 +46,6 @@ #define LOS_TILES_RATIO 8 #define DEBUG_RANGE_MANAGER_BOUNDS 0 -/** - * Representation of a range query. - */ -struct Query -{ - bool enabled; - bool parabolic; - CEntityHandle source; // TODO: this could crash if an entity is destroyed while a Query is still referencing it - entity_pos_t minRange; - entity_pos_t maxRange; - entity_pos_t elevationBonus; - u32 ownersMask; - i32 interface; - std::vector lastMatch; - u8 flagsMask; -}; /** * Convert an owner ID (-1 = unowned, 0 = gaia, 1..30 = players) @@ -81,7 +65,7 @@ static inline u32 CalcPlayerLosMask(player_id_t player) { if (player > 0 && player <= 16) - return ICmpRangeManager::LOS_MASK << (2*(player-1)); + return (u32)LosState::MASK << (2*(player-1)); return 0; } @@ -119,11 +103,11 @@ /** * Computes the 2-bit visibility for one player, given the total 32-bit visibilities */ -static inline u8 GetPlayerVisibility(u32 visibilities, player_id_t player) +static inline LosVisibility GetPlayerVisibility(u32 visibilities, player_id_t player) { if (player > 0 && player <= 16) - return (visibilities >> (2 *(player-1))) & 0x3; - return 0; + return static_cast( (visibilities >> (2 *(player-1))) & 0x3 ); + return LosVisibility::HIDDEN; } /** @@ -152,6 +136,23 @@ return 1 << (player-1); } +/** + * Representation of a range query. + */ +struct Query +{ + bool enabled; + bool parabolic; + CEntityHandle source; // TODO: this could crash if an entity is destroyed while a Query is still referencing it + entity_pos_t minRange; + entity_pos_t maxRange; + entity_pos_t elevationBonus; + u32 ownersMask; + i32 interface; + std::vector lastMatch; + u8 flagsMask; +}; + /** * Checks whether v is in a parabolic range of (0,0,0) * The highest point of the paraboloid is (0,range/2,0) @@ -372,16 +373,18 @@ // LOS state: static const player_id_t MAX_LOS_PLAYER_ID = 16; - std::vector m_LosRevealAll; + using LosTile = std::pair; + + std::array m_LosRevealAll; bool m_LosCircular; i32 m_TerrainVerticesPerSide; // Cache for visibility tracking i32 m_LosTilesPerSide; bool m_GlobalVisibilityUpdate; - std::vector m_GlobalPlayerVisibilityUpdate; - std::vector m_DirtyVisibility; - std::vector > m_LosTiles; + std::array m_GlobalPlayerVisibilityUpdate; + Grid m_DirtyVisibility; + Grid> m_LosTiles; // List of entities that must be updated, regardless of the status of their tile std::vector m_ModifiedEntities; @@ -390,19 +393,19 @@ // of units in a very small area. // (Note we use vertexes, not tiles, to better match the renderer.) // Lazily constructed when it's needed, to save memory in smaller games. - std::vector > m_LosPlayerCounts; + std::array, MAX_LOS_PLAYER_ID> m_LosPlayerCounts; - // 2-bit ELosState per player, starting with player 1 (not 0!) up to player MAX_LOS_PLAYER_ID (inclusive) - std::vector m_LosState; + // 2-bit LosState per player, starting with player 1 (not 0!) up to player MAX_LOS_PLAYER_ID (inclusive) + Grid m_LosState; // Special static visibility data for the "reveal whole map" mode // (TODO: this is usually a waste of memory) - std::vector m_LosStateRevealed; + Grid m_LosStateRevealed; // Shared LOS masks, one per player. - std::vector m_SharedLosMasks; + std::array m_SharedLosMasks; // Shared dirty visibility masks, one per player. - std::vector m_SharedDirtyVisibilityMasks; + std::array m_SharedDirtyVisibilityMasks; // Cache explored vertices per player (not serialized) u32 m_TotalInworldVertices; @@ -431,13 +434,9 @@ // The whole map should be visible to Gaia by default, else e.g. animals // will get confused when trying to run from enemies - m_LosRevealAll.resize(MAX_LOS_PLAYER_ID+2,false); m_LosRevealAll[0] = true; - m_SharedLosMasks.resize(MAX_LOS_PLAYER_ID+2,0); - m_SharedDirtyVisibilityMasks.resize(MAX_LOS_PLAYER_ID + 2, 0); m_GlobalVisibilityUpdate = true; - m_GlobalPlayerVisibilityUpdate.resize(MAX_LOS_PLAYER_ID); m_LosCircular = false; m_TerrainVerticesPerSide = 0; @@ -459,22 +458,22 @@ SerializeMap()(serialize, "queries", m_Queries, GetSimContext()); SerializeEntityMap()(serialize, "entity data", m_EntityData); - SerializeVector()(serialize, "los reveal all", m_LosRevealAll); + SerializeArray()(serialize, "los reveal all", m_LosRevealAll); serialize.Bool("los circular", m_LosCircular); serialize.NumberI32_Unbounded("terrain verts per side", m_TerrainVerticesPerSide); serialize.Bool("global visibility update", m_GlobalVisibilityUpdate); - SerializeVector()(serialize, "global player visibility update", m_GlobalPlayerVisibilityUpdate); - SerializeRepetitiveVector()(serialize, "dirty visibility", m_DirtyVisibility); + 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 // since they can be recomputed from the entity data when deserializing; // m_LosState must be serialized since it depends on the history of exploration - SerializeRepetitiveVector()(serialize, "los state", m_LosState); - SerializeVector()(serialize, "shared los masks", m_SharedLosMasks); - SerializeVector()(serialize, "shared dirty visibility masks", m_SharedDirtyVisibilityMasks); + SerializedGridCompressed()(serialize, "los state", m_LosState); + SerializeArray()(serialize, "shared los masks", m_SharedLosMasks); + SerializeArray()(serialize, "shared dirty visibility masks", m_SharedDirtyVisibilityMasks); } virtual void Serialize(ISerializer& serialize) @@ -563,8 +562,8 @@ SharingLosMove(it->second.visionSharing, it->second.visionRange, from, to); else LosMove(it->second.owner, it->second.visionRange, from, to); - i32 oldLosTile = PosToLosTilesHelper(it->second.x, it->second.z); - i32 newLosTile = PosToLosTilesHelper(msgData.x, msgData.z); + LosTile oldLosTile = PosToLosTilesHelper(it->second.x, it->second.z); + LosTile newLosTile = PosToLosTilesHelper(msgData.x, msgData.z); if (oldLosTile != newLosTile) { RemoveFromTile(oldLosTile, ent); @@ -782,10 +781,10 @@ // Check that calling ResetDerivedData (i.e. recomputing all the state from scratch) // does not affect the incrementally-computed state - std::vector > oldPlayerCounts = m_LosPlayerCounts; - std::vector oldStateRevealed = m_LosStateRevealed; + std::array, MAX_LOS_PLAYER_ID> oldPlayerCounts = m_LosPlayerCounts; + Grid oldStateRevealed = m_LosStateRevealed; FastSpatialSubdivision oldSubdivision = m_Subdivision; - std::vector > oldLosTiles = m_LosTiles; + Grid > oldLosTiles = m_LosTiles; m_Deserializing = true; ResetDerivedData(); @@ -793,19 +792,25 @@ if (oldPlayerCounts != m_LosPlayerCounts) { - for (size_t i = 0; i < oldPlayerCounts.size(); ++i) + for (size_t id = 0; id < m_LosPlayerCounts.size(); ++id) { - debug_printf("%d: ", (int)i); - for (size_t j = 0; j < oldPlayerCounts[i].size(); ++j) - debug_printf("%d ", oldPlayerCounts[i][j]); - debug_printf("\n"); + debug_printf("player %li\n", id); + for (size_t i = 0; i < oldPlayerCounts[id].width(); ++i) + { + for (size_t j = 0; j < oldPlayerCounts[id].height(); ++j) + debug_printf("%i ", oldPlayerCounts[id].get(i,j)); + debug_printf("\n"); + } } - for (size_t i = 0; i < m_LosPlayerCounts.size(); ++i) + for (size_t id = 0; id < m_LosPlayerCounts.size(); ++id) { - debug_printf("%d: ", (int)i); - for (size_t j = 0; j < m_LosPlayerCounts[i].size(); ++j) - debug_printf("%d ", m_LosPlayerCounts[i][j]); - debug_printf("\n"); + debug_printf("player %li\n", id); + for (size_t i = 0; i < m_LosPlayerCounts[id].width(); ++i) + { + for (size_t j = 0; j < m_LosPlayerCounts[id].height(); ++j) + debug_printf("%i ", m_LosPlayerCounts[id].get(i,j)); + debug_printf("\n"); + } } debug_warn(L"inconsistent player counts"); } @@ -830,10 +835,12 @@ m_LosTilesPerSide = (m_TerrainVerticesPerSide - 1)/LOS_TILES_RATIO; - m_LosPlayerCounts.clear(); - m_LosPlayerCounts.resize(MAX_LOS_PLAYER_ID+1); + for (size_t player_id = 0; player_id < m_LosPlayerCounts.size(); ++player_id) + m_LosPlayerCounts[player_id].reset(); + m_ExploredVertices.clear(); m_ExploredVertices.resize(MAX_LOS_PLAYER_ID+1, 0); + if (m_Deserializing) { // recalc current exploration stats. @@ -841,25 +848,19 @@ for (i32 i = 0; i < m_TerrainVerticesPerSide; i++) if (!LosIsOffWorld(i, j)) for (u8 k = 1; k < MAX_LOS_PLAYER_ID+1; ++k) - m_ExploredVertices.at(k) += ((m_LosState[j*m_TerrainVerticesPerSide + i] & (LOS_EXPLORED << (2*(k-1)))) > 0); - } - else - { - m_LosState.clear(); - m_LosState.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); - } - m_LosStateRevealed.clear(); - m_LosStateRevealed.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); + 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_LosStateRevealed.resize(m_TerrainVerticesPerSide, m_TerrainVerticesPerSide); if (!m_Deserializing) { - m_DirtyVisibility.clear(); - m_DirtyVisibility.resize(m_LosTilesPerSide*m_LosTilesPerSide); + m_DirtyVisibility.resize(m_LosTilesPerSide, m_LosTilesPerSide); } - ENSURE(m_DirtyVisibility.size() == (size_t)(m_LosTilesPerSide*m_LosTilesPerSide)); + //ENSURE(m_DirtyVisibility.size() == (size_t)(m_LosTilesPerSide*m_LosTilesPerSide)); - m_LosTiles.clear(); - m_LosTiles.resize(m_LosTilesPerSide*m_LosTilesPerSide); + m_LosTiles.resize(m_LosTilesPerSide, m_LosTilesPerSide); for (EntityMap::const_iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it) if (it->second.HasFlag()) @@ -879,10 +880,10 @@ for (ssize_t i = 0; i < m_TerrainVerticesPerSide; ++i) { if (LosIsOffWorld(i,j)) - m_LosStateRevealed[i + j*m_TerrainVerticesPerSide] = 0; + m_LosStateRevealed.get(i, j) = 0; else { - m_LosStateRevealed[i + j*m_TerrainVerticesPerSide] = 0xFFFFFFFFu; + m_LosStateRevealed.get(i, j) = 0xFFFFFFFFu; m_TotalInworldVertices++; } } @@ -1582,19 +1583,19 @@ it->second.SetFlag(status); } - ELosVisibility ComputeLosVisibility(CEntityHandle ent, player_id_t player) const + LosVisibility ComputeLosVisibility(CEntityHandle ent, player_id_t player) const { // Entities not with positions in the world are never visible if (ent.GetId() == INVALID_ENTITY) - return VIS_HIDDEN; + return LosVisibility::HIDDEN; CmpPtr cmpPosition(ent); if (!cmpPosition || !cmpPosition->IsInWorld()) - return VIS_HIDDEN; + return LosVisibility::HIDDEN; // Mirage entities, whatever the situation, are visible for one specific player CmpPtr cmpMirage(ent); if (cmpMirage && cmpMirage->GetPlayer() != player) - return VIS_HIDDEN; + return LosVisibility::HIDDEN; CFixedVector2D pos = cmpPosition->GetPosition2D(); int i = (pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest(); @@ -1604,9 +1605,9 @@ if (GetLosRevealAll(player)) { if (LosIsOffWorld(i, j) || cmpMirage) - return VIS_HIDDEN; + return LosVisibility::HIDDEN; else - return VIS_VISIBLE; + return LosVisibility::VISIBLE; } // Get visible regions @@ -1632,41 +1633,41 @@ if (los.IsVisible(i, j)) { if (cmpMirage) - return VIS_HIDDEN; + return LosVisibility::HIDDEN; - return VIS_VISIBLE; + return LosVisibility::VISIBLE; } if (!los.IsExplored(i, j)) - return VIS_HIDDEN; + return LosVisibility::HIDDEN; // Invisible if the 'retain in fog' flag is not set, and in a non-visible explored region // Try using the 'retainInFog' flag in m_EntityData to save a script call if (it != m_EntityData.end()) { if (!it->second.HasFlag()) - return VIS_HIDDEN; + return LosVisibility::HIDDEN; } else { if (!(cmpVisibility && cmpVisibility->GetRetainInFog())) - return VIS_HIDDEN; + return LosVisibility::HIDDEN; } if (cmpMirage) - return VIS_FOGGED; + return LosVisibility::FOGGED; CmpPtr cmpOwnership(ent); if (!cmpOwnership) - return VIS_FOGGED; + return LosVisibility::FOGGED; if (cmpOwnership->GetOwner() == player) { CmpPtr cmpFogging(ent); if (!(cmpFogging && cmpFogging->IsMiraged(player))) - return VIS_FOGGED; + return LosVisibility::FOGGED; - return VIS_HIDDEN; + return LosVisibility::HIDDEN; } // Fogged entities are hidden in two cases: @@ -1675,37 +1676,36 @@ CmpPtr cmpFogging(ent); if (cmpFogging && cmpFogging->IsActivated() && (!cmpFogging->WasSeen(player) || cmpFogging->IsMiraged(player))) - return VIS_HIDDEN; + return LosVisibility::HIDDEN; - return VIS_FOGGED; + return LosVisibility::FOGGED; } - ELosVisibility ComputeLosVisibility(entity_id_t ent, player_id_t player) const + LosVisibility ComputeLosVisibility(entity_id_t ent, player_id_t player) const { CEntityHandle handle = GetSimContext().GetComponentManager().LookupEntityHandle(ent); return ComputeLosVisibility(handle, player); } - virtual ELosVisibility GetLosVisibility(CEntityHandle ent, player_id_t player) const + virtual LosVisibility GetLosVisibility(CEntityHandle ent, player_id_t player) const { entity_id_t entId = ent.GetId(); // Entities not with positions in the world are never visible if (entId == INVALID_ENTITY) - return VIS_HIDDEN; + return LosVisibility::HIDDEN; CmpPtr cmpPosition(ent); if (!cmpPosition || !cmpPosition->IsInWorld()) - return VIS_HIDDEN; + return LosVisibility::HIDDEN; // Gaia and observers do not have a visibility cache if (player <= 0) return ComputeLosVisibility(ent, player); CFixedVector2D pos = cmpPosition->GetPosition2D(); - i32 n = PosToLosTilesHelper(pos.X, pos.Y); - if (IsVisibilityDirty(m_DirtyVisibility[n], player)) + if (IsVisibilityDirty(m_DirtyVisibility[PosToLosTilesHelper(pos.X, pos.Y)], player)) return ComputeLosVisibility(ent, player); if (std::find(m_ModifiedEntities.begin(), m_ModifiedEntities.end(), entId) != m_ModifiedEntities.end()) @@ -1715,16 +1715,16 @@ if (it == m_EntityData.end()) return ComputeLosVisibility(ent, player); - return static_cast(GetPlayerVisibility(it->second.visibilities, player)); + return static_cast(GetPlayerVisibility(it->second.visibilities, player)); } - virtual ELosVisibility GetLosVisibility(entity_id_t ent, player_id_t player) const + virtual LosVisibility GetLosVisibility(entity_id_t ent, player_id_t player) const { CEntityHandle handle = GetSimContext().GetComponentManager().LookupEntityHandle(ent); return GetLosVisibility(handle, player); } - virtual ELosVisibility GetLosVisibilityPosition(entity_pos_t x, entity_pos_t z, player_id_t player) const + 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(); @@ -1733,22 +1733,27 @@ if (GetLosRevealAll(player)) { if (LosIsOffWorld(i, j)) - return VIS_HIDDEN; + return LosVisibility::HIDDEN; else - return VIS_VISIBLE; + return LosVisibility::VISIBLE; } // Get visible regions CLosQuerier los(GetSharedLosMask(player), m_LosState, m_TerrainVerticesPerSide); if (los.IsVisible(i,j)) - return VIS_VISIBLE; + return LosVisibility::VISIBLE; if (los.IsExplored(i,j)) - return VIS_FOGGED; - return VIS_HIDDEN; + return LosVisibility::FOGGED; + return LosVisibility::HIDDEN; } - i32 PosToLosTilesHelper(entity_pos_t x, entity_pos_t z) const + LosTile PosToLosTilesHelper(u16 x, u16 z) const + { + return LosTile{ Clamp(x/LOS_TILES_RATIO, 0, m_LosTilesPerSide - 1), Clamp(z/LOS_TILES_RATIO, 0, m_LosTilesPerSide - 1) }; + } + + LosTile PosToLosTilesHelper(entity_pos_t x, entity_pos_t z) const { i32 i = Clamp( (x/(entity_pos_t::FromInt(TERRAIN_TILE_SIZE * LOS_TILES_RATIO))).ToInt_RoundToZero(), @@ -1758,15 +1763,15 @@ (z/(entity_pos_t::FromInt(TERRAIN_TILE_SIZE * LOS_TILES_RATIO))).ToInt_RoundToZero(), 0, m_LosTilesPerSide - 1); - return j*m_LosTilesPerSide + i; + return std::make_pair(i, j); } - void AddToTile(i32 tile, entity_id_t ent) + void AddToTile(LosTile tile, entity_id_t ent) { m_LosTiles[tile].insert(ent); } - void RemoveFromTile(i32 tile, entity_id_t ent) + void RemoveFromTile(LosTile tile, entity_id_t ent) { std::set::const_iterator tileIt = m_LosTiles[tile].find(ent); if (tileIt != m_LosTiles[tile].end()) @@ -1777,15 +1782,17 @@ { PROFILE("UpdateVisibilityData"); - for (i32 n = 0; n < m_LosTilesPerSide * m_LosTilesPerSide; ++n) - { - for (player_id_t player = 1; player < MAX_LOS_PLAYER_ID + 1; ++player) - if (IsVisibilityDirty(m_DirtyVisibility[n], player) || m_GlobalPlayerVisibilityUpdate[player-1] == 1 || m_GlobalVisibilityUpdate) - for (const entity_id_t& ent : m_LosTiles[n]) - UpdateVisibility(ent, player); + for (u16 i = 0; i < m_LosTilesPerSide; ++i) + for (u16 j = 0; j < m_LosTilesPerSide; ++j) + { + LosTile 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]) + UpdateVisibility(ent, player); - m_DirtyVisibility[n] = 0; - } + m_DirtyVisibility[pos] = 0; + } std::fill(m_GlobalPlayerVisibilityUpdate.begin(), m_GlobalPlayerVisibilityUpdate.end(), 0); m_GlobalVisibilityUpdate = false; @@ -1817,15 +1824,15 @@ if (itEnts == m_EntityData.end()) return; - u8 oldVis = GetPlayerVisibility(itEnts->second.visibilities, player); - u8 newVis = ComputeLosVisibility(itEnts->first, player); + LosVisibility oldVis = GetPlayerVisibility(itEnts->second.visibilities, player); + LosVisibility newVis = ComputeLosVisibility(itEnts->first, player); if (oldVis == newVis) return; - itEnts->second.visibilities = (itEnts->second.visibilities & ~(0x3 << 2 * (player - 1))) | (newVis << 2 * (player - 1)); + itEnts->second.visibilities = (itEnts->second.visibilities & ~(0x3 << 2 * (player - 1))) | ((u8)newVis << 2 * (player - 1)); - CMessageVisibilityChanged msg(player, ent, oldVis, newVis); + CMessageVisibilityChanged msg(player, ent, static_cast(oldVis), static_cast(newVis)); GetSimContext().GetComponentManager().PostMessage(ent, msg); } @@ -1909,8 +1916,8 @@ if (LosIsOffWorld(i,j)) continue; u32 &explored = m_ExploredVertices.at(p); - explored += !(m_LosState[i + j*m_TerrainVerticesPerSide] & (LOS_EXPLORED << (2*(p-1)))); - m_LosState[i + j*m_TerrainVerticesPerSide] |= (LOS_EXPLORED << (2*(p-1))); + explored += !(m_LosState.get(i, j) & ((u32)LosState::EXPLORED << (2*(p-1)))); + m_LosState.get(i, j) |= ((u32)LosState::EXPLORED << (2*(p-1))); } SeeExploredEntities(p); @@ -1951,11 +1958,11 @@ if (LosIsOffWorld(ti, tj)) continue; - u32& losState = m_LosState[ti + tj * m_TerrainVerticesPerSide]; - if (!(losState & (LOS_EXPLORED << (2*(p-1))))) + u32& losState = m_LosState.get(ti, tj); + if (!(losState & ((u32)LosState::EXPLORED << (2*(p-1))))) { ++explored; - losState |= (LOS_EXPLORED << (2*(p-1))); + losState |= ((u32)LosState::EXPLORED << (2*(p-1))); } } } @@ -2018,9 +2025,8 @@ const Grid& shoreGrid = cmpPathfinder->ComputeShoreGrid(true); ENSURE(shoreGrid.m_W == m_TerrainVerticesPerSide-1 && shoreGrid.m_H == m_TerrainVerticesPerSide-1); - std::vector& counts = m_LosPlayerCounts.at(p); - ENSURE(!counts.empty()); - u16* countsData = &counts[0]; + Grid& counts = m_LosPlayerCounts.at(p); + ENSURE(!counts.blank()); for (u16 j = 0; j < shoreGrid.m_H; ++j) for (u16 i = 0; i < shoreGrid.m_W; ++i) @@ -2031,9 +2037,9 @@ // Maybe we could be more clever and don't add dummy strips of one tile if (enable) - LosAddStripHelper(p, i, i, j, countsData); + LosAddStripHelper(p, i, i, j, counts); else - LosRemoveStripHelper(p, i, i, j, countsData); + LosRemoveStripHelper(p, i, i, j, counts); } } @@ -2069,56 +2075,50 @@ /** * Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive). */ - inline void LosAddStripHelper(u8 owner, i32 i0, i32 i1, i32 j, u16* counts) + inline void LosAddStripHelper(u8 owner, i32 i0, i32 i1, i32 j, Grid& counts) { if (i1 < i0) return; - i32 idx0 = j*m_TerrainVerticesPerSide + i0; - i32 idx1 = j*m_TerrainVerticesPerSide + i1; u32 &explored = m_ExploredVertices.at(owner); - for (i32 idx = idx0; idx <= idx1; ++idx) + for (i32 i = i0; i <= i1; ++i) { // Increasing from zero to non-zero - move from unexplored/explored to visible+explored - if (counts[idx] == 0) + if (counts.get(i, j) == 0) { - i32 i = i0 + idx - idx0; if (!LosIsOffWorld(i, j)) { - explored += !(m_LosState[idx] & (LOS_EXPLORED << (2*(owner-1)))); - m_LosState[idx] |= ((LOS_VISIBLE | LOS_EXPLORED) << (2*(owner-1))); + explored += !(m_LosState.get(i, j) & ((u32)LosState::EXPLORED << (2*(owner-1)))); + m_LosState.get(i, j) |= (((int)LosState::VISIBLE | (u32)LosState::EXPLORED) << (2*(owner-1))); } MarkVisibilityDirtyAroundTile(owner, i, j); } - ASSERT(counts[idx] < 65535); - counts[idx] = (u16)(counts[idx] + 1); // ignore overflow; the player should never have 64K units + ENSURE(counts.get(i, j) < std::numeric_limits::max()); + counts.get(i, j) = (u16)(counts.get(i, j) + 1); // ignore overflow; the player should never have 64K units } } /** * Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive). */ - inline void LosRemoveStripHelper(u8 owner, i32 i0, i32 i1, i32 j, u16* counts) + inline void LosRemoveStripHelper(u8 owner, i32 i0, i32 i1, i32 j, Grid& counts) { if (i1 < i0) return; - i32 idx0 = j*m_TerrainVerticesPerSide + i0; - i32 idx1 = j*m_TerrainVerticesPerSide + i1; - for (i32 idx = idx0; idx <= idx1; ++idx) + for (i32 i = i0; i <= i1; ++i) { - ASSERT(counts[idx] > 0); - counts[idx] = (u16)(counts[idx] - 1); + ASSERT(counts.get(i, j) > 0); + counts.get(i, j) = (u16)(counts.get(i, j) - 1); // Decreasing from non-zero to zero - move from visible+explored to explored - if (counts[idx] == 0) + if (counts.get(i, j) == 0) { // (If LosIsOffWorld then this is a no-op, so don't bother doing the check) - m_LosState[idx] &= ~(LOS_VISIBLE << (2*(owner-1))); + m_LosState.get(i, j) &= ~((int)LosState::VISIBLE << (2*(owner-1))); - i32 i = i0 + idx - idx0; MarkVisibilityDirtyAroundTile(owner, i, j); } } @@ -2132,10 +2132,10 @@ // Mark the LoS tiles around the updated vertex // 1: left-up, 2: right-up, 3: left-down, 4: right-down - int n1 = ((j-1)/LOS_TILES_RATIO)*m_LosTilesPerSide + (i-1)/LOS_TILES_RATIO; - int n2 = ((j-1)/LOS_TILES_RATIO)*m_LosTilesPerSide + i/LOS_TILES_RATIO; - int n3 = (j/LOS_TILES_RATIO)*m_LosTilesPerSide + (i-1)/LOS_TILES_RATIO; - int n4 = (j/LOS_TILES_RATIO)*m_LosTilesPerSide + i/LOS_TILES_RATIO; + LosTile n1 = PosToLosTilesHelper(i-1, j-1); + LosTile n2 = PosToLosTilesHelper(i-1, j); + LosTile n3 = PosToLosTilesHelper(i, j-1); + LosTile n4 = PosToLosTilesHelper(i, j); u16 sharedDirtyVisibilityMask = m_SharedDirtyVisibilityMasks[owner]; @@ -2162,13 +2162,11 @@ PROFILE("LosUpdateHelper"); - std::vector& counts = m_LosPlayerCounts.at(owner); + Grid& counts = m_LosPlayerCounts.at(owner); // Lazy initialisation of counts: - if (counts.empty()) - counts.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); - - u16* countsData = &counts[0]; + if (counts.blank()) + counts.resize(m_TerrainVerticesPerSide, m_TerrainVerticesPerSide); // Compute the circular region as a series of strips. // Rather than quantise pos to vertexes, we do more precise sub-tile computations @@ -2233,9 +2231,9 @@ i32 i0clamp = std::max(i0, 1); i32 i1clamp = std::min(i1, m_TerrainVerticesPerSide-2); if (adding) - LosAddStripHelper(owner, i0clamp, i1clamp, j, countsData); + LosAddStripHelper(owner, i0clamp, i1clamp, j, counts); else - LosRemoveStripHelper(owner, i0clamp, i1clamp, j, countsData); + LosRemoveStripHelper(owner, i0clamp, i1clamp, j, counts); } } @@ -2251,13 +2249,11 @@ PROFILE("LosUpdateHelperIncremental"); - std::vector& counts = m_LosPlayerCounts.at(owner); + Grid& counts = m_LosPlayerCounts.at(owner); // Lazy initialisation of counts: - if (counts.empty()) - counts.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); - - u16* countsData = &counts[0]; + if (counts.blank()) + counts.resize(m_TerrainVerticesPerSide, m_TerrainVerticesPerSide); // See comments in LosUpdateHelper. // This does exactly the same, except computing the strips for @@ -2343,11 +2339,11 @@ // and we can just add/remove the entire other strip if (i1clamp_from < i0clamp_from) { - LosAddStripHelper(owner, i0clamp_to, i1clamp_to, j, countsData); + LosAddStripHelper(owner, i0clamp_to, i1clamp_to, j, counts); } else if (i1clamp_to < i0clamp_to) { - LosRemoveStripHelper(owner, i0clamp_from, i1clamp_from, j, countsData); + LosRemoveStripHelper(owner, i0clamp_from, i1clamp_from, j, counts); } else { @@ -2360,10 +2356,10 @@ // movement speeds), the region between them will be both added and removed, // so we have to do the add first to avoid overflowing to -1 and triggering // assertion failures.) - LosAddStripHelper(owner, i0clamp_to, i0clamp_from-1, j, countsData); - LosAddStripHelper(owner, i1clamp_from+1, i1clamp_to, j, countsData); - LosRemoveStripHelper(owner, i0clamp_from, i0clamp_to-1, j, countsData); - LosRemoveStripHelper(owner, i1clamp_to+1, i1clamp_from, j, countsData); + LosAddStripHelper(owner, i0clamp_to, i0clamp_from-1, j, counts); + LosAddStripHelper(owner, i1clamp_from+1, i1clamp_to, j, counts); + LosRemoveStripHelper(owner, i0clamp_from, i0clamp_to-1, j, counts); + LosRemoveStripHelper(owner, i1clamp_to+1, i1clamp_from, j, counts); } } } @@ -2448,7 +2444,7 @@ continue; for (playerIt = players.begin(); playerIt != players.end(); ++playerIt) - if (m_LosState[j*m_TerrainVerticesPerSide + i] & (LOS_EXPLORED << (2*((*playerIt)-1)))) + if (m_LosState.get(i, j) & ((u32)LosState::EXPLORED << (2*((*playerIt)-1)))) { exploredVertices += 1; break; Index: source/simulation2/components/CCmpSoundManager.cpp =================================================================== --- source/simulation2/components/CCmpSoundManager.cpp +++ source/simulation2/components/CCmpSoundManager.cpp @@ -68,7 +68,7 @@ int currentPlayer = GetSimContext().GetCurrentDisplayedPlayer(); CmpPtr cmpRangeManager(GetSystemEntity()); - if (!cmpRangeManager || (cmpRangeManager->GetLosVisibility(source, currentPlayer) != ICmpRangeManager::VIS_VISIBLE)) + if (!cmpRangeManager || (cmpRangeManager->GetLosVisibility(source, currentPlayer) != LosVisibility::VISIBLE)) return; CmpPtr cmpPosition(GetSimContext(), source); Index: source/simulation2/components/CCmpUnitRenderer.cpp =================================================================== --- source/simulation2/components/CCmpUnitRenderer.cpp +++ source/simulation2/components/CCmpUnitRenderer.cpp @@ -88,7 +88,7 @@ /** * Cached LOS visibility status. */ - ICmpRangeManager::ELosVisibility visibility; + LosVisibility visibility; bool visibilityDirty; /** @@ -404,7 +404,7 @@ if (unit.visibilityDirty) UpdateVisibility(unit); - if (unit.visibility == ICmpRangeManager::VIS_HIDDEN) + if (unit.visibility == LosVisibility::HIDDEN) continue; if (!g_AtlasGameLoop->running && !g_RenderingOptions.GetRenderActors() && (unit.flags & ACTOR_ONLY)) @@ -451,7 +451,7 @@ // (regardless of whether the LOS system thinks it's visible) CmpPtr cmpVisibility(unit.entity); if (cmpVisibility && cmpVisibility->GetAlwaysVisible()) - unit.visibility = ICmpRangeManager::VIS_VISIBLE; + unit.visibility = LosVisibility::VISIBLE; else { CmpPtr cmpRangeManager(GetSystemEntity()); @@ -460,12 +460,12 @@ } } else - unit.visibility = ICmpRangeManager::VIS_HIDDEN; + unit.visibility = LosVisibility::HIDDEN; // Change the visibility of the visual actor's selectable if it has one. CmpPtr cmpSelectable(unit.entity); if (cmpSelectable) - cmpSelectable->SetVisibility(unit.visibility != ICmpRangeManager::VIS_HIDDEN); + cmpSelectable->SetVisibility(unit.visibility != LosVisibility::HIDDEN); unit.visibilityDirty = false; } Index: source/simulation2/components/ICmpRangeManager.h =================================================================== --- source/simulation2/components/ICmpRangeManager.h +++ source/simulation2/components/ICmpRangeManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ #include "maths/FixedVector2D.h" #include "simulation2/system/Interface.h" +#include "simulation2/helpers/Grid.h" #include "simulation2/helpers/Position.h" #include "simulation2/helpers/Player.h" @@ -29,6 +30,27 @@ class FastSpatialSubdivision; +/** + * Since GetVisibility queries are run by the range manager + * other code using these must include ICmpRangeManager.h anyways, + * so define this enum here (Ideally, it'd be in its own header file, + * but adding header file does incur its own compilation time increase). + */ +enum class LosVisibility : u8 +{ + HIDDEN = 0, + FOGGED = 1, + VISIBLE = 2 +}; + +enum class LosState : u8 +{ + UNEXPLORED = 0, + EXPLORED = 1, + VISIBLE = 2, + MASK = 3 +}; + /** * Provides efficient range-based queries of the game world, * and also LOS-based effects (fog of war). @@ -221,22 +243,6 @@ */ virtual void SetEntityFlag(entity_id_t ent, const std::string& identifier, bool value) = 0; - // LOS interface: - - enum ELosState - { - LOS_UNEXPLORED = 0, - LOS_EXPLORED = 1, - LOS_VISIBLE = 2, - LOS_MASK = 3 - }; - - enum ELosVisibility - { - VIS_HIDDEN = 0, - VIS_FOGGED = 1, - VIS_VISIBLE = 2 - }; /** * Object providing efficient abstracted access to the LOS state. @@ -250,8 +256,8 @@ friend class CCmpRangeManager; friend class TestLOSTexture; - CLosQuerier(u32 playerMask, const std::vector& data, ssize_t verticesPerSide) : - m_Data(&data[0]), m_PlayerMask(playerMask), m_VerticesPerSide(verticesPerSide) + CLosQuerier(u32 playerMask, const Grid& data, ssize_t verticesPerSide) : + m_Data(data), m_PlayerMask(playerMask), m_VerticesPerSide(verticesPerSide) { } @@ -267,7 +273,7 @@ return false; // Check high bit of each bit-pair - if ((m_Data[j*m_VerticesPerSide + i] & m_PlayerMask) & 0xAAAAAAAAu) + if ((m_Data.get(i, j) & m_PlayerMask) & 0xAAAAAAAAu) return true; else return false; @@ -282,7 +288,7 @@ return false; // Check low bit of each bit-pair - if ((m_Data[j*m_VerticesPerSide + i] & m_PlayerMask) & 0x55555555u) + if ((m_Data.get(i, j) & m_PlayerMask) & 0x55555555u) return true; else return false; @@ -298,7 +304,7 @@ ENSURE(i >= 0 && j >= 0 && i < m_VerticesPerSide && j < m_VerticesPerSide); #endif // Check high bit of each bit-pair - if ((m_Data[j*m_VerticesPerSide + i] & m_PlayerMask) & 0xAAAAAAAAu) + if ((m_Data.get(i, j) & m_PlayerMask) & 0xAAAAAAAAu) return true; else return false; @@ -314,7 +320,7 @@ ENSURE(i >= 0 && j >= 0 && i < m_VerticesPerSide && j < m_VerticesPerSide); #endif // Check low bit of each bit-pair - if ((m_Data[j*m_VerticesPerSide + i] & m_PlayerMask) & 0x55555555u) + if ((m_Data.get(i, j) & m_PlayerMask) & 0x55555555u) return true; else return false; @@ -322,9 +328,12 @@ private: u32 m_PlayerMask; - const u32* m_Data; + const Grid& m_Data; ssize_t m_VerticesPerSide; }; + ////////////////////////////////////////////////////////////////// + //// LOS interface below this line //// + ////////////////////////////////////////////////////////////////// /** * Returns a CLosQuerier for checking whether vertex positions are visible to the given player @@ -339,17 +348,17 @@ /** * Returns the visibility status of the given entity, with respect to the given player. - * Returns VIS_HIDDEN if the entity doesn't exist or is not in the world. + * Returns LosVisibility::HIDDEN if the entity doesn't exist or is not in the world. * This respects the GetLosRevealAll flag. */ - virtual ELosVisibility GetLosVisibility(CEntityHandle ent, player_id_t player) const = 0; - virtual ELosVisibility GetLosVisibility(entity_id_t ent, player_id_t player) const = 0; + virtual LosVisibility GetLosVisibility(CEntityHandle ent, player_id_t player) const = 0; + virtual LosVisibility GetLosVisibility(entity_id_t ent, player_id_t player) const = 0; /** * Returns the visibility status of the given position, with respect to the given player. * This respects the GetLosRevealAll flag. */ - virtual ELosVisibility GetLosVisibilityPosition(entity_pos_t x, entity_pos_t z, player_id_t player) const = 0; + virtual LosVisibility GetLosVisibilityPosition(entity_pos_t x, entity_pos_t z, player_id_t player) const = 0; /** * Request the update of the visibility cache of ent at next turn. Index: source/simulation2/components/ICmpRangeManager.cpp =================================================================== --- source/simulation2/components/ICmpRangeManager.cpp +++ source/simulation2/components/ICmpRangeManager.cpp @@ -22,13 +22,13 @@ #include "simulation2/system/InterfaceScripted.h" namespace { - std::string VisibilityToString(ICmpRangeManager::ELosVisibility visibility) + std::string VisibilityToString(LosVisibility visibility) { switch (visibility) { - case ICmpRangeManager::VIS_HIDDEN: return "hidden"; - case ICmpRangeManager::VIS_FOGGED: return "fogged"; - case ICmpRangeManager::VIS_VISIBLE: return "visible"; + case LosVisibility::HIDDEN: return "hidden"; + case LosVisibility::FOGGED: return "fogged"; + case LosVisibility::VISIBLE: return "visible"; default: return "error"; // should never happen } } Index: source/simulation2/components/ICmpVisibility.h =================================================================== --- source/simulation2/components/ICmpVisibility.h +++ source/simulation2/components/ICmpVisibility.h @@ -42,7 +42,7 @@ */ virtual bool IsActivated() = 0; - virtual ICmpRangeManager::ELosVisibility GetVisibility(player_id_t player, bool isVisible, bool isExplored) = 0; + virtual LosVisibility GetVisibility(player_id_t player, bool isVisible, bool isExplored) = 0; virtual bool GetRetainInFog() = 0; Index: source/simulation2/components/ICmpVisibility.cpp =================================================================== --- source/simulation2/components/ICmpVisibility.cpp +++ source/simulation2/components/ICmpVisibility.cpp @@ -35,21 +35,21 @@ return m_Script.Call("IsActivated"); } - virtual ICmpRangeManager::ELosVisibility GetVisibility(player_id_t player, bool isVisible, bool isExplored) + virtual LosVisibility GetVisibility(player_id_t player, bool isVisible, bool isExplored) { int visibility = m_Script.Call("GetVisibility", player, isVisible, isExplored); switch (visibility) { - case ICmpRangeManager::VIS_HIDDEN: - return ICmpRangeManager::VIS_HIDDEN; - case ICmpRangeManager::VIS_FOGGED: - return ICmpRangeManager::VIS_FOGGED; - case ICmpRangeManager::VIS_VISIBLE: - return ICmpRangeManager::VIS_VISIBLE; + case static_cast(LosVisibility::HIDDEN): + return LosVisibility::HIDDEN; + case static_cast(LosVisibility::FOGGED): + return LosVisibility::FOGGED; + case static_cast(LosVisibility::VISIBLE): + return LosVisibility::VISIBLE; default: LOGERROR("Received the invalid visibility value %d from the Visibility scripted component!", visibility); - return ICmpRangeManager::VIS_HIDDEN; + return LosVisibility::HIDDEN; } } Index: source/simulation2/helpers/Grid.h =================================================================== --- source/simulation2/helpers/Grid.h +++ source/simulation2/helpers/Grid.h @@ -20,12 +20,19 @@ #include +#include "simulation2/serialization/IDeserializer.h" +#include "simulation2/serialization/ISerializer.h" + + #ifdef NDEBUG #define GRID_BOUNDS_DEBUG 0 #else #define GRID_BOUNDS_DEBUG 1 #endif +template +struct SerializedGridCompressed; + /** * Basic 2D array, intended for storing tile data, plus support for lazy updates * by ICmpObstructionManager. @@ -34,32 +41,53 @@ template class Grid { + friend struct SerializedGridCompressed; +protected: + // Tag-dispatching internal utilities for convenience. + struct default_type{}; + struct is_pod { operator default_type() { return default_type{}; }}; + struct is_container { operator default_type() { return default_type{}; }}; + + // helper to detect value_type + template struct has_value_type : std::false_type { }; + template struct has_value_type (), 0)> : std::true_type { }; + + template using if_ = typename std::conditional::type; + + template + using dispatch = if_< std::is_pod, is_pod, + if_, is_container, + default_type>>; + public: - Grid() : m_W(0), m_H(0), m_Data(NULL), m_DirtyID(0) + Grid() : m_W(0), m_H(0), m_Data(NULL) { } - Grid(u16 w, u16 h) : m_W(w), m_H(h), m_Data(NULL), m_DirtyID(0) + Grid(u16 w, u16 h) : m_W(w), m_H(h), m_Data(NULL) { - if (m_W || m_H) - m_Data = new T[m_W * m_H]; - reset(); + resize(w, h); } - Grid(const Grid& g) : m_W(0), m_H(0), m_Data(NULL), m_DirtyID(0) + Grid(const Grid& g) : m_W(0), m_H(0), m_Data(NULL) { *this = g; } + using value_type = T; +public: + + void copy_data(T* o, default_type) { std::copy(o, o + m_H*m_W, &m_Data[0]); } + void copy_data(T* o, is_pod) { memcpy(m_Data, o, m_W*m_H*sizeof(T)); } + Grid& operator=(const Grid& g) { if (this == &g) return *this; - m_DirtyID = g.m_DirtyID; if (m_W == g.m_W && m_H == g.m_H) { - memcpy(m_Data, g.m_Data, m_W*m_H*sizeof(T)); + copy_data(g.m_Data, dispatch{}); return *this; } @@ -69,7 +97,7 @@ if (g.m_Data) { m_Data = new T[m_W * m_H]; - memcpy(m_Data, g.m_Data, m_W*m_H*sizeof(T)); + copy_data(g.m_Data, dispatch{}); } else m_Data = NULL; @@ -78,7 +106,6 @@ void swap(Grid& g) { - std::swap(m_DirtyID, g.m_DirtyID); std::swap(m_Data, g.m_Data); std::swap(m_H, g.m_H); std::swap(m_W, g.m_W); @@ -89,24 +116,42 @@ delete[] m_Data; } + bool compare_data(T* o, default_type) const { + for (int i = 0; i < m_W*m_H; ++i) + if (m_Data[i] != o[i]) + return false; + return true; + } + bool compare_data(T* o, is_pod) const { return memcmp(m_Data, o, m_W*m_H*sizeof(T)) == 0; } + bool operator==(const Grid& g) const { - if (!compare_sizes(&g) || m_DirtyID != g.m_DirtyID) + if (!compare_sizes(&g)) return false; - return memcmp(m_Data, g.m_Data, m_W*m_H*sizeof(T)) == 0; + return compare_data(g.m_Data, dispatch{}); } + bool operator!=(const Grid& g) const { return !(*this==g); } bool blank() const { return m_W == 0 && m_H == 0; } - bool any_set_in_square(int i0, int j0, int i1, int j1) const + u16 width() const { return m_W; }; + u16 height() const { return m_H; }; + + + bool _any_set_in_square(int, int, int, int, default_type) const { - #if GRID_BOUNDS_DEBUG + static_assert(!std::is_same::value, "Not implemented."); + return false; // Fix warnings. + } + bool _any_set_in_square(int i0, int j0, int i1, int j1, is_pod) const + { +#if GRID_BOUNDS_DEBUG ENSURE(i0 >= 0 && j0 >= 0 && i1 <= m_W && j1 <= m_H); - #endif +#endif for (int j = j0; j < j1; ++j) { int sum = 0; @@ -118,10 +163,33 @@ return false; } + bool any_set_in_square(int i0, int j0, int i1, int j1) const + { + return _any_set_in_square(i0, j0, i1, j1, dispatch{}); + } + + template + typename std::enable_if::value, ret>::type any_set_in_square(int i0, int j0, int i1, int j1) const; + + void reset_data(default_type) { std::fill(&m_Data[0], &m_Data[m_H*m_W], T{}); } + void reset_data(is_pod) { memset(m_Data, 0, m_W*m_H*sizeof(T)); } + void reset() { if (m_Data) - memset(m_Data, 0, m_W*m_H*sizeof(T)); + reset_data(dispatch{}); + } + + void resize(u16 w, u16 h) + { + if (m_Data) + delete[] m_Data; + m_W = w; + m_H = h; + if (m_W || m_H) + m_Data = new T[m_W * m_H]; + ENSURE(m_Data); + reset(); } // Add two grids of the same size @@ -154,6 +222,20 @@ m_Data[j*m_W + i] = value; } + T& operator[](std::pair coords) { return get(coords.first, coords.second); } + T& get(std::pair coords) { return get(coords.first, coords.second); } + + T& operator[](std::pair coords) const { return get(coords.first, coords.second); } + T& get(std::pair coords) const { return get(coords.first, coords.second); } + + T& get(int i, int j) + { +#if GRID_BOUNDS_DEBUG + ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H); +#endif + return m_Data[j*m_W + i]; + } + T& get(int i, int j) const { #if GRID_BOUNDS_DEBUG @@ -170,10 +252,60 @@ u16 m_W, m_H; T* m_Data; +}; + - size_t m_DirtyID; // if this is < the id maintained by ICmpObstructionManager then it needs to be updated +template +struct SerializedGridCompressed +{ + template + void operator()(ISerializer& serialize, const char* name, Grid& value) + { + size_t len = value.m_W + value.m_H * value.m_W; + serialize.NumberU16_Unbounded("width", value.m_W); + serialize.NumberU16_Unbounded("height", value.m_H); + if (len == 0) + return; + u32 count = 1; + T prevVal = value.m_Data[0]; + for (size_t i = 1; i < len; ++i) + { + if (prevVal == value.m_Data[i]) + { + count++; + continue; + } + serialize.NumberU32_Unbounded("#", count); + ELEM()(serialize, name, prevVal); + count = 1; + prevVal = value.m_Data[i]; + } + serialize.NumberU32_Unbounded("#", count); + ELEM()(serialize, name, prevVal); + } + + template + void operator()(IDeserializer& deserialize, const char* name, Grid& value) + { + u16 w, h; + deserialize.NumberU16_Unbounded("width", w); + deserialize.NumberU16_Unbounded("height", h); + u32 len = w + h * w; + value.resize(w, h); + for (size_t i = 0; i < len;) + { + u32 count; + deserialize.NumberU32_Unbounded("#", count); + T el; + ELEM()(deserialize, name, el); + std::fill(&value.m_Data[i], &value.m_Data[i+count], el); + i += count; +// value.insert(value.end(), count, el); + } + } }; + /** * Similar to Grid, except optimised for sparse usage (the grid is subdivided into * buckets whose contents are only initialised on demand, to save on memset cost). Index: source/simulation2/helpers/Selection.cpp =================================================================== --- source/simulation2/helpers/Selection.cpp +++ source/simulation2/helpers/Selection.cpp @@ -79,7 +79,7 @@ continue; // Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld) - if (cmpRangeManager->GetLosVisibility(handle, player) == ICmpRangeManager::VIS_HIDDEN) + if (cmpRangeManager->GetLosVisibility(handle, player) == LosVisibility::HIDDEN) continue; return handle.GetId(); @@ -126,7 +126,7 @@ static bool CheckEntityVisibleAndInRect(CEntityHandle handle, CmpPtr cmpRangeManager, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, player_id_t player, bool allowEditorSelectables) { // Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld) - if (cmpRangeManager->GetLosVisibility(handle, player) == ICmpRangeManager::VIS_HIDDEN) + if (cmpRangeManager->GetLosVisibility(handle, player) == LosVisibility::HIDDEN) return false; return CheckEntityInRect(handle, camera, sx0, sy0, sx1, sy1, allowEditorSelectables); @@ -224,7 +224,7 @@ // Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld) // In this case, the checking is done to avoid selecting garrisoned units - if (cmpRangeManager->GetLosVisibility(handle, owner) == ICmpRangeManager::VIS_HIDDEN) + if (cmpRangeManager->GetLosVisibility(handle, owner) == LosVisibility::HIDDEN) continue; // Ignore entities not owned by 'owner' Index: source/simulation2/serialization/SerializeTemplates.h =================================================================== --- source/simulation2/serialization/SerializeTemplates.h +++ source/simulation2/serialization/SerializeTemplates.h @@ -30,6 +30,24 @@ #include #include +template +struct SerializeArray +{ + template + void operator()(ISerializer& serialize, const char* name, std::array& value) + { + for (size_t i = 0; i < N; ++i) + ELEM()(serialize, name, value[i]); + } + + template + void operator()(IDeserializer& deserialize, const char* name, std::array& value) + { + for (size_t i = 0; i < N; ++i) + ELEM()(deserialize, name, value[i]); + } +}; + template struct SerializeVector { @@ -58,53 +76,6 @@ } }; -template -struct SerializeRepetitiveVector -{ - template - void operator()(ISerializer& serialize, const char* name, std::vector& value) - { - size_t len = value.size(); - serialize.NumberU32_Unbounded("length", (u32)len); - if (len == 0) - return; - u32 count = 1; - T prevVal = value[0]; - for (size_t i = 1; i < len; ++i) - { - if (prevVal == value[i]) - { - count++; - continue; - } - serialize.NumberU32_Unbounded("#", count); - ELEM()(serialize, name, prevVal); - count = 1; - prevVal = value[i]; - } - serialize.NumberU32_Unbounded("#", count); - ELEM()(serialize, name, prevVal); - } - - template - void operator()(IDeserializer& deserialize, const char* name, std::vector& value) - { - value.clear(); - u32 len; - deserialize.NumberU32_Unbounded("length", len); - value.reserve(len); // TODO: watch out for out-of-memory - for (size_t i = 0; i < len;) - { - u32 count; - deserialize.NumberU32_Unbounded("#", count); - T el; - ELEM()(deserialize, name, el); - i += count; - value.insert(value.end(), count, el); - } - } -}; - template struct SerializeSet {