using Unity.Burst; using Unity.Collections; using Unity.Jobs; using Unity.Mathematics; using UnityEngine; using UnityEngine.Rendering; namespace DanieleMarotta.RiversongCodeShowcase { [BurstCompile] public struct GenerateTerrainChunkJob : IJobParallelFor { public int2 WorldSize; [ReadOnly] public NativeArray ChunkCoordsArray; public int ChunkSize; [ReadOnly] public NativeArray Heightmap; [NativeDisableParallelForRestriction] public Mesh.MeshDataArray MeshDataArray; public void Execute(int index) { var coords = ChunkCoordsArray[index]; var vertexAttributes = new NativeArray(3, Allocator.Temp); vertexAttributes[0] = new VertexAttributeDescriptor(VertexAttribute.Position); vertexAttributes[1] = new VertexAttributeDescriptor(VertexAttribute.Normal); vertexAttributes[2] = new VertexAttributeDescriptor(VertexAttribute.TexCoord0, dimension: 2); var tileCount = ChunkSize * ChunkSize; var vertexTemp = new NativeList(20 * tileCount, Allocator.Temp); var indexTemp = new NativeList(30 * tileCount, Allocator.Temp); var tileMin = coords * ChunkSize; var tileMax = math.min(tileMin + ChunkSize, WorldSize); var tile = int2.zero; for (tile.y = tileMin.y; tile.y < tileMax.y; tile.y++) for (tile.x = tileMin.x; tile.x < tileMax.x; tile.x++) { var h = Heightmap[math.mad(tile.y, WorldSize.x, tile.x)]; if (h == 0) continue; var v0 = new Vertex(new float3(tile.x, h, tile.y), math.up(), float2.zero); var v1 = new Vertex(new float3(tile.x, h, tile.y + 1), math.up(), float2.zero); var v2 = new Vertex(new float3(tile.x + 1, h, tile.y + 1), math.up(), float2.zero); var v3 = new Vertex(new float3(tile.x + 1, h, tile.y), math.up(), float2.zero); WriteQuad(vertexTemp, indexTemp, v0, v1, v2, v3, false); } var cliffSubMeshIndexStart = indexTemp.Length; for (tile.y = tileMin.y; tile.y < tileMax.y && tile.y < WorldSize.y - 1; tile.y++) for (tile.x = tileMin.x; tile.x < tileMax.x && tile.x < WorldSize.x - 1; tile.x++) { var h = Heightmap[math.mad(tile.y, WorldSize.x, tile.x)]; var hx = Heightmap[math.mad(tile.y, WorldSize.x, tile.x + 1)]; if (h != hx) { var hMin = math.min(h, hx); var hMax = math.max(h, hx); for (var y = hMin; y < hMax; y++) { var bottomUV = math.unlerp(hMin, hMax, y); var topUV = math.unlerp(hMin, hMax, y + 1); var normal = h > hx ? math.right() : math.left(); var v0 = new Vertex(new float3(tile.x + 1, y, tile.y), normal, bottomUV); var v1 = new Vertex(new float3(tile.x + 1, y + 1, tile.y), normal, topUV); var v2 = new Vertex(new float3(tile.x + 1, y + 1, tile.y + 1), normal, topUV); var v3 = new Vertex(new float3(tile.x + 1, y, tile.y + 1), normal, bottomUV); WriteQuad(vertexTemp, indexTemp, v0, v1, v2, v3, h < hx); } } var hz = Heightmap[math.mad(tile.y + 1, WorldSize.x, tile.x)]; if (h != hz) { var hMin = math.min(h, hz); var hMax = math.max(h, hz); for (var y = hMin; y < hMax; y++) { var bottomUV = math.unlerp(hMin, hMax, y); var topUV = math.unlerp(hMin, hMax, y + 1); var normal = h > hz ? math.forward() : math.back(); var v0 = new Vertex(new float3(tile.x + 1, y, tile.y + 1), normal, bottomUV); var v1 = new Vertex(new float3(tile.x + 1, y + 1, tile.y + 1), normal, topUV); var v2 = new Vertex(new float3(tile.x, y + 1, tile.y + 1), normal, topUV); var v3 = new Vertex(new float3(tile.x, y, tile.y + 1), normal, bottomUV); WriteQuad(vertexTemp, indexTemp, v0, v1, v2, v3, h < hz); } } } var mesh = MeshDataArray[index]; mesh.SetVertexBufferParams(vertexTemp.Length, vertexAttributes); var chunkVertexData = mesh.GetVertexData(); chunkVertexData.CopyFrom(vertexTemp.AsArray()); mesh.SetIndexBufferParams(indexTemp.Length, IndexFormat.UInt32); var chunkIndexData = mesh.GetIndexData(); chunkIndexData.CopyFrom(indexTemp.AsArray()); mesh.subMeshCount = indexTemp.Length > cliffSubMeshIndexStart ? 2 : 1; mesh.SetSubMesh(0, new SubMeshDescriptor(0, cliffSubMeshIndexStart)); if (mesh.subMeshCount > 1) mesh.SetSubMesh(1, new SubMeshDescriptor(cliffSubMeshIndexStart, chunkIndexData.Length - cliffSubMeshIndexStart)); } private void WriteQuad(NativeList vertices, NativeList indices, in Vertex v0, in Vertex v1, in Vertex v2, in Vertex v3, bool flip) { var v = vertices.Length; vertices.AddNoResize(v0); vertices.AddNoResize(v1); vertices.AddNoResize(v2); vertices.AddNoResize(v3); var i0 = v + (flip ? 2 : 0); var i1 = v + 1; var i2 = v + (flip ? 0 : 2); var i3 = v + 3; indices.AddNoResize(i0); indices.AddNoResize(i1); indices.AddNoResize(i2); indices.AddNoResize(i2); indices.AddNoResize(i3); indices.AddNoResize(i0); } private struct Vertex { public float3 Position; public float3 Normal; public float2 TexCoord0; public Vertex(float3 position, float3 normal, float2 texCoord0) { Position = position; Normal = normal; TexCoord0 = texCoord0; } } } }