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.tilemapSamples = new TilemapSample[MapsFile.WorldMapTileDim, MapsFile.WorldMapTileDim]; mapPixel.heightmapSamples = new float[HeightmapDimension, HeightmapDimension]; // Divisor ensures continuous 0-1 range of height samples float div = (float)(HeightmapDimension - 1) / 3f; // Read neighbouring height samples for this map pixel int mx = mapPixel.mapPixelX; int my = mapPixel.mapPixelY; byte[,] shm = dfUnity.ContentReader.WoodsFileReader.GetHeightMapValuesRange(mx - 2, my - 2, 4); byte[,] lhm = dfUnity.ContentReader.WoodsFileReader.GetLargeHeightMapValuesRange(mx - 1, my, 3); // the number of parallel tasks (note Nystul: 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].shm = shm; dataForTasks[i].lhm = lhm; dataForTasks[i].mapPixel = mapPixel; ThreadPool.QueueUserWorkItem(heightsComputationTask.ProcessTaskThread, 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); }
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); }