From a8dda39a72ed3841cbcd90b58eb9d84280718f95 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 4 Apr 2016 16:07:08 -0400 Subject: [PATCH 1/4] Introduce float3 type. --- OpenRA.Game/FieldLoader.cs | 20 +++++++++ OpenRA.Game/Graphics/IGraphicsDevice.cs | 1 + OpenRA.Game/Graphics/Vertex.cs | 4 +- OpenRA.Game/OpenRA.Game.csproj | 1 + OpenRA.Game/Primitives/float3.cs | 54 +++++++++++++++++++++++++ OpenRA.Platforms.Default/OpenGL.cs | 4 ++ OpenRA.Platforms.Default/Shader.cs | 11 +++++ 7 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 OpenRA.Game/Primitives/float3.cs diff --git a/OpenRA.Game/FieldLoader.cs b/OpenRA.Game/FieldLoader.cs index 1d081f6d5a..5fff2b49ea 100644 --- a/OpenRA.Game/FieldLoader.cs +++ b/OpenRA.Game/FieldLoader.cs @@ -508,6 +508,26 @@ namespace OpenRA return InvalidValueAction(value, fieldType, fieldName); } + else if (fieldType == typeof(float3)) + { + if (value != null) + { + var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + float x = 0; + float y = 0; + float z = 0; + float.TryParse(parts[0], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out x); + float.TryParse(parts[1], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out y); + + // z component is optional for compatibility with older float2 definitions + if (parts.Length > 2) + float.TryParse(parts[2], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out z); + + return new float3(x, y, z); + } + + return InvalidValueAction(value, fieldType, fieldName); + } else if (fieldType == typeof(Rectangle)) { if (value != null) diff --git a/OpenRA.Game/Graphics/IGraphicsDevice.cs b/OpenRA.Game/Graphics/IGraphicsDevice.cs index b21154faab..27c41814b8 100644 --- a/OpenRA.Game/Graphics/IGraphicsDevice.cs +++ b/OpenRA.Game/Graphics/IGraphicsDevice.cs @@ -95,6 +95,7 @@ namespace OpenRA void SetBool(string name, bool value); void SetVec(string name, float x); void SetVec(string name, float x, float y); + void SetVec(string name, float x, float y, float z); void SetVec(string name, float[] vec, int length); void SetTexture(string param, ITexture texture); void SetMatrix(string param, float[] mtx); diff --git a/OpenRA.Game/Graphics/Vertex.cs b/OpenRA.Game/Graphics/Vertex.cs index f514177119..0d8df05e3a 100644 --- a/OpenRA.Game/Graphics/Vertex.cs +++ b/OpenRA.Game/Graphics/Vertex.cs @@ -18,8 +18,8 @@ namespace OpenRA.Graphics { public readonly float X, Y, Z, U, V, P, C; - public Vertex(float2 xy, float u, float v, float p, float c) - : this(xy.X, xy.Y, 0, u, v, p, c) { } + public Vertex(float3 xyz, float u, float v, float p, float c) + : this(xyz.X, xyz.Y, xyz.Z, u, v, p, c) { } public Vertex(float[] xyz, float u, float v, float p, float c) : this(xyz[0], xyz[1], xyz[2], u, v, p, c) { } diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index d75b30c3ee..dd968f003f 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -254,6 +254,7 @@ + diff --git a/OpenRA.Game/Primitives/float3.cs b/OpenRA.Game/Primitives/float3.cs new file mode 100644 index 0000000000..3012076471 --- /dev/null +++ b/OpenRA.Game/Primitives/float3.cs @@ -0,0 +1,54 @@ +#region Copyright & License Information +/* + * Copyright 2007-2016 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, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace OpenRA +{ + [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:ElementMustBeginWithUpperCaseLetter", Justification = "Mimic a built-in type alias.")] + [StructLayout(LayoutKind.Sequential)] + public struct float3 + { + public readonly float X, Y, Z; + public float2 XY { get { return new float2(X, Y); } } + + public float3(float x, float y, float z) { X = x; Y = y; Z = z; } + public float3(float2 xy, float z) { X = xy.X; Y = xy.Y; Z = z; } + + public static implicit operator float3(int2 src) { return new float3(src.X, src.Y, 0); } + public static implicit operator float3(float2 src) { return new float3(src.X, src.Y, 0); } + + public static float3 operator +(float3 a, float3 b) { return new float3(a.X + b.X, a.Y + b.Y, a.Z + b.Z); } + public static float3 operator -(float3 a, float3 b) { return new float3(a.X - b.X, a.Y - b.Y, a.Z - b.Z); } + public static float3 operator -(float3 a) { return new float3(-a.X, -a.Y, -a.Z); } + public static float3 operator *(float3 a, float3 b) { return new float3(a.X * b.X, a.Y * b.Y, a.Z * b.Z); } + public static float3 operator *(float a, float3 b) { return new float3(a * b.X, a * b.Y, a * b.Z); } + public static float3 operator /(float3 a, float3 b) { return new float3(a.X / b.X, a.Y / b.Y, a.Z / b.Z); } + public static float3 operator /(float3 a, float b) { return new float3(a.X / b, a.Y / b, a.Z / b); } + + public static bool operator ==(float3 me, float3 other) { return me.X == other.X && me.Y == other.Y && me.Z == other.Z; } + public static bool operator !=(float3 me, float3 other) { return !(me == other); } + public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); } + + public override bool Equals(object obj) + { + var o = obj as float3?; + return o != null && o == this; + } + + public override string ToString() { return "{0},{1},{2}".F(X, Y, Z); } + + public static readonly float3 Zero = new float3(0, 0, 0); + } +} diff --git a/OpenRA.Platforms.Default/OpenGL.cs b/OpenRA.Platforms.Default/OpenGL.cs index 5d0ed06b51..b3cf3dc251 100644 --- a/OpenRA.Platforms.Default/OpenGL.cs +++ b/OpenRA.Platforms.Default/OpenGL.cs @@ -215,6 +215,9 @@ namespace OpenRA.Platforms.Default public delegate void Uniform2f(int location, float v0, float v1); public static Uniform2f glUniform2f { get; private set; } + public delegate void Uniform3f(int location, float v0, float v1, float v2); + public static Uniform3f glUniform3f { get; private set; } + public delegate void Uniform1fv(int location, int count, IntPtr value); public static Uniform1fv glUniform1fv { get; private set; } @@ -395,6 +398,7 @@ namespace OpenRA.Platforms.Default glUniform1i = Bind("glUniform1i"); glUniform1f = Bind("glUniform1f"); glUniform2f = Bind("glUniform2f"); + glUniform3f = Bind("glUniform3f"); glUniform1fv = Bind("glUniform1fv"); glUniform2fv = Bind("glUniform2fv"); glUniform3fv = Bind("glUniform3fv"); diff --git a/OpenRA.Platforms.Default/Shader.cs b/OpenRA.Platforms.Default/Shader.cs index 557dcc3127..9facf102f4 100644 --- a/OpenRA.Platforms.Default/Shader.cs +++ b/OpenRA.Platforms.Default/Shader.cs @@ -189,6 +189,17 @@ namespace OpenRA.Platforms.Default OpenGL.CheckGLError(); } + public void SetVec(string name, float x, float y, float z) + { + VerifyThreadAffinity(); + OpenGL.glUseProgram(program); + OpenGL.CheckGLError(); + var param = OpenGL.glGetUniformLocation(program, name); + OpenGL.CheckGLError(); + OpenGL.glUniform3f(param, x, y, z); + OpenGL.CheckGLError(); + } + public void SetVec(string name, float[] vec, int length) { VerifyThreadAffinity(); From 19a276da2b80ec848f65bc7c01c9d1c5945fbdb8 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 1 Oct 2015 18:55:54 +0100 Subject: [PATCH 2/4] Replace float[] with float3 in voxel renderer. --- OpenRA.Game/Graphics/Vertex.cs | 3 --- OpenRA.Game/Graphics/VoxelLoader.cs | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/OpenRA.Game/Graphics/Vertex.cs b/OpenRA.Game/Graphics/Vertex.cs index 0d8df05e3a..206e50051d 100644 --- a/OpenRA.Game/Graphics/Vertex.cs +++ b/OpenRA.Game/Graphics/Vertex.cs @@ -21,9 +21,6 @@ namespace OpenRA.Graphics public Vertex(float3 xyz, float u, float v, float p, float c) : this(xyz.X, xyz.Y, xyz.Z, u, v, p, c) { } - public Vertex(float[] xyz, float u, float v, float p, float c) - : this(xyz[0], xyz[1], xyz[2], u, v, p, c) { } - public Vertex(float x, float y, float z, float u, float v, float p, float c) { X = x; Y = y; Z = z; diff --git a/OpenRA.Game/Graphics/VoxelLoader.cs b/OpenRA.Game/Graphics/VoxelLoader.cs index 7336cecc3c..195ba9fe27 100644 --- a/OpenRA.Game/Graphics/VoxelLoader.cs +++ b/OpenRA.Game/Graphics/VoxelLoader.cs @@ -71,7 +71,7 @@ namespace OpenRA.Graphics sheetBuilder = CreateSheetBuilder(); } - Vertex[] GenerateSlicePlane(int su, int sv, Func first, Func second, Func coord) + Vertex[] GenerateSlicePlane(int su, int sv, Func first, Func second, Func coord) { var colors = new byte[su * sv]; var normals = new byte[su * sv]; @@ -158,21 +158,21 @@ namespace OpenRA.Graphics yield return GenerateSlicePlane(l.Size[1], l.Size[2], (u, v) => get(x, u, v), (u, v) => get(x - 1, u, v), - (u, v) => new float[] { x, u, v }); + (u, v) => new float3(x, u, v)); for (var y = 0; y <= l.Size[1]; y++) if (yPlanes[y]) yield return GenerateSlicePlane(l.Size[0], l.Size[2], (u, v) => get(u, y, v), (u, v) => get(u, y - 1, v), - (u, v) => new float[] { u, y, v }); + (u, v) => new float3(u, y, v)); for (var z = 0; z <= l.Size[2]; z++) if (zPlanes[z]) yield return GenerateSlicePlane(l.Size[0], l.Size[1], (u, v) => get(u, v, z), (u, v) => get(u, v, z - 1), - (u, v) => new float[] { u, v, z }); + (u, v) => new float3(u, v, z)); } public VoxelRenderData GenerateRenderData(VxlLimb l) From c00ef40151892fdd20d67a38edf912617b66ae9f Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 4 Apr 2016 16:16:29 -0400 Subject: [PATCH 3/4] Add float3 support to TerrainSpriteLayer. --- OpenRA.Game/Graphics/TerrainSpriteLayer.cs | 9 ++++++--- OpenRA.Game/Graphics/Util.cs | 10 +++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs index fc680bef50..77b79eac27 100644 --- a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs +++ b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs @@ -68,12 +68,15 @@ namespace OpenRA.Graphics public void Update(CPos cell, Sprite sprite) { - var pos = sprite == null ? float2.Zero : + var xy = sprite == null ? float2.Zero : worldRenderer.ScreenPosition(map.CenterOfCell(cell)) + sprite.Offset - 0.5f * sprite.Size; - Update(cell.ToMPos(map.Grid.Type), sprite, pos); + + // TODO: Deal with sprite z offsets + var z = worldRenderer.ScreenZPosition(map.CenterOfCell(cell), 0); + Update(cell.ToMPos(map.Grid.Type), sprite, new float3(xy.X, xy.Y, z)); } - public void Update(MPos uv, Sprite sprite, float2 pos) + public void Update(MPos uv, Sprite sprite, float3 pos) { if (sprite != null) { diff --git a/OpenRA.Game/Graphics/Util.cs b/OpenRA.Game/Graphics/Util.cs index daa84a3016..2f6bf48559 100644 --- a/OpenRA.Game/Graphics/Util.cs +++ b/OpenRA.Game/Graphics/Util.cs @@ -21,15 +21,15 @@ namespace OpenRA.Graphics static readonly int[] ChannelMasks = { 2, 1, 0, 3 }; static readonly float[] ChannelSelect = { 0.2f, 0.4f, 0.6f, 0.8f }; - public static void FastCreateQuad(Vertex[] vertices, float2 o, Sprite r, float paletteTextureIndex, int nv, float2 size) + public static void FastCreateQuad(Vertex[] vertices, float3 o, Sprite r, float paletteTextureIndex, int nv, float3 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); + var b = new float3(o.X + size.X, o.Y, o.Z); + var c = new float3(o.X + size.X, o.Y + size.Y, o.Z + size.Z); + var d = new float3(o.X, o.Y + size.Y, o.Z + size.Z); FastCreateQuad(vertices, o, b, c, d, r, paletteTextureIndex, nv); } - public static void FastCreateQuad(Vertex[] vertices, float2 a, float2 b, float2 c, float2 d, Sprite r, float paletteTextureIndex, int nv) + public static void FastCreateQuad(Vertex[] vertices, float3 a, float3 b, float3 c, float3 d, Sprite r, float paletteTextureIndex, int nv) { var attribC = ChannelSelect[(int)r.Channel]; if (r.Sheet.Type == SheetType.DualIndexed) From dabea59b7d83aec97aea6020bf56919f918b3f3d Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 1 Oct 2015 18:55:54 +0100 Subject: [PATCH 4/4] Pass depth scale factors to vertex shaders. --- OpenRA.Game/Game.cs | 3 +++ OpenRA.Game/Graphics/SpriteRenderer.cs | 14 ++++++++++---- OpenRA.Game/Map/MapGrid.cs | 2 ++ OpenRA.Game/Renderer.cs | 25 +++++++++++++++++++++---- glsl/rgba.vert | 7 +++---- glsl/shp.frag | 22 +++++++++++++++++++--- glsl/shp.vert | 7 +++---- 7 files changed, 61 insertions(+), 19 deletions(-) diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 64031fe1e3..054a142a7b 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -359,6 +359,9 @@ namespace OpenRA ModData.InitializeLoaders(ModData.DefaultFileSystem); Renderer.InitializeFonts(ModData); + var grid = ModData.Manifest.Contains() ? ModData.Manifest.Get() : null; + Renderer.InitializeDepthBuffer(grid); + if (Cursor != null) Cursor.Dispose(); diff --git a/OpenRA.Game/Graphics/SpriteRenderer.cs b/OpenRA.Game/Graphics/SpriteRenderer.cs index 41c2f2a707..70580db08b 100644 --- a/OpenRA.Game/Graphics/SpriteRenderer.cs +++ b/OpenRA.Game/Graphics/SpriteRenderer.cs @@ -114,11 +114,17 @@ namespace OpenRA.Graphics shader.SetTexture("Palette", palette); } - public void SetViewportParams(Size screen, float zoom, int2 scroll) + public void SetViewportParams(Size screen, float depthScale, float depthOffset, float zoom, int2 scroll) { - shader.SetVec("Scroll", scroll.X, scroll.Y); - shader.SetVec("r1", zoom * 2f / screen.Width, -zoom * 2f / screen.Height); - shader.SetVec("r2", -1, 1); + shader.SetVec("Scroll", scroll.X, scroll.Y, scroll.Y); + shader.SetVec("r1", + zoom * 2f / screen.Width, + -zoom * 2f / screen.Height, + -depthScale * zoom / screen.Height); + shader.SetVec("r2", -1, 1, 1 - depthOffset); + + // Texture index is sampled as a float, so convert to pixels then scale + shader.SetVec("DepthTextureScale", 128 * depthScale * zoom / screen.Height); } public void SetDepthPreviewEnabled(bool enabled) diff --git a/OpenRA.Game/Map/MapGrid.cs b/OpenRA.Game/Map/MapGrid.cs index 05cdf4f63f..651288d3a7 100644 --- a/OpenRA.Game/Map/MapGrid.cs +++ b/OpenRA.Game/Map/MapGrid.cs @@ -28,6 +28,8 @@ namespace OpenRA public readonly int MaximumTileSearchRange = 50; + public readonly bool EnableDepthBuffer = false; + public readonly WVec[] SubCellOffsets = { new WVec(0, 0, 0), // full cell - index 0 diff --git a/OpenRA.Game/Renderer.cs b/OpenRA.Game/Renderer.cs index 4690dae2e4..266fa714d7 100644 --- a/OpenRA.Game/Renderer.cs +++ b/OpenRA.Game/Renderer.cs @@ -40,6 +40,9 @@ namespace OpenRA SheetBuilder fontSheetBuilder; + float depthScale; + float depthOffset; + Size? lastResolution; int2? lastScroll; float? lastZoom; @@ -102,6 +105,20 @@ namespace OpenRA } } + public void InitializeDepthBuffer(MapGrid mapGrid) + { + // The depth buffer needs to be initialized with enough range to cover: + // - the height of the screen + // - the z-offset of tiles from MaxTerrainHeight below the bottom of the screen (pushed into view) + // - additional z-offset from actors on top of MaxTerrainHeight terrain + // - a small margin so that tiles rendered partially above the top edge of the screen aren't pushed behind the clip plane + // We need an offset of mapGrid.MaximumTerrainHeight * mapGrid.TileSize.Height / 2 to cover the terrain height + // and choose to use mapGrid.MaximumTerrainHeight * mapGrid.TileSize.Height / 4 for each of the actor and top-edge cases + this.depthScale = mapGrid == null || !mapGrid.EnableDepthBuffer ? 0 : + (float)Resolution.Height / (Resolution.Height + mapGrid.TileSize.Height * mapGrid.MaximumTerrainHeight); + this.depthOffset = this.depthScale / 2; + } + public void BeginFrame(int2 scroll, float zoom) { Device.Clear(); @@ -115,8 +132,8 @@ namespace OpenRA if (resolutionChanged) { lastResolution = Resolution; - RgbaSpriteRenderer.SetViewportParams(Resolution, 1f, int2.Zero); - SpriteRenderer.SetViewportParams(Resolution, 1f, int2.Zero); + RgbaSpriteRenderer.SetViewportParams(Resolution, 0f, 0f, 1f, int2.Zero); + SpriteRenderer.SetViewportParams(Resolution, 0f, 0f, 1f, int2.Zero); RgbaColorRenderer.SetViewportParams(Resolution, 1f, int2.Zero); } @@ -125,8 +142,8 @@ namespace OpenRA { lastScroll = scroll; lastZoom = zoom; - WorldRgbaSpriteRenderer.SetViewportParams(Resolution, zoom, scroll); - WorldSpriteRenderer.SetViewportParams(Resolution, zoom, scroll); + WorldRgbaSpriteRenderer.SetViewportParams(Resolution, depthScale, depthOffset, zoom, scroll); + WorldSpriteRenderer.SetViewportParams(Resolution, depthScale, depthOffset, zoom, scroll); WorldVoxelRenderer.SetViewportParams(Resolution, zoom, scroll); WorldRgbaColorRenderer.SetViewportParams(Resolution, zoom, scroll); } diff --git a/glsl/rgba.vert b/glsl/rgba.vert index f5d8177526..7b7e5b7958 100644 --- a/glsl/rgba.vert +++ b/glsl/rgba.vert @@ -1,5 +1,5 @@ -uniform vec2 Scroll; -uniform vec2 r1, r2; +uniform vec3 Scroll; +uniform vec3 r1, r2; attribute vec4 aVertexPosition; attribute vec4 aVertexTexCoord; @@ -7,7 +7,6 @@ varying vec4 vTexCoord; void main() { - vec2 p = (aVertexPosition.xy - Scroll.xy)*r1 + r2; - gl_Position = vec4(p.x,p.y,0,1); + gl_Position = vec4((aVertexPosition.xyz - Scroll.xyz) * r1 + r2, 1); vTexCoord = aVertexTexCoord; } diff --git a/glsl/shp.frag b/glsl/shp.frag index c7e1c1189f..df27bb2284 100644 --- a/glsl/shp.frag +++ b/glsl/shp.frag @@ -1,6 +1,7 @@ uniform sampler2D DiffuseTexture, Palette; uniform bool EnableDepthPreview; +uniform float DepthTextureScale; varying vec4 vTexCoord; varying vec4 vChannelMask; @@ -17,9 +18,24 @@ void main() discard; if (EnableDepthPreview && length(vDepthMask) > 0.0) - { - float depth = dot(x, vDepthMask); - gl_FragColor = vec4(depth, depth, depth, 1); + { + if (abs(DepthTextureScale) > 0.0) + { + // Preview vertex aware depth + float depth = gl_FragCoord.z + DepthTextureScale * dot(x, vDepthMask); + + // Convert to window coords + depth = 0.5 * depth + 0.5; + + // Front of the depth buffer is at 0, but we want to render it as bright + gl_FragColor = vec4(vec3(1.0 - depth), 1.0); + } + else + { + // Preview boring sprite-only depth + float depth = dot(x, vDepthMask); + gl_FragColor = vec4(depth, depth, depth, 1.0); + } } else gl_FragColor = c; diff --git a/glsl/shp.vert b/glsl/shp.vert index c59498a96b..8e46666dfa 100644 --- a/glsl/shp.vert +++ b/glsl/shp.vert @@ -1,5 +1,5 @@ -uniform vec2 Scroll; -uniform vec2 r1,r2; // matrix elements +uniform vec3 Scroll; +uniform vec3 r1, r2; attribute vec4 aVertexPosition; attribute vec4 aVertexTexCoord; @@ -36,8 +36,7 @@ vec4 DecodeDepthChannelMask(float x) void main() { - vec2 p = (aVertexPosition.xy - Scroll.xy) * r1 + r2; - gl_Position = vec4(p.x,p.y,0,1); + gl_Position = vec4((aVertexPosition.xyz - Scroll.xyz) * r1 + r2, 1); vTexCoord = aVertexTexCoord; vChannelMask = DecodeChannelMask(aVertexTexCoord.w); vDepthMask = DecodeDepthChannelMask(aVertexTexCoord.w);