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
- {
- class JobData
- {
- public Mesh landMesh, waterMesh;
- public Vector2Int noiseOffset;
- 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;
- }
- public Texture2D atlas;
- public Material terrainMaterial;
- public Material waterMaterial;
- public Image loadingScreen;
- public SimpleCameraController player;
- public Vector2Int chunkDimensions;
- public static Vector2Int ChunkDimensions;
- [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 numOctaves;
- public Vector2Int offset;
- public AnimationCurve heightCurve;
- public float heightMultiplier;
- public float noise3DContribution;
- public int noiseSeed;
- [Header("Performance")]
- public int maxProccessingFrames;
- public int maxJobsPerFrame;
- Noise noise;
- NativeArray<int3> faceVertices;
- NativeArray<int3> faceDirections;
- NativeArray<float2> uvCoordinates;
- NativeArray<float> sampledHeightCurve;
- [HideInInspector]
- public NativeHashMap<int2, NativeArray<int>> chunkDataMap;
- List<JobData> jobList;
- List<JobData> jobQueue;
- bool loadedTerrain;
- public GameObject CreateChunkObject(Vector2Int offset)
- {
- GameObject chunk = new GameObject(transform.childCount.ToString());
- var meshRenderer = chunk.AddComponent<MeshRenderer>();
- meshRenderer.sharedMaterial = new Material(terrainMaterial)
- {
- 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)
- {
- var data = new JobData
- {
- landMesh = landMesh,
- waterMesh = waterMesh,
- noiseOffset = offset,
- instantCompletion = instantCompletion,
- customVoxelValues = customVoxelValues
- };
- CreateAndScheduleJob(data);
- CompleteJob(data);
- }
- else
- {
- jobQueue.Add(new JobData
- {
- landMesh = landMesh,
- waterMesh = waterMesh,
- noiseOffset = offset,
- instantCompletion = instantCompletion,
- customVoxelValues = customVoxelValues
- });
- }
- }
- void Start()
- {
- Initialize();
- player.enabled = false;
- loadingScreen.enabled = true;
- }
- public void Initialize()
- {
- System.Random rand = new(noiseSeed);
- chunkDataMap = new NativeHashMap<int2, NativeArray<int>>(1024, Allocator.Persistent);
- jobList = new();
- jobQueue = new();
- sampledHeightCurve = SampleCurve(heightCurve, 256);
- ChunkDimensions = chunkDimensions;
- noise = new Noise(
- frequency,
- persistance,
- lacunarity,
- numOctaves,
- heightMultiplier,
- rand.Next() / 10000,
- surfaceLevel,
- seaLevel,
- noise3DContribution,
- sandDensity,
- maxTreeHeight,
- sampledHeightCurve
- );
- 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
- }
- void LateUpdate()
- {
- for (int i = 0; i < jobList.Count; i++)
- {
- JobData jobData = jobList[i];
- if (jobData.scheduled && (jobData.handle.IsCompleted || jobData.frameCount == maxProccessingFrames || jobData.instantCompletion))
- {
- CompleteJob(jobData);
- jobList.RemoveAt(i);
- }
- else
- {
- CreateAndScheduleJob(jobData);
- }
- }
- 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(JobData data)
- {
- 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(data.noiseOffset.x, data.noiseOffset.y);
- if (data.customVoxelValues == default)
- {
- if (!chunkDataMap.ContainsKey(intOffset))
- chunkDataMap.Add(intOffset, noise.GenerateVoxelValues(data.noiseOffset));
- }
- else
- {
- if (!chunkDataMap.ContainsKey(intOffset))
- chunkDataMap.Add(intOffset, data.customVoxelValues);
- }
- AddNeighboringChunkData(intOffset, data.noiseOffset);
- 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)
- };
- data.vertices = vertices;
- data.triangles = triangles;
- data.uvs = uvs;
- data.waterVertices = waterVertices;
- data.waterTriangles = waterTriangles;
- data.waterUvs = waterUvs;
- data.handle = job.Schedule();
- data.scheduled = true;
- }
- void CompleteJob(JobData data)
- {
- data.handle.Complete();
- NativeArray<Vector3> vertexArray = data.vertices.AsArray().Reinterpret<Vector3>();
- NativeArray<int> triangleArray = data.triangles.AsArray();
- NativeArray<Vector2> uvArray = data.uvs.AsArray().Reinterpret<Vector2>();
- // Assign data to mesh
- data.landMesh.SetVertexBufferParams(vertexArray.Length, new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3));
- data.landMesh.SetVertexBufferData(vertexArray, 0, 0, vertexArray.Length, 0, MeshUpdateFlags.Default);
- data.landMesh.SetIndexBufferParams(triangleArray.Length, IndexFormat.UInt32);
- data.landMesh.SetIndexBufferData(triangleArray, 0, 0, triangleArray.Length, MeshUpdateFlags.Default);
- data.landMesh.SetSubMesh(0, new SubMeshDescriptor(0, triangleArray.Length), MeshUpdateFlags.Default);
- data.landMesh.SetUVs(0, uvArray);
- data.landMesh.RecalculateBounds();
- data.landMesh.RecalculateNormals();
- data.vertices.Dispose();
- data.triangles.Dispose();
- data.uvs.Dispose();
- vertexArray.Dispose();
- triangleArray.Dispose();
- uvArray.Dispose();
- if (data.waterVertices.Length > 0)
- {
- NativeArray<Vector3> wVertexArray = data.waterVertices.AsArray().Reinterpret<Vector3>();
- NativeArray<int> wTriangleArray = data.waterTriangles.AsArray();
- NativeArray<Vector2> wUvArray = data.waterUvs.AsArray().Reinterpret<Vector2>();
- // Assign data to mesh
- data.waterMesh.SetVertexBufferParams(wVertexArray.Length, new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3));
- data.waterMesh.SetVertexBufferData(wVertexArray, 0, 0, wVertexArray.Length, 0, MeshUpdateFlags.Default);
- data.waterMesh.SetIndexBufferParams(wTriangleArray.Length, IndexFormat.UInt32);
- data.waterMesh.SetIndexBufferData(wTriangleArray, 0, 0, wTriangleArray.Length, MeshUpdateFlags.Default);
- data.waterMesh.SetSubMesh(0, new SubMeshDescriptor(0, wTriangleArray.Length), MeshUpdateFlags.Default);
- data.waterMesh.SetUVs(0, wUvArray);
- data.waterMesh.RecalculateBounds();
- data.waterMesh.RecalculateNormals();
- wVertexArray.Dispose();
- wTriangleArray.Dispose();
- wUvArray.Dispose();
- }
- data.waterTriangles.Dispose();
- data.waterVertices.Dispose();
- data.waterUvs.Dispose();
- }
- 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));
- }
- //Converts 3D index into 1D
- public static int CalculateVoxelIndex(int x, int y, int z)
- {
- return x * ChunkDimensions.y * ChunkDimensions.x + y * ChunkDimensions.x + z;
- }
- //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;
- }
- 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();
- sampledHeightCurve.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