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;
- public class ChunkGenerator : MonoBehaviour
- {
- public Texture2D atlas;
- public int maxVoxelHeight;
- public Vector3Int chunkDimensions;
- public int maxProccessingFrames;
- public static int MaxVoxelHeight;
- [Header("Noise Settings")]
- public float persistance;
- public float frequency;
- public float lacunarity;
- public int numOfOctaves;
- public Vector2Int offset;
- public float heightMultiplier;
- [Header(" ")]
- public string executionTime;
- Noise noise;
- NativeArray<int3> faceVertices;
- NativeArray<int3> faceDirections;
- NativeArray<float2> uvCoordinates;
- [HideInInspector]
- public NativeHashMap<int2, NativeArray<int>> chunkDataMap;
- List<JobData> jobList;
- private void Start()
- {
- Initialize();
- executionTime = "N/A";
- }
- public void GenerateChunk()
- {
- Initialize();
- if(GameObject.Find("Chunk") == null)
- {
- float startTime = Time.realtimeSinceStartup;
- CreateChunkObject(offset);
- CompleteJobs(false);
- executionTime = (Time.realtimeSinceStartup - startTime) * 1000f + " ms";
- }
- else
- {
- float startTime = Time.realtimeSinceStartup;
- Mesh mesh = CreateChunkMesh(offset);
- CompleteJobs(false);
- executionTime = (Time.realtimeSinceStartup - startTime) * 1000f + " ms";
- GameObject.Find("Chunk").GetComponent<MeshFilter>().mesh = mesh;
- }
- faceVertices.Dispose();
- faceDirections.Dispose();
- uvCoordinates.Dispose();
- }
- public GameObject CreateChunkObject(Vector2Int offset)
- {
- GameObject chunk = new GameObject("Chunk");
- var meshRenderer = chunk.AddComponent<MeshRenderer>();
- meshRenderer.sharedMaterial = new Material(Shader.Find("Unlit/Texture"))
- {
- mainTexture = atlas
- };
- chunk.AddComponent<MeshFilter>().mesh = CreateChunkMesh(offset);
- return chunk;
- }
- public Mesh CreateChunkMesh(Vector2Int offset, NativeArray<int> customVoxelValues = default)
- {
- Mesh mesh = new Mesh
- {
- indexFormat = IndexFormat.UInt32
- };
- NativeList<float3> vertices = new NativeList<float3>(Allocator.TempJob);
- NativeList<int> triangles = new NativeList<int>(Allocator.TempJob);
- NativeList<float2> uvs = new NativeList<float2>(Allocator.TempJob);
- int2 intOffset = new int2(offset.x, offset.y);
- if(customVoxelValues == default){
- if (!chunkDataMap.ContainsKey(intOffset))
- chunkDataMap.Add(intOffset, noise.GenerateVoxelValues(offset));
- }
- else{
- if (!chunkDataMap.ContainsKey(intOffset))
- chunkDataMap.Add(intOffset, customVoxelValues);
- }
- AddNeighboringChunkData(intOffset, offset);
- ChunkJob job = new ChunkJob
- {
- vertices = vertices,
- triangles = triangles,
- uvs = uvs,
- 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.z)],
- backChunkData = chunkDataMap[intOffset + new int2(0, -chunkDimensions.z)],
- faceDirections = faceDirections,
- uvCordinates = uvCoordinates,
- faceVertices = faceVertices,
- maxVoxelHeight = maxVoxelHeight
- };
- JobHandle handle = job.Schedule();
- jobList.Add(new JobData
- {
- mesh = mesh,
- handle = handle,
- vertices = vertices,
- triangles = triangles,
- uvs = uvs,
- });
- return mesh;
- }
- 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.z)))
- chunkDataMap.Add(intOffset + new int2(0, chunkDimensions.z), noise.GenerateVoxelValues(offset + Vector2Int.up * chunkDimensions.z));
- if (!chunkDataMap.ContainsKey(intOffset + new int2(0, -chunkDimensions.z)))
- chunkDataMap.Add(intOffset + new int2(0, -chunkDimensions.z), noise.GenerateVoxelValues(offset + Vector2Int.down * 16));
- }
- public static int CalculateVoxelIndex(int x, int y, int z)
- {
- return x * (MaxVoxelHeight * 16) + y * 16 + z;
- }
- private void LateUpdate()
- {
- CompleteJobs(true);
- }
- void CompleteJobs(bool waitForCompletion)
- {
- for (int i = jobList.Count - 1; i >= 0; i--)
- {
- if (jobList[i].handle.IsCompleted || jobList[i].frameCount == maxProccessingFrames || !waitForCompletion)
- {
- 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].mesh.SetVertexBufferParams(vertexArray.Length, new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3));
- jobList[i].mesh.SetVertexBufferData(vertexArray, 0, 0, vertexArray.Length, 0, MeshUpdateFlags.Default);
- jobList[i].mesh.SetIndexBufferParams(triangleArray.Length, IndexFormat.UInt32);
- jobList[i].mesh.SetIndexBufferData(triangleArray, 0, 0, triangleArray.Length, MeshUpdateFlags.Default);
- jobList[i].mesh.SetSubMesh(0, new SubMeshDescriptor(0, triangleArray.Length), MeshUpdateFlags.Default);
- jobList[i].mesh.SetUVs(0, uvArray);
- jobList[i].mesh.RecalculateBounds();
- jobList[i].vertices.Dispose();
- jobList[i].triangles.Dispose();
- jobList[i].uvs.Dispose();
- }
- jobList[i].frameCount++;
- jobList.RemoveAt(i);
- }
- }
- class JobData
- {
- public JobHandle handle;
- public Mesh mesh;
- public NativeList<float3> vertices;
- public NativeList<int> triangles;
- public NativeList<float2> uvs;
- public int frameCount;
- }
- private void Initialize()
- {
- chunkDataMap = new NativeHashMap<int2, NativeArray<int>>(1024, Allocator.Persistent);
- jobList = new();
- noise = new Noise(frequency, persistance, lacunarity, numOfOctaves, heightMultiplier, maxVoxelHeight);
- MaxVoxelHeight = maxVoxelHeight;
- 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()
- {
- faceVertices.Dispose();
- faceDirections.Dispose();
- uvCoordinates.Dispose();
- foreach (var chunk in chunkDataMap)
- {
- chunk.Value.Dispose();
- }
- chunkDataMap.Dispose();
- }
- }
- using Unity.Collections;
- using UnityEngine;
- public class Noise
- {
- float frequency;
- float lacunarity;
- float persistance;
- int numOfOctaves;
- float height;
- public Noise(float frequency, float persistance, float lacunarity, int numOfOctaves, float height, int maxVoxelHeight)
- {
- this.frequency = frequency;
- this.persistance = persistance;
- this.lacunarity = lacunarity;
- this.numOfOctaves = numOfOctaves;
- this.height = height;
- }
- public NativeArray<int> GenerateVoxelValues(Vector2Int offset)
- {
- int maxVoxelHeight = ChunkGenerator.MaxVoxelHeight;
- NativeArray<int> voxelValues = new NativeArray<int>(256 * maxVoxelHeight, Allocator.Persistent);
- for (int z = 0; z < 16; z++)
- {
- for (int x = 0; x < 16; x++)
- {
- float noiseValue = CalculateNoiseValue(x, 0, z, offset);
- noiseValue *= height;
- noiseValue += maxVoxelHeight / 3;
- for (int y = 0; y < maxVoxelHeight; y++)
- {
- if (y < noiseValue)
- {
- voxelValues[ChunkGenerator.CalculateVoxelIndex(x, y, z)] = 1;
- }
- }
- }
- }
- return voxelValues;
- }
- float CalculateNoiseValue(int x, int y, int z, Vector2Int offset)
- {
- float sum = 0;
- for (int i = 0; i < numOfOctaves; i++)
- {
- float frequency = Mathf.Pow(lacunarity, i) * this.frequency;
- float amplitude = Mathf.Pow(persistance, i);
- sum += Mathf.PerlinNoise((x + offset.x) * frequency, (z + offset.y) * frequency) * amplitude;
- }
- return sum / numOfOctaves;
- }
- }
- using Unity.Burst;
- using Unity.Collections;
- using Unity.Jobs;
- using Unity.Mathematics;
- [BurstCompile(CompileSynchronously = true)]
- struct ChunkJob : IJob
- {
- public NativeList<float3> vertices;
- public NativeList<int> triangles;
- public NativeList<float2> uvs;
- [ReadOnly] public NativeArray<int3> faceDirections;
- [ReadOnly] public NativeArray<float2> uvCordinates;
- [ReadOnly] public NativeArray<int3> faceVertices;
- [ReadOnly] public int maxVoxelHeight;
- //Neighbouring chunks
- [ReadOnly] public NativeArray<int> thisChunkData;
- [ReadOnly] public NativeArray<int> leftChunkData;
- [ReadOnly] public NativeArray<int> rightChunkData;
- [ReadOnly] public NativeArray<int> frontChunkData;
- [ReadOnly] public NativeArray<int> backChunkData;
- public void Execute()
- {
- int width = 16;
- int height = maxVoxelHeight;
- int depth = 16;
- for (int x = 0; x < width; x++)
- {
- for (int y = 0; y < height; y++)
- {
- for (int z = 0; z < depth; z++)
- {
- if (thisChunkData[CalculateVoxelIndex(x, y, z)] == 1)
- {
- AddVoxel(x, y, z, vertices, triangles, uvs);
- }
- }
- }
- }
- }
- private void AddVoxel(int x, int y, int z, NativeList<float3> vertices, NativeList<int> triangles, NativeList<float2> uvs)
- {
- for (int i = 0; i < 6; i++)
- {
- if (IsFaceVisible(x, y, z, i))
- {
- AddQuad(new int3(x, y, z), i, vertices, triangles, uvs);
- }
- }
- }
- private void AddQuad(int3 quadPos, int i, NativeList<float3> vertices, NativeList<int> triangles, NativeList<float2> uvs)
- {
- int vertCount = vertices.Length;
- NativeArray<float3> verts = new NativeArray<float3>(4, Allocator.Temp);
- for (int j = 0; j < 4; j++)
- {
- verts[j] = faceVertices[(i * 4) + j] + quadPos;
- }
- vertices.AddRange(verts);
- NativeArray<int> tris = new NativeArray<int>(6, Allocator.Temp);
- tris[0] = vertCount;
- tris[1] = vertCount + 1;
- tris[2] = vertCount + 3;
- tris[3] = vertCount + 3;
- tris[4] = vertCount + 1;
- tris[5] = vertCount + 2;
- triangles.AddRange(tris);
- float2 bottomLeft = uvCordinates[i];
- NativeArray<float2> uv = new NativeArray<float2>(4, Allocator.Temp);
- uv[0] = bottomLeft + new float2(.5f, 0);
- uv[1] = bottomLeft + new float2(.5f, .5f);
- uv[2] = bottomLeft + new float2(0, .5f);
- uv[3] = bottomLeft;
- uvs.AddRange(uv);
- }
- private bool IsFaceVisible(int x, int y, int z, int faceIndex)
- {
- int3 direction = faceDirections[faceIndex];
- int3 neighbor = new int3(x + direction.x, y + direction.y, z + direction.z);
- if (neighbor.y < 0 || neighbor.y >= maxVoxelHeight)
- return false;
- if (neighbor.x < 0)
- return leftChunkData[CalculateVoxelIndex(15, neighbor.y, neighbor.z)] == 0;
- if (neighbor.x >= 16)
- return rightChunkData[CalculateVoxelIndex(0, neighbor.y, neighbor.z)] == 0;
- if (neighbor.z < 0)
- return backChunkData[CalculateVoxelIndex(neighbor.x, neighbor.y, 15)] == 0;
- if (neighbor.z >= 16)
- return frontChunkData[CalculateVoxelIndex(neighbor.x, neighbor.y, 0)] == 0;
- return thisChunkData[CalculateVoxelIndex(neighbor.x, neighbor.y, neighbor.z)] == 0;
- }
- public int CalculateVoxelIndex(int x, int y, int z)
- {
- return x * (maxVoxelHeight * 16) + y * 16 + z;
- }
- }
- using Unity.Collections;
- using UnityEngine;
- using Unity.Mathematics;
- public class BlockBreakingAndPlacing : MonoBehaviour
- {
- public float maxReach;
- public LayerMask mask;
- RaycastHit hit;
- GameObject cam;
- private void Start()
- {
- cam = GameObject.Find("PlayerCamera");
- }
- private void Update()
- {
- if (Input.GetMouseButtonDown(0))
- {
- if(Physics.Raycast(cam.transform.position, cam.transform.forward, out hit, maxReach, mask))
- {
- ChunkGenerator chunkGen = GameObject.Find("ChunkGenerator").GetComponent<ChunkGenerator>();
- int2 coordinates = new int2((int)hit.collider.transform.position.x + 8, (int)hit.collider.transform.position.z + 8);
- NativeArray<int> voxelValues = chunkGen.chunkDataMap[coordinates];
- int x = (int)(Mathf.Abs(hit.collider.transform.position.x - Mathf.Round(hit.point.x - .5f)));
- int y = Mathf.Abs(Mathf.RoundToInt(hit.point.y)) - 1;
- int z = (int)(Mathf.Abs(hit.collider.transform.position.z - Mathf.Round(hit.point.z - .5f)));
- voxelValues[ChunkGenerator.CalculateVoxelIndex(x, y, z)] = 0;
- Mesh mesh = chunkGen.CreateChunkMesh(new Vector2Int(coordinates.x, coordinates.y), voxelValues);
- hit.collider.GetComponent<MeshFilter>().mesh = mesh;
- //Reset collider
- hit.collider.GetComponent<MeshCollider>().sharedMesh = null;
- hit.collider.GetComponent<MeshCollider>().sharedMesh = mesh;
- }
- }
- if (Input.GetMouseButtonDown(1))
- {
- if (Physics.Raycast(cam.transform.position, cam.transform.forward, out hit, maxReach, mask))
- {
- ChunkGenerator chunkGen = GameObject.Find("ChunkGenerator").GetComponent<ChunkGenerator>();
- int2 coordinates = new int2((int)hit.collider.transform.position.x + 8, (int)hit.collider.transform.position.z + 8);
- NativeArray<int> voxelValues = chunkGen.chunkDataMap[coordinates];
- int x = (int)(Mathf.Abs(hit.collider.transform.position.x - Mathf.Round(hit.point.x - .5f)));
- int y = Mathf.Abs(Mathf.RoundToInt(hit.point.y));
- int z = (int)(Mathf.Abs(hit.collider.transform.position.z - Mathf.Round(hit.point.z - .5f)));
- voxelValues[ChunkGenerator.CalculateVoxelIndex(x, y, z)] = 1;
- Mesh mesh = chunkGen.CreateChunkMesh(new Vector2Int(coordinates.x, coordinates.y), voxelValues);
- hit.collider.GetComponent<MeshFilter>().mesh = mesh;
- //Reset collider
- hit.collider.GetComponent<MeshCollider>().sharedMesh = null;
- hit.collider.GetComponent<MeshCollider>().sharedMesh = mesh;
- }
- }
- }
- }
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- public class EndlessTerrain : MonoBehaviour
- {
- public float maxViewDst;
- public float colliderRange;
- public Transform viewer;
- public static Vector2 viewerPosition;
- int chunksVisibleInViewDst;
- Dictionary<Vector2, TerrainChunk> chunkDictionary = new Dictionary<Vector2, TerrainChunk>();
- List<TerrainChunk> chunksVisibleLastUpdate = new List<TerrainChunk>();
- void Start()
- {
- chunksVisibleInViewDst = Mathf.RoundToInt(maxViewDst / 16);
- }
- void Update()
- {
- viewerPosition = new Vector2(viewer.position.x, viewer.position.z);
- UpdateVisibleChunks();
- }
- void UpdateVisibleChunks()
- {
- for (int i = 0; i < chunksVisibleLastUpdate.Count; i++)
- {
- chunksVisibleLastUpdate[i].chunkObject.SetActive(false);
- }
- chunksVisibleLastUpdate.Clear();
- int currentChunkCoordX = (int)Mathf.Round(viewerPosition.x / 16);
- int currentChunkCoordY = (int)Mathf.Round(viewerPosition.y / 16);
- for (int yOffset = -chunksVisibleInViewDst; yOffset <= chunksVisibleInViewDst; yOffset++) //Loops through all the current chunks
- {
- for (int xOffset = -chunksVisibleInViewDst; xOffset <= chunksVisibleInViewDst; xOffset++)
- {
- Vector2Int viewedChunkCoord = new Vector2Int(currentChunkCoordX + xOffset, currentChunkCoordY + yOffset);
- if (chunkDictionary.ContainsKey(viewedChunkCoord)) //Checks if a chunk with the correct cordinates has been loaded previously
- {
- TerrainChunk viewedChunk = chunkDictionary[viewedChunkCoord];
- viewedChunk.UpdateTerrainChunk();
- if (viewedChunk.IsVisible())
- {
- chunksVisibleLastUpdate.Add(viewedChunk);
- }
- }
- else
- {
- chunkDictionary.Add(viewedChunkCoord, new TerrainChunk(viewedChunkCoord, transform, maxViewDst, colliderRange));
- }
- }
- }
- }
- public class TerrainChunk
- {
- public GameObject chunkObject;
- ChunkGenerator chunkGen;
- Vector2Int position;
- Bounds bounds;
- float maxViewDst;
- float colliderRange;
- bool hasCollider;
- public TerrainChunk(Vector2Int coord, Transform parent, float maxViewDst, float colliderRange)
- {
- position = coord * 16;
- bounds = new Bounds(new Vector2(position.x, position.y), Vector2.one * 16);
- chunkGen = GameObject.Find("ChunkGenerator").GetComponent<ChunkGenerator>();
- chunkObject = chunkGen.CreateChunkObject(position);
- chunkObject.transform.position = new Vector3(position.x - 8, 0, position.y - 8);
- chunkObject.transform.parent = parent;
- chunkObject.SetActive(false);
- this.maxViewDst = maxViewDst;
- this.colliderRange = colliderRange;
- }
- public void UpdateTerrainChunk()
- {
- float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
- if(viewerDstFromNearestEdge < colliderRange && !hasCollider)
- {
- chunkObject.AddComponent<MeshCollider>();
- hasCollider = true;
- }
- bool visible = viewerDstFromNearestEdge <= maxViewDst; //Checks if the chunk is too far away from the player, if so it deactivates it
- chunkObject.SetActive(visible);
- }
- public bool IsVisible()
- {
- return chunkObject.activeSelf;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement