void IMeshOptimiser.Apply(float[] originalVertexBuffer, ushort[] originalIndexBuffer, MeshElement meshElement)
        {
            _vertexDeclaration = meshElement.VertexDeclaration;
            _meshElement = meshElement;
            _originalIndexBuffer = originalIndexBuffer;
            unsmoothedChannels = new List<VertexChannel>();
            Dictionary<int, List<int>> smoothingCandidates = new Dictionary<int, List<int>>();

            foreach (VertexChannel channel in _vertexDeclaration.channels)
            {
                if (channel.Name != "NORMAL" && channel.Name != "TANGENT" && channel.Name != "BITANGENT")
                    unsmoothedChannels.Add(channel);
            }

            List<int> uniqueVertices = new List<int>(meshElement.VertexCount);

            for (int i = 0; i < meshElement.VertexCount; ++i)
            {
                int matching = -1;
                foreach (int j in uniqueVertices)
                {
                    if (ChannelsMatch(i, j))
                    {
                        matching = j;
                        break;
                    }
                }
                if (matching >= 0)
                {
                    if (smoothingCandidates.ContainsKey(matching))
                    {
                        smoothingCandidates[matching].Add(i);
                    }
                    else
                    {
                        smoothingCandidates.Add(matching, new List<int>());
                        smoothingCandidates[matching].Add(i);
                    }
                }
                else
                {
                    uniqueVertices.Add(i);
                }
            }

            _optimisedVertexBuffer = new float[uniqueVertices.Count * _vertexDeclaration.Stride];
            _optimisedIndexBuffer = new ushort[originalIndexBuffer.Length];

            for (int i = 0; i < uniqueVertices.Count; ++i)
            {
                MeshHelper.CopyFloatArray(originalVertexBuffer, uniqueVertices[i] * _vertexDeclaration.Stride, _optimisedVertexBuffer, i * _vertexDeclaration.Stride, _vertexDeclaration.Stride);
                ChangeIndices(uniqueVertices[i], i);

                if (smoothingCandidates.ContainsKey(uniqueVertices[i]))
                {
                    foreach (int j in smoothingCandidates[uniqueVertices[i]])
                    {
                        if (meshElement.VertexDeclaration.ContainsChannel("TANGENT"))
                        {
                            float[] tangent = meshElement.ReadAttribute("TANGENT", j);
                            MeshHelper.AddFloatArray(tangent, 0, _optimisedVertexBuffer, i * _vertexDeclaration.Stride + _vertexDeclaration.GetChannel("TANGENT").Offset, 3);
                            float[] bitangent = meshElement.ReadAttribute("BITANGENT", j);
                            MeshHelper.AddFloatArray(bitangent, 0, _optimisedVertexBuffer, i * _vertexDeclaration.Stride + _vertexDeclaration.GetChannel("BITANGENT").Offset, 3);
                        }
                        float[] normal = meshElement.ReadAttribute("NORMAL", j);
                        MeshHelper.AddFloatArray(normal, 0, _optimisedVertexBuffer, i * _vertexDeclaration.Stride + _vertexDeclaration.GetChannel("NORMAL").Offset, 3);
                        ChangeIndices(j, i);
                    }
                }
                MeshHelper.Normalize(_optimisedVertexBuffer, i * _vertexDeclaration.Stride + _vertexDeclaration.GetChannel("NORMAL").Offset, 3);
                if (meshElement.VertexDeclaration.ContainsChannel("TANGENT"))
                {
                    MeshHelper.Normalize(_optimisedVertexBuffer, i * _vertexDeclaration.Stride + _vertexDeclaration.GetChannel("TANGENT").Offset, 3);
                    MeshHelper.Normalize(_optimisedVertexBuffer, i * _vertexDeclaration.Stride + _vertexDeclaration.GetChannel("BITANGENT").Offset, 3);
                }
            }
        }
        private void CreateArrays(bool generateTangents)
        {
            if (generateTangents && _vertexDeclaration.ContainsChannel("TEXCOORD"))
            {
                Console.WriteLine("generating tangent frames....");
                List<VertexChannel> originalChannels = new List<VertexChannel>();
                foreach (VertexChannel channel in _vertexDeclaration.channels)
                {
                    originalChannels.Add(channel);
                }
                originalChannels.Add(new VertexChannel("TANGENT", _vertexDeclaration.Stride, 3));
                originalChannels.Add(new VertexChannel("BITANGENT", _vertexDeclaration.Stride + 3, 3));
                _vertexDeclaration = new VertexDeclaration(originalChannels);
                vertexData = new float[_vertexDeclaration.Stride * tris.Length * 3];
                indexData = new ushort[tris.Length * 3];
                int offset = 0;
                for (int i = 0; i < tris.Length; ++i)
                {
                    for (int j = 0; j < 3; ++j)
                    {
                        tris[i][j].ToArray().CopyTo(vertexData, offset * _vertexDeclaration.Stride);
                        indexData[i * 3 + j] = (ushort)offset;
                        int tangentOffset = _vertexDeclaration.GetChannel("TANGENT").Offset;
                        GenerateTangents(tris[i], j).CopyTo(vertexData, (offset * _vertexDeclaration.Stride) + tangentOffset);
                        ++offset;
                    }
                }
            }
            else
            {
                vertexData = new float[_vertexDeclaration.Stride * tris.Length * 3];
                indexData = new ushort[tris.Length * 3];
                int offset = 0;

                for (int i = 0; i < tris.Length; ++i)
                {
                    for (int j = 0; j < 3; ++j)
                    {
                        tris[i][j].ToArray().CopyTo(vertexData, offset * _vertexDeclaration.Stride);
                        indexData[i * 3 + j] = (ushort)offset;
                        ++offset;
                    }
                }
            }
        }