public void UpdateHeightMap(BlockCoordinates coords) { IChunkColumn chunk; var adjusted = World.FindBlockPosition(coords, out chunk); if (chunk == null) { return; } var chunkPos = new ChunkCoordinates(chunk.X, chunk.Z); if (!HeightMaps.ContainsKey(chunkPos)) { return; } var map = HeightMaps[chunkPos]; byte x = (byte)adjusted.X; byte z = (byte)adjusted.Z; BlockCoordinates _; for (byte y = (byte)(chunk.GetHeight(x, z) + 2); y > 0; y--) { if (y >= 255) { continue; } _.X = x; _.Y = y - 1; _.Z = z; var provider = chunk.GetBlockState(_.X, _.Y, _.Z).Block; if (!provider.Renderable) { continue; } if (provider.LightOpacity != 0) { map[x, z] = y; break; } } }
/// <summary> /// Computes the correct lighting value for a given voxel. /// </summary> private void LightVoxel(int x, int y, int z, LightingOperation op) { var coords = new BlockCoordinates(x, y, z); IChunkColumn chunk; var adjustedCoords = World.FindBlockPosition(coords, out chunk); if (chunk == null) // Move on if this chunk is empty { return; } var provider = chunk.GetBlockState(adjustedCoords.X, adjustedCoords.Y, adjustedCoords.Z).Block; // var provider = BlockRepository.GetBlockProvider(id); // The opacity of the block determines the amount of light it receives from // neighboring blocks. This is subtracted from the max of the neighboring // block values. We must subtract at least 1. byte opacity = (byte)Math.Max(provider.LightOpacity, (byte)1); byte current = op.SkyLight ? chunk.GetSkylight(adjustedCoords.X, adjustedCoords.Y, adjustedCoords.Z) : chunk.GetBlocklight(adjustedCoords.X, adjustedCoords.Y, adjustedCoords.Z); byte final = 0; // Calculate emissiveness byte emissiveness = (byte)provider.LightValue; if (op.SkyLight) { var chunkPos = new ChunkCoordinates(chunk.X, chunk.Z); byte[,] map; if (!HeightMaps.TryGetValue(chunkPos, out map)) { GenerateHeightMap(chunk); map = HeightMaps[chunkPos]; } // var height = chunk.GetHeight(adjustedCoords.X, adjustedCoords.Z); var height = map[adjustedCoords.X, adjustedCoords.Z]; // For skylight, the emissiveness is 15 if y >= height if (y >= height) { emissiveness = 15; } else { if (provider.LightOpacity >= 15) { emissiveness = 0; } } } if (opacity < 15 || emissiveness != 0) { // Compute the light based on the max of the neighbors byte max = 0; for (int i = 0; i < Neighbors.Length; i++) { IChunkColumn c; var adjusted = World.FindBlockPosition(coords + Neighbors[i], out c); //var n = coords + Neighbors[i]; // if (World.HasBlock(n.X, n.Y, n.Z)) if (c != null) { byte val; if (op.SkyLight) { val = c.GetSkylight(adjusted.X, adjusted.Y, adjusted.Z); } else { val = c.GetBlocklight(adjusted.X, adjusted.Y, adjusted.Z); } max = Math.Max(max, val); } } // final = MAX(max - opacity, emissiveness, 0) final = (byte)Math.Max(max - opacity, emissiveness); if (final < 0) { final = 0; } } if (final != current) { // Apply changes if (op.SkyLight) { chunk.SetSkyLight(adjustedCoords.X, adjustedCoords.Y, adjustedCoords.Z, final); } else { chunk.SetBlocklight(adjustedCoords.X, adjustedCoords.Y, adjustedCoords.Z, final); } byte propegated = (byte)Math.Max(final - 1, 0); // Propegate lighting change to neighboring blocks if (x - 1 <= op.Box.Min.X) { PropegateLightEvent(x - 1, y, z, propegated, op); } if (y - 1 <= op.Box.Min.Y) { PropegateLightEvent(x, y - 1, z, propegated, op); } if (z - 1 <= op.Box.Min.Z) { PropegateLightEvent(x, y, z - 1, propegated, op); } if (x + 1 >= op.Box.Max.X) { PropegateLightEvent(x + 1, y, z, propegated, op); } if (y + 1 >= op.Box.Max.Y) { PropegateLightEvent(x, y + 1, z, propegated, op); } if (z + 1 >= op.Box.Max.Z) { PropegateLightEvent(x, y, z + 1, propegated, op); } } }