555 lines
20 KiB
C#
555 lines
20 KiB
C#
using System.Collections.Generic;
|
|
using Aiwaz.Core;
|
|
using Aiwaz.Contracts;
|
|
using System;
|
|
using System.Linq;
|
|
using SlimDX.Direct3D10;
|
|
using SlimDX;
|
|
using System.Collections.ObjectModel;
|
|
|
|
namespace Aiwaz.Resources
|
|
{
|
|
[AiwazResource("Geometry Buffer", "Renderable buffer with vertex or index data.")]
|
|
public class GeometryBuffer : CommandUser, IDisposable, IGeometryBuffer
|
|
{
|
|
#region Constructor
|
|
|
|
public GeometryBuffer()
|
|
: base()
|
|
{
|
|
Console.WriteLine("Creating GeometryBuffer..");
|
|
|
|
PrimitiveTopology = PrimitiveTopology.TriangleList;
|
|
Commands.Add(new Command(this, CommandFlags.None, SetIndexBufferCommandType, 1));
|
|
Commands.Add(new Command(this, CommandFlags.None, SetVertexBufferCommandType, 0));
|
|
Commands.Add(new Command(this, CommandFlags.EndChain | CommandFlags.Unique, DrawGeometryCommandType, 6));
|
|
}
|
|
|
|
public GeometryBuffer(IEnumerable<uint> argIndexData, bool argNeedsDynamicAccess)
|
|
: this()
|
|
{
|
|
this.SetIndexData(argIndexData, argNeedsDynamicAccess);
|
|
}
|
|
|
|
public GeometryBuffer(int argIndexCount, SlimDX.DataStream argIndexData, int argVertexCount, SlimDX.DataStream argVertexData, VertexElement[] argVertexElements, bool argNeedsDynamicAccess)
|
|
: this()
|
|
{
|
|
this.SetIndexData(argIndexCount, argIndexData, argNeedsDynamicAccess);
|
|
this.SetVertexData(argVertexCount, argVertexData, argVertexElements, argNeedsDynamicAccess);
|
|
}
|
|
|
|
public static GeometryBuffer Create<TVertex>(IEnumerable<TVertex> argVertexData, VertexElement[] argVertexElements, bool argNeedsDynamicAccess) where TVertex : struct
|
|
{
|
|
var geoBuffer = new GeometryBuffer();
|
|
geoBuffer.SetVertexData(argVertexData, argVertexElements, argNeedsDynamicAccess);
|
|
|
|
return geoBuffer;
|
|
}
|
|
|
|
public static GeometryBuffer Create<TVertex>(IEnumerable<uint> argIndexData, IEnumerable<TVertex> argVertexData, VertexElement[] argVertexElements, bool argNeedsDynamicAccess) where TVertex : struct
|
|
{
|
|
var geoBuffer = new GeometryBuffer();
|
|
geoBuffer.SetIndexData(argIndexData, argNeedsDynamicAccess);
|
|
geoBuffer.SetVertexData(argVertexData, argVertexElements, argNeedsDynamicAccess);
|
|
|
|
return geoBuffer;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
public void SetVertexData(int argVertexCount, DataStream argVertexData, VertexElement[] argVertexElements, bool argNeedsDynamicAccess)
|
|
{
|
|
Console.WriteLine(string.Format("Vertex data: {0} bytes ({1} elements).", argVertexData.Length, argVertexCount));
|
|
|
|
var usePickHull = this.IsPickable;
|
|
DeleteVertexData();
|
|
|
|
BufferDescription desc = new BufferDescription((int)argVertexData.Length,
|
|
argNeedsDynamicAccess ? ResourceUsage.Dynamic : ResourceUsage.Default,
|
|
SlimDX.Direct3D10.BindFlags.VertexBuffer,
|
|
argNeedsDynamicAccess ? CpuAccessFlags.Write : 0, ResourceOptionFlags.None);
|
|
|
|
vertexBuffer = new SlimDX.Direct3D10.Buffer(Engine.Device, argVertexData, desc);
|
|
if (vertexBuffer == null)
|
|
{
|
|
throw new ActionFailedException("GeometryBuffer: Couldn't create vertex buffer!");
|
|
}
|
|
|
|
VertexBufferLength = argVertexCount;
|
|
vertexElementSize = (int)argVertexData.Length / argVertexCount;
|
|
vertexElements = argVertexElements;
|
|
|
|
rawVertexData = argVertexData;
|
|
|
|
if (inputElementDesc == null)
|
|
{
|
|
if (vertexElements.Length > 0)
|
|
{
|
|
inputElementDesc = new InputElement[vertexElements.Length];
|
|
|
|
int i = 0;
|
|
foreach (var element in vertexElements)
|
|
{
|
|
inputElementDesc[i++] = new InputElement(element.SemanticName, element.NameIndex, element.ElementFormat, -1, 0, InputClassification.PerVertexData, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.IsPickable = usePickHull;
|
|
}
|
|
|
|
public void DeleteVertexData()
|
|
{
|
|
if (vertexBuffer != null)
|
|
Engine.Device.ClearState();
|
|
|
|
vertexElements = new VertexElement[0];
|
|
|
|
if (vertexBuffer != null)
|
|
vertexBuffer.Dispose();
|
|
|
|
if (rawVertexData != null)
|
|
rawVertexData.Dispose();
|
|
|
|
vertexBuffer = null;
|
|
VertexBufferLength = 0;
|
|
vertexElementSize = 0;
|
|
rawMappedVertexData = null;
|
|
typeMappedVertexData = null;
|
|
rawVertexData = null;
|
|
inputElementDesc = null;
|
|
PickHull = null;
|
|
}
|
|
|
|
public void SetIndexData(int argIndexCount, DataStream argIndexData, bool argNeedsDynamicAccess)
|
|
{
|
|
Console.WriteLine(string.Format("Index data: {0} bytes ({1} elements).", argIndexData.Length, argIndexCount));
|
|
|
|
bool usePickHull = this.IsPickable;
|
|
DeleteIndexData();
|
|
|
|
BufferDescription desc = new BufferDescription(
|
|
sizeof(uint) * argIndexCount,
|
|
ResourceUsage.Default,
|
|
SlimDX.Direct3D10.BindFlags.IndexBuffer,
|
|
argNeedsDynamicAccess ? CpuAccessFlags.Write : 0, ResourceOptionFlags.None);
|
|
|
|
indexBuffer = new SlimDX.Direct3D10.Buffer(Engine.Device, argIndexData, desc);
|
|
if (indexBuffer == null)
|
|
{
|
|
throw new ActionFailedException("GeometryBuffer: Couldn't create index buffer!");
|
|
}
|
|
|
|
IndexBufferLength = argIndexCount;
|
|
IndexBufferUsableLength = argIndexCount;
|
|
IndexBufferOffset = 0;
|
|
rawIndexData = argIndexData;
|
|
|
|
this.IsPickable = usePickHull;
|
|
}
|
|
|
|
public void DeleteIndexData()
|
|
{
|
|
if (indexBuffer != null)
|
|
Engine.Device.ClearState();
|
|
|
|
if (indexBuffer != null)
|
|
indexBuffer.Dispose();
|
|
|
|
if (rawIndexData != null)
|
|
rawIndexData.Dispose();
|
|
|
|
indexBuffer = null;
|
|
rawIndexData = null;
|
|
IndexBufferLength = 0;
|
|
|
|
foreach (var inputLayouts in vertexLayoutsPerShaderAndPass.Values)
|
|
foreach (var inputLayout in inputLayouts)
|
|
inputLayout.Dispose();
|
|
vertexLayoutsPerShaderAndPass.Clear();
|
|
PickHull = null;
|
|
}
|
|
|
|
public DataStream MapVertexBufferRaw(MapMode argAccessMode)
|
|
{
|
|
if (vertexBuffer == null)
|
|
throw new NullReferenceException("GeometryBuffer: Access forbidden until resource has been initialized.");
|
|
|
|
if (rawMappedVertexData != null)
|
|
throw new InvalidOperationException("GeometryBuffer: Only one access to the vertex buffer at one time possible.");
|
|
|
|
try
|
|
{
|
|
rawMappedVertexData = vertexBuffer.Map(argAccessMode, MapFlags.None);
|
|
return rawMappedVertexData;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new ActionFailedException("GeometryBuffer: Access to the vertex buffer was not successful. " + ex.ToString());
|
|
}
|
|
}
|
|
|
|
public void UnmapVertexBuffer()
|
|
{
|
|
if (vertexBuffer == null)
|
|
throw new NullReferenceException("GeometryBuffer: Access forbidden until resource has been initialized.");
|
|
|
|
if (rawMappedVertexData == null)
|
|
throw new InvalidOperationException("GeometryBuffer: No access was done, ending the access to the vertex buffer is not possible.");
|
|
|
|
if (typeMappedVertexData != null)
|
|
{
|
|
rawMappedVertexData.Position = 0;
|
|
var tmp = (byte[])typeMappedVertexData;
|
|
rawMappedVertexData.WriteRange(tmp);
|
|
}
|
|
vertexBuffer.Unmap();
|
|
rawMappedVertexData = null;
|
|
typeMappedVertexData = null;
|
|
}
|
|
|
|
public DataStream MapIndexBufferRaw(MapMode argAccessMode)
|
|
{
|
|
if (indexBuffer == null)
|
|
throw new NullReferenceException("GeometryBuffer: Access forbidden until resource has been initialized.");
|
|
|
|
if (rawMappedIndexData != null)
|
|
throw new InvalidOperationException("GeometryBuffer: Only one access to the index buffer at one time possible.");
|
|
|
|
try
|
|
{
|
|
rawMappedIndexData = indexBuffer.Map(argAccessMode, MapFlags.None);
|
|
return rawMappedIndexData;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new ActionFailedException("GeometryBuffer: Access to the index buffer was not successful. " + ex.ToString());
|
|
}
|
|
}
|
|
|
|
public void UnmapIndexBuffer()
|
|
{
|
|
if (indexBuffer == null)
|
|
throw new NullReferenceException("GeometryBuffer: Access forbidden until resource has been initialized.");
|
|
|
|
if (rawMappedIndexData == null)
|
|
throw new InvalidOperationException("GeometryBuffer: No access was done ending, the access to the index buffer is not possible.");
|
|
|
|
if (typeMappedIndexData != null)
|
|
{
|
|
rawMappedIndexData.Position = 0;
|
|
rawMappedIndexData.WriteRange(typeMappedIndexData);
|
|
}
|
|
|
|
indexBuffer.Unmap();
|
|
rawMappedIndexData = null;
|
|
typeMappedIndexData = null;
|
|
}
|
|
|
|
public void ConvertToAdjacency()
|
|
{
|
|
Console.WriteLine(string.Format("Convert to adjacency."));
|
|
|
|
switch (PrimitiveTopology)
|
|
{
|
|
case PrimitiveTopology.LineList:
|
|
case PrimitiveTopology.LineStrip:
|
|
case PrimitiveTopology.TriangleList:
|
|
case PrimitiveTopology.TriangleStrip:
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
InputElement[] inputLayout = new InputElement[2];
|
|
inputLayout[0] = new InputElement("POSITION", 0, SlimDX.DXGI.Format.R32G32B32_Float, 0, 0, InputClassification.PerVertexData, 0);
|
|
inputLayout[1] = new InputElement("END", 0, SlimDX.DXGI.Format.R8_UInt, vertexElementSize - 1, 0, InputClassification.PerVertexData, 0);
|
|
|
|
// create the mesh
|
|
int numVertices = VertexBufferLength;
|
|
int numIndices = IndexBufferLength;
|
|
|
|
Mesh mesh = null;
|
|
try
|
|
{
|
|
mesh = new Mesh(Engine.Device, inputLayout, inputLayout[0].SemanticName, numVertices, numIndices / 3, MeshFlags.Has32BitIndices);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new ActionFailedException("GeometryBuffer: Unable to create temp. mesh for adjacency data generation. " + ex.ToString());
|
|
}
|
|
|
|
//set the VB
|
|
mesh.SetVertexData(0, rawVertexData);
|
|
|
|
//set the IB
|
|
mesh.SetIndexData(rawIndexData, numIndices);
|
|
|
|
//generate adjacency
|
|
const float epsilon = 0.0f;
|
|
mesh.GenerateAdjacencyAndPointRepresentation(epsilon);
|
|
|
|
//generate adjacency indices
|
|
mesh.GenerateGeometryShaderAdjacency();
|
|
|
|
//get the adjacency data out of the mesh
|
|
MeshBuffer indexBufferMesh = null;
|
|
DataStream adjIndices = null;
|
|
|
|
try
|
|
{
|
|
indexBufferMesh = mesh.GetIndexBuffer();
|
|
adjIndices = indexBufferMesh.Map();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new ActionFailedException("GeometryBuffer: Unable to retrive indexdata for adjacency data generation. " + ex.ToString());
|
|
}
|
|
|
|
SetIndexData((int)(adjIndices.Length / sizeof(uint)), adjIndices, false);
|
|
|
|
//cleanup
|
|
indexBufferMesh.Unmap();
|
|
indexBufferMesh.Dispose();
|
|
mesh.Dispose();
|
|
|
|
switch (this.PrimitiveTopology)
|
|
{
|
|
case PrimitiveTopology.LineList:
|
|
PrimitiveTopology = PrimitiveTopology.LineListWithAdjacency;
|
|
break;
|
|
case PrimitiveTopology.LineStrip:
|
|
PrimitiveTopology = PrimitiveTopology.LineStripWithAdjacency;
|
|
break;
|
|
case PrimitiveTopology.TriangleList:
|
|
PrimitiveTopology = PrimitiveTopology.TriangleListWithAdjacency;
|
|
break;
|
|
case PrimitiveTopology.TriangleStrip:
|
|
PrimitiveTopology = PrimitiveTopology.TriangleStripWithAdjacency;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void Render(IShader argShader)
|
|
{
|
|
if (vertexElements.Length == 0)
|
|
return;
|
|
|
|
if (argShader.Technique == null)
|
|
return;
|
|
|
|
int currentPass = argShader.CurrentPass;
|
|
EffectPass currentEffectPass = argShader.Technique.GetPassByIndex(currentPass);
|
|
|
|
InputLayout vertexLayout = null;
|
|
List<InputLayout> ilList;
|
|
if (vertexLayoutsPerShaderAndPass.TryGetValue(argShader, out ilList) && ilList.Count > currentPass)
|
|
{
|
|
vertexLayout = ilList[currentPass];
|
|
}
|
|
else
|
|
{
|
|
vertexLayout = new InputLayout(Engine.Device, inputElementDesc, currentEffectPass.Description.Signature);
|
|
if (vertexLayout != null)
|
|
{
|
|
if (ilList == null)
|
|
{
|
|
ilList = new List<InputLayout>();
|
|
ilList.Add(vertexLayout);
|
|
vertexLayoutsPerShaderAndPass[argShader] = ilList;
|
|
}
|
|
else
|
|
{
|
|
ilList.Add(vertexLayout);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (vertexLayout != null)
|
|
{
|
|
currentEffectPass.Apply();
|
|
Engine.Device.InputAssembler.SetInputLayout(vertexLayout);
|
|
if (indexBuffer == null)
|
|
Engine.Device.Draw(VertexBufferLength, 0);
|
|
else
|
|
Engine.Device.DrawIndexed(IndexBufferUsableLength, IndexBufferOffset, 0);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public int IndexBufferOffset { get; set; }
|
|
public int IndexBufferUsableLength { get; set; }
|
|
public int IndexBufferLength { get; private set; }
|
|
public int VertexBufferLength { get; private set; }
|
|
public PrimitiveTopology PrimitiveTopology { get; set; }
|
|
|
|
public bool IsPickable
|
|
{
|
|
get
|
|
{
|
|
return this.PickHull != null;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (this.IsPickable == value)
|
|
return;
|
|
|
|
this.PickHull = new PickHull(rawVertexData, VertexBufferLength, vertexElements, rawIndexData, IndexBufferLength);
|
|
}
|
|
}
|
|
|
|
public IPickHull PickHull { get; protected set; }
|
|
|
|
#endregion
|
|
|
|
#region Constants
|
|
public static byte SetIndexBufferCommandType = 0;
|
|
public static byte SetVertexBufferCommandType = 1;
|
|
public static byte DrawGeometryCommandType = 2;
|
|
#endregion
|
|
|
|
#region Private members
|
|
|
|
SlimDX.Direct3D10.Buffer indexBuffer;
|
|
DataStream rawIndexData;
|
|
uint[] typeMappedIndexData;
|
|
DataStream rawMappedIndexData;
|
|
|
|
int vertexElementSize;
|
|
SlimDX.Direct3D10.Buffer vertexBuffer;
|
|
DataStream rawVertexData;
|
|
object typeMappedVertexData;
|
|
Type typeMappedVertexDataType;
|
|
DataStream rawMappedVertexData;
|
|
|
|
VertexElement[] vertexElements;
|
|
InputElement[] inputElementDesc;
|
|
|
|
Dictionary<IShader, List<InputLayout>> vertexLayoutsPerShaderAndPass = new Dictionary<IShader, List<InputLayout>>();
|
|
|
|
#endregion
|
|
|
|
#region CommandUser Members
|
|
|
|
public override CommandExecuteResult ExecuteCommand(byte argCommandType, CommandBuffer argCurrentBuffer, int argCurrentPositon)
|
|
{
|
|
if (argCommandType == GeometryBuffer.SetIndexBufferCommandType)
|
|
{
|
|
if (Engine.EngineStates.LastIndexBufferProvider != this)
|
|
{
|
|
Engine.Device.InputAssembler.SetIndexBuffer(indexBuffer, SlimDX.DXGI.Format.R32_UInt, IndexBufferOffset);
|
|
Engine.EngineStates.LastIndexBufferProvider = this;
|
|
}
|
|
}
|
|
else if (argCommandType == GeometryBuffer.SetVertexBufferCommandType)
|
|
{
|
|
if (Engine.EngineStates.LastVertexBufferProvider != this)
|
|
{
|
|
Engine.Device.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, vertexElementSize, 0));
|
|
Engine.Device.InputAssembler.SetPrimitiveTopology(PrimitiveTopology);
|
|
Engine.EngineStates.LastVertexBufferProvider = this;
|
|
}
|
|
}
|
|
else if (argCommandType == GeometryBuffer.DrawGeometryCommandType)
|
|
{
|
|
if (Engine.EngineStates.LastShader == null)
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Red;
|
|
Console.WriteLine("GeometryBuffer could not render without a valid shader.");
|
|
Console.ForegroundColor = ConsoleColor.White;
|
|
}
|
|
else if (Engine.EngineStates.LastVertexBufferProvider == null)
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Red;
|
|
Console.WriteLine("GeometryBuffer could not render without a valid vertex buffer.");
|
|
Console.ForegroundColor = ConsoleColor.White;
|
|
}
|
|
else
|
|
{
|
|
Render(Engine.EngineStates.LastShader);
|
|
}
|
|
}
|
|
|
|
return CommandExecuteResult.None;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable Members
|
|
|
|
void IDisposable.Dispose()
|
|
{
|
|
if (Engine.EngineStates.LastIndexBufferProvider == this)
|
|
Engine.EngineStates.LastIndexBufferProvider = null;
|
|
if (Engine.EngineStates.LastVertexBufferProvider == this)
|
|
Engine.EngineStates.LastVertexBufferProvider = null;
|
|
|
|
DeleteVertexData();
|
|
DeleteIndexData();
|
|
|
|
base.Dispose();
|
|
}
|
|
|
|
#endregion
|
|
|
|
public override ICreationParams CreationParams
|
|
{
|
|
get { return null; }
|
|
}
|
|
|
|
public override ObservableCollection<IResource> Children
|
|
{
|
|
get { return null; }
|
|
}
|
|
|
|
#region IGeometryBuffer Members
|
|
|
|
|
|
public void SetIndexData(IEnumerable<uint> argIndexData, bool argNeedsDynamicAccess)
|
|
{
|
|
this.SetIndexData(argIndexData.Count(), new DataStream(argIndexData.ToArray(), true, true), argNeedsDynamicAccess);
|
|
}
|
|
|
|
public uint[] MapIndexBuffer(MapMode argAccessMode)
|
|
{
|
|
var rawData = this.MapIndexBufferRaw(argAccessMode);
|
|
this.typeMappedIndexData = new uint[this.IndexBufferLength];
|
|
rawData.Position = 0;
|
|
rawData.ReadRange(this.typeMappedIndexData, 0, this.IndexBufferLength);
|
|
rawData.Position = 0;
|
|
|
|
return this.typeMappedIndexData;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IGeometryBuffer<TVertex> Members
|
|
|
|
public void SetVertexData<TVertex>(IEnumerable<TVertex> argVertexData, VertexElement[] argVertexElements, bool argNeedsDynamicAccess) where TVertex : struct
|
|
{
|
|
this.SetVertexData(argVertexData.Count(), new DataStream(argVertexData.ToArray(), true, true), argVertexElements, argNeedsDynamicAccess);
|
|
}
|
|
|
|
public TVertex[] MapVertexBuffer<TVertex>(MapMode argAccessMode) where TVertex : struct
|
|
{
|
|
var rawData = this.MapVertexBufferRaw(argAccessMode);
|
|
typeMappedVertexDataType = typeof(TVertex);
|
|
var tmp = new TVertex[this.VertexBufferLength];
|
|
typeMappedVertexData = tmp;
|
|
rawData.Position = 0;
|
|
rawData.ReadRange<TVertex>(tmp, 0, this.VertexBufferLength);
|
|
rawData.Position = 0;
|
|
|
|
return tmp;
|
|
}
|
|
|
|
#endregion
|
|
};
|
|
}
|