/// <summary> /// Works the same as <see cref="GenerateCoroutine"/> without the /// yield instructions. Runs synchronously. /// </summary> /// <param name="remapMin"></param> /// <param name="remapMax"></param> private void GenerateEditor(float remapMin = 0f, float remapMax = 1f) { TerraConfig.Log($"Started tile {GridPosition.ToString()}"); //Make & set heightmap MeshManager.CalculateHeightmap(GridPosition, remapMin, remapMax); MeshManager.SetTerrainHeightmap(); MeshManager.SetVisible(); //Create TilePaint object TilePaint painter = new TilePaint(this); //Make biomemap int[,] map = painter.GetBiomeMap(); //Paint terrain painter.Paint(map); //Apply details to terrain // ReSharper disable once IteratorMethodResultIsIgnored ApplyDetails(painter, map); MeshManager.SetVisible(true); TerraConfig.Log("Completed tile " + GridPosition); }
private IEnumerator GenerateCoroutine(Action onComplete, float remapMin = 0f, float remapMax = 1f) { TerraConfig conf = TerraConfig.Instance; TerraConfig.Log("Started tile " + GridPosition); //Make & set heightmap bool madeHm = false; MeshManager.CalculateHeightmapAsync(remapMin, remapMax, () => madeHm = true); while (!madeHm) { yield return(null); } MeshManager.SetTerrainHeightmap(); MeshManager.SetVisible(); //Create TilePaint object TilePaint painter = new TilePaint(this); //Make biomemap bool madeBm = false; int[,] map = null; conf.Worker.Enqueue(() => map = painter.GetBiomeMap(), () => madeBm = true); while (!madeBm) { yield return(null); //Skip frame until biomemap made } //Paint terrain bool madePaint = false; yield return(StartCoroutine(painter.PaintAsync(map, () => madePaint = true))); while (!madePaint) { yield return(null); } //Apply details to terrain bool madeDetails = false; yield return(StartCoroutine(ApplyDetails(painter, map, () => madeDetails = true))); while (!madeDetails) { yield return(null); } MeshManager.SetVisible(true); TerraConfig.Log("Completed tile " + GridPosition); onComplete(); }
/// <summary> /// Calculates the min and maximum values to use when applying a heightmap /// remap. This sets <see cref="RemapMax"/> and <see cref="RemapMin"/>. /// </summary> public void CalculateHeightmapRemap() { bool shouldProfile = TerraConfig.Instance.EditorState.ShowDebugMessages; Stopwatch sw = null; if (shouldProfile) { sw = new Stopwatch(); sw.Start(); } float min = float.PositiveInfinity; float max = float.NegativeInfinity; int res = Config.Generator.RemapResolution; var generator = Config.Graph.GetEndGenerator(); for (int x = 0; x < res; x++) { for (int z = 0; z < res; z++) { float value = generator.GetValue(x / (float)res, z / (float)res, 0f); if (value > max) { max = value; } if (value < min) { min = value; } } } //Set remap values for instance _remapMin = min; _remapMax = max; if (shouldProfile) { sw.Stop(); TerraConfig.Log("CalculateHeightmapRemap took " + sw.ElapsedMilliseconds + "ms to complete. " + "New min=" + min + " New max=" + max); } }
/// <summary> /// Sets the heightmap to the current LOD asynchronously /// </summary> public IEnumerator UpdateHeightmapAsync(Action onComplete, float remapMin, float remapMax) { MeshManager.Lod = GetLodLevel(); bool updatedHm = false; TerraConfig.Log("Updating heightmap start"); MeshManager.CalculateHeightmapAsync(remapMin, remapMax, () => { MeshManager.SetTerrainHeightmap(); updatedHm = true; }); while (!updatedHm) { yield return(null); } if (onComplete != null) { onComplete(); } }
/// <summary> /// Creates a heightmap of resolution <see cref="HeightmapResolution"/> asynchronously. /// If a <see cref="Heightmap"/> of the same resolution or higher has already been /// created, this method does nothing. /// A heightmap is 2D array of floats that represents the Y values (or heights) /// of to-be created vertices in 3D space. /// </summary> /// <param name="remapMin">Optionally linear transform the heightmap from [min, max] to [0, 1]</param> /// <param name="remapMax">Optionally linear transform the heightmap from [min, max] to [0, 1]</param> /// <param name="onComplete">Called when the heightmap has been created</param> public void CalculateHeightmapAsync(float remapMin = 0f, float remapMax = 1f, Action onComplete = null) { _lastGeneratedLodLevel = _tile.GetLodLevel(); Lod = _lastGeneratedLodLevel; bool shouldProfile = TerraConfig.Instance.EditorState.ShowDebugMessages; Stopwatch wsw = null; if (shouldProfile) { wsw = new Stopwatch(); wsw.Start(); } TerraConfig.Instance.Worker.Enqueue(() => { Stopwatch sw = null; if (shouldProfile) { sw = new Stopwatch(); Profiler.BeginThreadProfiling("Heightmap Workers", "Tile " + _tile.GridPosition); sw.Start(); } CalculateHeightmap(null, remapMin, remapMax); if (shouldProfile) { sw.Stop(); TerraConfig.Log("Tile " + _tile.GridPosition + " heightmap time: " + sw.ElapsedMilliseconds); Profiler.EndThreadProfiling(); } }, () => { if (shouldProfile) { wsw.Stop(); MTDispatch.Instance().Enqueue(() => { TerraConfig.Log("CalculateHM worker time elapsed " + wsw.ElapsedMilliseconds); onComplete(); }); } }); }
/// <summary> /// Updates tiles that are surrounding the tracked GameObject /// asynchronously. When calling this method using /// <see cref="MonoBehaviour.StartCoroutine(IEnumerator)"/>, /// tiles are generated once per frame /// </summary> public IEnumerator UpdateTiles() { List <GridPosition> nearbyPositions = GetTilePositionsFromRadius(); List <GridPosition> newPositions = Cache.GetNewTilePositions(nearbyPositions); List <Tile> toRegenerate = new List <Tile>(); RemoveOldPositions(ref nearbyPositions, ref toRegenerate); //Add new positions _queuedTiles = newPositions.Count + toRegenerate.Count; foreach (GridPosition pos in newPositions) { if (_isGenerating && Application.isPlaying) //Wait and generate one tile per frame { yield return(null); } Tile cached = Cache.GetCachedTileAtPosition(pos); //Attempt to pull from cache, generate if not available if (cached != null) { if (cached.IsHeightmapLodValid()) //Cached tile is valid, use it { AddTile(cached); _queuedTiles--; continue; } TerraConfig.Log("Cached tile " + cached + " has heightmap res=" + cached.MeshManager.HeightmapResolution + ". requested res=" + cached.GetLodLevel().Resolution + ". Regenerating."); toRegenerate.Add(cached); Cache.AddActiveTile(cached); continue; } _isGenerating = true; CreateTileAt(pos, tile => { _queuedTiles--; if (_queuedTiles == 0) { UpdateNeighbors(newPositions.ToArray(), false); } _isGenerating = false; }); } //Regenerate tiles with outdated heightmaps for (int i = 0; i < toRegenerate.Count; i++) { Tile t = toRegenerate[i]; TerraConfig.Log("Active tile " + t + " has heightmap res=" + t.MeshManager.HeightmapResolution + ". requested res=" + t.GetLodLevel().Resolution + ". Regenerating."); //Generate one tile per frame if (Application.isPlaying) { yield return(null); } if (Application.isPlaying) { Config.StartCoroutine(t.UpdateHeightmapAsync(() => { UpdateNeighbors(new[] { t.GridPosition }, false); _queuedTiles--; }, RemapMin, RemapMax)); } else { t.UpdateHeightmap(RemapMin, RemapMax); } } //If tiles were updated synchronously, notify queue completion if (newPositions.Count > 0 && _queuedTiles == 0) { UpdateNeighbors(newPositions.ToArray(), false); } }