/// <summary>
        /// Static import of terrain files into project/scene. No streaming.
        /// </summary>
        /// <param name="folderPath"></param>
        public void ImportFolderIntoScene(int lodLevel, TaskProgressToken token)
        {
            /* Todo:
             * - Unify more code with the streaming system (i.e. this terrain is streamed once at startup, but same code)
             * - Allow iterative work flow (i.e. just reimporting splatmaps, not everything)
             * - support running batches, parallel processing
             */

            Debug.Log("Importing lod level: " + lodLevel);

            try {
                var lod = ImportCfg.LodLevels[lodLevel];

                List <string> heightmapFiles = FilterFolder(lod.FolderPath, ImportCfg.HeightmapTag, ImportCfg.HeightmapExtention);

                var terrainDatas = CreateTerrainDatas(lod.FolderPath, heightmapFiles, lod);
                if (heightmapFiles == null || heightmapFiles.Count == 0)
                {
                    throw new ArgumentException(string.Format("No heightmaps found in folder after filtering: " + lod.FolderPath));
                }
                ProcessHeightmapAssets(heightmapFiles, terrainDatas, lod, token);

                List <string> splatmapFiles = FilterFolder(lod.FolderPath, ImportCfg.SplatmapTag, ImportCfg.SplatmapExtention);
                if (splatmapFiles == null || splatmapFiles.Count != heightmapFiles.Count)
                {
                    throw new ArgumentException(string.Format("The number of splatmaps {0} does not match the number of heightmaps {1}.", splatmapFiles.Count, heightmapFiles.Count));
                }
                ProcessSplatmapAssets(splatmapFiles, terrainDatas, token);

                if (lod.HasDetailMap)
                {
                    List <string> detailmapFiles = FilterFolder(lod.FolderPath, ImportCfg.DetailmapTag, ImportCfg.SplatmapExtention);
                    if (detailmapFiles == null || detailmapFiles.Count != heightmapFiles.Count)
                    {
                        throw new ArgumentException(string.Format("The number of detailmaps {0} does not match the number of heightmaps {1}.", detailmapFiles.Count, heightmapFiles.Count));
                    }
                    ProcessDetailmapAssets(detailmapFiles, terrainDatas, token);
                }

                if (lod.HasTreeMap)
                {
                    List <string> treemapFiles = FilterFolder(lod.FolderPath, ImportCfg.TreemapTag, ImportCfg.SplatmapExtention);
                    if (treemapFiles == null || treemapFiles.Count != heightmapFiles.Count)
                    {
                        throw new ArgumentException(string.Format("The number of treemaps {0} does not match the number of heightmaps {1}.", treemapFiles.Count, heightmapFiles.Count));
                    }
                    ProcessTreemapAssets(treemapFiles, terrainDatas, token);
                }

                var terrains = InstantiateTerrainInScene(heightmapFiles, terrainDatas, lod);

                SetupLandmassUnityTerrain(terrains, lod, CustomShaderId, MaterialPath);
            }
            catch (Exception e) {
                Debug.LogError("Failed to import terrains into scene.");
                Debug.LogException(e);
            }

            IsProcessing = false;
        }
        public void ProcessTreemapAssets(IList <string> assets, IList <TerrainData> terrainDatas, TaskProgressToken token)
        {
            IsProcessing = true;

            for (int i = 0; i < assets.Count; i++)
            {
                // Display progress bar with cancel button
                token.Progress = (i + 1) / (float)assets.Count;

                if (token.Cancel)
                {
                    break;
                }

                string      asset       = assets[i];
                TerrainData terrainData = terrainDatas[i];

                LandmasImporter.Instance.ApplyTreePrototypes(terrainData, TreePrototypes);

                Texture2D treemapTexture = AssetDatabase.LoadAssetAtPath(asset, typeof(Texture2D)) as Texture2D;
                LandmasImporter.ParseTreemapTexturesToTerrain(treemapTexture, terrainData);

                if (i % ImportCfg.BatchLimit == 0)
                {
                    FlushAssetsToDisk();
                }
            }

            FlushAssetsToDisk();

            IsProcessing = false;
        }
        public void ProcessSplatmapAssets(IList <string> assets, IList <TerrainData> terrainDatas, TaskProgressToken token)
        {
            LandmasImporter.ValidateSplatPrototypes(SplatPrototypes);

            IsProcessing = true;

            for (int i = 0; i < assets.Count; i++)
            {
                // Display progress bar with cancel button
                token.Progress = (i + 1) / (float)assets.Count;

                if (token.Cancel)
                {
                    break;
                }

                string      asset       = assets[i];
                TerrainData terrainData = terrainDatas[i];

                LandmasImporter.Instance.ApplySplatPrototypes(terrainData, SplatPrototypes);

                Texture2D splatmapTexture = AssetDatabase.LoadAssetAtPath(asset, typeof(Texture2D)) as Texture2D;
                var       splatmap        = new float[terrainData.alphamapResolution, terrainData.alphamapResolution, 4];
                LandmasImporter.TextureToSplatMap(splatmapTexture, ref splatmap, false, false);
                LandmasImporter.Instance.ParseSplatmapToTerrain(splatmap, terrainData);

                if (i % ImportCfg.BatchLimit == 0)
                {
                    FlushAssetsToDisk();
                }
            }

            FlushAssetsToDisk();

            IsProcessing = false;
        }
        // Todo: process detail maps (grass and such)
        public void ProcessDetailmapAssets(IList <string> assets, IList <TerrainData> terrainDatas, TaskProgressToken token)
        {
            IsProcessing = true;

            for (int i = 0; i < assets.Count; i++)
            {
                // Display progress bar with cancel button
                token.Progress = (i + 1) / (float)assets.Count;

                if (token.Cancel)
                {
                    break;
                }

                string      asset       = assets[i];
                TerrainData terrainData = terrainDatas[i];

                LandmasImporter.Instance.ApplyDetailPrototypes(terrainData, DetailPrototypes);

                Texture2D texture = AssetDatabase.LoadAssetAtPath(asset, typeof(Texture2D)) as Texture2D;
                int[][,] map = new int[DetailPrototypes.Count][, ];
                for (int j = 0; j < map.Length; j++)
                {
                    map[j] = new int[texture.width, texture.height];
                }
                LandmasImporter.TextureToDetailMap(texture, ref map, false, false);
                LandmasImporter.Instance.ParseDetailmapToTerrain(map, terrainData);

                if (i % ImportCfg.BatchLimit == 0)
                {
                    FlushAssetsToDisk();
                }
            }

            FlushAssetsToDisk();

            IsProcessing = false;
        }
        public void ProcessHeightmapAssets(IList <string> assets, IList <TerrainData> terrains, LodLevel lod, TaskProgressToken token)
        {
            if (IsProcessing)
            {
                return;
            }

            IsProcessing = true;

            token.Title = "Processing heightmaps...";

            // Todo: split out terrain data creation/retrieval, call it here to get List<TerrainData>, or something

            for (int i = 0; i < assets.Count; i++)
            {
                token.Progress = (float)(i + 1) / assets.Count;

                if (token.Cancel)
                {
                    break;
                }

                string heightmapPath = assets[i];

                TerrainData terrainData = terrains[i];
                var         heightData  = new float[lod.HeightmapResolution, lod.HeightmapResolution];
                LandmasImporter.LoadRawFileAsFloats(
                    heightmapPath,
                    ref heightData,
                    ImportCfg.HeightFormat,
                    false,
                    true);

                // For 8-bit PNGs
                //var heightTexture = LandmasImporter.LoadTexture(heightmapPath, ImportCfg.HeightmapResolution);
                //LandmasImporter.TextureToHeightMap(heightTexture, ref heightData, false, true);

                terrainData.SetHeights(0, 0, heightData);

                // Save assets to disk and flush them from memory
                if (i % ImportCfg.BatchLimit == 0)
                {
                    FlushAssetsToDisk();
                }
            }

            FlushAssetsToDisk();

            IsProcessing = false;
        }