/// <summary>
        /// Loads a mesh and creates the vertex/index buffers for the part
        /// </summary>
        /// <param name="part"></param>
        /// <param name="meshData"></param>
        void LoadMesh(ref Part part, IXDataObject dataObject)
        {
            // load vertex data
            int dataOffset = 0;
            Match vertexCount = findArrayCount.Match(dataObject.Body);
            if(!vertexCount.Success)
                throw new System.IO.InvalidDataException("problem reading vertex count");

            List<Vector4F> vertexList = new List<Vector4F>();
            int verticies = int.Parse(vertexCount.Groups[1].Value, CultureInfo.InvariantCulture);
            dataOffset = vertexCount.Index + vertexCount.Length;
            for(int vertexIndex = 0; vertexIndex < verticies; vertexIndex++)
            {
                Match vertex = findVector3F.Match(dataObject.Body, dataOffset);
                if(!vertex.Success)
                    throw new System.IO.InvalidDataException("problem reading vertex");
                else
                    dataOffset = vertex.Index + vertex.Length;

                vertexList.Add(
                    new Vector4F(
                        float.Parse(vertex.Groups[1].Value, CultureInfo.InvariantCulture),
                        float.Parse(vertex.Groups[2].Value, CultureInfo.InvariantCulture),
                        float.Parse(vertex.Groups[3].Value, CultureInfo.InvariantCulture),
                        1.0f));
            }

            // load triangle index data
            Match triangleIndexCount = findArrayCount.Match(dataObject.Body, dataOffset);
            dataOffset = triangleIndexCount.Index + triangleIndexCount.Length;
            if(!triangleIndexCount.Success)
                throw new System.IO.InvalidDataException("problem reading index count");

            List<Int32> triangleIndiciesList = new List<Int32>();
            int triangleIndexListCount = int.Parse(triangleIndexCount.Groups[1].Value, CultureInfo.InvariantCulture);
            dataOffset = triangleIndexCount.Index + triangleIndexCount.Length;
            for(int triangleIndicyIndex = 0; triangleIndicyIndex < triangleIndexListCount; triangleIndicyIndex++)
            {
                Match indexEntry = findVertexIndex.Match(dataObject.Body, dataOffset);
                if(!indexEntry.Success)
                    throw new System.IO.InvalidDataException("problem reading vertex index entry");
                else
                    dataOffset = indexEntry.Index + indexEntry.Length;

                int indexEntryCount = int.Parse(indexEntry.Groups[1].Value, CultureInfo.InvariantCulture);
                string[] vertexIndexes = indexEntry.Groups[2].Value.Split(new char[] { ',' });
                if(indexEntryCount != vertexIndexes.Length)
                    throw new System.IO.InvalidDataException("vertex index count does not equal count of indicies found");

                for(int entryIndex = 0; entryIndex <= indexEntryCount - 3; entryIndex++)
                {
                    triangleIndiciesList.Add(int.Parse(vertexIndexes[0], CultureInfo.InvariantCulture));
                    triangleIndiciesList.Add(int.Parse(vertexIndexes[1 + entryIndex].ToString(), CultureInfo.InvariantCulture));
                    triangleIndiciesList.Add(int.Parse(vertexIndexes[2 + entryIndex].ToString(), CultureInfo.InvariantCulture));
                }
            }

            // load mesh colors
            IXDataObject vertexColorData = GetSingleChild(dataObject, "MeshVertexColors");
            Dictionary<int, Vector4F> colorDictionary = null;
            if (vertexColorData != null)
                colorDictionary = LoadMeshColors(vertexColorData);

            // load mesh normals
            IXDataObject meshNormalData = GetSingleChild(dataObject, "MeshNormals");
            IndexedMeshNormals meshNormals = null;
            if(meshNormalData != null)
            {
                meshNormals = LoadMeshNormals(meshNormalData);
            }

            // load mesh texture coordinates
            IXDataObject meshTextureCoordsData = GetSingleChild(dataObject, "MeshTextureCoords");
            List<Vector2F> meshTextureCoords = null;
            if(meshTextureCoordsData != null)
            {
                meshTextureCoords = LoadMeshTextureCoordinates(meshTextureCoordsData);
            }

            // load mesh material
            IXDataObject meshMaterialsData = GetSingleChild(dataObject, "MeshMaterialList");
            List<MaterialSpecification> meshMaterials = null;
            if(meshMaterialsData != null)
            {
                meshMaterials = LoadMeshMaterialList(meshMaterialsData);
            }
            
            // copy vertex data to HGLOBAL
            int byteLength = Marshal.SizeOf(typeof(XMeshVertex)) * triangleIndiciesList.Count;
            IntPtr nativeVertex = Marshal.AllocHGlobal(byteLength);
            byte[] byteBuffer = new byte[byteLength];
            XMeshVertex[] varray = new XMeshVertex[triangleIndiciesList.Count];
            for(int n = 0; n < triangleIndiciesList.Count; n++)
            {
                XMeshVertex vertex = new XMeshVertex()
                {
                    Vertex = vertexList[triangleIndiciesList[n]],
                    Normal = (meshNormals == null) ? new Vector4F(0, 0, 0, 1.0f) : meshNormals.normalVectors[meshNormals.normalIndexMap[n]],
                    Color = ((colorDictionary == null) ? new Vector4F(0, 0, 0, 0) : colorDictionary[triangleIndiciesList[n]]),
                    Texture = ((meshTextureCoords == null) ? new Vector2F(0, 0) : meshTextureCoords[triangleIndiciesList[n]])
                };
                byte[] vertexData = RawSerialize(vertex);
                Buffer.BlockCopy(vertexData, 0, byteBuffer, vertexData.Length * n, vertexData.Length);
            }
            Marshal.Copy(byteBuffer, 0, nativeVertex, byteLength);

            // build vertex buffer
            BufferDescription bdv = new BufferDescription()
            {
                Usage = Usage.Default,
                ByteWidth = (uint)(Marshal.SizeOf(typeof(XMeshVertex)) * triangleIndiciesList.Count),
                BindingOptions = BindingOptions.VertexBuffer,
                CpuAccessOptions = CpuAccessOptions.None,
                MiscellaneousResourceOptions = MiscellaneousResourceOptions.None
            };
            SubresourceData vertexInit = new SubresourceData()
            {
                SystemMemory = nativeVertex
            };

            part.vertexBuffer = device.CreateBuffer(bdv, vertexInit);
            Debug.Assert(part.vertexBuffer != null);


            part.vertexCount = triangleIndiciesList.Count;

            if(meshMaterials != null)
            {
                // only a single material is currently supported
                MaterialSpecification m = meshMaterials[0];

                part.material = new Material()
                {
                    emissiveColor = m.emissiveColor,
                    specularColor = m.specularColor,
                    materialColor = m.materialColor,
                    specularPower = m.specularPower
                };
                
                string texturePath = "";
                if(File.Exists(m.textureFileName))
                    texturePath = m.textureFileName;
                if(File.Exists(meshDirectory + "\\" + m.textureFileName))
                    texturePath = meshDirectory + "\\" + m.textureFileName;
                if(File.Exists(meshDirectory + "\\..\\" + m.textureFileName))
                    texturePath = meshDirectory + "\\..\\" + m.textureFileName;

                if(texturePath.Length == 0)
                {
                    part.material.textureResource = null;
                }
                else
                {
                    part.material.textureResource =
                        D3D10XHelpers.CreateShaderResourceViewFromFile(
                            device,
                            texturePath);
                }
            }
            Marshal.FreeHGlobal(nativeVertex);
        }
        /// <summary>
        /// Loads the per vertex color for a mesh
        /// </summary>
        /// <param name="vertexColorData"></param>
        /// <returns></returns>
        Dictionary<int, Vector4F> LoadMeshColors(IXDataObject dataObject)
        {
            Regex findVertexColor = new Regex(@"([\d]+); ([\d]+\.[\d]+);([\d]+\.[\d]+);([\d]+\.[\d]+);([\d]+\.[\d]+);;");

            Match vertexCount = findArrayCount.Match(dataObject.Body);
            if(!vertexCount.Success)
                throw new System.IO.InvalidDataException("problem reading vertex colors count");

            Dictionary<int, Vector4F> colorDictionary = new Dictionary<int,Vector4F>();
            int verticies = int.Parse(vertexCount.Groups[1].Value, CultureInfo.InvariantCulture);
            int dataOffset = vertexCount.Index + vertexCount.Length;
            for(int vertexIndex = 0; vertexIndex < verticies; vertexIndex++)
            {
                Match vertexColor = findVertexColor.Match(dataObject.Body, dataOffset);
                if(!vertexColor.Success)
                    throw new System.IO.InvalidDataException("problem reading vertex colors");
                else
                    dataOffset = vertexColor.Index + vertexColor.Length;

                colorDictionary[int.Parse(vertexColor.Groups[1].Value, CultureInfo.InvariantCulture)] =
                    new Vector4F(
                        float.Parse(vertexColor.Groups[2].Value, CultureInfo.InvariantCulture),
                        float.Parse(vertexColor.Groups[3].Value, CultureInfo.InvariantCulture),
                        float.Parse(vertexColor.Groups[4].Value, CultureInfo.InvariantCulture),
                        float.Parse(vertexColor.Groups[5].Value, CultureInfo.InvariantCulture));
            }

            return colorDictionary;
        }
        /// <summary>
        /// Loads the texture coordinates(U,V) for a mesh
        /// </summary>
        /// <param name="textureCoordinateData"></param>
        /// <returns></returns>
        List<Vector2F> LoadMeshTextureCoordinates(IXDataObject dataObject)
        {
            Match coordinateCount = findArrayCount.Match(dataObject.Body);
            if(!coordinateCount.Success)
                throw new System.IO.InvalidDataException("problem reading mesh texture coordinates count");

            List<Vector2F> textureCoordinates = new List<Vector2F>();
            int coordinates = int.Parse(coordinateCount.Groups[1].Value, CultureInfo.InvariantCulture);
            int dataOffset = coordinateCount.Index + coordinateCount.Length;
            for(int coordinateIndex = 0; coordinateIndex < coordinates; coordinateIndex++)
            {
                Match coordinate = findVector2F.Match(dataObject.Body, dataOffset);
                if(!coordinate.Success)
                    throw new System.IO.InvalidDataException("problem reading texture coordinate count");
                else
                    dataOffset = coordinate.Index + coordinate.Length;

                textureCoordinates.Add(
                    new Vector2F(
                        float.Parse(coordinate.Groups[1].Value, CultureInfo.InvariantCulture),
                        float.Parse(coordinate.Groups[2].Value, CultureInfo.InvariantCulture)));
            }

            return textureCoordinates;
        }
        /// <summary>
        /// Loads the indexed normal vectors for a mesh
        /// </summary>
        /// <param name="meshNormalData"></param>
        /// <returns></returns>
        IndexedMeshNormals LoadMeshNormals(IXDataObject dataObject)
        {
            IndexedMeshNormals indexedMeshNormals = new IndexedMeshNormals();

            Match normalCount = findArrayCount.Match(dataObject.Body);
            if(!normalCount.Success)
                throw new System.IO.InvalidDataException("problem reading mesh normals count");

            indexedMeshNormals.normalVectors = new List<Vector4F>();
            int normals = int.Parse(normalCount.Groups[1].Value, CultureInfo.InvariantCulture);
            int dataOffset = normalCount.Index + normalCount.Length;
            for(int normalIndex = 0; normalIndex < normals; normalIndex++)
            {
                Match normal = findVector3F.Match(dataObject.Body, dataOffset);
                if(!normal.Success)
                    throw new System.IO.InvalidDataException("problem reading mesh normal vector");
                else
                    dataOffset = normal.Index + normal.Length;

                indexedMeshNormals.normalVectors.Add(
                    new Vector4F(
                        float.Parse(normal.Groups[1].Value, CultureInfo.InvariantCulture),
                        float.Parse(normal.Groups[2].Value, CultureInfo.InvariantCulture),
                        float.Parse(normal.Groups[3].Value, CultureInfo.InvariantCulture),
                        1.0f));
            }

            Match faceNormalCount = findArrayCount.Match(dataObject.Body, dataOffset);
            if(!faceNormalCount.Success)
                throw new System.IO.InvalidDataException("problem reading mesh normals count");
            
            indexedMeshNormals.normalIndexMap = new List<Int32>();
            int faceCount = int.Parse(faceNormalCount.Groups[1].Value, CultureInfo.InvariantCulture);
            dataOffset = faceNormalCount.Index + faceNormalCount.Length;
            for(int faceNormalIndex = 0; faceNormalIndex < faceCount; faceNormalIndex++)
            {
                Match normalFace = findVertexIndex.Match(dataObject.Body, dataOffset);
                if(!normalFace.Success)
                    throw new System.IO.InvalidDataException("problem reading mesh normal face");
                else
                    dataOffset = normalFace.Index + normalFace.Length;

                string[] vertexIndexes = normalFace.Groups[2].Value.Split(new char[] { ',' });

                for(int n = 0; n <= vertexIndexes.Length - 3; n ++)
                {
                    indexedMeshNormals.normalIndexMap.Add(int.Parse(vertexIndexes[0], CultureInfo.InvariantCulture));
                    indexedMeshNormals.normalIndexMap.Add(int.Parse(vertexIndexes[1 + n], CultureInfo.InvariantCulture));
                    indexedMeshNormals.normalIndexMap.Add(int.Parse(vertexIndexes[2 + n], CultureInfo.InvariantCulture));
                }
            }

            return indexedMeshNormals;
        }
        /// <summary>
        /// Loads a MeshMaterial subresource
        /// </summary>
        /// <param name="materialData"></param>
        /// <returns></returns>
        MaterialSpecification LoadMeshMaterial(IXDataObject dataObject)
        {
            MaterialSpecification m = new MaterialSpecification();
            int dataOffset = 0;
            Match color = findVector4F.Match(dataObject.Body, dataOffset);
            if(!color.Success)
                throw new System.IO.InvalidDataException("problem reading material color");
            m.materialColor.X = float.Parse(color.Groups[1].ToString(), CultureInfo.InvariantCulture);
            m.materialColor.Y = float.Parse(color.Groups[2].ToString(), CultureInfo.InvariantCulture);
            m.materialColor.Z = float.Parse(color.Groups[3].ToString(), CultureInfo.InvariantCulture);
            m.materialColor.W = float.Parse(color.Groups[4].ToString(), CultureInfo.InvariantCulture);
            dataOffset = color.Index + color.Length;

            Match power = findScalarF.Match(dataObject.Body, dataOffset);
            if(!power.Success)
                throw new System.IO.InvalidDataException("problem reading material specular color exponent");
            m.specularPower = float.Parse(power.Groups[1].ToString(), CultureInfo.InvariantCulture);
            dataOffset = power.Index + power.Length;

            Match specular = findVector3F.Match(dataObject.Body, dataOffset);
            if(!specular.Success)
                throw new System.IO.InvalidDataException("problem reading material specular color");
            m.specularColor.X = float.Parse(specular.Groups[1].ToString(), CultureInfo.InvariantCulture);
            m.specularColor.Y = float.Parse(specular.Groups[2].ToString(), CultureInfo.InvariantCulture);
            m.specularColor.Z = float.Parse(specular.Groups[3].ToString(), CultureInfo.InvariantCulture);
            dataOffset = specular.Index + specular.Length;

            Match emissive = findVector3F.Match(dataObject.Body, dataOffset);
            if(!emissive.Success)
                throw new System.IO.InvalidDataException("problem reading material emissive color");
            m.emissiveColor.X = float.Parse(emissive.Groups[1].ToString(), CultureInfo.InvariantCulture);
            m.emissiveColor.Y = float.Parse(emissive.Groups[2].ToString(), CultureInfo.InvariantCulture);
            m.emissiveColor.Z = float.Parse(emissive.Groups[3].ToString(), CultureInfo.InvariantCulture);
            dataOffset = emissive.Index + emissive.Length;

            IXDataObject filenameObject = GetSingleChild(dataObject, "TextureFilename");

            if (filenameObject != null)
            {
                Regex findFilename = new Regex(@"[\s]+""([\\\w\.]+)"";");
                Match filename = findFilename.Match(filenameObject.Body);
                if (!filename.Success)
                    throw new System.IO.InvalidDataException("problem reading texture filename");
                m.textureFileName = filename.Groups[1].ToString();
            }

            return m;
        }
        /// <summary>
        /// Loads the first material for a mesh
        /// </summary>
        /// <param name="meshMAterialData"></param>
        /// <returns></returns>
        List<MaterialSpecification> LoadMeshMaterialList(IXDataObject dataObject)
        {
            var materials = from child in dataObject.Children
                            where child.DataObjectType == "Material"
                            select LoadMeshMaterial(child);

            return new List<MaterialSpecification>(materials);
        }
        /// <summary>
        /// Extracts the transformation associated with the current frame
        /// </summary>
        /// <param name="dataFile"></param>
        /// <param name="dataOffset"></param>
        /// <returns></returns>
        private Matrix4x4F ExtractFrameTransformation(IXDataObject dataObject)
        {
            IXDataObject matrixObject = GetSingleChild(dataObject, "FrameTransformMatrix");

            if (matrixObject == null)
            {
                return Matrix4x4F.Identity;
            }

            string rawMatrixData = matrixObject.Body;

            Regex matrixData = new Regex(@"([-\d\.,\s]+);;");
            Match data = matrixData.Match(rawMatrixData);
            if(!data.Success)
                throw new System.IO.InvalidDataException("Error parsing frame transformation.");

            string[] values = data.Groups[1].ToString().Split(new char[] { ',' });
            if(values.Length != 16)
                throw new System.IO.InvalidDataException("Error parsing frame transformation.");
            float[] fvalues = new float[16];
            for(int n = 0; n < 16; n++)
            {
                fvalues[n] = float.Parse(values[n], CultureInfo.InvariantCulture);
            }

            return new Matrix4x4F(fvalues);
        }
        private Part PartFromDataObject(IXDataObject dataObject)
        {
            Part part = new Part();

            part.parts = new List<Part>();

            part.name = dataObject.Name;

            switch (dataObject.DataObjectType)
            {
                case "Frame":
                    // Frame data objects translate to parts with only a transform,
                    // and no vertices, materials, etc.
                    part.partTransform = ExtractFrameTransformation(dataObject);
                    foreach (IXDataObject childObject in dataObject.Children.Where(obj => obj.IsVisualObject))
                    {
                        part.parts.Add(PartFromDataObject(childObject));
                    }
                    break;
                case "Mesh":
                    // Mesh data objects inherit transform from their parent,
                    // but do have vertices, materials, etc.
                    part.partTransform = Matrix4x4F.Identity;
                    part.dataDescription = description;
                    LoadMesh(ref part, dataObject);
                    break;
                default:
                    throw new ArgumentException(
                        string.Format(CultureInfo.InvariantCulture,
                        "Object type \"{0}\" is incorrect. Only Frame or Mesh data objects can be converted to Part instances",
                        dataObject.DataObjectType));
            }

            return part;
        }