Merge pull request #3407 from pchote/voxel-fbo

Voxel refactoring
This commit is contained in:
Chris Forbes
2013-06-19 14:57:17 -07:00
66 changed files with 1157 additions and 553 deletions

View File

@@ -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)

View File

@@ -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))

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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;

View File

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

View File

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

View File

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

View File

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

View File

@@ -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)

View File

@@ -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];

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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">

View File

@@ -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)

View File

@@ -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;

View File

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

View File

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

View File

@@ -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)

View File

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

View File

@@ -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;

View File

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

View File

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

View File

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

View File

@@ -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);

View File

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

View File

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

View File

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

View File

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

View File

@@ -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"; }

View File

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

View File

@@ -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;

View File

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

View File

@@ -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)

View File

@@ -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)
{

View File

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

View File

@@ -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;

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

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

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

View File

@@ -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">

View File

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

View File

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

View File

@@ -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;

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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