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