public void SetBlocks(int fromX, int fromY, int fromZ, int toX, int toY, int toZ, Block block) { VoxLogger.Log("VoxelWorld : SetBlocks " + block); FixValues(ref fromX, ref toX); FixValues(ref fromY, ref toY); FixValues(ref fromZ, ref toZ); int3 chunkFrom = Helpers.ContainingChunkPosition(fromX, fromY, fromZ); int3 chunkTo = Helpers.ContainingChunkPosition(toX, toY, toZ); int minY = Helpers.Mod(fromY, Chunk.CHUNK_SIZE); for (int cy = chunkFrom.y; cy <= chunkTo.y; cy += Chunk.CHUNK_SIZE, minY = 0) { int maxY = math.min(toY - cy, Chunk.CHUNK_SIZE - 1); int minZ = Helpers.Mod(fromZ, Chunk.CHUNK_SIZE); for (int cz = chunkFrom.z; cz <= chunkTo.z; cz += Chunk.CHUNK_SIZE, minZ = 0) { int maxZ = math.min(toZ - cz, Chunk.CHUNK_SIZE - 1); int minX = Helpers.Mod(fromX, Chunk.CHUNK_SIZE); for (int cx = chunkFrom.x; cx <= chunkTo.x; cx += Chunk.CHUNK_SIZE, minX = 0) { bool ghostChunk = false; int3 chunkPosition = new int3(cx, cy, cz); VoxLogger.Log("VoxelWorld : SetBlocks " + block + " | Update chunk at " + chunkPosition + "."); Chunk chunk = GetChunk(chunkPosition); if (chunk == null) { VoxLogger.Log("VoxelWorld : SetBlocks " + block + " | Chunk is ghost chunk."); ghostChunk = true; chunk = CreateChunk(chunkPosition); chunk.render = false; chunks.Add(chunkPosition, chunk); chunk.NeedsTerrain = !Serialization.LoadChunk(chunk, true); if (chunk.NeedsTerrain) { AddToQueue(generateQueue, chunkPosition, 0); } ghostChunks.Add(chunk); } int maxX = math.min(toX - cx, Chunk.CHUNK_SIZE - 1); int3 from = new int3(minX, minY, minZ); int3 to = new int3(maxX, maxY, maxZ); // Only update if it's placing blocks on the edges of the chunk and // if they are the last/first ones in the loop. We don't need the chunks // to update each other. bool updateNorth = (from.z == Chunk.CHUNK_SIZE - 1 || to.z == Chunk.CHUNK_SIZE - 1) && cz == chunkTo.z; bool updateSouth = (from.z == 0 || to.z == 0) && cz == chunkFrom.z; bool updateEast = (from.x == Chunk.CHUNK_SIZE - 1 || to.x == Chunk.CHUNK_SIZE - 1) && cx == chunkTo.x; bool updateWest = (from.x == 0 || to.x == 0) && cx == chunkFrom.x; bool updateTop = (from.y == Chunk.CHUNK_SIZE - 1 || to.y == Chunk.CHUNK_SIZE - 1) && cy == chunkTo.y; bool updateBottom = (from.y == 0 || to.y == 0) && cy == chunkFrom.y; chunk.SetRangeRaw(from, to, block); if (!ghostChunk) { // Only update this chunk. chunk.UpdateChunk(); UpdateChunkNeighbors(chunkPosition, updateNorth, updateSouth, updateEast, updateWest, updateTop, updateBottom); } else if (!chunk.NeedsTerrain) { VoxLogger.Log("VoxelWorld : SetBlocks " + block + " | Saving ghost chunk " + chunk + "."); Serialization.SaveChunk(chunk, true); DestroyChunk(chunk); } } } } }
private void FinishGeneratingChunks() { if (!generatingChunks) { return; } generateChunksJob.Complete(); voxelLoaderData.Dispose(); loadedChunks.Dispose(); NativeArray <int3> renChunksKeys = tempRenderChunks.GetKeyArray(Allocator.Temp); NativeArray <ChunkData> renChunksValues = tempRenderChunks.GetValueArray(Allocator.Temp); renderChunks.Clear(); renderChunks.AddRange(renChunksKeys); for (int i = 0; i < renChunksKeys.Length; i++) { int3 chunkPos = renChunksKeys[i]; bool shouldRender = renChunksValues[i].render; float priority = renChunksValues[i].priority; if (!chunks.TryGetValue(chunkPos, out Chunk chunk)) { chunk = CreateChunk(chunkPos); chunks.Add(chunkPos, chunk); chunk.NeedsTerrain = !Serialization.LoadChunk(chunk, true); if (chunk.NeedsTerrain) { AddToQueue(generateQueue, chunkPos, priority); } else if (shouldRender) { TryToQueueChunkRender(chunk, priority); } } else if (!shouldRender && chunkRenderers.TryGetValue(chunkPos, out MeshRenderer renderer)) { // This is used for removing edge chunks that shouldn't be rendered. // If we don't do this their meshes will stick around but not be properly updated. PoolChunkRenderer(renderer); chunkRenderers.Remove(chunkPos); } else if (shouldRender && chunk.HasRender && !chunkRenderers.ContainsKey(chunkPos)) { // This is in case there are chunks that should render but are missing renderers. // Missing renderers can be caused by the method above. CreateChunkRenderer(chunkPos, chunk.mesh); } else if (chunk.HasTerrain && !chunk.UpdatingRenderer && !chunk.HasRender && shouldRender) { // The chunk is new and needs a mesh. TryToQueueChunkRender(chunk, priority); } chunk.render = shouldRender; } renChunksKeys.Dispose(); renChunksValues.Dispose(); tempRenderChunks.Dispose(); chunksToRemove.Clear(); for (int i = 0; i < tempChunksToRemove.Length; i++) { DestroyChunk(chunks[tempChunksToRemove[i]]); } tempChunksToRemove.Dispose(); generatingChunks = false; }