public unsafe static BoundingBox ComputeBoundingBox(this VertexBufferBindingData vertexBufferBinding, ref Matrix matrix) { var positionOffset = vertexBufferBinding.Declaration .EnumerateWithOffsets() .First(x => x.VertexElement.SemanticAsText == "POSITION") .Offset; var boundingBox = BoundingBox.Empty; var vertexStride = vertexBufferBinding.Declaration.VertexStride; fixed(byte *bufferStart = &vertexBufferBinding.Buffer.Value.Content[vertexBufferBinding.Offset]) { byte *buffer = bufferStart; for (int i = 0; i < vertexBufferBinding.Count; ++i) { var position = (Vector3 *)(buffer + positionOffset); Vector3 transformedPosition; Vector3.TransformCoordinate(ref *position, ref matrix, out transformedPosition); BoundingBox.Merge(ref boundingBox, ref transformedPosition, out boundingBox); buffer += vertexStride; } } return(boundingBox); }
/// <summary> /// Generates an index mapping with the specified vertex elements. /// If no vertex elements are specified, use the whole vertex. /// </summary> /// <param name="vertexBufferBinding">The vertex buffer binding.</param> /// <param name="usages">The vertex element usages to consider.</param> /// <returns></returns> public static unsafe IndexMappingResult GenerateIndexMapping(this VertexBufferBindingData vertexBufferBinding, params string[] usages) { var bufferData = vertexBufferBinding.Buffer.Value.Content; var vertexStride = vertexBufferBinding.Declaration.VertexStride; var vertexCount = vertexBufferBinding.Count; var activeBytes = stackalloc byte[vertexStride]; var vertexMapping = new List <int>(); var indexMapping = new int[vertexCount]; var mapping = new Dictionary <VertexKey, int>(new VertexKeyEqualityComparer(activeBytes, vertexStride)); // Create a "mask" of part of the vertices that will be used // TODO: Use bit packing? for (int i = 0; i < vertexBufferBinding.Declaration.VertexStride; ++i) { activeBytes[i] = (byte)(usages.Length == 0 ? 1 : 0); } foreach (var vertexElement in vertexBufferBinding.Declaration.EnumerateWithOffsets()) { if (usages.Contains(vertexElement.VertexElement.SemanticAsText)) { for (int i = 0; i < vertexElement.Size; ++i) { activeBytes[vertexElement.Offset + i] = 1; } } } // Generate index buffer fixed(byte *bufferPointerStart = &bufferData[vertexBufferBinding.Offset]) { var bufferPointer = bufferPointerStart; for (int i = 0; i < vertexCount; ++i) { // Create VertexKey (will generate hash) var vertexKey = new VertexKey(bufferPointer, activeBytes, vertexStride); // Get or create new index int currentIndex; if (!mapping.TryGetValue(vertexKey, out currentIndex)) { currentIndex = vertexMapping.Count; mapping.Add(vertexKey, currentIndex); vertexMapping.Add(i); } // Assign index in result buffer indexMapping[i] = currentIndex; bufferPointer += vertexStride; } } return(new IndexMappingResult { Vertices = vertexMapping.ToArray(), Indices = indexMapping }); }
/// <summary> /// Determines whether the specified vertex buffer binding data is simple. /// A vertex buffer binding data is simple if: /// * Offset is 0. /// * Stride is 0 (automatic), or equals to Declaration.VertexStride. /// * Buffer.Content.Length is equal to Declaration.VertexStride * Count /// </summary> /// <param name="vertexBufferBindingData">The vertex buffer binding data.</param> /// <returns></returns> public static bool IsSimple(this VertexBufferBindingData vertexBufferBindingData) { if (vertexBufferBindingData.Offset != 0) { return(false); } var stride = vertexBufferBindingData.Declaration.VertexStride; if (vertexBufferBindingData.Stride != 0 && vertexBufferBindingData.Stride != stride) { return(false); } var buffer = vertexBufferBindingData.Buffer.Value; if (buffer.Content.Length != stride * vertexBufferBindingData.Count) { return(false); } return(true); }
public unsafe static void CompactHalf(this VertexBufferBindingData vertexBufferBinding) { var vertexElementsWithOffsets = vertexBufferBinding.Declaration .EnumerateWithOffsets() .OrderBy(x => x.Offset) .ToArray(); var vertexElements = new VertexElementConvertInfo[vertexElementsWithOffsets.Length]; int currentOffset = 0; for (int index = 0; index < vertexElementsWithOffsets.Length; index++) { var vertexElementConvertInfo = new VertexElementConvertInfo(); vertexElementConvertInfo.VertexElementWithOffset = vertexElementsWithOffsets[index]; var vertexElement = vertexElementsWithOffsets[index].VertexElement; var vertexElementFormat = vertexElementConvertInfo.VertexElementWithOffset.VertexElement.Format; // First iteration? if (index == 0) { currentOffset = vertexElementsWithOffsets[index].Offset; } vertexElements[index] = vertexElementConvertInfo; vertexElementConvertInfo.OldFormat = vertexElementConvertInfo.VertexElementWithOffset.VertexElement.Format; int offsetShift = 0; switch (vertexElementFormat) { case PixelFormat.R32G32_Float: vertexElementFormat = PixelFormat.R16G16_Float; // Adjust next offset if current object has been resized offsetShift = Utilities.SizeOf <Half2>() - Utilities.SizeOf <Vector2>(); break; case PixelFormat.R32G32B32_Float: vertexElementFormat = PixelFormat.R16G16B16A16_Float; // Adjust next offset if current object has been resized offsetShift = Utilities.SizeOf <Half4>() - Utilities.SizeOf <Vector3>(); break; case PixelFormat.R32G32B32A32_Float: vertexElementFormat = PixelFormat.R16G16B16A16_Float; // Adjust next offset if current object has been resized offsetShift = Utilities.SizeOf <Half4>() - Utilities.SizeOf <Vector4>(); break; } // Has format changed? vertexElementConvertInfo.NeedConversion = vertexElementFormat != vertexElementConvertInfo.VertexElementWithOffset.VertexElement.Format; // Create new vertex element with adjusted offset, and maybe new vertex format (if modified) vertexElementConvertInfo.VertexElementWithOffset.VertexElement = new VertexElement(vertexElement.semanticName, vertexElement.SemanticIndex, vertexElementFormat, currentOffset); // Increment next offset by the same difference as in original declaration if (index + 1 < vertexElementsWithOffsets.Length) { currentOffset += vertexElementsWithOffsets[index + 1].Offset - vertexElementsWithOffsets[index].Offset; } currentOffset += offsetShift; vertexElements[index] = vertexElementConvertInfo; } var oldVertexStride = vertexBufferBinding.Declaration.VertexStride; vertexBufferBinding.Declaration = new VertexDeclaration(vertexElements.Select(x => x.VertexElementWithOffset.VertexElement).ToArray()); var newVertexStride = vertexBufferBinding.Declaration.VertexStride; var newBufferData = new byte[vertexBufferBinding.Count * newVertexStride]; fixed(byte *oldBuffer = &vertexBufferBinding.Buffer.Value.Content[vertexBufferBinding.Offset]) fixed(byte *newBuffer = &newBufferData[0]) { var oldBufferVertexPtr = (IntPtr)oldBuffer; var newBufferVertexPtr = (IntPtr)newBuffer; for (int i = 0; i < vertexBufferBinding.Count; ++i) { foreach (var element in vertexElements) { var oldBufferElementPtr = oldBufferVertexPtr + element.VertexElementWithOffset.Offset; var newBufferElementPtr = newBufferVertexPtr + element.VertexElementWithOffset.VertexElement.AlignedByteOffset; if (element.NeedConversion) { // Convert floatX => halfX switch (element.OldFormat) { case PixelFormat.R32G32_Float: *((Half2 *)newBufferElementPtr) = (Half2)(*((Vector2 *)oldBufferElementPtr)); break; case PixelFormat.R32G32B32_Float: // Put 1.0f in *((Half4 *)newBufferElementPtr) = (Half4)(new Vector4(*((Vector3 *)oldBufferElementPtr), 1.0f)); break; case PixelFormat.R32G32B32A32_Float: *((Half4 *)newBufferElementPtr) = (Half4)(*((Vector4 *)oldBufferElementPtr)); break; } } else { // Copy as is Utilities.CopyMemory(newBufferElementPtr, oldBufferElementPtr, element.VertexElementWithOffset.Size); } } oldBufferVertexPtr += oldVertexStride; newBufferVertexPtr += newVertexStride; } } vertexBufferBinding.Offset = 0; vertexBufferBinding.Buffer = new BufferData(BufferFlags.VertexBuffer, newBufferData); }
/// <summary> /// Transform a vertex buffer positions, normals, tangents and bitangents using the given matrix. /// </summary> /// <param name="meshData">The mesh data.</param> public unsafe static void TransformBuffer(this VertexBufferBindingData vertexBufferBinding, ref Matrix matrix) { // List of items that need to be transformed by the matrix var vertexElementsToTransform1 = vertexBufferBinding.Declaration .EnumerateWithOffsets() .Where(x => x.VertexElement.SemanticName == VertexElementUsage.Position && (x.VertexElement.Format == PixelFormat.R32G32B32A32_Float || x.VertexElement.Format == PixelFormat.R32G32B32_Float)) .ToArray(); // List of items that need to be transformed by the inverse transpose matrix var vertexElementsToTransform2 = vertexBufferBinding.Declaration .EnumerateWithOffsets() .Where(x => (x.VertexElement.SemanticName == VertexElementUsage.Normal || x.VertexElement.SemanticName == VertexElementUsage.Tangent || x.VertexElement.SemanticName == VertexElementUsage.BiTangent) && x.VertexElement.Format == PixelFormat.R32G32B32_Float) .ToArray(); // If needed, compute matrix inverse transpose Matrix inverseTransposeMatrix; if (vertexElementsToTransform2.Length > 0) { Matrix.Invert(ref matrix, out inverseTransposeMatrix); Matrix.Transpose(ref inverseTransposeMatrix, out inverseTransposeMatrix); } else { inverseTransposeMatrix = Matrix.Identity; } // Transform buffer data var bufferData = vertexBufferBinding.Buffer.Value.Content; var vertexStride = vertexBufferBinding.Declaration.VertexStride; var vertexCount = vertexBufferBinding.Count; fixed(byte *bufferPointerStart = &bufferData[vertexBufferBinding.Offset]) { var bufferPointer = bufferPointerStart; for (int i = 0; i < vertexCount; ++i) { // Transform positions foreach (var vertexElement in vertexElementsToTransform1) { var elementPointer = bufferPointer + vertexElement.Offset; if (vertexElement.VertexElement.Format == PixelFormat.R32G32B32A32_Float) { Vector4.Transform(ref *(Vector4 *)elementPointer, ref matrix, out *(Vector4 *)elementPointer); } else { Vector3.TransformCoordinate(ref *(Vector3 *)elementPointer, ref matrix, out *(Vector3 *)elementPointer); } } // Transform normals foreach (var vertexElement in vertexElementsToTransform2) { var elementPointer = bufferPointer + vertexElement.Offset; Vector3.TransformNormal(ref *(Vector3 *)elementPointer, ref inverseTransposeMatrix, out *(Vector3 *)elementPointer); } bufferPointer += vertexStride; } } }