/// <summary> /// Update tile map based on current samples. /// </summary> public void UpdateTileMapData() { // Create tileMap array if not present if (TileMap == null) { TileMap = new Color32[tilemapDimension * tilemapDimension]; } // Also recreate if not sized appropriately if (TileMap.Length != tilemapDimension * tilemapDimension) { TileMap = new Color32[tilemapDimension * tilemapDimension]; } // Assign tile data to tilemap Color32 tileColor = new Color32(0, 0, 0, 0); for (int y = 0; y < tilemapDimension; y++) { for (int x = 0; x < tilemapDimension; x++) { // Get sample tile data TilemapSample sample = MapData.tilemapSamples[x, y]; // Calculate tile index byte record = (byte)(sample.record * 4); if (sample.rotate && !sample.flip) { record += 1; } if (!sample.rotate && sample.flip) { record += 2; } if (sample.rotate && sample.flip) { record += 3; } // Assign to tileMap tileColor.r = record; TileMap[y * tilemapDimension + x] = tileColor; } } }
// Drops nature flats based on random chance scaled by simple rules public static void LayoutNatureBillboards(DaggerfallTerrain dfTerrain, DaggerfallBillboardBatch dfBillboardBatch, float terrainScale) { const float maxSteepness = 50f; // 50 const float baseChanceOnDirt = 0.2f; // 0.2 const float baseChanceOnGrass = 0.9f; // 0.4 const float baseChanceOnStone = 0.05f; // 0.05 // Location Rect is expanded slightly to give extra clearance around locations const int natureClearance = 4; Rect rect = dfTerrain.MapData.locationRect; if (rect.x > 0 && rect.y > 0) { rect.xMin -= natureClearance; rect.xMax += natureClearance; rect.yMin -= natureClearance; rect.yMax += natureClearance; } // Chance scaled based on map pixel height // This tends to produce sparser lowlands and denser highlands // Adjust or remove clamp range to influence nature generation float elevationScale = (dfTerrain.MapData.worldHeight / 128f); elevationScale = Mathf.Clamp(elevationScale, 0.4f, 1.0f); // Chance scaled by base climate type float climateScale = 1.0f; DFLocation.ClimateSettings climate = MapsFile.GetWorldClimateSettings(dfTerrain.MapData.worldClimate); switch (climate.ClimateType) { case DFLocation.ClimateBaseType.Desert: // Just lower desert for now climateScale = 0.25f; break; } float chanceOnDirt = baseChanceOnDirt * elevationScale * climateScale; float chanceOnGrass = baseChanceOnGrass * elevationScale * climateScale; float chanceOnStone = baseChanceOnStone * elevationScale * climateScale; int heightmapDimension = DaggerfallUnity.Instance.TerrainSampler.HeightmapDimension; // Get terrain Terrain terrain = dfTerrain.gameObject.GetComponent <Terrain>(); if (!terrain) { return; } // Get terrain data TerrainData terrainData = terrain.terrainData; if (!terrainData) { return; } // Remove exiting billboards dfBillboardBatch.Clear(); MeshReplacement.ClearNatureGameObjects(terrain); // Seed random with terrain key Random.InitState(MakeTerrainKey(dfTerrain.MapPixelX, dfTerrain.MapPixelY)); // Just layout some random flats spread evenly across entire map pixel area // Flats are aligned with tiles, max 16129 billboards per batch Vector2 tilePos = Vector2.zero; int dim = MapsFile.WorldMapTileDim; float scale = terrainData.heightmapScale.x * (float)heightmapDimension / (float)dim; for (int y = 0; y < dim; y++) { for (int x = 0; x < dim; x++) { // Reject based on steepness float steepness = terrainData.GetSteepness((float)x / dim, (float)y / dim); if (steepness > maxSteepness) { continue; } // Reject if inside location rect // Rect is expanded slightly to give extra clearance around locations tilePos.x = x; tilePos.y = y; if (rect.Contains(tilePos)) { continue; } // Chance also determined by tile type TilemapSample sample = dfTerrain.MapData.tilemapSamples[x, y]; if (sample.record == 1) { // Dirt if (UnityEngine.Random.Range(0f, 1f) > chanceOnDirt) { continue; } } else if (sample.record == 2) { // Grass if (UnityEngine.Random.Range(0f, 1f) > chanceOnGrass) { continue; } } else if (sample.record == 3) { // Stone if (UnityEngine.Random.Range(0f, 1f) > chanceOnStone) { continue; } } else { // Anything else continue; } // Sample height and position billboard Vector3 pos = new Vector3(x * scale, 0, y * scale); float height = terrain.SampleHeight(pos + terrain.transform.position); pos.y = height; // Reject if too close to water float beachLine = DaggerfallUnity.Instance.TerrainSampler.BeachElevation * terrainScale; if (height < beachLine) { continue; } // Add to batch int record = UnityEngine.Random.Range(1, 32); if (!MeshReplacement.ImportNatureGameObject(dfBillboardBatch.TextureArchive, record, terrain, x, y)) { dfBillboardBatch.AddItem(record, pos); } } } // Apply new batch dfBillboardBatch.Apply(); }