Advertisement
JontePonte

ChunkGen

Aug 1st, 2024
290
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 15.47 KB | None | 0 0
  1. using System.Collections.Generic;
  2. using Unity.Collections;
  3. using UnityEngine;
  4. using Unity.Mathematics;
  5. using Unity.Jobs;
  6. using UnityEngine.Rendering;
  7.  
  8. public class ChunkGenerator : MonoBehaviour
  9. {
  10.     public Texture2D atlas;
  11.     public Material material;
  12.     public Material waterMaterial;
  13.  
  14.     public Vector2Int chunkDimensions;
  15.     public static Vector2Int ChunkDimensions;
  16.  
  17.     public int maxProccessingFrames;
  18.     public int maxJobsPerFrame;
  19.  
  20.     [Header("Terrain Settings")]
  21.     public int surfaceLevel;
  22.     public int seaLevel;
  23.     public float sandDensity;
  24.     public float maxTreeHeight;
  25.  
  26.  
  27.     [Header("Noise Settings")]
  28.     public float persistance;
  29.     public float frequency;
  30.     public float lacunarity;
  31.     public int numOfOctaves;
  32.     public Vector2Int offset;
  33.     public AnimationCurve heightCurve;
  34.     public float heightMultiplier;
  35.     public float noise3DContribution;
  36.     public int noiseSeed;
  37.  
  38.     [Header(" ")]
  39.     public bool autoUpdate;
  40.     public string executionTime;
  41.  
  42.     Noise noise;
  43.  
  44.     NativeArray<int3> faceVertices;
  45.     NativeArray<int3> faceDirections;
  46.     NativeArray<float2> uvCoordinates;
  47.  
  48.     NativeArray<float> sampledCurve;
  49.  
  50.     [HideInInspector]
  51.     public NativeHashMap<int2, NativeArray<int>> chunkDataMap;
  52.  
  53.     List<JobData> jobList;
  54.     List<JobData> jobQueue;
  55.  
  56.     private void Start()
  57.     {
  58.         Initialize();
  59.         executionTime = "N/A";
  60.     }
  61.  
  62.     private void OnValidate()
  63.     {
  64.         if (autoUpdate)
  65.         {
  66.             GenerateChunk();
  67.         }
  68.     }
  69.  
  70.     public void GenerateChunk()
  71.     {
  72.         Initialize();
  73.  
  74.         if (GameObject.Find("Chunk") == null)
  75.         {
  76.             float startTime = Time.realtimeSinceStartup;
  77.  
  78.             CreateChunkObject(offset);
  79.             CompleteJobs();
  80.  
  81.             executionTime = (Time.realtimeSinceStartup - startTime) * 1000f + " ms";
  82.         }
  83.  
  84.         else
  85.         {
  86.             float startTime = Time.realtimeSinceStartup;
  87.  
  88.             Mesh landMesh = new Mesh
  89.             {
  90.                 indexFormat = IndexFormat.UInt32
  91.             };
  92.  
  93.             Mesh waterMesh = new Mesh
  94.             {
  95.                 indexFormat = IndexFormat.UInt32
  96.             };
  97.  
  98.             CreateChunkMesh(landMesh, waterMesh, offset, true);
  99.             CompleteJobs();
  100.  
  101.             executionTime = (Time.realtimeSinceStartup - startTime) * 1000f + " ms";
  102.  
  103.             GameObject.Find("Chunk").GetComponent<MeshFilter>().mesh = landMesh;
  104.         }
  105.  
  106.         faceVertices.Dispose();
  107.         faceDirections.Dispose();
  108.         uvCoordinates.Dispose();
  109.     }
  110.  
  111.     public GameObject CreateChunkObject(Vector2Int offset)
  112.     {
  113.         GameObject chunk = new GameObject("Chunk");
  114.         var meshRenderer = chunk.AddComponent<MeshRenderer>();
  115.         meshRenderer.sharedMaterial = new Material(material)
  116.         {
  117.             mainTexture = atlas
  118.         };
  119.  
  120.         Mesh landMesh = new Mesh
  121.         {
  122.             indexFormat = IndexFormat.UInt32
  123.         };
  124.  
  125.         Mesh waterMesh = new Mesh
  126.         {
  127.             indexFormat = IndexFormat.UInt32
  128.         };
  129.  
  130.         CreateChunkMesh(landMesh, waterMesh, offset, false);
  131.  
  132.         chunk.AddComponent<MeshFilter>().mesh = landMesh;
  133.  
  134.         //Water is seperate object so it can have transparency
  135.         GameObject water = new GameObject("Water");
  136.         water.transform.parent = chunk.transform;
  137.         water.AddComponent<MeshRenderer>().sharedMaterial = waterMaterial;
  138.         water.AddComponent<MeshFilter>().mesh = waterMesh;
  139.  
  140.         return chunk;
  141.     }
  142.  
  143.     public void CreateChunkMesh(Mesh landMesh, Mesh waterMesh, Vector2Int offset, bool instantCompletion, NativeArray<int> customVoxelValues = default)
  144.     {
  145.         NativeList<float3> vertices = new NativeList<float3>(Allocator.Persistent);
  146.         NativeList<int> triangles = new NativeList<int>(Allocator.Persistent);
  147.         NativeList<float2> uvs = new NativeList<float2>(Allocator.Persistent);
  148.  
  149.         NativeList<float3> waterVertices = new NativeList<float3>(Allocator.Persistent);
  150.         NativeList<int> waterTriangles = new NativeList<int>(Allocator.Persistent);
  151.         NativeList<float2> waterUvs = new NativeList<float2>(Allocator.Persistent);
  152.  
  153.         int2 intOffset = new int2(offset.x, offset.y);
  154.  
  155.         if (customVoxelValues == default)
  156.         {
  157.             if (!chunkDataMap.ContainsKey(intOffset))
  158.                 chunkDataMap.Add(intOffset, noise.GenerateVoxelValues(offset));
  159.         }
  160.         else
  161.         {
  162.             if (!chunkDataMap.ContainsKey(intOffset))
  163.                 chunkDataMap.Add(intOffset, customVoxelValues);
  164.         }
  165.  
  166.         //Neighbouring data is used to determine if faces should be rendered at the border of the chunk
  167.         AddNeighboringChunkData(intOffset, offset);
  168.  
  169.         ChunkJob job = new ChunkJob
  170.         {
  171.             vertices = vertices,
  172.             triangles = triangles,
  173.             uvs = uvs,
  174.  
  175.             waterVertices = waterVertices,
  176.             waterTriangles = waterTriangles,
  177.             waterUvs = waterUvs,
  178.  
  179.             thisChunkData = chunkDataMap[intOffset],
  180.             rightChunkData = chunkDataMap[intOffset + new int2(chunkDimensions.x, 0)],
  181.             leftChunkData = chunkDataMap[intOffset + new int2(-chunkDimensions.x, 0)],
  182.             frontChunkData = chunkDataMap[intOffset + new int2(0, chunkDimensions.x)],
  183.             backChunkData = chunkDataMap[intOffset + new int2(0, -chunkDimensions.x)],
  184.  
  185.             faceDirections = faceDirections,
  186.             uvCordinates = uvCoordinates,
  187.             faceVertices = faceVertices,
  188.             chunkDimensions = new int2(chunkDimensions.x, chunkDimensions.y)
  189.         };
  190.  
  191.         JobHandle handle = job.Schedule();
  192.  
  193.         if (instantCompletion)
  194.         {
  195.             jobList.Add(new JobData
  196.             {
  197.                 mesh = landMesh,
  198.                 waterMesh = waterMesh,
  199.                 handle = handle,
  200.                 vertices = vertices,
  201.                 triangles = triangles,
  202.                 uvs = uvs,
  203.                 waterVertices = waterVertices,
  204.                 waterTriangles = waterTriangles,
  205.                 waterUvs = waterUvs,
  206.                 instantCompletion = true
  207.             });
  208.         }
  209.  
  210.         else
  211.         {
  212.             jobQueue.Add(new JobData
  213.             {
  214.                 mesh = landMesh,
  215.                 waterMesh = waterMesh,
  216.                 handle = handle,
  217.                 vertices = vertices,
  218.                 triangles = triangles,
  219.                 uvs = uvs,
  220.                 waterVertices = waterVertices,
  221.                 waterTriangles = waterTriangles,
  222.                 waterUvs = waterUvs,
  223.                 instantCompletion = false
  224.             });
  225.         }
  226.     }
  227.  
  228.     private void AddNeighboringChunkData(int2 intOffset, Vector2Int offset)
  229.     {
  230.         if (!chunkDataMap.ContainsKey(intOffset + new int2(chunkDimensions.x, 0)))
  231.             chunkDataMap.Add(intOffset + new int2(chunkDimensions.x, 0), noise.GenerateVoxelValues(offset + Vector2Int.right * chunkDimensions.x));
  232.  
  233.         if (!chunkDataMap.ContainsKey(intOffset + new int2(-chunkDimensions.x, 0)))
  234.             chunkDataMap.Add(intOffset + new int2(-chunkDimensions.x, 0), noise.GenerateVoxelValues(offset + Vector2Int.left * chunkDimensions.x));
  235.  
  236.         if (!chunkDataMap.ContainsKey(intOffset + new int2(0, chunkDimensions.x)))
  237.             chunkDataMap.Add(intOffset + new int2(0, chunkDimensions.x), noise.GenerateVoxelValues(offset + Vector2Int.up * chunkDimensions.x));
  238.  
  239.         if (!chunkDataMap.ContainsKey(intOffset + new int2(0, -chunkDimensions.x)))
  240.             chunkDataMap.Add(intOffset + new int2(0, -chunkDimensions.x), noise.GenerateVoxelValues(offset + Vector2Int.down * chunkDimensions.x));
  241.     }
  242.  
  243.  
  244.     public static int CalculateVoxelIndex(int x, int y, int z)
  245.     {
  246.         //Converts 3D index into 1D
  247.         return x * (ChunkDimensions.y * ChunkDimensions.x) + y * ChunkDimensions.x + z;
  248.     }
  249.  
  250.     private void LateUpdate()
  251.     {
  252.         CompleteJobs();
  253.     }
  254.  
  255.     void CompleteJobs()
  256.     {
  257.         for (int i = jobList.Count - 1; i >= 0; i--)
  258.         {
  259.             jobList[i].frameCount++;
  260.  
  261.             if (jobList[i].frameCount - 1 == maxProccessingFrames || jobList[i].handle.IsCompleted || jobList[i].instantCompletion)
  262.             {
  263.                 jobList[i].handle.Complete();
  264.  
  265.                 NativeArray<Vector3> vertexArray = jobList[i].vertices.AsArray().Reinterpret<Vector3>();
  266.                 NativeArray<int> triangleArray = jobList[i].triangles.AsArray();
  267.                 NativeArray<Vector2> uvArray = jobList[i].uvs.AsArray().Reinterpret<Vector2>();
  268.  
  269.                 // Assign data to mesh
  270.                 jobList[i].mesh.SetVertexBufferParams(vertexArray.Length, new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3));
  271.                 jobList[i].mesh.SetVertexBufferData(vertexArray, 0, 0, vertexArray.Length, 0, MeshUpdateFlags.Default);
  272.                 jobList[i].mesh.SetIndexBufferParams(triangleArray.Length, IndexFormat.UInt32);
  273.                 jobList[i].mesh.SetIndexBufferData(triangleArray, 0, 0, triangleArray.Length, MeshUpdateFlags.Default);
  274.                 jobList[i].mesh.SetSubMesh(0, new SubMeshDescriptor(0, triangleArray.Length), MeshUpdateFlags.Default);
  275.                 jobList[i].mesh.SetUVs(0, uvArray);
  276.  
  277.                 jobList[i].mesh.RecalculateBounds();
  278.                 jobList[i].mesh.RecalculateNormals();
  279.  
  280.                 jobList[i].vertices.Dispose();
  281.                 jobList[i].triangles.Dispose();
  282.                 jobList[i].uvs.Dispose();
  283.  
  284.                 vertexArray.Dispose();
  285.                 triangleArray.Dispose();
  286.                 uvArray.Dispose();
  287.  
  288.                 if (jobList[i].waterVertices.Length > 0)
  289.                 {
  290.                     NativeArray<Vector3> wVertexArray = jobList[i].waterVertices.AsArray().Reinterpret<Vector3>();
  291.                     NativeArray<int> wTriangleArray = jobList[i].waterTriangles.AsArray();
  292.                     NativeArray<Vector2> wUvArray = jobList[i].waterUvs.AsArray().Reinterpret<Vector2>();
  293.  
  294.                     // Assign data to mesh
  295.                     jobList[i].waterMesh.SetVertexBufferParams(wVertexArray.Length, new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3));
  296.                     jobList[i].waterMesh.SetVertexBufferData(wVertexArray, 0, 0, wVertexArray.Length, 0, MeshUpdateFlags.Default);
  297.                     jobList[i].waterMesh.SetIndexBufferParams(wTriangleArray.Length, IndexFormat.UInt32);
  298.                     jobList[i].waterMesh.SetIndexBufferData(wTriangleArray, 0, 0, wTriangleArray.Length, MeshUpdateFlags.Default);
  299.                     jobList[i].waterMesh.SetSubMesh(0, new SubMeshDescriptor(0, wTriangleArray.Length), MeshUpdateFlags.Default);
  300.                     jobList[i].waterMesh.SetUVs(0, wUvArray);
  301.  
  302.                     jobList[i].waterMesh.RecalculateBounds();
  303.                     jobList[i].waterMesh.RecalculateNormals();
  304.  
  305.                     jobList[i].waterVertices.Dispose();
  306.                     jobList[i].waterTriangles.Dispose();
  307.                     jobList[i].waterUvs.Dispose();
  308.  
  309.                     wVertexArray.Dispose();
  310.                     wTriangleArray.Dispose();
  311.                     wUvArray.Dispose();
  312.                 }
  313.  
  314.                 jobList.RemoveAt(i);
  315.             }
  316.         }
  317.  
  318.         if(jobList.Count < maxJobsPerFrame && jobQueue.Count > 0)
  319.         {
  320.             int length = Mathf.Min(maxJobsPerFrame, jobQueue.Count);
  321.  
  322.             for (int i = 0; i < length; i++)
  323.             {
  324.                 jobList.Add(jobQueue[i]);
  325.             }
  326.  
  327.             jobQueue.RemoveRange(0, length);
  328.         }
  329.     }
  330.  
  331.     class JobData
  332.     {
  333.         public JobHandle handle;
  334.  
  335.         public Mesh mesh;
  336.         public Mesh waterMesh;
  337.  
  338.         public NativeList<float3> vertices;
  339.         public NativeList<int> triangles;
  340.         public NativeList<float2> uvs;
  341.  
  342.         public NativeList<float3> waterVertices;
  343.         public NativeList<int> waterTriangles;
  344.         public NativeList<float2> waterUvs;
  345.  
  346.         public int frameCount;
  347.         public bool instantCompletion;
  348.     }
  349.  
  350.     //Sampled curve that can be used in job
  351.     private NativeArray<float> SampleCurve(AnimationCurve curve, int resolution)
  352.     {
  353.         NativeArray<float> curveArr = new NativeArray<float>(resolution, Allocator.Persistent);
  354.  
  355.         for (int i = 0; i < resolution; i++)
  356.         {
  357.             curveArr[i] = curve.Evaluate((float)i / resolution);
  358.         }
  359.  
  360.         return curveArr;
  361.     }
  362.  
  363.     private void Initialize()
  364.     {
  365.         System.Random rand = new System.Random(noiseSeed);
  366.  
  367.         chunkDataMap = new NativeHashMap<int2, NativeArray<int>>(1024, Allocator.Persistent);
  368.         jobList = new();
  369.         jobQueue = new();
  370.  
  371.         sampledCurve = SampleCurve(heightCurve, 256);
  372.  
  373.         ChunkDimensions = chunkDimensions;
  374.  
  375.         noise = new Noise(frequency, persistance, lacunarity, numOfOctaves, heightMultiplier, rand.Next() / 10000, surfaceLevel, seaLevel, noise3DContribution, sandDensity, maxTreeHeight, sampledCurve);
  376.  
  377.         faceVertices = new NativeArray<int3>(24, Allocator.Persistent);
  378.  
  379.         //Bottom face
  380.         faceVertices[0] = new int3(1, 0, 0);
  381.         faceVertices[1] = new int3(1, 0, 1);
  382.         faceVertices[2] = new int3(0, 0, 1);
  383.         faceVertices[3] = new int3(0, 0, 0);
  384.  
  385.         //Front face
  386.         faceVertices[4] = new int3(1, 0, 1);
  387.         faceVertices[5] = new int3(1, 1, 1);
  388.         faceVertices[6] = new int3(0, 1, 1);
  389.         faceVertices[7] = new int3(0, 0, 1);
  390.  
  391.         //Back face
  392.         faceVertices[8] = new int3(0, 0, 0);
  393.         faceVertices[9] = new int3(0, 1, 0);
  394.         faceVertices[10] = new int3(1, 1, 0);
  395.         faceVertices[11] = new int3(1, 0, 0);
  396.  
  397.         //Left face
  398.         faceVertices[12] = new int3(0, 0, 1);
  399.         faceVertices[13] = new int3(0, 1, 1);
  400.         faceVertices[14] = new int3(0, 1, 0);
  401.         faceVertices[15] = new int3(0, 0, 0);
  402.  
  403.         //Right face
  404.         faceVertices[16] = new int3(1, 0, 0);
  405.         faceVertices[17] = new int3(1, 1, 0);
  406.         faceVertices[18] = new int3(1, 1, 1);
  407.         faceVertices[19] = new int3(1, 0, 1);
  408.  
  409.         //Top face
  410.         faceVertices[20] = new int3(0, 1, 0);
  411.         faceVertices[21] = new int3(0, 1, 1);
  412.         faceVertices[22] = new int3(1, 1, 1);
  413.         faceVertices[23] = new int3(1, 1, 0);
  414.  
  415.  
  416.         uvCoordinates = new NativeArray<float2>(6, Allocator.Persistent);
  417.  
  418.         uvCoordinates[0] = new float2(.5f, 0);
  419.         uvCoordinates[1] = new float2(.5f, .5f);
  420.         uvCoordinates[2] = new float2(.5f, .5f);
  421.         uvCoordinates[3] = new float2(.5f, .5f);
  422.         uvCoordinates[4] = new float2(.5f, .5f);
  423.         uvCoordinates[5] = new float2(0, 0);
  424.  
  425.  
  426.         faceDirections = new NativeArray<int3>(6, Allocator.Persistent);
  427.  
  428.         faceDirections[0] = new int3(0, -1, 0); // Down
  429.         faceDirections[1] = new int3(0, 0, 1);  // Forward
  430.         faceDirections[2] = new int3(0, 0, -1); // Back
  431.         faceDirections[3] = new int3(-1, 0, 0); // Left
  432.         faceDirections[4] = new int3(1, 0, 0);  // Right
  433.         faceDirections[5] = new int3(0, 1, 0);  // Up
  434.     }
  435.  
  436.  
  437.  
  438.     private void OnDestroy()
  439.     {
  440.         faceVertices.Dispose();
  441.         faceDirections.Dispose();
  442.         uvCoordinates.Dispose();
  443.         sampledCurve.Dispose();
  444.         noise.Dispose();
  445.  
  446.         foreach (var chunk in chunkDataMap)
  447.         {
  448.             chunk.Value.Dispose();
  449.         }
  450.  
  451.         chunkDataMap.Dispose();
  452.     }
  453. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement