Merge pull request #3361 from pchote/voxels

TS/RA2 Voxel support
This commit is contained in:
Chris Forbes
2013-06-06 17:25:39 -07:00
35 changed files with 2201 additions and 4 deletions

View File

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

View File

@@ -50,6 +50,12 @@ namespace OpenRA.FileFormats.Graphics
void SetLineWidth( float width );
void EnableScissor( int left, int top, int width, int height );
void DisableScissor();
void EnableDepthBuffer();
void DisableDepthBuffer();
void EnableStencilBuffer();
void DisableStencilBuffer();
}
public interface IVertexBuffer<T>
@@ -60,8 +66,11 @@ 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);
}

View File

@@ -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;
}
}
}

View File

@@ -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<byte, VxlElement>[,] 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<byte, VxlElement>[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<byte, VxlElement>();
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);
}
}
}

View File

@@ -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");

View File

@@ -141,6 +141,8 @@
<Compile Include="Graphics\ShpTSReader.cs" />
<Compile Include="FileFormats\XccLocalDatabase.cs" />
<Compile Include="FileFormats\XccGlobalDatabase.cs" />
<Compile Include="Graphics\VxlReader.cs" />
<Compile Include="Graphics\HvaReader.cs" />
<Compile Include="StreamExts.cs" />
</ItemGroup>
<ItemGroup>

View File

@@ -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)
@@ -186,5 +190,29 @@ namespace OpenRA.Graphics
Flush();
Device.DisableScissor();
}
public void EnableDepthBuffer()
{
Flush();
Device.EnableDepthBuffer();
}
public void DisableDepthBuffer()
{
Flush();
Device.DisableDepthBuffer();
}
public void EnableStencilBuffer()
{
Flush();
Device.EnableStencilBuffer();
}
public void DisableStencilBuffer()
{
Flush();
Device.DisableStencilBuffer();
}
}
}

View File

@@ -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;
}
}
}

View 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])
});
}
}
}
}

View File

@@ -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<WVec> OffsetFunc;
public readonly Func<IEnumerable<WRot>> RotationFunc;
public readonly Func<bool> DisableFunc;
public readonly Func<uint> FrameFunc;
public VoxelAnimation(Voxel voxel, Func<WVec> offset, Func<IEnumerable<WRot>> rotation, Func<bool> disable, Func<uint> frame)
{
Voxel = voxel;
OffsetFunc = offset;
RotationFunc = rotation;
DisableFunc = disable;
FrameFunc = frame;
}
}
}

View File

@@ -0,0 +1,208 @@
#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;
Cache<Pair<string,string>, Voxel> voxels;
IVertexBuffer<Vertex> vertexBuffer;
List<Vertex[]> vertices;
int totalVertexCount;
int cachedVertexCount;
public VoxelLoader()
{
voxels = new Cache<Pair<string,string>, Voxel>(LoadFile);
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;
}
}
Voxel LoadFile(Pair<string,string> 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)];
}
}
}

View 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.IO;
using System.Linq;
using OpenRA.FileFormats;
namespace OpenRA.Graphics
{
public static class VoxelProvider
{
static Dictionary<string, Dictionary<string, Voxel>> units;
public static void Initialize(string[] voxelFiles, List<MiniYamlNode> voxelNodes)
{
units = new Dictionary<string, Dictionary<string, Voxel>>();
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);
}
}
}

View File

@@ -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<VoxelAnimation> 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<VoxelAnimation> 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();
}
}
}

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

View File

@@ -59,6 +59,7 @@ namespace OpenRA
[FieldLoader.Ignore] public List<MiniYamlNode> Rules = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Sequences = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> VoxelSequences = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Weapons = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Voices = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Notifications = new List<MiniYamlNode>();
@@ -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<SmudgeReference>( 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));

View File

@@ -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);
}
@@ -76,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;
}

View File

@@ -226,7 +226,13 @@
<Compile Include="Widgets\ClientTooltipRegionWidget.cs" />
<Compile Include="Graphics\Renderable.cs" />
<Compile Include="Traits\Render\RenderSprites.cs" />
<Compile Include="Graphics\Voxel.cs" />
<Compile Include="Graphics\VoxelRenderer.cs" />
<Compile Include="Graphics\VoxelLoader.cs" />
<Compile Include="Graphics\VoxelProvider.cs" />
<Compile Include="Traits\BodyOrientation.cs" />
<Compile Include="Graphics\VoxelAnimation.cs" />
<Compile Include="Graphics\VoxelRenderable.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -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<AttackBaseInfo>
{
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;

View File

@@ -439,6 +439,12 @@
<Compile Include="Render\WithTurret.cs" />
<Compile Include="Widgets\Logic\AssetBrowserLogic.cs" />
<Compile Include="Widgets\Logic\ConvertGameFilesLogic.cs" />
<Compile Include="VoxelNormalsPalette.cs" />
<Compile Include="Render\RenderVoxels.cs" />
<Compile Include="Render\WithVoxelTurret.cs" />
<Compile Include="Render\WithVoxelBody.cs" />
<Compile Include="Render\WithVoxelBarrel.cs" />
<Compile Include="Render\WithVoxelWalkerBody.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -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<IBodyOrientationInfo>
{
[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<VoxelAnimation> components = new List<VoxelAnimation>();
IBodyOrientation body;
WRot camera;
WRot lightSource;
public RenderVoxels(Actor self, RenderVoxelsInfo info)
{
this.self = self;
this.info = info;
body = self.Trait<IBodyOrientation>();
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<IRenderable> 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); }
}
}

View File

@@ -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<RenderVoxelsInfo>
{
[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<IBodyOrientation>();
armament = self.TraitsImplementing<Armament>()
.First(a => a.Info.Name == info.Armament);
turreted = self.TraitsImplementing<Turreted>()
.First(tt => tt.Name == armament.Info.Turret);
var rv = self.Trait<RenderVoxels>();
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<WRot> 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;
}
}
}

View File

@@ -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<RenderVoxelsInfo>
{
public object Create(ActorInitializer init) { return new WithVoxelBody(init.self); }
}
public class WithVoxelBody : IAutoSelectionSize
{
int2 size;
public WithVoxelBody(Actor self)
{
var body = self.Trait<IBodyOrientation>();
var rv = self.Trait<RenderVoxels>();
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<RenderVoxelsInfo>();
var s = (int)(rvi.Scale*voxel.Size.Aggregate(Math.Max));
size = new int2(s, s);
}
public int2 SelectionSize(Actor self) { return size; }
}
}

View File

@@ -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<RenderVoxelsInfo>
{
[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<IBodyOrientation>();
turreted = self.TraitsImplementing<Turreted>()
.First(tt => tt.Name == info.Turret);
var rv = self.Trait<RenderVoxels>();
rv.Add(new VoxelAnimation(VoxelProvider.GetVoxel(rv.Image, info.Sequence),
() => turreted.Position(self), () => TurretRotation(),
() => false, () => 0));
}
IEnumerable<WRot> 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;
}
}
}

View File

@@ -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<RenderVoxelsInfo>, Requires<MobileInfo>
{
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<Mobile>();
var body = self.Trait<IBodyOrientation>();
var rv = self.Trait<RenderVoxels>();
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<RenderVoxelsInfo>();
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;
}
}
}

View File

@@ -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
};
}
}

View File

@@ -75,11 +75,44 @@ 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);
if (param != IntPtr.Zero)
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)
throw new InvalidDataException("Invalid 4x4 matrix");
var param = Tao.Cg.Cg.cgGetNamedEffectParameter(effect, name);
if (param != IntPtr.Zero)
Tao.Cg.CgGl.cgGLSetMatrixParameterfr(param, mtx);
}
}
}

View File

@@ -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);
@@ -150,5 +160,33 @@ namespace OpenRA.Renderer.Glsl
Gl.glUniform2fARB(param,x,y);
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)
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();
}
}
}

View File

@@ -38,6 +38,12 @@ 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 EnableStencilBuffer() { }
public void DisableStencilBuffer() { }
public void Clear() { }
public void Present() { }
@@ -58,8 +64,11 @@ 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() { }
public void Render(Action a) { }
}

View File

@@ -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,38 @@ 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);
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;

86
cg/vxl.fx Normal file
View 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
View 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
View 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
View 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
View 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
View 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);
}