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 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(IEnumerable argVertexData, VertexElement[] argVertexElements, bool argNeedsDynamicAccess) where TVertex : struct { var geoBuffer = new GeometryBuffer(); geoBuffer.SetVertexData(argVertexData, argVertexElements, argNeedsDynamicAccess); return geoBuffer; } public static GeometryBuffer Create(IEnumerable argIndexData, IEnumerable 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 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(); 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> vertexLayoutsPerShaderAndPass = new Dictionary>(); #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 Children { get { return null; } } #region IGeometryBuffer Members public void SetIndexData(IEnumerable 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 Members public void SetVertexData(IEnumerable argVertexData, VertexElement[] argVertexElements, bool argNeedsDynamicAccess) where TVertex : struct { this.SetVertexData(argVertexData.Count(), new DataStream(argVertexData.ToArray(), true, true), argVertexElements, argNeedsDynamicAccess); } public TVertex[] MapVertexBuffer(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(tmp, 0, this.VertexBufferLength); rawData.Position = 0; return tmp; } #endregion }; }