public TerrainData(TiledTerrainConfig config, BinaryReader reader) { Config = config; Reader = reader; Heights = new float[Config.PatchHeightRes, Config.PatchHeightRes]; Normals = new Vector3[Config.PatchHeightRes, Config.PatchHeightRes]; Splats = new Vector2[Config.PatchSplatRes, Config.PatchSplatRes]; }
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 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(); } }