Reimplement voxel rendering with a FBO.
This commit is contained in:
@@ -19,7 +19,7 @@ namespace OpenRA.FileFormats
|
|||||||
{
|
{
|
||||||
public readonly uint FrameCount;
|
public readonly uint FrameCount;
|
||||||
public readonly uint LimbCount;
|
public readonly uint LimbCount;
|
||||||
float[] Transforms;
|
public readonly float[] Transforms;
|
||||||
|
|
||||||
public HvaReader(Stream s)
|
public HvaReader(Stream s)
|
||||||
{
|
{
|
||||||
@@ -48,19 +48,6 @@ namespace OpenRA.FileFormats
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
public static HvaReader Load(string filename)
|
||||||
{
|
{
|
||||||
using (var s = File.OpenRead(filename))
|
using (var s = File.OpenRead(filename))
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ namespace OpenRA.Graphics
|
|||||||
WorldSpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp"));
|
WorldSpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp"));
|
||||||
WorldRgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba"));
|
WorldRgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba"));
|
||||||
WorldLineRenderer = new LineRenderer(this, device.CreateShader("line"));
|
WorldLineRenderer = new LineRenderer(this, device.CreateShader("line"));
|
||||||
WorldVoxelRenderer = new VoxelRenderer(this, device.CreateShader("vxl"), device.CreateShader("vxlshadow"));
|
WorldVoxelRenderer = new VoxelRenderer(this, device.CreateShader("vxl"));
|
||||||
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"));
|
||||||
|
|||||||
@@ -27,19 +27,22 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
public class Voxel
|
public class Voxel
|
||||||
{
|
{
|
||||||
Limb[] limbs;
|
Limb[] limbData;
|
||||||
HvaReader hva;
|
float[] transforms;
|
||||||
VoxelLoader loader;
|
|
||||||
|
|
||||||
float[][] transform, lightDirection, groundNormal;
|
public readonly uint Frames;
|
||||||
float[] groundZ;
|
public readonly uint Limbs;
|
||||||
|
|
||||||
public Voxel(VoxelLoader loader, VxlReader vxl, HvaReader hva)
|
public Voxel(VoxelLoader loader, VxlReader vxl, HvaReader hva)
|
||||||
{
|
{
|
||||||
this.hva = hva;
|
if (vxl.LimbCount != hva.LimbCount)
|
||||||
this.loader = loader;
|
throw new InvalidOperationException("Voxel and hva limb counts don't match");
|
||||||
|
|
||||||
limbs = new Limb[vxl.LimbCount];
|
transforms = hva.Transforms;
|
||||||
|
Frames = hva.FrameCount;
|
||||||
|
Limbs = hva.LimbCount;
|
||||||
|
|
||||||
|
limbData = new Limb[vxl.LimbCount];
|
||||||
for (var i = 0; i < vxl.LimbCount; i++)
|
for (var i = 0; i < vxl.LimbCount; i++)
|
||||||
{
|
{
|
||||||
var vl = vxl.Limbs[i];
|
var vl = vxl.Limbs[i];
|
||||||
@@ -48,53 +51,20 @@ namespace OpenRA.Graphics
|
|||||||
l.Bounds = (float[])vl.Bounds.Clone();
|
l.Bounds = (float[])vl.Bounds.Clone();
|
||||||
l.Size = (byte[])vl.Size.Clone();
|
l.Size = (byte[])vl.Size.Clone();
|
||||||
l.RenderData = loader.GenerateRenderData(vxl.Limbs[i]);
|
l.RenderData = loader.GenerateRenderData(vxl.Limbs[i]);
|
||||||
limbs[i] = l;
|
limbData[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
|
public float[] TransformationMatrix(uint limb, uint frame)
|
||||||
static float[] ExtractRotationVector(float[] mtx, WVec vec)
|
|
||||||
{
|
{
|
||||||
var tVec = Util.MatrixVectorMultiply(mtx, new float[] {vec.X, vec.Y, vec.Z, 1});
|
if (frame >= Frames)
|
||||||
var tOrigin = Util.MatrixVectorMultiply(mtx, new float[] {0,0,0,1});
|
throw new ArgumentOutOfRangeException("frame", "Only {0} frames exist.".F(Frames));
|
||||||
tVec[0] -= tOrigin[0]*tVec[3]/tOrigin[3];
|
if (limb >= Limbs)
|
||||||
tVec[1] -= tOrigin[1]*tVec[3]/tOrigin[3];
|
throw new ArgumentOutOfRangeException("limb", "Only {1} limbs exist.".F(Limbs));
|
||||||
tVec[2] -= tOrigin[2]*tVec[3]/tOrigin[3];
|
|
||||||
|
|
||||||
// Renormalize
|
var l = limbData[limb];
|
||||||
var w = (float)Math.Sqrt(tVec[0]*tVec[0] + tVec[1]*tVec[1] + tVec[2]*tVec[2]);
|
var t = new float[16];
|
||||||
tVec[0] /= w;
|
Array.Copy(transforms, 16*(Limbs*frame + limb), t, 0, 16);
|
||||||
tVec[1] /= w;
|
|
||||||
tVec[2] /= w;
|
|
||||||
tVec[3] = 1f;
|
|
||||||
|
|
||||||
return tVec;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
float[] TransformationMatrix(uint limb, uint frame)
|
|
||||||
{
|
|
||||||
var l = limbs[limb];
|
|
||||||
var t = hva.TransformationMatrix(limb, frame);
|
|
||||||
|
|
||||||
// Fix limb position
|
// Fix limb position
|
||||||
t[12] *= l.Scale*(l.Bounds[3] - l.Bounds[0]) / l.Size[0];
|
t[12] *= l.Scale*(l.Bounds[3] - l.Bounds[0]) / l.Size[0];
|
||||||
@@ -108,46 +78,16 @@ namespace OpenRA.Graphics
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static readonly WVec forward = new WVec(1024,0,0);
|
public VoxelRenderData RenderData(uint limb)
|
||||||
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
|
return limbData[limb].RenderData;
|
||||||
var pxPos = wr.ScreenPosition(pos);
|
|
||||||
var posMtx = Util.TranslationMatrix(pxPos.X, pxPos.Y, pxPos.Y);
|
|
||||||
var scaleMtx = Util.ScaleMatrix(scale, scale, scale);
|
|
||||||
var rotMtx = rotations.Reverse().Aggregate(Util.MakeFloatMatrix(camera.AsMatrix()),
|
|
||||||
(a,b) => Util.MatrixMultiply(a, Util.MakeFloatMatrix(b.AsMatrix())));
|
|
||||||
|
|
||||||
// Each limb has its own transformation matrix
|
|
||||||
for (uint i = 0; i < limbs.Length; i++)
|
|
||||||
{
|
|
||||||
var t = TransformationMatrix(i, frame);
|
|
||||||
transform[i] = Util.MatrixMultiply(rotMtx, t);
|
|
||||||
transform[i] = Util.MatrixMultiply(scaleMtx, transform[i]);
|
|
||||||
transform[i] = Util.MatrixMultiply(posMtx, transform[i]);
|
|
||||||
|
|
||||||
// Transform light direction into limb-space
|
|
||||||
var undoPitch = Util.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
|
public float[] Size
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return limbs.Select(a => a.Size.Select(b => a.Scale*b).ToArray())
|
return limbData.Select(a => a.Size.Select(b => a.Scale*b).ToArray())
|
||||||
.Aggregate((a,b) => new float[]
|
.Aggregate((a,b) => new float[]
|
||||||
{
|
{
|
||||||
Math.Max(a[0], b[0]),
|
Math.Max(a[0], b[0]),
|
||||||
@@ -162,14 +102,15 @@ namespace OpenRA.Graphics
|
|||||||
var ret = new float[] {float.MaxValue,float.MaxValue,float.MaxValue,
|
var ret = new float[] {float.MaxValue,float.MaxValue,float.MaxValue,
|
||||||
float.MinValue,float.MinValue,float.MinValue};
|
float.MinValue,float.MinValue,float.MinValue};
|
||||||
|
|
||||||
for (uint j = 0; j < limbs.Length; j++)
|
for (uint j = 0; j < Limbs; j++)
|
||||||
{
|
{
|
||||||
|
var l = limbData[j];
|
||||||
var b = new float[]
|
var b = new float[]
|
||||||
{
|
{
|
||||||
0, 0, 0,
|
0, 0, 0,
|
||||||
(limbs[j].Bounds[3] - limbs[j].Bounds[0]),
|
(l.Bounds[3] - l.Bounds[0]),
|
||||||
(limbs[j].Bounds[4] - limbs[j].Bounds[1]),
|
(l.Bounds[4] - l.Bounds[1]),
|
||||||
(limbs[j].Bounds[5] - limbs[j].Bounds[2])
|
(l.Bounds[5] - l.Bounds[2])
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate limb bounding box
|
// Calculate limb bounding box
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ namespace OpenRA.Graphics
|
|||||||
readonly PaletteReference shadowPalette;
|
readonly PaletteReference shadowPalette;
|
||||||
readonly float scale;
|
readonly float scale;
|
||||||
|
|
||||||
|
// Generated at render-time
|
||||||
|
VoxelRenderProxy renderProxy;
|
||||||
|
|
||||||
public VoxelRenderable(IEnumerable<VoxelAnimation> voxels, WPos pos, int zOffset, WRot camera, float scale,
|
public VoxelRenderable(IEnumerable<VoxelAnimation> voxels, WPos pos, int zOffset, WRot camera, float scale,
|
||||||
WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
|
WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
|
||||||
PaletteReference color, PaletteReference normals, PaletteReference shadow)
|
PaletteReference color, PaletteReference normals, PaletteReference shadow)
|
||||||
@@ -44,6 +47,7 @@ namespace OpenRA.Graphics
|
|||||||
this.palette = color;
|
this.palette = color;
|
||||||
this.normalsPalette = normals;
|
this.normalsPalette = normals;
|
||||||
this.shadowPalette = shadow;
|
this.shadowPalette = shadow;
|
||||||
|
this.renderProxy = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WPos Pos { get { return pos; } }
|
public WPos Pos { get { return pos; } }
|
||||||
@@ -79,133 +83,72 @@ namespace OpenRA.Graphics
|
|||||||
palette, normalsPalette, shadowPalette);
|
palette, normalsPalette, shadowPalette);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeforeRender(WorldRenderer wr) {}
|
// This will need generalizing once we support TS/RA2 terrain
|
||||||
|
static readonly float[] groundNormal = new float[] {0,0,1,1};
|
||||||
|
public void BeforeRender(WorldRenderer wr)
|
||||||
|
{
|
||||||
|
renderProxy = Game.Renderer.WorldVoxelRenderer.RenderAsync(
|
||||||
|
wr, voxels, camera, scale, groundNormal, lightSource,
|
||||||
|
lightAmbientColor, lightDiffuseColor,
|
||||||
|
palette, normalsPalette, shadowPalette);
|
||||||
|
}
|
||||||
|
|
||||||
public void Render(WorldRenderer wr)
|
public void Render(WorldRenderer wr)
|
||||||
{
|
{
|
||||||
// Depth and shadow buffers are cleared between actors so that
|
var pxOrigin = wr.ScreenPosition(pos);
|
||||||
// overlapping units and shadows behave like overlapping sprites.
|
var groundZ = 0.5f*(pxOrigin.Y - wr.ScreenZPosition(pos, 0));
|
||||||
var vr = Game.Renderer.WorldVoxelRenderer;
|
var shadowOrigin = pxOrigin - groundZ*(new float2(renderProxy.ShadowDirection, 1));
|
||||||
var draw = voxels.Where(v => v.DisableFunc == null || !v.DisableFunc());
|
|
||||||
|
|
||||||
foreach (var v in draw)
|
var psb = renderProxy.ProjectedShadowBounds;
|
||||||
v.Voxel.PrepareForDraw(wr, pos + v.OffsetFunc(), v.RotationFunc(), camera,
|
var sa = shadowOrigin + psb[0];
|
||||||
v.FrameFunc(), scale, lightSource);
|
var sb = shadowOrigin + psb[2];
|
||||||
|
var sc = shadowOrigin + psb[1];
|
||||||
Game.Renderer.EnableDepthBuffer();
|
var sd = shadowOrigin + psb[3];
|
||||||
Game.Renderer.EnableStencilBuffer();
|
Game.Renderer.WorldRgbaSpriteRenderer.DrawSprite(renderProxy.ShadowSprite, sa, sb, sc, sd);
|
||||||
foreach (var v in draw)
|
Game.Renderer.WorldRgbaSpriteRenderer.DrawSprite(renderProxy.Sprite, pxOrigin - 0.5f*renderProxy.Sprite.size);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RenderDebugGeometry(WorldRenderer wr)
|
public void RenderDebugGeometry(WorldRenderer wr)
|
||||||
{
|
{
|
||||||
|
var pxOrigin = wr.ScreenPosition(pos);
|
||||||
|
var groundZ = 0.5f*(pxOrigin.Y - wr.ScreenZPosition(pos, 0));
|
||||||
|
var shadowOrigin = pxOrigin - groundZ*(new float2(renderProxy.ShadowDirection, 1));
|
||||||
|
|
||||||
|
// Draw sprite rect
|
||||||
|
var offset = pxOrigin + renderProxy.Sprite.offset - 0.5f*renderProxy.Sprite.size;
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + renderProxy.Sprite.size, Color.Red);
|
||||||
|
|
||||||
|
// Draw transformed shadow sprite rect
|
||||||
|
var c = Color.Purple;
|
||||||
|
var psb = renderProxy.ProjectedShadowBounds;
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[1], shadowOrigin + psb[3], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[3], shadowOrigin + psb[0], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[0], shadowOrigin + psb[2], c, c);
|
||||||
|
Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[2], shadowOrigin + psb[1], c, c);
|
||||||
|
|
||||||
|
// Draw voxel bounding box
|
||||||
var draw = voxels.Where(v => v.DisableFunc == null || !v.DisableFunc());
|
var draw = voxels.Where(v => v.DisableFunc == null || !v.DisableFunc());
|
||||||
var scaleTransform = Util.ScaleMatrix(scale, scale, scale);
|
var scaleTransform = Util.ScaleMatrix(scale, scale, scale);
|
||||||
var pxOrigin = wr.ScreenPosition(pos);
|
|
||||||
|
|
||||||
// Correct for bogus light source definition
|
|
||||||
var shadowTransform = Util.MakeFloatMatrix(new WRot(new WAngle(256) - lightSource.Pitch,
|
|
||||||
WAngle.Zero, lightSource.Yaw + new WAngle(512)).AsMatrix());
|
|
||||||
|
|
||||||
var invShadowTransform = Util.MatrixInverse(shadowTransform);
|
|
||||||
var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix());
|
var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix());
|
||||||
|
|
||||||
// TODO: Generalize this once we support sloped terrain
|
|
||||||
var groundNormal = new float[] {0,0,1,1};
|
|
||||||
var groundPos = new float[] {0, 0, 0.5f*(wr.ScreenPosition(pos).Y - wr.ScreenZPosition(pos, 0)), 1};
|
|
||||||
var shadowGroundNormal = Util.MatrixVectorMultiply(shadowTransform, groundNormal);
|
|
||||||
var shadowGroundPos = Util.MatrixVectorMultiply(shadowTransform, groundPos);
|
|
||||||
|
|
||||||
// Sprite rectangle
|
|
||||||
var tl = new float2(float.MaxValue, float.MaxValue);
|
|
||||||
var br = new float2(float.MinValue, float.MinValue);
|
|
||||||
|
|
||||||
// Shadow sprite rectangle
|
|
||||||
var stl = new float2(float.MaxValue, float.MaxValue);
|
|
||||||
var sbr = new float2(float.MinValue, float.MinValue);
|
|
||||||
|
|
||||||
foreach (var v in draw)
|
foreach (var v in draw)
|
||||||
{
|
{
|
||||||
var bounds = v.Voxel.Bounds(v.FrameFunc());
|
var bounds = v.Voxel.Bounds(v.FrameFunc());
|
||||||
var worldTransform = v.RotationFunc().Reverse().Aggregate(scaleTransform,
|
var worldTransform = v.RotationFunc().Reverse().Aggregate(scaleTransform,
|
||||||
(x,y) => Util.MatrixMultiply(x, Util.MakeFloatMatrix(y.AsMatrix())));
|
(x,y) => Util.MatrixMultiply(x, Util.MakeFloatMatrix(y.AsMatrix())));
|
||||||
|
|
||||||
var worldBounds = Util.MatrixAABBMultiply(worldTransform, bounds);
|
|
||||||
var screenBounds = Util.MatrixAABBMultiply(cameraTransform, worldBounds);
|
|
||||||
|
|
||||||
// Aggregate bounds rect
|
|
||||||
var pxOffset = wr.ScreenVector(v.OffsetFunc());
|
var pxOffset = wr.ScreenVector(v.OffsetFunc());
|
||||||
var pxPos = pxOrigin + new float2(pxOffset[0], pxOffset[1]);
|
var pxPos = pxOrigin + new float2(pxOffset[0], pxOffset[1]);
|
||||||
tl = float2.Min(tl, pxPos + new float2(screenBounds[0], screenBounds[1]));
|
|
||||||
br = float2.Max(br, pxPos + new float2(screenBounds[3], screenBounds[4]));
|
|
||||||
|
|
||||||
// Box to render the shadow image from
|
|
||||||
var shadowBounds = Util.MatrixAABBMultiply(shadowTransform, worldBounds);
|
|
||||||
var shadowPxOffset = Util.MatrixVectorMultiply(shadowTransform, pxOffset);
|
|
||||||
|
|
||||||
stl = float2.Min(stl, new float2(shadowPxOffset[0] + shadowBounds[0], shadowPxOffset[1] + shadowBounds[1]));
|
|
||||||
sbr = float2.Max(sbr, new float2(shadowPxOffset[0] + shadowBounds[3], shadowPxOffset[1] + shadowBounds[4]));
|
|
||||||
|
|
||||||
// Draw voxel bounding box
|
|
||||||
var screenTransform = Util.MatrixMultiply(cameraTransform, worldTransform);
|
var screenTransform = Util.MatrixMultiply(cameraTransform, worldTransform);
|
||||||
DrawBoundsBox(pxPos, screenTransform, bounds, Color.Yellow);
|
DrawBoundsBox(pxPos, screenTransform, bounds, Color.Yellow);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inflate rects by 1px each side to ensure rendering is within bounds
|
|
||||||
var pad = new float2(1,1);
|
|
||||||
tl -= pad;
|
|
||||||
br += pad;
|
|
||||||
stl -= pad;
|
|
||||||
sbr += pad;
|
|
||||||
|
|
||||||
// Corners of the shadow quad, in shadow-space
|
|
||||||
var corners = new float[][]
|
|
||||||
{
|
|
||||||
new float[] {stl.X, stl.Y, 0, 1},
|
|
||||||
new float[] {sbr.X, sbr.Y, 0, 1},
|
|
||||||
new float[] {sbr.X, stl.Y, 0, 1},
|
|
||||||
new float[] {stl.X, sbr.Y, 0, 1}
|
|
||||||
};
|
|
||||||
|
|
||||||
var shadowScreenTransform = Util.MatrixMultiply(cameraTransform, invShadowTransform);
|
|
||||||
var screenCorners = new float2[4];
|
|
||||||
for (var j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
// Project to ground plane
|
|
||||||
corners[j][2] -= (corners[j][2] - shadowGroundPos[2]) +
|
|
||||||
(corners[j][1] - shadowGroundPos[1])*shadowGroundNormal[1]/shadowGroundNormal[2] +
|
|
||||||
(corners[j][0] - shadowGroundPos[0])*shadowGroundNormal[0]/shadowGroundNormal[2];
|
|
||||||
|
|
||||||
// Rotate to camera-space
|
|
||||||
corners[j] = Util.MatrixVectorMultiply(shadowScreenTransform, corners[j]);
|
|
||||||
screenCorners[j] = pxOrigin + new float2(corners[j][0], corners[j][1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw transformed shadow sprite rect
|
|
||||||
var c = Color.Purple;
|
|
||||||
Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[1], screenCorners[3], c, c);
|
|
||||||
Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[3], screenCorners[0], c, c);
|
|
||||||
Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[0], screenCorners[2], c, c);
|
|
||||||
Game.Renderer.WorldLineRenderer.DrawLine(screenCorners[2], screenCorners[1], c, c);
|
|
||||||
|
|
||||||
// Draw sprite rect
|
|
||||||
Game.Renderer.WorldLineRenderer.DrawRect(tl, br, Color.Red);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static readonly uint[] ix = new uint[] {0,0,0,0,3,3,3,3};
|
||||||
|
static readonly uint[] iy = new uint[] {1,1,4,4,1,1,4,4};
|
||||||
|
static readonly uint[] iz = new uint[] {2,5,2,5,2,5,2,5};
|
||||||
static void DrawBoundsBox(float2 pxPos, float[] transform, float[] bounds, Color c)
|
static void DrawBoundsBox(float2 pxPos, float[] transform, float[] bounds, Color c)
|
||||||
{
|
{
|
||||||
// Corner offsets
|
|
||||||
var ix = new uint[] {0,0,0,0,3,3,3,3};
|
|
||||||
var iy = new uint[] {1,1,4,4,1,1,4,4};
|
|
||||||
var iz = new uint[] {2,5,2,5,2,5,2,5};
|
|
||||||
|
|
||||||
var corners = new float2[8];
|
var corners = new float2[8];
|
||||||
for (var i = 0; i < 8; i++)
|
for (var i = 0; i < 8; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,72 +17,326 @@ using OpenRA.FileFormats.Graphics;
|
|||||||
|
|
||||||
namespace OpenRA.Graphics
|
namespace OpenRA.Graphics
|
||||||
{
|
{
|
||||||
|
public class VoxelRenderProxy
|
||||||
|
{
|
||||||
|
public readonly Sprite Sprite;
|
||||||
|
public readonly Sprite ShadowSprite;
|
||||||
|
public readonly float ShadowDirection;
|
||||||
|
public readonly float2[] ProjectedShadowBounds;
|
||||||
|
|
||||||
|
public VoxelRenderProxy(Sprite sprite, Sprite shadowSprite, float2[] projectedShadowBounds, float shadowDirection)
|
||||||
|
{
|
||||||
|
Sprite = sprite;
|
||||||
|
ShadowSprite = shadowSprite;
|
||||||
|
ProjectedShadowBounds = projectedShadowBounds;
|
||||||
|
ShadowDirection = shadowDirection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class VoxelRenderer
|
public class VoxelRenderer
|
||||||
{
|
{
|
||||||
Renderer renderer;
|
Renderer renderer;
|
||||||
IShader shader;
|
IShader shader;
|
||||||
IShader shadowShader;
|
|
||||||
|
|
||||||
public VoxelRenderer(Renderer renderer, IShader shader, IShader shadowShader)
|
SheetBuilder sheetBuilder;
|
||||||
|
Dictionary<Sheet, IFrameBuffer> mappedBuffers;
|
||||||
|
Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers;
|
||||||
|
List<Pair<Sheet, Action>> doRender;
|
||||||
|
|
||||||
|
// Static constants
|
||||||
|
static readonly float[] shadowDiffuse = new float[] {0,0,0};
|
||||||
|
static readonly float[] shadowAmbient = new float[] {1,1,1};
|
||||||
|
static readonly float2 spritePadding = new float2(2, 2);
|
||||||
|
static readonly float[] zeroVector = new float[] {0,0,0,1};
|
||||||
|
static readonly float[] zVector = new float[] {0,0,1,1};
|
||||||
|
static readonly float[] flipMtx = Util.ScaleMatrix(1, -1, 1);
|
||||||
|
static readonly float[] shadowScaleFlipMtx = Util.ScaleMatrix(2, -2, 2);
|
||||||
|
|
||||||
|
public VoxelRenderer(Renderer renderer, IShader shader)
|
||||||
{
|
{
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.shader = shader;
|
this.shader = shader;
|
||||||
this.shadowShader = shadowShader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Render(VoxelLoader loader, VoxelRenderData renderData,
|
mappedBuffers = new Dictionary<Sheet, IFrameBuffer>();
|
||||||
float[] t, float[] lightDirection,
|
unmappedBuffers = new Stack<KeyValuePair<Sheet, IFrameBuffer>>();
|
||||||
float[] ambientLight, float[] diffuseLight,
|
doRender = new List<Pair<Sheet, Action>>();
|
||||||
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)
|
public void SetPalette(ITexture palette)
|
||||||
{
|
{
|
||||||
shader.SetTexture("Palette", palette);
|
shader.SetTexture("Palette", palette);
|
||||||
shadowShader.SetTexture("Palette", palette);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetViewportParams(Size screen, float zoom, float2 scroll)
|
public void SetViewportParams(Size screen, float zoom, float2 scroll)
|
||||||
{
|
{
|
||||||
// Construct projection matrix
|
var a = 2f / Renderer.SheetSize;
|
||||||
// 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[]
|
var view = new float[]
|
||||||
{
|
{
|
||||||
tiw, 0, 0, 0,
|
a, 0, 0, 0,
|
||||||
0, -tih, 0, 0,
|
0, -a, 0, 0,
|
||||||
0, 0, -tih/3, 0,
|
0, 0, -2*a, 0,
|
||||||
-1 - tiw*scroll.X,
|
-1, 1, 0, 1
|
||||||
1 + tih*scroll.Y,
|
|
||||||
1 + tih*scroll.Y/3,
|
|
||||||
1
|
|
||||||
};
|
};
|
||||||
|
|
||||||
shader.SetMatrix("View", view);
|
shader.SetMatrix("View", view);
|
||||||
shadowShader.SetMatrix("View", view);
|
}
|
||||||
|
|
||||||
|
public VoxelRenderProxy RenderAsync(WorldRenderer wr, IEnumerable<VoxelAnimation> voxels, WRot camera, float scale,
|
||||||
|
float[] groundNormal, WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
|
||||||
|
PaletteReference color, PaletteReference normals, PaletteReference shadowPalette)
|
||||||
|
{
|
||||||
|
// Correct for inverted y-axis
|
||||||
|
var scaleTransform = Util.ScaleMatrix(scale, scale, scale);
|
||||||
|
|
||||||
|
// Correct for bogus light source definition
|
||||||
|
var lightYaw = Util.MakeFloatMatrix(new WRot(WAngle.Zero, WAngle.Zero, -lightSource.Yaw).AsMatrix());
|
||||||
|
var lightPitch = Util.MakeFloatMatrix(new WRot(WAngle.Zero, -lightSource.Pitch, WAngle.Zero).AsMatrix());
|
||||||
|
var shadowTransform = Util.MatrixMultiply(lightPitch, lightYaw);
|
||||||
|
|
||||||
|
var invShadowTransform = Util.MatrixInverse(shadowTransform);
|
||||||
|
var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix());
|
||||||
|
var invCameraTransform = Util.MatrixInverse(cameraTransform);
|
||||||
|
|
||||||
|
// Sprite rectangle
|
||||||
|
var tl = new float2(float.MaxValue, float.MaxValue);
|
||||||
|
var br = new float2(float.MinValue, float.MinValue);
|
||||||
|
|
||||||
|
// Shadow sprite rectangle
|
||||||
|
var stl = new float2(float.MaxValue, float.MaxValue);
|
||||||
|
var sbr = new float2(float.MinValue, float.MinValue);
|
||||||
|
|
||||||
|
foreach (var v in voxels)
|
||||||
|
{
|
||||||
|
// Convert screen offset back to world coords
|
||||||
|
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(v.OffsetFunc()));
|
||||||
|
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
|
||||||
|
|
||||||
|
var worldTransform = v.RotationFunc().Aggregate(Util.IdentityMatrix(),
|
||||||
|
(x,y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
|
||||||
|
worldTransform = Util.MatrixMultiply(scaleTransform, worldTransform);
|
||||||
|
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
|
||||||
|
|
||||||
|
var bounds = v.Voxel.Bounds(v.FrameFunc());
|
||||||
|
var worldBounds = Util.MatrixAABBMultiply(worldTransform, bounds);
|
||||||
|
var screenBounds = Util.MatrixAABBMultiply(cameraTransform, worldBounds);
|
||||||
|
var shadowBounds = Util.MatrixAABBMultiply(shadowTransform, worldBounds);
|
||||||
|
|
||||||
|
// Aggregate bounds rects
|
||||||
|
tl = float2.Min(tl, new float2(screenBounds[0], screenBounds[1]));
|
||||||
|
br = float2.Max(br, new float2(screenBounds[3], screenBounds[4]));
|
||||||
|
stl = float2.Min(stl, new float2(shadowBounds[0], shadowBounds[1]));
|
||||||
|
sbr = float2.Max(sbr, new float2(shadowBounds[3], shadowBounds[4]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inflate rects to ensure rendering is within bounds
|
||||||
|
tl -= spritePadding;
|
||||||
|
br += spritePadding;
|
||||||
|
stl -= spritePadding;
|
||||||
|
sbr += spritePadding;
|
||||||
|
|
||||||
|
// Corners of the shadow quad, in shadow-space
|
||||||
|
var corners = new float[][]
|
||||||
|
{
|
||||||
|
new float[] {stl.X, stl.Y, 0, 1},
|
||||||
|
new float[] {sbr.X, sbr.Y, 0, 1},
|
||||||
|
new float[] {sbr.X, stl.Y, 0, 1},
|
||||||
|
new float[] {stl.X, sbr.Y, 0, 1}
|
||||||
|
};
|
||||||
|
|
||||||
|
var shadowScreenTransform = Util.MatrixMultiply(cameraTransform, invShadowTransform);
|
||||||
|
var shadowGroundNormal = Util.MatrixVectorMultiply(shadowTransform, groundNormal);
|
||||||
|
var screenCorners = new float2[4];
|
||||||
|
for (var j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
// Project to ground plane
|
||||||
|
corners[j][2] = -(corners[j][1]*shadowGroundNormal[1]/shadowGroundNormal[2] +
|
||||||
|
corners[j][0]*shadowGroundNormal[0]/shadowGroundNormal[2]);
|
||||||
|
|
||||||
|
// Rotate to camera-space
|
||||||
|
corners[j] = Util.MatrixVectorMultiply(shadowScreenTransform, corners[j]);
|
||||||
|
screenCorners[j] = new float2(corners[j][0], corners[j][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shadows are rendered at twice the resolution to reduce artefacts
|
||||||
|
Size spriteSize, shadowSpriteSize;
|
||||||
|
int2 spriteOffset, shadowSpriteOffset;
|
||||||
|
CalculateSpriteGeometry(tl, br, 1, out spriteSize, out spriteOffset);
|
||||||
|
CalculateSpriteGeometry(stl, sbr, 2, out shadowSpriteSize, out shadowSpriteOffset);
|
||||||
|
|
||||||
|
var sprite = sheetBuilder.Allocate(spriteSize, spriteOffset);
|
||||||
|
var shadowSprite = sheetBuilder.Allocate(shadowSpriteSize, shadowSpriteOffset);
|
||||||
|
var sb = sprite.bounds;
|
||||||
|
var ssb = shadowSprite.bounds;
|
||||||
|
var spriteCenter = new float2(sb.Left + sb.Width / 2, sb.Top + sb.Height / 2);
|
||||||
|
var shadowCenter = new float2(ssb.Left + ssb.Width / 2, ssb.Top + ssb.Height / 2);
|
||||||
|
|
||||||
|
var translateMtx = Util.TranslationMatrix(spriteCenter.X - spriteOffset.X, Renderer.SheetSize - (spriteCenter.Y - spriteOffset.Y), 0);
|
||||||
|
var shadowTranslateMtx = Util.TranslationMatrix(shadowCenter.X - shadowSpriteOffset.X, Renderer.SheetSize - (shadowCenter.Y - shadowSpriteOffset.Y), 0);
|
||||||
|
var correctionTransform = Util.MatrixMultiply(translateMtx, flipMtx);
|
||||||
|
var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, shadowScaleFlipMtx);
|
||||||
|
|
||||||
|
doRender.Add(Pair.New<Sheet, Action>(sprite.sheet, () =>
|
||||||
|
{
|
||||||
|
foreach (var v in voxels)
|
||||||
|
{
|
||||||
|
// Convert screen offset to world offset
|
||||||
|
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(v.OffsetFunc()));
|
||||||
|
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
|
||||||
|
|
||||||
|
var rotations = v.RotationFunc().Aggregate(Util.IdentityMatrix(),
|
||||||
|
(x,y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
|
||||||
|
var worldTransform = Util.MatrixMultiply(scaleTransform, rotations);
|
||||||
|
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
|
||||||
|
|
||||||
|
var transform = Util.MatrixMultiply(cameraTransform, worldTransform);
|
||||||
|
transform = Util.MatrixMultiply(correctionTransform, transform);
|
||||||
|
|
||||||
|
var shadow = Util.MatrixMultiply(shadowTransform, worldTransform);
|
||||||
|
shadow = Util.MatrixMultiply(shadowCorrectionTransform, shadow);
|
||||||
|
|
||||||
|
var lightTransform = Util.MatrixMultiply(Util.MatrixInverse(rotations), invShadowTransform);
|
||||||
|
|
||||||
|
var frame = v.FrameFunc();
|
||||||
|
for (uint i = 0; i < v.Voxel.Limbs; i++)
|
||||||
|
{
|
||||||
|
var rd = v.Voxel.RenderData(i);
|
||||||
|
var t = v.Voxel.TransformationMatrix(i, frame);
|
||||||
|
|
||||||
|
// Transform light vector from shadow -> world -> limb coords
|
||||||
|
var lightDirection = ExtractRotationVector(Util.MatrixMultiply(Util.MatrixInverse(t), lightTransform));
|
||||||
|
|
||||||
|
Render(rd, Util.MatrixMultiply(transform, t), lightDirection,
|
||||||
|
lightAmbientColor, lightDiffuseColor, color.Index, normals.Index);
|
||||||
|
|
||||||
|
// Disable shadow normals by forcing zero diffuse and identity ambient light
|
||||||
|
Render(rd, Util.MatrixMultiply(shadow, t), lightDirection,
|
||||||
|
shadowAmbient, shadowDiffuse, shadowPalette.Index, normals.Index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
var screenLightVector = Util.MatrixVectorMultiply(invShadowTransform, zVector);
|
||||||
|
screenLightVector = Util.MatrixVectorMultiply(cameraTransform, screenLightVector);
|
||||||
|
return new VoxelRenderProxy(sprite, shadowSprite, screenCorners, -screenLightVector[2]/screenLightVector[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CalculateSpriteGeometry(float2 tl, float2 br, float scale, out Size size, out int2 offset)
|
||||||
|
{
|
||||||
|
var width = (int)(scale*(br.X - tl.X));
|
||||||
|
var height = (int)(scale*(br.Y - tl.Y));
|
||||||
|
offset = (0.5f*scale*(br + tl)).ToInt2();
|
||||||
|
|
||||||
|
// Width and height must be even to avoid rendering glitches
|
||||||
|
if ((width & 1) == 1)
|
||||||
|
width += 1;
|
||||||
|
if ((height & 1) == 1)
|
||||||
|
height += 1;
|
||||||
|
|
||||||
|
size = new Size(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float[] ExtractRotationVector(float[] mtx)
|
||||||
|
{
|
||||||
|
var tVec = Util.MatrixVectorMultiply(mtx, zVector);
|
||||||
|
var tOrigin = Util.MatrixVectorMultiply(mtx, zeroVector);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Render(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(Game.modData.VoxelLoader.VertexBuffer, renderData.Start, renderData.Count, PrimitiveType.QuadList));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginFrame()
|
||||||
|
{
|
||||||
|
foreach (var kv in mappedBuffers)
|
||||||
|
unmappedBuffers.Push(kv);
|
||||||
|
mappedBuffers.Clear();
|
||||||
|
|
||||||
|
sheetBuilder = new SheetBuilder(SheetType.BGRA, AllocateSheet);
|
||||||
|
doRender.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
IFrameBuffer EnableFrameBuffer(Sheet s)
|
||||||
|
{
|
||||||
|
var fbo = mappedBuffers[s];
|
||||||
|
Game.Renderer.Flush();
|
||||||
|
fbo.Bind();
|
||||||
|
|
||||||
|
Game.Renderer.Device.EnableDepthBuffer();
|
||||||
|
return fbo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisableFrameBuffer(IFrameBuffer fbo)
|
||||||
|
{
|
||||||
|
Game.Renderer.Flush();
|
||||||
|
Game.Renderer.Device.DisableDepthBuffer();
|
||||||
|
fbo.Unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndFrame()
|
||||||
|
{
|
||||||
|
if (doRender.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Sheet currentSheet = null;
|
||||||
|
IFrameBuffer fbo = null;
|
||||||
|
foreach (var v in doRender)
|
||||||
|
{
|
||||||
|
// Change sheet
|
||||||
|
if (v.First != currentSheet)
|
||||||
|
{
|
||||||
|
if (fbo != null)
|
||||||
|
DisableFrameBuffer(fbo);
|
||||||
|
|
||||||
|
currentSheet = v.First;
|
||||||
|
fbo = EnableFrameBuffer(currentSheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Second();
|
||||||
|
}
|
||||||
|
|
||||||
|
DisableFrameBuffer(fbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sheet AllocateSheet()
|
||||||
|
{
|
||||||
|
// Reuse cached fbo
|
||||||
|
if (unmappedBuffers.Count > 0)
|
||||||
|
{
|
||||||
|
var kv = unmappedBuffers.Pop();
|
||||||
|
mappedBuffers.Add(kv.Key, kv.Value);
|
||||||
|
return kv.Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
var size = new Size(Renderer.SheetSize, Renderer.SheetSize);
|
||||||
|
var framebuffer = renderer.Device.CreateFrameBuffer(size);
|
||||||
|
var sheet = new Sheet(framebuffer.Texture);
|
||||||
|
mappedBuffers.Add(sheet, framebuffer);
|
||||||
|
|
||||||
|
return sheet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,8 +87,11 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
// Iterating via foreach() copies the structs, so enumerate by index
|
// Iterating via foreach() copies the structs, so enumerate by index
|
||||||
var renderables = worldRenderables.Concat(effectRenderables).ToList();
|
var renderables = worldRenderables.Concat(effectRenderables).ToList();
|
||||||
|
|
||||||
|
Game.Renderer.WorldVoxelRenderer.BeginFrame();
|
||||||
for (var i = 0; i < renderables.Count; i++)
|
for (var i = 0; i < renderables.Count; i++)
|
||||||
renderables[i].BeforeRender(this);
|
renderables[i].BeforeRender(this);
|
||||||
|
Game.Renderer.WorldVoxelRenderer.EndFrame();
|
||||||
|
|
||||||
return renderables;
|
return renderables;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,11 +32,10 @@ namespace OpenRA.Mods.RA.Render
|
|||||||
[Desc("Change the image size.")]
|
[Desc("Change the image size.")]
|
||||||
public readonly float Scale = 10;
|
public readonly float Scale = 10;
|
||||||
|
|
||||||
public readonly WAngle LightPitch = new WAngle(170); // 60 degrees
|
public readonly WAngle LightPitch = WAngle.FromDegrees(50);
|
||||||
public readonly WAngle LightYaw = new WAngle(739); // 260 degrees
|
public readonly WAngle LightYaw = WAngle.FromDegrees(240);
|
||||||
public readonly float[] LightAmbientColor = new float[] {0.6f, 0.6f, 0.6f};
|
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 readonly float[] LightDiffuseColor = new float[] {0.4f, 0.4f, 0.4f};
|
||||||
|
|
||||||
public virtual object Create(ActorInitializer init) { return new RenderVoxels(init.self, this); }
|
public virtual object Create(ActorInitializer init) { return new RenderVoxels(init.self, this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ namespace OpenRA.Mods.RA.Render
|
|||||||
this.info = info;
|
this.info = info;
|
||||||
body = self.Trait<IBodyOrientation>();
|
body = self.Trait<IBodyOrientation>();
|
||||||
camera = new WRot(WAngle.Zero, body.CameraPitch - new WAngle(256), new WAngle(256));
|
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));
|
lightSource = new WRot(WAngle.Zero,new WAngle(256) - info.LightPitch, info.LightYaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool initializePalettes = true;
|
bool initializePalettes = true;
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
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