diff --git a/OpenRA.FileFormats/Map/TileSet.cs b/OpenRA.FileFormats/Map/TileSet.cs index e6d973b09d..621a47bd26 100644 --- a/OpenRA.FileFormats/Map/TileSet.cs +++ b/OpenRA.FileFormats/Map/TileSet.cs @@ -39,17 +39,17 @@ namespace OpenRA.FileFormats public bool PickAny; public string Category; - [FieldLoader.LoadUsing( "LoadTiles" )] + [FieldLoader.LoadUsing("LoadTiles")] public Dictionary Tiles = new Dictionary(); public TileTemplate() {} - public TileTemplate(MiniYaml my) { FieldLoader.Load( this, my ); } + public TileTemplate(MiniYaml my) { FieldLoader.Load(this, my); } - static object LoadTiles( MiniYaml y ) + static object LoadTiles(MiniYaml y) { return y.NodesDict["Tiles"].NodesDict.ToDictionary( t => byte.Parse(t.Key), - t => t.Value.Value ); + t => t.Value.Value); } static readonly string[] Fields = { "Id", "Image", "Size", "PickAny" }; @@ -60,12 +60,14 @@ namespace OpenRA.FileFormats foreach (var field in Fields) { FieldInfo f = this.GetType().GetField(field); - if (f.GetValue(this) == null) continue; - root.Add( new MiniYamlNode( field, FieldSaver.FormatValue( this, f ) ) ); + if (f.GetValue(this) == null) + continue; + + root.Add(new MiniYamlNode(field, FieldSaver.FormatValue(this, f))); } - root.Add( new MiniYamlNode( "Tiles", null, - Tiles.Select( x => new MiniYamlNode( x.Key.ToString(), x.Value ) ).ToList() ) ); + root.Add(new MiniYamlNode("Tiles", null, + Tiles.Select(x => new MiniYamlNode(x.Key.ToString(), x.Value)).ToList())); return new MiniYaml(null, root); } @@ -90,9 +92,9 @@ namespace OpenRA.FileFormats public TileSet() {} - public TileSet( string filepath ) + public TileSet(string filepath) { - var yaml = MiniYaml.DictFromFile( filepath ); + var yaml = MiniYaml.DictFromFile(filepath); // General info FieldLoader.Load(this, yaml["General"]); @@ -110,7 +112,7 @@ namespace OpenRA.FileFormats { foreach (var t in Templates) if (t.Value.Data == null) - using( var s = FileSystem.OpenWithExts(t.Value.Image, Extensions) ) + using (var s = FileSystem.OpenWithExts(t.Value.Image, Extensions)) t.Value.Data = new Terrain(s, TileSize); } @@ -122,33 +124,35 @@ namespace OpenRA.FileFormats foreach (var field in fields) { FieldInfo f = this.GetType().GetField(field); - if (f.GetValue(this) == null) continue; - gen.Add( new MiniYamlNode( field, FieldSaver.FormatValue( this, f ) ) ); + if (f.GetValue(this) == null) + continue; + + gen.Add(new MiniYamlNode(field, FieldSaver.FormatValue(this, f))); } - root.Add( new MiniYamlNode( "General", null, gen ) ); + root.Add(new MiniYamlNode("General", null, gen)); - root.Add( new MiniYamlNode( "Terrain", null, - Terrain.Select( t => new MiniYamlNode( - "TerrainType@{0}".F( t.Value.Type ), - t.Value.Save() ) ).ToList() ) ); + root.Add(new MiniYamlNode( "Terrain", null, + Terrain.Select(t => new MiniYamlNode( + "TerrainType@{0}".F(t.Value.Type), + t.Value.Save())).ToList())); - root.Add( new MiniYamlNode( "Templates", null, - Templates.Select( t => new MiniYamlNode( - "Template@{0}".F( t.Value.Id ), - t.Value.Save() ) ).ToList() ) ); + root.Add(new MiniYamlNode("Templates", null, + Templates.Select(t => new MiniYamlNode( + "Template@{0}".F(t.Value.Id), + t.Value.Save())).ToList())); root.WriteToFile(filepath); } public byte[] GetBytes(TileReference r) { TileTemplate tile; - if( Templates.TryGetValue( r.type, out tile ) ) - return tile.Data.TileBitmapBytes[ r.index ]; + if (Templates.TryGetValue(r.type, out tile)) + return tile.Data.TileBitmapBytes[r.index]; - byte[] missingTile = new byte[ TileSize * TileSize ]; - for( int i = 0 ; i < missingTile.Length ; i++ ) - missingTile[ i ] = 0x36; + byte[] missingTile = new byte[TileSize*TileSize]; + for (var i = 0; i < missingTile.Length; i++) + missingTile[i] = 0x36; return missingTile; } @@ -159,6 +163,7 @@ namespace OpenRA.FileFormats string ret; if (!tt.TryGetValue(r.index, out ret)) return "Clear"; // Default walkable + return ret; } diff --git a/OpenRA.Game/Graphics/Animation.cs b/OpenRA.Game/Graphics/Animation.cs index d614fa82cf..99fa370093 100644 --- a/OpenRA.Game/Graphics/Animation.cs +++ b/OpenRA.Game/Graphics/Animation.cs @@ -24,15 +24,13 @@ namespace OpenRA.Graphics public string Name { get { return name; } } - public Animation( string name ) - : this( name, () => 0 ) - { - } + public Animation(string name) + : this(name, () => 0) {} - public Animation( string name, Func facingFunc ) + public Animation(string name, Func facingFunc) { this.name = name.ToLowerInvariant(); - this.tickFunc = () => { }; + this.tickFunc = () => {}; this.facingFunc = facingFunc; } @@ -46,12 +44,12 @@ namespace OpenRA.Graphics } } - public void Play( string sequenceName ) + public void Play(string sequenceName) { PlayThen(sequenceName, null); } - public void PlayRepeating( string sequenceName ) + public void PlayRepeating(string sequenceName) { backwards = false; tickAlways = false; @@ -75,16 +73,16 @@ namespace OpenRA.Graphics return true; } - public void PlayThen( string sequenceName, Action after ) + public void PlayThen(string sequenceName, Action after) { backwards = false; tickAlways = false; - CurrentSequence = SequenceProvider.GetSequence( name, sequenceName ); + CurrentSequence = SequenceProvider.GetSequence(name, sequenceName); frame = 0; tickFunc = () => { ++frame; - if( frame >= CurrentSequence.Length ) + if (frame >= CurrentSequence.Length) { frame = CurrentSequence.Length - 1; tickFunc = () => { }; @@ -99,11 +97,11 @@ namespace OpenRA.Graphics backwards = true; } - public void PlayFetchIndex( string sequenceName, Func func ) + public void PlayFetchIndex(string sequenceName, Func func) { backwards = false; tickAlways = true; - CurrentSequence = SequenceProvider.GetSequence( name, sequenceName ); + CurrentSequence = SequenceProvider.GetSequence(name, sequenceName); frame = func(); tickFunc = () => frame = func(); } @@ -113,19 +111,19 @@ namespace OpenRA.Graphics public void Tick() { - Tick( 40 ); // tick one frame + Tick(40); // tick one frame } - public bool HasSequence(string seq) { return SequenceProvider.HasSequence( name, seq ); } + public bool HasSequence(string seq) { return SequenceProvider.HasSequence(name, seq); } - public void Tick( int t ) + public void Tick(int t) { - if( tickAlways ) + if (tickAlways) tickFunc(); else { timeUntilNextFrame -= t; - while( timeUntilNextFrame <= 0 ) + while (timeUntilNextFrame <= 0) { tickFunc(); timeUntilNextFrame += CurrentSequence != null ? CurrentSequence.Tick : 40; // 25 fps == 40 ms @@ -145,9 +143,9 @@ namespace OpenRA.Graphics } } - public Sequence GetSequence( string sequenceName ) + public Sequence GetSequence(string sequenceName) { - return SequenceProvider.GetSequence( name, sequenceName ); + return SequenceProvider.GetSequence(name, sequenceName); } } } diff --git a/OpenRA.Game/Graphics/Sheet.cs b/OpenRA.Game/Graphics/Sheet.cs index 2e7277f580..a390d218be 100644 --- a/OpenRA.Game/Graphics/Sheet.cs +++ b/OpenRA.Game/Graphics/Sheet.cs @@ -9,6 +9,7 @@ #endregion using System.Drawing; +using System.Drawing.Imaging; using OpenRA.FileFormats; using OpenRA.FileFormats.Graphics; @@ -16,21 +17,44 @@ namespace OpenRA.Graphics { public class Sheet { - Bitmap bitmap; ITexture texture; bool dirty; - byte[] data; + public byte[] Data { get; private set; } public readonly Size Size; public Sheet(Size size) { Size = size; + Data = new byte[4*Size.Width*Size.Height]; } public Sheet(string filename) { - bitmap = (Bitmap)Image.FromStream(FileSystem.Open(filename)); + var bitmap = (Bitmap)Image.FromStream(FileSystem.Open(filename)); Size = bitmap.Size; + + Data = new byte[4*Size.Width*Size.Height]; + var b = bitmap.LockBits(bitmap.Bounds(), + ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + + unsafe + { + int* c = (int*)b.Scan0; + + for (var x = 0; x < Size.Width; x++) + for (var y = 0; y < Size.Height; y++) + { + var i = 4*Size.Width*y + 4*x; + + // Convert argb to bgra + var argb = *(c + (y * b.Stride >> 2) + x); + Data[i++] = (byte)(argb >> 0); + Data[i++] = (byte)(argb >> 8); + Data[i++] = (byte)(argb >> 16); + Data[i++] = (byte)(argb >> 24); + } + } + bitmap.UnlockBits(b); } public ITexture Texture @@ -45,23 +69,61 @@ namespace OpenRA.Graphics if (dirty) { - if (data != null) - { - texture.SetData(data, Size.Width, Size.Height); - dirty = false; - } - else if (bitmap != null) - { - texture.SetData(bitmap); - dirty = false; - } + texture.SetData(Data, Size.Width, Size.Height); + dirty = false; } return texture; } } - public byte[] Data { get { if (data == null) data = new byte[4 * Size.Width * Size.Height]; return data; } } + public Bitmap AsBitmap() + { + var b = new Bitmap(Size.Width, Size.Height); + var output = b.LockBits(new Rectangle(0, 0, Size.Width, Size.Height), + ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + + unsafe + { + int* c = (int*)output.Scan0; + + for (var x = 0; x < Size.Width; x++) + for (var y = 0; y < Size.Height; y++) + { + var i = 4*Size.Width*y + 4*x; + + // Convert bgra to argb + var argb = (Data[i+3] << 24) | (Data[i+2] << 16) | (Data[i+1] << 8) | Data[i]; + *(c + (y * output.Stride >> 2) + x) = argb; + } + } + b.UnlockBits(output); + + return b; + } + + public Bitmap AsBitmap(TextureChannel channel, Palette pal) + { + var b = new Bitmap(Size.Width, Size.Height); + var output = b.LockBits(new Rectangle(0, 0, Size.Width, Size.Height), + ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + + unsafe + { + int* c = (int*)output.Scan0; + + for (var x = 0; x < Size.Width; x++) + for (var y = 0; y < Size.Height; y++) + { + var index = Data[4*Size.Width*y + 4*x + (int)channel]; + *(c + (y * output.Stride >> 2) + x) = pal.GetColor(index).ToArgb(); + } + } + b.UnlockBits(output); + + return b; + } + public void MakeDirty() { dirty = true; } } } diff --git a/OpenRA.Game/Graphics/SheetBuilder.cs b/OpenRA.Game/Graphics/SheetBuilder.cs index 1340e501ce..74c57f92a2 100644 --- a/OpenRA.Game/Graphics/SheetBuilder.cs +++ b/OpenRA.Game/Graphics/SheetBuilder.cs @@ -8,68 +8,69 @@ */ #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 enum SheetType + { + Indexed = 1, + DualIndexed = 2, + BGRA = 4, + } + public class SheetBuilder { - internal SheetBuilder(TextureChannel ch) + Sheet current; + TextureChannel channel; + SheetType type; + int rowHeight = 0; + Point p; + + internal SheetBuilder(SheetType t) { - current = null; - rowHeight = 0; - channel = null; - initialChannel = ch; + current = new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize));; + channel = TextureChannel.Red; + type = t; } - public Sprite Add(byte[] src, Size size) + public Sprite Add(byte[] src, Size size, bool allowSheetOverflow) { - Sprite rect = Allocate(size); + var 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++) + var data = new byte[size.Width * size.Height]; + for (var 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; + var nextChannel = (int)t + (int)type; + if (nextChannel > (int)TextureChannel.Alpha) + return null; - switch (t.Value) - { - case TextureChannel.Red: return TextureChannel.Green; - case TextureChannel.Green: return TextureChannel.Blue; - case TextureChannel.Blue: return TextureChannel.Alpha; - case TextureChannel.Alpha: return null; - - default: return null; - } + return (TextureChannel)nextChannel; } - 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,22 +82,29 @@ 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); + var rect = new Sprite(current, new Rectangle(p, imageSize), channel); current.MakeDirty(); p.X += imageSize.Width; return rect; } + + public Sheet Current { get { return current; } } } } diff --git a/OpenRA.Game/Graphics/Sprite.cs b/OpenRA.Game/Graphics/Sprite.cs index e62359f828..1ebcb9eaad 100644 --- a/OpenRA.Game/Graphics/Sprite.cs +++ b/OpenRA.Game/Graphics/Sprite.cs @@ -17,57 +17,52 @@ namespace OpenRA.Graphics public readonly Rectangle bounds; public readonly Sheet sheet; public readonly TextureChannel channel; - public readonly RectangleF uv; public readonly float2 size; - - readonly float2[] uvhax; + readonly float2[] textureCoords; public Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel) { this.bounds = bounds; this.sheet = sheet; this.channel = channel; - - uv = new RectangleF( - (float)(bounds.Left) / sheet.Size.Width, - (float)(bounds.Top) / sheet.Size.Height, - (float)(bounds.Width) / sheet.Size.Width, - (float)(bounds.Height) / sheet.Size.Height); - - uvhax = new float2[] - { - new float2( uv.Left, uv.Top ), - new float2( uv.Right, uv.Top ), - new float2( uv.Left, uv.Bottom ), - new float2( uv.Right, uv.Bottom ), - }; - this.size = new float2(bounds.Size); + + var left = (float)(bounds.Left) / sheet.Size.Width; + var top = (float)(bounds.Top) / sheet.Size.Height; + var right = (float)(bounds.Right) / sheet.Size.Width; + var bottom = (float)(bounds.Bottom) / sheet.Size.Height; + textureCoords = new float2[] + { + new float2(left, top), + new float2(right, top), + new float2(left, bottom), + new float2(right, bottom), + }; } - public float2 FastMapTextureCoords( int k ) + public float2 FastMapTextureCoords(int k) { - return uvhax[ k ]; + return textureCoords[k]; } - public void DrawAt( WorldRenderer wr, float2 location, string palette ) + public void DrawAt(WorldRenderer wr, float2 location, string palette) { - Game.Renderer.WorldSpriteRenderer.DrawSprite( this, location, wr, palette, this.size ); + Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, wr, palette, size); } - public void DrawAt( float2 location, int paletteIndex ) + public void DrawAt(float2 location, int paletteIndex) { - Game.Renderer.WorldSpriteRenderer.DrawSprite( this, location, paletteIndex, this.size ); + Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, paletteIndex, size); } public void DrawAt(float2 location, int paletteIndex, float scale) { - Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, paletteIndex, this.size * scale); + Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, paletteIndex, size*scale); } - public void DrawAt( float2 location, int paletteIndex, float2 size ) + public void DrawAt(float2 location, int paletteIndex, float2 size) { - Game.Renderer.WorldSpriteRenderer.DrawSprite( this, location, paletteIndex, size ); + Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, paletteIndex, size); } } diff --git a/OpenRA.Game/Graphics/SpriteFont.cs b/OpenRA.Game/Graphics/SpriteFont.cs index 974792d5dc..f4284b8055 100644 --- a/OpenRA.Game/Graphics/SpriteFont.cs +++ b/OpenRA.Game/Graphics/SpriteFont.cs @@ -31,8 +31,10 @@ namespace OpenRA.Graphics glyphs = new Cache, GlyphInfo>(CreateGlyph, Pair.EqualityComparer); - // setup a 1-channel SheetBuilder for our private use - if (builder == null) builder = new SheetBuilder(TextureChannel.Alpha); + // setup a SheetBuilder for our private use + // TODO: SheetBuilder state is leaked between mod switches + if (builder == null) + builder = new SheetBuilder(SheetType.BGRA); PrecacheColor(Color.White); PrecacheColor(Color.Red); @@ -46,7 +48,7 @@ namespace OpenRA.Graphics throw new InvalidOperationException(); } - public void DrawText (string text, float2 location, Color c) + public void DrawText(string text, float2 location, Color c) { location.Y += size; // baseline vs top @@ -84,7 +86,7 @@ namespace OpenRA.Graphics public int2 Measure(string text) { - return new int2((int)text.Split( '\n' ).Max( s => s.Sum(a => glyphs[Pair.New(a, Color.White)].Advance)), text.Split('\n').Count()*size); + return new int2((int)text.Split('\n').Max(s => s.Sum(a => glyphs[Pair.New(a, Color.White)].Advance)), text.Split('\n').Count()*size); } Cache, GlyphInfo> glyphs; @@ -96,9 +98,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 3231ef4347..c8a43df742 100644 --- a/OpenRA.Game/Graphics/TerrainRenderer.cs +++ b/OpenRA.Game/Graphics/TerrainRenderer.cs @@ -18,8 +18,8 @@ namespace OpenRA.Graphics { class TerrainRenderer { + SheetBuilder sheetBuilder; IVertexBuffer vertexBuffer; - Sheet terrainSheet; World world; Map map; @@ -29,37 +29,31 @@ namespace OpenRA.Graphics this.world = world; this.map = world.Map; - var tileSize = new Size( Game.CellSize, Game.CellSize ); + // TODO: Use a fixed sheet size specified in the tileset yaml + sheetBuilder = new SheetBuilder(SheetType.Indexed); + var tileSize = new Size(Game.CellSize, Game.CellSize); var tileMapping = new Cache, Sprite>( - x => Game.modData.SheetBuilder.Add(world.TileSet.GetBytes(x), tileSize)); - - var vertices = new Vertex[4 * map.Bounds.Height * map.Bounds.Width]; - - terrainSheet = tileMapping[map.MapTiles.Value[map.Bounds.Left, map.Bounds.Top]].sheet; - - int nv = 0; + x => sheetBuilder.Add(world.TileSet.GetBytes(x), tileSize, false)); var terrainPalette = wr.Palette("terrain").Index; + var vertices = new Vertex[4 * map.Bounds.Height * map.Bounds.Width]; + int nv = 0; - for( int j = map.Bounds.Top; j < map.Bounds.Bottom; j++ ) - for( int i = map.Bounds.Left; i < map.Bounds.Right; i++ ) + for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++) + for (var i = map.Bounds.Left; i < map.Bounds.Right; i++) { var tile = tileMapping[map.MapTiles.Value[i, j]]; - // TODO: move GetPaletteIndex out of the inner loop. 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 ); - vertexBuffer.SetData( vertices, nv ); + vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(vertices.Length); + vertexBuffer.SetData(vertices, nv); } - public void Draw( WorldRenderer wr, Viewport viewport ) + public void Draw(WorldRenderer wr, Viewport viewport) { - int verticesPerRow = map.Bounds.Width * 4; + int verticesPerRow = 4*map.Bounds.Width; int visibleRows = (int)(viewport.Height * 1f / Game.CellSize / viewport.Zoom + 2); @@ -79,17 +73,22 @@ namespace OpenRA.Graphics firstRow = r.Bottom - map.Bounds.Top; } - if (firstRow < 0) firstRow = 0; - if (lastRow > map.Bounds.Height) lastRow = map.Bounds.Height; + // Sanity checking + if (firstRow < 0) + firstRow = 0; - if( lastRow < firstRow ) lastRow = firstRow; + if (lastRow > map.Bounds.Height) + lastRow = map.Bounds.Height; + + if (lastRow < firstRow) + lastRow = firstRow; Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer( vertexBuffer, verticesPerRow * firstRow, verticesPerRow * (lastRow - firstRow), - PrimitiveType.QuadList, terrainSheet); + PrimitiveType.QuadList, sheetBuilder.Current); foreach (var r in world.WorldActor.TraitsImplementing()) - r.Render( wr ); + r.Render(wr); } } } diff --git a/OpenRA.Game/Graphics/Util.cs b/OpenRA.Game/Graphics/Util.cs index 20d79f4f0f..d0efeadc3e 100644 --- a/OpenRA.Game/Graphics/Util.cs +++ b/OpenRA.Game/Graphics/Util.cs @@ -32,12 +32,13 @@ namespace OpenRA.Graphics static readonly int[] channelMasks = { 2, 1, 0, 3 }; // yes, our channel order is nuts. - public static void FastCopyIntoChannel(Sprite dest, byte[] src) + public static void FastCopyIntoChannel(Sprite dest, byte[] src) { FastCopyIntoChannel(dest, 0, src); } + public static void FastCopyIntoChannel(Sprite dest, int channelOffset, byte[] src) { var data = dest.sheet.Data; var srcStride = dest.bounds.Width; var destStride = dest.sheet.Size.Width * 4; - var destOffset = destStride * dest.bounds.Top + dest.bounds.Left * 4 + channelMasks[(int)dest.channel]; + var destOffset = destStride * dest.bounds.Top + dest.bounds.Left * 4 + channelMasks[(int)dest.channel + channelOffset]; var destSkip = destStride - 4 * srcStride; var height = dest.bounds.Height; diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index dd456520d4..f5c298d4b7 100755 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -53,7 +53,7 @@ namespace OpenRA ChromeMetrics.Initialize(Manifest.ChromeMetrics); ChromeProvider.Initialize(Manifest.Chrome); - SheetBuilder = new SheetBuilder(TextureChannel.Red); + SheetBuilder = new SheetBuilder(SheetType.Indexed); SpriteLoader = new SpriteLoader(new string[] { ".shp" }, SheetBuilder); CursorProvider.Initialize(Manifest.Cursors); } 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