[Add] Optimization
This commit is contained in:
@@ -42,21 +42,34 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
[Header("Runtime")]
|
[Header("Runtime")]
|
||||||
[SerializeField, Min(1)] private int maxAsyncChunkJobs = 2;
|
[SerializeField, Min(1)] private int maxAsyncChunkJobs = 2;
|
||||||
[SerializeField, Min(1)] private int maxChunkBuildsPerFrame = 1;
|
[SerializeField, Min(1)] private int maxChunkBuildsPerFrame = 1;
|
||||||
|
[SerializeField, Min(1)] private int maxChunkMeshBuildsPerFrame = 1;
|
||||||
|
[SerializeField, Min(1)] private int maxColliderAppliesPerFrame = 1;
|
||||||
|
[SerializeField, Min(0)] private int maxNeighborRefreshesPerFrame = 2;
|
||||||
[SerializeField, Min(1)] private int renderRegionSizeInChunks = 4;
|
[SerializeField, Min(1)] private int renderRegionSizeInChunks = 4;
|
||||||
[SerializeField, Min(1)] private int maxRegionBuildsPerFrame = 1;
|
[SerializeField, Min(1)] private int maxRegionBuildsPerFrame = 1;
|
||||||
|
|
||||||
private readonly Dictionary<Vector2Int, ChunkRuntime> chunks = new Dictionary<Vector2Int, ChunkRuntime>();
|
private readonly Dictionary<Vector2Int, ChunkRuntime> chunks = new Dictionary<Vector2Int, ChunkRuntime>();
|
||||||
private readonly Dictionary<Vector2Int, RegionRuntime> regions = new Dictionary<Vector2Int, RegionRuntime>();
|
private readonly Dictionary<Vector2Int, RegionRuntime> regions = new Dictionary<Vector2Int, RegionRuntime>();
|
||||||
|
private readonly Dictionary<Vector2Int, int> regionVersions = new Dictionary<Vector2Int, int>();
|
||||||
private readonly Queue<ChunkBuildResult> completedBuilds = new Queue<ChunkBuildResult>();
|
private readonly Queue<ChunkBuildResult> completedBuilds = new Queue<ChunkBuildResult>();
|
||||||
|
private readonly Queue<RegionBuildResult> completedRegionBuilds = new Queue<RegionBuildResult>();
|
||||||
|
private readonly Queue<PendingColliderMeshApply> pendingColliderApplies = new Queue<PendingColliderMeshApply>();
|
||||||
|
private readonly Queue<Vector2Int> dirtyChunkMeshes = new Queue<Vector2Int>();
|
||||||
|
private readonly HashSet<Vector2Int> queuedChunkMeshes = new HashSet<Vector2Int>();
|
||||||
private readonly Queue<Vector2Int> dirtyRegions = new Queue<Vector2Int>();
|
private readonly Queue<Vector2Int> dirtyRegions = new Queue<Vector2Int>();
|
||||||
private readonly HashSet<Vector2Int> queuedRegions = new HashSet<Vector2Int>();
|
private readonly HashSet<Vector2Int> queuedRegions = new HashSet<Vector2Int>();
|
||||||
|
private readonly Queue<Vector2Int> pendingNeighborRefreshes = new Queue<Vector2Int>();
|
||||||
|
private readonly HashSet<Vector2Int> queuedNeighborRefreshes = new HashSet<Vector2Int>();
|
||||||
private readonly object generationLock = new object();
|
private readonly object generationLock = new object();
|
||||||
|
private readonly object regionBuildLock = new object();
|
||||||
|
|
||||||
private Transform chunkRoot;
|
private Transform chunkRoot;
|
||||||
private Transform regionRoot;
|
private Transform regionRoot;
|
||||||
private Vector2Int lastGeneratedCenter = new Vector2Int(int.MinValue, int.MinValue);
|
private Vector2Int lastGeneratedCenter = new Vector2Int(int.MinValue, int.MinValue);
|
||||||
private int activeGenerationJobs;
|
private int activeGenerationJobs;
|
||||||
|
private int nextChunkRuntimeId;
|
||||||
private int generationSession;
|
private int generationSession;
|
||||||
|
private int regionRebuildSession;
|
||||||
private VoxelWorldAtlas atlas;
|
private VoxelWorldAtlas atlas;
|
||||||
private int atlasBiomeCount;
|
private int atlasBiomeCount;
|
||||||
private bool regionRebuildLoopRunning;
|
private bool regionRebuildLoopRunning;
|
||||||
@@ -64,6 +77,7 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
generationSession++;
|
generationSession++;
|
||||||
|
regionRebuildSession++;
|
||||||
EnsureRuntimeData();
|
EnsureRuntimeData();
|
||||||
EnsureChunkRoot();
|
EnsureChunkRoot();
|
||||||
EnsureRegionRoot();
|
EnsureRegionRoot();
|
||||||
@@ -88,6 +102,10 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
}
|
}
|
||||||
|
|
||||||
DrainCompletedBuilds(maxChunkBuildsPerFrame);
|
DrainCompletedBuilds(maxChunkBuildsPerFrame);
|
||||||
|
DrainDirtyChunkMeshes(maxChunkMeshBuildsPerFrame);
|
||||||
|
DrainPendingColliderApplies(maxColliderAppliesPerFrame);
|
||||||
|
DrainNeighborRefreshes(maxNeighborRefreshesPerFrame);
|
||||||
|
DrainCompletedRegionBuilds(maxRegionBuildsPerFrame);
|
||||||
ScheduleChunkGeneration(centerChunk);
|
ScheduleChunkGeneration(centerChunk);
|
||||||
EnsureBlockingChunksLoaded(centerChunk);
|
EnsureBlockingChunksLoaded(centerChunk);
|
||||||
}
|
}
|
||||||
@@ -95,9 +113,15 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
private void OnDisable()
|
private void OnDisable()
|
||||||
{
|
{
|
||||||
generationSession++;
|
generationSession++;
|
||||||
|
regionRebuildSession++;
|
||||||
lock (generationLock)
|
lock (generationLock)
|
||||||
{
|
{
|
||||||
completedBuilds.Clear();
|
completedBuilds.Clear();
|
||||||
|
activeGenerationJobs = 0;
|
||||||
|
}
|
||||||
|
lock (regionBuildLock)
|
||||||
|
{
|
||||||
|
completedRegionBuilds.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
CleanupChunks();
|
CleanupChunks();
|
||||||
@@ -110,6 +134,9 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
{
|
{
|
||||||
maxMountainHeight = Mathf.Max(1, maxMountainHeight);
|
maxMountainHeight = Mathf.Max(1, maxMountainHeight);
|
||||||
chunkSize = Mathf.Max(8, chunkSize);
|
chunkSize = Mathf.Max(8, chunkSize);
|
||||||
|
maxChunkMeshBuildsPerFrame = Mathf.Max(1, maxChunkMeshBuildsPerFrame);
|
||||||
|
maxColliderAppliesPerFrame = Mathf.Max(1, maxColliderAppliesPerFrame);
|
||||||
|
maxNeighborRefreshesPerFrame = Mathf.Max(0, maxNeighborRefreshesPerFrame);
|
||||||
renderRegionSizeInChunks = Mathf.Max(1, renderRegionSizeInChunks);
|
renderRegionSizeInChunks = Mathf.Max(1, renderRegionSizeInChunks);
|
||||||
maxRegionBuildsPerFrame = Mathf.Max(1, maxRegionBuildsPerFrame);
|
maxRegionBuildsPerFrame = Mathf.Max(1, maxRegionBuildsPerFrame);
|
||||||
int configuredBiomeCount = CountConfiguredBiomes();
|
int configuredBiomeCount = CountConfiguredBiomes();
|
||||||
@@ -220,7 +247,7 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
|
|
||||||
runtime.State = ChunkState.Generating;
|
runtime.State = ChunkState.Generating;
|
||||||
runtime.Version++;
|
runtime.Version++;
|
||||||
GenerateChunkDataAsync(coord, runtime.Version, generationSession).Forget();
|
GenerateChunkDataAsync(coord, runtime.Version, generationSession, runtime.RuntimeId).Forget();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,21 +258,18 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
{
|
{
|
||||||
Vector2Int coord = requiredCoords[i];
|
Vector2Int coord = requiredCoords[i];
|
||||||
ChunkRuntime runtime = GetOrCreateChunkRuntime(coord);
|
ChunkRuntime runtime = GetOrCreateChunkRuntime(coord);
|
||||||
if (runtime.IsRendered)
|
if (runtime.IsRendered || runtime.HasData || runtime.State == ChunkState.Generating || runtime.State == ChunkState.SyncBuilding)
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (runtime.State == ChunkState.Generating)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.Version++;
|
runtime.Version++;
|
||||||
runtime.State = ChunkState.SyncBuilding;
|
runtime.State = ChunkState.SyncBuilding;
|
||||||
ApplyBuildResult(GenerateChunkData(coord, runtime.Version, generationSession));
|
if (ApplyBuildResult(GenerateChunkData(coord, runtime.Version, generationSession, runtime.RuntimeId)))
|
||||||
RenderChunk(coord);
|
{
|
||||||
RefreshNeighborBorders(coord);
|
EnqueueChunkMeshBuild(coord);
|
||||||
|
QueueNeighborRefresh(coord);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,11 +297,11 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
MarkRegionDirty(coord);
|
MarkRegionDirty(coord);
|
||||||
chunks.Remove(coord);
|
chunks.Remove(coord);
|
||||||
runtime.Dispose();
|
runtime.Dispose();
|
||||||
RefreshNeighborBorders(coord);
|
QueueNeighborRefresh(coord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChunkBuildResult GenerateChunkData(Vector2Int coord, int version, int session)
|
private ChunkBuildResult GenerateChunkData(Vector2Int coord, int version, int session, int runtimeId)
|
||||||
{
|
{
|
||||||
int margin = Mathf.Max(2, smoothingPasses + 1);
|
int margin = Mathf.Max(2, smoothingPasses + 1);
|
||||||
int sampleSize = chunkSize + margin * 2;
|
int sampleSize = chunkSize + margin * 2;
|
||||||
@@ -317,7 +341,7 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ChunkBuildResult(coord, heights, biomeIndices, version, session);
|
return new ChunkBuildResult(coord, heights, biomeIndices, version, session, runtimeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool[,] SmoothSampledMask(bool[,] source)
|
private bool[,] SmoothSampledMask(bool[,] source)
|
||||||
@@ -439,7 +463,8 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
|
|
||||||
runtime.EnsureCreated(coord, chunkRoot, chunkSize);
|
runtime.EnsureCreated(coord, chunkRoot, chunkSize);
|
||||||
ChunkMeshBuild meshBuild = BuildChunkMesh(coord, runtime.Heights, runtime.BiomeIndices);
|
ChunkMeshBuild meshBuild = BuildChunkMesh(coord, runtime.Heights, runtime.BiomeIndices);
|
||||||
runtime.ApplyMeshes(meshBuild, chunkSize);
|
runtime.ApplyRenderData(meshBuild.RenderSnapshot);
|
||||||
|
EnqueueColliderApply(coord, runtime.Version, runtime.RuntimeId, meshBuild.ColliderMesh);
|
||||||
runtime.State = ChunkState.Rendered;
|
runtime.State = ChunkState.Rendered;
|
||||||
MarkRegionDirty(coord);
|
MarkRegionDirty(coord);
|
||||||
}
|
}
|
||||||
@@ -453,7 +478,8 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
BuildMountainTops(coord, heights, biomeIndices, renderBuffers, colliderBuffers);
|
BuildMountainTops(coord, heights, biomeIndices, renderBuffers, colliderBuffers);
|
||||||
BuildMountainSides(coord, heights, biomeIndices, renderBuffers, colliderBuffers);
|
BuildMountainSides(coord, heights, biomeIndices, renderBuffers, colliderBuffers);
|
||||||
|
|
||||||
return new ChunkMeshBuild(renderBuffers.ToMesh($"Render_{coord.x}_{coord.y}"), colliderBuffers.ToMesh($"Collider_{coord.x}_{coord.y}"));
|
ChunkRenderSnapshot renderSnapshot = renderBuffers.ToSnapshot();
|
||||||
|
return new ChunkMeshBuild(colliderBuffers.ToMesh($"Collider_{coord.x}_{coord.y}"), renderSnapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildGroundSurface(int[] heights, byte[] biomeIndices, MeshBuffers renderBuffers)
|
private void BuildGroundSurface(int[] heights, byte[] biomeIndices, MeshBuffers renderBuffers)
|
||||||
@@ -884,22 +910,33 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
return new Vector2Int(coord.x * chunkSize + localX, coord.y * chunkSize + localZ);
|
return new Vector2Int(coord.x * chunkSize + localX, coord.y * chunkSize + localZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async UniTaskVoid GenerateChunkDataAsync(Vector2Int coord, int version, int session)
|
private async UniTaskVoid GenerateChunkDataAsync(Vector2Int coord, int version, int session, int runtimeId)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ChunkBuildResult result = await UniTask.RunOnThreadPool(() => GenerateChunkData(coord, version, session));
|
ChunkBuildResult result = await UniTask.RunOnThreadPool(() => GenerateChunkData(coord, version, session, runtimeId));
|
||||||
lock (generationLock)
|
lock (generationLock)
|
||||||
{
|
{
|
||||||
completedBuilds.Enqueue(result);
|
if (session == generationSession)
|
||||||
activeGenerationJobs = Mathf.Max(0, activeGenerationJobs - 1);
|
{
|
||||||
|
completedBuilds.Enqueue(result);
|
||||||
|
activeGenerationJobs = Mathf.Max(0, activeGenerationJobs - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
lock (generationLock)
|
lock (generationLock)
|
||||||
{
|
{
|
||||||
activeGenerationJobs = Mathf.Max(0, activeGenerationJobs - 1);
|
if (session == generationSession)
|
||||||
|
{
|
||||||
|
activeGenerationJobs = Mathf.Max(0, activeGenerationJobs - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session == generationSession && chunks.TryGetValue(coord, out ChunkRuntime runtime) && runtime.Version == version && runtime.RuntimeId == runtimeId)
|
||||||
|
{
|
||||||
|
runtime.State = ChunkState.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.LogException(exception, this);
|
Debug.LogException(exception, this);
|
||||||
@@ -927,12 +964,100 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderChunk(result.Coord);
|
EnqueueChunkMeshBuild(result.Coord);
|
||||||
RefreshNeighborBorders(result.Coord);
|
QueueNeighborRefresh(result.Coord);
|
||||||
builds++;
|
builds++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrainDirtyChunkMeshes(int maxBuilds)
|
||||||
|
{
|
||||||
|
int builds = 0;
|
||||||
|
while (builds < maxBuilds)
|
||||||
|
{
|
||||||
|
if (dirtyChunkMeshes.Count == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2Int coord = dirtyChunkMeshes.Dequeue();
|
||||||
|
queuedChunkMeshes.Remove(coord);
|
||||||
|
|
||||||
|
if (!chunks.TryGetValue(coord, out ChunkRuntime runtime) || !runtime.HasData)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderChunk(coord);
|
||||||
|
builds++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnqueueChunkMeshBuild(Vector2Int coord)
|
||||||
|
{
|
||||||
|
if (!queuedChunkMeshes.Add(coord))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dirtyChunkMeshes.Enqueue(coord);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnqueueColliderApply(Vector2Int coord, int version, int runtimeId, Mesh colliderMesh)
|
||||||
|
{
|
||||||
|
pendingColliderApplies.Enqueue(new PendingColliderMeshApply(coord, version, runtimeId, colliderMesh));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrainPendingColliderApplies(int maxApplies)
|
||||||
|
{
|
||||||
|
int applies = 0;
|
||||||
|
while (applies < maxApplies)
|
||||||
|
{
|
||||||
|
if (pendingColliderApplies.Count == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingColliderMeshApply pending = pendingColliderApplies.Dequeue();
|
||||||
|
if (!chunks.TryGetValue(pending.Coord, out ChunkRuntime runtime) || runtime.Version != pending.Version || runtime.RuntimeId != pending.RuntimeId)
|
||||||
|
{
|
||||||
|
DestroyMeshAsset(pending.ColliderMesh);
|
||||||
|
applies++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.ApplyColliderMesh(pending.ColliderMesh);
|
||||||
|
applies++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void QueueNeighborRefresh(Vector2Int coord)
|
||||||
|
{
|
||||||
|
if (!queuedNeighborRefreshes.Add(coord))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingNeighborRefreshes.Enqueue(coord);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrainNeighborRefreshes(int maxRefreshes)
|
||||||
|
{
|
||||||
|
int refreshes = 0;
|
||||||
|
while (refreshes < maxRefreshes)
|
||||||
|
{
|
||||||
|
if (pendingNeighborRefreshes.Count == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2Int coord = pendingNeighborRefreshes.Dequeue();
|
||||||
|
queuedNeighborRefreshes.Remove(coord);
|
||||||
|
RefreshNeighborBorders(coord);
|
||||||
|
refreshes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void RefreshNeighborBorders(Vector2Int coord)
|
private void RefreshNeighborBorders(Vector2Int coord)
|
||||||
{
|
{
|
||||||
TryRenderNeighbor(coord + Vector2Int.up);
|
TryRenderNeighbor(coord + Vector2Int.up);
|
||||||
@@ -948,12 +1073,15 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderChunk(coord);
|
EnqueueChunkMeshBuild(coord);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MarkRegionDirty(Vector2Int chunkCoord)
|
private void MarkRegionDirty(Vector2Int chunkCoord)
|
||||||
{
|
{
|
||||||
Vector2Int regionCoord = ChunkToRegion(chunkCoord);
|
Vector2Int regionCoord = ChunkToRegion(chunkCoord);
|
||||||
|
regionVersions.TryGetValue(regionCoord, out int version);
|
||||||
|
regionVersions[regionCoord] = version + 1;
|
||||||
|
|
||||||
if (!queuedRegions.Add(regionCoord))
|
if (!queuedRegions.Add(regionCoord))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -971,16 +1099,16 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
}
|
}
|
||||||
|
|
||||||
regionRebuildLoopRunning = true;
|
regionRebuildLoopRunning = true;
|
||||||
ProcessDirtyRegionsAsync().Forget();
|
ProcessDirtyRegionsAsync(regionRebuildSession).Forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async UniTaskVoid ProcessDirtyRegionsAsync()
|
private async UniTaskVoid ProcessDirtyRegionsAsync(int session)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (dirtyRegions.Count > 0)
|
while (dirtyRegions.Count > 0)
|
||||||
{
|
{
|
||||||
if (!this || !isActiveAndEnabled)
|
if (!this || !isActiveAndEnabled || session != regionRebuildSession)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -990,7 +1118,19 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
{
|
{
|
||||||
Vector2Int regionCoord = dirtyRegions.Dequeue();
|
Vector2Int regionCoord = dirtyRegions.Dequeue();
|
||||||
queuedRegions.Remove(regionCoord);
|
queuedRegions.Remove(regionCoord);
|
||||||
RebuildRegion(regionCoord);
|
|
||||||
|
if (!TryCaptureRegionBuildRequest(regionCoord, out RegionBuildRequest request))
|
||||||
|
{
|
||||||
|
buildsThisFrame++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegionBuildResult result = await UniTask.RunOnThreadPool(() => BuildRegionBuffers(request));
|
||||||
|
lock (regionBuildLock)
|
||||||
|
{
|
||||||
|
completedRegionBuilds.Enqueue(result);
|
||||||
|
}
|
||||||
|
|
||||||
buildsThisFrame++;
|
buildsThisFrame++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1002,61 +1142,45 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
regionRebuildLoopRunning = false;
|
if (session == regionRebuildSession)
|
||||||
if (dirtyRegions.Count > 0)
|
|
||||||
{
|
{
|
||||||
EnsureRegionRebuildLoop();
|
regionRebuildLoopRunning = false;
|
||||||
|
if (dirtyRegions.Count > 0)
|
||||||
|
{
|
||||||
|
EnsureRegionRebuildLoop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RebuildRegion(Vector2Int regionCoord)
|
private bool TryCaptureRegionBuildRequest(Vector2Int regionCoord, out RegionBuildRequest request)
|
||||||
{
|
{
|
||||||
if (atlas == null || regionRoot == null)
|
if (atlas == null || regionRoot == null)
|
||||||
{
|
{
|
||||||
return;
|
request = new RegionBuildRequest();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2Int baseChunk = new Vector2Int(regionCoord.x * renderRegionSizeInChunks, regionCoord.y * renderRegionSizeInChunks);
|
Vector2Int baseChunk = new Vector2Int(regionCoord.x * renderRegionSizeInChunks, regionCoord.y * renderRegionSizeInChunks);
|
||||||
List<CombineInstance> combineInstances = new List<CombineInstance>(renderRegionSizeInChunks * renderRegionSizeInChunks);
|
List<RegionChunkSnapshot> chunkSnapshots = new List<RegionChunkSnapshot>(renderRegionSizeInChunks * renderRegionSizeInChunks);
|
||||||
|
|
||||||
for (int z = 0; z < renderRegionSizeInChunks; z++)
|
for (int z = 0; z < renderRegionSizeInChunks; z++)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < renderRegionSizeInChunks; x++)
|
for (int x = 0; x < renderRegionSizeInChunks; x++)
|
||||||
{
|
{
|
||||||
Vector2Int chunkCoord = new Vector2Int(baseChunk.x + x, baseChunk.y + z);
|
Vector2Int chunkCoord = new Vector2Int(baseChunk.x + x, baseChunk.y + z);
|
||||||
if (!chunks.TryGetValue(chunkCoord, out ChunkRuntime runtime) || runtime.RenderMesh == null || runtime.RenderMesh.vertexCount == 0)
|
if (!chunks.TryGetValue(chunkCoord, out ChunkRuntime runtime) || runtime.RenderSnapshot == null || runtime.RenderSnapshot.IsEmpty)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
combineInstances.Add(new CombineInstance
|
chunkSnapshots.Add(new RegionChunkSnapshot(runtime.RenderSnapshot, new Vector3(x * chunkSize, 0f, z * chunkSize)));
|
||||||
{
|
|
||||||
mesh = runtime.RenderMesh,
|
|
||||||
transform = Matrix4x4.Translate(new Vector3(x * chunkSize, 0f, z * chunkSize))
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (combineInstances.Count == 0)
|
regionVersions.TryGetValue(regionCoord, out int version);
|
||||||
{
|
request = new RegionBuildRequest(regionCoord, version, generationSession, chunkSnapshots.ToArray());
|
||||||
if (regions.TryGetValue(regionCoord, out RegionRuntime regionToRemove))
|
return true;
|
||||||
{
|
|
||||||
regionToRemove.Dispose();
|
|
||||||
regions.Remove(regionCoord);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RegionRuntime region = GetOrCreateRegionRuntime(regionCoord);
|
|
||||||
region.EnsureCreated(regionCoord, regionRoot, chunkSize, renderRegionSizeInChunks, atlas.Material);
|
|
||||||
|
|
||||||
Mesh combinedMesh = new Mesh { name = $"Region_{regionCoord.x}_{regionCoord.y}" };
|
|
||||||
combinedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
|
||||||
combinedMesh.CombineMeshes(combineInstances.ToArray(), true, true, false);
|
|
||||||
combinedMesh.RecalculateBounds();
|
|
||||||
region.ApplyMesh(combinedMesh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector2Int ChunkToRegion(Vector2Int chunkCoord)
|
private Vector2Int ChunkToRegion(Vector2Int chunkCoord)
|
||||||
@@ -1077,6 +1201,131 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static RegionBuildResult BuildRegionBuffers(RegionBuildRequest request)
|
||||||
|
{
|
||||||
|
if (request.Chunks == null || request.Chunks.Length == 0)
|
||||||
|
{
|
||||||
|
return RegionBuildResult.CreateEmpty(request.RegionCoord, request.Version, request.Session);
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalVertexCount = 0;
|
||||||
|
int totalTriangleCount = 0;
|
||||||
|
for (int i = 0; i < request.Chunks.Length; i++)
|
||||||
|
{
|
||||||
|
ChunkRenderSnapshot snapshot = request.Chunks[i].Snapshot;
|
||||||
|
totalVertexCount += snapshot.Vertices.Length;
|
||||||
|
totalTriangleCount += snapshot.Triangles.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalVertexCount == 0 || totalTriangleCount == 0)
|
||||||
|
{
|
||||||
|
return RegionBuildResult.CreateEmpty(request.RegionCoord, request.Version, request.Session);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3[] vertices = new Vector3[totalVertexCount];
|
||||||
|
Vector3[] normals = new Vector3[totalVertexCount];
|
||||||
|
Vector2[] uv0 = new Vector2[totalVertexCount];
|
||||||
|
Vector2[] uv1 = new Vector2[totalVertexCount];
|
||||||
|
int[] triangles = new int[totalTriangleCount];
|
||||||
|
|
||||||
|
int vertexOffset = 0;
|
||||||
|
int triangleOffset = 0;
|
||||||
|
Vector3 min = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
|
||||||
|
Vector3 max = new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
|
||||||
|
|
||||||
|
for (int chunkIndex = 0; chunkIndex < request.Chunks.Length; chunkIndex++)
|
||||||
|
{
|
||||||
|
RegionChunkSnapshot chunk = request.Chunks[chunkIndex];
|
||||||
|
ChunkRenderSnapshot snapshot = chunk.Snapshot;
|
||||||
|
Vector3 offset = chunk.LocalOffset;
|
||||||
|
|
||||||
|
for (int vertexIndex = 0; vertexIndex < snapshot.Vertices.Length; vertexIndex++)
|
||||||
|
{
|
||||||
|
Vector3 vertex = snapshot.Vertices[vertexIndex] + offset;
|
||||||
|
int writeIndex = vertexOffset + vertexIndex;
|
||||||
|
vertices[writeIndex] = vertex;
|
||||||
|
normals[writeIndex] = snapshot.Normals[vertexIndex];
|
||||||
|
uv0[writeIndex] = snapshot.Uv0[vertexIndex];
|
||||||
|
uv1[writeIndex] = snapshot.Uv1[vertexIndex];
|
||||||
|
|
||||||
|
min = Vector3.Min(min, vertex);
|
||||||
|
max = Vector3.Max(max, vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int triangleIndex = 0; triangleIndex < snapshot.Triangles.Length; triangleIndex++)
|
||||||
|
{
|
||||||
|
triangles[triangleOffset + triangleIndex] = snapshot.Triangles[triangleIndex] + vertexOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertexOffset += snapshot.Vertices.Length;
|
||||||
|
triangleOffset += snapshot.Triangles.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bounds bounds = new Bounds((min + max) * 0.5f, max - min);
|
||||||
|
return new RegionBuildResult(request.RegionCoord, request.Version, request.Session, vertices, normals, uv0, uv1, triangles, bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrainCompletedRegionBuilds(int maxBuilds)
|
||||||
|
{
|
||||||
|
int builds = 0;
|
||||||
|
while (builds < maxBuilds)
|
||||||
|
{
|
||||||
|
RegionBuildResult result;
|
||||||
|
lock (regionBuildLock)
|
||||||
|
{
|
||||||
|
if (completedRegionBuilds.Count == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = completedRegionBuilds.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyRegionBuildResult(result);
|
||||||
|
builds++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyRegionBuildResult(RegionBuildResult result)
|
||||||
|
{
|
||||||
|
if (result.Session != generationSession || atlas == null || regionRoot == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
regionVersions.TryGetValue(result.RegionCoord, out int currentVersion);
|
||||||
|
if (result.Version != currentVersion)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.IsEmpty)
|
||||||
|
{
|
||||||
|
if (regions.TryGetValue(result.RegionCoord, out RegionRuntime regionToRemove))
|
||||||
|
{
|
||||||
|
regionToRemove.Dispose();
|
||||||
|
regions.Remove(result.RegionCoord);
|
||||||
|
}
|
||||||
|
|
||||||
|
regionVersions.Remove(result.RegionCoord);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegionRuntime region = GetOrCreateRegionRuntime(result.RegionCoord);
|
||||||
|
region.EnsureCreated(result.RegionCoord, regionRoot, chunkSize, renderRegionSizeInChunks, atlas.Material);
|
||||||
|
|
||||||
|
Mesh mesh = new Mesh { name = $"Region_{result.RegionCoord.x}_{result.RegionCoord.y}" };
|
||||||
|
mesh.indexFormat = result.Vertices.Length > 65535 ? UnityEngine.Rendering.IndexFormat.UInt32 : UnityEngine.Rendering.IndexFormat.UInt16;
|
||||||
|
mesh.vertices = result.Vertices;
|
||||||
|
mesh.normals = result.Normals;
|
||||||
|
mesh.uv = result.Uv0;
|
||||||
|
mesh.uv2 = result.Uv1;
|
||||||
|
mesh.triangles = result.Triangles;
|
||||||
|
mesh.bounds = result.Bounds;
|
||||||
|
region.ApplyMesh(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
private bool ApplyBuildResult(ChunkBuildResult result)
|
private bool ApplyBuildResult(ChunkBuildResult result)
|
||||||
{
|
{
|
||||||
if (result.Session != generationSession)
|
if (result.Session != generationSession)
|
||||||
@@ -1094,6 +1343,11 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (runtime.RuntimeId != result.RuntimeId)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
runtime.Heights = result.Heights;
|
runtime.Heights = result.Heights;
|
||||||
runtime.BiomeIndices = result.BiomeIndices;
|
runtime.BiomeIndices = result.BiomeIndices;
|
||||||
if (!runtime.IsRendered)
|
if (!runtime.IsRendered)
|
||||||
@@ -1165,6 +1419,7 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
if (!chunks.TryGetValue(coord, out ChunkRuntime runtime))
|
if (!chunks.TryGetValue(coord, out ChunkRuntime runtime))
|
||||||
{
|
{
|
||||||
runtime = new ChunkRuntime();
|
runtime = new ChunkRuntime();
|
||||||
|
runtime.RuntimeId = ++nextChunkRuntimeId;
|
||||||
chunks.Add(coord, runtime);
|
chunks.Add(coord, runtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1186,6 +1441,31 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
}
|
}
|
||||||
|
|
||||||
chunks.Clear();
|
chunks.Clear();
|
||||||
|
dirtyChunkMeshes.Clear();
|
||||||
|
queuedChunkMeshes.Clear();
|
||||||
|
pendingNeighborRefreshes.Clear();
|
||||||
|
queuedNeighborRefreshes.Clear();
|
||||||
|
while (pendingColliderApplies.Count > 0)
|
||||||
|
{
|
||||||
|
DestroyMeshAsset(pendingColliderApplies.Dequeue().ColliderMesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DestroyMeshAsset(Mesh mesh)
|
||||||
|
{
|
||||||
|
if (mesh == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Application.isPlaying)
|
||||||
|
{
|
||||||
|
UnityEngine.Object.Destroy(mesh);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UnityEngine.Object.DestroyImmediate(mesh);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CleanupRegions()
|
private void CleanupRegions()
|
||||||
@@ -1196,8 +1476,13 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
}
|
}
|
||||||
|
|
||||||
regions.Clear();
|
regions.Clear();
|
||||||
|
regionVersions.Clear();
|
||||||
dirtyRegions.Clear();
|
dirtyRegions.Clear();
|
||||||
queuedRegions.Clear();
|
queuedRegions.Clear();
|
||||||
|
lock (regionBuildLock)
|
||||||
|
{
|
||||||
|
completedRegionBuilds.Clear();
|
||||||
|
}
|
||||||
regionRebuildLoopRunning = false;
|
regionRebuildLoopRunning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1215,15 +1500,16 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
return Mathf.Max(1, count);
|
return Mathf.Max(1, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly struct ChunkBuildResult
|
private struct ChunkBuildResult
|
||||||
{
|
{
|
||||||
public ChunkBuildResult(Vector2Int coord, int[] heights, byte[] biomeIndices, int version, int session)
|
public ChunkBuildResult(Vector2Int coord, int[] heights, byte[] biomeIndices, int version, int session, int runtimeId)
|
||||||
{
|
{
|
||||||
Coord = coord;
|
Coord = coord;
|
||||||
Heights = heights;
|
Heights = heights;
|
||||||
BiomeIndices = biomeIndices;
|
BiomeIndices = biomeIndices;
|
||||||
Version = version;
|
Version = version;
|
||||||
Session = session;
|
Session = session;
|
||||||
|
RuntimeId = runtimeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2Int Coord { get; }
|
public Vector2Int Coord { get; }
|
||||||
@@ -1231,20 +1517,116 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
public byte[] BiomeIndices { get; }
|
public byte[] BiomeIndices { get; }
|
||||||
public int Version { get; }
|
public int Version { get; }
|
||||||
public int Session { get; }
|
public int Session { get; }
|
||||||
|
public int RuntimeId { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly struct ChunkMeshBuild
|
private struct ChunkMeshBuild
|
||||||
{
|
{
|
||||||
public ChunkMeshBuild(Mesh renderMesh, Mesh colliderMesh)
|
public ChunkMeshBuild(Mesh colliderMesh, ChunkRenderSnapshot renderSnapshot)
|
||||||
{
|
{
|
||||||
RenderMesh = renderMesh;
|
ColliderMesh = colliderMesh;
|
||||||
|
RenderSnapshot = renderSnapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mesh ColliderMesh { get; }
|
||||||
|
public ChunkRenderSnapshot RenderSnapshot { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct PendingColliderMeshApply
|
||||||
|
{
|
||||||
|
public PendingColliderMeshApply(Vector2Int coord, int version, int runtimeId, Mesh colliderMesh)
|
||||||
|
{
|
||||||
|
Coord = coord;
|
||||||
|
Version = version;
|
||||||
|
RuntimeId = runtimeId;
|
||||||
ColliderMesh = colliderMesh;
|
ColliderMesh = colliderMesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mesh RenderMesh { get; }
|
public Vector2Int Coord { get; }
|
||||||
|
public int Version { get; }
|
||||||
|
public int RuntimeId { get; }
|
||||||
public Mesh ColliderMesh { get; }
|
public Mesh ColliderMesh { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class ChunkRenderSnapshot
|
||||||
|
{
|
||||||
|
public ChunkRenderSnapshot(Vector3[] vertices, Vector3[] normals, Vector2[] uv0, Vector2[] uv1, int[] triangles)
|
||||||
|
{
|
||||||
|
Vertices = vertices;
|
||||||
|
Normals = normals;
|
||||||
|
Uv0 = uv0;
|
||||||
|
Uv1 = uv1;
|
||||||
|
Triangles = triangles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3[] Vertices { get; }
|
||||||
|
public Vector3[] Normals { get; }
|
||||||
|
public Vector2[] Uv0 { get; }
|
||||||
|
public Vector2[] Uv1 { get; }
|
||||||
|
public int[] Triangles { get; }
|
||||||
|
public bool IsEmpty => Vertices == null || Vertices.Length == 0 || Triangles == null || Triangles.Length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct RegionChunkSnapshot
|
||||||
|
{
|
||||||
|
public RegionChunkSnapshot(ChunkRenderSnapshot snapshot, Vector3 localOffset)
|
||||||
|
{
|
||||||
|
Snapshot = snapshot;
|
||||||
|
LocalOffset = localOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkRenderSnapshot Snapshot { get; }
|
||||||
|
public Vector3 LocalOffset { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct RegionBuildRequest
|
||||||
|
{
|
||||||
|
public RegionBuildRequest(Vector2Int regionCoord, int version, int session, RegionChunkSnapshot[] chunks)
|
||||||
|
{
|
||||||
|
RegionCoord = regionCoord;
|
||||||
|
Version = version;
|
||||||
|
Session = session;
|
||||||
|
Chunks = chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2Int RegionCoord { get; }
|
||||||
|
public int Version { get; }
|
||||||
|
public int Session { get; }
|
||||||
|
public RegionChunkSnapshot[] Chunks { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct RegionBuildResult
|
||||||
|
{
|
||||||
|
public RegionBuildResult(Vector2Int regionCoord, int version, int session, Vector3[] vertices, Vector3[] normals, Vector2[] uv0, Vector2[] uv1, int[] triangles, Bounds bounds)
|
||||||
|
{
|
||||||
|
RegionCoord = regionCoord;
|
||||||
|
Version = version;
|
||||||
|
Session = session;
|
||||||
|
Vertices = vertices;
|
||||||
|
Normals = normals;
|
||||||
|
Uv0 = uv0;
|
||||||
|
Uv1 = uv1;
|
||||||
|
Triangles = triangles;
|
||||||
|
Bounds = bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2Int RegionCoord { get; }
|
||||||
|
public int Version { get; }
|
||||||
|
public int Session { get; }
|
||||||
|
public Vector3[] Vertices { get; }
|
||||||
|
public Vector3[] Normals { get; }
|
||||||
|
public Vector2[] Uv0 { get; }
|
||||||
|
public Vector2[] Uv1 { get; }
|
||||||
|
public int[] Triangles { get; }
|
||||||
|
public Bounds Bounds { get; }
|
||||||
|
public bool IsEmpty => Vertices == null || Vertices.Length == 0 || Triangles == null || Triangles.Length == 0;
|
||||||
|
|
||||||
|
public static RegionBuildResult CreateEmpty(Vector2Int regionCoord, int version, int session)
|
||||||
|
{
|
||||||
|
return new RegionBuildResult(regionCoord, version, session, null, null, null, null, null, new Bounds());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class MeshBuffers
|
private sealed class MeshBuffers
|
||||||
{
|
{
|
||||||
public readonly List<Vector3> Vertices = new List<Vector3>(512);
|
public readonly List<Vector3> Vertices = new List<Vector3>(512);
|
||||||
@@ -1279,6 +1661,16 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
mesh.RecalculateBounds();
|
mesh.RecalculateBounds();
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ChunkRenderSnapshot ToSnapshot()
|
||||||
|
{
|
||||||
|
return new ChunkRenderSnapshot(
|
||||||
|
Vertices.ToArray(),
|
||||||
|
Normals.ToArray(),
|
||||||
|
Uvs.ToArray(),
|
||||||
|
TextureData.ToArray(),
|
||||||
|
Triangles.ToArray());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class ChunkRuntime
|
private sealed class ChunkRuntime
|
||||||
@@ -1286,12 +1678,13 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
public Transform Root;
|
public Transform Root;
|
||||||
public MeshCollider MountainCollider;
|
public MeshCollider MountainCollider;
|
||||||
public BoxCollider GroundCollider;
|
public BoxCollider GroundCollider;
|
||||||
public Mesh RenderMesh;
|
|
||||||
public Mesh ColliderMesh;
|
public Mesh ColliderMesh;
|
||||||
|
public ChunkRenderSnapshot RenderSnapshot;
|
||||||
public int[] Heights;
|
public int[] Heights;
|
||||||
public byte[] BiomeIndices;
|
public byte[] BiomeIndices;
|
||||||
public ChunkState State;
|
public ChunkState State;
|
||||||
public int Version;
|
public int Version;
|
||||||
|
public int RuntimeId;
|
||||||
|
|
||||||
public bool HasData => Heights != null && BiomeIndices != null;
|
public bool HasData => Heights != null && BiomeIndices != null;
|
||||||
public bool IsRendered => State == ChunkState.Rendered && Root != null;
|
public bool IsRendered => State == ChunkState.Rendered && Root != null;
|
||||||
@@ -1312,20 +1705,19 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
GroundCollider.center = new Vector3(chunkSize * 0.5f, -0.1f, chunkSize * 0.5f);
|
GroundCollider.center = new Vector3(chunkSize * 0.5f, -0.1f, chunkSize * 0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyMeshes(ChunkMeshBuild build, int chunkSize)
|
public void ApplyRenderData(ChunkRenderSnapshot renderSnapshot)
|
||||||
{
|
{
|
||||||
if (RenderMesh != null)
|
RenderSnapshot = renderSnapshot;
|
||||||
{
|
}
|
||||||
DestroyMesh(RenderMesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public void ApplyColliderMesh(Mesh colliderMesh)
|
||||||
|
{
|
||||||
if (ColliderMesh != null)
|
if (ColliderMesh != null)
|
||||||
{
|
{
|
||||||
DestroyMesh(ColliderMesh);
|
DestroyMesh(ColliderMesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderMesh = build.RenderMesh;
|
ColliderMesh = colliderMesh;
|
||||||
ColliderMesh = build.ColliderMesh;
|
|
||||||
MountainCollider.sharedMesh = null;
|
MountainCollider.sharedMesh = null;
|
||||||
MountainCollider.sharedMesh = ColliderMesh != null && ColliderMesh.vertexCount > 0 ? ColliderMesh : null;
|
MountainCollider.sharedMesh = ColliderMesh != null && ColliderMesh.vertexCount > 0 ? ColliderMesh : null;
|
||||||
}
|
}
|
||||||
@@ -1344,8 +1736,8 @@ namespace InfiniteWorld.VoxelWorld
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DestroyMesh(RenderMesh);
|
|
||||||
DestroyMesh(ColliderMesh);
|
DestroyMesh(ColliderMesh);
|
||||||
|
RenderSnapshot = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DestroyMesh(Mesh mesh)
|
private static void DestroyMesh(Mesh mesh)
|
||||||
|
|||||||
Reference in New Issue
Block a user