public DisplacementBufferGPU(int size, Shader fourierSdr)
            : base(size, fourierSdr, NUM_BUFFERS)
        {
            int GRIDS = QueryDisplacements.GRIDS;
            int CHANNELS = QueryDisplacements.CHANNELS;

            m_displacements = new InterpolatedArray2f[GRIDS];

            for (int i = 0; i < GRIDS; i++)
            {
                m_displacements[i] = new InterpolatedArray2f(size, size, CHANNELS, true);
            }
        }
        public static void CopyAndCreateDisplacements(IList<InterpolatedArray2f> source, out IList<InterpolatedArray2f> des)
        {
            if(source.Count != GRIDS)
                throw new InvalidOperationException("Query Displacements requires a displacement buffer for each of the " + GRIDS + " grids.");

            if(source[0].Channels != CHANNELS)
                throw new InvalidOperationException("Query Displacements requires displacement buffers have " + CHANNELS + " channels.");

            int size = source[0].SX;

            des = new InterpolatedArray2f[GRIDS];

            des[0] = new InterpolatedArray2f(source[0].Data, size, size, CHANNELS, true);
            des[1] = new InterpolatedArray2f(source[1].Data, size, size, CHANNELS, true);
            des[2] = new InterpolatedArray2f(source[2].Data, size, size, CHANNELS, true);
            des[3] = new InterpolatedArray2f(source[3].Data, size, size, CHANNELS, true);
        }
        public DisplacementBufferCPU(int size, Scheduler scheduler)
            : base(size, NUM_BUFFERS, scheduler)
        {
            int GRIDS = QueryDisplacements.GRIDS;
            int CHANNELS = QueryDisplacements.CHANNELS;

            m_displacements = new List<InterpolatedArray2f[]>(2);

            m_displacements.Add( new InterpolatedArray2f[GRIDS] );
            m_displacements.Add( new InterpolatedArray2f[GRIDS] );

            for (int i = 0; i < GRIDS; i++)
            {
                m_displacements[0][i] = new InterpolatedArray2f(size, size, CHANNELS, true);
                m_displacements[1][i] = new InterpolatedArray2f(size, size, CHANNELS, true);
            }
        }
        public static Texture2D CreateClipMask(InterpolatedArray2f heightMap, int width, int height, float shoreLevel, TextureFormat format)
        {
            Texture2D mask = new Texture2D(width, height, format, false, true);
            mask.filterMode = FilterMode.Bilinear;

            Color[] colors = new Color[width * height];

            bool matches = width == heightMap.SX && height == heightMap.SY;

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    int i = x + y * height;

                    float h = 0.0f;

                    if (matches)
                    {
                        h = Mathf.Clamp(heightMap.Data[i] - shoreLevel, 0.0f, 1.0f);
                    }
                    else
                    {
                        float fx = x / (width - 1.0f);
                        float fy = y / (height - 1.0f);
                        h = Mathf.Clamp(heightMap.Get(fx, fy, 0) - shoreLevel, 0.0f, 1.0f);
                    }

                    if (h > 0.0f) h = 1.0f;

                    colors[i].r = h;
                    colors[i].g = h;
                    colors[i].b = h;
                    colors[i].a = h;
                }
            }

            mask.SetPixels(colors);

            mask.Apply();

            return mask;
        }
        void ExportMask(float[] data, int size, int resolution, string name, DateTime stamp, float spread, bool isClip, bool isReadable)
        {
            //Create interpolated array from data so the mask resolution does not need to match the terrain height map size.
            InterpolatedArray2f heightMap = new InterpolatedArray2f(data, size, size, 1, false);

            //Create the path name to save mask.
            string fileName = string.Format("{0}-{1:yyyyMMdd-HHmmss}.png", name, stamp);
            string path = System.IO.Path.Combine(Application.dataPath, fileName);
            path = path.Replace('\\', '/');

            Texture2D mask = null;

            //Create the mask texture. Clip masks are created slightly different.
            if(isClip)
                mask = ShoreMaskGenerator.CreateClipMask(heightMap, resolution, resolution, m_oceanLevel + spread, TextureFormat.ARGB32);
            else
                mask = ShoreMaskGenerator.CreateMask(heightMap, resolution, resolution, m_oceanLevel, spread, TextureFormat.ARGB32);

            //Save the texture.
            byte[] bytes = mask.EncodeToPNG();
            System.IO.File.WriteAllBytes(path, bytes);

            DestroyImmediate(mask);

            string relativePath = "Assets/" + fileName;

            //Update asset data base with new mask texture.
            AssetDatabase.ImportAsset(relativePath, ImportAssetOptions.ForceUpdate);

            //Create a importer from the texture so some of its settings can be changed.
            AssetImporter tmp = TextureImporter.GetAtPath(relativePath);
            TextureImporter importer = tmp as TextureImporter;

            if (importer != null)
            {
                //Change some of the settings of textures.
                //Should always be bilinear clamped with no mipmaps.
                //Height and clip masks should also be readable.
                importer.wrapMode = TextureWrapMode.Clamp;
                importer.filterMode = FilterMode.Bilinear;
                importer.textureType = TextureImporterType.Advanced;
                importer.mipmapEnabled = false;
                importer.isReadable = isReadable;

                AssetDatabase.ImportAsset(relativePath, ImportAssetOptions.ForceUpdate);
            }
            else
            {
                string msg = string.Format("{0:MM/dd/yy H:mm:ss zzz} - Failed to modify texture settings after creation. You will need to manually adjust texture settings.", DateTime.Now);
                Ocean.LogInfo(msg);
            }
        }
        void CreateShoreMasks()
        {

            //float t = Time.realtimeSinceStartup;

            Release();

            Terrain terrain = GetComponent<Terrain>();

            if (terrain == null)
            {
                //If there gameobject has not terrain print a warning and return.
                //Do this rather than have a terrain as a required component as it would be
                //rather annoying for the script to create a terrain if added to wrong gameobject.
                Ocean.LogWarning("The AddAutoShoreMask script must be attached to a component with a Terrain. The shore mask will not be created.");
				enabled = false;
                return;
            }

			if (terrain.terrainData == null)
			{
				//This can happen if the terrain data in asset folder is deleted
				Ocean.LogWarning("The terrain data is null. The shore mask will not be created.");
				enabled = false;
				return;
			}

			Vector3 size = terrain.terrainData.size;

            resolution = Mathf.Clamp(resolution, 32, 4096);

            m_width = size.x;
			m_height = size.z;

            float level = Ocean.Instance.level;

            float[] data = ShoreMaskGenerator.CreateHeightMap(terrain);

            int actualResolution = terrain.terrainData.heightmapResolution;
            InterpolatedArray2f heightMap = new InterpolatedArray2f(data, actualResolution, actualResolution, 1, false);

            if(useHeightMask || useNormalMask || useFoamMask)
                m_heightMask = ShoreMaskGenerator.CreateMask(heightMap, resolution, resolution, level, heightSpread, TextureFormat.ARGB32);

            if(useEdgeFoam)
                m_edgeFoam = ShoreMaskGenerator.CreateMask(heightMap, resolution, resolution, level, foamSpread, TextureFormat.ARGB32);

            if (useClipMask)
                m_clipMask = ShoreMaskGenerator.CreateClipMask(heightMap, resolution, resolution, level + clipOffset, TextureFormat.ARGB32);

            if(useHeightMask)
                m_overlays[0].HeightTex.mask = m_heightMask;

            if(useNormalMask)
                m_overlays[0].NormalTex.mask = m_heightMask;

            if(useFoamMask)
                m_overlays[0].FoamTex.mask = m_heightMask;

            if(useEdgeFoam)
                m_overlays[0].FoamTex.tex = m_edgeFoam;

            if(useClipMask)
                m_overlays[0].ClipTex.tex = m_clipMask;

            if (!m_registered)
            {
                Ocean.Instance.OverlayManager.Add(m_overlays[0]);
                m_registered = true;
            }

            m_heightSpread = heightSpread;
            m_foamSpread = foamSpread;
            m_clipOffset = clipOffset;
            m_resolution = resolution;

            //Debug.Log("Shore mask creation time = " + (Time.realtimeSinceStartup - t) * 1000.0f);

        }