Skip to content

Commit

Permalink
Ignore unused frames when loading Sequences into Sheets.
Browse files Browse the repository at this point in the history
  • Loading branch information
pchote committed Apr 30, 2018
1 parent 40155cb commit 76b74b6
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 54 deletions.
7 changes: 4 additions & 3 deletions OpenRA.Game/Graphics/SpriteLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,13 @@ public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders, Shee
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)
Expand Down
129 changes: 78 additions & 51 deletions OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,62 @@ public DefaultSpriteSequence(ModData modData, TileSet tileSet, SpriteCache cache
var offset = LoadField(d, "Offset", float3.Zero);
var blendMode = LoadField(d, "BlendMode", BlendMode.Alpha);

Func<int, IEnumerable<int>> 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<int>();
for (var facing = 0; facing < Facings; facing++)
{
for (var frame = 0; frame < Length; frame++)
{
var i = frame * Facings + facing;
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))
{
Expand All @@ -165,36 +221,42 @@ public DefaultSpriteSequence(ModData modData, TileSet tileSet, SpriteCache cache
var subOffset = LoadField(sd, "Offset", float3.Zero);
var subFlipX = LoadField(sd, "FlipX", false);
var subFlipY = LoadField(sd, "FlipY", false);
var subLength = 0;

Func<int, IEnumerable<int>> 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<string>(d, "DepthSprite", null);
Expand All @@ -207,6 +269,9 @@ public DefaultSpriteSequence(ModData modData, TileSet tileSet, SpriteCache cache

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)
Expand Down Expand Up @@ -234,44 +299,6 @@ public DefaultSpriteSequence(ModData modData, TileSet tileSet, SpriteCache cache
}).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);
if (ShadowStart > 0)
boundSprites = boundSprites.Concat(SpriteBounds(sprites, Frames, ShadowStart, Facings, Length));
Expand Down

0 comments on commit 76b74b6

Please sign in to comment.