@@ -27,7 +27,7 @@ namespace OpenRA.Editor
|
||||
{
|
||||
// change the bits in the map
|
||||
var template = surface.TileSet.Templates[brushTemplate.N];
|
||||
var tile = template.Data;
|
||||
var tile = surface.TileSetRenderer.Data(brushTemplate.N);
|
||||
var pos = surface.GetBrushLocation();
|
||||
|
||||
if (surface.GetModifiers() == Keys.Shift)
|
||||
@@ -42,7 +42,7 @@ namespace OpenRA.Editor
|
||||
if (surface.Map.IsInMap(new CVec(u, v) + pos))
|
||||
{
|
||||
var z = u + v * template.Size.X;
|
||||
if (tile.TileBitmapBytes[z] != null)
|
||||
if (tile[z] != null)
|
||||
surface.Map.MapTiles.Value[u + pos.X, v + pos.Y] =
|
||||
new TileReference<ushort, byte>
|
||||
{
|
||||
@@ -63,8 +63,8 @@ namespace OpenRA.Editor
|
||||
public void Preview(Surface surface, SGraphics g)
|
||||
{
|
||||
g.DrawImage(brushTemplate.Bitmap,
|
||||
surface.TileSet.TileSize * surface.GetBrushLocation().X * surface.Zoom + surface.GetOffset().X,
|
||||
surface.TileSet.TileSize * surface.GetBrushLocation().Y * surface.Zoom + surface.GetOffset().Y,
|
||||
surface.TileSetRenderer.TileSize.Width * surface.GetBrushLocation().X * surface.Zoom + surface.GetOffset().X,
|
||||
surface.TileSetRenderer.TileSize.Height * surface.GetBrushLocation().Y * surface.Zoom + surface.GetOffset().Y,
|
||||
brushTemplate.Bitmap.Width * surface.Zoom,
|
||||
brushTemplate.Bitmap.Height * surface.Zoom);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace OpenRA.Editor
|
||||
tilePalette.ResumeLayout();
|
||||
actorPalette.ResumeLayout();
|
||||
resourcePalette.ResumeLayout();
|
||||
surface1.Bind(null, null, null, null);
|
||||
surface1.Bind(null, null, null, null, null);
|
||||
miniMapBox.Image = null;
|
||||
currentMod = toolStripComboBox1.SelectedItem as string;
|
||||
|
||||
@@ -101,6 +101,7 @@ namespace OpenRA.Editor
|
||||
string loadedMapName;
|
||||
string currentMod = "ra";
|
||||
TileSet tileset;
|
||||
TileSetRenderer tilesetRenderer;
|
||||
bool dirty = false;
|
||||
|
||||
void LoadMap(string mapname)
|
||||
@@ -144,7 +145,7 @@ namespace OpenRA.Editor
|
||||
{
|
||||
Rules.LoadRules(manifest, map);
|
||||
tileset = Rules.TileSets[map.Tileset];
|
||||
tileset.LoadTiles();
|
||||
tilesetRenderer = new TileSetRenderer(tileset, new Size(manifest.TileSize, manifest.TileSize));
|
||||
var shadowIndex = new int[] { 3, 4 };
|
||||
var palette = new Palette(FileSystem.Open(tileset.Palette), shadowIndex);
|
||||
|
||||
@@ -152,7 +153,7 @@ namespace OpenRA.Editor
|
||||
var playerPalette = tileset.PlayerPalette ?? tileset.Palette;
|
||||
var shadowedPalette = new Palette(FileSystem.Open(playerPalette), shadowIndex);
|
||||
|
||||
surface1.Bind(map, tileset, palette, shadowedPalette);
|
||||
surface1.Bind(map, tileset, tilesetRenderer, palette, shadowedPalette);
|
||||
|
||||
// construct the palette of tiles
|
||||
var palettes = new[] { tilePalette, actorPalette, resourcePalette };
|
||||
@@ -185,7 +186,7 @@ namespace OpenRA.Editor
|
||||
{
|
||||
try
|
||||
{
|
||||
var bitmap = tileset.RenderTemplate((ushort)t.Key, palette);
|
||||
var bitmap = tilesetRenderer.RenderTemplate((ushort)t.Key, palette);
|
||||
var ibox = new PictureBox
|
||||
{
|
||||
Image = bitmap,
|
||||
@@ -329,7 +330,7 @@ namespace OpenRA.Editor
|
||||
if ((int)rd.MapWidth.Value != surface1.Map.MapSize.X || (int)rd.MapHeight.Value != surface1.Map.MapSize.Y)
|
||||
{
|
||||
surface1.Map.Resize((int)rd.MapWidth.Value, (int)rd.MapHeight.Value);
|
||||
surface1.Bind(surface1.Map, surface1.TileSet, surface1.Palette, surface1.PlayerPalette); // rebind it to invalidate all caches
|
||||
surface1.Bind(surface1.Map, surface1.TileSet, surface1.TileSetRenderer, surface1.Palette, surface1.PlayerPalette); // rebind it to invalidate all caches
|
||||
}
|
||||
|
||||
surface1.Invalidate();
|
||||
@@ -519,8 +520,7 @@ namespace OpenRA.Editor
|
||||
{
|
||||
var tr = surface1.Map.MapTiles.Value[i, j];
|
||||
if (tr.type == 0xff || tr.type == 0xffff || tr.type == 1 || tr.type == 2)
|
||||
tr.index = (byte)r.Next(0,
|
||||
Rules.TileSets[surface1.Map.Tileset].Templates[tr.type].Data.TileBitmapBytes.Count);
|
||||
tr.index = (byte)r.Next(0, surface1.TileSetRenderer.Data(tr.type).Count);
|
||||
|
||||
surface1.Map.MapTiles.Value[i, j] = tr;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace OpenRA.Editor
|
||||
{
|
||||
public Map Map { get; private set; }
|
||||
public TileSet TileSet { get; private set; }
|
||||
public TileSetRenderer TileSetRenderer { get; private set; }
|
||||
public Palette Palette { get; private set; }
|
||||
public Palette PlayerPalette { get; private set; }
|
||||
public int2 Offset;
|
||||
@@ -79,10 +80,11 @@ namespace OpenRA.Editor
|
||||
|
||||
public Keys GetModifiers() { return ModifierKeys; }
|
||||
|
||||
public void Bind(Map m, TileSet ts, Palette p, Palette pp)
|
||||
public void Bind(Map m, TileSet ts, TileSetRenderer tsr, Palette p, Palette pp)
|
||||
{
|
||||
Map = m;
|
||||
TileSet = ts;
|
||||
TileSetRenderer = tsr;
|
||||
Palette = p;
|
||||
PlayerPalette = pp;
|
||||
playerPalettes = null;
|
||||
@@ -256,7 +258,7 @@ namespace OpenRA.Editor
|
||||
|
||||
Bitmap RenderChunk(int u, int v)
|
||||
{
|
||||
var bitmap = new Bitmap(ChunkSize * TileSet.TileSize, ChunkSize * TileSet.TileSize);
|
||||
var bitmap = new Bitmap(ChunkSize * TileSetRenderer.TileSize.Width, ChunkSize * TileSetRenderer.TileSize.Height);
|
||||
|
||||
var data = bitmap.LockBits(bitmap.Bounds(),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
@@ -270,12 +272,12 @@ namespace OpenRA.Editor
|
||||
for (var j = 0; j < ChunkSize; j++)
|
||||
{
|
||||
var tr = Map.MapTiles.Value[u * ChunkSize + i, v * ChunkSize + j];
|
||||
var tile = TileSet.Templates[tr.type].Data;
|
||||
var index = (tr.index < tile.TileBitmapBytes.Count) ? tr.index : (byte)0;
|
||||
var rawImage = tile.TileBitmapBytes[index];
|
||||
for (var x = 0; x < TileSet.TileSize; x++)
|
||||
for (var y = 0; y < TileSet.TileSize; y++)
|
||||
p[(j * TileSet.TileSize + y) * stride + i * TileSet.TileSize + x] = Palette.GetColor(rawImage[x + TileSet.TileSize * y]).ToArgb();
|
||||
var tile = TileSetRenderer.Data(tr.type);
|
||||
var index = (tr.index < tile.Count) ? tr.index : (byte)0;
|
||||
var rawImage = tile[index];
|
||||
for (var x = 0; x < TileSetRenderer.TileSize.Width; x++)
|
||||
for (var y = 0; y < TileSetRenderer.TileSize.Height; y++)
|
||||
p[(j * TileSetRenderer.TileSize.Width + y) * stride + i * TileSetRenderer.TileSize.Width + x] = Palette.GetColor(rawImage[x + TileSetRenderer.TileSize.Width * y]).ToArgb();
|
||||
|
||||
if (Map.MapResources.Value[u * ChunkSize + i, v * ChunkSize + j].type != 0)
|
||||
{
|
||||
@@ -286,12 +288,12 @@ namespace OpenRA.Editor
|
||||
int* q = (int*)srcdata.Scan0.ToPointer();
|
||||
var srcstride = srcdata.Stride >> 2;
|
||||
|
||||
for (var x = 0; x < TileSet.TileSize; x++)
|
||||
for (var y = 0; y < TileSet.TileSize; y++)
|
||||
for (var x = 0; x < TileSetRenderer.TileSize.Width; x++)
|
||||
for (var y = 0; y < TileSetRenderer.TileSize.Height; y++)
|
||||
{
|
||||
var c = q[y * srcstride + x];
|
||||
if ((c & 0xff000000) != 0) /* quick & dirty, i cbf doing real alpha */
|
||||
p[(j * TileSet.TileSize + y) * stride + i * TileSet.TileSize + x] = c;
|
||||
p[(j * TileSetRenderer.TileSize.Width + y) * stride + i * TileSetRenderer.TileSize.Width + x] = c;
|
||||
}
|
||||
|
||||
resourceImage.UnlockBits(srcdata);
|
||||
@@ -317,15 +319,15 @@ namespace OpenRA.Editor
|
||||
{
|
||||
var vX = (int)Math.Floor((mousePos.X - Offset.X) / Zoom);
|
||||
var vY = (int)Math.Floor((mousePos.Y - Offset.Y) / Zoom);
|
||||
return new CPos(vX / TileSet.TileSize, vY / TileSet.TileSize);
|
||||
return new CPos(vX / TileSetRenderer.TileSize.Width, vY / TileSetRenderer.TileSize.Height);
|
||||
}
|
||||
|
||||
public CPos GetBrushLocationBR()
|
||||
{
|
||||
var vX = (int)Math.Floor((mousePos.X - Offset.X) / Zoom);
|
||||
var vY = (int)Math.Floor((mousePos.Y - Offset.Y) / Zoom);
|
||||
return new CPos((vX + TileSet.TileSize - 1) / TileSet.TileSize,
|
||||
(vY + TileSet.TileSize - 1) / TileSet.TileSize);
|
||||
return new CPos((vX + TileSetRenderer.TileSize.Width - 1) / TileSetRenderer.TileSize.Width,
|
||||
(vY + TileSetRenderer.TileSize.Height - 1) / TileSetRenderer.TileSize.Height);
|
||||
}
|
||||
|
||||
public void DrawActor(SGraphics g, CPos p, ActorTemplate t, ColorPalette cp)
|
||||
@@ -339,11 +341,11 @@ namespace OpenRA.Editor
|
||||
|
||||
float2 GetDrawPosition(CPos location, Bitmap bmp, bool centered)
|
||||
{
|
||||
float offsetX = centered ? bmp.Width / 2 - TileSet.TileSize / 2 : 0;
|
||||
float drawX = TileSet.TileSize * location.X * Zoom + Offset.X - offsetX;
|
||||
float offsetX = centered ? bmp.Width / 2 - TileSetRenderer.TileSize.Width / 2 : 0;
|
||||
float drawX = TileSetRenderer.TileSize.Width * location.X * Zoom + Offset.X - offsetX;
|
||||
|
||||
float offsetY = centered ? bmp.Height / 2 - TileSet.TileSize / 2 : 0;
|
||||
float drawY = TileSet.TileSize * location.Y * Zoom + Offset.Y - offsetY;
|
||||
float offsetY = centered ? bmp.Height / 2 - TileSetRenderer.TileSize.Height / 2 : 0;
|
||||
float drawY = TileSetRenderer.TileSize.Height * location.Y * Zoom + Offset.Y - offsetY;
|
||||
|
||||
return new float2(drawX, drawY);
|
||||
}
|
||||
@@ -411,24 +413,24 @@ namespace OpenRA.Editor
|
||||
|
||||
var bmp = Chunks[x];
|
||||
|
||||
var drawX = TileSet.TileSize * (float)ChunkSize * (float)x.X * Zoom + Offset.X;
|
||||
var drawY = TileSet.TileSize * (float)ChunkSize * (float)x.Y * Zoom + Offset.Y;
|
||||
var drawX = TileSetRenderer.TileSize.Width * (float)ChunkSize * (float)x.X * Zoom + Offset.X;
|
||||
var drawY = TileSetRenderer.TileSize.Height * (float)ChunkSize * (float)x.Y * Zoom + Offset.Y;
|
||||
RectangleF sourceRect = new RectangleF(0, 0, bmp.Width, bmp.Height);
|
||||
RectangleF destRect = new RectangleF(drawX, drawY, bmp.Width * Zoom, bmp.Height * Zoom);
|
||||
e.Graphics.DrawImage(bmp, destRect, sourceRect, GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
e.Graphics.DrawRectangle(CordonPen,
|
||||
Map.Bounds.Left * TileSet.TileSize * Zoom + Offset.X,
|
||||
Map.Bounds.Top * TileSet.TileSize * Zoom + Offset.Y,
|
||||
Map.Bounds.Width * TileSet.TileSize * Zoom,
|
||||
Map.Bounds.Height * TileSet.TileSize * Zoom);
|
||||
Map.Bounds.Left * TileSetRenderer.TileSize.Width * Zoom + Offset.X,
|
||||
Map.Bounds.Top * TileSetRenderer.TileSize.Height * Zoom + Offset.Y,
|
||||
Map.Bounds.Width * TileSetRenderer.TileSize.Width * Zoom,
|
||||
Map.Bounds.Height * TileSetRenderer.TileSize.Height * Zoom);
|
||||
|
||||
e.Graphics.DrawRectangle(SelectionPen,
|
||||
(SelectionStart.X * TileSet.TileSize * Zoom) + Offset.X,
|
||||
(SelectionStart.Y * TileSet.TileSize * Zoom) + Offset.Y,
|
||||
(SelectionEnd - SelectionStart).X * TileSet.TileSize * Zoom,
|
||||
(SelectionEnd - SelectionStart).Y * TileSet.TileSize * Zoom);
|
||||
(SelectionStart.X * TileSetRenderer.TileSize.Width * Zoom) + Offset.X,
|
||||
(SelectionStart.Y * TileSetRenderer.TileSize.Height * Zoom) + Offset.Y,
|
||||
(SelectionEnd - SelectionStart).X * TileSetRenderer.TileSize.Width * Zoom,
|
||||
(SelectionEnd - SelectionStart).Y * TileSetRenderer.TileSize.Height * Zoom);
|
||||
|
||||
if (IsPaste)
|
||||
{
|
||||
@@ -437,10 +439,10 @@ namespace OpenRA.Editor
|
||||
var height = Math.Abs((SelectionStart - SelectionEnd).Y);
|
||||
|
||||
e.Graphics.DrawRectangle(PastePen,
|
||||
(loc.X * TileSet.TileSize * Zoom) + Offset.X,
|
||||
(loc.Y * TileSet.TileSize * Zoom) + Offset.Y,
|
||||
width * (TileSet.TileSize * Zoom),
|
||||
height * (TileSet.TileSize * Zoom));
|
||||
(loc.X * TileSetRenderer.TileSize.Width * Zoom) + Offset.X,
|
||||
(loc.Y * TileSetRenderer.TileSize.Height * Zoom) + Offset.Y,
|
||||
width * (TileSetRenderer.TileSize.Width * Zoom),
|
||||
height * (TileSetRenderer.TileSize.Height * Zoom));
|
||||
}
|
||||
|
||||
foreach (var ar in Map.Actors.Value)
|
||||
@@ -456,8 +458,8 @@ namespace OpenRA.Editor
|
||||
foreach (var ar in Map.Actors.Value)
|
||||
if (!ar.Key.StartsWith("Actor")) // if it has a custom name
|
||||
e.Graphics.DrawStringContrast(Font, ar.Key,
|
||||
(int)(ar.Value.Location().X * TileSet.TileSize * Zoom + Offset.X),
|
||||
(int)(ar.Value.Location().Y * TileSet.TileSize * Zoom + Offset.Y),
|
||||
(int)(ar.Value.Location().X * TileSetRenderer.TileSize.Width * Zoom + Offset.X),
|
||||
(int)(ar.Value.Location().Y * TileSetRenderer.TileSize.Height * Zoom + Offset.Y),
|
||||
Brushes.White,
|
||||
Brushes.Black);
|
||||
|
||||
@@ -467,7 +469,7 @@ namespace OpenRA.Editor
|
||||
{
|
||||
if (i % 8 == 0)
|
||||
{
|
||||
PointF point = new PointF(i * TileSet.TileSize * Zoom + Offset.X, (Map.Bounds.Top - 8) * TileSet.TileSize * Zoom + Offset.Y);
|
||||
PointF point = new PointF(i * TileSetRenderer.TileSize.Width * Zoom + Offset.X, (Map.Bounds.Top - 8) * TileSetRenderer.TileSize.Height * Zoom + Offset.Y);
|
||||
e.Graphics.DrawString((i - Map.Bounds.Left).ToString(), MarkerFont, TextBrush, point);
|
||||
}
|
||||
}
|
||||
@@ -476,7 +478,7 @@ namespace OpenRA.Editor
|
||||
{
|
||||
if (i % 8 == 0)
|
||||
{
|
||||
PointF point = new PointF((Map.Bounds.Left - 8) * TileSet.TileSize * Zoom + Offset.X, i * TileSet.TileSize * Zoom + Offset.Y);
|
||||
PointF point = new PointF((Map.Bounds.Left - 8) * TileSetRenderer.TileSize.Width * Zoom + Offset.X, i * TileSetRenderer.TileSize.Height * Zoom + Offset.Y);
|
||||
e.Graphics.DrawString((i - Map.Bounds.Left).ToString(), MarkerFont, TextBrush, point);
|
||||
}
|
||||
}
|
||||
|
||||
97
OpenRA.FileFormats/Graphics/TileSetRenderer.cs
Normal file
97
OpenRA.FileFormats/Graphics/TileSetRenderer.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
#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.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
public class TileSetRenderer
|
||||
{
|
||||
public TileSet TileSet;
|
||||
Dictionary<ushort, List<byte[]>> templates;
|
||||
public Size TileSize;
|
||||
|
||||
List<byte[]> LoadTemplate(string filename, string[] exts, Cache<string, R8Reader> r8Cache, int[] frames)
|
||||
{
|
||||
if (exts.Contains(".R8") && FileSystem.Exists(filename+".R8"))
|
||||
{
|
||||
var data = new List<byte[]>();
|
||||
|
||||
foreach (var f in frames)
|
||||
data.Add(f >= 0 ? r8Cache[filename][f].Image : null);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
using (var s = FileSystem.OpenWithExts(filename, exts))
|
||||
return new Terrain(s).TileBitmapBytes;
|
||||
}
|
||||
|
||||
public TileSetRenderer(TileSet tileset, Size tileSize)
|
||||
{
|
||||
this.TileSet = tileset;
|
||||
this.TileSize = tileSize;
|
||||
|
||||
templates = new Dictionary<ushort, List<byte[]>>();
|
||||
var r8Cache = new Cache<string, R8Reader>(s => new R8Reader(FileSystem.OpenWithExts(s, ".R8")));
|
||||
foreach (var t in TileSet.Templates)
|
||||
templates.Add(t.Key, LoadTemplate(t.Value.Image, tileset.Extensions, r8Cache, t.Value.Frames));
|
||||
}
|
||||
|
||||
public Bitmap RenderTemplate(ushort id, Palette p)
|
||||
{
|
||||
var template = TileSet.Templates[id];
|
||||
var templateData = templates[id];
|
||||
|
||||
var bitmap = new Bitmap(TileSize.Width * template.Size.X, TileSize.Height * template.Size.Y,
|
||||
PixelFormat.Format8bppIndexed);
|
||||
|
||||
bitmap.Palette = p.AsSystemPalette();
|
||||
|
||||
var data = bitmap.LockBits(bitmap.Bounds(),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
|
||||
|
||||
unsafe
|
||||
{
|
||||
var q = (byte*)data.Scan0.ToPointer();
|
||||
var stride = data.Stride;
|
||||
|
||||
for (var u = 0; u < template.Size.X; u++)
|
||||
for (var v = 0; v < template.Size.Y; v++)
|
||||
if (templateData[u + v * template.Size.X] != null)
|
||||
{
|
||||
var rawImage = templateData[u + v * template.Size.X];
|
||||
for (var i = 0; i < TileSize.Width; i++)
|
||||
for (var j = 0; j < TileSize.Height; j++)
|
||||
q[(v * TileSize.Width + j) * stride + u * TileSize.Width + i] = rawImage[i + TileSize.Width * j];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < TileSize.Width; i++)
|
||||
for (var j = 0; j < TileSize.Height; j++)
|
||||
q[(v * TileSize.Width + j) * stride + u * TileSize.Width + i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bitmap.UnlockBits(data);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public List<byte[]> Data(ushort id)
|
||||
{
|
||||
return templates[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,16 +16,15 @@ namespace OpenRA.FileFormats
|
||||
public class Terrain
|
||||
{
|
||||
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
|
||||
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);
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
public ushort Id;
|
||||
public string Image;
|
||||
public int[] Frames;
|
||||
public int2 Size;
|
||||
public bool PickAny;
|
||||
public string Category;
|
||||
@@ -52,7 +53,7 @@ namespace OpenRA.FileFormats
|
||||
t => t.Value.Value);
|
||||
}
|
||||
|
||||
static readonly string[] Fields = { "Id", "Image", "Size", "PickAny" };
|
||||
static readonly string[] Fields = { "Id", "Image", "Frames", "Size", "PickAny" };
|
||||
|
||||
public MiniYaml Save()
|
||||
{
|
||||
@@ -71,24 +72,22 @@ namespace OpenRA.FileFormats
|
||||
|
||||
return new MiniYaml(null, root);
|
||||
}
|
||||
|
||||
public Terrain Data;
|
||||
}
|
||||
|
||||
public class TileSet
|
||||
{
|
||||
public string Name;
|
||||
public string Id;
|
||||
public int SheetSize = 512;
|
||||
public string Palette;
|
||||
public string PlayerPalette;
|
||||
public int TileSize = 24;
|
||||
public string[] Extensions;
|
||||
public int WaterPaletteRotationBase = 0x60;
|
||||
public Dictionary<string, TerrainTypeInfo> Terrain = new Dictionary<string, TerrainTypeInfo>();
|
||||
public Dictionary<ushort, TileTemplate> Templates = new Dictionary<ushort, TileTemplate>();
|
||||
public string[] EditorTemplateOrder;
|
||||
|
||||
static readonly string[] fields = {"Name", "TileSize", "Id", "Palette", "Extensions"};
|
||||
static readonly string[] fields = {"Name", "TileSize", "Id", "SheetSize", "Palette", "Extensions"};
|
||||
|
||||
public TileSet() {}
|
||||
|
||||
@@ -108,14 +107,6 @@ namespace OpenRA.FileFormats
|
||||
.Select(y => new TileTemplate(y)).ToDictionary(t => t.Id);
|
||||
}
|
||||
|
||||
public void LoadTiles()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
public void Save(string filepath)
|
||||
{
|
||||
var root = new List<MiniYamlNode>();
|
||||
@@ -144,23 +135,6 @@ namespace OpenRA.FileFormats
|
||||
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)
|
||||
{
|
||||
var tt = Templates[r.type].Tiles;
|
||||
@@ -170,43 +144,5 @@ namespace OpenRA.FileFormats
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Bitmap RenderTemplate(ushort n, Palette p)
|
||||
{
|
||||
var template = Templates[n];
|
||||
|
||||
var bitmap = new Bitmap(TileSize * template.Size.X, TileSize * template.Size.Y,
|
||||
PixelFormat.Format8bppIndexed);
|
||||
|
||||
bitmap.Palette = p.AsSystemPalette();
|
||||
|
||||
var data = bitmap.LockBits(bitmap.Bounds(),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
|
||||
|
||||
unsafe
|
||||
{
|
||||
byte* q = (byte*)data.Scan0.ToPointer();
|
||||
var stride = data.Stride;
|
||||
|
||||
for (var u = 0; u < template.Size.X; u++)
|
||||
for (var v = 0; v < template.Size.Y; v++)
|
||||
if (template.Data.TileBitmapBytes[u + v * template.Size.X] != null)
|
||||
{
|
||||
var rawImage = template.Data.TileBitmapBytes[u + v * template.Size.X];
|
||||
for (var i = 0; i < TileSize; i++)
|
||||
for (var j = 0; j < TileSize; j++)
|
||||
q[(v * TileSize + j) * stride + u * TileSize + i] = rawImage[i + TileSize * j];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < TileSize; i++)
|
||||
for (var j = 0; j < TileSize; j++)
|
||||
q[(v * TileSize + j) * stride + u * TileSize + i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bitmap.UnlockBits(data);
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
<Compile Include="FileFormats\WavLoader.cs" />
|
||||
<Compile Include="Filesystem\D2kSoundResources.cs" />
|
||||
<Compile Include="Graphics\R8Reader.cs" />
|
||||
<Compile Include="Graphics\TileSetRenderer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
class TerrainRenderer
|
||||
{
|
||||
SheetBuilder sheetBuilder;
|
||||
IVertexBuffer<Vertex> vertexBuffer;
|
||||
|
||||
World world;
|
||||
@@ -29,23 +28,6 @@ namespace OpenRA.Graphics
|
||||
this.world = world;
|
||||
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 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<IRenderOverlay>())
|
||||
r.Render(wr);
|
||||
|
||||
85
OpenRA.Game/Graphics/Theater.cs
Normal file
85
OpenRA.Game/Graphics/Theater.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
#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, Cache<string, R8Reader> r8Cache, int[] frames)
|
||||
{
|
||||
if (exts.Contains(".R8") && FileSystem.Exists(filename+".R8"))
|
||||
{
|
||||
return frames.Select(f =>
|
||||
{
|
||||
if (f < 0)
|
||||
return null;
|
||||
|
||||
var image = r8Cache[filename][f];
|
||||
return sheetBuilder.Add(image.Image, new Size(image.Size.Width, image.Size.Height));
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
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. Try increasing the tileset SheetSize parameter.");
|
||||
allocated = true;
|
||||
|
||||
return new Sheet(new Size(tileset.SheetSize, tileset.SheetSize));
|
||||
};
|
||||
|
||||
var r8Cache = new Cache<string, R8Reader>(s => new R8Reader(FileSystem.OpenWithExts(s, ".R8")));
|
||||
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, r8Cache, t.Value.Frames));
|
||||
|
||||
// 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 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);
|
||||
|
||||
@@ -117,10 +120,6 @@ namespace OpenRA.Graphics
|
||||
Game.Renderer.EnableScissor(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
|
||||
|
||||
terrainRenderer.Draw(this, Game.viewport);
|
||||
foreach (var a in world.traitDict.ActorsWithTraitMultiple<IRenderAsTerrain>(world))
|
||||
foreach (var r in a.Trait.RenderAsTerrain(this, a.Actor))
|
||||
r.Render(this);
|
||||
|
||||
Game.Renderer.Flush();
|
||||
|
||||
for (var i = 0; i < renderables.Count; i++)
|
||||
|
||||
@@ -235,6 +235,7 @@
|
||||
<Compile Include="Graphics\ContrailRenderable.cs" />
|
||||
<Compile Include="Widgets\ViewportControllerWidget.cs" />
|
||||
<Compile Include="Traits\Player\FrozenActorLayer.cs" />
|
||||
<Compile Include="Graphics\Theater.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||
|
||||
@@ -192,7 +192,6 @@ namespace OpenRA.Traits
|
||||
public interface IPostRender { void RenderAfterWorld(WorldRenderer wr, Actor self); }
|
||||
|
||||
public interface IPostRenderSelection { void RenderAfterWorld(WorldRenderer wr); }
|
||||
public interface IRenderAsTerrain { IEnumerable<IRenderable> RenderAsTerrain(WorldRenderer wr, Actor self); }
|
||||
public interface IBodyOrientation
|
||||
{
|
||||
WAngle CameraPitch { get; }
|
||||
|
||||
@@ -119,8 +119,6 @@ namespace OpenRA
|
||||
Map = map;
|
||||
|
||||
TileSet = Rules.TileSets[Map.Tileset];
|
||||
TileSet.LoadTiles();
|
||||
|
||||
SharedRandom = new XRandom(orderManager.LobbyInfo.GlobalSettings.RandomSeed);
|
||||
|
||||
WorldActor = CreateActor( "World", new TypeDictionary() );
|
||||
|
||||
@@ -67,14 +67,10 @@ namespace OpenRA.Mods.RA
|
||||
}
|
||||
}
|
||||
|
||||
class Bridge: IRenderAsTerrain, INotifyDamageStateChanged
|
||||
class Bridge: IRender, INotifyDamageStateChanged
|
||||
{
|
||||
static string cachedTileset;
|
||||
static Cache<TileReference<ushort,byte>, Sprite> sprites;
|
||||
|
||||
Dictionary<ushort, Dictionary<CPos, Sprite>> TileSprites = new Dictionary<ushort, Dictionary<CPos, Sprite>>();
|
||||
Dictionary<ushort, TileTemplate> Templates = new Dictionary<ushort, TileTemplate>();
|
||||
ushort currentTemplate;
|
||||
ushort template;
|
||||
Dictionary<CPos, byte> footprint;
|
||||
|
||||
Actor self;
|
||||
BridgeInfo Info;
|
||||
@@ -91,38 +87,21 @@ namespace OpenRA.Mods.RA
|
||||
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;
|
||||
|
||||
// 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)]));
|
||||
}
|
||||
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<ushort, byte>(currentTemplate,(byte)index));
|
||||
var index = dx.X + self.World.TileSet.Templates[template].Size.X * dx.Y;
|
||||
return self.World.TileSet.GetTerrainType(new TileReference<ushort, byte>(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;
|
||||
public IEnumerable<IRenderable> RenderAsTerrain(WorldRenderer wr, Actor self)
|
||||
IRenderable[] TemplateRenderables(WorldRenderer wr, PaletteReference palette, ushort template)
|
||||
{
|
||||
if (initializePalettes)
|
||||
return footprint.Select(c => (IRenderable)(new SpriteRenderable(
|
||||
wr.Theater.TileSprite(new TileReference<ushort, byte>(template, c.Value)),
|
||||
c.Key.CenterPosition, WVec.Zero, -512, palette, 1f, true))).ToArray();
|
||||
}
|
||||
|
||||
bool initialized;
|
||||
Dictionary<ushort, IRenderable[]> renderables;
|
||||
public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
terrainPalette = wr.Palette("terrain");
|
||||
initializePalettes = false;
|
||||
var palette = wr.Palette("terrain");
|
||||
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])
|
||||
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<IPositionable>() && !a.Trait<IPositionable>().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<DomainIndex>();
|
||||
if (domainIndex != null)
|
||||
domainIndex.UpdateCells(self.World, TileSprites[currentTemplate].Keys);
|
||||
domainIndex.UpdateCells(self.World, footprint.Keys);
|
||||
|
||||
if (LongBridgeSegmentIsDead() && !killedUnits)
|
||||
{
|
||||
|
||||
@@ -366,7 +366,6 @@ namespace OpenRA.TilesetBuilder
|
||||
{
|
||||
Name = tilesetName,
|
||||
Id = tilesetID.ToUpper(),
|
||||
TileSize = size,
|
||||
Palette = tilesetPalette.ToLower(),
|
||||
Extensions = new string[] { ext[0], ext[1] }
|
||||
};
|
||||
|
||||
@@ -341,7 +341,7 @@ namespace OpenRA.Utility
|
||||
if (tileset == null)
|
||||
throw new InvalidOperationException("No theater named '{0}'".F(theater));
|
||||
|
||||
tileset.LoadTiles();
|
||||
var renderer = new TileSetRenderer(tileset, new Size(manifest.TileSize, manifest.TileSize));
|
||||
var palette = new Palette(FileSystem.Open(tileset.Palette), shadowIndex);
|
||||
|
||||
foreach (var templateName in templateNames)
|
||||
@@ -350,7 +350,7 @@ namespace OpenRA.Utility
|
||||
if (template.Value == null)
|
||||
throw new InvalidOperationException("No such template '{0}'".F(templateName));
|
||||
|
||||
using (var image = tileset.RenderTemplate(template.Value.Id, palette))
|
||||
using (var image = renderer.RenderTemplate(template.Value.Id, palette))
|
||||
image.Save(Path.ChangeExtension(templateName, ".png"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
General:
|
||||
Name: Arrakis
|
||||
TileSize: 32
|
||||
Id: ARRAKIS
|
||||
SheetSize: 1024
|
||||
Palette: d2k.pal
|
||||
Extensions: .bas,.bat,.bgb,.ice,.tre,.was,.ext,.shp
|
||||
|
||||
|
||||
Reference in New Issue
Block a user