Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.Rendering;
- using System.Threading.Tasks;
- using Unity.Mathematics;
- using Unity.Jobs;
- using Unity.Collections;
- using Unity.Burst;
- using UnityEngine.Profiling;
- using System.Diagnostics;
- using System.Xml.Serialization;
- public class MeshGenerator : MonoBehaviour
- {
- public static Vector2Int chunkDimensions = new Vector2Int(32, 192);
- public Material material;
- public ComputeShader voxelGridShader;
- public int brushSize;
- public float brushStrength;
- public int lod;
- public int maxLod;
- public int maxChunksProcessing;
- public float surfaceLevel;
- public bool weldedVertices;
- public bool hideMeshOnPlay;
- public bool interpolate;
- public bool stopWatch;
- [Header("Noise Settings")]
- public float heightMultiplier;
- public int curveResolution;
- public AnimationCurve heightCurve;
- public float frequency;
- public float lacunarity;
- public float persistance;
- public int octaves;
- public float density;
- public float warpingStrength;
- public float warpingFrequency;
- public Vector2 offset;
- [Header("")]
- public bool autoUpdate;
- public string executionTime;
- NativeArray<int> nativeTriTable;
- NativeArray<int> nativeCaseToNumPolys;
- Camera cam;
- VoxelGrid voxelGrid;
- List<ChunkData> chunksToProcess;
- int chunksProcessing;
- private void Start()
- {
- Init();
- if (GameObject.Find("Mesh") != null)
- {
- GameObject.Find("Mesh").SetActive(!hideMeshOnPlay);
- }
- }
- private void Update()
- {
- if (Input.GetKeyDown(KeyCode.Space))
- {
- EditorMesh();
- }
- //Limit the number of chunks that can be processed at once, preventing lag spikes
- for (int i = 0; i < chunksToProcess.Count; i++)
- {
- if(chunksProcessing < maxChunksProcessing)
- {
- ProcessChunk(chunksToProcess[i]);
- chunksProcessing++;
- chunksToProcess.RemoveAt(i);
- }
- }
- }
- public GameObject ChunkObject(Vector2 position)
- {
- GameObject chunk = new GameObject("Chunk");
- chunk.AddComponent<MeshFilter>().mesh = ChunkMesh(position, 0);
- chunk.AddComponent<MeshRenderer>().material = material;
- chunk.GetComponent<MeshRenderer>().receiveShadows = false;
- chunk.GetComponent<MeshRenderer>().shadowCastingMode = ShadowCastingMode.Off;
- return chunk;
- }
- public Mesh ChunkMesh(Vector2 position, int lod)
- {
- Mesh mesh = new Mesh();
- mesh.indexFormat = IndexFormat.UInt32;
- lod = lod == 0 ? 1 : lod * 2;
- chunksToProcess.Add(new ChunkData
- {
- mesh = mesh,
- position = position,
- lod = lod
- });
- return mesh;
- }
- public void EditorMesh()
- {
- Init();
- Mesh mesh = ChunkMesh(offset / 10, lod);
- ProcessChunk(chunksToProcess[0]);
- chunksToProcess.Clear();
- if (GameObject.Find("Mesh") == null)
- {
- GameObject obj = new GameObject("Mesh");
- obj.transform.parent = transform;
- obj.AddComponent<MeshFilter>().mesh = mesh;
- obj.AddComponent<MeshRenderer>().material = material;
- obj.AddComponent<MeshCollider>();
- }
- else
- {
- var mf = GameObject.Find("Mesh").GetComponent<MeshFilter>();
- var collider = GameObject.Find("Mesh").GetComponent<MeshCollider>();
- mf.mesh = mesh;
- collider.sharedMesh = null;
- collider.sharedMesh = mesh;
- }
- }
- void Init()
- {
- chunksToProcess = new();
- cam = Camera.main;
- nativeTriTable = new NativeArray<int>(256 * 15, Allocator.Persistent);
- nativeCaseToNumPolys = new NativeArray<int>(256, Allocator.Persistent);
- for (int i = 0; i < Tables.triTable.Length; i++)
- {
- nativeCaseToNumPolys[i] = Tables.caseToNumPolys[i];
- for (int j = 0; j < 15; j++)
- {
- nativeTriTable[(i * 15) + j] = Tables.triTable[i][j];
- }
- }
- voxelGrid = new VoxelGrid(frequency, lacunarity, persistance, octaves, surfaceLevel, density, heightMultiplier, warpingFrequency, warpingStrength, curveResolution, heightCurve, voxelGridShader);
- }
- void ProcessChunk(ChunkData cd)
- {
- //Getting back data from the GPU is slow, so first the voxel grid is requested then
- //whenever the data has reached the CPU the mesh is constructed
- Profiler.BeginSample("Requesting Voxel grid");
- voxelGrid.RequestVoxelGrid(cd.position, cd.lod, out ShaderData shaderData);
- Profiler.EndSample();
- Stopwatch watch = new Stopwatch();
- watch.Start();
- AsyncGPUReadback.Request(shaderData.voxelGridBuffer, request =>
- {
- if (request.hasError)
- {
- UnityEngine.Debug.LogWarning("GPU readback error detected.");
- }
- else
- {
- watch.Stop();
- if (stopWatch)
- UnityEngine.Debug.Log("Getting voxelGrid took " + watch.ElapsedMilliseconds + " ms");
- Profiler.BeginSample("Readback after completion");
- NativeArray<float> voxelGrid = request.GetData<float>();
- ConstructMesh(voxelGrid, cd.mesh, cd.lod);
- Profiler.EndSample();
- }
- });
- //CompleteReadBack(shaderData, cd.mesh, cd.lod, watch);
- }
- //void CompleteReadBack(ShaderData sd, Mesh mesh, int lod, Stopwatch watch)
- //{
- // //var request = AsyncGPUReadback.Request(sd.voxelGridBuffer);
- // //// Wait for the request to complete
- // //while (!request.done)
- // //{
- // // await Task.Yield();
- // //}
- // //if (request.hasError)
- // //{
- // // UnityEngine.Debug.LogWarning("GPU readback error detected.");
- // //}
- // //else
- // //{
- // // watch.Stop();
- // // if (stopWatch)
- // // {
- // // UnityEngine.Debug.Log("Getting voxelGrid took " + watch.ElapsedMilliseconds + " ms");
- // // }
- // // Profiler.BeginSample("Readback after completion");
- // // //Get voxelGrid after readback is complete
- // // sd.voxelGridBuffer.Release();
- // // sd.noiseMap2DBuffer.Release();
- // // sd.noiseMap2D.Dispose();
- // // NativeArray<float> voxelGrid = request.GetData<float>();
- // // ConstructMesh(voxelGrid, mesh, lod);
- // // Profiler.EndSample();
- // //}
- // AsyncGPUReadback.Request(sd.voxelGridBuffer, request =>
- // {
- // if (request.hasError)
- // {
- // UnityEngine.Debug.LogWarning("GPU readback error detected.");
- // }
- // else
- // {
- // watch.Stop();
- // if (stopWatch)
- // UnityEngine.Debug.Log("Getting voxelGrid took " + watch.ElapsedMilliseconds + " ms");
- // Profiler.BeginSample("Readback after completion");
- // NativeArray<float> voxelGrid = request.GetData<float>();
- // ConstructMesh(voxelGrid, mesh, lod);
- // Profiler.EndSample();
- // }
- // });
- //}
- async void ConstructMesh(NativeArray<float> voxelGrid, Mesh mesh, int lod)
- {
- Profiler.BeginSample("MeshJob");
- NativeQueue<Triangle> triangleQueue = new NativeQueue<Triangle>(Allocator.Persistent);
- MeshJob job = new MeshJob
- {
- triangles = triangleQueue.AsParallelWriter(),
- voxelGrid = voxelGrid,
- lod = lod,
- chunkDimensions = new int2(chunkDimensions.x, chunkDimensions.y),
- interpolate = interpolate,
- triTable = nativeTriTable,
- caseToNumTris = nativeCaseToNumPolys
- };
- int length = chunkDimensions.x / lod * chunkDimensions.y / lod * chunkDimensions.x / lod;
- job.Schedule(length, 64).Complete();
- Profiler.EndSample();
- List<Vector3> vertices = new();
- List<int> triangleIndices = new();
- Dictionary<Vector3, int> vertDictionary = new();
- Stopwatch watch = new Stopwatch();
- watch.Start();
- await Task.Run( () =>
- {
- while (triangleQueue.Count > 0)
- {
- Triangle triangle = triangleQueue.Dequeue();
- int vertexCount = vertices.Count;
- if (weldedVertices)
- {
- //For each vertex in the queue we chech whether it already exists in the dictionary, if so the correct index is added to the indexlist.
- // if not the vertex is added to the dictionary.
- if (vertDictionary.TryGetValue(triangle.vertex0, out int triIndex0))
- {
- triangleIndices.Add(triIndex0);
- }
- else
- {
- vertices.Add(triangle.vertex0);
- triangleIndices.Add(vertexCount);
- vertDictionary.Add(triangle.vertex0, vertexCount);
- }
- if (vertDictionary.TryGetValue(triangle.vertex1, out int triIndex1))
- {
- triangleIndices.Add(triIndex1);
- }
- else
- {
- vertices.Add(triangle.vertex1);
- triangleIndices.Add(vertexCount);
- vertDictionary.Add(triangle.vertex1, vertexCount);
- }
- if (vertDictionary.TryGetValue(triangle.vertex2, out int triIndex2))
- {
- triangleIndices.Add(triIndex2);
- }
- else
- {
- vertices.Add(triangle.vertex2);
- triangleIndices.Add(vertexCount);
- vertDictionary.Add(triangle.vertex2, vertexCount);
- }
- }
- else
- {
- vertices.AddRange(new List<Vector3>
- {
- triangle.vertex0,
- triangle.vertex1,
- triangle.vertex2
- });
- triangleIndices.AddRange(new List<int>
- {
- vertexCount,
- vertexCount + 1,
- vertexCount + 2
- });
- }
- }
- });
- watch.Stop();
- if (stopWatch)
- {
- UnityEngine.Debug.Log("ConstructMesh() took " + watch.ElapsedMilliseconds + " ms");
- }
- triangleQueue.Dispose();
- voxelGrid.Dispose();
- mesh.SetVertices(vertices);
- mesh.SetTriangles(triangleIndices, 0);
- mesh.RecalculateNormals();
- chunksProcessing--;
- }
- struct ChunkData
- {
- public Mesh mesh;
- public Vector2 position;
- public int lod;
- }
- }
- [BurstCompile]
- struct MeshJob : IJobParallelFor
- {
- [WriteOnly]
- public NativeQueue<Triangle>.ParallelWriter triangles;
- [ReadOnly]
- public NativeArray<float> voxelGrid;
- [ReadOnly]
- public int lod;
- [ReadOnly]
- public int2 chunkDimensions;
- [ReadOnly]
- public bool interpolate;
- [ReadOnly]
- public NativeArray<int> triTable;
- [ReadOnly]
- public NativeArray<int> caseToNumTris;
- public void Execute(int i)
- {
- int z = i / (chunkDimensions.x / lod * chunkDimensions.y / lod) * lod;
- int y = (i / (chunkDimensions.x / lod) * lod) - z / lod * chunkDimensions.y;
- int x = i % (chunkDimensions.x / lod) * lod;
- AddTriangles(i, x, y, z);
- }
- void AddTriangles(int index, int x, int y, int z)
- {
- int triCase = CalculateCase(index);
- int triIndex = 0;
- for (int i = 0; i < caseToNumTris[triCase]; i++)
- {
- int edge0 = triTable[(triCase * 15) + triIndex + 2];
- float3 vertex0 = CalculateVertexPosition(edge0, index, lod, x, y, z);
- int edge1 = triTable[(triCase * 15) + triIndex + 1];
- float3 vertex1 = CalculateVertexPosition(edge1, index, lod, x, y, z);
- int edge2 = triTable[(triCase * 15) + triIndex];
- float3 vertex2 = CalculateVertexPosition(edge2, index, lod, x, y, z);
- triIndex += 3;
- triangles.Enqueue(new Triangle
- {
- vertex0 = vertex0,
- vertex1 = vertex1,
- vertex2 = vertex2
- });
- }
- }
- //Returns vertex position at on of the 12 edges of a cube
- float3 CalculateVertexPosition(int edge, int index, int lod, int x, int y, int z)
- {
- int i = index * 8;
- switch (edge)
- {
- case 0:
- return interpolate ? InterpolatePosition(x, y, z, voxelGrid[i], voxelGrid[i + 1], 0, 1, 0) : new float3(x, y + 1, z);
- case 1:
- return interpolate ? InterpolatePosition(x, y + lod, z, voxelGrid[i + 1], voxelGrid[i + 2], 1, 0, 0) : new float3(x + 1, y + lod, z);
- case 2:
- return interpolate ? InterpolatePosition(x + lod, y, z, voxelGrid[i + 3], voxelGrid[i + 2], 0, 1, 0) : new float3(x + lod, y + 1, z);
- case 3:
- return interpolate ? InterpolatePosition(x, y, z, voxelGrid[i], voxelGrid[i + 3], 1, 0, 0) : new float3(x + 1, y, z);
- case 4:
- return interpolate ? InterpolatePosition(x, y, z + lod, voxelGrid[i + 4], voxelGrid[i + 5], 0, 1, 0) : new float3(x, y + 1, z + lod);
- case 5:
- return interpolate ? InterpolatePosition(x, y + lod, z + lod, voxelGrid[i + 5], voxelGrid[i + 6], 1, 0, 0) : new float3(x + 1, y + lod, z + lod);
- case 6:
- return interpolate ? InterpolatePosition(x + lod, y, z + lod, voxelGrid[i + 7], voxelGrid[i + 6], 0, 1, 0) : new float3(x + lod, y + 1, z + lod);
- case 7:
- return interpolate ? InterpolatePosition(x, y, z + lod, voxelGrid[i + 4], voxelGrid[i + 7], 1, 0, 0) : new float3(x + 1, y, z + lod);
- case 8:
- return interpolate ? InterpolatePosition(x, y, z, voxelGrid[i], voxelGrid[i + 4], 0, 0, 1) : new float3(x, y, z + 1);
- case 9:
- return interpolate ? InterpolatePosition(x, y + lod, z, voxelGrid[i + 1], voxelGrid[i + 5], 0, 0, 1) : new float3(x, y + lod, z + 1);
- case 10:
- return interpolate ? InterpolatePosition(x + lod, y + lod, z, voxelGrid[i + 2], voxelGrid[i + 6], 0, 0, 1) : new float3(x + lod, y + lod, z + 1);
- case 11:
- return interpolate ? InterpolatePosition(x + lod, y, z, voxelGrid[i + 3], voxelGrid[i + 7], 0, 0, 1) : new float3(x + lod, y, z + 1);
- default:
- throw new ArgumentOutOfRangeException(nameof(edge), "Invalid edge index: " + edge);
- }
- }
- public float3 InterpolatePosition(int x, int y, int z, float cornerValue0, float cornerValue1, int offsetX, int offsetY, int offsetZ)
- {
- float interpolation = (0 - cornerValue0) / (cornerValue1 - cornerValue0);
- return new float3(x + offsetX * interpolation, y + offsetY * interpolation, z + offsetZ * interpolation);
- }
- public int CalculateCase(int index)
- {
- int triCase = 0;
- for (int i = 0; i < 8; i++)
- {
- int bit = voxelGrid[(index * 8) + i] < 0 ? 1 : 0;
- triCase |= bit << i;
- }
- return triCase;
- }
- }
- public struct Triangle
- {
- public float3 vertex0;
- public float3 vertex1;
- public float3 vertex2;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement