Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

[Help] Infinite 3D Terrain Generation

Discussion in 'Scripting' started by sjoerdev, Jun 4, 2020.

  1. sjoerdev

    sjoerdev

    Joined:
    Jan 4, 2020
    Posts:
    9
    Need some help on my infinite 3d perlin noise terrain generator.

    the terrain looks and generates just fine, but when my character/camera moves to far from the worlds origin, some chunks just dont generate, so after a while the entire terrain just stops generating all together. only when i move the character closer to the worlds origin will the terrain/chunks start generating again!

    here is an video on YouTube showing the problem (around 1:25 it shows up)


    I will insert some code and pictures below.
    I really hope someone can help me with this.

    Image of the inspector with all the scripts
    Aantekening 2020-06-04 152701.png

    Script called EndlessTerrain:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using MarchingCubesProject;
    5.  
    6. public class EndlessTerrain : MonoBehaviour
    7. {
    8.     public bool regenerateConstantly = false;
    9.     public int seed = 40;
    10.     public float zoom = 1;
    11.     public int complexity = 1;
    12.     public const float maxViewDst = 10;
    13.     public const int chunkSize = 10;
    14.     public Transform viewer;
    15.     public static Vector3 viewerPosition;
    16.     int chunksVisibleInViewDst;
    17.  
    18.     Dictionary<Vector3, TerrainChunk> terrainChunkDictionary = new Dictionary<Vector3, TerrainChunk>();
    19.     List<TerrainChunk> terrainChunksVisibleLastUpdate = new List<TerrainChunk>();
    20.  
    21.     void Start()
    22.     {
    23.         chunksVisibleInViewDst = Mathf.RoundToInt(maxViewDst / chunkSize);
    24.         InvokeRepeating("everysec", 1, 1);
    25.     }
    26.  
    27.     void everysec()
    28.     {
    29.         if (regenerateConstantly)
    30.         {
    31.             regenerate();
    32.         }
    33.         else
    34.         {
    35.             return;
    36.         }
    37.     }
    38.  
    39.     void Update()
    40.     {
    41.         viewerPosition = new Vector3(viewer.position.x, viewer.position.z, viewer.position.y);
    42.         UpdateVisibleChunks();
    43.     }
    44.  
    45.     public void regenerate()
    46.     {
    47.         terrainChunkDictionary.Clear();
    48.         terrainChunksVisibleLastUpdate.Clear();
    49.  
    50.         //destroy old terrain
    51.         GameObject[] all = GameObject.FindGameObjectsWithTag("meshtag");
    52.         foreach (GameObject uno in all)
    53.         GameObject.DestroyImmediate(uno);
    54.     }
    55.  
    56.  
    57.  
    58.     void UpdateVisibleChunks()
    59.     {
    60.         for (int i = 0; i < terrainChunksVisibleLastUpdate.Count; i++)
    61.         {
    62.             terrainChunksVisibleLastUpdate[i].SetVisible(false);
    63.         }
    64.  
    65.         terrainChunksVisibleLastUpdate.Clear();
    66.  
    67.         int currentChunkCoordX = Mathf.RoundToInt(viewerPosition.x / chunkSize);
    68.         int currentChunkCoordY = Mathf.RoundToInt(viewerPosition.y / chunkSize);
    69.         int currentChunkCoordZ = Mathf.RoundToInt(viewerPosition.z / chunkSize);
    70.  
    71.  
    72.  
    73.         for (int yOffset = -chunksVisibleInViewDst; yOffset <= chunksVisibleInViewDst; yOffset++)
    74.         {
    75.             for (int xOffset = -chunksVisibleInViewDst; xOffset <= chunksVisibleInViewDst; xOffset++)
    76.             {
    77.                 for (int zOffset = -chunksVisibleInViewDst; zOffset <= chunksVisibleInViewDst; zOffset++)
    78.                 {
    79.                     Vector3 viewedChunkCoord = new Vector3(currentChunkCoordX + xOffset, currentChunkCoordY + yOffset, currentChunkCoordZ + zOffset);
    80.  
    81.                     if (terrainChunkDictionary.ContainsKey(viewedChunkCoord))
    82.                     {
    83.                         terrainChunkDictionary[viewedChunkCoord].UpdateTerrainChunk();
    84.  
    85.                         if (terrainChunkDictionary[viewedChunkCoord].IsVisible())
    86.                         {
    87.                             terrainChunksVisibleLastUpdate.Add(terrainChunkDictionary[viewedChunkCoord]);
    88.                         }
    89.                     }
    90.                     else
    91.                     {
    92.                         terrainChunkDictionary.Add(viewedChunkCoord, new TerrainChunk(viewedChunkCoord, chunkSize -1));
    93.                     }
    94.                 }
    95.             }
    96.         }
    97.     }
    98.  
    99.     public class TerrainChunk
    100.     {
    101.         GameObject meshObject;
    102.         Vector3 position;
    103.         Bounds bounds;
    104.  
    105.         public TerrainChunk(Vector3 coord, int size)
    106.         {
    107.             position = coord * size;
    108.             bounds = new Bounds(position, Vector3.one * size);
    109.             Vector3 positionV3 = new Vector3(position.x, position.z, position.y);
    110.  
    111.  
    112.             var s = GameObject.FindObjectOfType<EndlessTerrain>().GetComponent<EndlessTerrain>().seed;
    113.             var z = GameObject.FindObjectOfType<EndlessTerrain>().GetComponent<EndlessTerrain>().zoom;
    114.             var c =
    115. GameObject.FindObjectOfType<EndlessTerrain>().GetComponent<EndlessTerrain>().complexity;
    116.  
    117.             meshObject = GameObject.FindGameObjectWithTag("generator").GetComponent<Example>().calculateChunk(s, z, c, chunkSize, positionV3);
    118.  
    119.          
    120.             SetVisible(false);
    121.         }
    122.  
    123.  
    124.  
    125.         public void UpdateTerrainChunk()
    126.         {
    127.             float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
    128.             bool visible = viewerDstFromNearestEdge <= maxViewDst;
    129.             SetVisible(visible);
    130.         }
    131.         public void SetVisible(bool visible)
    132.         {
    133.             meshObject.SetActive(visible);
    134.         }
    135.         public bool IsVisible()
    136.         {
    137.             return meshObject.activeSelf;
    138.         }
    139.     }
    140. }
    Script Called Example:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3. using ProceduralNoiseProject;
    4.  
    5. namespace MarchingCubesProject
    6. {
    7.     public enum MARCHING_MODE { CUBES, TETRAHEDRON };
    8.     public class Example : MonoBehaviour
    9.     {
    10.         public Material m_material;
    11.         public MARCHING_MODE mode = MARCHING_MODE.CUBES;
    12.         List<GameObject> meshes = new List<GameObject>();
    13.  
    14.         public GameObject calculateChunk(int seed, float zoom, int complexity, int chunksize, Vector3 fposition)
    15.         {
    16.             INoise perlin = new PerlinNoise(seed, zoom);
    17.             FractalNoise fractal = new FractalNoise(perlin, complexity, 1f);
    18.  
    19.             //zet de modus (cube of tetrahedron)
    20.             //cube modus is een stuk sneller
    21.             Marching marching = null;
    22.             if (mode == MARCHING_MODE.TETRAHEDRON)
    23.                 marching = new MarchingTertrahedron();
    24.             else
    25.                 marching = new MarchingCubes();
    26.  
    27.             //surface is de variablele die het oppervlakte van de mesh bepaalt
    28.             //perlin noise is van -1 tot 1, dus dan moet de surface 0 zijn
    29.             //maar het hoeft niet perse nul te zijn
    30.             marching.Surface = 0.0f;
    31.  
    32.             //het formaat van de voxel array
    33.             float[] voxels = new float[chunksize * chunksize * chunksize];
    34.  
    35.             //hier krijgen de voxels een noise waarde
    36.             for (int x = 0; x < chunksize; x++)
    37.             {
    38.                 for (int y = 0; y < chunksize; y++)
    39.                 {
    40.                     for (int z = 0; z < chunksize; z++)
    41.                     {
    42.                         float fx = (x + fposition.x) / (chunksize - 1.0f);
    43.                         float fy = (y + fposition.y) / (chunksize - 1.0f);
    44.                         float fz = (z + fposition.z) / (chunksize - 1.0f);
    45.  
    46.                         int idx = x + y * chunksize + z * chunksize * chunksize;
    47.                      
    48.                         voxels[idx] = fractal.Sample3D(fx, fy, fz);
    49.                     }
    50.                 }
    51.             }
    52.  
    53.             List<Vector3> verts = new List<Vector3>();
    54.             List<int> indices = new List<int>();
    55.  
    56.             marching.Generate(voxels, chunksize, chunksize, chunksize, verts, indices);
    57.  
    58.             //een mesh kan maar bestaan uit 65000 vertexes!!
    59.  
    60.             var go = new GameObject("Mesh");
    61.  
    62.             Mesh mesh = new Mesh();
    63.             mesh.SetVertices(verts);
    64.             mesh.SetTriangles(indices, 0);
    65.             mesh.RecalculateBounds();
    66.             mesh.RecalculateNormals();
    67.  
    68.             go.transform.parent = transform;
    69.             go.AddComponent<MeshFilter>();
    70.             go.AddComponent<MeshRenderer>();
    71.             go.GetComponent<Renderer>().material = m_material;
    72.             go.GetComponent<MeshFilter>().mesh = mesh;
    73.             var goCollider = go.AddComponent<MeshCollider>();
    74.             goCollider.sharedMesh = mesh;
    75.             go.transform.localPosition = Vector3.zero;
    76.  
    77.             go.transform.position = fposition;
    78.             go.transform.localScale = Vector3.one;
    79.             go.transform.parent = gameObject.transform;
    80.  
    81.             meshes.Add(go);
    82.             go.tag = "meshtag";
    83.             return (go);
    84.         }
    85.  
    86.         public void DestroyMesh()
    87.         {
    88.             GameObject[] all = GameObject.FindGameObjectsWithTag("meshtag");
    89.             foreach (GameObject uno in all)
    90.             GameObject.DestroyImmediate(uno);
    91.         }
    92.     }
    93. }
     
    Last edited: Jun 4, 2020
  2. sjoerdev

    sjoerdev

    Joined:
    Jan 4, 2020
    Posts:
    9
    nvm, i fixed it. turns ou i was calculating every chunks position 1m short. only problem is, that i have these gaps between my chunks meshes now:
     

    Attached Files:

  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,520
    I see you're using a terrain chunk dictionary indexed by vertex... is that why it is discarding edge shared verts because it thinks they belong to another chunk??
     
  4. sjoerdev

    sjoerdev

    Joined:
    Jan 4, 2020
    Posts:
    9
    i dont really know how it works, as long as it does im good ;)