#region Using Statements using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.Runtime.InteropServices; using ANX.Framework.Content.Pipeline.Helpers; #endregion // This file is part of the ANX.Framework created by the // "ANX.Framework developer group" and released under the Ms-PL license. // For details see: http://anxframework.codeplex.com/license namespace ANX.Framework.Content.Pipeline.Processors { public class VertexBufferContent : ContentItem { public VertexBufferContent() : this(0) { } public VertexBufferContent(int size) { VertexData = new byte[size]; } public byte[] VertexData { get; private set; } public VertexDeclarationContent VertexDeclaration { get; set; } /// /// Gets the size of the specified type, in bytes. /// /// /// /// /// public static int SizeOf(Type type) { if (type == null) throw new ArgumentNullException("type"); if (!type.IsValueType) throw new NotSupportedException("The type of a value that should be used for a VertexBuffer should be a struct."); return Marshal.SizeOf(type); } /// /// Writes additional data into the vertex buffer. Writing begins at the specified byte offset, and each value is spaced according to the specified stride value (in bytes). /// /// /// This method automatically grows the vertex buffer if an attempt is made to write past the buffer end. Write throws NotSupportedException /// if the specified data type cannot be packed into a vertex buffer. For example, if data is not a valid value type. /// /// Use this method to interleave vertex data channels into a single buffer. This can be done by passing the total vertex size as the stride and suitable smaller offsets for each channel. /// You can also concatenate entire vertex buffers by passing the length of the vertex as the offset, 1 as the stride, and the vertex data as the data parameter. /// /// Type being written. /// Offset to begin writing at. /// Stride of the data being written (in bytes). /// Enumerated collection of data. /// /// /// public void Write(int offset, int stride, IEnumerable data) where T : struct { this.Write(offset, stride, typeof(T), data); } /// /// Writes additional data into the vertex buffer. Writing begins at the specified byte offset, and each value is spaced according to the specified stride value (in bytes). /// /// /// This method automatically grows the vertex buffer if an attempt is made to write past the buffer end. Write throws NotSupportedException /// if the specified data type cannot be packed into a vertex buffer. For example, if data is not a valid value type. /// /// Use this method to interleave vertex data channels into a single buffer. This can be done by passing the total vertex size as the stride and suitable smaller offsets for each channel. /// You can also concatenate entire vertex buffers by passing the length of the vertex as the offset, 1 as the stride, and the vertex data as the data parameter. /// /// /// /// /// /// /// /// /// public void Write(int offset, int stride, Type dataType, IEnumerable data) { if (offset < 0) throw new ArgumentOutOfRangeException("offset"); if (dataType == null) throw new ArgumentNullException("dataType"); //TODO: support the second documented use case. int elementSize = SizeOf(dataType); //Make sure we don't intercopy the data if we write it. Having a bigger stride is no problem, in that case we write what we can and then skip all unused bytes to get to the next stride. if (stride < elementSize) throw new ArgumentOutOfRangeException("stride"); if (data == null) throw new ArgumentNullException("data"); List dataList = new List(); foreach (var obj in data) { //obj should not be able to be null, because we tested with SizeOf if it is a ValueType. if (!dataType.IsAssignableFrom(obj.GetType())) throw new ArgumentException(string.Format("data contains elements that are not compatible with \"{0}\"", dataType.Name)); dataList.Add(obj); } //regrow the array if the data would write over the boundaries. int dataLength = dataList.Count * stride; int vertexOffset = offset / stride * stride; if (vertexOffset + dataLength > VertexData.Length) { byte[] newVertexData = new byte[vertexOffset + dataLength]; Array.Copy(VertexData, newVertexData, VertexData.Length); this.VertexData = newVertexData; } //Copy the data over, the content of dataList is guaranteed to be a value type by the SizeOf check earlier. for (int i = 0; i < dataList.Count; i++) { //We have to copy every element separately to support the first documented use case where someone passed a stride that is much bigger than the real data to write only data into the vertex buffer for //a single vertex channel, even though the data in the vertex buffer must be arranged per vertex and not per vertex channel. var dataHandle = GCHandle.Alloc(dataList[i], GCHandleType.Pinned); try { Marshal.Copy(dataHandle.AddrOfPinnedObject(), VertexData, offset + i * stride, elementSize); } finally { dataHandle.Free(); } } } } }