public static string GetBitDepth(Heightmap.Depth depth)
        {
            switch (depth)
            {
            case Heightmap.Depth.Bit16:
                return("16 bit");

            case Heightmap.Depth.Bit8:
                return("8 bit");

            default:
                return("8 bit");
            }
        }
        public static void ExportTerrainHeightsToRawFile(TerrainData terrainData, string path, Heightmap.Depth depth, bool flipVertical, ByteOrder byteOrder, Vector2 inputLevelsRange)
        {
            // trim off the extra 1 pixel, so we get a power of two sized texture
#if UNITY_2019_3_OR_NEWER
            int heightmapWidth  = terrainData.heightmapResolution - 1;
            int heightmapHeight = terrainData.heightmapResolution - 1;
#else
            int heightmapWidth  = terrainData.heightmapWidth - 1;
            int heightmapHeight = terrainData.heightmapHeight - 1;
#endif
            float[,] heights = terrainData.GetHeights(0, 0, heightmapWidth, heightmapHeight);
            byte[] data = new byte[heightmapWidth * heightmapHeight * (int)depth];

            if (depth == Heightmap.Depth.Bit16)
            {
                float normalize = (1 << 16);
                for (int y = 0; y < heightmapHeight; ++y)
                {
                    for (int x = 0; x < heightmapWidth; ++x)
                    {
                        int index = x + y * heightmapWidth;
                        int srcY  = flipVertical ? heightmapHeight - 1 - y : y;

                        float remappedHeight = (heights[srcY, x] - inputLevelsRange.x) / (inputLevelsRange.y - inputLevelsRange.x);

                        int    height           = Mathf.RoundToInt(remappedHeight * normalize);
                        ushort compressedHeight = (ushort)Mathf.Clamp(height, 0, ushort.MaxValue);

                        byte[] byteData = System.BitConverter.GetBytes(compressedHeight);
                        if ((byteOrder == ByteOrder.Mac) == System.BitConverter.IsLittleEndian)
                        {
                            data[index * 2 + 0] = byteData[1];
                            data[index * 2 + 1] = byteData[0];
                        }
                        else
                        {
                            data[index * 2 + 0] = byteData[0];
                            data[index * 2 + 1] = byteData[1];
                        }
                    }
                }
            }
            else
            {
                float normalize = (1 << 8);
                for (int y = 0; y < heightmapHeight; ++y)
                {
                    for (int x = 0; x < heightmapWidth; ++x)
                    {
                        int  index            = x + y * heightmapWidth;
                        int  srcY             = flipVertical ? heightmapHeight - 1 - y : y;
                        int  height           = Mathf.RoundToInt(heights[srcY, x] * normalize);
                        byte compressedHeight = (byte)Mathf.Clamp(height, 0, byte.MaxValue);
                        data[index] = compressedHeight;
                    }
                }
            }

            FileStream fs = new FileStream((path + ".raw"), FileMode.Create);
            fs.Write(data, 0, data.Length);
            fs.Close();
        }
        /// <summary>
        /// This overloaded method deals specifically with testing the level correction of the raw format
        /// </summary>
        /// <param name="toolboxWindow">Window where the Export Heightmap Utilities live</param>
        /// <param name="minMaxRemap">Min and Max values of the remap</param>
        /// <param name="format">Heightmap File Format</param>
        /// <param name="path">String path of the files directory location</param>
        /// <param name="depth">Heightmap Bit Depth</param>
        /// <returns></returns>
        bool TestLevelCorrection(TerrainToolboxWindow toolboxWindow, Vector2 minMaxRemap, string path, Heightmap.Depth depth)
        {
            //Execute the repro steps in code
            toolboxWindow.m_TerrainUtilitiesMode.m_Settings.ExportHeightRemapMin = minMaxRemap.x;
            toolboxWindow.m_TerrainUtilitiesMode.m_Settings.ExportHeightRemapMax = minMaxRemap.y;

            toolboxWindow.m_TerrainUtilitiesMode.m_Settings.HeightFormat   = Heightmap.Format.RAW;
            toolboxWindow.m_TerrainUtilitiesMode.m_Settings.HeightmapDepth = depth;
            toolboxWindow.m_TerrainUtilitiesMode.m_SelectedDepth           = (depth == Heightmap.Depth.Bit16) ? 0 : 1;
            toolboxWindow.m_TerrainUtilitiesMode.ExportHeightmaps(new Object[] { m_TerrainComponent });

            //Get byte data of the terrain's heightmap
            TerrainData terrainData = m_TerrainComponent.terrainData;

#if UNITY_2019_3_OR_NEWER
            int heightmapWidth  = terrainData.heightmapResolution - 1;
            int heightmapHeight = terrainData.heightmapResolution - 1;
#else
            int heightmapWidth  = terrainData.heightmapWidth - 1;
            int heightmapHeight = terrainData.heightmapHeight - 1;
#endif
            float[,] heights = terrainData.GetHeights(0, 0, heightmapWidth, heightmapHeight);
            byte[] data = new byte[heightmapWidth * heightmapHeight * (int)depth];

            if (depth == Heightmap.Depth.Bit16)
            {
                float normalize = (1 << 16);
                for (int y = 0; y < heightmapHeight; ++y)
                {
                    for (int x = 0; x < heightmapWidth; ++x)
                    {
                        //Remapping the heightmap data
                        int   index          = x + y * heightmapWidth;
                        float remappedHeight = heights[y, x] * (minMaxRemap.y - minMaxRemap.x) + minMaxRemap.x;

                        int    height           = Mathf.RoundToInt(remappedHeight * normalize);
                        ushort compressedHeight = (ushort)Mathf.Clamp(height, 0, ushort.MaxValue);

                        byte[] byteData = System.BitConverter.GetBytes(compressedHeight);
                        if ((toolboxWindow.m_TerrainUtilitiesMode.m_Settings.HeightmapByteOrder == ToolboxHelper.ByteOrder.Mac) == System.BitConverter.IsLittleEndian)
                        {
                            data[index * 2 + 0] = byteData[1];
                            data[index * 2 + 1] = byteData[0];
                        }
                        else
                        {
                            data[index * 2 + 0] = byteData[0];
                            data[index * 2 + 1] = byteData[1];
                        }
                    }
                }
            }
            else
            {
                float normalize = (1 << 8);
                for (int y = 0; y < heightmapHeight; ++y)
                {
                    for (int x = 0; x < heightmapWidth; ++x)
                    {
                        //Remapping the heightmap data
                        int   index          = x + y * heightmapWidth;
                        float remappedHeight = heights[y, x] * (minMaxRemap.y - minMaxRemap.x) + minMaxRemap.x;

                        int  height           = Mathf.RoundToInt(remappedHeight * normalize);
                        byte compressedHeight = (byte)Mathf.Clamp(height, 0, byte.MaxValue);
                        data[index] = compressedHeight;
                    }
                }
            }

            //Compare both the original and regression test data
            byte[] rawByteData = File.ReadAllBytes(path);
            return(data.SequenceEqual(rawByteData));
        }
        public void TerrainToolboxUtilites_WhenExportHeightmap_LevelCorrectionWorks(float min, float max, Heightmap.Format format, Heightmap.Depth depth = Heightmap.Depth.Bit16)
        {
            TerrainToolboxWindow toolboxWindow   = EditorWindow.GetWindow(typeof(TerrainToolboxWindow)) as TerrainToolboxWindow;
            Texture2D            gradientTexture = CreateGradientTexture();

            int heightmapResolution = 513;
            int numberOfTiles       = 1;
            int baseLevel           = 0;
            int remapLevel          = 1;

            ToolboxHelper.CopyTextureToTerrainHeight(m_TerrainComponent.terrainData, gradientTexture, Vector2Int.zero, heightmapResolution, numberOfTiles, baseLevel, remapLevel);

            Selection.activeGameObject = m_TerrainGO;
            m_TerrainGO.name           = "TestTerrain";
            m_TerrainComponent.name    = "TestComponent";

            RenderTexture oldRT = RenderTexture.active;

            RenderTexture.active = m_TerrainComponent.terrainData.heightmapTexture;

            //Run Tests and Cleanup files
            string fileName = m_TerrainGO.name + "_heightmap";
            string path     = Path.Combine(toolboxWindow.m_TerrainUtilitiesMode.m_Settings.HeightmapFolderPath, fileName);

            switch (format)
            {
            case Heightmap.Format.PNG:
                path += ".png";
                Assert.IsTrue(TestLevelCorrection(toolboxWindow, new Vector2(min, max), path, format));
                FileUtil.DeleteFileOrDirectory(path);
                FileUtil.DeleteFileOrDirectory(path + ".meta");
                break;

            case Heightmap.Format.TGA:
                path += ".tga";
                Assert.IsTrue(TestLevelCorrection(toolboxWindow, new Vector2(min, max), path, format));
                FileUtil.DeleteFileOrDirectory(path);
                FileUtil.DeleteFileOrDirectory(path + ".meta");
                break;

            case Heightmap.Format.RAW:
                path += ".raw";
                Assert.IsTrue(TestLevelCorrection(toolboxWindow, new Vector2(min, max), path, depth));
                FileUtil.DeleteFileOrDirectory(path);
                FileUtil.DeleteFileOrDirectory(path + ".meta");
                break;
            }

            AssetDatabase.Refresh();
            RenderTexture.active = oldRT;
            toolboxWindow.Close();
        }