Files
bluflame/aiwaz/Aiwaz.Resources/GeometryBuffer.cs
2026-04-18 22:31:51 +02:00

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
};
}