public override JobHandle ScheduleGenerateSamplesJob(ref MapPixelData mapPixel)
        {
            GenerateSamples(ref mapPixel);


            return(new JobHandle());
        }
        public override void GenerateSamples(ref MapPixelData mapPixel)
        {
            mapPixel.maxHeight = MaxTerrainHeight;
            var computer = TerrainComputer.Create(mapPixel, this);

            computer.DispatchAndProcess(InterestingTerrains.csPrototype, ref mapPixel, InterestingTerrains.instance.csParams);
        }
        void HandleBaseMapSampleParams(ref MapPixelData mapPixel, ref ComputeShader cs, int k)
        {
            DaggerfallUnity dfUnity = DaggerfallUnity.Instance;

            // Divisor ensures continuous 0-1 range of height samples
            float div = (sampler.HeightmapDimension - 1) / 3f;

            // Read neighbouring height samples for this map pixel
            int mx      = mapPixel.mapPixelX;
            int my      = mapPixel.mapPixelY;
            int sDim    = 4;
            var shmByte = dfUnity.ContentReader.WoodsFileReader.GetHeightMapValuesRange1Dim(mx - 2, my - 2, sDim);
            var shm     = new float[shmByte.Length];
            int i;

            for (i = 0; i < shm.Length; i++)
            {
                shm[i] = Convert.ToSingle(shmByte[i]);
            }

            // Convert & flatten large height samples 2d array into 1d native array.
            byte[,] lhm2 = dfUnity.ContentReader.WoodsFileReader.GetLargeHeightMapValuesRange(mx - 1, my, 3);
            float[] lhm  = new float[lhm2.Length];
            int     lDim = lhm2.GetLength(0);

            i = 0;
            for (int y = 0; y < lDim; y++)
            {
                for (int x = 0; x < lDim; x++)
                {
                    lhm[i++] = Convert.ToSingle(lhm2[x, y]);
                }
            }

            // Extract height samples for all chunks
            int hDim = sampler.HeightmapDimension;

            // Create buffers with extracted heightmap data
            heightmapBuffers.shm = new ComputeBuffer(shm.Length, sizeof(float));
            heightmapBuffers.shm.SetData(shm);

            heightmapBuffers.lhm = new ComputeBuffer(lhm.Length, sizeof(float));
            heightmapBuffers.lhm.SetData(lhm);

            // Assign properties to CS
            cs.SetBuffer(k, "shm", heightmapBuffers.shm);
            cs.SetBuffer(k, "lhm", heightmapBuffers.lhm);
            cs.SetInt("sd", sDim);
            cs.SetInt("ld", lDim);
            cs.SetInt("hDim", hDim);
            cs.SetFloat("div", div);
            cs.SetInt("mapPixelX", mapPixel.mapPixelX);
            cs.SetInt("mapPixelY", mapPixel.mapPixelY);
            cs.SetFloat("maxTerrainHeight", 2308.5f);
            cs.SetFloat("baseHeightScale", 8f);
            cs.SetFloat("noiseMapScale", 4f);
            cs.SetFloat("extraNoiseScale", 10f);
            cs.SetFloat("scaledOceanElevation", 27.2f);
        }
Пример #4
0
        private void MessageReceiver(string message, object data, DFModMessageCallback callBack)
        {
            try {
                Vector2Int mpCoords;
                byte       point;
                switch (message)
                {
                case GET_PATH_DATA:
                    callBack?.Invoke(GET_PATH_DATA, roadTexturing.GetPathData((int)data));
                    break;

                case GET_ROAD_POINT:
                    mpCoords = (Vector2Int)data;
                    point    = roadTexturing.GetPathDataPoint(BasicRoadsTexturing.roads, mpCoords.x, mpCoords.y);
                    callBack?.Invoke(GET_ROAD_POINT, point);
                    break;

                case GET_TRACK_POINT:
                    mpCoords = (Vector2Int)data;
                    point    = roadTexturing.GetPathDataPoint(BasicRoadsTexturing.tracks, mpCoords.x, mpCoords.y);
                    callBack?.Invoke(GET_TRACK_POINT, point);
                    break;

                case GET_PATHS_POINT:
                    mpCoords = (Vector2Int)data;
                    byte roadPt  = roadTexturing.GetPathDataPoint(BasicRoadsTexturing.roads, mpCoords.x, mpCoords.y);
                    byte trackPt = roadTexturing.GetPathDataPoint(BasicRoadsTexturing.tracks, mpCoords.x, mpCoords.y);
                    point = (byte)(roadPt | trackPt);
                    callBack?.Invoke(GET_PATHS_POINT, point);
                    break;

                case SCHEDULE_ROADS_JOB:
                    // Get the parameters
                    object[]           paramArray   = (object[])data;
                    MapPixelData       mapData      = (MapPixelData)paramArray[0];
                    NativeArray <byte> tileData     = (NativeArray <byte>)paramArray[1];
                    JobHandle          dependencies = (JobHandle)paramArray[2];

                    // Instantiate PaintRoadsJob, schedule, then return job handle
                    JobHandle paintRoadsHandle = roadTexturing.SchedulePaintRoadsJob(ref mapData, ref tileData, dependencies);
                    callBack?.Invoke(SCHEDULE_ROADS_JOB, paintRoadsHandle);
                    break;

                default:
                    Debug.LogErrorFormat("{0}: unknown message received ({1}).", this, message);
                    break;
                }
            }
            catch
            {
                Debug.LogErrorFormat("{0}: error handling message ({1}).", this, message);
                callBack?.Invoke("error", "Data passed is invalid for " + message);
            }
        }
Пример #5
0
        public JobHandle SchedulePaintRoadsJob(ref MapPixelData mapData, ref NativeArray <byte> tileData, JobHandle dependencies)
        {
            // Assign tile data to terrain, painting paths in the process
            int  pathsIndex   = mapData.mapPixelX + (mapData.mapPixelY * MapsFile.MaxMapPixelX);
            byte roadDataPt   = pathsData[roads][pathsIndex];
            byte roadCorners  = (byte)(InRange(pathsIndex) ? (pathsData[roads][pathsIndex + 1] & 0x5) | (pathsData[roads][pathsIndex - 1] & 0x50) : 0);
            byte trackDataPt  = pathsData[tracks][pathsIndex];
            byte trackCorners = (byte)(InRange(pathsIndex) ? (pathsData[tracks][pathsIndex + 1] & 0x5) | (pathsData[tracks][pathsIndex - 1] & 0x50) : 0);

            if (editorEnabled)
            {
                roadDataPt   = BasicRoadsPathEditor.pathsData[roads][pathsIndex];
                roadCorners  = (byte)(InRange(pathsIndex) ? (BasicRoadsPathEditor.pathsData[roads][pathsIndex + 1] & 0x5) | (BasicRoadsPathEditor.pathsData[roads][pathsIndex - 1] & 0x50) : 0);
                trackDataPt  = BasicRoadsPathEditor.pathsData[tracks][pathsIndex];
                trackCorners = (byte)(InRange(pathsIndex) ? (BasicRoadsPathEditor.pathsData[tracks][pathsIndex + 1] & 0x5) | (BasicRoadsPathEditor.pathsData[tracks][pathsIndex - 1] & 0x50) : 0);
            }

            PaintRoadsJob paintRoadsJob = new PaintRoadsJob
            {
                tileData     = tileData,
                tilemapData  = mapData.tilemapData,
                tdDim        = tileDataDim,
                tDim         = assignTilesDim,
                locationRect = mapData.locationRect,
                midLo        = (assignTilesDim / 2) - 1,
                midHi        = assignTilesDim / 2,
                roadDataPt   = roadDataPt,
                roadCorners  = roadCorners,
                trackDataPt  = trackDataPt,
                trackCorners = trackCorners,
            };
            JobHandle paintRoadsHandle = paintRoadsJob.Schedule(assignTilesDim * assignTilesDim, 64, dependencies);

            JobHandle returnHandle = paintRoadsHandle;

            if (smoothPaths)
            {
                SmoothRoadsTerrainJob smoothRoadTerrainJob = new SmoothRoadsTerrainJob()
                {
                    heightmapData = mapData.heightmapData,
                    tilemapData   = mapData.tilemapData,
                    hDim          = DaggerfallUnity.Instance.TerrainSampler.HeightmapDimension,
                    tDim          = assignTilesDim,
                    locationRect  = mapData.locationRect,
                };
                JobHandle smoothRoadHandle = smoothRoadTerrainJob.Schedule(paintRoadsHandle);
                returnHandle = smoothRoadHandle;
            }

            return(returnHandle);
        }
        /// <summary>
        /// Create a TerrainComputer for a specific mapData/terrain instance.
        /// </summary>
        /// <param name="mapPixelData"></param>
        /// <param name="sampler"></param>
        /// <returns></returns>
        public static TerrainComputer Create(MapPixelData mapPixelData, InterestingTerrainSampler sampler)
        {
            var tSize = Utility.GetTerrainVertexSize();

            return(new TerrainComputer()
            {
                sampler = sampler,
                heightmapBuffers = BufferIO.CreateHeightmapBuffers(),
                heightmapResolution = (int)InterestingTerrains.settings.heightmapResolution,
                locationRect = mapPixelData.hasLocation
                    ? mapPixelData.locationRect
                    : new Rect(-10, -10, 1, 1),
                terrainPosition = Utility.GetTerrainVertexPosition(mapPixelData.mapPixelX, mapPixelData.mapPixelY),
                terrainSize = new Vector2(tSize, tSize)
            });
        }
Пример #7
0
        public override JobHandle ScheduleAssignTilesJob(ITerrainSampler terrainSampler, ref MapPixelData mapData, JobHandle dependencies, bool march = true)
        {
            // Cache tile data to minimise noise sampling during march (using default job)
            NativeArray <byte>  tileData    = new NativeArray <byte>(tileDataDim * tileDataDim, Allocator.TempJob);
            GenerateTileDataJob tileDataJob = new GenerateTileDataJob
            {
                heightmapData    = mapData.heightmapData,
                tileData         = tileData,
                tdDim            = tileDataDim,
                hDim             = terrainSampler.HeightmapDimension,
                maxTerrainHeight = terrainSampler.MaxTerrainHeight,
                oceanElevation   = terrainSampler.OceanElevation,
                beachElevation   = terrainSampler.BeachElevation,
                mapPixelX        = mapData.mapPixelX,
                mapPixelY        = mapData.mapPixelY,
            };
            JobHandle tileDataHandle = tileDataJob.Schedule(tileDataDim * tileDataDim, 64, dependencies);

            // Schedule painting of roads, including smoothing if enabled
            JobHandle paintRoadsHandle = SchedulePaintRoadsJob(ref mapData, ref tileData, tileDataHandle);

            // Assign tile data to terrain (using default job)
            NativeArray <byte> lookupData     = new NativeArray <byte>(lookupTable, Allocator.TempJob);
            AssignTilesJob     assignTilesJob = new AssignTilesJob
            {
                lookupTable  = lookupData,
                tileData     = tileData,
                tilemapData  = mapData.tilemapData,
                tdDim        = tileDataDim,
                tDim         = assignTilesDim,
                march        = march,
                locationRect = mapData.locationRect,
            };
            JobHandle assignTilesHandle = assignTilesJob.Schedule(assignTilesDim * assignTilesDim, 64, paintRoadsHandle);

            // Add both working native arrays to disposal list.
            mapData.nativeArrayList.Add(tileData);
            mapData.nativeArrayList.Add(lookupData);

            return(assignTilesHandle);
        }
        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);
        }
        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);
        }
Пример #10
0
        /// <summary>
        /// Works exactly like the default TerrainTexturer, except that it skips the GenerateTileDataJob
        /// and uses the tileData generated during terrain sampling instead.
        /// Use the mod messaging system to obtain tileData for a map pixel in a custom TerrainTexturer.
        /// </summary>
        ///
        /// To access tileData in a separate mod, you can use this code:
        /// <code>
        /// byte[] tData = null;
        /// ModManager.Instance.SendModMessage("Interesting Terrains", "getTileData", new int[] { mapData.mapPixelX, mapData.mapPixelY }, (string message, object data) =>
        /// {
        ///    if (message == "error")
        ///        Debug.LogError(data as string);
        ///    else
        ///        tData = data as byte[];
        /// });
        /// </code>
        public override JobHandle ScheduleAssignTilesJob(ITerrainSampler terrainSampler, ref MapPixelData mapData, JobHandle dependencies, bool march = true)
        {
            // Load tile data generated by the Terrain Sampler
            var tData = InterestingTerrains.tileDataCache.Get(mapData.mapPixelX, mapData.mapPixelY);
            NativeArray <byte> tileData = new NativeArray <byte>(tData, Allocator.TempJob);

            // Schedule the paint roads jobs if basic roads mod is enabled
            JobHandle preAssignTilesHandle = dependencies;

            if (CompatibilityUtils.BasicRoadsLoaded)
            {
                ModManager.Instance.SendModMessage("BasicRoads", "scheduleRoadsJob", new object[] { mapData, tileData, dependencies },
                                                   (string message, object data) =>
                {
                    if (message == "error")
                    {
                        Debug.LogError(data as string);
                    }
                    else
                    {
                        preAssignTilesHandle = (JobHandle)data;
                    }
                });
            }

            // Assign tile data to terrain
            NativeArray <byte> lookupData     = new NativeArray <byte>(lookupTable, Allocator.TempJob);
            AssignTilesJob     assignTilesJob = new AssignTilesJob
            {
                lookupTable  = lookupData,
                tileData     = tileData,
                tilemapData  = mapData.tilemapData,
                tdDim        = tileDataDim,
                tDim         = assignTilesDim,
                march        = march,
                locationRect = mapData.locationRect,
            };
            JobHandle assignTilesHandle = assignTilesJob.Schedule(assignTilesDim * assignTilesDim, 64, preAssignTilesHandle);

            // Add both working native arrays to disposal list.
            mapData.nativeArrayList.Add(tileData);
            mapData.nativeArrayList.Add(lookupData);

            return(assignTilesHandle);
        }
        public override void GenerateSamples(ref MapPixelData mapPixel)
        {
            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 tile 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);

            float[,] multiplierValue = 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));

                    multiplierValue[x, y] = ImprovedWorldTerrain.computeHeightMultiplier(mapPixelX, mapPixelY);
                }
            }

            // Extract height samples for all chunks
            float averageHeight = 0;
            float maxHeight = float.MinValue;
            float baseHeight, noiseHeight;
            float x1, x2, x3, x4;
            int   dim = HeightmapDimension;

            //mapPixel.heightmapSamples = new float[dim, dim];
            for (int y = 0; y < dim; y++)
            {
                for (int x = 0; x < dim; x++)
                {
                    float rx           = (float)x / div;
                    float ry           = (float)y / div;
                    int   ix           = Mathf.FloorToInt(rx);
                    int   iy           = Mathf.FloorToInt(ry);
                    float sfracx       = (float)x / (float)(dim - 1);
                    float sfracy       = (float)y / (float)(dim - 1);
                    float fracx        = (float)(x - ix * div) / div;
                    float fracy        = (float)(y - iy * div) / div;
                    float scaledHeight = 0;

                    // Bicubic sample small height map for base terrain elevation
                    x1            = TerrainHelper.CubicInterpolator(shm[0, 3] * multiplierValue[0, 3], shm[1, 3] * multiplierValue[1, 3], shm[2, 3] * multiplierValue[2, 3], shm[3, 3] * multiplierValue[3, 3], sfracx);
                    x2            = TerrainHelper.CubicInterpolator(shm[0, 2] * multiplierValue[0, 2], shm[1, 2] * multiplierValue[1, 2], shm[2, 2] * multiplierValue[2, 2], shm[3, 2] * multiplierValue[3, 2], sfracx);
                    x3            = TerrainHelper.CubicInterpolator(shm[0, 1] * multiplierValue[0, 1], shm[1, 1] * multiplierValue[1, 1], shm[2, 1] * multiplierValue[2, 1], shm[3, 1] * multiplierValue[3, 1], sfracx);
                    x4            = TerrainHelper.CubicInterpolator(shm[0, 0] * multiplierValue[0, 0], shm[1, 0] * multiplierValue[1, 0], shm[2, 0] * multiplierValue[2, 0], shm[3, 0] * multiplierValue[3, 0], sfracx);
                    baseHeight    = TerrainHelper.CubicInterpolator(x1, x2, x3, x4, sfracy);
                    scaledHeight += baseHeight * baseHeightScale;

                    // Bicubic sample large height map for noise mask over terrain features
                    x1            = TerrainHelper.CubicInterpolator(lhm[ix, iy + 0], lhm[ix + 1, iy + 0], lhm[ix + 2, iy + 0], lhm[ix + 3, iy + 0], fracx);
                    x2            = TerrainHelper.CubicInterpolator(lhm[ix, iy + 1], lhm[ix + 1, iy + 1], lhm[ix + 2, iy + 1], lhm[ix + 3, iy + 1], fracx);
                    x3            = TerrainHelper.CubicInterpolator(lhm[ix, iy + 2], lhm[ix + 1, iy + 2], lhm[ix + 2, iy + 2], lhm[ix + 3, iy + 2], fracx);
                    x4            = TerrainHelper.CubicInterpolator(lhm[ix, iy + 3], lhm[ix + 1, iy + 3], lhm[ix + 2, iy + 3], lhm[ix + 3, iy + 3], fracx);
                    noiseHeight   = TerrainHelper.CubicInterpolator(x1, x2, x3, x4, fracy);
                    scaledHeight += noiseHeight * noiseMapScale;

                    // Additional noise mask for small terrain features at ground level
                    int   noisex   = mapPixel.mapPixelX * (HeightmapDimension - 1) + x;
                    int   noisey   = (MapsFile.MaxMapPixelY - mapPixel.mapPixelY) * (HeightmapDimension - 1) + y;
                    float lowFreq  = TerrainHelper.GetNoise(noisex, noisey, 0.3f, 0.5f, 0.5f, 1);
                    float highFreq = TerrainHelper.GetNoise(noisex, noisey, 0.9f, 0.5f, 0.5f, 1);
                    scaledHeight += (lowFreq * highFreq) * extraNoiseScale;

                    // Clamp lower values to ocean elevation
                    if (scaledHeight < scaledOceanElevation)
                    {
                        scaledHeight = scaledOceanElevation;
                    }

                    // Accumulate average height
                    averageHeight += scaledHeight;

                    // Get max height
                    if (scaledHeight > maxHeight)
                    {
                        maxHeight = scaledHeight;
                    }

                    // Set sample
                    float height = Mathf.Clamp01(scaledHeight / MaxTerrainHeight);
                    mapPixel.heightmapSamples[y, x] = height;
                }
            }

            // Average and max heights are passed back for locations
            mapPixel.averageHeight = (averageHeight /= (float)(dim * dim)) / MaxTerrainHeight;
            mapPixel.maxHeight     = maxHeight / MaxTerrainHeight;
        }
Пример #12
0
        public override JobHandle ScheduleAssignTilesJob(ITerrainSampler terrainSampler, ref MapPixelData mapData, JobHandle dependencies, bool march = true)
        {
            // Cache tile data to minimise noise sampling during march.
            NativeArray <byte>  tileData    = new NativeArray <byte>(tileDataDim * tileDataDim, Allocator.TempJob);
            GenerateTileDataJob tileDataJob = new GenerateTileDataJob
            {
                heightmapData    = mapData.heightmapData,
                tileData         = tileData,
                tdDim            = tileDataDim,
                hDim             = terrainSampler.HeightmapDimension,
                maxTerrainHeight = terrainSampler.MaxTerrainHeight,
                oceanElevation   = terrainSampler.OceanElevation,
                beachElevation   = terrainSampler.BeachElevation,
                mapPixelX        = mapData.mapPixelX,
                mapPixelY        = mapData.mapPixelY,
            };
            JobHandle tileDataHandle = tileDataJob.Schedule(tileDataDim * tileDataDim, 64, dependencies);

            // Assign tile data to terrain, painting paths in the process
            int  pathsIndex   = mapData.mapPixelX + (mapData.mapPixelY * MapsFile.MaxMapPixelX);
            byte roadDataPt   = pathsData[roads][pathsIndex];
            byte roadCorners  = (byte)(InRange(pathsIndex) ? (pathsData[roads][pathsIndex + 1] & 0x5) | (pathsData[roads][pathsIndex - 1] & 0x50) : 0);
            byte trackDataPt  = pathsData[tracks][pathsIndex];
            byte trackCorners = (byte)(InRange(pathsIndex) ? (pathsData[tracks][pathsIndex + 1] & 0x5) | (pathsData[tracks][pathsIndex - 1] & 0x50) : 0);

            if (editorEnabled)
            {
                roadDataPt   = BasicRoadsPathEditor.pathsData[roads][pathsIndex];
                roadCorners  = (byte)(InRange(pathsIndex) ? (BasicRoadsPathEditor.pathsData[roads][pathsIndex + 1] & 0x5) | (BasicRoadsPathEditor.pathsData[roads][pathsIndex - 1] & 0x50) : 0);
                trackDataPt  = BasicRoadsPathEditor.pathsData[tracks][pathsIndex];
                trackCorners = (byte)(InRange(pathsIndex) ? (BasicRoadsPathEditor.pathsData[tracks][pathsIndex + 1] & 0x5) | (BasicRoadsPathEditor.pathsData[tracks][pathsIndex - 1] & 0x50) : 0);
            }

            NativeArray <byte>      lookupData     = new NativeArray <byte>(lookupTable, Allocator.TempJob);
            AssignTilesWithRoadsJob assignTilesJob = new AssignTilesWithRoadsJob
            {
                lookupTable  = lookupData,
                tileData     = tileData,
                tilemapData  = mapData.tilemapData,
                tdDim        = tileDataDim,
                tDim         = assignTilesDim,
                hDim         = terrainSampler.HeightmapDimension,
                march        = march,
                locationRect = mapData.locationRect,
                midLo        = (assignTilesDim / 2) - 1,
                midHi        = assignTilesDim / 2,
                roadDataPt   = roadDataPt,
                roadCorners  = roadCorners,
                trackDataPt  = trackDataPt,
                trackCorners = trackCorners,
            };
            JobHandle assignTilesHandle = assignTilesJob.Schedule(assignTilesDim * assignTilesDim, 64, tileDataHandle);

            JobHandle returnHandle = assignTilesHandle;

            if (smoothPaths)
            {
                SmoothRoadsTerrainJob smoothRoadTerrainJob = new SmoothRoadsTerrainJob()
                {
                    heightmapData = mapData.heightmapData,
                    tilemapData   = mapData.tilemapData,
                    hDim          = DaggerfallUnity.Instance.TerrainSampler.HeightmapDimension,
                    tDim          = assignTilesDim,
                    locationRect  = mapData.locationRect,
                };
                JobHandle smoothRoadHandle = smoothRoadTerrainJob.Schedule(assignTilesHandle);
                returnHandle = smoothRoadHandle;
            }

            // Add both working native arrays to disposal list.
            mapData.nativeArrayList.Add(tileData);
            mapData.nativeArrayList.Add(lookupData);

            return(returnHandle);
        }
        /// <summary>
        /// Extracts data from the ComputeBuffers, applies it to the terrain, then disposes of the buffers.
        /// </summary>
        /// <param name="buffers"></param>
        /// <param name="mapData"></param>
        ///
        public static void ProcessBufferValuesAndDispose(HeightmapBufferCollection buffers, ref MapPixelData mapData)
        {
            // Extract heights, generated by TerrainComputer, as float array
            var heightValues = new float[buffers.heightmapBuffer.count];

            buffers.heightmapBuffer.GetData(heightValues);

            // Assign generated heights to mapData
            CopyToNative(heightValues, ref mapData.heightmapData);
            mapData.heightmapSamples = To2D(mapData.heightmapData, HeightmapRes());

            // Extract tileData, generated by TerrainComputer, as int array
            int res         = MapsFile.WorldMapTileDim + 1;
            var tilemapInts = new int[res * res];

            buffers.tilemapData.GetData(tilemapInts);

            // Convert to byte array
            var tileData = tilemapInts.Select(v => AsByte(v)).ToArray();

            // Add to internal tileData storage for use in TerrainTexturer
            var pos = new DFPosition(mapData.mapPixelX, mapData.mapPixelY);

            InterestingTerrains.tileDataCache.Add(pos, tileData);

            // Cleanup
            buffers.Dispose();
        }
        /// <summary>
        /// Initializes and runs a TerrainComputer GPU job, then processes the generated data.
        /// </summary>
        /// <param name="csPrototype"></param>
        /// <param name="mapData"></param>
        /// <param name="csParams"></param>
        public void DispatchAndProcess(ComputeShader csPrototype, ref MapPixelData mapData, TerrainComputerParams csParams)
        {
            var  woodsFile = DaggerfallUnity.Instance.ContentReader.WoodsFileReader;
            var  cs = UnityEngine.Object.Instantiate(csPrototype);
            var  k = cs.FindKernel("TerrainComputer");
            uint _x, _y, _z;

            cs.GetKernelThreadGroupSizes(k, out _x, out _y, out _z);

            int res = heightmapResolution + 1;

            DaggerfallUnity dfUnity;

            DaggerfallUnity.FindDaggerfallUnity(out dfUnity);
            int searchSize = 16;
            var locations  = new List <Rect>();

            int x, y;

            for (x = -searchSize; x <= searchSize; x++)
            {
                for (y = -searchSize; y <= searchSize; y++)
                {
                    var mpx = mapData.mapPixelX + x;
                    var mpy = mapData.mapPixelY + y;
                    var key = new DoubleInt()
                    {
                        Item1 = mpx, Item2 = mpy
                    };

                    if (locationRectCache.ContainsKey(key))
                    {
                        locations.Add(locationRectCache[key]);
                        continue;
                    }

                    var mapPixelPos  = new DFPosition(mpx, mpy);
                    var mapPixelData = TerrainHelper.GetMapPixelData(dfUnity.ContentReader, mpx, mpy);

                    if (!mapPixelData.hasLocation)
                    {
                        continue;
                    }

                    var location = dfUnity.ContentReader.MapFileReader.GetLocation(mapPixelData.mapRegionIndex, mapPixelData.mapLocationIndex);

                    var locationRect = GetLocationRect(location);
                    if (locationRect.width == 0 || locationRect.height == 0)
                    {
                        continue;
                    }
                    locationRect = ExpandInEachDirection(locationRect, 1);

                    locationRectCache.Add(key, locationRect);
                    locations.Add(locationRect);
                }
            }

            x = (int)_x;
            y = (int)_y;

            cs.SetVector("terrainPosition", terrainPosition);
            cs.SetVector("terrainSize", terrainSize);
            cs.SetInt("heightmapResolution", heightmapResolution);
            cs.SetVector("locationPosition", locationRect.min);
            cs.SetVector("locationSize", locationRect.size);
            cs.SetVectorArray("locationPositions", locations.Select(r => new Vector4(r.min.x, r.min.y)).ToArray());
            cs.SetVectorArray("locationSizes", locations.Select(r => new Vector4(r.size.x, r.size.y)).ToArray());
            cs.SetInt("locationCount", locations.Count);
            cs.SetTexture(k, "BiomeMap", InterestingTerrains.biomeMap);
            cs.SetTexture(k, "DerivMap", InterestingTerrains.derivMap);
            cs.SetTexture(k, "tileableNoise", InterestingTerrains.tileableNoise);
            cs.SetFloat("originalHeight", Utility.GetOriginalTerrainHeight());
            cs.SetFloat("newHeight", Constants.TERRAIN_HEIGHT);
            cs.SetTexture(k, "mapPixelHeights", baseHeightmap);
            cs.SetBuffer(k, "heightmapBuffer", heightmapBuffers.heightmapBuffer);
            cs.SetBuffer(k, "rawNoise", heightmapBuffers.rawNoise);
            cs.SetBuffer(k, "locationHeightData", locationHeightData);
            cs.SetVector("worldSize", Utility.GetWorldVertexSize());

            var rd = Compatibility.BasicRoadsUtils.GetRoadData(mapData.mapPixelX, mapData.mapPixelY);

            cs.SetVectorArray("NW_NE_SW_SE", rd.NW_NE_SW_SE);
            cs.SetVectorArray("N_E_S_W", rd.N_E_S_W);

            csParams.ApplyToCS(cs);

            woodsFile.Buffer = originalHeightmapBuffer;
            HandleBaseMapSampleParams(ref mapData, ref cs, k);
            woodsFile.Buffer = alteredHeightmapBuffer;

            cs.Dispatch(k, res / x, res / y, 1);

            k = cs.FindKernel("TilemapComputer");
            cs.SetTexture(k, "BiomeMap", InterestingTerrains.biomeMap);
            cs.SetTexture(k, "DerivMap", InterestingTerrains.derivMap);
            cs.SetBuffer(k, "heightmapBuffer", heightmapBuffers.heightmapBuffer);
            cs.SetBuffer(k, "tilemapData", heightmapBuffers.tilemapData);
            cs.SetBuffer(k, "rawNoise", heightmapBuffers.rawNoise);

            cs.Dispatch(k, res / x, res / y, 1);

            BufferIO.ProcessBufferValuesAndDispose(heightmapBuffers, ref mapData);
        }
Пример #15
0
        public virtual JobHandle ScheduleAssignTilesJob(ITerrainSampler terrainSampler, ref MapPixelData mapData, JobHandle dependencies, bool march = true)
        {
            // Cache tile data to minimise noise sampling during march.
            NativeArray <byte> tileData = new NativeArray <byte>(tileDataDim * tileDataDim, Allocator.TempJob);

            currentMapData = mapData;

            GenerateTileDataJob tileDataJob = new GenerateTileDataJob
            {
                heightmapData    = mapData.heightmapData,
                tileData         = tileData,
                tdDim            = tileDataDim,
                hDim             = terrainSampler.HeightmapDimension,
                maxTerrainHeight = terrainSampler.MaxTerrainHeight,
                oceanElevation   = terrainSampler.OceanElevation,
                beachElevation   = terrainSampler.BeachElevation,
                mapPixelX        = mapData.mapPixelX,
                mapPixelY        = mapData.mapPixelY,
                worldClimate     = mapData.worldClimate,
            };
            JobHandle tileDataHandle = tileDataJob.Schedule(tileDataDim * tileDataDim, 64, dependencies);

            // Schedule the paint roads jobs if basic roads mod is enabled
            JobHandle preAssignTilesHandle = tileDataHandle;

            if (basicRoadsEnabled)
            {
                ModManager.Instance.SendModMessage("BasicRoads", "scheduleRoadsJob", new object[] { mapData, tileData, tileDataHandle },
                                                   (string message, object data) =>
                {
                    if (message == "error")
                    {
                        Debug.LogError(data as string);
                    }
                    else
                    {
                        preAssignTilesHandle = (JobHandle)data;
                    }
                });
            }

            // Assign tile data to terrain
            NativeArray <byte> lookupData     = new NativeArray <byte>(lookupTable, Allocator.TempJob);
            AssignTilesJob     assignTilesJob = new AssignTilesJob
            {
                lookupTable  = lookupData,
                tileData     = tileData,
                tilemapData  = mapData.tilemapData,
                tdDim        = tileDataDim,
                tDim         = assignTilesDim,
                march        = march,
                locationRect = mapData.locationRect,
            };
            JobHandle assignTilesHandle = assignTilesJob.Schedule(assignTilesDim * assignTilesDim, 64, preAssignTilesHandle);

            // Add both working native arrays to disposal list.
            mapData.nativeArrayList.Add(tileData);
            mapData.nativeArrayList.Add(lookupData);

            return(assignTilesHandle);
        }