From ef95faa9b9935307692e6dd778a7a52ccdc514e4 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 9 Jun 2013 23:31:32 +1200 Subject: [PATCH 01/23] Fix LaserZap LineRenderer flushing. --- OpenRA.Mods.RA/Effects/LaserZap.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/OpenRA.Mods.RA/Effects/LaserZap.cs b/OpenRA.Mods.RA/Effects/LaserZap.cs index c5f5ca4fb1..d98ff53a68 100755 --- a/OpenRA.Mods.RA/Effects/LaserZap.cs +++ b/OpenRA.Mods.RA/Effects/LaserZap.cs @@ -90,10 +90,22 @@ namespace OpenRA.Mods.RA.Effects 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; + + // TODO: Push this into a BeamRenderable, with support for refraction/ripples on sonic weapons + var lineWidth = wlr.LineWidth; + if (lineWidth != info.BeamWidth) + { + wlr.Flush(); + wlr.LineWidth = info.BeamWidth; + } + wlr.DrawLine(src.ToFloat2(), dest.ToFloat2(), rc, rc); - wlr.Flush(); - wlr.LineWidth = 1f; + + if (lineWidth != info.BeamWidth) + { + wlr.Flush(); + wlr.LineWidth = lineWidth; + } } } } From da8202a15e8b4005e0312eded80bd56ea69821dc Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Tue, 11 Jun 2013 18:48:11 +1200 Subject: [PATCH 02/23] Clean up LineRenderer whitespace (no code changes). --- OpenRA.Game/Graphics/LineRenderer.cs | 48 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/OpenRA.Game/Graphics/LineRenderer.cs b/OpenRA.Game/Graphics/LineRenderer.cs index 96776cc684..4f21a49f55 100644 --- a/OpenRA.Game/Graphics/LineRenderer.cs +++ b/OpenRA.Game/Graphics/LineRenderer.cs @@ -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,49 @@ namespace OpenRA.Graphics public void Flush() { - if( nv > 0 ) + if (nv > 0) { - shader.Render( () => + 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); + }); 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); } From cd268c11ee62f90e8d22dfbea95190d608c9a3c0 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 8 Jun 2013 11:46:44 +1200 Subject: [PATCH 03/23] Move IsPowerOf2 to Exts. --- OpenRA.FileFormats/Exts.cs | 5 +++++ OpenRA.Renderer.SdlCommon/Texture.cs | 14 +++----------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/OpenRA.FileFormats/Exts.cs b/OpenRA.FileFormats/Exts.cs index e58d62321c..9c071b73e0 100755 --- a/OpenRA.FileFormats/Exts.cs +++ b/OpenRA.FileFormats/Exts.cs @@ -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(this IEnumerable ts, string j) diff --git a/OpenRA.Renderer.SdlCommon/Texture.cs b/OpenRA.Renderer.SdlCommon/Texture.cs index 82a34af274..8aadc8e740 100644 --- a/OpenRA.Renderer.SdlCommon/Texture.cs +++ b/OpenRA.Renderer.SdlCommon/Texture.cs @@ -56,7 +56,7 @@ 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)); unsafe @@ -78,7 +78,7 @@ 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)); unsafe @@ -96,11 +96,8 @@ 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()); - } var bits = bitmap.LockBits(bitmap.Bounds(), ImageLockMode.ReadOnly, @@ -112,10 +109,5 @@ namespace OpenRA.Renderer.SdlCommon ErrorHandler.CheckGlError(); bitmap.UnlockBits(bits); } - - bool IsPowerOf2(int v) - { - return (v & (v - 1)) == 0; - } } } From 0e1c12131a83fa5f8cc534f29ebdbaadd4f083fa Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 7 Jun 2013 17:06:16 +1200 Subject: [PATCH 04/23] Generalize SheetBuilder overflow behavior. --- OpenRA.Game/Graphics/SheetBuilder.cs | 40 ++++++++++++++----------- OpenRA.Game/Graphics/SpriteFont.cs | 2 +- OpenRA.Game/Graphics/SpriteLoader.cs | 4 +-- OpenRA.Game/Graphics/TerrainRenderer.cs | 17 +++++++++-- OpenRA.Game/Graphics/VoxelLoader.cs | 20 +++++++++++-- OpenRA.Mods.RA/Bridge.cs | 2 +- 6 files changed, 57 insertions(+), 28 deletions(-) diff --git a/OpenRA.Game/Graphics/SheetBuilder.cs b/OpenRA.Game/Graphics/SheetBuilder.cs index 74c57f92a2..6b8a0ff930 100644 --- a/OpenRA.Game/Graphics/SheetBuilder.cs +++ b/OpenRA.Game/Graphics/SheetBuilder.cs @@ -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,38 @@ namespace OpenRA.Graphics SheetType type; int rowHeight = 0; Point p; + Func 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 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); 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 +76,7 @@ namespace OpenRA.Graphics return (TextureChannel)nextChannel; } - public Sprite Allocate(Size imageSize, bool allowSheetOverflow) + public Sprite Allocate(Size imageSize) { if (imageSize.Width + p.X > current.Size.Width) { @@ -85,10 +92,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 diff --git a/OpenRA.Game/Graphics/SpriteFont.cs b/OpenRA.Game/Graphics/SpriteFont.cs index f4284b8055..66e1c8dfb7 100644 --- a/OpenRA.Game/Graphics/SpriteFont.cs +++ b/OpenRA.Game/Graphics/SpriteFont.cs @@ -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 { diff --git a/OpenRA.Game/Graphics/SpriteLoader.cs b/OpenRA.Game/Graphics/SpriteLoader.cs index ca6e7f49b9..9ada3d035e 100644 --- a/OpenRA.Game/Graphics/SpriteLoader.cs +++ b/OpenRA.Game/Graphics/SpriteLoader.cs @@ -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(); } } diff --git a/OpenRA.Game/Graphics/TerrainRenderer.cs b/OpenRA.Game/Graphics/TerrainRenderer.cs index c8a43df742..240bceca21 100644 --- a/OpenRA.Game/Graphics/TerrainRenderer.cs +++ b/OpenRA.Game/Graphics/TerrainRenderer.cs @@ -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 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, 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]; diff --git a/OpenRA.Game/Graphics/VoxelLoader.cs b/OpenRA.Game/Graphics/VoxelLoader.cs index 89ad57590d..500b41d708 100644 --- a/OpenRA.Game/Graphics/VoxelLoader.cs +++ b/OpenRA.Game/Graphics/VoxelLoader.cs @@ -41,6 +41,20 @@ namespace OpenRA.Graphics int totalVertexCount; int cachedVertexCount; + SheetBuilder CreateSheetBuilder() + { + var allocated = false; + Func allocate = () => + { + if (allocated) + throw new SheetOverflowException(""); + allocated = true; + return SheetBuilder.AllocateSheet(); + }; + + return new SheetBuilder(SheetType.DualIndexed, allocate); + } + public VoxelLoader() { voxels = new Cache, 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,7 +81,7 @@ 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(); @@ -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(); } diff --git a/OpenRA.Mods.RA/Bridge.cs b/OpenRA.Mods.RA/Bridge.cs index c3b4425594..b889b4e8c9 100644 --- a/OpenRA.Mods.RA/Bridge.cs +++ b/OpenRA.Mods.RA/Bridge.cs @@ -101,7 +101,7 @@ namespace OpenRA.Mods.RA cachedTileset = self.World.Map.Tileset; var tileSize = new Size(Game.CellSize, Game.CellSize); sprites = new Cache, 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 From 4c8c01050617da7b3aaaa9d419b54f34d0ce9a42 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 7 Jun 2013 22:20:19 +1200 Subject: [PATCH 05/23] Expose FBOs to engine code. --- .../Graphics/IGraphicsDevice.cs | 8 ++ OpenRA.Renderer.Cg/GraphicsDevice.cs | 3 +- OpenRA.Renderer.Gl/GraphicsDevice.cs | 3 +- OpenRA.Renderer.Null/NullGraphicsDevice.cs | 8 ++ OpenRA.Renderer.SdlCommon/FrameBuffer.cs | 123 ++++++++++++++++++ .../OpenRA.Renderer.SdlCommon.csproj | 1 + OpenRA.Renderer.SdlCommon/SdlGraphics.cs | 1 + 7 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 OpenRA.Renderer.SdlCommon/FrameBuffer.cs diff --git a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs index ec82cff24e..b8cf0787e4 100755 --- a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs +++ b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs @@ -37,6 +37,7 @@ namespace OpenRA.FileFormats.Graphics IVertexBuffer CreateVertexBuffer( int length ); ITexture CreateTexture( Bitmap bitmap ); ITexture CreateTexture(); + IFrameBuffer CreateFrameBuffer(Size s); IShader CreateShader( string name ); Size WindowSize { get; } @@ -81,6 +82,13 @@ namespace OpenRA.FileFormats.Graphics void SetData(byte[] colors, int width, int height); } + public interface IFrameBuffer + { + void Bind(); + void Unbind(); + ITexture Texture { get; } + } + public enum PrimitiveType { PointList, diff --git a/OpenRA.Renderer.Cg/GraphicsDevice.cs b/OpenRA.Renderer.Cg/GraphicsDevice.cs index 0ac9a1dff2..656dca6136 100755 --- a/OpenRA.Renderer.Cg/GraphicsDevice.cs +++ b/OpenRA.Renderer.Cg/GraphicsDevice.cs @@ -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_ARB_framebuffer_object" }; internal IntPtr cgContext; diff --git a/OpenRA.Renderer.Gl/GraphicsDevice.cs b/OpenRA.Renderer.Gl/GraphicsDevice.cs index b9a091bac8..a2ffc688bc 100755 --- a/OpenRA.Renderer.Gl/GraphicsDevice.cs +++ b/OpenRA.Renderer.Gl/GraphicsDevice.cs @@ -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_ARB_framebuffer_object" }; public GraphicsDevice(Size size, WindowMode window) diff --git a/OpenRA.Renderer.Null/NullGraphicsDevice.cs b/OpenRA.Renderer.Null/NullGraphicsDevice.cs index cc8dd57233..359915d371 100644 --- a/OpenRA.Renderer.Null/NullGraphicsDevice.cs +++ b/OpenRA.Renderer.Null/NullGraphicsDevice.cs @@ -59,6 +59,7 @@ namespace OpenRA.Renderer.Null public IVertexBuffer CreateVertexBuffer(int size) { return new NullVertexBuffer(); } 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(); } } @@ -80,6 +81,13 @@ namespace OpenRA.Renderer.Null public void SetData(byte[] colors, int width, int height) { } } + public class NullFrameBuffer : IFrameBuffer + { + public void Bind() { } + public void Unbind() { } + public ITexture Texture { get { return new NullTexture(); } } + } + class NullVertexBuffer : IVertexBuffer { public void Bind() { } diff --git a/OpenRA.Renderer.SdlCommon/FrameBuffer.cs b/OpenRA.Renderer.SdlCommon/FrameBuffer.cs new file mode 100644 index 0000000000..ca96757b68 --- /dev/null +++ b/OpenRA.Renderer.SdlCommon/FrameBuffer.cs @@ -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; } } + } +} diff --git a/OpenRA.Renderer.SdlCommon/OpenRA.Renderer.SdlCommon.csproj b/OpenRA.Renderer.SdlCommon/OpenRA.Renderer.SdlCommon.csproj index 46589e297b..a489aae220 100644 --- a/OpenRA.Renderer.SdlCommon/OpenRA.Renderer.SdlCommon.csproj +++ b/OpenRA.Renderer.SdlCommon/OpenRA.Renderer.SdlCommon.csproj @@ -71,6 +71,7 @@ + diff --git a/OpenRA.Renderer.SdlCommon/SdlGraphics.cs b/OpenRA.Renderer.SdlCommon/SdlGraphics.cs index 0cf17f2d3e..0dd415474e 100644 --- a/OpenRA.Renderer.SdlCommon/SdlGraphics.cs +++ b/OpenRA.Renderer.SdlCommon/SdlGraphics.cs @@ -194,6 +194,7 @@ namespace OpenRA.Renderer.SdlCommon public IVertexBuffer CreateVertexBuffer(int size) { return new VertexBuffer(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); } } From 22e6966c8e7f92c582d9e1a65248fade3ccfc013 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 8 Jun 2013 11:39:27 +1200 Subject: [PATCH 06/23] Texture changes: * The GL texture id is now readonly. * Added Size property. * Added GetData() for reading data back from the GPU. * Added SetEmpty() for creating an empty texture of a given size. --- .../Graphics/IGraphicsDevice.cs | 2 + OpenRA.Renderer.Cg/Shader.cs | 2 +- OpenRA.Renderer.Gl/Shader.cs | 4 +- OpenRA.Renderer.Null/NullGraphicsDevice.cs | 2 + OpenRA.Renderer.SdlCommon/Texture.cs | 38 ++++++++++++++++++- 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs index b8cf0787e4..8e44f22817 100755 --- a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs +++ b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs @@ -80,6 +80,8 @@ 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 diff --git a/OpenRA.Renderer.Cg/Shader.cs b/OpenRA.Renderer.Cg/Shader.cs index 9ef2c1d3c3..7be1a3b3b7 100644 --- a/OpenRA.Renderer.Cg/Shader.cs +++ b/OpenRA.Renderer.Cg/Shader.cs @@ -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) diff --git a/OpenRA.Renderer.Gl/Shader.cs b/OpenRA.Renderer.Gl/Shader.cs index 1bc9427f1d..ea6db304d7 100644 --- a/OpenRA.Renderer.Gl/Shader.cs +++ b/OpenRA.Renderer.Gl/Shader.cs @@ -115,8 +115,8 @@ 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 */ diff --git a/OpenRA.Renderer.Null/NullGraphicsDevice.cs b/OpenRA.Renderer.Null/NullGraphicsDevice.cs index 359915d371..e1fbfab9a1 100644 --- a/OpenRA.Renderer.Null/NullGraphicsDevice.cs +++ b/OpenRA.Renderer.Null/NullGraphicsDevice.cs @@ -79,6 +79,8 @@ 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 diff --git a/OpenRA.Renderer.SdlCommon/Texture.cs b/OpenRA.Renderer.SdlCommon/Texture.cs index 8aadc8e740..915d0edf33 100644 --- a/OpenRA.Renderer.SdlCommon/Texture.cs +++ b/OpenRA.Renderer.SdlCommon/Texture.cs @@ -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() { @@ -59,6 +63,7 @@ namespace OpenRA.Renderer.SdlCommon 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]) @@ -81,6 +86,7 @@ namespace OpenRA.Renderer.SdlCommon 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]) @@ -99,6 +105,7 @@ namespace OpenRA.Renderer.SdlCommon 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); @@ -109,5 +116,34 @@ namespace OpenRA.Renderer.SdlCommon ErrorHandler.CheckGlError(); bitmap.UnlockBits(bits); } + + public byte[] GetData() + { + 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(); + } } } From 013ad0617e832bdc4ccb098146f680484d90ba24 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 7 Jun 2013 17:55:11 +1200 Subject: [PATCH 07/23] Allow sheets to wrap an ITexture directly. --- OpenRA.Game/Graphics/Sheet.cs | 39 ++++++++++++++++++++-------- OpenRA.Game/Graphics/SheetBuilder.cs | 2 +- OpenRA.Game/Graphics/SpriteFont.cs | 2 ++ OpenRA.Game/Graphics/VoxelLoader.cs | 2 +- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/OpenRA.Game/Graphics/Sheet.cs b/OpenRA.Game/Graphics/Sheet.cs index a390d218be..4df475e86a 100644 --- a/OpenRA.Game/Graphics/Sheet.cs +++ b/OpenRA.Game/Graphics/Sheet.cs @@ -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; + } } } diff --git a/OpenRA.Game/Graphics/SheetBuilder.cs b/OpenRA.Game/Graphics/SheetBuilder.cs index 6b8a0ff930..32e06f5883 100644 --- a/OpenRA.Game/Graphics/SheetBuilder.cs +++ b/OpenRA.Game/Graphics/SheetBuilder.cs @@ -55,6 +55,7 @@ namespace OpenRA.Graphics { var rect = Allocate(size); Util.FastCopyIntoChannel(rect, src); + current.CommitData(); return rect; } @@ -103,7 +104,6 @@ namespace OpenRA.Graphics } var rect = new Sprite(current, new Rectangle(p, imageSize), channel); - current.MakeDirty(); p.X += imageSize.Width; return rect; diff --git a/OpenRA.Game/Graphics/SpriteFont.cs b/OpenRA.Game/Graphics/SpriteFont.cs index 66e1c8dfb7..9c62ad8ef0 100644 --- a/OpenRA.Game/Graphics/SpriteFont.cs +++ b/OpenRA.Game/Graphics/SpriteFont.cs @@ -129,6 +129,8 @@ namespace OpenRA.Graphics p += face.Glyph.Bitmap.Pitch; } } + s.sheet.CommitData(); + return g; } diff --git a/OpenRA.Game/Graphics/VoxelLoader.cs b/OpenRA.Game/Graphics/VoxelLoader.cs index 500b41d708..8467ec4746 100644 --- a/OpenRA.Game/Graphics/VoxelLoader.cs +++ b/OpenRA.Game/Graphics/VoxelLoader.cs @@ -84,7 +84,7 @@ namespace OpenRA.Graphics 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] From 37770a4e474c91b1fd8949d28f0171e38689b4c5 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 8 Jun 2013 13:15:54 +1200 Subject: [PATCH 08/23] Extract voxel transform matrix into a function. --- OpenRA.Game/Graphics/Voxel.cs | 41 ++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/OpenRA.Game/Graphics/Voxel.cs b/OpenRA.Game/Graphics/Voxel.cs index 841886e1be..dcabecfcd3 100644 --- a/OpenRA.Game/Graphics/Voxel.cs +++ b/OpenRA.Game/Graphics/Voxel.cs @@ -99,6 +99,23 @@ namespace OpenRA.Graphics groundNormal[i], groundZ[i], shadowPalette); } + float[] TransformationMatrix(uint limb, uint frame) + { + var l = limbs[limb]; + var t = hva.TransformationMatrix(limb, 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]; + + // 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; + } + 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 rotations, @@ -106,28 +123,18 @@ namespace OpenRA.Graphics { // Calculate the shared view matrix components var pxPos = wr.ScreenPosition(pos); - - // Rotations - var rot = rotations.Reverse().Aggregate(MakeFloatMatrix(camera.AsMatrix()), + var posMtx = Util.TranslationMatrix(pxPos.X, pxPos.Y, pxPos.Y); + var scaleMtx = Util.ScaleMatrix(scale, scale, scale); + var rotMtx = 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); + var t = TransformationMatrix(i, frame); + transform[i] = Util.MatrixMultiply(rotMtx, t); + transform[i] = Util.MatrixMultiply(scaleMtx, transform[i]); + transform[i] = Util.MatrixMultiply(posMtx, transform[i]); // Transform light direction into limb-space var undoPitch = MakeFloatMatrix(new WRot(camera.Pitch, WAngle.Zero, WAngle.Zero).AsMatrix()); From 5f0ab1f62dc4083575754e069e5be33e69e9f814 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 8 Jun 2013 14:13:47 +1200 Subject: [PATCH 09/23] Add functions for calculating voxel bounding boxes. --- OpenRA.Game/Graphics/Util.cs | 29 +++++++++++++++++++++++++++++ OpenRA.Game/Graphics/Voxel.cs | 27 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/OpenRA.Game/Graphics/Util.cs b/OpenRA.Game/Graphics/Util.cs index 1d08371ddb..278c514f93 100644 --- a/OpenRA.Game/Graphics/Util.cs +++ b/OpenRA.Game/Graphics/Util.cs @@ -8,6 +8,7 @@ */ #endregion +using System; using OpenRA.FileFormats.Graphics; namespace OpenRA.Graphics @@ -229,5 +230,33 @@ namespace OpenRA.Graphics return mtx; } + + 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; + } } } diff --git a/OpenRA.Game/Graphics/Voxel.cs b/OpenRA.Game/Graphics/Voxel.cs index dcabecfcd3..d69500cf97 100644 --- a/OpenRA.Game/Graphics/Voxel.cs +++ b/OpenRA.Game/Graphics/Voxel.cs @@ -164,5 +164,32 @@ 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.Length; j++) + { + var b = new float[] + { + 0, 0, 0, + (limbs[j].Bounds[3] - limbs[j].Bounds[0]), + (limbs[j].Bounds[4] - limbs[j].Bounds[1]), + (limbs[j].Bounds[5] - limbs[j].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; + } } } From 4152f6199951f9cb4c10007aba5d7aa233bc7203 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 8 Jun 2013 18:19:23 +1200 Subject: [PATCH 10/23] Move MakeFloatMatrix to Graphics.Util. --- OpenRA.Game/Graphics/Util.cs | 8 ++++++++ OpenRA.Game/Graphics/Voxel.cs | 14 +++----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/OpenRA.Game/Graphics/Util.cs b/OpenRA.Game/Graphics/Util.cs index 278c514f93..98673f2486 100644 --- a/OpenRA.Game/Graphics/Util.cs +++ b/OpenRA.Game/Graphics/Util.cs @@ -231,6 +231,14 @@ 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 diff --git a/OpenRA.Game/Graphics/Voxel.cs b/OpenRA.Game/Graphics/Voxel.cs index d69500cf97..9d9efd58cd 100644 --- a/OpenRA.Game/Graphics/Voxel.cs +++ b/OpenRA.Game/Graphics/Voxel.cs @@ -76,14 +76,6 @@ namespace OpenRA.Graphics 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) { @@ -125,8 +117,8 @@ namespace OpenRA.Graphics var pxPos = wr.ScreenPosition(pos); var posMtx = Util.TranslationMatrix(pxPos.X, pxPos.Y, pxPos.Y); var scaleMtx = Util.ScaleMatrix(scale, scale, scale); - var rotMtx = rotations.Reverse().Aggregate(MakeFloatMatrix(camera.AsMatrix()), - (a,b) => Util.MatrixMultiply(a, MakeFloatMatrix(b.AsMatrix()))); + var rotMtx = rotations.Reverse().Aggregate(Util.MakeFloatMatrix(camera.AsMatrix()), + (a,b) => Util.MatrixMultiply(a, Util.MakeFloatMatrix(b.AsMatrix()))); // Each limb has its own transformation matrix for (uint i = 0; i < limbs.Length; i++) @@ -137,7 +129,7 @@ namespace OpenRA.Graphics transform[i] = Util.MatrixMultiply(posMtx, transform[i]); // Transform light direction into limb-space - var undoPitch = MakeFloatMatrix(new WRot(camera.Pitch, WAngle.Zero, WAngle.Zero).AsMatrix()); + var undoPitch = Util.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)); From 9b576d3fdd68fea4b2fd21ef24d99faad4a241b4 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 9 Jun 2013 22:37:44 +1200 Subject: [PATCH 11/23] Add a visualization layer for renderable geometry. --- OpenRA.Game/Graphics/Renderable.cs | 7 ++ OpenRA.Game/Graphics/VoxelRenderable.cs | 126 ++++++++++++++++++++ OpenRA.Game/Graphics/WorldRenderer.cs | 27 ++++- OpenRA.Game/Traits/Player/DeveloperMode.cs | 3 + OpenRA.Mods.RA/Widgets/Logic/CheatsLogic.cs | 8 ++ mods/cnc/chrome/cheats.yaml | 8 +- mods/ra/chrome/cheats.yaml | 14 ++- 7 files changed, 183 insertions(+), 10 deletions(-) diff --git a/OpenRA.Game/Graphics/Renderable.cs b/OpenRA.Game/Graphics/Renderable.cs index b2e84114ce..a3ec62dbd6 100644 --- a/OpenRA.Game/Graphics/Renderable.cs +++ b/OpenRA.Game/Graphics/Renderable.cs @@ -41,6 +41,7 @@ namespace OpenRA.Graphics IRenderable WithZOffset(int newOffset); IRenderable WithPos(WPos pos); void Render(WorldRenderer wr); + void RenderDebugGeometry(WorldRenderer wr); } public struct SpriteRenderable : IRenderable @@ -83,5 +84,11 @@ namespace OpenRA.Graphics { sprite.DrawAt(wr.ScreenPxPosition(pos) - pxCenter, palette.Index, scale); } + + public void RenderDebugGeometry(WorldRenderer wr) + { + var offset = wr.ScreenPxPosition(pos) - pxCenter; + Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + sprite.size, Color.Red); + } } } diff --git a/OpenRA.Game/Graphics/VoxelRenderable.cs b/OpenRA.Game/Graphics/VoxelRenderable.cs index 168d3b0513..a2517c1680 100644 --- a/OpenRA.Game/Graphics/VoxelRenderable.cs +++ b/OpenRA.Game/Graphics/VoxelRenderable.cs @@ -102,5 +102,131 @@ namespace OpenRA.Graphics v.Voxel.Draw(vr, lightAmbientColor, lightDiffuseColor, palette.Index, normalsPalette.Index); Game.Renderer.DisableDepthBuffer(); } + + public void RenderDebugGeometry(WorldRenderer wr) + { + var draw = voxels.Where(v => v.DisableFunc == null || !v.DisableFunc()); + var scaleTransform = Util.ScaleMatrix(scale, scale, scale); + var pxOrigin = wr.ScreenPosition(pos); + + // Correct for bogus light source definition + var shadowTransform = Util.MakeFloatMatrix(new WRot(new WAngle(256) - lightSource.Pitch, + WAngle.Zero, lightSource.Yaw + new WAngle(512)).AsMatrix()); + + var invShadowTransform = Util.MatrixInverse(shadowTransform); + var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix()); + + // TODO: Generalize this once we support sloped terrain + var groundNormal = new float[] {0,0,1,1}; + var groundPos = new float[] {0, 0, 0.5f*(wr.ScreenPosition(pos).Y - wr.ScreenZPosition(pos, 0)), 1}; + var shadowGroundNormal = Util.MatrixVectorMultiply(shadowTransform, groundNormal); + var shadowGroundPos = Util.MatrixVectorMultiply(shadowTransform, groundPos); + + // 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 draw) + { + var bounds = v.Voxel.Bounds(v.FrameFunc()); + var worldTransform = v.RotationFunc().Reverse().Aggregate(scaleTransform, + (x,y) => Util.MatrixMultiply(x, Util.MakeFloatMatrix(y.AsMatrix()))); + + var worldBounds = Util.MatrixAABBMultiply(worldTransform, bounds); + var screenBounds = Util.MatrixAABBMultiply(cameraTransform, worldBounds); + + // Aggregate bounds rect + var pxOffset = wr.ScreenVector(v.OffsetFunc()); + var pxPos = pxOrigin + new float2(pxOffset[0], pxOffset[1]); + tl = float2.Min(tl, pxPos + new float2(screenBounds[0], screenBounds[1])); + br = float2.Max(br, pxPos + new float2(screenBounds[3], screenBounds[4])); + + // Box to render the shadow image from + var shadowBounds = Util.MatrixAABBMultiply(shadowTransform, worldBounds); + var shadowPxOffset = Util.MatrixVectorMultiply(shadowTransform, pxOffset); + + stl = float2.Min(stl, new float2(shadowPxOffset[0] + shadowBounds[0], shadowPxOffset[1] + shadowBounds[1])); + sbr = float2.Max(sbr, new float2(shadowPxOffset[0] + shadowBounds[3], shadowPxOffset[1] + shadowBounds[4])); + + // Draw voxel bounding box + var screenTransform = Util.MatrixMultiply(cameraTransform, worldTransform); + DrawBoundsBox(pxPos, screenTransform, bounds, Color.Yellow); + } + + // Inflate rects by 1px each side to ensure rendering is within bounds + var pad = new float2(1,1); + tl -= pad; + br += pad; + stl -= pad; + sbr += pad; + + // 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 screenCorners = new float2[4]; + for (var j = 0; j < 4; j++) + { + // Project to ground plane + corners[j][2] -= (corners[j][2] - shadowGroundPos[2]) + + (corners[j][1] - shadowGroundPos[1])*shadowGroundNormal[1]/shadowGroundNormal[2] + + (corners[j][0] - shadowGroundPos[0])*shadowGroundNormal[0]/shadowGroundNormal[2]; + + // Rotate to camera-space + corners[j] = Util.MatrixVectorMultiply(shadowScreenTransform, corners[j]); + screenCorners[j] = pxOrigin + new float2(corners[j][0], corners[j][1]); + } + + // Draw transformed shadow sprite rect + var c = Color.Purple; + Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[1], screenCorners[3], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[3], screenCorners[0], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[0], screenCorners[2], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[2], screenCorners[1], c, c); + + // Draw sprite rect + Game.Renderer.WorldLineRenderer.DrawRect(tl, br, Color.Red); + } + + static void DrawBoundsBox(float2 pxPos, float[] transform, float[] bounds, Color c) + { + // 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}; + + 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); + } } } diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index 87eef096f2..676d7fd931 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -37,6 +37,7 @@ namespace OpenRA.Graphics internal readonly ShroudRenderer shroudRenderer; internal readonly HardwarePalette palette; internal Cache palettes; + Lazy 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() : null); } PaletteReference CreatePaletteReference(string name) @@ -74,14 +77,21 @@ 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 + var effectRenderables = world.Effects.SelectMany(e => e.Render(this)); + // TODO: Allow effects to be interleaved with actors - world.Effects.SelectMany(e => e.Render(this)) - .Do(rr => rr.Render(this)); + worldRenderables.Do(rr => rr.Render(this)); + effectRenderables.Do(rr => rr.Render(this)); + + if (devTrait.Value != null && devTrait.Value.ShowDebugGeometry) + { + worldRenderables.Do(rr => rr.RenderDebugGeometry(this)); + effectRenderables.Do(rr => rr.RenderDebugGeometry(this)); + } } public void Draw() @@ -219,6 +229,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; } } } diff --git a/OpenRA.Game/Traits/Player/DeveloperMode.cs b/OpenRA.Game/Traits/Player/DeveloperMode.cs index 769d53eca0..51b33a0211 100644 --- a/OpenRA.Game/Traits/Player/DeveloperMode.cs +++ b/OpenRA.Game/Traits/Player/DeveloperMode.cs @@ -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) diff --git a/OpenRA.Mods.RA/Widgets/Logic/CheatsLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/CheatsLogic.cs index 68f1d6bea1..a4cb298ffa 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/CheatsLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/CheatsLogic.cs @@ -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("SHOW_GEOMETRY"); + if (showGeometryCheckbox != null) + { + showGeometryCheckbox.IsChecked = () => devTrait.ShowDebugGeometry; + showGeometryCheckbox.OnClick = () => devTrait.ShowDebugGeometry ^= true; + } + var allTechCheckbox = widget.GetOrNull("ENABLE_TECH"); if (allTechCheckbox != null) { diff --git a/mods/cnc/chrome/cheats.yaml b/mods/cnc/chrome/cheats.yaml index 7f68f3c389..665d7985aa 100644 --- a/mods/cnc/chrome/cheats.yaml +++ b/mods/cnc/chrome/cheats.yaml @@ -98,4 +98,10 @@ Container@CHEATS_PANEL: Y:235 Height:20 Width:200 - Text:Show Muzzle Positions \ No newline at end of file + Text:Show Muzzle Positions + Checkbox@SHOW_GEOMETRY: + X:290 + Y:265 + Height:20 + Width:200 + Text:Show Render Geometry \ No newline at end of file diff --git a/mods/ra/chrome/cheats.yaml b/mods/ra/chrome/cheats.yaml index b07fee7405..ffa5f7507c 100644 --- a/mods/ra/chrome/cheats.yaml +++ b/mods/ra/chrome/cheats.yaml @@ -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 From e5bcb88b0e755f5ffdb3a1fdf98a89aba5ae6755 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 12 Jun 2013 00:45:19 +1200 Subject: [PATCH 12/23] Support sprites with an internal offset. --- OpenRA.Game/Graphics/SheetBuilder.cs | 5 +++-- OpenRA.Game/Graphics/Sprite.cs | 7 ++++++- OpenRA.Game/Graphics/SpriteRenderer.cs | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/OpenRA.Game/Graphics/SheetBuilder.cs b/OpenRA.Game/Graphics/SheetBuilder.cs index 32e06f5883..a6ab4b0bce 100644 --- a/OpenRA.Game/Graphics/SheetBuilder.cs +++ b/OpenRA.Game/Graphics/SheetBuilder.cs @@ -77,7 +77,8 @@ namespace OpenRA.Graphics return (TextureChannel)nextChannel; } - public Sprite Allocate(Size imageSize) + 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) { @@ -103,7 +104,7 @@ namespace OpenRA.Graphics p = new Point(0,0); } - var rect = new Sprite(current, new Rectangle(p, imageSize), channel); + var rect = new Sprite(current, new Rectangle(p, imageSize), spriteOffset, channel); p.X += imageSize.Width; return rect; diff --git a/OpenRA.Game/Graphics/Sprite.cs b/OpenRA.Game/Graphics/Sprite.cs index 1ebcb9eaad..fca0632536 100644 --- a/OpenRA.Game/Graphics/Sprite.cs +++ b/OpenRA.Game/Graphics/Sprite.cs @@ -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); diff --git a/OpenRA.Game/Graphics/SpriteRenderer.cs b/OpenRA.Game/Graphics/SpriteRenderer.cs index c73485213f..985f194800 100644 --- a/OpenRA.Game/Graphics/SpriteRenderer.cs +++ b/OpenRA.Game/Graphics/SpriteRenderer.cs @@ -66,7 +66,7 @@ namespace OpenRA.Graphics Flush(); currentSheet = s.sheet; - Util.FastCreateQuad(vertices, location.ToInt2(), s, paletteIndex, nv, size); + Util.FastCreateQuad(vertices, (location + s.offset).ToInt2(), s, paletteIndex, nv, size); nv += 4; } From 7beef85a646600e4149d1ad524f5733116a94103 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 13 Jun 2013 00:07:47 +1200 Subject: [PATCH 13/23] Use PaletteReferences everywhere. --- OpenRA.Game/Graphics/CursorProvider.cs | 23 ++++++++++++++----- OpenRA.Game/Graphics/Renderable.cs | 2 +- OpenRA.Game/Graphics/ShroudRenderer.cs | 6 ++--- OpenRA.Game/Graphics/Sprite.cs | 17 +++++--------- OpenRA.Game/Graphics/SpriteRenderer.cs | 10 ++++---- OpenRA.Game/Traits/SelectionDecorations.cs | 9 ++++---- OpenRA.Game/Traits/World/ResourceLayer.cs | 2 +- OpenRA.Game/Widgets/ShpImageWidget.cs | 2 +- OpenRA.Game/Widgets/WidgetUtils.cs | 4 ++-- OpenRA.Mods.RA/Buildings/BibLayer.cs | 3 ++- .../Orders/PlaceBuildingOrderGenerator.cs | 3 ++- .../SupportPowers/ChronoshiftPower.cs | 10 ++++---- .../SupportPowers/IronCurtainPower.cs | 3 ++- OpenRA.Mods.RA/World/SmudgeLayer.cs | 4 +++- 14 files changed, 56 insertions(+), 42 deletions(-) diff --git a/OpenRA.Game/Graphics/CursorProvider.cs b/OpenRA.Game/Graphics/CursorProvider.cs index 4ea40508e0..79e4db3966 100644 --- a/OpenRA.Game/Graphics/CursorProvider.cs +++ b/OpenRA.Game/Graphics/CursorProvider.cs @@ -20,12 +20,23 @@ namespace OpenRA.Graphics { public static class CursorProvider { - static HardwarePalette Palette; + static HardwarePalette palette; static Dictionary cursors; + static Cache 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(); + palettes = new Cache(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); } diff --git a/OpenRA.Game/Graphics/Renderable.cs b/OpenRA.Game/Graphics/Renderable.cs index a3ec62dbd6..fcc264e399 100644 --- a/OpenRA.Game/Graphics/Renderable.cs +++ b/OpenRA.Game/Graphics/Renderable.cs @@ -82,7 +82,7 @@ namespace OpenRA.Graphics 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) diff --git a/OpenRA.Game/Graphics/ShroudRenderer.cs b/OpenRA.Game/Graphics/ShroudRenderer.cs index 7abb76ffae..3a8358ae2f 100644 --- a/OpenRA.Game/Graphics/ShroudRenderer.cs +++ b/OpenRA.Game/Graphics/ShroudRenderer.cs @@ -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)); } } diff --git a/OpenRA.Game/Graphics/Sprite.cs b/OpenRA.Game/Graphics/Sprite.cs index fca0632536..7296bbb25c 100644 --- a/OpenRA.Game/Graphics/Sprite.cs +++ b/OpenRA.Game/Graphics/Sprite.cs @@ -50,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); } } diff --git a/OpenRA.Game/Graphics/SpriteRenderer.cs b/OpenRA.Game/Graphics/SpriteRenderer.cs index 985f194800..11e1989127 100644 --- a/OpenRA.Game/Graphics/SpriteRenderer.cs +++ b/OpenRA.Game/Graphics/SpriteRenderer.cs @@ -45,17 +45,17 @@ namespace OpenRA.Graphics } } - 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; diff --git a/OpenRA.Game/Traits/SelectionDecorations.cs b/OpenRA.Game/Traits/SelectionDecorations.cs index ab9293d688..d9981c52b5 100644 --- a/OpenRA.Game/Traits/SelectionDecorations.cs +++ b/OpenRA.Game/Traits/SelectionDecorations.cs @@ -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()) { 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; diff --git a/OpenRA.Game/Traits/World/ResourceLayer.cs b/OpenRA.Game/Traits/World/ResourceLayer.cs index 4b10b173fc..59c25462c2 100644 --- a/OpenRA.Game/Traits/World/ResourceLayer.cs +++ b/OpenRA.Game/Traits/World/ResourceLayer.cs @@ -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); } } diff --git a/OpenRA.Game/Widgets/ShpImageWidget.cs b/OpenRA.Game/Widgets/ShpImageWidget.cs index 7198598ccb..f7842aa5fa 100644 --- a/OpenRA.Game/Widgets/ShpImageWidget.cs +++ b/OpenRA.Game/Widgets/ShpImageWidget.cs @@ -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 diff --git a/OpenRA.Game/Widgets/WidgetUtils.cs b/OpenRA.Game/Widgets/WidgetUtils.cs index 7120dacc3e..6f3f1ac19f 100644 --- a/OpenRA.Game/Widgets/WidgetUtils.cs +++ b/OpenRA.Game/Widgets/WidgetUtils.cs @@ -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) diff --git a/OpenRA.Mods.RA/Buildings/BibLayer.cs b/OpenRA.Mods.RA/Buildings/BibLayer.cs index d5f33a2243..415a08ae0a 100755 --- a/OpenRA.Mods.RA/Buildings/BibLayer.cs +++ b/OpenRA.Mods.RA/Buildings/BibLayer.cs @@ -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); } } } diff --git a/OpenRA.Mods.RA/Orders/PlaceBuildingOrderGenerator.cs b/OpenRA.Mods.RA/Orders/PlaceBuildingOrderGenerator.cs index 84cb7a52b7..b561ca2dfd 100755 --- a/OpenRA.Mods.RA/Orders/PlaceBuildingOrderGenerator.cs +++ b/OpenRA.Mods.RA/Orders/PlaceBuildingOrderGenerator.cs @@ -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"; } diff --git a/OpenRA.Mods.RA/SupportPowers/ChronoshiftPower.cs b/OpenRA.Mods.RA/SupportPowers/ChronoshiftPower.cs index a6b76c1fc8..7b63802360 100755 --- a/OpenRA.Mods.RA/SupportPowers/ChronoshiftPower.cs +++ b/OpenRA.Mods.RA/SupportPowers/ChronoshiftPower.cs @@ -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().CanChronoshiftTo(unit,targetCell)); var tile = canEnter ? validTile : invalidTile; - tile.DrawAt(wr, targetCell.ToPPos().ToFloat2(), "terrain"); + tile.DrawAt(targetCell.ToPPos().ToFloat2(), pal); } } } diff --git a/OpenRA.Mods.RA/SupportPowers/IronCurtainPower.cs b/OpenRA.Mods.RA/SupportPowers/IronCurtainPower.cs index 62ea72c3ce..0890649e9b 100755 --- a/OpenRA.Mods.RA/SupportPowers/IronCurtainPower.cs +++ b/OpenRA.Mods.RA/SupportPowers/IronCurtainPower.cs @@ -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) diff --git a/OpenRA.Mods.RA/World/SmudgeLayer.cs b/OpenRA.Mods.RA/World/SmudgeLayer.cs index 98ac1bbb25..883a4c8fdb 100755 --- a/OpenRA.Mods.RA/World/SmudgeLayer.cs +++ b/OpenRA.Mods.RA/World/SmudgeLayer.cs @@ -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); } } } From 7d09e78655bcbcf563b5fdb8f6ae5a2e0a0e1576 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 13 Jun 2013 00:16:40 +1200 Subject: [PATCH 14/23] Add WorldRgbaSpriteRenderer. --- OpenRA.Game/Graphics/Renderer.cs | 4 ++++ cg/rgba.fx | 3 ++- glsl/rgba.vert | 7 ++++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/OpenRA.Game/Graphics/Renderer.cs b/OpenRA.Game/Graphics/Renderer.cs index 723373964c..bb803babe8 100644 --- a/OpenRA.Game/Graphics/Renderer.cs +++ b/OpenRA.Game/Graphics/Renderer.cs @@ -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,6 +47,7 @@ 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")); LineRenderer = new LineRenderer(this, device.CreateShader("line")); @@ -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); } diff --git a/cg/rgba.fx b/cg/rgba.fx index 4dad0637e8..18d89b7d5e 100644 --- a/cg/rgba.fx +++ b/cg/rgba.fx @@ -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; diff --git a/glsl/rgba.vert b/glsl/rgba.vert index 0a10445d30..89ffea0e96 100644 --- a/glsl/rgba.vert +++ b/glsl/rgba.vert @@ -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; } From 18311be3ae5416a081683d0fefc46fb823ea42ee Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 13 Jun 2013 02:23:17 +1200 Subject: [PATCH 15/23] Remove nearest-int position rounding from SpriteRenderer. The things that want to be rounded already do this much earlier (wr.ScreenPxPosition, etc). --- OpenRA.Game/Graphics/SpriteRenderer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenRA.Game/Graphics/SpriteRenderer.cs b/OpenRA.Game/Graphics/SpriteRenderer.cs index 11e1989127..465db7ce75 100644 --- a/OpenRA.Game/Graphics/SpriteRenderer.cs +++ b/OpenRA.Game/Graphics/SpriteRenderer.cs @@ -62,11 +62,11 @@ namespace OpenRA.Graphics if (s.sheet != currentSheet) Flush(); - if( nv + 4 > Renderer.TempBufferSize ) + if (nv + 4 > Renderer.TempBufferSize) Flush(); currentSheet = s.sheet; - Util.FastCreateQuad(vertices, (location + s.offset).ToInt2(), s, paletteIndex, nv, size); + Util.FastCreateQuad(vertices, location + s.offset, s, paletteIndex, nv, size); nv += 4; } From 7a71f87d9f38e87644be6b98f2cf66b1642091c1 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Tue, 11 Jun 2013 19:07:50 +1200 Subject: [PATCH 16/23] Introduce Renderable.BeforeRender(). WorldRenderer.Draw() has been slightly reorganized to ensure that BeforeRender is called before any drawing render state (e.g. scissor) has been set. --- OpenRA.Game/Graphics/Renderable.cs | 2 ++ OpenRA.Game/Graphics/VoxelRenderable.cs | 1 + OpenRA.Game/Graphics/WorldRenderer.cs | 28 +++++++++++++++---------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/OpenRA.Game/Graphics/Renderable.cs b/OpenRA.Game/Graphics/Renderable.cs index fcc264e399..1353101eae 100644 --- a/OpenRA.Game/Graphics/Renderable.cs +++ b/OpenRA.Game/Graphics/Renderable.cs @@ -40,6 +40,7 @@ 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); } @@ -80,6 +81,7 @@ 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, scale); diff --git a/OpenRA.Game/Graphics/VoxelRenderable.cs b/OpenRA.Game/Graphics/VoxelRenderable.cs index a2517c1680..36b4eab252 100644 --- a/OpenRA.Game/Graphics/VoxelRenderable.cs +++ b/OpenRA.Game/Graphics/VoxelRenderable.cs @@ -79,6 +79,7 @@ namespace OpenRA.Graphics palette, normalsPalette, shadowPalette); } + public void BeforeRender(WorldRenderer wr) {} public void Render(WorldRenderer wr) { // Depth and shadow buffers are cleared between actors so that diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index 676d7fd931..b7c0228611 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -68,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 GenerateRenderables() { var bounds = Game.viewport.WorldBounds(world); var comparer = new RenderableComparer(this); @@ -81,17 +81,16 @@ namespace OpenRA.Graphics .OrderBy(r => r, comparer); // Effects are drawn on top of all actors - var effectRenderables = world.Effects.SelectMany(e => e.Render(this)); - // TODO: Allow effects to be interleaved with actors - worldRenderables.Do(rr => rr.Render(this)); - effectRenderables.Do(rr => rr.Render(this)); + var effectRenderables = world.Effects + .SelectMany(e => e.Render(this)); - if (devTrait.Value != null && devTrait.Value.ShowDebugGeometry) - { - worldRenderables.Do(rr => rr.RenderDebugGeometry(this)); - effectRenderables.Do(rr => rr.RenderDebugGeometry(this)); - } + // Iterating via foreach() copies the structs, so enumerate by index + var renderables = worldRenderables.Concat(effectRenderables).ToList(); + for (var i = 0; i < renderables.Count; i++) + renderables[i].BeforeRender(this); + + return renderables; } public void Draw() @@ -101,6 +100,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); @@ -119,7 +119,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()) @@ -131,6 +132,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) From 2215f749597f15c23bb802d3bd6f19a1e7e30460 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 13 Jun 2013 05:07:47 +1200 Subject: [PATCH 17/23] Support rendering sprites into non-rectangular quads. --- OpenRA.Game/Graphics/SpriteRenderer.cs | 15 +++++++++++++++ OpenRA.Game/Graphics/Util.cs | 20 ++++++++++++-------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/OpenRA.Game/Graphics/SpriteRenderer.cs b/OpenRA.Game/Graphics/SpriteRenderer.cs index 465db7ce75..14049bb4af 100644 --- a/OpenRA.Game/Graphics/SpriteRenderer.cs +++ b/OpenRA.Game/Graphics/SpriteRenderer.cs @@ -81,6 +81,21 @@ 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 buffer, int start, int length, PrimitiveType type, Sheet sheet) { shader.SetTexture("DiffuseTexture", sheet.Texture); diff --git a/OpenRA.Game/Graphics/Util.cs b/OpenRA.Game/Graphics/Util.cs index 98673f2486..c859121b9d 100644 --- a/OpenRA.Game/Graphics/Util.cs +++ b/OpenRA.Game/Graphics/Util.cs @@ -18,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. From c5337cdcf33f20d36c14af7c0537d8e924bc1900 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 12 Jun 2013 16:28:01 +1200 Subject: [PATCH 18/23] Reimplement voxel rendering with a FBO. --- OpenRA.FileFormats/Graphics/HvaReader.cs | 15 +- OpenRA.Game/Graphics/Renderer.cs | 2 +- OpenRA.Game/Graphics/Voxel.cs | 115 ++------ OpenRA.Game/Graphics/VoxelRenderable.cs | 145 +++------- OpenRA.Game/Graphics/VoxelRenderer.cs | 340 ++++++++++++++++++++--- OpenRA.Game/Graphics/WorldRenderer.cs | 3 + OpenRA.Mods.RA/Render/RenderVoxels.cs | 7 +- cg/vxlshadow.fx | 90 ------ glsl/vxlshadow.frag | 12 - glsl/vxlshadow.vert | 26 -- 10 files changed, 377 insertions(+), 378 deletions(-) delete mode 100644 cg/vxlshadow.fx delete mode 100644 glsl/vxlshadow.frag delete mode 100644 glsl/vxlshadow.vert diff --git a/OpenRA.FileFormats/Graphics/HvaReader.cs b/OpenRA.FileFormats/Graphics/HvaReader.cs index 662be7d8a9..177d6e4b6d 100644 --- a/OpenRA.FileFormats/Graphics/HvaReader.cs +++ b/OpenRA.FileFormats/Graphics/HvaReader.cs @@ -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)) diff --git a/OpenRA.Game/Graphics/Renderer.cs b/OpenRA.Game/Graphics/Renderer.cs index bb803babe8..865d296b93 100644 --- a/OpenRA.Game/Graphics/Renderer.cs +++ b/OpenRA.Game/Graphics/Renderer.cs @@ -49,7 +49,7 @@ namespace OpenRA.Graphics 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")); diff --git a/OpenRA.Game/Graphics/Voxel.cs b/OpenRA.Game/Graphics/Voxel.cs index 9d9efd58cd..98d6f72544 100644 --- a/OpenRA.Game/Graphics/Voxel.cs +++ b/OpenRA.Game/Graphics/Voxel.cs @@ -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,53 +51,20 @@ 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; + limbData[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) + public float[] TransformationMatrix(uint limb, uint frame) { - 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]; + 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)); - // 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; - } - - 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); - } - - float[] TransformationMatrix(uint limb, uint frame) - { - var l = limbs[limb]; - var t = hva.TransformationMatrix(limb, frame); + 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]; @@ -108,46 +78,16 @@ namespace OpenRA.Graphics return t; } - 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 rotations, - WRot camera, uint frame, float scale, WRot lightSource) + public VoxelRenderData RenderData(uint limb) { - // Calculate the shared view matrix components - var pxPos = wr.ScreenPosition(pos); - var posMtx = Util.TranslationMatrix(pxPos.X, pxPos.Y, pxPos.Y); - var scaleMtx = Util.ScaleMatrix(scale, scale, scale); - var rotMtx = rotations.Reverse().Aggregate(Util.MakeFloatMatrix(camera.AsMatrix()), - (a,b) => Util.MatrixMultiply(a, Util.MakeFloatMatrix(b.AsMatrix()))); - - // Each limb has its own transformation matrix - for (uint i = 0; i < limbs.Length; i++) - { - var t = TransformationMatrix(i, frame); - transform[i] = Util.MatrixMultiply(rotMtx, t); - transform[i] = Util.MatrixMultiply(scaleMtx, transform[i]); - transform[i] = Util.MatrixMultiply(posMtx, transform[i]); - - // Transform light direction into limb-space - var undoPitch = Util.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; - } + return limbData[limb].RenderData; } - public uint Frames { get { return hva.FrameCount; }} - public uint LimbCount { get { return (uint)limbs.Length; }} - 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]), @@ -162,14 +102,15 @@ namespace OpenRA.Graphics var ret = new float[] {float.MaxValue,float.MaxValue,float.MaxValue, float.MinValue,float.MinValue,float.MinValue}; - for (uint j = 0; j < limbs.Length; j++) + for (uint j = 0; j < Limbs; j++) { + var l = limbData[j]; var b = new float[] { 0, 0, 0, - (limbs[j].Bounds[3] - limbs[j].Bounds[0]), - (limbs[j].Bounds[4] - limbs[j].Bounds[1]), - (limbs[j].Bounds[5] - limbs[j].Bounds[2]) + (l.Bounds[3] - l.Bounds[0]), + (l.Bounds[4] - l.Bounds[1]), + (l.Bounds[5] - l.Bounds[2]) }; // Calculate limb bounding box diff --git a/OpenRA.Game/Graphics/VoxelRenderable.cs b/OpenRA.Game/Graphics/VoxelRenderable.cs index 36b4eab252..525b7161c3 100644 --- a/OpenRA.Game/Graphics/VoxelRenderable.cs +++ b/OpenRA.Game/Graphics/VoxelRenderable.cs @@ -29,6 +29,9 @@ namespace OpenRA.Graphics readonly PaletteReference shadowPalette; readonly float scale; + // Generated at render-time + VoxelRenderProxy renderProxy; + public VoxelRenderable(IEnumerable 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,133 +83,72 @@ namespace OpenRA.Graphics palette, normalsPalette, shadowPalette); } - public void BeforeRender(WorldRenderer wr) {} + // 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 draw = voxels.Where(v => v.DisableFunc == null || !v.DisableFunc()); + var pxOrigin = wr.ScreenPosition(pos); + var groundZ = 0.5f*(pxOrigin.Y - wr.ScreenZPosition(pos, 0)); + var shadowOrigin = pxOrigin - groundZ*(new float2(renderProxy.ShadowDirection, 1)); - foreach (var v in draw) - v.Voxel.PrepareForDraw(wr, pos + v.OffsetFunc(), v.RotationFunc(), camera, - v.FrameFunc(), scale, lightSource); - - Game.Renderer.EnableDepthBuffer(); - Game.Renderer.EnableStencilBuffer(); - foreach (var v in draw) - v.Voxel.DrawShadow(vr, shadowPalette.Index); - Game.Renderer.DisableStencilBuffer(); - Game.Renderer.DisableDepthBuffer(); - - Game.Renderer.EnableDepthBuffer(); - foreach (var v in draw) - v.Voxel.Draw(vr, lightAmbientColor, lightDiffuseColor, palette.Index, normalsPalette.Index); - Game.Renderer.DisableDepthBuffer(); + 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 pxOrigin = wr.ScreenPosition(pos); - - // Correct for bogus light source definition - var shadowTransform = Util.MakeFloatMatrix(new WRot(new WAngle(256) - lightSource.Pitch, - WAngle.Zero, lightSource.Yaw + new WAngle(512)).AsMatrix()); - - var invShadowTransform = Util.MatrixInverse(shadowTransform); var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix()); - // TODO: Generalize this once we support sloped terrain - var groundNormal = new float[] {0,0,1,1}; - var groundPos = new float[] {0, 0, 0.5f*(wr.ScreenPosition(pos).Y - wr.ScreenZPosition(pos, 0)), 1}; - var shadowGroundNormal = Util.MatrixVectorMultiply(shadowTransform, groundNormal); - var shadowGroundPos = Util.MatrixVectorMultiply(shadowTransform, groundPos); - - // 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 draw) { var bounds = v.Voxel.Bounds(v.FrameFunc()); var worldTransform = v.RotationFunc().Reverse().Aggregate(scaleTransform, (x,y) => Util.MatrixMultiply(x, Util.MakeFloatMatrix(y.AsMatrix()))); - var worldBounds = Util.MatrixAABBMultiply(worldTransform, bounds); - var screenBounds = Util.MatrixAABBMultiply(cameraTransform, worldBounds); - - // Aggregate bounds rect var pxOffset = wr.ScreenVector(v.OffsetFunc()); var pxPos = pxOrigin + new float2(pxOffset[0], pxOffset[1]); - tl = float2.Min(tl, pxPos + new float2(screenBounds[0], screenBounds[1])); - br = float2.Max(br, pxPos + new float2(screenBounds[3], screenBounds[4])); - - // Box to render the shadow image from - var shadowBounds = Util.MatrixAABBMultiply(shadowTransform, worldBounds); - var shadowPxOffset = Util.MatrixVectorMultiply(shadowTransform, pxOffset); - - stl = float2.Min(stl, new float2(shadowPxOffset[0] + shadowBounds[0], shadowPxOffset[1] + shadowBounds[1])); - sbr = float2.Max(sbr, new float2(shadowPxOffset[0] + shadowBounds[3], shadowPxOffset[1] + shadowBounds[4])); - - // Draw voxel bounding box var screenTransform = Util.MatrixMultiply(cameraTransform, worldTransform); DrawBoundsBox(pxPos, screenTransform, bounds, Color.Yellow); } - - // Inflate rects by 1px each side to ensure rendering is within bounds - var pad = new float2(1,1); - tl -= pad; - br += pad; - stl -= pad; - sbr += pad; - - // 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 screenCorners = new float2[4]; - for (var j = 0; j < 4; j++) - { - // Project to ground plane - corners[j][2] -= (corners[j][2] - shadowGroundPos[2]) + - (corners[j][1] - shadowGroundPos[1])*shadowGroundNormal[1]/shadowGroundNormal[2] + - (corners[j][0] - shadowGroundPos[0])*shadowGroundNormal[0]/shadowGroundNormal[2]; - - // Rotate to camera-space - corners[j] = Util.MatrixVectorMultiply(shadowScreenTransform, corners[j]); - screenCorners[j] = pxOrigin + new float2(corners[j][0], corners[j][1]); - } - - // Draw transformed shadow sprite rect - var c = Color.Purple; - Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[1], screenCorners[3], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[3], screenCorners[0], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[0], screenCorners[2], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[2], screenCorners[1], c, c); - - // Draw sprite rect - Game.Renderer.WorldLineRenderer.DrawRect(tl, br, Color.Red); } + 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) { - // 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}; - var corners = new float2[8]; for (var i = 0; i < 8; i++) { diff --git a/OpenRA.Game/Graphics/VoxelRenderer.cs b/OpenRA.Game/Graphics/VoxelRenderer.cs index 2961f13322..a587aab82d 100644 --- a/OpenRA.Game/Graphics/VoxelRenderer.cs +++ b/OpenRA.Game/Graphics/VoxelRenderer.cs @@ -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 mappedBuffers; + Stack> unmappedBuffers; + List> 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(); + unmappedBuffers = new Stack>(); + doRender = new List>(); } 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 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(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; } } } diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index b7c0228611..5d46f60a08 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -87,8 +87,11 @@ namespace OpenRA.Graphics // 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; } diff --git a/OpenRA.Mods.RA/Render/RenderVoxels.cs b/OpenRA.Mods.RA/Render/RenderVoxels.cs index ecf9e20960..e18e91966a 100755 --- a/OpenRA.Mods.RA/Render/RenderVoxels.cs +++ b/OpenRA.Mods.RA/Render/RenderVoxels.cs @@ -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(); 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; diff --git a/cg/vxlshadow.fx b/cg/vxlshadow.fx deleted file mode 100644 index 43a41ec8a3..0000000000 --- a/cg/vxlshadow.fx +++ /dev/null @@ -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); - } -} \ No newline at end of file diff --git a/glsl/vxlshadow.frag b/glsl/vxlshadow.frag deleted file mode 100644 index e75b8403ae..0000000000 --- a/glsl/vxlshadow.frag +++ /dev/null @@ -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; -} diff --git a/glsl/vxlshadow.vert b/glsl/vxlshadow.vert deleted file mode 100644 index 89b8671de1..0000000000 --- a/glsl/vxlshadow.vert +++ /dev/null @@ -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); -} From 4c22193446c92834041c57a78b57e3896891dd52 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 13 Jun 2013 05:24:00 +1200 Subject: [PATCH 19/23] Remove obsolete Stencil Buffer functions. --- OpenRA.FileFormats/Graphics/IGraphicsDevice.cs | 3 --- OpenRA.Game/Graphics/Renderer.cs | 12 ------------ OpenRA.Renderer.Null/NullGraphicsDevice.cs | 3 --- OpenRA.Renderer.SdlCommon/SdlGraphics.cs | 18 ------------------ 4 files changed, 36 deletions(-) diff --git a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs index 8e44f22817..bd75998507 100755 --- a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs +++ b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs @@ -54,9 +54,6 @@ namespace OpenRA.FileFormats.Graphics void EnableDepthBuffer(); void DisableDepthBuffer(); - - void EnableStencilBuffer(); - void DisableStencilBuffer(); } public interface IVertexBuffer diff --git a/OpenRA.Game/Graphics/Renderer.cs b/OpenRA.Game/Graphics/Renderer.cs index 865d296b93..ccdbc0526c 100644 --- a/OpenRA.Game/Graphics/Renderer.cs +++ b/OpenRA.Game/Graphics/Renderer.cs @@ -206,17 +206,5 @@ namespace OpenRA.Graphics Flush(); Device.DisableDepthBuffer(); } - - public void EnableStencilBuffer() - { - Flush(); - Device.EnableStencilBuffer(); - } - - public void DisableStencilBuffer() - { - Flush(); - Device.DisableStencilBuffer(); - } } } diff --git a/OpenRA.Renderer.Null/NullGraphicsDevice.cs b/OpenRA.Renderer.Null/NullGraphicsDevice.cs index e1fbfab9a1..a390ec6b30 100644 --- a/OpenRA.Renderer.Null/NullGraphicsDevice.cs +++ b/OpenRA.Renderer.Null/NullGraphicsDevice.cs @@ -41,9 +41,6 @@ namespace OpenRA.Renderer.Null public void EnableDepthBuffer() { } public void DisableDepthBuffer() { } - public void EnableStencilBuffer() { } - public void DisableStencilBuffer() { } - public void Clear() { } public void Present() { } diff --git a/OpenRA.Renderer.SdlCommon/SdlGraphics.cs b/OpenRA.Renderer.SdlCommon/SdlGraphics.cs index 0dd415474e..5c0ed284b1 100644 --- a/OpenRA.Renderer.SdlCommon/SdlGraphics.cs +++ b/OpenRA.Renderer.SdlCommon/SdlGraphics.cs @@ -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); From 52335a37bfa2ec7dc2cf9457abeeb7dcac1502f3 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 14 Jun 2013 06:35:38 +1200 Subject: [PATCH 20/23] Allow/require renderers to enable alpha blending when needed. Fixes voxel shadow issues. --- OpenRA.FileFormats/Graphics/IGraphicsDevice.cs | 3 +++ OpenRA.Game/Graphics/LineRenderer.cs | 3 ++- OpenRA.Game/Graphics/QuadRenderer.cs | 2 ++ OpenRA.Game/Graphics/SpriteRenderer.cs | 4 ++++ OpenRA.Renderer.Gl/Shader.cs | 9 --------- OpenRA.Renderer.Null/NullGraphicsDevice.cs | 3 +++ OpenRA.Renderer.SdlCommon/SdlGraphics.cs | 14 ++++++++++++++ cg/line.fx | 2 -- cg/rgba.fx | 2 -- cg/shp.fx | 2 -- cg/vxl.fx | 2 -- 11 files changed, 28 insertions(+), 18 deletions(-) diff --git a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs index bd75998507..a59f8042bd 100755 --- a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs +++ b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs @@ -54,6 +54,9 @@ namespace OpenRA.FileFormats.Graphics void EnableDepthBuffer(); void DisableDepthBuffer(); + + void EnableAlphaBlending(); + void DisableAlphaBlending(); } public interface IVertexBuffer diff --git a/OpenRA.Game/Graphics/LineRenderer.cs b/OpenRA.Game/Graphics/LineRenderer.cs index 4f21a49f55..d2d07e39a0 100644 --- a/OpenRA.Game/Graphics/LineRenderer.cs +++ b/OpenRA.Game/Graphics/LineRenderer.cs @@ -34,6 +34,7 @@ namespace OpenRA.Graphics { if (nv > 0) { + renderer.Device.EnableAlphaBlending(); shader.Render(() => { var vb = renderer.GetTempVertexBuffer(); @@ -41,7 +42,7 @@ namespace OpenRA.Graphics renderer.SetLineWidth(LineWidth * Game.viewport.Zoom); renderer.DrawBatch(vb, 0, nv, PrimitiveType.LineList); }); - + renderer.Device.DisableAlphaBlending(); nv = 0; } } diff --git a/OpenRA.Game/Graphics/QuadRenderer.cs b/OpenRA.Game/Graphics/QuadRenderer.cs index 05140a5ac6..bce702c982 100644 --- a/OpenRA.Game/Graphics/QuadRenderer.cs +++ b/OpenRA.Game/Graphics/QuadRenderer.cs @@ -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; } diff --git a/OpenRA.Game/Graphics/SpriteRenderer.cs b/OpenRA.Game/Graphics/SpriteRenderer.cs index 14049bb4af..b8d144bc1a 100644 --- a/OpenRA.Game/Graphics/SpriteRenderer.cs +++ b/OpenRA.Game/Graphics/SpriteRenderer.cs @@ -33,12 +33,14 @@ 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; @@ -99,7 +101,9 @@ namespace OpenRA.Graphics public void DrawVertexBuffer(IVertexBuffer 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) diff --git a/OpenRA.Renderer.Gl/Shader.cs b/OpenRA.Renderer.Gl/Shader.cs index ea6db304d7..0892f364f9 100644 --- a/OpenRA.Renderer.Gl/Shader.cs +++ b/OpenRA.Renderer.Gl/Shader.cs @@ -104,7 +104,6 @@ namespace OpenRA.Renderer.Glsl ++nextTexUnit; } } - } public void Render(Action a) @@ -119,17 +118,9 @@ namespace OpenRA.Renderer.Glsl 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) diff --git a/OpenRA.Renderer.Null/NullGraphicsDevice.cs b/OpenRA.Renderer.Null/NullGraphicsDevice.cs index a390ec6b30..dfa36f21aa 100644 --- a/OpenRA.Renderer.Null/NullGraphicsDevice.cs +++ b/OpenRA.Renderer.Null/NullGraphicsDevice.cs @@ -41,6 +41,9 @@ namespace OpenRA.Renderer.Null public void EnableDepthBuffer() { } public void DisableDepthBuffer() { } + public void EnableAlphaBlending() { } + public void DisableAlphaBlending() { } + public void Clear() { } public void Present() { } diff --git a/OpenRA.Renderer.SdlCommon/SdlGraphics.cs b/OpenRA.Renderer.SdlCommon/SdlGraphics.cs index 5c0ed284b1..2fe9891249 100644 --- a/OpenRA.Renderer.SdlCommon/SdlGraphics.cs +++ b/OpenRA.Renderer.SdlCommon/SdlGraphics.cs @@ -148,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; diff --git a/cg/line.fx b/cg/line.fx index 7b9c3f6014..156f18f5c9 100644 --- a/cg/line.fx +++ b/cg/line.fx @@ -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; diff --git a/cg/rgba.fx b/cg/rgba.fx index 18d89b7d5e..ffea272583 100644 --- a/cg/rgba.fx +++ b/cg/rgba.fx @@ -39,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(); @@ -52,7 +51,6 @@ technique high_quality { technique high_quality_cg21 { pass p0 { - BlendEnable = true; DepthTestEnable = false; CullFaceEnable = false; VertexProgram = compile arbvp1 Simple_vp(); diff --git a/cg/shp.fx b/cg/shp.fx index c5ba7ae69b..8bca263e41 100644 --- a/cg/shp.fx +++ b/cg/shp.fx @@ -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(); diff --git a/cg/vxl.fx b/cg/vxl.fx index c0f8395937..efaf711432 100644 --- a/cg/vxl.fx +++ b/cg/vxl.fx @@ -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(); From 1eb04a70a5cbd9c0e92e76feedc19248975ac4ee Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 16 Jun 2013 19:10:38 +1200 Subject: [PATCH 21/23] Add TextRenderable for CashTick. Make ticks consistent. --- OpenRA.Game/Graphics/TextRenderable.cs | 57 ++++++++++++++++++++ OpenRA.Game/OpenRA.Game.csproj | 1 + OpenRA.Mods.RA/Activities/DonateSupplies.cs | 8 +-- OpenRA.Mods.RA/Activities/Sell.cs | 2 +- OpenRA.Mods.RA/CashTrickler.cs | 6 +-- OpenRA.Mods.RA/Crates/GiveCashCrateAction.cs | 2 +- OpenRA.Mods.RA/Effects/CashTick.cs | 34 +++++------- OpenRA.Mods.RA/GivesBounty.cs | 2 +- OpenRA.Mods.RA/InfiltrateForCash.cs | 3 +- OpenRA.Mods.RA/OreRefinery.cs | 2 +- 10 files changed, 81 insertions(+), 36 deletions(-) create mode 100644 OpenRA.Game/Graphics/TextRenderable.cs diff --git a/OpenRA.Game/Graphics/TextRenderable.cs b/OpenRA.Game/Graphics/TextRenderable.cs new file mode 100644 index 0000000000..cbebceb6cd --- /dev/null +++ b/OpenRA.Game/Graphics/TextRenderable.cs @@ -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); + } + } +} diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index e16d1f3b80..36357505a1 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -233,6 +233,7 @@ + diff --git a/OpenRA.Mods.RA/Activities/DonateSupplies.cs b/OpenRA.Mods.RA/Activities/DonateSupplies.cs index c140c3f89e..5d82168c76 100644 --- a/OpenRA.Mods.RA/Activities/DonateSupplies.cs +++ b/OpenRA.Mods.RA/Activities/DonateSupplies.cs @@ -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().GiveCash(payload); + var targetActor = target.Actor; + targetActor.Owner.PlayerActor.Trait().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; } diff --git a/OpenRA.Mods.RA/Activities/Sell.cs b/OpenRA.Mods.RA/Activities/Sell.cs index ddc2f78652..2222cedacf 100755 --- a/OpenRA.Mods.RA/Activities/Sell.cs +++ b/OpenRA.Mods.RA/Activities/Sell.cs @@ -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; diff --git a/OpenRA.Mods.RA/CashTrickler.cs b/OpenRA.Mods.RA/CashTrickler.cs index ca4130736b..290403c389 100644 --- a/OpenRA.Mods.RA/CashTrickler.cs +++ b/OpenRA.Mods.RA/CashTrickler.cs @@ -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))); } } } diff --git a/OpenRA.Mods.RA/Crates/GiveCashCrateAction.cs b/OpenRA.Mods.RA/Crates/GiveCashCrateAction.cs index 43ecd9770e..4d2c0c848a 100644 --- a/OpenRA.Mods.RA/Crates/GiveCashCrateAction.cs +++ b/OpenRA.Mods.RA/Crates/GiveCashCrateAction.cs @@ -34,7 +34,7 @@ namespace OpenRA.Mods.RA collector.Owner.PlayerActor.Trait().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); diff --git a/OpenRA.Mods.RA/Effects/CashTick.cs b/OpenRA.Mods.RA/Effects/CashTick.cs index ef84230ec2..a1b5998211 100644 --- a/OpenRA.Mods.RA/Effects/CashTick.cs +++ b/OpenRA.Mods.RA/Effects/CashTick.cs @@ -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 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); } } } diff --git a/OpenRA.Mods.RA/GivesBounty.cs b/OpenRA.Mods.RA/GivesBounty.cs index 0a3064308d..d4cf88cbc0 100644 --- a/OpenRA.Mods.RA/GivesBounty.cs +++ b/OpenRA.Mods.RA/GivesBounty.cs @@ -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().GiveCash(bounty); } diff --git a/OpenRA.Mods.RA/InfiltrateForCash.cs b/OpenRA.Mods.RA/InfiltrateForCash.cs index 1e7ba1c929..b0f27fa33c 100644 --- a/OpenRA.Mods.RA/InfiltrateForCash.cs +++ b/OpenRA.Mods.RA/InfiltrateForCash.cs @@ -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))); } } } diff --git a/OpenRA.Mods.RA/OreRefinery.cs b/OpenRA.Mods.RA/OreRefinery.cs index a1069c7b9f..a398b98dd8 100644 --- a/OpenRA.Mods.RA/OreRefinery.cs +++ b/OpenRA.Mods.RA/OreRefinery.cs @@ -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; } From 82059dca6dd982377cc5bd3049d3b4120e1fa67e Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 16 Jun 2013 19:25:05 +1200 Subject: [PATCH 22/23] Add BeamRenderable for lasers. --- OpenRA.Game/Graphics/BeamRenderable.cs | 68 ++++++++++++++++++++++++++ OpenRA.Game/OpenRA.Game.csproj | 1 + OpenRA.Mods.RA/Effects/LaserZap.cs | 31 ++++-------- 3 files changed, 78 insertions(+), 22 deletions(-) create mode 100644 OpenRA.Game/Graphics/BeamRenderable.cs diff --git a/OpenRA.Game/Graphics/BeamRenderable.cs b/OpenRA.Game/Graphics/BeamRenderable.cs new file mode 100644 index 0000000000..49bf882792 --- /dev/null +++ b/OpenRA.Game/Graphics/BeamRenderable.cs @@ -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) {} + } +} diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 36357505a1..d2f7dea76d 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -234,6 +234,7 @@ + diff --git a/OpenRA.Mods.RA/Effects/LaserZap.cs b/OpenRA.Mods.RA/Effects/LaserZap.cs index d98ff53a68..8514f80a97 100755 --- a/OpenRA.Mods.RA/Effects/LaserZap.cs +++ b/OpenRA.Mods.RA/Effects/LaserZap.cs @@ -78,34 +78,21 @@ namespace OpenRA.Mods.RA.Effects public IEnumerable 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; - - // TODO: Push this into a BeamRenderable, with support for refraction/ripples on sonic weapons - var lineWidth = wlr.LineWidth; - if (lineWidth != info.BeamWidth) - { - wlr.Flush(); - wlr.LineWidth = info.BeamWidth; - } - - wlr.DrawLine(src.ToFloat2(), dest.ToFloat2(), rc, rc); - - if (lineWidth != info.BeamWidth) - { - wlr.Flush(); - wlr.LineWidth = lineWidth; - } } } } From e229194b62a54b54b4d56848eb489e8414894b95 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 16 Jun 2013 19:50:52 +1200 Subject: [PATCH 23/23] Change extension check to EXT_fbo. --- OpenRA.Renderer.Cg/GraphicsDevice.cs | 2 +- OpenRA.Renderer.Gl/GraphicsDevice.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenRA.Renderer.Cg/GraphicsDevice.cs b/OpenRA.Renderer.Cg/GraphicsDevice.cs index 656dca6136..473c41e483 100755 --- a/OpenRA.Renderer.Cg/GraphicsDevice.cs +++ b/OpenRA.Renderer.Cg/GraphicsDevice.cs @@ -36,7 +36,7 @@ namespace OpenRA.Renderer.Cg "GL_ARB_vertex_program", "GL_ARB_fragment_program", "GL_ARB_vertex_buffer_object", - "GL_ARB_framebuffer_object" + "GL_EXT_framebuffer_object" }; internal IntPtr cgContext; diff --git a/OpenRA.Renderer.Gl/GraphicsDevice.cs b/OpenRA.Renderer.Gl/GraphicsDevice.cs index a2ffc688bc..d2e73d5ae7 100755 --- a/OpenRA.Renderer.Gl/GraphicsDevice.cs +++ b/OpenRA.Renderer.Gl/GraphicsDevice.cs @@ -35,7 +35,7 @@ namespace OpenRA.Renderer.Glsl "GL_ARB_vertex_shader", "GL_ARB_fragment_shader", "GL_ARB_vertex_buffer_object", - "GL_ARB_framebuffer_object" + "GL_EXT_framebuffer_object" }; public GraphicsDevice(Size size, WindowMode window)