diff --git a/examples/dminer/src/dminer/core/chunk.d b/examples/dminer/src/dminer/core/chunk.d index 9a503187..73ea95fc 100644 --- a/examples/dminer/src/dminer/core/chunk.d +++ b/examples/dminer/src/dminer/core/chunk.d @@ -5,6 +5,8 @@ import dminer.core.blocks; import dminer.core.world; import dlangui.graphics.scene.mesh; + + // Y range: 0..CHUNK_DY-1 immutable int CHUNK_DY = 128; @@ -25,6 +27,10 @@ interface CellVisitor { void visit(World world, ref Position camPosition, Vector3d pos, cell_t cell, int visibleFaces); } +interface ChunkVisitor { + void visit(World world, SmallChunk * chunk); +} + // vertical stack of chunks with same X, Z, and different Y struct ChunkStack { protected int _minChunkY; @@ -135,15 +141,15 @@ struct ChunkStack { struct SmallChunk { protected cell_t[8*8*8] cells; // 512 bytes protected ubyte[8*8*8] sunlight; // 512 bytes - protected ulong[8] opaquePlanesX; // 64 bytes - protected ulong[8] opaquePlanesY; // 64 bytes - protected ulong[8] opaquePlanesZ; // 64 bytes - protected ulong[8] visiblePlanesX; // 64 bytes - protected ulong[8] visiblePlanesY; // 64 bytes - protected ulong[8] visiblePlanesZ; // 64 bytes - protected ulong[8] canPassPlanesX; // 64 bytes - protected ulong[8] canPassPlanesY; // 64 bytes - protected ulong[8] canPassPlanesZ; // 64 bytes + protected ulong[8] opaquePlanesX; // 64 bytes WEST to EAST + protected ulong[8] opaquePlanesY; // 64 bytes DOWN to UP + protected ulong[8] opaquePlanesZ; // 64 bytes NORTH to SOUTH + protected ulong[8] visiblePlanesX; // 64 bytes WEST to EAST + protected ulong[8] visiblePlanesY; // 64 bytes DOWN to UP + protected ulong[8] visiblePlanesZ; // 64 bytes NORTH to SOUTH + protected ulong[8] canPassPlanesX; // 64 bytes WEST to EAST + protected ulong[8] canPassPlanesY; // 64 bytes DOWN to UP + protected ulong[8] canPassPlanesZ; // 64 bytes NORTH to SOUTH //ulong[6][6] canPassFromTo; // 288 bytes SmallChunk * [6] nearChunks; protected Vector3d _pos; @@ -389,6 +395,40 @@ struct SmallChunk { } return count; } + /* + X planes (WEST EAST): z, y + z=0 z=1 z=2 z=3 z=4 z=5 z=6 z=7 + y=0 0 1 2 3 4 5 6 7 + y=1 8 9 10 11 12 13 14 15 + y=2 16 17 18 19 29 21 22 23 + y=3 24 25 26 27 28 29 30 31 + y=4 32 33 34 35 36 37 38 39 + y=5 40 41 42 43 44 45 46 47 + y=6 48 49 50 51 52 53 54 55 + y=7 56 57 58 59 60 61 62 63 + + Y planes (DOWN UP): x, z + x=0 x=1 x=2 x=3 x=4 x=5 x=6 x=7 + z=0 0 1 2 3 4 5 6 7 + z=1 8 9 10 11 12 13 14 15 + z=2 16 17 18 19 29 21 22 23 + z=3 24 25 26 27 28 29 30 31 + z=4 32 33 34 35 36 37 38 39 + z=5 40 41 42 43 44 45 46 47 + z=6 48 49 50 51 52 53 54 55 + z=7 56 57 58 59 60 61 62 63 + + Z planes (NORTH SOUTH): x, y + x=0 x=1 x=2 x=3 x=4 x=5 x=6 x=7 + y=0 0 1 2 3 4 5 6 7 + y=1 8 9 10 11 12 13 14 15 + y=2 16 17 18 19 29 21 22 23 + y=3 24 25 26 27 28 29 30 31 + y=4 32 33 34 35 36 37 38 39 + y=5 40 41 42 43 44 45 46 47 + y=6 48 49 50 51 52 53 54 55 + y=7 56 57 58 59 60 61 62 63 + */ private void generateMasks() { // x planes: z,y for(int x = 0; x < 8; x++) { @@ -707,3 +747,334 @@ void testDirMaskToSpreadMask() { Log.d("Source: \n", generateDirMaskSource()); } + +/// mask for available spread direction for chunk dest visited from camera chunk position origin +ubyte calcSpreadMask(Vector3d dest, Vector3d origin) { + ubyte res = 0; + if (dest.x < origin.x) { + res |= DirMask.MASK_WEST; + } else if (dest.x > origin.x) { + res |= DirMask.MASK_EAST; + } else { + res |= DirMask.MASK_WEST | DirMask.MASK_EAST; + } + if (dest.y < origin.y) { + res |= DirMask.MASK_DOWN; + } else if (dest.y > origin.y) { + res |= DirMask.MASK_UP; + } else { + res |= DirMask.MASK_DOWN | DirMask.MASK_UP; + } + if (dest.z < origin.z) { + res |= DirMask.MASK_NORTH; + } else if (dest.z > origin.z) { + res |= DirMask.MASK_SOUTH; + } else { + res |= DirMask.MASK_NORTH | DirMask.MASK_SOUTH; + } + return res; +} + +/* + Z planes (NORTH SOUTH): x, y + x=0 x=1 x=2 x=3 x=4 x=5 x=6 x=7 + y=0 0 1 2 3 4 5 6 7 + y=1 8 9 10 11 12 13 14 15 + y=2 16 17 18 19 29 21 22 23 + y=3 24 25 26 27 28 29 30 31 + y=4 32 33 34 35 36 37 38 39 + y=5 40 41 42 43 44 45 46 47 + y=6 48 49 50 51 52 53 54 55 + y=7 56 57 58 59 60 61 62 63 +*/ +ulong spreadZPlane(ulong mask, ulong canPassMask, ubyte spreadToDirMask) { + ulong res = mask & canPassMask; + if (!res) + return 0; + if (spreadToDirMask & DirMask.MASK_WEST) { // x-- + res |= ((mask & 0xFEFEFEFEFEFEFEFEL) >> 1) & canPassMask; + } + if (spreadToDirMask & DirMask.MASK_EAST) { // x++ + res |= ((mask & 0x7f7f7f7f7f7f7f7fL) << 1) & canPassMask; + } + if (spreadToDirMask & DirMask.MASK_UP) { // y++ + res |= ((mask & 0x00ffffffffffffffL) << 8) & canPassMask; + } + if (spreadToDirMask & DirMask.MASK_DOWN) { // y-- + res |= ((mask & 0xffffffffffffff00L) >> 8) & canPassMask; + } + return res; +} + + /* + X planes (WEST EAST): z, y + z=0 z=1 z=2 z=3 z=4 z=5 z=6 z=7 + y=0 0 1 2 3 4 5 6 7 + y=1 8 9 10 11 12 13 14 15 + y=2 16 17 18 19 29 21 22 23 + y=3 24 25 26 27 28 29 30 31 + y=4 32 33 34 35 36 37 38 39 + y=5 40 41 42 43 44 45 46 47 + y=6 48 49 50 51 52 53 54 55 + y=7 56 57 58 59 60 61 62 63 + */ +ulong spreadXPlane(ulong mask, ulong canPassMask, ubyte spreadToDirMask) { + ulong res = mask & canPassMask; + if (!res) + return 0; + if (spreadToDirMask & DirMask.MASK_NORTH) { // z-- + res |= ((mask & 0xFEFEFEFEFEFEFEFEL) >> 1) & canPassMask; + } + if (spreadToDirMask & DirMask.MASK_SOUTH) { // z++ + res |= ((mask & 0x7f7f7f7f7f7f7f7fL) << 1) & canPassMask; + } + if (spreadToDirMask & DirMask.MASK_UP) { // y++ + res |= ((mask & 0x00ffffffffffffffL) << 8) & canPassMask; + } + if (spreadToDirMask & DirMask.MASK_DOWN) { // y-- + res |= ((mask & 0xffffffffffffff00L) >> 8) & canPassMask; + } + return res; +} + + /* + + Y planes (DOWN UP): x, z + x=0 x=1 x=2 x=3 x=4 x=5 x=6 x=7 + z=0 0 1 2 3 4 5 6 7 + z=1 8 9 10 11 12 13 14 15 + z=2 16 17 18 19 29 21 22 23 + z=3 24 25 26 27 28 29 30 31 + z=4 32 33 34 35 36 37 38 39 + z=5 40 41 42 43 44 45 46 47 + z=6 48 49 50 51 52 53 54 55 + z=7 56 57 58 59 60 61 62 63 + + */ + +ulong spreadYPlane(ulong mask, ulong canPassMask, ubyte spreadToDirMask) { + ulong res = mask & canPassMask; + if (!res) + return 0; + if (spreadToDirMask & DirMask.MASK_WEST) { // x-- + res |= ((mask & 0xFEFEFEFEFEFEFEFEL) >> 1) & canPassMask; + } + if (spreadToDirMask & DirMask.MASK_EAST) { // x++ + res |= ((mask & 0x7f7f7f7f7f7f7f7fL) << 1) & canPassMask; + } + if (spreadToDirMask & DirMask.MASK_SOUTH) { // z++ + res |= ((mask & 0x00ffffffffffffffL) << 8) & canPassMask; + } + if (spreadToDirMask & DirMask.MASK_NORTH) { // z-- + res |= ((mask & 0xffffffffffffff00L) >> 8) & canPassMask; + } + return res; +} + + /* + Z planes (NORTH SOUTH): x, y + x=0 x=1 x=2 x=3 x=4 x=5 x=6 x=7 + y=0 0 1 2 3 4 5 6 7 + y=1 8 9 10 11 12 13 14 15 + y=2 16 17 18 19 29 21 22 23 + y=3 24 25 26 27 28 29 30 31 + y=4 32 33 34 35 36 37 38 39 + y=5 40 41 42 43 44 45 46 47 + y=6 48 49 50 51 52 53 54 55 + y=7 56 57 58 59 60 61 62 63 + + X planes (WEST EAST): z, y + z=0 z=1 z=2 z=3 z=4 z=5 z=6 z=7 + y=0 0 1 2 3 4 5 6 7 + y=1 8 9 10 11 12 13 14 15 + y=2 16 17 18 19 29 21 22 23 + y=3 24 25 26 27 28 29 30 31 + y=4 32 33 34 35 36 37 38 39 + y=5 40 41 42 43 44 45 46 47 + y=6 48 49 50 51 52 53 54 55 + y=7 56 57 58 59 60 61 62 63 + */ +ulong xPlaneFromZplanes(ref ulong[8] planes, int x) { + ulong res = 0; + for (int z = 0; z < 8; z++) { + ulong n = planes[z]; // one plane == z + n = n >> x; // move to low bit + n &= 0x0101010101010101L; + n = n << z; // move to Z bit + res |= n; + } + return res; +} + + /* + Z planes (NORTH SOUTH): x, y + x=0 x=1 x=2 x=3 x=4 x=5 x=6 x=7 + y=0 0 1 2 3 4 5 6 7 + y=1 8 9 10 11 12 13 14 15 + y=2 16 17 18 19 29 21 22 23 + y=3 24 25 26 27 28 29 30 31 + y=4 32 33 34 35 36 37 38 39 + y=5 40 41 42 43 44 45 46 47 + y=6 48 49 50 51 52 53 54 55 + y=7 56 57 58 59 60 61 62 63 + + Y planes (DOWN UP): x, z + x=0 x=1 x=2 x=3 x=4 x=5 x=6 x=7 + z=0 0 1 2 3 4 5 6 7 + z=1 8 9 10 11 12 13 14 15 + z=2 16 17 18 19 29 21 22 23 + z=3 24 25 26 27 28 29 30 31 + z=4 32 33 34 35 36 37 38 39 + z=5 40 41 42 43 44 45 46 47 + z=6 48 49 50 51 52 53 54 55 + z=7 56 57 58 59 60 61 62 63 + */ +ulong yPlaneFromZplanes(ref ulong[8] planes, int y) { + ulong res = 0; + for (int z = 0; z < 8; z++) { + ulong n = planes[z]; // one plane == z + n = n >> (y * 3); // move to low byte + n &= 0xFF; + n = n << (z * 3); // move to Z position + res |= n; + } + return res; +} + +struct VisibilityCheckChunk { + SmallChunk * chunk; + ulong[6] maskFrom; + ulong[6] maskTo; + ubyte visitedFromDirMask; + ubyte spreadToDirMask; + void setMask(ulong mask, Dir fromDir) { + maskFrom[fromDir] |= mask; + visitedFromDirMask |= (1 << fromDir); + } + /* + Z planes (NORTH SOUTH): x, y + x=0 x=1 x=2 x=3 x=4 x=5 x=6 x=7 + y=0 0 1 2 3 4 5 6 7 + y=1 8 9 10 11 12 13 14 15 + y=2 16 17 18 19 29 21 22 23 + y=3 24 25 26 27 28 29 30 31 + y=4 32 33 34 35 36 37 38 39 + y=5 40 41 42 43 44 45 46 47 + y=6 48 49 50 51 52 53 54 55 + y=7 56 57 58 59 60 61 62 63 + */ + void applyZPlanesTrace(ref ulong[8] planes) { + if (spreadToDirMask & DirMask.MASK_WEST) { // x-- + // X planes (WEST EAST): z, y + maskTo[Dir.WEST] |= xPlaneFromZplanes(planes, 0); + } + if (spreadToDirMask & DirMask.MASK_EAST) { // x++ + // X planes (WEST EAST): z, y + maskTo[Dir.EAST] |= xPlaneFromZplanes(planes, 7); + } + if (spreadToDirMask & DirMask.MASK_DOWN) { // y-- + // Y planes (DOWN UP): x, z + maskTo[Dir.DOWN] |= yPlaneFromZplanes(planes, 0); + } + if (spreadToDirMask & DirMask.MASK_UP) { // y++ + // Y planes (DOWN UP): x, z + maskTo[Dir.UP] |= yPlaneFromZplanes(planes, 7); + } + } + void tracePaths() { + if (auto mask = maskFrom[Dir.NORTH]) { + ulong[8] planes; + for (int i = 7; i >= 0; i--) { + mask = spreadZPlane(mask, chunk.canPassPlanesZ[i], spreadToDirMask); + if (!mask) + break; + planes[i] = mask; + } + maskTo[Dir.NORTH] |= planes[0]; + applyZPlanesTrace(planes); + } else if (auto mask = maskFrom[Dir.SOUTH]) { + ulong[8] planes; + for (int i = 0; i <= 7; i++) { + mask = spreadZPlane(mask, chunk.canPassPlanesZ[i], spreadToDirMask); + if (!mask) + break; + planes[i] = mask; + } + maskTo[Dir.SOUTH] |= planes[7]; + applyZPlanesTrace(planes); + } + if (auto mask = maskFrom[Dir.DOWN]) { + } else if (auto mask = maskFrom[Dir.UP]) { + } + if (auto mask = maskFrom[Dir.WEST]) { + } else if (auto mask = maskFrom[Dir.EAST]) { + } + } +} + +/// Diamond iterator for visibility check +struct VisibilityCheckIterator { + World world; + Vector3d startPos; + SmallChunk * startChunk; + ChunkVisitor visitor; + VisibilityCheckChunk[] plannedChunks; + VisibilityCheckChunk[] visitedChunks; + VisibilityCheckChunk * getOrAddPlannedChunk(SmallChunk * newChunk) { + foreach(ref p; plannedChunks) { + if (p.chunk is newChunk) + return &p; + } + VisibilityCheckChunk plan; + plan.chunk = newChunk; + plannedChunks ~= plan; + return &plannedChunks[$ - 1]; + } + // step 1: plan visiting chunk + void planVisitChunk(int x, int y, int z, Dir fromDir, ulong mask) { + if (!mask) + return; + SmallChunk * newChunk = world.getCellChunk(x, y, z); + if (!newChunk) + return; + VisibilityCheckChunk * plan = getOrAddPlannedChunk(newChunk); + plan.setMask(mask, fromDir); + } + // step 2: visit all planned chunks: move planned to visited; trace paths; plan new visits + void visitPlannedChunks() { + import std.algorithm : swap; + swap(visitedChunks, plannedChunks); + plannedChunks.length = 0; + foreach (ref p; visitedChunks) { + visitor.visit(world, p.chunk); + /// set mask of spread directions + p.spreadToDirMask = calcSpreadMask(p.chunk.position, startPos); + p.tracePaths; + } + } + void start(World world, Vector3d startPos) { + this.world = world; + this.startChunk = world.getCellChunk(startPos.x, startPos.y, startPos.z); + this.startPos = this.startChunk.position; // position aligned by 8 cells + plannedChunks.assumeSafeAppend; + plannedChunks.length = 0; + visitedChunks.assumeSafeAppend; + visitedChunks.length = 0; + } + void visitVisibleChunks(ChunkVisitor visitor) { + this.visitor = visitor; + visitor.visit(world, startChunk); + if (auto mask = startChunk.getSideCanPassToMask(Dir.NORTH)) + planVisitChunk(startPos.x, startPos.y, startPos.z, Dir.NORTH, mask); + if (auto mask = startChunk.getSideCanPassToMask(Dir.SOUTH)) + planVisitChunk(startPos.x, startPos.y, startPos.z, Dir.SOUTH, mask); + if (auto mask = startChunk.getSideCanPassToMask(Dir.WEST)) + planVisitChunk(startPos.x, startPos.y, startPos.z, Dir.WEST, mask); + if (auto mask = startChunk.getSideCanPassToMask(Dir.EAST)) + planVisitChunk(startPos.x, startPos.y, startPos.z, Dir.EAST, mask); + if (auto mask = startChunk.getSideCanPassToMask(Dir.UP)) + planVisitChunk(startPos.x, startPos.y, startPos.z, Dir.UP, mask); + if (auto mask = startChunk.getSideCanPassToMask(Dir.DOWN)) + planVisitChunk(startPos.x, startPos.y, startPos.z, Dir.DOWN, mask); + } +} diff --git a/examples/dminer/src/dminer/core/minetypes.d b/examples/dminer/src/dminer/core/minetypes.d index e1aff614..dd4bb148 100644 --- a/examples/dminer/src/dminer/core/minetypes.d +++ b/examples/dminer/src/dminer/core/minetypes.d @@ -10,13 +10,31 @@ immutable cell_t VISITED_OCCUPIED = 254; immutable cell_t BOUND_BOTTOM = 253; immutable cell_t BOUND_SKY = 252; + +/* + World coordinates + + A UP + | + | / NORTH + | / + WEST |/ + -----------|-----------> EAST + /| + / | + SOUTH/ | + L | + | DOWN +*/ + +/// World direction enum Dir : ubyte { - NORTH = 0, - SOUTH, - EAST, - WEST, - UP, - DOWN, + NORTH = 0, /// z-- + SOUTH, /// z++ + EAST, /// x++ + WEST, /// x-- + UP, /// y++ + DOWN, /// y-- } // 26 direction masks based on Dir @@ -64,10 +82,15 @@ struct Vector2d { immutable Vector2d ZERO2 = Vector2d(0, 0); +/// Integer 3d vector: x,y,z struct Vector3d { + /// WEST-EAST int x; + /// DOWN-UP int y; + /// NORTH-SOUTH int z; + this(int xx, int yy, int zz) { x = xx; y = yy; @@ -249,152 +272,6 @@ alias CellArray = Array!(cell_t); alias Vector2dArray = Array!(Vector2d); alias Vector3dArray = Array!(Vector3d); -/* -/// array with support of both positive and negative indexes -struct InfiniteArray(T) { -private: - T[] dataPlus; - T[] dataMinus; - int minIdx; - int maxIdx; -public: - @property int minIndex() { return minIdx; } - @property int maxIndex() { return maxIdx; } - void disposeFunction(T p) { - destroy(p); - } - ~this() { - foreach(p; dataPlus) - if (p !is T.init) - disposeFunction(p); - foreach(p; dataMinus) - if (p !is T.init) - disposeFunction(p); - } - T get(int index) { - if (index >= 0) { - if (index >= maxIdx) - return T.init; - return dataPlus[index]; - } else { - if (index <= minIdx) - return T.init; - return dataMinus[-index]; - } - } - void set(int index, T value) { - if (index >= 0) { - if (index >= maxIdx) { - // extend array - if (index <= dataPlus.length) { - int oldsize = dataPlus.length; - int newsize = 1024; - while (newsize <= index) - newsize <<= 1; - dataPlus.length = newsize; - dataPlus.assumeSafeAppend; - for(int i = oldsize; i < newsize; i++) - dataPlus[i] = T.init; - } - maxIdx = index + 1; - } - if (dataPlus[index] !is T.init && dataPlus[index] !is value) - disposeFunction(dataPlus[index]); - dataPlus[index] = value; - } else { - if (index <= minIdx) { - // extend array - if (-index <= dataMinus.length) { - int oldsize = dataMinus.length; - int newsize = 1024; - while (newsize <= -index) - newsize <<= 1; - dataMinus.length = newsize; - dataMinus.assumeSafeAppend; - for(int i = oldsize; i < newsize; i++) - dataMinus[i] = T.init; - } - maxIdx = index - 1; - } - if (dataMinus[-index] !is T.init && dataMinus[-index] !is value) - disposeFunction(dataMinus[-index]); - dataMinus[-index] = value; - } - } -} - -struct InfiniteMatrix(T) { -private: - int _size = 0; - int _sizeShift = 0; - int _sizeShiftMul2 = 0; - int _sizeMask = 0; - int _invSizeMask = 0; - T[] _data; - void resize(int newSizeShift) { - int newSize = (1<= newSize || y < -newSize || y >= newSize) { - // destory: // outside new size - destroy(v); - } else { - // move - newdata[((y + newSize) << (newSizeShift + 1)) | (x + newSize)] = v; - } - } - } - _data = newdata; - _size = newSize; - _sizeShift = newSizeShift; - _sizeShiftMul2 = _sizeShift + 1; - _sizeMask = (1 << _sizeShiftMul2) - 1; - _invSizeMask = ~_sizeMask; - } - int calcIndex(int x, int y) { - return (y << _sizeShiftMul2) + x; - } -public: - @property int size() { return _size; } - T get(int x, int y) { - if (!_data) - return null; - x += _size; - y += _size; - if (!((x | y) & ~_sizeMask)) { - return _data.ptr[(y << _sizeShiftMul2) + x]; //calcIndex(x, y) - } - return null; - } - void set(int x, int y, T v) { - if (x < -_size || x >= _size || y < -_size || y >= _size) { - int newSizeShift = _sizeShift < 6 ? 6 : _sizeShift + 1; - for (; ;newSizeShift++) { - int sz = 1 << newSizeShift; - if (x < -sz || x >= sz || y < -sz || y >= sz) - continue; - break; - } - resize(newSizeShift); - } - x += _size; - y += _size; - int index = calcIndex(x, y); - if (_data.ptr[index]) - destroy(_data.ptr[index]); - _data.ptr[index] = v; - } - ~this() { - foreach(ref v; _data) - if (v) - destroy(v); - } -} -*/ struct Position { diff --git a/examples/dminer/src/dminer/core/world.d b/examples/dminer/src/dminer/core/world.d index 3e21db6c..181e2b23 100644 --- a/examples/dminer/src/dminer/core/world.d +++ b/examples/dminer/src/dminer/core/world.d @@ -219,10 +219,6 @@ private: int maxVisibleRange = MAX_VIEW_DISTANCE; } -interface ChunkVisitor { - void visit(World world, SmallChunk * chunk); -} - struct VisitorCell { SmallChunk * chunk; ulong[6] accessible; diff --git a/examples/dminer/src/minermain.d b/examples/dminer/src/minermain.d index 70d23ad7..7afa8eda 100644 --- a/examples/dminer/src/minermain.d +++ b/examples/dminer/src/minermain.d @@ -430,6 +430,7 @@ class UiWidget : VerticalLayout { //, CellVisitor } void updatePositionMessage() { + import std.string : format; Widget w = childById("lblPosition"); string dir = _world.camPosition.direction.dir.to!string; dstring s = format("pos(%d,%d) h=%d fps:%d %s [F]lying: %s [U]pdateMesh: %s", _world.camPosition.pos.x, _world.camPosition.pos.z, _world.camPosition.pos.y,