Sped up shroud rendering.
- Only update shroud within the visible screen area, rather than the whole map. This improves performance on larger maps significantly when scrolling around since large portions of the shroud do not need to be updated. - Provide methods in Shroud to return delegates to check for explored/visibility for tiles within a certain region. This allows it to return more efficient delegates whenever the region is within the map bounds, or shroud/fog is disabled. In the typical case where the region is in bounds and shroud/fog is enabled, the fast check is almost twice as fast as the slow check. - Use the Shroud delegate functions in shroud rendering, frozen actors, minimap rendering and resource layer areas to provide a speedup since these areas of code can often take advantage of the fact they perform checks within the map boundary. - Cache current element in CellRegionEnumerator to prevent repeated work if the element is accessed more than once. - Decrease the size of elements in some arrays in hopes of reducing memory needs and improving cache hits.
This commit is contained in:
@@ -180,12 +180,14 @@ namespace OpenRA.Graphics
|
|||||||
{
|
{
|
||||||
var colors = (int*)bitmapData.Scan0;
|
var colors = (int*)bitmapData.Scan0;
|
||||||
var stride = bitmapData.Stride / 4;
|
var stride = bitmapData.Stride / 4;
|
||||||
|
var shroudObscured = world.ShroudObscuresTest(map.Cells);
|
||||||
|
var fogObscured = world.FogObscuresTest(map.Cells);
|
||||||
foreach (var cell in map.Cells)
|
foreach (var cell in map.Cells)
|
||||||
{
|
{
|
||||||
var uv = Map.CellToMap(map.TileShape, cell) - offset;
|
var uv = Map.CellToMap(map.TileShape, cell) - offset;
|
||||||
if (world.ShroudObscures(cell))
|
if (shroudObscured(cell))
|
||||||
colors[uv.Y * stride + uv.X] = shroud;
|
colors[uv.Y * stride + uv.X] = shroud;
|
||||||
else if (world.FogObscures(cell))
|
else if (fogObscured(cell))
|
||||||
colors[uv.Y * stride + uv.X] = fog;
|
colors[uv.Y * stride + uv.X] = fog;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,10 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace OpenRA
|
namespace OpenRA
|
||||||
{
|
{
|
||||||
@@ -47,29 +49,69 @@ namespace OpenRA
|
|||||||
return new CellRegion(region.shape, tl, br);
|
return new CellRegion(region.shape, tl, br);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns the minimal region that covers at least the specified cells.</summary>
|
||||||
|
public static CellRegion BoundingRegion(TileShape shape, IEnumerable<CPos> cells)
|
||||||
|
{
|
||||||
|
if (cells == null || !cells.Any())
|
||||||
|
throw new ArgumentException("cells must not be null or empty.", "cells");
|
||||||
|
|
||||||
|
var minX = int.MaxValue;
|
||||||
|
var minY = int.MaxValue;
|
||||||
|
var maxX = int.MinValue;
|
||||||
|
var maxY = int.MinValue;
|
||||||
|
foreach (var cell in cells)
|
||||||
|
{
|
||||||
|
if (minX > cell.X)
|
||||||
|
minX = cell.X;
|
||||||
|
if (maxX < cell.X)
|
||||||
|
maxX = cell.X;
|
||||||
|
if (minY > cell.Y)
|
||||||
|
minY = cell.Y;
|
||||||
|
if (maxY < cell.Y)
|
||||||
|
maxY = cell.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CellRegion(shape, new CPos(minX, minY), new CPos(maxX, maxY));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(CellRegion region)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
TopLeft.X <= region.TopLeft.X && TopLeft.Y <= region.TopLeft.Y &&
|
||||||
|
BottomRight.X >= region.BottomRight.X && BottomRight.Y >= region.BottomRight.Y;
|
||||||
|
}
|
||||||
|
|
||||||
public bool Contains(CPos cell)
|
public bool Contains(CPos cell)
|
||||||
{
|
{
|
||||||
var uv = Map.CellToMap(shape, cell);
|
var uv = Map.CellToMap(shape, cell);
|
||||||
return uv.X >= mapTopLeft.X && uv.X <= mapBottomRight.X && uv.Y >= mapTopLeft.Y && uv.Y <= mapBottomRight.Y;
|
return uv.X >= mapTopLeft.X && uv.X <= mapBottomRight.X && uv.Y >= mapTopLeft.Y && uv.Y <= mapBottomRight.Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<CPos> GetEnumerator()
|
public CellRegionEnumerator GetEnumerator()
|
||||||
{
|
{
|
||||||
return new CellRegionEnumerator(this);
|
return new CellRegionEnumerator(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IEnumerator<CPos> IEnumerable<CPos>.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
{
|
{
|
||||||
return GetEnumerator();
|
return GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
class CellRegionEnumerator : IEnumerator<CPos>
|
public class CellRegionEnumerator : IEnumerator<CPos>
|
||||||
{
|
{
|
||||||
readonly CellRegion r;
|
readonly CellRegion r;
|
||||||
|
|
||||||
// Current position, in map coordinates
|
// Current position, in map coordinates
|
||||||
int u, v;
|
int u, v;
|
||||||
|
|
||||||
|
// Current position, in cell coordinates
|
||||||
|
CPos current;
|
||||||
|
|
||||||
public CellRegionEnumerator(CellRegion region)
|
public CellRegionEnumerator(CellRegion region)
|
||||||
{
|
{
|
||||||
r = region;
|
r = region;
|
||||||
@@ -91,6 +133,7 @@ namespace OpenRA
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current = Map.MapToCell(r.shape, new CPos(u, v));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +144,7 @@ namespace OpenRA
|
|||||||
v = r.mapTopLeft.Y;
|
v = r.mapTopLeft.Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CPos Current { get { return Map.MapToCell(r.shape, new CPos(u, v)); } }
|
public CPos Current { get { return current; } }
|
||||||
object IEnumerator.Current { get { return Current; } }
|
object IEnumerator.Current { get { return Current; } }
|
||||||
public void Dispose() { }
|
public void Dispose() { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ namespace OpenRA.Traits
|
|||||||
public class FrozenActor
|
public class FrozenActor
|
||||||
{
|
{
|
||||||
public readonly CPos[] Footprint;
|
public readonly CPos[] Footprint;
|
||||||
|
public readonly CellRegion FootprintRegion;
|
||||||
public readonly WPos CenterPosition;
|
public readonly WPos CenterPosition;
|
||||||
public readonly Rectangle Bounds;
|
public readonly Rectangle Bounds;
|
||||||
readonly Actor actor;
|
readonly Actor actor;
|
||||||
@@ -38,10 +39,12 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
public bool Visible;
|
public bool Visible;
|
||||||
|
|
||||||
public FrozenActor(Actor self, IEnumerable<CPos> footprint)
|
public FrozenActor(Actor self, CPos[] footprint, CellRegion footprintRegion)
|
||||||
{
|
{
|
||||||
actor = self;
|
actor = self;
|
||||||
Footprint = footprint.ToArray();
|
Footprint = footprint;
|
||||||
|
FootprintRegion = footprintRegion;
|
||||||
|
|
||||||
CenterPosition = self.CenterPosition;
|
CenterPosition = self.CenterPosition;
|
||||||
Bounds = self.Bounds.Value;
|
Bounds = self.Bounds.Value;
|
||||||
}
|
}
|
||||||
@@ -54,16 +57,7 @@ namespace OpenRA.Traits
|
|||||||
int flashTicks;
|
int flashTicks;
|
||||||
public void Tick(World world, Shroud shroud)
|
public void Tick(World world, Shroud shroud)
|
||||||
{
|
{
|
||||||
Visible = true;
|
Visible = !Footprint.Any(shroud.IsVisibleTest(FootprintRegion));
|
||||||
foreach (var pos in Footprint)
|
|
||||||
{
|
|
||||||
if (shroud.IsVisible(pos))
|
|
||||||
{
|
|
||||||
Visible = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flashTicks > 0)
|
if (flashTicks > 0)
|
||||||
flashTicks--;
|
flashTicks--;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ namespace OpenRA.Traits
|
|||||||
readonly Actor self;
|
readonly Actor self;
|
||||||
readonly Map map;
|
readonly Map map;
|
||||||
|
|
||||||
readonly CellLayer<int> visibleCount;
|
readonly CellLayer<short> visibleCount;
|
||||||
readonly CellLayer<int> generatedShroudCount;
|
readonly CellLayer<short> generatedShroudCount;
|
||||||
readonly CellLayer<bool> explored;
|
readonly CellLayer<bool> explored;
|
||||||
|
|
||||||
readonly Lazy<IFogVisibilityModifier[]> fogVisibilities;
|
readonly Lazy<IFogVisibilityModifier[]> fogVisibilities;
|
||||||
@@ -39,13 +39,19 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
public int Hash { get; private set; }
|
public int Hash { get; private set; }
|
||||||
|
|
||||||
|
static readonly Func<CPos, bool> TruthPredicate = cell => true;
|
||||||
|
readonly Func<CPos, bool> fastExploredTest;
|
||||||
|
readonly Func<CPos, bool> slowExploredTest;
|
||||||
|
readonly Func<CPos, bool> fastVisibleTest;
|
||||||
|
readonly Func<CPos, bool> slowVisibleTest;
|
||||||
|
|
||||||
public Shroud(Actor self)
|
public Shroud(Actor self)
|
||||||
{
|
{
|
||||||
this.self = self;
|
this.self = self;
|
||||||
map = self.World.Map;
|
map = self.World.Map;
|
||||||
|
|
||||||
visibleCount = new CellLayer<int>(map);
|
visibleCount = new CellLayer<short>(map);
|
||||||
generatedShroudCount = new CellLayer<int>(map);
|
generatedShroudCount = new CellLayer<short>(map);
|
||||||
explored = new CellLayer<bool>(map);
|
explored = new CellLayer<bool>(map);
|
||||||
|
|
||||||
self.World.ActorAdded += AddVisibility;
|
self.World.ActorAdded += AddVisibility;
|
||||||
@@ -55,6 +61,11 @@ namespace OpenRA.Traits
|
|||||||
self.World.ActorRemoved += RemoveShroudGeneration;
|
self.World.ActorRemoved += RemoveShroudGeneration;
|
||||||
|
|
||||||
fogVisibilities = Exts.Lazy(() => self.TraitsImplementing<IFogVisibilityModifier>().ToArray());
|
fogVisibilities = Exts.Lazy(() => self.TraitsImplementing<IFogVisibilityModifier>().ToArray());
|
||||||
|
|
||||||
|
fastExploredTest = IsExploredCore;
|
||||||
|
slowExploredTest = IsExplored;
|
||||||
|
fastVisibleTest = IsVisibleCore;
|
||||||
|
slowVisibleTest = IsVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Invalidate()
|
void Invalidate()
|
||||||
@@ -223,10 +234,32 @@ namespace OpenRA.Traits
|
|||||||
if (!map.Contains(cell))
|
if (!map.Contains(cell))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (Disabled || !self.World.LobbyInfo.GlobalSettings.Shroud)
|
if (!ShroudEnabled)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return explored[cell] && (generatedShroudCount[cell] == 0 || visibleCount[cell] > 0);
|
return IsExploredCore(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShroudEnabled { get { return !Disabled && self.World.LobbyInfo.GlobalSettings.Shroud; } }
|
||||||
|
|
||||||
|
bool IsExploredCore(CPos cell)
|
||||||
|
{
|
||||||
|
var uv = Map.CellToMap(map.TileShape, cell);
|
||||||
|
return explored[uv.X, uv.Y] && (generatedShroudCount[uv.X, uv.Y] == 0 || visibleCount[uv.X, uv.Y] > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Func<CPos, bool> IsExploredTest(CellRegion region)
|
||||||
|
{
|
||||||
|
// If the region to test extends outside the map we must use the slow test that checks the map boundary every time.
|
||||||
|
if (!map.Cells.Contains(region))
|
||||||
|
return slowExploredTest;
|
||||||
|
|
||||||
|
// If shroud isn't enabled, then we can see everything.
|
||||||
|
if (!ShroudEnabled)
|
||||||
|
return TruthPredicate;
|
||||||
|
|
||||||
|
// If shroud is enabled, we can use the fast test that just does the core check.
|
||||||
|
return fastExploredTest;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsExplored(Actor a)
|
public bool IsExplored(Actor a)
|
||||||
@@ -239,12 +272,33 @@ namespace OpenRA.Traits
|
|||||||
if (!map.Contains(cell))
|
if (!map.Contains(cell))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (Disabled || !self.World.LobbyInfo.GlobalSettings.Fog)
|
if (!FogEnabled)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
return IsVisibleCore(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FogEnabled { get { return !Disabled && self.World.LobbyInfo.GlobalSettings.Fog; } }
|
||||||
|
|
||||||
|
bool IsVisibleCore(CPos cell)
|
||||||
|
{
|
||||||
return visibleCount[cell] > 0;
|
return visibleCount[cell] > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Func<CPos, bool> IsVisibleTest(CellRegion region)
|
||||||
|
{
|
||||||
|
// If the region to test extends outside the map we must use the slow test that checks the map boundary every time.
|
||||||
|
if (!map.Cells.Contains(region))
|
||||||
|
return slowVisibleTest;
|
||||||
|
|
||||||
|
// If fog isn't enabled, then we can see everything.
|
||||||
|
if (!FogEnabled)
|
||||||
|
return TruthPredicate;
|
||||||
|
|
||||||
|
// If fog is enabled, we can use the fast test that just does the core check.
|
||||||
|
return fastVisibleTest;
|
||||||
|
}
|
||||||
|
|
||||||
// Actors are hidden under shroud, but not under fog by default
|
// Actors are hidden under shroud, but not under fog by default
|
||||||
public bool IsVisible(Actor a)
|
public bool IsVisible(Actor a)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
public class World
|
public class World
|
||||||
{
|
{
|
||||||
|
static readonly Func<CPos, bool> FalsePredicate = cell => false;
|
||||||
internal readonly TraitDictionary traitDict = new TraitDictionary();
|
internal readonly TraitDictionary traitDict = new TraitDictionary();
|
||||||
readonly HashSet<Actor> actors = new HashSet<Actor>();
|
readonly HashSet<Actor> actors = new HashSet<Actor>();
|
||||||
readonly List<IEffect> effects = new List<IEffect>();
|
readonly List<IEffect> effects = new List<IEffect>();
|
||||||
@@ -53,6 +54,24 @@ namespace OpenRA
|
|||||||
public bool FogObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsVisible(p); }
|
public bool FogObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsVisible(p); }
|
||||||
public bool ShroudObscures(Actor a) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(a); }
|
public bool ShroudObscures(Actor a) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(a); }
|
||||||
public bool ShroudObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(p); }
|
public bool ShroudObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(p); }
|
||||||
|
|
||||||
|
public Func<CPos, bool> FogObscuresTest(CellRegion region)
|
||||||
|
{
|
||||||
|
var rp = RenderPlayer;
|
||||||
|
if (rp == null)
|
||||||
|
return FalsePredicate;
|
||||||
|
var predicate = rp.Shroud.IsVisibleTest(region);
|
||||||
|
return cell => !predicate(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Func<CPos, bool> ShroudObscuresTest(CellRegion region)
|
||||||
|
{
|
||||||
|
var rp = RenderPlayer;
|
||||||
|
if (rp == null)
|
||||||
|
return FalsePredicate;
|
||||||
|
var predicate = rp.Shroud.IsExploredTest(region);
|
||||||
|
return cell => !predicate(cell);
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsReplay
|
public bool IsReplay
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
readonly bool startsRevealed;
|
readonly bool startsRevealed;
|
||||||
readonly CPos[] footprint;
|
readonly CPos[] footprint;
|
||||||
|
readonly CellRegion footprintRegion;
|
||||||
|
|
||||||
readonly Lazy<IToolTip> tooltip;
|
readonly Lazy<IToolTip> tooltip;
|
||||||
readonly Lazy<Health> health;
|
readonly Lazy<Health> health;
|
||||||
@@ -44,6 +45,7 @@ namespace OpenRA.Mods.RA
|
|||||||
// Spawned actors (e.g. building husks) shouldn't be revealed
|
// Spawned actors (e.g. building husks) shouldn't be revealed
|
||||||
startsRevealed = info.StartsRevealed && !init.Contains<ParentActorInit>();
|
startsRevealed = info.StartsRevealed && !init.Contains<ParentActorInit>();
|
||||||
footprint = FootprintUtils.Tiles(init.self).ToArray();
|
footprint = FootprintUtils.Tiles(init.self).ToArray();
|
||||||
|
footprintRegion = CellRegion.BoundingRegion(init.world.Map.TileShape, footprint);
|
||||||
tooltip = Exts.Lazy(() => init.self.TraitsImplementing<IToolTip>().FirstOrDefault());
|
tooltip = Exts.Lazy(() => init.self.TraitsImplementing<IToolTip>().FirstOrDefault());
|
||||||
tooltip = Exts.Lazy(() => init.self.TraitsImplementing<IToolTip>().FirstOrDefault());
|
tooltip = Exts.Lazy(() => init.self.TraitsImplementing<IToolTip>().FirstOrDefault());
|
||||||
health = Exts.Lazy(() => init.self.TraitOrDefault<Health>());
|
health = Exts.Lazy(() => init.self.TraitOrDefault<Health>());
|
||||||
@@ -65,15 +67,7 @@ namespace OpenRA.Mods.RA
|
|||||||
VisibilityHash = 0;
|
VisibilityHash = 0;
|
||||||
foreach (var p in self.World.Players)
|
foreach (var p in self.World.Players)
|
||||||
{
|
{
|
||||||
var isVisible = false;
|
var isVisible = footprint.Any(p.Shroud.IsVisibleTest(footprintRegion));
|
||||||
foreach (var pos in footprint)
|
|
||||||
{
|
|
||||||
if (p.Shroud.IsVisible(pos))
|
|
||||||
{
|
|
||||||
isVisible = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
visible[p] = isVisible;
|
visible[p] = isVisible;
|
||||||
if (isVisible)
|
if (isVisible)
|
||||||
VisibilityHash += p.ClientIndex;
|
VisibilityHash += p.ClientIndex;
|
||||||
@@ -84,7 +78,7 @@ namespace OpenRA.Mods.RA
|
|||||||
foreach (var p in self.World.Players)
|
foreach (var p in self.World.Players)
|
||||||
{
|
{
|
||||||
visible[p] |= startsRevealed;
|
visible[p] |= startsRevealed;
|
||||||
p.PlayerActor.Trait<FrozenActorLayer>().Add(frozen[p] = new FrozenActor(self, footprint));
|
p.PlayerActor.Trait<FrozenActorLayer>().Add(frozen[p] = new FrozenActor(self, footprint, footprintRegion));
|
||||||
}
|
}
|
||||||
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
|||||||
@@ -65,33 +65,37 @@ namespace OpenRA.Mods.RA
|
|||||||
All = Top | Right | Bottom | Left
|
All = Top | Right | Bottom | Left
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShroudTile
|
struct ShroudTile
|
||||||
{
|
{
|
||||||
public readonly CPos Position;
|
|
||||||
public readonly float2 ScreenPosition;
|
public readonly float2 ScreenPosition;
|
||||||
public readonly int Variant;
|
public readonly byte Variant;
|
||||||
|
|
||||||
public Sprite Fog;
|
public Sprite Fog;
|
||||||
public Sprite Shroud;
|
public Sprite Shroud;
|
||||||
|
|
||||||
public ShroudTile(CPos position, float2 screenPosition, int variant)
|
public ShroudTile(float2 screenPosition, byte variant)
|
||||||
{
|
{
|
||||||
Position = position;
|
|
||||||
ScreenPosition = screenPosition;
|
ScreenPosition = screenPosition;
|
||||||
Variant = variant;
|
Variant = variant;
|
||||||
|
|
||||||
|
Fog = null;
|
||||||
|
Shroud = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly ShroudRendererInfo info;
|
readonly ShroudRendererInfo info;
|
||||||
readonly Sprite[] shroudSprites, fogSprites;
|
readonly Sprite[] shroudSprites, fogSprites;
|
||||||
readonly int[] spriteMap;
|
readonly byte[] spriteMap;
|
||||||
readonly CellLayer<ShroudTile> tiles;
|
readonly CellLayer<ShroudTile> tiles;
|
||||||
readonly int variantStride;
|
readonly byte variantStride;
|
||||||
readonly Map map;
|
readonly Map map;
|
||||||
readonly Edges notVisibleEdges;
|
readonly Edges notVisibleEdges;
|
||||||
|
|
||||||
|
bool clearedForNullShroud;
|
||||||
|
int lastShroudHash;
|
||||||
|
CellRegion updatedRegion;
|
||||||
|
|
||||||
PaletteReference fogPalette, shroudPalette;
|
PaletteReference fogPalette, shroudPalette;
|
||||||
int shroudHash;
|
|
||||||
|
|
||||||
public ShroudRenderer(World world, ShroudRendererInfo info)
|
public ShroudRenderer(World world, ShroudRendererInfo info)
|
||||||
{
|
{
|
||||||
@@ -101,17 +105,20 @@ namespace OpenRA.Mods.RA
|
|||||||
if ((info.OverrideFullFog == null) ^ (info.OverrideFullShroud == null))
|
if ((info.OverrideFullFog == null) ^ (info.OverrideFullShroud == null))
|
||||||
throw new ArgumentException("ShroudRenderer cannot define overrides for only one of shroud or fog!", "info");
|
throw new ArgumentException("ShroudRenderer cannot define overrides for only one of shroud or fog!", "info");
|
||||||
|
|
||||||
|
if (info.ShroudVariants.Length > byte.MaxValue)
|
||||||
|
throw new ArgumentException("ShroudRenderer cannot define this many shroud and fog variants.", "info");
|
||||||
|
|
||||||
|
if (info.Index.Length >= byte.MaxValue)
|
||||||
|
throw new ArgumentException("ShroudRenderer cannot define this many indexes for shroud directions.", "info");
|
||||||
|
|
||||||
this.info = info;
|
this.info = info;
|
||||||
map = world.Map;
|
map = world.Map;
|
||||||
|
|
||||||
tiles = new CellLayer<ShroudTile>(map);
|
tiles = new CellLayer<ShroudTile>(map);
|
||||||
|
|
||||||
// Force update on first render
|
|
||||||
shroudHash = -1;
|
|
||||||
|
|
||||||
// Load sprite variants
|
// Load sprite variants
|
||||||
var variantCount = info.ShroudVariants.Length;
|
var variantCount = info.ShroudVariants.Length;
|
||||||
variantStride = info.Index.Length + (info.OverrideFullShroud != null ? 1 : 0);
|
variantStride = (byte)(info.Index.Length + (info.OverrideFullShroud != null ? 1 : 0));
|
||||||
shroudSprites = new Sprite[variantCount * variantStride];
|
shroudSprites = new Sprite[variantCount * variantStride];
|
||||||
fogSprites = new Sprite[variantCount * variantStride];
|
fogSprites = new Sprite[variantCount * variantStride];
|
||||||
|
|
||||||
@@ -134,33 +141,33 @@ namespace OpenRA.Mods.RA
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mapping of shrouded directions -> sprite index
|
// Mapping of shrouded directions -> sprite index
|
||||||
spriteMap = new int[info.UseExtendedIndex ? 256 : 16];
|
spriteMap = new byte[(byte)(info.UseExtendedIndex ? Edges.All : Edges.AllCorners) + 1];
|
||||||
for (var i = 0; i < info.Index.Length; i++)
|
for (var i = 0; i < info.Index.Length; i++)
|
||||||
spriteMap[info.Index[i]] = i;
|
spriteMap[info.Index[i]] = (byte)i;
|
||||||
|
|
||||||
if (info.OverrideFullShroud != null)
|
if (info.OverrideFullShroud != null)
|
||||||
spriteMap[info.OverrideShroudIndex] = variantStride - 1;
|
spriteMap[info.OverrideShroudIndex] = (byte)(variantStride - 1);
|
||||||
|
|
||||||
notVisibleEdges = info.UseExtendedIndex ? Edges.AllSides : Edges.AllCorners;
|
notVisibleEdges = info.UseExtendedIndex ? Edges.AllSides : Edges.AllCorners;
|
||||||
}
|
}
|
||||||
|
|
||||||
Edges GetEdges(int x, int y, Func<int, int, bool> isVisible)
|
Edges GetEdges(CPos p, Func<CPos, bool> isVisible)
|
||||||
{
|
{
|
||||||
if (!isVisible(x, y))
|
if (!isVisible(p))
|
||||||
return notVisibleEdges;
|
return notVisibleEdges;
|
||||||
|
|
||||||
// If a side is shrouded then we also count the corners
|
// If a side is shrouded then we also count the corners
|
||||||
var u = Edges.None;
|
var u = Edges.None;
|
||||||
if (!isVisible(x, y - 1)) u |= Edges.Top;
|
if (!isVisible(p + new CVec(0, -1))) u |= Edges.Top;
|
||||||
if (!isVisible(x + 1, y)) u |= Edges.Right;
|
if (!isVisible(p + new CVec(1, 0))) u |= Edges.Right;
|
||||||
if (!isVisible(x, y + 1)) u |= Edges.Bottom;
|
if (!isVisible(p + new CVec(0, 1))) u |= Edges.Bottom;
|
||||||
if (!isVisible(x - 1, y)) u |= Edges.Left;
|
if (!isVisible(p + new CVec(-1, 0))) u |= Edges.Left;
|
||||||
|
|
||||||
var ucorner = u & Edges.AllCorners;
|
var ucorner = u & Edges.AllCorners;
|
||||||
if (!isVisible(x - 1, y - 1)) u |= Edges.TopLeft;
|
if (!isVisible(p + new CVec(-1, -1))) u |= Edges.TopLeft;
|
||||||
if (!isVisible(x + 1, y - 1)) u |= Edges.TopRight;
|
if (!isVisible(p + new CVec(1, -1))) u |= Edges.TopRight;
|
||||||
if (!isVisible(x + 1, y + 1)) u |= Edges.BottomRight;
|
if (!isVisible(p + new CVec(1, 1))) u |= Edges.BottomRight;
|
||||||
if (!isVisible(x - 1, y + 1)) u |= Edges.BottomLeft;
|
if (!isVisible(p + new CVec(-1, 1))) u |= Edges.BottomLeft;
|
||||||
|
|
||||||
// RA provides a set of frames for tiles with shrouded
|
// RA provides a set of frames for tiles with shrouded
|
||||||
// corners but unshrouded edges. We want to detect this
|
// corners but unshrouded edges. We want to detect this
|
||||||
@@ -171,19 +178,19 @@ namespace OpenRA.Mods.RA
|
|||||||
return info.UseExtendedIndex ? u ^ ucorner : u & Edges.AllCorners;
|
return info.UseExtendedIndex ? u ^ ucorner : u & Edges.AllCorners;
|
||||||
}
|
}
|
||||||
|
|
||||||
Edges GetObserverEdges(int x, int y)
|
Edges GetObserverEdges(CPos p)
|
||||||
{
|
{
|
||||||
var u = Edges.None;
|
var u = Edges.None;
|
||||||
if (!map.Contains(new CPos(x, y - 1))) u |= Edges.Top;
|
if (!map.Contains(p + new CVec(0, -1))) u |= Edges.Top;
|
||||||
if (!map.Contains(new CPos(x + 1, y))) u |= Edges.Right;
|
if (!map.Contains(p + new CVec(1, 0))) u |= Edges.Right;
|
||||||
if (!map.Contains(new CPos(x, y + 1))) u |= Edges.Bottom;
|
if (!map.Contains(p + new CVec(0, 1))) u |= Edges.Bottom;
|
||||||
if (!map.Contains(new CPos(x - 1, y))) u |= Edges.Left;
|
if (!map.Contains(p + new CVec(-1, 0))) u |= Edges.Left;
|
||||||
|
|
||||||
var ucorner = u & Edges.AllCorners;
|
var ucorner = u & Edges.AllCorners;
|
||||||
if (!map.Contains(new CPos(x - 1, y - 1))) u |= Edges.TopLeft;
|
if (!map.Contains(p + new CVec(-1, -1))) u |= Edges.TopLeft;
|
||||||
if (!map.Contains(new CPos(x + 1, y - 1))) u |= Edges.TopRight;
|
if (!map.Contains(p + new CVec(1, -1))) u |= Edges.TopRight;
|
||||||
if (!map.Contains(new CPos(x + 1, y + 1))) u |= Edges.BottomRight;
|
if (!map.Contains(p + new CVec(1, 1))) u |= Edges.BottomRight;
|
||||||
if (!map.Contains(new CPos(x - 1, y + 1))) u |= Edges.BottomLeft;
|
if (!map.Contains(p + new CVec(-1, 1))) u |= Edges.BottomLeft;
|
||||||
|
|
||||||
return info.UseExtendedIndex ? u ^ ucorner : u & Edges.AllCorners;
|
return info.UseExtendedIndex ? u ^ ucorner : u & Edges.AllCorners;
|
||||||
}
|
}
|
||||||
@@ -195,64 +202,84 @@ namespace OpenRA.Mods.RA
|
|||||||
foreach (var cell in CellRegion.Expand(w.Map.Cells, 1))
|
foreach (var cell in CellRegion.Expand(w.Map.Cells, 1))
|
||||||
{
|
{
|
||||||
var screen = wr.ScreenPosition(w.Map.CenterOfCell(cell));
|
var screen = wr.ScreenPosition(w.Map.CenterOfCell(cell));
|
||||||
var variant = Game.CosmeticRandom.Next(info.ShroudVariants.Length);
|
var variant = (byte)Game.CosmeticRandom.Next(info.ShroudVariants.Length);
|
||||||
tiles[cell] = new ShroudTile(cell, screen, variant);
|
tiles[cell] = new ShroudTile(screen, variant);
|
||||||
|
|
||||||
// Set the cells outside the border so they don't need to be touched again
|
// Set the cells outside the border so they don't need to be touched again
|
||||||
if (!map.Contains(cell))
|
if (!map.Contains(cell))
|
||||||
tiles[cell].Shroud = GetTile(notVisibleEdges, variant);
|
{
|
||||||
|
var shroudTile = tiles[cell];
|
||||||
|
shroudTile.Shroud = GetTile(shroudSprites, notVisibleEdges, variant);
|
||||||
|
tiles[cell] = shroudTile;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fogPalette = wr.Palette(info.FogPalette);
|
fogPalette = wr.Palette(info.FogPalette);
|
||||||
shroudPalette = wr.Palette(info.ShroudPalette);
|
shroudPalette = wr.Palette(info.ShroudPalette);
|
||||||
}
|
}
|
||||||
|
|
||||||
Sprite GetTile(Edges edges, int variant)
|
Sprite GetTile(Sprite[] sprites, Edges edges, int variant)
|
||||||
{
|
{
|
||||||
if (edges == Edges.None)
|
if (edges == Edges.None)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return shroudSprites[variant * variantStride + spriteMap[(byte)edges]];
|
return sprites[variant * variantStride + spriteMap[(byte)edges]];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update(Shroud shroud)
|
void Update(Shroud shroud, CellRegion region)
|
||||||
{
|
{
|
||||||
var hash = shroud != null ? shroud.Hash : 0;
|
if (shroud != null)
|
||||||
if (shroudHash == hash)
|
|
||||||
return;
|
|
||||||
|
|
||||||
shroudHash = hash;
|
|
||||||
if (shroud == null)
|
|
||||||
{
|
{
|
||||||
// Players with no shroud see the whole map so we only need to set the edges
|
// If the current shroud hasn't changed and we have already updated the specified area, we don't need to do anything.
|
||||||
foreach (var cell in map.Cells)
|
if (lastShroudHash == shroud.Hash && !clearedForNullShroud && updatedRegion.Contains(region))
|
||||||
{
|
return;
|
||||||
var t = tiles[cell];
|
|
||||||
var shrouded = GetObserverEdges(cell.X, cell.Y);
|
|
||||||
|
|
||||||
t.Shroud = shrouded != 0 ? shroudSprites[t.Variant * variantStride + spriteMap[(byte)shrouded]] : null;
|
lastShroudHash = shroud.Hash;
|
||||||
t.Fog = shrouded != 0 ? fogSprites[t.Variant * variantStride + spriteMap[(byte)shrouded]] : null;
|
clearedForNullShroud = false;
|
||||||
}
|
updatedRegion = region;
|
||||||
|
UpdateShroud(shroud);
|
||||||
}
|
}
|
||||||
else
|
else if (!clearedForNullShroud)
|
||||||
{
|
{
|
||||||
foreach (var cell in map.Cells)
|
// We need to clear any applied shroud.
|
||||||
{
|
clearedForNullShroud = true;
|
||||||
Func<int, int, bool> visibleUnderShroud = (x, y) => shroud.IsExplored(new CPos(x, y));
|
updatedRegion = new CellRegion(map.TileShape, new CPos(0, 0), new CPos(-1, -1));
|
||||||
Func<int, int, bool> visibleUnderFog = (x, y) => shroud.IsVisible(new CPos(x, y));
|
UpdateNullShroud();
|
||||||
var t = tiles[cell];
|
}
|
||||||
var shrouded = GetEdges(cell.X, cell.Y, visibleUnderShroud);
|
}
|
||||||
var fogged = GetEdges(cell.X, cell.Y, visibleUnderFog);
|
|
||||||
|
|
||||||
t.Shroud = shrouded != 0 ? shroudSprites[t.Variant * variantStride + spriteMap[(byte)shrouded]] : null;
|
void UpdateShroud(Shroud shroud)
|
||||||
t.Fog = fogged != 0 ? fogSprites[t.Variant * variantStride + spriteMap[(byte)fogged]] : null;
|
{
|
||||||
}
|
var visibleUnderShroud = shroud.IsExploredTest(updatedRegion);
|
||||||
|
var visibleUnderFog = shroud.IsVisibleTest(updatedRegion);
|
||||||
|
foreach (var cell in updatedRegion)
|
||||||
|
{
|
||||||
|
var shrouded = GetEdges(cell, visibleUnderShroud);
|
||||||
|
var fogged = GetEdges(cell, visibleUnderFog);
|
||||||
|
var shroudTile = tiles[cell];
|
||||||
|
var variant = shroudTile.Variant;
|
||||||
|
shroudTile.Shroud = GetTile(shroudSprites, shrouded, variant);
|
||||||
|
shroudTile.Fog = GetTile(fogSprites, fogged, variant);
|
||||||
|
tiles[cell] = shroudTile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateNullShroud()
|
||||||
|
{
|
||||||
|
foreach (var cell in map.Cells)
|
||||||
|
{
|
||||||
|
var edges = GetObserverEdges(cell);
|
||||||
|
var shroudTile = tiles[cell];
|
||||||
|
var variant = shroudTile.Variant;
|
||||||
|
shroudTile.Shroud = GetTile(shroudSprites, edges, variant);
|
||||||
|
shroudTile.Fog = GetTile(fogSprites, edges, variant);
|
||||||
|
tiles[cell] = shroudTile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RenderShroud(WorldRenderer wr, Shroud shroud)
|
public void RenderShroud(WorldRenderer wr, Shroud shroud)
|
||||||
{
|
{
|
||||||
Update(shroud);
|
Update(shroud, wr.Viewport.VisibleCells);
|
||||||
|
|
||||||
foreach (var cell in CellRegion.Expand(wr.Viewport.VisibleCells, 1))
|
foreach (var cell in CellRegion.Expand(wr.Viewport.VisibleCells, 1))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,9 +34,10 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public void Render(WorldRenderer wr)
|
public void Render(WorldRenderer wr)
|
||||||
{
|
{
|
||||||
|
var shroudObscured = world.ShroudObscuresTest(wr.Viewport.VisibleCells);
|
||||||
foreach (var cell in wr.Viewport.VisibleCells)
|
foreach (var cell in wr.Viewport.VisibleCells)
|
||||||
{
|
{
|
||||||
if (world.ShroudObscures(cell))
|
if (shroudObscured(cell))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var c = render[cell];
|
var c = render[cell];
|
||||||
|
|||||||
Reference in New Issue
Block a user