diff --git a/OpenRA.Game/Graphics/Sprite.cs b/OpenRA.Game/Graphics/Sprite.cs index 0ee1aa91dfe8..8646521c9281 100644 --- a/OpenRA.Game/Graphics/Sprite.cs +++ b/OpenRA.Game/Graphics/Sprite.cs @@ -50,13 +50,15 @@ public Sprite(Sheet sheet, Rectangle bounds, float zRamp, float3 offset, Texture public class SpriteWithSecondaryData : Sprite { + public readonly Sheet SecondarySheet; public readonly Rectangle SecondaryBounds; public readonly TextureChannel SecondaryChannel; public readonly float SecondaryTop, SecondaryLeft, SecondaryBottom, SecondaryRight; - public SpriteWithSecondaryData(Sprite s, Rectangle secondaryBounds, TextureChannel secondaryChannel) + public SpriteWithSecondaryData(Sprite s, Sheet secondarySheet, Rectangle secondaryBounds, TextureChannel secondaryChannel) : base(s.Sheet, s.Bounds, s.ZRamp, s.Offset, s.Channel, s.BlendMode) { + SecondarySheet = secondarySheet; SecondaryBounds = secondaryBounds; SecondaryChannel = secondaryChannel; SecondaryLeft = (float)Math.Min(secondaryBounds.Left, secondaryBounds.Right) / s.Sheet.Size.Width; diff --git a/OpenRA.Game/Graphics/SpriteLoader.cs b/OpenRA.Game/Graphics/SpriteLoader.cs index 648eb95da6e5..186e9addb71b 100644 --- a/OpenRA.Game/Graphics/SpriteLoader.cs +++ b/OpenRA.Game/Graphics/SpriteLoader.cs @@ -111,23 +111,6 @@ public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders, Shee return sprite; } } - - /// Returns all instances of sets of sprites with the given filename - public IEnumerable AllCached(string filename) - { - return sprites.GetOrAdd(filename); - } - - /// Loads and caches a new instance of sprites with the given filename - public Sprite[] Reload(string filename) - { - var sprite = FrameLoader.GetFrames(fileSystem, filename, loaders) - .Select(a => SheetBuilder.Add(a)) - .ToArray(); - - sprites.GetOrAdd(filename).Add(sprite); - return sprite; - } } public class FrameCache diff --git a/OpenRA.Game/Graphics/SpriteRenderer.cs b/OpenRA.Game/Graphics/SpriteRenderer.cs index 150820143136..5f412f90d2ea 100644 --- a/OpenRA.Game/Graphics/SpriteRenderer.cs +++ b/OpenRA.Game/Graphics/SpriteRenderer.cs @@ -62,26 +62,46 @@ int2 SetRenderStateForSprite(Sprite s) currentBlend = s.BlendMode; + // Check if the sheet (or secondary data sheet) have already been mapped var sheet = s.Sheet; var sheetIndex = 0; for (; sheetIndex < ns; sheetIndex++) if (sheets[sheetIndex] == sheet) break; - if (sheetIndex == ns) + var secondarySheetIndex = 0; + var ss = s as SpriteWithSecondaryData; + if (ss != null) { - if (sheetIndex == sheets.Length) - { - Flush(); - sheetIndex = 0; - } + var secondarySheet = ss.SecondarySheet; + for (; secondarySheetIndex < ns; secondarySheetIndex++) + if (sheets[secondarySheetIndex] == secondarySheet) + break; + } + + // Make sure that we have enough free samplers to map both if needed, otherwise flush + var needSamplers = (sheetIndex == ns ? 1 : 0) + (secondarySheetIndex == ns ? 1 : 0); + if (ns + needSamplers >= sheets.Length) + { + Flush(); + sheetIndex = 0; + if (ss != null) + secondarySheetIndex = 1; + } + if (sheetIndex >= ns) + { sheets[sheetIndex] = sheet; ns += 1; } - // TODO: Add support for secondary channels on different sheets - return new int2(sheetIndex, sheetIndex); + if (secondarySheetIndex >= ns && ss != null) + { + sheets[secondarySheetIndex] = ss.SecondarySheet; + ns += 1; + } + + return new int2(sheetIndex, secondarySheetIndex); } internal void DrawSprite(Sprite s, float3 location, float paletteTextureIndex, float3 size) diff --git a/OpenRA.Game/Graphics/Theater.cs b/OpenRA.Game/Graphics/Theater.cs index ed01fe9f8a23..924e7bb122ae 100644 --- a/OpenRA.Game/Graphics/Theater.cs +++ b/OpenRA.Game/Graphics/Theater.cs @@ -85,7 +85,7 @@ public Theater(TileSet tileset) // s and ss are guaranteed to use the same sheet // because of the custom terrain sheet allocation - s = new SpriteWithSecondaryData(s, ss.Bounds, ss.Channel); + s = new SpriteWithSecondaryData(s, s.Sheet, ss.Bounds, ss.Channel); } return s; diff --git a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs index 4073ef3a6dc5..37ff76c14eef 100644 --- a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs +++ b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs @@ -268,38 +268,21 @@ public DefaultSpriteSequence(ModData modData, TileSet tileSet, SpriteCache cache { var depthSpriteFrame = LoadField(d, "DepthSpriteFrame", 0); var depthOffset = LoadField(d, "DepthSpriteOffset", float2.Zero); - var depthSprites = cache.AllCached(depthSprite) - .Select(s => s[depthSpriteFrame]); + Func> getDepthFrame = _ => new int[] { depthSpriteFrame }; + var ds = cache[depthSprite, getDepthFrame][depthSpriteFrame]; sprites = sprites.Select(s => { if (s == null) return null; - // The depth sprite must live on the same sheet as the main sprite - var ds = depthSprites.FirstOrDefault(dss => dss.Sheet == s.Sheet); - if (ds == null) - { - // The sequence has probably overflowed onto a new sheet. - // Allocating a new depth sprite on this sheet will almost certainly work - ds = cache.Reload(depthSprite)[depthSpriteFrame]; - depthSprites = cache.AllCached(depthSprite) - .Select(ss => ss[depthSpriteFrame]); - - // If that doesn't work then we may be referencing a cached sprite from an earlier sheet - // TODO: We could try and reallocate the main sprite, but that requires more complicated code and a perf hit - // We'll only cross that bridge if this becomes a problem in reality - if (ds.Sheet != s.Sheet) - throw new SheetOverflowException("Cross-sheet depth sprite reference: {0}.{1}: {2}"); - } - var cw = (ds.Bounds.Left + ds.Bounds.Right) / 2 + (int)(s.Offset.X + depthOffset.X); var ch = (ds.Bounds.Top + ds.Bounds.Bottom) / 2 + (int)(s.Offset.Y + depthOffset.Y); var w = s.Bounds.Width / 2; var h = s.Bounds.Height / 2; var r = Rectangle.FromLTRB(cw - w, ch - h, cw + w, ch + h); - return new SpriteWithSecondaryData(s, r, ds.Channel); + return new SpriteWithSecondaryData(s, ds.Sheet, r, ds.Channel); }).ToArray(); }