Move Voxel code to Mods.Cnc.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user