NBTExplorer/SubstrateCS/Source/Core/BlockLight.cs
2011-10-08 00:01:51 -04:00

885 lines
30 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
namespace Substrate.Core
{
public class BlockLight
{
private struct LightRecord
{
public int x;
public int y;
public int z;
public int str;
public LightRecord (int _x, int _y, int _z, int s)
{
x = _x;
y = _y;
z = _z;
str = s;
}
}
private readonly int _xdim;
private readonly int _ydim;
private readonly int _zdim;
private IBoundedLitBlockCollection _blockset;
// Maintains internal state of multi-block relighting algorithms
private BitArray _lightbit;
private Queue<BlockKey> _update;
public delegate IBoundedLitBlockCollection NeighborLookupHandler (int relx, int rely, int relz);
public event NeighborLookupHandler ResolveNeighbor;
public BlockLight (IBoundedLitBlockCollection blockset)
{
_blockset = blockset;
_xdim = _blockset.XDim;
_ydim = _blockset.YDim;
_zdim = _blockset.ZDim;
_lightbit = new BitArray(_blockset.XDim * 3 * _blockset.ZDim * 3 * _blockset.YDim);
_update = new Queue<BlockKey>();
}
public BlockLight (BlockLight bl)
{
_blockset = bl._blockset;
_xdim = bl._xdim;
_ydim = bl._ydim;
_zdim = bl._zdim;
_lightbit = new BitArray(_blockset.XDim * 3 * _blockset.ZDim * 3 * _blockset.YDim);
_update = new Queue<BlockKey>();
}
public void UpdateBlockLight (int lx, int ly, int lz)
{
BlockKey primary = new BlockKey(lx, ly, lz);
_update.Enqueue(primary);
//BlockInfo info = _blockset.GetInfo(lx, ly, lz);
//if (info.Luminance > BlockInfo.MIN_LUMINANCE || info.TransmitsLight) {
if (ly > 0) {
QueueRelight(new BlockKey(lx, ly - 1, lz));
}
if (ly < _ydim - 1) {
QueueRelight(new BlockKey(lx, ly + 1, lz));
}
QueueRelight(new BlockKey(lx - 1, ly, lz));
QueueRelight(new BlockKey(lx + 1, ly, lz));
QueueRelight(new BlockKey(lx, ly, lz - 1));
QueueRelight(new BlockKey(lx, ly, lz + 1));
//}
UpdateBlockLight();
}
public void UpdateBlockSkyLight (int lx, int ly, int lz)
{
BlockKey primary = new BlockKey(lx, ly, lz);
_update.Enqueue(primary);
UpdateBlockSkyLight();
}
public void UpdateHeightMap (int lx, int ly, int lz)
{
BlockInfo info = _blockset.GetInfo(lx, ly, lz);
int h = Math.Min(ly + 1, _ydim - 1);
int height = _blockset.GetHeight(lx, lz);
if (h < height) {
return;
}
if (h == height && !info.ObscuresLight) {
for (int i = ly - 1; i >= 0; i--) {
BlockInfo info2 = _blockset.GetInfo(lx, i, lz);
if (info2.ObscuresLight) {
_blockset.SetHeight(lx, lz, Math.Min(i + 1, _ydim - 1));
break;
}
}
UpdateBlockSkyLight(lx, h, lz);
}
else if (h > height && info.ObscuresLight) {
_blockset.SetHeight(lx, lz, h);
UpdateBlockSkyLight(lx, h, lz);
}
}
public void RebuildBlockLight ()
{
IBoundedLitBlockCollection[,] chunkMap = LocalBlockLightMap();
// Because the JIT is less intelligent than I hoped
int xdim = _xdim;
int ydim = _ydim;
int zdim = _zdim;
for (int x = 0; x < xdim; x++) {
for (int z = 0; z < zdim; z++) {
for (int y = 0; y < ydim; y++) {
BlockInfo info = _blockset.GetInfo(x, y, z);
if (info.Luminance > 0) {
SpreadBlockLight(chunkMap, x, y, z);
}
}
}
}
}
public void RebuildBlockSkyLight ()
{
IBoundedLitBlockCollection[,] chunkMap = LocalBlockLightMap();
int[,] heightMap = LocalHeightMap(chunkMap);
int xdim = _xdim;
int ydim = _ydim;
int zdim = _zdim;
// Optimization - only need to queue at level of highest neighbor's height
for (int x = 0; x < xdim; x++) {
for (int z = 0; z < zdim; z++) {
int xi = x + xdim;
int zi = z + zdim;
int h = heightMap[xi, zi];
h = Math.Max(h, heightMap[xi, zi - 1]);
h = Math.Max(h, heightMap[xi - 1, zi]);
h = Math.Max(h, heightMap[xi + 1, zi]);
h = Math.Max(h, heightMap[xi, zi + 1]);
for (int y = h + 1; y < ydim; y++) {
_blockset.SetSkyLight(x, y, z, BlockInfo.MAX_LUMINANCE);
}
//QueueRelight(new BlockKey(x, h, z));
SpreadSkyLight(chunkMap, heightMap, x, h, z);
}
}
}
public void RebuildHeightMap ()
{
int xdim = _xdim;
int ydim = _ydim;
int zdim = _zdim;
for (int x = 0; x < xdim; x++) {
for (int z = 0; z < zdim; z++) {
for (int y = ydim - 1; y >= 0; y--) {
BlockInfo info = _blockset.GetInfo(x, y, z);
if (info.ObscuresLight) {
_blockset.SetHeight(x, z, Math.Min(y + 1, ydim - 1));
break;
}
}
}
}
}
public void StitchBlockLight ()
{
IBoundedLitBlockCollection[,] map = LocalBlockLightMap();
if (map[1, 0] != null) {
StitchBlockLight(map[1, 0], BlockCollectionEdge.EAST);
}
if (map[0, 1] != null) {
StitchBlockLight(map[0, 1], BlockCollectionEdge.NORTH);
}
if (map[1, 2] != null) {
StitchBlockLight(map[1, 2], BlockCollectionEdge.WEST);
}
if (map[2, 1] != null) {
StitchBlockLight(map[2, 1], BlockCollectionEdge.SOUTH);
}
}
// TODO: Revise to cache the specified chunk into local map
public void StitchBlockLight (IBoundedLitBlockCollection chunk, BlockCollectionEdge edge)
{
int xdim = _xdim;
int ydim = _ydim;
int zdim = _zdim;
if (chunk.XDim != xdim ||
chunk.YDim != ydim ||
chunk.ZDim != zdim) {
throw new InvalidOperationException("BlockLight must have same dimensions to be stitched");
}
switch (edge) {
case BlockCollectionEdge.EAST:
for (int x = 0; x < xdim; x++) {
for (int y = 0; y < ydim; y++) {
TestBlockLight(chunk, x, y, 0, x, y, zdim - 1);
}
}
break;
case BlockCollectionEdge.NORTH:
for (int z = 0; z < zdim; z++) {
for (int y = 0; y < ydim; y++) {
TestBlockLight(chunk, 0, y, z, xdim - 1, y, z);
}
}
break;
case BlockCollectionEdge.WEST:
for (int x = 0; x < xdim; x++) {
for (int y = 0; y < ydim; y++) {
TestBlockLight(chunk, x, y, zdim - 1, x, y, 0);
}
}
break;
case BlockCollectionEdge.SOUTH:
for (int z = 0; z < zdim; z++) {
for (int y = 0; y < ydim; y++) {
TestBlockLight(chunk, xdim - 1, y, z, 0, y, z);
}
}
break;
}
UpdateBlockLight();
}
public void StitchBlockSkyLight ()
{
IBoundedLitBlockCollection[,] map = LocalBlockLightMap();
if (map[1, 0] != null) {
StitchBlockSkyLight(map[1, 0], BlockCollectionEdge.EAST);
}
if (map[0, 1] != null) {
StitchBlockSkyLight(map[0, 1], BlockCollectionEdge.NORTH);
}
if (map[1, 2] != null) {
StitchBlockSkyLight(map[1, 2], BlockCollectionEdge.WEST);
}
if (map[2, 1] != null) {
StitchBlockSkyLight(map[2, 1], BlockCollectionEdge.SOUTH);
}
}
public void StitchBlockSkyLight (IBoundedLitBlockCollection chunk, BlockCollectionEdge edge)
{
int xdim = _xdim;
int ydim = _ydim;
int zdim = _zdim;
if (chunk.XDim != xdim ||
chunk.YDim != ydim ||
chunk.ZDim != zdim) {
throw new InvalidOperationException("BlockLight must have same dimensions to be stitched");
}
switch (edge) {
case BlockCollectionEdge.EAST:
for (int x = 0; x < xdim; x++) {
for (int y = 0; y < ydim; y++) {
TestSkyLight(chunk, x, y, 0, x, y, zdim - 1);
}
}
break;
case BlockCollectionEdge.NORTH:
for (int z = 0; z < zdim; z++) {
for (int y = 0; y < ydim; y++) {
TestSkyLight(chunk, 0, y, z, xdim - 1, y, z);
}
}
break;
case BlockCollectionEdge.WEST:
for (int x = 0; x < xdim; x++) {
for (int y = 0; y < ydim; y++) {
TestSkyLight(chunk, x, y, zdim - 1, x, y, 0);
}
}
break;
case BlockCollectionEdge.SOUTH:
for (int z = 0; z < zdim; z++) {
for (int y = 0; y < ydim; y++) {
TestSkyLight(chunk, xdim - 1, y, z, 0, y, z);
}
}
break;
}
UpdateBlockSkyLight();
}
private void UpdateBlockLight ()
{
IBoundedLitBlockCollection[,] chunkMap = LocalBlockLightMap();
int xdim = _xdim;
int ydim = _ydim;
int zdim = _zdim;
while (_update.Count > 0) {
BlockKey k = _update.Dequeue();
int index = LightBitmapIndex(k);
_lightbit[index] = false;
int xi = k.x + xdim;
int zi = k.z + zdim;
IBoundedLitBlockCollection cc = chunkMap[xi / xdim, zi / zdim];
if (cc == null) {
continue;
}
int lle = NeighborLight(chunkMap, k.x, k.y, k.z - 1);
int lln = NeighborLight(chunkMap, k.x - 1, k.y, k.z);
int lls = NeighborLight(chunkMap, k.x, k.y, k.z + 1);
int llw = NeighborLight(chunkMap, k.x + 1, k.y, k.z);
int lld = NeighborLight(chunkMap, k.x, k.y - 1, k.z);
int llu = NeighborLight(chunkMap, k.x, k.y + 1, k.z);
int x = xi % xdim;
int y = k.y;
int z = zi % zdim;
int lightval = cc.GetBlockLight(x, y, z);
BlockInfo info = cc.GetInfo(x, y, z);
int light = Math.Max(info.Luminance, 0);
light = Math.Max(light, lle);
light = Math.Max(light, lln);
light = Math.Max(light, lls);
light = Math.Max(light, llw);
light = Math.Max(light, lld);
light = Math.Max(light, llu);
light = Math.Max(light - info.Opacity, 0);
if (light != lightval) {
//Console.WriteLine("Block Light: ({0},{1},{2}) " + lightval + " -> " + light, k.x, k.y, k.z);
cc.SetBlockLight(x, y, z, light);
if (info.TransmitsLight) {
if (k.y > 0) {
QueueRelight(new BlockKey(k.x, k.y - 1, k.z));
}
if (k.y < ydim - 1) {
QueueRelight(new BlockKey(k.x, k.y + 1, k.z));
}
QueueRelight(new BlockKey(k.x - 1, k.y, k.z));
QueueRelight(new BlockKey(k.x + 1, k.y, k.z));
QueueRelight(new BlockKey(k.x, k.y, k.z - 1));
QueueRelight(new BlockKey(k.x, k.y, k.z + 1));
}
}
}
}
private void UpdateBlockSkyLight ()
{
IBoundedLitBlockCollection[,] chunkMap = LocalBlockLightMap();
int xdim = _xdim;
int ydim = _ydim;
int zdim = _zdim;
while (_update.Count > 0) {
BlockKey k = _update.Dequeue();
int index = LightBitmapIndex(k);
_lightbit[index] = false;
int xi = k.x + xdim;
int zi = k.z + zdim;
IBoundedLitBlockCollection cc = chunkMap[xi / xdim, zi / zdim];
if (cc == null) {
continue;
}
int x = xi % xdim;
int y = k.y;
int z = zi % zdim;
int lightval = cc.GetSkyLight(x, y, z);
BlockInfo info = cc.GetInfo(x, y, z);
int light = BlockInfo.MIN_LUMINANCE;
if (cc.GetHeight(x, z) <= y) {
light = BlockInfo.MAX_LUMINANCE;
}
else {
int lle = NeighborSkyLight(chunkMap, k.x, k.y, k.z - 1);
int lln = NeighborSkyLight(chunkMap, k.x - 1, k.y, k.z);
int lls = NeighborSkyLight(chunkMap, k.x, k.y, k.z + 1);
int llw = NeighborSkyLight(chunkMap, k.x + 1, k.y, k.z);
int lld = NeighborSkyLight(chunkMap, k.x, k.y - 1, k.z);
int llu = NeighborSkyLight(chunkMap, k.x, k.y + 1, k.z);
light = Math.Max(light, lle);
light = Math.Max(light, lln);
light = Math.Max(light, lls);
light = Math.Max(light, llw);
light = Math.Max(light, lld);
light = Math.Max(light, llu);
}
light = Math.Max(light - info.Opacity, 0);
if (light != lightval) {
//Console.WriteLine("Block SkyLight: ({0},{1},{2}) " + lightval + " -> " + light, k.x, k.y, k.z);
cc.SetSkyLight(x, y, z, light);
if (info.TransmitsLight) {
if (k.y > 0) {
QueueRelight(new BlockKey(k.x, k.y - 1, k.z));
}
if (k.y < ydim - 1) {
QueueRelight(new BlockKey(k.x, k.y + 1, k.z));
}
QueueRelight(new BlockKey(k.x - 1, k.y, k.z));
QueueRelight(new BlockKey(k.x + 1, k.y, k.z));
QueueRelight(new BlockKey(k.x, k.y, k.z - 1));
QueueRelight(new BlockKey(k.x, k.y, k.z + 1));
}
}
}
}
private void SpreadBlockLight (IBoundedLitBlockCollection[,] chunkMap, int lx, int ly, int lz)
{
BlockInfo primary = _blockset.GetInfo(lx, ly, lz);
int primaryLight = _blockset.GetBlockLight(lx, ly, lz);
int priLum = Math.Max(primary.Luminance - primary.Opacity, 0);
if (primaryLight < priLum) {
_blockset.SetBlockLight(lx, ly, lz, priLum);
}
if (primaryLight > primary.Luminance - 1 && !primary.TransmitsLight) {
return;
}
int xdim = _xdim;
int ydim = _ydim;
int zdim = _zdim;
Queue<LightRecord> spread = new Queue<LightRecord>();
if (ly > 0) {
spread.Enqueue(new LightRecord(lx, ly - 1, lz, primary.Luminance - 1));
}
if (ly < ydim - 1) {
spread.Enqueue(new LightRecord(lx, ly + 1, lz, primary.Luminance - 1));
}
spread.Enqueue(new LightRecord(lx - 1, ly, lz, primary.Luminance - 1));
spread.Enqueue(new LightRecord(lx + 1, ly, lz, primary.Luminance - 1));
spread.Enqueue(new LightRecord(lx, ly, lz - 1, primary.Luminance - 1));
spread.Enqueue(new LightRecord(lx, ly, lz + 1, primary.Luminance - 1));
while (spread.Count > 0) {
LightRecord rec = spread.Dequeue();
int xi = rec.x + xdim;
int zi = rec.z + zdim;
IBoundedLitBlockCollection cc = chunkMap[xi / xdim, zi / zdim];
if (cc == null) {
continue;
}
int x = xi % xdim;
int y = rec.y;
int z = zi % zdim;
BlockInfo info = cc.GetInfo(x, y, z);
int light = cc.GetBlockLight(x, y, z);
int dimStr = Math.Max(rec.str - info.Opacity, 0);
if (dimStr > light) {
cc.SetBlockLight(x, y, z, dimStr);
if (info.TransmitsLight) {
int strength = (info.Opacity > 0) ? dimStr : dimStr - 1;
if (rec.y > 0) {
spread.Enqueue(new LightRecord(rec.x, rec.y - 1, rec.z, strength));
}
if (rec.y < ydim - 1) {
spread.Enqueue(new LightRecord(rec.x, rec.y + 1, rec.z, strength));
}
spread.Enqueue(new LightRecord(rec.x - 1, rec.y, rec.z, strength));
spread.Enqueue(new LightRecord(rec.x + 1, rec.y, rec.z, strength));
spread.Enqueue(new LightRecord(rec.x, rec.y, rec.z - 1, strength));
spread.Enqueue(new LightRecord(rec.x, rec.y, rec.z + 1, strength));
}
}
}
}
private void SpreadSkyLight (IBoundedLitBlockCollection[,] chunkMap, int[,] heightMap, int lx, int ly, int lz)
{
BlockInfo primary = _blockset.GetInfo(lx, ly, lz);
int primaryLight = _blockset.GetSkyLight(lx, ly, lz);
int priLum = Math.Max(BlockInfo.MAX_LUMINANCE - primary.Opacity, 0);
if (primaryLight < priLum) {
_blockset.SetSkyLight(lx, ly, lz, priLum);
}
if (primaryLight > BlockInfo.MAX_LUMINANCE - 1 || !primary.TransmitsLight) {
return;
}
Queue<LightRecord> spread = new Queue<LightRecord>();
int xdim = _xdim;
int ydim = _ydim;
int zdim = _zdim;
int lxi = lx + xdim;
int lzi = lz + zdim;
int strength = (primary.Opacity > 0) ? priLum : priLum - 1;
if (ly > 0) {
if (heightMap[lxi, lzi] > ly - 1) {
spread.Enqueue(new LightRecord(lx, ly - 1, lz, strength));
}
else {
spread.Enqueue(new LightRecord(lx, ly - 1, lz, priLum));
}
}
if (ly < ydim - 1) {
if (heightMap[lxi, lzi] > ly + 1) {
spread.Enqueue(new LightRecord(lx, ly + 1, lz, strength));
}
}
if (heightMap[lxi - 1, lzi] > ly) {
spread.Enqueue(new LightRecord(lx - 1, ly, lz, strength));
}
if (heightMap[lxi + 1, lzi] > ly) {
spread.Enqueue(new LightRecord(lx + 1, ly, lz, strength));
}
if (heightMap[lxi, lzi - 1] > ly) {
spread.Enqueue(new LightRecord(lx, ly, lz - 1, strength));
}
if (heightMap[lxi, lzi + 1] > ly) {
spread.Enqueue(new LightRecord(lx, ly, lz + 1, strength));
}
while (spread.Count > 0) {
LightRecord rec = spread.Dequeue();
int xi = rec.x + xdim;
int zi = rec.z + zdim;
IBoundedLitBlockCollection cc = chunkMap[xi / xdim, zi / zdim];
if (cc == null) {
continue;
}
int x = xi % xdim;
int y = rec.y;
int z = zi % zdim;
BlockInfo info = cc.GetInfo(x, y, z);
int light = cc.GetSkyLight(x, y, z);
int dimStr = Math.Max(rec.str - info.Opacity, 0);
if (dimStr > light) {
cc.SetSkyLight(x, y, z, dimStr);
if (info.TransmitsLight) {
strength = (info.Opacity > 0) ? dimStr : dimStr - 1;
if (rec.y > 0) {
if (heightMap[xi, zi] > rec.y - 1) {
spread.Enqueue(new LightRecord(rec.x, rec.y - 1, rec.z, strength));
}
else {
spread.Enqueue(new LightRecord(rec.x, rec.y - 1, rec.z, dimStr));
}
}
if (rec.y < ydim - 1) {
if (heightMap[xi, zi] > rec.y + 1) {
spread.Enqueue(new LightRecord(rec.x, rec.y + 1, rec.z, strength));
}
}
if (heightMap[xi - 1, zi] > rec.y) {
spread.Enqueue(new LightRecord(rec.x - 1, rec.y, rec.z, strength));
}
if (heightMap[xi + 1, zi] > rec.y) {
spread.Enqueue(new LightRecord(rec.x + 1, rec.y, rec.z, strength));
}
if (heightMap[xi, zi - 1] > rec.y) {
spread.Enqueue(new LightRecord(rec.x, rec.y, rec.z - 1, strength));
}
if (heightMap[xi, zi + 1] > rec.y) {
spread.Enqueue(new LightRecord(rec.x, rec.y, rec.z + 1, strength));
}
}
}
}
}
private int LightBitmapIndex (BlockKey key)
{
int x = key.x + _xdim;
int y = key.y;
int z = key.z + _zdim;
int zstride = _ydim;
int xstride = _zdim * 3 * zstride;
return (x * xstride) + (z * zstride) + y;
}
private void QueueRelight (BlockKey key)
{
if (key.x < -15 || key.x >= 31 || key.z < -15 || key.z >= 31) {
return;
}
int index = LightBitmapIndex(key);
if (!_lightbit[index]) {
_lightbit[index] = true;
_update.Enqueue(key);
}
}
private IBoundedLitBlockCollection LocalChunk (int lx, int ly, int lz)
{
if (ly < 0 || ly >= _ydim) {
return null;
}
if (lx < 0) {
if (lz < 0) {
return OnResolveNeighbor(-1, 0, -1);
}
else if (lz >= _zdim) {
return OnResolveNeighbor(-1, 0, 1);
}
return OnResolveNeighbor(-1, 0, 0);
}
else if (lx >= _xdim) {
if (lz < 0) {
return OnResolveNeighbor(1, 0, -1);
}
else if (lz >= _zdim) {
return OnResolveNeighbor(1, 0, 1);
}
return OnResolveNeighbor(1, 0, 0);
}
else {
if (lz < 0) {
return OnResolveNeighbor(0, 0, -1);
}
else if (lz >= _zdim) {
return OnResolveNeighbor(0, 0, 1);
}
return _blockset;
}
}
private int NeighborLight (IBoundedLitBlockCollection[,] chunkMap, int x, int y, int z)
{
if (y < 0 || y >= _ydim) {
return 0;
}
int xdim = _xdim;
int zdim = _zdim;
int xi = x + xdim;
int zi = z + zdim;
IBoundedLitBlockCollection src = chunkMap[xi / xdim, zi / zdim];
if (src == null) {
return 0;
}
x = xi % xdim;
z = zi % zdim;
BlockInfo info = src.GetInfo(x, y, z);
if (!info.TransmitsLight) {
return info.Luminance;
}
int light = src.GetBlockLight(x, y, z);
return Math.Max((info.Opacity > 0) ? light : light - 1, info.Luminance - 1);
}
private int NeighborSkyLight (IBoundedLitBlockCollection[,] chunkMap, int x, int y, int z)
{
if (y < 0 || y >= _ydim) {
return 0;
}
int xdim = _xdim;
int zdim = _zdim;
int xi = x + xdim;
int zi = z + zdim;
IBoundedLitBlockCollection src = chunkMap[xi / xdim, zi / zdim];
if (src == null) {
return 0;
}
x = xi % xdim;
z = zi % zdim;
BlockInfo info = src.GetInfo(x, y, z);
if (!info.TransmitsLight) {
return BlockInfo.MIN_LUMINANCE;
}
int light = src.GetSkyLight(x, y, z);
return (info.Opacity > 0) ? light : light - 1;
}
private int NeighborHeight (int x, int z)
{
IBoundedLitBlockCollection src = LocalChunk(x, 0, z);
if (src == null) {
return _ydim - 1;
}
x = (x + _xdim * 2) % _xdim;
z = (z + _zdim * 2) % _zdim;
return src.GetHeight(x, z);
}
private void TestBlockLight (IBoundedLitBlockCollection chunk, int x1, int y1, int z1, int x2, int y2, int z2)
{
int light1 = _blockset.GetBlockLight(x1, y1, z1);
int light2 = chunk.GetBlockLight(x2, y2, z2);
int lum1 = _blockset.GetInfo(x1, y1, z1).Luminance;
int lum2 = chunk.GetInfo(x2, y2, z2).Luminance;
int v1 = Math.Max(light1, lum1);
int v2 = Math.Max(light2, lum2);
if (Math.Abs(v1 - v2) > 1) {
QueueRelight(new BlockKey(x1, y1, z1));
}
}
private void TestSkyLight (IBoundedLitBlockCollection chunk, int x1, int y1, int z1, int x2, int y2, int z2)
{
int light1 = _blockset.GetSkyLight(x1, y1, z1);
int light2 = chunk.GetSkyLight(x2, y2, z2);
if (Math.Abs(light1 - light2) > 1) {
QueueRelight(new BlockKey(x1, y1, z1));
}
}
private IBoundedLitBlockCollection[,] LocalBlockLightMap ()
{
IBoundedLitBlockCollection[,] map = new IBoundedLitBlockCollection[3, 3];
map[0, 0] = OnResolveNeighbor(-1, 0, -1);
map[0, 1] = OnResolveNeighbor(-1, 0, 0);
map[0, 2] = OnResolveNeighbor(-1, 0, 1);
map[1, 0] = OnResolveNeighbor(0, 0, -1);
map[1, 1] = _blockset;
map[1, 2] = OnResolveNeighbor(0, 0, 1);
map[2, 0] = OnResolveNeighbor(1, 0, -1);
map[2, 1] = OnResolveNeighbor(1, 0, 0);
map[2, 2] = OnResolveNeighbor(1, 0, 1);
return map;
}
private int[,] LocalHeightMap (IBoundedLitBlockCollection[,] chunkMap)
{
int xdim = _xdim;
int zdim = _zdim;
int[,] map = new int[3 * xdim, 3 * zdim];
for (int xi = 0; xi < 3; xi++) {
int xoff = xi * xdim;
for (int zi = 0; zi < 3; zi++) {
int zoff = zi * zdim;
if (chunkMap[xi, zi] == null) {
continue;
}
for (int x = 0; x < xdim; x++) {
int xx = xoff + x;
for (int z = 0; z < zdim; z++) {
int zz = zoff + z;
map[xx, zz] = chunkMap[xi, zi].GetHeight(x, z);
}
}
}
}
return map;
}
private IBoundedLitBlockCollection OnResolveNeighbor (int relX, int relY, int relZ)
{
if (ResolveNeighbor != null) {
IBoundedLitBlockCollection n = ResolveNeighbor(relX, relY, relZ);
if (n == null) {
return null;
}
if (n.XDim != _xdim ||
n.YDim != _ydim ||
n.ZDim != _zdim) {
throw new InvalidOperationException("Subscriber returned incompatible ILitBlockCollection");
}
return n;
}
return null;
}
}
}