public override JobHandle ScheduleGenerateSamplesJob(ref MapPixelData mapPixel) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; // Divisor ensures continuous 0-1 range of tile samples float div = (float)(HeightmapDimension - 1) / 3f; // Read neighbouring height samples for this map pixel int mx = mapPixel.mapPixelX; int my = mapPixel.mapPixelY; // Seed random with terrain key UnityEngine.Random.InitState(TerrainHelper.MakeTerrainKey(mx, my)); byte[,] shm = dfUnity.ContentReader.WoodsFileReader.GetHeightMapValuesRange(mx - 2, my - 2, 4); byte[,] lhm = dfUnity.ContentReader.WoodsFileReader.GetLargeHeightMapValuesRange(mx - 1, my, 3); float[,] baseHeightValue = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); baseHeightValue[x, y] = shm[x, y] * ImprovedWorldTerrain.computeHeightMultiplier(mapPixelX, mapPixelY); } } float[,] waterMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { if (shm[x, y] <= 2) // mappixel is water { waterMap[x, y] = 0.0f; } else { waterMap[x, y] = 1.0f; } } } float[,] climateMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); climateMap[x, y] = GetNoiseMapScaleBasedOnClimate(mapPixelX, mapPixelY); } } float[,] waterDistanceMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); waterDistanceMap[x, y] = (float)Math.Sqrt(ImprovedWorldTerrain.MapDistanceSquaredFromWater[mapPixelY * WoodsFile.mapWidthValue + mapPixelX]); } } float[,] noiseHeightMultiplierMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { // interpolation multiplier taking near coast map pixels into account // (multiply with 0 at coast line and 1 at interpolationEndDistanceFromWaterForNoiseScaleMultiplier) float multFact = (Mathf.Min(interpolationEndDistanceFromWaterForNoiseScaleMultiplier, waterDistanceMap[x, y]) / interpolationEndDistanceFromWaterForNoiseScaleMultiplier); // blend watermap with climatemap taking into account multFact noiseHeightMultiplierMap[x, y] = waterMap[x, y] * climateMap[x, y] * multFact; } } float extraNoiseScaleBasedOnClimate = GetExtraNoiseScaleBasedOnClimate(mx, my); byte sDim = 4; NativeArray <float> baseHeightValueNativeArray = new NativeArray <float>(shm.Length, Allocator.TempJob); int i = 0; for (int y = 0; y < sDim; y++) { for (int x = 0; x < sDim; x++) { baseHeightValueNativeArray[i++] = baseHeightValue[x, y]; } } i = 0; NativeArray <float> noiseHeightMultiplierNativeArray = new NativeArray <float>(noiseHeightMultiplierMap.Length, Allocator.TempJob); for (int y = 0; y < sDim; y++) { for (int x = 0; x < sDim; x++) { noiseHeightMultiplierNativeArray[i++] = noiseHeightMultiplierMap[x, y]; } } // TODO - shortcut conversion & flattening. NativeArray <byte> lhmNativeArray = new NativeArray <byte>(lhm.Length, Allocator.TempJob); byte lDim = (byte)lhm.GetLength(0); i = 0; for (int y = 0; y < lDim; y++) { for (int x = 0; x < lDim; x++) { lhmNativeArray[i++] = lhm[x, y]; } } // Add the working native arrays to list for later disposal. mapPixel.nativeArrayList.Add(baseHeightValueNativeArray); mapPixel.nativeArrayList.Add(noiseHeightMultiplierNativeArray); mapPixel.nativeArrayList.Add(lhmNativeArray); // Extract height samples for all chunks int hDim = HeightmapDimension; GenerateSamplesJob generateSamplesJob = new GenerateSamplesJob { baseHeightValue = baseHeightValueNativeArray, lhm = lhmNativeArray, noiseHeightMultiplierMap = noiseHeightMultiplierNativeArray, heightmapData = mapPixel.heightmapData, sd = sDim, ld = lDim, hDim = hDim, div = div, mapPixelX = mapPixel.mapPixelX, mapPixelY = mapPixel.mapPixelY, maxTerrainHeight = MaxTerrainHeight, extraNoiseScaleBasedOnClimate = extraNoiseScaleBasedOnClimate, }; JobHandle generateSamplesHandle = generateSamplesJob.Schedule(hDim * hDim, 64); // Batch = 1 breaks it since shm not copied... test again later return(generateSamplesHandle); }
/// <summary> /// Add Grass and other details on terrain. /// </summary> private void AddTerrainDetails(DaggerfallTerrain daggerTerrain, TerrainData terrainData) { #if TEST_PERFORMANCE var stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Start(); #endif UnityEngine.Random.InitState(TerrainHelper.MakeTerrainKey(daggerTerrain.MapPixelX, daggerTerrain.MapPixelY)); // Terrain settings terrainData.SetDetailResolution(256, 8); terrainData.wavingGrassTint = Color.gray; Terrain terrain = daggerTerrain.gameObject.GetComponent <Terrain>(); terrain.detailObjectDistance = options.DetailObjectDistance; terrain.detailObjectDensity = options.DetailObjectDensity; // Get the current season and climate var currentSeason = DaggerfallUnity.Instance.WorldTime.Now.SeasonValue; ClimateBases climate = GetClimate(daggerTerrain.MapData.worldClimate); // Update detail layers Color32[] tilemap = daggerTerrain.TileMap; densityManager.InitDetailsLayers(); switch (climate) { case ClimateBases.Temperate: case ClimateBases.Mountain: case ClimateBases.Swamp: if (currentSeason != DaggerfallDateTime.Seasons.Winter) { detailPrototypesManager.UpdateClimateSummer(climate); densityManager.SetDensitySummer(terrain, tilemap, climate); } else { detailPrototypesManager.UpdateClimateWinter(climate); densityManager.SetDensityWinter(terrain, tilemap, climate); } break; case ClimateBases.Desert: detailPrototypesManager.UpdateClimateDesert(); densityManager.SetDensityDesert(tilemap); break; } // Assign detail prototypes to the terrain terrainData.detailPrototypes = detailPrototypesManager.DetailPrototypes; // Assign detail layers to the terrain terrainData.SetDetailLayer(0, 0, detailPrototypesManager.Grass, densityManager.Grass); if ((options.GrassStyle & GrassStyle.Mixed) == GrassStyle.Mixed && climate != ClimateBases.Desert) { terrainData.SetDetailLayer(0, 0, detailPrototypesManager.GrassDetails, densityManager.GrassDetails); terrainData.SetDetailLayer(0, 0, detailPrototypesManager.GrassAccents, densityManager.GrassAccents); } if (options.WaterPlants) { terrainData.SetDetailLayer(0, 0, detailPrototypesManager.WaterPlants, densityManager.WaterPlants); } if (options.TerrainStones) { terrainData.SetDetailLayer(0, 0, detailPrototypesManager.Rocks, densityManager.Rocks); } #if TEST_PERFORMANCE stopwatch.Stop(); Debug.LogFormat("RealGrass - Time elapsed: {0} ms.", stopwatch.Elapsed.Milliseconds); #endif }
public override void GenerateSamples(ref MapPixelData mapPixel) { //System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew(); //long startTime = stopwatch.ElapsedMilliseconds; DaggerfallUnity dfUnity = DaggerfallUnity.Instance; // Create samples arrays mapPixel.heightmapSamples = new float[HeightmapDimension, HeightmapDimension]; // Divisor ensures continuous 0-1 range of tile samples float div = (float)(HeightmapDimension - 1) / 3f; // Read neighbouring height samples for this map pixel int mx = mapPixel.mapPixelX; int my = mapPixel.mapPixelY; // Seed random with terrain key UnityEngine.Random.InitState(TerrainHelper.MakeTerrainKey(mx, my)); byte[,] shm = dfUnity.ContentReader.WoodsFileReader.GetHeightMapValuesRange(mx - 2, my - 2, 4); byte[,] lhm = dfUnity.ContentReader.WoodsFileReader.GetLargeHeightMapValuesRange(mx - 1, my, 3); float[,] baseHeightValue = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); baseHeightValue[x, y] = shm[x, y] * ImprovedWorldTerrain.computeHeightMultiplier(mapPixelX, mapPixelY); } } float[,] waterMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { if (shm[x, y] <= 2) // mappixel is water { waterMap[x, y] = 0.0f; } else { waterMap[x, y] = 1.0f; } } } float[,] climateMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); climateMap[x, y] = GetNoiseMapScaleBasedOnClimate(mapPixelX, mapPixelY); } } float[,] waterDistanceMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); waterDistanceMap[x, y] = (float)Math.Sqrt(ImprovedWorldTerrain.MapDistanceSquaredFromWater[mapPixelY * WoodsFile.mapWidthValue + mapPixelX]); } } float[,] noiseHeightMultiplierMap = new float[4, 4]; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { // interpolation multiplier taking near coast map pixels into account // (multiply with 0 at coast line and 1 at interpolationEndDistanceFromWaterForNoiseScaleMultiplier) float multFact = (Mathf.Min(interpolationEndDistanceFromWaterForNoiseScaleMultiplier, waterDistanceMap[x, y]) / interpolationEndDistanceFromWaterForNoiseScaleMultiplier); // blend watermap with climatemap taking into account multFact noiseHeightMultiplierMap[x, y] = waterMap[x, y] * climateMap[x, y] * multFact; } } //float[,] noiseHeightMultiplierMap = new float[4, 4]; //for (int y = 0; y < 4; y++) //{ // for (int x = 0; x < 4; x++) // { // int mapPixelX = Math.Max(0, Math.Min(mx + x - 2, WoodsFile.mapWidthValue)); // int mapPixelY = Math.Max(0, Math.Min(my + y - 2, WoodsFile.mapHeightValue)); // float climateValue = GetNoiseMapScaleBasedOnClimate(mapPixelX, mapPixelY); // float waterDistance = (float)Math.Sqrt(ImprovedWorldTerrain.MapDistanceSquaredFromWater[mapPixelY * WoodsFile.mapWidthValue + mapPixelX]); // float waterValue; // if (shm[x, y] <= 2) // mappixel is water // waterValue = 0.0f; // else // waterValue = 1.0f; // // interpolation multiplier taking near coast map pixels into account // // (multiply with 0 at coast line and 1 at interpolationEndDistanceFromWaterForNoiseScaleMultiplier) // float multFact = (Mathf.Min(interpolationEndDistanceFromWaterForNoiseScaleMultiplier, waterDistance) / interpolationEndDistanceFromWaterForNoiseScaleMultiplier); // // blend watermap with climatemap taking into account multFact // noiseHeightMultiplierMap[x, y] = waterValue * climateValue * multFact; // } //} //int numWorkerThreads = 0, completionPortThreads = 0; //int numMinWorkerThreads = 0, numMaxWorkerThreads = 0; //ThreadPool.GetAvailableThreads(out numWorkerThreads, out completionPortThreads); //ThreadPool.GetMinThreads(out numMinWorkerThreads, out completionPortThreads); //ThreadPool.GetMaxThreads(out numMaxWorkerThreads, out completionPortThreads); //Debug.Log(String.Format("available threads: {0}, numMinWorkerThreads: {1}, numMaxWorkerThreads: {2}", numWorkerThreads, numMinWorkerThreads, numMaxWorkerThreads)); float extraNoiseScaleBasedOnClimate = GetExtraNoiseScaleBasedOnClimate(mx, my); // the number of parallel tasks (use logical processor count for now - seems to be a good value) int numParallelTasks = Environment.ProcessorCount; // events used to synchronize thread computations (wait for them to finish) var doneEvents = new ManualResetEvent[numParallelTasks]; // the array of instances of the height computations helper class var heightsComputationTaskArray = new HeightsComputationTask[numParallelTasks]; // array of the data needed by the different tasks var dataForTasks = new HeightsComputationTask.DataForTask[numParallelTasks]; for (int i = 0; i < numParallelTasks; i++) { doneEvents[i] = new ManualResetEvent(false); var heightsComputationTask = new HeightsComputationTask(doneEvents[i]); heightsComputationTaskArray[i] = heightsComputationTask; dataForTasks[i] = new HeightsComputationTask.DataForTask(); dataForTasks[i].numTasks = numParallelTasks; dataForTasks[i].currentTask = i; dataForTasks[i].HeightmapDimension = HeightmapDimension; dataForTasks[i].MaxTerrainHeight = MaxTerrainHeight; dataForTasks[i].div = div; dataForTasks[i].baseHeightValue = baseHeightValue; dataForTasks[i].lhm = lhm; dataForTasks[i].noiseHeightMultiplierMap = noiseHeightMultiplierMap; dataForTasks[i].extraNoiseScaleBasedOnClimate = extraNoiseScaleBasedOnClimate; dataForTasks[i].mapPixel = mapPixel; ThreadPool.QueueUserWorkItem(heightsComputationTask.ThreadProc, dataForTasks[i]); } // wait for all tasks to finish computation WaitHandle.WaitAll(doneEvents); // computed average and max height in a second pass (after threaded tasks computed all heights) float averageHeight = 0; float maxHeight = float.MinValue; int dim = HeightmapDimension; for (int y = 0; y < dim; y++) { for (int x = 0; x < dim; x++) { // get sample float height = mapPixel.heightmapSamples[y, x]; // Accumulate average height averageHeight += height; // Get max height if (height > maxHeight) { maxHeight = height; } } } // Average and max heights are passed back for locations mapPixel.averageHeight = (averageHeight /= (float)(dim * dim)); mapPixel.maxHeight = maxHeight; //long totalTime = stopwatch.ElapsedMilliseconds - startTime; //DaggerfallUnity.LogMessage(string.Format("GenerateSamples took: {0}ms", totalTime), true); }
/// <summary> /// initializes resources (mapDistanceSquaredFromWater, mapDistanceSquaredFromLocations, mapMultipliers) and smoothes small height map /// </summary> public static void InitImprovedWorldTerrain(ContentReader contentReader) { if (!init) { #if CREATE_PERSISTENT_LOCATION_RANGE_MAPS { int width = WoodsFile.mapWidthValue; int height = WoodsFile.mapHeightValue; mapLocationRangeX = new byte[width * height]; mapLocationRangeY = new byte[width * height]; //int y = 204; //int x = 718; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { //MapPixelData MapData = TerrainHelper.GetMapPixelData(contentReader, x, y); //if (MapData.hasLocation) //{ // int locationRangeX = (int)MapData.locationRect.xMax - (int)MapData.locationRect.xMin; // int locationRangeY = (int)MapData.locationRect.yMax - (int)MapData.locationRect.yMin; //} ContentReader.MapSummary mapSummary; int regionIndex = -1, mapIndex = -1; bool hasLocation = contentReader.HasLocation(x, y, out mapSummary); if (hasLocation) { regionIndex = mapSummary.RegionIndex; mapIndex = mapSummary.MapIndex; DFLocation location = contentReader.MapFileReader.GetLocation(regionIndex, mapIndex); byte locationRangeX = location.Exterior.ExteriorData.Width; byte locationRangeY = location.Exterior.ExteriorData.Height; mapLocationRangeX[y * width + x] = locationRangeX; mapLocationRangeY[y * width + x] = locationRangeY; } } } // save to files FileStream ostream; ostream = new FileStream(Path.Combine(Application.dataPath, out_filepathMapLocationRangeX), FileMode.Create, FileAccess.Write); BinaryWriter writerMapLocationRangeX = new BinaryWriter(ostream, Encoding.UTF8); writerMapLocationRangeX.Write(mapLocationRangeX, 0, width * height); writerMapLocationRangeX.Close(); ostream.Close(); ostream = new FileStream(Path.Combine(Application.dataPath, out_filepathMapLocationRangeY), FileMode.Create, FileAccess.Write); BinaryWriter writerMapLocationRangeY = new BinaryWriter(ostream, Encoding.UTF8); writerMapLocationRangeY.Write(mapLocationRangeY, 0, width * height); writerMapLocationRangeY.Close(); ostream.Close(); } #else { int width = WoodsFile.mapWidthValue; int height = WoodsFile.mapHeightValue; mapLocationRangeX = new byte[width * height]; mapLocationRangeY = new byte[width * height]; MemoryStream istream; TextAsset assetMapLocationRangeX = Resources.Load <TextAsset>(filenameMapLocationRangeX); if (assetMapLocationRangeX != null) { istream = new MemoryStream(assetMapLocationRangeX.bytes); BinaryReader readerMapLocationRangeX = new BinaryReader(istream, Encoding.UTF8); readerMapLocationRangeX.Read(mapLocationRangeX, 0, width * height); readerMapLocationRangeX.Close(); istream.Close(); } TextAsset assetMapLocationRangeY = Resources.Load <TextAsset>(filenameMapLocationRangeY); if (assetMapLocationRangeY) { istream = new MemoryStream(assetMapLocationRangeY.bytes); BinaryReader readerMapLocationRangeY = new BinaryReader(istream, Encoding.UTF8); readerMapLocationRangeY.Read(mapLocationRangeY, 0, width * height); readerMapLocationRangeY.Close(); istream.Close(); } //FileStream istream; //istream = new FileStream(filepathMapLocationRangeX, FileMode.Open, FileAccess.Read); //BinaryReader readerMapLocationRangeX = new BinaryReader(istream, Encoding.UTF8); //readerMapLocationRangeX.Read(mapLocationRangeX, 0, width * height); //readerMapLocationRangeX.Close(); //istream.Close(); //istream = new FileStream(filepathMapLocationRangeY, FileMode.Open, FileAccess.Read); //BinaryReader readerMapLocationRangeY = new BinaryReader(istream, Encoding.UTF8); //readerMapLocationRangeY.Read(mapLocationRangeY, 0, width * height); //readerMapLocationRangeY.Close(); //istream.Close(); } #endif if (mapDistanceSquaredFromWater == null) { byte[] heightMapArray = contentReader.WoodsFileReader.Buffer.Clone() as byte[]; int width = WoodsFile.mapWidthValue; int height = WoodsFile.mapHeightValue; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (heightMapArray[y * width + x] <= 2) { heightMapArray[y * width + x] = 1; } else { heightMapArray[y * width + x] = 0; } } } //now set image borders to "water" (this is a workaround to prevent mountains to become too high in north-east and south-east edge of map) for (int y = 0; y < height; y++) { heightMapArray[y * width + 0] = 1; heightMapArray[y * width + width - 1] = 1; } for (int x = 0; x < width; x++) { heightMapArray[0 * width + x] = 1; heightMapArray[(height - 1) * width + x] = 1; } mapDistanceSquaredFromWater = imageDistanceTransform(heightMapArray, width, height, 1); heightMapArray = null; } if (mapDistanceSquaredFromLocations == null) { int width = WoodsFile.mapWidthValue; int height = WoodsFile.mapHeightValue; mapLocations = new byte[width * height]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { ContentReader.MapSummary summary; if (contentReader.HasLocation(x + 1, y + 1, out summary)) { mapLocations[y * width + x] = 1; } else { mapLocations[y * width + x] = 0; } } } mapDistanceSquaredFromLocations = imageDistanceTransform(mapLocations, width, height, 1); } if (mapMultipliers == null) { int width = WoodsFile.mapWidthValue; int height = WoodsFile.mapHeightValue; mapMultipliers = new float[width * height]; // compute the multiplier and store it in mapMultipliers for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { float distanceFromWater = (float)Math.Sqrt(mapDistanceSquaredFromWater[y * WoodsFile.mapWidthValue + x]); float distanceFromLocation = (float)Math.Sqrt(mapDistanceSquaredFromLocations[y * WoodsFile.mapWidthValue + x]); float multiplierLocation = (distanceFromLocation * extraExaggerationFactorLocationDistance + 1.0f); // terrain distant from location gets extra exaggeration if (distanceFromWater < minDistanceFromWaterForExtraExaggeration) // except if it is near water { multiplierLocation = 1.0f; } // Seed random with terrain key UnityEngine.Random.InitState(TerrainHelper.MakeTerrainKey(x, y)); float additionalHeightBasedOnClimate = GetAdditionalHeightBasedOnClimate(x, y); float additionalHeightApplied = UnityEngine.Random.Range(-additionalHeightBasedOnClimate * 0.5f, additionalHeightBasedOnClimate); mapMultipliers[y * width + x] = (Math.Min(maxHeightsExaggerationMultiplier, additionalHeightApplied + /*multiplierLocation **/ Math.Max(1.0f, distanceFromWater * exaggerationFactorWaterDistance))); } } // multipliedMap gets smoothed float[] newmapMultipliers = mapMultipliers.Clone() as float[]; float[,] weights = { { 0.0625f, 0.125f, 0.0625f }, { 0.125f, 0.25f, 0.125f }, { 0.0625f, 0.125f, 0.0625f } }; for (int y = 1; y < height - 1; y++) { for (int x = 1; x < width - 1; x++) { if (mapDistanceSquaredFromLocations[y * width + x] <= 2) // at and around locations ( <= 2 ... only map pixels in 8-connected neighborhood (distanceFromLocationMaps stores squared distances...)) { newmapMultipliers[y * width + x] = weights[0, 0] * mapMultipliers[(y - 1) * width + (x - 1)] + weights[0, 1] * mapMultipliers[(y - 1) * width + (x)] + weights[0, 2] * mapMultipliers[(y - 1) * width + (x + 1)] + weights[1, 0] * mapMultipliers[(y - 0) * width + (x - 1)] + weights[1, 1] * mapMultipliers[(y - 0) * width + (x)] + weights[1, 2] * mapMultipliers[(y - 0) * width + (x + 1)] + weights[2, 0] * mapMultipliers[(y + 1) * width + (x - 1)] + weights[2, 1] * mapMultipliers[(y + 1) * width + (x)] + weights[2, 2] * mapMultipliers[(y + 1) * width + (x + 1)]; } } } mapMultipliers = newmapMultipliers; newmapMultipliers = null; weights = null; } //the height map gets smoothed as well { int width = WoodsFile.mapWidthValue; int height = WoodsFile.mapHeightValue; byte[] heightMapBuffer = contentReader.WoodsFileReader.Buffer.Clone() as byte[]; int[,] intWeights = { { 1, 2, 1 }, { 2, 4, 2 }, { 1, 2, 1 } }; for (int y = 1; y < height - 1; y++) { for (int x = 1; x < width - 1; x++) { if (mapDistanceSquaredFromWater[y * width + x] > 0) // check if squared distance from water is greater than zero -> if it is no water pixel { int value = intWeights[0, 0] * (int)heightMapBuffer[(y - 1) * width + (x - 1)] + intWeights[0, 1] * (int)heightMapBuffer[(y - 1) * width + (x)] + intWeights[0, 2] * (int)heightMapBuffer[(y - 1) * width + (x + 1)] + intWeights[1, 0] * (int)heightMapBuffer[(y - 0) * width + (x - 1)] + intWeights[1, 1] * (int)heightMapBuffer[(y - 0) * width + (x)] + intWeights[1, 2] * (int)heightMapBuffer[(y - 0) * width + (x + 1)] + intWeights[2, 0] * (int)heightMapBuffer[(y + 1) * width + (x - 1)] + intWeights[2, 1] * (int)heightMapBuffer[(y + 1) * width + (x)] + intWeights[2, 2] * (int)heightMapBuffer[(y + 1) * width + (x + 1)]; heightMapBuffer[y * width + x] = (byte)(value / 16); } } } contentReader.WoodsFileReader.Buffer = heightMapBuffer; heightMapBuffer = null; intWeights = null; } // build tree coverage map if (mapTreeCoverage == null) { int width = WoodsFile.mapWidthValue; int height = WoodsFile.mapHeightValue; mapTreeCoverage = new byte[width * height]; #if !LOAD_TREE_COVERAGE_MAP { float startTreeCoverageAtElevation = ImprovedTerrainSampler.baseHeightScale * 2.0f; // ImprovedTerrainSampler.scaledBeachElevation; float minTreeCoverageSaturated = ImprovedTerrainSampler.baseHeightScale * 6.0f; float maxTreeCoverageSaturated = ImprovedTerrainSampler.baseHeightScale * 60.0f; float endTreeCoverageAtElevation = ImprovedTerrainSampler.baseHeightScale * 80.0f; //float maxElevation = 0.0f; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int readIndex = (height - 1 - y) * width + x; float w = 0.0f; //float elevation = ((float)contentReader.WoodsFileReader.Buffer[(height - 1 - y) * width + x]) / 255.0f; // *mapMultipliers[index]; float elevation = ((float)contentReader.WoodsFileReader.Buffer[readIndex]) * mapMultipliers[readIndex]; //maxElevation = Math.Max(maxElevation, elevation); if ((elevation > minTreeCoverageSaturated) && (elevation < maxTreeCoverageSaturated)) { w = 1.0f; } else if ((elevation >= startTreeCoverageAtElevation) && (elevation <= minTreeCoverageSaturated)) { w = (elevation - startTreeCoverageAtElevation) / (minTreeCoverageSaturated - startTreeCoverageAtElevation); } else if ((elevation >= maxTreeCoverageSaturated) && (elevation <= endTreeCoverageAtElevation)) { w = 1.0f - ((elevation - maxTreeCoverageSaturated) / (endTreeCoverageAtElevation - maxTreeCoverageSaturated)); } //w = 0.65f * w + 0.35f * Math.Min(6.0f, (float)Math.Sqrt(mapDistanceSquaredFromLocations[y * width + x])) / 6.0f; mapTreeCoverage[(y) * width + x] = Convert.ToByte(w * 255.0f); //if (elevation>0.05f) // mapTreeCoverage[index] = Convert.ToByte(250); //w * 255.0f); //else mapTreeCoverage[index] = Convert.ToByte(0); //if (elevation >= startTreeCoverageAtElevation) //{ // mapTreeCoverage[(y) * width + x] = Convert.ToByte(255.0f); //} else{ // mapTreeCoverage[(y) * width + x] = Convert.ToByte(0.0f); //} } } } #else { MemoryStream istream; TextAsset assetMapTreeCoverage = Resources.Load <TextAsset>(filenameTreeCoverageMap); if (assetMapTreeCoverage) { istream = new MemoryStream(assetMapTreeCoverage.bytes); BinaryReader readerMapTreeCoverage = new BinaryReader(istream, Encoding.UTF8); readerMapTreeCoverage.Read(mapTreeCoverage, 0, width * height); readerMapTreeCoverage.Close(); istream.Close(); } } #endif #if CREATE_PERSISTENT_TREE_COVERAGE_MAP { FileStream ostream = new FileStream(Path.Combine(Application.dataPath, out_filepathOutTreeCoverageMap), FileMode.Create, FileAccess.Write); BinaryWriter writerMapTreeCoverage = new BinaryWriter(ostream, Encoding.UTF8); writerMapTreeCoverage.Write(mapTreeCoverage, 0, width * height); writerMapTreeCoverage.Close(); ostream.Close(); } #endif //Debug.Log(string.Format("max elevation: {0}", maxElevation)); } init = true; } }