/// <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)
            });
        }
        /// <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);
        }