private void Enqueue(ConcurrentDictionary <ChunkCoordinates, ConcurrentQueue <LightingOperation> > dict, LightingOperation op) { var r = dict.GetOrAdd(op.Coordinates, coordinates => new ConcurrentQueue <LightingOperation>()); r.Enqueue(op); }
private void LightBox(LightingOperation op) { var corners = op.Box.GetCorners(); // var center = op.Coordinates; ChunkCoordinates center = new ChunkCoordinates(new PlayerLocation(corners.Average(p => p.X), corners.Average(p => p.Y), corners.Average(p => p.Z))); var chunk = World.GetChunkColumn(center.X, center.Z); if (chunk == null) { return; } for (int x = (int)op.Box.Min.X; x < (int)op.Box.Max.X; x++) { for (int z = (int)op.Box.Min.Z; z < (int)op.Box.Max.Z; z++) { for (int y = (int)op.Box.Max.Y - 1; y >= (int)op.Box.Min.Y; y--) { LightVoxel(x, y, z, op); } } } chunk.SkyLightDirty = false; }
private void LightBox(LightingOperation op) { var chunk = World.FindChunk((Coordinates3D)op.Box.Center, generate: false); if (chunk == null || !chunk.TerrainPopulated) { return; } for (int x = (int)op.Box.Min.X; x < (int)op.Box.Max.X; x++) { for (int z = (int)op.Box.Min.Z; z < (int)op.Box.Max.Z; z++) { for (int y = (int)op.Box.Max.Y - 1; y >= (int)op.Box.Min.Y; y--) { LightVoxel(x, y, z, op); } } } }
private void FixPriority(LightingOperation op, CheckResult result) { switch (result) { case CheckResult.LowPriority: Enqueue(LowPriorityQueue, op); break; case CheckResult.MediumPriority: Enqueue(MidPriorityQueue, op); break; case CheckResult.HighPriority: Enqueue(HighPriorityQueue, op); break; case CheckResult.Cancel: break; } }
private void LightBox(LightingOperation op) { var chunk = World.FindChunk((Coordinates3D)op.Box.Center(), false); if (chunk == null || !chunk.TerrainPopulated) { return; } Profiler.Start("lighting.box"); for (var x = (int)op.Box.Min.X; x < (int)op.Box.Max.X; x++) { for (var z = (int)op.Box.Min.Z; z < (int)op.Box.Max.Z; z++) { for (var y = (int)op.Box.Max.Y - 1; y >= (int)op.Box.Min.Y; y--) { LightVoxel(x, y, z, op); } } } Profiler.Done(); }
private void PropegateLightEvent(int x, int y, int z, byte value, LightingOperation op) { var coords = new BlockCoordinates(x, y, z); if (!World.HasBlock(x, y, z)) { return; } IChunkColumn chunk; var adjustedCoords = World.FindBlockPosition(coords, out chunk); if (chunk == null) { return; } byte current = op.SkyLight ? chunk.GetSkylight(adjustedCoords.X, adjustedCoords.Y, adjustedCoords.Z) : chunk.GetBlocklight(adjustedCoords.X, adjustedCoords.Y, adjustedCoords.Z); if (value == current) { return; } var provider = chunk.GetBlockState(adjustedCoords.X, adjustedCoords.Y, adjustedCoords.Z); if (op.Initial) { byte emissiveness = (byte)provider.Block.LightValue; if (chunk.GetHeight((byte)adjustedCoords.X, (byte)adjustedCoords.Z) <= y) { emissiveness = 15; } if (emissiveness >= current) { return; } } EnqueueOperation(new ChunkCoordinates(chunk.X, chunk.Z), new BoundingBox(new Vector3(x, y, z), new Vector3(x + 1, y + 1, z + 1)), op.SkyLight, op.Initial); }
/// <summary> /// Propegates a lighting change to an adjacent voxel (if neccesary) /// </summary> private void PropegateLightEvent(int x, int y, int z, byte value, LightingOperation op) { var coords = new Coordinates3D(x, y, z); if (!World.IsValidPosition(coords)) { return; } IChunk chunk; var adjustedCoords = World.FindBlockPosition(coords, out chunk, false); if (chunk == null || !chunk.TerrainPopulated) { return; } var current = op.SkyLight ? World.GetSkyLight(coords) : World.GetBlockLight(coords); if (value == current) { return; } var provider = BlockRepository.GetBlockProvider(World.GetBlockId(coords)); if (op.Initial) { var emissiveness = provider.Luminance; if (chunk.GetHeight((byte)adjustedCoords.X, (byte)adjustedCoords.Z) <= y) { emissiveness = 15; } if (emissiveness >= current) { return; } } EnqueueOperation(new BoundingBox(new Vector3(x, y, z), new Vector3(x + 1, y + 1, z + 1)), op.SkyLight, op.Initial); }
public void EnqueueOperation(ChunkCoordinates coords, BoundingBox box, bool skyLight, bool initial = false) { var op = new LightingOperation { SkyLight = skyLight, Box = box, Initial = initial, Coordinates = coords }; CheckResult result = CheckResult.HighPriority; if (!op.Initial) { result = CheckDistance(coords); if (result == CheckResult.Cancel) { return; } } FixPriority(op, result); ResetEvent.Set(); }
/// <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 Coordinates3D(x, y, z); IChunk chunk; var adjustedCoords = World.FindBlockPosition(coords, out chunk, generate: false); if (chunk == null || !chunk.TerrainPopulated) // Move on if this chunk is empty { return; } var id = World.GetBlockID(coords); 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 = Math.Max(provider.LightOpacity, (byte)1); byte current = op.SkyLight ? World.GetSkyLight(coords) : World.GetBlockLight(coords); byte final = 0; // Calculate emissiveness byte emissiveness = provider.Luminance; if (op.SkyLight) { var height = HeightMaps[chunk.Coordinates][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++) { if (World.IsValidPosition(coords + Neighbors[i])) { IChunk c; var adjusted = World.FindBlockPosition(coords + Neighbors[i], out c, generate: false); if (c != null) // We don't want to generate new chunks just to light this voxel { byte val; if (op.SkyLight) { val = c.GetSkyLight(adjusted); } else { val = c.GetBlockLight(adjusted); } 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, final); } else { chunk.SetBlockLight(adjustedCoords, final); } byte propegated = (byte)Math.Max(final - 1, 0); // Propegate lighting change to neighboring blocks PropegateLightEvent(x - 1, y, z, propegated, op); PropegateLightEvent(x, y - 1, z, propegated, op); 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); } } }
private bool TryDequeue( ConcurrentDictionary <ChunkCoordinates, ConcurrentQueue <LightingOperation> > dict, ChunkCoordinates center, out LightingOperation op) { var f = dict.OrderBy(x => Math.Abs(x.Key.DistanceTo(center))).FirstOrDefault(x => !x.Value.IsEmpty); if (f.Value == null) { op = default(LightingOperation); return(false); } return(f.Value.TryDequeue(out op)); }
/// <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); } } }
/// <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 Coordinates3D(x, y, z); IChunk chunk; var adjustedCoords = World.FindBlockPosition(coords, out chunk, generate: false); if (chunk == null || !chunk.TerrainPopulated) // Move on if this chunk is empty return; Profiler.Start("lighting.voxel"); var id = World.GetBlockID(coords); 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 = Math.Max(provider.LightOpacity, (byte)1); byte current = op.SkyLight ? World.GetSkyLight(coords) : World.GetBlockLight(coords); byte final = 0; // Calculate emissiveness byte emissiveness = provider.Luminance; if (op.SkyLight) { var height = HeightMaps[chunk.Coordinates][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++) { if (World.IsValidPosition(coords + Neighbors[i])) { IChunk c; var adjusted = World.FindBlockPosition(coords + Neighbors[i], out c, generate: false); if (c != null) // We don't want to generate new chunks just to light this voxel { byte val; if (op.SkyLight) val = c.GetSkyLight(adjusted); else val = c.GetBlockLight(adjusted); 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, final); else chunk.SetBlockLight(adjustedCoords, final); byte propegated = (byte)Math.Max(final - 1, 0); // Propegate lighting change to neighboring blocks PropegateLightEvent(x - 1, y, z, propegated, op); PropegateLightEvent(x, y - 1, z, propegated, op); 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); } Profiler.Done(); }
/// <summary> /// Propegates a lighting change to an adjacent voxel (if neccesary) /// </summary> private void PropegateLightEvent(int x, int y, int z, byte value, LightingOperation op) { var coords = new Coordinates3D(x, y, z); if (!World.IsValidPosition(coords)) return; IChunk chunk; var adjustedCoords = World.FindBlockPosition(coords, out chunk, generate: false); if (chunk == null || !chunk.TerrainPopulated) return; byte current = op.SkyLight ? World.GetSkyLight(coords) : World.GetBlockLight(coords); if (value == current) return; var provider = BlockRepository.GetBlockProvider(World.GetBlockID(coords)); if (op.Initial) { byte emissiveness = provider.Luminance; if (chunk.GetHeight((byte)adjustedCoords.X, (byte)adjustedCoords.Z) <= y) emissiveness = 15; if (emissiveness >= current) return; } EnqueueOperation(new BoundingBox(new Vector3(x, y, z), new Vector3(x, y, z) + 1), op.SkyLight, op.Initial); }
private void LightBox(LightingOperation op) { var chunk = World.FindChunk((Coordinates3D)op.Box.Center, generate: false); if (chunk == null || !chunk.TerrainPopulated) return; Profiler.Start("lighting.box"); for (int x = (int)op.Box.Min.X; x < (int)op.Box.Max.X; x++) for (int z = (int)op.Box.Min.Z; z < (int)op.Box.Max.Z; z++) for (int y = (int)op.Box.Max.Y - 1; y >= (int)op.Box.Min.Y; y--) { LightVoxel(x, y, z, op); } Profiler.Done(); }