Skip to content
Snippets Groups Projects
TerrainGenerator.cs 6.92 KiB
Newer Older
Chang, Ryan's avatar
Chang, Ryan committed
using System.Threading;
Chang, Ryan's avatar
Chang, Ryan committed
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Mathematics;
Chang, Ryan's avatar
Chang, Ryan committed

[System.Serializable]
public struct Octave
{
    public float amplitude;
    public float frequency;
    public NoiseType type;
}

[System.Serializable]
public enum NoiseType
{
    MathfPerlin,
    MathematicsPerlin,
    Simplex,
    Celluar
Chang, Ryan's avatar
Chang, Ryan committed
}

Chang, Ryan's avatar
Chang, Ryan committed
public class TerrainGenThread
{
    private Vector2 initialCrawlPosition;
Chang, Ryan's avatar
Chang, Ryan committed
    private List<Octave> octaves;
    private int xSamples, ySamples;
Chang, Ryan's avatar
Chang, Ryan committed
    private TerrainData terrainData;
Chang, Ryan's avatar
Chang, Ryan committed
    private float[,] heights;
    private bool ready;
Chang, Ryan's avatar
Chang, Ryan committed

Chang, Ryan's avatar
Chang, Ryan committed
    public TerrainGenThread(Vector2 initialCrawlPos, List<Octave> octaves, int xSamples, int ySamples, out TerrainData data)
Chang, Ryan's avatar
Chang, Ryan committed
    {
        initialCrawlPosition = initialCrawlPos;
Chang, Ryan's avatar
Chang, Ryan committed
        this.octaves = octaves;
        this.xSamples = xSamples;
        this.ySamples = ySamples;
Chang, Ryan's avatar
Chang, Ryan committed
        data = new();
        data.size = new(512, 512, 512);
        terrainData = data;
    }

Chang, Ryan's avatar
Chang, Ryan committed
    public float[,] GetHeights()
    {
        if (ready)
            return heights;
        else
            throw new System.InvalidOperationException("Heights not built yet.");
    }

Chang, Ryan's avatar
Chang, Ryan committed
    public void Generate()
    {
Chang, Ryan's avatar
Chang, Ryan committed
        heights = new float[xSamples, ySamples];

        foreach (var octave in octaves)
        {
            for (int x = 0; x < xSamples; x++)
            {
                for (int y = 0; y < ySamples; y++)
                {
                    float2 crawlPos = new(x * octave.frequency + initialCrawlPosition.x, y * octave.frequency + initialCrawlPosition.y);
                    float addition;
Chang, Ryan's avatar
Chang, Ryan committed

Chang, Ryan's avatar
Chang, Ryan committed
                    switch (octave.type)
                    {
                        default:
                        case NoiseType.MathfPerlin:
                            addition = Mathf.PerlinNoise(crawlPos.x, crawlPos.y);
                            break;
                        case NoiseType.MathematicsPerlin:
                            addition = noise.cnoise(crawlPos) / 2.3f + 0.5f;
                            break;
                        case NoiseType.Simplex:
                            addition = noise.snoise(crawlPos) / 4.6f + 0.5f;
                            break;
                        case NoiseType.Celluar:
                            addition = noise.cellular(crawlPos).x / 2.3f + 0.5f;
                            break;
                    }
                    heights[x, y] += addition * octave.amplitude;
                }
            }
        }

        ready = true;

        //terrainData.SetHeights(0, 0, heights);

        //GameObject obj = Terrain.CreateTerrainGameObject(terrainData);
        //obj.name = initialCrawlPosition.ToString() + "Terrain";
Chang, Ryan's avatar
Chang, Ryan committed
public class TerrainGenerator : MonoBehaviour
{
Chang, Ryan's avatar
Chang, Ryan committed
    [Header("User settings")]
Chang, Ryan's avatar
Chang, Ryan committed
    public Terrain terrain;

    public List<Octave> octaves;

    [Tooltip("The seed of the generation.")]
    public Vector2 crawlStartPosition;

    public bool generateNewSeed = true;

    public bool refresh;
Chang, Ryan's avatar
Chang, Ryan committed

    //[Tooltip("How many terrain elements are there?")]
    //public Vector2Int terrainsSize = new(1,1);

    [Tooltip("How fine grain the generated terrain will be. " +
        "Larger values = more precise. Recommended value is 513.")]
    public int xSamples = 513, ySamples = 513;
Chang, Ryan's avatar
Chang, Ryan committed

Chang, Ryan's avatar
Chang, Ryan committed
    [Header("Autogenerated values")]
    [Tooltip("Which terrain grid are we on right now?")]
    public Vector2Int currentGridPosition;

    [Tooltip("Dictionary of previously loaded terrains.")]
Chang, Ryan's avatar
Chang, Ryan committed
    public Dictionary<Vector2Int, TerrainData> previouslyLoaded = new();
Chang, Ryan's avatar
Chang, Ryan committed

    /// <summary>
    /// 4 threads for each cardinal direction. Responsible for creating
    /// their respective terrains in each cardinal direction.
    /// </summary>
    private Thread northThread, eastThread, southThread, westThread;

Chang, Ryan's avatar
Chang, Ryan committed
    private void GenerateThread(Vector2 crawlPos, Vector2Int gridPos, ref Thread thread)
Chang, Ryan's avatar
Chang, Ryan committed
    {
Chang, Ryan's avatar
Chang, Ryan committed
        if (!previouslyLoaded.ContainsKey(gridPos))
Chang, Ryan's avatar
Chang, Ryan committed
        {
Chang, Ryan's avatar
Chang, Ryan committed
            TerrainGenThread genThread = new(crawlPos, octaves, xSamples, ySamples, out TerrainData data);
            thread = new Thread(new ThreadStart(genThread.Generate));
            previouslyLoaded[gridPos] = data;
            thread.Start();
            thread.Join();
            data.SetHeights(0, 0, genThread.GetHeights());

            GameObject obj = Terrain.CreateTerrainGameObject(data);
            obj.name = crawlPos.ToString() + "Terrain";
Chang, Ryan's avatar
Chang, Ryan committed
        }
Chang, Ryan's avatar
Chang, Ryan committed
    }

    public virtual void GenerateSurronding(Vector2 initCrawlPos)
    {
        GenerateThread(initCrawlPos + Vector2.up * ySamples,
            currentGridPosition + Vector2Int.up, ref northThread);
        //GenerateThread(initCrawlPos + Vector2.down * ySamples,
        //    currentGridPosition + Vector2Int.down, ref southThread);
        //GenerateThread(initCrawlPos + Vector2.right * xSamples,
        //    currentGridPosition + Vector2Int.right, ref eastThread);
        //GenerateThread(initCrawlPos + Vector2.left * xSamples,
        //    currentGridPosition + Vector2Int.left, ref westThread);
Chang, Ryan's avatar
Chang, Ryan committed

Chang, Ryan's avatar
Chang, Ryan committed
        //northThread.Join();
Chang, Ryan's avatar
Chang, Ryan committed

Chang, Ryan's avatar
Chang, Ryan committed
        //terrainData.SetHeights(0, 0, heights);

        //GameObject obj = Terrain.CreateTerrainGameObject(terrainData);
        //obj.name = initialCrawlPosition.ToString() + "Terrain";
Chang, Ryan's avatar
Chang, Ryan committed
    public virtual void Generate()
Chang, Ryan's avatar
Chang, Ryan committed
    {
        float[,] heights = new float[xSamples, ySamples];

Chang, Ryan's avatar
Chang, Ryan committed
            crawlStartPosition = RNG.GetRandomVector2(-xSamples, -ySamples, xSamples, ySamples);
Chang, Ryan's avatar
Chang, Ryan committed

        foreach (var octave in octaves)
        {
            for (int x = 0; x < xSamples; x++)
            {
                for (int y = 0; y < ySamples; y++)
                {
                    float2 crawlPos = new(x * octave.frequency + crawlStartPosition.x, y * octave.frequency + crawlStartPosition.y);
                    float addition;

                    switch (octave.type)
                    {
                        default:
                        case NoiseType.MathfPerlin:
                            addition = Mathf.PerlinNoise(crawlPos.x, crawlPos.y);
                            break;
                        case NoiseType.MathematicsPerlin:
                            addition = noise.cnoise(crawlPos) / 2.3f + 0.5f;
                            break;
                        case NoiseType.Simplex:
                            addition = noise.snoise(crawlPos) / 4.6f + 0.5f;
                            break;
                        case NoiseType.Celluar:
                            addition = noise.cellular(crawlPos).x / 2.3f + 0.5f;
                            break;
                    }
                    heights[x, y] += addition * octave.amplitude;
                }
            }
        }

        terrain.terrainData.SetHeights(0, 0, heights);
    }

Chang, Ryan's avatar
Chang, Ryan committed
    private void Start()
    {
Chang, Ryan's avatar
Chang, Ryan committed
        //Generate();

        GenerateSurronding(new Vector2(0, 0));
Chang, Ryan's avatar
Chang, Ryan committed
    }

    private void Update()
    {
Chang, Ryan's avatar
Chang, Ryan committed
        //if (refresh)
        //{
        //    refresh = false;
        //    Generate();
        //}
Chang, Ryan's avatar
Chang, Ryan committed
    }
}