diff --git a/OpenRA.Game/Traits/World/Shroud.cs b/OpenRA.Game/Traits/World/Shroud.cs index 5898b26e23..efc1bd498b 100644 --- a/OpenRA.Game/Traits/World/Shroud.cs +++ b/OpenRA.Game/Traits/World/Shroud.cs @@ -24,6 +24,8 @@ namespace OpenRA.Traits { [Sync] public bool Disabled; + public event Action> CellsChanged; + readonly Actor self; readonly Map map; @@ -31,23 +33,6 @@ namespace OpenRA.Traits readonly CellLayer generatedShroudCount; readonly CellLayer explored; - public event Action CellEntryChanged - { - add - { - visibleCount.CellEntryChanged += value; - generatedShroudCount.CellEntryChanged += value; - explored.CellEntryChanged += value; - } - - remove - { - visibleCount.CellEntryChanged -= value; - generatedShroudCount.CellEntryChanged -= value; - explored.CellEntryChanged -= value; - } - } - readonly Lazy fogVisibilities; // Cache of visibility that was added, so no matter what crazy trait code does, it @@ -88,8 +73,11 @@ namespace OpenRA.Traits slowVisibleTest = IsVisible; } - void Invalidate() + void Invalidate(IEnumerable changed) { + if (CellsChanged != null) + CellsChanged(changed); + var oldHash = Hash; Hash = Sync.HashPlayer(self.Owner) + self.World.WorldTick * 3; @@ -148,7 +136,7 @@ namespace OpenRA.Traits throw new InvalidOperationException("Attempting to add duplicate actor visibility"); visibility[a] = visible; - Invalidate(); + Invalidate(visible); } void RemoveVisibility(Actor a) @@ -161,7 +149,7 @@ namespace OpenRA.Traits visibleCount[c]--; visibility.Remove(a); - Invalidate(); + Invalidate(visible); } void UpdateVisibility(Actor a, ref CPos[] visible) @@ -190,7 +178,7 @@ namespace OpenRA.Traits throw new InvalidOperationException("Attempting to add duplicate shroud generation"); generation[a] = shrouded; - Invalidate(); + Invalidate(shrouded); } void RemoveShroudGeneration(Actor a) @@ -203,7 +191,7 @@ namespace OpenRA.Traits generatedShroudCount[c]--; generation.Remove(a); - Invalidate(); + Invalidate(shrouded); } void UpdateShroudGeneration(Actor a, ref CPos[] shrouded) @@ -241,10 +229,17 @@ namespace OpenRA.Traits public void Explore(World world, CPos center, WRange range) { + var changed = new List(); foreach (var c in FindVisibleTiles(world, center, range)) - explored[c] = true; + { + if (!explored[c]) + { + explored[c] = true; + changed.Add(c); + } + } - Invalidate(); + Invalidate(changed); } public void Explore(Shroud s) @@ -252,27 +247,48 @@ namespace OpenRA.Traits if (map.Bounds != s.map.Bounds) throw new ArgumentException("The map bounds of these shrouds do not match.", "s"); + var changed = new List(); foreach (var uv in map.Cells.MapCoords) - if (s.explored[uv]) + { + if (!explored[uv] && s.explored[uv]) + { explored[uv] = true; + changed.Add(uv.ToCPos(map)); + } + } - Invalidate(); + Invalidate(changed); } public void ExploreAll(World world) { + var changed = new List(); foreach (var uv in map.Cells.MapCoords) - explored[uv] = true; + { + if (!explored[uv]) + { + explored[uv] = true; + changed.Add(uv.ToCPos(map)); + } + } - Invalidate(); + Invalidate(changed); } public void ResetExploration() { + var changed = new List(); foreach (var uv in map.Cells.MapCoords) - explored[uv] = visibleCount[uv] > 0; + { + var visible = visibleCount[uv] > 0; + if (explored[uv] != visible) + { + explored[uv] = visible; + changed.Add(uv.ToCPos(map)); + } + } - Invalidate(); + Invalidate(changed); } public bool IsExplored(CPos cell) diff --git a/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs b/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs index 7f31a923b8..b4cd58db1c 100644 --- a/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs +++ b/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs @@ -9,6 +9,7 @@ #endregion using System; +using System.Collections.Generic; using OpenRA.Graphics; using OpenRA.Traits; @@ -85,6 +86,8 @@ namespace OpenRA.Mods.Common.Traits readonly CellLayer tileInfos; readonly CellLayer shroudDirty; + readonly HashSet cellsDirty; + readonly HashSet cellsAndNeighborsDirty; readonly Vertex[] fogVertices, shroudVertices; readonly Sprite[] fogSprites, shroudSprites; @@ -113,6 +116,8 @@ namespace OpenRA.Mods.Common.Traits tileInfos = new CellLayer(map); shroudDirty = new CellLayer(map); + cellsDirty = new HashSet(); + cellsAndNeighborsDirty = new HashSet(); var verticesLength = map.MapSize.X * map.MapSize.Y * 4; fogVertices = new Vertex[verticesLength]; shroudVertices = new Vertex[verticesLength]; @@ -209,20 +214,38 @@ namespace OpenRA.Mods.Common.Traits if (currentShroud != newShroud) { if (currentShroud != null) - currentShroud.CellEntryChanged -= MarkCellAndNeighborsDirty; + currentShroud.CellsChanged -= MarkCellsDirty; if (newShroud != null) { shroudDirty.Clear(true); - newShroud.CellEntryChanged += MarkCellAndNeighborsDirty; + newShroud.CellsChanged += MarkCellsDirty; } + cellsDirty.Clear(); + cellsAndNeighborsDirty.Clear(); + currentShroud = newShroud; } if (currentShroud != null) { mapBorderShroudIsCached = false; + + // We need to mark newly dirtied areas of the shroud. + // Expand the dirty area to cover the neighboring cells, since shroud is affected by neighboring cells. + foreach (var cell in cellsDirty) + { + cellsAndNeighborsDirty.Add(cell); + foreach (var direction in CVec.Directions) + cellsAndNeighborsDirty.Add(cell + direction); + } + + foreach (var cell in cellsAndNeighborsDirty) + shroudDirty[cell] = true; + + cellsDirty.Clear(); + cellsAndNeighborsDirty.Clear(); } else if (!mapBorderShroudIsCached) { @@ -231,9 +254,9 @@ namespace OpenRA.Mods.Common.Traits } } - void MarkCellAndNeighborsDirty(CPos cell) + void MarkCellsDirty(IEnumerable cellsChanged) { - // Mark this cell and its 8 neighbors as being out of date. + // Mark changed cells as being out of date. // We don't want to do anything more than this for several performance reasons: // - If the cells remain off-screen for a long time, they may change several times before we next view // them, so calculating their new vertices is wasted effort since we may recalculate them again before we @@ -242,15 +265,7 @@ namespace OpenRA.Mods.Common.Traits // leaves a trail of fog filling in behind). If we recalculated a cell and its neighbors when the first // cell in a group changed, many cells would be recalculated again when the second cell, right next to the // first, is updated. In fact we might do on the order of 3x the work we needed to! - shroudDirty[cell + new CVec(-1, -1)] = true; - shroudDirty[cell + new CVec(0, -1)] = true; - shroudDirty[cell + new CVec(1, -1)] = true; - shroudDirty[cell + new CVec(-1, 0)] = true; - shroudDirty[cell] = true; - shroudDirty[cell + new CVec(1, 0)] = true; - shroudDirty[cell + new CVec(-1, 1)] = true; - shroudDirty[cell + new CVec(0, 1)] = true; - shroudDirty[cell + new CVec(1, 1)] = true; + cellsDirty.UnionWith(cellsChanged); } void CacheMapBorderShroud() diff --git a/OpenRA.Mods.Common/Widgets/RadarWidget.cs b/OpenRA.Mods.Common/Widgets/RadarWidget.cs index 7c9964d46c..b10eee9c9b 100644 --- a/OpenRA.Mods.Common/Widgets/RadarWidget.cs +++ b/OpenRA.Mods.Common/Widgets/RadarWidget.cs @@ -9,6 +9,7 @@ #endregion using System; +using System.Collections.Generic; using System.Drawing; using System.Linq; using OpenRA.Graphics; @@ -33,6 +34,8 @@ namespace OpenRA.Mods.Common.Widgets readonly WorldRenderer worldRenderer; readonly RadarPings radarPings; + readonly HashSet dirtyShroudCells = new HashSet(); + float radarMinimapHeight; int frame; bool hasRadar; @@ -130,6 +133,11 @@ namespace OpenRA.Mods.Common.Widgets } } + void MarkShroudDirty(IEnumerable cellsChanged) + { + dirtyShroudCells.UnionWith(cellsChanged); + } + public override string GetCursor(int2 pos) { if (world == null || !hasRadar) @@ -194,6 +202,15 @@ namespace OpenRA.Mods.Common.Widgets if (world == null) return; + if (renderShroud != null) + { + foreach (var cell in dirtyShroudCells) + UpdateShroudCell(cell); + dirtyShroudCells.Clear(); + } + + radarSheet.CommitData(); + var o = new float2(mapRect.Location.X, mapRect.Location.Y + world.Map.Bounds.Height * previewScale * (1 - radarMinimapHeight) / 2); var s = new float2(mapRect.Size.Width, mapRect.Size.Height * radarMinimapHeight); @@ -255,7 +272,7 @@ namespace OpenRA.Mods.Common.Widgets if (newRenderShroud != renderShroud) { if (renderShroud != null) - renderShroud.CellEntryChanged -= UpdateShroudCell; + renderShroud.CellsChanged -= MarkShroudDirty; if (newRenderShroud != null) { @@ -264,9 +281,11 @@ namespace OpenRA.Mods.Common.Widgets OpenRA.Graphics.Util.FastCopyIntoSprite(shroudSprite, bitmap); // Update the notification binding - newRenderShroud.CellEntryChanged += UpdateShroudCell; + newRenderShroud.CellsChanged += MarkShroudDirty; } + dirtyShroudCells.Clear(); + renderShroud = newRenderShroud; } @@ -299,8 +318,6 @@ namespace OpenRA.Mods.Common.Widgets } } } - - radarSheet.CommitData(); } var targetFrame = enabled ? AnimationLength : 0;