Add core voxel rendering code.
This commit is contained in:
@@ -30,6 +30,7 @@ namespace OpenRA.Graphics
|
|||||||
public SpriteRenderer WorldSpriteRenderer { get; private set; }
|
public SpriteRenderer WorldSpriteRenderer { get; private set; }
|
||||||
public QuadRenderer WorldQuadRenderer { get; private set; }
|
public QuadRenderer WorldQuadRenderer { get; private set; }
|
||||||
public LineRenderer WorldLineRenderer { get; private set; }
|
public LineRenderer WorldLineRenderer { get; private set; }
|
||||||
|
public VoxelRenderer WorldVoxelRenderer { get; private set; }
|
||||||
public LineRenderer LineRenderer { get; private set; }
|
public LineRenderer LineRenderer { get; private set; }
|
||||||
public SpriteRenderer RgbaSpriteRenderer { get; private set; }
|
public SpriteRenderer RgbaSpriteRenderer { get; private set; }
|
||||||
public SpriteRenderer SpriteRenderer { get; private set; }
|
public SpriteRenderer SpriteRenderer { get; private set; }
|
||||||
@@ -46,6 +47,7 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
WorldSpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp"));
|
WorldSpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp"));
|
||||||
WorldLineRenderer = new LineRenderer(this, device.CreateShader("line"));
|
WorldLineRenderer = new LineRenderer(this, device.CreateShader("line"));
|
||||||
|
WorldVoxelRenderer = new VoxelRenderer(this, device.CreateShader("vxl"), device.CreateShader("vxlshadow"));
|
||||||
LineRenderer = new LineRenderer(this, device.CreateShader("line"));
|
LineRenderer = new LineRenderer(this, device.CreateShader("line"));
|
||||||
WorldQuadRenderer = new QuadRenderer(this, device.CreateShader("line"));
|
WorldQuadRenderer = new QuadRenderer(this, device.CreateShader("line"));
|
||||||
RgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba"));
|
RgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba"));
|
||||||
@@ -71,6 +73,7 @@ namespace OpenRA.Graphics
|
|||||||
WorldLineRenderer.SetViewportParams(Resolution, zoom, scroll);
|
WorldLineRenderer.SetViewportParams(Resolution, zoom, scroll);
|
||||||
WorldQuadRenderer.SetViewportParams(Resolution, zoom, scroll);
|
WorldQuadRenderer.SetViewportParams(Resolution, zoom, scroll);
|
||||||
LineRenderer.SetViewportParams(Resolution, 1f, float2.Zero);
|
LineRenderer.SetViewportParams(Resolution, 1f, float2.Zero);
|
||||||
|
WorldVoxelRenderer.SetViewportParams(Resolution, zoom, scroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
ITexture currentPaletteTexture;
|
ITexture currentPaletteTexture;
|
||||||
@@ -85,6 +88,7 @@ namespace OpenRA.Graphics
|
|||||||
RgbaSpriteRenderer.SetPalette(currentPaletteTexture);
|
RgbaSpriteRenderer.SetPalette(currentPaletteTexture);
|
||||||
SpriteRenderer.SetPalette(currentPaletteTexture);
|
SpriteRenderer.SetPalette(currentPaletteTexture);
|
||||||
WorldSpriteRenderer.SetPalette(currentPaletteTexture);
|
WorldSpriteRenderer.SetPalette(currentPaletteTexture);
|
||||||
|
WorldVoxelRenderer.SetPalette(currentPaletteTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EndFrame(IInputHandler inputHandler)
|
public void EndFrame(IInputHandler inputHandler)
|
||||||
|
|||||||
161
OpenRA.Game/Graphics/Voxel.cs
Normal file
161
OpenRA.Game/Graphics/Voxel.cs
Normal file
@@ -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<WRot> rotations,
|
||||||
|
WRot camera, uint frame, float scale, WRot lightSource)
|
||||||
|
{
|
||||||
|
// Calculate the shared view matrix components
|
||||||
|
var pxPos = wr.ScreenPosition(pos);
|
||||||
|
|
||||||
|
// Rotations
|
||||||
|
var rot = rotations.Reverse().Aggregate(MakeFloatMatrix(camera.AsMatrix()),
|
||||||
|
(a,b) => Util.MatrixMultiply(a, MakeFloatMatrix(b.AsMatrix())));
|
||||||
|
|
||||||
|
// Each limb has its own transformation matrix
|
||||||
|
for (uint i = 0; i < limbs.Length; i++)
|
||||||
|
{
|
||||||
|
Limb l = limbs[i];
|
||||||
|
var t = Util.MatrixMultiply(Util.ScaleMatrix(1,-1,1),
|
||||||
|
hva.TransformationMatrix(i, frame));
|
||||||
|
// Fix limb position
|
||||||
|
t[12] *= l.Scale*(l.Bounds[3] - l.Bounds[0]) / l.Size[0];
|
||||||
|
t[13] *= l.Scale*(l.Bounds[4] - l.Bounds[1]) / l.Size[1];
|
||||||
|
t[14] *= l.Scale*(l.Bounds[5] - l.Bounds[2]) / l.Size[2];
|
||||||
|
t = Util.MatrixMultiply(t, Util.TranslationMatrix(l.Bounds[0], l.Bounds[1], l.Bounds[2]));
|
||||||
|
|
||||||
|
// Apply view matrix
|
||||||
|
transform[i] = Util.MatrixMultiply(Util.TranslationMatrix(pxPos.X, pxPos.Y, pxPos.Y),
|
||||||
|
Util.ScaleMatrix(scale*l.Scale, scale*l.Scale, scale*l.Scale));
|
||||||
|
transform[i] = Util.MatrixMultiply(transform[i], rot);
|
||||||
|
transform[i] = Util.MatrixMultiply(transform[i], t);
|
||||||
|
|
||||||
|
// Transform light direction into limb-space
|
||||||
|
var undoPitch = MakeFloatMatrix(new WRot(camera.Pitch, WAngle.Zero, WAngle.Zero).AsMatrix());
|
||||||
|
var lightTransform = Util.MatrixMultiply(Util.MatrixInverse(transform[i]), undoPitch);
|
||||||
|
|
||||||
|
lightDirection[i] = ExtractRotationVector(lightTransform, forward.Rotate(lightSource));
|
||||||
|
groundNormal[i] = ExtractRotationVector(Util.MatrixInverse(t), up);
|
||||||
|
|
||||||
|
// Hack: Extract the ground z position independently of y.
|
||||||
|
groundZ[i] = (wr.ScreenPosition(pos).Y - wr.ScreenZPosition(pos, 0)) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
194
OpenRA.Game/Graphics/VoxelLoader.cs
Normal file
194
OpenRA.Game/Graphics/VoxelLoader.cs
Normal file
@@ -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<Vertex> vertexBuffer;
|
||||||
|
List<Vertex[]> vertices;
|
||||||
|
int totalVertexCount;
|
||||||
|
int cachedVertexCount;
|
||||||
|
|
||||||
|
public VoxelLoader()
|
||||||
|
{
|
||||||
|
vertices = new List<Vertex[]>();
|
||||||
|
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<int,int,VxlElement> first, Func<int,int,VxlElement> second, Func<int, int, float[]> 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<Vertex[]> GenerateSlicePlanes(VxlLimb l)
|
||||||
|
{
|
||||||
|
Func<int,int,int,VxlElement> 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<Vertex> VertexBuffer
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (cachedVertexCount != totalVertexCount)
|
||||||
|
RefreshBuffer();
|
||||||
|
return vertexBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
88
OpenRA.Game/Graphics/VoxelRenderer.cs
Normal file
88
OpenRA.Game/Graphics/VoxelRenderer.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ namespace OpenRA
|
|||||||
public ILoadScreen LoadScreen = null;
|
public ILoadScreen LoadScreen = null;
|
||||||
public SheetBuilder SheetBuilder;
|
public SheetBuilder SheetBuilder;
|
||||||
public SpriteLoader SpriteLoader;
|
public SpriteLoader SpriteLoader;
|
||||||
|
public VoxelLoader VoxelLoader;
|
||||||
|
|
||||||
public ModData(params string[] mods)
|
public ModData(params string[] mods)
|
||||||
{
|
{
|
||||||
@@ -55,6 +56,7 @@ namespace OpenRA
|
|||||||
ChromeProvider.Initialize(Manifest.Chrome);
|
ChromeProvider.Initialize(Manifest.Chrome);
|
||||||
SheetBuilder = new SheetBuilder(SheetType.Indexed);
|
SheetBuilder = new SheetBuilder(SheetType.Indexed);
|
||||||
SpriteLoader = new SpriteLoader(new string[] { ".shp" }, SheetBuilder);
|
SpriteLoader = new SpriteLoader(new string[] { ".shp" }, SheetBuilder);
|
||||||
|
VoxelLoader = new VoxelLoader();
|
||||||
CursorProvider.Initialize(Manifest.Cursors);
|
CursorProvider.Initialize(Manifest.Cursors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -226,6 +226,9 @@
|
|||||||
<Compile Include="Widgets\ClientTooltipRegionWidget.cs" />
|
<Compile Include="Widgets\ClientTooltipRegionWidget.cs" />
|
||||||
<Compile Include="Graphics\Renderable.cs" />
|
<Compile Include="Graphics\Renderable.cs" />
|
||||||
<Compile Include="Traits\Render\RenderSprites.cs" />
|
<Compile Include="Traits\Render\RenderSprites.cs" />
|
||||||
|
<Compile Include="Graphics\Voxel.cs" />
|
||||||
|
<Compile Include="Graphics\VoxelRenderer.cs" />
|
||||||
|
<Compile Include="Graphics\VoxelLoader.cs" />
|
||||||
<Compile Include="Traits\BodyOrientation.cs" />
|
<Compile Include="Traits\BodyOrientation.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
86
cg/vxl.fx
Normal file
86
cg/vxl.fx
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
90
cg/vxlshadow.fx
Normal file
90
cg/vxlshadow.fx
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
glsl/vxl.frag
Normal file
17
glsl/vxl.frag
Normal file
@@ -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);
|
||||||
|
}
|
||||||
18
glsl/vxl.vert
Normal file
18
glsl/vxl.vert
Normal file
@@ -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);
|
||||||
|
}
|
||||||
12
glsl/vxlshadow.frag
Normal file
12
glsl/vxlshadow.frag
Normal file
@@ -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;
|
||||||
|
}
|
||||||
26
glsl/vxlshadow.vert
Normal file
26
glsl/vxlshadow.vert
Normal file
@@ -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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user