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); } }
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); } }
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(); }
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(); } }