/// <summary> /// Update tile map data based on current map pixel data. /// </summary> public void UpdateTileMapData() { // Create tileMap array if not present if (tileMap == null) { tileMap = new Color32[tileMapDim * tileMapDim]; } // Also recreate if not sized appropriately if (tileMap.Length != tileMapDim * tileMapDim) { tileMap = new Color32[tileMapDim * tileMapDim]; } // Assign tile data to tilemap Color32 tileColor = new Color32(0, 0, 0, 0); for (int y = 0; y < tileMapDim; y++) { for (int x = 0; x < tileMapDim; x++) { // Get sample tile data WorldSample sample = MapData.samples[y * TerrainHelper.terrainSampleDim + x]; // 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 * tileMapDim + 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 chanceOnDirt = 0.2f; // 0.2 const float chanceOnGrass = 0.9f; // 0.4 const float chanceOnStone = 0.05f; // 0.05 // 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(); // Seed random with terrain key UnityEngine.Random.seed = MakeTerrainKey(dfTerrain.MapPixelX, dfTerrain.MapPixelY); // Just layout some random flats spread evenly across entire map pixel area // Flats are aligned with tiles, max 127x127 in billboard batch Vector2 tilePos = Vector2.zero; float scale = terrainData.heightmapScale.x; int dim = TerrainHelper.terrainTileDim - 1; 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; const int natureClearance = 4; Rect rect = dfTerrain.MapData.locationRect; if (rect.x > 0 && rect.y > 0) { rect.xMin -= natureClearance; rect.xMin += natureClearance; rect.yMin -= natureClearance; rect.yMax += natureClearance; if (rect.Contains(tilePos)) { continue; } } // 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; } // Chance also determined by tile type WorldSample sample = TerrainHelper.GetSample(ref dfTerrain.MapData.samples, x, y); if (sample.record == 1) { // Dirt if (UnityEngine.Random.Range(0f, 1f) > chanceOnDirt * elevationScale * climateScale) { continue; } } else if (sample.record == 2) { // Grass if (UnityEngine.Random.Range(0f, 1f) > chanceOnGrass * elevationScale * climateScale) { continue; } } else if (sample.record == 3) { // Stone if (UnityEngine.Random.Range(0f, 1f) > chanceOnStone * elevationScale * climateScale) { 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); dfBillboardBatch.AddItem(record, pos); } } // Apply new batch dfBillboardBatch.Apply(); }
/// <summary> /// Set height value at coordinates. /// </summary> public static void SetHeight(ref WorldSample[] samples, int x, int y, float height) { samples[y * terrainSampleDim + x].scaledHeight = height; }
/// <summary> /// Set clamped height value at coordinates. /// </summary> public static void SetClampedHeight(ref WorldSample[] samples, int x, int y, float height) { x = Mathf.Clamp(x, 0, terrainTileDim - 1); y = Mathf.Clamp(y, 0, terrainTileDim - 1); samples[y * terrainSampleDim + x].scaledHeight = height; }
/// <summary> /// Gets sample data at coordinate. /// </summary> public static WorldSample GetSample(ref WorldSample[] samples, int x, int y) { return samples[y * terrainSampleDim + x]; }
/// <summary> /// Get height value at coordinates. /// </summary> public static float GetHeight(ref WorldSample[] samples, int x, int y) { return samples[y * terrainSampleDim + x].scaledHeight; }
/// <summary> /// Get clamped height value at coordinates. /// </summary> public static float GetClampedHeight(ref WorldSample[] samples, int x, int y) { x = Mathf.Clamp(x, 0, terrainTileDim - 1); y = Mathf.Clamp(y, 0, terrainTileDim - 1); return samples[y * terrainSampleDim + x].scaledHeight; }