Add core voxel rendering code.
This commit is contained in:
194
OpenRA.Game/Graphics/VoxelLoader.cs
Normal file
194
OpenRA.Game/Graphics/VoxelLoader.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileFormats.Graphics;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public struct VoxelRenderData
|
||||
{
|
||||
public readonly int Start;
|
||||
public readonly int Count;
|
||||
public readonly Sheet Sheet;
|
||||
|
||||
public VoxelRenderData(int start, int count, Sheet sheet)
|
||||
{
|
||||
Start = start;
|
||||
Count = count;
|
||||
Sheet = sheet;
|
||||
}
|
||||
}
|
||||
|
||||
public class VoxelLoader
|
||||
{
|
||||
SheetBuilder sheetBuilder;
|
||||
|
||||
IVertexBuffer<Vertex> vertexBuffer;
|
||||
List<Vertex[]> vertices;
|
||||
int totalVertexCount;
|
||||
int cachedVertexCount;
|
||||
|
||||
public VoxelLoader()
|
||||
{
|
||||
vertices = new List<Vertex[]>();
|
||||
totalVertexCount = 0;
|
||||
cachedVertexCount = 0;
|
||||
|
||||
sheetBuilder = new SheetBuilder(SheetType.DualIndexed);
|
||||
}
|
||||
|
||||
static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f };
|
||||
Vertex[] GenerateSlicePlane(int su, int sv, Func<int,int,VxlElement> first, Func<int,int,VxlElement> second, Func<int, int, float[]> coord)
|
||||
{
|
||||
var colors = new byte[su*sv];
|
||||
var normals = new byte[su*sv];
|
||||
|
||||
var c = 0;
|
||||
for (var v = 0; v < sv; v++)
|
||||
for (var u = 0; u < su; u++)
|
||||
{
|
||||
var voxel = first(u,v) ?? second(u,v);
|
||||
colors[c] = voxel == null ? (byte)0 : voxel.Color;
|
||||
normals[c] = voxel == null ? (byte)0 : voxel.Normal;
|
||||
c++;
|
||||
}
|
||||
|
||||
Sprite s = sheetBuilder.Allocate(new Size(su, sv), false);
|
||||
Util.FastCopyIntoChannel(s, 0, colors);
|
||||
Util.FastCopyIntoChannel(s, 1, normals);
|
||||
s.sheet.MakeDirty();
|
||||
|
||||
var channels = new float2(channelSelect[(int)s.channel], channelSelect[(int)s.channel + 1]);
|
||||
return new Vertex[4]
|
||||
{
|
||||
new Vertex(coord(0, 0), s.FastMapTextureCoords(0), channels),
|
||||
new Vertex(coord(su, 0), s.FastMapTextureCoords(1), channels),
|
||||
new Vertex(coord(su, sv), s.FastMapTextureCoords(3), channels),
|
||||
new Vertex(coord(0, sv), s.FastMapTextureCoords(2), channels)
|
||||
};
|
||||
}
|
||||
|
||||
IEnumerable<Vertex[]> GenerateSlicePlanes(VxlLimb l)
|
||||
{
|
||||
Func<int,int,int,VxlElement> get = (x,y,z) =>
|
||||
{
|
||||
if (x < 0 || y < 0 || z < 0)
|
||||
return null;
|
||||
|
||||
if (x >= l.Size[0] || y >= l.Size[1] || z >= l.Size[2])
|
||||
return null;
|
||||
|
||||
var v = l.VoxelMap[(byte)x,(byte)y];
|
||||
if (v == null || !v.ContainsKey((byte)z))
|
||||
return null;
|
||||
|
||||
return l.VoxelMap[(byte)x,(byte)y][(byte)z];
|
||||
};
|
||||
|
||||
// Cull slices without any visible faces
|
||||
var xPlanes = new bool[l.Size[0]+1];
|
||||
var yPlanes = new bool[l.Size[1]+1];
|
||||
var zPlanes = new bool[l.Size[2]+1];
|
||||
for (var x = 0; x < l.Size[0]; x++)
|
||||
{
|
||||
for (var y = 0; y < l.Size[1]; y++)
|
||||
{
|
||||
for (var z = 0; z < l.Size[2]; z++)
|
||||
{
|
||||
if (get(x,y,z) == null)
|
||||
continue;
|
||||
|
||||
// Only generate a plane if it is actually visible
|
||||
if (!xPlanes[x] && get(x-1,y,z) == null)
|
||||
xPlanes[x] = true;
|
||||
if (!xPlanes[x+1] && get(x+1,y,z) == null)
|
||||
xPlanes[x+1] = true;
|
||||
|
||||
if (!yPlanes[y] && get(x,y-1,z) == null)
|
||||
yPlanes[y] = true;
|
||||
if (!yPlanes[y+1] && get(x,y+1,z) == null)
|
||||
yPlanes[y+1] = true;
|
||||
|
||||
if (!zPlanes[z] && get(x,y,z-1) == null)
|
||||
zPlanes[z] = true;
|
||||
if (!zPlanes[z+1] && get(x,y,z+1) == null)
|
||||
zPlanes[z+1] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var x = 0; x <= l.Size[0]; x++)
|
||||
if (xPlanes[x])
|
||||
yield return GenerateSlicePlane(l.Size[1], l.Size[2],
|
||||
(u,v) => get(x, u, v),
|
||||
(u,v) => get(x - 1, u, v),
|
||||
(u,v) => new float[] {x, u, v});
|
||||
|
||||
for (var y = 0; y <= l.Size[1]; y++)
|
||||
if (yPlanes[y])
|
||||
yield return GenerateSlicePlane(l.Size[0], l.Size[2],
|
||||
(u,v) => get(u, y, v),
|
||||
(u,v) => get(u, y - 1, v),
|
||||
(u,v) => new float[] {u, y, v});
|
||||
|
||||
for (var z = 0; z <= l.Size[2]; z++)
|
||||
if (zPlanes[z])
|
||||
yield return GenerateSlicePlane(l.Size[0], l.Size[1],
|
||||
(u,v) => get(u, v, z),
|
||||
(u,v) => get(u, v, z - 1),
|
||||
(u,v) => new float[] {u, v, z});
|
||||
}
|
||||
|
||||
public VoxelRenderData GenerateRenderData(VxlLimb l)
|
||||
{
|
||||
Vertex[] v;
|
||||
try
|
||||
{
|
||||
v = GenerateSlicePlanes(l).SelectMany(x => x).ToArray();
|
||||
}
|
||||
catch (SheetOverflowException)
|
||||
{
|
||||
// Sheet overflow - allocate a new sheet and try once more
|
||||
Log.Write("debug", "Voxel sheet overflow! Generating new sheet");
|
||||
sheetBuilder = new SheetBuilder(SheetType.DualIndexed);
|
||||
v = GenerateSlicePlanes(l).SelectMany(x => x).ToArray();
|
||||
}
|
||||
|
||||
vertices.Add(v);
|
||||
|
||||
var start = totalVertexCount;
|
||||
var count = v.Length;
|
||||
totalVertexCount += count;
|
||||
return new VoxelRenderData(start, count, sheetBuilder.Current);
|
||||
}
|
||||
|
||||
public void RefreshBuffer()
|
||||
{
|
||||
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(totalVertexCount);
|
||||
vertexBuffer.SetData(vertices.SelectMany(v => v).ToArray(), totalVertexCount);
|
||||
cachedVertexCount = totalVertexCount;
|
||||
}
|
||||
|
||||
public IVertexBuffer<Vertex> VertexBuffer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (cachedVertexCount != totalVertexCount)
|
||||
RefreshBuffer();
|
||||
return vertexBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user