/// <summary>
        /// Gets a 3x3 square surrounded by a border so a blur kernel can be applied correctly
        /// The center 'pixel' will represent the biome value for this whole chunk
        /// </summary>
        /// <param name="coord"></param>
        /// <param name="worldSettings"></param>
        /// <param name="extraBorderSize"></param>
        /// <returns></returns>
        private static Region GetBiomeMap(Vector2 coord, WorldSettings worldSettings, out int extraBorderSize)
        {
            // Based on max edge-smoothing kernel width, determine how many extra points we need to grab outside of this area to calculate smoothing correctly at edge of chunk
            float worldSmoothing = worldSettings.globalBiomeSettings.heightMapEdgeSmoothing;
            float maxSmoothing   = worldSmoothing;

            foreach (BiomeSettings biome in worldSettings.biomes)
            {
                float biomeSmoothing = worldSmoothing * biome.heightMapEdgeSmoothingModifier;
                if (biomeSmoothing > maxSmoothing)
                {
                    maxSmoothing = biomeSmoothing;
                }
            }
            float[] maxBlurKernel = StandardDeviation.GetKernel(maxSmoothing);
            extraBorderSize = maxBlurKernel.Length;// / 2;

            int     biomeRegionSize       = 3 + (2 * extraBorderSize);
            Vector2 biomeRegionStartPoint = new Vector2(coord.x - 1 - extraBorderSize, coord.y - 1 - extraBorderSize);

            return(worldSettings.biomeMapSettings == null ? new Region(new float[biomeRegionSize, biomeRegionSize], 0, 0) : RegionGenerator.GenerateRegion(biomeRegionSize, biomeRegionSize, worldSettings.biomeMapSettings, biomeRegionStartPoint));
        }
        private static float[,] CalculateBlurredBiomeMask(float[,] biomeMapValues, int width, int height, float biomeMapValue, float deviation)//, string debug = "")
        {
            // Get a mask for just this biome ID
            float[,] biomeMask = Matrix.ValueFilter(biomeMapValues, (val, x, y) => Mathf.Approximately(val, biomeMapValue) ? 1f : 0f);
            if (deviation < blurDeviationLowerThreshold)
            {
                return(biomeMask);
            }
            float[] blurKernel = StandardDeviation.GetKernel(deviation);

            /*float[,] gaussian = new float[,]
             * {
             *  {0.0625f,0.125f,0.0625f},
             *  {0.125f,0.25f,0.125f},
             *  {0.0625f,0.125f,0.0625f},
             * };*/
            int kernelOffset = blurKernel.Length / 2;

            // Blur the center 3x3 'pixels'
            int xCenter = biomeMask.GetLength(0) / 2;
            int xLeft   = xCenter - 1;
            int xRight  = xCenter + 1;
            int yCenter = biomeMask.GetLength(1) / 2;
            int yBottom = yCenter - 1;
            int yTop    = yCenter + 1;

            float[,] blurredMask = Convolution.Symmetric(biomeMask, blurKernel, xLeft - kernelOffset, xRight + kernelOffset, yBottom - kernelOffset, yTop + kernelOffset);

            // From surrounding 'pixel' values, get values to build gradient from the center
            float centerVal            = blurredMask[xCenter, yCenter];
            float topLeftCornerVal     = (centerVal + blurredMask[xLeft, yCenter] + blurredMask[xLeft, yTop] + blurredMask[xCenter, yTop]) * 0.25f;
            float topRightCornerVal    = (centerVal + blurredMask[xCenter, yTop] + blurredMask[xRight, yTop] + blurredMask[xRight, yCenter]) * 0.25f;
            float bottomLeftCornerVal  = (centerVal + blurredMask[xCenter, yBottom] + blurredMask[xLeft, yBottom] + blurredMask[xLeft, yCenter]) * 0.25f;
            float bottomRightCornerVal = (centerVal + blurredMask[xRight, yCenter] + blurredMask[xRight, yBottom] + blurredMask[xCenter, yBottom]) * 0.25f;

            /*
             * if (debug != "")
             * {
             *  float val = biomeMapValues[biomeMapValues.GetLength(0) / 2, biomeMapValues.GetLength(1) / 2];
             *  Debug.Log(debug + "worldBiome: " + val + ", biome " + biomeMapValue + " center: " + centerVal + " topLeft: " + blurredMask[xLeft, yTop] + " topRight: " + blurredMask[xRight, yTop] + " bottomLeft: " + blurredMask[xLeft, yBottom] + " bottomRight: " + blurredMask[xRight, yBottom]);
             * }*/

            // Generate gradient map for this chunk
            float[,] gradientMap = new float[width, height];
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    // Outside vertices are out-of-mesh, need to reach edge values one vertex in
                    float yPct = (y - 1) / (float)(height - 3);
                    float xPct = (x - 1) / (float)(width - 3);
                    // @TODO: fix outside edge values to fix vertex normals

                    // Get gradient value for horizontal, vertical, and diagonal axes
                    float xyGradientAmount = xPct + yPct - 1f;
                    float yxGradientAmount = yPct - xPct;

                    float topLeftCornerWeight     = Mathf.Max(0, yxGradientAmount);
                    float topRightCornerWeight    = Mathf.Max(0, xyGradientAmount);
                    float bottomRightCornerWeight = Mathf.Max(0, -yxGradientAmount);
                    float bottomLeftCornerWeight  = Mathf.Max(0, -xyGradientAmount);
                    float centerWeight            = Mathf.Clamp01(1 - (topLeftCornerWeight + topRightCornerWeight + bottomRightCornerWeight + bottomLeftCornerWeight));

                    gradientMap[x, y] = (centerVal * centerWeight) + (topLeftCornerVal * topLeftCornerWeight) + (topRightCornerVal * topRightCornerWeight) + (bottomRightCornerVal * bottomRightCornerWeight) + (bottomLeftCornerVal * bottomLeftCornerWeight);
                }
            }

            return(gradientMap);
        }