diff --git a/OpenRA.FileFormats/Map/Terrain.cs b/OpenRA.FileFormats/Map/Terrain.cs index 4e5f1326c4..5bb4989b88 100644 --- a/OpenRA.FileFormats/Map/Terrain.cs +++ b/OpenRA.FileFormats/Map/Terrain.cs @@ -16,16 +16,15 @@ namespace OpenRA.FileFormats public class Terrain { public readonly List TileBitmapBytes = new List(); + public readonly int Width; + public readonly int Height; - public Terrain( Stream stream, int size ) + public Terrain(Stream stream) { // Try loading as a cnc .tem BinaryReader reader = new BinaryReader( stream ); - int Width = reader.ReadUInt16(); - int Height = reader.ReadUInt16(); - - if( Width != size || Height != size ) - throw new InvalidDataException( "{0}x{1} != {2}x{2}".F(Width, Height, size ) ); + Width = reader.ReadUInt16(); + Height = reader.ReadUInt16(); /*NumTiles = */reader.ReadUInt16(); /*Zero1 = */reader.ReadUInt16(); @@ -65,8 +64,8 @@ namespace OpenRA.FileFormats { if (b != 255) { - stream.Position = ImgStart + b * size * size; - TileBitmapBytes.Add(new BinaryReader(stream).ReadBytes(size * size)); + stream.Position = ImgStart + b * Width * Height; + TileBitmapBytes.Add(new BinaryReader(stream).ReadBytes(Width * Height)); } else TileBitmapBytes.Add(null); diff --git a/OpenRA.FileFormats/Map/TileSet.cs b/OpenRA.FileFormats/Map/TileSet.cs index 0cec68450e..7fe1145459 100644 --- a/OpenRA.FileFormats/Map/TileSet.cs +++ b/OpenRA.FileFormats/Map/TileSet.cs @@ -113,7 +113,7 @@ namespace OpenRA.FileFormats foreach (var t in Templates) if (t.Value.Data == null) using (var s = FileSystem.OpenWithExts(t.Value.Image, Extensions)) - t.Value.Data = new Terrain(s, TileSize); + t.Value.Data = new Terrain(s); } public void Save(string filepath) @@ -144,23 +144,6 @@ namespace OpenRA.FileFormats root.WriteToFile(filepath); } - public byte[] GetBytes(TileReference r) - { - TileTemplate tile; - if (Templates.TryGetValue(r.type, out tile)) - { - var data = tile.Data.TileBitmapBytes[r.index]; - if (data != null) - return data; - } - - byte[] missingTile = new byte[TileSize*TileSize]; - for (var i = 0; i < missingTile.Length; i++) - missingTile[i] = 0x00; - - return missingTile; - } - public string GetTerrainType(TileReference r) { var tt = Templates[r.type].Tiles; diff --git a/OpenRA.Game/Graphics/TerrainRenderer.cs b/OpenRA.Game/Graphics/TerrainRenderer.cs index 240bceca21..cc092e2b6f 100644 --- a/OpenRA.Game/Graphics/TerrainRenderer.cs +++ b/OpenRA.Game/Graphics/TerrainRenderer.cs @@ -18,7 +18,6 @@ namespace OpenRA.Graphics { class TerrainRenderer { - SheetBuilder sheetBuilder; IVertexBuffer vertexBuffer; World world; @@ -29,23 +28,6 @@ namespace OpenRA.Graphics this.world = world; this.map = world.Map; - var allocated = false; - Func allocate = () => - { - if (allocated) - throw new SheetOverflowException("Terrain sheet overflow"); - allocated = true; - - // TODO: Use a fixed sheet size specified in the tileset yaml - return SheetBuilder.AllocateSheet(); - }; - - sheetBuilder = new SheetBuilder(SheetType.Indexed, allocate); - - var tileSize = new Size(Game.CellSize, Game.CellSize); - var tileMapping = new Cache, Sprite>( - x => sheetBuilder.Add(world.TileSet.GetBytes(x), tileSize)); - var terrainPalette = wr.Palette("terrain").Index; var vertices = new Vertex[4 * map.Bounds.Height * map.Bounds.Width]; int nv = 0; @@ -53,7 +35,7 @@ namespace OpenRA.Graphics 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]]; + var tile = wr.Theater.TileSprite(map.MapTiles.Value[i, j]); Util.FastCreateQuad(vertices, Game.CellSize * new float2(i, j), tile, terrainPalette, nv, tile.size); nv += 4; } @@ -96,7 +78,7 @@ namespace OpenRA.Graphics Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer( vertexBuffer, verticesPerRow * firstRow, verticesPerRow * (lastRow - firstRow), - PrimitiveType.QuadList, sheetBuilder.Current); + PrimitiveType.QuadList, wr.Theater.Sheet); foreach (var r in world.WorldActor.TraitsImplementing()) r.Render(wr); diff --git a/OpenRA.Game/Graphics/Theater.cs b/OpenRA.Game/Graphics/Theater.cs new file mode 100644 index 0000000000..4ef7f0ef81 --- /dev/null +++ b/OpenRA.Game/Graphics/Theater.cs @@ -0,0 +1,73 @@ +#region Copyright & License Information +/* + * Copyright 2007-2013 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.FileFormats.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Graphics +{ + public class Theater + { + SheetBuilder sheetBuilder; + Dictionary templates; + Sprite missingTile; + + Sprite[] LoadTemplate(string filename, string[] exts) + { + using (var s = FileSystem.OpenWithExts(filename, exts)) + { + var t = new Terrain(s); + return t.TileBitmapBytes + .Select(b => b != null ? sheetBuilder.Add(b, new Size(t.Width, t.Height)) : null) + .ToArray(); + } + } + + public Theater(TileSet tileset) + { + var allocated = false; + Func allocate = () => + { + if (allocated) + throw new SheetOverflowException("Terrain sheet overflow"); + allocated = true; + + // TODO: Use a fixed sheet size specified in the tileset yaml + return SheetBuilder.AllocateSheet(); + }; + + templates = new Dictionary(); + sheetBuilder = new SheetBuilder(SheetType.Indexed, allocate); + foreach (var t in tileset.Templates) + templates.Add(t.Value.Id, LoadTemplate(t.Value.Image, tileset.Extensions)); + + // 1x1px transparent tile + missingTile = sheetBuilder.Add(new byte[1], new Size(1, 1)); + } + + public Sprite TileSprite(TileReference r) + { + Sprite[] template; + if (templates.TryGetValue(r.type, out template)) + if (template.Length > r.index && template[r.index] != null) + return template[r.index]; + + return missingTile; + } + + public Sheet Sheet { get { return sheetBuilder.Current; } } + } +} diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index bfd1a91990..cf0773c0aa 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -33,6 +33,8 @@ namespace OpenRA.Graphics public class WorldRenderer { public readonly World world; + public readonly Theater Theater; + internal readonly TerrainRenderer terrainRenderer; internal readonly ShroudRenderer shroudRenderer; internal readonly HardwarePalette palette; @@ -50,6 +52,7 @@ namespace OpenRA.Graphics palette.Initialize(); + Theater = new Theater(world.TileSet); terrainRenderer = new TerrainRenderer(world, this); shroudRenderer = new ShroudRenderer(world); diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index e44917e4c5..e15a4185e7 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -235,6 +235,7 @@ + diff --git a/OpenRA.Mods.RA/Bridge.cs b/OpenRA.Mods.RA/Bridge.cs index 8a13908a43..8a8b72d410 100644 --- a/OpenRA.Mods.RA/Bridge.cs +++ b/OpenRA.Mods.RA/Bridge.cs @@ -69,12 +69,8 @@ namespace OpenRA.Mods.RA class Bridge: IRenderAsTerrain, INotifyDamageStateChanged { - static string cachedTileset; - static Cache, Sprite> sprites; - - Dictionary> TileSprites = new Dictionary>(); - Dictionary Templates = new Dictionary(); - ushort currentTemplate; + ushort template; + Dictionary footprint; Actor self; BridgeInfo Info; @@ -91,38 +87,21 @@ namespace OpenRA.Mods.RA this.Type = self.Info.Name; } - public void Create(ushort template, Dictionary subtiles) + public void Create(ushort template, Dictionary footprint) { - currentTemplate = template; - - // Create a new cache to store the tile data - 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), tileSize)); - } - - // Cache templates and tiles for the different states - foreach (var t in Info.Templates) - { - Templates.Add(t.First,self.World.TileSet.Templates[t.First]); - TileSprites.Add(t.First, subtiles.ToDictionary( - a => a.Key, - a => sprites[new TileReference(t.First, (byte)a.Value)])); - } + this.template = template; + this.footprint = footprint; // Set the initial custom terrain types - foreach (var c in TileSprites[currentTemplate].Keys) + foreach (var c in footprint.Keys) self.World.Map.CustomTerrain[c.X, c.Y] = GetTerrainType(c); } - public string GetTerrainType(CPos cell) + string GetTerrainType(CPos cell) { var dx = cell - self.Location; - var index = dx.X + Templates[currentTemplate].Size.X * dx.Y; - return self.World.TileSet.GetTerrainType(new TileReference(currentTemplate,(byte)index)); + var index = dx.X + self.World.TileSet.Templates[template].Size.X * dx.Y; + return self.World.TileSet.GetTerrainType(new TileReference(template, (byte)index)); } public void LinkNeighbouringBridges(World world, BridgeLayer bridges) @@ -136,27 +115,39 @@ namespace OpenRA.Mods.RA public Bridge GetNeighbor(int[] offset, BridgeLayer bridges) { - if (offset == null) return null; + if (offset == null) + return null; + return bridges.GetBridge(self.Location + new CVec(offset[0], offset[1])); } - bool initializePalettes = true; - PaletteReference terrainPalette; + IRenderable[] TemplateRenderables(WorldRenderer wr, PaletteReference palette, ushort template) + { + return footprint.Select(c => (IRenderable)(new SpriteRenderable( + wr.Theater.TileSprite(new TileReference(template, c.Value)), + c.Key.CenterPosition, WVec.Zero, 0, palette, 1f, true))).ToArray(); + } + + bool initialized; + Dictionary renderables; public IEnumerable RenderAsTerrain(WorldRenderer wr, Actor self) { - if (initializePalettes) + if (!initialized) { - terrainPalette = wr.Palette("terrain"); - initializePalettes = false; + var palette = wr.Palette("terrain"); + renderables = new Dictionary(); + foreach (var t in Info.Templates) + renderables.Add(t.First, TemplateRenderables(wr, palette, t.First)); + + initialized = true; } - foreach (var t in TileSprites[currentTemplate]) - yield return new SpriteRenderable(t.Value, t.Key.CenterPosition, WVec.Zero, 0, terrainPalette, 1f, true); + return renderables[template]; } void KillUnitsOnBridge() { - foreach (var c in TileSprites[currentTemplate].Keys) + foreach (var c in footprint.Keys) foreach (var a in self.World.ActorMap.GetUnitsAt(c)) if (a.HasTrait() && !a.Trait().CanEnterCell(c)) a.Kill(self); @@ -208,21 +199,21 @@ namespace OpenRA.Mods.RA bool killedUnits = false; void UpdateState() { - var oldTemplate = currentTemplate; + var oldTemplate = template; - currentTemplate = ChooseTemplate(); - if (currentTemplate == oldTemplate) + template = ChooseTemplate(); + if (template == oldTemplate) return; // Update map - foreach (var c in TileSprites[currentTemplate].Keys) + foreach (var c in footprint.Keys) self.World.Map.CustomTerrain[c.X, c.Y] = GetTerrainType(c); // If this bridge repair operation connects two pathfinding domains, // update the domain index. var domainIndex = self.World.WorldActor.TraitOrDefault(); if (domainIndex != null) - domainIndex.UpdateCells(self.World, TileSprites[currentTemplate].Keys); + domainIndex.UpdateCells(self.World, footprint.Keys); if (LongBridgeSegmentIsDead() && !killedUnits) {