Advertisement
JontePonte

MeshGen draft 3

Oct 5th, 2024
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 16.16 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.Rendering;
  5. using System.Threading.Tasks;
  6. using Unity.Mathematics;
  7. using Unity.Jobs;
  8. using Unity.Collections;
  9. using Unity.Burst;
  10. using UnityEngine.Profiling;
  11. using System.Diagnostics;
  12. using System.Xml.Serialization;
  13.  
  14. public class MeshGenerator : MonoBehaviour
  15. {
  16.     public static Vector2Int chunkDimensions = new Vector2Int(32, 192);
  17.  
  18.     public Material material;
  19.     public ComputeShader voxelGridShader;
  20.  
  21.     public int brushSize;
  22.     public float brushStrength;
  23.  
  24.     public int lod;
  25.     public int maxLod;
  26.  
  27.     public int maxChunksProcessing;
  28.  
  29.     public float surfaceLevel;
  30.  
  31.     public bool weldedVertices;
  32.     public bool hideMeshOnPlay;
  33.     public bool interpolate;
  34.     public bool stopWatch;
  35.  
  36.  
  37.     [Header("Noise Settings")]
  38.     public float heightMultiplier;
  39.     public int curveResolution;
  40.     public AnimationCurve heightCurve;
  41.     public float frequency;
  42.     public float lacunarity;
  43.     public float persistance;
  44.     public int octaves;
  45.     public float density;
  46.     public float warpingStrength;
  47.     public float warpingFrequency;
  48.     public Vector2 offset;
  49.  
  50.     [Header("")]
  51.     public bool autoUpdate;
  52.     public string executionTime;
  53.  
  54.     NativeArray<int> nativeTriTable;
  55.     NativeArray<int> nativeCaseToNumPolys;
  56.  
  57.     Camera cam;
  58.  
  59.     VoxelGrid voxelGrid;
  60.  
  61.     List<ChunkData> chunksToProcess;
  62.     int chunksProcessing;
  63.  
  64.     private void Start()
  65.     {
  66.         Init();
  67.  
  68.         if (GameObject.Find("Mesh") != null)
  69.         {
  70.             GameObject.Find("Mesh").SetActive(!hideMeshOnPlay);
  71.         }
  72.  
  73.     }
  74.  
  75.     private void Update()
  76.     {
  77.         if (Input.GetKeyDown(KeyCode.Space))
  78.         {
  79.             EditorMesh();
  80.         }
  81.  
  82.         //Limit the number of chunks that can be processed at once, preventing lag spikes
  83.         for (int i = 0; i < chunksToProcess.Count; i++)
  84.         {
  85.             if(chunksProcessing < maxChunksProcessing)
  86.             {
  87.                 ProcessChunk(chunksToProcess[i]);
  88.                 chunksProcessing++;
  89.                 chunksToProcess.RemoveAt(i);
  90.             }
  91.         }
  92.     }
  93.  
  94.     public GameObject ChunkObject(Vector2 position)
  95.     {
  96.         GameObject chunk = new GameObject("Chunk");
  97.         chunk.AddComponent<MeshFilter>().mesh = ChunkMesh(position, 0);
  98.         chunk.AddComponent<MeshRenderer>().material = material;
  99.         chunk.GetComponent<MeshRenderer>().receiveShadows = false;
  100.         chunk.GetComponent<MeshRenderer>().shadowCastingMode = ShadowCastingMode.Off;
  101.  
  102.         return chunk;
  103.     }
  104.  
  105.     public Mesh ChunkMesh(Vector2 position, int lod)
  106.     {
  107.         Mesh mesh = new Mesh();
  108.         mesh.indexFormat = IndexFormat.UInt32;
  109.  
  110.         lod = lod == 0 ? 1 : lod * 2;
  111.  
  112.         chunksToProcess.Add(new ChunkData
  113.         {
  114.             mesh = mesh,
  115.             position = position,
  116.             lod = lod
  117.         });
  118.  
  119.         return mesh;
  120.     }
  121.  
  122.     public void EditorMesh()
  123.     {
  124.         Init();
  125.  
  126.         Mesh mesh = ChunkMesh(offset / 10, lod);
  127.         ProcessChunk(chunksToProcess[0]);
  128.         chunksToProcess.Clear();
  129.  
  130.         if (GameObject.Find("Mesh") == null)
  131.         {
  132.             GameObject obj = new GameObject("Mesh");
  133.             obj.transform.parent = transform;
  134.             obj.AddComponent<MeshFilter>().mesh = mesh;
  135.             obj.AddComponent<MeshRenderer>().material = material;
  136.             obj.AddComponent<MeshCollider>();
  137.         }
  138.  
  139.         else
  140.         {
  141.             var mf = GameObject.Find("Mesh").GetComponent<MeshFilter>();
  142.             var collider = GameObject.Find("Mesh").GetComponent<MeshCollider>();
  143.  
  144.             mf.mesh = mesh;
  145.             collider.sharedMesh = null;
  146.             collider.sharedMesh = mesh;
  147.         }
  148.     }
  149.  
  150.     void Init()
  151.     {
  152.         chunksToProcess = new();
  153.         cam = Camera.main;
  154.  
  155.         nativeTriTable = new NativeArray<int>(256 * 15, Allocator.Persistent);
  156.         nativeCaseToNumPolys = new NativeArray<int>(256, Allocator.Persistent);
  157.  
  158.  
  159.         for (int i = 0; i < Tables.triTable.Length; i++)
  160.         {
  161.             nativeCaseToNumPolys[i] = Tables.caseToNumPolys[i];
  162.  
  163.             for (int j = 0; j < 15; j++)
  164.             {
  165.                 nativeTriTable[(i * 15) + j] = Tables.triTable[i][j];
  166.             }
  167.         }
  168.  
  169.         voxelGrid = new VoxelGrid(frequency, lacunarity, persistance, octaves, surfaceLevel, density, heightMultiplier, warpingFrequency, warpingStrength, curveResolution, heightCurve, voxelGridShader);
  170.     }
  171.  
  172.     void ProcessChunk(ChunkData cd)
  173.     {
  174.         //Getting back data from the GPU is slow, so first the voxel grid is requested then
  175.         //whenever the data has reached the CPU the mesh is constructed
  176.         Profiler.BeginSample("Requesting Voxel grid");
  177.         voxelGrid.RequestVoxelGrid(cd.position, cd.lod, out ShaderData shaderData);
  178.         Profiler.EndSample();
  179.  
  180.         Stopwatch watch = new Stopwatch();
  181.         watch.Start();
  182.  
  183.         AsyncGPUReadback.Request(shaderData.voxelGridBuffer, request =>
  184.         {
  185.             if (request.hasError)
  186.             {
  187.                 UnityEngine.Debug.LogWarning("GPU readback error detected.");
  188.             }
  189.             else
  190.             {
  191.                 watch.Stop();
  192.                 if (stopWatch)
  193.                     UnityEngine.Debug.Log("Getting voxelGrid took " + watch.ElapsedMilliseconds + " ms");
  194.  
  195.                 Profiler.BeginSample("Readback after completion");
  196.                 NativeArray<float> voxelGrid = request.GetData<float>();
  197.                 ConstructMesh(voxelGrid, cd.mesh, cd.lod);
  198.                 Profiler.EndSample();
  199.             }
  200.         });
  201.  
  202.         //CompleteReadBack(shaderData, cd.mesh, cd.lod, watch);
  203.     }
  204.  
  205.     //void CompleteReadBack(ShaderData sd, Mesh mesh, int lod, Stopwatch watch)
  206.     //{
  207.     //    //var request = AsyncGPUReadback.Request(sd.voxelGridBuffer);
  208.  
  209.     //    //// Wait for the request to complete
  210.     //    //while (!request.done)
  211.     //    //{
  212.     //    //    await Task.Yield();
  213.     //    //}
  214.  
  215.     //    //if (request.hasError)
  216.     //    //{
  217.     //    //    UnityEngine.Debug.LogWarning("GPU readback error detected.");
  218.     //    //}
  219.     //    //else
  220.     //    //{
  221.     //    //    watch.Stop();
  222.     //    //    if (stopWatch)
  223.     //    //    {
  224.     //    //        UnityEngine.Debug.Log("Getting voxelGrid took " + watch.ElapsedMilliseconds + " ms");
  225.     //    //    }
  226.  
  227.     //    //    Profiler.BeginSample("Readback after completion");
  228.     //    //    //Get voxelGrid after readback is complete
  229.     //    //    sd.voxelGridBuffer.Release();
  230.     //    //    sd.noiseMap2DBuffer.Release();
  231.     //    //    sd.noiseMap2D.Dispose();
  232.  
  233.     //    //    NativeArray<float> voxelGrid = request.GetData<float>();
  234.     //    //    ConstructMesh(voxelGrid, mesh, lod);
  235.     //    //    Profiler.EndSample();
  236.     //    //}
  237.  
  238.     //    AsyncGPUReadback.Request(sd.voxelGridBuffer, request =>
  239.     //    {
  240.     //        if (request.hasError)
  241.     //        {
  242.     //            UnityEngine.Debug.LogWarning("GPU readback error detected.");
  243.     //        }
  244.     //        else
  245.     //        {
  246.     //            watch.Stop();
  247.     //            if (stopWatch)
  248.     //                UnityEngine.Debug.Log("Getting voxelGrid took " + watch.ElapsedMilliseconds + " ms");
  249.  
  250.     //            Profiler.BeginSample("Readback after completion");
  251.     //            NativeArray<float> voxelGrid = request.GetData<float>();
  252.     //            ConstructMesh(voxelGrid, mesh, lod);
  253.     //            Profiler.EndSample();
  254.     //        }
  255.     //    });
  256.  
  257.     //}
  258.  
  259.     async void ConstructMesh(NativeArray<float> voxelGrid, Mesh mesh, int lod)
  260.     {
  261.         Profiler.BeginSample("MeshJob");
  262.         NativeQueue<Triangle> triangleQueue = new NativeQueue<Triangle>(Allocator.Persistent);
  263.  
  264.         MeshJob job = new MeshJob
  265.         {
  266.             triangles = triangleQueue.AsParallelWriter(),
  267.             voxelGrid = voxelGrid,
  268.             lod = lod,
  269.             chunkDimensions = new int2(chunkDimensions.x, chunkDimensions.y),
  270.             interpolate = interpolate,
  271.             triTable = nativeTriTable,
  272.             caseToNumTris = nativeCaseToNumPolys
  273.         };
  274.  
  275.         int length = chunkDimensions.x / lod * chunkDimensions.y / lod * chunkDimensions.x / lod;
  276.         job.Schedule(length, 64).Complete();
  277.         Profiler.EndSample();
  278.  
  279.         List<Vector3> vertices = new();
  280.         List<int> triangleIndices = new();
  281.  
  282.         Dictionary<Vector3, int> vertDictionary = new();
  283.  
  284.         Stopwatch watch = new Stopwatch();
  285.         watch.Start();
  286.  
  287.         await Task.Run( () =>
  288.         {
  289.             while (triangleQueue.Count > 0)
  290.             {
  291.                 Triangle triangle = triangleQueue.Dequeue();
  292.  
  293.                 int vertexCount = vertices.Count;
  294.  
  295.                 if (weldedVertices)
  296.                 {
  297.                     //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.
  298.                     // if not the vertex is added to the dictionary.
  299.                     if (vertDictionary.TryGetValue(triangle.vertex0, out int triIndex0))
  300.                     {
  301.                         triangleIndices.Add(triIndex0);
  302.                     }
  303.                     else
  304.                     {
  305.                         vertices.Add(triangle.vertex0);
  306.                         triangleIndices.Add(vertexCount);
  307.  
  308.                         vertDictionary.Add(triangle.vertex0, vertexCount);
  309.                     }
  310.  
  311.                     if (vertDictionary.TryGetValue(triangle.vertex1, out int triIndex1))
  312.                     {
  313.                         triangleIndices.Add(triIndex1);
  314.                     }
  315.                     else
  316.                     {
  317.                         vertices.Add(triangle.vertex1);
  318.                         triangleIndices.Add(vertexCount);
  319.  
  320.                         vertDictionary.Add(triangle.vertex1, vertexCount);
  321.                     }
  322.  
  323.                     if (vertDictionary.TryGetValue(triangle.vertex2, out int triIndex2))
  324.                     {
  325.                         triangleIndices.Add(triIndex2);
  326.                     }
  327.                     else
  328.                     {
  329.                         vertices.Add(triangle.vertex2);
  330.                         triangleIndices.Add(vertexCount);
  331.  
  332.                         vertDictionary.Add(triangle.vertex2, vertexCount);
  333.                     }
  334.                 }
  335.                 else
  336.                 {
  337.                     vertices.AddRange(new List<Vector3>
  338.                     {
  339.                         triangle.vertex0,
  340.                         triangle.vertex1,
  341.                         triangle.vertex2
  342.                     });
  343.  
  344.                     triangleIndices.AddRange(new List<int>
  345.                     {
  346.                         vertexCount,
  347.                         vertexCount + 1,
  348.                         vertexCount + 2
  349.                     });
  350.                 }
  351.  
  352.             }
  353.         });
  354.  
  355.         watch.Stop();
  356.  
  357.         if (stopWatch)
  358.         {
  359.             UnityEngine.Debug.Log("ConstructMesh() took " + watch.ElapsedMilliseconds + " ms");
  360.         }
  361.  
  362.         triangleQueue.Dispose();
  363.         voxelGrid.Dispose();
  364.  
  365.         mesh.SetVertices(vertices);
  366.         mesh.SetTriangles(triangleIndices, 0);
  367.         mesh.RecalculateNormals();
  368.  
  369.         chunksProcessing--;
  370.     }
  371.  
  372.     struct ChunkData
  373.     {
  374.         public Mesh mesh;
  375.         public Vector2 position;
  376.         public int lod;
  377.     }
  378. }
  379.  
  380. [BurstCompile]
  381. struct MeshJob : IJobParallelFor
  382. {
  383.     [WriteOnly]
  384.     public NativeQueue<Triangle>.ParallelWriter triangles;
  385.  
  386.     [ReadOnly]
  387.     public NativeArray<float> voxelGrid;
  388.  
  389.     [ReadOnly]
  390.     public int lod;
  391.  
  392.     [ReadOnly]
  393.     public int2 chunkDimensions;
  394.  
  395.     [ReadOnly]
  396.     public bool interpolate;
  397.  
  398.     [ReadOnly]
  399.     public NativeArray<int> triTable;
  400.     [ReadOnly]
  401.     public NativeArray<int> caseToNumTris;
  402.  
  403.  
  404.     public void Execute(int i)
  405.     {
  406.         int z = i / (chunkDimensions.x / lod * chunkDimensions.y / lod) * lod;
  407.         int y = (i / (chunkDimensions.x / lod) * lod) - z / lod * chunkDimensions.y;
  408.         int x = i % (chunkDimensions.x / lod) * lod;
  409.  
  410.         AddTriangles(i, x, y, z);
  411.     }
  412.  
  413.     void AddTriangles(int index, int x, int y, int z)
  414.     {
  415.         int triCase = CalculateCase(index);
  416.  
  417.         int triIndex = 0;
  418.  
  419.         for (int i = 0; i < caseToNumTris[triCase]; i++)
  420.         {
  421.             int edge0 = triTable[(triCase * 15) + triIndex + 2];
  422.             float3 vertex0 = CalculateVertexPosition(edge0, index, lod, x, y, z);
  423.  
  424.             int edge1 = triTable[(triCase * 15) + triIndex + 1];
  425.             float3 vertex1 = CalculateVertexPosition(edge1, index, lod, x, y, z);
  426.  
  427.             int edge2 = triTable[(triCase * 15) + triIndex];
  428.             float3 vertex2 = CalculateVertexPosition(edge2, index, lod, x, y, z);
  429.  
  430.             triIndex += 3;
  431.  
  432.             triangles.Enqueue(new Triangle
  433.             {
  434.                 vertex0 = vertex0,
  435.                 vertex1 = vertex1,
  436.                 vertex2 = vertex2
  437.             });
  438.         }
  439.     }
  440.  
  441.     //Returns vertex position at on of the 12 edges of a cube
  442.     float3 CalculateVertexPosition(int edge, int index, int lod, int x, int y, int z)
  443.     {
  444.         int i = index * 8;
  445.  
  446.         switch (edge)
  447.         {
  448.             case 0:
  449.                 return interpolate ? InterpolatePosition(x, y, z, voxelGrid[i], voxelGrid[i + 1], 0, 1, 0) : new float3(x, y + 1, z);
  450.             case 1:
  451.                 return interpolate ? InterpolatePosition(x, y + lod, z, voxelGrid[i + 1], voxelGrid[i + 2], 1, 0, 0) : new float3(x + 1, y + lod, z);
  452.             case 2:
  453.                 return interpolate ? InterpolatePosition(x + lod, y, z, voxelGrid[i + 3], voxelGrid[i + 2], 0, 1, 0) : new float3(x + lod, y + 1, z);
  454.             case 3:
  455.                 return interpolate ? InterpolatePosition(x, y, z, voxelGrid[i], voxelGrid[i + 3], 1, 0, 0) : new float3(x + 1, y, z);
  456.  
  457.             case 4:
  458.                 return interpolate ? InterpolatePosition(x, y, z + lod, voxelGrid[i + 4], voxelGrid[i + 5], 0, 1, 0) : new float3(x, y + 1, z + lod);
  459.             case 5:
  460.                 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);
  461.             case 6:
  462.                 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);
  463.             case 7:
  464.                 return interpolate ? InterpolatePosition(x, y, z + lod, voxelGrid[i + 4], voxelGrid[i + 7], 1, 0, 0) : new float3(x + 1, y, z + lod);
  465.  
  466.             case 8:
  467.                 return interpolate ? InterpolatePosition(x, y, z, voxelGrid[i], voxelGrid[i + 4], 0, 0, 1) : new float3(x, y, z + 1);
  468.             case 9:
  469.                 return interpolate ? InterpolatePosition(x, y + lod, z, voxelGrid[i + 1], voxelGrid[i + 5], 0, 0, 1) : new float3(x, y + lod, z + 1);
  470.             case 10:
  471.                 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);
  472.             case 11:
  473.                 return interpolate ? InterpolatePosition(x + lod, y, z, voxelGrid[i + 3], voxelGrid[i + 7], 0, 0, 1) : new float3(x + lod, y, z + 1);
  474.  
  475.             default:
  476.                 throw new ArgumentOutOfRangeException(nameof(edge), "Invalid edge index: " + edge);
  477.         }
  478.  
  479.     }
  480.  
  481.     public float3 InterpolatePosition(int x, int y, int z, float cornerValue0, float cornerValue1, int offsetX, int offsetY, int offsetZ)
  482.     {
  483.         float interpolation = (0 - cornerValue0) / (cornerValue1 - cornerValue0);
  484.  
  485.         return new float3(x + offsetX * interpolation, y + offsetY * interpolation, z + offsetZ * interpolation);
  486.     }
  487.  
  488.     public int CalculateCase(int index)
  489.     {
  490.         int triCase = 0;
  491.  
  492.         for (int i = 0; i < 8; i++)
  493.         {
  494.             int bit = voxelGrid[(index * 8) + i] < 0 ? 1 : 0;
  495.             triCase |= bit << i;
  496.         }
  497.  
  498.         return triCase;
  499.     }
  500. }
  501.  
  502. public struct Triangle
  503. {
  504.     public float3 vertex0;
  505.     public float3 vertex1;
  506.     public float3 vertex2;
  507. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement