/// <summary> /// Compacts the index buffer from 32 bits to 16 bits per index, if possible. /// </summary> /// <param name="meshData">The mesh data.</param> /// <returns>Returns true if index buffer was actually compacted.</returns> /// <exception cref="System.NotImplementedException"></exception> public static unsafe bool CompactIndexBuffer(this MeshDrawData meshData) { // Already processed? if (meshData.IndexBuffer == null || !meshData.IndexBuffer.Is32Bit) { return(false); } // For now, require a MeshData with only one vertex buffer and no index buffer if (meshData.VertexBuffers.Length != 1) { throw new NotImplementedException(); } var vertexBufferBinding = meshData.VertexBuffers[0]; var vertexCount = vertexBufferBinding.Count; // Can't compact? // Note that 65536 could be valid, but 0xFFFF is kept for primitive restart in strips. if (vertexCount >= 65536 || !meshData.IndexBuffer.Is32Bit) { return(false); } // Create new index buffer var indexCount = meshData.IndexBuffer.Count; var indexBufferData = new byte[indexCount * Utilities.SizeOf <ushort>()]; fixed(byte *oldIndexBufferDataStart = &meshData.IndexBuffer.Buffer.Value.Content[0]) fixed(byte *indexBufferDataStart = &indexBufferData[0]) { var oldIndexBufferDataPtr = (int *)oldIndexBufferDataStart; var indexBufferDataPtr = (ushort *)indexBufferDataStart; for (int i = 0; i < indexCount; ++i) { // This also works to convert 0xFFFFFFFF into 0xFFFF (primitive restart in strips). *indexBufferDataPtr++ = (ushort)*oldIndexBufferDataPtr++; } meshData.IndexBuffer = new IndexBufferBindingData(new BufferData(BufferFlags.IndexBuffer, indexBufferData), false, indexCount); } return(true); }
/// <summary> /// Extracts a selection of vertices from a vertex buffer stored in this mesh data. /// </summary> /// <param name="meshData">The mesh data.</param> /// <param name="vertexElementToExtract">The declaration to extract (e.g. "POSITION0"...etc.) </param> public static T[] GetVertexBufferData <T>(this MeshDrawData meshData, params string[] vertexElementToExtract) where T : struct { var declaration = meshData.VertexBuffers[0].Declaration; var offsets = declaration.EnumerateWithOffsets().Where(vertexElementOffset => vertexElementToExtract.Contains(vertexElementOffset.VertexElement.SemanticAsText)).ToList(); int expectedSize = offsets.Sum(vertexElementWithOffset => vertexElementWithOffset.Size); var count = meshData.VertexBuffers[0].Count; int outputSize = expectedSize * count; int checkSize = (int)(outputSize / Utilities.SizeOf <T>()) * Utilities.SizeOf <T>(); if (checkSize != outputSize) { throw new ArgumentException(string.Format("Size of T is not a multiple of totalSize {0}", outputSize)); } var output = new T[outputSize / Utilities.SizeOf <T>()]; var handleOutput = GCHandle.Alloc(output, GCHandleType.Pinned); var ptrOutput = handleOutput.AddrOfPinnedObject(); var handleInput = GCHandle.Alloc(meshData.VertexBuffers[0].Buffer.Value.Content, GCHandleType.Pinned); var ptrInput = handleInput.AddrOfPinnedObject(); for (int i = 0; i < count; i++) { foreach (var vertexElementWithOffset in offsets) { Utilities.CopyMemory(ptrOutput, ptrInput + vertexElementWithOffset.Offset, vertexElementWithOffset.Size); ptrOutput = ptrOutput + vertexElementWithOffset.Size; } ptrInput += declaration.VertexStride; } handleInput.Free(); handleOutput.Free(); return(output); }
/// <summary> /// Determines whether the specified mesh draw data is simple. /// A <see cref="MeshDrawData"/> is simple if: /// * It contains only one <see cref="VertexBufferBindingData"/>, which must be simple. /// * It contains either no <see cref="IndexBufferBindingData"/>, or a simple one. /// * StartLocation is 0. /// * DrawCount is IndexBuffer.Count if there is an index buffer, otherwise VertexBuffers[0].Count. /// </summary> /// <param name="meshDrawData">The mesh draw data.</param> /// <returns></returns> public static bool IsSimple(this MeshDrawData meshDrawData) { if (meshDrawData.VertexBuffers.Length != 1) { return(false); } if (!meshDrawData.VertexBuffers[0].IsSimple()) { return(false); } if (meshDrawData.IndexBuffer != null) { if (!meshDrawData.IndexBuffer.IsSimple()) { return(false); } if (meshDrawData.DrawCount != meshDrawData.IndexBuffer.Count) { return(false); } } else { if (meshDrawData.DrawCount != meshDrawData.VertexBuffers[0].Count) { return(false); } } if (meshDrawData.StartLocation != 0) { return(false); } return(true); }
/// <summary> /// Generates an index buffer for this mesh data. /// </summary> /// <param name="meshData">The mesh data.</param> public unsafe static void GenerateIndexBuffer(this MeshDrawData meshData) { // For now, require a MeshData with only one vertex buffer and no index buffer if (meshData.VertexBuffers.Length != 1 || meshData.IndexBuffer != null) { throw new NotImplementedException(); } var oldVertexBuffer = meshData.VertexBuffers[0]; var vertexStride = oldVertexBuffer.Declaration.VertexStride; var indexMapping = GenerateIndexMapping(oldVertexBuffer); var vertices = indexMapping.Vertices; // Generate vertex buffer var vertexBufferData = new byte[oldVertexBuffer.Declaration.VertexStride * indexMapping.Vertices.Length]; fixed(byte *oldVertexBufferDataStart = &oldVertexBuffer.Buffer.Value.Content[oldVertexBuffer.Offset]) fixed(byte *vertexBufferDataStart = &vertexBufferData[0]) { var vertexBufferDataCurrent = vertexBufferDataStart; for (int i = 0; i < vertices.Length; ++i) { Utilities.CopyMemory((IntPtr)vertexBufferDataCurrent, new IntPtr(&oldVertexBufferDataStart[vertexStride * vertices[i]]), vertexStride); vertexBufferDataCurrent += vertexStride; } meshData.VertexBuffers[0] = new VertexBufferBindingData(new BufferData(BufferFlags.VertexBuffer, vertexBufferData), oldVertexBuffer.Declaration, indexMapping.Vertices.Length); } // Generate index buffer var indexBufferData = new byte[indexMapping.Indices.Length * Utilities.SizeOf <int>()]; fixed(int *indexDataStart = &indexMapping.Indices[0]) fixed(byte *indexBufferDataStart = &indexBufferData[0]) { Utilities.CopyMemory((IntPtr)indexBufferDataStart, (IntPtr)indexDataStart, indexBufferData.Length); meshData.IndexBuffer = new IndexBufferBindingData(new BufferData(BufferFlags.IndexBuffer, indexBufferData), true, indexMapping.Indices.Length); } }
/// <summary> /// Generates the tangents and binormals for this mesh data. /// Tangents and bitangents will be encoded as float4: /// float3 for tangent and an additional float for handedness (1 or -1), /// so that bitangent can be reconstructed. /// More info at http://www.terathon.com/code/tangent.html /// </summary> /// <param name="meshData">The mesh data.</param> public static unsafe void GenerateTangentBinormal(this MeshDrawData meshData) { if (!meshData.IsSimple()) { throw new ArgumentException("meshData is not simple."); } if (meshData.PrimitiveType != PrimitiveType.TriangleList && meshData.PrimitiveType != PrimitiveType.TriangleListWithAdjacency) { throw new NotImplementedException(); } var vertexBufferBinding = meshData.VertexBuffers[0]; var indexBufferBinding = meshData.IndexBuffer; var indexData = indexBufferBinding != null ? indexBufferBinding.Buffer.Value.Content : null; var oldVertexStride = vertexBufferBinding.Declaration.VertexStride; var bufferData = vertexBufferBinding.Buffer.Value.Content; // TODO: Usage index in key var offsetMapping = vertexBufferBinding.Declaration .EnumerateWithOffsets() .ToDictionary(x => x.VertexElement.SemanticAsText, x => x.Offset); var positionOffset = offsetMapping["POSITION"]; var uvOffset = offsetMapping[VertexElementUsage.TextureCoordinate]; var normalOffset = offsetMapping[VertexElementUsage.Normal]; // Add tangent to vertex declaration var vertexElements = vertexBufferBinding.Declaration.VertexElements.ToList(); if (!offsetMapping.ContainsKey(VertexElementUsage.Tangent)) { vertexElements.Add(VertexElement.Tangent <Vector4>()); } vertexBufferBinding.Declaration = new VertexDeclaration(vertexElements.ToArray()); var newVertexStride = vertexBufferBinding.Declaration.VertexStride; // Update mapping offsetMapping = vertexBufferBinding.Declaration .EnumerateWithOffsets() .ToDictionary(x => x.VertexElement.SemanticAsText, x => x.Offset); var tangentOffset = offsetMapping[VertexElementUsage.Tangent]; var newBufferData = new byte[vertexBufferBinding.Count * newVertexStride]; var tangents = new Vector3[vertexBufferBinding.Count]; var bitangents = new Vector3[vertexBufferBinding.Count]; fixed(byte *indexBufferStart = indexData) fixed(byte *oldBuffer = &bufferData[vertexBufferBinding.Offset]) fixed(byte *newBuffer = &newBufferData[0]) { var indexBuffer32 = indexBufferBinding != null && indexBufferBinding.Is32Bit ? (int *)indexBufferStart : null; var indexBuffer16 = indexBufferBinding != null && !indexBufferBinding.Is32Bit ? (short *)indexBufferStart : null; var indexCount = indexBufferBinding != null ? indexBufferBinding.Count : vertexBufferBinding.Count; for (int i = 0; i < indexCount; i += 3) { // Get indices int index1 = i + 0; int index2 = i + 1; int index3 = i + 2; if (indexBuffer32 != null) { index1 = indexBuffer32[index1]; index2 = indexBuffer32[index2]; index3 = indexBuffer32[index3]; } else if (indexBuffer16 != null) { index1 = indexBuffer16[index1]; index2 = indexBuffer16[index2]; index3 = indexBuffer16[index3]; } int vertexOffset1 = index1 * oldVertexStride; int vertexOffset2 = index2 * oldVertexStride; int vertexOffset3 = index3 * oldVertexStride; // Get positions var position1 = (Vector3 *)&oldBuffer[vertexOffset1 + positionOffset]; var position2 = (Vector3 *)&oldBuffer[vertexOffset2 + positionOffset]; var position3 = (Vector3 *)&oldBuffer[vertexOffset3 + positionOffset]; // Get texture coordinates var uv1 = (Vector3 *)&oldBuffer[vertexOffset1 + uvOffset]; var uv2 = (Vector3 *)&oldBuffer[vertexOffset2 + uvOffset]; var uv3 = (Vector3 *)&oldBuffer[vertexOffset3 + uvOffset]; // Calculate position and UV vectors from vertex 1 to vertex 2 and 3 var edge1 = *position2 - *position1; var edge2 = *position3 - *position1; var uvEdge1 = *uv2 - *uv1; var uvEdge2 = *uv3 - *uv1; var t = Vector3.Normalize(uvEdge2.Y * edge1 - uvEdge1.Y * edge2); var b = Vector3.Normalize(uvEdge1.X * edge2 - uvEdge2.X * edge1); // Contribute to every vertex tangents[index1] += t; tangents[index2] += t; tangents[index3] += t; bitangents[index1] += b; bitangents[index2] += b; bitangents[index3] += b; } var oldVertexOffset = 0; var newVertexOffset = 0; for (int i = 0; i < vertexBufferBinding.Count; ++i) { Utilities.CopyMemory(new IntPtr(&newBuffer[newVertexOffset]), new IntPtr(&oldBuffer[oldVertexOffset]), oldVertexStride); var normal = *(Vector3 *)&oldBuffer[oldVertexOffset + normalOffset]; var target = ((float *)(&newBuffer[newVertexOffset + tangentOffset])); var tangent = -tangents[i]; var bitangent = bitangents[i]; // Gram-Schmidt orthogonalize *((Vector3 *)target) = Vector3.Normalize(tangent - normal * Vector3.Dot(normal, tangent)); // Calculate handedness target[3] = Vector3.Dot(Vector3.Cross(normal, tangent), bitangent) < 0.0f ? -1.0f : 1.0f; oldVertexOffset += oldVertexStride; newVertexOffset += newVertexStride; } } vertexBufferBinding.Offset = 0; vertexBufferBinding.Buffer = new BufferData(BufferFlags.VertexBuffer, newBufferData); }
public unsafe static void SortMeshPolygons(this MeshDrawData meshData, Vector3 viewDirectionForSorting) { // need to have alreade an vertex buffer if (meshData.VertexBuffers == null) { throw new ArgumentException(); } // For now, require a MeshData with an index buffer if (meshData.IndexBuffer == null) { throw new NotImplementedException("The mesh Data needs to have index buffer"); } if (meshData.VertexBuffers.Length != 1) { throw new NotImplementedException("Sorting not implemented for multiple vertex buffers by submeshdata"); } if (viewDirectionForSorting == Vector3.Zero) { // By default to -Z if sorting is set to null viewDirectionForSorting = -Vector3.UnitZ; } const int PolySize = 3; // currently only triangle list are supported var polyIndicesSize = PolySize * Utilities.SizeOf <int>(); var vertexBuffer = meshData.VertexBuffers[0]; var oldIndexBuffer = meshData.IndexBuffer; var vertexStride = vertexBuffer.Declaration.VertexStride; // Generate the sort list var sortList = new List <KeyValuePair <int, Vector3> >(); var pointList = new List <Vector3>(); fixed(byte *vertexBufferPointerStart = &vertexBuffer.Buffer.Value.Content[vertexBuffer.Offset]) fixed(byte *indexBufferPointerStart = &oldIndexBuffer.Buffer.Value.Content[oldIndexBuffer.Offset]) { for (var i = 0; i < oldIndexBuffer.Count / PolySize; ++i) { // fill the point list of the polygon vertices pointList.Clear(); for (var u = 0; u < PolySize; ++u) { var curIndex = *(int *)(indexBufferPointerStart + Utilities.SizeOf <int>() * (i * PolySize + u)); var pVertexPos = (Vector3 *)(vertexBufferPointerStart + vertexStride * curIndex); pointList.Add(*pVertexPos); } // compute the bary-center var accu = Vector3.Zero; foreach (var pt in pointList) //linq do not seems to work on Vector3 type, so compute the mean by hand ... { accu += pt; } var center = accu / pointList.Count; // add to the list to sort sortList.Add(new KeyValuePair <int, Vector3>(i, center)); } } // sort the list var sortedIndices = sortList.OrderBy(x => Vector3.Dot(x.Value, viewDirectionForSorting)).Select(x => x.Key).ToList(); // TODO have a generic delegate for sorting // re-write the index buffer var newIndexBufferData = new byte[oldIndexBuffer.Count * Utilities.SizeOf <int>()]; fixed(byte *newIndexDataStart = &newIndexBufferData[0]) fixed(byte *oldIndexDataStart = &oldIndexBuffer.Buffer.Value.Content[0]) { var newIndexBufferPointer = newIndexDataStart; foreach (var index in sortedIndices) { Utilities.CopyMemory((IntPtr)(newIndexBufferPointer), (IntPtr)(oldIndexDataStart + index * polyIndicesSize), polyIndicesSize); newIndexBufferPointer += polyIndicesSize; } } meshData.IndexBuffer = new IndexBufferBindingData(new BufferData(BufferFlags.IndexBuffer, newIndexBufferData), oldIndexBuffer.Is32Bit, oldIndexBuffer.Count); }
/// <summary> /// Transform a vertex buffer positions, normals, tangents and bitangents using the given matrix. /// </summary> /// <param name="meshDrawDatas">The mesh draw datas.</param> /// <param name="can32BitIndex">A flag stating if 32 bit index buffers.</param> public unsafe static MeshDrawData MergeDrawData(IList <MeshDrawData> meshDrawDatas, bool can32BitIndex) { if (meshDrawDatas.Count == 0) { throw new ArgumentException("Need at least 1 MeshDrawData.", "meshDrawDatas"); } if (meshDrawDatas.Count == 1) { return(meshDrawDatas[0]); } // Check that vertex buffer declarations are matching var firstMeshDrawData = meshDrawDatas[0]; if (!firstMeshDrawData.IsSimple()) { throw new InvalidOperationException("Can only merge simple MeshDrawData."); } var firstVertexBuffer = firstMeshDrawData.VertexBuffers[0]; var hasIndexBuffer = IsIndexed(meshDrawDatas); int totalVertexCount = 0; int totalIndexCount = 0; //TODO: extend to non-simple vertex declarations, fill with default values it missing declarations etc. ? for (int i = 0; i < meshDrawDatas.Count; i++) { var meshDrawData = meshDrawDatas[i]; // This should not happen anymore if (i != 0) { if (!meshDrawData.IsSimple()) { throw new InvalidOperationException("Can only merge simple MeshDrawData."); } if (meshDrawData.VertexBuffers.Length != firstMeshDrawData.VertexBuffers.Length) { throw new InvalidOperationException("Non-matching vertex buffer declarations."); } if (!meshDrawData.VertexBuffers[0].Declaration.Equals(firstMeshDrawData.VertexBuffers[0].Declaration)) { throw new InvalidOperationException("Non-matching vertex buffer declarations."); } } if (meshDrawData.PrimitiveType != PrimitiveType.TriangleList) { throw new InvalidOperationException("Can only merge TriangleList."); } // Update vertex/index counts totalVertexCount += meshDrawData.VertexBuffers[0].Count; if (hasIndexBuffer) { if (meshDrawData.IndexBuffer != null) { totalIndexCount += meshDrawData.IndexBuffer.Count; } else { totalIndexCount += meshDrawData.VertexBuffers[0].Count; } } } // Allocate vertex buffer var result = new MeshDrawData { PrimitiveType = PrimitiveType.TriangleList }; var destBufferData = new byte[firstVertexBuffer.Declaration.VertexStride * totalVertexCount]; result.VertexBuffers = new VertexBufferBindingData[] { new VertexBufferBindingData( new BufferData(BufferFlags.VertexBuffer, destBufferData), firstVertexBuffer.Declaration, totalVertexCount, firstVertexBuffer.Stride) }; // Copy vertex buffers fixed(byte *destBufferDataStart = &destBufferData[0]) { var destBufferDataCurrent = destBufferDataStart; foreach (MeshDrawData meshDrawData in meshDrawDatas) { var sourceBuffer = meshDrawData.VertexBuffers[0].Buffer.Value; fixed(byte *sourceBufferDataStart = &sourceBuffer.Content[0]) { Utilities.CopyMemory((IntPtr)destBufferDataCurrent, (IntPtr)sourceBufferDataStart, sourceBuffer.Content.Length); destBufferDataCurrent += sourceBuffer.Content.Length; } } } if (hasIndexBuffer) { var use32BitIndex = can32BitIndex && totalVertexCount > ushort.MaxValue; // 65535 = 0xFFFF is kept for primitive restart in strip // Allocate index buffer destBufferData = new byte[(use32BitIndex ? sizeof(uint) : sizeof(ushort)) * totalIndexCount]; result.IndexBuffer = new IndexBufferBindingData( new BufferData(BufferFlags.IndexBuffer, destBufferData), use32BitIndex, totalIndexCount); // Copy index buffers fixed(byte *destBufferDataStart = &destBufferData[0]) { var destBufferDataCurrent = destBufferDataStart; var offset = 0; foreach (MeshDrawData meshDrawData in meshDrawDatas) { var indexBuffer = meshDrawData.IndexBuffer; byte[] sourceBufferContent = null; var is32Bit = false; if (indexBuffer != null) { sourceBufferContent = indexBuffer.Buffer.Value.Content; is32Bit = indexBuffer.Is32Bit; } if (offset != 0 || (use32BitIndex != meshDrawData.IndexBuffer.Is32Bit)) { if (use32BitIndex) { sourceBufferContent = CreateIntIndexBuffer(offset, meshDrawData.IndexBuffer.Count, sourceBufferContent, is32Bit); } else { sourceBufferContent = CreateShortIndexBuffer(offset, meshDrawData.IndexBuffer.Count, sourceBufferContent, is32Bit); } } fixed(byte *sourceBufferDataStart = &sourceBufferContent[0]) { Utilities.CopyMemory((IntPtr)destBufferDataCurrent, (IntPtr)sourceBufferDataStart, sourceBufferContent.Length); destBufferDataCurrent += sourceBufferContent.Length; } offset += meshDrawData.VertexBuffers[0].Count; } } result.DrawCount = totalIndexCount; } else { result.DrawCount = totalVertexCount; } return(result); }
/// <summary> /// Generates the index buffer with dominant edge and vertex informations. /// Each triangle gets its indices expanded to 12 control points, with 0 to 2 being original triangle, /// 3 to 8 being dominant edges and 9 to 11 being dominant vertices. /// </summary> /// <param name="meshData">The mesh data.</param> public static unsafe void GenerateIndexBufferAEN(this MeshDrawData meshData) { // For now, require a MeshData with only one vertex buffer and one index buffer if (meshData.VertexBuffers.Length != 1 || meshData.IndexBuffer == null) { throw new NotImplementedException(); } // More info at http://developer.download.nvidia.com/whitepapers/2010/PN-AEN-Triangles-Whitepaper.pdf // This implementation might need some performance improvements var indexBuffer = meshData.IndexBuffer; var triangleCount = indexBuffer.Count / 3; var newIndices = new int[triangleCount * 12]; var positionMapping = GenerateIndexMapping(meshData.VertexBuffers[0], "POSITION"); var dominantEdges = new Dictionary <EdgeKeyAEN, EdgeAEN>(); var dominantVertices = new Dictionary <int, int>(); fixed(byte *indexBufferStart = &indexBuffer.Buffer.Value.Content[indexBuffer.Offset]) { var oldIndices = (int *)indexBufferStart; var triangleIndices = stackalloc int[3]; var positionIndices = stackalloc int[3]; // Step 2: prepare initial data for (int i = 0; i < triangleCount; ++i) { triangleIndices[0] = oldIndices[i * 3 + 0]; triangleIndices[1] = oldIndices[i * 3 + 1]; triangleIndices[2] = oldIndices[i * 3 + 2]; positionIndices[0] = positionMapping.Indices[triangleIndices[0]]; positionIndices[1] = positionMapping.Indices[triangleIndices[1]]; positionIndices[2] = positionMapping.Indices[triangleIndices[2]]; newIndices[i * 12 + 0] = triangleIndices[0]; newIndices[i * 12 + 1] = triangleIndices[1]; newIndices[i * 12 + 2] = triangleIndices[2]; newIndices[i * 12 + 3] = triangleIndices[0]; newIndices[i * 12 + 4] = triangleIndices[1]; newIndices[i * 12 + 5] = triangleIndices[1]; newIndices[i * 12 + 6] = triangleIndices[2]; newIndices[i * 12 + 7] = triangleIndices[2]; newIndices[i * 12 + 8] = triangleIndices[0]; newIndices[i * 12 + 9] = triangleIndices[0]; newIndices[i * 12 + 10] = triangleIndices[1]; newIndices[i * 12 + 11] = triangleIndices[2]; // Step 2b/2c: Build dominant vertex/edge list for (int j = 0; j < 3; ++j) { dominantVertices[positionIndices[j]] = triangleIndices[j]; var edge = new EdgeAEN( triangleIndices[((j + 0) % 3)], triangleIndices[((j + 1) % 3)], positionIndices[((j + 0) % 3)], positionIndices[((j + 1) % 3)]); dominantEdges[new EdgeKeyAEN(edge)] = edge; edge = edge.Reverse(); dominantEdges[new EdgeKeyAEN(edge)] = edge; } } // Step3: Find dominant vertex/edge for (int i = 0; i < triangleCount; ++i) { triangleIndices[0] = oldIndices[i * 3 + 0]; triangleIndices[1] = oldIndices[i * 3 + 1]; triangleIndices[2] = oldIndices[i * 3 + 2]; positionIndices[0] = positionMapping.Indices[triangleIndices[0]]; positionIndices[1] = positionMapping.Indices[triangleIndices[1]]; positionIndices[2] = positionMapping.Indices[triangleIndices[2]]; for (int j = 0; j < 3; ++j) { // Dominant edge int vertexKey; if (dominantVertices.TryGetValue(positionIndices[j], out vertexKey)) { newIndices[i * 12 + 9 + j] = vertexKey; } // Dominant vertex EdgeAEN edge; var edgeKey = new EdgeKeyAEN(positionIndices[((j + 0) % 3)], positionIndices[((j + 1) % 3)]); if (dominantEdges.TryGetValue(edgeKey, out edge)) { newIndices[i * 12 + 3 + j * 2 + 0] = edge.Index0; newIndices[i * 12 + 3 + j * 2 + 1] = edge.Index1; } } } } // Generate index buffer var indexBufferData = new byte[triangleCount * 12 * Utilities.SizeOf <int>()]; fixed(int *indexDataStart = &newIndices[0]) fixed(byte *indexBufferDataStart = &indexBufferData[0]) { Utilities.CopyMemory((IntPtr)indexBufferDataStart, (IntPtr)indexDataStart, indexBufferData.Length); meshData.IndexBuffer = new IndexBufferBindingData(new BufferData(BufferFlags.IndexBuffer, indexBufferData), true, triangleCount * 12); } meshData.DrawCount = triangleCount * 12; meshData.PrimitiveType = PrimitiveType.PatchList.ControlPointCount(12); }
/// <summary> /// Split the mesh if it has strictly more than 65535 vertices (max index = 65534) on a plaftorm that does not support 32 bits indices. /// </summary> /// <param name="meshDrawData">The mesh to analyze.</param> /// <param name="can32bitIndex">A flag stating if 32 bit indices are allowed.</param> /// <returns>A list of meshes.</returns> public unsafe static List <MeshDrawData> SplitMesh(MeshDrawData meshDrawData, bool can32bitIndex) { if (meshDrawData.IndexBuffer == null) { return new List <MeshDrawData> { meshDrawData } } ; if (!meshDrawData.IndexBuffer.Is32Bit) // already 16 bits buffer { return new List <MeshDrawData> { meshDrawData } } ; var verticesCount = meshDrawData.VertexBuffers[0].Count; if (verticesCount <= ushort.MaxValue) // can be put in a 16 bits buffer - 65535 = 0xFFFF is kept for primitive restart in strip { meshDrawData.CompactIndexBuffer(); return(new List <MeshDrawData> { meshDrawData }); } // now, we only have a 32 bits buffer that is justified because of a large vertex buffer if (can32bitIndex) // do nothing { return new List <MeshDrawData> { meshDrawData } } ; // TODO: handle primitives other than triangle list if (meshDrawData.PrimitiveType != PrimitiveType.TriangleList) { return new List <MeshDrawData> { meshDrawData } } ; // Split the mesh var finalList = new List <MeshDrawData>(); fixed(byte *indicesByte = &meshDrawData.IndexBuffer.Buffer.Value.Content[0]) { var indicesUint = (uint *)indicesByte; var splitInfos = new List <SplitInformation>(); var currentSplit = new SplitInformation(); currentSplit.StartTriangleIndex = 0; var currentIndexUintPtr = indicesUint; for (int triangleIndex = 0; triangleIndex < meshDrawData.IndexBuffer.Count / 3; ++triangleIndex) { var verticesToAdd = 0; var index0 = *currentIndexUintPtr++; var index1 = *currentIndexUintPtr++; var index2 = *currentIndexUintPtr++; if (!currentSplit.UsedIndices.Contains(index0)) { ++verticesToAdd; } if (!currentSplit.UsedIndices.Contains(index1)) { ++verticesToAdd; } if (!currentSplit.UsedIndices.Contains(index2)) { ++verticesToAdd; } if (currentSplit.UsedIndices.Count + verticesToAdd > 65535) // append in the same group { splitInfos.Add(currentSplit); currentSplit = new SplitInformation(); currentSplit.StartTriangleIndex = triangleIndex; } AddTriangle(currentSplit, index0, index1, index2, triangleIndex); } if (currentSplit.UsedIndices.Count > 0) { splitInfos.Add(currentSplit); } foreach (var splitInfo in splitInfos) { var triangleCount = splitInfo.LastTriangleIndex - splitInfo.StartTriangleIndex + 1; var newMeshDrawData = new MeshDrawData { PrimitiveType = PrimitiveType.TriangleList, DrawCount = 3 * triangleCount, VertexBuffers = new VertexBufferBindingData[meshDrawData.VertexBuffers.Length] }; // vertex buffers for (int vbIndex = 0; vbIndex < meshDrawData.VertexBuffers.Length; ++vbIndex) { var stride = meshDrawData.VertexBuffers[vbIndex].Stride; if (stride == 0) { stride = meshDrawData.VertexBuffers[vbIndex].Declaration.VertexStride; } var newVertexBuffer = new byte[splitInfo.UsedIndices.Count * stride]; fixed(byte *vertexBufferPtr = &meshDrawData.VertexBuffers[vbIndex].Buffer.Value.Content[0]) fixed(byte *newVertexBufferPtr = &newVertexBuffer[vbIndex]) { //copy vertex buffer foreach (var index in splitInfo.UsedIndices) { Utilities.CopyMemory((IntPtr)(newVertexBufferPtr + stride * splitInfo.IndexRemapping[index]), (IntPtr)(vertexBufferPtr + stride * index), stride); } } newMeshDrawData.VertexBuffers[vbIndex] = new VertexBufferBindingData { Offset = 0, Count = splitInfo.UsedIndices.Count, Buffer = new BufferData(BufferFlags.VertexBuffer, newVertexBuffer), Declaration = meshDrawData.VertexBuffers[vbIndex].Declaration, Stride = 0 }; } // index buffer var newIndexBuffer = new byte[sizeof(ushort) * 3 * triangleCount]; fixed(byte *newIndexBufferPtr = &newIndexBuffer[0]) { var newIndexBufferUshortPtr = (ushort *)newIndexBufferPtr; var currentIndexPtr = &indicesUint[3 * splitInfo.StartTriangleIndex]; for (int triangleIndex = 0; triangleIndex < triangleCount; ++triangleIndex) { var index0 = *currentIndexPtr++; var index1 = *currentIndexPtr++; var index2 = *currentIndexPtr++; var newIndex0 = splitInfo.IndexRemapping[index0]; var newIndex1 = splitInfo.IndexRemapping[index1]; var newIndex2 = splitInfo.IndexRemapping[index2]; *newIndexBufferUshortPtr++ = newIndex0; *newIndexBufferUshortPtr++ = newIndex1; *newIndexBufferUshortPtr++ = newIndex2; } } newMeshDrawData.IndexBuffer = new IndexBufferBindingData { Offset = 0, Count = triangleCount * 3, Buffer = new BufferData(BufferFlags.IndexBuffer, newIndexBuffer), Is32Bit = false }; finalList.Add(newMeshDrawData); } } return(finalList); }