git-svn-id: svn://svn.ijw.co.nz/svn/OpenRa@2049 993157c7-ee19-0410-b2c4-bb4e9862e678
This commit is contained in:
74
OpenRa.Game/Graphics/Animation.cs
Normal file
74
OpenRa.Game/Graphics/Animation.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
class Animation
|
||||
{
|
||||
readonly string name;
|
||||
Sequence currentSequence;
|
||||
int frame = 0;
|
||||
bool tickAlways;
|
||||
|
||||
public Animation( string name )
|
||||
{
|
||||
this.name = name;
|
||||
Play( "idle" );
|
||||
}
|
||||
|
||||
public Sprite[] Images { get { return new Sprite[] { currentSequence.GetSprite( frame ) }; } }
|
||||
public float2 Center { get { return 0.25f * new float2(currentSequence.GetSprite(0).bounds.Size); } }
|
||||
|
||||
public void Play( string sequenceName )
|
||||
{
|
||||
PlayThen(sequenceName, () => { });
|
||||
}
|
||||
|
||||
public void PlayRepeating( string sequenceName )
|
||||
{
|
||||
PlayThen( sequenceName, () => PlayRepeating( sequenceName ) );
|
||||
}
|
||||
|
||||
public void PlayThen( string sequenceName, Action after )
|
||||
{
|
||||
tickAlways = false;
|
||||
currentSequence = SequenceProvider.GetSequence( name, sequenceName );
|
||||
frame = 0;
|
||||
tickFunc = () =>
|
||||
{
|
||||
++frame;
|
||||
if( frame >= currentSequence.Length )
|
||||
{
|
||||
frame = currentSequence.Length - 1;
|
||||
tickFunc = () => { };
|
||||
after();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void PlayFetchIndex( string sequenceName, Func<int> func )
|
||||
{
|
||||
tickAlways = true;
|
||||
currentSequence = SequenceProvider.GetSequence( name, sequenceName );
|
||||
frame = func();
|
||||
tickFunc = () => frame = func();
|
||||
}
|
||||
|
||||
int timeUntilNextFrame;
|
||||
Action tickFunc;
|
||||
|
||||
public void Tick( int t )
|
||||
{
|
||||
if( tickAlways )
|
||||
tickFunc();
|
||||
else
|
||||
{
|
||||
timeUntilNextFrame -= t;
|
||||
while( timeUntilNextFrame <= 0 )
|
||||
{
|
||||
tickFunc();
|
||||
timeUntilNextFrame += 40; // 25 fps == 40 ms
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
131
OpenRa.Game/Graphics/Graphics.cd
Normal file
131
OpenRa.Game/Graphics/Graphics.cd
Normal file
@@ -0,0 +1,131 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ClassDiagram MajorVersion="1" MinorVersion="1">
|
||||
<Class Name="OpenRa.Game.Graphics.Animation">
|
||||
<Position X="15.75" Y="9" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAQCgACAAAAAAAAAAAAAAAAAAAAEABAEgAAAAQQIQAA=</HashCode>
|
||||
<FileName>Graphics\Animation.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<ShowAsCollectionAssociation>
|
||||
<Property Name="Images" />
|
||||
</ShowAsCollectionAssociation>
|
||||
</Class>
|
||||
<Class Name="OpenRa.Game.Graphics.Viewport">
|
||||
<Position X="4.25" Y="0.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAQAABAAAgAAAAIYAAEAAAABCAAAAAABgAAAAgAE=</HashCode>
|
||||
<FileName>Graphics\Viewport.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<ShowAsAssociation>
|
||||
<Field Name="renderer" />
|
||||
</ShowAsAssociation>
|
||||
</Class>
|
||||
<Class Name="OpenRa.Game.Graphics.HardwarePalette">
|
||||
<Position X="9.75" Y="10" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAQAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAQAAAAA=</HashCode>
|
||||
<FileName>Graphics\HardwarePalette.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="OpenRa.Game.Graphics.Region" Collapsed="true">
|
||||
<Position X="18.25" Y="0.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AIQAAAAAAAAAggAABAIAIAQCAAAAAGAAAAAAAAAQAAA=</HashCode>
|
||||
<FileName>Graphics\Region.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="OpenRa.Game.Graphics.Renderer">
|
||||
<Position X="4.25" Y="5.75" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>QACAEAAGAAAAAgMAAAAAAAAAAAAAAAAAAAAAAEAAAAA=</HashCode>
|
||||
<FileName>Graphics\Renderer.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="OpenRa.Game.Graphics.Sheet">
|
||||
<Position X="9.75" Y="6" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAACAgAAACAgAAAAIAAAAAgAAAAIAAAAggAA=</HashCode>
|
||||
<FileName>Graphics\Sheet.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<ShowAsAssociation>
|
||||
<Field Name="renderer" />
|
||||
</ShowAsAssociation>
|
||||
</Class>
|
||||
<Class Name="OpenRa.Game.Graphics.SheetBuilder">
|
||||
<Position X="1.25" Y="6" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AQIACAAAAAAAIQAAAAAAAAAAgAAAAIAAAEAAAAACgAA=</HashCode>
|
||||
<FileName>Graphics\SheetBuilder.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<ShowAsAssociation>
|
||||
<Field Name="renderer" />
|
||||
</ShowAsAssociation>
|
||||
</Class>
|
||||
<Class Name="OpenRa.Game.Graphics.Sprite">
|
||||
<Position X="12.75" Y="6.25" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAQAAIAAAAIAAAAAAAAAAAAABAAACAAAEAgA=</HashCode>
|
||||
<FileName>Graphics\Sprite.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<ShowAsAssociation>
|
||||
<Field Name="sheet" />
|
||||
</ShowAsAssociation>
|
||||
</Class>
|
||||
<Class Name="OpenRa.Game.Graphics.SpriteRenderer">
|
||||
<Position X="6.75" Y="5.75" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAQQAAAAECAAAAAAAAAAAAAABAQABAAAAAAAACIwAQ=</HashCode>
|
||||
<FileName>Graphics\SpriteRenderer.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<ShowAsAssociation>
|
||||
<Field Name="currentSheet" />
|
||||
<Field Name="renderer" />
|
||||
</ShowAsAssociation>
|
||||
</Class>
|
||||
<Class Name="OpenRa.Game.Graphics.SpriteSheetBuilder">
|
||||
<Position X="12.75" Y="3.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAACAAAA=</HashCode>
|
||||
<FileName>Graphics\SpriteSheetBuilder.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<ShowAsCollectionAssociation>
|
||||
<Field Name="sprites" />
|
||||
</ShowAsCollectionAssociation>
|
||||
</Class>
|
||||
<Class Name="OpenRa.Game.Graphics.TerrainRenderer">
|
||||
<Position X="7" Y="2.25" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AACQEAAAAAAAAgAAAAAACAAAABgAAAAAAAAAAAAAgAA=</HashCode>
|
||||
<FileName>Graphics\TerrainRenderer.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<ShowAsAssociation>
|
||||
<Field Name="renderer" />
|
||||
<Field Name="terrainSheet" />
|
||||
</ShowAsAssociation>
|
||||
</Class>
|
||||
<Class Name="OpenRa.Game.Graphics.UnitSheetBuilder">
|
||||
<Position X="15.75" Y="6" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAgAAAAAAAAAAAAAAAAQAAAAAAAQAAAACAAAA=</HashCode>
|
||||
<FileName>Graphics\UnitSheetBuilder.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<ShowAsCollectionAssociation>
|
||||
<Field Name="sprites" />
|
||||
</ShowAsCollectionAssociation>
|
||||
</Class>
|
||||
<Struct Name="OpenRa.Game.Graphics.Vertex" Collapsed="true">
|
||||
<Position X="18.25" Y="1.75" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAgAAACAAFJgAQAAAAA=</HashCode>
|
||||
<FileName>Graphics\Vertex.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Struct>
|
||||
<Enum Name="OpenRa.Game.Graphics.TextureChannel" Collapsed="true">
|
||||
<Position X="18.25" Y="3" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAACAAAAAAAAAgAAAAAAAAAAAAAAABAAAAAAABAAAAA=</HashCode>
|
||||
<FileName>Graphics\Sprite.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Enum>
|
||||
<Font Name="Segoe UI" Size="9" />
|
||||
</ClassDiagram>
|
||||
34
OpenRa.Game/Graphics/HardwarePalette.cs
Normal file
34
OpenRa.Game/Graphics/HardwarePalette.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Ijw.DirectX;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using OpenRa.FileFormats;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
class HardwarePalette : Sheet
|
||||
{
|
||||
const int maxEntries = 16;
|
||||
int allocated = 0;
|
||||
|
||||
public HardwarePalette(Renderer renderer, Map map)
|
||||
: base(renderer,new Size(256, maxEntries))
|
||||
{
|
||||
Palette pal = new Palette(FileSystem.Open(map.Theater + ".pal"));
|
||||
AddPalette(pal);
|
||||
|
||||
foreach (string remap in new string[] { "blue", "red", "orange", "teal", "salmon", "green", "gray" })
|
||||
AddPalette(new Palette(pal, new PaletteRemap(FileSystem.Open(remap + ".rem"))));
|
||||
}
|
||||
|
||||
int AddPalette(Palette p)
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
this[new Point(i, allocated)] = p.GetColor(i);
|
||||
|
||||
return allocated++;
|
||||
}
|
||||
}
|
||||
}
|
||||
83
OpenRa.Game/Graphics/Region.cs
Normal file
83
OpenRa.Game/Graphics/Region.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
class Region
|
||||
{
|
||||
float2 location;
|
||||
Viewport viewport;
|
||||
|
||||
public float2 Location
|
||||
{
|
||||
get { return location + viewport.Location; } // WTF HACK HACK HACK
|
||||
}
|
||||
|
||||
public readonly float2 Size;
|
||||
|
||||
Action drawFunction;
|
||||
MouseEventHandler mouseHandler;
|
||||
Rectangle rect;
|
||||
|
||||
static int2 MakeSize(Viewport v, DockStyle d, int size)
|
||||
{
|
||||
switch (d)
|
||||
{
|
||||
case DockStyle.Top:
|
||||
case DockStyle.Bottom:
|
||||
return new int2(v.Width, size);
|
||||
|
||||
case DockStyle.Left:
|
||||
case DockStyle.Right:
|
||||
return new int2(size, v.Height);
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public void Clicked(MouseEventArgs e)
|
||||
{
|
||||
mouseHandler(this, new MouseEventArgs(e.Button, e.Clicks, e.X - rect.Left, e.Y - rect.Top, e.Delta));
|
||||
}
|
||||
|
||||
public static Region Create(Viewport v, DockStyle d, int size, Action f, MouseEventHandler m)
|
||||
{
|
||||
int2 s = MakeSize(v, d, size);
|
||||
|
||||
switch (d)
|
||||
{
|
||||
case DockStyle.Top:
|
||||
case DockStyle.Left:
|
||||
return new Region(int2.Zero, s, v, f, m);
|
||||
|
||||
case DockStyle.Right:
|
||||
case DockStyle.Bottom:
|
||||
return new Region(new int2( v.Width - s.X, v.Height - s.Y ), s, v, f, m);
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
Region(int2 location, int2 size, Viewport viewport, Action drawFunction, MouseEventHandler mouseHandler)
|
||||
{
|
||||
this.location = location;
|
||||
this.Size = size;
|
||||
this.drawFunction = drawFunction;
|
||||
this.viewport = viewport;
|
||||
this.mouseHandler = mouseHandler;
|
||||
rect = new Rectangle(location.X, location.Y, size.X, size.Y);
|
||||
}
|
||||
|
||||
public bool Contains(int2 point) { return rect.Contains(point.ToPoint()); }
|
||||
|
||||
public void Draw(Renderer renderer)
|
||||
{
|
||||
renderer.Device.EnableScissor((int)location.X, (int)location.Y, (int)Size.X, (int)Size.Y);
|
||||
drawFunction();
|
||||
renderer.Device.DisableScissor();
|
||||
}
|
||||
}
|
||||
}
|
||||
68
OpenRa.Game/Graphics/Renderer.cs
Normal file
68
OpenRa.Game/Graphics/Renderer.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using Ijw.DirectX;
|
||||
using OpenRa.FileFormats;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
class Renderer
|
||||
{
|
||||
readonly GraphicsDevice device;
|
||||
readonly Shader shader;
|
||||
|
||||
const string shaderName = "diffuse.fx";
|
||||
|
||||
public void SetPalette(HardwarePalette hp)
|
||||
{
|
||||
shader.SetValue("Palette", hp.Texture);
|
||||
}
|
||||
|
||||
public Renderer(Control host, Size resolution, bool windowed)
|
||||
{
|
||||
host.ClientSize = resolution;
|
||||
device = GraphicsDevice.Create(host,
|
||||
resolution.Width, resolution.Height, windowed, false);
|
||||
|
||||
shader = new Shader(device, FileSystem.Open(shaderName));
|
||||
shader.Quality = ShaderQuality.Low;
|
||||
}
|
||||
|
||||
public GraphicsDevice Device { get { return device; } }
|
||||
|
||||
public void BeginFrame( float2 r1, float2 r2, float2 scroll )
|
||||
{
|
||||
device.Begin();
|
||||
|
||||
shader.SetValue("Scroll", scroll);
|
||||
shader.SetValue("r1", r1);
|
||||
shader.SetValue("r2", r2);
|
||||
}
|
||||
|
||||
public void EndFrame()
|
||||
{
|
||||
device.End();
|
||||
device.Present();
|
||||
}
|
||||
|
||||
public void DrawWithShader(ShaderQuality quality, Action task)
|
||||
{
|
||||
shader.Quality = quality;
|
||||
shader.Render(() => task());
|
||||
}
|
||||
|
||||
public void DrawBatch<T>(FvfVertexBuffer<T> vertices, IndexBuffer indices,
|
||||
Range<int> vertexRange, Range<int> indexRange, Texture texture)
|
||||
where T : struct
|
||||
{
|
||||
shader.SetValue("DiffuseTexture", texture);
|
||||
shader.Commit();
|
||||
|
||||
vertices.Bind(0);
|
||||
indices.Bind();
|
||||
|
||||
device.DrawIndexedPrimitives(PrimitiveType.TriangleList,
|
||||
vertexRange, indexRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
OpenRa.Game/Graphics/Sequence.cs
Normal file
38
OpenRa.Game/Graphics/Sequence.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Xml;
|
||||
using Ijw.DirectX;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
class Sequence
|
||||
{
|
||||
readonly int start, length;
|
||||
|
||||
public int Start { get { return start; } }
|
||||
public int End { get { return start + length; } }
|
||||
public int Length { get { return length; } }
|
||||
|
||||
public Sequence(string unit, XmlElement e)
|
||||
{
|
||||
string srcOverride = e.GetAttribute("src");
|
||||
|
||||
Range<int> src = UnitSheetBuilder.GetUnit(
|
||||
string.IsNullOrEmpty(srcOverride) ? unit : srcOverride);
|
||||
|
||||
start = src.Start + int.Parse(e.GetAttribute("start"));
|
||||
|
||||
if (e.GetAttribute("length") == "*" || e.GetAttribute("end") == "*")
|
||||
length = src.End - start;
|
||||
else if (e.HasAttribute("length"))
|
||||
length = int.Parse(e.GetAttribute("length"));
|
||||
else if (e.HasAttribute("end"))
|
||||
length = int.Parse(e.GetAttribute("end")) - start;
|
||||
else
|
||||
length = 1;
|
||||
}
|
||||
|
||||
public Sprite GetSprite(int frame)
|
||||
{
|
||||
return UnitSheetBuilder.sprites[frame + start];
|
||||
}
|
||||
}
|
||||
}
|
||||
39
OpenRa.Game/Graphics/SequenceProvider.cs
Normal file
39
OpenRa.Game/Graphics/SequenceProvider.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using OpenRa.FileFormats;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
static class SequenceProvider
|
||||
{
|
||||
static Dictionary<string, Dictionary<string, Sequence>> units =
|
||||
new Dictionary<string, Dictionary<string, Sequence>>();
|
||||
|
||||
static SequenceProvider()
|
||||
{
|
||||
XmlDocument document = new XmlDocument();
|
||||
document.Load(FileSystem.Open("sequences.xml"));
|
||||
|
||||
foreach (XmlElement eUnit in document.SelectNodes("/sequences/unit"))
|
||||
LoadSequencesForUnit(eUnit);
|
||||
}
|
||||
|
||||
public static void ForcePrecache() { } // force static ctor to run
|
||||
|
||||
static void LoadSequencesForUnit(XmlElement eUnit)
|
||||
{
|
||||
string unitName = eUnit.GetAttribute("name");
|
||||
Dictionary<string, Sequence> sequences = new Dictionary<string, Sequence>();
|
||||
|
||||
foreach (XmlElement eSequence in eUnit.SelectNodes("./sequence"))
|
||||
sequences.Add(eSequence.GetAttribute("name"), new Sequence(unitName, eSequence));
|
||||
|
||||
units.Add(unitName, sequences);
|
||||
}
|
||||
|
||||
public static Sequence GetSequence(string unitName, string sequenceName)
|
||||
{
|
||||
return units[unitName][sequenceName];
|
||||
}
|
||||
}
|
||||
}
|
||||
51
OpenRa.Game/Graphics/Sheet.cs
Normal file
51
OpenRa.Game/Graphics/Sheet.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using Ijw.DirectX;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
class Sheet
|
||||
{
|
||||
readonly Renderer renderer;
|
||||
protected readonly Bitmap bitmap;
|
||||
|
||||
Texture texture;
|
||||
static int suffix = 0;
|
||||
|
||||
public Sheet(Renderer renderer, Size size)
|
||||
{
|
||||
this.renderer = renderer;
|
||||
this.bitmap = new Bitmap(size.Width, size.Height);
|
||||
}
|
||||
|
||||
void Resolve()
|
||||
{
|
||||
string filename = string.Format("../../../sheet-{0}.png", suffix++);
|
||||
bitmap.Save(filename);
|
||||
|
||||
using (Stream s = File.OpenRead(filename))
|
||||
texture = Texture.Create(s, renderer.Device);
|
||||
}
|
||||
|
||||
public Texture Texture
|
||||
{
|
||||
get
|
||||
{
|
||||
if (texture == null)
|
||||
Resolve();
|
||||
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
public Size Size { get { return bitmap.Size; } }
|
||||
|
||||
public Color this[Point p]
|
||||
{
|
||||
get { return bitmap.GetPixel(p.X, p.Y); }
|
||||
set { bitmap.SetPixel(p.X, p.Y, value); }
|
||||
}
|
||||
|
||||
public Bitmap Bitmap { get { return bitmap; } } // for perf
|
||||
}
|
||||
}
|
||||
88
OpenRa.Game/Graphics/SheetBuilder.cs
Normal file
88
OpenRa.Game/Graphics/SheetBuilder.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
static class SheetBuilder
|
||||
{
|
||||
public static void Initialize(Renderer r)
|
||||
{
|
||||
renderer = r;
|
||||
}
|
||||
|
||||
public static Sprite Add(byte[] src, Size size)
|
||||
{
|
||||
Sprite rect = AddImage(size);
|
||||
Util.FastCopyIntoChannel(rect, src);
|
||||
return rect;
|
||||
}
|
||||
|
||||
public static Sprite Add(Size size, byte paletteIndex)
|
||||
{
|
||||
byte[] data = new byte[size.Width * size.Height];
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
data[i] = paletteIndex;
|
||||
|
||||
return Add(data, size);
|
||||
}
|
||||
|
||||
static Sheet NewSheet() { return new Sheet(renderer, new Size(512, 512)); }
|
||||
|
||||
static Renderer renderer;
|
||||
static Sheet current = null;
|
||||
static int rowHeight = 0;
|
||||
static Point p;
|
||||
static TextureChannel? channel = null;
|
||||
|
||||
static TextureChannel? NextChannel(TextureChannel? t)
|
||||
{
|
||||
if (t == null)
|
||||
return TextureChannel.Red;
|
||||
|
||||
switch (t.Value)
|
||||
{
|
||||
case TextureChannel.Red: return TextureChannel.Green;
|
||||
case TextureChannel.Green: return TextureChannel.Blue;
|
||||
case TextureChannel.Blue: return TextureChannel.Alpha;
|
||||
case TextureChannel.Alpha: return null;
|
||||
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Sprite AddImage(Size imageSize)
|
||||
{
|
||||
if (current == null)
|
||||
{
|
||||
current = NewSheet();
|
||||
channel = NextChannel(null);
|
||||
}
|
||||
|
||||
if (imageSize.Width + p.X > current.Size.Width)
|
||||
{
|
||||
p = new Point(0, p.Y + rowHeight);
|
||||
rowHeight = imageSize.Height;
|
||||
}
|
||||
|
||||
if (imageSize.Height > rowHeight)
|
||||
rowHeight = imageSize.Height;
|
||||
|
||||
if (p.Y + imageSize.Height > current.Size.Height)
|
||||
{
|
||||
|
||||
if (null == (channel = NextChannel(channel)))
|
||||
{
|
||||
current = NewSheet();
|
||||
channel = NextChannel(channel);
|
||||
}
|
||||
|
||||
rowHeight = 0;
|
||||
p = new Point(0,0);
|
||||
}
|
||||
|
||||
Sprite rect = new Sprite(current, new Rectangle(p, imageSize), channel.Value);
|
||||
p.X += imageSize.Width;
|
||||
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
OpenRa.Game/Graphics/Sprite.cs
Normal file
54
OpenRa.Game/Graphics/Sprite.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Drawing;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
class Sprite
|
||||
{
|
||||
public readonly Rectangle bounds;
|
||||
public readonly Sheet sheet;
|
||||
public readonly TextureChannel channel;
|
||||
public readonly RectangleF uv;
|
||||
public readonly float2 size;
|
||||
|
||||
readonly float2[] uvhax;
|
||||
|
||||
internal Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel)
|
||||
{
|
||||
this.bounds = bounds;
|
||||
this.sheet = sheet;
|
||||
this.channel = channel;
|
||||
|
||||
uv = new RectangleF(
|
||||
(float)(bounds.Left + 0.5f) / sheet.Size.Width,
|
||||
(float)(bounds.Top + 0.5f) / 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);
|
||||
}
|
||||
|
||||
public float2 FastMapTextureCoords(int k)
|
||||
{
|
||||
return uvhax[k];
|
||||
}
|
||||
}
|
||||
|
||||
public enum TextureChannel
|
||||
{
|
||||
Red = 0,
|
||||
Green = 1,
|
||||
Blue = 2,
|
||||
Alpha = 3,
|
||||
}
|
||||
}
|
||||
67
OpenRa.Game/Graphics/SpriteRenderer.cs
Normal file
67
OpenRa.Game/Graphics/SpriteRenderer.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenRa.FileFormats;
|
||||
using System.Drawing;
|
||||
using Ijw.DirectX;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
class SpriteRenderer
|
||||
{
|
||||
FvfVertexBuffer<Vertex> vertexBuffer;
|
||||
IndexBuffer indexBuffer;
|
||||
Renderer renderer;
|
||||
|
||||
const int spritesPerBatch = 1024;
|
||||
|
||||
Vertex[] vertices = new Vertex[4 * spritesPerBatch];
|
||||
ushort[] indices = new ushort[6 * spritesPerBatch];
|
||||
Sheet currentSheet = null;
|
||||
int sprites = 0;
|
||||
ShaderQuality quality;
|
||||
int nv = 0, ni = 0;
|
||||
|
||||
public SpriteRenderer(Renderer renderer, bool allowAlpha)
|
||||
{
|
||||
this.renderer = renderer;
|
||||
|
||||
vertexBuffer = new FvfVertexBuffer<Vertex>(renderer.Device, vertices.Length, Vertex.Format);
|
||||
indexBuffer = new IndexBuffer(renderer.Device, indices.Length);
|
||||
|
||||
quality = allowAlpha ? ShaderQuality.High : ShaderQuality.Low;
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
if (sprites > 0)
|
||||
{
|
||||
renderer.DrawWithShader(quality, () =>
|
||||
{
|
||||
vertexBuffer.SetData(vertices);
|
||||
indexBuffer.SetData(indices);
|
||||
renderer.DrawBatch(vertexBuffer, indexBuffer,
|
||||
new Range<int>(0, nv),
|
||||
new Range<int>(0, ni),
|
||||
currentSheet.Texture);
|
||||
});
|
||||
|
||||
nv = 0; ni = 0;
|
||||
currentSheet = null;
|
||||
sprites = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float2 location, int palette)
|
||||
{
|
||||
if (s.sheet != currentSheet)
|
||||
Flush();
|
||||
|
||||
currentSheet = s.sheet;
|
||||
Util.FastCreateQuad(vertices, indices, location, s, palette, nv, ni);
|
||||
nv += 4; ni += 6;
|
||||
if (++sprites >= spritesPerBatch)
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
23
OpenRa.Game/Graphics/SpriteSheetBuilder.cs
Normal file
23
OpenRa.Game/Graphics/SpriteSheetBuilder.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using OpenRa.FileFormats;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
static class SpriteSheetBuilder
|
||||
{
|
||||
static Dictionary<string, Sprite> sprites =
|
||||
new Dictionary<string, Sprite>();
|
||||
|
||||
public static Sprite LoadSprite(string filename)
|
||||
{
|
||||
Sprite value;
|
||||
if (!sprites.TryGetValue(filename, out value))
|
||||
{
|
||||
ShpReader shp = new ShpReader(FileSystem.Open(filename));
|
||||
sprites.Add(filename, value = SheetBuilder.Add(shp[0].Image, shp.Size));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
80
OpenRa.Game/Graphics/TerrainRenderer.cs
Normal file
80
OpenRa.Game/Graphics/TerrainRenderer.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using Ijw.DirectX;
|
||||
using IjwFramework.Collections;
|
||||
using OpenRa.FileFormats;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
class TerrainRenderer
|
||||
{
|
||||
FvfVertexBuffer<Vertex> vertexBuffer;
|
||||
IndexBuffer indexBuffer;
|
||||
Sheet terrainSheet;
|
||||
public TileSet tileSet;
|
||||
Region region;
|
||||
|
||||
Renderer renderer;
|
||||
Map map;
|
||||
|
||||
public TerrainRenderer(Renderer renderer, Map map, Viewport viewport)
|
||||
{
|
||||
this.renderer = renderer;
|
||||
region = Region.Create(viewport, DockStyle.Left, viewport.Width - 128, Draw, (_, e) => { } );
|
||||
viewport.AddRegion(region);
|
||||
this.map = map;
|
||||
|
||||
tileSet = new TileSet( map.TileSuffix );
|
||||
|
||||
Size tileSize = new Size( 24, 24 );
|
||||
|
||||
var tileMapping = new Cache<TileReference, Sprite>(
|
||||
x => SheetBuilder.Add(tileSet.GetBytes(x), tileSize));
|
||||
|
||||
Vertex[] vertices = new Vertex[4 * map.Height * map.Width];
|
||||
ushort[] indices = new ushort[6 * map.Height * map.Width];
|
||||
|
||||
int nv = 0;
|
||||
int ni = 0;
|
||||
for( int j = 0 ; j < map.Height ; j++ )
|
||||
for (int i = 0; i < map.Width; i++)
|
||||
{
|
||||
Sprite tile = tileMapping[map.MapTiles[i + map.XOffset, j + map.YOffset]];
|
||||
Util.FastCreateQuad(vertices, indices, 24 * new float2(i, j), tile, 0, nv, ni);
|
||||
nv += 4;
|
||||
ni += 6;
|
||||
}
|
||||
|
||||
terrainSheet = tileMapping[map.MapTiles[map.XOffset, map.YOffset]].sheet;
|
||||
|
||||
vertexBuffer = new FvfVertexBuffer<Vertex>( renderer.Device, vertices.Length, Vertex.Format );
|
||||
vertexBuffer.SetData( vertices );
|
||||
|
||||
indexBuffer = new IndexBuffer( renderer.Device, indices.Length );
|
||||
indexBuffer.SetData( indices );
|
||||
}
|
||||
|
||||
void Draw()
|
||||
{
|
||||
int indicesPerRow = map.Width * 6;
|
||||
int verticesPerRow = map.Width * 4;
|
||||
|
||||
int visibleRows = (int)(region.Size.Y / 24.0f + 2);
|
||||
|
||||
int firstRow = (int)(region.Location.Y / 24.0f);
|
||||
int lastRow = firstRow + visibleRows;
|
||||
|
||||
if (lastRow < 0 || firstRow > map.Height)
|
||||
return;
|
||||
|
||||
if (firstRow < 0) firstRow = 0;
|
||||
if (lastRow > map.Height) lastRow = map.Height;
|
||||
|
||||
renderer.DrawWithShader(ShaderQuality.Low, () =>
|
||||
renderer.DrawBatch(vertexBuffer, indexBuffer,
|
||||
new Range<int>(verticesPerRow * firstRow, verticesPerRow * lastRow),
|
||||
new Range<int>(indicesPerRow * firstRow, indicesPerRow * lastRow),
|
||||
terrainSheet.Texture));
|
||||
}
|
||||
}
|
||||
}
|
||||
26
OpenRa.Game/Graphics/TreeCache.cs
Normal file
26
OpenRa.Game/Graphics/TreeCache.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using OpenRa.FileFormats;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
class TreeCache
|
||||
{
|
||||
Dictionary<string, Sprite> trees = new Dictionary<string, Sprite>();
|
||||
|
||||
public TreeCache(Map map)
|
||||
{
|
||||
foreach (TreeReference r in map.Trees)
|
||||
{
|
||||
if (trees.ContainsKey(r.Image))
|
||||
continue;
|
||||
|
||||
string filename = r.Image + "." + map.Theater.Substring(0, 3);
|
||||
|
||||
ShpReader reader = new ShpReader( FileSystem.Open( filename ) );
|
||||
trees.Add(r.Image, SheetBuilder.Add(reader[0].Image, reader.Size));
|
||||
}
|
||||
}
|
||||
|
||||
public Sprite GetImage(string tree) { return trees[tree]; }
|
||||
}
|
||||
}
|
||||
39
OpenRa.Game/Graphics/UnitSheetBuilder.cs
Normal file
39
OpenRa.Game/Graphics/UnitSheetBuilder.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Collections.Generic;
|
||||
using Ijw.DirectX;
|
||||
using OpenRa.FileFormats;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
static class UnitSheetBuilder
|
||||
{
|
||||
public static readonly List<Sprite> sprites = new List<Sprite>();
|
||||
static Dictionary<string, Range<int>> sequences = new Dictionary<string, Range<int>>();
|
||||
|
||||
public static Range<int> GetUnit(string name)
|
||||
{
|
||||
Range<int> result;
|
||||
if (sequences.TryGetValue(name, out result))
|
||||
return result;
|
||||
|
||||
return AddUnit(name);
|
||||
}
|
||||
|
||||
static Range<int> AddUnit( string name )
|
||||
{
|
||||
Log.Write("Loading SHP for {0}", name);
|
||||
|
||||
int low = sprites.Count;
|
||||
|
||||
ShpReader reader = new ShpReader(FileSystem.Open(name + ".shp"));
|
||||
foreach (ImageHeader h in reader)
|
||||
sprites.Add(SheetBuilder.Add(h.Image, reader.Size));
|
||||
|
||||
Range<int> sequence = new Range<int>(low, sprites.Count - 1);
|
||||
sequences.Add(name, sequence);
|
||||
|
||||
Log.Write("Loaded SHP for {0}", name);
|
||||
|
||||
return sequence;
|
||||
}
|
||||
}
|
||||
}
|
||||
99
OpenRa.Game/Graphics/Util.cs
Normal file
99
OpenRa.Game/Graphics/Util.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
static class Util
|
||||
{
|
||||
static float2 KLerp(float2 o, float2 d, int k)
|
||||
{
|
||||
switch (k)
|
||||
{
|
||||
case 0: return o;
|
||||
case 1: return new float2(o.X + d.X, o.Y);
|
||||
case 2: return new float2(o.X, o.Y + d.Y);
|
||||
case 3: return new float2(o.X + d.X, o.Y + d.Y);
|
||||
default: throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public static string[] ReadAllLines(Stream s)
|
||||
{
|
||||
List<string> result = new List<string>();
|
||||
using (StreamReader reader = new StreamReader(s))
|
||||
while (!reader.EndOfStream)
|
||||
result.Add(reader.ReadLine());
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public static T[] MakeArray<T>(int count, Converter<int, T> f)
|
||||
{
|
||||
T[] result = new T[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
result[i] = f(i);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f };
|
||||
|
||||
public static void FastCreateQuad(Vertex[] vertices, ushort[] indices, float2 o, Sprite r, int palette, int nv, int ni)
|
||||
{
|
||||
float2 attrib = new float2(palette / 16.0f, channelSelect[(int)r.channel]);
|
||||
|
||||
vertices[nv] = new Vertex(KLerp(o, r.size, 0), r.FastMapTextureCoords(0), attrib);
|
||||
vertices[nv + 1] = new Vertex(KLerp(o, r.size, 1), r.FastMapTextureCoords(1), attrib);
|
||||
vertices[nv + 2] = new Vertex(KLerp(o, r.size, 2), r.FastMapTextureCoords(2), attrib);
|
||||
vertices[nv + 3] = new Vertex(KLerp(o, r.size, 3), r.FastMapTextureCoords(3), attrib);
|
||||
|
||||
indices[ni] = (ushort)(nv);
|
||||
indices[ni + 1] = indices[ni + 3] = (ushort)(nv + 1);
|
||||
indices[ni + 2] = indices[ni + 5] = (ushort)(nv + 2);
|
||||
indices[ni + 4] = (ushort)(nv + 3);
|
||||
}
|
||||
|
||||
public static void FastCopyIntoChannel(Sprite dest, byte[] src)
|
||||
{
|
||||
var bitmap = dest.sheet.Bitmap;
|
||||
BitmapData bits = null;
|
||||
uint[] channelMasks = { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 };
|
||||
int[] shifts = { 16, 8, 0, 24 };
|
||||
|
||||
uint mask = channelMasks[(int)dest.channel];
|
||||
int shift = shifts[(int)dest.channel];
|
||||
|
||||
try
|
||||
{
|
||||
bits = bitmap.LockBits(dest.bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
|
||||
|
||||
int width = dest.bounds.Width;
|
||||
int height = dest.bounds.Height;
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* srcbase = &src[0])
|
||||
{
|
||||
byte* s = srcbase;
|
||||
uint* t = (uint*)bits.Scan0.ToPointer();
|
||||
int stride = bits.Stride >> 2;
|
||||
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
uint* p = t;
|
||||
for (int i = 0; i < width; i++, p++)
|
||||
*p = (*p & ~mask) | ((mask & ((uint)*s++) << shift));
|
||||
t += stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
bitmap.UnlockBits(bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
OpenRa.Game/Graphics/Vertex.cs
Normal file
21
OpenRa.Game/Graphics/Vertex.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Ijw.DirectX;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct Vertex
|
||||
{
|
||||
public float x, y, z, u, v;
|
||||
public float p, c;
|
||||
|
||||
public Vertex(float2 xy, float2 uv, float2 pc)
|
||||
{
|
||||
this.x = xy.X; this.y = xy.Y; this.z = 0;
|
||||
this.u = uv.X; this.v = uv.Y;
|
||||
this.p = pc.X; this.c = pc.Y;
|
||||
}
|
||||
|
||||
public const VertexFormat Format = VertexFormat.Position | VertexFormat.Texture2;
|
||||
}
|
||||
}
|
||||
54
OpenRa.Game/Graphics/Viewport.cs
Normal file
54
OpenRa.Game/Graphics/Viewport.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenRa.Game.Graphics
|
||||
{
|
||||
class Viewport
|
||||
{
|
||||
readonly float2 size;
|
||||
readonly float2 mapSize;
|
||||
float2 scrollPosition;
|
||||
readonly Renderer renderer;
|
||||
|
||||
public float2 Location { get { return scrollPosition; } }
|
||||
public float2 Size { get { return size; } }
|
||||
|
||||
public int Width { get { return (int)size.X; } }
|
||||
public int Height { get { return (int)size.Y; } }
|
||||
|
||||
public void Scroll(float2 delta)
|
||||
{
|
||||
scrollPosition = (scrollPosition + delta).Constrain(float2.Zero, mapSize);
|
||||
}
|
||||
|
||||
public Viewport(float2 size, float2 mapSize, Renderer renderer)
|
||||
{
|
||||
this.size = size;
|
||||
this.mapSize = 24 * mapSize - size + new float2(128, 0);
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
List<Region> regions = new List<Region>();
|
||||
public void AddRegion(Region r)
|
||||
{
|
||||
regions.Add(r);
|
||||
}
|
||||
|
||||
public void DrawRegions(Game game)
|
||||
{
|
||||
float2 r1 = new float2(2, -2) / Size;
|
||||
float2 r2 = new float2(-1, 1);
|
||||
|
||||
renderer.BeginFrame(r1, r2, scrollPosition);
|
||||
|
||||
foreach (Region region in regions)
|
||||
region.Draw(renderer);
|
||||
|
||||
renderer.EndFrame();
|
||||
}
|
||||
|
||||
public IEnumerable<Region> Regions
|
||||
{
|
||||
get { return regions; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user