new chunks format

This commit is contained in:
Vadim Lopatin 2016-04-26 15:48:21 +03:00
parent 29db6b73a5
commit e31fd88107
4 changed files with 310 additions and 158 deletions

View File

@ -209,6 +209,7 @@
<Folder name="dminer"> <Folder name="dminer">
<Folder name="core"> <Folder name="core">
<File path="src\dminer\core\blocks.d" /> <File path="src\dminer\core\blocks.d" />
<File path="src\dminer\core\chunk.d" />
<File path="src\dminer\core\generators.d" /> <File path="src\dminer\core\generators.d" />
<File path="src\dminer\core\minetypes.d" /> <File path="src\dminer\core\minetypes.d" />
<File path="src\dminer\core\terrain.d" /> <File path="src\dminer\core\terrain.d" />

View File

@ -4,6 +4,7 @@ import dminer.core.minetypes;
import dminer.core.blocks; import dminer.core.blocks;
import dminer.core.world; import dminer.core.world;
import dminer.core.terrain; import dminer.core.terrain;
import dminer.core.chunk;
__gshared static short[] TERRAIN_INIT_DATA = [ __gshared static short[] TERRAIN_INIT_DATA = [

View File

@ -2,6 +2,7 @@ module dminer.core.world;
import dminer.core.minetypes; import dminer.core.minetypes;
import dminer.core.blocks; import dminer.core.blocks;
import dminer.core.chunk;
version (Android) { version (Android) {
const int MAX_VIEW_DISTANCE_BITS = 6; const int MAX_VIEW_DISTANCE_BITS = 6;
@ -10,180 +11,328 @@ version (Android) {
} }
const int MAX_VIEW_DISTANCE = (1 << MAX_VIEW_DISTANCE_BITS); const int MAX_VIEW_DISTANCE = (1 << MAX_VIEW_DISTANCE_BITS);
// Layer is 16x16 (CHUNK_DX_SHIFT x CHUNK_DX_SHIFT) cells
immutable int CHUNK_DX_SHIFT = 4;
immutable int CHUNK_DX = (1<<CHUNK_DX_SHIFT);
immutable int CHUNK_DX_MASK = (CHUNK_DX - 1);
// Y range: 0..CHUNK_DY-1 static if (USE_NEW_WORLD_IMPL) {
immutable int CHUNK_DY_SHIFT = 7;
immutable int CHUNK_DY = (1<<CHUNK_DY_SHIFT);
immutable int CHUNK_DY_MASK = (CHUNK_DY - 1);
immutable int CHUNK_DY_INV_MASK = ~CHUNK_DY_MASK;
//extern bool HIGHLIGHT_GRID; class World {
// Layer is 256x16x16 CHUNK_DY layers = CHUNK_DY * (CHUNK_DX_SHIFT x CHUNK_DX_SHIFT) cells this() {
struct ChunkLayer { _camPosition = Position(Vector3d(0, 13, 0), Vector3d(0, 0, 1));
cell_t[CHUNK_DX * CHUNK_DX] cells;
cell_t* ptr(int x, int z) {
return &cells.ptr[(z << CHUNK_DX_SHIFT) + x];
}
cell_t get(int x, int z) {
return cells.ptr[(z << CHUNK_DX_SHIFT) + x];
}
void set(int x, int z, cell_t cell) {
cells.ptr[(z << CHUNK_DX_SHIFT) + x] = cell;
}
}
struct Chunk {
private:
ChunkLayer*[CHUNK_DY] layers;
int bottomLayer = - 1;
int topLayer = -1;
int topNonEmpty = -1;
public:
~this() {
for (int i = 0; i < CHUNK_DY; i++)
if (layers[i])
destroy(layers[i]);
}
int getMinLayer() { return bottomLayer; }
int getMaxLayer() { return topLayer; }
int getTopNonEmpty() { return topNonEmpty; }
void updateMinMaxLayer(ref int minLayer, ref int maxLayer) {
if (minLayer == -1 || minLayer > bottomLayer)
minLayer = bottomLayer;
if (maxLayer == -1 || maxLayer < topLayer)
maxLayer = topLayer;
}
cell_t get(int x, int y, int z) {
//if (!this)
// return NO_CELL;
ChunkLayer * layer = layers[y & CHUNK_DY_MASK];
if (!layer)
return NO_CELL;
return layer.get(x & CHUNK_DX_MASK, z & CHUNK_DY_MASK);
}
/// get, x, y, z are already checked for bounds
cell_t getNoCheck(int x, int y, int z) {
ChunkLayer * layer = layers.ptr[y];
if (!layer) // likely
return NO_CELL;
return layer.cells.ptr[(z << CHUNK_DX_SHIFT) + x]; // inlined return layer.get(x, z);
}
void set(int x, int y, int z, cell_t cell) {
int layerIndex = y & CHUNK_DY_MASK;
ChunkLayer * layer = layers.ptr[layerIndex];
if (!layer) {
layer = new ChunkLayer();
layers.ptr[layerIndex] = layer;
if (topLayer == -1 || topLayer < layerIndex)
topLayer = layerIndex;
if (bottomLayer == -1 || bottomLayer > layerIndex)
bottomLayer = layerIndex;
} }
layer.set(x & CHUNK_DX_MASK, z & CHUNK_DY_MASK, cell); ~this() {
if (cell != BlockId.air && topNonEmpty < y)
topNonEmpty = y;
}
/// srcpos coords x, z are in chunk bounds
//void getCells(Vector3d srcpos, Vector3d dstpos, Vector3d size, VolumeData & buf);
}
alias ChunkMatrix = InfiniteMatrix!(Chunk *);
/// Voxel World
class World {
private:
Position _camPosition;
int maxVisibleRange = MAX_VIEW_DISTANCE;
ChunkMatrix chunks;
DiamondVisitor visitorHelper;
public:
this() {
_camPosition = Position(Vector3d(0, 13, 0), Vector3d(0, 0, 1));
}
~this() {
}
@property final ref Position camPosition() { return _camPosition; }
final cell_t getCell(int x, int y, int z) {
if (!(y & CHUNK_DY_INV_MASK)) {
if (Chunk * p = chunks.get(x >> CHUNK_DX_SHIFT, z >> CHUNK_DX_SHIFT))
return p.getNoCheck(x & CHUNK_DX_MASK, y, z & CHUNK_DX_MASK);
return NO_CELL;
} }
// y out of bounds @property final ref Position camPosition() { return _camPosition; }
if (y < 0)
return BOUND_BOTTOM;
//if (y >= CHUNK_DY)
else
return BOUND_SKY;
}
final bool isOpaque(int x, int y, int z) {
cell_t cell = getCell(x, y, z);
return BLOCK_TYPE_OPAQUE.ptr[cell] && cell != BOUND_SKY;
}
/// get max Y position of non-empty cell in region (x +- size, z +- size) protected ChunkStack*[CHUNKS_X * 2 * CHUNKS_Z * 2] _chunkStacks;
int regionHeight(int x, int z, int size) { //pragma(msg, "stack pointers array size, Kb:");
int top = -1; //pragma(msg, _chunkStacks.sizeof / 1024);
int delta = size / CHUNK_DX + 1; final cell_t getCell(int x, int y, int z) {
for (int dx = x - delta; dx <= x + delta; dx += CHUNK_DX) { int chunkx = (x >> 3) + CHUNKS_X;
for (int dz = z - delta; dz <= z + delta; dz += CHUNK_DX) { int chunkz = (z >> 3) + CHUNKS_Z;
if (Chunk * p = chunks.get(dx >> CHUNK_DX_SHIFT, dz >> CHUNK_DX_SHIFT)) if ((chunkx & (~CHUNKS_X_MASK)) || (chunkz & (~CHUNKS_Z_MASK)))
if (top < p.getTopNonEmpty) return 0; // out of bounds x,z
top = p.getTopNonEmpty; int index = chunkx + (chunkz << (CHUNKS_BITS_X + 1));
if (ChunkStack * stack = _chunkStacks[index]) {
int chunkY = (y >> 3);
if (SmallChunk * chunk = stack.get(chunkY))
return chunk.getCell(x, y, z);
}
return 0;
}
/// get chunk stack for cell by world cell coordinates x, z
ChunkStack * getCellChunkStack(int x, int z) {
int chunkx = (x >> 3) + CHUNKS_X;
int chunkz = (z >> 3) + CHUNKS_Z;
if ((chunkx & (~CHUNKS_X_MASK)) || (chunkz & (~CHUNKS_Z_MASK)))
return null; // out of bounds x,z
int index = chunkx + (chunkz << (CHUNKS_BITS_X + 1));
return _chunkStacks[index];
}
/// get chunk by chunkx = x / 8 + CHUNKS_X, chunky = y / 8, chunkz = z / 8 + CHUNKS_Z
SmallChunk * getChunk(int chunkx, int chunky, int chunkz) {
if ((chunkx & (~CHUNKS_X_MASK)) || (chunkz & (~CHUNKS_Z_MASK)))
return null; // out of bounds x,z
int index = chunkx + (chunkz << (CHUNKS_BITS_X + 1));
if (ChunkStack * stack = _chunkStacks[index])
return stack.get(chunky);
return null;
}
/// get chunk for cell by world cell coordinates x, y, z
SmallChunk * getCellChunk(int x, int y, int z) {
int chunkx = (x >> 3) + CHUNKS_X;
int chunkz = (z >> 3) + CHUNKS_Z;
if ((chunkx & (~CHUNKS_X_MASK)) || (chunkz & (~CHUNKS_Z_MASK)))
return null; // out of bounds x,z
int index = chunkx + (chunkz << (CHUNKS_BITS_X + 1));
int chunky = (y >> 3);
if (ChunkStack * stack = _chunkStacks[index])
return stack.get(chunky);
return null;
}
final void setCell(int x, int y, int z, cell_t value) {
int chunkx = (x >> 3) + CHUNKS_X;
int chunkz = (z >> 3) + CHUNKS_Z;
if ((chunkx & (~CHUNKS_X_MASK)) || (chunkz & (~CHUNKS_Z_MASK)))
return; // out of bounds x,z
int index = chunkx + (chunkz << (CHUNKS_BITS_X + 1));
ChunkStack * stack = _chunkStacks[index];
SmallChunk * chunk;
if (stack) {
int chunkY = (y >> 3);
chunk = stack.get(chunkY);
if (chunk)
chunk.setCell(x, y, z, value);
else {
// create chunk
if (!value)
return; // don't create chunk for 0
chunk = SmallChunk.alloc();
stack.set(chunkY, chunk);
chunk.setCell(x, y, z, value);
}
} else {
if (!value)
return; // don't create chunk for 0
stack = new ChunkStack();
_chunkStacks[index] = stack;
int chunkY = (y >> 3);
chunk = SmallChunk.alloc();
stack.set(chunkY, chunk);
chunk.setCell(x, y, z, value);
} }
} }
return top;
}
final void setCell(int x, int y, int z, cell_t value) {
int chunkx = x >> CHUNK_DX_SHIFT;
int chunkz = z >> CHUNK_DX_SHIFT;
Chunk * p = chunks.get(chunkx, chunkz); void setCellRange(Vector3d pos, Vector3d sz, cell_t value) {
if (!p) { for (int x = 0; x < sz.x; x++)
p = new Chunk(); for (int y = 0; y < sz.y; y++)
chunks.set(chunkx, chunkz, p); for (int z = 0; z < sz.z; z++)
setCell(pos.x + x, pos.y + y, pos.z + z, value);
} }
p.set(x & CHUNK_DX_MASK, y, z & CHUNK_DX_MASK, value);
}
void setCellRange(Vector3d pos, Vector3d sz, cell_t value) { final bool isOpaque(int x, int y, int z) {
for (int x = 0; x < sz.x; x++) cell_t cell = getCell(x, y, z);
for (int y = 0; y < sz.y; y++) return BLOCK_TYPE_OPAQUE.ptr[cell] && cell != BOUND_SKY;
for (int z = 0; z < sz.z; z++) }
setCell(pos.x + x, pos.y + y, pos.z + z, value);
}
bool canPass(Vector3d pos) { bool canPass(Vector3d pos) {
return canPass(Vector3d(pos.x - 2, pos.y - 3, pos.z - 2), Vector3d(4, 5, 4)); return canPass(Vector3d(pos.x - 2, pos.y - 3, pos.z - 2), Vector3d(4, 5, 4));
} }
bool canPass(Vector3d pos, Vector3d size) { bool canPass(Vector3d pos, Vector3d size) {
for (int x = 0; x <= size.x; x++) for (int x = 0; x <= size.x; x++)
for (int z = 0; z <= size.z; z++) for (int z = 0; z <= size.z; z++)
for (int y = 0; y < size.y; y++) { for (int y = 0; y < size.y; y++) {
if (isOpaque(pos.x + x, pos.y + y, pos.z + z)) if (isOpaque(pos.x + x, pos.y + y, pos.z + z))
return false; return false;
}
return true;
}
final void visitVisibleCells(ref Position position, CellVisitor visitor) {
visitorHelper.init(this,
&position,
visitor);
visitorHelper.visitAll(maxVisibleRange);
}
/// get max Y position of non-empty cell in region (x +- size, z +- size)
int regionHeight(int x, int z, int size) {
int top = -1;
int delta = size / 8 + 1;
for (int dx = x - delta; dx <= x + delta; dx += 8) {
for (int dz = z - delta; dz <= z + delta; dz += 8) {
if (ChunkStack * stack = getCellChunkStack(x, z)) {
if (top < stack.topNonEmptyY)
top = stack.topNonEmptyY;
}
} }
return true; }
return top;
}
private:
Position _camPosition;
int maxVisibleRange = MAX_VIEW_DISTANCE;
DiamondVisitor visitorHelper;
} }
final void visitVisibleCells(ref Position position, CellVisitor visitor) {
visitorHelper.init(this, } else {
&position,
visitor);
visitorHelper.visitAll(maxVisibleRange); // Layer is 16x16 (CHUNK_DX_SHIFT x CHUNK_DX_SHIFT) cells
immutable int CHUNK_DX_SHIFT = 4;
immutable int CHUNK_DX = (1<<CHUNK_DX_SHIFT);
immutable int CHUNK_DX_MASK = (CHUNK_DX - 1);
//extern bool HIGHLIGHT_GRID;
// Layer is 256x16x16 CHUNK_DY layers = CHUNK_DY * (CHUNK_DX_SHIFT x CHUNK_DX_SHIFT) cells
struct ChunkLayer {
cell_t[CHUNK_DX * CHUNK_DX] cells;
cell_t* ptr(int x, int z) {
return &cells.ptr[(z << CHUNK_DX_SHIFT) + x];
}
cell_t get(int x, int z) {
return cells.ptr[(z << CHUNK_DX_SHIFT) + x];
}
void set(int x, int z, cell_t cell) {
cells.ptr[(z << CHUNK_DX_SHIFT) + x] = cell;
}
}
struct Chunk {
private:
ChunkLayer*[CHUNK_DY] layers;
int bottomLayer = - 1;
int topLayer = -1;
int topNonEmpty = -1;
public:
~this() {
for (int i = 0; i < CHUNK_DY; i++)
if (layers[i])
destroy(layers[i]);
}
int getMinLayer() { return bottomLayer; }
int getMaxLayer() { return topLayer; }
int getTopNonEmpty() { return topNonEmpty; }
void updateMinMaxLayer(ref int minLayer, ref int maxLayer) {
if (minLayer == -1 || minLayer > bottomLayer)
minLayer = bottomLayer;
if (maxLayer == -1 || maxLayer < topLayer)
maxLayer = topLayer;
}
cell_t get(int x, int y, int z) {
//if (!this)
// return NO_CELL;
ChunkLayer * layer = layers[y & CHUNK_DY_MASK];
if (!layer)
return NO_CELL;
return layer.get(x & CHUNK_DX_MASK, z & CHUNK_DY_MASK);
}
/// get, x, y, z are already checked for bounds
cell_t getNoCheck(int x, int y, int z) {
ChunkLayer * layer = layers.ptr[y];
if (!layer) // likely
return NO_CELL;
return layer.cells.ptr[(z << CHUNK_DX_SHIFT) + x]; // inlined return layer.get(x, z);
}
void set(int x, int y, int z, cell_t cell) {
int layerIndex = y & CHUNK_DY_MASK;
ChunkLayer * layer = layers.ptr[layerIndex];
if (!layer) {
layer = new ChunkLayer();
layers.ptr[layerIndex] = layer;
if (topLayer == -1 || topLayer < layerIndex)
topLayer = layerIndex;
if (bottomLayer == -1 || bottomLayer > layerIndex)
bottomLayer = layerIndex;
}
layer.set(x & CHUNK_DX_MASK, z & CHUNK_DY_MASK, cell);
if (cell != BlockId.air && topNonEmpty < y)
topNonEmpty = y;
}
/// srcpos coords x, z are in chunk bounds
//void getCells(Vector3d srcpos, Vector3d dstpos, Vector3d size, VolumeData & buf);
}
alias ChunkMatrix = InfiniteMatrix!(Chunk *);
/// Voxel World
class World {
private:
Position _camPosition;
int maxVisibleRange = MAX_VIEW_DISTANCE;
ChunkMatrix chunks;
DiamondVisitor visitorHelper;
public:
this() {
_camPosition = Position(Vector3d(0, 13, 0), Vector3d(0, 0, 1));
}
~this() {
}
@property final ref Position camPosition() { return _camPosition; }
final cell_t getCell(int x, int y, int z) {
if (!(y & CHUNK_DY_INV_MASK)) {
if (Chunk * p = chunks.get(x >> CHUNK_DX_SHIFT, z >> CHUNK_DX_SHIFT))
return p.getNoCheck(x & CHUNK_DX_MASK, y, z & CHUNK_DX_MASK);
return NO_CELL;
}
// y out of bounds
if (y < 0)
return BOUND_BOTTOM;
//if (y >= CHUNK_DY)
else
return BOUND_SKY;
}
final bool isOpaque(int x, int y, int z) {
cell_t cell = getCell(x, y, z);
return BLOCK_TYPE_OPAQUE.ptr[cell] && cell != BOUND_SKY;
}
final void setCell(int x, int y, int z, cell_t value) {
int chunkx = x >> CHUNK_DX_SHIFT;
int chunkz = z >> CHUNK_DX_SHIFT;
Chunk * p = chunks.get(chunkx, chunkz);
if (!p) {
p = new Chunk();
chunks.set(chunkx, chunkz, p);
}
p.set(x & CHUNK_DX_MASK, y, z & CHUNK_DX_MASK, value);
}
void setCellRange(Vector3d pos, Vector3d sz, cell_t value) {
for (int x = 0; x < sz.x; x++)
for (int y = 0; y < sz.y; y++)
for (int z = 0; z < sz.z; z++)
setCell(pos.x + x, pos.y + y, pos.z + z, value);
}
/// get max Y position of non-empty cell in region (x +- size, z +- size)
int regionHeight(int x, int z, int size) {
int top = -1;
int delta = size / CHUNK_DX + 1;
for (int dx = x - delta; dx <= x + delta; dx += CHUNK_DX) {
for (int dz = z - delta; dz <= z + delta; dz += CHUNK_DX) {
if (Chunk * p = chunks.get(dx >> CHUNK_DX_SHIFT, dz >> CHUNK_DX_SHIFT))
if (top < p.getTopNonEmpty)
top = p.getTopNonEmpty;
}
}
return top;
}
bool canPass(Vector3d pos) {
return canPass(Vector3d(pos.x - 2, pos.y - 3, pos.z - 2), Vector3d(4, 5, 4));
}
bool canPass(Vector3d pos, Vector3d size) {
for (int x = 0; x <= size.x; x++)
for (int z = 0; z <= size.z; z++)
for (int y = 0; y < size.y; y++) {
if (isOpaque(pos.x + x, pos.y + y, pos.z + z))
return false;
}
return true;
}
final void visitVisibleCells(ref Position position, CellVisitor visitor) {
visitorHelper.init(this,
&position,
visitor);
visitorHelper.visitAll(maxVisibleRange);
}
} }
} }

View File

@ -29,6 +29,7 @@ import dminer.core.minetypes;
import dminer.core.blocks; import dminer.core.blocks;
import dminer.core.world; import dminer.core.world;
import dminer.core.generators; import dminer.core.generators;
import dminer.core.chunk;
mixin APP_ENTRY_POINT; mixin APP_ENTRY_POINT;