Advertisement
JontePonte

very good but slow voxel engine

Jul 30th, 2024
175
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 27.21 KB | None | 0 0
  1. using Unity.Burst;
  2. using Unity.Collections;
  3. using Unity.Jobs;
  4. using Unity.Mathematics;
  5.  
  6. [BurstCompile(CompileSynchronously = true)]
  7. struct ChunkJob : IJob
  8. {
  9.     public NativeList<float3> vertices;
  10.     public NativeList<int> triangles;
  11.     public NativeList<float2> uvs;
  12.  
  13.     [ReadOnly] public NativeArray<int3> faceDirections;
  14.     [ReadOnly] public NativeArray<float2> uvCordinates;
  15.     [ReadOnly] public NativeArray<int3> faceVertices;
  16.  
  17.     [ReadOnly] public int2 chunkDimensions;
  18.  
  19.     //Neighbouring chunks
  20.     [ReadOnly] public NativeArray<int> thisChunkData;
  21.     [ReadOnly] public NativeArray<int> leftChunkData;
  22.     [ReadOnly] public NativeArray<int> rightChunkData;
  23.     [ReadOnly] public NativeArray<int> frontChunkData;
  24.     [ReadOnly] public NativeArray<int> backChunkData;
  25.  
  26.     public void Execute()
  27.     {
  28.         for (int x = 0; x < chunkDimensions.x; x++)
  29.         {
  30.             for (int y = 0; y < chunkDimensions.y; y++)
  31.             {
  32.                 for (int z = 0; z < chunkDimensions.x; z++)
  33.                 {
  34.                     int blockIndex = thisChunkData[CalculateVoxelIndex(x, y, z)];
  35.                     if (blockIndex != 0)
  36.                     {
  37.                         AddVoxel(x, y, z, blockIndex, vertices, triangles, uvs);
  38.                     }
  39.                 }
  40.             }
  41.         }
  42.     }
  43.  
  44.     private void AddVoxel(int x, int y, int z, int blockIndex, NativeList<float3> vertices, NativeList<int> triangles, NativeList<float2> uvs)
  45.     {
  46.         NativeArray<float2> blockUVs = GetBlockUVs(blockIndex);
  47.         for (int i = 0; i < 6; i++)
  48.         {
  49.             if (IsFaceVisible(x, y, z, i))
  50.             {
  51.                 AddQuad(new int3(x, y, z), i, blockUVs[i], vertices, triangles, uvs);
  52.             }
  53.         }
  54.     }
  55.  
  56.     private void AddQuad(int3 quadPos, int i, float2 bottomLeftUV, NativeList<float3> vertices, NativeList<int> triangles, NativeList<float2> uvs)
  57.     {
  58.         int vertCount = vertices.Length;
  59.  
  60.         NativeArray<float3> verts = new NativeArray<float3>(4, Allocator.Temp);
  61.  
  62.         for (int j = 0; j < 4; j++)
  63.         {
  64.             verts[j] = faceVertices[(i * 4) + j] + quadPos;
  65.         }
  66.  
  67.         vertices.AddRange(verts);
  68.  
  69.         NativeArray<int> tris = new NativeArray<int>(6, Allocator.Temp);
  70.  
  71.         tris[0] = vertCount;
  72.         tris[1] = vertCount + 1;
  73.         tris[2] = vertCount + 3;
  74.         tris[3] = vertCount + 3;
  75.         tris[4] = vertCount + 1;
  76.         tris[5] = vertCount + 2;
  77.  
  78.         triangles.AddRange(tris);
  79.  
  80.         NativeArray<float2> uv = new NativeArray<float2>(4, Allocator.Temp);
  81.  
  82.         uv[0] = bottomLeftUV + new float2(.5f, 0);
  83.         uv[1] = bottomLeftUV + new float2(.5f, .25f);
  84.         uv[2] = bottomLeftUV + new float2(0, .25f);
  85.         uv[3] = bottomLeftUV;
  86.  
  87.         uvs.AddRange(uv);
  88.     }
  89.  
  90.     private bool IsFaceVisible(int x, int y, int z, int faceIndex)
  91.     {
  92.         int3 direction = faceDirections[faceIndex];
  93.         int3 neighbor = new int3(x + direction.x, y + direction.y, z + direction.z);
  94.  
  95.         if (neighbor.y < 0 || neighbor.y >= chunkDimensions.y)
  96.             return false;
  97.  
  98.         if (neighbor.x < 0)
  99.             return leftChunkData[CalculateVoxelIndex(chunkDimensions.x-1, neighbor.y, neighbor.z)] == 0;
  100.  
  101.         if (neighbor.x >= chunkDimensions.x)
  102.             return rightChunkData[CalculateVoxelIndex(0, neighbor.y, neighbor.z)] == 0;
  103.  
  104.         if (neighbor.z < 0)
  105.             return backChunkData[CalculateVoxelIndex(neighbor.x, neighbor.y, chunkDimensions.x-1)] == 0;
  106.  
  107.         if (neighbor.z >= chunkDimensions.x)
  108.             return frontChunkData[CalculateVoxelIndex(neighbor.x, neighbor.y, 0)] == 0;
  109.  
  110.         return thisChunkData[CalculateVoxelIndex(neighbor.x, neighbor.y, neighbor.z)] == 0;
  111.     }
  112.  
  113.     //Get the bottom left UV for all 6 faces
  114.     public NativeArray<float2> GetBlockUVs(int blockIndex)
  115.     {
  116.  
  117.         NativeArray<float2> uvs = new NativeArray<float2>(6, Allocator.Temp);
  118.         switch (blockIndex)
  119.         {
  120.             case 1:
  121.                 uvs[0] = new float2(0, 0);
  122.                 uvs[1] = new float2(0, 0);
  123.                 uvs[2] = new float2(0, 0);
  124.                 uvs[3] = new float2(0, 0);
  125.                 uvs[4] = new float2(0, 0);
  126.                 uvs[5] = new float2(0, 0);
  127.                 break;
  128.                
  129.             case 2:
  130.                 uvs[0] = new float2(0, .5f);
  131.                 uvs[1] = new float2(0, .5f);
  132.                 uvs[2] = new float2(0, .5f);
  133.                 uvs[3] = new float2(0, .5f);
  134.                 uvs[4] = new float2(0, .5f);
  135.                 uvs[5] = new float2(0, .5f);
  136.                 break;
  137.             case 3:
  138.                 uvs[0] = new float2(0, .5f);
  139.                 uvs[1] = new float2(.5f, .5f);
  140.                 uvs[2] = new float2(.5f, .5f);
  141.                 uvs[3] = new float2(.5f, .5f);
  142.                 uvs[4] = new float2(.5f, .5f);
  143.                 uvs[5] = new float2(0, .25f);
  144.                 break;
  145.             case 4:
  146.                 uvs[0] = new float2(.5f, 0);
  147.                 uvs[1] = new float2(.5f, 0);
  148.                 uvs[2] = new float2(.5f, 0);
  149.                 uvs[3] = new float2(.5f, 0);
  150.                 uvs[4] = new float2(.5f, 0);
  151.                 uvs[5] = new float2(.5f, 0);
  152.                 break;
  153.             case 5:
  154.                 uvs[0] = new float2(.5f, .25f);
  155.                 uvs[1] = new float2(.5f, .25f);
  156.                 uvs[2] = new float2(.5f, .25f);
  157.                 uvs[3] = new float2(.5f, .25f);
  158.                 uvs[4] = new float2(.5f, .25f);
  159.                 uvs[5] = new float2(.5f, .25f);
  160.                 break;
  161.  
  162.         }
  163.  
  164.         return uvs;
  165.     }
  166.  
  167.     public int CalculateVoxelIndex(int x, int y, int z)
  168.     {
  169.         return x * (chunkDimensions.y * chunkDimensions.x) + y * chunkDimensions.x + z;
  170.     }
  171. }
  172.  
  173. using System;
  174. using System.Collections.Generic;
  175. using UnityEngine;
  176.  
  177. public class EndlessTerrain : MonoBehaviour
  178. {
  179.     public float maxViewDst;
  180.     public float colliderRange;
  181.  
  182.     public Transform viewer;
  183.     public static Vector2 viewerPosition;
  184.  
  185.     int chunksVisibleInViewDst;
  186.     Vector2Int chunkDimensions;
  187.  
  188.     Dictionary<Vector2, TerrainChunk> chunkDictionary = new Dictionary<Vector2, TerrainChunk>();
  189.     List<TerrainChunk> chunksVisibleLastUpdate = new List<TerrainChunk>();
  190.  
  191.     void Start()
  192.     {
  193.         chunkDimensions = ChunkGenerator.ChunkDimensions;
  194.         chunksVisibleInViewDst = Mathf.RoundToInt(maxViewDst / chunkDimensions.x);
  195.     }
  196.  
  197.     void Update()
  198.     {
  199.         viewerPosition = new Vector2(viewer.position.x, viewer.position.z);
  200.         UpdateVisibleChunks();
  201.     }
  202.  
  203.     void UpdateVisibleChunks()
  204.     {
  205.         for (int i = 0; i < chunksVisibleLastUpdate.Count; i++)
  206.         {
  207.             chunksVisibleLastUpdate[i].chunkObject.SetActive(false);
  208.         }
  209.  
  210.         chunksVisibleLastUpdate.Clear();
  211.  
  212.         int currentChunkCoordX = (int)Mathf.Round(viewerPosition.x / chunkDimensions.x);
  213.         int currentChunkCoordY = (int)Mathf.Round(viewerPosition.y / chunkDimensions.x);
  214.  
  215.         for (int yOffset = -chunksVisibleInViewDst; yOffset <= chunksVisibleInViewDst; yOffset++) //Loops through all the current chunks
  216.         {
  217.             for (int xOffset = -chunksVisibleInViewDst; xOffset <= chunksVisibleInViewDst; xOffset++)
  218.             {
  219.                 Vector2Int viewedChunkCoord = new Vector2Int(currentChunkCoordX + xOffset, currentChunkCoordY + yOffset);
  220.  
  221.                 if (chunkDictionary.ContainsKey(viewedChunkCoord)) //Checks if a chunk with the correct cordinates has been loaded previously
  222.                 {
  223.                     TerrainChunk viewedChunk = chunkDictionary[viewedChunkCoord];
  224.  
  225.                     viewedChunk.UpdateTerrainChunk();
  226.  
  227.                     if (viewedChunk.IsVisible())
  228.                     {
  229.                         chunksVisibleLastUpdate.Add(viewedChunk);
  230.                     }
  231.                 }
  232.                 else
  233.                 {
  234.                     chunkDictionary.Add(viewedChunkCoord, new TerrainChunk(viewedChunkCoord, transform, maxViewDst, colliderRange));
  235.                 }
  236.             }
  237.         }
  238.     }
  239.  
  240.     public class TerrainChunk
  241.     {
  242.         public GameObject chunkObject;
  243.  
  244.         ChunkGenerator chunkGen;
  245.  
  246.         Vector2Int position;
  247.         Bounds bounds;
  248.  
  249.         float maxViewDst;
  250.         float colliderRange;
  251.  
  252.         bool hasCollider;
  253.  
  254.         public TerrainChunk(Vector2Int coord, Transform parent, float maxViewDst, float colliderRange)
  255.         {
  256.             Vector2Int chunkDimensions = ChunkGenerator.ChunkDimensions;
  257.  
  258.             position = coord * chunkDimensions.x;
  259.             bounds = new Bounds(new Vector2(position.x, position.y), Vector2.one * chunkDimensions.x);        
  260.  
  261.             chunkGen = GameObject.Find("ChunkGenerator").GetComponent<ChunkGenerator>();
  262.  
  263.             chunkObject = chunkGen.CreateChunkObject(position);
  264.             chunkObject.transform.position = new Vector3(position.x - (chunkDimensions.x / 2), 0, position.y - (chunkDimensions.x / 2));
  265.  
  266.             chunkObject.transform.parent = parent;
  267.             chunkObject.SetActive(false);
  268.  
  269.             this.maxViewDst = maxViewDst;
  270.             this.colliderRange = colliderRange;
  271.         }
  272.  
  273.         public void UpdateTerrainChunk()
  274.         {
  275.             float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
  276.  
  277.             if(viewerDstFromNearestEdge < colliderRange && !hasCollider)
  278.             {
  279.                 chunkObject.AddComponent<MeshCollider>();
  280.                 hasCollider = true;
  281.             }
  282.  
  283.             bool visible = viewerDstFromNearestEdge <= maxViewDst; //Checks if the chunk is too far away from the player, if so it deactivates it
  284.  
  285.             chunkObject.SetActive(visible);
  286.         }
  287.  
  288.         public bool IsVisible()
  289.         {
  290.             return chunkObject.activeSelf;
  291.         }
  292.     }
  293. }
  294.  
  295. using System.Collections.Generic;
  296. using Unity.Collections;
  297. using UnityEngine;
  298. using Unity.Mathematics;
  299. using Unity.Jobs;
  300. using UnityEngine.Rendering;
  301.  
  302. public class ChunkGenerator : MonoBehaviour
  303. {
  304.     public Texture2D atlas;
  305.     public Vector2Int chunkDimensions;
  306.     public static Vector2Int ChunkDimensions;
  307.  
  308.     public int maxProccessingFrames;
  309.  
  310.     [Header("Terrain Settings")]
  311.     public int surfaceLevel;
  312.     public int seaLevel;
  313.     public float sandDensity;
  314.     public float maxTreeHeight;
  315.  
  316.  
  317.     [Header("Noise Settings")]
  318.     public float persistance;
  319.     public float frequency;
  320.     public float lacunarity;
  321.     public int numOfOctaves;
  322.     public Vector2Int offset;
  323.     public AnimationCurve heightCurve;
  324.     public float heightMultiplier;
  325.     public float noise3DContribution;
  326.     public int noiseSeed;
  327.  
  328.     [Header(" ")]
  329.     public bool autoUpdate;
  330.     public string executionTime;
  331.  
  332.     Noise noise;
  333.  
  334.     NativeArray<int3> faceVertices;
  335.     NativeArray<int3> faceDirections;
  336.     NativeArray<float2> uvCoordinates;
  337.  
  338.     NativeArray<float> sampledCurve;
  339.  
  340.  
  341.     [HideInInspector]
  342.     public NativeHashMap<int2, NativeArray<int>> chunkDataMap;
  343.  
  344.     List<JobData> jobList;
  345.  
  346.     private void Start()
  347.     {
  348.         Initialize();
  349.         executionTime = "N/A";
  350.     }
  351.  
  352.     private void OnValidate()
  353.     {
  354.         if (autoUpdate)
  355.         {
  356.             GenerateChunk();
  357.         }
  358.     }
  359.  
  360.     public void GenerateChunk()
  361.     {
  362.         Initialize();
  363.  
  364.         if(GameObject.Find("Chunk") == null)
  365.         {
  366.             float startTime = Time.realtimeSinceStartup;
  367.  
  368.             CreateChunkObject(offset);
  369.             CompleteJobs(false);
  370.  
  371.             executionTime = (Time.realtimeSinceStartup - startTime) * 1000f + " ms";
  372.         }
  373.  
  374.         else
  375.         {
  376.             float startTime = Time.realtimeSinceStartup;
  377.  
  378.             Mesh mesh = CreateChunkMesh(offset);
  379.             CompleteJobs(false);
  380.  
  381.             executionTime = (Time.realtimeSinceStartup - startTime) * 1000f + " ms";
  382.  
  383.             GameObject.Find("Chunk").GetComponent<MeshFilter>().mesh = mesh;
  384.         }
  385.        
  386.        
  387.  
  388.         faceVertices.Dispose();
  389.         faceDirections.Dispose();
  390.         uvCoordinates.Dispose();
  391.     }
  392.  
  393.     public GameObject CreateChunkObject(Vector2Int offset)
  394.     {
  395.         GameObject chunk = new GameObject("Chunk");
  396.         var meshRenderer = chunk.AddComponent<MeshRenderer>();
  397.         meshRenderer.sharedMaterial = new Material(Shader.Find("Standard"))
  398.         {
  399.             mainTexture = atlas
  400.         };
  401.  
  402.         chunk.AddComponent<MeshFilter>().mesh = CreateChunkMesh(offset);
  403.  
  404.         return chunk;
  405.     }
  406.  
  407.     public Mesh CreateChunkMesh(Vector2Int offset, NativeArray<int> customVoxelValues = default)
  408.     {
  409.         Mesh mesh = new Mesh
  410.         {
  411.             indexFormat = IndexFormat.UInt32
  412.         };
  413.  
  414.         NativeList<float3> vertices = new NativeList<float3>(Allocator.TempJob);
  415.         NativeList<int> triangles = new NativeList<int>(Allocator.TempJob);
  416.         NativeList<float2> uvs = new NativeList<float2>(Allocator.TempJob);
  417.  
  418.         int2 intOffset = new int2(offset.x, offset.y);
  419.  
  420.         if(customVoxelValues == default){
  421.  
  422.             if (!chunkDataMap.ContainsKey(intOffset))
  423.                 chunkDataMap.Add(intOffset, noise.GenerateVoxelValues(offset));
  424.         }
  425.         else{
  426.  
  427.             if (!chunkDataMap.ContainsKey(intOffset))
  428.                 chunkDataMap.Add(intOffset, customVoxelValues);
  429.         }
  430.        
  431.  
  432.         AddNeighboringChunkData(intOffset, offset);
  433.  
  434.  
  435.         ChunkJob job = new ChunkJob
  436.         {
  437.             vertices = vertices,
  438.             triangles = triangles,
  439.             uvs = uvs,
  440.  
  441.             thisChunkData = chunkDataMap[intOffset],
  442.             rightChunkData = chunkDataMap[intOffset + new int2(chunkDimensions.x, 0)],
  443.             leftChunkData = chunkDataMap[intOffset + new int2(-chunkDimensions.x, 0)],
  444.             frontChunkData = chunkDataMap[intOffset + new int2(0, chunkDimensions.x)],
  445.             backChunkData = chunkDataMap[intOffset + new int2(0, -chunkDimensions.x)],
  446.  
  447.             faceDirections = faceDirections,
  448.             uvCordinates = uvCoordinates,
  449.             faceVertices = faceVertices,
  450.             chunkDimensions = new int2(chunkDimensions.x, chunkDimensions.y)
  451.         };
  452.  
  453.         JobHandle handle = job.Schedule();
  454.  
  455.         jobList.Add(new JobData
  456.         {
  457.             mesh = mesh,
  458.             handle = handle,
  459.             vertices = vertices,
  460.             triangles = triangles,
  461.             uvs = uvs,
  462.         });
  463.  
  464.         return mesh;
  465.     }
  466.  
  467.     private void AddNeighboringChunkData(int2 intOffset, Vector2Int offset)
  468.     {
  469.         if(!chunkDataMap.ContainsKey(intOffset + new int2(chunkDimensions.x, 0)))
  470.             chunkDataMap.Add(intOffset + new int2(chunkDimensions.x, 0), noise.GenerateVoxelValues(offset + Vector2Int.right * chunkDimensions.x));
  471.  
  472.         if (!chunkDataMap.ContainsKey(intOffset + new int2(-chunkDimensions.x, 0)))
  473.             chunkDataMap.Add(intOffset + new int2(-chunkDimensions.x, 0), noise.GenerateVoxelValues(offset + Vector2Int.left * chunkDimensions.x));
  474.  
  475.         if (!chunkDataMap.ContainsKey(intOffset + new int2(0, chunkDimensions.x)))
  476.             chunkDataMap.Add(intOffset + new int2(0, chunkDimensions.x), noise.GenerateVoxelValues(offset + Vector2Int.up * chunkDimensions.x));
  477.  
  478.         if (!chunkDataMap.ContainsKey(intOffset + new int2(0, -chunkDimensions.x)))
  479.             chunkDataMap.Add(intOffset + new int2(0, -chunkDimensions.x), noise.GenerateVoxelValues(offset + Vector2Int.down * chunkDimensions.x));
  480.     }
  481.  
  482.  
  483.     public static int CalculateVoxelIndex(int x, int y, int z)
  484.     {
  485.         return x * (ChunkDimensions.y * ChunkDimensions.x) + y * ChunkDimensions.x + z;
  486.     }
  487.  
  488.     private void LateUpdate()
  489.     {
  490.         CompleteJobs(true);
  491.     }
  492.  
  493.     void CompleteJobs(bool waitForCompletion)
  494.     {
  495.         for (int i = jobList.Count - 1; i >= 0; i--)
  496.         {
  497.             if (jobList[i].handle.IsCompleted || jobList[i].frameCount == maxProccessingFrames || !waitForCompletion)
  498.             {
  499.                 jobList[i].handle.Complete();
  500.  
  501.                 NativeArray<Vector3> vertexArray = jobList[i].vertices.AsArray().Reinterpret<Vector3>();
  502.                 NativeArray<int> triangleArray = jobList[i].triangles.AsArray();
  503.                 NativeArray<Vector2> uvArray = jobList[i].uvs.AsArray().Reinterpret<Vector2>();
  504.  
  505.                 // Assign data to mesh
  506.                 jobList[i].mesh.SetVertexBufferParams(vertexArray.Length, new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3));
  507.                 jobList[i].mesh.SetVertexBufferData(vertexArray, 0, 0, vertexArray.Length, 0, MeshUpdateFlags.Default);
  508.                 jobList[i].mesh.SetIndexBufferParams(triangleArray.Length, IndexFormat.UInt32);
  509.                 jobList[i].mesh.SetIndexBufferData(triangleArray, 0, 0, triangleArray.Length, MeshUpdateFlags.Default);
  510.                 jobList[i].mesh.SetSubMesh(0, new SubMeshDescriptor(0, triangleArray.Length), MeshUpdateFlags.Default);
  511.                 jobList[i].mesh.SetUVs(0, uvArray);
  512.  
  513.                 jobList[i].mesh.RecalculateBounds();
  514.                 jobList[i].mesh.RecalculateNormals();
  515.  
  516.                 jobList[i].vertices.Dispose();
  517.                 jobList[i].triangles.Dispose();
  518.                 jobList[i].uvs.Dispose();
  519.             }
  520.  
  521.             jobList[i].frameCount++;
  522.             jobList.RemoveAt(i);
  523.         }
  524.  
  525.     }
  526.  
  527.  
  528.     class JobData
  529.     {
  530.         public JobHandle handle;
  531.  
  532.         public Mesh mesh;
  533.  
  534.         public NativeList<float3> vertices;
  535.         public NativeList<int> triangles;
  536.         public NativeList<float2> uvs;
  537.  
  538.         public int frameCount;
  539.     }
  540.  
  541.     private NativeArray<float> SampleCurve(AnimationCurve curve, int resolution)
  542.     {
  543.         NativeArray<float> curveArr = new NativeArray<float>(resolution, Allocator.Persistent);
  544.  
  545.         for (int i = 0; i < resolution; i++)
  546.         {
  547.             curveArr[i] = curve.Evaluate((float)i / resolution);
  548.         }
  549.  
  550.         return curveArr;
  551.     }
  552.  
  553.     private void Initialize()
  554.     {
  555.         System.Random rand = new System.Random(noiseSeed);
  556.  
  557.         chunkDataMap = new NativeHashMap<int2, NativeArray<int>>(1024, Allocator.Persistent);
  558.         jobList = new();
  559.  
  560.         sampledCurve = SampleCurve(heightCurve, 256);
  561.  
  562.         ChunkDimensions = chunkDimensions;
  563.  
  564.         noise = new Noise(frequency, persistance, lacunarity, numOfOctaves, heightMultiplier, rand.Next() / 10000, surfaceLevel, seaLevel, noise3DContribution, sandDensity, maxTreeHeight, sampledCurve);
  565.  
  566.         faceVertices = new NativeArray<int3>(24, Allocator.Persistent);
  567.  
  568.         //Bottom face
  569.         faceVertices[0] = new int3(1, 0, 0);
  570.         faceVertices[1] = new int3(1, 0, 1);
  571.         faceVertices[2] = new int3(0, 0, 1);
  572.         faceVertices[3] = new int3(0, 0, 0);
  573.  
  574.         //Front face
  575.         faceVertices[4] = new int3(1, 0, 1);
  576.         faceVertices[5] = new int3(1, 1, 1);
  577.         faceVertices[6] = new int3(0, 1, 1);
  578.         faceVertices[7] = new int3(0, 0, 1);
  579.  
  580.         //Back face
  581.         faceVertices[8] = new int3(0, 0, 0);
  582.         faceVertices[9] = new int3(0, 1, 0);
  583.         faceVertices[10] = new int3(1, 1, 0);
  584.         faceVertices[11] = new int3(1, 0, 0);
  585.  
  586.         //Left face
  587.         faceVertices[12] = new int3(0, 0, 1);
  588.         faceVertices[13] = new int3(0, 1, 1);
  589.         faceVertices[14] = new int3(0, 1, 0);
  590.         faceVertices[15] = new int3(0, 0, 0);
  591.  
  592.         //Right face
  593.         faceVertices[16] = new int3(1, 0, 0);
  594.         faceVertices[17] = new int3(1, 1, 0);
  595.         faceVertices[18] = new int3(1, 1, 1);
  596.         faceVertices[19] = new int3(1, 0, 1);
  597.  
  598.         //Top face
  599.         faceVertices[20] = new int3(0, 1, 0);
  600.         faceVertices[21] = new int3(0, 1, 1);
  601.         faceVertices[22] = new int3(1, 1, 1);
  602.         faceVertices[23] = new int3(1, 1, 0);
  603.  
  604.  
  605.         uvCoordinates = new NativeArray<float2>(6, Allocator.Persistent);
  606.  
  607.         uvCoordinates[0] = new float2(.5f, 0);
  608.         uvCoordinates[1] = new float2(.5f, .5f);
  609.         uvCoordinates[2] = new float2(.5f, .5f);
  610.         uvCoordinates[3] = new float2(.5f, .5f);
  611.         uvCoordinates[4] = new float2(.5f, .5f);
  612.         uvCoordinates[5] = new float2(0, 0);
  613.  
  614.  
  615.         faceDirections = new NativeArray<int3>(6, Allocator.Persistent);
  616.  
  617.         faceDirections[0] = new int3(0, -1, 0); // Down
  618.         faceDirections[1] = new int3(0, 0, 1);  // Forward
  619.         faceDirections[2] = new int3(0, 0, -1); // Back
  620.         faceDirections[3] = new int3(-1, 0, 0); // Left
  621.         faceDirections[4] = new int3(1, 0, 0);  // Right
  622.         faceDirections[5] = new int3(0, 1, 0);  // Up
  623.     }
  624.  
  625.  
  626.  
  627.     private void OnDestroy()
  628.     {
  629.         faceVertices.Dispose();
  630.         faceDirections.Dispose();
  631.         uvCoordinates.Dispose();
  632.  
  633.         foreach (var chunk in chunkDataMap)
  634.         {
  635.             chunk.Value.Dispose();
  636.         }
  637.  
  638.         chunkDataMap.Dispose();
  639.     }
  640. }
  641.  
  642. using Unity.Collections;
  643. using UnityEngine;
  644. using Unity.Mathematics;
  645. using UnityEngine.Jobs;
  646. using Unity.Jobs;
  647. using Unity.Burst;
  648.  
  649. //Block Indexes:
  650. //1 = Stone
  651. //2 = Dirt
  652. //3 = Grass
  653. //4 = Sand
  654. //5 = Water
  655.  
  656. public class Noise
  657. {
  658.     float frequency;
  659.     float lacunarity;
  660.     float persistance;
  661.     float seed;
  662.     float height;
  663.     float noise3DContribution;
  664.     int numOfOctaves;
  665.  
  666.     int surfaceLevel;
  667.     int seaLevel;
  668.     float sandDensity;
  669.     float maxTreeHeight;
  670.  
  671.     NativeArray<float> curve;
  672.  
  673.     int2 chunkDimensions;
  674.  
  675.     public Noise(float frequency, float persistance, float lacunarity, int numOfOctaves, float height, float seed, int surfaceLevel, int seaLevel, float noise3DContribution, float sandDensity, float maxTreeHeight, NativeArray<float> curve)
  676.     {
  677.         this.frequency = frequency;
  678.         this.persistance = persistance;
  679.         this.lacunarity = lacunarity;
  680.         this.numOfOctaves = numOfOctaves;
  681.         this.height = height;
  682.         this.seed = seed;
  683.         this.noise3DContribution = noise3DContribution;
  684.  
  685.         this.curve = curve;
  686.  
  687.         this.surfaceLevel = surfaceLevel;
  688.         this.seaLevel = seaLevel;
  689.         this.sandDensity = sandDensity;
  690.         this.maxTreeHeight = maxTreeHeight;
  691.  
  692.         chunkDimensions = new int2(ChunkGenerator.ChunkDimensions.x, ChunkGenerator.ChunkDimensions.y);
  693.     }
  694.  
  695.     public NativeArray<int> GenerateVoxelValues(Vector2Int offset)
  696.     {
  697.         NativeArray<int> voxelValues = new NativeArray<int>(chunkDimensions.x * chunkDimensions.y * chunkDimensions.x, Allocator.Persistent);
  698.  
  699.         NoiseJob job = new NoiseJob
  700.         {
  701.             voxelValues = voxelValues,
  702.             chunkDimensions = chunkDimensions,
  703.             offset = new int2(offset.x, offset.y),
  704.             curve = curve,
  705.             height = height,
  706.             sandDensity = sandDensity,
  707.             maxTreeHeight = maxTreeHeight,
  708.             surfaceLevel = surfaceLevel,
  709.             seaLevel = seaLevel,
  710.             numOfOctaves = numOfOctaves,
  711.             seed = seed,
  712.             lacunarity = lacunarity,
  713.             persistance = persistance,
  714.             frequency = frequency,
  715.             noise3DContribution = noise3DContribution
  716.         };
  717.  
  718.         job.Schedule(chunkDimensions.x * chunkDimensions.x, 16).Complete();
  719.  
  720.         return voxelValues;
  721.     }
  722.  
  723.     [BurstCompile]
  724.     struct NoiseJob : IJobParallelFor
  725.     {
  726.         [NativeDisableParallelForRestriction]
  727.         public NativeArray<int> voxelValues;
  728.  
  729.         public int2 chunkDimensions;
  730.         public int2 offset;
  731.  
  732.         [ReadOnly]
  733.         public NativeArray<float> curve;
  734.  
  735.         public float height;
  736.         public float sandDensity;
  737.         public float maxTreeHeight;
  738.         public float noise3DContribution;
  739.         public int surfaceLevel;
  740.         public int seaLevel;
  741.  
  742.         public int numOfOctaves;
  743.         public float seed;
  744.         public float lacunarity;
  745.         public float persistance;
  746.         public float frequency;
  747.  
  748.         public void Execute(int i)
  749.         {
  750.             int x = i % chunkDimensions.x;
  751.             int z = i / chunkDimensions.x;
  752.  
  753.             float noiseValue = GetNoise(x + offset.x, z + offset.y);
  754.             float noise01 = Remap(noiseValue, -1, 1, 0, 1);
  755.             int curveIndex = (int)math.clamp(noise01 * curve.Length, 0, curve.Length - 1);
  756.  
  757.             noiseValue *= height * curve[curveIndex];
  758.             noiseValue += surfaceLevel;
  759.  
  760.             for (int y = 0; y < chunkDimensions.y; y++)
  761.             {
  762.                 float noise3D = Get3DNoise(x + offset.x, y, z + offset.y);
  763.  
  764.                 //Water
  765.                 if (y == seaLevel)
  766.                 {
  767.                     voxelValues[CalculateVoxelIndex(x, y, z)] = 5;
  768.                 }
  769.  
  770.                 if (y < noiseValue + (noise3D * height * noise3DContribution))
  771.                 {
  772.                     voxelValues[CalculateVoxelIndex(x, y, z)] = 1;
  773.  
  774.                     bool isSand = (y + 1 < seaLevel && noise.snoise(new float2(x * .1f, z * .1f)) < sandDensity) || y < seaLevel - 3;
  775.  
  776.                     //Add grass / sand
  777.                     if (y + 3 < chunkDimensions.y)
  778.                     {
  779.                         voxelValues[CalculateVoxelIndex(x, y + 1, z)] = isSand ? 4 : 2;
  780.                         voxelValues[CalculateVoxelIndex(x, y + 2, z)] = isSand ? 4 : 2;
  781.  
  782.                         voxelValues[CalculateVoxelIndex(x, y + 3, z)] = isSand ? 4 : 3;
  783.                     }
  784.  
  785.                     float treeNoise = noise.snoise(new float2((x + offset.x) * .01f, (z + offset.y) * .01f));
  786.                     float random = noise.snoise(new float2((x + offset.x) * 10000, (z + offset.y) * 10000));
  787.  
  788.                     if (treeNoise < 0 && random < -0.7f && y < maxTreeHeight)
  789.                     {
  790.                         voxelValues[CalculateVoxelIndex(x, y + 4, z)] = 5;
  791.                     }
  792.                 }
  793.  
  794.  
  795.             }
  796.         }
  797.  
  798.         public float Remap(float value, float fromLow, float fromHigh, float toLow, float toHigh)
  799.         {
  800.             // Calculate the normalized position of the value in the original range
  801.             float normalized = (value - fromLow) / (fromHigh - fromLow);
  802.  
  803.             // Scale and shift the normalized value to the new range
  804.             float remapped = toLow + normalized * (toHigh - toLow);
  805.  
  806.             return remapped;
  807.         }
  808.  
  809.         public int CalculateVoxelIndex(int x, int y, int z)
  810.         {
  811.             return x * (chunkDimensions.y * chunkDimensions.x) + y * chunkDimensions.x + z;
  812.         }
  813.  
  814.         float Get3DNoise(int x, int y, int z)
  815.         {
  816.             float sum = 0;
  817.  
  818.             for (int i = 0; i < numOfOctaves; i++)
  819.             {
  820.                 float frequency = math.pow(lacunarity, i) * this.frequency * 10;
  821.                 float amplitude = math.pow(persistance, i);
  822.  
  823.                 sum += noise.snoise(new float3(x * frequency, y * frequency, z * frequency)) * amplitude;
  824.             }
  825.  
  826.             return sum;
  827.         }
  828.  
  829.         float GetNoise(int x, int y)
  830.         {
  831.             float sum = 0;
  832.             for (int i = 0; i < numOfOctaves; i++)
  833.             {
  834.                 float frequency = math.pow(lacunarity, i) * this.frequency;
  835.                 float amplitude = math.pow(persistance, i);
  836.  
  837.                 sum += noise.snoise(new float2((x + seed) * frequency, (y - seed) * frequency)) * amplitude;
  838.             }
  839.  
  840.             return sum;
  841.         }
  842.     }
  843.  
  844. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement