private void LoadMeshFromObj(string filename)
        {
            // Force garbage collection to ensure that unmanaged resources are released.
            // Temporary workaround until unmanaged resource leak is identified
            GC.Collect();

            bool objectFound = false;
            //Dictionary<string, ObjectNode> objectTable = new Dictionary<string, ObjectNode>();
            List<ObjectNode> objects = new List<ObjectNode>();
            ObjectNode currentObject = new ObjectNode();
            currentObject.Name = "Default";

            int triangleCount = 0;
            int vertexCount = 0;

             //           List<Mesh.Group> matGroups = new List<Mesh.Group>();

            List<PositionNormalTextured> vertexList = new List<PositionNormalTextured>();
            List<Vector3> vertList = new List<Vector3>();
            List<Vector3> normList = new List<Vector3>();
            List<Vector2> uvList = new List<Vector2>();

            vertList.Add(new Vector3());
            normList.Add(new Vector3());
            uvList.Add(new Vector2());

            List<int> indexList = new List<int>();
            List<int> attribList = new List<int>();
            List<int[]> applyLists = new List<int[]>();
            List<int> applyListsIndex = new List<int>();
            List<string> materialNames = new List<string>();
            int currentMaterialIndex = -1;
            Material currentMaterial = new Material();
            Mesh.Group currentGroup = new Mesh.Group();

            int currentIndex = 0;

            //initialize the default material

            currentMaterial = new Material();
            currentMaterial.Diffuse = Color;
            currentMaterial.Ambient = Color;
            currentMaterial.Specular = System.Drawing.Color.White;
            currentMaterial.SpecularSharpness = 30.0f;
            currentMaterial.Opacity = 1.0f;
            currentMaterial.Default = true;

            //initialize the group
            currentGroup.startIndex = 0;
            currentGroup.indexCount = 0;
            currentGroup.materialIndex = 0;

            using (Stream fs = new FileStream(filename, FileMode.Open,FileAccess.Read))
            {
                StreamReader sr = new StreamReader(fs);

                while (!sr.EndOfStream)
                {
                    string line = sr.ReadLine().Replace("  ", " ");

                    string[] parts = line.Trim().Split(new char[] { ' ' });

                    if (parts.Length > 0)
                    {
                        switch (parts[0])
                        {
                            case "mtllib":
                                {
                                    string path = filename.Substring(0, filename.LastIndexOf('\\') + 1);
                                    string matFile = path + "\\" + parts[1];
                                    LoadMatLib(matFile);
                                }
                                break;
                            case "usemtl":
                                string materialName = parts[1];
                                if (matLib.ContainsKey(materialName))
                                {
                                    if (currentMaterialIndex == -1 && currentIndex > 0)
                                    {
                                        addMaterial(currentMaterial);
                                        currentMaterialIndex++;
                                    }

                                    if (currentMaterialIndex > -1)
                                    {
                                        currentGroup.indexCount = currentIndex - currentGroup.startIndex;
                                  //      matGroups.Add(currentGroup);
                                        currentObject.DrawGroup.Add(currentGroup);
                                    }

                                    currentMaterialIndex++;

                                    if (matLib.ContainsKey(materialName))
                                    {
                                        currentMaterial = matLib[materialName];

                                        if (textureLib.ContainsKey(materialName))
                                        {
                                            try
                                            {
                                                if (!TextureCache.ContainsKey(textureLib[materialName]))
                                                {
                                                    string path = filename.Substring(0, filename.LastIndexOf('\\') + 1);

                                                    Texture11 tex = LoadTexture(path + textureLib[materialName]);
                                                    if (tex != null)
                                                    {
                                                        meshFilenames.Add(textureLib[materialName]);
                                                        TextureCache.Add(textureLib[materialName], tex);
                                                    }
                                                }
                                                meshTextures.Add(TextureCache[textureLib[materialName]]);
                                            }
                                            catch
                                            {
                                            }
                                        }

                                        addMaterial(currentMaterial);

                                        currentGroup = new Mesh.Group();
                                        currentGroup.startIndex = currentIndex;
                                        currentGroup.indexCount = 0;
                                        currentGroup.materialIndex = currentMaterialIndex;
                                    }
                                }

                                break;
                            case "v":
                                vertexCount++;
                                if (FlipHandedness)
                                {
                                    vertList.Add(new Vector3(-float.Parse(parts[1]), float.Parse(parts[2]), float.Parse(parts[3])));
                                }
                                else
                                {
                                    vertList.Add(new Vector3(float.Parse(parts[1]), float.Parse(parts[2]), float.Parse(parts[3])));
                                }
                                break;
                            case "vn":
                                if (FlipHandedness)
                                {
                                    normList.Add(new Vector3(-float.Parse(parts[1]), float.Parse(parts[2]), float.Parse(parts[3])));
                                }
                                else
                                {
                                    normList.Add(new Vector3(float.Parse(parts[1]), float.Parse(parts[2]), float.Parse(parts[3])));
                                }
                                break;
                            case "vt":
                                uvList.Add(new Vector2(float.Parse(parts[1]), FlipV ? (1 - float.Parse(parts[2])) : float.Parse(parts[2])));
                                break;
                            case "g":
                            case "o":
                                if (objectFound)
                                {
                                    if (currentMaterialIndex > -1)
                                    {
                                        currentGroup.indexCount = currentIndex - currentGroup.startIndex;
                       //                 matGroups.Add(currentGroup);
                                        currentObject.DrawGroup.Add(currentGroup);
                                        currentGroup = new Mesh.Group();
                                        currentGroup.startIndex = currentIndex;
                                        currentGroup.indexCount = 0;
                                        currentGroup.materialIndex = currentMaterialIndex;
                                    }
                                    currentObject = new ObjectNode();
                                }

                                objectFound = true;
                                if (parts.Length > 1)
                                {
                                    currentObject.Name = parts[1];
                                }
                                else
                                {
                                    currentObject.Name = "Unnamed";
                                }
                                objects.Add(currentObject);
                                //if (!objectTable.ContainsKey(currentObject.Name))
                                //{
                                //    objectTable.Add(currentObject.Name, currentObject);
                                //}
                                break;
                            case "f":
                                int[] indexiesA = GetIndexies(parts[1]);
                                int[] indexiesB = GetIndexies(parts[2]);
                                int[] indexiesC = GetIndexies(parts[3]);

                                vertexList.Add(new PositionNormalTextured(vertList[indexiesA[0]], normList[indexiesA[2]], uvList[indexiesA[1]]));
                                vertexList.Add(new PositionNormalTextured(vertList[indexiesB[0]], normList[indexiesB[2]], uvList[indexiesB[1]]));
                                vertexList.Add(new PositionNormalTextured(vertList[indexiesC[0]], normList[indexiesC[2]], uvList[indexiesC[1]]));

                                if (FlipHandedness)
                                {
                                    indexList.Add(currentIndex);
                                    indexList.Add(currentIndex + 2);
                                    indexList.Add(currentIndex + 1);
                                }
                                else
                                {
                                    indexList.Add(currentIndex);
                                    indexList.Add(currentIndex + 1);
                                    indexList.Add(currentIndex + 2);

                                }

                                triangleCount++;
                                currentIndex += 3;
                                //bool flip = true;
                                if (parts.Length > 4)
                                {
                                    int partIndex = 4;

                                    while (partIndex < (parts.Length))
                                    {
                                        if (FlipHandedness)
                                        {
                                            indexiesA = GetIndexies(parts[1]);
                                            indexiesC = GetIndexies(parts[partIndex]);
                                            indexiesB = GetIndexies(parts[partIndex - 1]);
                                        }
                                        else
                                        {
                                            indexiesA = GetIndexies(parts[1]);
                                            indexiesB = GetIndexies(parts[partIndex - 1]);
                                            indexiesC = GetIndexies(parts[partIndex]);
                                        }
                                        vertexList.Add(new PositionNormalTextured(vertList[indexiesA[0]], normList[indexiesA[2]], uvList[indexiesA[1]]));
                                        vertexList.Add(new PositionNormalTextured(vertList[indexiesB[0]], normList[indexiesB[2]], uvList[indexiesB[1]]));
                                        vertexList.Add(new PositionNormalTextured(vertList[indexiesC[0]], normList[indexiesC[2]], uvList[indexiesC[1]]));

                                        indexList.Add(currentIndex);
                                        indexList.Add(currentIndex + 1);
                                        indexList.Add(currentIndex + 2);
                                        triangleCount++;

                                        currentIndex += 3;
                                        partIndex++;
                                    }
                                }
                                break;
                        }
                    }
                }
            }

            if (!objectFound)
            {
                // add the default object
                objects.Add(currentObject);
            }

            if (currentMaterialIndex == -1 && currentIndex > 0)
            {
                addMaterial(currentMaterial);
                currentMaterialIndex++;
            }

            if (currentMaterialIndex > -1)
            {
                currentGroup.indexCount = currentIndex - currentGroup.startIndex;
                currentObject.DrawGroup.Add(currentGroup);
            }

            if (normList.Count < 2)
            {

                float creaseAngleRad = MathUtil.DegreesToRadians(Smooth ? 170.0f : 45.0f);

                Vector3[] vertexNormals = CalculateVertexNormalsMerged(vertexList, indexList, creaseAngleRad);
                List<PositionNormalTextured> newVertexList = new List<PositionNormalTextured>();
                int newVertexCount = indexList.Count;

                for (int vertexIndex = 0; vertexIndex < newVertexCount; ++vertexIndex)
                {
                    PositionNormalTextured v = vertexList[indexList[vertexIndex]];
                    v.Normal = vertexNormals[vertexIndex];
                    newVertexList.Add(v);
                }

                vertexList = newVertexList;
            }

            mesh = new Mesh(vertexList.ToArray(), indexList.ToArray());
            ObjectNode rootDummy = new ObjectNode();
            rootDummy.Name = "Root";
            rootDummy.Parent = null;
            rootDummy.Level = -1;
            rootDummy.DrawGroup = null;
            rootDummy.Children = objects;

            Objects = new List<ObjectNode>();
            Objects.Add(rootDummy);

            mesh.setObjects(Objects);

            //List<ObjectNode> objects = new List<ObjectNode>();

            //ObjectNode node = new ObjectNode();
            //node.Name = "Default";
            //node.DrawGroup = matGroups;
            //objects.Add(node);
            //mesh.setObjects(objects);
            //Objects = objects;

            mesh.commitToDevice(RenderContext11.PrepDevice);

            dirty = false;

            GC.Collect();
        }
        internal void Dispose()
        {
            if (mesh != null)
            {
                mesh.Dispose();
                GC.SuppressFinalize(mesh);
                mesh = null;
            }

            foreach (Texture11 tex in TextureCache.Values)
            {
                if (tex != null)
                {
                    tex.Dispose();
                }
            }
            TextureCache.Clear();

            DisposeTextureList(meshTextures);
            DisposeTextureList(meshSpecularTextures);
            DisposeTextureList(meshNormalMaps);

            meshMaterials.Clear();
            dirty = true;
        }
        private void LoadMeshFrom3ds(string filename, float scale)
        {
            // Force garbage collection to ensure that unmanaged resources are released.
            // Temporary workaround until unmanaged resource leak is identified
            GC.Collect();

            int i;

            ushort sectionID;
            uint sectionLength;

            string name = "";
            string material = "";
            int triangleCount = 0;
            int vertexCount = 0;
            List<PositionNormalTextured> vertexList = new List<PositionNormalTextured>();
            List<int> indexList = new List<int>();
            List<int> attribList = new List<int>();
            //List<int[]> applyLists = new List<int[]>();
            //List<int> applyListsIndex = new List<int>();
            List<string> materialNames = new List<string>();
            int currentMaterialIndex = -1;
            Material currentMaterial = new Material();
            int attributeID = 0;

            int count = 0;
            UInt16 lastID = 0;
            bool exit = false;
            bool normalMapFound = false;

            float offsetX = 0;
            float offsetY = 0;
            float offsetZ = 0;
            List<ObjectNode> objects = new List<ObjectNode>();
            ObjectNode currentObject = null;
            List<int> objHierarchy = new List<int>();
            List<string> objNames = new List<string>();
            Dictionary<string, ObjectNode> objectTable = new Dictionary<string, ObjectNode>();

            int dummyCount = 0;

            using (Stream fs = new FileStream(filename, FileMode.Open))
            {
                BinaryReader br = new BinaryReader(fs);
                long length = fs.Length - 1;
                int startMapIndex = 0;
                int startTriangleIndex = 0;
                while (br.BaseStream.Position < length && !exit) //Loop to scan the whole file
                {
                    sectionID = br.ReadUInt16();
                    sectionLength = br.ReadUInt32();

                    switch (sectionID)
                    {

                        //This section the begining of the file
                        case 0x4d4d:
                            break;

                        // This section marks the edit section containing the 3d models (3d3d get it? very punny!)
                        case 0x3d3d:
                            break;

                        // Object section contains meshes, etc.
                        case 0x4000:
                            name = "";
                            Byte b;
                            do
                            {
                                b = br.ReadByte();
                                if (b > 0)
                                {
                                    name += (char)b;
                                }

                            } while (b != '\0');

                            currentObject = new ObjectNode();
                            currentObject.Name = name;
                            objects.Add(currentObject);
                            if (!objectTable.ContainsKey(currentObject.Name))
                            {
                                objectTable.Add(currentObject.Name, currentObject);
                            }

                            break;

                        // Marks the start of a mesh section
                        case 0x4100:
                            startMapIndex = vertexList.Count;
                            startTriangleIndex = indexList.Count / 3;
                            break;

                        // This section has the vertex list.. Maps to Vertext buffer in Direct3d
                        case 0x4110:
                            vertexCount = br.ReadUInt16();

                            for (i = 0; i < vertexCount; i++)
                            {
                                float x = br.ReadSingle() - offsetX;
                                float y = br.ReadSingle() - offsetY;
                                float z = br.ReadSingle() - offsetZ;

                                PositionNormalTextured vert = new PositionNormalTextured(x * scale, z * scale, y * scale, 0, 0, 0, 0, 0);
                                vertexList.Add(vert);
                            }
                            break;

                        // This section is a tiangle index list. Maps to Index Buffer in Direct3d
                        case 0x4120:
                            {
                                int triCount = br.ReadUInt16();
                                triangleCount += triCount;

                                for (i = 0; i < triCount; i++)
                                {
                                    int aa = br.ReadUInt16() + startMapIndex;
                                    int bb = br.ReadUInt16() + startMapIndex;
                                    int cc = br.ReadUInt16() + startMapIndex;
                                    indexList.Add(cc);
                                    indexList.Add(bb);
                                    indexList.Add(aa);
                                    UInt16 flags = br.ReadUInt16();
                                }
                            }
                            break;

                        // Material for face from start face to triCount
                        case 0x4130:
                            {
                                material = "";
                                i = 0;
                                byte b1;
                                do
                                {
                                    b1 = br.ReadByte();
                                    if (b1 > 0)
                                    {
                                        material += (char)b1;
                                    }

                                    i++;
                                } while (b1 != '\0');
                                int triCount = br.ReadUInt16();
                                int[] applyList = new int[triCount];

                                attributeID = GetMaterialID(material, materialNames);

                                for (i = 0; i < triCount; i++)
                                {
                                    applyList[i] = br.ReadUInt16() + startTriangleIndex;
                                }
                                currentObject.ApplyLists.Add(applyList);
                                currentObject.ApplyListsIndex.Add(attributeID);

                            }
                            break;

                        // Section for UV texture maps
                        case 0x4140:
                            count = br.ReadUInt16();
                            for (i = 0; i < count; i++)
                            {
                                PositionNormalTextured vert = vertexList[startMapIndex + i];
                                Vector2 texCoord = new Vector2(br.ReadSingle(), FlipV ? (1.0f - br.ReadSingle()) : (br.ReadSingle()));
                                vertexList[startMapIndex + i] = new PositionNormalTextured(vert.Position, Vector3.Zero, texCoord);
                            }
                            break;

                        // Section for Smoothing Groups
                        //case 0x4150:
                        //    count = br.ReadUInt16();
                        //    for (i = 0; i < count; i++)
                        //    {
                        //        CustomVertex.PositionNormalTextured vert = vertexList[startMapIndex + i];
                        //        vertexList[startMapIndex + i] = new CustomVertex.PositionNormalTextured(vert.Position, new Vector3(0,0,0), br.ReadSingle(), FlipV ? (1.0f -  br.ReadSingle() ) : (br.ReadSingle()));
                        //    }
                        //    break;
                        case 0x4160:
                            float[] mat = new float[12];
                            for (i = 0; i < 12; i++)
                            {
                                mat[i] = br.ReadSingle();
                            }
                            //offsetX = mat[9];
                            //offsetY = mat[11];
                            //offsetZ = mat[10];

                            if (objectTable.ContainsKey(name))
                            {
                                objectTable[name].LocalMat = new Matrix3d(
                                    mat[0], mat[1], mat[2], 0,
                                    mat[3], mat[4], mat[5], 0,
                                    mat[6], mat[7], mat[8], 0,
                                    mat[9], mat[10], mat[11], 1);

                                objectTable[name].LocalMat.Invert();

                                //objectTable[name].PivotPoint = new Vector3(mat[9]*mat[0],mat[10]*mat[1],mat[11]*mat[2]);
                            }

                            break;
                        // Materials library section
                        case 0xAFFF:
                            break;
                        // Material Name
                        case 0xA000:
                            {
                                string matName = "";
                                i = 0;
                                byte b2;
                                do
                                {
                                    b2 = br.ReadByte();
                                    if (b2 > 0)
                                    {
                                        matName += (char)b2;
                                    }

                                    i++;
                                } while (b2 != '\0');
                                materialNames.Add(matName);

                                if (currentMaterialIndex > -1)
                                {
                                    addMaterial(currentMaterial);
                                }

                                currentMaterialIndex++;

                                currentMaterial = new Material();
                                currentMaterial.Diffuse = System.Drawing.Color.White;
                                currentMaterial.Ambient = System.Drawing.Color.White;
                                currentMaterial.Specular = System.Drawing.Color.Black;
                                currentMaterial.SpecularSharpness = 30.0f;
                                currentMaterial.Opacity = 1.0f;
                            }
                            break;

                        // Ambient color
                        case 0xA010:
                            currentMaterial.Ambient = LoadColorChunk(br);
                            break;

                        // Diffuse color
                        case 0xA020:
                            currentMaterial.Diffuse = LoadColorChunk(br);
                            break;

                        // Specular color
                        case 0xA030:
                            currentMaterial.Specular = LoadColorChunk(br);
                            break;

                        // Specular power
                        case 0xA040:
                            // This is just a reasonable guess at the mapping from percentage to
                            // specular exponent used by 3D Studio.
                            currentMaterial.SpecularSharpness = 1.0f + 2 * LoadPercentageChunk(br);

                            // Minimum sharpness of 10 enforced here because of bad specular exponents
                            // in ISS model.
                            // TODO: Fix ISS and permit lower specular exponents here
                            currentMaterial.SpecularSharpness = Math.Max(10.0f, currentMaterial.SpecularSharpness);
                            break;

                        //Texture map file
                        case 0xA200:
                            break;

                        // Texture file name
                        case 0xA300:
                            {
                                string textureFilename = "";
                                i = 0;
                                byte b2;
                                do
                                {
                                    b2 = br.ReadByte();
                                    if (b2 > 0)
                                    {
                                        textureFilename += (char)b2;
                                    }

                                    i++;
                                } while (b2 != '\0');
                                string path = filename.Substring(0, filename.LastIndexOf('\\') + 1);
                                try
                                {
                                    Texture11 tex = LoadTexture(path + textureFilename);

                                    if (tex != null)
                                    {
                                        meshTextures.Add(tex);
                                        meshFilenames.Add(textureFilename);

                                        // The ISS model has black for the diffuse color; to work around this
                                        // we'll set the diffuse color to white when there's a texture present.
                                        // The correct fix is to modify the 3ds model of ISS.
                                        currentMaterial.Diffuse = System.Drawing.Color.White;
                                    }
                                    else
                                    {
                                        meshTextures.Add(null);
                                    }
                                }
                                catch
                                {
                                    meshTextures.Add(null);
                                }
                            }
                            break;

                        // Bump map
                        case 0xA230:
                            {
                                float percentage = LoadPercentageChunk(br);

                                int nameId = br.ReadUInt16();
                                uint nameBlockLength = br.ReadUInt32();
                                string textureFilename = "";
                                i = 0;
                                byte b2;
                                do
                                {
                                    b2 = br.ReadByte();
                                    if (b2 > 0)
                                    {
                                        textureFilename += (char)b2;
                                    }

                                    i++;
                                } while (b2 != '\0');

                                string path = filename.Substring(0, filename.LastIndexOf('\\') + 1);
                                try
                                {
                                    Texture11 tex = LoadTexture(path + textureFilename);

                                    if (tex != null)
                                    {
                                        meshNormalMaps.Add(tex);
                                        meshFilenames.Add(textureFilename);

                                        // Indicate that we have a normal map so that we know to generate tangent vectors for the mesh
                                        normalMapFound = true;
                                    }
                                    else
                                    {
                                        meshNormalMaps.Add(null);
                                    }

                                }
                                catch
                                {
                                    meshNormalMaps.Add(null);
                                }
                            }

                            break;

                        // Specular map
                        case 0xA204:
                            {
                                float strength = LoadPercentageChunk(br);

                                int nameId = br.ReadUInt16();
                                uint nameBlockLength = br.ReadUInt32();
                                string textureFilename = "";
                                i = 0;
                                byte b2;
                                do
                                {
                                    b2 = br.ReadByte();
                                    if (b2 > 0)
                                    {
                                        textureFilename += (char)b2;
                                    }

                                    i++;
                                } while (b2 != '\0');

                                string path = filename.Substring(0, filename.LastIndexOf('\\') + 1);
                                try
                                {
                                    Texture11 tex = LoadTexture(path + textureFilename);
                                    if (tex != null)
                                    {
                                        meshSpecularTextures.Add(tex);
                                        meshFilenames.Add(textureFilename);

                                        // Set the current specular color from the specular texture strength
                                        int gray = (int)(255.99f * strength / 100.0f);
                                        currentMaterial.Specular = System.Drawing.Color.FromArgb(255, gray, gray, gray);
                                    }
                                    else
                                    {
                                        meshSpecularTextures.Add(null);
                                    }
                                }
                                catch
                                {
                                    meshSpecularTextures.Add(null);
                                }
                            }

                            break;
                        case 0xB000:
                            break;
                        case 0xB002:
                            break;
                        case 0xB010:
                            {
                                name = "";
                                i = 0;
                                byte b1;
                                do
                                {
                                    b1 = br.ReadByte();
                                    if (b1 > 0)
                                    {
                                        name += (char)b1;
                                    }

                                    i++;
                                } while (b1 != '\0');
                                int dum1 = (int)br.ReadUInt16();
                                int dum2 = (int)br.ReadUInt16();
                                int level = (int)br.ReadUInt16();

                                if (level == 65535)
                                {
                                    level = -1;
                                }
                                if (name.StartsWith("$"))
                                {
                                    dummyCount++;

                                }
                                else
                                {
                                    objNames.Add(name);
                                }
                                objHierarchy.Add(level);

                                if (objectTable.ContainsKey(name))
                                {

                                    objectTable[name].Level = level;
                                }
                            }
                            break;
                        case 0xB011:
                            {
                                name = "";
                                i = 0;
                                byte b1;
                                do
                                {
                                    b1 = br.ReadByte();
                                    if (b1 > 0)
                                    {
                                        name += (char)b1;
                                    }

                                    i++;
                                } while (b1 != '\0');
                                objNames.Add( "$$$" + name);
                            }
                            break;
                        case 0xB013:
                            //pivot point
                            float[] points = new float[3];
                            for (i = 0; i < 3; i++)
                            {
                                points[i] = br.ReadSingle();
                            }

                            if (objectTable.ContainsKey(name))
                            {
                                   objectTable[name].PivotPoint = -new Vector3(points[0], points[1], points[2]);
                            }
                            break;
                        case 0xB020:
                            {
                                float[] pos = new float[8];
                                for (i = 0; i < 8; i++)
                                {
                                    pos[i] = br.ReadSingle();
                                }

                            }
                            break;

                        // If we don't recognize a section then jump over it. Subract the header from the section length
                        default:
                            br.BaseStream.Seek(sectionLength - 6, SeekOrigin.Current);
                            break;
                    }
                    lastID = sectionID;
                }
                br.Close();
                if (currentMaterialIndex > -1)
                {
                    addMaterial(currentMaterial);
                }
            }

            ////debug

            //for ( i = 0; i < 99; i++)
            //{
            //    System.Diagnostics.Debug.WriteLine(objNames[i]);
            //}

            //foreach (ObjectNode node in objects)
            //{
            //    System.Diagnostics.Debug.WriteLine(node.Name);
            //}

            ////debug

            // Generate vertex normals

            // Vertex normals are computed by averaging the normals of all faces
            // with an angle between them less than the crease angle. By setting
            // the crease angle to 0 degrees, the model will have a faceted appearance.
            // Right now, the smooth flag selects between one of two crease angles,
            // but some smoothing is always applied.
            float creaseAngleRad = MathUtil.DegreesToRadians(Smooth ? 70.0f : 45.0f);

            Vector3[] vertexNormals = CalculateVertexNormalsMerged(vertexList, indexList, creaseAngleRad);
            List<PositionNormalTextured> newVertexList = new List<PositionNormalTextured>();
            int newVertexCount = triangleCount * 3;

            for (int vertexIndex = 0; vertexIndex < newVertexCount; ++vertexIndex)
            {
                PositionNormalTextured v = vertexList[indexList[vertexIndex]];
                v.Normal = vertexNormals[vertexIndex];
                newVertexList.Add(v);
            }

            // Use the triangle mesh and material assignments to create a single
            // index list for the mesh.
            List<uint> newIndexList = new List<uint>();

            foreach (ObjectNode node in objects)
            {

                List<Mesh.Group> materialGroups = new List<Mesh.Group>();
                for (i = 0; i < node.ApplyLists.Count; i++)
                {
                    int matId = node.ApplyListsIndex[i];
                    int startIndex = newIndexList.Count;
                    foreach (int triangleIndex in node.ApplyLists[i])
                    {
                        newIndexList.Add((uint)(triangleIndex * 3));
                        newIndexList.Add((uint)(triangleIndex * 3 + 1));
                        newIndexList.Add((uint)(triangleIndex * 3 + 2));
                    }

                    var group = new Mesh.Group();
                    group.startIndex = startIndex;
                    group.indexCount = node.ApplyLists[i].Length * 3;
                    group.materialIndex = matId;
                    materialGroups.Add(group);
                }
                node.DrawGroup = materialGroups;
            }

            // Turn objects into tree
            Stack<ObjectNode> nodeStack = new Stack<ObjectNode>();

            List<ObjectNode> nodeTreeRoot = new List<ObjectNode>();

            ObjectNode rootDummy = new ObjectNode();
            rootDummy.Name = "Root";
            rootDummy.Parent = null;
            rootDummy.Level = -1;
            rootDummy.DrawGroup = null;

            int currentLevel = -1;

            nodeStack.Push(rootDummy);
            nodeTreeRoot.Add(rootDummy);

            for (i = 0; i < objHierarchy.Count; i++)
            {
                int level = objHierarchy[i];

                if (level <= currentLevel)
                {
                    // pop out all the nodes to intended parent
                    while (level <= nodeStack.Peek().Level && nodeStack.Count > 1)
                    {
                        nodeStack.Pop();
                    }
                    currentLevel = level;

                }

                if (objNames[i].StartsWith("$$$"))
                {
                    ObjectNode dummy = new ObjectNode();
                    dummy.Name = objNames[i].Replace("$$$", "");
                    dummy.Parent = nodeStack.Peek();
                    dummy.Parent.Children.Add(dummy);
                    dummy.Level = currentLevel = level;
                    dummy.DrawGroup = null;
                    nodeStack.Push(dummy);
                }
                else
                {
                    objectTable[objNames[i]].Level = currentLevel = level;
                    objectTable[objNames[i]].Parent = nodeStack.Peek();
                    objectTable[objNames[i]].Parent.Children.Add(objectTable[objNames[i]]);
                    nodeStack.Push(objectTable[objNames[i]]);
                }
            }

            if (objHierarchy.Count == 0)
            {
                foreach (ObjectNode node in objects)
                {
                    rootDummy.Children.Add(node);
                    node.Parent = rootDummy;
                }
            }

            if (normalMapFound)
            {
                // If we've got a normal map, we want to generate tangent vectors for the mesh

                // Mapping of vertices to geometry is extremely straightforward now, but this could
                // change when a mesh optimization step is introduced.
                var tangentIndexList = new List<uint>();
                for (uint tangentIndex = 0; tangentIndex < newVertexCount; ++tangentIndex)
                {
                    tangentIndexList.Add(tangentIndex);
                }

                Vector3[] tangents = CalculateVertexTangents(newVertexList, tangentIndexList, creaseAngleRad);

                // Copy the tangents in the vertex data list
                var vertices = new PositionNormalTexturedTangent[newVertexList.Count];
                int vertexIndex = 0;
                foreach (PositionNormalTextured v in newVertexList)
                {
                    PositionNormalTexturedTangent tvertex = new PositionNormalTexturedTangent(v.Position, v.Normal, new Vector2(v.Tu, v.Tv), tangents[vertexIndex]);
                    vertices[vertexIndex] = tvertex;
                    ++vertexIndex;
                }
                mesh = new Mesh(vertices, newIndexList.ToArray());
            }
            else
            {
                mesh = new Mesh(newVertexList.ToArray(), newIndexList.ToArray());
            }

            Objects = nodeTreeRoot;
            mesh.setObjects(nodeTreeRoot);
            mesh.commitToDevice(RenderContext11.PrepDevice);

            dirty = false;

            GC.Collect();
        }