/// <summary>
        /// Fills in a set of spherically mapped texture coordinates to the passed in mesh
        /// </summary>
        private static void SetSphericalTextureCoordinates(Mesh mesh)
        {
            BoundingSphere boundingSphere = BufferHelper.ComputeBoundingSphere(mesh);

            var vertexes = BufferHelper.ReadVertexBuffer <PositionNormalTextured>(mesh);

            for (int i = 0; i < vertexes.Length; i++)
            {
                Vector3 vertexRay = Vector3.Normalize(vertexes[i].Position - boundingSphere.Center);
                double  phi       = Math.Acos(vertexRay.Z);

                vertexes[i].Tu = CalculateTu(vertexRay, phi);
                vertexes[i].Tv = (float)(phi / Math.PI);
            }
            BufferHelper.WriteVertexBuffer(mesh, vertexes);
        }
        /// <summary>
        /// Sets box texture coordinates where a 3x2 grid of face textures is split one per face
        /// </summary>
        private static void SetBoxTextureCoordinates(Mesh mesh)
        {
            #region Sanity checks
            // Check the mesh looks like a box and has texture coordinates
            if (mesh.VertexCount != 24 || mesh.FaceCount != 12)
            {
                throw new ArgumentException("The mesh does not look like a box.", nameof(mesh));
            }
            if (mesh.VertexFormat != PositionNormalTextured.Format)
            {
                throw new ArgumentException("The mesh doesn't have the correct format.", nameof(mesh));
            }
            #endregion

            // Copy the vertex buffer content to an array
            var vertexes = BufferHelper.ReadVertexBuffer <PositionNormalTextured>(mesh);

            #region Set texture coordinates
            // Bottom
            vertexes[13].Tu = 0f;
            vertexes[13].Tv = 0f;
            vertexes[14].Tu = 1f / 3f;
            vertexes[14].Tv = 0f;
            vertexes[15].Tu = 1f / 3f;
            vertexes[15].Tv = 0.5f;
            vertexes[12].Tu = 0f;
            vertexes[12].Tv = 0.5f;

            // Top
            vertexes[5].Tu = 1f / 3f;
            vertexes[5].Tv = 0f;
            vertexes[6].Tu = 2f / 3f;
            vertexes[6].Tv = 0f;
            vertexes[7].Tu = 2f / 3f;
            vertexes[7].Tv = 0.5f;
            vertexes[4].Tu = 1f / 3f;
            vertexes[4].Tv = 0.5f;

            // Back
            vertexes[18].Tu = 2f / 3f;
            vertexes[18].Tv = 0f;
            vertexes[19].Tu = 1f;
            vertexes[19].Tv = 0f;
            vertexes[16].Tu = 1f;
            vertexes[16].Tv = 0.5f;
            vertexes[17].Tu = 2f / 3f;
            vertexes[17].Tv = 0.5f;

            // Left
            vertexes[2].Tu = 0f;
            vertexes[2].Tv = 0.5f;
            vertexes[3].Tu = 1f / 3f;
            vertexes[3].Tv = 0.5f;
            vertexes[0].Tu = 1f / 3f;
            vertexes[0].Tv = 1f;
            vertexes[1].Tu = 0f;
            vertexes[1].Tv = 1f;

            // Front
            vertexes[21].Tu = 1f / 3f;
            vertexes[21].Tv = 0.5f;
            vertexes[22].Tu = 2f / 3f;
            vertexes[22].Tv = 0.5f;
            vertexes[23].Tu = 2f / 3f;
            vertexes[23].Tv = 1f;
            vertexes[20].Tu = 1f / 3f;
            vertexes[20].Tv = 1f;

            // Right
            vertexes[8].Tu  = 2f / 3f;
            vertexes[8].Tv  = 0.5f;
            vertexes[9].Tu  = 1f;
            vertexes[9].Tv  = 0.5f;
            vertexes[10].Tu = 1f;
            vertexes[10].Tv = 1f;
            vertexes[11].Tu = 2f / 3f;
            vertexes[11].Tv = 1f;
            #endregion

            // Copy the array back into the vertex buffer
            BufferHelper.WriteVertexBuffer(mesh, vertexes);
        }
        /// <summary>
        /// Creates a model of a textured round disc with a hole in the middle.
        /// </summary>
        /// <param name="device">The <see cref="Device"/> to create the mesh in.</param>
        /// <param name="radiusInner">The radius of the inner circle of the ring.</param>
        /// <param name="radiusOuter">The radius of the outer circle of the ring.</param>
        /// <param name="height">The height of the ring.</param>
        /// <param name="segments">The number of segments the ring shall consist of.</param>
        public static Mesh Disc(Device device, float radiusInner, float radiusOuter, float height, int segments)
        {
            #region Sanity checks
            if (device == null)
            {
                throw new ArgumentNullException(nameof(device));
            }
            #endregion

            const float tuInner = 0, tuOuter = 1;

            Log.Info("Generate predefined model: Disc");
            var posInner = new Vector3(radiusInner, 0, 0);
            var posOuter = new Vector3(radiusOuter, 0, 0);

            #region Generate vertexes
            int vertCount = 0;
            var step      = (float)(Math.PI * 2 / segments);
            var vertexes  = new PositionTextured[segments * 4];

            for (int i = 0; i < segments; i++)
            {
                vertexes[vertCount++] = new PositionTextured(posInner.X, posInner.Y - height / 2, posInner.Z, tuInner, 0);
                vertexes[vertCount++] = new PositionTextured(posInner.X, posInner.Y + height / 2, posInner.Z, tuInner, 0);
                vertexes[vertCount++] = new PositionTextured(posOuter.X, posOuter.Y - height / 2, posOuter.Z, tuOuter, 0);
                vertexes[vertCount++] = new PositionTextured(posOuter.X, posOuter.Y + height / 2, posOuter.Z, tuOuter, 0);

                // Increment rotation
                posInner = Vector3.TransformCoordinate(posInner, Matrix.RotationY(step));
                posOuter = Vector3.TransformCoordinate(posOuter, Matrix.RotationY(step));
            }
            #endregion

            #region Generate indexes
            int indexCount = 0;
            var indexes    = new short[segments * 24];

            for (int i = 0; i < segments; i++)
            {
                short innerBottom1 = (short)(i * 4), innerTop1 = (short)(i * 4 + 1);
                short outerBottom1 = (short)(i * 4 + 2), outerTop1 = (short)(i * 4 + 3);
                short innerBottom2 = (short)(i * 4 + 4), innerTop2 = (short)(i * 4 + 5);
                short outerBottom2 = (short)(i * 4 + 6), outerTop2 = (short)(i * 4 + 7);

                if (innerBottom2 >= vertexes.Length)
                {
                    innerBottom2 -= (short)vertexes.Length;
                }
                if (innerTop2 >= vertexes.Length)
                {
                    innerTop2 -= (short)vertexes.Length;
                }
                if (outerBottom2 >= vertexes.Length)
                {
                    outerBottom2 -= (short)vertexes.Length;
                }
                if (outerTop2 >= vertexes.Length)
                {
                    outerTop2 -= (short)vertexes.Length;
                }

                // Bottom 2 triangles
                indexes[indexCount++] = innerBottom1;
                indexes[indexCount++] = innerBottom2;
                indexes[indexCount++] = outerBottom2;
                indexes[indexCount++] = innerBottom1;
                indexes[indexCount++] = outerBottom2;
                indexes[indexCount++] = outerBottom1;

                // Top 2 triangles
                indexes[indexCount++] = innerTop1;
                indexes[indexCount++] = outerTop2;
                indexes[indexCount++] = innerTop2;
                indexes[indexCount++] = innerTop1;
                indexes[indexCount++] = outerTop1;
                indexes[indexCount++] = outerTop2;

                // Inner 2 triangles
                indexes[indexCount++] = innerTop1;
                indexes[indexCount++] = innerBottom2;
                indexes[indexCount++] = innerBottom1;
                indexes[indexCount++] = innerTop1;
                indexes[indexCount++] = innerTop2;
                indexes[indexCount++] = innerBottom2;

                // Outer 2 triangles
                indexes[indexCount++] = outerBottom1;
                indexes[indexCount++] = outerBottom2;
                indexes[indexCount++] = outerTop1;
                indexes[indexCount++] = outerBottom2;
                indexes[indexCount++] = outerTop2;
                indexes[indexCount++] = outerTop1;
            }
            #endregion

            var mesh = new Mesh(device, indexes.Length / 3, vertexes.Length, MeshFlags.Managed, PositionTextured.Format);
            BufferHelper.WriteVertexBuffer(mesh, vertexes);
            BufferHelper.WriteIndexBuffer(mesh, indexes);
            MeshHelper.GenerateNormalsAndTangents(device, ref mesh, false);

            return(mesh);
        }
        /// <summary>
        /// Creates a new <see cref="Mesh"/> representing a textured sphere with spherical mapping.
        /// </summary>
        /// <param name="device">The <see cref="Device"/> to create the mesh in</param>
        /// <param name="radius">The radius of the sphere.</param>
        /// <param name="slices">The number of vertical slices to divide the sphere into.</param>
        /// <param name="stacks">The number of horizontal stacks to divide the sphere into.</param>
        /// <remarks>The sphere is formed like the one created by <see cref="Mesh.CreateSphere"/>.</remarks>
        public static Mesh Sphere(Device device, float radius, int slices, int stacks)
        {
            #region Sanity checks
            if (device == null)
            {
                throw new ArgumentNullException(nameof(device));
            }
            if (slices <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(slices));
            }
            if (stacks <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(stacks));
            }
            #endregion

            int numVertexes = (slices + 1) * (stacks + 1);
            int numFaces    = slices * stacks * 2;
            int indexCount  = numFaces * 3;

            var mesh = new Mesh(device, numFaces, numVertexes, MeshFlags.Managed, PositionNormalTextured.Format);

            #region Build sphere vertexes
            var vertexes  = new PositionNormalTextured[mesh.VertexCount];
            int vertIndex = 0;
            for (int slice = 0; slice <= slices; slice++)
            {
                float alphaY = (float)slice / slices * (float)Math.PI * 2.0f; // Angle around Y-axis
                for (int stack = 0; stack <= stacks; stack++)
                {
                    if (slice == slices)
                    {
                        vertexes[vertIndex] = vertexes[stack];
                    }
                    else
                    {
                        var   pnt    = new PositionNormalTextured();
                        float alphaZ = ((stack - stacks * 0.5f) / stacks) * (float)Math.PI * 1.0f; // Angle around Z-axis
                        pnt.X  = (float)(Math.Cos(alphaY) * radius) * (float)Math.Cos(alphaZ);
                        pnt.Z  = (float)(Math.Sin(alphaY) * radius) * (float)Math.Cos(alphaZ);
                        pnt.Y  = (float)(Math.Sin(alphaZ) * radius);
                        pnt.Nx = pnt.X / radius;
                        pnt.Ny = pnt.Y / radius;
                        pnt.Nz = pnt.Z / radius;
                        pnt.Tv = 0.5f - (float)(Math.Asin(pnt.Y / radius) / Math.PI);
                        vertexes.SetValue(pnt, vertIndex);
                    }
                    vertexes[vertIndex++].Tu = (float)slice / slices;
                }
            }
            #endregion

            BufferHelper.WriteVertexBuffer(mesh, vertexes);

            #region Build index buffer
            var indexes = new short[indexCount];
            int i       = 0;
            for (short x = 0; x < slices; x++)
            {
                var leftVertex  = (short)((stacks + 1) * x);
                var rightVertex = (short)(leftVertex + stacks + 1);
                for (int y = 0; y < stacks; y++)
                {
                    indexes[i++] = rightVertex;
                    indexes[i++] = leftVertex;
                    indexes[i++] = (short)(leftVertex + 1);
                    indexes[i++] = rightVertex;
                    indexes[i++] = (short)(leftVertex + 1);
                    indexes[i++] = (short)(rightVertex + 1);
                    leftVertex++;
                    rightVertex++;
                }
            }
            #endregion

            BufferHelper.WriteIndexBuffer(mesh, indexes);

            return(mesh);
        }
        /// <summary>
        /// Generate normals and tangents if not present and convert into TangentVertex format for shaders.
        /// </summary>
        /// <param name="device">The <see cref="Device"/> containing the mesh</param>
        /// <param name="mesh">The mesh to be manipulated</param>
        /// <param name="weldVertexes">Weld vertexes before generating tangents.
        /// Useful for organic objects, stones, trees, etc. (anything with a lot of round surfaces).
        /// If a lot of single faces are not connected on the texture (e.g. rockets, buildings, etc.) do not use.</param>
        public static void GenerateNormalsAndTangents(Device device, ref Mesh mesh, bool weldVertexes)
        {
            #region Sanity checks
            if (device == null)
            {
                throw new ArgumentNullException(nameof(device));
            }
            if (mesh == null)
            {
                throw new ArgumentNullException(nameof(mesh));
            }
            #endregion

            bool hadNormals, hadTangents;
            if (!ExpandDeclaration(device, ref mesh, out hadNormals, out hadTangents))
            {
                return;
            }
            var decl = mesh.GetDeclaration();

            #region Check existing info
            bool gotMilkErmTexCoords = false;
            bool gotValidNormals     = true;
            bool gotValidTangents    = true;
            var  vertexes            = BufferHelper.ReadVertexBuffer <PositionNormalBinormalTangentTextured>(mesh);

            // Check all vertexes
            for (int num = 0; num < vertexes.Length; num++)
            {
                // We need at least 1 texture coordinate different from (0, 0)
                if (vertexes[num].Tu != 0.0f ||
                    vertexes[num].Tv != 0.0f)
                {
                    gotMilkErmTexCoords = true;
                }

                // All normals and tangents must be valid, otherwise generate them below
                if (vertexes[num].Normal == default(Vector3))
                {
                    gotValidNormals = false;
                }
                if (vertexes[num].Tangent == default(Vector3))
                {
                    gotValidTangents = false;
                }

                // If we found valid texture coordinates and no normals or tangents,
                // there isn't anything left to check here
                if (gotMilkErmTexCoords && !gotValidNormals && !gotValidTangents)
                {
                    break;
                }
            }

            // If declaration had normals, but we found no valid normals,
            // set hadNormals to false and generate valid normals (see below)
            if (!gotValidNormals)
            {
                hadNormals = false;
            }
            // Same check for tangents
            if (!gotValidTangents)
            {
                hadTangents = false;
            }

            // Generate dummy texture coordinates
            if (!gotMilkErmTexCoords)
            {
                for (int num = 0; num < vertexes.Length; num++)
                {
                    vertexes[num].Tu = -0.75f + vertexes[num].Position.X / 2.0f;
                    vertexes[num].Tv = +0.75f - vertexes[num].Position.Y / 2.0f;
                }
            }
            BufferHelper.WriteVertexBuffer(mesh, vertexes);
            #endregion

            if (!hadNormals)
            {
                using (new TimedLogEvent("Computed normals"))
                    mesh.ComputeNormals();
            }

            if (weldVertexes)
            {
                // Reduce amount of vertexes
                var weldEpsilons = new WeldEpsilons {
                    Position = 0.0001f, Normal = 0.0001f
                };
                mesh.WeldVertices(WeldFlags.WeldPartialMatches, weldEpsilons);

                if (!hadTangents)
                {
                    #region Compute tangents
                    using (new TimedLogEvent("Computed tangents"))
                    {
                        // If the vertexes for a smoothend point exist several times the
                        // DirectX ComputeTangent method is not able to treat them all the
                        // same way.
                        // To circumvent this, we collapse all vertexes in a cloned mesh
                        // even if the texture coordinates don't fit. Then we copy the
                        // generated tangents back to the original mesh vertexes (duplicating
                        // the tangents for vertexes at the same point with the same normals
                        // if required). This happens usually with models exported from 3DSMax.

                        // Clone mesh just for tangent generation
                        Mesh dummyTangentGenerationMesh = mesh.Clone(device, mesh.CreationOptions, decl);

                        // Reuse weldEpsilons, just change the TextureCoordinates, which we don't care about anymore
                        weldEpsilons.TextureCoordinate1 = 1;
                        weldEpsilons.TextureCoordinate2 = 1;
                        weldEpsilons.TextureCoordinate3 = 1;
                        weldEpsilons.TextureCoordinate4 = 1;
                        weldEpsilons.TextureCoordinate5 = 1;
                        weldEpsilons.TextureCoordinate6 = 1;
                        weldEpsilons.TextureCoordinate7 = 1;
                        weldEpsilons.TextureCoordinate8 = 1;
                        // Rest of the weldEpsilons values can stay 0, we don't use them
                        dummyTangentGenerationMesh.WeldVertices(WeldFlags.WeldPartialMatches, weldEpsilons);

                        // Compute tangents
                        if (!CompareDecl(PositionNormalMultiTextured.GetVertexElements(), decl))
                        {
                            dummyTangentGenerationMesh.ComputeTangent(0, 0, 0, false);
                        }
                        var tangentVertexes = BufferHelper.ReadVertexBuffer <PositionNormalBinormalTangentTextured>(dummyTangentGenerationMesh);
                        dummyTangentGenerationMesh.Dispose();

                        // Copy generated tangents back
                        vertexes = BufferHelper.ReadVertexBuffer <PositionNormalBinormalTangentTextured>(mesh);
                        for (int num = 0; num < vertexes.Length; num++)
                        {
                            // Search for tangent vertex with the exact same position and normal.
                            for (int tangentVertexNum = 0; tangentVertexNum < tangentVertexes.Length; tangentVertexNum++)
                            {
                                if (vertexes[num].Position == tangentVertexes[tangentVertexNum].Position && vertexes[num].Normal == tangentVertexes[tangentVertexNum].Normal)
                                {
                                    // Copy the tangent over
                                    vertexes[num].Tangent = tangentVertexes[tangentVertexNum].Tangent;
                                    // No more checks required, proceed with next vertex
                                    break;
                                }
                            }
                        }
                        BufferHelper.WriteVertexBuffer(mesh, vertexes);
                    }
                    #endregion
                }
            }
            else
            {
                if (!hadTangents && CompareDecl(PositionNormalMultiTextured.GetVertexElements(), decl))
                {
                    using (new TimedLogEvent("Computed tangents"))
                        mesh.ComputeTangent(0, 0, D3DX.Default, false);
                }
            }

            Optimize(mesh);
        }
        /// <summary>
        /// Generate normals if not present and convert into TangentVertex format for shaders.
        /// Tangent data is left empty
        /// </summary>
        /// <param name="device">The <see cref="Device"/> containing the mesh</param>
        /// <param name="mesh">The mesh to be manipulated</param>
        public static void GenerateNormals(Device device, ref Mesh mesh)
        {
            #region Sanity checks
            if (device == null)
            {
                throw new ArgumentNullException(nameof(device));
            }
            if (mesh == null)
            {
                throw new ArgumentNullException(nameof(mesh));
            }
            #endregion

            bool hadNormals, hadTangents;
            if (!ExpandDeclaration(device, ref mesh, out hadNormals, out hadTangents))
            {
                return;
            }

            #region Check existing info
            bool gotMilkErmTexCoords = false;
            bool gotValidNormals     = true;
            var  vertexes            = BufferHelper.ReadVertexBuffer <PositionNormalBinormalTangentTextured>(mesh);

            // Check all vertexes
            for (int num = 0; num < vertexes.Length; num++)
            {
                // We need at least 1 texture coordinate different from (0, 0)
                if (vertexes[num].Tu != 0.0f ||
                    vertexes[num].Tv != 0.0f)
                {
                    gotMilkErmTexCoords = true;
                }

                // All normals and tangents must be valid, otherwise generate them below
                if (vertexes[num].Normal == default(Vector3))
                {
                    gotValidNormals = false;
                }
            }

            // If declaration had normals, but we found no valid normals,
            // set hadNormals to false and generate valid normals (see below)
            if (!gotValidNormals)
            {
                hadNormals = false;
            }

            // Generate dummy texture coordinates
            if (!gotMilkErmTexCoords)
            {
                for (int num = 0; num < vertexes.Length; num++)
                {
                    vertexes[num].Tu = -0.75f + vertexes[num].Position.X / 2.0f;
                    vertexes[num].Tv = +0.75f - vertexes[num].Position.Y / 2.0f;
                }
            }
            BufferHelper.WriteVertexBuffer(mesh, vertexes);
            #endregion

            // Assume meshes with propper normal data also have been optimized for rendering
            if (!hadNormals)
            {
                using (new TimedLogEvent("Computed normals"))
                    mesh.ComputeNormals();

                Optimize(mesh);
            }
        }