Move Voxel code to Mods.Cnc.

This commit is contained in:
Paul Chote
2017-06-09 15:58:00 +00:00
committed by reaperrr
parent dc4c3fd546
commit 34810756c2
19 changed files with 185 additions and 157 deletions

View File

@@ -22,7 +22,21 @@ namespace OpenRA.Graphics
float[] TransformationMatrix(uint section, uint frame);
float[] Size { get; }
float[] Bounds(uint frame);
VoxelRenderData RenderData(uint section);
ModelRenderData RenderData(uint section);
}
public struct ModelRenderData
{
public readonly int Start;
public readonly int Count;
public readonly Sheet Sheet;
public ModelRenderData(int start, int count, Sheet sheet)
{
Start = start;
Count = count;
Sheet = sheet;
}
}
public interface IModelCache : IDisposable

View File

@@ -208,12 +208,12 @@ namespace OpenRA.Graphics
// Transform light vector from shadow -> world -> limb coords
var lightDirection = ExtractRotationVector(Util.MatrixMultiply(it, lightTransform));
Render(rd, Util.MatrixMultiply(transform, t), lightDirection,
Render(rd, wr.World.ModelCache, Util.MatrixMultiply(transform, t), lightDirection,
lightAmbientColor, lightDiffuseColor, color.TextureMidIndex, normals.TextureMidIndex);
// Disable shadow normals by forcing zero diffuse and identity ambient light
if (v.ShowShadow)
Render(rd, Util.MatrixMultiply(shadow, t), lightDirection,
Render(rd, wr.World.ModelCache, Util.MatrixMultiply(shadow, t), lightDirection,
ShadowAmbient, ShadowDiffuse, shadowPalette.TextureMidIndex, normals.TextureMidIndex);
}
}
@@ -258,7 +258,8 @@ namespace OpenRA.Graphics
}
void Render(
VoxelRenderData renderData,
ModelRenderData renderData,
IModelCache cache,
float[] t, float[] lightDirection,
float[] ambientLight, float[] diffuseLight,
float colorPaletteTextureMidIndex, float normalsPaletteTextureMidIndex)
@@ -270,7 +271,7 @@ namespace OpenRA.Graphics
shader.SetVec("AmbientLight", ambientLight, 3);
shader.SetVec("DiffuseLight", diffuseLight, 3);
shader.Render(() => renderer.DrawBatch(Game.ModData.VoxelLoader.VertexBuffer, renderData.Start, renderData.Count, PrimitiveType.TriangleList));
shader.Render(() => renderer.DrawBatch(cache.VertexBuffer, renderData.Start, renderData.Count, PrimitiveType.TriangleList));
}
public void BeginFrame()

View File

@@ -1,129 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Linq;
using OpenRA.FileFormats;
namespace OpenRA.Graphics
{
struct Limb
{
public float Scale;
public float[] Bounds;
public byte[] Size;
public VoxelRenderData RenderData;
}
public class Voxel : IModel
{
Limb[] limbData;
float[] transforms;
public readonly uint Frames;
public readonly uint Limbs;
uint IModel.Frames { get { return Frames; } }
uint IModel.Sections { get { return Limbs; } }
public Voxel(VoxelLoader loader, VxlReader vxl, HvaReader hva)
{
if (vxl.LimbCount != hva.LimbCount)
throw new InvalidOperationException("Voxel and hva limb counts don't match");
transforms = hva.Transforms;
Frames = hva.FrameCount;
Limbs = hva.LimbCount;
limbData = 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]);
limbData[i] = l;
}
}
public float[] TransformationMatrix(uint limb, uint frame)
{
if (frame >= Frames)
throw new ArgumentOutOfRangeException("frame", "Only {0} frames exist.".F(Frames));
if (limb >= Limbs)
throw new ArgumentOutOfRangeException("limb", "Only {1} limbs exist.".F(Limbs));
var l = limbData[limb];
var t = new float[16];
Array.Copy(transforms, 16 * (Limbs * frame + limb), t, 0, 16);
// 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];
// Center, flip and scale
t = Util.MatrixMultiply(t, Util.TranslationMatrix(l.Bounds[0], l.Bounds[1], l.Bounds[2]));
t = Util.MatrixMultiply(Util.ScaleMatrix(l.Scale, -l.Scale, l.Scale), t);
return t;
}
public VoxelRenderData RenderData(uint limb)
{
return limbData[limb].RenderData;
}
public float[] Size
{
get
{
return limbData.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])
});
}
}
public float[] Bounds(uint frame)
{
var ret = new float[] { float.MaxValue, float.MaxValue, float.MaxValue,
float.MinValue, float.MinValue, float.MinValue };
for (uint j = 0; j < Limbs; j++)
{
var l = limbData[j];
var b = new float[]
{
0, 0, 0,
(l.Bounds[3] - l.Bounds[0]),
(l.Bounds[4] - l.Bounds[1]),
(l.Bounds[5] - l.Bounds[2])
};
// Calculate limb bounding box
var bb = Util.MatrixAABBMultiply(TransformationMatrix(j, frame), b);
for (var i = 0; i < 3; i++)
{
ret[i] = Math.Min(ret[i], bb[i]);
ret[i + 3] = Math.Max(ret[i + 3], bb[i + 3]);
}
}
return ret;
}
}
}

View File

@@ -1,256 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.FileSystem;
using OpenRA.Primitives;
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 sealed class VoxelLoader : IDisposable
{
static readonly float[] ChannelSelect = { 0.75f, 0.25f, -0.25f, -0.75f };
readonly List<Vertex[]> vertices = new List<Vertex[]>();
readonly Cache<Pair<string, string>, Voxel> voxels;
readonly IReadOnlyFileSystem fileSystem;
IVertexBuffer<Vertex> vertexBuffer;
int totalVertexCount;
int cachedVertexCount;
SheetBuilder sheetBuilder;
static SheetBuilder CreateSheetBuilder()
{
var allocated = false;
Func<Sheet> allocate = () =>
{
if (allocated)
throw new SheetOverflowException("");
allocated = true;
return SheetBuilder.AllocateSheet(SheetType.Indexed, Game.Settings.Graphics.SheetSize);
};
return new SheetBuilder(SheetType.Indexed, allocate);
}
public VoxelLoader(IReadOnlyFileSystem fileSystem)
{
this.fileSystem = fileSystem;
voxels = new Cache<Pair<string, string>, Voxel>(LoadFile);
vertices = new List<Vertex[]>();
totalVertexCount = 0;
cachedVertexCount = 0;
sheetBuilder = CreateSheetBuilder();
}
Vertex[] GenerateSlicePlane(int su, int sv, Func<int, int, VxlElement> first, Func<int, int, VxlElement> second, Func<int, int, float3> 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++;
}
}
var size = new Size(su, sv);
var s = sheetBuilder.Allocate(size);
var t = sheetBuilder.Allocate(size);
Util.FastCopyIntoChannel(s, colors);
Util.FastCopyIntoChannel(t, normals);
// s and t are guaranteed to use the same sheet because
// of the custom voxel sheet allocation implementation
s.Sheet.CommitBufferedData();
var channelP = ChannelSelect[(int)s.Channel];
var channelC = ChannelSelect[(int)t.Channel];
return new Vertex[6]
{
new Vertex(coord(0, 0), s.Left, s.Top, t.Left, t.Top, channelP, channelC),
new Vertex(coord(su, 0), s.Right, s.Top, t.Right, t.Top, channelP, channelC),
new Vertex(coord(su, sv), s.Right, s.Bottom, t.Right, t.Bottom, channelP, channelC),
new Vertex(coord(su, sv), s.Right, s.Bottom, t.Right, t.Bottom, channelP, channelC),
new Vertex(coord(0, sv), s.Left, s.Bottom, t.Left, t.Bottom, channelP, channelC),
new Vertex(coord(0, 0), s.Left, s.Top, t.Left, t.Top, channelP, channelC)
};
}
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 float3(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 float3(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 float3(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.Current.ReleaseBuffer();
sheetBuilder = CreateSheetBuilder();
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()
{
if (vertexBuffer != null)
vertexBuffer.Dispose();
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)
{
VxlReader vxl;
HvaReader hva;
using (var s = fileSystem.Open(files.First + ".vxl"))
vxl = new VxlReader(s);
using (var s = fileSystem.Open(files.Second + ".hva"))
hva = new HvaReader(s, files.Second + ".hva");
return new Voxel(this, vxl, hva);
}
public Voxel Load(string vxl, string hva)
{
return voxels[Pair.New(vxl, hva)];
}
public void Finish()
{
sheetBuilder.Current.ReleaseBuffer();
}
public void Dispose()
{
if (vertexBuffer != null)
vertexBuffer.Dispose();
sheetBuilder.Dispose();
}
}
}

View File

@@ -1,84 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
namespace OpenRA.Graphics
{
public static class VoxelProvider
{
static Dictionary<string, Dictionary<string, Voxel>> units;
public static void Initialize(VoxelLoader loader, IReadOnlyFileSystem fileSystem, List<MiniYamlNode> sequences)
{
units = new Dictionary<string, Dictionary<string, Voxel>>();
foreach (var s in sequences)
LoadVoxelsForUnit(loader, s.Key, s.Value);
loader.RefreshBuffer();
}
static Voxel LoadVoxel(VoxelLoader voxelLoader, string unit, 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 voxelLoader.Load(vxl, hva);
}
static void LoadVoxelsForUnit(VoxelLoader loader, string unit, MiniYaml sequences)
{
Game.ModData.LoadScreen.Display();
try
{
var seq = sequences.ToDictionary(my => LoadVoxel(loader, unit, my));
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);
}
}
}