Split tileset artwork loading out of TileSet.
This simplifies terrain loading and allows for non-square tiles in game. The editor still relies on the old code for now.
This commit is contained in:
@@ -16,16 +16,15 @@ namespace OpenRA.FileFormats
|
|||||||
public class Terrain
|
public class Terrain
|
||||||
{
|
{
|
||||||
public readonly List<byte[]> TileBitmapBytes = new List<byte[]>();
|
public readonly List<byte[]> TileBitmapBytes = new List<byte[]>();
|
||||||
|
public readonly int Width;
|
||||||
|
public readonly int Height;
|
||||||
|
|
||||||
public Terrain( Stream stream, int size )
|
public Terrain(Stream stream)
|
||||||
{
|
{
|
||||||
// Try loading as a cnc .tem
|
// Try loading as a cnc .tem
|
||||||
BinaryReader reader = new BinaryReader( stream );
|
BinaryReader reader = new BinaryReader( stream );
|
||||||
int Width = reader.ReadUInt16();
|
Width = reader.ReadUInt16();
|
||||||
int Height = reader.ReadUInt16();
|
Height = reader.ReadUInt16();
|
||||||
|
|
||||||
if( Width != size || Height != size )
|
|
||||||
throw new InvalidDataException( "{0}x{1} != {2}x{2}".F(Width, Height, size ) );
|
|
||||||
|
|
||||||
/*NumTiles = */reader.ReadUInt16();
|
/*NumTiles = */reader.ReadUInt16();
|
||||||
/*Zero1 = */reader.ReadUInt16();
|
/*Zero1 = */reader.ReadUInt16();
|
||||||
@@ -65,8 +64,8 @@ namespace OpenRA.FileFormats
|
|||||||
{
|
{
|
||||||
if (b != 255)
|
if (b != 255)
|
||||||
{
|
{
|
||||||
stream.Position = ImgStart + b * size * size;
|
stream.Position = ImgStart + b * Width * Height;
|
||||||
TileBitmapBytes.Add(new BinaryReader(stream).ReadBytes(size * size));
|
TileBitmapBytes.Add(new BinaryReader(stream).ReadBytes(Width * Height));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
TileBitmapBytes.Add(null);
|
TileBitmapBytes.Add(null);
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ namespace OpenRA.FileFormats
|
|||||||
foreach (var t in Templates)
|
foreach (var t in Templates)
|
||||||
if (t.Value.Data == null)
|
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);
|
t.Value.Data = new Terrain(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save(string filepath)
|
public void Save(string filepath)
|
||||||
@@ -144,23 +144,6 @@ namespace OpenRA.FileFormats
|
|||||||
root.WriteToFile(filepath);
|
root.WriteToFile(filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] GetBytes(TileReference<ushort,byte> 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<ushort, byte> r)
|
public string GetTerrainType(TileReference<ushort, byte> r)
|
||||||
{
|
{
|
||||||
var tt = Templates[r.type].Tiles;
|
var tt = Templates[r.type].Tiles;
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ namespace OpenRA.Graphics
|
|||||||
{
|
{
|
||||||
class TerrainRenderer
|
class TerrainRenderer
|
||||||
{
|
{
|
||||||
SheetBuilder sheetBuilder;
|
|
||||||
IVertexBuffer<Vertex> vertexBuffer;
|
IVertexBuffer<Vertex> vertexBuffer;
|
||||||
|
|
||||||
World world;
|
World world;
|
||||||
@@ -29,23 +28,6 @@ namespace OpenRA.Graphics
|
|||||||
this.world = world;
|
this.world = world;
|
||||||
this.map = world.Map;
|
this.map = world.Map;
|
||||||
|
|
||||||
var allocated = false;
|
|
||||||
Func<Sheet> 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<TileReference<ushort,byte>, Sprite>(
|
|
||||||
x => sheetBuilder.Add(world.TileSet.GetBytes(x), tileSize));
|
|
||||||
|
|
||||||
var terrainPalette = wr.Palette("terrain").Index;
|
var terrainPalette = wr.Palette("terrain").Index;
|
||||||
var vertices = new Vertex[4 * map.Bounds.Height * map.Bounds.Width];
|
var vertices = new Vertex[4 * map.Bounds.Height * map.Bounds.Width];
|
||||||
int nv = 0;
|
int nv = 0;
|
||||||
@@ -53,7 +35,7 @@ namespace OpenRA.Graphics
|
|||||||
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
||||||
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
|
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);
|
Util.FastCreateQuad(vertices, Game.CellSize * new float2(i, j), tile, terrainPalette, nv, tile.size);
|
||||||
nv += 4;
|
nv += 4;
|
||||||
}
|
}
|
||||||
@@ -96,7 +78,7 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer(
|
Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer(
|
||||||
vertexBuffer, verticesPerRow * firstRow, verticesPerRow * (lastRow - firstRow),
|
vertexBuffer, verticesPerRow * firstRow, verticesPerRow * (lastRow - firstRow),
|
||||||
PrimitiveType.QuadList, sheetBuilder.Current);
|
PrimitiveType.QuadList, wr.Theater.Sheet);
|
||||||
|
|
||||||
foreach (var r in world.WorldActor.TraitsImplementing<IRenderOverlay>())
|
foreach (var r in world.WorldActor.TraitsImplementing<IRenderOverlay>())
|
||||||
r.Render(wr);
|
r.Render(wr);
|
||||||
|
|||||||
73
OpenRA.Game/Graphics/Theater.cs
Normal file
73
OpenRA.Game/Graphics/Theater.cs
Normal file
@@ -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<ushort, Sprite[]> 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<Sheet> 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<ushort, Sprite[]>();
|
||||||
|
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<ushort, byte> 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; } }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,8 @@ namespace OpenRA.Graphics
|
|||||||
public class WorldRenderer
|
public class WorldRenderer
|
||||||
{
|
{
|
||||||
public readonly World world;
|
public readonly World world;
|
||||||
|
public readonly Theater Theater;
|
||||||
|
|
||||||
internal readonly TerrainRenderer terrainRenderer;
|
internal readonly TerrainRenderer terrainRenderer;
|
||||||
internal readonly ShroudRenderer shroudRenderer;
|
internal readonly ShroudRenderer shroudRenderer;
|
||||||
internal readonly HardwarePalette palette;
|
internal readonly HardwarePalette palette;
|
||||||
@@ -50,6 +52,7 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
palette.Initialize();
|
palette.Initialize();
|
||||||
|
|
||||||
|
Theater = new Theater(world.TileSet);
|
||||||
terrainRenderer = new TerrainRenderer(world, this);
|
terrainRenderer = new TerrainRenderer(world, this);
|
||||||
shroudRenderer = new ShroudRenderer(world);
|
shroudRenderer = new ShroudRenderer(world);
|
||||||
|
|
||||||
|
|||||||
@@ -235,6 +235,7 @@
|
|||||||
<Compile Include="Graphics\ContrailRenderable.cs" />
|
<Compile Include="Graphics\ContrailRenderable.cs" />
|
||||||
<Compile Include="Widgets\ViewportControllerWidget.cs" />
|
<Compile Include="Widgets\ViewportControllerWidget.cs" />
|
||||||
<Compile Include="Traits\Player\FrozenActorLayer.cs" />
|
<Compile Include="Traits\Player\FrozenActorLayer.cs" />
|
||||||
|
<Compile Include="Graphics\Theater.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||||
|
|||||||
@@ -69,12 +69,8 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
class Bridge: IRenderAsTerrain, INotifyDamageStateChanged
|
class Bridge: IRenderAsTerrain, INotifyDamageStateChanged
|
||||||
{
|
{
|
||||||
static string cachedTileset;
|
ushort template;
|
||||||
static Cache<TileReference<ushort,byte>, Sprite> sprites;
|
Dictionary<CPos, byte> footprint;
|
||||||
|
|
||||||
Dictionary<ushort, Dictionary<CPos, Sprite>> TileSprites = new Dictionary<ushort, Dictionary<CPos, Sprite>>();
|
|
||||||
Dictionary<ushort, TileTemplate> Templates = new Dictionary<ushort, TileTemplate>();
|
|
||||||
ushort currentTemplate;
|
|
||||||
|
|
||||||
Actor self;
|
Actor self;
|
||||||
BridgeInfo Info;
|
BridgeInfo Info;
|
||||||
@@ -91,38 +87,21 @@ namespace OpenRA.Mods.RA
|
|||||||
this.Type = self.Info.Name;
|
this.Type = self.Info.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Create(ushort template, Dictionary<CPos, byte> subtiles)
|
public void Create(ushort template, Dictionary<CPos, byte> footprint)
|
||||||
{
|
{
|
||||||
currentTemplate = template;
|
this.template = template;
|
||||||
|
this.footprint = footprint;
|
||||||
// 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<TileReference<ushort,byte>, 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<ushort,byte>(t.First, (byte)a.Value)]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the initial custom terrain types
|
// 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);
|
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 dx = cell - self.Location;
|
||||||
var index = dx.X + Templates[currentTemplate].Size.X * dx.Y;
|
var index = dx.X + self.World.TileSet.Templates[template].Size.X * dx.Y;
|
||||||
return self.World.TileSet.GetTerrainType(new TileReference<ushort, byte>(currentTemplate,(byte)index));
|
return self.World.TileSet.GetTerrainType(new TileReference<ushort, byte>(template, (byte)index));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LinkNeighbouringBridges(World world, BridgeLayer bridges)
|
public void LinkNeighbouringBridges(World world, BridgeLayer bridges)
|
||||||
@@ -136,27 +115,39 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public Bridge GetNeighbor(int[] offset, BridgeLayer bridges)
|
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]));
|
return bridges.GetBridge(self.Location + new CVec(offset[0], offset[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool initializePalettes = true;
|
IRenderable[] TemplateRenderables(WorldRenderer wr, PaletteReference palette, ushort template)
|
||||||
PaletteReference terrainPalette;
|
{
|
||||||
|
return footprint.Select(c => (IRenderable)(new SpriteRenderable(
|
||||||
|
wr.Theater.TileSprite(new TileReference<ushort, byte>(template, c.Value)),
|
||||||
|
c.Key.CenterPosition, WVec.Zero, 0, palette, 1f, true))).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initialized;
|
||||||
|
Dictionary<ushort, IRenderable[]> renderables;
|
||||||
public IEnumerable<IRenderable> RenderAsTerrain(WorldRenderer wr, Actor self)
|
public IEnumerable<IRenderable> RenderAsTerrain(WorldRenderer wr, Actor self)
|
||||||
{
|
{
|
||||||
if (initializePalettes)
|
if (!initialized)
|
||||||
{
|
{
|
||||||
terrainPalette = wr.Palette("terrain");
|
var palette = wr.Palette("terrain");
|
||||||
initializePalettes = false;
|
renderables = new Dictionary<ushort, IRenderable[]>();
|
||||||
|
foreach (var t in Info.Templates)
|
||||||
|
renderables.Add(t.First, TemplateRenderables(wr, palette, t.First));
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var t in TileSprites[currentTemplate])
|
return renderables[template];
|
||||||
yield return new SpriteRenderable(t.Value, t.Key.CenterPosition, WVec.Zero, 0, terrainPalette, 1f, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KillUnitsOnBridge()
|
void KillUnitsOnBridge()
|
||||||
{
|
{
|
||||||
foreach (var c in TileSprites[currentTemplate].Keys)
|
foreach (var c in footprint.Keys)
|
||||||
foreach (var a in self.World.ActorMap.GetUnitsAt(c))
|
foreach (var a in self.World.ActorMap.GetUnitsAt(c))
|
||||||
if (a.HasTrait<IPositionable>() && !a.Trait<IPositionable>().CanEnterCell(c))
|
if (a.HasTrait<IPositionable>() && !a.Trait<IPositionable>().CanEnterCell(c))
|
||||||
a.Kill(self);
|
a.Kill(self);
|
||||||
@@ -208,21 +199,21 @@ namespace OpenRA.Mods.RA
|
|||||||
bool killedUnits = false;
|
bool killedUnits = false;
|
||||||
void UpdateState()
|
void UpdateState()
|
||||||
{
|
{
|
||||||
var oldTemplate = currentTemplate;
|
var oldTemplate = template;
|
||||||
|
|
||||||
currentTemplate = ChooseTemplate();
|
template = ChooseTemplate();
|
||||||
if (currentTemplate == oldTemplate)
|
if (template == oldTemplate)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Update map
|
// 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);
|
self.World.Map.CustomTerrain[c.X, c.Y] = GetTerrainType(c);
|
||||||
|
|
||||||
// If this bridge repair operation connects two pathfinding domains,
|
// If this bridge repair operation connects two pathfinding domains,
|
||||||
// update the domain index.
|
// update the domain index.
|
||||||
var domainIndex = self.World.WorldActor.TraitOrDefault<DomainIndex>();
|
var domainIndex = self.World.WorldActor.TraitOrDefault<DomainIndex>();
|
||||||
if (domainIndex != null)
|
if (domainIndex != null)
|
||||||
domainIndex.UpdateCells(self.World, TileSprites[currentTemplate].Keys);
|
domainIndex.UpdateCells(self.World, footprint.Keys);
|
||||||
|
|
||||||
if (LongBridgeSegmentIsDead() && !killedUnits)
|
if (LongBridgeSegmentIsDead() && !killedUnits)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user