Advertisement
JontePonte

meshgen draft 2

Oct 2nd, 2024
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 13.09 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 System.Diagnostics;
  8. using UnityEngine.UIElements;
  9. using Unity.VisualScripting;
  10. using Unity.Jobs;
  11. using Unity.Collections;
  12. using static UnityEngine.Mesh;
  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 hideMeshOnPlay;
  32.  
  33.     [Header("Noise Settings")]
  34.     public float heightMultiplier;
  35.     public int curveResolution;
  36.     public AnimationCurve heightCurve;
  37.     public float frequency;
  38.     public float lacunarity;
  39.     public float persistance;
  40.     public int octaves;
  41.     public float density;
  42.     public float warpingStrength;
  43.     public float warpingFrequency;
  44.     public Vector2 offset;
  45.  
  46.     [Header("")]
  47.     public bool autoUpdate;
  48.     public string executionTime;
  49.  
  50.     Camera cam;
  51.  
  52.     VoxelGrid voxelGrid;
  53.  
  54.     List<ChunkData> chunksToProcess;
  55.     int chunksProcessing;
  56.  
  57.     Stopwatch watch;
  58.  
  59.     private void Start()
  60.     {
  61.         Init();
  62.  
  63.         if (GameObject.Find("Mesh") != null)
  64.         {
  65.             GameObject.Find("Mesh").SetActive(!hideMeshOnPlay);
  66.         }
  67.  
  68.     }
  69.  
  70.     private void Update()
  71.     {
  72.         if (Input.GetKeyDown(KeyCode.Space))
  73.         {
  74.             EditorMesh();
  75.         }
  76.  
  77.         //Limit the number of chunks that can be processed at once, preventing lag spikes
  78.         for (int i = 0; i < chunksToProcess.Count; i++)
  79.         {
  80.             if(chunksProcessing < maxChunksProcessing)
  81.             {
  82.                 ProcessChunk(chunksToProcess[i]);
  83.                 chunksProcessing++;
  84.                 chunksToProcess.RemoveAt(i);
  85.             }
  86.         }
  87.     }
  88.  
  89.     public GameObject ChunkObject(Vector2 position)
  90.     {
  91.         GameObject chunk = new GameObject("Chunk");
  92.         chunk.AddComponent<MeshFilter>().mesh = ChunkMesh(position, 0);
  93.         chunk.AddComponent<MeshRenderer>().material = material;
  94.         chunk.GetComponent<MeshRenderer>().receiveShadows = false;
  95.         chunk.GetComponent<MeshRenderer>().shadowCastingMode = ShadowCastingMode.Off;
  96.  
  97.         return chunk;
  98.     }
  99.  
  100.     public Mesh ChunkMesh(Vector2 position, int lod)
  101.     {
  102.         Mesh mesh = new Mesh();
  103.         mesh.indexFormat = IndexFormat.UInt32;
  104.  
  105.         lod = lod == 0 ? 1 : lod * 2;
  106.  
  107.         chunksToProcess.Add(new ChunkData
  108.         {
  109.             mesh = mesh,
  110.             position = position,
  111.             lod = lod
  112.         });
  113.  
  114.         return mesh;
  115.     }
  116.  
  117.     public void EditorMesh()
  118.     {
  119.         Init();
  120.  
  121.         Mesh mesh = ChunkMesh(offset / 10, lod);
  122.         ProcessChunk(chunksToProcess[0]);
  123.         chunksToProcess.Clear();
  124.  
  125.         if (GameObject.Find("Mesh") == null)
  126.         {
  127.             GameObject obj = new GameObject("Mesh");
  128.             obj.transform.parent = transform;
  129.             obj.AddComponent<MeshFilter>().mesh = mesh;
  130.             obj.AddComponent<MeshRenderer>().material = material;
  131.             obj.AddComponent<MeshCollider>();
  132.         }
  133.  
  134.         else
  135.         {
  136.             var mf = GameObject.Find("Mesh").GetComponent<MeshFilter>();
  137.             var collider = GameObject.Find("Mesh").GetComponent<MeshCollider>();
  138.  
  139.             mf.mesh = mesh;
  140.             collider.sharedMesh = null;
  141.             collider.sharedMesh = mesh;
  142.         }
  143.     }
  144.  
  145.     void Init()
  146.     {
  147.         chunksToProcess = new();
  148.         cam = Camera.main;
  149.  
  150.         voxelGrid = new VoxelGrid(frequency, lacunarity, persistance, octaves, surfaceLevel, density, heightMultiplier, warpingFrequency, warpingStrength, curveResolution, heightCurve, voxelGridShader);
  151.     }
  152.  
  153.     void ProcessChunk(ChunkData cd)
  154.     {
  155.         //Getting back data from the GPU is slow, so first the voxel grid is requested then
  156.         //whenever the data has reached the CPU the mesh is constructed
  157.  
  158.         voxelGrid.RequestVoxelGrid(cd.position, cd.lod, out ShaderData shaderData);
  159.         CompleteReadBack(shaderData, cd.mesh, cd.lod);
  160.     }
  161.  
  162.     async void CompleteReadBack(ShaderData sd, Mesh mesh, int lod)
  163.     {
  164.         var request = AsyncGPUReadback.Request(sd.voxelGridBuffer);
  165.  
  166.         // Wait for the request to complete
  167.         while (!request.done)
  168.         {
  169.             await Task.Yield();
  170.         }
  171.  
  172.         if (request.hasError)
  173.         {
  174.             UnityEngine.Debug.LogWarning("GPU readback error detected.");
  175.         }
  176.         else
  177.         {
  178.             //Get voxelGrid after readback is complete
  179.             sd.voxelGridBuffer.Release();
  180.             sd.noiseMap2DBuffer.Release();
  181.             sd.noiseMap2D.Dispose();
  182.  
  183.             float[] voxelGrid = request.GetData<float>().ToArray();
  184.  
  185.             ConstructMesh(voxelGrid, mesh, lod);
  186.         }
  187.     }
  188.  
  189.     async void ConstructMesh(float[] voxelGrid, Mesh mesh, int lod)
  190.     {
  191.         List<Vector3> vertices = new();
  192.         List<int> indices = new();
  193.         Dictionary<Vector3, int> vertDictionary = new();
  194.  
  195.         MeshData meshData = new MeshData
  196.         {
  197.             vertices = vertices,
  198.             indices = indices,
  199.             vertDictionary = vertDictionary,
  200.             voxelGrid = voxelGrid,
  201.             lod = lod
  202.         };
  203.  
  204.         //Offloads mesh creation to another thread so the main thread is not halted
  205.         await Task.Run(() =>
  206.         {
  207.             for (int z = 0, i = 0; z < chunkDimensions.x; z += lod)
  208.             {
  209.                 for (int y = 0; y < chunkDimensions.y; y += lod)
  210.                 {
  211.                     for (int x = 0; x < chunkDimensions.x; x += lod, i++)
  212.                     {
  213.                         AddVertices(i, new Vector3Int(x, y, z), meshData);
  214.                     }
  215.                 }
  216.             }
  217.         });
  218.  
  219.         mesh.vertices = vertices.ToArray();
  220.         mesh.triangles = indices.ToArray();
  221.         mesh.RecalculateNormals();
  222.  
  223.         chunksProcessing--;
  224.     }
  225.  
  226.     void AddVertices(int index, Vector3Int pos, MeshData md)
  227.     {
  228.         float[] voxelCornerValues = new float[8];
  229.  
  230.         for (int i = 0; i < 8; i++)
  231.         {
  232.             voxelCornerValues[i] = md.voxelGrid[(index * 8) + i];
  233.         }
  234.  
  235.         int triCase = CalculateCase(voxelCornerValues);
  236.         int[] edges = Tables.triTable[triCase];
  237.  
  238.         for (int j = 0, i = 0; j < Tables.caseToNumPolys[triCase]; j++, i += 3)
  239.         {
  240.             for (int h = 0; h < 3; h++)
  241.             {
  242.                 Vector3 vertex = GetEdgePosition(edges[i + (2 - h)], voxelCornerValues, md.lod, pos);
  243.  
  244.                 //Checks for duplicate vertices
  245.                 if(md.vertDictionary.TryGetValue(vertex, out int triIndex))
  246.                 {
  247.                     md.indices.Add(triIndex);
  248.                 }
  249.                 else
  250.                 {
  251.                     md.vertDictionary.Add(vertex, md.vertices.Count);
  252.                     md.vertices.Add(vertex);
  253.                     md.indices.Add(md.vertices.Count - 1);
  254.                 }
  255.             }
  256.         }
  257.     }
  258.  
  259.     Vector3 GetEdgePosition(int edge, float[] voxelData, int lod, Vector3Int pos)
  260.     {
  261.         int x = pos.x;
  262.         int y = pos.y;
  263.         int z = pos.z;
  264.  
  265.  
  266.         switch (edge)
  267.         {
  268.             case 0: return GetInterpolatedEdgePosition(x, y, z, voxelData[0], voxelData[1], 0, 1, 0);
  269.             case 1: return GetInterpolatedEdgePosition(x, y + lod, z, voxelData[1], voxelData[2], 1, 0, 0);
  270.             case 2: return GetInterpolatedEdgePosition(x + lod, y, z, voxelData[3], voxelData[2], 0, 1, 0);
  271.             case 3: return GetInterpolatedEdgePosition(x, y, z, voxelData[0], voxelData[3], 1, 0, 0);
  272.  
  273.             case 4: return GetInterpolatedEdgePosition(x, y, z + lod, voxelData[4], voxelData[5], 0, 1, 0);
  274.             case 5: return GetInterpolatedEdgePosition(x, y + lod, z + lod, voxelData[5], voxelData[6], 1, 0, 0);
  275.             case 6: return GetInterpolatedEdgePosition(x + lod, y, z + lod, voxelData[7], voxelData[6], 0, 1, 0);
  276.             case 7: return GetInterpolatedEdgePosition(x, y, z + lod, voxelData[4], voxelData[7], 1, 0, 0);
  277.  
  278.             case 8: return GetInterpolatedEdgePosition(x, y, z, voxelData[0], voxelData[4], 0, 0, 1);
  279.             case 9: return GetInterpolatedEdgePosition(x, y + lod, z, voxelData[1], voxelData[5], 0, 0, 1);
  280.             case 10: return GetInterpolatedEdgePosition(x + lod, y + lod, z, voxelData[2], voxelData[6], 0, 0, 1);
  281.             case 11: return GetInterpolatedEdgePosition(x + lod, y, z, voxelData[3], voxelData[7], 0, 0, 1);
  282.  
  283.             default: throw new ArgumentOutOfRangeException(nameof(edge), "Invalid edge index: " + edge);
  284.         }
  285.     }
  286.     public Vector3 GetInterpolatedEdgePosition(int x, int y, int z, float voxelA, float voxelB, int offsetX, int offsetY, int offsetZ)
  287.     {
  288.         float interpolation = (0 - voxelA) / (voxelB - voxelA);
  289.  
  290.         return new Vector3(x + offsetX * interpolation, y + offsetY * interpolation, z + offsetZ * interpolation);
  291.     }
  292.  
  293.     public int CalculateCase(float[] voxelData)
  294.     {
  295.         int triCase = 0;
  296.  
  297.         for (int i = 0; i < 8; i++)
  298.         {
  299.             int bit = voxelData[i] < 0 ? 1 : 0;
  300.             triCase |= bit << i;
  301.         }
  302.  
  303.         return triCase;
  304.     }
  305.  
  306.     struct MeshData
  307.     {
  308.         public List<Vector3> vertices;
  309.         public List<int> indices;
  310.         public Dictionary<Vector3, int> vertDictionary;
  311.         public float[] voxelGrid;
  312.         public int lod;
  313.     }
  314.  
  315.     struct ChunkData
  316.     {
  317.         public Mesh mesh;
  318.         public Vector2 position;
  319.         public int lod;
  320.     }
  321. }
  322.  
  323. struct MeshJob : IJobParallelFor
  324. {
  325.     [WriteOnly]
  326.     public NativeList<float3>.ParallelWriter vertices;
  327.  
  328.     [ReadOnly]
  329.     public NativeArray<float> voxelGrid;
  330.  
  331.     [ReadOnly]
  332.     public int lod;
  333.  
  334.     [ReadOnly]
  335.     public int2 chunkDimensions;
  336.  
  337.     [ReadOnly]
  338.     public NativeArray<int> triTable;
  339.     public NativeArray<int> caseToNumPolys;
  340.  
  341.  
  342.     public void Execute(int i)
  343.     {
  344.         int z = i / (chunkDimensions.x / lod * chunkDimensions.y / lod) * lod;
  345.         int y = (i / (chunkDimensions.x / lod) * lod) - z / lod * chunkDimensions.y;
  346.         int x = i % (chunkDimensions.x / lod) * lod;
  347.  
  348.         AddVertices(i, x, y, z);
  349.     }
  350.  
  351.     void AddVertices(int index, int x, int y, int z)
  352.     {
  353.         int triCase = CalculateCase(index);
  354.  
  355.         for (int j = 0, i = 0; j < caseToNumPolys[triCase]; j++, i += 3)
  356.         {
  357.             for (int h = 0; h < 3; h++)
  358.             {
  359.                 int edge = triTable[(triCase * 15) + i + (2 - h)];
  360.                 float3 vertex = GetEdgePosition(edge, index, lod, x, y, z);
  361.  
  362.                 vertices.AddNoResize(vertex);
  363.             }
  364.         }
  365.     }
  366.  
  367.     float3 GetEdgePosition(int edge, int index, int lod, int x, int y, int z)
  368.     {
  369.         int i = index * 8;
  370.  
  371.         switch (edge)
  372.         {
  373.             case 0: return GetInterpolatedEdgePosition(x, y, z, voxelGrid[i], voxelGrid[i + 1], 0, 1, 0);
  374.             case 1: return GetInterpolatedEdgePosition(x, y + lod, z, voxelGrid[i + 1], voxelGrid[i + 2], 1, 0, 0);
  375.             case 2: return GetInterpolatedEdgePosition(x + lod, y, z, voxelGrid[i + 3], voxelGrid[i + 2], 0, 1, 0);
  376.             case 3: return GetInterpolatedEdgePosition(x, y, z, voxelGrid[i], voxelGrid[i + 3], 1, 0, 0);
  377.  
  378.             case 4: return GetInterpolatedEdgePosition(x, y, z + lod, voxelGrid[i + 4], voxelGrid[i + 5], 0, 1, 0);
  379.             case 5: return GetInterpolatedEdgePosition(x, y + lod, z + lod, voxelGrid[i + 5], voxelGrid[i + 6], 1, 0, 0);
  380.             case 6: return GetInterpolatedEdgePosition(x + lod, y, z + lod, voxelGrid[i + 7], voxelGrid[i + 6], 0, 1, 0);
  381.             case 7: return GetInterpolatedEdgePosition(x, y, z + lod, voxelGrid[i + 4], voxelGrid[i + 7], 1, 0, 0);
  382.  
  383.             case 8: return GetInterpolatedEdgePosition(x, y, z, voxelGrid[i], voxelGrid[i + 4], 0, 0, 1);
  384.             case 9: return GetInterpolatedEdgePosition(x, y + lod, z, voxelGrid[i + 1], voxelGrid[i + 5], 0, 0, 1);
  385.             case 10: return GetInterpolatedEdgePosition(x + lod, y + lod, z, voxelGrid[i + 2], voxelGrid[i + 6], 0, 0, 1);
  386.             case 11: return GetInterpolatedEdgePosition(x + lod, y, z, voxelGrid[i + 3], voxelGrid[i + 7], 0, 0, 1);
  387.  
  388.             default: throw new ArgumentOutOfRangeException(nameof(edge), "Invalid edge index: " + edge);
  389.         }
  390.     }
  391.  
  392.     public float3 GetInterpolatedEdgePosition(int x, int y, int z, float voxelA, float voxelB, int offsetX, int offsetY, int offsetZ)
  393.     {
  394.         float interpolation = (0 - voxelA) / (voxelB - voxelA);
  395.  
  396.         return new float3(x + offsetX * interpolation, y + offsetY * interpolation, z + offsetZ * interpolation);
  397.     }
  398.  
  399.     public int CalculateCase(int index)
  400.     {
  401.         int triCase = 0;
  402.  
  403.         for (int i = 0; i < 8; i++)
  404.         {
  405.             int bit = voxelGrid[(index * 8) + i] < 0 ? 1 : 0;
  406.             triCase |= bit << i;
  407.         }
  408.  
  409.         return triCase;
  410.     }
  411. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement