} // ExtractSphericalHarmonicForCubeFace

        #endregion

        #region Generate Spherical Harmonic from Lat-Long Texture

        /// <summary>
        /// Generate a spherical harmonic from the faces of a cubemap, treating each pixel as a light source and averaging the result.
        /// This method only accepts floating point textures.
        /// </summary>
        public static SphericalHarmonicL2 GenerateSphericalHarmonicFromLatLongTexture(Texture texture)
        {
            if (texture.Resource.Format != SurfaceFormat.Vector4)
            {
                throw new InvalidOperationException("Spherical Harmonic: the texture has to have a floating point surface format. DXT formats are not supported for the moment.");
            }

            SphericalHarmonicL2 sh = new SphericalHarmonicL2();

            float[] colorArray = new float[texture.Width * texture.Height * 4];
            texture.Resource.GetData(colorArray);

            int pixelIndex = 0;

            for (int y = 0; y < texture.Height; y++)
            {
                SphericalHarmonicL2 lineSh = new SphericalHarmonicL2();

                for (int x = 0; x < texture.Width; x++)
                {
                    // http://www.scratchapixel.com/lessons/3d-advanced-lessons/reflection-mapping/converting-latitute-longitude-maps-and-mirror-balls/
                    float   theta     = (x / (float)texture.Width) * (float)Math.PI;
                    float   phi       = (1 - (y / (float)texture.Height)) * 2 * (float)Math.PI;
                    Vector3 direction = -new Vector3((float)(Math.Sin(theta) * Math.Cos(phi)), (float)(Math.Sin(theta) * Math.Sin(phi)), (float)(Math.Cos(theta)));
                    direction.Normalize();

                    Vector3 rgb = new Vector3(colorArray[pixelIndex * 4], colorArray[pixelIndex * 4 + 1], colorArray[pixelIndex * 4 + 2]);

                    //Add it to the SH
                    lineSh.AddLight(rgb, direction, 1);

                    pixelIndex++;
                }

                //average the SH
                if (lineSh.weighting > 0)
                {
                    lineSh *= 1 / lineSh.weighting;
                }

                // Add the line to the full SH
                // (SH is generated line by line to ease problems with floating point accuracy loss)
                sh += lineSh;
            }

            //average out over the sphere
            return(sh.GetWeightedAverageLightInputFromSphere());
        } // GenerateSphericalHarmonicFromLatLongHdrTexture
        } // GetWeightedAverageLightInputFromSphere

        private static SphericalHarmonicL2 ExtractSphericalHarmonicForCubeFace(Matrix faceTransform, Color[] colorDataRgb, int faceSize, bool isRgbm, float rgbmMaxRange)
        {
            SphericalHarmonicL2 sh = new SphericalHarmonicL2();

            // For each pixel in the face, generate it's SH contribution.
            // Treat each pixel in the cube as a light source, which gets added to the SH.
            // This is used to generate an indirect lighting SH for the scene.

            float directionStep = 2.0f / (faceSize - 1.0f);
            int   pixelIndex    = 0;

            float dirY = 1.0f;

            for (int y = 0; y < faceSize; y++)
            {
                SphericalHarmonicL2 lineSh = new SphericalHarmonicL2();
                float dirX = -1.0f;

                for (int x = 0; x < faceSize; x++)
                {
                    // The direction to the pixel in the cube.
                    Vector3 direction = new Vector3(dirX, dirY, 1);
                    Vector3.TransformNormal(ref direction, ref faceTransform, out direction);

                    // Length of the direction vector.
                    float length = direction.Length();
                    // Approximate area of the pixel (pixels close to the cube edges appear smaller when projected).
                    float weight = 1.0f / length;

                    direction.Normalize();

                    Vector3 rgbFloat;
                    if (isRgbm)
                    {
                        Color rgbm = colorDataRgb[pixelIndex++];
                        rgbFloat = RgbmHelper.RgbmGammaToFloatLinear(rgbm, rgbmMaxRange);
                    }
                    else
                    {
                        Color rgb = colorDataRgb[pixelIndex++];
                        rgbFloat = new Vector3(GammaLinearSpaceHelper.GammaToLinear(rgb).X, GammaLinearSpaceHelper.GammaToLinear(rgb).Y, GammaLinearSpaceHelper.GammaToLinear(rgb).Z);
                    }

                    //Add it to the SH
                    lineSh.AddLight(rgbFloat, direction, weight);

                    dirX += directionStep;
                }

                // Average the SH.
                if (lineSh.weighting > 0)
                {
                    lineSh *= 1 / lineSh.weighting;
                }

                // Add the line to the full SH
                // (SH is generated line by line to ease problems with floating point accuracy loss)
                sh += lineSh;

                dirY -= directionStep;
            }

            // Average the SH.
            if (sh.weighting > 0)
            {
                sh *= 1 / sh.weighting;
            }

            return(sh);
        } // ExtractSphericalHarmonicForCubeFace