Centralize shroud changes in one pass to improve performance.
This commit is contained in:
@@ -228,13 +228,11 @@ namespace OpenRA.Graphics
|
||||
if (a.Actor.IsInWorld && !a.Actor.Disposed)
|
||||
a.Trait.RenderAboveWorld(a.Actor, this);
|
||||
|
||||
var renderShroud = World.RenderPlayer != null ? World.RenderPlayer.Shroud : null;
|
||||
|
||||
if (enableDepthBuffer)
|
||||
Game.Renderer.ClearDepthBuffer();
|
||||
|
||||
foreach (var a in World.ActorsWithTrait<IRenderShroud>())
|
||||
a.Trait.RenderShroud(renderShroud, this);
|
||||
a.Trait.RenderShroud(this);
|
||||
|
||||
if (enableDepthBuffer)
|
||||
Game.Renderer.Context.DisableDepthBuffer();
|
||||
|
||||
@@ -215,9 +215,7 @@ namespace OpenRA.Traits
|
||||
readonly Player owner;
|
||||
readonly Dictionary<uint, FrozenActor> frozenActorsById;
|
||||
readonly SpatiallyPartitioned<uint> partitionedFrozenActorIds;
|
||||
readonly bool[] dirtyBins;
|
||||
readonly HashSet<uint> dirtyFrozenActorIds = new HashSet<uint>();
|
||||
readonly int rows, cols;
|
||||
|
||||
public FrozenActorLayer(Actor self, FrozenActorLayerInfo info)
|
||||
{
|
||||
@@ -226,24 +224,10 @@ namespace OpenRA.Traits
|
||||
owner = self.Owner;
|
||||
frozenActorsById = new Dictionary<uint, FrozenActor>();
|
||||
|
||||
// PERF: Partition the map into a series of coarse-grained bins and track changes in the shroud against
|
||||
// bin - marking that bin dirty if it changes. This is fairly cheap to track and allows us to perform the
|
||||
// expensive visibility update for frozen actors in these regions.
|
||||
partitionedFrozenActorIds = new SpatiallyPartitioned<uint>(
|
||||
world.Map.MapSize.X, world.Map.MapSize.Y, binSize);
|
||||
|
||||
cols = world.Map.MapSize.X / binSize + 1;
|
||||
rows = world.Map.MapSize.Y / binSize + 1;
|
||||
dirtyBins = new bool[cols * rows];
|
||||
self.Trait<Shroud>().CellsChanged += cells =>
|
||||
{
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
var x = cell.U / binSize;
|
||||
var y = cell.V / binSize;
|
||||
dirtyBins[y * cols + x] = true;
|
||||
}
|
||||
};
|
||||
self.Trait<Shroud>().OnShroudChanged += uv => dirtyFrozenActorIds.UnionWith(partitionedFrozenActorIds.At(new int2(uv.U, uv.V)));
|
||||
}
|
||||
|
||||
public void Add(FrozenActor fa)
|
||||
@@ -285,8 +269,6 @@ namespace OpenRA.Traits
|
||||
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
UpdateDirtyFrozenActorsFromDirtyBins();
|
||||
|
||||
var frozenActorsToRemove = new List<FrozenActor>();
|
||||
VisibilityHash = 0;
|
||||
FrozenHash = 0;
|
||||
@@ -314,25 +296,6 @@ namespace OpenRA.Traits
|
||||
Remove(fa);
|
||||
}
|
||||
|
||||
void UpdateDirtyFrozenActorsFromDirtyBins()
|
||||
{
|
||||
// Check which bins on the map were dirtied due to changes in the shroud and gather the frozen actors whose
|
||||
// footprint overlap with these bins.
|
||||
for (var y = 0; y < rows; y++)
|
||||
{
|
||||
for (var x = 0; x < cols; x++)
|
||||
{
|
||||
if (!dirtyBins[y * cols + x])
|
||||
continue;
|
||||
|
||||
var box = new Rectangle(x * binSize, y * binSize, binSize, binSize);
|
||||
dirtyFrozenActorIds.UnionWith(partitionedFrozenActorIds.InBox(box));
|
||||
}
|
||||
}
|
||||
|
||||
Array.Clear(dirtyBins, 0, dirtyBins.Length);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return world.ScreenMap.RenderableFrozenActorsInBox(owner, wr.Viewport.TopLeft, wr.Viewport.BottomRight)
|
||||
|
||||
@@ -68,10 +68,10 @@ namespace OpenRA.Traits
|
||||
public object Create(ActorInitializer init) { return new Shroud(init.Self, this); }
|
||||
}
|
||||
|
||||
public class Shroud : ISync, INotifyCreated
|
||||
public class Shroud : ISync, INotifyCreated, ITick
|
||||
{
|
||||
public enum SourceType : byte { PassiveVisibility, Shroud, Visibility }
|
||||
public event Action<IEnumerable<PPos>> CellsChanged;
|
||||
public event Action<PPos> OnShroudChanged;
|
||||
|
||||
enum ShroudCellType : byte { Shroud, Fog, Visible }
|
||||
class ShroudSource
|
||||
@@ -98,6 +98,7 @@ namespace OpenRA.Traits
|
||||
readonly CellLayer<short> visibleCount;
|
||||
readonly CellLayer<short> generatedShroudCount;
|
||||
readonly CellLayer<bool> explored;
|
||||
readonly CellLayer<bool> touched;
|
||||
|
||||
// Per-cell cache of the resolved cell type (shroud/fog/visible)
|
||||
readonly CellLayer<ShroudCellType> resolvedType;
|
||||
@@ -117,7 +118,6 @@ namespace OpenRA.Traits
|
||||
return;
|
||||
|
||||
disabled = value;
|
||||
Invalidate(map.ProjectedCellBounds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@ namespace OpenRA.Traits
|
||||
visibleCount = new CellLayer<short>(map);
|
||||
generatedShroudCount = new CellLayer<short>(map);
|
||||
explored = new CellLayer<bool>(map);
|
||||
touched = new CellLayer<bool>(map);
|
||||
|
||||
// Defaults to 0 = Shroud
|
||||
resolvedType = new CellLayer<ShroudCellType>(map);
|
||||
@@ -156,11 +157,19 @@ namespace OpenRA.Traits
|
||||
self.World.AddFrameEndTask(w => ExploreAll());
|
||||
}
|
||||
|
||||
void Invalidate(IEnumerable<PPos> changed)
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
foreach (var puv in changed)
|
||||
if (OnShroudChanged == null)
|
||||
return;
|
||||
|
||||
foreach (var puv in map.ProjectedCellBounds)
|
||||
{
|
||||
var uv = (MPos)puv;
|
||||
if (!touched[uv])
|
||||
continue;
|
||||
|
||||
touched[uv] = false;
|
||||
|
||||
var type = ShroudCellType.Shroud;
|
||||
|
||||
if (explored[uv] && (!shroudGenerationEnabled || generatedShroudCount[uv] == 0 || visibleCount[uv] > 0))
|
||||
@@ -172,18 +181,13 @@ namespace OpenRA.Traits
|
||||
type = count > 0 ? ShroudCellType.Visible : ShroudCellType.Fog;
|
||||
}
|
||||
|
||||
var oldResolvedType = resolvedType[uv];
|
||||
resolvedType[uv] = type;
|
||||
if (type != oldResolvedType)
|
||||
OnShroudChanged((PPos)uv);
|
||||
}
|
||||
|
||||
if (CellsChanged != null)
|
||||
CellsChanged(changed);
|
||||
|
||||
var oldHash = Hash;
|
||||
Hash = Sync.HashPlayer(self.Owner) + self.World.WorldTick * 3;
|
||||
|
||||
// Invalidate may be called multiple times in one world tick, which is decoupled from rendering.
|
||||
if (oldHash == Hash)
|
||||
Hash += 1;
|
||||
Hash = Sync.HashPlayer(self.Owner) + self.World.WorldTick;
|
||||
}
|
||||
|
||||
public static IEnumerable<PPos> ProjectedCellsInRange(Map map, WPos pos, WDist minRange, WDist maxRange, int maxHeightDelta = -1)
|
||||
@@ -229,6 +233,7 @@ namespace OpenRA.Traits
|
||||
continue;
|
||||
|
||||
var uv = (MPos)puv;
|
||||
touched[uv] = true;
|
||||
switch (type)
|
||||
{
|
||||
case SourceType.PassiveVisibility:
|
||||
@@ -246,8 +251,6 @@ namespace OpenRA.Traits
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Invalidate(projectedCells);
|
||||
}
|
||||
|
||||
public void RemoveSource(object key)
|
||||
@@ -262,6 +265,7 @@ namespace OpenRA.Traits
|
||||
if (map.Contains(puv))
|
||||
{
|
||||
var uv = (MPos)puv;
|
||||
touched[uv] = true;
|
||||
switch (state.Type)
|
||||
{
|
||||
case SourceType.PassiveVisibility:
|
||||
@@ -278,23 +282,19 @@ namespace OpenRA.Traits
|
||||
}
|
||||
|
||||
sources.Remove(key);
|
||||
Invalidate(state.ProjectedCells);
|
||||
}
|
||||
|
||||
public void ExploreProjectedCells(World world, IEnumerable<PPos> cells)
|
||||
{
|
||||
var changed = new HashSet<PPos>();
|
||||
foreach (var puv in cells)
|
||||
{
|
||||
var uv = (MPos)puv;
|
||||
if (map.Contains(puv) && !explored[uv])
|
||||
{
|
||||
touched[uv] = true;
|
||||
explored[uv] = true;
|
||||
changed.Add(puv);
|
||||
}
|
||||
}
|
||||
|
||||
Invalidate(changed);
|
||||
}
|
||||
|
||||
public void Explore(Shroud s)
|
||||
@@ -302,51 +302,38 @@ 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<PPos>();
|
||||
foreach (var puv in map.ProjectedCellBounds)
|
||||
{
|
||||
var uv = (MPos)puv;
|
||||
if (!explored[uv] && s.explored[uv])
|
||||
{
|
||||
touched[uv] = true;
|
||||
explored[uv] = true;
|
||||
changed.Add(puv);
|
||||
}
|
||||
}
|
||||
|
||||
Invalidate(changed);
|
||||
}
|
||||
|
||||
public void ExploreAll()
|
||||
{
|
||||
var changed = new List<PPos>();
|
||||
foreach (var puv in map.ProjectedCellBounds)
|
||||
{
|
||||
var uv = (MPos)puv;
|
||||
if (!explored[uv])
|
||||
{
|
||||
touched[uv] = true;
|
||||
explored[uv] = true;
|
||||
changed.Add(puv);
|
||||
}
|
||||
}
|
||||
|
||||
Invalidate(changed);
|
||||
}
|
||||
|
||||
public void ResetExploration()
|
||||
{
|
||||
var changed = new List<PPos>();
|
||||
foreach (var puv in map.ProjectedCellBounds)
|
||||
{
|
||||
var uv = (MPos)puv;
|
||||
var visible = visibleCount[uv] + passiveVisibleCount[uv] > 0;
|
||||
if (explored[uv] != visible)
|
||||
{
|
||||
explored[uv] = visible;
|
||||
changed.Add(puv);
|
||||
}
|
||||
touched[uv] = true;
|
||||
explored[uv] = (visibleCount[uv] + passiveVisibleCount[uv]) > 0;
|
||||
}
|
||||
|
||||
Invalidate(changed);
|
||||
}
|
||||
|
||||
public bool IsExplored(WPos pos)
|
||||
|
||||
@@ -410,7 +410,7 @@ namespace OpenRA.Traits
|
||||
public interface INotifyIdle { void TickIdle(Actor self); }
|
||||
|
||||
public interface IRenderAboveWorld { void RenderAboveWorld(Actor self, WorldRenderer wr); }
|
||||
public interface IRenderShroud { void RenderShroud(Shroud shroud, WorldRenderer wr); }
|
||||
public interface IRenderShroud { void RenderShroud(WorldRenderer wr); }
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface IRenderTerrain { void RenderTerrain(WorldRenderer wr, Viewport viewport); }
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
readonly World world;
|
||||
CellLayer<Pair<int, int>> terrainColor;
|
||||
readonly HashSet<MPos> dirtyTerrainCells = new HashSet<MPos>();
|
||||
readonly Shroud shroud;
|
||||
|
||||
public event Action<MPos> CellTerrainColorChanged = null;
|
||||
@@ -40,33 +39,22 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
world = self.World;
|
||||
shroud = self.Trait<Shroud>();
|
||||
shroud.CellsChanged += OnShroudCellsChanged;
|
||||
shroud.OnShroudChanged += UpdateShroudCell;
|
||||
}
|
||||
|
||||
void OnShroudCellsChanged(IEnumerable<PPos> puvs)
|
||||
void UpdateShroudCell(PPos puv)
|
||||
{
|
||||
foreach (var puv in puvs)
|
||||
{
|
||||
foreach (var uv in world.Map.Unproject(puv))
|
||||
{
|
||||
if (dirtyTerrainCells.Contains(uv))
|
||||
{
|
||||
UpdateTerrainCellColor(uv);
|
||||
dirtyTerrainCells.Remove(uv);
|
||||
}
|
||||
}
|
||||
}
|
||||
var uvs = world.Map.Unproject(puv);
|
||||
foreach (var uv in uvs)
|
||||
UpdateTerrainCell(uv);
|
||||
}
|
||||
|
||||
void UpdateTerrainCell(CPos cell)
|
||||
void UpdateTerrainCell(MPos uv)
|
||||
{
|
||||
var uv = cell.ToMPos(world.Map);
|
||||
if (!world.Map.CustomTerrain.Contains(uv))
|
||||
return;
|
||||
|
||||
if (!shroud.IsVisible(uv))
|
||||
dirtyTerrainCells.Add(uv);
|
||||
else
|
||||
if (shroud.IsVisible(uv))
|
||||
UpdateTerrainCellColor(uv);
|
||||
}
|
||||
|
||||
@@ -88,8 +76,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
foreach (var uv in world.Map.AllCells.MapCoords)
|
||||
UpdateTerrainCellColor(uv);
|
||||
|
||||
world.Map.Tiles.CellEntryChanged += UpdateTerrainCell;
|
||||
world.Map.CustomTerrain.CellEntryChanged += UpdateTerrainCell;
|
||||
world.Map.Tiles.CellEntryChanged += cell => UpdateTerrainCell(cell.ToMPos(world.Map));
|
||||
world.Map.CustomTerrain.CellEntryChanged += cell => UpdateTerrainCell(cell.ToMPos(world.Map));
|
||||
|
||||
IsInitialized = true;
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
@@ -92,17 +93,17 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
readonly ShroudRendererInfo info;
|
||||
readonly World world;
|
||||
readonly Map map;
|
||||
readonly Edges notVisibleEdges;
|
||||
readonly byte variantStride;
|
||||
readonly byte[] edgesToSpriteIndexOffset;
|
||||
|
||||
readonly CellLayer<TileInfo> tileInfos;
|
||||
readonly CellLayer<bool> cellsDirty;
|
||||
readonly Sprite[] fogSprites, shroudSprites;
|
||||
readonly HashSet<PPos> cellsDirty = new HashSet<PPos>();
|
||||
readonly HashSet<PPos> cellsAndNeighborsDirty = new HashSet<PPos>();
|
||||
|
||||
Shroud currentShroud;
|
||||
Shroud shroud;
|
||||
Func<PPos, bool> visibleUnderShroud, visibleUnderFog;
|
||||
TerrainSpriteLayer shroudLayer, fogLayer;
|
||||
bool disposed;
|
||||
@@ -122,10 +123,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
throw new ArgumentException("ShroudRenderer cannot define this many indexes for shroud directions.", "info");
|
||||
|
||||
this.info = info;
|
||||
this.world = world;
|
||||
map = world.Map;
|
||||
|
||||
tileInfos = new CellLayer<TileInfo>(map);
|
||||
|
||||
cellsDirty = new CellLayer<bool>(map);
|
||||
|
||||
// Load sprite variants
|
||||
var variantCount = info.ShroudVariants.Length;
|
||||
variantStride = (byte)(info.Index.Length + (info.OverrideFullShroud != null ? 1 : 0));
|
||||
@@ -135,12 +139,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var sequenceProvider = map.Rules.Sequences;
|
||||
for (var j = 0; j < variantCount; j++)
|
||||
{
|
||||
var shroud = sequenceProvider.GetSequence(info.Sequence, info.ShroudVariants[j]);
|
||||
var fog = sequenceProvider.GetSequence(info.Sequence, info.FogVariants[j]);
|
||||
var shroudSequence = sequenceProvider.GetSequence(info.Sequence, info.ShroudVariants[j]);
|
||||
var fogSequence = sequenceProvider.GetSequence(info.Sequence, info.FogVariants[j]);
|
||||
for (var i = 0; i < info.Index.Length; i++)
|
||||
{
|
||||
shroudSprites[j * variantStride + i] = shroud.GetSprite(i);
|
||||
fogSprites[j * variantStride + i] = fog.GetSprite(i);
|
||||
shroudSprites[j * variantStride + i] = shroudSequence.GetSprite(i);
|
||||
fogSprites[j * variantStride + i] = fogSequence.GetSprite(i);
|
||||
}
|
||||
|
||||
if (info.OverrideFullShroud != null)
|
||||
@@ -160,6 +164,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
edgesToSpriteIndexOffset[info.OverrideShroudIndex] = (byte)(variantStride - 1);
|
||||
|
||||
notVisibleEdges = info.UseExtendedIndex ? Edges.AllSides : Edges.AllCorners;
|
||||
|
||||
world.RenderPlayerChanged += WorldOnRenderPlayerChanged;
|
||||
}
|
||||
|
||||
void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr)
|
||||
@@ -174,9 +180,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
tileInfos[uv] = new TileInfo(screen, variant);
|
||||
}
|
||||
|
||||
// Dirty the whole projected space
|
||||
DirtyCells(map.AllCells.MapCoords.Select(uv => (PPos)uv));
|
||||
|
||||
// All tiles are visible in the editor
|
||||
if (w.Type == WorldType.Editor)
|
||||
visibleUnderShroud = _ => true;
|
||||
@@ -203,6 +206,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
shroudLayer = new TerrainSpriteLayer(w, wr, shroudSheet, shroudBlend, wr.Palette(info.ShroudPalette), false);
|
||||
fogLayer = new TerrainSpriteLayer(w, wr, fogSheet, fogBlend, wr.Palette(info.FogPalette), false);
|
||||
|
||||
WorldOnRenderPlayerChanged(world.RenderPlayer);
|
||||
}
|
||||
|
||||
Edges GetEdges(PPos puv, Func<PPos, bool> isVisible)
|
||||
@@ -234,54 +239,48 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return info.UseExtendedIndex ? edge ^ ucorner : edge & Edges.AllCorners;
|
||||
}
|
||||
|
||||
void DirtyCells(IEnumerable<PPos> cells)
|
||||
void WorldOnRenderPlayerChanged(Player player)
|
||||
{
|
||||
// PERF: Many cells in the shroud change every tick. We only track the changes here and defer the real work
|
||||
// we need to do until we render. This allows us to avoid wasted work.
|
||||
cellsDirty.UnionWith(cells);
|
||||
var newShroud = player != null ? player.Shroud : null;
|
||||
|
||||
if (shroud != newShroud)
|
||||
{
|
||||
if (shroud != null)
|
||||
shroud.OnShroudChanged -= UpdateShroudCell;
|
||||
|
||||
if (newShroud != null)
|
||||
{
|
||||
visibleUnderShroud = puv => newShroud.IsExplored(puv);
|
||||
visibleUnderFog = puv => newShroud.IsVisible(puv);
|
||||
newShroud.OnShroudChanged += UpdateShroudCell;
|
||||
}
|
||||
else
|
||||
{
|
||||
visibleUnderShroud = puv => map.Contains(puv);
|
||||
visibleUnderFog = puv => map.Contains(puv);
|
||||
}
|
||||
|
||||
shroud = newShroud;
|
||||
}
|
||||
|
||||
// Dirty the full projected space so the cells outside
|
||||
// the map bounds can be initialized as fully shrouded.
|
||||
cellsDirty.Clear(true);
|
||||
var tl = new PPos(0, 0);
|
||||
var br = new PPos(map.MapSize.X - 1, map.MapSize.Y - 1);
|
||||
UpdateShroud(new ProjectedCellRegion(map, tl, br));
|
||||
}
|
||||
|
||||
void IRenderShroud.RenderShroud(Shroud shroud, WorldRenderer wr)
|
||||
void UpdateShroud(ProjectedCellRegion region)
|
||||
{
|
||||
if (currentShroud != shroud)
|
||||
{
|
||||
if (currentShroud != null)
|
||||
currentShroud.CellsChanged -= DirtyCells;
|
||||
|
||||
if (shroud != null)
|
||||
shroud.CellsChanged += DirtyCells;
|
||||
|
||||
// Needs the anonymous function to ensure the correct overload is chosen
|
||||
if (shroud != null)
|
||||
visibleUnderShroud = puv => currentShroud.IsExplored(puv);
|
||||
else
|
||||
visibleUnderShroud = puv => map.Contains(puv);
|
||||
|
||||
if (shroud != null)
|
||||
visibleUnderFog = puv => currentShroud.IsVisible(puv);
|
||||
else
|
||||
visibleUnderFog = puv => map.Contains(puv);
|
||||
|
||||
currentShroud = shroud;
|
||||
DirtyCells(map.ProjectedCellBounds);
|
||||
}
|
||||
|
||||
// We need to update newly dirtied areas of the shroud.
|
||||
// Expand the dirty area to cover the neighboring cells, since shroud is affected by neighboring cells.
|
||||
foreach (var uv in cellsDirty)
|
||||
{
|
||||
cellsAndNeighborsDirty.Add(uv);
|
||||
var cell = ((MPos)uv).ToCPos(map);
|
||||
foreach (var direction in CVec.Directions)
|
||||
cellsAndNeighborsDirty.Add((PPos)(cell + direction).ToMPos(map));
|
||||
}
|
||||
|
||||
foreach (var puv in cellsAndNeighborsDirty)
|
||||
foreach (var puv in region)
|
||||
{
|
||||
var uv = (MPos)puv;
|
||||
if (!tileInfos.Contains(uv))
|
||||
if (!cellsDirty[uv] || !tileInfos.Contains(uv))
|
||||
continue;
|
||||
|
||||
cellsDirty[uv] = false;
|
||||
|
||||
var tileInfo = tileInfos[uv];
|
||||
var shroudSprite = GetSprite(shroudSprites, GetEdges(puv, visibleUnderShroud), tileInfo.Variant);
|
||||
var shroudPos = tileInfo.ScreenPosition;
|
||||
@@ -296,14 +295,25 @@ namespace OpenRA.Mods.Common.Traits
|
||||
shroudLayer.Update(uv, shroudSprite, shroudPos);
|
||||
fogLayer.Update(uv, fogSprite, fogPos);
|
||||
}
|
||||
}
|
||||
|
||||
cellsDirty.Clear();
|
||||
cellsAndNeighborsDirty.Clear();
|
||||
|
||||
void IRenderShroud.RenderShroud(WorldRenderer wr)
|
||||
{
|
||||
UpdateShroud(map.ProjectedCellBounds);
|
||||
fogLayer.Draw(wr.Viewport);
|
||||
shroudLayer.Draw(wr.Viewport);
|
||||
}
|
||||
|
||||
void UpdateShroudCell(PPos puv)
|
||||
{
|
||||
var uv = (MPos)puv;
|
||||
cellsDirty[uv] = true;
|
||||
var cell = uv.ToCPos(map);
|
||||
foreach (var direction in CVec.Directions)
|
||||
if (map.Contains((PPos)(cell + direction).ToMPos(map)))
|
||||
cellsDirty[cell + direction] = true;
|
||||
}
|
||||
|
||||
Sprite GetSprite(Sprite[] sprites, Edges edges, int variant)
|
||||
{
|
||||
if (edges == Edges.None)
|
||||
|
||||
@@ -39,8 +39,6 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
readonly int previewWidth;
|
||||
readonly int previewHeight;
|
||||
|
||||
readonly HashSet<PPos> dirtyShroudCells = new HashSet<PPos>();
|
||||
|
||||
float radarMinimapHeight;
|
||||
int frame;
|
||||
bool hasRadar;
|
||||
@@ -56,7 +54,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
Sprite terrainSprite;
|
||||
Sprite actorSprite;
|
||||
Sprite shroudSprite;
|
||||
Shroud renderShroud;
|
||||
Shroud shroud;
|
||||
PlayerRadarTerrain playerRadarTerrain;
|
||||
Player currentPlayer;
|
||||
|
||||
@@ -70,7 +68,6 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
this.worldRenderer = worldRenderer;
|
||||
|
||||
radarPings = world.WorldActor.TraitOrDefault<RadarPings>();
|
||||
MarkShroudDirty(world.Map.AllCells.MapCoords.Select(uv => (PPos)uv));
|
||||
|
||||
isRectangularIsometric = world.Map.Grid.Type == MapGridType.RectangularIsometric;
|
||||
cellWidth = isRectangularIsometric ? 2 : 1;
|
||||
@@ -131,22 +128,21 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
currentPlayer = player;
|
||||
|
||||
var newRenderShroud = player != null ? player.Shroud : null;
|
||||
if (newRenderShroud != renderShroud)
|
||||
var newShroud = player != null ? player.Shroud : null;
|
||||
|
||||
if (newShroud != shroud)
|
||||
{
|
||||
if (renderShroud != null)
|
||||
renderShroud.CellsChanged -= MarkShroudDirty;
|
||||
if (shroud != null)
|
||||
shroud.OnShroudChanged -= UpdateShroudCell;
|
||||
|
||||
if (newRenderShroud != null)
|
||||
if (newShroud != null)
|
||||
{
|
||||
// Redraw the full shroud sprite
|
||||
MarkShroudDirty(world.Map.AllCells.MapCoords.Select(uv => (PPos)uv));
|
||||
|
||||
// Update the notification binding
|
||||
newRenderShroud.CellsChanged += MarkShroudDirty;
|
||||
newShroud.OnShroudChanged += UpdateShroudCell;
|
||||
foreach (var puv in world.Map.ProjectedCellBounds)
|
||||
UpdateShroudCell(puv);
|
||||
}
|
||||
|
||||
renderShroud = newRenderShroud;
|
||||
shroud = newShroud;
|
||||
}
|
||||
|
||||
var newPlayerRadarTerrain =
|
||||
@@ -233,14 +229,10 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
void UpdateShroudCell(PPos puv)
|
||||
{
|
||||
var color = 0;
|
||||
var rp = world.RenderPlayer;
|
||||
if (rp != null)
|
||||
{
|
||||
if (!rp.Shroud.IsExplored(puv))
|
||||
color = Color.Black.ToArgb();
|
||||
else if (!rp.Shroud.IsVisible(puv))
|
||||
color = Color.FromArgb(128, Color.Black).ToArgb();
|
||||
}
|
||||
if (!currentPlayer.Shroud.IsExplored(puv))
|
||||
color = Color.Black.ToArgb();
|
||||
else if (!currentPlayer.Shroud.IsVisible(puv))
|
||||
color = Color.FromArgb(128, Color.Black).ToArgb();
|
||||
|
||||
var stride = radarSheet.Size.Width;
|
||||
unsafe
|
||||
@@ -248,32 +240,25 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
fixed (byte* colorBytes = &radarData[0])
|
||||
{
|
||||
var colors = (int*)colorBytes;
|
||||
foreach (var uv in world.Map.Unproject(puv))
|
||||
foreach (var iuv in world.Map.Unproject(puv))
|
||||
{
|
||||
if (isRectangularIsometric)
|
||||
{
|
||||
// Odd rows are shifted right by 1px
|
||||
var dx = uv.V & 1;
|
||||
if (uv.U + dx > 0)
|
||||
colors[uv.V * stride + 2 * uv.U + dx - 1 + previewWidth] = color;
|
||||
var dx = iuv.V & 1;
|
||||
if (iuv.U + dx > 0)
|
||||
colors[iuv.V * stride + 2 * iuv.U + dx - 1 + previewWidth] = color;
|
||||
|
||||
if (2 * uv.U + dx < stride)
|
||||
colors[uv.V * stride + 2 * uv.U + dx + previewWidth] = color;
|
||||
if (2 * iuv.U + dx < stride)
|
||||
colors[iuv.V * stride + 2 * iuv.U + dx + previewWidth] = color;
|
||||
}
|
||||
else
|
||||
colors[uv.V * stride + uv.U + previewWidth] = color;
|
||||
colors[iuv.V * stride + iuv.U + previewWidth] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MarkShroudDirty(IEnumerable<PPos> projectedCellsChanged)
|
||||
{
|
||||
// PERF: Many cells in the shroud change every tick. We only track the changes here and defer the real work
|
||||
// we need to do until we render. This allows us to avoid wasted work.
|
||||
dirtyShroudCells.UnionWith(projectedCellsChanged);
|
||||
}
|
||||
|
||||
public override string GetCursor(int2 pos)
|
||||
{
|
||||
if (world == null || !hasRadar)
|
||||
@@ -342,13 +327,6 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
if (world == null)
|
||||
return;
|
||||
|
||||
if (renderShroud != null)
|
||||
{
|
||||
foreach (var cell in dirtyShroudCells)
|
||||
UpdateShroudCell(cell);
|
||||
dirtyShroudCells.Clear();
|
||||
}
|
||||
|
||||
radarSheet.CommitBufferedData();
|
||||
|
||||
var o = new float2(mapRect.Location.X, mapRect.Location.Y + world.Map.Bounds.Height * previewScale * (1 - radarMinimapHeight) / 2);
|
||||
@@ -358,7 +336,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
rsr.DrawSprite(terrainSprite, o, s);
|
||||
rsr.DrawSprite(actorSprite, o, s);
|
||||
|
||||
if (renderShroud != null)
|
||||
if (shroud != null)
|
||||
rsr.DrawSprite(shroudSprite, o, s);
|
||||
|
||||
// Draw viewport rect
|
||||
|
||||
Reference in New Issue
Block a user