Merge pull request #3282 from pchote/renderer-refactoring
Renderer refactoring - SheetBuilder
This commit is contained in:
@@ -39,17 +39,17 @@ namespace OpenRA.FileFormats
|
|||||||
public bool PickAny;
|
public bool PickAny;
|
||||||
public string Category;
|
public string Category;
|
||||||
|
|
||||||
[FieldLoader.LoadUsing( "LoadTiles" )]
|
[FieldLoader.LoadUsing("LoadTiles")]
|
||||||
public Dictionary<byte, string> Tiles = new Dictionary<byte, string>();
|
public Dictionary<byte, string> Tiles = new Dictionary<byte, string>();
|
||||||
|
|
||||||
public TileTemplate() {}
|
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(
|
return y.NodesDict["Tiles"].NodesDict.ToDictionary(
|
||||||
t => byte.Parse(t.Key),
|
t => byte.Parse(t.Key),
|
||||||
t => t.Value.Value );
|
t => t.Value.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static readonly string[] Fields = { "Id", "Image", "Size", "PickAny" };
|
static readonly string[] Fields = { "Id", "Image", "Size", "PickAny" };
|
||||||
@@ -60,12 +60,14 @@ namespace OpenRA.FileFormats
|
|||||||
foreach (var field in Fields)
|
foreach (var field in Fields)
|
||||||
{
|
{
|
||||||
FieldInfo f = this.GetType().GetField(field);
|
FieldInfo f = this.GetType().GetField(field);
|
||||||
if (f.GetValue(this) == null) continue;
|
if (f.GetValue(this) == null)
|
||||||
root.Add( new MiniYamlNode( field, FieldSaver.FormatValue( this, f ) ) );
|
continue;
|
||||||
|
|
||||||
|
root.Add(new MiniYamlNode(field, FieldSaver.FormatValue(this, f)));
|
||||||
}
|
}
|
||||||
|
|
||||||
root.Add( new MiniYamlNode( "Tiles", null,
|
root.Add(new MiniYamlNode("Tiles", null,
|
||||||
Tiles.Select( x => new MiniYamlNode( x.Key.ToString(), x.Value ) ).ToList() ) );
|
Tiles.Select(x => new MiniYamlNode(x.Key.ToString(), x.Value)).ToList()));
|
||||||
|
|
||||||
return new MiniYaml(null, root);
|
return new MiniYaml(null, root);
|
||||||
}
|
}
|
||||||
@@ -90,9 +92,9 @@ namespace OpenRA.FileFormats
|
|||||||
|
|
||||||
public TileSet() {}
|
public TileSet() {}
|
||||||
|
|
||||||
public TileSet( string filepath )
|
public TileSet(string filepath)
|
||||||
{
|
{
|
||||||
var yaml = MiniYaml.DictFromFile( filepath );
|
var yaml = MiniYaml.DictFromFile(filepath);
|
||||||
|
|
||||||
// General info
|
// General info
|
||||||
FieldLoader.Load(this, yaml["General"]);
|
FieldLoader.Load(this, yaml["General"]);
|
||||||
@@ -110,7 +112,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, TileSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,33 +124,35 @@ namespace OpenRA.FileFormats
|
|||||||
foreach (var field in fields)
|
foreach (var field in fields)
|
||||||
{
|
{
|
||||||
FieldInfo f = this.GetType().GetField(field);
|
FieldInfo f = this.GetType().GetField(field);
|
||||||
if (f.GetValue(this) == null) continue;
|
if (f.GetValue(this) == null)
|
||||||
gen.Add( new MiniYamlNode( field, FieldSaver.FormatValue( this, f ) ) );
|
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,
|
root.Add(new MiniYamlNode( "Terrain", null,
|
||||||
Terrain.Select( t => new MiniYamlNode(
|
Terrain.Select(t => new MiniYamlNode(
|
||||||
"TerrainType@{0}".F( t.Value.Type ),
|
"TerrainType@{0}".F(t.Value.Type),
|
||||||
t.Value.Save() ) ).ToList() ) );
|
t.Value.Save())).ToList()));
|
||||||
|
|
||||||
root.Add( new MiniYamlNode( "Templates", null,
|
root.Add(new MiniYamlNode("Templates", null,
|
||||||
Templates.Select( t => new MiniYamlNode(
|
Templates.Select(t => new MiniYamlNode(
|
||||||
"Template@{0}".F( t.Value.Id ),
|
"Template@{0}".F(t.Value.Id),
|
||||||
t.Value.Save() ) ).ToList() ) );
|
t.Value.Save())).ToList()));
|
||||||
root.WriteToFile(filepath);
|
root.WriteToFile(filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] GetBytes(TileReference<ushort,byte> r)
|
public byte[] GetBytes(TileReference<ushort,byte> r)
|
||||||
{
|
{
|
||||||
TileTemplate tile;
|
TileTemplate tile;
|
||||||
if( Templates.TryGetValue( r.type, out tile ) )
|
if (Templates.TryGetValue(r.type, out tile))
|
||||||
return tile.Data.TileBitmapBytes[ r.index ];
|
return tile.Data.TileBitmapBytes[r.index];
|
||||||
|
|
||||||
byte[] missingTile = new byte[ TileSize * TileSize ];
|
byte[] missingTile = new byte[TileSize*TileSize];
|
||||||
for( int i = 0 ; i < missingTile.Length ; i++ )
|
for (var i = 0; i < missingTile.Length; i++)
|
||||||
missingTile[ i ] = 0x36;
|
missingTile[i] = 0x36;
|
||||||
|
|
||||||
return missingTile;
|
return missingTile;
|
||||||
}
|
}
|
||||||
@@ -159,6 +163,7 @@ namespace OpenRA.FileFormats
|
|||||||
string ret;
|
string ret;
|
||||||
if (!tt.TryGetValue(r.index, out ret))
|
if (!tt.TryGetValue(r.index, out ret))
|
||||||
return "Clear"; // Default walkable
|
return "Clear"; // Default walkable
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,15 +24,13 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
public string Name { get { return name; } }
|
public string Name { get { return name; } }
|
||||||
|
|
||||||
public Animation( string name )
|
public Animation(string name)
|
||||||
: this( name, () => 0 )
|
: this(name, () => 0) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public Animation( string name, Func<int> facingFunc )
|
public Animation(string name, Func<int> facingFunc)
|
||||||
{
|
{
|
||||||
this.name = name.ToLowerInvariant();
|
this.name = name.ToLowerInvariant();
|
||||||
this.tickFunc = () => { };
|
this.tickFunc = () => {};
|
||||||
this.facingFunc = facingFunc;
|
this.facingFunc = facingFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,12 +44,12 @@ namespace OpenRA.Graphics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Play( string sequenceName )
|
public void Play(string sequenceName)
|
||||||
{
|
{
|
||||||
PlayThen(sequenceName, null);
|
PlayThen(sequenceName, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayRepeating( string sequenceName )
|
public void PlayRepeating(string sequenceName)
|
||||||
{
|
{
|
||||||
backwards = false;
|
backwards = false;
|
||||||
tickAlways = false;
|
tickAlways = false;
|
||||||
@@ -75,16 +73,16 @@ namespace OpenRA.Graphics
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayThen( string sequenceName, Action after )
|
public void PlayThen(string sequenceName, Action after)
|
||||||
{
|
{
|
||||||
backwards = false;
|
backwards = false;
|
||||||
tickAlways = false;
|
tickAlways = false;
|
||||||
CurrentSequence = SequenceProvider.GetSequence( name, sequenceName );
|
CurrentSequence = SequenceProvider.GetSequence(name, sequenceName);
|
||||||
frame = 0;
|
frame = 0;
|
||||||
tickFunc = () =>
|
tickFunc = () =>
|
||||||
{
|
{
|
||||||
++frame;
|
++frame;
|
||||||
if( frame >= CurrentSequence.Length )
|
if (frame >= CurrentSequence.Length)
|
||||||
{
|
{
|
||||||
frame = CurrentSequence.Length - 1;
|
frame = CurrentSequence.Length - 1;
|
||||||
tickFunc = () => { };
|
tickFunc = () => { };
|
||||||
@@ -99,11 +97,11 @@ namespace OpenRA.Graphics
|
|||||||
backwards = true;
|
backwards = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayFetchIndex( string sequenceName, Func<int> func )
|
public void PlayFetchIndex(string sequenceName, Func<int> func)
|
||||||
{
|
{
|
||||||
backwards = false;
|
backwards = false;
|
||||||
tickAlways = true;
|
tickAlways = true;
|
||||||
CurrentSequence = SequenceProvider.GetSequence( name, sequenceName );
|
CurrentSequence = SequenceProvider.GetSequence(name, sequenceName);
|
||||||
frame = func();
|
frame = func();
|
||||||
tickFunc = () => frame = func();
|
tickFunc = () => frame = func();
|
||||||
}
|
}
|
||||||
@@ -113,19 +111,19 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
public void Tick()
|
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();
|
tickFunc();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
timeUntilNextFrame -= t;
|
timeUntilNextFrame -= t;
|
||||||
while( timeUntilNextFrame <= 0 )
|
while (timeUntilNextFrame <= 0)
|
||||||
{
|
{
|
||||||
tickFunc();
|
tickFunc();
|
||||||
timeUntilNextFrame += CurrentSequence != null ? CurrentSequence.Tick : 40; // 25 fps == 40 ms
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
using OpenRA.FileFormats;
|
using OpenRA.FileFormats;
|
||||||
using OpenRA.FileFormats.Graphics;
|
using OpenRA.FileFormats.Graphics;
|
||||||
|
|
||||||
@@ -16,21 +17,44 @@ namespace OpenRA.Graphics
|
|||||||
{
|
{
|
||||||
public class Sheet
|
public class Sheet
|
||||||
{
|
{
|
||||||
Bitmap bitmap;
|
|
||||||
ITexture texture;
|
ITexture texture;
|
||||||
bool dirty;
|
bool dirty;
|
||||||
byte[] data;
|
public byte[] Data { get; private set; }
|
||||||
public readonly Size Size;
|
public readonly Size Size;
|
||||||
|
|
||||||
public Sheet(Size size)
|
public Sheet(Size size)
|
||||||
{
|
{
|
||||||
Size = size;
|
Size = size;
|
||||||
|
Data = new byte[4*Size.Width*Size.Height];
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sheet(string filename)
|
public Sheet(string filename)
|
||||||
{
|
{
|
||||||
bitmap = (Bitmap)Image.FromStream(FileSystem.Open(filename));
|
var bitmap = (Bitmap)Image.FromStream(FileSystem.Open(filename));
|
||||||
Size = bitmap.Size;
|
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
|
public ITexture Texture
|
||||||
@@ -45,23 +69,61 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
if (dirty)
|
if (dirty)
|
||||||
{
|
{
|
||||||
if (data != null)
|
texture.SetData(Data, Size.Width, Size.Height);
|
||||||
{
|
dirty = false;
|
||||||
texture.SetData(data, Size.Width, Size.Height);
|
|
||||||
dirty = false;
|
|
||||||
}
|
|
||||||
else if (bitmap != null)
|
|
||||||
{
|
|
||||||
texture.SetData(bitmap);
|
|
||||||
dirty = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return texture;
|
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; }
|
public void MakeDirty() { dirty = true; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,68 +8,69 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
|
||||||
namespace OpenRA.Graphics
|
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
|
public class SheetBuilder
|
||||||
{
|
{
|
||||||
internal SheetBuilder(TextureChannel ch)
|
Sheet current;
|
||||||
|
TextureChannel channel;
|
||||||
|
SheetType type;
|
||||||
|
int rowHeight = 0;
|
||||||
|
Point p;
|
||||||
|
|
||||||
|
internal SheetBuilder(SheetType t)
|
||||||
{
|
{
|
||||||
current = null;
|
current = new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize));;
|
||||||
rowHeight = 0;
|
channel = TextureChannel.Red;
|
||||||
channel = null;
|
type = t;
|
||||||
initialChannel = ch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
Util.FastCopyIntoChannel(rect, src);
|
||||||
return rect;
|
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];
|
var data = new byte[size.Width * size.Height];
|
||||||
for (int i = 0; i < data.Length; i++)
|
for (var i = 0; i < data.Length; i++)
|
||||||
data[i] = paletteIndex;
|
data[i] = paletteIndex;
|
||||||
|
|
||||||
return Add(data, size);
|
return Add(data, size, allowSheetOverflow);
|
||||||
}
|
}
|
||||||
|
|
||||||
Sheet NewSheet() { return new Sheet(new Size( Renderer.SheetSize, Renderer.SheetSize ) ); }
|
TextureChannel? NextChannel(TextureChannel t)
|
||||||
|
|
||||||
Sheet current = null;
|
|
||||||
int rowHeight = 0;
|
|
||||||
Point p;
|
|
||||||
TextureChannel? channel = null;
|
|
||||||
TextureChannel initialChannel;
|
|
||||||
|
|
||||||
TextureChannel? NextChannel(TextureChannel? t)
|
|
||||||
{
|
{
|
||||||
if (t == null)
|
var nextChannel = (int)t + (int)type;
|
||||||
return initialChannel;
|
if (nextChannel > (int)TextureChannel.Alpha)
|
||||||
|
return null;
|
||||||
|
|
||||||
switch (t.Value)
|
return (TextureChannel)nextChannel;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
if (imageSize.Width + p.X > current.Size.Width)
|
||||||
{
|
{
|
||||||
p = new Point(0, p.Y + rowHeight);
|
p = new Point(0, p.Y + rowHeight);
|
||||||
@@ -81,22 +82,29 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
if (p.Y + imageSize.Height > current.Size.Height)
|
if (p.Y + imageSize.Height > current.Size.Height)
|
||||||
{
|
{
|
||||||
|
var next = NextChannel(channel);
|
||||||
if (null == (channel = NextChannel(channel)))
|
if (next == null)
|
||||||
{
|
{
|
||||||
current = NewSheet();
|
if (!allowSheetOverflow)
|
||||||
channel = NextChannel(channel);
|
throw new SheetOverflowException();
|
||||||
|
|
||||||
|
current = new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize));
|
||||||
|
channel = TextureChannel.Red;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
channel = next.Value;
|
||||||
|
|
||||||
rowHeight = imageSize.Height;
|
rowHeight = imageSize.Height;
|
||||||
p = new Point(0,0);
|
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();
|
current.MakeDirty();
|
||||||
p.X += imageSize.Width;
|
p.X += imageSize.Width;
|
||||||
|
|
||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Sheet Current { get { return current; } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,57 +17,52 @@ namespace OpenRA.Graphics
|
|||||||
public readonly Rectangle bounds;
|
public readonly Rectangle bounds;
|
||||||
public readonly Sheet sheet;
|
public readonly Sheet sheet;
|
||||||
public readonly TextureChannel channel;
|
public readonly TextureChannel channel;
|
||||||
public readonly RectangleF uv;
|
|
||||||
public readonly float2 size;
|
public readonly float2 size;
|
||||||
|
readonly float2[] textureCoords;
|
||||||
readonly float2[] uvhax;
|
|
||||||
|
|
||||||
public Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel)
|
public Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel)
|
||||||
{
|
{
|
||||||
this.bounds = bounds;
|
this.bounds = bounds;
|
||||||
this.sheet = sheet;
|
this.sheet = sheet;
|
||||||
this.channel = channel;
|
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);
|
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)
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,10 @@ namespace OpenRA.Graphics
|
|||||||
glyphs = new Cache<Pair<char, Color>, GlyphInfo>(CreateGlyph,
|
glyphs = new Cache<Pair<char, Color>, GlyphInfo>(CreateGlyph,
|
||||||
Pair<char,Color>.EqualityComparer);
|
Pair<char,Color>.EqualityComparer);
|
||||||
|
|
||||||
// setup a 1-channel SheetBuilder for our private use
|
// setup a SheetBuilder for our private use
|
||||||
if (builder == null) builder = new SheetBuilder(TextureChannel.Alpha);
|
// TODO: SheetBuilder state is leaked between mod switches
|
||||||
|
if (builder == null)
|
||||||
|
builder = new SheetBuilder(SheetType.BGRA);
|
||||||
|
|
||||||
PrecacheColor(Color.White);
|
PrecacheColor(Color.White);
|
||||||
PrecacheColor(Color.Red);
|
PrecacheColor(Color.Red);
|
||||||
@@ -46,7 +48,7 @@ namespace OpenRA.Graphics
|
|||||||
throw new InvalidOperationException();
|
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
|
location.Y += size; // baseline vs top
|
||||||
|
|
||||||
@@ -84,7 +86,7 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
public int2 Measure(string text)
|
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<Pair<char,Color>, GlyphInfo> glyphs;
|
Cache<Pair<char,Color>, GlyphInfo> glyphs;
|
||||||
@@ -96,9 +98,8 @@ namespace OpenRA.Graphics
|
|||||||
face.LoadGlyph(index, LoadFlags.Default, LoadTarget.Normal);
|
face.LoadGlyph(index, LoadFlags.Default, LoadTarget.Normal);
|
||||||
face.Glyph.RenderGlyph(RenderMode.Normal);
|
face.Glyph.RenderGlyph(RenderMode.Normal);
|
||||||
|
|
||||||
var s = builder.Allocate(
|
var size = new Size((int)face.Glyph.Metrics.Width >> 6, (int)face.Glyph.Metrics.Height >> 6);
|
||||||
new Size((int)face.Glyph.Metrics.Width >> 6,
|
var s = builder.Allocate(size, true);
|
||||||
(int)face.Glyph.Metrics.Height >> 6));
|
|
||||||
|
|
||||||
var g = new GlyphInfo
|
var g = new GlyphInfo
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ namespace OpenRA.Graphics
|
|||||||
if (ImageCount == 0)
|
if (ImageCount == 0)
|
||||||
{
|
{
|
||||||
var shp = new ShpTSReader(FileSystem.OpenWithExts(filename, exts));
|
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
|
else
|
||||||
{
|
{
|
||||||
var shp = new ShpReader(FileSystem.OpenWithExts(filename, exts));
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ namespace OpenRA.Graphics
|
|||||||
{
|
{
|
||||||
class TerrainRenderer
|
class TerrainRenderer
|
||||||
{
|
{
|
||||||
|
SheetBuilder sheetBuilder;
|
||||||
IVertexBuffer<Vertex> vertexBuffer;
|
IVertexBuffer<Vertex> vertexBuffer;
|
||||||
Sheet terrainSheet;
|
|
||||||
|
|
||||||
World world;
|
World world;
|
||||||
Map map;
|
Map map;
|
||||||
@@ -29,37 +29,31 @@ namespace OpenRA.Graphics
|
|||||||
this.world = world;
|
this.world = world;
|
||||||
this.map = world.Map;
|
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<TileReference<ushort,byte>, Sprite>(
|
var tileMapping = new Cache<TileReference<ushort,byte>, Sprite>(
|
||||||
x => Game.modData.SheetBuilder.Add(world.TileSet.GetBytes(x), tileSize));
|
x => sheetBuilder.Add(world.TileSet.GetBytes(x), tileSize, false));
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
var terrainPalette = wr.Palette("terrain").Index;
|
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 (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
||||||
for( int 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 = 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);
|
Util.FastCreateQuad(vertices, Game.CellSize * new float2(i, j), tile, terrainPalette, nv, tile.size);
|
||||||
nv += 4;
|
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 = Game.Renderer.Device.CreateVertexBuffer(vertices.Length);
|
||||||
vertexBuffer.SetData( vertices, nv );
|
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);
|
int visibleRows = (int)(viewport.Height * 1f / Game.CellSize / viewport.Zoom + 2);
|
||||||
|
|
||||||
@@ -79,17 +73,22 @@ namespace OpenRA.Graphics
|
|||||||
firstRow = r.Bottom - map.Bounds.Top;
|
firstRow = r.Bottom - map.Bounds.Top;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstRow < 0) firstRow = 0;
|
// Sanity checking
|
||||||
if (lastRow > map.Bounds.Height) lastRow = map.Bounds.Height;
|
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(
|
Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer(
|
||||||
vertexBuffer, verticesPerRow * firstRow, verticesPerRow * (lastRow - firstRow),
|
vertexBuffer, verticesPerRow * firstRow, verticesPerRow * (lastRow - firstRow),
|
||||||
PrimitiveType.QuadList, terrainSheet);
|
PrimitiveType.QuadList, sheetBuilder.Current);
|
||||||
|
|
||||||
foreach (var r in world.WorldActor.TraitsImplementing<IRenderOverlay>())
|
foreach (var r in world.WorldActor.TraitsImplementing<IRenderOverlay>())
|
||||||
r.Render( wr );
|
r.Render(wr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,13 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
static readonly int[] channelMasks = { 2, 1, 0, 3 }; // yes, our channel order is nuts.
|
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 data = dest.sheet.Data;
|
||||||
var srcStride = dest.bounds.Width;
|
var srcStride = dest.bounds.Width;
|
||||||
var destStride = dest.sheet.Size.Width * 4;
|
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 destSkip = destStride - 4 * srcStride;
|
||||||
var height = dest.bounds.Height;
|
var height = dest.bounds.Height;
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ namespace OpenRA
|
|||||||
|
|
||||||
ChromeMetrics.Initialize(Manifest.ChromeMetrics);
|
ChromeMetrics.Initialize(Manifest.ChromeMetrics);
|
||||||
ChromeProvider.Initialize(Manifest.Chrome);
|
ChromeProvider.Initialize(Manifest.Chrome);
|
||||||
SheetBuilder = new SheetBuilder(TextureChannel.Red);
|
SheetBuilder = new SheetBuilder(SheetType.Indexed);
|
||||||
SpriteLoader = new SpriteLoader(new string[] { ".shp" }, SheetBuilder);
|
SpriteLoader = new SpriteLoader(new string[] { ".shp" }, SheetBuilder);
|
||||||
CursorProvider.Initialize(Manifest.Cursors);
|
CursorProvider.Initialize(Manifest.Cursors);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,9 +99,9 @@ namespace OpenRA.Mods.RA
|
|||||||
if (cachedTileset != self.World.Map.Tileset)
|
if (cachedTileset != self.World.Map.Tileset)
|
||||||
{
|
{
|
||||||
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>(
|
sprites = new Cache<TileReference<ushort,byte>, Sprite>(
|
||||||
x => Game.modData.SheetBuilder.Add(self.World.TileSet.GetBytes(x),
|
x => Game.modData.SheetBuilder.Add(self.World.TileSet.GetBytes(x), tileSize, true));
|
||||||
new Size(Game.CellSize, Game.CellSize)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache templates and tiles for the different states
|
// Cache templates and tiles for the different states
|
||||||
|
|||||||
Reference in New Issue
Block a user