From 041431d966bc0afc2f6c2b5fdbdf83b6d18b1b66 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Tue, 5 Dec 2017 16:26:44 +0000 Subject: [PATCH] Add ISpriteSequence.Bounds and Animation(WithOffset).ScreenBounds. The bounds define the smallest Rectangle that covers all frames within a sequence. --- OpenRA.Game/Graphics/Animation.cs | 12 ++++++++ OpenRA.Game/Graphics/AnimationWithOffset.cs | 9 ++++++ OpenRA.Game/Graphics/SequenceProvider.cs | 3 +- .../Graphics/DefaultSpriteSequence.cs | 30 +++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/OpenRA.Game/Graphics/Animation.cs b/OpenRA.Game/Graphics/Animation.cs index f93118e353..3e2880ef07 100644 --- a/OpenRA.Game/Graphics/Animation.cs +++ b/OpenRA.Game/Graphics/Animation.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; using OpenRA.Support; @@ -66,6 +67,17 @@ namespace OpenRA.Graphics return new IRenderable[] { imageRenderable }; } + public Rectangle ScreenBounds(WorldRenderer wr, WPos pos, WVec offset, float scale) + { + var xy = wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset); + var cb = CurrentSequence.Bounds; + return Rectangle.FromLTRB( + xy.X + (int)(cb.Left * scale), + xy.Y + (int)(cb.Top * scale), + xy.X + (int)(cb.Right * scale), + xy.Y + (int)(cb.Bottom * scale)); + } + public IEnumerable Render(WPos pos, PaletteReference palette) { return Render(pos, WVec.Zero, 0, palette, 1f); diff --git a/OpenRA.Game/Graphics/AnimationWithOffset.cs b/OpenRA.Game/Graphics/AnimationWithOffset.cs index 47e44008cc..3c191b011a 100644 --- a/OpenRA.Game/Graphics/AnimationWithOffset.cs +++ b/OpenRA.Game/Graphics/AnimationWithOffset.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Drawing; namespace OpenRA.Graphics { @@ -44,6 +45,14 @@ namespace OpenRA.Graphics return Animation.Render(center, offset, z, pal, scale); } + public Rectangle ScreenBounds(Actor self, WorldRenderer wr, float scale) + { + var center = self.CenterPosition; + var offset = OffsetFunc != null ? OffsetFunc() : WVec.Zero; + + return Animation.ScreenBounds(wr, center, offset, scale); + } + public static implicit operator AnimationWithOffset(Animation a) { return new AnimationWithOffset(a, null, null, null); diff --git a/OpenRA.Game/Graphics/SequenceProvider.cs b/OpenRA.Game/Graphics/SequenceProvider.cs index 93678d96ec..5333ccd107 100644 --- a/OpenRA.Game/Graphics/SequenceProvider.cs +++ b/OpenRA.Game/Graphics/SequenceProvider.cs @@ -11,7 +11,7 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.Drawing; using OpenRA.FileSystem; namespace OpenRA.Graphics @@ -31,6 +31,7 @@ namespace OpenRA.Graphics int ShadowStart { get; } int ShadowZOffset { get; } int[] Frames { get; } + Rectangle Bounds { get; } Sprite GetSprite(int frame); Sprite GetSprite(int frame, int facing); diff --git a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs index c1335ef449..8619732db7 100644 --- a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs +++ b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs @@ -90,6 +90,7 @@ namespace OpenRA.Mods.Common.Graphics public int ShadowStart { get; private set; } public int ShadowZOffset { get; private set; } public int[] Frames { get; private set; } + public Rectangle Bounds { get; private set; } protected virtual string GetSpriteSrc(ModData modData, TileSet tileSet, string sequence, string animation, string sprite, Dictionary d) { @@ -270,6 +271,17 @@ namespace OpenRA.Mods.Common.Graphics "{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)); + + if (boundSprites.Any()) + { + Bounds = boundSprites.First(); + foreach (var b in boundSprites.Skip(1)) + Bounds = Rectangle.Union(Bounds, b); + } } catch (FormatException f) { @@ -277,6 +289,24 @@ namespace OpenRA.Mods.Common.Graphics } } + /// Returns the bounds of all of the sprites that can appear in this animation + static IEnumerable SpriteBounds(Sprite[] sprites, int[] frames, int start, int facings, int length) + { + for (var facing = 0; facing < facings; facing++) + { + for (var frame = 0; frame < length; frame++) + { + var i = frame * facings + facing; + var s = frames != null ? sprites[frames[i]] : sprites[start + i]; + if (!s.Bounds.IsEmpty) + yield return new Rectangle( + (int)(s.Offset.X - s.Size.X / 2), + (int)(s.Offset.Y - s.Size.Y / 2), + s.Bounds.Width, s.Bounds.Height); + } + } + } + public Sprite GetSprite(int frame) { return GetSprite(Start, frame, 0);