/// <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);
        }