git-svn-id: svn://svn.ijw.co.nz/svn/OpenRa@2049 993157c7-ee19-0410-b2c4-bb4e9862e678

This commit is contained in:
chrisf
2008-07-20 20:06:19 +00:00
parent 6f8919d301
commit 4ea033f63d
42 changed files with 247 additions and 198 deletions

View 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
}
}
}
}
}

View 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>

View 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++;
}
}
}

View 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();
}
}
}

View 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);
}
}
}

View 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];
}
}
}

View 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];
}
}
}

View 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
}
}

View 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;
}
}
}

View 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,
}
}

View 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();
}
}
}

View 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;
}
}
}

View 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));
}
}
}

View 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]; }
}
}

View 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;
}
}
}

View 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);
}
}
}
}

View 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;
}
}

View 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; }
}
}
}