From bf4b91741afb06fe678c1716343f39f632376cf7 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Tue, 1 May 2018 19:39:31 +0100 Subject: [PATCH] Ignore unused frames when loading Sequences into Sheets. --- OpenRA.Game/Graphics/SpriteLoader.cs | 7 +- .../Graphics/DefaultSpriteSequence.cs | 141 +++++++++++------- 2 files changed, 91 insertions(+), 57 deletions(-) diff --git a/OpenRA.Game/Graphics/SpriteLoader.cs b/OpenRA.Game/Graphics/SpriteLoader.cs index 5bad9d2da6..648eb95da6 100644 --- a/OpenRA.Game/Graphics/SpriteLoader.cs +++ b/OpenRA.Game/Graphics/SpriteLoader.cs @@ -87,12 +87,13 @@ namespace OpenRA.Graphics allSprites.Add(sprite); } + // HACK: The sequency code relies on side-effects from getUsedFrames + var indices = getUsedFrames != null ? getUsedFrames(sprite.Length) : + Enumerable.Range(0, sprite.Length); + // Load any unused frames into the SheetBuilder if (unloaded != null) { - var indices = getUsedFrames != null ? getUsedFrames(sprite.Length) : - Enumerable.Range(0, sprite.Length); - foreach (var i in indices) { if (unloaded[i] != null) diff --git a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs index 42535b71eb..3e5422b0b1 100644 --- a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs +++ b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs @@ -79,6 +79,7 @@ namespace OpenRA.Mods.Common.Graphics protected readonly ISpriteSequenceLoader Loader; + readonly string sequence; public string Name { get; private set; } public int Start { get; private set; } public int Length { get; private set; } @@ -118,6 +119,7 @@ namespace OpenRA.Mods.Common.Graphics public DefaultSpriteSequence(ModData modData, TileSet tileSet, SpriteCache cache, ISpriteSequenceLoader loader, string sequence, string animation, MiniYaml info) { + this.sequence = sequence; Name = animation; Loader = loader; var d = info.ToDictionary(); @@ -152,6 +154,64 @@ namespace OpenRA.Mods.Common.Graphics var offset = LoadField(d, "Offset", float3.Zero); var blendMode = LoadField(d, "BlendMode", BlendMode.Alpha); + Func> getUsedFrames = frameCount => + { + MiniYaml length; + if (d.TryGetValue("Length", out length) && length.Value == "*") + Length = frameCount - Start; + else + Length = LoadField(d, "Length", 1); + + // Plays the animation forwards, and then in reverse + if (LoadField(d, "Reverses", false)) + { + var frames = Frames ?? Exts.MakeArray(Length, i => Start + i); + Frames = frames.Concat(frames.Skip(1).Take(frames.Length - 2).Reverse()).ToArray(); + Length = 2 * Length - 2; + } + + Stride = LoadField(d, "Stride", Length); + + if (Length > Stride) + throw new InvalidOperationException( + "{0}: Sequence {1}.{2}: Length must be <= stride" + .F(info.Nodes[0].Location, sequence, animation)); + + if (Frames != null && Length > Frames.Length) + throw new InvalidOperationException( + "{0}: Sequence {1}.{2}: Length must be <= Frames.Length" + .F(info.Nodes[0].Location, sequence, animation)); + + if (Start < 0 || Start + Facings * Stride > frameCount) + throw new InvalidOperationException( + "{5}: Sequence {0}.{1} uses frames [{2}..{3}], but only 0..{4} actually exist" + .F(sequence, animation, Start, Start + Facings * Stride - 1, frameCount - 1, + info.Nodes[0].Location)); + + if (ShadowStart + Facings * Stride > frameCount) + throw new InvalidOperationException( + "{5}: Sequence {0}.{1}'s shadow frames use frames [{2}..{3}], but only [0..{4}] actually exist" + .F(sequence, animation, ShadowStart, ShadowStart + Facings * Stride - 1, frameCount - 1, + info.Nodes[0].Location)); + + var usedFrames = new List(); + for (var facing = 0; facing < Facings; facing++) + { + for (var frame = 0; frame < Length; frame++) + { + var i = transpose ? (frame % Length) * Facings + facing : + (facing * Stride) + (frame % Length); + + usedFrames.Add(Frames != null ? Frames[i] : Start + i); + } + } + + if (ShadowStart >= 0) + return usedFrames.Concat(usedFrames.Select(i => i + ShadowStart - Start)); + + return usedFrames; + }; + MiniYaml combine; if (d.TryGetValue("Combine", out combine)) { @@ -165,36 +225,42 @@ namespace OpenRA.Mods.Common.Graphics var subOffset = LoadField(sd, "Offset", float3.Zero); var subFlipX = LoadField(sd, "FlipX", false); var subFlipY = LoadField(sd, "FlipY", false); + var subLength = 0; + + Func> subGetUsedFrames = subFrameCount => + { + MiniYaml subLengthYaml; + if (sd.TryGetValue("Length", out subLengthYaml) && subLengthYaml.Value == "*") + subLength = subFrameCount - subStart; + else + subLength = LoadField(sd, "Length", 1); + + return Enumerable.Range(subStart, subLength); + }; var subSrc = GetSpriteSrc(modData, tileSet, sequence, animation, sub.Key, sd); - var subSprites = cache[subSrc].Select( - s => new Sprite(s.Sheet, + var subSprites = cache[subSrc, subGetUsedFrames].Select( + s => s != null ? new Sprite(s.Sheet, FlipRectangle(s.Bounds, subFlipX, subFlipY), ZRamp, new float3(subFlipX ? -s.Offset.X : s.Offset.X, subFlipY ? -s.Offset.Y : s.Offset.Y, s.Offset.Z) + subOffset + offset, - s.Channel, blendMode)); - - var subLength = 0; - MiniYaml subLengthYaml; - if (sd.TryGetValue("Length", out subLengthYaml) && subLengthYaml.Value == "*") - subLength = subSprites.Count() - subStart; - else - subLength = LoadField(sd, "Length", 1); + s.Channel, blendMode) : null); combined = combined.Concat(subSprites.Skip(subStart).Take(subLength)); } sprites = combined.ToArray(); + getUsedFrames(sprites.Length); } else { // Apply offset to each sprite in the sequence // Different sequences may apply different offsets to the same frame var src = GetSpriteSrc(modData, tileSet, sequence, animation, info.Value, d); - sprites = cache[src].Select( - s => new Sprite(s.Sheet, + sprites = cache[src, getUsedFrames].Select( + s => s != null ? new Sprite(s.Sheet, FlipRectangle(s.Bounds, flipX, flipY), ZRamp, new float3(flipX ? -s.Offset.X : s.Offset.X, flipY ? -s.Offset.Y : s.Offset.Y, s.Offset.Z) + offset, - s.Channel, blendMode)).ToArray(); + s.Channel, blendMode) : null).ToArray(); } var depthSprite = LoadField(d, "DepthSprite", null); @@ -207,6 +273,9 @@ namespace OpenRA.Mods.Common.Graphics 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) @@ -234,44 +303,6 @@ namespace OpenRA.Mods.Common.Graphics }).ToArray(); } - MiniYaml length; - if (d.TryGetValue("Length", out length) && length.Value == "*") - Length = sprites.Length - Start; - else - Length = LoadField(d, "Length", 1); - - // Plays the animation forwards, and then in reverse - if (LoadField(d, "Reverses", false)) - { - var frames = Frames ?? Exts.MakeArray(Length, i => Start + i); - Frames = frames.Concat(frames.Skip(1).Take(frames.Length - 2).Reverse()).ToArray(); - Length = 2 * Length - 2; - } - - Stride = LoadField(d, "Stride", Length); - - if (Length > Stride) - throw new InvalidOperationException( - "{0}: Sequence {1}.{2}: Length must be <= stride" - .F(info.Nodes[0].Location, sequence, animation)); - - if (Frames != null && Length > Frames.Length) - throw new InvalidOperationException( - "{0}: Sequence {1}.{2}: Length must be <= Frames.Length" - .F(info.Nodes[0].Location, sequence, animation)); - - if (Start < 0 || Start + Facings * Stride > sprites.Length) - throw new InvalidOperationException( - "{5}: Sequence {0}.{1} uses frames [{2}..{3}], but only 0..{4} actually exist" - .F(sequence, animation, Start, Start + Facings * Stride - 1, sprites.Length - 1, - info.Nodes[0].Location)); - - if (ShadowStart + Facings * Stride > sprites.Length) - throw new InvalidOperationException( - "{5}: Sequence {0}.{1}'s shadow frames use frames [{2}..{3}], but only [0..{4}] actually exist" - .F(sequence, animation, ShadowStart, ShadowStart + Facings * Stride - 1, sprites.Length - 1, - info.Nodes[0].Location)); - var boundSprites = SpriteBounds(sprites, Frames, Start, Facings, Length, Stride, transpose); if (ShadowStart > 0) boundSprites = boundSprites.Concat(SpriteBounds(sprites, Frames, ShadowStart, Facings, Length, Stride, transpose)); @@ -332,10 +363,12 @@ namespace OpenRA.Mods.Common.Graphics var i = transpose ? (frame % Length) * Facings + f : (f * Stride) + (frame % Length); - if (Frames != null) - return sprites[Frames[i]]; + var j = Frames != null ? Frames[i] : start + i; + if (sprites[j] == null) + throw new InvalidOperationException("Attempted to query unloaded sprite from {0}.{1}".F(Name, sequence) + + " start={2} frame={3} facing={4}".F(start, frame, facing)); - return sprites[start + i]; + return sprites[j]; } } }