68
OpenRA.Game/Graphics/BeamRenderable.cs
Normal file
68
OpenRA.Game/Graphics/BeamRenderable.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
#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;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public struct BeamRenderable : IRenderable
|
||||
{
|
||||
readonly WPos pos;
|
||||
readonly int zOffset;
|
||||
readonly WVec length;
|
||||
readonly Color color;
|
||||
readonly float width;
|
||||
|
||||
public BeamRenderable(WPos pos, int zOffset, WVec length, float width, Color color)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.zOffset = zOffset;
|
||||
this.length = length;
|
||||
this.color = color;
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public WPos Pos { get { return pos; } }
|
||||
public float Scale { get { return 1f; } }
|
||||
public PaletteReference Palette { get { return null; } }
|
||||
public int ZOffset { get { return zOffset; } }
|
||||
|
||||
public IRenderable WithScale(float newScale) { return new BeamRenderable(pos, zOffset, length, width, color); }
|
||||
public IRenderable WithPalette(PaletteReference newPalette) { return new BeamRenderable(pos, zOffset, length, width, color); }
|
||||
public IRenderable WithZOffset(int newOffset) { return new BeamRenderable(pos, zOffset, length, width, color); }
|
||||
public IRenderable WithPos(WPos pos) { return new BeamRenderable(pos, zOffset, length, width, color); }
|
||||
|
||||
public void BeforeRender(WorldRenderer wr) {}
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
var wlr = Game.Renderer.WorldLineRenderer;
|
||||
var src = wr.ScreenPosition(pos);
|
||||
var dest = wr.ScreenPosition(pos + length);
|
||||
|
||||
var lineWidth = wlr.LineWidth;
|
||||
if (lineWidth != width)
|
||||
{
|
||||
wlr.Flush();
|
||||
wlr.LineWidth = width;
|
||||
}
|
||||
|
||||
wlr.DrawLine(src, dest, color, color);
|
||||
|
||||
if (lineWidth != width)
|
||||
{
|
||||
wlr.Flush();
|
||||
wlr.LineWidth = lineWidth;
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderDebugGeometry(WorldRenderer wr) {}
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,23 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public static class CursorProvider
|
||||
{
|
||||
static HardwarePalette Palette;
|
||||
static HardwarePalette palette;
|
||||
static Dictionary<string, CursorSequence> cursors;
|
||||
static Cache<string, PaletteReference> palettes;
|
||||
|
||||
static PaletteReference CreatePaletteReference(string name)
|
||||
{
|
||||
var pal = palette.GetPalette(name);
|
||||
if (pal == null)
|
||||
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
|
||||
|
||||
return new PaletteReference(name, palette.GetPaletteIndex(name), pal);
|
||||
}
|
||||
|
||||
public static void Initialize(string[] sequenceFiles)
|
||||
{
|
||||
cursors = new Dictionary<string, CursorSequence>();
|
||||
palettes = new Cache<string, PaletteReference>(CreatePaletteReference);
|
||||
var sequences = new MiniYaml(null, sequenceFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(MiniYaml.MergeLiberal));
|
||||
int[] ShadowIndex = { };
|
||||
|
||||
@@ -35,14 +46,14 @@ namespace OpenRA.Graphics
|
||||
int.TryParse(sequences.NodesDict["ShadowIndex"].Value, out ShadowIndex[ShadowIndex.Length - 1]);
|
||||
}
|
||||
|
||||
Palette = new HardwarePalette();
|
||||
palette = new HardwarePalette();
|
||||
foreach (var p in sequences.NodesDict["Palettes"].Nodes)
|
||||
Palette.AddPalette(p.Key, new Palette(FileSystem.Open(p.Value.Value), ShadowIndex), false);
|
||||
palette.AddPalette(p.Key, new Palette(FileSystem.Open(p.Value.Value), ShadowIndex), false);
|
||||
|
||||
foreach (var s in sequences.NodesDict["Cursors"].Nodes)
|
||||
LoadSequencesForCursor(s.Key, s.Value);
|
||||
|
||||
Palette.Initialize();
|
||||
palette.Initialize();
|
||||
}
|
||||
|
||||
static void LoadSequencesForCursor(string cursorSrc, MiniYaml cursor)
|
||||
@@ -63,10 +74,10 @@ namespace OpenRA.Graphics
|
||||
var cursorSequence = GetCursorSequence(cursorName);
|
||||
var cursorSprite = cursorSequence.GetSprite(cursorFrame);
|
||||
|
||||
renderer.SetPalette(Palette);
|
||||
renderer.SetPalette(palette);
|
||||
renderer.SpriteRenderer.DrawSprite(cursorSprite,
|
||||
lastMousePos - cursorSequence.Hotspot,
|
||||
Palette.GetPaletteIndex(cursorSequence.Palette),
|
||||
palettes[cursorSequence.Palette],
|
||||
cursorSprite.size);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ namespace OpenRA.Graphics
|
||||
Renderer renderer;
|
||||
IShader shader;
|
||||
|
||||
Vertex[] vertices = new Vertex[ Renderer.TempBufferSize ];
|
||||
Vertex[] vertices = new Vertex[Renderer.TempBufferSize];
|
||||
int nv = 0;
|
||||
|
||||
public LineRenderer( Renderer renderer, IShader shader )
|
||||
public LineRenderer(Renderer renderer, IShader shader)
|
||||
{
|
||||
this.renderer = renderer;
|
||||
this.shader = shader;
|
||||
@@ -32,49 +32,50 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
if( nv > 0 )
|
||||
if (nv > 0)
|
||||
{
|
||||
shader.Render( () =>
|
||||
renderer.Device.EnableAlphaBlending();
|
||||
shader.Render(() =>
|
||||
{
|
||||
var vb = renderer.GetTempVertexBuffer();
|
||||
vb.SetData( vertices, nv );
|
||||
vb.SetData(vertices, nv);
|
||||
renderer.SetLineWidth(LineWidth * Game.viewport.Zoom);
|
||||
renderer.DrawBatch( vb, 0, nv, PrimitiveType.LineList );
|
||||
} );
|
||||
|
||||
renderer.DrawBatch(vb, 0, nv, PrimitiveType.LineList);
|
||||
});
|
||||
renderer.Device.DisableAlphaBlending();
|
||||
nv = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawRect( float2 tl, float2 br, Color c )
|
||||
public void DrawRect(float2 tl, float2 br, Color c)
|
||||
{
|
||||
var tr = new float2( br.X, tl.Y );
|
||||
var bl = new float2( tl.X, br.Y );
|
||||
DrawLine( tl, tr, c, c );
|
||||
DrawLine( tl, bl, c, c );
|
||||
DrawLine( tr, br, c, c );
|
||||
DrawLine( bl, br, c, c );
|
||||
var tr = new float2(br.X, tl.Y);
|
||||
var bl = new float2(tl.X, br.Y);
|
||||
DrawLine(tl, tr, c, c);
|
||||
DrawLine(tl, bl, c, c);
|
||||
DrawLine(tr, br, c, c);
|
||||
DrawLine(bl, br, c, c);
|
||||
}
|
||||
|
||||
public void DrawLine( float2 start, float2 end, Color startColor, Color endColor )
|
||||
public void DrawLine(float2 start, float2 end, Color startColor, Color endColor)
|
||||
{
|
||||
Renderer.CurrentBatchRenderer = this;
|
||||
|
||||
if( nv + 2 > Renderer.TempBufferSize )
|
||||
if (nv + 2 > Renderer.TempBufferSize)
|
||||
Flush();
|
||||
|
||||
vertices[ nv++ ] = new Vertex( start + offset,
|
||||
new float2( startColor.R / 255.0f, startColor.G / 255.0f ),
|
||||
new float2( startColor.B / 255.0f, startColor.A / 255.0f ) );
|
||||
vertices[nv++] = new Vertex(start + offset,
|
||||
new float2(startColor.R / 255.0f, startColor.G / 255.0f),
|
||||
new float2(startColor.B / 255.0f, startColor.A / 255.0f));
|
||||
|
||||
vertices[ nv++ ] = new Vertex( end + offset,
|
||||
new float2( endColor.R / 255.0f, endColor.G / 255.0f ),
|
||||
new float2( endColor.B / 255.0f, endColor.A / 255.0f ) );
|
||||
vertices[nv++] = new Vertex(end + offset,
|
||||
new float2(endColor.R / 255.0f, endColor.G / 255.0f),
|
||||
new float2(endColor.B / 255.0f, endColor.A / 255.0f));
|
||||
}
|
||||
|
||||
public void FillRect( RectangleF r, Color color )
|
||||
public void FillRect(RectangleF r, Color color)
|
||||
{
|
||||
for (float y = r.Top; y < r.Bottom; y++)
|
||||
for (var y = r.Top; y < r.Bottom; y++)
|
||||
DrawLine(new float2(r.Left, y), new float2(r.Right, y), color, color);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,12 +31,14 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
if (nv > 0)
|
||||
{
|
||||
renderer.Device.EnableAlphaBlending();
|
||||
shader.Render(() =>
|
||||
{
|
||||
var vb = renderer.GetTempVertexBuffer();
|
||||
vb.SetData(vertices, nv);
|
||||
renderer.DrawBatch(vb, 0, nv, PrimitiveType.QuadList);
|
||||
});
|
||||
renderer.Device.DisableAlphaBlending();
|
||||
|
||||
nv = 0;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,9 @@ namespace OpenRA.Graphics
|
||||
IRenderable WithPalette(PaletteReference newPalette);
|
||||
IRenderable WithZOffset(int newOffset);
|
||||
IRenderable WithPos(WPos pos);
|
||||
void BeforeRender(WorldRenderer wr);
|
||||
void Render(WorldRenderer wr);
|
||||
void RenderDebugGeometry(WorldRenderer wr);
|
||||
}
|
||||
|
||||
public struct SpriteRenderable : IRenderable
|
||||
@@ -79,9 +81,16 @@ namespace OpenRA.Graphics
|
||||
public IRenderable WithZOffset(int newOffset) { return new SpriteRenderable(sprite, pos, newOffset, palette, scale); }
|
||||
public IRenderable WithPos(WPos pos) { return new SpriteRenderable(sprite, pos, zOffset, palette, scale); }
|
||||
|
||||
public void BeforeRender(WorldRenderer wr) {}
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
sprite.DrawAt(wr.ScreenPxPosition(pos) - pxCenter, palette.Index, scale);
|
||||
sprite.DrawAt(wr.ScreenPxPosition(pos) - pxCenter, palette, scale);
|
||||
}
|
||||
|
||||
public void RenderDebugGeometry(WorldRenderer wr)
|
||||
{
|
||||
var offset = wr.ScreenPxPosition(pos) - pxCenter;
|
||||
Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + sprite.size, Color.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace OpenRA.Graphics
|
||||
internal static int TempBufferCount;
|
||||
|
||||
public SpriteRenderer WorldSpriteRenderer { get; private set; }
|
||||
public SpriteRenderer WorldRgbaSpriteRenderer { get; private set; }
|
||||
public QuadRenderer WorldQuadRenderer { get; private set; }
|
||||
public LineRenderer WorldLineRenderer { get; private set; }
|
||||
public VoxelRenderer WorldVoxelRenderer { get; private set; }
|
||||
@@ -46,8 +47,9 @@ namespace OpenRA.Graphics
|
||||
SheetSize = Game.Settings.Graphics.SheetSize;
|
||||
|
||||
WorldSpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp"));
|
||||
WorldRgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba"));
|
||||
WorldLineRenderer = new LineRenderer(this, device.CreateShader("line"));
|
||||
WorldVoxelRenderer = new VoxelRenderer(this, device.CreateShader("vxl"), device.CreateShader("vxlshadow"));
|
||||
WorldVoxelRenderer = new VoxelRenderer(this, device.CreateShader("vxl"));
|
||||
LineRenderer = new LineRenderer(this, device.CreateShader("line"));
|
||||
WorldQuadRenderer = new QuadRenderer(this, device.CreateShader("line"));
|
||||
RgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba"));
|
||||
@@ -68,6 +70,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
device.Clear();
|
||||
WorldSpriteRenderer.SetViewportParams(Resolution, zoom, scroll);
|
||||
WorldRgbaSpriteRenderer.SetViewportParams(Resolution, zoom, scroll);
|
||||
SpriteRenderer.SetViewportParams(Resolution, 1f, float2.Zero);
|
||||
RgbaSpriteRenderer.SetViewportParams(Resolution, 1f, float2.Zero);
|
||||
WorldLineRenderer.SetViewportParams(Resolution, zoom, scroll);
|
||||
@@ -88,6 +91,7 @@ namespace OpenRA.Graphics
|
||||
RgbaSpriteRenderer.SetPalette(currentPaletteTexture);
|
||||
SpriteRenderer.SetPalette(currentPaletteTexture);
|
||||
WorldSpriteRenderer.SetPalette(currentPaletteTexture);
|
||||
WorldRgbaSpriteRenderer.SetPalette(currentPaletteTexture);
|
||||
WorldVoxelRenderer.SetPalette(currentPaletteTexture);
|
||||
}
|
||||
|
||||
@@ -202,17 +206,5 @@ namespace OpenRA.Graphics
|
||||
Flush();
|
||||
Device.DisableDepthBuffer();
|
||||
}
|
||||
|
||||
public void EnableStencilBuffer()
|
||||
{
|
||||
Flush();
|
||||
Device.EnableStencilBuffer();
|
||||
}
|
||||
|
||||
public void DisableStencilBuffer()
|
||||
{
|
||||
Flush();
|
||||
Device.DisableStencilBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using OpenRA.FileFormats;
|
||||
@@ -19,13 +20,21 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
ITexture texture;
|
||||
bool dirty;
|
||||
public byte[] Data { get; private set; }
|
||||
byte[] data;
|
||||
|
||||
public readonly Size Size;
|
||||
public byte[] Data { get { return data ?? texture.GetData(); } }
|
||||
|
||||
public Sheet(Size size)
|
||||
{
|
||||
Size = size;
|
||||
Data = new byte[4*Size.Width*Size.Height];
|
||||
data = new byte[4*Size.Width*Size.Height];
|
||||
}
|
||||
|
||||
public Sheet(ITexture texture)
|
||||
{
|
||||
this.texture = texture;
|
||||
Size = texture.Size;
|
||||
}
|
||||
|
||||
public Sheet(string filename)
|
||||
@@ -33,7 +42,7 @@ namespace OpenRA.Graphics
|
||||
var bitmap = (Bitmap)Image.FromStream(FileSystem.Open(filename));
|
||||
Size = bitmap.Size;
|
||||
|
||||
Data = new byte[4*Size.Width*Size.Height];
|
||||
data = new byte[4*Size.Width*Size.Height];
|
||||
var b = bitmap.LockBits(bitmap.Bounds(),
|
||||
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
@@ -48,10 +57,10 @@ namespace OpenRA.Graphics
|
||||
|
||||
// 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);
|
||||
data[i++] = (byte)(argb >> 0);
|
||||
data[i++] = (byte)(argb >> 8);
|
||||
data[i++] = (byte)(argb >> 16);
|
||||
data[i++] = (byte)(argb >> 24);
|
||||
}
|
||||
}
|
||||
bitmap.UnlockBits(b);
|
||||
@@ -69,7 +78,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
if (dirty)
|
||||
{
|
||||
texture.SetData(Data, Size.Width, Size.Height);
|
||||
texture.SetData(data, Size.Width, Size.Height);
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
@@ -79,6 +88,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public Bitmap AsBitmap()
|
||||
{
|
||||
var d = Data;
|
||||
var b = new Bitmap(Size.Width, Size.Height);
|
||||
var output = b.LockBits(new Rectangle(0, 0, Size.Width, Size.Height),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
@@ -93,7 +103,7 @@ namespace OpenRA.Graphics
|
||||
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];
|
||||
var argb = (d[i+3] << 24) | (d[i+2] << 16) | (d[i+1] << 8) | d[i];
|
||||
*(c + (y * output.Stride >> 2) + x) = argb;
|
||||
}
|
||||
}
|
||||
@@ -104,6 +114,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public Bitmap AsBitmap(TextureChannel channel, Palette pal)
|
||||
{
|
||||
var d = Data;
|
||||
var b = new Bitmap(Size.Width, Size.Height);
|
||||
var output = b.LockBits(new Rectangle(0, 0, Size.Width, Size.Height),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
@@ -115,7 +126,7 @@ namespace OpenRA.Graphics
|
||||
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];
|
||||
var index = d[4*Size.Width*y + 4*x + (int)channel];
|
||||
*(c + (y * output.Stride >> 2) + x) = pal.GetColor(index).ToArgb();
|
||||
}
|
||||
}
|
||||
@@ -124,6 +135,12 @@ namespace OpenRA.Graphics
|
||||
return b;
|
||||
}
|
||||
|
||||
public void MakeDirty() { dirty = true; }
|
||||
public void CommitData()
|
||||
{
|
||||
if (data == null)
|
||||
throw new InvalidOperationException("Texture-wrappers are read-only");
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,8 @@ 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 SheetOverflowException(string message)
|
||||
: base(message) {}
|
||||
}
|
||||
|
||||
public enum SheetType
|
||||
@@ -36,28 +33,39 @@ namespace OpenRA.Graphics
|
||||
SheetType type;
|
||||
int rowHeight = 0;
|
||||
Point p;
|
||||
Func<Sheet> allocateSheet;
|
||||
|
||||
internal SheetBuilder(SheetType t)
|
||||
public static Sheet AllocateSheet()
|
||||
{
|
||||
current = new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize));;
|
||||
channel = TextureChannel.Red;
|
||||
type = t;
|
||||
return new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize));;
|
||||
}
|
||||
|
||||
public Sprite Add(byte[] src, Size size, bool allowSheetOverflow)
|
||||
internal SheetBuilder(SheetType t)
|
||||
: this(t, AllocateSheet) {}
|
||||
|
||||
internal SheetBuilder(SheetType t, Func<Sheet> allocateSheet)
|
||||
{
|
||||
var rect = Allocate(size, allowSheetOverflow);
|
||||
channel = TextureChannel.Red;
|
||||
type = t;
|
||||
current = allocateSheet();
|
||||
this.allocateSheet = allocateSheet;
|
||||
}
|
||||
|
||||
public Sprite Add(byte[] src, Size size)
|
||||
{
|
||||
var rect = Allocate(size);
|
||||
Util.FastCopyIntoChannel(rect, src);
|
||||
current.CommitData();
|
||||
return rect;
|
||||
}
|
||||
|
||||
public Sprite Add(Size size, byte paletteIndex, bool allowSheetOverflow)
|
||||
public Sprite Add(Size size, byte paletteIndex)
|
||||
{
|
||||
var data = new byte[size.Width * size.Height];
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
data[i] = paletteIndex;
|
||||
|
||||
return Add(data, size, allowSheetOverflow);
|
||||
return Add(data, size);
|
||||
}
|
||||
|
||||
TextureChannel? NextChannel(TextureChannel t)
|
||||
@@ -69,7 +77,8 @@ namespace OpenRA.Graphics
|
||||
return (TextureChannel)nextChannel;
|
||||
}
|
||||
|
||||
public Sprite Allocate(Size imageSize, bool allowSheetOverflow)
|
||||
public Sprite Allocate(Size imageSize) { return Allocate(imageSize, float2.Zero); }
|
||||
public Sprite Allocate(Size imageSize, float2 spriteOffset)
|
||||
{
|
||||
if (imageSize.Width + p.X > current.Size.Width)
|
||||
{
|
||||
@@ -85,10 +94,7 @@ namespace OpenRA.Graphics
|
||||
var next = NextChannel(channel);
|
||||
if (next == null)
|
||||
{
|
||||
if (!allowSheetOverflow)
|
||||
throw new SheetOverflowException();
|
||||
|
||||
current = new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize));
|
||||
current = allocateSheet();
|
||||
channel = TextureChannel.Red;
|
||||
}
|
||||
else
|
||||
@@ -98,8 +104,7 @@ namespace OpenRA.Graphics
|
||||
p = new Point(0,0);
|
||||
}
|
||||
|
||||
var rect = new Sprite(current, new Rectangle(p, imageSize), channel);
|
||||
current.MakeDirty();
|
||||
var rect = new Sprite(current, new Rectangle(p, imageSize), spriteOffset, channel);
|
||||
p.X += imageSize.Width;
|
||||
|
||||
return rect;
|
||||
|
||||
@@ -185,14 +185,14 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
s[starti, j].DrawAt(
|
||||
Game.CellSize * new float2(starti, j),
|
||||
pal.Index,
|
||||
pal,
|
||||
new float2(Game.CellSize * (i - starti), Game.CellSize));
|
||||
starti = i + 1;
|
||||
}
|
||||
|
||||
s[i, j].DrawAt(
|
||||
Game.CellSize * new float2(i, j),
|
||||
pal.Index);
|
||||
pal);
|
||||
starti = i + 1;
|
||||
last = s[i, j];
|
||||
}
|
||||
@@ -200,7 +200,7 @@ namespace OpenRA.Graphics
|
||||
if (starti < clip.Right)
|
||||
s[starti, j].DrawAt(
|
||||
Game.CellSize * new float2(starti, j),
|
||||
pal.Index,
|
||||
pal,
|
||||
new float2(Game.CellSize * (clip.Right - starti), Game.CellSize));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,17 @@ namespace OpenRA.Graphics
|
||||
public readonly Sheet sheet;
|
||||
public readonly TextureChannel channel;
|
||||
public readonly float2 size;
|
||||
public readonly float2 offset;
|
||||
readonly float2[] textureCoords;
|
||||
|
||||
public Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel)
|
||||
: this(sheet, bounds, float2.Zero, channel) {}
|
||||
|
||||
public Sprite(Sheet sheet, Rectangle bounds, float2 offset, TextureChannel channel)
|
||||
{
|
||||
this.bounds = bounds;
|
||||
this.sheet = sheet;
|
||||
this.bounds = bounds;
|
||||
this.offset = offset;
|
||||
this.channel = channel;
|
||||
this.size = new float2(bounds.Size);
|
||||
|
||||
@@ -45,24 +50,19 @@ namespace OpenRA.Graphics
|
||||
return textureCoords[k];
|
||||
}
|
||||
|
||||
public void DrawAt(WorldRenderer wr, float2 location, string palette)
|
||||
public void DrawAt(float2 location, PaletteReference pal)
|
||||
{
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, wr, palette, size);
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, pal, size);
|
||||
}
|
||||
|
||||
public void DrawAt(float2 location, int paletteIndex)
|
||||
public void DrawAt(float2 location, PaletteReference pal, float scale)
|
||||
{
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, paletteIndex, size);
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, pal, size*scale);
|
||||
}
|
||||
|
||||
public void DrawAt(float2 location, int paletteIndex, float scale)
|
||||
public void DrawAt(float2 location, PaletteReference pal, float2 size)
|
||||
{
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, paletteIndex, size*scale);
|
||||
}
|
||||
|
||||
public void DrawAt(float2 location, int paletteIndex, float2 size)
|
||||
{
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, paletteIndex, size);
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, pal, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace OpenRA.Graphics
|
||||
face.Glyph.RenderGlyph(RenderMode.Normal);
|
||||
|
||||
var size = new Size((int)face.Glyph.Metrics.Width >> 6, (int)face.Glyph.Metrics.Height >> 6);
|
||||
var s = builder.Allocate(size, true);
|
||||
var s = builder.Allocate(size);
|
||||
|
||||
var g = new GlyphInfo
|
||||
{
|
||||
@@ -129,6 +129,8 @@ namespace OpenRA.Graphics
|
||||
p += face.Glyph.Bitmap.Pitch;
|
||||
}
|
||||
}
|
||||
s.sheet.CommitData();
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,12 +35,12 @@ namespace OpenRA.Graphics
|
||||
if (ImageCount == 0)
|
||||
{
|
||||
var shp = new ShpTSReader(FileSystem.OpenWithExts(filename, exts));
|
||||
return shp.Select(a => SheetBuilder.Add(a.Image, shp.Size, true)).ToArray();
|
||||
return shp.Select(a => SheetBuilder.Add(a.Image, shp.Size)).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
var shp = new ShpReader(FileSystem.OpenWithExts(filename, exts));
|
||||
return shp.Frames.Select(a => SheetBuilder.Add(a.Image, shp.Size, true)).ToArray();
|
||||
return shp.Frames.Select(a => SheetBuilder.Add(a.Image, shp.Size)).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,40 +33,42 @@ namespace OpenRA.Graphics
|
||||
if (nv > 0)
|
||||
{
|
||||
shader.SetTexture("DiffuseTexture", currentSheet.Texture);
|
||||
renderer.Device.EnableAlphaBlending();
|
||||
shader.Render(() =>
|
||||
{
|
||||
var vb = renderer.GetTempVertexBuffer();
|
||||
vb.SetData(vertices, nv);
|
||||
renderer.DrawBatch(vb, 0, nv, PrimitiveType.QuadList);
|
||||
});
|
||||
renderer.Device.DisableAlphaBlending();
|
||||
|
||||
nv = 0;
|
||||
currentSheet = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float2 location, WorldRenderer wr, string palette)
|
||||
public void DrawSprite(Sprite s, float2 location, PaletteReference pal)
|
||||
{
|
||||
DrawSprite(s, location, wr.Palette(palette).Index, s.size);
|
||||
DrawSprite(s, location, pal.Index, s.size);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float2 location, WorldRenderer wr, string palette, float2 size)
|
||||
public void DrawSprite(Sprite s, float2 location, PaletteReference pal, float2 size)
|
||||
{
|
||||
DrawSprite(s, location, wr.Palette(palette).Index, size);
|
||||
DrawSprite(s, location, pal.Index, size);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float2 location, int paletteIndex, float2 size)
|
||||
void DrawSprite(Sprite s, float2 location, int paletteIndex, float2 size)
|
||||
{
|
||||
Renderer.CurrentBatchRenderer = this;
|
||||
|
||||
if (s.sheet != currentSheet)
|
||||
Flush();
|
||||
|
||||
if( nv + 4 > Renderer.TempBufferSize )
|
||||
if (nv + 4 > Renderer.TempBufferSize)
|
||||
Flush();
|
||||
|
||||
currentSheet = s.sheet;
|
||||
Util.FastCreateQuad(vertices, location.ToInt2(), s, paletteIndex, nv, size);
|
||||
Util.FastCreateQuad(vertices, location + s.offset, s, paletteIndex, nv, size);
|
||||
nv += 4;
|
||||
}
|
||||
|
||||
@@ -81,10 +83,27 @@ namespace OpenRA.Graphics
|
||||
DrawSprite(s, location, 0, size);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float2 a, float2 b, float2 c, float2 d)
|
||||
{
|
||||
Renderer.CurrentBatchRenderer = this;
|
||||
|
||||
if (s.sheet != currentSheet)
|
||||
Flush();
|
||||
|
||||
if (nv + 4 > Renderer.TempBufferSize)
|
||||
Flush();
|
||||
|
||||
currentSheet = s.sheet;
|
||||
Util.FastCreateQuad(vertices, a, b, c, d, s, 0, nv);
|
||||
nv += 4;
|
||||
}
|
||||
|
||||
public void DrawVertexBuffer(IVertexBuffer<Vertex> buffer, int start, int length, PrimitiveType type, Sheet sheet)
|
||||
{
|
||||
shader.SetTexture("DiffuseTexture", sheet.Texture);
|
||||
renderer.Device.EnableAlphaBlending();
|
||||
shader.Render(() => renderer.DrawBatch(buffer, start, length, type));
|
||||
renderer.Device.DisableAlphaBlending();
|
||||
}
|
||||
|
||||
public void SetPalette(ITexture palette)
|
||||
|
||||
@@ -29,11 +29,22 @@ namespace OpenRA.Graphics
|
||||
this.world = world;
|
||||
this.map = world.Map;
|
||||
|
||||
// TODO: Use a fixed sheet size specified in the tileset yaml
|
||||
sheetBuilder = new SheetBuilder(SheetType.Indexed);
|
||||
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, false));
|
||||
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];
|
||||
|
||||
57
OpenRA.Game/Graphics/TextRenderable.cs
Normal file
57
OpenRA.Game/Graphics/TextRenderable.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
#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;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public struct TextRenderable : IRenderable
|
||||
{
|
||||
readonly SpriteFont font;
|
||||
readonly WPos pos;
|
||||
readonly int zOffset;
|
||||
readonly Color color;
|
||||
readonly string text;
|
||||
|
||||
public TextRenderable(SpriteFont font, WPos pos, int zOffset, Color color, string text)
|
||||
{
|
||||
this.font = font;
|
||||
this.pos = pos;
|
||||
this.zOffset = zOffset;
|
||||
this.color = color;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public WPos Pos { get { return pos; } }
|
||||
public float Scale { get { return 1f; } }
|
||||
public PaletteReference Palette { get { return null; } }
|
||||
public int ZOffset { get { return zOffset; } }
|
||||
|
||||
public IRenderable WithScale(float newScale) { return new TextRenderable(font, pos, zOffset, color, text); }
|
||||
public IRenderable WithPalette(PaletteReference newPalette) { return new TextRenderable(font, pos, zOffset, color, text); }
|
||||
public IRenderable WithZOffset(int newOffset) { return new TextRenderable(font, pos, zOffset, color, text); }
|
||||
public IRenderable WithPos(WPos pos) { return new TextRenderable(font, pos, zOffset, color, text); }
|
||||
|
||||
public void BeforeRender(WorldRenderer wr) {}
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
var screenPos = Game.viewport.Zoom*(wr.ScreenPxPosition(pos) - Game.viewport.Location) - 0.5f*font.Measure(text).ToFloat2();
|
||||
font.DrawTextWithContrast(text, screenPos, color, Color.Black, 1);
|
||||
}
|
||||
|
||||
public void RenderDebugGeometry(WorldRenderer wr)
|
||||
{
|
||||
var size = font.Measure(text).ToFloat2();
|
||||
var offset = wr.ScreenPxPosition(pos) - 0.5f*size;
|
||||
Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + size, Color.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using OpenRA.FileFormats.Graphics;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
@@ -17,17 +18,21 @@ namespace OpenRA.Graphics
|
||||
static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f };
|
||||
|
||||
public static void FastCreateQuad(Vertex[] vertices, float2 o, Sprite r, int palette, int nv, float2 size)
|
||||
{
|
||||
var b = new float2(o.X + size.X, o.Y);
|
||||
var c = new float2(o.X + size.X, o.Y + size.Y);
|
||||
var d = new float2(o.X, o.Y + size.Y);
|
||||
FastCreateQuad(vertices, o, b, c, d, r, palette, nv);
|
||||
}
|
||||
|
||||
public static void FastCreateQuad(Vertex[] vertices, float2 a, float2 b, float2 c, float2 d, Sprite r, int palette, int nv)
|
||||
{
|
||||
var attrib = new float2(palette / (float)HardwarePalette.MaxPalettes, channelSelect[(int)r.channel]);
|
||||
|
||||
vertices[nv] = new Vertex(o,
|
||||
r.FastMapTextureCoords(0), attrib);
|
||||
vertices[nv + 1] = new Vertex(new float2(o.X + size.X, o.Y),
|
||||
r.FastMapTextureCoords(1), attrib);
|
||||
vertices[nv + 2] = new Vertex(new float2(o.X + size.X, o.Y + size.Y),
|
||||
r.FastMapTextureCoords(3), attrib);
|
||||
vertices[nv + 3] = new Vertex(new float2(o.X, o.Y + size.Y),
|
||||
r.FastMapTextureCoords(2), attrib);
|
||||
vertices[nv] = new Vertex(a, r.FastMapTextureCoords(0), attrib);
|
||||
vertices[nv + 1] = new Vertex(b, r.FastMapTextureCoords(1), attrib);
|
||||
vertices[nv + 2] = new Vertex(c, r.FastMapTextureCoords(3), attrib);
|
||||
vertices[nv + 3] = new Vertex(d, r.FastMapTextureCoords(2), attrib);
|
||||
}
|
||||
|
||||
static readonly int[] channelMasks = { 2, 1, 0, 3 }; // yes, our channel order is nuts.
|
||||
@@ -229,5 +234,41 @@ namespace OpenRA.Graphics
|
||||
|
||||
return mtx;
|
||||
}
|
||||
|
||||
public static float[] MakeFloatMatrix(int[] imtx)
|
||||
{
|
||||
var fmtx = new float[16];
|
||||
for (var i = 0; i < 16; i++)
|
||||
fmtx[i] = imtx[i]*1f / imtx[15];
|
||||
return fmtx;
|
||||
}
|
||||
|
||||
public static float[] MatrixAABBMultiply(float[] mtx, float[] bounds)
|
||||
{
|
||||
// Corner offsets
|
||||
var ix = new uint[] {0,0,0,0,3,3,3,3};
|
||||
var iy = new uint[] {1,1,4,4,1,1,4,4};
|
||||
var iz = new uint[] {2,5,2,5,2,5,2,5};
|
||||
|
||||
// Vectors to opposing corner
|
||||
var ret = new float[] {float.MaxValue, float.MaxValue, float.MaxValue,
|
||||
float.MinValue, float.MinValue, float.MinValue};
|
||||
|
||||
// Transform vectors and find new bounding box
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
var vec = new float[] {bounds[ix[i]], bounds[iy[i]], bounds[iz[i]], 1};
|
||||
var tvec = Util.MatrixVectorMultiply(mtx, vec);
|
||||
|
||||
ret[0] = Math.Min(ret[0], tvec[0]/tvec[3]);
|
||||
ret[1] = Math.Min(ret[1], tvec[1]/tvec[3]);
|
||||
ret[2] = Math.Min(ret[2], tvec[2]/tvec[3]);
|
||||
ret[3] = Math.Max(ret[3], tvec[0]/tvec[3]);
|
||||
ret[4] = Math.Max(ret[4], tvec[1]/tvec[3]);
|
||||
ret[5] = Math.Max(ret[5], tvec[2]/tvec[3]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,19 +27,22 @@ namespace OpenRA.Graphics
|
||||
|
||||
public class Voxel
|
||||
{
|
||||
Limb[] limbs;
|
||||
HvaReader hva;
|
||||
VoxelLoader loader;
|
||||
Limb[] limbData;
|
||||
float[] transforms;
|
||||
|
||||
float[][] transform, lightDirection, groundNormal;
|
||||
float[] groundZ;
|
||||
public readonly uint Frames;
|
||||
public readonly uint Limbs;
|
||||
|
||||
public Voxel(VoxelLoader loader, VxlReader vxl, HvaReader hva)
|
||||
{
|
||||
this.hva = hva;
|
||||
this.loader = loader;
|
||||
if (vxl.LimbCount != hva.LimbCount)
|
||||
throw new InvalidOperationException("Voxel and hva limb counts don't match");
|
||||
|
||||
limbs = new Limb[vxl.LimbCount];
|
||||
transforms = hva.Transforms;
|
||||
Frames = hva.FrameCount;
|
||||
Limbs = hva.LimbCount;
|
||||
|
||||
limbData = new Limb[vxl.LimbCount];
|
||||
for (var i = 0; i < vxl.LimbCount; i++)
|
||||
{
|
||||
var vl = vxl.Limbs[i];
|
||||
@@ -48,107 +51,43 @@ namespace OpenRA.Graphics
|
||||
l.Bounds = (float[])vl.Bounds.Clone();
|
||||
l.Size = (byte[])vl.Size.Clone();
|
||||
l.RenderData = loader.GenerateRenderData(vxl.Limbs[i]);
|
||||
limbs[i] = l;
|
||||
}
|
||||
|
||||
transform = new float[vxl.LimbCount][];
|
||||
lightDirection = new float[vxl.LimbCount][];
|
||||
groundNormal = new float[vxl.LimbCount][];
|
||||
groundZ = new float[vxl.LimbCount];
|
||||
}
|
||||
|
||||
// Extract the rotation components from a matrix and apply them to a vector
|
||||
static float[] ExtractRotationVector(float[] mtx, WVec vec)
|
||||
{
|
||||
var tVec = Util.MatrixVectorMultiply(mtx, new float[] {vec.X, vec.Y, vec.Z, 1});
|
||||
var tOrigin = Util.MatrixVectorMultiply(mtx, new float[] {0,0,0,1});
|
||||
tVec[0] -= tOrigin[0]*tVec[3]/tOrigin[3];
|
||||
tVec[1] -= tOrigin[1]*tVec[3]/tOrigin[3];
|
||||
tVec[2] -= tOrigin[2]*tVec[3]/tOrigin[3];
|
||||
|
||||
// Renormalize
|
||||
var w = (float)Math.Sqrt(tVec[0]*tVec[0] + tVec[1]*tVec[1] + tVec[2]*tVec[2]);
|
||||
tVec[0] /= w;
|
||||
tVec[1] /= w;
|
||||
tVec[2] /= w;
|
||||
tVec[3] = 1f;
|
||||
|
||||
return tVec;
|
||||
}
|
||||
|
||||
static float[] MakeFloatMatrix(int[] imtx)
|
||||
{
|
||||
var fmtx = new float[16];
|
||||
for (var i = 0; i < 16; i++)
|
||||
fmtx[i] = imtx[i]*1f / imtx[15];
|
||||
return fmtx;
|
||||
}
|
||||
|
||||
public void Draw(VoxelRenderer r, float[] lightAmbientColor, float[] lightDiffuseColor,
|
||||
int colorPalette, int normalsPalette)
|
||||
{
|
||||
for (var i = 0; i < limbs.Length; i++)
|
||||
r.Render(loader, limbs[i].RenderData, transform[i], lightDirection[i],
|
||||
lightAmbientColor, lightDiffuseColor, colorPalette, normalsPalette);
|
||||
}
|
||||
|
||||
public void DrawShadow(VoxelRenderer r, int shadowPalette)
|
||||
{
|
||||
for (var i = 0; i < limbs.Length; i++)
|
||||
r.RenderShadow(loader, limbs[i].RenderData, transform[i], lightDirection[i],
|
||||
groundNormal[i], groundZ[i], shadowPalette);
|
||||
}
|
||||
|
||||
static readonly WVec forward = new WVec(1024,0,0);
|
||||
static readonly WVec up = new WVec(0,0,1024);
|
||||
public void PrepareForDraw(WorldRenderer wr, WPos pos, IEnumerable<WRot> rotations,
|
||||
WRot camera, uint frame, float scale, WRot lightSource)
|
||||
{
|
||||
// Calculate the shared view matrix components
|
||||
var pxPos = wr.ScreenPosition(pos);
|
||||
|
||||
// Rotations
|
||||
var rot = rotations.Reverse().Aggregate(MakeFloatMatrix(camera.AsMatrix()),
|
||||
(a,b) => Util.MatrixMultiply(a, MakeFloatMatrix(b.AsMatrix())));
|
||||
|
||||
// Each limb has its own transformation matrix
|
||||
for (uint i = 0; i < limbs.Length; i++)
|
||||
{
|
||||
Limb l = limbs[i];
|
||||
var t = Util.MatrixMultiply(Util.ScaleMatrix(1,-1,1),
|
||||
hva.TransformationMatrix(i, frame));
|
||||
// Fix limb position
|
||||
t[12] *= l.Scale*(l.Bounds[3] - l.Bounds[0]) / l.Size[0];
|
||||
t[13] *= l.Scale*(l.Bounds[4] - l.Bounds[1]) / l.Size[1];
|
||||
t[14] *= l.Scale*(l.Bounds[5] - l.Bounds[2]) / l.Size[2];
|
||||
t = Util.MatrixMultiply(t, Util.TranslationMatrix(l.Bounds[0], l.Bounds[1], l.Bounds[2]));
|
||||
|
||||
// Apply view matrix
|
||||
transform[i] = Util.MatrixMultiply(Util.TranslationMatrix(pxPos.X, pxPos.Y, pxPos.Y),
|
||||
Util.ScaleMatrix(scale*l.Scale, scale*l.Scale, scale*l.Scale));
|
||||
transform[i] = Util.MatrixMultiply(transform[i], rot);
|
||||
transform[i] = Util.MatrixMultiply(transform[i], t);
|
||||
|
||||
// Transform light direction into limb-space
|
||||
var undoPitch = MakeFloatMatrix(new WRot(camera.Pitch, WAngle.Zero, WAngle.Zero).AsMatrix());
|
||||
var lightTransform = Util.MatrixMultiply(Util.MatrixInverse(transform[i]), undoPitch);
|
||||
|
||||
lightDirection[i] = ExtractRotationVector(lightTransform, forward.Rotate(lightSource));
|
||||
groundNormal[i] = ExtractRotationVector(Util.MatrixInverse(t), up);
|
||||
|
||||
// Hack: Extract the ground z position independently of y.
|
||||
groundZ[i] = (wr.ScreenPosition(pos).Y - wr.ScreenZPosition(pos, 0)) / 2;
|
||||
limbData[i] = l;
|
||||
}
|
||||
}
|
||||
|
||||
public uint Frames { get { return hva.FrameCount; }}
|
||||
public uint LimbCount { get { return (uint)limbs.Length; }}
|
||||
public float[] TransformationMatrix(uint limb, uint frame)
|
||||
{
|
||||
if (frame >= Frames)
|
||||
throw new ArgumentOutOfRangeException("frame", "Only {0} frames exist.".F(Frames));
|
||||
if (limb >= Limbs)
|
||||
throw new ArgumentOutOfRangeException("limb", "Only {1} limbs exist.".F(Limbs));
|
||||
|
||||
var l = limbData[limb];
|
||||
var t = new float[16];
|
||||
Array.Copy(transforms, 16*(Limbs*frame + limb), t, 0, 16);
|
||||
|
||||
// Fix limb position
|
||||
t[12] *= l.Scale*(l.Bounds[3] - l.Bounds[0]) / l.Size[0];
|
||||
t[13] *= l.Scale*(l.Bounds[4] - l.Bounds[1]) / l.Size[1];
|
||||
t[14] *= l.Scale*(l.Bounds[5] - l.Bounds[2]) / l.Size[2];
|
||||
|
||||
// Center, flip and scale
|
||||
t = Util.MatrixMultiply(t, Util.TranslationMatrix(l.Bounds[0], l.Bounds[1], l.Bounds[2]));
|
||||
t = Util.MatrixMultiply(Util.ScaleMatrix(l.Scale, -l.Scale, l.Scale), t);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public VoxelRenderData RenderData(uint limb)
|
||||
{
|
||||
return limbData[limb].RenderData;
|
||||
}
|
||||
|
||||
public float[] Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return limbs.Select(a => a.Size.Select(b => a.Scale*b).ToArray())
|
||||
return limbData.Select(a => a.Size.Select(b => a.Scale*b).ToArray())
|
||||
.Aggregate((a,b) => new float[]
|
||||
{
|
||||
Math.Max(a[0], b[0]),
|
||||
@@ -157,5 +96,33 @@ namespace OpenRA.Graphics
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public float[] Bounds(uint frame)
|
||||
{
|
||||
var ret = new float[] {float.MaxValue,float.MaxValue,float.MaxValue,
|
||||
float.MinValue,float.MinValue,float.MinValue};
|
||||
|
||||
for (uint j = 0; j < Limbs; j++)
|
||||
{
|
||||
var l = limbData[j];
|
||||
var b = new float[]
|
||||
{
|
||||
0, 0, 0,
|
||||
(l.Bounds[3] - l.Bounds[0]),
|
||||
(l.Bounds[4] - l.Bounds[1]),
|
||||
(l.Bounds[5] - l.Bounds[2])
|
||||
};
|
||||
|
||||
// Calculate limb bounding box
|
||||
var bb = Util.MatrixAABBMultiply(TransformationMatrix(j, frame), b);
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
ret[i] = Math.Min(ret[i], bb[i]);
|
||||
ret[i+3] = Math.Max(ret[i+3], bb[i+3]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,20 @@ namespace OpenRA.Graphics
|
||||
int totalVertexCount;
|
||||
int cachedVertexCount;
|
||||
|
||||
SheetBuilder CreateSheetBuilder()
|
||||
{
|
||||
var allocated = false;
|
||||
Func<Sheet> allocate = () =>
|
||||
{
|
||||
if (allocated)
|
||||
throw new SheetOverflowException("");
|
||||
allocated = true;
|
||||
return SheetBuilder.AllocateSheet();
|
||||
};
|
||||
|
||||
return new SheetBuilder(SheetType.DualIndexed, allocate);
|
||||
}
|
||||
|
||||
public VoxelLoader()
|
||||
{
|
||||
voxels = new Cache<Pair<string,string>, Voxel>(LoadFile);
|
||||
@@ -48,7 +62,7 @@ namespace OpenRA.Graphics
|
||||
totalVertexCount = 0;
|
||||
cachedVertexCount = 0;
|
||||
|
||||
sheetBuilder = new SheetBuilder(SheetType.DualIndexed);
|
||||
sheetBuilder = CreateSheetBuilder();
|
||||
}
|
||||
|
||||
static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f };
|
||||
@@ -67,10 +81,10 @@ namespace OpenRA.Graphics
|
||||
c++;
|
||||
}
|
||||
|
||||
Sprite s = sheetBuilder.Allocate(new Size(su, sv), false);
|
||||
Sprite s = sheetBuilder.Allocate(new Size(su, sv));
|
||||
Util.FastCopyIntoChannel(s, 0, colors);
|
||||
Util.FastCopyIntoChannel(s, 1, normals);
|
||||
s.sheet.MakeDirty();
|
||||
s.sheet.CommitData();
|
||||
|
||||
var channels = new float2(channelSelect[(int)s.channel], channelSelect[(int)s.channel + 1]);
|
||||
return new Vertex[4]
|
||||
@@ -164,7 +178,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
// Sheet overflow - allocate a new sheet and try once more
|
||||
Log.Write("debug", "Voxel sheet overflow! Generating new sheet");
|
||||
sheetBuilder = new SheetBuilder(SheetType.DualIndexed);
|
||||
sheetBuilder = CreateSheetBuilder();
|
||||
v = GenerateSlicePlanes(l).SelectMany(x => x).ToArray();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ namespace OpenRA.Graphics
|
||||
readonly PaletteReference shadowPalette;
|
||||
readonly float scale;
|
||||
|
||||
// Generated at render-time
|
||||
VoxelRenderProxy renderProxy;
|
||||
|
||||
public VoxelRenderable(IEnumerable<VoxelAnimation> voxels, WPos pos, int zOffset, WRot camera, float scale,
|
||||
WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
|
||||
PaletteReference color, PaletteReference normals, PaletteReference shadow)
|
||||
@@ -44,6 +47,7 @@ namespace OpenRA.Graphics
|
||||
this.palette = color;
|
||||
this.normalsPalette = normals;
|
||||
this.shadowPalette = shadow;
|
||||
this.renderProxy = null;
|
||||
}
|
||||
|
||||
public WPos Pos { get { return pos; } }
|
||||
@@ -79,28 +83,94 @@ namespace OpenRA.Graphics
|
||||
palette, normalsPalette, shadowPalette);
|
||||
}
|
||||
|
||||
// This will need generalizing once we support TS/RA2 terrain
|
||||
static readonly float[] groundNormal = new float[] {0,0,1,1};
|
||||
public void BeforeRender(WorldRenderer wr)
|
||||
{
|
||||
renderProxy = Game.Renderer.WorldVoxelRenderer.RenderAsync(
|
||||
wr, voxels, camera, scale, groundNormal, lightSource,
|
||||
lightAmbientColor, lightDiffuseColor,
|
||||
palette, normalsPalette, shadowPalette);
|
||||
}
|
||||
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
// Depth and shadow buffers are cleared between actors so that
|
||||
// overlapping units and shadows behave like overlapping sprites.
|
||||
var vr = Game.Renderer.WorldVoxelRenderer;
|
||||
var pxOrigin = wr.ScreenPosition(pos);
|
||||
var groundZ = 0.5f*(pxOrigin.Y - wr.ScreenZPosition(pos, 0));
|
||||
var shadowOrigin = pxOrigin - groundZ*(new float2(renderProxy.ShadowDirection, 1));
|
||||
|
||||
var psb = renderProxy.ProjectedShadowBounds;
|
||||
var sa = shadowOrigin + psb[0];
|
||||
var sb = shadowOrigin + psb[2];
|
||||
var sc = shadowOrigin + psb[1];
|
||||
var sd = shadowOrigin + psb[3];
|
||||
Game.Renderer.WorldRgbaSpriteRenderer.DrawSprite(renderProxy.ShadowSprite, sa, sb, sc, sd);
|
||||
Game.Renderer.WorldRgbaSpriteRenderer.DrawSprite(renderProxy.Sprite, pxOrigin - 0.5f*renderProxy.Sprite.size);
|
||||
}
|
||||
|
||||
public void RenderDebugGeometry(WorldRenderer wr)
|
||||
{
|
||||
var pxOrigin = wr.ScreenPosition(pos);
|
||||
var groundZ = 0.5f*(pxOrigin.Y - wr.ScreenZPosition(pos, 0));
|
||||
var shadowOrigin = pxOrigin - groundZ*(new float2(renderProxy.ShadowDirection, 1));
|
||||
|
||||
// Draw sprite rect
|
||||
var offset = pxOrigin + renderProxy.Sprite.offset - 0.5f*renderProxy.Sprite.size;
|
||||
Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + renderProxy.Sprite.size, Color.Red);
|
||||
|
||||
// Draw transformed shadow sprite rect
|
||||
var c = Color.Purple;
|
||||
var psb = renderProxy.ProjectedShadowBounds;
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[1], shadowOrigin + psb[3], c, c);
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[3], shadowOrigin + psb[0], c, c);
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[0], shadowOrigin + psb[2], c, c);
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[2], shadowOrigin + psb[1], c, c);
|
||||
|
||||
// Draw voxel bounding box
|
||||
var draw = voxels.Where(v => v.DisableFunc == null || !v.DisableFunc());
|
||||
var scaleTransform = Util.ScaleMatrix(scale, scale, scale);
|
||||
var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix());
|
||||
|
||||
foreach (var v in draw)
|
||||
v.Voxel.PrepareForDraw(wr, pos + v.OffsetFunc(), v.RotationFunc(), camera,
|
||||
v.FrameFunc(), scale, lightSource);
|
||||
{
|
||||
var bounds = v.Voxel.Bounds(v.FrameFunc());
|
||||
var worldTransform = v.RotationFunc().Reverse().Aggregate(scaleTransform,
|
||||
(x,y) => Util.MatrixMultiply(x, Util.MakeFloatMatrix(y.AsMatrix())));
|
||||
|
||||
Game.Renderer.EnableDepthBuffer();
|
||||
Game.Renderer.EnableStencilBuffer();
|
||||
foreach (var v in draw)
|
||||
v.Voxel.DrawShadow(vr, shadowPalette.Index);
|
||||
Game.Renderer.DisableStencilBuffer();
|
||||
Game.Renderer.DisableDepthBuffer();
|
||||
var pxOffset = wr.ScreenVector(v.OffsetFunc());
|
||||
var pxPos = pxOrigin + new float2(pxOffset[0], pxOffset[1]);
|
||||
var screenTransform = Util.MatrixMultiply(cameraTransform, worldTransform);
|
||||
DrawBoundsBox(pxPos, screenTransform, bounds, Color.Yellow);
|
||||
}
|
||||
}
|
||||
|
||||
Game.Renderer.EnableDepthBuffer();
|
||||
foreach (var v in draw)
|
||||
v.Voxel.Draw(vr, lightAmbientColor, lightDiffuseColor, palette.Index, normalsPalette.Index);
|
||||
Game.Renderer.DisableDepthBuffer();
|
||||
static readonly uint[] ix = new uint[] {0,0,0,0,3,3,3,3};
|
||||
static readonly uint[] iy = new uint[] {1,1,4,4,1,1,4,4};
|
||||
static readonly uint[] iz = new uint[] {2,5,2,5,2,5,2,5};
|
||||
static void DrawBoundsBox(float2 pxPos, float[] transform, float[] bounds, Color c)
|
||||
{
|
||||
var corners = new float2[8];
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
var vec = new float[] {bounds[ix[i]], bounds[iy[i]], bounds[iz[i]], 1};
|
||||
var screen = Util.MatrixVectorMultiply(transform, vec);
|
||||
corners[i] = pxPos + new float2(screen[0], screen[1]);
|
||||
}
|
||||
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(corners[0], corners[1], c, c);
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(corners[1], corners[3], c, c);
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(corners[3], corners[2], c, c);
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(corners[2], corners[0], c, c);
|
||||
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(corners[4], corners[5], c, c);
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(corners[5], corners[7], c, c);
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(corners[7], corners[6], c, c);
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(corners[6], corners[4], c, c);
|
||||
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(corners[0], corners[4], c, c);
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(corners[1], corners[5], c, c);
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(corners[2], corners[6], c, c);
|
||||
Game.Renderer.WorldLineRenderer.DrawLine(corners[3], corners[7], c, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,72 +17,326 @@ using OpenRA.FileFormats.Graphics;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public class VoxelRenderProxy
|
||||
{
|
||||
public readonly Sprite Sprite;
|
||||
public readonly Sprite ShadowSprite;
|
||||
public readonly float ShadowDirection;
|
||||
public readonly float2[] ProjectedShadowBounds;
|
||||
|
||||
public VoxelRenderProxy(Sprite sprite, Sprite shadowSprite, float2[] projectedShadowBounds, float shadowDirection)
|
||||
{
|
||||
Sprite = sprite;
|
||||
ShadowSprite = shadowSprite;
|
||||
ProjectedShadowBounds = projectedShadowBounds;
|
||||
ShadowDirection = shadowDirection;
|
||||
}
|
||||
}
|
||||
|
||||
public class VoxelRenderer
|
||||
{
|
||||
Renderer renderer;
|
||||
IShader shader;
|
||||
IShader shadowShader;
|
||||
|
||||
public VoxelRenderer(Renderer renderer, IShader shader, IShader shadowShader)
|
||||
SheetBuilder sheetBuilder;
|
||||
Dictionary<Sheet, IFrameBuffer> mappedBuffers;
|
||||
Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers;
|
||||
List<Pair<Sheet, Action>> doRender;
|
||||
|
||||
// Static constants
|
||||
static readonly float[] shadowDiffuse = new float[] {0,0,0};
|
||||
static readonly float[] shadowAmbient = new float[] {1,1,1};
|
||||
static readonly float2 spritePadding = new float2(2, 2);
|
||||
static readonly float[] zeroVector = new float[] {0,0,0,1};
|
||||
static readonly float[] zVector = new float[] {0,0,1,1};
|
||||
static readonly float[] flipMtx = Util.ScaleMatrix(1, -1, 1);
|
||||
static readonly float[] shadowScaleFlipMtx = Util.ScaleMatrix(2, -2, 2);
|
||||
|
||||
public VoxelRenderer(Renderer renderer, IShader shader)
|
||||
{
|
||||
this.renderer = renderer;
|
||||
this.shader = shader;
|
||||
this.shadowShader = shadowShader;
|
||||
}
|
||||
|
||||
public void Render(VoxelLoader loader, VoxelRenderData renderData,
|
||||
float[] t, float[] lightDirection,
|
||||
float[] ambientLight, float[] diffuseLight,
|
||||
int colorPalette, int normalsPalette)
|
||||
{
|
||||
shader.SetTexture("DiffuseTexture", renderData.Sheet.Texture);
|
||||
shader.SetVec("PaletteRows", (colorPalette + 0.5f) / HardwarePalette.MaxPalettes,
|
||||
(normalsPalette + 0.5f) / HardwarePalette.MaxPalettes);
|
||||
shader.SetMatrix("TransformMatrix", t);
|
||||
shader.SetVec("LightDirection", lightDirection, 4);
|
||||
shader.SetVec("AmbientLight", ambientLight, 3);
|
||||
shader.SetVec("DiffuseLight", diffuseLight, 3);
|
||||
shader.Render(() => renderer.DrawBatch(loader.VertexBuffer, renderData.Start, renderData.Count, PrimitiveType.QuadList));
|
||||
}
|
||||
|
||||
public void RenderShadow(VoxelLoader loader, VoxelRenderData renderData,
|
||||
float[] t, float[] lightDirection, float[] groundNormal, float groundZ, int colorPalette)
|
||||
{
|
||||
shadowShader.SetTexture("DiffuseTexture", renderData.Sheet.Texture);
|
||||
shadowShader.SetVec("PaletteRows", (colorPalette + 0.5f) / HardwarePalette.MaxPalettes, 0);
|
||||
shadowShader.SetMatrix("TransformMatrix", t);
|
||||
shadowShader.SetVec("LightDirection", lightDirection, 4);
|
||||
shadowShader.SetVec("GroundNormal", groundNormal, 3);
|
||||
shadowShader.SetVec("GroundZ", groundZ);
|
||||
shadowShader.Render(() => renderer.DrawBatch(loader.VertexBuffer, renderData.Start, renderData.Count, PrimitiveType.QuadList));
|
||||
mappedBuffers = new Dictionary<Sheet, IFrameBuffer>();
|
||||
unmappedBuffers = new Stack<KeyValuePair<Sheet, IFrameBuffer>>();
|
||||
doRender = new List<Pair<Sheet, Action>>();
|
||||
}
|
||||
|
||||
public void SetPalette(ITexture palette)
|
||||
{
|
||||
shader.SetTexture("Palette", palette);
|
||||
shadowShader.SetTexture("Palette", palette);
|
||||
}
|
||||
|
||||
public void SetViewportParams(Size screen, float zoom, float2 scroll)
|
||||
{
|
||||
// Construct projection matrix
|
||||
// Clip planes are set at -height and +2*height
|
||||
|
||||
var tiw = 2*zoom / screen.Width;
|
||||
var tih = 2*zoom / screen.Height;
|
||||
var a = 2f / Renderer.SheetSize;
|
||||
var view = new float[]
|
||||
{
|
||||
tiw, 0, 0, 0,
|
||||
0, -tih, 0, 0,
|
||||
0, 0, -tih/3, 0,
|
||||
-1 - tiw*scroll.X,
|
||||
1 + tih*scroll.Y,
|
||||
1 + tih*scroll.Y/3,
|
||||
1
|
||||
a, 0, 0, 0,
|
||||
0, -a, 0, 0,
|
||||
0, 0, -2*a, 0,
|
||||
-1, 1, 0, 1
|
||||
};
|
||||
|
||||
shader.SetMatrix("View", view);
|
||||
shadowShader.SetMatrix("View", view);
|
||||
}
|
||||
|
||||
public VoxelRenderProxy RenderAsync(WorldRenderer wr, IEnumerable<VoxelAnimation> voxels, WRot camera, float scale,
|
||||
float[] groundNormal, WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
|
||||
PaletteReference color, PaletteReference normals, PaletteReference shadowPalette)
|
||||
{
|
||||
// Correct for inverted y-axis
|
||||
var scaleTransform = Util.ScaleMatrix(scale, scale, scale);
|
||||
|
||||
// Correct for bogus light source definition
|
||||
var lightYaw = Util.MakeFloatMatrix(new WRot(WAngle.Zero, WAngle.Zero, -lightSource.Yaw).AsMatrix());
|
||||
var lightPitch = Util.MakeFloatMatrix(new WRot(WAngle.Zero, -lightSource.Pitch, WAngle.Zero).AsMatrix());
|
||||
var shadowTransform = Util.MatrixMultiply(lightPitch, lightYaw);
|
||||
|
||||
var invShadowTransform = Util.MatrixInverse(shadowTransform);
|
||||
var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix());
|
||||
var invCameraTransform = Util.MatrixInverse(cameraTransform);
|
||||
|
||||
// Sprite rectangle
|
||||
var tl = new float2(float.MaxValue, float.MaxValue);
|
||||
var br = new float2(float.MinValue, float.MinValue);
|
||||
|
||||
// Shadow sprite rectangle
|
||||
var stl = new float2(float.MaxValue, float.MaxValue);
|
||||
var sbr = new float2(float.MinValue, float.MinValue);
|
||||
|
||||
foreach (var v in voxels)
|
||||
{
|
||||
// Convert screen offset back to world coords
|
||||
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(v.OffsetFunc()));
|
||||
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
|
||||
|
||||
var worldTransform = v.RotationFunc().Aggregate(Util.IdentityMatrix(),
|
||||
(x,y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
|
||||
worldTransform = Util.MatrixMultiply(scaleTransform, worldTransform);
|
||||
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
|
||||
|
||||
var bounds = v.Voxel.Bounds(v.FrameFunc());
|
||||
var worldBounds = Util.MatrixAABBMultiply(worldTransform, bounds);
|
||||
var screenBounds = Util.MatrixAABBMultiply(cameraTransform, worldBounds);
|
||||
var shadowBounds = Util.MatrixAABBMultiply(shadowTransform, worldBounds);
|
||||
|
||||
// Aggregate bounds rects
|
||||
tl = float2.Min(tl, new float2(screenBounds[0], screenBounds[1]));
|
||||
br = float2.Max(br, new float2(screenBounds[3], screenBounds[4]));
|
||||
stl = float2.Min(stl, new float2(shadowBounds[0], shadowBounds[1]));
|
||||
sbr = float2.Max(sbr, new float2(shadowBounds[3], shadowBounds[4]));
|
||||
}
|
||||
|
||||
// Inflate rects to ensure rendering is within bounds
|
||||
tl -= spritePadding;
|
||||
br += spritePadding;
|
||||
stl -= spritePadding;
|
||||
sbr += spritePadding;
|
||||
|
||||
// Corners of the shadow quad, in shadow-space
|
||||
var corners = new float[][]
|
||||
{
|
||||
new float[] {stl.X, stl.Y, 0, 1},
|
||||
new float[] {sbr.X, sbr.Y, 0, 1},
|
||||
new float[] {sbr.X, stl.Y, 0, 1},
|
||||
new float[] {stl.X, sbr.Y, 0, 1}
|
||||
};
|
||||
|
||||
var shadowScreenTransform = Util.MatrixMultiply(cameraTransform, invShadowTransform);
|
||||
var shadowGroundNormal = Util.MatrixVectorMultiply(shadowTransform, groundNormal);
|
||||
var screenCorners = new float2[4];
|
||||
for (var j = 0; j < 4; j++)
|
||||
{
|
||||
// Project to ground plane
|
||||
corners[j][2] = -(corners[j][1]*shadowGroundNormal[1]/shadowGroundNormal[2] +
|
||||
corners[j][0]*shadowGroundNormal[0]/shadowGroundNormal[2]);
|
||||
|
||||
// Rotate to camera-space
|
||||
corners[j] = Util.MatrixVectorMultiply(shadowScreenTransform, corners[j]);
|
||||
screenCorners[j] = new float2(corners[j][0], corners[j][1]);
|
||||
}
|
||||
|
||||
// Shadows are rendered at twice the resolution to reduce artefacts
|
||||
Size spriteSize, shadowSpriteSize;
|
||||
int2 spriteOffset, shadowSpriteOffset;
|
||||
CalculateSpriteGeometry(tl, br, 1, out spriteSize, out spriteOffset);
|
||||
CalculateSpriteGeometry(stl, sbr, 2, out shadowSpriteSize, out shadowSpriteOffset);
|
||||
|
||||
var sprite = sheetBuilder.Allocate(spriteSize, spriteOffset);
|
||||
var shadowSprite = sheetBuilder.Allocate(shadowSpriteSize, shadowSpriteOffset);
|
||||
var sb = sprite.bounds;
|
||||
var ssb = shadowSprite.bounds;
|
||||
var spriteCenter = new float2(sb.Left + sb.Width / 2, sb.Top + sb.Height / 2);
|
||||
var shadowCenter = new float2(ssb.Left + ssb.Width / 2, ssb.Top + ssb.Height / 2);
|
||||
|
||||
var translateMtx = Util.TranslationMatrix(spriteCenter.X - spriteOffset.X, Renderer.SheetSize - (spriteCenter.Y - spriteOffset.Y), 0);
|
||||
var shadowTranslateMtx = Util.TranslationMatrix(shadowCenter.X - shadowSpriteOffset.X, Renderer.SheetSize - (shadowCenter.Y - shadowSpriteOffset.Y), 0);
|
||||
var correctionTransform = Util.MatrixMultiply(translateMtx, flipMtx);
|
||||
var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, shadowScaleFlipMtx);
|
||||
|
||||
doRender.Add(Pair.New<Sheet, Action>(sprite.sheet, () =>
|
||||
{
|
||||
foreach (var v in voxels)
|
||||
{
|
||||
// Convert screen offset to world offset
|
||||
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(v.OffsetFunc()));
|
||||
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
|
||||
|
||||
var rotations = v.RotationFunc().Aggregate(Util.IdentityMatrix(),
|
||||
(x,y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
|
||||
var worldTransform = Util.MatrixMultiply(scaleTransform, rotations);
|
||||
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
|
||||
|
||||
var transform = Util.MatrixMultiply(cameraTransform, worldTransform);
|
||||
transform = Util.MatrixMultiply(correctionTransform, transform);
|
||||
|
||||
var shadow = Util.MatrixMultiply(shadowTransform, worldTransform);
|
||||
shadow = Util.MatrixMultiply(shadowCorrectionTransform, shadow);
|
||||
|
||||
var lightTransform = Util.MatrixMultiply(Util.MatrixInverse(rotations), invShadowTransform);
|
||||
|
||||
var frame = v.FrameFunc();
|
||||
for (uint i = 0; i < v.Voxel.Limbs; i++)
|
||||
{
|
||||
var rd = v.Voxel.RenderData(i);
|
||||
var t = v.Voxel.TransformationMatrix(i, frame);
|
||||
|
||||
// Transform light vector from shadow -> world -> limb coords
|
||||
var lightDirection = ExtractRotationVector(Util.MatrixMultiply(Util.MatrixInverse(t), lightTransform));
|
||||
|
||||
Render(rd, Util.MatrixMultiply(transform, t), lightDirection,
|
||||
lightAmbientColor, lightDiffuseColor, color.Index, normals.Index);
|
||||
|
||||
// Disable shadow normals by forcing zero diffuse and identity ambient light
|
||||
Render(rd, Util.MatrixMultiply(shadow, t), lightDirection,
|
||||
shadowAmbient, shadowDiffuse, shadowPalette.Index, normals.Index);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
var screenLightVector = Util.MatrixVectorMultiply(invShadowTransform, zVector);
|
||||
screenLightVector = Util.MatrixVectorMultiply(cameraTransform, screenLightVector);
|
||||
return new VoxelRenderProxy(sprite, shadowSprite, screenCorners, -screenLightVector[2]/screenLightVector[1]);
|
||||
}
|
||||
|
||||
static void CalculateSpriteGeometry(float2 tl, float2 br, float scale, out Size size, out int2 offset)
|
||||
{
|
||||
var width = (int)(scale*(br.X - tl.X));
|
||||
var height = (int)(scale*(br.Y - tl.Y));
|
||||
offset = (0.5f*scale*(br + tl)).ToInt2();
|
||||
|
||||
// Width and height must be even to avoid rendering glitches
|
||||
if ((width & 1) == 1)
|
||||
width += 1;
|
||||
if ((height & 1) == 1)
|
||||
height += 1;
|
||||
|
||||
size = new Size(width, height);
|
||||
}
|
||||
|
||||
static float[] ExtractRotationVector(float[] mtx)
|
||||
{
|
||||
var tVec = Util.MatrixVectorMultiply(mtx, zVector);
|
||||
var tOrigin = Util.MatrixVectorMultiply(mtx, zeroVector);
|
||||
tVec[0] -= tOrigin[0]*tVec[3]/tOrigin[3];
|
||||
tVec[1] -= tOrigin[1]*tVec[3]/tOrigin[3];
|
||||
tVec[2] -= tOrigin[2]*tVec[3]/tOrigin[3];
|
||||
|
||||
// Renormalize
|
||||
var w = (float)Math.Sqrt(tVec[0]*tVec[0] + tVec[1]*tVec[1] + tVec[2]*tVec[2]);
|
||||
tVec[0] /= w;
|
||||
tVec[1] /= w;
|
||||
tVec[2] /= w;
|
||||
tVec[3] = 1f;
|
||||
|
||||
return tVec;
|
||||
}
|
||||
|
||||
void Render(VoxelRenderData renderData,
|
||||
float[] t, float[] lightDirection,
|
||||
float[] ambientLight, float[] diffuseLight,
|
||||
int colorPalette, int normalsPalette)
|
||||
{
|
||||
shader.SetTexture("DiffuseTexture", renderData.Sheet.Texture);
|
||||
shader.SetVec("PaletteRows", (colorPalette + 0.5f) / HardwarePalette.MaxPalettes,
|
||||
(normalsPalette + 0.5f) / HardwarePalette.MaxPalettes);
|
||||
shader.SetMatrix("TransformMatrix", t);
|
||||
shader.SetVec("LightDirection", lightDirection, 4);
|
||||
shader.SetVec("AmbientLight", ambientLight, 3);
|
||||
shader.SetVec("DiffuseLight", diffuseLight, 3);
|
||||
|
||||
shader.Render(() => renderer.DrawBatch(Game.modData.VoxelLoader.VertexBuffer, renderData.Start, renderData.Count, PrimitiveType.QuadList));
|
||||
}
|
||||
|
||||
public void BeginFrame()
|
||||
{
|
||||
foreach (var kv in mappedBuffers)
|
||||
unmappedBuffers.Push(kv);
|
||||
mappedBuffers.Clear();
|
||||
|
||||
sheetBuilder = new SheetBuilder(SheetType.BGRA, AllocateSheet);
|
||||
doRender.Clear();
|
||||
}
|
||||
|
||||
IFrameBuffer EnableFrameBuffer(Sheet s)
|
||||
{
|
||||
var fbo = mappedBuffers[s];
|
||||
Game.Renderer.Flush();
|
||||
fbo.Bind();
|
||||
|
||||
Game.Renderer.Device.EnableDepthBuffer();
|
||||
return fbo;
|
||||
}
|
||||
|
||||
void DisableFrameBuffer(IFrameBuffer fbo)
|
||||
{
|
||||
Game.Renderer.Flush();
|
||||
Game.Renderer.Device.DisableDepthBuffer();
|
||||
fbo.Unbind();
|
||||
}
|
||||
|
||||
public void EndFrame()
|
||||
{
|
||||
if (doRender.Count == 0)
|
||||
return;
|
||||
|
||||
Sheet currentSheet = null;
|
||||
IFrameBuffer fbo = null;
|
||||
foreach (var v in doRender)
|
||||
{
|
||||
// Change sheet
|
||||
if (v.First != currentSheet)
|
||||
{
|
||||
if (fbo != null)
|
||||
DisableFrameBuffer(fbo);
|
||||
|
||||
currentSheet = v.First;
|
||||
fbo = EnableFrameBuffer(currentSheet);
|
||||
}
|
||||
|
||||
v.Second();
|
||||
}
|
||||
|
||||
DisableFrameBuffer(fbo);
|
||||
}
|
||||
|
||||
public Sheet AllocateSheet()
|
||||
{
|
||||
// Reuse cached fbo
|
||||
if (unmappedBuffers.Count > 0)
|
||||
{
|
||||
var kv = unmappedBuffers.Pop();
|
||||
mappedBuffers.Add(kv.Key, kv.Value);
|
||||
return kv.Key;
|
||||
}
|
||||
|
||||
var size = new Size(Renderer.SheetSize, Renderer.SheetSize);
|
||||
var framebuffer = renderer.Device.CreateFrameBuffer(size);
|
||||
var sheet = new Sheet(framebuffer.Texture);
|
||||
mappedBuffers.Add(sheet, framebuffer);
|
||||
|
||||
return sheet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace OpenRA.Graphics
|
||||
internal readonly ShroudRenderer shroudRenderer;
|
||||
internal readonly HardwarePalette palette;
|
||||
internal Cache<string, PaletteReference> palettes;
|
||||
Lazy<DeveloperMode> devTrait;
|
||||
|
||||
internal WorldRenderer(World world)
|
||||
{
|
||||
@@ -51,6 +52,8 @@ namespace OpenRA.Graphics
|
||||
|
||||
terrainRenderer = new TerrainRenderer(world, this);
|
||||
shroudRenderer = new ShroudRenderer(world);
|
||||
|
||||
devTrait = Lazy.New(() => world.LocalPlayer != null ? world.LocalPlayer.PlayerActor.Trait<DeveloperMode>() : null);
|
||||
}
|
||||
|
||||
PaletteReference CreatePaletteReference(string name)
|
||||
@@ -65,7 +68,7 @@ namespace OpenRA.Graphics
|
||||
public PaletteReference Palette(string name) { return palettes[name]; }
|
||||
public void AddPalette(string name, Palette pal, bool allowModifiers) { palette.AddPalette(name, pal, allowModifiers); }
|
||||
|
||||
void DrawRenderables()
|
||||
List<IRenderable> GenerateRenderables()
|
||||
{
|
||||
var bounds = Game.viewport.WorldBounds(world);
|
||||
var comparer = new RenderableComparer(this);
|
||||
@@ -74,14 +77,23 @@ namespace OpenRA.Graphics
|
||||
bounds.TopLeftAsCPos().ToPPos(),
|
||||
bounds.BottomRightAsCPos().ToPPos());
|
||||
|
||||
actors.SelectMany(a => a.Render(this))
|
||||
.OrderBy(r => r, comparer)
|
||||
.Do(rr => rr.Render(this));
|
||||
var worldRenderables = actors.SelectMany(a => a.Render(this))
|
||||
.OrderBy(r => r, comparer);
|
||||
|
||||
// Effects are drawn on top of all actors
|
||||
// TODO: Allow effects to be interleaved with actors
|
||||
world.Effects.SelectMany(e => e.Render(this))
|
||||
.Do(rr => rr.Render(this));
|
||||
var effectRenderables = world.Effects
|
||||
.SelectMany(e => e.Render(this));
|
||||
|
||||
// Iterating via foreach() copies the structs, so enumerate by index
|
||||
var renderables = worldRenderables.Concat(effectRenderables).ToList();
|
||||
|
||||
Game.Renderer.WorldVoxelRenderer.BeginFrame();
|
||||
for (var i = 0; i < renderables.Count; i++)
|
||||
renderables[i].BeforeRender(this);
|
||||
Game.Renderer.WorldVoxelRenderer.EndFrame();
|
||||
|
||||
return renderables;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
@@ -91,6 +103,7 @@ namespace OpenRA.Graphics
|
||||
if (world.IsShellmap && !Game.Settings.Game.ShowShellmap)
|
||||
return;
|
||||
|
||||
var renderables = GenerateRenderables();
|
||||
var bounds = Game.viewport.ViewBounds(world);
|
||||
Game.Renderer.EnableScissor(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
|
||||
|
||||
@@ -109,7 +122,8 @@ namespace OpenRA.Graphics
|
||||
if (world.OrderGenerator != null)
|
||||
world.OrderGenerator.RenderBeforeWorld(this, world);
|
||||
|
||||
DrawRenderables();
|
||||
for (var i = 0; i < renderables.Count; i++)
|
||||
renderables[i].Render(this);
|
||||
|
||||
// added for contrails
|
||||
foreach (var a in world.ActorsWithTrait<IPostRender>())
|
||||
@@ -121,6 +135,11 @@ namespace OpenRA.Graphics
|
||||
|
||||
var renderShroud = world.RenderPlayer != null ? world.RenderPlayer.Shroud : null;
|
||||
shroudRenderer.Draw(this, renderShroud);
|
||||
|
||||
if (devTrait.Value != null && devTrait.Value.ShowDebugGeometry)
|
||||
for (var i = 0; i < renderables.Count; i++)
|
||||
renderables[i].RenderDebugGeometry(this);
|
||||
|
||||
Game.Renderer.DisableScissor();
|
||||
|
||||
foreach (var g in world.Selection.Actors.Where(a => !a.Destroyed)
|
||||
@@ -219,6 +238,13 @@ namespace OpenRA.Graphics
|
||||
return new int2((int)Math.Round(px.X), (int)Math.Round(px.Y));
|
||||
}
|
||||
|
||||
// For scaling vectors to pixel sizes in the voxel renderer
|
||||
public float[] ScreenVector(WVec vec)
|
||||
{
|
||||
var c = Game.CellSize/1024f;
|
||||
return new float[] {c*vec.X, c*vec.Y, c*vec.Z, 1};
|
||||
}
|
||||
|
||||
public float ScreenZPosition(WPos pos, int zOffset) { return (pos.Y + pos.Z + zOffset)*Game.CellSize/1024f; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,6 +233,8 @@
|
||||
<Compile Include="Traits\BodyOrientation.cs" />
|
||||
<Compile Include="Graphics\VoxelAnimation.cs" />
|
||||
<Compile Include="Graphics\VoxelRenderable.cs" />
|
||||
<Compile Include="Graphics\TextRenderable.cs" />
|
||||
<Compile Include="Graphics\BeamRenderable.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace OpenRA.Traits
|
||||
public bool UnlimitedPower;
|
||||
public bool BuildAnywhere;
|
||||
public bool ShowMuzzles;
|
||||
public bool ShowDebugGeometry;
|
||||
|
||||
public object Create (ActorInitializer init) { return new DeveloperMode(this); }
|
||||
}
|
||||
@@ -39,6 +40,7 @@ namespace OpenRA.Traits
|
||||
|
||||
// Client size only
|
||||
public bool ShowMuzzles;
|
||||
public bool ShowDebugGeometry;
|
||||
|
||||
public DeveloperMode(DeveloperModeInfo info)
|
||||
{
|
||||
@@ -50,6 +52,7 @@ namespace OpenRA.Traits
|
||||
UnlimitedPower = info.UnlimitedPower;
|
||||
BuildAnywhere = info.BuildAnywhere;
|
||||
ShowMuzzles = info.ShowMuzzles;
|
||||
ShowDebugGeometry = info.ShowDebugGeometry;
|
||||
}
|
||||
|
||||
public void ResolveOrder (Actor self, Order order)
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace OpenRA.Traits
|
||||
var pipImages = new Animation("pips");
|
||||
pipImages.PlayFetchIndex("groups", () => (int)group);
|
||||
pipImages.Tick();
|
||||
pipImages.Image.DrawAt(wr, basePosition + new float2(-8, 1), "chrome");
|
||||
pipImages.Image.DrawAt(basePosition + new float2(-8, 1), wr.Palette("chrome"));
|
||||
}
|
||||
|
||||
void DrawPips(WorldRenderer wr, Actor self, float2 basePosition)
|
||||
@@ -69,6 +69,7 @@ namespace OpenRA.Traits
|
||||
var pipSize = pipImages.Image.size;
|
||||
var pipxyBase = basePosition + new float2(1, -pipSize.Y);
|
||||
var pipxyOffset = new float2(0, 0); // Correct for offset due to multiple columns/rows
|
||||
var pal = wr.Palette("chrome");
|
||||
|
||||
foreach (var pips in pipSources)
|
||||
{
|
||||
@@ -86,7 +87,7 @@ namespace OpenRA.Traits
|
||||
pipxyOffset.Y -= pipSize.Y;
|
||||
}
|
||||
pipImages.PlayRepeating(pipStrings[(int)pip]);
|
||||
pipImages.Image.DrawAt(wr, pipxyBase + pipxyOffset, "chrome");
|
||||
pipImages.Image.DrawAt(pipxyBase + pipxyOffset, pal);
|
||||
pipxyOffset += new float2(pipSize.X, 0);
|
||||
}
|
||||
|
||||
@@ -104,7 +105,7 @@ namespace OpenRA.Traits
|
||||
// If a mod wants to implement a unit with multiple tags, then they are placed on multiple rows
|
||||
var tagxyBase = basePosition + new float2(-16, 2); // Correct for the offset in the shp file
|
||||
var tagxyOffset = new float2(0, 0); // Correct for offset due to multiple rows
|
||||
|
||||
var pal = wr.Palette("chrome");
|
||||
foreach (var tags in self.TraitsImplementing<ITags>())
|
||||
{
|
||||
foreach (var tag in tags.GetTags())
|
||||
@@ -114,7 +115,7 @@ namespace OpenRA.Traits
|
||||
|
||||
var tagImages = new Animation("pips");
|
||||
tagImages.PlayRepeating(tagStrings[(int)tag]);
|
||||
tagImages.Image.DrawAt(wr, tagxyBase + tagxyOffset, "chrome");
|
||||
tagImages.Image.DrawAt(tagxyBase + tagxyOffset, pal);
|
||||
|
||||
// Increment row
|
||||
tagxyOffset.Y += 8;
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace OpenRA.Traits
|
||||
if (c.image != null)
|
||||
c.image[c.density].DrawAt(
|
||||
new CPos(x, y).ToPPos().ToFloat2(),
|
||||
c.type.info.PaletteRef.Index);
|
||||
c.type.info.PaletteRef);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace OpenRA.Widgets
|
||||
cachedFrame = frame;
|
||||
}
|
||||
|
||||
Game.Renderer.SpriteRenderer.DrawSprite(sprite, RenderOrigin, worldRenderer, palette);
|
||||
Game.Renderer.SpriteRenderer.DrawSprite(sprite, RenderOrigin, worldRenderer.Palette(palette));
|
||||
}
|
||||
|
||||
public int FrameCount
|
||||
|
||||
@@ -31,12 +31,12 @@ namespace OpenRA.Widgets
|
||||
|
||||
public static void DrawSHP(Sprite s, float2 pos, WorldRenderer wr)
|
||||
{
|
||||
Game.Renderer.SpriteRenderer.DrawSprite(s,pos, wr, "chrome");
|
||||
Game.Renderer.SpriteRenderer.DrawSprite(s, pos, wr.Palette("chrome"));
|
||||
}
|
||||
|
||||
public static void DrawSHP(Sprite s, float2 pos, WorldRenderer wr, float2 size)
|
||||
{
|
||||
Game.Renderer.SpriteRenderer.DrawSprite(s, pos, wr, "chrome", size);
|
||||
Game.Renderer.SpriteRenderer.DrawSprite(s, pos, wr.Palette("chrome"), size);
|
||||
}
|
||||
|
||||
public static void DrawPanel(string collection, Rectangle Bounds)
|
||||
|
||||
Reference in New Issue
Block a user