using Unity.Burst; using Unity.Collections; using Unity.Jobs; using Unity.Mathematics; using UnityEngine; using UnityEngine.Rendering; using Random = Unity.Mathematics.Random; namespace DanieleMarotta.RiversongCodeShowcase { [BurstCompile] public struct GenerateGrassChunkJob : IJobParallelFor where T : unmanaged, IGrassTileMask { public int2 WorldSize; [ReadOnly] public NativeArray ChunkCoordsArray; public int ChunkSize; [ReadOnly] public T Mask; [ReadOnly] public NativeArray Heightmap; [NativeDisableParallelForRestriction] public Mesh.MeshDataArray MeshDataArray; public NativeArray RandomArray; public int Density; public float NoiseScale; public float DiscardThreshold; public float BladeWidth; public float2 BladeHeightRange; public void Execute(int index) { var chunkCoords = ChunkCoordsArray[index]; var random = RandomArray[index]; var vertexAttributes = new NativeArray(3, Allocator.Temp); vertexAttributes[0] = new VertexAttributeDescriptor(VertexAttribute.Position); vertexAttributes[1] = new VertexAttributeDescriptor(VertexAttribute.Color); vertexAttributes[2] = new VertexAttributeDescriptor(VertexAttribute.TexCoord0, dimension: 2); var triangleCount = ChunkSize * ChunkSize * Density * Density; var vertexTemp = new NativeList(3 * triangleCount, Allocator.Temp); var indexTemp = new NativeList(3 * triangleCount, Allocator.Temp); var tileMin = chunkCoords * ChunkSize; var tileMax = math.min(tileMin + ChunkSize, WorldSize); var tile = int2.zero; var spacing = 1f / Density; var triangle = 0; for (tile.y = tileMin.y; tile.y < tileMax.y; tile.y++) for (tile.x = tileMin.x; tile.x < tileMax.x; tile.x++) { var tileIndex = math.mad(tile.y, WorldSize.x, tile.x); if (!Mask.CanGenerateAtTile(tileIndex)) continue; var worldY = Heightmap[tileIndex]; for (var stepZ = 0; stepZ < Density; stepZ++) { var worldZ = math.mad(0.5f + stepZ, spacing, tile.y); for (var stepX = 0; stepX < Density; stepX++) { var worldX = math.mad(0.5f + stepX, spacing, tile.x); var bladeOrigin = new float3(worldX, worldY, worldZ); bladeOrigin += 0.25f * spacing * new float3(random.NextFloat(-1, 1), 0, random.NextFloat(-1, 1)); const float edgeDistance = 0.3f; var d = math.frac(bladeOrigin.xz); if ((d.x < edgeDistance && (tile.x == 0 || Heightmap[tileIndex - 1] != worldY)) || (d.x > 1 - edgeDistance && (tile.x == WorldSize.x - 1 || Heightmap[tileIndex + 1] != worldY)) || (d.y < edgeDistance && (tile.y == 0 || Heightmap[tileIndex - WorldSize.x] != worldY)) || (d.y > 1 - edgeDistance && (tile.y == WorldSize.y - 1 || Heightmap[tileIndex + WorldSize.x] != worldY)) || (d is { x: < edgeDistance, y: < edgeDistance } && Heightmap[tileIndex - 1 - WorldSize.x] != worldY) || (d is { x: > 1 - edgeDistance, y: < edgeDistance } && Heightmap[tileIndex + 1 - WorldSize.x] != worldY) || (d is { x: < edgeDistance, y: > 1 - edgeDistance } && Heightmap[tileIndex - 1 + WorldSize.x] != worldY) || (d is { x: > 1 - edgeDistance, y: > 1 - edgeDistance } && Heightmap[tileIndex + 1 + WorldSize.x] != worldY)) continue; var n = noise.snoise(NoiseScale * bladeOrigin); n = math.remap(-1, 1, 0, 1, n); if (n < DiscardThreshold) continue; var rotation = quaternion.Euler(0, random.NextFloat(math.PI2), 0); var halfBladeWidth = 0.5f * BladeWidth; var bladeHeight = math.lerp(BladeHeightRange.x, BladeHeightRange.y, n); var side = math.mul(rotation, new float3(halfBladeWidth, 0, 0)); var v0 = bladeOrigin - side; var v1 = bladeOrigin + bladeHeight * math.up(); var v2 = bladeOrigin + side; vertexTemp.AddNoResize(new Vertex(v0, bladeOrigin, new float2(0, bladeHeight))); vertexTemp.AddNoResize(new Vertex(v1, bladeOrigin, new float2(bladeHeight, bladeHeight))); vertexTemp.AddNoResize(new Vertex(v2, bladeOrigin, new float2(0, bladeHeight))); indexTemp.AddNoResize(triangle); indexTemp.AddNoResize(triangle + 1); indexTemp.AddNoResize(triangle + 2); triangle += 3; } } } 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 = 1; mesh.SetSubMesh(0, new SubMeshDescriptor(0, indexTemp.Length)); } private struct Vertex { public float3 Position; public float3 Color; public float2 TexCoord0; public Vertex(float3 position, float3 color, float2 texCoord0) { Position = position; Color = color; TexCoord0 = texCoord0; } } } }