diff --git a/OpenRA.Game/Graphics/SheetBuilder.cs b/OpenRA.Game/Graphics/SheetBuilder.cs index 1340e501ce..c72a6f71ac 100644 --- a/OpenRA.Game/Graphics/SheetBuilder.cs +++ b/OpenRA.Game/Graphics/SheetBuilder.cs @@ -8,68 +8,64 @@ */ #endregion +using System; using System.Drawing; namespace OpenRA.Graphics { + public class SheetOverflowException : Exception + { + public SheetOverflowException() + : base("Sprite sequence spans multiple sheets.\n"+ + "This should be considered as a bug, but you "+ + "can increase the Graphics.SheetSize setting "+ + "to temporarily avoid the problem.") {} + } + public class SheetBuilder { + Sheet current; + int rowHeight = 0; + Point p; + TextureChannel channel; + Sheet NewSheet() { return new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize)); } + internal SheetBuilder(TextureChannel ch) { - current = null; - rowHeight = 0; - channel = null; - initialChannel = ch; + current = NewSheet(); + channel = ch; } - public Sprite Add(byte[] src, Size size) + public Sprite Add(byte[] src, Size size, bool allowSheetOverflow) { - Sprite rect = Allocate(size); + Sprite rect = Allocate(size, allowSheetOverflow); Util.FastCopyIntoChannel(rect, src); return rect; } - public Sprite Add(Size size, byte paletteIndex) + public Sprite Add(Size size, byte paletteIndex, bool allowSheetOverflow) { byte[] data = new byte[size.Width * size.Height]; for (int i = 0; i < data.Length; i++) data[i] = paletteIndex; - return Add(data, size); + return Add(data, size, allowSheetOverflow); } - Sheet NewSheet() { return new Sheet(new Size( Renderer.SheetSize, Renderer.SheetSize ) ); } - - Sheet current = null; - int rowHeight = 0; - Point p; - TextureChannel? channel = null; - TextureChannel initialChannel; - - TextureChannel? NextChannel(TextureChannel? t) + TextureChannel? NextChannel(TextureChannel t) { - if (t == null) - return initialChannel; - - switch (t.Value) + switch (t) { case TextureChannel.Red: return TextureChannel.Green; case TextureChannel.Green: return TextureChannel.Blue; case TextureChannel.Blue: return TextureChannel.Alpha; - case TextureChannel.Alpha: return null; - + case TextureChannel.Alpha: default: return null; } } - public Sprite Allocate(Size imageSize) + public Sprite Allocate(Size imageSize, bool allowSheetOverflow) { - if (current == null) - { - current = NewSheet(); - channel = NextChannel(null); - } - if (imageSize.Width + p.X > current.Size.Width) { p = new Point(0, p.Y + rowHeight); @@ -81,18 +77,23 @@ namespace OpenRA.Graphics if (p.Y + imageSize.Height > current.Size.Height) { - - if (null == (channel = NextChannel(channel))) + var next = NextChannel(channel); + if (next == null) { - current = NewSheet(); - channel = NextChannel(channel); + if (!allowSheetOverflow) + throw new SheetOverflowException(); + + current = new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize)); + channel = TextureChannel.Red; } + else + channel = next.Value; rowHeight = imageSize.Height; p = new Point(0,0); } - Sprite rect = new Sprite(current, new Rectangle(p, imageSize), channel.Value); + Sprite rect = new Sprite(current, new Rectangle(p, imageSize), channel); current.MakeDirty(); p.X += imageSize.Width; diff --git a/OpenRA.Game/Graphics/SpriteFont.cs b/OpenRA.Game/Graphics/SpriteFont.cs index 974792d5dc..89ad06964f 100644 --- a/OpenRA.Game/Graphics/SpriteFont.cs +++ b/OpenRA.Game/Graphics/SpriteFont.cs @@ -96,9 +96,8 @@ namespace OpenRA.Graphics face.LoadGlyph(index, LoadFlags.Default, LoadTarget.Normal); face.Glyph.RenderGlyph(RenderMode.Normal); - var s = builder.Allocate( - new Size((int)face.Glyph.Metrics.Width >> 6, - (int)face.Glyph.Metrics.Height >> 6)); + var size = new Size((int)face.Glyph.Metrics.Width >> 6, (int)face.Glyph.Metrics.Height >> 6); + var s = builder.Allocate(size, true); var g = new GlyphInfo { diff --git a/OpenRA.Game/Graphics/SpriteLoader.cs b/OpenRA.Game/Graphics/SpriteLoader.cs index 79cbe5f08a..97134fc646 100644 --- a/OpenRA.Game/Graphics/SpriteLoader.cs +++ b/OpenRA.Game/Graphics/SpriteLoader.cs @@ -35,12 +35,12 @@ namespace OpenRA.Graphics if (ImageCount == 0) { var shp = new ShpTSReader(FileSystem.OpenWithExts(filename, exts)); - return shp.Select(a => Game.modData.SheetBuilder.Add(a.Image, shp.Size)).ToArray(); + return shp.Select(a => SheetBuilder.Add(a.Image, shp.Size, true)).ToArray(); } else { var shp = new ShpReader(FileSystem.OpenWithExts(filename, exts)); - return shp.Frames.Select(a => SheetBuilder.Add(a.Image, shp.Size)).ToArray(); + return shp.Frames.Select(a => SheetBuilder.Add(a.Image, shp.Size, true)).ToArray(); } } diff --git a/OpenRA.Game/Graphics/TerrainRenderer.cs b/OpenRA.Game/Graphics/TerrainRenderer.cs index b30666a525..02ff8aced4 100644 --- a/OpenRA.Game/Graphics/TerrainRenderer.cs +++ b/OpenRA.Game/Graphics/TerrainRenderer.cs @@ -30,8 +30,8 @@ namespace OpenRA.Graphics this.map = world.Map; var tileSize = new Size(Game.CellSize, Game.CellSize); - var tileMapping = new Cache, Sprite>(x => - Game.modData.SheetBuilder.Add(world.TileSet.GetBytes(x), tileSize)); + var tileMapping = new Cache, Sprite>( + x => Game.modData.SheetBuilder.Add(world.TileSet.GetBytes(x), tileSize, false)); terrainSheet = tileMapping[map.MapTiles.Value[map.Bounds.Left, map.Bounds.Top]].sheet; var terrainPalette = wr.Palette("terrain").Index; @@ -44,9 +44,6 @@ namespace OpenRA.Graphics var tile = tileMapping[map.MapTiles.Value[i, j]]; Util.FastCreateQuad(vertices, Game.CellSize * new float2(i, j), tile, terrainPalette, nv, tile.size); nv += 4; - - if (tileMapping[map.MapTiles.Value[i, j]].sheet != terrainSheet) - throw new InvalidOperationException("Terrain sprites span multiple sheets. Try increasing Game.Settings.Graphics.SheetSize."); } vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(vertices.Length); diff --git a/OpenRA.Mods.RA/Bridge.cs b/OpenRA.Mods.RA/Bridge.cs index 495e006ce6..d7fda56878 100644 --- a/OpenRA.Mods.RA/Bridge.cs +++ b/OpenRA.Mods.RA/Bridge.cs @@ -99,9 +99,9 @@ namespace OpenRA.Mods.RA if (cachedTileset != self.World.Map.Tileset) { cachedTileset = self.World.Map.Tileset; + var tileSize = new Size(Game.CellSize, Game.CellSize); sprites = new Cache, Sprite>( - x => Game.modData.SheetBuilder.Add(self.World.TileSet.GetBytes(x), - new Size(Game.CellSize, Game.CellSize))); + x => Game.modData.SheetBuilder.Add(self.World.TileSet.GetBytes(x), tileSize, true)); } // Cache templates and tiles for the different states