Advertisement
JontePonte

messy chunkGen

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