Exemple #1
0
        /// <summary>
        /// Returns true if the input face and the current strip share an edge.
        /// </summary>
        /// <returns></returns>
        public bool SharesEdge(FaceInfo faceInfo, List <EdgeInfo> edgeInfos)
        {
            //check v0.v1 edge
            var currEdge = Stripifier.FindEdgeInfo(edgeInfos, faceInfo.V0, faceInfo.V1);

            if (IsInStrip(currEdge.Face0) || IsInStrip(currEdge.Face1))
            {
                return(true);
            }

            //check v1.v2 edge
            currEdge = Stripifier.FindEdgeInfo(edgeInfos, faceInfo.V1, faceInfo.V2);

            if (IsInStrip(currEdge.Face0) || IsInStrip(currEdge.Face1))
            {
                return(true);
            }

            //check v2.v0 edge
            currEdge = Stripifier.FindEdgeInfo(edgeInfos, faceInfo.V2, faceInfo.V0);

            if (IsInStrip(currEdge.Face0) || IsInStrip(currEdge.Face1))
            {
                return(true);
            }

            return(false);
        }
Exemple #2
0
        public models Stripify(string filePath)
        {
            var stgpv    = new Model();
            var textures = new MikuMikuLibrary.Textures.TextureSet();
            var texdb    = new TextureDatabase();

            using (var farcArchive = BinaryFile.Load <FarcArchive>(filePath))
            {
                using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("txi")).First(), EntryStreamMode.MemoryStream))
                    texdb.Load(entryStream);
                using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("txd")).First(), EntryStreamMode.MemoryStream))
                    textures.Load(entryStream);
                using (var entryStream = farcArchive.Open(farcArchive.Entries.First(), EntryStreamMode.MemoryStream))
                    stgpv.Load(entryStream, textures, texdb);
            }

            foreach (var meshes in stgpv.Meshes)
            {
                foreach (var submeshes in meshes.SubMeshes)
                {
                    foreach (var indexTable in submeshes.IndexTables)
                    {
                        ushort[] triangleStrip = Stripifier.Stripify(indexTable.Indices);
                        if (triangleStrip != null)
                        {
                            indexTable.PrimitiveType = PrimitiveType.TriangleStrip;
                            indexTable.Indices       = triangleStrip;
                        }
                    }
                }
            }

            var le_model = new models();

            le_model.model    = stgpv;
            le_model.fileName = filePath;
            Logs.WriteLine("Stripified " + Path.GetFileName(filePath));
            return(le_model);
        }
        private static Mesh ConvertMeshFromAiNode(Ai.Node aiNode, Ai.Scene aiScene, Matrix4x4 parentTransformation,
                                                  Dictionary <string, int> boneMap, List <BoneInfo> bones, Dictionary <string, int> materialMap,
                                                  List <Material> materials, string texturesDirectory, TextureSet textureSet)
        {
            if (!aiNode.HasMeshes)
            {
                return(null);
            }

            // Select meshes that have triangles
            var aiMeshes = aiNode.MeshIndices.Select(x => aiScene.Meshes[x]).Where(x =>
                                                                                   x.PrimitiveType == Ai.PrimitiveType.Triangle && x.Faces.Any(y => y.IndexCount == 3)).ToList();

            if (aiMeshes.Count == 0)
            {
                return(null);
            }

            var transformation = parentTransformation * aiNode.Transform.ToNumericsTransposed();
            int vertexCount    = aiMeshes.Sum(x => x.VertexCount);

            var mesh = new Mesh
            {
                Name     = aiNode.Name,
                Vertices = new Vector3[vertexCount],
            };

            int vertexOffset = 0;

            foreach (var aiMesh in aiMeshes)
            {
                for (int i = 0; i < aiMesh.Vertices.Count; i++)
                {
                    mesh.Vertices[vertexOffset + i] =
                        Vector3.Transform(aiMesh.Vertices[i].ToNumerics(), transformation);
                }

                if (aiMesh.HasNormals)
                {
                    if (mesh.Normals == null)
                    {
                        mesh.Normals = new Vector3[vertexCount];
                    }

                    for (int i = 0; i < aiMesh.Normals.Count; i++)
                    {
                        mesh.Normals[vertexOffset + i] =
                            Vector3.Normalize(Vector3.TransformNormal(aiMesh.Normals[i].ToNumerics(),
                                                                      transformation));
                    }
                }

                if (aiMesh.HasTangentBasis)
                {
                    if (mesh.Tangents == null)
                    {
                        mesh.Tangents = new Vector4[vertexCount];
                    }

                    for (int i = 0; i < aiMesh.Tangents.Count; i++)
                    {
                        Vector3 tangent =
                            Vector3.Normalize(Vector3.TransformNormal(aiMesh.Tangents[i].ToNumerics(),
                                                                      transformation));
                        Vector3 bitangent =
                            Vector3.Normalize(Vector3.TransformNormal(aiMesh.BiTangents[i].ToNumerics(),
                                                                      transformation));
                        int direction = Math.Sign(Vector3.Dot(bitangent,
                                                              Vector3.Normalize(Vector3.Cross(mesh.Normals[vertexOffset + i], tangent))));

                        mesh.Tangents[vertexOffset + i] = new Vector4(tangent, direction);
                    }
                }

                if (aiMesh.HasTextureCoords(0))
                {
                    if (mesh.UVChannel1 == null)
                    {
                        mesh.UVChannel1 = new Vector2[vertexCount];
                    }

                    for (int i = 0; i < aiMesh.TextureCoordinateChannels[0].Count; i++)
                    {
                        mesh.UVChannel1[vertexOffset + i] = new Vector2(aiMesh.TextureCoordinateChannels[0][i].X,
                                                                        1f - aiMesh.TextureCoordinateChannels[0][i].Y);
                    }
                }

                if (aiMesh.HasTextureCoords(1))
                {
                    if (mesh.UVChannel2 == null)
                    {
                        mesh.UVChannel2 = new Vector2[vertexCount];
                    }

                    for (int i = 0; i < aiMesh.TextureCoordinateChannels[1].Count; i++)
                    {
                        mesh.UVChannel2[vertexOffset + i] = new Vector2(aiMesh.TextureCoordinateChannels[1][i].X,
                                                                        1f - aiMesh.TextureCoordinateChannels[1][i].Y);
                    }
                }

                if (aiMesh.HasVertexColors(0))
                {
                    if (mesh.Colors == null)
                    {
                        mesh.Colors = new Color[vertexCount];
                        for (int i = 0; i < mesh.Colors.Length; i++)
                        {
                            mesh.Colors[i] = Color.White;
                        }
                    }

                    for (int i = 0; i < aiMesh.VertexColorChannels[0].Count; i++)
                    {
                        mesh.Colors[vertexOffset + i] = new Color(aiMesh.VertexColorChannels[0][i].R,
                                                                  aiMesh.VertexColorChannels[0][i].G, aiMesh.VertexColorChannels[0][i].B,
                                                                  aiMesh.VertexColorChannels[0][i].A);
                    }
                }

                var subMesh = new SubMesh();

                if (aiMesh.HasBones)
                {
                    if (mesh.BoneWeights == null)
                    {
                        mesh.BoneWeights = new BoneWeight[vertexCount];
                        for (int i = 0; i < mesh.BoneWeights.Length; i++)
                        {
                            mesh.BoneWeights[i] = BoneWeight.Empty;
                        }
                    }

                    subMesh.BoneIndices = new ushort[aiMesh.Bones.Count];
                    for (int i = 0; i < aiMesh.Bones.Count; i++)
                    {
                        var aiBone = aiMesh.Bones[i];

                        if (!boneMap.TryGetValue(aiBone.Name, out int boneIndex))
                        {
                            boneIndex            = bones.Count;
                            boneMap[aiBone.Name] = boneIndex;
                            bones.Add(ConvertBoneFromAiBone(aiBone, aiScene, boneIndex));
                        }

                        subMesh.BoneIndices[i] = ( ushort )boneIndex;

                        foreach (var aiWeight in aiBone.VertexWeights)
                        {
                            mesh.BoneWeights[vertexOffset + aiWeight.VertexID].AddWeight(i, aiWeight.Weight);
                        }
                    }
                }

                subMesh.Indices = aiMesh.Faces.Where(x => x.IndexCount == 3).SelectMany(x => x.Indices)
                                  .Select(x => ( ushort )(vertexOffset + x)).ToArray();

                ushort[] triangleStrip = Stripifier.Stripify(subMesh.Indices);
                if (triangleStrip != null)
                {
                    subMesh.PrimitiveType = PrimitiveType.TriangleStrip;
                    subMesh.Indices       = triangleStrip;
                }

                var aiMaterial = aiScene.Materials[aiMesh.MaterialIndex];
                if (!materialMap.TryGetValue(aiMaterial.Name, out int materialIndex))
                {
                    materialIndex = materials.Count;
                    materialMap[aiMaterial.Name] = materialIndex;
                    materials.Add(ConvertMaterialFromAiMaterial(aiMaterial, texturesDirectory, textureSet));
                }

                subMesh.MaterialIndex = materialIndex;

                var axisAlignedBoundingBox =
                    new AxisAlignedBoundingBox(mesh.Vertices.Skip(vertexOffset).Take(aiMesh.Vertices.Count));

                subMesh.BoundingSphere = axisAlignedBoundingBox.ToBoundingSphere();
                subMesh.BoundingBox    = axisAlignedBoundingBox.ToBoundingBox();

                mesh.SubMeshes.Add(subMesh);

                vertexOffset += aiMesh.VertexCount;
            }

            mesh.BoundingSphere = new AxisAlignedBoundingBox(mesh.Vertices).ToBoundingSphere();

            return(mesh);
        }
        protected override void Initialize()
        {
            RegisterExportHandler <Model>(filePath =>
            {
                var configuration = ConfigurationList.Instance.CurrentConfiguration;

                var objectDatabase  = configuration?.ObjectDatabase;
                var textureDatabase = configuration?.TextureDatabase;
                var boneDatabase    = configuration?.BoneDatabase;

                Data.Save(filePath, objectDatabase, textureDatabase, boneDatabase);
            });
            RegisterExportHandler <Scene>(filePath => Exporter.ConvertAiSceneFromModel(Data, filePath));
            RegisterReplaceHandler <Model>(filePath =>
            {
                var configuration = ConfigurationList.Instance.CurrentConfiguration;

                var objectDatabase  = configuration?.ObjectDatabase;
                var textureDatabase = configuration?.TextureDatabase;

                var model = new Model();
                model.Load(filePath, objectDatabase, textureDatabase);
                return(model);
            });
            RegisterReplaceHandler <Scene>(filePath =>
            {
                if (Data.Meshes.Count > 1)
                {
                    return(Importer.ConvertModelFromAiScene(filePath));
                }

                return(Importer.ConvertModelFromAiSceneWithSingleMesh(filePath));
            });
            RegisterCustomHandler("Rename all shaders to...", () =>
            {
                using (var inputDialog = new InputDialog {
                    WindowTitle = "Rename all shaders", Input = "BLINN"
                })
                {
                    if (inputDialog.ShowDialog() != DialogResult.OK)
                    {
                        return;
                    }

                    foreach (var material in Data.Meshes.SelectMany(x => x.Materials))
                    {
                        material.Shader = inputDialog.Input;
                    }
                }

                IsDirty = true;
            });
            RegisterCustomHandler("Convert triangles to triangle strips", () =>
            {
                foreach (var indexTable in Data.Meshes.SelectMany(x => x.SubMeshes).SelectMany(x => x.IndexTables))
                {
                    if (indexTable.PrimitiveType == PrimitiveType.Triangles)
                    {
                        ushort[] triangleStrip = Stripifier.Stripify(indexTable.Indices);
                        if (triangleStrip != null)
                        {
                            indexTable.PrimitiveType = PrimitiveType.TriangleStrip;
                            indexTable.Indices       = triangleStrip;
                        }
                    }
                }

                IsDirty = true;
            });
            RegisterCustomHandler("Combine all meshes into one", () =>
            {
                if (Data.Meshes.Count <= 1)
                {
                    return;
                }

                var combinedMesh = new Mesh {
                    Name = "Combined mesh"
                };
                var indexMap = new Dictionary <int, int>();

                foreach (var mesh in Data.Meshes)
                {
                    if (mesh.Skin != null)
                    {
                        if (combinedMesh.Skin == null)
                        {
                            combinedMesh.Skin = new Skin();
                            combinedMesh.Skin.Bones.AddRange(mesh.Skin.Bones);
                        }
                        else
                        {
                            for (int i = 0; i < mesh.Skin.Bones.Count; i++)
                            {
                                var bone      = mesh.Skin.Bones[i];
                                var boneIndex = combinedMesh.Skin.Bones
                                                .FindIndex(x => x.Name.Equals(bone.Name, StringComparison.OrdinalIgnoreCase));

                                if (boneIndex == -1)
                                {
                                    indexMap[i] = combinedMesh.Skin.Bones.Count;
                                    combinedMesh.Skin.Bones.Add(bone);
                                }
                                else
                                {
                                    indexMap[i] = boneIndex;
                                }
                            }

                            foreach (var indexTable in mesh.SubMeshes.SelectMany(x => x.IndexTables))
                            {
                                if (indexTable.BoneIndices?.Length >= 1)
                                {
                                    for (int i = 0; i < indexTable.BoneIndices.Length; i++)
                                    {
                                        indexTable.BoneIndices[i] = ( ushort )indexMap[indexTable.BoneIndices[i]];
                                    }
                                }
                            }
                        }
                    }

                    foreach (var indexTable in mesh.SubMeshes.SelectMany(x => x.IndexTables))
                    {
                        indexTable.MaterialIndex += combinedMesh.Materials.Count;
                    }

                    combinedMesh.SubMeshes.AddRange(mesh.SubMeshes);
                    combinedMesh.Materials.AddRange(mesh.Materials);
                }

                Data.Meshes.Clear();
                Data.Meshes.Add(combinedMesh);

                if (IsPopulated)
                {
                    IsPopulated = false;
                    Populate();
                }

                IsDirty = true;
            });

            base.Initialize();
        }
Exemple #5
0
        /// <summary>
        /// Builds a strip forward as far as we can go, then builds backwards, and joins the two lists.
        /// </summary>
        public void Build(List <EdgeInfo> edgeInfos, List <FaceInfo> faceInfos)
        {
            // used in building the strips forward and backward
            var scratchIndices = new List <ushort>();

            // build forward... start with the initial face
            var forwardFaces  = new List <FaceInfo>();
            var backwardFaces = new List <FaceInfo>();

            forwardFaces.Add(StartInfo.StartFace);

            MarkTriangle(StartInfo.StartFace);

            int v0 = (StartInfo.ToV1 ? StartInfo.StartEdge.V0 : StartInfo.StartEdge.V1);
            int v1 = (StartInfo.ToV1 ? StartInfo.StartEdge.V1 : StartInfo.StartEdge.V0);

            // easiest way to get v2 is to use this function which requires the
            // other indices to already be in the list.
            scratchIndices.Add(( ushort )v0);
            scratchIndices.Add(( ushort )v1);
            int v2 = Stripifier.GetNextIndex(scratchIndices, StartInfo.StartFace);

            scratchIndices.Add(( ushort )v2);

            //
            // build the forward list
            //
            int nv0 = v1;
            int nv1 = v2;

            var nextFace = Stripifier.FindOtherFace(edgeInfos, nv0, nv1, StartInfo.StartFace);

            while (nextFace != null && !IsMarked(nextFace))
            {
                //check to see if this next face is going to cause us to die soon
                int testnv0 = nv1;
                int testnv1 = Stripifier.GetNextIndex(scratchIndices, nextFace);

                var nextNextFace = Stripifier.FindOtherFace(edgeInfos, testnv0, testnv1, nextFace);

                if ((nextNextFace == null) || (IsMarked(nextNextFace)))
                {
                    //uh, oh, we're following a dead end, try swapping
                    var testNextFace = Stripifier.FindOtherFace(edgeInfos, nv0, testnv1, nextFace);

                    if (((testNextFace != null) && !IsMarked(testNextFace)))
                    {
                        //we only swap if it buys us something

                        //add a "fake" degenerate face
                        var tempFace = new FaceInfo(nv0, nv1, nv0, true);

                        forwardFaces.Add(tempFace);
                        MarkTriangle(tempFace);

                        scratchIndices.Add(( ushort )nv0);
                        testnv0 = nv0;

                        ++DegenerateCount;
                    }
                }

                // add this to the strip
                forwardFaces.Add(nextFace);

                MarkTriangle(nextFace);

                // add the index
                //nv0 = nv1;
                //nv1 = Stripifier.GetNextIndex(scratchIndices, nextFace);
                scratchIndices.Add(( ushort )testnv1);

                // and get the next face
                nv0 = testnv0;
                nv1 = testnv1;

                nextFace = Stripifier.FindOtherFace(edgeInfos, nv0, nv1, nextFace);
            }

            // tempAllFaces is going to be forwardFaces + backwardFaces
            // it's used for Unique()
            var tempAllFaces = new List <FaceInfo>();

            for (var i = 0; i < forwardFaces.Count; i++)
            {
                tempAllFaces.Add(forwardFaces[i]);
            }

            //
            // reset the indices for building the strip backwards and do so
            //
            scratchIndices.Clear();
            scratchIndices.Add(( ushort )v2);
            scratchIndices.Add(( ushort )v1);
            scratchIndices.Add(( ushort )v0);
            nv0      = v1;
            nv1      = v0;
            nextFace = Stripifier.FindOtherFace(edgeInfos, nv0, nv1, StartInfo.StartFace);
            while (nextFace != null && !IsMarked(nextFace))
            {
                //this tests to see if a face is "unique", meaning that its vertices aren't already in the list
                // so, strips which "wrap-around" are not allowed
                if (!Unique(tempAllFaces, nextFace))
                {
                    break;
                }

                //check to see if this next face is going to cause us to die soon
                int testnv0 = nv1;
                int testnv1 = Stripifier.GetNextIndex(scratchIndices, nextFace);

                var nextNextFace = Stripifier.FindOtherFace(edgeInfos, testnv0, testnv1, nextFace);

                if ((nextNextFace == null) || (IsMarked(nextNextFace)))
                {
                    //uh, oh, we're following a dead end, try swapping
                    var testNextFace = Stripifier.FindOtherFace(edgeInfos, nv0, testnv1, nextFace);
                    if (((testNextFace != null) && !IsMarked(testNextFace)))
                    {
                        //we only swap if it buys us something

                        //add a "fake" degenerate face
                        var tempFace = new FaceInfo(nv0, nv1, nv0, true);

                        backwardFaces.Add(tempFace);
                        MarkTriangle(tempFace);
                        scratchIndices.Add(( ushort )nv0);
                        testnv0 = nv0;

                        ++DegenerateCount;
                    }
                }

                // add this to the strip
                backwardFaces.Add(nextFace);

                //this is just so Unique() will work
                tempAllFaces.Add(nextFace);

                MarkTriangle(nextFace);

                // add the index
                //nv0 = nv1;
                //nv1 = Stripifier.GetNextIndex(scratchIndices, nextFace);
                scratchIndices.Add(( ushort )testnv1);

                // and get the next face
                nv0      = testnv0;
                nv1      = testnv1;
                nextFace = Stripifier.FindOtherFace(edgeInfos, nv0, nv1, nextFace);
            }

            // Combine the forward and backwards stripification lists and put into our own face vector
            Combine(forwardFaces, backwardFaces);
        }
Exemple #6
0
        /// <summary>
        /// Generates strips from the given input indices.
        /// </summary>
        /// <param name="indices">Input index list, the indices you would use to render.</param>
        /// <param name="primitiveGroups">Array of optimized/stripified PrimitiveGroups</param>
        /// <param name="validateEnabled">Whether to validate the output or not.</param>
        /// <returns>A boolean indicating whether the operation completed successfully.</returns>
        public bool GenerateStrips(ushort[] indices, out PrimitiveGroup[] primitiveGroups, bool validateEnabled = false)
        {
            var numGroups = 0;

            //put data in format that the stripifier likes
            var    tempIndices = new List <ushort>(indices.Length);
            ushort maxIndex    = 0;
            ushort minIndex    = 0xFFFF;

            for (var i = 0; i < indices.Length; i++)
            {
                tempIndices.Add(indices[i]);
                if (indices[i] > maxIndex)
                {
                    maxIndex = indices[i];
                }
                if (indices[i] < minIndex)
                {
                    minIndex = indices[i];
                }
            }

            var tempStrips = new List <StripInfo>();
            var tempFaces  = new List <FaceInfo>();

            var stripifier = new Stripifier();

            //do actual stripification
            stripifier.Stripify(tempIndices, CacheSize, MinStripSize, maxIndex, tempStrips, tempFaces);

            //stitch strips together
            var  stripIndices      = new List <int>();
            uint numSeparateStrips = 0;

            if (ListsOnly)
            {
                //if we're outputting only lists, we're done
                numGroups       = 1;
                primitiveGroups = new PrimitiveGroup[numGroups];
                var primGroupArray = primitiveGroups;

                //count the total number of indices
                uint numIndices = 0;
                for (var i = 0; i < tempStrips.Count; i++)
                {
                    numIndices += ( uint )(tempStrips[i].Faces.Count * 3);
                }

                //add in the list
                numIndices       += ( uint )(tempFaces.Count * 3);
                primGroupArray[0] = new PrimitiveGroup(PrimitiveType.TriangleList, new ushort[numIndices]);

                //do strips
                uint indexCtr = 0;
                for (var i = 0; i < tempStrips.Count; i++)
                {
                    for (var j = 0; j < tempStrips[i].Faces.Count; j++)
                    {
                        //degenerates are of no use with lists
                        if (!Stripifier.IsDegenerate(tempStrips[i].Faces[j]))
                        {
                            primGroupArray[0].Indices[indexCtr++] = ( ushort )tempStrips[i].Faces[j].V0;
                            primGroupArray[0].Indices[indexCtr++] = ( ushort )tempStrips[i].Faces[j].V1;
                            primGroupArray[0].Indices[indexCtr++] = ( ushort )tempStrips[i].Faces[j].V2;
                        }
                        else
                        {
                            //we've removed a tri, reduce the number of indices
                            var resizedIndices = primGroupArray[0].Indices;
                            Array.Resize(ref resizedIndices, primGroupArray[0].Indices.Length - 3);
                            primGroupArray[0].Indices = resizedIndices;
                        }
                    }
                }

                //do lists
                for (var i = 0; i < tempFaces.Count; i++)
                {
                    primGroupArray[0].Indices[indexCtr++] = ( ushort )tempFaces[i].V0;
                    primGroupArray[0].Indices[indexCtr++] = ( ushort )tempFaces[i].V1;
                    primGroupArray[0].Indices[indexCtr++] = ( ushort )tempFaces[i].V2;
                }
            }
            else
            {
                stripifier.CreateStrips(tempStrips, stripIndices, StitchStrips, ref numSeparateStrips, UseRestart, ( uint )RestartValue);

                //if we're stitching strips together, we better get back only one strip from CreateStrips()
                Debug.Assert(StitchStrips && numSeparateStrips == 1 || !StitchStrips);

                //convert to output format
                numGroups = ( ushort )numSeparateStrips; //for the strips
                if (tempFaces.Count != 0)
                {
                    numGroups++; //we've got a list as well, increment
                }
                primitiveGroups = new PrimitiveGroup[numGroups];

                var primGroupArray = primitiveGroups;

                //first, the strips
                var startingLoc = 0;
                for (var stripCtr = 0; stripCtr < numSeparateStrips; stripCtr++)
                {
                    var stripLength = 0;

                    if (!StitchStrips)
                    {
                        //if we've got multiple strips, we need to figure out the correct length
                        int i;
                        for (i = startingLoc; i < stripIndices.Count; i++)
                        {
                            if (stripIndices[i] == -1)
                            {
                                break;
                            }
                        }

                        stripLength = i - startingLoc;
                    }
                    else
                    {
                        stripLength = stripIndices.Count;
                    }

                    primGroupArray[stripCtr] = new PrimitiveGroup(PrimitiveType.TriangleStrip, new ushort[stripLength]);

                    var indexCtr = 0;
                    for (int i = startingLoc; i < stripLength + startingLoc; i++)
                    {
                        primGroupArray[stripCtr].Indices[indexCtr++] = ( ushort )stripIndices[i];
                    }

                    //we add 1 to account for the -1 separating strips
                    //this doesn't break the stitched case since we'll exit the loop
                    startingLoc += stripLength + 1;
                }

                //next, the list
                if (tempFaces.Count != 0)
                {
                    int faceGroupLoc = numGroups - 1; //the face group is the last one
                    primGroupArray[faceGroupLoc] = new PrimitiveGroup(PrimitiveType.TriangleList, new ushort[tempFaces.Count * 3]);
                    var indexCtr = 0;
                    for (var i = 0; i < tempFaces.Count; i++)
                    {
                        primGroupArray[faceGroupLoc].Indices[indexCtr++] = ( ushort )tempFaces[i].V0;
                        primGroupArray[faceGroupLoc].Indices[indexCtr++] = ( ushort )tempFaces[i].V1;
                        primGroupArray[faceGroupLoc].Indices[indexCtr++] = ( ushort )tempFaces[i].V2;
                    }
                }
            }

            //validate generated data against input
            if (validateEnabled)
            {
                var numbins = 100;

                var inBins = new List <FaceInfo> [numbins];
                for (var i = 0; i < inBins.Length; ++i)
                {
                    inBins[i] = new List <FaceInfo>();
                }

                //hash input indices on first index
                for (var i = 0; i < indices.Length; i += 3)
                {
                    var faceInfo = new FaceInfo(indices[i], indices[i + 1], indices[i + 2]);
                    inBins[indices[i] % numbins].Add(faceInfo);
                }

                for (var i = 0; i < numGroups; ++i)
                {
                    switch (primitiveGroups[i].Type)
                    {
                    case PrimitiveType.TriangleList:
                    {
                        for (var j = 0; j < primitiveGroups[i].Indices.Length; j += 3)
                        {
                            ushort v0 = primitiveGroups[i].Indices[j];
                            ushort v1 = primitiveGroups[i].Indices[j + 1];
                            ushort v2 = primitiveGroups[i].Indices[j + 2];

                            //ignore degenerates
                            if (Stripifier.IsDegenerate(v0, v1, v2))
                            {
                                continue;
                            }

                            if (!TestTriangle(v0, v1, v2, inBins, numbins))
                            {
                                Cleanup(tempStrips, tempFaces);
                                return(false);
                            }
                        }

                        break;
                    }

                    case PrimitiveType.TriangleStrip:
                    {
                        var flip = false;
                        for (var j = 2; j < primitiveGroups[i].Indices.Length; ++j)
                        {
                            ushort v0 = primitiveGroups[i].Indices[j - 2];
                            ushort v1 = primitiveGroups[i].Indices[j - 1];
                            ushort v2 = primitiveGroups[i].Indices[j];

                            if (flip)
                            {
                                //swap v1 and v2
                                ushort swap = v1;
                                v1 = v2;
                                v2 = swap;
                            }

                            //ignore degenerates
                            if (Stripifier.IsDegenerate(v0, v1, v2))
                            {
                                flip = !flip;
                                continue;
                            }

                            if (!TestTriangle(v0, v1, v2, inBins, numbins))
                            {
                                Cleanup(tempStrips, tempFaces);
                                return(false);
                            }

                            flip = !flip;
                        }

                        break;
                    }

                    case PrimitiveType.TriangleFan:
                    default:
                        break;
                    }
                }
            }

            //clean up everything
            Cleanup(tempStrips, tempFaces);

            return(true);
        }