public float LightingAt(int x, int y, int z) { int sunlight, normallight; if (x < 0 || y < 0 || z < 0 || x >= Chunk.HSIZE || y >= Chunk.VSIZE || z >= Chunk.HSIZE) { int dx = (int)Math.Floor((float)x / Chunk.HSIZE); int dy = (int)Math.Floor((float)y / Chunk.VSIZE); int dz = (int)Math.Floor((float)z / Chunk.HSIZE); Chunk otherChunk; if (terrain.TryGetChunk(chunk.IndexPosition + new IndexPosition(dx, dy, dz), out otherChunk)) { ChunkLightingContainer otherLighting = otherChunk.Lighting; int bx = x < 0 ? x + Chunk.HSIZE : x % Chunk.HSIZE; int by = y < 0 ? y + Chunk.VSIZE : y % Chunk.VSIZE; int bz = z < 0 ? z + Chunk.HSIZE : z % Chunk.HSIZE; sunlight = otherLighting.GetSunlight(bx, by, bz); normallight = otherLighting.GetNormalLight(bx, by, bz); } else { return(1f); } } else { sunlight = GetSunlight(x, y, z); normallight = GetNormalLight(x, y, z); } return(Math.Max(Math.Max(sunlight, normallight) / 31f, 0f)); }
void AddInitialSunlight() { // Check if there is a chunk above us Chunk aboveChunk; if (terrain.TryGetChunk(new IndexPosition(chunk.IndexPosition.X, chunk.IndexPosition.Y + 1, chunk.IndexPosition.Z), out aboveChunk)) { ChunkLightingContainer aboveLighting = aboveChunk.Lighting; if (!aboveLighting.IsInitialSunlightPhaseDone) { aboveLighting.Process(); } // Copy the bottom sunlight values from the above chunk, into the top blocks of this chunk. for (int x = 0; x < Chunk.HSIZE; x++) { for (int z = 0; z < Chunk.HSIZE; z++) { if (!chunk.IsBlockAt(x, Chunk.VSIZE - 1, z) && !aboveChunk.IsBlockAt(x, 0, z)) { SetSunlight(x, Chunk.VSIZE - 1, z, aboveLighting.GetSunlight(x, 0, z)); sunlightQueue.Enqueue(new IndexPosition(x, Chunk.VSIZE - 1, z)); } } } } else { // Assume we are the highest chunk in this column, set every air block to max lighting // at the very top of the chunk. for (int x = 0; x < Chunk.HSIZE; x++) { for (int z = 0; z < Chunk.HSIZE; z++) { if (!chunk.IsBlockAt(x, Chunk.VSIZE - 1, z)) { SetSunlight(x, Chunk.VSIZE - 1, z, MAX_LIGHT_LEVEL); sunlightQueue.Enqueue(new IndexPosition(x, Chunk.VSIZE - 1, z)); } } } } }
void RunSunlightRemove() { while (sunlightRemovalQueue.TryDequeue(out LightRemovalNode node)) { int x = node.Index.X; int y = node.Index.Y; int z = node.Index.Z; int lightLevel = node.Value; for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { for (int dz = -1; dz <= 1; dz++) { int numZero = 0; if (dx == 0) { numZero++; } if (dy == 0) { numZero++; } if (dz == 0) { numZero++; } if (numZero == 2) { int nx = x + dx; int ny = y + dy; int nz = z + dz; if (nx < 0 || ny < 0 || nz < 0 || nx >= Chunk.HSIZE || ny >= Chunk.VSIZE || nz >= Chunk.HSIZE) { // Indexes are out of this chunk, check for the other chunk and pass on the light value. int cx = (int)Math.Floor((float)nx / Chunk.HSIZE); int cy = (int)Math.Floor((float)ny / Chunk.VSIZE); int cz = (int)Math.Floor((float)nz / Chunk.HSIZE); Chunk otherChunk; if (terrain.TryGetChunk(chunk.IndexPosition + new IndexPosition(cx, cy, cz), out otherChunk)) { ChunkLightingContainer otherLighting = otherChunk.Lighting; int bx = nx < 0 ? nx + Chunk.HSIZE : nx % Chunk.HSIZE; int by = ny < 0 ? ny + Chunk.VSIZE : ny % Chunk.VSIZE; int bz = nz < 0 ? nz + Chunk.HSIZE : nz % Chunk.HSIZE; int neighborLevel = otherLighting.GetSunlight(bx, by, bz); if (neighborLevel != 0 && ((dy == -1 && lightLevel == MAX_LIGHT_LEVEL) || neighborLevel < lightLevel)) { otherLighting.RequestSunlightRemoval(bx, by, bz); } else if (neighborLevel >= lightLevel) { // TODO: Should this make a request? otherLighting.sunlightQueue.Enqueue(new IndexPosition(bx, by, bz)); } } } else { int neighborLevel = GetSunlight(nx, ny, nz); if (neighborLevel != 0 && ((dy == -1 && lightLevel == MAX_LIGHT_LEVEL) || neighborLevel < lightLevel)) { SetSunlight(nx, ny, nz, 0); sunlightRemovalQueue.Enqueue(new LightRemovalNode(new IndexPosition(nx, ny, nz), (short)neighborLevel)); } else if (neighborLevel >= lightLevel) { sunlightQueue.Enqueue(new IndexPosition(nx, ny, nz)); } } } } } } } }
void RunSunlightFill() { // Flood fill sunlight while (sunlightQueue.TryDequeue(out IndexPosition index)) { int x = index.X; int y = index.Y; int z = index.Z; int lightLevel = GetSunlight(x, y, z); for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { for (int dz = -1; dz <= 1; dz++) { int numZero = 0; if (dx == 0) { numZero++; } if (dy == 0) { numZero++; } if (dz == 0) { numZero++; } if (numZero == 2) { int nx = x + dx; int ny = y + dy; int nz = z + dz; if (nx < 0 || ny < 0 || nz < 0 || nx >= Chunk.HSIZE || ny >= Chunk.VSIZE || nz >= Chunk.HSIZE) { // Indexes are out of this chunk, check for the other chunk and pass on the light value. int cx = (int)Math.Floor((float)nx / Chunk.HSIZE); int cy = (int)Math.Floor((float)ny / Chunk.VSIZE); int cz = (int)Math.Floor((float)nz / Chunk.HSIZE); Chunk otherChunk; if (terrain.TryGetChunk(chunk.IndexPosition + new IndexPosition(cx, cy, cz), out otherChunk)) { ChunkLightingContainer otherLighting = otherChunk.Lighting; int bx = nx < 0 ? nx + Chunk.HSIZE : nx % Chunk.HSIZE; int by = ny < 0 ? ny + Chunk.VSIZE : ny % Chunk.VSIZE; int bz = nz < 0 ? nz + Chunk.HSIZE : nz % Chunk.HSIZE; if (!otherChunk.IsBlockAt(bx, by, bz)) { int neighborSunlight = otherLighting.GetSunlight(bx, by, bz); bool downwardSunlight = lightLevel == MAX_LIGHT_LEVEL && dy == -1; if ((downwardSunlight && neighborSunlight != MAX_LIGHT_LEVEL) || (neighborSunlight + 2 <= lightLevel)) { int newLight = Math.Max(downwardSunlight ? MAX_LIGHT_LEVEL : lightLevel - 1, 0); otherLighting.RequestSetSunlight(bx, by, bz, (short)newLight); } } } } else if (!chunk.IsBlockAt(nx, ny, nz)) { // Update sunlight level in our chunk int neighborSunlight = GetSunlight(nx, ny, nz); bool downwardSunlight = lightLevel == MAX_LIGHT_LEVEL && dy == -1; if ((downwardSunlight && neighborSunlight != MAX_LIGHT_LEVEL) || (neighborSunlight + 2 <= lightLevel)) { int newLight = Math.Max(downwardSunlight ? MAX_LIGHT_LEVEL : lightLevel - 1, 0); SetSunlight(nx, ny, nz, newLight); sunlightQueue.Enqueue(new IndexPosition(nx, ny, nz)); } } } } } } } }
void ProcessRequestQueues() { while (sunlightRemovalRequestQueue.TryDequeue(out IndexPosition index)) { int x = index.X; int y = index.Y; int z = index.Z; int currentSunlight = GetSunlight(x, y, z); if (currentSunlight != 0) { sunlightRemovalQueue.Enqueue(new LightRemovalNode(new IndexPosition(x, y, z), (short)currentSunlight)); SetSunlight(x, y, z, 0); } } RunSunlightRemove(); while (sunlightRequestQueue.TryDequeue(out LightRequestNode node)) { int x = node.Index.X; int y = node.Index.Y; int z = node.Index.Z; int currentSunlight = GetSunlight(x, y, z); if (currentSunlight < node.Value) { SetSunlight(x, y, z, node.Value); sunlightQueue.Enqueue(node.Index); } } while (sunlightRefillRequestQueue.TryDequeue(out IndexPosition index)) { int x = index.X; int y = index.Y; int z = index.Z; for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { for (int dz = -1; dz <= 1; dz++) { int numZero = 0; if (dx == 0) { numZero++; } if (dy == 0) { numZero++; } if (dz == 0) { numZero++; } if (numZero == 2) { int nx = x + dx; int ny = y + dy; int nz = z + dz; if (nx < 0 || ny < 0 || nz < 0 || nx >= Chunk.HSIZE || ny >= Chunk.VSIZE || nz >= Chunk.HSIZE) { // Indexes are out of this chunk, check for the other chunk and pass on the light value. int cx = (int)Math.Floor((float)nx / Chunk.HSIZE); int cy = (int)Math.Floor((float)ny / Chunk.VSIZE); int cz = (int)Math.Floor((float)nz / Chunk.HSIZE); Chunk otherChunk; if (terrain.TryGetChunk(chunk.IndexPosition + new IndexPosition(cx, cy, cz), out otherChunk)) { ChunkLightingContainer otherLighting = otherChunk.Lighting; int bx = nx < 0 ? nx + Chunk.HSIZE : nx % Chunk.HSIZE; int by = ny < 0 ? ny + Chunk.VSIZE : ny % Chunk.VSIZE; int bz = nz < 0 ? nz + Chunk.HSIZE : nz % Chunk.HSIZE; int neighborSunlight = otherLighting.GetSunlight(bx, by, bz); if (neighborSunlight > 0) { // TODO: Should this make a request instead? otherLighting.sunlightQueue.Enqueue(new IndexPosition(bx, by, bz)); } } } else { int neighborSunlight = GetSunlight(nx, ny, nz); if (neighborSunlight > 0) { sunlightQueue.Enqueue(new IndexPosition(nx, ny, nz)); } } } } } } } RunSunlightFill(); }