@@ -137,6 +137,11 @@ namespace OpenRA
|
||||
return v;
|
||||
}
|
||||
|
||||
public static bool IsPowerOf2(int v)
|
||||
{
|
||||
return (v & (v - 1)) == 0;
|
||||
}
|
||||
|
||||
public static Size NextPowerOf2(this Size s) { return new Size(NextPowerOf2(s.Width), NextPowerOf2(s.Height)); }
|
||||
|
||||
public static string JoinWith<T>(this IEnumerable<T> ts, string j)
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
public readonly uint FrameCount;
|
||||
public readonly uint LimbCount;
|
||||
float[] Transforms;
|
||||
public readonly float[] Transforms;
|
||||
|
||||
public HvaReader(Stream s)
|
||||
{
|
||||
@@ -48,19 +48,6 @@ namespace OpenRA.FileFormats
|
||||
}
|
||||
}
|
||||
|
||||
public float[] TransformationMatrix(uint limb, uint frame)
|
||||
{
|
||||
if (frame >= FrameCount)
|
||||
throw new ArgumentOutOfRangeException("frame", "Only {0} frames exist.".F(FrameCount));
|
||||
if (limb >= LimbCount)
|
||||
throw new ArgumentOutOfRangeException("limb", "Only {1} limbs exist.".F(LimbCount));
|
||||
|
||||
var t = new float[16];
|
||||
Array.Copy(Transforms, 16*(LimbCount*frame + limb), t, 0, 16);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public static HvaReader Load(string filename)
|
||||
{
|
||||
using (var s = File.OpenRead(filename))
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace OpenRA.FileFormats.Graphics
|
||||
IVertexBuffer<Vertex> CreateVertexBuffer( int length );
|
||||
ITexture CreateTexture( Bitmap bitmap );
|
||||
ITexture CreateTexture();
|
||||
IFrameBuffer CreateFrameBuffer(Size s);
|
||||
IShader CreateShader( string name );
|
||||
|
||||
Size WindowSize { get; }
|
||||
@@ -54,8 +55,8 @@ namespace OpenRA.FileFormats.Graphics
|
||||
void EnableDepthBuffer();
|
||||
void DisableDepthBuffer();
|
||||
|
||||
void EnableStencilBuffer();
|
||||
void DisableStencilBuffer();
|
||||
void EnableAlphaBlending();
|
||||
void DisableAlphaBlending();
|
||||
}
|
||||
|
||||
public interface IVertexBuffer<T>
|
||||
@@ -79,6 +80,15 @@ namespace OpenRA.FileFormats.Graphics
|
||||
void SetData(Bitmap bitmap);
|
||||
void SetData(uint[,] colors);
|
||||
void SetData(byte[] colors, int width, int height);
|
||||
byte[] GetData();
|
||||
Size Size { get; }
|
||||
}
|
||||
|
||||
public interface IFrameBuffer
|
||||
{
|
||||
void Bind();
|
||||
void Unbind();
|
||||
ITexture Texture { get; }
|
||||
}
|
||||
|
||||
public enum PrimitiveType
|
||||
|
||||
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)
|
||||
|
||||
@@ -27,15 +27,15 @@ namespace OpenRA.Mods.RA.Activities
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
if (IsCanceled || !target.IsValid)
|
||||
if (IsCanceled || !target.IsValid || !target.IsActor)
|
||||
return NextActivity;
|
||||
|
||||
var targetPlayer = target.Actor.Owner;
|
||||
targetPlayer.PlayerActor.Trait<PlayerResources>().GiveCash(payload);
|
||||
var targetActor = target.Actor;
|
||||
targetActor.Owner.PlayerActor.Trait<PlayerResources>().GiveCash(payload);
|
||||
self.Destroy();
|
||||
|
||||
if (self.Owner.IsAlliedWith(self.World.RenderPlayer))
|
||||
self.World.AddFrameEndTask(w => w.Add(new CashTick(payload, 30, 2, target.CenterLocation, targetPlayer.Color.RGB)));
|
||||
self.World.AddFrameEndTask(w => w.Add(new CashTick(targetActor.CenterPosition, targetActor.Owner.Color.RGB, payload)));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace OpenRA.Mods.RA.Activities
|
||||
ns.Sold(self);
|
||||
|
||||
if (refund > 0 && self.Owner.IsAlliedWith(self.World.RenderPlayer))
|
||||
self.World.AddFrameEndTask(w => w.Add(new CashTick(refund, 30, 2, self.CenterLocation, self.Owner.Color.RGB)));
|
||||
self.World.AddFrameEndTask(w => w.Add(new CashTick(self.CenterPosition, self.Owner.Color.RGB, refund)));
|
||||
|
||||
self.Destroy();
|
||||
return this;
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace OpenRA.Mods.RA
|
||||
cachedTileset = self.World.Map.Tileset;
|
||||
var tileSize = new Size(Game.CellSize, Game.CellSize);
|
||||
sprites = new Cache<TileReference<ushort,byte>, Sprite>(
|
||||
x => Game.modData.SheetBuilder.Add(self.World.TileSet.GetBytes(x), tileSize, true));
|
||||
x => Game.modData.SheetBuilder.Add(self.World.TileSet.GetBytes(x), tileSize));
|
||||
}
|
||||
|
||||
// Cache templates and tiles for the different states
|
||||
|
||||
@@ -73,6 +73,7 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
|
||||
public void Render( WorldRenderer wr )
|
||||
{
|
||||
var pal = wr.Palette("terrain");
|
||||
var cliprect = Game.viewport.WorldBounds(world);
|
||||
foreach (var kv in tiles)
|
||||
{
|
||||
@@ -81,7 +82,7 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
if (world.ShroudObscures(kv.Key))
|
||||
continue;
|
||||
|
||||
bibSprites[kv.Value.type - 1][kv.Value.index].DrawAt(wr, kv.Key.ToPPos().ToFloat2(), "terrain");
|
||||
bibSprites[kv.Value.type - 1][kv.Value.index].DrawAt(kv.Key.ToPPos().ToFloat2(), pal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,6 @@ namespace OpenRA.Mods.RA
|
||||
public readonly int Amount = 15;
|
||||
[Desc("Whether to show the cash tick indicators (+$15 rising from actor).")]
|
||||
public readonly bool ShowTicks = true;
|
||||
[Desc("How long the cash tick indicator should be shown for.")]
|
||||
public readonly int TickLifetime = 30;
|
||||
[Desc("Pixels/tick upward movement of the cash tick indicator.")]
|
||||
public readonly int TickVelocity = 1;
|
||||
[Desc("Amount of money awarded for capturing the actor.")]
|
||||
public readonly int CaptureAmount = 0;
|
||||
|
||||
@@ -64,7 +60,7 @@ namespace OpenRA.Mods.RA
|
||||
void MaybeAddCashTick(Actor self, int amount)
|
||||
{
|
||||
if (Info.ShowTicks)
|
||||
self.World.AddFrameEndTask(w => w.Add(new CashTick(amount, Info.TickLifetime, Info.TickVelocity, self.CenterLocation, self.Owner.Color.RGB)));
|
||||
self.World.AddFrameEndTask(w => w.Add(new CashTick(self.CenterPosition, self.Owner.Color.RGB, amount)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace OpenRA.Mods.RA
|
||||
collector.Owner.PlayerActor.Trait<PlayerResources>().GiveCash(amount);
|
||||
|
||||
if ((info as GiveCashCrateActionInfo).UseCashTick)
|
||||
w.Add(new CashTick(amount, 20, 1, collector.CenterLocation, collector.Owner.Color.RGB));
|
||||
w.Add(new CashTick(collector.CenterPosition, collector.Owner.Color.RGB, amount));
|
||||
});
|
||||
|
||||
base.Activate(collector);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenRA.Effects;
|
||||
@@ -18,41 +19,32 @@ namespace OpenRA.Mods.RA.Effects
|
||||
{
|
||||
class CashTick : IEffect
|
||||
{
|
||||
readonly string s;
|
||||
int remaining;
|
||||
readonly int velocity;
|
||||
PPos pos;
|
||||
readonly float2 offset;
|
||||
readonly Color color;
|
||||
readonly SpriteFont font;
|
||||
readonly string text;
|
||||
Color color;
|
||||
int remaining = 30;
|
||||
WPos pos;
|
||||
|
||||
static string FormatCashAmount(int x) { return "{0}${1}".F(x < 0 ? "-" : "+", x); }
|
||||
|
||||
public CashTick(int value, int lifetime, int velocity, PPos pos, Color color)
|
||||
: this(FormatCashAmount(value), lifetime, velocity, pos, color) { }
|
||||
|
||||
public CashTick(string value, int lifetime, int velocity, PPos pos, Color color)
|
||||
public CashTick(WPos pos, Color color, int value)
|
||||
{
|
||||
this.color = color;
|
||||
this.velocity = velocity;
|
||||
this.font = Game.Renderer.Fonts["TinyBold"];
|
||||
this.pos = pos;
|
||||
s = value;
|
||||
font = Game.Renderer.Fonts["TinyBold"];
|
||||
offset = 0.5f*font.Measure(s).ToFloat2();
|
||||
remaining = lifetime;
|
||||
this.color = color;
|
||||
this.text = "{0}${1}".F(value < 0 ? "-" : "+", Math.Abs(value));
|
||||
}
|
||||
|
||||
static readonly WVec velocity = new WVec(0,0,86);
|
||||
public void Tick(World world)
|
||||
{
|
||||
if (--remaining <= 0)
|
||||
world.AddFrameEndTask(w => w.Remove(this));
|
||||
pos -= new PVecInt(0, velocity);
|
||||
|
||||
pos += velocity;
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr)
|
||||
{
|
||||
font.DrawTextWithContrast(s, Game.viewport.Zoom*(pos.ToFloat2() - Game.viewport.Location) - offset, color, Color.Black,1);
|
||||
yield break;
|
||||
yield return new TextRenderable(font, pos, 0, color, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,22 +78,21 @@ namespace OpenRA.Mods.RA.Effects
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr)
|
||||
{
|
||||
if (ticks < info.BeamDuration)
|
||||
{
|
||||
var src = new PPos(args.src.X, args.src.Y).ToWPos(args.srcAltitude);
|
||||
var dest = new PPos(args.dest.X, args.dest.Y).ToWPos(args.destAltitude);
|
||||
var rc = Color.FromArgb((info.BeamDuration - ticks)*255/info.BeamDuration, color);
|
||||
|
||||
yield return new BeamRenderable(src, 0, dest - src, info.BeamWidth, rc);
|
||||
}
|
||||
|
||||
if (hitanim != null)
|
||||
yield return new SpriteRenderable(hitanim.Image, args.dest.ToFloat2(),
|
||||
wr.Palette("effect"), (int)args.dest.Y);
|
||||
|
||||
if (ticks >= info.BeamDuration)
|
||||
yield break;
|
||||
|
||||
var rc = Color.FromArgb((info.BeamDuration - ticks)*255/info.BeamDuration, color);
|
||||
|
||||
var src = new PPos(args.src.X, args.src.Y - args.srcAltitude);
|
||||
var dest = new PPos(args.dest.X, args.dest.Y - args.destAltitude);
|
||||
var wlr = Game.Renderer.WorldLineRenderer;
|
||||
wlr.LineWidth = info.BeamWidth;
|
||||
wlr.DrawLine(src.ToFloat2(), dest.ToFloat2(), rc, rc);
|
||||
wlr.Flush();
|
||||
wlr.LineWidth = 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace OpenRA.Mods.RA
|
||||
var bounty = cost * GetMultiplier(self) * info.Percentage / 10000;
|
||||
|
||||
if (bounty > 0 && e.Attacker.Owner.IsAlliedWith(self.World.RenderPlayer))
|
||||
e.Attacker.World.AddFrameEndTask(w => w.Add(new CashTick(bounty, 20, 1, self.CenterLocation, e.Attacker.Owner.Color.RGB)));
|
||||
e.Attacker.World.AddFrameEndTask(w => w.Add(new CashTick(self.CenterPosition, e.Attacker.Owner.Color.RGB, bounty)));
|
||||
|
||||
e.Attacker.Owner.PlayerActor.Trait<PlayerResources>().GiveCash(bounty);
|
||||
}
|
||||
|
||||
@@ -44,8 +44,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
Sound.PlayToPlayer(self.Owner, info.SoundToVictim);
|
||||
|
||||
self.World.AddFrameEndTask(w => w.Add(new CashTick(toGive, 30, 2, self.CenterLocation,
|
||||
infiltrator.Owner.Color.RGB)));
|
||||
self.World.AddFrameEndTask(w => w.Add(new CashTick(self.CenterPosition, infiltrator.Owner.Color.RGB, toGive)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +108,9 @@ namespace OpenRA.Mods.RA.Orders
|
||||
cells.Add(t, isCloseEnough && world.IsCellBuildable(t, BuildingInfo) && res.GetResource(t) == null);
|
||||
}
|
||||
|
||||
var pal = wr.Palette("terrain");
|
||||
foreach (var c in cells)
|
||||
(c.Value ? buildOk : buildBlocked).DrawAt(wr, c.Key.ToPPos().ToFloat2(), "terrain");
|
||||
(c.Value ? buildOk : buildBlocked).DrawAt(c.Key.ToPPos().ToFloat2(), pal);
|
||||
}
|
||||
|
||||
public string GetCursor(World world, CPos xy, MouseInput mi) { return "default"; }
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
var temp = currentDisplayValue;
|
||||
if (self.Owner.IsAlliedWith(self.World.RenderPlayer))
|
||||
self.World.AddFrameEndTask(w => w.Add(new CashTick(temp, Info.TickLifetime, Info.TickVelocity, self.CenterLocation, self.Owner.Color.RGB)));
|
||||
self.World.AddFrameEndTask(w => w.Add(new CashTick(self.CenterPosition, self.Owner.Color.RGB, temp)));
|
||||
currentDisplayTick = Info.TickRate;
|
||||
currentDisplayValue = 0;
|
||||
}
|
||||
|
||||
@@ -32,11 +32,10 @@ namespace OpenRA.Mods.RA.Render
|
||||
[Desc("Change the image size.")]
|
||||
public readonly float Scale = 10;
|
||||
|
||||
public readonly WAngle LightPitch = new WAngle(170); // 60 degrees
|
||||
public readonly WAngle LightYaw = new WAngle(739); // 260 degrees
|
||||
public readonly WAngle LightPitch = WAngle.FromDegrees(50);
|
||||
public readonly WAngle LightYaw = WAngle.FromDegrees(240);
|
||||
public readonly float[] LightAmbientColor = new float[] {0.6f, 0.6f, 0.6f};
|
||||
public readonly float[] LightDiffuseColor = new float[] {0.4f, 0.4f, 0.4f};
|
||||
|
||||
public virtual object Create(ActorInitializer init) { return new RenderVoxels(init.self, this); }
|
||||
}
|
||||
|
||||
@@ -55,7 +54,7 @@ namespace OpenRA.Mods.RA.Render
|
||||
this.info = info;
|
||||
body = self.Trait<IBodyOrientation>();
|
||||
camera = new WRot(WAngle.Zero, body.CameraPitch - new WAngle(256), new WAngle(256));
|
||||
lightSource = new WRot(WAngle.Zero, info.LightPitch, info.LightYaw - new WAngle(256));
|
||||
lightSource = new WRot(WAngle.Zero,new WAngle(256) - info.LightPitch, info.LightYaw);
|
||||
}
|
||||
|
||||
bool initializePalettes = true;
|
||||
|
||||
@@ -151,8 +151,9 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
var xy = Game.viewport.ViewToWorld(Viewport.LastMousePos);
|
||||
var tiles = world.FindTilesInCircle(xy, range);
|
||||
var pal = wr.Palette("terrain");
|
||||
foreach (var t in tiles)
|
||||
tile.DrawAt( wr, t.ToPPos().ToFloat2(), "terrain" );
|
||||
tile.DrawAt(t.ToPPos().ToFloat2(), pal);
|
||||
}
|
||||
|
||||
public string GetCursor(World world, CPos xy, MouseInput mi)
|
||||
@@ -230,14 +231,15 @@ namespace OpenRA.Mods.RA
|
||||
public void RenderBeforeWorld(WorldRenderer wr, World world)
|
||||
{
|
||||
var xy = Game.viewport.ViewToWorld(Viewport.LastMousePos);
|
||||
var pal = wr.Palette("terrain");
|
||||
|
||||
// Source tiles
|
||||
foreach (var t in world.FindTilesInCircle(sourceLocation, range))
|
||||
sourceTile.DrawAt( wr, t.ToPPos().ToFloat2(), "terrain" );
|
||||
sourceTile.DrawAt(t.ToPPos().ToFloat2(), pal);
|
||||
|
||||
// Destination tiles
|
||||
foreach (var t in world.FindTilesInCircle(xy, range))
|
||||
sourceTile.DrawAt( wr, t.ToPPos().ToFloat2(), "terrain" );
|
||||
sourceTile.DrawAt(t.ToPPos().ToFloat2(), pal);
|
||||
|
||||
// Unit previews
|
||||
foreach (var unit in power.UnitsInRange(sourceLocation))
|
||||
@@ -257,7 +259,7 @@ namespace OpenRA.Mods.RA
|
||||
var canEnter = ((manager.self.Owner.Shroud.IsExplored(targetCell) || manager.self.Owner.HasFogVisibility()) &&
|
||||
unit.Trait<Chronoshiftable>().CanChronoshiftTo(unit,targetCell));
|
||||
var tile = canEnter ? validTile : invalidTile;
|
||||
tile.DrawAt(wr, targetCell.ToPPos().ToFloat2(), "terrain");
|
||||
tile.DrawAt(targetCell.ToPPos().ToFloat2(), pal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,8 +98,9 @@ namespace OpenRA.Mods.RA
|
||||
public void RenderBeforeWorld(WorldRenderer wr, World world)
|
||||
{
|
||||
var xy = Game.viewport.ViewToWorld(Viewport.LastMousePos);
|
||||
var pal = wr.Palette("terrain");
|
||||
foreach (var t in world.FindTilesInCircle(xy, range))
|
||||
tile.DrawAt( wr, t.ToPPos().ToFloat2(), "terrain" );
|
||||
tile.DrawAt(t.ToPPos().ToFloat2(), pal);
|
||||
}
|
||||
|
||||
public string GetCursor(World world, CPos xy, MouseInput mi)
|
||||
|
||||
@@ -13,6 +13,7 @@ using System.Drawing;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using OpenRA;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Widgets;
|
||||
using XRandom = OpenRA.Thirdparty.Random;
|
||||
@@ -68,6 +69,13 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
showMuzzlesCheckbox.OnClick = () => devTrait.ShowMuzzles ^= true;
|
||||
}
|
||||
|
||||
var showGeometryCheckbox = widget.GetOrNull<CheckboxWidget>("SHOW_GEOMETRY");
|
||||
if (showGeometryCheckbox != null)
|
||||
{
|
||||
showGeometryCheckbox.IsChecked = () => devTrait.ShowDebugGeometry;
|
||||
showGeometryCheckbox.OnClick = () => devTrait.ShowDebugGeometry ^= true;
|
||||
}
|
||||
|
||||
var allTechCheckbox = widget.GetOrNull<CheckboxWidget>("ENABLE_TECH");
|
||||
if (allTechCheckbox != null)
|
||||
{
|
||||
|
||||
@@ -78,6 +78,8 @@ namespace OpenRA.Mods.RA
|
||||
public void Render( WorldRenderer wr )
|
||||
{
|
||||
var cliprect = Game.viewport.WorldBounds(world);
|
||||
var pal = wr.Palette("terrain");
|
||||
|
||||
foreach (var kv in tiles)
|
||||
{
|
||||
if (!cliprect.Contains(kv.Key.X,kv.Key.Y))
|
||||
@@ -86,7 +88,7 @@ namespace OpenRA.Mods.RA
|
||||
if (world.ShroudObscures(kv.Key))
|
||||
continue;
|
||||
|
||||
smudgeSprites[kv.Value.type- 1][kv.Value.index].DrawAt(wr, kv.Key.ToPPos().ToFloat2(), "terrain");
|
||||
smudgeSprites[kv.Value.type- 1][kv.Value.index].DrawAt(kv.Key.ToPPos().ToFloat2(), pal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,8 @@ namespace OpenRA.Renderer.Cg
|
||||
{
|
||||
"GL_ARB_vertex_program",
|
||||
"GL_ARB_fragment_program",
|
||||
"GL_ARB_vertex_buffer_object"
|
||||
"GL_ARB_vertex_buffer_object",
|
||||
"GL_EXT_framebuffer_object"
|
||||
};
|
||||
|
||||
internal IntPtr cgContext;
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace OpenRA.Renderer.Cg
|
||||
var texture = (Texture)t;
|
||||
var param = Tao.Cg.Cg.cgGetNamedEffectParameter(effect, name);
|
||||
if (param != IntPtr.Zero && texture != null)
|
||||
Tao.Cg.CgGl.cgGLSetupSampler(param, texture.texture);
|
||||
Tao.Cg.CgGl.cgGLSetupSampler(param, texture.ID);
|
||||
}
|
||||
|
||||
public void SetVec(string name, float x)
|
||||
|
||||
@@ -34,7 +34,8 @@ namespace OpenRA.Renderer.Glsl
|
||||
{
|
||||
"GL_ARB_vertex_shader",
|
||||
"GL_ARB_fragment_shader",
|
||||
"GL_ARB_vertex_buffer_object"
|
||||
"GL_ARB_vertex_buffer_object",
|
||||
"GL_EXT_framebuffer_object"
|
||||
};
|
||||
|
||||
public GraphicsDevice(Size size, WindowMode window)
|
||||
|
||||
@@ -104,7 +104,6 @@ namespace OpenRA.Renderer.Glsl
|
||||
++nextTexUnit;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Render(Action a)
|
||||
@@ -115,21 +114,13 @@ namespace OpenRA.Renderer.Glsl
|
||||
|
||||
foreach (var kv in textures)
|
||||
{
|
||||
Gl.glActiveTextureARB( Gl.GL_TEXTURE0_ARB + kv.Key );
|
||||
Gl.glBindTexture( Gl.GL_TEXTURE_2D, ((Texture)kv.Value).texture );
|
||||
Gl.glActiveTextureARB(Gl.GL_TEXTURE0_ARB + kv.Key);
|
||||
Gl.glBindTexture(Gl.GL_TEXTURE_2D, ((Texture)kv.Value).ID);
|
||||
}
|
||||
|
||||
/* configure blend state */
|
||||
ErrorHandler.CheckGlError();
|
||||
// TODO: Only enable alpha blending if we need it
|
||||
Gl.glEnable(Gl.GL_BLEND);
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);
|
||||
ErrorHandler.CheckGlError();
|
||||
a();
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glDisable(Gl.GL_BLEND);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void SetTexture(string name, ITexture t)
|
||||
|
||||
@@ -41,8 +41,8 @@ namespace OpenRA.Renderer.Null
|
||||
public void EnableDepthBuffer() { }
|
||||
public void DisableDepthBuffer() { }
|
||||
|
||||
public void EnableStencilBuffer() { }
|
||||
public void DisableStencilBuffer() { }
|
||||
public void EnableAlphaBlending() { }
|
||||
public void DisableAlphaBlending() { }
|
||||
|
||||
public void Clear() { }
|
||||
public void Present() { }
|
||||
@@ -59,6 +59,7 @@ namespace OpenRA.Renderer.Null
|
||||
public IVertexBuffer<Vertex> CreateVertexBuffer(int size) { return new NullVertexBuffer<Vertex>(); }
|
||||
public ITexture CreateTexture() { return new NullTexture(); }
|
||||
public ITexture CreateTexture(Bitmap bitmap) { return new NullTexture(); }
|
||||
public IFrameBuffer CreateFrameBuffer(Size s) { return new NullFrameBuffer(); }
|
||||
public IShader CreateShader(string name) { return new NullShader(); }
|
||||
}
|
||||
|
||||
@@ -78,6 +79,15 @@ namespace OpenRA.Renderer.Null
|
||||
public void SetData(Bitmap bitmap) { }
|
||||
public void SetData(uint[,] colors) { }
|
||||
public void SetData(byte[] colors, int width, int height) { }
|
||||
public byte[] GetData() { return new byte[0]; }
|
||||
public Size Size { get { return new Size(0, 0); } }
|
||||
}
|
||||
|
||||
public class NullFrameBuffer : IFrameBuffer
|
||||
{
|
||||
public void Bind() { }
|
||||
public void Unbind() { }
|
||||
public ITexture Texture { get { return new NullTexture(); } }
|
||||
}
|
||||
|
||||
class NullVertexBuffer<T> : IVertexBuffer<T>
|
||||
|
||||
123
OpenRA.Renderer.SdlCommon/FrameBuffer.cs
Normal file
123
OpenRA.Renderer.SdlCommon/FrameBuffer.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileFormats.Graphics;
|
||||
using Tao.OpenGl;
|
||||
|
||||
namespace OpenRA.Renderer.SdlCommon
|
||||
{
|
||||
public class FrameBuffer : IFrameBuffer
|
||||
{
|
||||
Texture texture;
|
||||
Size size;
|
||||
int framebuffer, depth;
|
||||
|
||||
public FrameBuffer(Size size)
|
||||
{
|
||||
this.size = size;
|
||||
if (!Exts.IsPowerOf2(size.Width) || !Exts.IsPowerOf2(size.Height))
|
||||
throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(size.Width, size.Height));
|
||||
|
||||
Gl.glGenFramebuffersEXT(1, out framebuffer);
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glBindFramebufferEXT(Gl.GL_FRAMEBUFFER_EXT, framebuffer);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
// Color
|
||||
texture = new Texture();
|
||||
texture.SetEmpty(size.Width, size.Height);
|
||||
Gl.glFramebufferTexture2DEXT(Gl.GL_FRAMEBUFFER_EXT, Gl.GL_COLOR_ATTACHMENT0_EXT, Gl.GL_TEXTURE_2D, texture.ID, 0);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
// Depth
|
||||
Gl.glGenRenderbuffersEXT(1, out depth);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
Gl.glBindRenderbufferEXT(Gl.GL_RENDERBUFFER_EXT, depth);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
Gl.glRenderbufferStorageEXT(Gl.GL_RENDERBUFFER_EXT, Gl.GL_DEPTH_COMPONENT16, size.Width, size.Height);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
Gl.glFramebufferRenderbufferEXT(Gl.GL_FRAMEBUFFER_EXT, Gl.GL_DEPTH_ATTACHMENT_EXT, Gl.GL_RENDERBUFFER_EXT, depth);
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
// Test for completeness
|
||||
var status = Gl.glCheckFramebufferStatusEXT(Gl.GL_FRAMEBUFFER_EXT);
|
||||
if (status != Gl.GL_FRAMEBUFFER_COMPLETE_EXT)
|
||||
throw new InvalidOperationException("Error creating framebuffer");
|
||||
|
||||
// Restore default buffer
|
||||
Gl.glBindFramebufferEXT(Gl.GL_FRAMEBUFFER_EXT, 0);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
void FinalizeInner()
|
||||
{
|
||||
Gl.glDeleteFramebuffersEXT(1, ref framebuffer);
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glDeleteRenderbuffersEXT(1, ref depth);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
~FrameBuffer() { Game.RunAfterTick(FinalizeInner); }
|
||||
|
||||
static int[] ViewportRectangle()
|
||||
{
|
||||
int[] v = new int[4];
|
||||
unsafe
|
||||
{
|
||||
fixed (int *ptr = &v[0])
|
||||
{
|
||||
IntPtr intPtr = new IntPtr((void*)ptr);
|
||||
Gl.glGetIntegerv(Gl.GL_VIEWPORT, intPtr);
|
||||
}
|
||||
}
|
||||
ErrorHandler.CheckGlError();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
int[] cv = new int[4];
|
||||
public void Bind()
|
||||
{
|
||||
// Cache viewport rect to restore when unbinding
|
||||
cv = ViewportRectangle();
|
||||
|
||||
Gl.glFlush();
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glBindFramebufferEXT(Gl.GL_FRAMEBUFFER_EXT, framebuffer);
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glViewport(0, 0, size.Width, size.Height);
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glClearColor(0, 0, 0, 0);
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void Unbind()
|
||||
{
|
||||
Gl.glFlush();
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glBindFramebufferEXT(Gl.GL_FRAMEBUFFER_EXT, 0);
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glViewport(cv[0], cv[1], cv[2], cv[3]);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public ITexture Texture { get { return texture; } }
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,7 @@
|
||||
<Compile Include="SdlInput.cs" />
|
||||
<Compile Include="Texture.cs" />
|
||||
<Compile Include="VertexBuffer.cs" />
|
||||
<Compile Include="FrameBuffer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||
|
||||
@@ -134,24 +134,6 @@ namespace OpenRA.Renderer.SdlCommon
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void EnableStencilBuffer()
|
||||
{
|
||||
Gl.glClear(Gl.GL_STENCIL_BUFFER_BIT);
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glEnable(Gl.GL_STENCIL_TEST);
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glStencilFunc(Gl.GL_NOTEQUAL, 1, 1);
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glStencilOp(Gl.GL_KEEP, Gl.GL_KEEP, Gl.GL_INCR);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void DisableStencilBuffer()
|
||||
{
|
||||
Gl.glDisable(Gl.GL_STENCIL_TEST);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void EnableDepthBuffer()
|
||||
{
|
||||
Gl.glClear(Gl.GL_DEPTH_BUFFER_BIT);
|
||||
@@ -166,6 +148,20 @@ namespace OpenRA.Renderer.SdlCommon
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void EnableAlphaBlending()
|
||||
{
|
||||
Gl.glEnable(Gl.GL_BLEND);
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void DisableAlphaBlending()
|
||||
{
|
||||
Gl.glDisable(Gl.GL_BLEND);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
|
||||
public void EnableScissor(int left, int top, int width, int height)
|
||||
{
|
||||
if (width < 0) width = 0;
|
||||
@@ -194,6 +190,7 @@ namespace OpenRA.Renderer.SdlCommon
|
||||
public IVertexBuffer<Vertex> CreateVertexBuffer(int size) { return new VertexBuffer<Vertex>(size); }
|
||||
public ITexture CreateTexture() { return new Texture(); }
|
||||
public ITexture CreateTexture(Bitmap bitmap) { return new Texture(bitmap); }
|
||||
public IFrameBuffer CreateFrameBuffer(Size s) { return new FrameBuffer(s); }
|
||||
public abstract IShader CreateShader(string name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,11 @@ namespace OpenRA.Renderer.SdlCommon
|
||||
{
|
||||
public class Texture : ITexture
|
||||
{
|
||||
public int texture; /* temp: can be internal again once shaders are in shared code */
|
||||
int texture;
|
||||
public int ID { get { return texture; } }
|
||||
|
||||
Size size;
|
||||
public Size Size { get { return size; } }
|
||||
|
||||
public Texture()
|
||||
{
|
||||
@@ -56,9 +60,10 @@ namespace OpenRA.Renderer.SdlCommon
|
||||
|
||||
public void SetData(byte[] colors, int width, int height)
|
||||
{
|
||||
if (!IsPowerOf2(width) || !IsPowerOf2(height))
|
||||
if (!Exts.IsPowerOf2(width) || !Exts.IsPowerOf2(height))
|
||||
throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(width, height));
|
||||
|
||||
size = new Size(width, height);
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = &colors[0])
|
||||
@@ -78,9 +83,10 @@ namespace OpenRA.Renderer.SdlCommon
|
||||
int width = colors.GetUpperBound(1) + 1;
|
||||
int height = colors.GetUpperBound(0) + 1;
|
||||
|
||||
if (!IsPowerOf2(width) || !IsPowerOf2(height))
|
||||
if (!Exts.IsPowerOf2(width) || !Exts.IsPowerOf2(height))
|
||||
throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(width,height));
|
||||
|
||||
size = new Size(width, height);
|
||||
unsafe
|
||||
{
|
||||
fixed (uint* ptr = &colors[0,0])
|
||||
@@ -96,12 +102,10 @@ namespace OpenRA.Renderer.SdlCommon
|
||||
|
||||
public void SetData(Bitmap bitmap)
|
||||
{
|
||||
if (!IsPowerOf2(bitmap.Width) || !IsPowerOf2(bitmap.Height))
|
||||
{
|
||||
//throw new InvalidOperationException( "non-power-of-2-texture" );
|
||||
if (!Exts.IsPowerOf2(bitmap.Width) || !Exts.IsPowerOf2(bitmap.Height))
|
||||
bitmap = new Bitmap(bitmap, bitmap.Size.NextPowerOf2());
|
||||
}
|
||||
|
||||
size = new Size(bitmap.Width, bitmap.Height);
|
||||
var bits = bitmap.LockBits(bitmap.Bounds(),
|
||||
ImageLockMode.ReadOnly,
|
||||
PixelFormat.Format32bppArgb);
|
||||
@@ -113,9 +117,33 @@ namespace OpenRA.Renderer.SdlCommon
|
||||
bitmap.UnlockBits(bits);
|
||||
}
|
||||
|
||||
bool IsPowerOf2(int v)
|
||||
public byte[] GetData()
|
||||
{
|
||||
return (v & (v - 1)) == 0;
|
||||
var data = new byte[4*size.Width * size.Height];
|
||||
|
||||
ErrorHandler.CheckGlError();
|
||||
Gl.glBindTexture(Gl.GL_TEXTURE_2D, texture);
|
||||
unsafe
|
||||
{
|
||||
fixed (byte *ptr = &data[0])
|
||||
{
|
||||
IntPtr intPtr = new IntPtr((void*)ptr);
|
||||
Gl.glGetTexImage(Gl.GL_TEXTURE_2D, 0, Gl.GL_BGRA, Gl.GL_UNSIGNED_BYTE, intPtr);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public void SetEmpty(int width, int height)
|
||||
{
|
||||
if (!Exts.IsPowerOf2(width) || !Exts.IsPowerOf2(height))
|
||||
throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(width, height));
|
||||
|
||||
size = new Size(width, height);
|
||||
PrepareTexture();
|
||||
Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA8, width, height,
|
||||
0, Gl.GL_BGRA, Gl.GL_UNSIGNED_BYTE, null);
|
||||
ErrorHandler.CheckGlError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ float4 Simple_fp(VertexOut f) : COLOR0 {
|
||||
|
||||
technique high_quality {
|
||||
pass p0 {
|
||||
BlendEnable = true;
|
||||
DepthTestEnable = false;
|
||||
//CullMode = None;
|
||||
//FillMode = Wireframe;
|
||||
@@ -44,7 +43,6 @@ technique high_quality {
|
||||
|
||||
technique high_quality_cg21 {
|
||||
pass p0 {
|
||||
BlendEnable = true;
|
||||
DepthTestEnable = false;
|
||||
//CullMode = None;
|
||||
//FillMode = Wireframe;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Author: C. Forbes
|
||||
//--------------------------------------------------------
|
||||
|
||||
float2 Scroll;
|
||||
float2 r1, r2; // matrix elements
|
||||
|
||||
sampler2D DiffuseTexture = sampler_state {
|
||||
@@ -25,7 +26,7 @@ struct FragmentIn {
|
||||
|
||||
VertexOut Simple_vp(VertexIn v) {
|
||||
VertexOut o;
|
||||
float2 p = v.Position.xy * r1 + r2;
|
||||
float2 p = (v.Position.xy - Scroll.xy) * r1 + r2;
|
||||
o.Position = float4(p.x,p.y,0,1);
|
||||
o.Tex0 = v.Tex0.xy;
|
||||
return o;
|
||||
@@ -38,7 +39,6 @@ float4 Simple_fp(FragmentIn f) : COLOR0 {
|
||||
|
||||
technique high_quality {
|
||||
pass p0 {
|
||||
BlendEnable = true;
|
||||
DepthTestEnable = false;
|
||||
CullFaceEnable = false;
|
||||
VertexProgram = compile latest Simple_vp();
|
||||
@@ -51,7 +51,6 @@ technique high_quality {
|
||||
|
||||
technique high_quality_cg21 {
|
||||
pass p0 {
|
||||
BlendEnable = true;
|
||||
DepthTestEnable = false;
|
||||
CullFaceEnable = false;
|
||||
VertexProgram = compile arbvp1 Simple_vp();
|
||||
|
||||
@@ -55,7 +55,6 @@ float4 Palette_fp(VertexOut f) : COLOR0 {
|
||||
|
||||
technique low_quality {
|
||||
pass p0 {
|
||||
BlendEnable = true;
|
||||
DepthTestEnable = false;
|
||||
CullFaceEnable = false;
|
||||
VertexProgram = compile latest Simple_vp();
|
||||
@@ -68,7 +67,6 @@ technique low_quality {
|
||||
|
||||
technique low_quality_cg21 {
|
||||
pass p0 {
|
||||
BlendEnable = true;
|
||||
DepthTestEnable = false;
|
||||
CullFaceEnable = false;
|
||||
VertexProgram = compile arbvp1 Simple_vp();
|
||||
|
||||
@@ -61,7 +61,6 @@ float4 Simple_fp(VertexOut f) : COLOR0 {
|
||||
|
||||
technique high_quality {
|
||||
pass p0 {
|
||||
BlendEnable = true;
|
||||
DepthTestEnable = true;
|
||||
CullFaceEnable = false;
|
||||
VertexProgram = compile latest Simple_vp();
|
||||
@@ -74,7 +73,6 @@ technique high_quality {
|
||||
|
||||
technique high_quality_cg21 {
|
||||
pass p0 {
|
||||
BlendEnable = true;
|
||||
DepthTestEnable = true;
|
||||
CullFaceEnable = false;
|
||||
VertexProgram = compile arbvp1 Simple_vp();
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
mat4x4 View;
|
||||
mat4x4 TransformMatrix;
|
||||
float4 LightDirection;
|
||||
float GroundZ;
|
||||
float3 GroundNormal;
|
||||
|
||||
float2 PaletteRows;
|
||||
float3 AmbientLight, DiffuseLight;
|
||||
|
||||
sampler2D DiffuseTexture = sampler_state {
|
||||
MinFilter = Nearest;
|
||||
MagFilter = Nearest;
|
||||
WrapS = Repeat;
|
||||
WrapT = Repeat;
|
||||
};
|
||||
|
||||
sampler2D Palette = sampler_state {
|
||||
MinFilter = Nearest;
|
||||
MagFilter = Nearest;
|
||||
WrapS = Repeat;
|
||||
WrapT = Repeat;
|
||||
};
|
||||
|
||||
struct VertexIn {
|
||||
float4 Position: POSITION;
|
||||
float4 Tex0: TEXCOORD0;
|
||||
};
|
||||
|
||||
struct VertexOut {
|
||||
float4 Position: POSITION;
|
||||
float2 Tex0: TEXCOORD0;
|
||||
float4 ColorChannel: TEXCOORD1;
|
||||
float4 NormalsChannel: TEXCOORD2;
|
||||
};
|
||||
|
||||
float4 DecodeChannelMask(float x)
|
||||
{
|
||||
if (x > 0)
|
||||
return (x > 0.5f) ? float4(1,0,0,0) : float4(0,1,0,0);
|
||||
else
|
||||
return (x <-0.5f) ? float4(0,0,0,1) : float4(0,0,1,0);
|
||||
}
|
||||
|
||||
VertexOut Simple_vp(VertexIn v) {
|
||||
// Distance between vertex and ground
|
||||
float d = dot(v.Position.xyz - float3(0.0,0.0,GroundZ), GroundNormal) / dot(LightDirection.xyz, GroundNormal);
|
||||
float3 shadow = v.Position.xyz - d*LightDirection.xyz;
|
||||
|
||||
VertexOut o;
|
||||
o.Position = mul(mul(vec4(shadow, 1), TransformMatrix), View);
|
||||
o.Tex0 = v.Tex0.xy;
|
||||
o.ColorChannel = DecodeChannelMask(v.Tex0.z);
|
||||
o.NormalsChannel = DecodeChannelMask(v.Tex0.w);
|
||||
return o;
|
||||
}
|
||||
|
||||
float4 Simple_fp(VertexOut f) : COLOR0 {
|
||||
float4 x = tex2D(DiffuseTexture, f.Tex0.xy);
|
||||
vec4 color = tex2D(Palette, float2(dot(x, f.ColorChannel), PaletteRows.x));
|
||||
if (color.a < 0.01)
|
||||
discard;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
technique high_quality {
|
||||
pass p0 {
|
||||
BlendEnable = true;
|
||||
DepthTestEnable = true;
|
||||
CullFaceEnable = false;
|
||||
VertexProgram = compile latest Simple_vp();
|
||||
FragmentProgram = compile latest Simple_fp();
|
||||
|
||||
BlendEquation = FuncAdd;
|
||||
BlendFunc = int2(SrcAlpha, OneMinusSrcAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
technique high_quality_cg21 {
|
||||
pass p0 {
|
||||
BlendEnable = true;
|
||||
DepthTestEnable = true;
|
||||
CullFaceEnable = false;
|
||||
VertexProgram = compile arbvp1 Simple_vp();
|
||||
FragmentProgram = compile arbfp1 Simple_fp();
|
||||
|
||||
BlendEquation = FuncAdd;
|
||||
BlendFunc = int2(SrcAlpha, OneMinusSrcAlpha);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
uniform vec2 r1;
|
||||
uniform vec2 r2; // matrix elements
|
||||
uniform vec2 Scroll;
|
||||
uniform vec2 r1, r2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 p = gl_Vertex.xy*r1 + r2;
|
||||
vec2 p = (gl_Vertex.xy - Scroll.xy)*r1 + r2;
|
||||
gl_Position = vec4(p.x,p.y,0,1);
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
uniform sampler2D Palette, DiffuseTexture;
|
||||
uniform vec2 PaletteRows;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 x = texture2D(DiffuseTexture, gl_TexCoord[0].st);
|
||||
vec4 color = texture2D(Palette, vec2(dot(x, gl_TexCoord[1]), PaletteRows.x));
|
||||
if (color.a < 0.01)
|
||||
discard;
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
uniform mat4 View;
|
||||
uniform mat4 TransformMatrix;
|
||||
uniform vec4 LightDirection;
|
||||
uniform float GroundZ;
|
||||
uniform vec3 GroundNormal;
|
||||
|
||||
vec4 DecodeChannelMask(float x)
|
||||
{
|
||||
if (x > 0.0)
|
||||
return (x > 0.5) ? vec4(1,0,0,0) : vec4(0,1,0,0);
|
||||
else
|
||||
return (x < -0.5) ? vec4(0,0,0,1) : vec4(0,0,1,0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
// Distance between vertex and ground
|
||||
float d = dot(gl_Vertex.xyz - vec3(0.0,0.0,GroundZ), GroundNormal) / dot(LightDirection.xyz, GroundNormal);
|
||||
|
||||
// Project onto ground plane
|
||||
vec3 shadow = gl_Vertex.xyz - d*LightDirection.xyz;
|
||||
gl_Position = View*TransformMatrix*vec4(shadow, 1);
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
gl_TexCoord[1] = DecodeChannelMask(gl_MultiTexCoord0.z);
|
||||
gl_TexCoord[2] = DecodeChannelMask(gl_MultiTexCoord0.w);
|
||||
}
|
||||
@@ -98,4 +98,10 @@ Container@CHEATS_PANEL:
|
||||
Y:235
|
||||
Height:20
|
||||
Width:200
|
||||
Text:Show Muzzle Positions
|
||||
Text:Show Muzzle Positions
|
||||
Checkbox@SHOW_GEOMETRY:
|
||||
X:290
|
||||
Y:265
|
||||
Height:20
|
||||
Width:200
|
||||
Text:Show Render Geometry
|
||||
@@ -3,7 +3,7 @@ Background@CHEATS_PANEL:
|
||||
X:(WINDOW_RIGHT - WIDTH)/2
|
||||
Y:(WINDOW_BOTTOM - HEIGHT)/2
|
||||
Width:350
|
||||
Height:475
|
||||
Height:505
|
||||
Visible:false
|
||||
Children:
|
||||
Label@LABEL_TITLE:
|
||||
@@ -87,21 +87,27 @@ Background@CHEATS_PANEL:
|
||||
Height:20
|
||||
Width:200
|
||||
Text:Show Muzzle Positions
|
||||
Checkbox@SHOW_GEOMETRY:
|
||||
X:30
|
||||
Y:380
|
||||
Height:20
|
||||
Width:200
|
||||
Text:Show Render Geometry
|
||||
Checkbox@DESYNC_ARMED:
|
||||
X:30
|
||||
Y:382
|
||||
Y:412
|
||||
Width:20
|
||||
Height:20
|
||||
Button@DESYNC:
|
||||
X:60
|
||||
Y:380
|
||||
Y:410
|
||||
Width:150
|
||||
Height:20
|
||||
Text: Force Desync
|
||||
Height:25
|
||||
Button@CLOSE:
|
||||
X:30
|
||||
Y:420
|
||||
Y:450
|
||||
Width:PARENT_RIGHT - 60
|
||||
Height:25
|
||||
Text:Close
|
||||
|
||||
Reference in New Issue
Block a user