/// <summary>
        /// Loads this SBM model
        /// </summary>
        /// <returns></returns>
        public bool Load(out string err)
        {
            // Check the file exists
            if (!File.Exists(filename))
            {
                err = "File not found";
                return false;
            }

            // Load it
            using (FileStream strm = File.OpenRead(filename))
            {
                // Create reader
                BinaryReader rdr = new BinaryReader(strm);

                // Read header
                string magic = new string(rdr.ReadChars(4));
                if (magic != "SBM\0")
                {
                    err = "Specified file is not an SBM file";
                    return false;
                }
                int version = rdr.ReadInt32();
                if (version != 1)
                {
                    err = "Unsupported SBM version";
                    return false;
                }
                int num_materials = rdr.ReadInt32();
                int num_meshes = rdr.ReadInt32();

                // Read all materials
                string[] materials = new string[num_materials];
                for (int i = 0; i < num_materials; i++)
                    materials[i] = rdr.ReadNullTerminatedString();

                // Read all meshes
                meshes = new SBMMesh[num_meshes];
                for (int i = 0; i < num_meshes; i++)
                {
                    // Read mesh header
                    int num_vertices = rdr.ReadInt32();
                    int num_submeshes = rdr.ReadInt32();

                    // Create mesh builder
                    MeshBuilder builder = new MeshBuilder();
                    builder.UseNormals = true;
                    builder.UseTexCoords = true;
                    builder.UseTangents = true;

                    // Read all vertices
                    for (int j = 0; j < num_vertices; j++)
                    {
                        builder.AddPosition(new Vector3(rdr.ReadSingle(), rdr.ReadSingle(), rdr.ReadSingle()));
                        builder.AddNormal(new Vector3(rdr.ReadSingle(), rdr.ReadSingle(), rdr.ReadSingle()));
                        builder.AddTextureCoord(new Vector2(rdr.ReadSingle(), rdr.ReadSingle()));
                        builder.AddTangent(new Vector3(rdr.ReadSingle(), rdr.ReadSingle(), rdr.ReadSingle()));
                    }

                    // Loop each submesh
                    string[] meshmats = new string[num_submeshes];
                    for (int j = 0; j < num_submeshes; j++)
                    {
                        // Read submesh header
                        int num_indices = rdr.ReadInt32();
                        int material_index = rdr.ReadInt32();
                        meshmats[j] = materials[material_index];

                        // Read all indices
                        for (int k = 0; k < num_indices; k++)
                            builder.AddIndex(j, rdr.ReadUInt32());
                    }

                    // Add element to mesh array
                    SBMMesh sbmmesh = new SBMMesh();
                    sbmmesh.Mesh = builder.Build();
                    sbmmesh.Materials = meshmats;
                    meshes[i] = sbmmesh;
                }
            }

            // Success
            err = null;
            return true;
        }
        public static Mesh BuildPlane(bool texcoords, bool tangents)
        {
            // Create mesh builder
            MeshBuilder builder = new MeshBuilder();
            builder.UseNormals = true;
            builder.UseTexCoords = texcoords;
            builder.UseTangents = tangents;

            // Add vertices
            builder.AddPosition(new Vector3(0.0f, 0.0f, 0.0f));
            builder.AddPosition(new Vector3(0.0f, 0.0f, 1.0f));
            builder.AddPosition(new Vector3(1.0f, 0.0f, 1.0f));
            builder.AddPosition(new Vector3(1.0f, 0.0f, 0.0f));
            builder.AddNormal(Vector3.UnitY);
            builder.AddNormal(Vector3.UnitY);
            builder.AddNormal(Vector3.UnitY);
            builder.AddNormal(Vector3.UnitY);
            if (texcoords)
            {
                builder.AddTextureCoord(new Vector2(0.0f, 1.0f));
                builder.AddTextureCoord(new Vector2(1.0f, 1.0f));
                builder.AddTextureCoord(new Vector2(1.0f, 0.0f));
                builder.AddTextureCoord(new Vector2(0.0f, 0.0f));
            }
            if (tangents)
            {
                builder.AddTangent(new Vector3(1.0f, 0.0f, 0.0f));
                builder.AddTangent(new Vector3(1.0f, 0.0f, 0.0f));
                builder.AddTangent(new Vector3(1.0f, 0.0f, 0.0f));
                builder.AddTangent(new Vector3(1.0f, 0.0f, 0.0f));
            }

            // Add indices
            builder.AddIndex(0);
            builder.AddIndex(1);
            builder.AddIndex(2);
            builder.AddIndex(2);
            builder.AddIndex(3);
            builder.AddIndex(0);

            // Build mesh
            return builder.Build();
        }
        public static Mesh BuildSphere(float radius, int subdivisions, bool usetexcoords, bool usetangents)
        {
            // Starting points
            var A = new Vector3(-1.0f, -(1.0f / Root3), -(1.0f / Root6));
            var B = new Vector3(0.0f, 2.0f / Root3, -1.0f / Root6);
            var C = new Vector3(1.0f, -(1.0f / Root3), -(1.0f / Root6));
            var D = new Vector3(0.0f, 0.0f, 3.0f / Root6);

            // Build a triangular based pyramid
            var tris = new List<Triangle>();
            tris.Add(new Triangle(A, B, C));
            tris.Add(new Triangle(C, D, A));
            tris.Add(new Triangle(B, D, C));
            tris.Add(new Triangle(A, D, B));
            var tmplist = new List<Triangle>();

            // Run subdivide passes
            for (int i = 0; i < subdivisions; i++)
            {
                // Normalise all vertices
                for (int j = 0; j < tris.Count; j++)
                {
                    Triangle tri = tris[j];
                    tri.A.Normalize();
                    tri.A *= radius;
                    tri.B.Normalize();
                    tri.B *= radius;
                    tri.C.Normalize();
                    tri.C *= radius;
                    tris[j] = tri;
                }

                // Check for last pass
                if (i == subdivisions - 1) break;

                // Subdivide all triangles
                tmplist.Clear();
                for (int j = 0; j < tris.Count; j++)
                {
                    Triangle tri = tris[j];
                    Vector3 midAB = (tri.A + tri.B) * 0.5f;
                    Vector3 midBC = (tri.B + tri.C) * 0.5f;
                    Vector3 midCA = (tri.C + tri.A) * 0.5f;
                    tmplist.Add(new Triangle(tri.A, midAB, midCA));
                    tmplist.Add(new Triangle(midAB, midBC, midCA));
                    tmplist.Add(new Triangle(midCA, midBC, tri.C));
                    tmplist.Add(new Triangle(midAB, tri.B, midBC));
                }

                // Swap the lists
                var tmp = tris;
                tris = tmplist;
                tmplist = tmp;
            }

            // Create builder
            var builder = new MeshBuilder();
            builder.UseNormals = true;
            builder.UseTexCoords = usetexcoords;
            builder.UseTangents = usetangents;
            for (int i = 0; i < tris.Count; i++)
            {
                Triangle tri = tris[i];
                builder.AddPosition(tri.A);
                builder.AddPosition(tri.B);
                builder.AddPosition(tri.C);
                builder.AddNormal(GetNormal(tri.A));
                builder.AddNormal(GetNormal(tri.B));
                builder.AddNormal(GetNormal(tri.C));
                if (usetangents)
                {
                    builder.AddTangent(GetSphereTangent(tri.A));
                    builder.AddTangent(GetSphereTangent(tri.B));
                    builder.AddTangent(GetSphereTangent(tri.C));
                }
                if (usetexcoords)
                {
                    // TODO: This
                    builder.AddTextureCoord(new Vector2(0.0f, 0.0f));
                    builder.AddTextureCoord(new Vector2(0.0f, 0.0f));
                    builder.AddTextureCoord(new Vector2(0.0f, 0.0f));
                }
                builder.AddIndex((ushort)(i * 3));
                builder.AddIndex((ushort)(i * 3 + 1));
                builder.AddIndex((ushort)(i * 3 + 2));
            }

            // Create mesh
            return builder.Build();
        }
        public static Mesh BuildFullscreenQuad()
        {
            // Create mesh builder
            MeshBuilder builder = new MeshBuilder();
            builder.UseTexCoords = true;

            // Add vertices
            builder.AddPosition(new Vector3(-1.0f, -1.0f, 0.5f));
            builder.AddPosition(new Vector3(1.0f, -1.0f, 0.5f));
            builder.AddPosition(new Vector3(1.0f, 1.0f, 0.5f));
            builder.AddPosition(new Vector3(-1.0f, 1.0f, 0.5f));
            builder.AddTextureCoord(new Vector2(0.0f, 1.0f));
            builder.AddTextureCoord(new Vector2(1.0f, 1.0f));
            builder.AddTextureCoord(new Vector2(1.0f, 0.0f));
            builder.AddTextureCoord(new Vector2(0.0f, 0.0f));

            // Add indices
            builder.AddIndex(0);
            builder.AddIndex(1);
            builder.AddIndex(2);
            builder.AddIndex(2);
            builder.AddIndex(3);
            builder.AddIndex(0);

            // Build mesh
            return builder.Build();
        }