예제 #1
0
    public static void MakeTerrainsPoTSize()
    {
        Debug.Log("Make Terrains POT");

        var tiles = LandmasImporter.FindTerrainTilesInScene();

        const float lod0Size      = 1024f;
        const float lod1Size      = 8192;
        const float lod0HalfWorld = 8f * lod0Size / 2f;
        const float lod1HalfWorld = 3f * lod1Size / 2f;

        foreach (var lodGroup in tiles)
        {
            if (lodGroup.Key == 0)
            {
                foreach (var regionTile in lodGroup.Value)
                {
                    Vector3 pos = new Vector3(regionTile.Key.X * lod0Size - lod0HalfWorld, 0f, regionTile.Key.Z * lod0Size - lod0HalfWorld);
                    regionTile.Value.transform.position       = pos;
                    regionTile.Value.Terrain.terrainData.size = new Vector3(lod0Size, regionTile.Value.Terrain.terrainData.size.y, lod0Size);
                }
            }

            if (lodGroup.Key == 1)
            {
                foreach (var regionTile in lodGroup.Value)
                {
                    Vector3 pos = new Vector3(regionTile.Key.X * lod1Size - lod1HalfWorld, 0f, regionTile.Key.Z * lod1Size - lod1HalfWorld);
                    regionTile.Value.transform.position       = pos;
                    regionTile.Value.Terrain.terrainData.size = new Vector3(lod1Size, regionTile.Value.Terrain.terrainData.size.y, lod1Size);
                }
            }
        }
    }
        public static void ApplyTreemaps()
        {
            var tiles = LandmasImporter.FindTerrainTilesInScene();

            foreach (var lodLevel in tiles)
            {
                var lod = Instance.ImportCfg.LodLevels[lodLevel.Key];

                foreach (var pair in lodLevel.Value)
                {
                    var region = pair.Key;

                    int    index       = (int)(region.X + (lod.GridSize - 1 - region.Z) * lod.GridSize);
                    string treemapName = string.Format("terrain_t_{0:00}.png", index);
                    var    treeMap     = AssetDatabase.LoadAssetAtPath(UPath.Combine(lod.FolderPath, treemapName), typeof(Texture2D)) as Texture2D;
                    if (treeMap == null)
                    {
                        Debug.LogError("Couldn't find treemap: " + treemapName);
                        continue;
                    }

                    LandmasImporter.ParseTreemapTexturesToTerrain(treeMap, pair.Value.Terrain.terrainData);
                }
            }
        }
        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 static void SetupMaterialsDefault()
        {
            var tiles = LandmasImporter.FindTerrainTilesInScene();

            foreach (var lodLevel in tiles)
            {
                SetupLandmassUnityTerrain(lodLevel.Value, Instance.ImportCfg.LodLevels[lodLevel.Key], DefaultShaderId, MaterialPath);
            }
        }
예제 #5
0
    private void Initialize()
    {
        if (_tilesList != null)
        {
            return;
        }

        _config = new TiledTerrainConfig(
            _lod0NumTiles,
            _lod0TileSize,
            _lod0PatchSize,
            _lod0HeightResolution,
            _lod0SplatResolution,
            _terrainHeight);

        _trees = new List <TreeLODHelper>(10000);
        _treeQualitySettings = new Dictionary <int, TreeLODConfiguration>()
        {
            { 0, new TreeLODConfiguration(false, 1.0f, 0.75f, 0.5f, 0.02f) },
            { 1, new TreeLODConfiguration(false, 0.75f, 0.5f, 0.33f, 0.01f) },
            { 2, new TreeLODConfiguration(false, 0.5f, 0.25f, 0.12f, 0.005f) },
            { 3, new TreeLODConfiguration(true, 0.33f, 0.15f, 0.05f, 0.0025f) }
        };

        _tiles     = LandmasImporter.FindTerrainTilesInScene();
        _tilesList = new List <TerrainTile>(128);

        foreach (var lodGroup in _tiles)
        {
            foreach (var regionTile in lodGroup.Value)
            {
                _tilesList.Add(regionTile.Value);

                LandmasImporter.SetNeighbours(regionTile.Key, lodGroup.Value);

                if (Application.isPlaying && _generateTreeColliders)
                {
                    CreateTrees(regionTile.Value);
                    CreateTreeColliders(regionTile.Value);
                }
            }
        }

        _lod0Grid = new TerrainTile[8, 8];
        for (int x = 0; x < 8; x++)
        {
            for (int z = 0; z < 8; z++)
            {
                _lod0Grid[x, z] = _tiles[0][new IntVector3(x, 0, z)];
            }
        }
    }
        public static void PasteDetails()
        {
            var tiles = LandmasImporter.FindTerrainTilesInScene();

            foreach (var lodLevel in tiles)
            {
                foreach (var tile in lodLevel.Value)
                {
                    var terrainData = tile.Value.Terrain.terrainData;
                    terrainData.detailPrototypes = CopiedDetailPrototypes;
                }
            }
        }
        public IDictionary <IntVector3, TerrainTile> InstantiateTerrainInScene(IList <string> assets, IList <TerrainData> terrainDatas, LodLevel lod)
        {
            var terrains = new Dictionary <IntVector3, TerrainTile>();

            var existingTerrains = Object.FindObjectsOfType <TerrainTile>();

            for (int i = 0; i < existingTerrains.Length; i++)
            {
                var tile = existingTerrains[i];
                if (tile.LodLevel == lod.Level)
                {
                    Object.DestroyImmediate(tile.gameObject);
                }
            }

            float   halfGridWidth = (float)lod.GridSize * lod.TerrainWidth * 0.5f;
            Vector3 offset        = new Vector3(-halfGridWidth, 0f, -halfGridWidth);

            for (int i = 0; i < terrainDatas.Count; i++)
            {
                string asset = assets[i];

                string  name     = GetTerrainNameFromAsset(asset);
                var     region   = LandmasImporter.GetTerrainRegionFromName(name, lod.GridSize);
                Vector3 position = offset + LandmasImporter.GetTerrainPosition(region, lod.TerrainWidth);

                TerrainData terrainData = terrainDatas[i];
                Terrain     terrain     = new GameObject(name).AddComponent <Terrain>();
                terrain.terrainData = terrainData;
                if (lod.Level < 1)
                {
                    terrain.gameObject.AddComponent <TerrainCollider>().terrainData = terrainData;
                }
                terrain.gameObject.isStatic = true;
                terrain.gameObject.name     = name + "_lod" + lod.Level;
                terrain.transform.position  = position;

                LandmasImporter.ApplyTerrainQualitySettings(terrain, ImportCfg.TerrainConfiguration);

                terrain.Flush();

                var tile = terrain.gameObject.AddComponent <TerrainTile>();
                tile.Region   = region;
                tile.LodLevel = lod.Level;
                tile.Terrain  = terrain;

                terrains.Add(region, tile);
            }

            return(terrains);
        }
        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;
        }
        public static void SetupMaterials()
        {
            if (!Directory.Exists(UPath.GetAbsolutePath(MaterialPath)))
            {
                Directory.CreateDirectory(MaterialPath);
            }

            var tiles = LandmasImporter.FindTerrainTilesInScene();

            foreach (var lodLevel in tiles)
            {
                SetupLandmassUnityTerrain(lodLevel.Value, Instance.ImportCfg.LodLevels[lodLevel.Key], CustomShaderId, MaterialPath);
            }
        }
예제 #10
0
    public void ApplyTerrainQualitySettings(TerrainConfiguration configuration)
    {
        Initialize();

        foreach (var lodGroup in _tiles)
        {
            foreach (var regionTile in lodGroup.Value)
            {
                LandmasImporter.ApplyTerrainQualitySettings(regionTile.Value.Terrain, configuration);
            }
        }

        _terrainConfiguration = configuration;
    }
        // 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 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;
        }
    void ShowSingleImport()
    {
        ImporterConfiguration config = LandmassEditorUtilities.Instance.ImportCfg;

        EGL.BeginVertical();
        {
            EGL.BeginVertical(GuiUtils.Skin.box);
            {
                GUILayout.Label("Target Terrain");
                _singleTargetTerrain = EGL.ObjectField(_singleTargetTerrain, typeof(Terrain), true, GUILayout.Width(240f)) as Terrain;
            }
            EGL.EndVertical();

            EGL.Separator();

            EGL.BeginVertical(GuiUtils.Skin.box);
            {
                /* ----- Heightmap ----- */

                GUILayout.Label("Heightmap");

                GUILayout.BeginHorizontal();
                {
                    EGL.LabelField("Path", _singleHeightmapPath);
                    if (GUILayout.Button("Browse", GUILayout.Width(60f)))
                    {
                        _singleHeightmapPath = EditorUtility.OpenFilePanel("Browse to Heightmap file",
                                                                           _singleHeightmapPath, "r16");
                    }
                }
                GUILayout.EndHorizontal();

                GUI.enabled = _singleHeightmapPath != "";
                {
                    if (GUILayout.Button("Apply"))
                    {
                        LandmasImporter.ParseHeightmapFileToTerrain(
                            _singleHeightmapPath,
                            _singleTargetTerrain.terrainData,
                            config.HeightFormat,
                            config.HeightmapFlipX,
                            config.HeightmapFlipY
                            );
                    }
                }
                GUI.enabled = true;
            }
            EGL.EndVertical();

            EGL.Separator();

            EGL.BeginVertical(GuiUtils.Skin.box);
            {
                /* ----- Splatmaps ------ */

                GUILayout.Label("Splatmap");
                _singleSplatmap = EGL.ObjectField(_singleSplatmap, typeof(Texture2D), false, GUILayout.Width(100f), GUILayout.Height(100f)) as Texture2D;

                EGL.Separator();

                GUI.enabled = _singleSplatmap != null && _singleTargetTerrain != null;
                {
                    if (GUILayout.Button("Apply"))
                    {
                        var splatmap = new float[_singleSplatmap.width, _singleSplatmap.height, 4];
                        LandmasImporter.TextureToSplatMap(
                            _singleSplatmap,
                            ref splatmap,
                            false,
                            true);

                        LandmasImporter.Instance.NormalizeSplatmap(ref splatmap, config.NormalizationMode);
                        LandmasImporter.Instance.ParseSplatmapToTerrain(splatmap, _singleTargetTerrain.terrainData);
                    }
                }
                GUI.enabled = true;
            }
            EGL.EndVertical();

            EGL.Separator();

            EGL.BeginVertical(GuiUtils.Skin.box);
            {
                /* ------ Treemaps ----- */

                GUILayout.Label("Treemap");
                _singleTreemap = EGL.ObjectField(_singleTreemap, typeof(Texture2D), false, GUILayout.Width(100f), GUILayout.Height(100f)) as Texture2D;

                GUI.enabled = _singleTreemap != null;
                {
                    if (GUILayout.Button("Apply"))
                    {
                        LandmasImporter.ParseTreemapTexturesToTerrain(_singleTreemap, _singleTargetTerrain.terrainData);
                    }
                }
                GUI.enabled = true;

                EGL.Separator();

                if (GUILayout.Button("Flush Terrain"))
                {
                    Terrain terrain = Selection.activeGameObject.GetComponent <Terrain>();
                    if (terrain)
                    {
                        Debug.Log("Flushing Terrain");
                        terrain.Flush();
                    }
                }
            }
            EGL.EndVertical();
        }
        EGL.EndVertical();
    }
예제 #14
0
    public static void ExportTerrain()
    {
        var cfg          = new TiledTerrainConfig(8, 1024, 32, 513, 512, 4000f); // Todo: get from global instead of hardcoding
        int totalPatches = cfg.NumTiles * cfg.PatchesPerTile;

        var lodTiles = LandmasImporter.FindTerrainTilesInScene();

        var path = UPath.GetAbsolutePath("Assets/Terrains/SwissAlps/swissalps.land");

        Debug.Log("Exporting terrain to: " + path);

        using (var writer = new BinaryWriter(File.Open(path, FileMode.Create))) {
            try {
                for (int x = 0; x < totalPatches; x++)
                {
                    for (int z = 0; z < totalPatches; z++)
                    {
                        // Find tile this patch is from
                        var tileIndex = new IntVector3(x / cfg.PatchesPerTile, 0, z / cfg.PatchesPerTile);
                        var tile      = lodTiles[0][tileIndex];

                        // Find out which pixels we need to read for this patch
                        var startPatchIndex = new IntVector2(tileIndex.X * cfg.PatchesPerTile, tileIndex.Z * cfg.PatchesPerTile);
                        var localPatchIndex = new IntVector2(x, z) - startPatchIndex;
                        var heightPixIndex  = new IntVector2(localPatchIndex.X * (cfg.PatchHeightRes - 1), localPatchIndex.Y * (cfg.PatchHeightRes - 1));
                        var splatPixIndex   = new IntVector2(localPatchIndex.X * cfg.PatchSplatRes, localPatchIndex.Y * cfg.PatchSplatRes);

                        // Sample PoT+1 blocks, so that each patch's edge overlaps the first edge of its neighbour
                        float[,] heights   = GetHeights(tile.Terrain.terrainData, heightPixIndex, cfg.PatchHeightRes);
                        Vector3[,] normals = GetNormals(tile.Terrain.terrainData, heightPixIndex, cfg.PatchHeightRes);
                        float[,,] splats   = GetSplats(tile.Terrain.terrainData, splatPixIndex, cfg.PatchSplatRes);

                        // Write heights
                        for (int xPixel = 0; xPixel < cfg.PatchHeightRes; xPixel++)
                        {
                            for (int zPixel = 0; zPixel < cfg.PatchHeightRes; zPixel++)
                            {
                                ushort val = (ushort)(heights[xPixel, zPixel] * 65000f);
                                writer.Write(val);
                            }
                        }

                        // Write normals
                        // Todo: calculate normals from loaded height values instead of caching them on disk
                        // Todo: single byte per axis, implicit 3rd axis

                        for (int xPixel = 0; xPixel < cfg.PatchHeightRes; xPixel++)
                        {
                            for (int zPixel = 0; zPixel < cfg.PatchHeightRes; zPixel++)
                            {
                                Vector3 normal = normals[xPixel, zPixel];
                                byte    valX   = (byte)((normal.x * 0.5f + 0.5f) * 250f);
                                byte    valY   = (byte)((normal.y * 0.5f + 0.5f) * 250f);
                                byte    valZ   = (byte)((normal.z * 0.5f + 0.5f) * 250f);
                                writer.Write(valX);
                                writer.Write(valY);
                                writer.Write(valZ);
                            }
                        }

                        // Write splats (todo: byte)
                        for (int xPixel = 0; xPixel < cfg.PatchSplatRes; xPixel++)
                        {
                            for (int zPixel = 0; zPixel < cfg.PatchSplatRes; zPixel++)
                            {
                                // Swizzled writes because Unity's terrain data is store wrong
                                byte valR = (byte)(splats[zPixel, xPixel, 0] * 250f); // Grass
                                byte valA = (byte)(splats[zPixel, xPixel, 3] * 250f); // Snow
                                writer.Write(valR);
                                writer.Write(valA);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                Debug.LogException(e);
            }

            writer.Close();
        }
    }