From eff46cf40dcbdf5fee0511741a476d59e672bbdc Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 30 May 2013 20:11:26 +1200 Subject: [PATCH 01/15] Add a Name field to Armament for other traits to reference. --- OpenRA.Mods.RA/Armament.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenRA.Mods.RA/Armament.cs b/OpenRA.Mods.RA/Armament.cs index 95482d0bf9..3039dcb224 100755 --- a/OpenRA.Mods.RA/Armament.cs +++ b/OpenRA.Mods.RA/Armament.cs @@ -27,12 +27,12 @@ namespace OpenRA.Mods.RA [Desc("Allows you to attach weapons to the unit (use @IdentifierSuffix for > 1)")] public class ArmamentInfo : ITraitInfo, Requires { + public readonly string Name = "primary"; + [WeaponReference] [Desc("Has to be defined here and in weapons.yaml.")] public readonly string Weapon = null; public readonly string Turret = "primary"; - [Desc("Move the turret backwards when firing.")] - public readonly int LegacyRecoil = 0; [Desc("Time (in frames) until the weapon can fire again.")] public readonly int FireDelay = 0; From fb3e776cb9a6af88b622f0e6d9cf442c58408942 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 9 Mar 2013 03:13:32 +1300 Subject: [PATCH 02/15] Add matrix helpers to Graphics.Util. --- OpenRA.Game/Graphics/Util.cs | 176 +++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/OpenRA.Game/Graphics/Util.cs b/OpenRA.Game/Graphics/Util.cs index d0efeadc3e..1d08371ddb 100644 --- a/OpenRA.Game/Graphics/Util.cs +++ b/OpenRA.Game/Graphics/Util.cs @@ -53,5 +53,181 @@ namespace OpenRA.Graphics destOffset += destSkip; } } + + public static float[] IdentityMatrix() + { + return Exts.MakeArray(16, j => (j % 5 == 0) ? 1.0f : 0); + } + + public static float[] ScaleMatrix(float sx, float sy, float sz) + { + var mtx = IdentityMatrix(); + mtx[0] = sx; + mtx[5] = sy; + mtx[10] = sz; + return mtx; + } + + public static float[] TranslationMatrix(float x, float y, float z) + { + var mtx = IdentityMatrix(); + mtx[12] = x; + mtx[13] = y; + mtx[14] = z; + return mtx; + } + + public static float[] MatrixMultiply(float[] lhs, float[] rhs) + { + var mtx = new float[16]; + for (var i = 0; i < 4; i++) + for (var j = 0; j < 4; j++) + { + mtx[4*i + j] = 0; + for (var k = 0; k < 4; k++) + mtx[4*i + j] += lhs[4*k + j]*rhs[4*i + k]; + } + + return mtx; + } + + public static float[] MatrixVectorMultiply(float[] mtx, float[] vec) + { + var ret = new float[4]; + for (var j = 0; j < 4; j++) + { + ret[j] = 0; + for (var k = 0; k < 4; k++) + ret[j] += mtx[4*k + j]*vec[k]; + } + + return ret; + } + + public static float[] MatrixInverse(float[] m) + { + var mtx = new float[16]; + + mtx[0] = m[5]*m[10]*m[15] - + m[5]*m[11]*m[14] - + m[9]*m[6]*m[15] + + m[9]*m[7]*m[14] + + m[13]*m[6]*m[11] - + m[13]*m[7]*m[10]; + + mtx[4] = -m[4]*m[10]*m[15] + + m[4]*m[11]*m[14] + + m[8]*m[6]*m[15] - + m[8]*m[7]*m[14] - + m[12]*m[6]*m[11] + + m[12]*m[7]*m[10]; + + mtx[8] = m[4]*m[9]*m[15] - + m[4]*m[11]*m[13] - + m[8]*m[5]*m[15] + + m[8]*m[7]*m[13] + + m[12]*m[5]*m[11] - + m[12]*m[7]*m[9]; + + mtx[12] = -m[4]*m[9]*m[14] + + m[4]*m[10]*m[13] + + m[8]*m[5]*m[14] - + m[8]*m[6]*m[13] - + m[12]*m[5]*m[10] + + m[12]*m[6]*m[9]; + + mtx[1] = -m[1]*m[10]*m[15] + + m[1]*m[11]*m[14] + + m[9]*m[2]*m[15] - + m[9]*m[3]*m[14] - + m[13]*m[2]*m[11] + + m[13]*m[3]*m[10]; + + mtx[5] = m[0]*m[10]*m[15] - + m[0]*m[11]*m[14] - + m[8]*m[2]*m[15] + + m[8]*m[3]*m[14] + + m[12]*m[2]*m[11] - + m[12]*m[3]*m[10]; + + mtx[9] = -m[0]*m[9]*m[15] + + m[0]*m[11]*m[13] + + m[8]*m[1]*m[15] - + m[8]*m[3]*m[13] - + m[12]*m[1]*m[11] + + m[12]*m[3]*m[9]; + + mtx[13] = m[0]*m[9]*m[14] - + m[0]*m[10]*m[13] - + m[8]*m[1]*m[14] + + m[8]*m[2]*m[13] + + m[12]*m[1]*m[10] - + m[12]*m[2]*m[9]; + + mtx[2] = m[1]*m[6]*m[15] - + m[1]*m[7]*m[14] - + m[5]*m[2]*m[15] + + m[5]*m[3]*m[14] + + m[13]*m[2]*m[7] - + m[13]*m[3]*m[6]; + + mtx[6] = -m[0]*m[6]*m[15] + + m[0]*m[7]*m[14] + + m[4]*m[2]*m[15] - + m[4]*m[3]*m[14] - + m[12]*m[2]*m[7] + + m[12]*m[3]*m[6]; + + mtx[10] = m[0]*m[5]*m[15] - + m[0]*m[7]*m[13] - + m[4]*m[1]*m[15] + + m[4]*m[3]*m[13] + + m[12]*m[1]*m[7] - + m[12]*m[3]*m[5]; + + mtx[14] = -m[0]*m[5]*m[14] + + m[0]*m[6]*m[13] + + m[4]*m[1]*m[14] - + m[4]*m[2]*m[13] - + m[12]*m[1]*m[6] + + m[12]*m[2]*m[5]; + + mtx[3] = -m[1]*m[6]*m[11] + + m[1]*m[7]*m[10] + + m[5]*m[2]*m[11] - + m[5]*m[3]*m[10] - + m[9]*m[2]*m[7] + + m[9]*m[3]*m[6]; + + mtx[7] = m[0]*m[6]*m[11] - + m[0]*m[7]*m[10] - + m[4]*m[2]*m[11] + + m[4]*m[3]*m[10] + + m[8]*m[2]*m[7] - + m[8]*m[3]*m[6]; + + mtx[11] = -m[0]*m[5]*m[11] + + m[0]*m[7]*m[9] + + m[4]*m[1]*m[11] - + m[4]*m[3]*m[9] - + m[8]*m[1]*m[7] + + m[8]*m[3]*m[5]; + + mtx[15] = m[0]*m[5]*m[10] - + m[0]*m[6]*m[9] - + m[4]*m[1]*m[10] + + m[4]*m[2]*m[9] + + m[8]*m[1]*m[6] - + m[8]*m[2]*m[5]; + + var det = m[0]*mtx[0] + m[1]*mtx[4] + m[2]*mtx[8] + m[3]*mtx[12]; + if (det == 0) + return null; + + for (var i = 0; i < 16; i++) + mtx[i] *= 1/det; + + return mtx; + } } } From 064938378f4fd5ebe9db8d55a515f07945e797e7 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 22 Feb 2013 23:05:13 +1300 Subject: [PATCH 03/15] Add renderer support for matrix uniforms. --- OpenRA.FileFormats/Graphics/IGraphicsDevice.cs | 1 + OpenRA.Renderer.Cg/Shader.cs | 10 ++++++++++ OpenRA.Renderer.Gl/Shader.cs | 13 +++++++++++++ OpenRA.Renderer.Null/NullGraphicsDevice.cs | 1 + 4 files changed, 25 insertions(+) diff --git a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs index 12231dadca..620f633011 100755 --- a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs +++ b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs @@ -62,6 +62,7 @@ namespace OpenRA.FileFormats.Graphics { void SetVec(string name, float x, float y); void SetTexture(string param, ITexture texture); + void SetMatrix(string param, float[] mtx); void Render(Action a); } diff --git a/OpenRA.Renderer.Cg/Shader.cs b/OpenRA.Renderer.Cg/Shader.cs index 59aac51084..88b341adce 100644 --- a/OpenRA.Renderer.Cg/Shader.cs +++ b/OpenRA.Renderer.Cg/Shader.cs @@ -81,5 +81,15 @@ namespace OpenRA.Renderer.Cg if (param != IntPtr.Zero) Tao.Cg.CgGl.cgGLSetParameter2f(param, x, y); } + + public void SetMatrix(string name, float[] mtx) + { + if (mtx.Length != 16) + throw new InvalidDataException("Invalid 4x4 matrix"); + + var param = Tao.Cg.Cg.cgGetNamedEffectParameter(effect, name); + if (param != IntPtr.Zero) + Tao.Cg.CgGl.cgGLSetMatrixParameterfr(param, mtx); + } } } diff --git a/OpenRA.Renderer.Gl/Shader.cs b/OpenRA.Renderer.Gl/Shader.cs index 069f15d536..9df56e1504 100644 --- a/OpenRA.Renderer.Gl/Shader.cs +++ b/OpenRA.Renderer.Gl/Shader.cs @@ -150,5 +150,18 @@ namespace OpenRA.Renderer.Glsl Gl.glUniform2fARB(param,x,y); ErrorHandler.CheckGlError(); } + + public void SetMatrix(string name, float[] mtx) + { + if (mtx.Length != 16) + throw new InvalidDataException("Invalid 4x4 matrix"); + + Gl.glUseProgramObjectARB(program); + ErrorHandler.CheckGlError(); + int param = Gl.glGetUniformLocationARB(program, name); + ErrorHandler.CheckGlError(); + Gl.glUniformMatrix4fv(param, 1, Gl.GL_FALSE, mtx); + ErrorHandler.CheckGlError(); + } } } diff --git a/OpenRA.Renderer.Null/NullGraphicsDevice.cs b/OpenRA.Renderer.Null/NullGraphicsDevice.cs index 742ac7f6b9..79497ead37 100644 --- a/OpenRA.Renderer.Null/NullGraphicsDevice.cs +++ b/OpenRA.Renderer.Null/NullGraphicsDevice.cs @@ -60,6 +60,7 @@ namespace OpenRA.Renderer.Null { public void SetVec(string name, float x, float y) { } public void SetTexture(string param, ITexture texture) { } + public void SetMatrix(string param, float[] mtx) { } public void Commit() { } public void Render(Action a) { } } From 9566385aac8bdc298f5afb9e0809a8e98483f89d Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 2 Mar 2013 10:36:58 +1300 Subject: [PATCH 04/15] Add renderer support for additional vec* uniforms. --- .../Graphics/IGraphicsDevice.cs | 2 ++ OpenRA.Renderer.Cg/Shader.cs | 23 +++++++++++++++++ OpenRA.Renderer.Gl/Shader.cs | 25 +++++++++++++++++++ OpenRA.Renderer.Null/NullGraphicsDevice.cs | 2 ++ 4 files changed, 52 insertions(+) diff --git a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs index 620f633011..bb57b64c1c 100755 --- a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs +++ b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs @@ -60,7 +60,9 @@ namespace OpenRA.FileFormats.Graphics public interface IShader { + void SetVec(string name, float x); void SetVec(string name, float x, float y); + void SetVec(string name, float[] vec, int length); void SetTexture(string param, ITexture texture); void SetMatrix(string param, float[] mtx); void Render(Action a); diff --git a/OpenRA.Renderer.Cg/Shader.cs b/OpenRA.Renderer.Cg/Shader.cs index 88b341adce..9ef2c1d3c3 100644 --- a/OpenRA.Renderer.Cg/Shader.cs +++ b/OpenRA.Renderer.Cg/Shader.cs @@ -75,6 +75,13 @@ namespace OpenRA.Renderer.Cg Tao.Cg.CgGl.cgGLSetupSampler(param, texture.texture); } + public void SetVec(string name, float x) + { + var param = Tao.Cg.Cg.cgGetNamedEffectParameter(effect, name); + if (param != IntPtr.Zero) + Tao.Cg.CgGl.cgGLSetParameter1f(param, x); + } + public void SetVec(string name, float x, float y) { var param = Tao.Cg.Cg.cgGetNamedEffectParameter(effect, name); @@ -82,6 +89,22 @@ namespace OpenRA.Renderer.Cg Tao.Cg.CgGl.cgGLSetParameter2f(param, x, y); } + public void SetVec(string name, float[] vec, int length) + { + var param = Tao.Cg.Cg.cgGetNamedEffectParameter(effect, name); + if (param == IntPtr.Zero) + return; + + switch(length) + { + case 1: Tao.Cg.CgGl.cgGLSetParameter1fv(param, vec); break; + case 2: Tao.Cg.CgGl.cgGLSetParameter2fv(param, vec); break; + case 3: Tao.Cg.CgGl.cgGLSetParameter3fv(param, vec); break; + case 4: Tao.Cg.CgGl.cgGLSetParameter4fv(param, vec); break; + default: throw new InvalidDataException("Invalid vector length"); + } + } + public void SetMatrix(string name, float[] mtx) { if (mtx.Length != 16) diff --git a/OpenRA.Renderer.Gl/Shader.cs b/OpenRA.Renderer.Gl/Shader.cs index 9df56e1504..1bc9427f1d 100644 --- a/OpenRA.Renderer.Gl/Shader.cs +++ b/OpenRA.Renderer.Gl/Shader.cs @@ -141,6 +141,16 @@ namespace OpenRA.Renderer.Glsl textures[texUnit] = t; } + public void SetVec(string name, float x) + { + Gl.glUseProgramObjectARB(program); + ErrorHandler.CheckGlError(); + int param = Gl.glGetUniformLocationARB(program, name); + ErrorHandler.CheckGlError(); + Gl.glUniform1fARB(param,x); + ErrorHandler.CheckGlError(); + } + public void SetVec(string name, float x, float y) { Gl.glUseProgramObjectARB(program); @@ -151,6 +161,21 @@ namespace OpenRA.Renderer.Glsl ErrorHandler.CheckGlError(); } + public void SetVec(string name, float[] vec, int length) + { + int param = Gl.glGetUniformLocationARB(program, name); + ErrorHandler.CheckGlError(); + switch(length) + { + case 1: Gl.glUniform1fv(param, 1, vec); break; + case 2: Gl.glUniform2fv(param, 1, vec); break; + case 3: Gl.glUniform3fv(param, 1, vec); break; + case 4: Gl.glUniform4fv(param, 1, vec); break; + default: throw new InvalidDataException("Invalid vector length"); + } + ErrorHandler.CheckGlError(); + } + public void SetMatrix(string name, float[] mtx) { if (mtx.Length != 16) diff --git a/OpenRA.Renderer.Null/NullGraphicsDevice.cs b/OpenRA.Renderer.Null/NullGraphicsDevice.cs index 79497ead37..a98c6db52c 100644 --- a/OpenRA.Renderer.Null/NullGraphicsDevice.cs +++ b/OpenRA.Renderer.Null/NullGraphicsDevice.cs @@ -58,7 +58,9 @@ namespace OpenRA.Renderer.Null public class NullShader : IShader { + public void SetVec(string name, float x) { } public void SetVec(string name, float x, float y) { } + public void SetVec(string name, float[] vec, int length) { } public void SetTexture(string param, ITexture texture) { } public void SetMatrix(string param, float[] mtx) { } public void Commit() { } From 1b34c7d6b9aef78b8c182f29cfa3c01cb85d2178 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 22 Feb 2013 23:29:34 +1300 Subject: [PATCH 05/15] Expose depth buffer to render code. --- OpenRA.FileFormats/Graphics/IGraphicsDevice.cs | 3 +++ OpenRA.Game/Graphics/Renderer.cs | 12 ++++++++++++ OpenRA.Renderer.Null/NullGraphicsDevice.cs | 3 +++ OpenRA.Renderer.SdlCommon/SdlGraphics.cs | 14 ++++++++++++++ 4 files changed, 32 insertions(+) diff --git a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs index bb57b64c1c..ae7c76df0c 100755 --- a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs +++ b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs @@ -50,6 +50,9 @@ namespace OpenRA.FileFormats.Graphics void SetLineWidth( float width ); void EnableScissor( int left, int top, int width, int height ); void DisableScissor(); + + void EnableDepthBuffer(); + void DisableDepthBuffer(); } public interface IVertexBuffer diff --git a/OpenRA.Game/Graphics/Renderer.cs b/OpenRA.Game/Graphics/Renderer.cs index 6509778669..e129851745 100644 --- a/OpenRA.Game/Graphics/Renderer.cs +++ b/OpenRA.Game/Graphics/Renderer.cs @@ -186,5 +186,17 @@ namespace OpenRA.Graphics Flush(); Device.DisableScissor(); } + + public void EnableDepthBuffer() + { + Flush(); + Device.EnableDepthBuffer(); + } + + public void DisableDepthBuffer() + { + Flush(); + Device.DisableDepthBuffer(); + } } } diff --git a/OpenRA.Renderer.Null/NullGraphicsDevice.cs b/OpenRA.Renderer.Null/NullGraphicsDevice.cs index a98c6db52c..61c7878caf 100644 --- a/OpenRA.Renderer.Null/NullGraphicsDevice.cs +++ b/OpenRA.Renderer.Null/NullGraphicsDevice.cs @@ -38,6 +38,9 @@ namespace OpenRA.Renderer.Null public void EnableScissor(int left, int top, int width, int height) { } public void DisableScissor() { } + public void EnableDepthBuffer() { } + public void DisableDepthBuffer() { } + public void Clear() { } public void Present() { } diff --git a/OpenRA.Renderer.SdlCommon/SdlGraphics.cs b/OpenRA.Renderer.SdlCommon/SdlGraphics.cs index e274c54a78..ccd7ca08aa 100644 --- a/OpenRA.Renderer.SdlCommon/SdlGraphics.cs +++ b/OpenRA.Renderer.SdlCommon/SdlGraphics.cs @@ -133,6 +133,20 @@ namespace OpenRA.Renderer.SdlCommon ErrorHandler.CheckGlError(); } + public void EnableDepthBuffer() + { + Gl.glClear(Gl.GL_DEPTH_BUFFER_BIT); + ErrorHandler.CheckGlError(); + Gl.glEnable(Gl.GL_DEPTH_TEST); + ErrorHandler.CheckGlError(); + } + + public void DisableDepthBuffer() + { + Gl.glDisable(Gl.GL_DEPTH_TEST); + ErrorHandler.CheckGlError(); + } + public void EnableScissor(int left, int top, int width, int height) { if (width < 0) width = 0; From f6264eeba4b6e2bbcf4537315786c541fda0dcf4 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 3 Mar 2013 22:03:04 +1300 Subject: [PATCH 06/15] Expose stencil buffer to render code. The intention is to provide a layer for tracking shadow rendering, so a single bit is sufficient for now. --- .../Graphics/IGraphicsDevice.cs | 3 +++ OpenRA.Game/Graphics/Renderer.cs | 12 ++++++++++++ OpenRA.Renderer.Null/NullGraphicsDevice.cs | 3 +++ OpenRA.Renderer.SdlCommon/SdlGraphics.cs | 19 +++++++++++++++++++ 4 files changed, 37 insertions(+) diff --git a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs index ae7c76df0c..ec82cff24e 100755 --- a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs +++ b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs @@ -53,6 +53,9 @@ 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 e129851745..3be7b230f7 100644 --- a/OpenRA.Game/Graphics/Renderer.cs +++ b/OpenRA.Game/Graphics/Renderer.cs @@ -198,5 +198,17 @@ 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 61c7878caf..cc8dd57233 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 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 ccd7ca08aa..0cf17f2d3e 100644 --- a/OpenRA.Renderer.SdlCommon/SdlGraphics.cs +++ b/OpenRA.Renderer.SdlCommon/SdlGraphics.cs @@ -49,6 +49,7 @@ namespace OpenRA.Renderer.SdlCommon Sdl.SDL_GL_SetAttribute( Sdl.SDL_GL_GREEN_SIZE, 8 ); Sdl.SDL_GL_SetAttribute( Sdl.SDL_GL_BLUE_SIZE, 8 ); Sdl.SDL_GL_SetAttribute( Sdl.SDL_GL_ALPHA_SIZE, 0 ); + Sdl.SDL_GL_SetAttribute( Sdl.SDL_GL_STENCIL_SIZE, 1 ); int windowFlags = 0; switch (window) @@ -133,6 +134,24 @@ 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 656a5292490f36ee33ed921bc9abd23d5a7dc6fe Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 22 Feb 2013 23:05:43 +1300 Subject: [PATCH 07/15] Support Vertex objects with custom z coordinate. --- OpenRA.FileFormats/Graphics/Vertex.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/OpenRA.FileFormats/Graphics/Vertex.cs b/OpenRA.FileFormats/Graphics/Vertex.cs index 70fe13f4df..d0f0815554 100644 --- a/OpenRA.FileFormats/Graphics/Vertex.cs +++ b/OpenRA.FileFormats/Graphics/Vertex.cs @@ -24,5 +24,12 @@ namespace OpenRA.FileFormats.Graphics this.u = uv.X; this.v = uv.Y; this.p = pc.X; this.c = pc.Y; } + + public Vertex(float[] xyz, float2 uv, float2 pc) + { + this.x = xyz[0]; this.y = xyz[1]; this.z = xyz[2]; + this.u = uv.X; this.v = uv.Y; + this.p = pc.X; this.c = pc.Y; + } } } From 992db08f71738e2591f72855ffcc6c629890c1c8 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 21 Feb 2013 23:39:16 +1300 Subject: [PATCH 08/15] Implement vxl parser. --- OpenRA.FileFormats/Graphics/VxlReader.cs | 158 +++++++++++++++++++ OpenRA.FileFormats/OpenRA.FileFormats.csproj | 1 + 2 files changed, 159 insertions(+) create mode 100644 OpenRA.FileFormats/Graphics/VxlReader.cs diff --git a/OpenRA.FileFormats/Graphics/VxlReader.cs b/OpenRA.FileFormats/Graphics/VxlReader.cs new file mode 100644 index 0000000000..69548661e5 --- /dev/null +++ b/OpenRA.FileFormats/Graphics/VxlReader.cs @@ -0,0 +1,158 @@ +#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; +using System.Collections.Generic; +using System.IO; + +namespace OpenRA.FileFormats +{ + public enum NormalType { TiberianSun = 2, RedAlert2 = 4 } + public class VxlElement + { + public byte Color; + public byte Normal; + } + + public class VxlLimb + { + public string Name; + public float Scale; + public float[] Bounds; + public byte[] Size; + public NormalType Type; + + public uint VoxelCount; + public Dictionary[,] VoxelMap; + } + + public class VxlReader + { + public readonly uint LimbCount; + public VxlLimb[] Limbs; + + uint BodySize; + + void ReadVoxelData(Stream s, VxlLimb l) + { + var baseSize = l.Size[0]*l.Size[1]; + var colStart = new int[baseSize]; + for (var i = 0; i < baseSize; i++) + colStart[i] = s.ReadInt32(); + s.Seek(4*baseSize, SeekOrigin.Current); + var dataStart = s.Position; + + // Count the voxels in this limb + l.VoxelCount = 0; + for (var i = 0; i < baseSize; i++) + { + // Empty column + if (colStart[i] == -1) + continue; + + s.Seek(dataStart + colStart[i], SeekOrigin.Begin); + var z = 0; + do + { + z += s.ReadUInt8(); + var count = s.ReadUInt8(); + z += count; + l.VoxelCount += count; + s.Seek(2*count + 1, SeekOrigin.Current); + } while (z < l.Size[2]); + } + + // Read the data + l.VoxelMap = new Dictionary[l.Size[0],l.Size[1]]; + for (var i = 0; i < baseSize; i++) + { + // Empty column + if (colStart[i] == -1) + continue; + + s.Seek(dataStart + colStart[i], SeekOrigin.Begin); + + byte x = (byte)(i % l.Size[0]); + byte y = (byte)(i / l.Size[0]); + byte z = 0; + l.VoxelMap[x,y] = new Dictionary(); + do + { + z += s.ReadUInt8(); + var count = s.ReadUInt8(); + for (var j = 0; j < count; j++) + { + var v = new VxlElement(); + v.Color = s.ReadUInt8(); + v.Normal = s.ReadUInt8(); + + l.VoxelMap[x,y].Add(z, v); + z++; + } + // Skip duplicate count + s.ReadUInt8(); + } while (z < l.Size[2]); + } + } + + public VxlReader(Stream s) + { + + if (!s.ReadASCII(16).StartsWith("Voxel Animation")) + throw new InvalidDataException("Invalid vxl header"); + + s.ReadUInt32(); + LimbCount = s.ReadUInt32(); + s.ReadUInt32(); + BodySize = s.ReadUInt32(); + s.Seek(770, SeekOrigin.Current); + + // Read Limb headers + Limbs = new VxlLimb[LimbCount]; + for (var i = 0; i < LimbCount; i++) + { + Limbs[i] = new VxlLimb(); + Limbs[i].Name = s.ReadASCII(16); + s.Seek(12, SeekOrigin.Current); + } + + // Skip to the Limb footers + s.Seek(802 + 28*LimbCount + BodySize, SeekOrigin.Begin); + + var LimbDataOffset = new uint[LimbCount]; + for (var i = 0; i < LimbCount; i++) + { + LimbDataOffset[i] = s.ReadUInt32(); + s.Seek(8, SeekOrigin.Current); + Limbs[i].Scale = s.ReadFloat(); + s.Seek(48, SeekOrigin.Current); + + Limbs[i].Bounds = new float[6]; + for (var j = 0; j < 6; j++) + Limbs[i].Bounds[j] = s.ReadFloat(); + Limbs[i].Size = s.ReadBytes(3); + Limbs[i].Type = (NormalType)s.ReadByte(); + } + + for (var i = 0; i < LimbCount; i++) + { + s.Seek(802 + 28*LimbCount + LimbDataOffset[i], SeekOrigin.Begin); + ReadVoxelData(s, Limbs[i]); + } + } + + public static VxlReader Load(string filename) + { + using (var s = File.OpenRead(filename)) + return new VxlReader(s); + } + } +} diff --git a/OpenRA.FileFormats/OpenRA.FileFormats.csproj b/OpenRA.FileFormats/OpenRA.FileFormats.csproj index abe857a003..df1eaa56db 100644 --- a/OpenRA.FileFormats/OpenRA.FileFormats.csproj +++ b/OpenRA.FileFormats/OpenRA.FileFormats.csproj @@ -141,6 +141,7 @@ + From af6791d942388468616c66b52bba3c69113b0a54 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 22 Feb 2013 00:17:14 +1300 Subject: [PATCH 09/15] Implement hva parser. --- OpenRA.FileFormats/Graphics/HvaReader.cs | 70 ++++++++++++++++++++ OpenRA.FileFormats/OpenRA.FileFormats.csproj | 1 + 2 files changed, 71 insertions(+) create mode 100644 OpenRA.FileFormats/Graphics/HvaReader.cs diff --git a/OpenRA.FileFormats/Graphics/HvaReader.cs b/OpenRA.FileFormats/Graphics/HvaReader.cs new file mode 100644 index 0000000000..662be7d8a9 --- /dev/null +++ b/OpenRA.FileFormats/Graphics/HvaReader.cs @@ -0,0 +1,70 @@ +#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; +using System.IO; +using System.Linq; + +namespace OpenRA.FileFormats +{ + public class HvaReader + { + public readonly uint FrameCount; + public readonly uint LimbCount; + float[] Transforms; + + public HvaReader(Stream s) + { + // Index swaps for transposing a matrix + var ids = new byte[]{0,4,8,12,1,5,9,13,2,6,10,14}; + + s.Seek(16, SeekOrigin.Begin); + FrameCount = s.ReadUInt32(); + LimbCount = s.ReadUInt32(); + + // Skip limb names + s.Seek(16*LimbCount, SeekOrigin.Current); + Transforms = new float[16*FrameCount*LimbCount]; + for (var j = 0; j < FrameCount; j++) + for (var i = 0; i < LimbCount; i++) + { + // Convert to column-major matrices and add the final matrix row + var c = 16*(LimbCount*j + i); + Transforms[c + 3] = 0; + Transforms[c + 7] = 0; + Transforms[c + 11] = 0; + Transforms[c + 15] = 1; + + for (var k = 0; k < 12; k++) + Transforms[c + ids[k]] = s.ReadFloat(); + } + } + + 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)) + return new HvaReader(s); + } + } +} diff --git a/OpenRA.FileFormats/OpenRA.FileFormats.csproj b/OpenRA.FileFormats/OpenRA.FileFormats.csproj index df1eaa56db..1c4f6b8767 100644 --- a/OpenRA.FileFormats/OpenRA.FileFormats.csproj +++ b/OpenRA.FileFormats/OpenRA.FileFormats.csproj @@ -142,6 +142,7 @@ + From 6cef4290df3a989c0e61505b96e3b59201c8b472 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 21 Feb 2013 20:35:55 +1300 Subject: [PATCH 10/15] Store the voxel normals table in a palette. --- OpenRA.Mods.RA/OpenRA.Mods.RA.csproj | 1 + OpenRA.Mods.RA/VoxelNormalsPalette.cs | 347 ++++++++++++++++++++++++++ 2 files changed, 348 insertions(+) create mode 100644 OpenRA.Mods.RA/VoxelNormalsPalette.cs diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 5ed8d3d4bb..5aa44eec6f 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -439,6 +439,7 @@ + diff --git a/OpenRA.Mods.RA/VoxelNormalsPalette.cs b/OpenRA.Mods.RA/VoxelNormalsPalette.cs new file mode 100644 index 0000000000..4584b844f9 --- /dev/null +++ b/OpenRA.Mods.RA/VoxelNormalsPalette.cs @@ -0,0 +1,347 @@ +#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; +using OpenRA.FileFormats; +using OpenRA.Traits; +using OpenRA.Graphics; + +namespace OpenRA.Mods.RA +{ + public class VoxelNormalsPaletteInfo : ITraitInfo + { + public readonly string Name = "normals"; + public readonly NormalType Type = NormalType.TiberianSun; + + public object Create(ActorInitializer init) { return new VoxelNormalsPalette(this); } + } + + public class VoxelNormalsPalette : IPalette + { + readonly VoxelNormalsPaletteInfo info; + + public VoxelNormalsPalette(VoxelNormalsPaletteInfo info) + { + this.info = info; + } + + public void InitPalette(WorldRenderer wr) + { + // Rotate vectors to expected orientation + // Voxel coordinates are x=forward, y=right, z=up + var channel = new int[] {2,1,0}; + var n = info.Type == NormalType.RedAlert2 ? RA2Normals : TSNormals; + + // Map normals into color range + // Introduces a maximum error of ~0.5% + var data = new uint[256]; + for (var i = 0; i < n.Length / 3; i++) + { + data[i] = 0xFF000000; + for (int j = 0; j < 3; j++) + { + var t = (n[3*i + j] + 1) / 2; + data[i] |= (uint)((byte)(t*0xFF + 0.5) << (8*channel[j])); + } + } + + wr.AddPalette(info.Name, new Palette(data), false); + } + + // Normal vector tables from http://www.sleipnirstuff.com/forum/viewtopic.php?t=8048 + static readonly float[] TSNormals = + { + 0.671214f, 0.198492f, -0.714194f, + 0.269643f, 0.584394f, -0.765360f, + -0.040546f, 0.096988f, -0.994459f, + -0.572428f, -0.091914f, -0.814787f, + -0.171401f, -0.572710f, -0.801639f, + 0.362557f, -0.302999f, -0.881331f, + 0.810347f, -0.348972f, -0.470698f, + 0.103962f, 0.938672f, -0.328767f, + -0.324047f, 0.587669f, -0.741376f, + -0.800865f, 0.340461f, -0.492647f, + -0.665498f, -0.590147f, -0.456989f, + 0.314767f, -0.803002f, -0.506073f, + 0.972629f, 0.151076f, -0.176550f, + 0.680291f, 0.684236f, -0.262727f, + -0.520079f, 0.827777f, -0.210483f, + -0.961644f, -0.179001f, -0.207847f, + -0.262714f, -0.937451f, -0.228401f, + 0.219707f, -0.971301f, 0.091125f, + 0.923808f, -0.229975f, 0.306087f, + -0.082489f, 0.970660f, 0.225866f, + -0.591798f, 0.696790f, 0.405289f, + -0.925296f, 0.366601f, 0.097111f, + -0.705051f, -0.687775f, 0.172828f, + 0.732400f, -0.680367f, -0.026305f, + 0.855162f, 0.374582f, 0.358311f, + 0.473006f, 0.836480f, 0.276705f, + -0.097617f, 0.654112f, 0.750072f, + -0.904124f, -0.153725f, 0.398658f, + -0.211916f, -0.858090f, 0.467732f, + 0.500227f, -0.674408f, 0.543091f, + 0.584539f, -0.110249f, 0.803841f, + 0.437373f, 0.454644f, 0.775889f, + -0.042441f, 0.083318f, 0.995619f, + -0.596251f, 0.220132f, 0.772028f, + -0.506455f, -0.396977f, 0.765449f, + 0.070569f, -0.478474f, 0.875262f + }; + + static readonly float[] RA2Normals = + { + 0.526578f, -0.359621f, -0.770317f, + 0.150482f, 0.435984f, 0.887284f, + 0.414195f, 0.738255f, -0.532374f, + 0.075152f, 0.916249f, -0.393498f, + -0.316149f, 0.930736f, -0.183793f, + -0.773819f, 0.623334f, -0.112510f, + -0.900842f, 0.428537f, -0.069568f, + -0.998942f, -0.010971f, 0.044665f, + -0.979761f, -0.157670f, -0.123324f, + -0.911274f, -0.362371f, -0.195620f, + -0.624069f, -0.720941f, -0.301301f, + -0.310173f, -0.809345f, -0.498752f, + 0.146613f, -0.815819f, -0.559414f, + -0.716516f, -0.694356f, -0.066888f, + 0.503972f, -0.114202f, -0.856137f, + 0.455491f, 0.872627f, -0.176211f, + -0.005010f, -0.114373f, -0.993425f, + -0.104675f, -0.327701f, -0.938965f, + 0.560412f, 0.752589f, -0.345756f, + -0.060576f, 0.821628f, -0.566796f, + -0.302341f, 0.797007f, -0.522847f, + -0.671543f, 0.670740f, -0.314863f, + -0.778401f, -0.128357f, 0.614505f, + -0.924050f, 0.278382f, -0.261985f, + -0.699773f, -0.550491f, -0.455278f, + -0.568248f, -0.517189f, -0.640008f, + 0.054098f, -0.932864f, -0.356143f, + 0.758382f, 0.572893f, -0.310888f, + 0.003620f, 0.305026f, -0.952337f, + -0.060850f, -0.986886f, -0.149511f, + 0.635230f, 0.045478f, -0.770983f, + 0.521705f, 0.241309f, -0.818287f, + 0.269404f, 0.635425f, -0.723641f, + 0.045676f, 0.672754f, -0.738455f, + -0.180511f, 0.674657f, -0.715719f, + -0.397131f, 0.636640f, -0.661042f, + -0.552004f, 0.472515f, -0.687038f, + -0.772170f, 0.083090f, -0.629960f, + -0.669819f, -0.119533f, -0.732840f, + -0.540455f, -0.318444f, -0.778782f, + -0.386135f, -0.522789f, -0.759994f, + -0.261466f, -0.688567f, -0.676395f, + -0.019412f, -0.696103f, -0.717680f, + 0.303569f, -0.481844f, -0.821993f, + 0.681939f, -0.195129f, -0.704900f, + -0.244889f, -0.116562f, -0.962519f, + 0.800759f, -0.022979f, -0.598546f, + -0.370275f, 0.095584f, -0.923991f, + -0.330671f, -0.326578f, -0.885440f, + -0.163220f, -0.527579f, -0.833679f, + 0.126390f, -0.313146f, -0.941257f, + 0.349548f, -0.272226f, -0.896498f, + 0.239918f, -0.085825f, -0.966992f, + 0.390845f, 0.081537f, -0.916838f, + 0.255267f, 0.268697f, -0.928785f, + 0.146245f, 0.480438f, -0.864749f, + -0.326016f, 0.478456f, -0.815349f, + -0.469682f, -0.112519f, -0.875636f, + 0.818440f, -0.258520f, -0.513151f, + -0.474318f, 0.292238f, -0.830433f, + 0.778943f, 0.395842f, -0.486371f, + 0.624094f, 0.393773f, -0.674870f, + 0.740886f, 0.203834f, -0.639953f, + 0.480217f, 0.565768f, -0.670297f, + 0.380930f, 0.424535f, -0.821378f, + -0.093422f, 0.501124f, -0.860318f, + -0.236485f, 0.296198f, -0.925387f, + -0.131531f, 0.093959f, -0.986849f, + -0.823562f, 0.295777f, -0.484006f, + 0.611066f, -0.624304f, -0.486664f, + 0.069496f, -0.520330f, -0.851133f, + 0.226522f, -0.664879f, -0.711775f, + 0.471308f, -0.568904f, -0.673957f, + 0.388425f, -0.742624f, -0.545560f, + 0.783675f, -0.480729f, -0.393385f, + 0.962394f, 0.135676f, -0.235349f, + 0.876607f, 0.172034f, -0.449406f, + 0.633405f, 0.589793f, -0.500941f, + 0.182276f, 0.800658f, -0.570721f, + 0.177003f, 0.764134f, 0.620297f, + -0.544016f, 0.675515f, -0.497721f, + -0.679297f, 0.286467f, -0.675642f, + -0.590391f, 0.091369f, -0.801929f, + -0.824360f, -0.133124f, -0.550189f, + -0.715794f, -0.334542f, -0.612961f, + 0.174286f, -0.892484f, 0.416049f, + -0.082528f, -0.837123f, -0.540753f, + 0.283331f, -0.880874f, -0.379189f, + 0.675134f, -0.426627f, -0.601817f, + 0.843720f, -0.512335f, -0.160156f, + 0.977304f, -0.098556f, -0.187520f, + 0.846295f, 0.522672f, -0.102947f, + 0.677141f, 0.721325f, -0.145501f, + 0.320965f, 0.870892f, -0.372194f, + -0.178978f, 0.911533f, -0.370236f, + -0.447169f, 0.826701f, -0.341474f, + -0.703203f, 0.496328f, -0.509081f, + -0.977181f, 0.063563f, -0.202674f, + -0.878170f, -0.412938f, 0.241455f, + -0.835831f, -0.358550f, -0.415728f, + -0.499174f, -0.693433f, -0.519592f, + -0.188789f, -0.923753f, -0.333225f, + 0.192254f, -0.969361f, -0.152896f, + 0.515940f, -0.783907f, -0.345392f, + 0.905925f, -0.300952f, -0.297871f, + 0.991112f, -0.127746f, 0.037107f, + 0.995135f, 0.098424f, -0.004383f, + 0.760123f, 0.646277f, 0.067367f, + 0.205221f, 0.959580f, -0.192591f, + -0.042750f, 0.979513f, -0.196791f, + -0.438017f, 0.898927f, 0.008492f, + -0.821994f, 0.480785f, -0.305239f, + -0.899917f, 0.081710f, -0.428337f, + -0.926612f, -0.144618f, -0.347096f, + -0.793660f, -0.557792f, -0.242839f, + -0.431350f, -0.847779f, -0.308558f, + -0.005492f, -0.965000f, 0.262193f, + 0.587905f, -0.804026f, -0.088940f, + 0.699493f, -0.667686f, -0.254765f, + 0.889303f, 0.359795f, -0.282291f, + 0.780972f, 0.197037f, 0.592672f, + 0.520121f, 0.506696f, 0.687557f, + 0.403895f, 0.693961f, 0.596060f, + -0.154983f, 0.899236f, 0.409090f, + -0.657338f, 0.537168f, 0.528543f, + -0.746195f, 0.334091f, 0.575827f, + -0.624952f, -0.049144f, 0.779115f, + 0.318141f, -0.254715f, 0.913185f, + -0.555897f, 0.405294f, 0.725752f, + -0.794434f, 0.099406f, 0.599160f, + -0.640361f, -0.689463f, 0.338495f, + -0.126713f, -0.734095f, 0.667120f, + 0.105457f, -0.780817f, 0.615795f, + 0.407993f, -0.480916f, 0.776055f, + 0.695136f, -0.545120f, 0.468647f, + 0.973191f, -0.006489f, 0.229908f, + 0.946894f, 0.317509f, -0.050799f, + 0.563583f, 0.825612f, 0.027183f, + 0.325773f, 0.945423f, 0.006949f, + -0.171821f, 0.985097f, -0.007815f, + -0.670441f, 0.739939f, 0.054769f, + -0.822981f, 0.554962f, 0.121322f, + -0.966193f, 0.117857f, 0.229307f, + -0.953769f, -0.294704f, 0.058945f, + -0.864387f, -0.502728f, -0.010015f, + -0.530609f, -0.842006f, -0.097366f, + -0.162618f, -0.984075f, 0.071772f, + 0.081447f, -0.996011f, 0.036439f, + 0.745984f, -0.665963f, 0.000762f, + 0.942057f, -0.329269f, -0.064106f, + 0.939702f, -0.281090f, 0.194803f, + 0.771214f, 0.550670f, 0.319363f, + 0.641348f, 0.730690f, 0.234021f, + 0.080682f, 0.996691f, 0.009879f, + -0.046725f, 0.976643f, 0.209725f, + -0.531076f, 0.821001f, 0.209562f, + -0.695815f, 0.655990f, 0.292435f, + -0.976122f, 0.216709f, -0.014913f, + -0.961661f, -0.144129f, 0.233314f, + -0.772084f, -0.613647f, 0.165299f, + -0.449600f, -0.836060f, 0.314426f, + -0.392700f, -0.914616f, 0.096247f, + 0.390589f, -0.919470f, 0.044890f, + 0.582529f, -0.799198f, 0.148127f, + 0.866431f, -0.489812f, 0.096864f, + 0.904587f, 0.111498f, 0.411450f, + 0.953537f, 0.232330f, 0.191806f, + 0.497311f, 0.770803f, 0.398177f, + 0.194066f, 0.956320f, 0.218611f, + 0.422876f, 0.882276f, 0.206797f, + -0.373797f, 0.849566f, 0.372174f, + -0.534497f, 0.714023f, 0.452200f, + -0.881827f, 0.237160f, 0.407598f, + -0.904948f, -0.014069f, 0.425289f, + -0.751827f, -0.512817f, 0.414458f, + -0.501015f, -0.697917f, 0.511758f, + -0.235190f, -0.925923f, 0.295555f, + 0.228983f, -0.953940f, 0.193819f, + 0.734025f, -0.634898f, 0.241062f, + 0.913753f, -0.063253f, -0.401316f, + 0.905735f, -0.161487f, 0.391875f, + 0.858930f, 0.342446f, 0.380749f, + 0.624486f, 0.607581f, 0.490777f, + 0.289264f, 0.857479f, 0.425508f, + 0.069968f, 0.902169f, 0.425671f, + -0.286180f, 0.940700f, 0.182165f, + -0.574013f, 0.805119f, -0.149309f, + 0.111258f, 0.099718f, -0.988776f, + -0.305393f, -0.944228f, -0.123160f, + -0.601166f, -0.789576f, 0.123163f, + -0.290645f, -0.812140f, 0.505919f, + -0.064920f, -0.877163f, 0.475785f, + 0.408301f, -0.862216f, 0.299789f, + 0.566097f, -0.725566f, 0.391264f, + 0.839364f, -0.427387f, 0.335869f, + 0.818900f, -0.041305f, 0.572448f, + 0.719784f, 0.414997f, 0.556497f, + 0.881744f, 0.450270f, 0.140659f, + 0.401823f, -0.898220f, -0.178152f, + -0.054020f, 0.791344f, 0.608980f, + -0.293774f, 0.763994f, 0.574465f, + -0.450798f, 0.610347f, 0.651351f, + -0.638221f, 0.186694f, 0.746873f, + -0.872870f, -0.257127f, 0.414708f, + -0.587257f, -0.521710f, 0.618828f, + -0.353658f, -0.641974f, 0.680291f, + 0.041649f, -0.611273f, 0.790323f, + 0.348342f, -0.779183f, 0.521087f, + 0.499167f, -0.622441f, 0.602826f, + 0.790019f, -0.303831f, 0.532500f, + 0.660118f, 0.060733f, 0.748702f, + 0.604921f, 0.294161f, 0.739960f, + 0.385697f, 0.379346f, 0.841032f, + 0.239693f, 0.207876f, 0.948332f, + 0.012623f, 0.258532f, 0.965920f, + -0.100557f, 0.457147f, 0.883688f, + 0.046967f, 0.628588f, 0.776319f, + -0.430391f, -0.445405f, 0.785097f, + -0.434291f, -0.196228f, 0.879139f, + -0.256637f, -0.336867f, 0.905902f, + -0.131372f, -0.158910f, 0.978514f, + 0.102379f, -0.208767f, 0.972592f, + 0.195687f, -0.450129f, 0.871258f, + 0.627319f, -0.423148f, 0.653771f, + 0.687439f, -0.171583f, 0.705682f, + 0.275920f, -0.021255f, 0.960946f, + 0.459367f, 0.157466f, 0.874178f, + 0.285395f, 0.583184f, 0.760556f, + -0.812174f, 0.460303f, 0.358461f, + -0.189068f, 0.641223f, 0.743698f, + -0.338875f, 0.476480f, 0.811252f, + -0.920994f, 0.347186f, 0.176727f, + 0.040639f, 0.024465f, 0.998874f, + -0.739132f, -0.353747f, 0.573190f, + -0.603512f, -0.286615f, 0.744060f, + -0.188676f, -0.547059f, 0.815554f, + -0.026045f, -0.397820f, 0.917094f, + 0.267897f, -0.649041f, 0.712023f, + 0.518246f, -0.284891f, 0.806386f, + 0.493451f, -0.066533f, 0.867225f, + -0.328188f, 0.140251f, 0.934143f, + -0.328188f, 0.140251f, 0.934143f, + -0.328188f, 0.140251f, 0.934143f, + -0.328188f, 0.140251f, 0.934143f + }; + } +} From a00696ec3b9714807498e0e5b4b3a806bff0cb43 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 23 Feb 2013 00:56:24 +1300 Subject: [PATCH 11/15] Add core voxel rendering code. --- OpenRA.Game/Graphics/Renderer.cs | 4 + OpenRA.Game/Graphics/Voxel.cs | 161 +++++++++++++++++++++ OpenRA.Game/Graphics/VoxelLoader.cs | 194 ++++++++++++++++++++++++++ OpenRA.Game/Graphics/VoxelRenderer.cs | 88 ++++++++++++ OpenRA.Game/ModData.cs | 2 + OpenRA.Game/OpenRA.Game.csproj | 3 + cg/vxl.fx | 86 ++++++++++++ cg/vxlshadow.fx | 90 ++++++++++++ glsl/vxl.frag | 17 +++ glsl/vxl.vert | 18 +++ glsl/vxlshadow.frag | 12 ++ glsl/vxlshadow.vert | 26 ++++ 12 files changed, 701 insertions(+) create mode 100644 OpenRA.Game/Graphics/Voxel.cs create mode 100644 OpenRA.Game/Graphics/VoxelLoader.cs create mode 100644 OpenRA.Game/Graphics/VoxelRenderer.cs create mode 100644 cg/vxl.fx create mode 100644 cg/vxlshadow.fx create mode 100644 glsl/vxl.frag create mode 100644 glsl/vxl.vert create mode 100644 glsl/vxlshadow.frag create mode 100644 glsl/vxlshadow.vert diff --git a/OpenRA.Game/Graphics/Renderer.cs b/OpenRA.Game/Graphics/Renderer.cs index 3be7b230f7..723373964c 100644 --- a/OpenRA.Game/Graphics/Renderer.cs +++ b/OpenRA.Game/Graphics/Renderer.cs @@ -30,6 +30,7 @@ namespace OpenRA.Graphics public SpriteRenderer WorldSpriteRenderer { get; private set; } public QuadRenderer WorldQuadRenderer { get; private set; } public LineRenderer WorldLineRenderer { get; private set; } + public VoxelRenderer WorldVoxelRenderer { get; private set; } public LineRenderer LineRenderer { get; private set; } public SpriteRenderer RgbaSpriteRenderer { get; private set; } public SpriteRenderer SpriteRenderer { get; private set; } @@ -46,6 +47,7 @@ namespace OpenRA.Graphics WorldSpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp")); WorldLineRenderer = new LineRenderer(this, device.CreateShader("line")); + WorldVoxelRenderer = new VoxelRenderer(this, device.CreateShader("vxl"), device.CreateShader("vxlshadow")); LineRenderer = new LineRenderer(this, device.CreateShader("line")); WorldQuadRenderer = new QuadRenderer(this, device.CreateShader("line")); RgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba")); @@ -71,6 +73,7 @@ namespace OpenRA.Graphics WorldLineRenderer.SetViewportParams(Resolution, zoom, scroll); WorldQuadRenderer.SetViewportParams(Resolution, zoom, scroll); LineRenderer.SetViewportParams(Resolution, 1f, float2.Zero); + WorldVoxelRenderer.SetViewportParams(Resolution, zoom, scroll); } ITexture currentPaletteTexture; @@ -85,6 +88,7 @@ namespace OpenRA.Graphics RgbaSpriteRenderer.SetPalette(currentPaletteTexture); SpriteRenderer.SetPalette(currentPaletteTexture); WorldSpriteRenderer.SetPalette(currentPaletteTexture); + WorldVoxelRenderer.SetPalette(currentPaletteTexture); } public void EndFrame(IInputHandler inputHandler) diff --git a/OpenRA.Game/Graphics/Voxel.cs b/OpenRA.Game/Graphics/Voxel.cs new file mode 100644 index 0000000000..841886e1be --- /dev/null +++ b/OpenRA.Game/Graphics/Voxel.cs @@ -0,0 +1,161 @@ +#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; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.FileFormats.Graphics; + +namespace OpenRA.Graphics +{ + struct Limb + { + public float Scale; + public float[] Bounds; + public byte[] Size; + public VoxelRenderData RenderData; + } + + public class Voxel + { + Limb[] limbs; + HvaReader hva; + VoxelLoader loader; + + float[][] transform, lightDirection, groundNormal; + float[] groundZ; + + public Voxel(VoxelLoader loader, VxlReader vxl, HvaReader hva) + { + this.hva = hva; + this.loader = loader; + + limbs = new Limb[vxl.LimbCount]; + for (var i = 0; i < vxl.LimbCount; i++) + { + var vl = vxl.Limbs[i]; + var l = new Limb(); + l.Scale = vl.Scale; + l.Bounds = (float[])vl.Bounds.Clone(); + l.Size = (byte[])vl.Size.Clone(); + l.RenderData = loader.GenerateRenderData(vxl.Limbs[i]); + limbs[i] = l; + } + + transform = new float[vxl.LimbCount][]; + lightDirection = new float[vxl.LimbCount][]; + groundNormal = new float[vxl.LimbCount][]; + groundZ = new float[vxl.LimbCount]; + } + + // Extract the rotation components from a matrix and apply them to a vector + static float[] ExtractRotationVector(float[] mtx, WVec vec) + { + var tVec = Util.MatrixVectorMultiply(mtx, new float[] {vec.X, vec.Y, vec.Z, 1}); + var tOrigin = Util.MatrixVectorMultiply(mtx, new float[] {0,0,0,1}); + tVec[0] -= tOrigin[0]*tVec[3]/tOrigin[3]; + tVec[1] -= tOrigin[1]*tVec[3]/tOrigin[3]; + tVec[2] -= tOrigin[2]*tVec[3]/tOrigin[3]; + + // Renormalize + var w = (float)Math.Sqrt(tVec[0]*tVec[0] + tVec[1]*tVec[1] + tVec[2]*tVec[2]); + tVec[0] /= w; + tVec[1] /= w; + tVec[2] /= w; + tVec[3] = 1f; + + return tVec; + } + + static float[] MakeFloatMatrix(int[] imtx) + { + var fmtx = new float[16]; + for (var i = 0; i < 16; i++) + fmtx[i] = imtx[i]*1f / imtx[15]; + return fmtx; + } + + public void Draw(VoxelRenderer r, float[] lightAmbientColor, float[] lightDiffuseColor, + int colorPalette, int normalsPalette) + { + for (var i = 0; i < limbs.Length; i++) + r.Render(loader, limbs[i].RenderData, transform[i], lightDirection[i], + lightAmbientColor, lightDiffuseColor, colorPalette, normalsPalette); + } + + public void DrawShadow(VoxelRenderer r, int shadowPalette) + { + for (var i = 0; i < limbs.Length; i++) + r.RenderShadow(loader, limbs[i].RenderData, transform[i], lightDirection[i], + groundNormal[i], groundZ[i], shadowPalette); + } + + static readonly WVec forward = new WVec(1024,0,0); + static readonly WVec up = new WVec(0,0,1024); + public void PrepareForDraw(WorldRenderer wr, WPos pos, IEnumerable rotations, + WRot camera, uint frame, float scale, WRot lightSource) + { + // Calculate the shared view matrix components + var pxPos = wr.ScreenPosition(pos); + + // Rotations + var rot = rotations.Reverse().Aggregate(MakeFloatMatrix(camera.AsMatrix()), + (a,b) => Util.MatrixMultiply(a, MakeFloatMatrix(b.AsMatrix()))); + + // Each limb has its own transformation matrix + for (uint i = 0; i < limbs.Length; i++) + { + Limb l = limbs[i]; + var t = Util.MatrixMultiply(Util.ScaleMatrix(1,-1,1), + hva.TransformationMatrix(i, frame)); + // Fix limb position + t[12] *= l.Scale*(l.Bounds[3] - l.Bounds[0]) / l.Size[0]; + t[13] *= l.Scale*(l.Bounds[4] - l.Bounds[1]) / l.Size[1]; + t[14] *= l.Scale*(l.Bounds[5] - l.Bounds[2]) / l.Size[2]; + t = Util.MatrixMultiply(t, Util.TranslationMatrix(l.Bounds[0], l.Bounds[1], l.Bounds[2])); + + // Apply view matrix + transform[i] = Util.MatrixMultiply(Util.TranslationMatrix(pxPos.X, pxPos.Y, pxPos.Y), + Util.ScaleMatrix(scale*l.Scale, scale*l.Scale, scale*l.Scale)); + transform[i] = Util.MatrixMultiply(transform[i], rot); + transform[i] = Util.MatrixMultiply(transform[i], t); + + // Transform light direction into limb-space + var undoPitch = MakeFloatMatrix(new WRot(camera.Pitch, WAngle.Zero, WAngle.Zero).AsMatrix()); + var lightTransform = Util.MatrixMultiply(Util.MatrixInverse(transform[i]), undoPitch); + + lightDirection[i] = ExtractRotationVector(lightTransform, forward.Rotate(lightSource)); + groundNormal[i] = ExtractRotationVector(Util.MatrixInverse(t), up); + + // Hack: Extract the ground z position independently of y. + groundZ[i] = (wr.ScreenPosition(pos).Y - wr.ScreenZPosition(pos, 0)) / 2; + } + } + + 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()) + .Aggregate((a,b) => new float[] + { + Math.Max(a[0], b[0]), + Math.Max(a[1], b[1]), + Math.Max(a[2], b[2]) + }); + } + } + } +} diff --git a/OpenRA.Game/Graphics/VoxelLoader.cs b/OpenRA.Game/Graphics/VoxelLoader.cs new file mode 100644 index 0000000000..bf016e8b93 --- /dev/null +++ b/OpenRA.Game/Graphics/VoxelLoader.cs @@ -0,0 +1,194 @@ +#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; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.FileFormats.Graphics; + +namespace OpenRA.Graphics +{ + public struct VoxelRenderData + { + public readonly int Start; + public readonly int Count; + public readonly Sheet Sheet; + + public VoxelRenderData(int start, int count, Sheet sheet) + { + Start = start; + Count = count; + Sheet = sheet; + } + } + + public class VoxelLoader + { + SheetBuilder sheetBuilder; + + IVertexBuffer vertexBuffer; + List vertices; + int totalVertexCount; + int cachedVertexCount; + + public VoxelLoader() + { + vertices = new List(); + totalVertexCount = 0; + cachedVertexCount = 0; + + sheetBuilder = new SheetBuilder(SheetType.DualIndexed); + } + + static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f }; + Vertex[] GenerateSlicePlane(int su, int sv, Func first, Func second, Func coord) + { + var colors = new byte[su*sv]; + var normals = new byte[su*sv]; + + var c = 0; + for (var v = 0; v < sv; v++) + for (var u = 0; u < su; u++) + { + var voxel = first(u,v) ?? second(u,v); + colors[c] = voxel == null ? (byte)0 : voxel.Color; + normals[c] = voxel == null ? (byte)0 : voxel.Normal; + c++; + } + + Sprite s = sheetBuilder.Allocate(new Size(su, sv), false); + Util.FastCopyIntoChannel(s, 0, colors); + Util.FastCopyIntoChannel(s, 1, normals); + s.sheet.MakeDirty(); + + var channels = new float2(channelSelect[(int)s.channel], channelSelect[(int)s.channel + 1]); + return new Vertex[4] + { + new Vertex(coord(0, 0), s.FastMapTextureCoords(0), channels), + new Vertex(coord(su, 0), s.FastMapTextureCoords(1), channels), + new Vertex(coord(su, sv), s.FastMapTextureCoords(3), channels), + new Vertex(coord(0, sv), s.FastMapTextureCoords(2), channels) + }; + } + + IEnumerable GenerateSlicePlanes(VxlLimb l) + { + Func get = (x,y,z) => + { + if (x < 0 || y < 0 || z < 0) + return null; + + if (x >= l.Size[0] || y >= l.Size[1] || z >= l.Size[2]) + return null; + + var v = l.VoxelMap[(byte)x,(byte)y]; + if (v == null || !v.ContainsKey((byte)z)) + return null; + + return l.VoxelMap[(byte)x,(byte)y][(byte)z]; + }; + + // Cull slices without any visible faces + var xPlanes = new bool[l.Size[0]+1]; + var yPlanes = new bool[l.Size[1]+1]; + var zPlanes = new bool[l.Size[2]+1]; + for (var x = 0; x < l.Size[0]; x++) + { + for (var y = 0; y < l.Size[1]; y++) + { + for (var z = 0; z < l.Size[2]; z++) + { + if (get(x,y,z) == null) + continue; + + // Only generate a plane if it is actually visible + if (!xPlanes[x] && get(x-1,y,z) == null) + xPlanes[x] = true; + if (!xPlanes[x+1] && get(x+1,y,z) == null) + xPlanes[x+1] = true; + + if (!yPlanes[y] && get(x,y-1,z) == null) + yPlanes[y] = true; + if (!yPlanes[y+1] && get(x,y+1,z) == null) + yPlanes[y+1] = true; + + if (!zPlanes[z] && get(x,y,z-1) == null) + zPlanes[z] = true; + if (!zPlanes[z+1] && get(x,y,z+1) == null) + zPlanes[z+1] = true; + } + } + } + + for (var x = 0; x <= l.Size[0]; x++) + if (xPlanes[x]) + 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}); + + 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}); + + 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}); + } + + public VoxelRenderData GenerateRenderData(VxlLimb l) + { + Vertex[] v; + try + { + v = GenerateSlicePlanes(l).SelectMany(x => x).ToArray(); + } + catch (SheetOverflowException) + { + // Sheet overflow - allocate a new sheet and try once more + Log.Write("debug", "Voxel sheet overflow! Generating new sheet"); + sheetBuilder = new SheetBuilder(SheetType.DualIndexed); + v = GenerateSlicePlanes(l).SelectMany(x => x).ToArray(); + } + + vertices.Add(v); + + var start = totalVertexCount; + var count = v.Length; + totalVertexCount += count; + return new VoxelRenderData(start, count, sheetBuilder.Current); + } + + public void RefreshBuffer() + { + vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(totalVertexCount); + vertexBuffer.SetData(vertices.SelectMany(v => v).ToArray(), totalVertexCount); + cachedVertexCount = totalVertexCount; + } + + public IVertexBuffer VertexBuffer + { + get + { + if (cachedVertexCount != totalVertexCount) + RefreshBuffer(); + return vertexBuffer; + } + } + } +} diff --git a/OpenRA.Game/Graphics/VoxelRenderer.cs b/OpenRA.Game/Graphics/VoxelRenderer.cs new file mode 100644 index 0000000000..2961f13322 --- /dev/null +++ b/OpenRA.Game/Graphics/VoxelRenderer.cs @@ -0,0 +1,88 @@ +#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; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.FileFormats.Graphics; + +namespace OpenRA.Graphics +{ + public class VoxelRenderer + { + Renderer renderer; + IShader shader; + IShader shadowShader; + + public VoxelRenderer(Renderer renderer, IShader shader, IShader shadowShader) + { + 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)); + } + + 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 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 + }; + + shader.SetMatrix("View", view); + shadowShader.SetMatrix("View", view); + } + } +} diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index ac7e753e5f..453e4a0fe1 100755 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -28,6 +28,7 @@ namespace OpenRA public ILoadScreen LoadScreen = null; public SheetBuilder SheetBuilder; public SpriteLoader SpriteLoader; + public VoxelLoader VoxelLoader; public ModData(params string[] mods) { @@ -55,6 +56,7 @@ namespace OpenRA ChromeProvider.Initialize(Manifest.Chrome); SheetBuilder = new SheetBuilder(SheetType.Indexed); SpriteLoader = new SpriteLoader(new string[] { ".shp" }, SheetBuilder); + VoxelLoader = new VoxelLoader(); CursorProvider.Initialize(Manifest.Cursors); } diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index ce040adc49..7012afba9f 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -226,6 +226,9 @@ + + + diff --git a/cg/vxl.fx b/cg/vxl.fx new file mode 100644 index 0000000000..c0f8395937 --- /dev/null +++ b/cg/vxl.fx @@ -0,0 +1,86 @@ +mat4x4 View; +mat4x4 TransformMatrix; +float2 PaletteRows; + +float4 LightDirection; +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) { + VertexOut o; + o.Position = mul(mul(v.Position, 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; + + float4 normal = (2.0*tex2D(Palette, vec2(dot(x, f.NormalsChannel), PaletteRows.y)) - 1.0); + float3 intensity = AmbientLight + DiffuseLight*max(dot(normal, LightDirection), 0.0); + return float4(intensity*color.rgb, color.a); +} + +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/cg/vxlshadow.fx b/cg/vxlshadow.fx new file mode 100644 index 0000000000..43a41ec8a3 --- /dev/null +++ b/cg/vxlshadow.fx @@ -0,0 +1,90 @@ +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/vxl.frag b/glsl/vxl.frag new file mode 100644 index 0000000000..d9d65ac2ba --- /dev/null +++ b/glsl/vxl.frag @@ -0,0 +1,17 @@ +uniform sampler2D Palette, DiffuseTexture; +uniform vec2 PaletteRows; + +uniform vec4 LightDirection; +uniform vec3 AmbientLight, DiffuseLight; + +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; + + vec4 normal = (2.0*texture2D(Palette, vec2(dot(x, gl_TexCoord[2]), PaletteRows.y)) - 1.0); + vec3 intensity = AmbientLight + DiffuseLight*max(dot(normal, LightDirection), 0.0); + gl_FragColor = vec4(intensity*color.rgb, color.a); +} diff --git a/glsl/vxl.vert b/glsl/vxl.vert new file mode 100644 index 0000000000..8eb665dddb --- /dev/null +++ b/glsl/vxl.vert @@ -0,0 +1,18 @@ +uniform mat4 View; +uniform mat4 TransformMatrix; + +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() +{ + gl_Position = View*TransformMatrix*gl_Vertex; + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1] = DecodeChannelMask(gl_MultiTexCoord0.z); + gl_TexCoord[2] = DecodeChannelMask(gl_MultiTexCoord0.w); +} diff --git a/glsl/vxlshadow.frag b/glsl/vxlshadow.frag new file mode 100644 index 0000000000..e75b8403ae --- /dev/null +++ b/glsl/vxlshadow.frag @@ -0,0 +1,12 @@ +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 new file mode 100644 index 0000000000..89b8671de1 --- /dev/null +++ b/glsl/vxlshadow.vert @@ -0,0 +1,26 @@ +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 82faf7e929f06df3c2b009997fab6c9d566e92e7 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 23 Feb 2013 14:42:44 +1300 Subject: [PATCH 12/15] Allow mods and maps to define voxel sequences. --- OpenRA.FileFormats/Manifest.cs | 3 +- OpenRA.Game/Graphics/VoxelLoader.cs | 14 +++++ OpenRA.Game/Graphics/VoxelProvider.cs | 88 +++++++++++++++++++++++++++ OpenRA.Game/Map.cs | 3 + OpenRA.Game/ModData.cs | 2 +- OpenRA.Game/OpenRA.Game.csproj | 1 + 6 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 OpenRA.Game/Graphics/VoxelProvider.cs diff --git a/OpenRA.FileFormats/Manifest.cs b/OpenRA.FileFormats/Manifest.cs index 4fc63cc62f..54c46ea58e 100644 --- a/OpenRA.FileFormats/Manifest.cs +++ b/OpenRA.FileFormats/Manifest.cs @@ -20,7 +20,7 @@ namespace OpenRA.FileFormats { public readonly string[] Mods, Folders, Rules, ServerTraits, - Sequences, Cursors, Chrome, Assemblies, ChromeLayout, + Sequences, VoxelSequences, Cursors, Chrome, Assemblies, ChromeLayout, Weapons, Voices, Notifications, Music, Movies, TileSets, ChromeMetrics, PackageContents; @@ -42,6 +42,7 @@ namespace OpenRA.FileFormats Rules = YamlList(yaml, "Rules"); ServerTraits = YamlList(yaml, "ServerTraits"); Sequences = YamlList(yaml, "Sequences"); + VoxelSequences = YamlList(yaml, "VoxelSequences"); Cursors = YamlList(yaml, "Cursors"); Chrome = YamlList(yaml, "Chrome"); Assemblies = YamlList(yaml, "Assemblies"); diff --git a/OpenRA.Game/Graphics/VoxelLoader.cs b/OpenRA.Game/Graphics/VoxelLoader.cs index bf016e8b93..89ad57590d 100644 --- a/OpenRA.Game/Graphics/VoxelLoader.cs +++ b/OpenRA.Game/Graphics/VoxelLoader.cs @@ -35,6 +35,7 @@ namespace OpenRA.Graphics { SheetBuilder sheetBuilder; + Cache, Voxel> voxels; IVertexBuffer vertexBuffer; List vertices; int totalVertexCount; @@ -42,6 +43,7 @@ namespace OpenRA.Graphics public VoxelLoader() { + voxels = new Cache, Voxel>(LoadFile); vertices = new List(); totalVertexCount = 0; cachedVertexCount = 0; @@ -190,5 +192,17 @@ namespace OpenRA.Graphics return vertexBuffer; } } + + Voxel LoadFile(Pair files) + { + var vxl = new VxlReader(FileSystem.OpenWithExts(files.First, ".vxl")); + var hva = new HvaReader(FileSystem.OpenWithExts(files.Second, ".hva")); + return new Voxel(this, vxl, hva); + } + + public Voxel Load(string vxl, string hva) + { + return voxels[Pair.New(vxl, hva)]; + } } } diff --git a/OpenRA.Game/Graphics/VoxelProvider.cs b/OpenRA.Game/Graphics/VoxelProvider.cs new file mode 100644 index 0000000000..e9f764fb31 --- /dev/null +++ b/OpenRA.Game/Graphics/VoxelProvider.cs @@ -0,0 +1,88 @@ +#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; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using OpenRA.FileFormats; + +namespace OpenRA.Graphics +{ + public static class VoxelProvider + { + static Dictionary> units; + + public static void Initialize(string[] voxelFiles, List voxelNodes) + { + units = new Dictionary>(); + + var sequences = voxelFiles + .Select(s => MiniYaml.FromFile(s)) + .Aggregate(voxelNodes, MiniYaml.MergeLiberal); + + foreach (var s in sequences) + LoadVoxelsForUnit(s.Key, s.Value); + + Game.modData.VoxelLoader.RefreshBuffer(); + } + + static Voxel LoadVoxel(string unit, string name, MiniYaml info) + { + var vxl = unit; + var hva = unit; + if (info.Value != null) + { + var fields = info.Value.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries); + if (fields.Length >= 1) + vxl = hva = fields[0].Trim(); + + if (fields.Length >= 2) + hva = fields[1].Trim(); + } + + return Game.modData.VoxelLoader.Load(vxl, hva); + } + + static void LoadVoxelsForUnit(string unit, MiniYaml sequences) + { + Game.modData.LoadScreen.Display(); + try + { + var seq = sequences.NodesDict.ToDictionary(x => x.Key, x => LoadVoxel(unit,x.Key,x.Value)); + units.Add(unit, seq); + } + catch (FileNotFoundException) {} // Do nothing; we can crash later if we actually wanted art + } + + public static Voxel GetVoxel(string unitName, string voxelName) + { + try { return units[unitName][voxelName]; } + catch (KeyNotFoundException) + { + if (units.ContainsKey(unitName)) + throw new InvalidOperationException( + "Unit `{0}` does not have a voxel `{1}`".F(unitName, voxelName)); + else + throw new InvalidOperationException( + "Unit `{0}` does not have any voxels defined.".F(unitName)); + } + } + + public static bool HasVoxel(string unit, string seq) + { + if (!units.ContainsKey(unit)) + throw new InvalidOperationException( + "Unit `{0}` does not have any voxels defined.".F(unit)); + + return units[unit].ContainsKey(seq); + } + } +} diff --git a/OpenRA.Game/Map.cs b/OpenRA.Game/Map.cs index 45a590f93b..9b532ecedf 100644 --- a/OpenRA.Game/Map.cs +++ b/OpenRA.Game/Map.cs @@ -59,6 +59,7 @@ namespace OpenRA [FieldLoader.Ignore] public List Rules = new List(); [FieldLoader.Ignore] public List Sequences = new List(); + [FieldLoader.Ignore] public List VoxelSequences = new List(); [FieldLoader.Ignore] public List Weapons = new List(); [FieldLoader.Ignore] public List Voices = new List(); [FieldLoader.Ignore] public List Notifications = new List(); @@ -150,6 +151,7 @@ namespace OpenRA Rules = NodesOrEmpty(yaml, "Rules"); Sequences = NodesOrEmpty(yaml, "Sequences"); + VoxelSequences = NodesOrEmpty(yaml, "VoxelSequences"); Weapons = NodesOrEmpty(yaml, "Weapons"); Voices = NodesOrEmpty(yaml, "Voices"); Notifications = NodesOrEmpty(yaml, "Notifications"); @@ -206,6 +208,7 @@ namespace OpenRA root.Add(new MiniYamlNode("Smudges", MiniYaml.FromList( Smudges.Value ))); root.Add(new MiniYamlNode("Rules", null, Rules)); root.Add(new MiniYamlNode("Sequences", null, Sequences)); + root.Add(new MiniYamlNode("VoxelSequences", null, VoxelSequences)); root.Add(new MiniYamlNode("Weapons", null, Weapons)); root.Add(new MiniYamlNode("Voices", null, Voices)); root.Add(new MiniYamlNode("Notifications", null, Notifications)); diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index 453e4a0fe1..65a8fa2d43 100755 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -78,7 +78,7 @@ namespace OpenRA SpriteLoader = new SpriteLoader(Rules.TileSets[map.Tileset].Extensions, SheetBuilder); // TODO: Don't load the sequences for assets that are not used in this tileset. Maybe use the existing EditorTilesetFilters. SequenceProvider.Initialize(Manifest.Sequences, map.Sequences); - + VoxelProvider.Initialize(Manifest.VoxelSequences, map.VoxelSequences); return map; } diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 7012afba9f..01c53d1d11 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -229,6 +229,7 @@ + From 235042ea6517fb3bb31f002ff00a3bc85d92379e Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 23 Feb 2013 20:49:43 +1300 Subject: [PATCH 13/15] Add VoxelRenderable to integrate voxel drawing with render traits. --- OpenRA.Game/Graphics/VoxelAnimation.cs | 35 ++++++++ OpenRA.Game/Graphics/VoxelRenderable.cs | 106 ++++++++++++++++++++++++ OpenRA.Game/OpenRA.Game.csproj | 2 + 3 files changed, 143 insertions(+) create mode 100644 OpenRA.Game/Graphics/VoxelAnimation.cs create mode 100644 OpenRA.Game/Graphics/VoxelRenderable.cs diff --git a/OpenRA.Game/Graphics/VoxelAnimation.cs b/OpenRA.Game/Graphics/VoxelAnimation.cs new file mode 100644 index 0000000000..7cbb003878 --- /dev/null +++ b/OpenRA.Game/Graphics/VoxelAnimation.cs @@ -0,0 +1,35 @@ +#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.Collections.Generic; +using OpenRA.Traits; + +namespace OpenRA.Graphics +{ + public struct VoxelAnimation + { + public readonly Voxel Voxel; + public readonly Func OffsetFunc; + public readonly Func> RotationFunc; + public readonly Func DisableFunc; + public readonly Func FrameFunc; + + public VoxelAnimation(Voxel voxel, Func offset, Func> rotation, Func disable, Func frame) + { + Voxel = voxel; + OffsetFunc = offset; + RotationFunc = rotation; + DisableFunc = disable; + FrameFunc = frame; + } + } +} + diff --git a/OpenRA.Game/Graphics/VoxelRenderable.cs b/OpenRA.Game/Graphics/VoxelRenderable.cs new file mode 100644 index 0000000000..168d3b0513 --- /dev/null +++ b/OpenRA.Game/Graphics/VoxelRenderable.cs @@ -0,0 +1,106 @@ +#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; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; + +namespace OpenRA.Graphics +{ + public struct VoxelRenderable : IRenderable + { + readonly IEnumerable voxels; + readonly WPos pos; + readonly int zOffset; + readonly WRot camera; + readonly WRot lightSource; + readonly float[] lightAmbientColor; + readonly float[] lightDiffuseColor; + readonly PaletteReference palette; + readonly PaletteReference normalsPalette; + readonly PaletteReference shadowPalette; + readonly float scale; + + public VoxelRenderable(IEnumerable voxels, WPos pos, int zOffset, WRot camera, float scale, + WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, + PaletteReference color, PaletteReference normals, PaletteReference shadow) + { + this.voxels = voxels; + this.pos = pos; + this.zOffset = zOffset; + this.scale = scale; + this.camera = camera; + this.lightSource = lightSource; + this.lightAmbientColor = lightAmbientColor; + this.lightDiffuseColor = lightDiffuseColor; + this.palette = color; + this.normalsPalette = normals; + this.shadowPalette = shadow; + } + + public WPos Pos { get { return pos; } } + public float Scale { get { return scale; } } + public PaletteReference Palette { get { return palette; } } + public int ZOffset { get { return zOffset; } } + + public IRenderable WithScale(float newScale) + { + return new VoxelRenderable(voxels, pos, zOffset, camera, newScale, + lightSource, lightAmbientColor, lightDiffuseColor, + palette, normalsPalette, shadowPalette); + } + + public IRenderable WithPalette(PaletteReference newPalette) + { + return new VoxelRenderable(voxels, pos, zOffset, camera, scale, + lightSource, lightAmbientColor, lightDiffuseColor, + newPalette, normalsPalette, shadowPalette); + } + + public IRenderable WithZOffset(int newOffset) + { + return new VoxelRenderable(voxels, pos, newOffset, camera, scale, + lightSource, lightAmbientColor, lightDiffuseColor, + palette, normalsPalette, shadowPalette); + } + + public IRenderable WithPos(WPos newPos) + { + return new VoxelRenderable(voxels, newPos, zOffset, camera, scale, + 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()); + + 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(); + } + } +} diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 01c53d1d11..e16d1f3b80 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -231,6 +231,8 @@ + + From 5cfde95358f7fe934acb7151456752376d8d7452 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 1 Jun 2013 14:57:30 +1200 Subject: [PATCH 14/15] Add a basic set of voxel render traits. --- OpenRA.Mods.RA/OpenRA.Mods.RA.csproj | 4 ++ OpenRA.Mods.RA/Render/RenderVoxels.cs | 85 ++++++++++++++++++++++++ OpenRA.Mods.RA/Render/WithVoxelBarrel.cs | 75 +++++++++++++++++++++ OpenRA.Mods.RA/Render/WithVoxelBody.cs | 47 +++++++++++++ OpenRA.Mods.RA/Render/WithVoxelTurret.cs | 58 ++++++++++++++++ 5 files changed, 269 insertions(+) create mode 100755 OpenRA.Mods.RA/Render/RenderVoxels.cs create mode 100755 OpenRA.Mods.RA/Render/WithVoxelBarrel.cs create mode 100755 OpenRA.Mods.RA/Render/WithVoxelBody.cs create mode 100755 OpenRA.Mods.RA/Render/WithVoxelTurret.cs diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 5aa44eec6f..1b7998fdaf 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -440,6 +440,10 @@ + + + + diff --git a/OpenRA.Mods.RA/Render/RenderVoxels.cs b/OpenRA.Mods.RA/Render/RenderVoxels.cs new file mode 100755 index 0000000000..ecf9e20960 --- /dev/null +++ b/OpenRA.Mods.RA/Render/RenderVoxels.cs @@ -0,0 +1,85 @@ +#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; +using System.Collections.Generic; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Render +{ + public class RenderVoxelsInfo : ITraitInfo, Requires + { + [Desc("Defaults to the actor name.")] + public readonly string Image = null; + + [Desc("Custom palette name")] + public readonly string Palette = null; + + [Desc("Custom PlayerColorPalette: BaseName")] + public readonly string PlayerPalette = "player"; + public readonly string NormalsPalette = "normals"; + + [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 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); } + } + + public class RenderVoxels : IRender, INotifyOwnerChanged + { + Actor self; + RenderVoxelsInfo info; + List components = new List(); + IBodyOrientation body; + WRot camera; + WRot lightSource; + + public RenderVoxels(Actor self, RenderVoxelsInfo info) + { + this.self = self; + 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)); + } + + bool initializePalettes = true; + public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { initializePalettes = true; } + + protected PaletteReference colorPalette, normalsPalette, shadowPalette; + public IEnumerable Render(Actor self, WorldRenderer wr) + { + if (initializePalettes) + { + var paletteName = info.Palette ?? info.PlayerPalette + self.Owner.InternalName; + colorPalette = wr.Palette(paletteName); + normalsPalette = wr.Palette(info.NormalsPalette); + shadowPalette = wr.Palette("shadow"); + initializePalettes = false; + } + + yield return new VoxelRenderable(components, self.CenterPosition, 0, camera, info.Scale, + lightSource, info.LightAmbientColor, info.LightDiffuseColor, + colorPalette, normalsPalette, shadowPalette); + } + + public string Image { get { return info.Image ?? self.Info.Name; } } + public void Add(VoxelAnimation v) { components.Add(v); } + public void Remove(VoxelAnimation v) { components.Remove(v); } + } +} diff --git a/OpenRA.Mods.RA/Render/WithVoxelBarrel.cs b/OpenRA.Mods.RA/Render/WithVoxelBarrel.cs new file mode 100755 index 0000000000..afb3cd8ac3 --- /dev/null +++ b/OpenRA.Mods.RA/Render/WithVoxelBarrel.cs @@ -0,0 +1,75 @@ +#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; +using System.Collections.Generic; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Render +{ + public class WithVoxelBarrelInfo : ITraitInfo, Requires + { + [Desc("Voxel sequence name to use")] + public readonly string Sequence = "barrel"; + [Desc("Armament to use for recoil")] + public readonly string Armament = "primary"; + [Desc("Visual offset")] + public readonly WVec LocalOffset = WVec.Zero; + + public object Create(ActorInitializer init) { return new WithVoxelBarrel(init.self, this); } + } + + public class WithVoxelBarrel + { + WithVoxelBarrelInfo info; + Actor self; + Armament armament; + Turreted turreted; + IBodyOrientation body; + + public WithVoxelBarrel(Actor self, WithVoxelBarrelInfo info) + { + this.self = self; + this.info = info; + body = self.Trait(); + armament = self.TraitsImplementing() + .First(a => a.Info.Name == info.Armament); + turreted = self.TraitsImplementing() + .First(tt => tt.Name == armament.Info.Turret); + + var rv = self.Trait(); + rv.Add(new VoxelAnimation(VoxelProvider.GetVoxel(rv.Image, info.Sequence), + () => BarrelOffset(), () => BarrelRotation(), + () => false, () => 0)); + } + + WVec BarrelOffset() + { + var localOffset = info.LocalOffset + new WVec(-armament.Recoil, WRange.Zero, WRange.Zero); + var turretOffset = turreted != null ? turreted.Position(self) : WVec.Zero; + var turretOrientation = turreted != null ? turreted.LocalOrientation(self) : WRot.Zero; + + var quantizedBody = body.QuantizeOrientation(self, self.Orientation); + var quantizedTurret = body.QuantizeOrientation(self, turretOrientation); + return turretOffset + body.LocalToWorld(localOffset.Rotate(quantizedTurret).Rotate(quantizedBody)); + } + + IEnumerable BarrelRotation() + { + var b = self.Orientation; + var qb = body.QuantizeOrientation(self, b); + yield return turreted.LocalOrientation(self) + WRot.FromYaw(b.Yaw - qb.Yaw); + yield return qb; + } + } +} diff --git a/OpenRA.Mods.RA/Render/WithVoxelBody.cs b/OpenRA.Mods.RA/Render/WithVoxelBody.cs new file mode 100755 index 0000000000..175ac1ed4c --- /dev/null +++ b/OpenRA.Mods.RA/Render/WithVoxelBody.cs @@ -0,0 +1,47 @@ +#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; +using System.Collections.Generic; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Render +{ + public class WithVoxelBodyInfo : ITraitInfo, Requires + { + public object Create(ActorInitializer init) { return new WithVoxelBody(init.self); } + } + + public class WithVoxelBody : IAutoSelectionSize + { + int2 size; + + public WithVoxelBody(Actor self) + { + var body = self.Trait(); + var rv = self.Trait(); + + var voxel = VoxelProvider.GetVoxel(rv.Image, "idle"); + rv.Add(new VoxelAnimation(voxel, () => WVec.Zero, + () => new[]{ body.QuantizeOrientation(self, self.Orientation) }, + () => false, () => 0)); + + // Selection size + var rvi = self.Info.Traits.Get(); + var s = (int)(rvi.Scale*voxel.Size.Aggregate(Math.Max)); + size = new int2(s, s); + } + + public int2 SelectionSize(Actor self) { return size; } + } +} diff --git a/OpenRA.Mods.RA/Render/WithVoxelTurret.cs b/OpenRA.Mods.RA/Render/WithVoxelTurret.cs new file mode 100755 index 0000000000..29f47c8fe2 --- /dev/null +++ b/OpenRA.Mods.RA/Render/WithVoxelTurret.cs @@ -0,0 +1,58 @@ +#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; +using System.Collections.Generic; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Render +{ + public class WithVoxelTurretInfo : ITraitInfo, Requires + { + [Desc("Voxel sequence name to use")] + public readonly string Sequence = "turret"; + + [Desc("Turreted 'Turret' key to display")] + public readonly string Turret = "primary"; + + public object Create(ActorInitializer init) { return new WithVoxelTurret(init.self, this); } + } + + public class WithVoxelTurret + { + Actor self; + Turreted turreted; + IBodyOrientation body; + + public WithVoxelTurret(Actor self, WithVoxelTurretInfo info) + { + this.self = self; + body = self.Trait(); + turreted = self.TraitsImplementing() + .First(tt => tt.Name == info.Turret); + + var rv = self.Trait(); + rv.Add(new VoxelAnimation(VoxelProvider.GetVoxel(rv.Image, info.Sequence), + () => turreted.Position(self), () => TurretRotation(), + () => false, () => 0)); + } + + IEnumerable TurretRotation() + { + var b = self.Orientation; + var qb = body.QuantizeOrientation(self, b); + yield return turreted.LocalOrientation(self) + WRot.FromYaw(b.Yaw - qb.Yaw); + yield return qb; + } + } +} From 7fb643f962f64ca23862f9aa824557cc237b3951 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 1 Jun 2013 20:58:46 +1200 Subject: [PATCH 15/15] Add render trait for Mammoth Mk II. --- OpenRA.Mods.RA/OpenRA.Mods.RA.csproj | 1 + OpenRA.Mods.RA/Render/WithVoxelWalkerBody.cs | 69 ++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100755 OpenRA.Mods.RA/Render/WithVoxelWalkerBody.cs diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 1b7998fdaf..08a365901f 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -444,6 +444,7 @@ + diff --git a/OpenRA.Mods.RA/Render/WithVoxelWalkerBody.cs b/OpenRA.Mods.RA/Render/WithVoxelWalkerBody.cs new file mode 100755 index 0000000000..4a2181f676 --- /dev/null +++ b/OpenRA.Mods.RA/Render/WithVoxelWalkerBody.cs @@ -0,0 +1,69 @@ +#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; +using System.Collections.Generic; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Mods.RA.Move; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Render +{ + public class WithVoxelWalkerBodyInfo : ITraitInfo, Requires, Requires + { + public readonly int TickRate = 5; + public object Create(ActorInitializer init) { return new WithVoxelWalkerBody(init.self, this); } + } + + public class WithVoxelWalkerBody : IAutoSelectionSize, ITick + { + WithVoxelWalkerBodyInfo info; + Mobile mobile; + int2 size; + uint tick, frame, frames; + + public WithVoxelWalkerBody(Actor self, WithVoxelWalkerBodyInfo info) + { + this.info = info; + mobile = self.Trait(); + + var body = self.Trait(); + var rv = self.Trait(); + + var voxel = VoxelProvider.GetVoxel(rv.Image, "idle"); + frames = voxel.Frames; + rv.Add(new VoxelAnimation(voxel, () => WVec.Zero, + () => new[]{ body.QuantizeOrientation(self, self.Orientation) }, + () => false, () => frame)); + + // Selection size + var rvi = self.Info.Traits.Get(); + var s = (int)(rvi.Scale*voxel.Size.Aggregate(Math.Max)); + size = new int2(s, s); + } + + public int2 SelectionSize(Actor self) { return size; } + + public void Tick(Actor self) + { + if (mobile.IsMoving) + tick++; + + if (tick < info.TickRate) + return; + + tick = 0; + if (++frame == frames) + frame = 0; + } + } +}