Files
riversong-code-showcase/Source/Riversong/Game/WorldGen/ChunkGenerators/ChunkMeshGenerator.cs
2026-05-21 16:04:49 +02:00

124 lines
3.8 KiB
C#

using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
namespace DanieleMarotta.RiversongCodeShowcase
{
public abstract class ChunkMeshGenerator
{
private readonly List<ChunkGenerationJobState> _pendingJobs = new();
private JobHandle _dependency;
protected ChunkMeshGenerator(World world, GameConfig config)
{
World = world;
Config = config;
}
protected World World { get; }
protected GameConfig Config { get; }
public void InitializeChunk(TerrainChunk chunk)
{
for (var lod = 0; lod < GetLodCount(); lod++) InitializeChunk(chunk, lod);
}
private void InitializeChunk(TerrainChunk chunk, int lod)
{
var childName = GetGameObjectName(chunk.Root.name, chunk.Coords, lod);
var child = new GameObject(childName);
child.transform.SetParent(chunk.Root.transform);
var mesh = new Mesh { name = childName };
mesh.MarkDynamic();
var meshFilter = child.AddComponent<MeshFilter>();
meshFilter.sharedMesh = mesh;
var renderer = child.AddComponent<MeshRenderer>();
InitializeChunk(chunk, lod, child, mesh, renderer);
}
protected abstract int GetLodCount();
protected abstract string GetGameObjectName(string chunkName, int2 chunkCoords, int lod);
protected abstract void InitializeChunk(TerrainChunk chunk, int lod, GameObject gameObject, Mesh mesh, Renderer renderer);
public void BeginGeneratingChunks(List<TerrainChunk> chunks)
{
var chunkCoords = new NativeArray<int2>(chunks.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
for (var i = 0; i < chunks.Count; i++) chunkCoords[i] = chunks[i].Coords;
for (var lod = 0; lod < GetLodCount(); lod++)
{
var jobState = CreateJobState(lod, chunks);
_pendingJobs.Add(jobState);
var jobHandle = ScheduleJob(jobState, chunkCoords);
_dependency = JobHandle.CombineDependencies(_dependency, jobHandle);
}
chunkCoords.Dispose(_dependency);
}
private ChunkGenerationJobState CreateJobState(int lod, List<TerrainChunk> chunks)
{
var jobState = new ChunkGenerationJobState { Lod = lod };
jobState.Chunks.AddRange(chunks);
foreach (var chunk in chunks)
{
var mesh = GetMesh(chunk, lod);
jobState.Meshes.Add(mesh);
}
jobState.MeshDataArray = Mesh.AllocateWritableMeshData(jobState.Meshes);
return jobState;
}
protected abstract Mesh GetMesh(TerrainChunk chunk, int lod);
protected abstract JobHandle ScheduleJob(ChunkGenerationJobState jobState, NativeArray<int2> chunkCoords);
public async UniTask Wait()
{
while (!_dependency.IsCompleted) await UniTask.NextFrame();
_dependency.Complete();
_dependency = default;
}
public void EndGeneratingChunks()
{
foreach (var jobState in _pendingJobs) OnCompleted(jobState);
_pendingJobs.Clear();
}
private void OnCompleted(ChunkGenerationJobState jobState)
{
Mesh.ApplyAndDisposeWritableMeshData(jobState.MeshDataArray, jobState.Meshes);
foreach (var mesh in jobState.Meshes)
{
mesh.RecalculateBounds();
mesh.UploadMeshData(false);
}
OnCompleted(jobState.Lod, jobState.Chunks);
jobState.Clear();
}
protected abstract void OnCompleted(int lod, List<TerrainChunk> chunks);
}
}