Exemplo n.º 1
0
        /// <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);
        }
Exemplo n.º 2
0
        /// <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);
        }
Exemplo n.º 3
0
        /// <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);
        }
Exemplo n.º 4
0
        /// <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);
            }
        }
Exemplo n.º 5
0
        /// <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);
        }
Exemplo n.º 6
0
        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);
        }
Exemplo n.º 7
0
        /// <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);
        }
Exemplo n.º 8
0
        /// <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);
        }
Exemplo n.º 9
0
        /// <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);
        }