} // Add

        #endregion

        #region Multiply by a scalar

        /// <summary>
        /// Multiply a spherical harmonic by a constant scale factor
        /// </summary>
        public static SphericalHarmonicL1 Multiply(SphericalHarmonicL1 x, float scale)
        {
            return(new SphericalHarmonicL1
            {
                weighting = x.weighting * scale,
                c0 = { X = x.c0.X * scale, Y = x.c0.Y * scale, Z = x.c0.Z * scale },
                c1 = { X = x.c1.X * scale, Y = x.c1.Y * scale, Z = x.c1.Z * scale },
                c2 = { X = x.c2.X * scale, Y = x.c2.Y * scale, Z = x.c2.Z * scale },
                c3 = { X = x.c3.X * scale, Y = x.c3.Y * scale, Z = x.c3.Z * scale }
            });
        } // Multiply
        } // ExtractSphericalHarmonicForCubeFace

        #endregion

        #region Add

        /// <summary>
        /// Add two spherical harmonics together.
        /// </summary>
        public static SphericalHarmonicL1 Add(SphericalHarmonicL1 x, SphericalHarmonicL1 y)
        {
            return(new SphericalHarmonicL1
            {
                weighting = x.weighting + y.weighting,
                c0 = { X = x.c0.X + y.c0.X, Y = x.c0.Y + y.c0.Y, Z = x.c0.Z + y.c0.Z },
                c1 = { X = x.c1.X + y.c1.X, Y = x.c1.Y + y.c1.Y, Z = x.c1.Z + y.c1.Z },
                c2 = { X = x.c2.X + y.c2.X, Y = x.c2.Y + y.c2.Y, Z = x.c2.Z + y.c2.Z },
                c3 = { X = x.c3.X + y.c3.X, Y = x.c3.Y + y.c3.Y, Z = x.c3.Z + y.c3.Z }
            });
        } // Add
        } // Multiply

        #endregion

        #region Lerp

        /// <summary>
        /// Linear interpolate (Lerp) between two spherical harmonics based on a interpolation factor.
        /// </summary>
        /// <param name="factor">Determines the interpolation point. When factor is 1.0, the output will be x, when factor is 0.0, the output will be y</param>
        public static SphericalHarmonicL1 Lerp(SphericalHarmonicL1 x, SphericalHarmonicL1 y, float factor)
        {
            float xs = factor;
            float ys = 1.0f - factor;

            return(new SphericalHarmonicL1
            {
                weighting = x.weighting * xs + y.weighting * ys,
                c0 = { X = xs * x.c0.X + ys * y.c0.X, Y = xs * x.c0.Y + ys * y.c0.Y, Z = xs * x.c0.Z + ys * y.c0.Z },
                c1 = { X = xs * x.c1.X + ys * y.c1.X, Y = xs * x.c1.Y + ys * y.c1.Y, Z = xs * x.c1.Z + ys * y.c1.Z },
                c2 = { X = xs * x.c2.X + ys * y.c2.X, Y = xs * x.c2.Y + ys * y.c2.Y, Z = xs * x.c2.Z + ys * y.c2.Z },
                c3 = { X = xs * x.c3.X + ys * y.c3.X, Y = xs * x.c3.Y + ys * y.c3.Y, Z = xs * x.c3.Z + ys * y.c3.Z },
            });
        } // Lerp
        } // SampleDirection

        #endregion

        #region Generate Spherical Harmonic from CubeMap

        /// <summary>
        /// Generate a spherical harmonic from the faces of a cubemap, treating each pixel as a light source and averaging the result.
        /// </summary>
        public static SphericalHarmonicL1 GenerateSphericalHarmonicFromCubeMap(TextureCube cubeMap)
        {
            SphericalHarmonicL1 sh = new SphericalHarmonicL1();

            // Extract the 6 faces of the cubemap.
            for (int face = 0; face < 6; face++)
            {
                CubeMapFace faceId = (CubeMapFace)face;

                // Get the transformation for this face,
                Matrix cubeFaceMatrix;
                switch (faceId)
                {
                case CubeMapFace.PositiveX:
                    cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(1, 0, 0), new Vector3(0, 1, 0));
                    break;

                case CubeMapFace.NegativeX:
                    cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(-1, 0, 0), new Vector3(0, 1, 0));
                    break;

                case CubeMapFace.PositiveY:
                    cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(0, 1, 0), new Vector3(0, 0, 1));
                    break;

                case CubeMapFace.NegativeY:
                    cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(0, -1, 0), new Vector3(0, 0, -1));
                    break;

                case CubeMapFace.PositiveZ:
                    cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(0, 0, -1), new Vector3(0, 1, 0));
                    break;

                case CubeMapFace.NegativeZ:
                    cubeFaceMatrix = Matrix.CreateLookAt(Vector3.Zero, new Vector3(0, 0, 1), new Vector3(0, 1, 0));
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
                Color[] colorArray = new Color[cubeMap.Size * cubeMap.Size];
                cubeMap.Resource.GetData <Color>(faceId, colorArray);

                // Extract the spherical harmonic for this face and accumulate it.
                sh += ExtractSphericalHarmonicForCubeFace(cubeFaceMatrix, colorArray, cubeMap.Size, cubeMap.IsRgbm, cubeMap.RgbmMaxRange);
            }

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

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

            // 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++)
            {
                SphericalHarmonicL1 lineSh = new SphericalHarmonicL1();
                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;

                    //normalise:
                    direction.X *= weight;
                    direction.Y *= weight;
                    direction.Z *= weight;

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

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

            return(sh);
        } // ExtractSphericalHarmonicForCubeFace