NBTExplorer/Architect/ChunkLight.cs
2011-02-08 07:35:33 +00:00

430 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NBT
{
class KeyCoord
{
public int x;
public int y;
public int z;
public KeyCoord (int _x, int _y, int _z)
{
x = _x;
y = _y;
z = _z;
}
public override bool Equals (object obj)
{
if (obj is KeyCoord) {
KeyCoord that = (KeyCoord)obj;
if (this.x == that.x && this.y == that.y && this.z == that.z) {
return true;
}
}
return false;
}
}
class LightNode
{
public MC.Chunk chunk = null;
public Block block = null;
// Absolute coordinates within chunk
public short absX = 0;
public short absY = 0;
public short absZ = 0;
// Relative coordinates from updated block
public short relX = 0;
public short relY = 0;
public short relZ = 0;
public short intensity = 0;
public short blocklight = 0;
public LightNode nTop = null;
public LightNode nBottom = null;
public LightNode nNorth = null;
public LightNode nEast = null;
public LightNode nSouth = null;
public LightNode nWest = null;
public bool dirty = false;
}
class LightTrace
{
LightNode root = null;
Stack<LightNode> stack = null;
Dictionary<KeyCoord, LightNode> index = null;
LightNode TraceAffected (MC.Chunk chunk, short x, short y, short z, short intensity)
{
root = new LightNode();
root.chunk = chunk;
root.block = BlockInfo.Index[chunk.GetBlockId(x, y, z)];
root.absX = x;
root.absY = y;
root.absZ = z;
root.intensity = intensity;
// Setup quick lookup index
index = new Dictionary<KeyCoord, LightNode>();
index.Add(new KeyCoord(0, 0, 0), root);
// Setup processing stack
stack = new Stack<LightNode>();
stack.Push(root);
while (stack.Count > 0) {
TraceNeighbors(stack.Pop());
}
return root;
}
void TraceNeighbors (LightNode node)
{
// Already at a hard edge
if (node.intensity == 0) {
return;
}
// Try X-1
if (node.nEast == null) {
LightNode nn = new LightNode();
nn.chunk = node.chunk;
nn.absX = (short)(node.absX - 1);
if (node.absX < 0) {
nn.chunk = node.chunk.GetEastNeighbor();
nn.absX = 15;
}
if (nn.chunk != null) {
nn.absY = node.absY;
nn.absZ = node.absZ;
nn.relX = (short)(node.relX - 1);
nn.relY = node.relY;
nn.relZ = node.relZ;
nn.block = BlockInfo.Index[node.chunk.GetBlockId(nn.absX, nn.absY, nn.absZ)];
nn.intensity = (short)(node.intensity - nn.block.transp() - 1);
if (nn.intensity < 0) {
nn.intensity = 0;
}
nn.blocklight = (short)nn.chunk.GetBlockLight(nn.absX, nn.absY, nn.absZ);
ConnectNeighbors(nn);
index.Add(new KeyCoord(nn.relX, nn.relY, nn.relZ), nn);
stack.Push(nn);
}
}
// Try X+1
if (node.nEast == null) {
LightNode nn = new LightNode();
nn.chunk = node.chunk;
nn.absX = (short)(node.absX + 1);
if (node.absX == 16) {
nn.chunk = node.chunk.GetWestNeighbor();
nn.absX = 0;
}
if (nn.chunk != null) {
nn.absY = node.absY;
nn.absZ = node.absZ;
nn.relX = (short)(node.relX + 1);
nn.relY = node.relY;
nn.relZ = node.relZ;
nn.block = BlockInfo.Index[node.chunk.GetBlockId(nn.absX, nn.absY, nn.absZ)];
nn.intensity = (short)(node.intensity - nn.block.transp() - 1);
if (nn.intensity < 0) {
nn.intensity = 0;
}
nn.blocklight = (short)nn.chunk.GetBlockLight(nn.absX, nn.absY, nn.absZ);
ConnectNeighbors(nn);
index.Add(new KeyCoord(nn.relX, nn.relY, nn.relZ), nn);
stack.Push(nn);
}
}
// Try Y-1
if (node.nTop == null && node.absY > 0) {
LightNode nn = new LightNode();
nn.chunk = node.chunk;
nn.block = BlockInfo.Index[node.chunk.GetBlockId(node.absX, node.absY - 1, node.absZ)];
nn.absX = node.absX;
nn.absY = (short)(node.absY - 1);
nn.absZ = node.absZ;
nn.relX = node.relX;
nn.relY = (short)(node.relY - 1);
nn.relZ = node.relZ;
nn.intensity = (short)(node.intensity - nn.block.transp() - 1);
if (nn.intensity < 0) {
nn.intensity = 0;
}
nn.blocklight = (short)nn.chunk.GetBlockLight(nn.absX, nn.absY, nn.absZ);
ConnectNeighbors(nn);
index.Add(new KeyCoord(nn.relX, nn.relY, nn.relZ), nn);
stack.Push(nn);
}
// Try Y+1
if (node.nTop == null && node.absY < 127) {
LightNode nn = new LightNode();
nn.chunk = node.chunk;
nn.block = BlockInfo.Index[node.chunk.GetBlockId(node.absX, node.absY + 1, node.absZ)];
nn.absX = node.absX;
nn.absY = (short)(node.absY + 1);
nn.absZ = node.absZ;
nn.relX = node.relX;
nn.relY = (short)(node.relY + 1);
nn.relZ = node.relZ;
nn.intensity = (short)(node.intensity - nn.block.transp() - 1);
if (nn.intensity < 0) {
nn.intensity = 0;
}
nn.blocklight = (short)nn.chunk.GetBlockLight(nn.absX, nn.absY, nn.absZ);
ConnectNeighbors(nn);
index.Add(new KeyCoord(nn.relX, nn.relY, nn.relZ), nn);
stack.Push(nn);
}
// Try Z-1
if (node.nEast == null) {
LightNode nn = new LightNode();
nn.chunk = node.chunk;
nn.absZ = (short)(node.absZ - 1);
if (node.absZ < 0) {
nn.chunk = node.chunk.GetNorthNeighbor();
nn.absZ = 15;
}
if (nn.chunk != null) {
nn.absX = node.absX;
nn.absY = node.absY;
nn.relX = node.relX;
nn.relY = node.relY;
nn.relZ = (short)(node.relZ - 1);
nn.block = BlockInfo.Index[node.chunk.GetBlockId(nn.absX, nn.absY, nn.absZ)];
nn.intensity = (short)(node.intensity - nn.block.transp() - 1);
if (nn.intensity < 0) {
nn.intensity = 0;
}
nn.blocklight = (short)nn.chunk.GetBlockLight(nn.absX, nn.absY, nn.absZ);
ConnectNeighbors(nn);
index.Add(new KeyCoord(nn.relX, nn.relY, nn.relZ), nn);
stack.Push(nn);
}
}
// Try Z+1
if (node.nEast == null) {
LightNode nn = new LightNode();
nn.chunk = node.chunk;
nn.absZ = (short)(node.absZ + 1);
if (node.absZ == 16) {
nn.chunk = node.chunk.GetSouthNeighbor();
nn.absZ = 0;
}
if (nn.chunk != null) {
nn.absX = node.absX;
nn.absY = node.absY;
nn.relX = node.relX;
nn.relY = node.relY;
nn.relZ = (short)(node.relZ + 1);
nn.block = BlockInfo.Index[node.chunk.GetBlockId(nn.absX, nn.absY, nn.absZ)];
nn.intensity = (short)(node.intensity - nn.block.transp() - 1);
if (nn.intensity < 0) {
nn.intensity = 0;
}
nn.blocklight = (short)nn.chunk.GetBlockLight(nn.absX, nn.absY, nn.absZ);
ConnectNeighbors(nn);
index.Add(new KeyCoord(nn.relX, nn.relY, nn.relZ), nn);
stack.Push(nn);
}
}
}
void ConnectNeighbors (LightNode node)
{
LightNode n = null;
if (index.TryGetValue(new KeyCoord(node.relX - 1, node.relY, node.relZ), out n)) {
node.nEast = n;
n.nWest = node;
}
if (index.TryGetValue(new KeyCoord(node.relX + 1, node.relY, node.relZ), out n)) {
node.nWest = n;
n.nEast = node;
}
if (index.TryGetValue(new KeyCoord(node.relX, node.relY - 1, node.relZ), out n)) {
node.nBottom = n;
n.nTop = node;
}
if (index.TryGetValue(new KeyCoord(node.relX, node.relY + 1, node.relZ), out n)) {
node.nTop = n;
n.nBottom = node;
}
if (index.TryGetValue(new KeyCoord(node.relX, node.relY, node.relZ - 1), out n)) {
node.nNorth = n;
n.nSouth = node;
}
if (index.TryGetValue(new KeyCoord(node.relX, node.relY, node.relZ + 1), out n)) {
node.nSouth = n;
n.nNorth = node;
}
}
private void BlankAffected ()
{
foreach (KeyValuePair<KeyCoord, LightNode> entry in index) {
// Don't blank the edge, which we will rebuild our light info from
if (entry.Value.intensity > 0) {
entry.Value.blocklight = (short)entry.Value.block.luminance();
}
}
}
private void SetAffected ()
{
foreach (KeyValuePair<KeyCoord, LightNode> entry in index) {
if (entry.Value.intensity > 0) {
entry.Value.chunk.SetBlockLight(entry.Value.absX, entry.Value.absY, entry.Value.absZ, entry.Value.blocklight);
}
}
}
void RelightAffected ()
{
BlankAffected();
Queue<LightNode> queue = new Queue<LightNode>();
foreach (KeyValuePair<KeyCoord, LightNode> entry in index) {
if (entry.Value.blocklight > 1) {
queue.Enqueue(entry.Value);
}
}
while (queue.Count > 0) {
RelightNode(queue);
}
SetAffected();
}
private void RelightNode (Queue<LightNode> queue)
{
LightNode node = queue.Dequeue();
// Find strongest nearby light source
short blocklight = node.blocklight;
if (node.nEast != null && (node.nEast.blocklight - node.block.transp() - 1) > blocklight) {
blocklight = (short)(node.nEast.blocklight - node.block.transp() - 1);
}
if (node.nWest != null && (node.nWest.blocklight - node.block.transp() - 1) > blocklight) {
blocklight = (short)(node.nEast.blocklight - node.block.transp() - 1);
}
if (node.nTop != null && (node.nTop.blocklight - node.block.transp() - 1) > blocklight) {
blocklight = (short)(node.nTop.blocklight - node.block.transp() - 1);
}
if (node.nBottom != null && (node.nBottom.blocklight - node.block.transp() - 1) > blocklight) {
blocklight = (short)(node.nBottom.blocklight - node.block.transp() - 1);
}
if (node.nNorth != null && (node.nNorth.blocklight - node.block.transp() - 1) > blocklight) {
blocklight = (short)(node.nNorth.blocklight - node.block.transp() - 1);
}
if (node.nSouth != null && (node.nSouth.blocklight - node.block.transp() - 1) > blocklight) {
blocklight = (short)(node.nSouth.blocklight - node.block.transp() - 1);
}
// Select neighbors to invalidate if we increased our light level
if (blocklight > node.blocklight) {
if (node.nEast != null && !node.nEast.dirty && (blocklight - node.nEast.block.transp() - 1) > node.nEast.blocklight) {
node.nEast.dirty = true;
queue.Enqueue(node.nEast);
}
if (node.nWest != null && !node.nWest.dirty && (blocklight - node.nWest.block.transp() - 1) > node.nWest.blocklight) {
node.nWest.dirty = true;
queue.Enqueue(node.nWest);
}
if (node.nTop != null && !node.nTop.dirty && (blocklight - node.nTop.block.transp() - 1) > node.nTop.blocklight) {
node.nTop.dirty = true;
queue.Enqueue(node.nTop);
}
if (node.nBottom != null && !node.nBottom.dirty && (blocklight - node.nBottom.block.transp() - 1) > node.nBottom.blocklight) {
node.nBottom.dirty = true;
queue.Enqueue(node.nBottom);
}
if (node.nNorth != null && !node.nNorth.dirty && (blocklight - node.nNorth.block.transp() - 1) > node.nNorth.blocklight) {
node.nWest.dirty = true;
queue.Enqueue(node.nNorth);
}
if (node.nSouth != null && !node.nSouth.dirty && (blocklight - node.nSouth.block.transp() - 1) > node.nSouth.blocklight) {
node.nWest.dirty = true;
queue.Enqueue(node.nSouth);
}
}
node.blocklight = blocklight;
node.dirty = false;
}
void AugmentAffected ()
{
Queue<LightNode> queue = new Queue<LightNode>();
root.dirty = true;
queue.Enqueue(root);
while (queue.Count > 0) {
//AugmentNode(queue);
}
}
}
}