Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System.Collections.Generic;
- using Unity.Collections;
- using UnityEngine;
- using Unity.Mathematics;
- using Unity.Jobs;
- using UnityEngine.Rendering;
- using UnityEngine.UI;
- using UnityTemplateProjects;
- public class ChunkGenerator : MonoBehaviour
- {
- public Texture2D atlas;
- public Material material;
- public Material waterMaterial;
- public Image loadingScreen;
- public SimpleCameraController player;
- public Vector2Int chunkDimensions;
- public static Vector2Int ChunkDimensions;
- public int maxProccessingFrames;
- public int maxJobsPerFrame;
- [Header("Terrain Settings")]
- public int surfaceLevel;
- public int seaLevel;
- public float sandDensity;
- public float maxTreeHeight;
- [Header("Noise Settings")]
- public float persistance;
- public float frequency;
- public float lacunarity;
- public int numOfOctaves;
- public Vector2Int offset;
- public AnimationCurve heightCurve;
- public float heightMultiplier;
- public float noise3DContribution;
- public int noiseSeed;
- [Header(" ")]
- public bool autoUpdate;
- public string executionTime;
- Noise noise;
- NativeArray<int3> faceVertices;
- NativeArray<int3> faceDirections;
- NativeArray<float2> uvCoordinates;
- NativeArray<float> sampledCurve;
- [HideInInspector]
- public NativeHashMap<int2, NativeArray<int>> chunkDataMap;
- List<JobData> jobList;
- List<JobData> jobQueue;
- bool loadedTerrain;
- private void Start()
- {
- Initialize();
- executionTime = "N/A";
- player.enabled = false;
- loadingScreen.enabled = true;
- }
- public GameObject CreateChunkObject(Vector2Int offset)
- {
- GameObject chunk = new GameObject(transform.childCount.ToString());
- var meshRenderer = chunk.AddComponent<MeshRenderer>();
- meshRenderer.sharedMaterial = new Material(material)
- {
- mainTexture = atlas
- };
- Mesh landMesh = new Mesh
- {
- indexFormat = IndexFormat.UInt32
- };
- Mesh waterMesh = new Mesh
- {
- indexFormat = IndexFormat.UInt32
- };
- CreateChunkMesh(landMesh, waterMesh, offset, false);
- chunk.AddComponent<MeshFilter>().mesh = landMesh;
- //Water is seperate object so it can have transparency
- GameObject water = new GameObject("Water");
- water.transform.parent = chunk.transform;
- water.AddComponent<MeshRenderer>().sharedMaterial = waterMaterial;
- water.AddComponent<MeshFilter>().mesh = waterMesh;
- return chunk;
- }
- public void CreateChunkMesh(Mesh landMesh, Mesh waterMesh, Vector2Int offset, bool instantCompletion, NativeArray<int> customVoxelValues = default)
- {
- if (instantCompletion)
- {
- jobList.Add(new JobData
- {
- landMesh = landMesh,
- waterMesh = waterMesh,
- offset = offset,
- instantCompletion = instantCompletion,
- customVoxelValues = customVoxelValues
- });
- CreateAndScheduleJob(jobList.Count - 1);
- CompleteJob(jobList.Count - 1);
- }
- else
- {
- jobQueue.Add(new JobData
- {
- landMesh = landMesh,
- waterMesh = waterMesh,
- offset = offset,
- instantCompletion = instantCompletion,
- customVoxelValues = customVoxelValues
- });
- }
- }
- private void LateUpdate()
- {
- for (int i = 0; i < jobList.Count; i++)
- {
- if (jobList[i].scheduled && (jobList[i].handle.IsCompleted || jobList[i].frameCount == maxProccessingFrames || jobList[i].instantCompletion))
- {
- CompleteJob(i);
- }
- else
- {
- CreateAndScheduleJob(i);
- }
- }
- if (jobList.Count < maxJobsPerFrame && jobQueue.Count > 0)
- {
- int length = Mathf.Min(maxJobsPerFrame, jobQueue.Count);
- for (int i = 0; i < length; i++)
- {
- jobList.Add(jobQueue[i]);
- }
- jobQueue.RemoveRange(0, length);
- }
- if (!loadedTerrain && jobQueue.Count == 0)
- {
- player.enabled = true;
- loadingScreen.enabled = false;
- loadedTerrain = true;
- }
- }
- void CreateAndScheduleJob(int i)
- {
- NativeList<float3> vertices = new NativeList<float3>(Allocator.Persistent);
- NativeList<int> triangles = new NativeList<int>(Allocator.Persistent);
- NativeList<float2> uvs = new NativeList<float2>(Allocator.Persistent);
- NativeList<float3> waterVertices = new NativeList<float3>(Allocator.Persistent);
- NativeList<int> waterTriangles = new NativeList<int>(Allocator.Persistent);
- NativeList<float2> waterUvs = new NativeList<float2>(Allocator.Persistent);
- int2 intOffset = new int2(jobList[i].offset.x, jobList[i].offset.y);
- if (jobList[i].customVoxelValues == default)
- {
- if (!chunkDataMap.ContainsKey(intOffset))
- chunkDataMap.Add(intOffset, noise.GenerateVoxelValues(jobList[i].offset));
- }
- else
- {
- if (!chunkDataMap.ContainsKey(intOffset))
- chunkDataMap.Add(intOffset, jobList[i].customVoxelValues);
- }
- AddNeighboringChunkData(intOffset, jobList[i].offset);
- ChunkJob job = new ChunkJob
- {
- vertices = vertices,
- triangles = triangles,
- uvs = uvs,
- waterVertices = waterVertices,
- waterTriangles = waterTriangles,
- waterUvs = waterUvs,
- thisChunkData = chunkDataMap[intOffset],
- rightChunkData = chunkDataMap[intOffset + new int2(chunkDimensions.x, 0)],
- leftChunkData = chunkDataMap[intOffset + new int2(-chunkDimensions.x, 0)],
- frontChunkData = chunkDataMap[intOffset + new int2(0, chunkDimensions.x)],
- backChunkData = chunkDataMap[intOffset + new int2(0, -chunkDimensions.x)],
- faceDirections = faceDirections,
- uvCordinates = uvCoordinates,
- faceVertices = faceVertices,
- chunkDimensions = new int2(chunkDimensions.x, chunkDimensions.y)
- };
- jobList[i].vertices = vertices;
- jobList[i].triangles = triangles;
- jobList[i].uvs = uvs;
- jobList[i].waterVertices = waterVertices;
- jobList[i].waterTriangles = waterTriangles;
- jobList[i].waterUvs = waterUvs;
- jobList[i].handle = job.Schedule();
- jobList[i].scheduled = true;
- }
- void CompleteJob(int i)
- {
- jobList[i].handle.Complete();
- NativeArray<Vector3> vertexArray = jobList[i].vertices.AsArray().Reinterpret<Vector3>();
- NativeArray<int> triangleArray = jobList[i].triangles.AsArray();
- NativeArray<Vector2> uvArray = jobList[i].uvs.AsArray().Reinterpret<Vector2>();
- // Assign data to mesh
- jobList[i].landMesh.SetVertexBufferParams(vertexArray.Length, new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3));
- jobList[i].landMesh.SetVertexBufferData(vertexArray, 0, 0, vertexArray.Length, 0, MeshUpdateFlags.Default);
- jobList[i].landMesh.SetIndexBufferParams(triangleArray.Length, IndexFormat.UInt32);
- jobList[i].landMesh.SetIndexBufferData(triangleArray, 0, 0, triangleArray.Length, MeshUpdateFlags.Default);
- jobList[i].landMesh.SetSubMesh(0, new SubMeshDescriptor(0, triangleArray.Length), MeshUpdateFlags.Default);
- jobList[i].landMesh.SetUVs(0, uvArray);
- jobList[i].landMesh.RecalculateBounds();
- jobList[i].landMesh.RecalculateNormals();
- jobList[i].vertices.Dispose();
- jobList[i].triangles.Dispose();
- jobList[i].uvs.Dispose();
- vertexArray.Dispose();
- triangleArray.Dispose();
- uvArray.Dispose();
- if (jobList[i].waterVertices.Length > 0)
- {
- NativeArray<Vector3> wVertexArray = jobList[i].waterVertices.AsArray().Reinterpret<Vector3>();
- NativeArray<int> wTriangleArray = jobList[i].waterTriangles.AsArray();
- NativeArray<Vector2> wUvArray = jobList[i].waterUvs.AsArray().Reinterpret<Vector2>();
- // Assign data to mesh
- jobList[i].waterMesh.SetVertexBufferParams(wVertexArray.Length, new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3));
- jobList[i].waterMesh.SetVertexBufferData(wVertexArray, 0, 0, wVertexArray.Length, 0, MeshUpdateFlags.Default);
- jobList[i].waterMesh.SetIndexBufferParams(wTriangleArray.Length, IndexFormat.UInt32);
- jobList[i].waterMesh.SetIndexBufferData(wTriangleArray, 0, 0, wTriangleArray.Length, MeshUpdateFlags.Default);
- jobList[i].waterMesh.SetSubMesh(0, new SubMeshDescriptor(0, wTriangleArray.Length), MeshUpdateFlags.Default);
- jobList[i].waterMesh.SetUVs(0, wUvArray);
- jobList[i].waterMesh.RecalculateBounds();
- jobList[i].waterMesh.RecalculateNormals();
- wVertexArray.Dispose();
- wTriangleArray.Dispose();
- wUvArray.Dispose();
- }
- jobList[i].waterTriangles.Dispose();
- jobList[i].waterVertices.Dispose();
- jobList[i].waterUvs.Dispose();
- jobList.RemoveAt(i);
- }
- private void AddNeighboringChunkData(int2 intOffset, Vector2Int offset)
- {
- if (!chunkDataMap.ContainsKey(intOffset + new int2(chunkDimensions.x, 0)))
- chunkDataMap.Add(intOffset + new int2(chunkDimensions.x, 0), noise.GenerateVoxelValues(offset + Vector2Int.right * chunkDimensions.x));
- if (!chunkDataMap.ContainsKey(intOffset + new int2(-chunkDimensions.x, 0)))
- chunkDataMap.Add(intOffset + new int2(-chunkDimensions.x, 0), noise.GenerateVoxelValues(offset + Vector2Int.left * chunkDimensions.x));
- if (!chunkDataMap.ContainsKey(intOffset + new int2(0, chunkDimensions.x)))
- chunkDataMap.Add(intOffset + new int2(0, chunkDimensions.x), noise.GenerateVoxelValues(offset + Vector2Int.up * chunkDimensions.x));
- if (!chunkDataMap.ContainsKey(intOffset + new int2(0, -chunkDimensions.x)))
- chunkDataMap.Add(intOffset + new int2(0, -chunkDimensions.x), noise.GenerateVoxelValues(offset + Vector2Int.down * chunkDimensions.x));
- }
- public static int CalculateVoxelIndex(int x, int y, int z)
- {
- //Converts 3D index into 1D
- return x * (ChunkDimensions.y * ChunkDimensions.x) + y * ChunkDimensions.x + z;
- }
- class JobData
- {
- public Mesh landMesh, waterMesh;
- public Vector2Int offset;
- public JobHandle handle;
- public bool scheduled;
- public bool instantCompletion;
- public int frameCount;
- public NativeArray<int> customVoxelValues;
- public NativeList<float3> vertices;
- public NativeList<int> triangles;
- public NativeList<float2> uvs;
- public NativeList<float3> waterVertices;
- public NativeList<int> waterTriangles;
- public NativeList<float2> waterUvs;
- }
- //Sampled curve that can be used in job
- private NativeArray<float> SampleCurve(AnimationCurve curve, int resolution)
- {
- NativeArray<float> curveArr = new NativeArray<float>(resolution, Allocator.Persistent);
- for (int i = 0; i < resolution; i++)
- {
- curveArr[i] = curve.Evaluate((float)i / resolution);
- }
- return curveArr;
- }
- public void Initialize()
- {
- System.Random rand = new System.Random(noiseSeed);
- chunkDataMap = new NativeHashMap<int2, NativeArray<int>>(1024, Allocator.Persistent);
- jobList = new();
- jobQueue = new();
- sampledCurve = SampleCurve(heightCurve, 256);
- ChunkDimensions = chunkDimensions;
- noise = new Noise(frequency, persistance, lacunarity, numOfOctaves, heightMultiplier, rand.Next() / 10000, surfaceLevel, seaLevel, noise3DContribution, sandDensity, maxTreeHeight, sampledCurve);
- faceVertices = new NativeArray<int3>(24, Allocator.Persistent);
- //Bottom face
- faceVertices[0] = new int3(1, 0, 0);
- faceVertices[1] = new int3(1, 0, 1);
- faceVertices[2] = new int3(0, 0, 1);
- faceVertices[3] = new int3(0, 0, 0);
- //Front face
- faceVertices[4] = new int3(1, 0, 1);
- faceVertices[5] = new int3(1, 1, 1);
- faceVertices[6] = new int3(0, 1, 1);
- faceVertices[7] = new int3(0, 0, 1);
- //Back face
- faceVertices[8] = new int3(0, 0, 0);
- faceVertices[9] = new int3(0, 1, 0);
- faceVertices[10] = new int3(1, 1, 0);
- faceVertices[11] = new int3(1, 0, 0);
- //Left face
- faceVertices[12] = new int3(0, 0, 1);
- faceVertices[13] = new int3(0, 1, 1);
- faceVertices[14] = new int3(0, 1, 0);
- faceVertices[15] = new int3(0, 0, 0);
- //Right face
- faceVertices[16] = new int3(1, 0, 0);
- faceVertices[17] = new int3(1, 1, 0);
- faceVertices[18] = new int3(1, 1, 1);
- faceVertices[19] = new int3(1, 0, 1);
- //Top face
- faceVertices[20] = new int3(0, 1, 0);
- faceVertices[21] = new int3(0, 1, 1);
- faceVertices[22] = new int3(1, 1, 1);
- faceVertices[23] = new int3(1, 1, 0);
- uvCoordinates = new NativeArray<float2>(6, Allocator.Persistent);
- uvCoordinates[0] = new float2(.5f, 0);
- uvCoordinates[1] = new float2(.5f, .5f);
- uvCoordinates[2] = new float2(.5f, .5f);
- uvCoordinates[3] = new float2(.5f, .5f);
- uvCoordinates[4] = new float2(.5f, .5f);
- uvCoordinates[5] = new float2(0, 0);
- faceDirections = new NativeArray<int3>(6, Allocator.Persistent);
- faceDirections[0] = new int3(0, -1, 0); // Down
- faceDirections[1] = new int3(0, 0, 1); // Forward
- faceDirections[2] = new int3(0, 0, -1); // Back
- faceDirections[3] = new int3(-1, 0, 0); // Left
- faceDirections[4] = new int3(1, 0, 0); // Right
- faceDirections[5] = new int3(0, 1, 0); // Up
- }
- private void OnDestroy()
- {
- foreach (var chunk in chunkDataMap)
- {
- chunk.Value.Dispose();
- }
- chunkDataMap.Dispose();
- foreach (var job in jobList)
- {
- job.handle.Complete();
- DisposeJobData(job);
- }
- foreach (var queueJob in jobQueue)
- {
- DisposeJobData(queueJob);
- }
- faceVertices.Dispose();
- faceDirections.Dispose();
- uvCoordinates.Dispose();
- sampledCurve.Dispose();
- noise.Dispose();
- }
- void DisposeJobData(JobData job)
- {
- if (job.vertices.IsCreated) job.vertices.Dispose();
- if (job.triangles.IsCreated) job.triangles.Dispose();
- if (job.uvs.IsCreated) job.uvs.Dispose();
- if (job.waterVertices.IsCreated) job.waterVertices.Dispose();
- if (job.waterTriangles.IsCreated) job.waterTriangles.Dispose();
- if (job.waterUvs.IsCreated) job.waterUvs.Dispose();
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement