Пример #1
0
        /// <summary>
        /// Gets the import settings for the current settings.
        /// </summary>
        /// <returns>The import settings.</returns>
        public static HairImportSettings GetImportSettings()
        {
            HairImportSettings importSettings = new HairImportSettings();

            importSettings.scale = new TressFXLib.Numerics.Vector3(importScale.x, importScale.y, importScale.z);
            return(importSettings);
        }
Пример #2
0
        public override HairMesh[] Import(BinaryReader reader, string path, Hair hair, HairImportSettings importSettings)
        {
            HairMesh[] meshes = base.Import(reader, path, hair, importSettings);
            int id = 0;

            foreach (HairMesh mesh in meshes)
                if (mesh != null)
                    foreach (HairStrand strand in mesh.strands)
                        foreach (HairStrandVertex vertex in strand.vertices)
                        {
                            vertex.texcoord = ReadVector4(reader);
                            id++;
                        }

            return meshes;
        }
Пример #3
0
        public HairMesh[] Import(BinaryReader reader, string path, Hair hair, HairImportSettings importSettings)
        {
            HairMesh[] meshes = base.Import(reader, path, hair, importSettings);
            int        id     = 0;

            foreach (HairMesh mesh in meshes)
            {
                if (mesh != null)
                {
                    foreach (HairStrand strand in mesh.strands)
                    {
                        foreach (HairStrandVertex vertex in strand.vertices)
                        {
                            vertex.texcoord = ReadVector4(reader);
                            id++;
                        }
                    }
                }
            }

            return(meshes);
        }
Пример #4
0
        public HairMesh[] Import(BinaryReader reader, string path, Hair hair, HairImportSettings importSettings)
        {
            reader.Close();
            // Initialize the import
            // Load the ase file content
            // Create a List for all meshes
            // Create a list for the current-mesh strands
            string[]          aseContent     = File.ReadAllLines(path);
            List <HairMesh>   hairMeshes     = new List <HairMesh>();
            List <HairStrand> currentStrands = new List <HairStrand>();

            // Init "state"-variables
            int   currentStrand      = 0;
            int   currentHairId      = -1;
            float texcoordMultiplier = 0;

            // Now the hard part begins...
            for (int i = 0; i < aseContent.Length; i++)
            {
                string[] tokens = aseContent[i].Split('\t');

                if (aseContent[i].Contains("*SHAPE_LINECOUNT"))
                {
                    tokens = tokens[1].Split(' ');
                }
                else if (aseContent[i].Contains("SHAPE_LINE"))
                {
                    tokens = tokens[1].Split(' ');
                }

                if (tokens.Length >= 2)
                {
                    if (tokens[0] == "*SHAPE_LINECOUNT")
                    {
                        if (currentStrand > 0)
                        {
                            // Start parsing next mesh after flushing the current strands buffer
                            currentHairId++;
                            currentStrand = 0;

                            // Add to mesh list / flush current strands buffer
                            HairMesh hairMesh = new HairMesh();
                            foreach (HairStrand strand in currentStrands)
                            {
                                hairMesh.strands.Add(strand);
                            }
                            hairMeshes.Add(hairMesh);

                            // Clear current strands
                            currentStrands.Clear();
                            texcoordMultiplier = 1.0f / (float)int.Parse(tokens[1]);
                        }
                    }
                    else if (tokens[0] == "*SHAPE_LINE")
                    {
                        HairStrand strand = new HairStrand();
                        strand.isGuidanceStrand = true;

                        string[] vertexCountTokens = aseContent[i + 1].Split(' ');

                        // Parse the current line
                        int vertexCount = int.Parse(vertexCountTokens[1]);

                        // Parse vertices
                        for (int j = 0; j < vertexCount; j++)
                        {
                            string[] vertexTokens = aseContent[i + 2 + j].Replace('.', ',').Split('\t');

                            if (vertexTokens[2] == "*SHAPE_VERTEX_INTERP")
                            {
                                continue;
                            }

                            System.Globalization.NumberFormatInfo nf
                                = new System.Globalization.NumberFormatInfo()
                                {
                                NumberGroupSeparator = "."
                                };

                            vertexTokens[4] = vertexTokens[4].Replace(',', '.');
                            vertexTokens[5] = vertexTokens[5].Replace(',', '.');
                            vertexTokens[6] = vertexTokens[6].Replace(',', '.');
                            Vector3 position = new Vector3(float.Parse(vertexTokens[4], nf), float.Parse(vertexTokens[6], nf), float.Parse(vertexTokens[5], nf));

                            position = Vector3.Multiply(position, importSettings.scale);
                            HairStrandVertex v = new HairStrandVertex(position, Vector3.Zero, Vector4.Zero);

                            if (strand.vertices.Count == 0)
                            {
                                v.isMovable = false;
                            }

                            strand.vertices.Add(v);
                        }
                        currentStrands.Add(strand);

                        // Increment file-line-pointer
                        i = i + 1 + vertexCount;

                        currentStrand++;
                    }
                }
            }

            // Get last mesh
            HairMesh lastMesh = new HairMesh();

            lastMesh.strands.AddRange(currentStrands);
            hairMeshes.Add(lastMesh);

            return(hairMeshes.ToArray());
        }
Пример #5
0
        public HairMesh[] Import(BinaryReader reader, string path, Hair hair, HairImportSettings importSettings)
        {
            reader.Close();
            // Initialize the import
            // Load the ase file content
            // Create a List for all meshes
            // Create a list for the current-mesh strands
            string[] aseContent = File.ReadAllLines(path);
            List<HairMesh> hairMeshes = new List<HairMesh>();
            List<HairStrand> currentStrands = new List<HairStrand>();

            // Init "state"-variables
            int currentStrand = 0;
            int currentHairId = -1;
            float texcoordMultiplier = 0;

            // Now the hard part begins...
            for (int i = 0; i < aseContent.Length; i++)
            {
                string[] tokens = aseContent[i].Split('\t');

                if (aseContent[i].Contains("*SHAPE_LINECOUNT"))
                {
                    tokens = tokens[1].Split(' ');
                }
                else if (aseContent[i].Contains("SHAPE_LINE"))
                {
                    tokens = tokens[1].Split(' ');
                }

                if (tokens.Length >= 2)
                {
                    if (tokens[0] == "*SHAPE_LINECOUNT")
                    {
                        if (currentStrand > 0)
                        {
                            // Start parsing next mesh after flushing the current strands buffer
                            currentHairId++;
                            currentStrand = 0;

                            // Add to mesh list / flush current strands buffer
                            HairMesh hairMesh = new HairMesh();
                            foreach (HairStrand strand in currentStrands)
                            {
                                hairMesh.strands.Add(strand);
                            }
                            hairMeshes.Add(hairMesh);

                            // Clear current strands
                            currentStrands.Clear();
                            texcoordMultiplier = 1.0f / (float)int.Parse(tokens[1]);
                        }
                    }
                    else if (tokens[0] == "*SHAPE_LINE")
                    {
                        HairStrand strand = new HairStrand();
                        strand.isGuidanceStrand = true;

                        string[] vertexCountTokens = aseContent[i + 1].Split(' ');

                        // Parse the current line
                        int vertexCount = int.Parse(vertexCountTokens[1]);

                        // Parse vertices
                        for (int j = 0; j < vertexCount; j++)
                        {
                            string[] vertexTokens = aseContent[i + 2 + j].Replace('.', ',').Split('\t');

                            if (vertexTokens[2] == "*SHAPE_VERTEX_INTERP")
                                continue;

                            System.Globalization.NumberFormatInfo nf
                            = new System.Globalization.NumberFormatInfo()
                            {
                                NumberGroupSeparator = "."
                            };

                            vertexTokens[4] = vertexTokens[4].Replace(',', '.');
                            vertexTokens[5] = vertexTokens[5].Replace(',', '.');
                            vertexTokens[6] = vertexTokens[6].Replace(',', '.');
                            Vector3 position = new Vector3(float.Parse(vertexTokens[4], nf), float.Parse(vertexTokens[6], nf), float.Parse(vertexTokens[5], nf));

                            position = Vector3.Multiply(position, importSettings.scale);
                            HairStrandVertex v = new HairStrandVertex(position, Vector3.Zero, Vector4.Zero);

                            if (strand.vertices.Count == 0)
                                v.isMovable = false;

                            strand.vertices.Add(v);
                        }
                        currentStrands.Add(strand);

                        // Increment file-line-pointer
                        i = i + 1 + vertexCount;

                        currentStrand++;
                    }
                }
            }

            // Get last mesh
            HairMesh lastMesh = new HairMesh();
            lastMesh.strands.AddRange(currentStrands);
            hairMeshes.Add(lastMesh);

            return hairMeshes.ToArray();
        }
Пример #6
0
        public HairMesh[] Import(BinaryReader reader, string path, Hair hair, HairImportSettings importSettings)
        {
            reader.Close();
            // Initialize the importer
            string[] objContent = File.ReadAllLines(path);
            List<Vector3> vertices = new List<Vector3>();
            List<Line> indices = new List<Line>();
            List<HairStrand> currentStrands = new List<HairStrand>();
            
            // Parse all vertices and indices
            for (int i = 0; i < objContent.Length; i++)
            {
                // Get all parts
                List<string> partsList = new List<string>(objContent[i].Split(' '));
                partsList.RemoveAll(x => string.IsNullOrEmpty(x.Replace(" ", "")));
                string[] parts = partsList.ToArray();

                switch (parts[0])
                {
                    case "v":
                        {
                            vertices.Add(new Vector3(SafeParse(parts[1]), SafeParse(parts[2]), SafeParse(parts[3])));
                        }
                        break;
                    case "l":
                        {
                            indices.Add(new Line(int.Parse(parts[1])-1, int.Parse(parts[2])-1));
                        }
                        break;
                }
            }

            // Parse all strands
            List<int> alreadyLoadedIndices = new List<int>(640000);
            List<HairStrandVertex> currentStrandVertices = new List<HairStrandVertex>(64);
            for (int i = 0; i < indices.Count; i++)
            {
                // In order to detect when a new hair starts, we detect if the first index in a line was not added to the mesh already.
                // If it was NOT, we are in a new strand
                if (i != 0 && !alreadyLoadedIndices.Contains(indices[i].index1))
                {
                    // We are in a new hair strand
                    HairStrand hs = new HairStrand();
                    currentStrandVertices[0].isMovable = false;
                    currentStrandVertices.ForEach((sv) =>
                    {
                        hs.vertices.Add(sv);
                    });

                    hs.isGuidanceStrand = true;
                    currentStrands.Add(hs);

                    // Cleanup
                    currentStrandVertices.Clear();
                }

                // Add
                if (!alreadyLoadedIndices.Contains(indices[i].index1))
                {
                    currentStrandVertices.Add(new HairStrandVertex(vertices[indices[i].index1], Vector3.Zero, Vector4.Zero));
                    alreadyLoadedIndices.Add(indices[i].index1);
                }
                if (!alreadyLoadedIndices.Contains(indices[i].index2))
                {
                    currentStrandVertices.Add(new HairStrandVertex(vertices[indices[i].index2], Vector3.Zero, Vector4.Zero));
                    alreadyLoadedIndices.Add(indices[i].index2);
                }
            }

            HairMesh hm = new HairMesh();
            currentStrands.ForEach((item) =>
            {
                hm.strands.Add(item);
            });

            return new HairMesh[] { hm };
        }
Пример #7
0
        public virtual HairMesh[] Import(BinaryReader reader, string path, Hair hair, HairImportSettings importSettings)
        {
            // TODO: Implement import settings
            HairMesh[] returnData = null;

            // Load header information
            int numVertices = reader.ReadInt32();
            int numStrands = reader.ReadInt32();

            // maxVerticesPerStrand is what amd calls it
            // Actually this should be vertices per strand, as tressfx needs a uniform vertex count on every strand
            // We will assume that the input file is has a uniform vertex count.
            // If it doesnt, the import will fail.
            int maxVerticesPerStrand = reader.ReadInt32();
            int numGuideHairVertices = reader.ReadInt32();
            int numGuideHairStrands = reader.ReadInt32();
            /*int numFollowHairsPerOneGuideHair = */reader.ReadInt32();

            // Load hair data information
            int[] strandTypes = ReadIntegerArray(reader, numStrands);
            Vector4[] referenceVectors = ReadVector4Array(reader, numVertices);
            Quaternion[] globalRotations = ReadQuaternionArray(reader, numVertices);
            Quaternion[] localRotations = ReadQuaternionArray(reader, numVertices);
            Vector4[] vertices = ReadVector4Array(reader, numVertices);
            Vector4[] tangents = ReadVector4Array(reader, numVertices);

            // Read the triangle vertices
            // Actually those are __NEVER__ used anywhere.
            // So, we are going to skip / ignore them
            ReadVector3Array(reader, numVertices);
            ReadVector3Array(reader, numVertices);
            ReadVector4Array(reader, numVertices);

            float[] thicknessCoefficients = ReadFloatArray(reader, numVertices);
            Vector4[] followRootOffsets = ReadVector4Array(reader, numStrands);
            float[] restLengths = ReadFloatArray(reader, numVertices);

            // Get bounding sphere
            hair.InitializeBoundingSphere(ReadVector3(reader), reader.ReadSingle());

            // Load indices
            int indexTmp = reader.ReadInt32();
            int[] triangleIndices = ReadIntegerArray(reader, indexTmp);
            indexTmp = reader.ReadInt32();
            int[] lineIndices = ReadIntegerArray(reader, indexTmp);

            // Init indices
            hair.InitializeIndices(lineIndices, triangleIndices);

            // Set simulation data
            HairSimulationData simulationData = new HairSimulationData();
            simulationData.followRootOffsets = followRootOffsets.ToList();
            simulationData.globalRotations = globalRotations.ToList();
            simulationData.guideHairStrandCount = numGuideHairStrands;
            simulationData.guideHairVertexCount = numGuideHairVertices;
            simulationData.maxNumVerticesPerStrand = maxVerticesPerStrand;
            simulationData.localRotations = localRotations.ToList();
            simulationData.referenceVectors = referenceVectors.ToList();
            simulationData.strandTypes = strandTypes.ToList();
            simulationData.tangents = tangents.ToList();
            simulationData.thicknessCoefficients = thicknessCoefficients.ToList();
            simulationData.vertices = vertices.ToList();
            simulationData.restLength = restLengths.ToList();

            hair.InitializeSimulationData(simulationData);

            // So, the data is read now.
            // Next, we are going to construct the hair meshes.
            HairMesh[] hairMeshes = new HairMesh[4];

            // Current list "pointer"
            int vertexPointer = 0;
            int strandPointer = 0;

            List<HairStrand> strandReferences = new List<HairStrand>();

            foreach (int meshIndex in strandTypes)
            {
                // Check if we got a valid strand types array
                if (meshIndex < 0 || meshIndex > 3)
                    throw new FormatException("Mesh ids (strand types) < 0 or > 3 are not supported by TressFX!");

                // Get mesh and create it if it doesnt exist
                if (hairMeshes[meshIndex] == null)
                    hairMeshes[meshIndex] = new HairMesh();
                HairMesh mesh = hairMeshes[meshIndex];

                // Create new strand
                HairStrand strand = new HairStrand();
                strandReferences.Add(strand);
                strand.followRootOffset = followRootOffsets[strandPointer];

                if (strand.followRootOffset.x != 0 || strand.followRootOffset.y != 0 || strand.followRootOffset.z != 0)
                {
                    // This is a follow hair
                    strand.guidanceStrand = strandReferences[(int)strand.followRootOffset.w];
                    strand.isGuidanceStrand = false;
                }
                else
                {
                    // This is a guidance hair
                    strand.isGuidanceStrand = true;
                }

                // Read all vertices
                for (int i = 0; i < maxVerticesPerStrand; i++)
                {
                    // Read vertex data
                    // As texcoord we will create a coordinate which projects the hair along the x-axis based on the strand index
                    // and the y-axis based on the vertex index inside of the current strand
                    HairStrandVertex vertex = new HairStrandVertex(new Vector3(vertices[vertexPointer].X, vertices[vertexPointer].Y, vertices[vertexPointer].Z),
                                                    new Vector3(tangents[vertexPointer].X, tangents[vertexPointer].Y, tangents[vertexPointer].Z),
                                                    new Vector4(strandPointer / (float)numStrands, i / (float)maxVerticesPerStrand, 0, 0));

                    // Set movable flag
                    vertex.isMovable = (vertices[vertexPointer].W > 0);

                    // Set simulation data
                    vertex.globalRotation = globalRotations[vertexPointer];
                    vertex.localRotation = localRotations[vertexPointer];
                    vertex.referenceVector = referenceVectors[vertexPointer];
                    vertex.restLength = restLengths[vertexPointer];
                    vertex.thicknessCoefficient = thicknessCoefficients[vertexPointer];

                    // Add to strand
                    strand.vertices.Add(vertex);

                    vertexPointer++;
                }

                // Add strand to mesh
                mesh.strands.Add(strand);

                strandPointer++;
            }

            // Construct return array
            List<HairMesh> meshes = new List<HairMesh>();

            for (int i = 0; i < hairMeshes.Length; i++)
                if (hairMeshes[i] == null)
                {
                    // We're done reading
                    break;
                }
                else
                {
                    // Read the mesh
                    meshes.Add(hairMeshes[i]);
                }

            returnData = hairMeshes.ToArray();

            return returnData;
        }
Пример #8
0
        public HairMesh[] Import(BinaryReader reader, string path, Hair hair, HairImportSettings importSettings)
        {
            reader.Close();
            // Initialize the importer
            string[]          objContent     = File.ReadAllLines(path);
            List <Vector3>    vertices       = new List <Vector3>();
            List <Line>       indices        = new List <Line>();
            List <HairStrand> currentStrands = new List <HairStrand>();

            // Parse all vertices and indices
            for (int i = 0; i < objContent.Length; i++)
            {
                // Get all parts
                List <string> partsList = new List <string>(objContent[i].Split(' '));
                partsList.RemoveAll(x => string.IsNullOrEmpty(x.Replace(" ", "")));
                string[] parts = partsList.ToArray();

                switch (parts[0])
                {
                case "v":
                {
                    vertices.Add(Vector3.Multiply(new Vector3(SafeParse(parts[1]), SafeParse(parts[2]), SafeParse(parts[3])), importSettings.scale));
                }
                break;

                case "l":
                {
                    indices.Add(new Line(int.Parse(parts[1]) - 1, int.Parse(parts[2]) - 1));
                }
                break;
                }
            }

            // Parse all strands
            List <int> alreadyLoadedIndices = new List <int>(640000);
            List <HairStrandVertex> currentStrandVertices = new List <HairStrandVertex>(64);

            for (int i = 0; i < indices.Count; i++)
            {
                // In order to detect when a new hair starts, we detect if the first index in a line was not added to the mesh already.
                // If it was NOT, we are in a new strand
                if (i != 0 && !alreadyLoadedIndices.Contains(indices[i].index1))
                {
                    // We are in a new hair strand
                    HairStrand hs = new HairStrand();
                    currentStrandVertices[0].isMovable = false;
                    currentStrandVertices.ForEach((sv) =>
                    {
                        hs.vertices.Add(sv);
                    });

                    hs.isGuidanceStrand = true;
                    currentStrands.Add(hs);

                    // Cleanup
                    currentStrandVertices.Clear();
                }

                // Add
                if (!alreadyLoadedIndices.Contains(indices[i].index1))
                {
                    currentStrandVertices.Add(new HairStrandVertex(vertices[indices[i].index1], Vector3.Zero, Vector4.Zero));
                    alreadyLoadedIndices.Add(indices[i].index1);
                }
                if (!alreadyLoadedIndices.Contains(indices[i].index2))
                {
                    currentStrandVertices.Add(new HairStrandVertex(vertices[indices[i].index2], Vector3.Zero, Vector4.Zero));
                    alreadyLoadedIndices.Add(indices[i].index2);
                }
            }

            // Shuffle strands
            currentStrands = FormatHelper.Shuffle(currentStrands);

            HairMesh hm = new HairMesh();

            currentStrands.ForEach((item) =>
            {
                hm.strands.Add(item);
            });

            return(new HairMesh[] { hm });
        }
Пример #9
0
        public virtual HairMesh[] Import(BinaryReader reader, string path, Hair hair, HairImportSettings importSettings)
        {
            // TODO: Implement import settings
            HairMesh[] returnData = null;

            // Load header information
            int numVertices = reader.ReadInt32();
            int numStrands  = reader.ReadInt32();

            // maxVerticesPerStrand is what amd calls it
            // Actually this should be vertices per strand, as tressfx needs a uniform vertex count on every strand
            // We will assume that the input file is has a uniform vertex count.
            // If it doesnt, the import will fail.
            int maxVerticesPerStrand = reader.ReadInt32();
            int numGuideHairVertices = reader.ReadInt32();
            int numGuideHairStrands  = reader.ReadInt32();

            /*int numFollowHairsPerOneGuideHair = */ reader.ReadInt32();

            // Load hair data information
            int[]        strandTypes      = ReadIntegerArray(reader, numStrands);
            Vector4[]    referenceVectors = ReadVector4Array(reader, numVertices);
            Quaternion[] globalRotations  = ReadQuaternionArray(reader, numVertices);
            Quaternion[] localRotations   = ReadQuaternionArray(reader, numVertices);
            Vector4[]    vertices         = ReadVector4Array(reader, numVertices);
            Vector4[]    tangents         = ReadVector4Array(reader, numVertices);

            // Read the triangle vertices
            // Actually those are __NEVER__ used anywhere.
            // So, we are going to skip / ignore them
            ReadVector3Array(reader, numVertices);
            ReadVector3Array(reader, numVertices);
            ReadVector4Array(reader, numVertices);

            float[]   thicknessCoefficients = ReadFloatArray(reader, numVertices);
            Vector4[] followRootOffsets     = ReadVector4Array(reader, numStrands);
            float[]   restLengths           = ReadFloatArray(reader, numVertices);

            // Get bounding sphere
            hair.InitializeBoundingSphere(ReadVector3(reader), reader.ReadSingle());

            // Load indices
            int indexTmp = reader.ReadInt32();

            int[] triangleIndices = ReadIntegerArray(reader, indexTmp);
            indexTmp = reader.ReadInt32();
            int[] lineIndices = ReadIntegerArray(reader, indexTmp);

            // Init indices
            hair.InitializeIndices(lineIndices, triangleIndices);

            // Set simulation data
            HairSimulationData simulationData = new HairSimulationData();

            simulationData.followRootOffsets       = followRootOffsets.ToList();
            simulationData.globalRotations         = globalRotations.ToList();
            simulationData.guideHairStrandCount    = numGuideHairStrands;
            simulationData.guideHairVertexCount    = numGuideHairVertices;
            simulationData.maxNumVerticesPerStrand = maxVerticesPerStrand;
            simulationData.localRotations          = localRotations.ToList();
            simulationData.referenceVectors        = referenceVectors.ToList();
            simulationData.strandTypes             = strandTypes.ToList();
            simulationData.tangents = tangents.ToList();
            simulationData.thicknessCoefficients = thicknessCoefficients.ToList();
            simulationData.vertices   = vertices.ToList();
            simulationData.restLength = restLengths.ToList();

            hair.InitializeSimulationData(simulationData);

            // So, the data is read now.
            // Next, we are going to construct the hair meshes.
            HairMesh[] hairMeshes = new HairMesh[4];

            // Current list "pointer"
            int vertexPointer = 0;
            int strandPointer = 0;

            List <HairStrand> strandReferences = new List <HairStrand>();

            foreach (int meshIndex in strandTypes)
            {
                // Check if we got a valid strand types array
                if (meshIndex < 0 || meshIndex > 3)
                {
                    throw new FormatException("Mesh ids (strand types) < 0 or > 3 are not supported by TressFX!");
                }

                // Get mesh and create it if it doesnt exist
                if (hairMeshes[meshIndex] == null)
                {
                    hairMeshes[meshIndex] = new HairMesh();
                }
                HairMesh mesh = hairMeshes[meshIndex];

                // Create new strand
                HairStrand strand = new HairStrand();
                strandReferences.Add(strand);
                strand.followRootOffset = followRootOffsets[strandPointer];


                if (strand.followRootOffset.x != 0 || strand.followRootOffset.y != 0 || strand.followRootOffset.z != 0)
                {
                    // This is a follow hair
                    strand.guidanceStrand   = strandReferences[(int)strand.followRootOffset.w];
                    strand.isGuidanceStrand = false;
                }
                else
                {
                    // This is a guidance hair
                    strand.isGuidanceStrand = true;
                }


                // Read all vertices
                for (int i = 0; i < maxVerticesPerStrand; i++)
                {
                    // Read vertex data
                    // As texcoord we will create a coordinate which projects the hair along the x-axis based on the strand index
                    // and the y-axis based on the vertex index inside of the current strand
                    HairStrandVertex vertex = new HairStrandVertex(new Vector3(vertices[vertexPointer].X, vertices[vertexPointer].Y, vertices[vertexPointer].Z),
                                                                   new Vector3(tangents[vertexPointer].X, tangents[vertexPointer].Y, tangents[vertexPointer].Z),
                                                                   new Vector4(strandPointer / (float)numStrands, i / (float)maxVerticesPerStrand, 0, 0));

                    // Set movable flag
                    vertex.isMovable = (vertices[vertexPointer].W > 0);

                    // Set simulation data
                    vertex.globalRotation       = globalRotations[vertexPointer];
                    vertex.localRotation        = localRotations[vertexPointer];
                    vertex.referenceVector      = referenceVectors[vertexPointer];
                    vertex.restLength           = restLengths[vertexPointer];
                    vertex.thicknessCoefficient = thicknessCoefficients[vertexPointer];

                    // Add to strand
                    strand.vertices.Add(vertex);

                    vertexPointer++;
                }

                // Add strand to mesh
                mesh.strands.Add(strand);

                strandPointer++;
            }

            // Construct return array
            List <HairMesh> meshes = new List <HairMesh>();

            for (int i = 0; i < hairMeshes.Length; i++)
            {
                if (hairMeshes[i] == null)
                {
                    // We're done reading
                    break;
                }
                else
                {
                    // Read the mesh
                    meshes.Add(hairMeshes[i]);
                }
            }

            returnData = hairMeshes.ToArray();

            return(returnData);
        }