public static Mesh BuildCube()
        {
            // Create mesh builder
            MeshBuilder builder = new MeshBuilder();
            builder.UseTexCoords = true;
            builder.UseNormals = true;

            // X facing sides
            builder.AddQuad(new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 0.0f, 1.0f), new Vector3(0.0f, 1.0f, 1.0f), new Vector3(0.0f, 1.0f, 0.0f),
                new Vector2(1.0f, 1.0f), new Vector2(0.0f, 1.0f), new Vector2(0.0f, 0.0f), new Vector2(1.0f, 0.0f), false);
            builder.AddQuad(new Vector3(1.0f, 0.0f, 0.0f), new Vector3(1.0f, 0.0f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), new Vector3(1.0f, 1.0f, 0.0f),
                new Vector2(0.0f, 1.0f), new Vector2(1.0f, 1.0f), new Vector2(1.0f, 0.0f), new Vector2(0.0f, 0.0f), true);

            // Y facing sides
            builder.AddQuad(new Vector3(0.0f, 0.0f, 0.0f), new Vector3(1.0f, 0.0f, 0.0f), new Vector3(1.0f, 0.0f, 1.0f), new Vector3(0.0f, 0.0f, 1.0f),
                new Vector2(0.0f, 0.0f), new Vector2(1.0f, 0.0f), new Vector2(1.0f, 1.0f), new Vector2(0.0f, 1.0f), false);
            builder.AddQuad(new Vector3(0.0f, 1.0f, 0.0f), new Vector3(1.0f, 1.0f, 0.0f), new Vector3(1.0f, 1.0f, 1.0f), new Vector3(0.0f, 1.0f, 1.0f),
                new Vector2(1.0f, 0.0f), new Vector2(0.0f, 0.0f), new Vector2(0.0f, 1.0f), new Vector2(1.0f, 1.0f), true);

            // Z facing sides
            builder.AddQuad(new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f), new Vector3(1.0f, 1.0f, 0.0f), new Vector3(1.0f, 0.0f, 0.0f),
                new Vector2(0.0f, 1.0f), new Vector2(0.0f, 0.0f), new Vector2(1.0f, 0.0f), new Vector2(1.0f, 1.0f), false);
            builder.AddQuad(new Vector3(0.0f, 0.0f, 1.0f), new Vector3(0.0f, 1.0f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), new Vector3(1.0f, 0.0f, 1.0f),
                new Vector2(1.0f, 1.0f), new Vector2(1.0f, 0.0f), new Vector2(0.0f, 0.0f), new Vector2(0.0f, 1.0f), true);

            // Done
            return builder.Build();
        }
        private static void MergeMeshes(SBMLoader loader, out Mesh result, out string[] materials)
        {
            if (loader.MeshCount == 0)
            {
                result = null;
                materials = null;
                return;
            }
            if (loader.MeshCount == 1)
            {
                loader.GetMesh(0, out result, out materials);
                return;
            }

            List<string> finalmaterials = new List<string>();

            MeshBuilder merged = new MeshBuilder();
            merged.UseNormals = true;
            merged.UseTangents = true;
            merged.UseTexCoords = true;

            int totalsubmeshes = 0;

            for (int i = 0; i < loader.MeshCount; i++)
            {
                Mesh cmesh;
                string[] curmats;
                loader.GetMesh(i, out cmesh, out curmats);

                totalsubmeshes += cmesh.Submeshes.Length;

                int baseindex = merged.CurrentVertexCount;
                merged.AddPositions(cmesh.Positions);
                merged.AddNormals(cmesh.Normals);
                merged.AddTextureCoords(cmesh.TextureCoordinates);
                merged.AddTangents(cmesh.Tangents);

                for (int j = 0; j < cmesh.Submeshes.Length; j++)
                {
                    int submeshindex;
                    if (finalmaterials.Contains(curmats[j]))
                        submeshindex = finalmaterials.IndexOf(curmats[j]);
                    else
                    {
                        submeshindex = finalmaterials.Count;
                        finalmaterials.Add(curmats[j]);
                    }

                    merged.AddIndices(submeshindex, cmesh.Submeshes[j], (uint)baseindex);
                }

            }

            Console.WriteLine("Merged {0} meshes with a total of {1} submeshes into 1 mesh with a total of {2} submeshes.", loader.MeshCount, totalsubmeshes, finalmaterials.Count);

            result = merged.Build();
            materials = finalmaterials.ToArray();
        }
        /// <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 CombineMeshes(params Mesh[] meshes)
 {
     MeshBuilder builder = new MeshBuilder();
     if (meshes[0].TextureCoordinates != null) builder.UseTexCoords = true;
     if (meshes[0].Normals != null) builder.UseNormals = true;
     uint indexpos = 0;
     for (int i = 0; i < meshes.Length; i++)
     {
         var mesh = meshes[i];
         if (builder.UseTexCoords) builder.AddTextureCoords(mesh.TextureCoordinates);
         if (builder.UseNormals) builder.AddNormals(mesh.Normals);
         for (int j = 0; j < mesh.Submeshes.Length; j++)
             builder.AddIndices(j, mesh.Submeshes[j]);
         builder.AddPositions(mesh.Positions);
         indexpos += (uint)mesh.Positions.Length;
     }
     return builder.Build();
 }
        public static Mesh BuildSkybox()
        {
            MeshBuilder builder = new MeshBuilder();
            builder.AddPosition(new Vector3(-1.0f, -1.0f, -1.0f));  // 0
            builder.AddPosition(new Vector3(-1.0f, -1.0f, 1.0f));   // 1
            builder.AddPosition(new Vector3(1.0f, -1.0f, 1.0f));    // 2
            builder.AddPosition(new Vector3(1.0f, -1.0f, -1.0f));   // 3
            builder.AddPosition(new Vector3(-1.0f, 1.0f, -1.0f));   // 4
            builder.AddPosition(new Vector3(-1.0f, 1.0f, 1.0f));    // 5
            builder.AddPosition(new Vector3(1.0f, 1.0f, 1.0f));     // 6
            builder.AddPosition(new Vector3(1.0f, 1.0f, -1.0f));    // 7

            // North
            builder.AddIndex(1);
            builder.AddIndex(5);
            builder.AddIndex(6);
            builder.AddIndex(6);
            builder.AddIndex(2);
            builder.AddIndex(1);

            // East
            builder.AddIndex(2);
            builder.AddIndex(6);
            builder.AddIndex(7);
            builder.AddIndex(7);
            builder.AddIndex(3);
            builder.AddIndex(2);

            // South
            builder.AddIndex(3);
            builder.AddIndex(7);
            builder.AddIndex(4);
            builder.AddIndex(4);
            builder.AddIndex(0);
            builder.AddIndex(3);

            // West
            builder.AddIndex(0);
            builder.AddIndex(4);
            builder.AddIndex(5);
            builder.AddIndex(5);
            builder.AddIndex(2);
            builder.AddIndex(0);

            // Up
            builder.AddIndex(5);
            builder.AddIndex(4);
            builder.AddIndex(7);
            builder.AddIndex(7);
            builder.AddIndex(6);
            builder.AddIndex(5);

            // Down
            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 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 BuildNormalDebugger(Mesh mesh)
 {
     if (mesh.Normals == null) return null;
     MeshBuilder builder = new MeshBuilder();
     for (int i = 0; i < mesh.Normals.Length; i++)
     {
         builder.AddPosition(mesh.Positions[i]);
         builder.AddPosition(mesh.Positions[i] + mesh.Normals[i]);
     }
     return builder.Build();
 }
        public static Mesh BuildHeightmap(HeightSampler sampler, int width, int height, float tilesize, int subdivide = 1)
        {
            // Determine sizes
            int totalW = width * subdivide;
            int totalH = height * subdivide;
            float invtotalW = 1.0f / totalW;
            float invtotalH = 1.0f / totalH;

            // Precache an array of heights
            float[,] heights = new float[totalW, totalH];
            for (int x = 0; x < totalW; x++)
            {
                float fX = x * invtotalW * width;
                for (int y = 0; y < totalH; y++)
                {
                    float fY = y * invtotalH * height;
                    heights[x, y] = sampler(fX, fY);
                }
            }

            // Recreate the sampler to use the cache
            sampler = new HeightSampler((x, y) =>
            {
                int ix = (int)(x * subdivide);
                int iy = (int)(y * subdivide);
                if (ix < 0) ix = 0; if (iy < 0) iy = 0;
                if (ix >= totalW) ix = totalW - 1;
                if (iy >= totalH) iy = totalH - 1;
                return heights[ix, iy];
            });

            // Prepare the builder
            MeshBuilder builder = new MeshBuilder();
            builder.UseNormals = true;
            builder.UseTangents = true;

            // Perform the loop
            for (int x = 0; x < totalW; x++)
            {
                float fX = x * invtotalW * width;
                for (int y = 0; y < totalH; y++)
                {
                    float fY = y * invtotalH * height;
                    builder.AddPosition(new Vector3(fX * tilesize, sampler(fX, fY), fY * tilesize));
                    builder.AddNormal(CalculateNormal(sampler, fX, fY));
                    builder.AddTangent(CalculateTangent(sampler, fX, fY));
                    if ((x < totalW - 1) && (y < totalH - 1))
                    {
                        int idx = (x * totalH) + y;
                        builder.AddIndex((ushort)idx);
                        builder.AddIndex((ushort)(idx + 1));
                        builder.AddIndex((ushort)(idx + totalH + 1));
                        builder.AddIndex((ushort)(idx + totalH + 1));
                        builder.AddIndex((ushort)(idx + totalH));
                        builder.AddIndex((ushort)idx);
                    }
                }
            }
            return builder.Build();
        }
 public static void BuildGrassPatch(int points, float range, out Mesh high, out Mesh medium, out Mesh low)
 {
     Random rnd = new Random();
     MeshBuilder builder = new MeshBuilder();
     builder.UseNormals = true;
     for (int i = 0; i < points; i++)
     {
         builder.AddPosition(new Vector3((float)rnd.NextDouble() * range, 0.0f, (float)rnd.NextDouble() * range));
         builder.AddNormal(new Vector3((float)(rnd.NextDouble() * Math.PI * 2.0), (float)rnd.NextDouble(), (float)rnd.NextDouble()));
     }
     high = builder.Build();
     builder.RemovePoints(points / 3);
     medium = builder.Build();
     builder.RemovePoints(points / 3);
     low = 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();
        }