1 module dminer.core.world; 2 3 import dminer.core.minetypes; 4 import dminer.core.blocks; 5 import dminer.core.chunk; 6 7 version (Android) { 8 const int MAX_VIEW_DISTANCE = 60; 9 } else { 10 const int MAX_VIEW_DISTANCE = 120; 11 } 12 13 14 class World { 15 16 this() { 17 _camPosition = Position(Vector3d(0, 13, 0), Vector3d(0, 0, 1)); 18 } 19 ~this() { 20 clear(); 21 } 22 void clear() { 23 for(int index = 0; index < _chunkStacks.length; index++) { 24 if (_chunkStacks[index]) { 25 destroy(_chunkStacks[index]); 26 _chunkStacks[index] = null; 27 } 28 } 29 } 30 @property final ref Position camPosition() { return _camPosition; } 31 32 33 protected ChunkStack*[CHUNKS_X * 2 * CHUNKS_Z * 2] _chunkStacks; 34 35 //pragma(msg, "stack pointers array size, Kb:"); 36 //pragma(msg, _chunkStacks.sizeof / 1024); 37 final cell_t getCell(int x, int y, int z) { 38 int chunkx = (x >> 3) + CHUNKS_X; 39 int chunkz = (z >> 3) + CHUNKS_Z; 40 if ((chunkx & (~CHUNKS_X_MASK)) || (chunkz & (~CHUNKS_Z_MASK))) 41 return 0; // out of bounds x,z 42 int index = chunkx + (chunkz << (CHUNKS_BITS_X + 1)); 43 if (ChunkStack * stack = _chunkStacks[index]) { 44 int chunkY = (y >> 3); 45 if (SmallChunk * chunk = stack.get(chunkY)) 46 return chunk.getCell(x, y, z); 47 } 48 return 0; 49 } 50 /// get chunk stack for cell by world cell coordinates x, z 51 ChunkStack * getCellChunkStack(int x, int z) { 52 int chunkx = (x >> 3) + CHUNKS_X; 53 int chunkz = (z >> 3) + CHUNKS_Z; 54 if ((chunkx & (~CHUNKS_X_MASK)) || (chunkz & (~CHUNKS_Z_MASK))) 55 return null; // out of bounds x,z 56 int index = chunkx + (chunkz << (CHUNKS_BITS_X + 1)); 57 return _chunkStacks[index]; 58 } 59 /// get chunk by chunkx = x / 8 + CHUNKS_X, chunky = y / 8, chunkz = z / 8 + CHUNKS_Z 60 SmallChunk * getChunk(int chunkx, int chunky, int chunkz) { 61 if ((chunkx & (~CHUNKS_X_MASK)) || (chunkz & (~CHUNKS_Z_MASK))) 62 return null; // out of bounds x,z 63 int index = chunkx + (chunkz << (CHUNKS_BITS_X + 1)); 64 if (ChunkStack * stack = _chunkStacks[index]) 65 return stack.get(chunky); 66 return null; 67 } 68 /// get chunk for cell by world cell coordinates x, y, z 69 SmallChunk * getCellChunk(int x, int y, int z) { 70 int chunkx = (x >> 3) + CHUNKS_X; 71 int chunkz = (z >> 3) + CHUNKS_Z; 72 if ((chunkx & (~CHUNKS_X_MASK)) || (chunkz & (~CHUNKS_Z_MASK))) 73 return null; // out of bounds x,z 74 int index = chunkx + (chunkz << (CHUNKS_BITS_X + 1)); 75 int chunky = (y >> 3); 76 if (ChunkStack * stack = _chunkStacks[index]) 77 return stack.get(chunky); 78 return null; 79 } 80 81 private void updateNearChunks(SmallChunk * thisChunk, int x, int y, int z) { 82 // UP 83 SmallChunk * chunkAbove = getCellChunk(x, y + 8, z); 84 thisChunk.nearChunks[Dir.UP] = chunkAbove; 85 if (chunkAbove) 86 chunkAbove.nearChunks[Dir.DOWN] = thisChunk; 87 // DOWN 88 SmallChunk * chunkBelow = getCellChunk(x, y - 8, z); 89 thisChunk.nearChunks[Dir.DOWN] = chunkBelow; 90 if (chunkBelow) 91 chunkBelow.nearChunks[Dir.UP] = thisChunk; 92 // WEST 93 SmallChunk * chunkWest = getCellChunk(x - 8, y, z); 94 thisChunk.nearChunks[Dir.WEST] = chunkWest; 95 if (chunkWest) 96 chunkWest.nearChunks[Dir.EAST] = thisChunk; 97 // EAST 98 SmallChunk * chunkEast = getCellChunk(x + 8, y, z); 99 thisChunk.nearChunks[Dir.EAST] = chunkEast; 100 if (chunkEast) 101 chunkEast.nearChunks[Dir.WEST] = thisChunk; 102 // NORTH 103 SmallChunk * chunkNorth = getCellChunk(x, y, z - 8); 104 thisChunk.nearChunks[Dir.NORTH] = chunkNorth; 105 if (chunkNorth) 106 chunkNorth.nearChunks[Dir.SOUTH] = thisChunk; 107 // SOUTH 108 SmallChunk * chunkSouth = getCellChunk(x, y, z + 8); 109 thisChunk.nearChunks[Dir.SOUTH] = chunkSouth; 110 if (chunkSouth) 111 chunkSouth.nearChunks[Dir.NORTH] = thisChunk; 112 } 113 114 final void setCell(int x, int y, int z, cell_t value) { 115 int chunkx = (x >> 3) + CHUNKS_X; 116 int chunkz = (z >> 3) + CHUNKS_Z; 117 if ((chunkx & (~CHUNKS_X_MASK)) || (chunkz & (~CHUNKS_Z_MASK))) 118 return; // out of bounds x,z 119 int index = chunkx + (chunkz << (CHUNKS_BITS_X + 1)); 120 ChunkStack * stack = _chunkStacks[index]; 121 SmallChunk * chunk; 122 if (stack) { 123 int chunkY = (y >> 3); 124 chunk = stack.get(chunkY); 125 if (chunk) 126 chunk.setCell(x, y, z, value); 127 else { 128 // create chunk 129 if (!value) 130 return; // don't create chunk for 0 131 chunk = SmallChunk.alloc(x, y, z); 132 stack.set(chunkY, chunk); 133 chunk.setCell(x, y, z, value); 134 updateNearChunks(chunk, x, y, z); 135 } 136 } else { 137 if (!value) 138 return; // don't create chunk for 0 139 stack = new ChunkStack(); 140 _chunkStacks[index] = stack; 141 int chunkY = (y >> 3); 142 chunk = SmallChunk.alloc(x, y, z); 143 stack.set(chunkY, chunk); 144 chunk.setCell(x, y, z, value); 145 updateNearChunks(chunk, x, y, z); 146 } 147 } 148 149 150 151 152 void setCellRange(Vector3d pos, Vector3d sz, cell_t value) { 153 for (int x = 0; x < sz.x; x++) 154 for (int y = 0; y < sz.y; y++) 155 for (int z = 0; z < sz.z; z++) 156 setCell(pos.x + x, pos.y + y, pos.z + z, value); 157 } 158 159 final bool isOpaque(int x, int y, int z) { 160 cell_t cell = getCell(x, y, z); 161 return BLOCK_TYPE_OPAQUE.ptr[cell] && cell != BOUND_SKY; 162 } 163 164 bool canPass(Vector3d pos) { 165 return canPass(Vector3d(pos.x - 2, pos.y - 3, pos.z - 2), Vector3d(4, 5, 4)); 166 } 167 168 bool canPass(Vector3d pos, Vector3d size) { 169 for (int x = 0; x <= size.x; x++) 170 for (int z = 0; z <= size.z; z++) 171 for (int y = 0; y < size.y; y++) { 172 if (isOpaque(pos.x + x, pos.y + y, pos.z + z)) 173 return false; 174 } 175 return true; 176 } 177 178 /// get max Y position of non-empty cell in region (x +- size, z +- size) 179 int regionHeight(int x, int z, int size) { 180 int top = -1; 181 int delta = size / 8 + 1; 182 for (int dx = x - delta; dx <= x + delta; dx += 8) { 183 for (int dz = z - delta; dz <= z + delta; dz += 8) { 184 if (ChunkStack * stack = getCellChunkStack(x, z)) { 185 if (top < stack.topNonEmptyY) 186 top = stack.topNonEmptyY; 187 } 188 } 189 } 190 return top; 191 } 192 193 private void visitChunk(ChunkVisitor visitor, Vector3d pos) { 194 SmallChunk * chunk = getCellChunk(pos.x, pos.y, pos.z); 195 if (chunk && chunk.hasVisibleFaces) 196 visitor.visit(this, chunk); 197 } 198 199 /// visit visible chunks, starting from specified position 200 void visitVisibleChunks(ChunkVisitor visitor, Vector3d pos, int maxDistance) { 201 int chunkDist = (maxDistance + 7) >> 3; 202 visitChunk(visitor, pos); 203 for (int dist = 1; dist <= chunkDist; dist++) { 204 int d = dist << 3; 205 visitChunk(visitor, Vector3d(pos.x - d, pos.y, pos.z)); 206 visitChunk(visitor, Vector3d(pos.x + d, pos.y, pos.z)); 207 visitChunk(visitor, Vector3d(pos.x, pos.y - d, pos.z)); 208 visitChunk(visitor, Vector3d(pos.x, pos.y + d, pos.z)); 209 visitChunk(visitor, Vector3d(pos.x, pos.y, pos.z - d)); 210 visitChunk(visitor, Vector3d(pos.x, pos.y, pos.z + d)); 211 for (int i = 1; i <= dist; i++) { 212 } 213 } 214 } 215 216 private: 217 218 Position _camPosition; 219 int maxVisibleRange = MAX_VIEW_DISTANCE; 220 } 221 222 struct VisitorCell { 223 SmallChunk * chunk; 224 ulong[6] accessible; 225 bool visited; 226 int dirFlags; 227 } 228 229 struct ChunkDiamondVisitor { 230 World world; 231 ChunkVisitor visitor; 232 Vector3d pos; 233 Array3d!VisitorCell cells; 234 int maxDist; 235 Vector3dArray oldcells; 236 Vector3dArray newcells; 237 void init(World world, int distance, ChunkVisitor visitor) { 238 this.world = world; 239 this.maxDist = (distance + 7) / 8; 240 cells.reset(maxDist); 241 this.visitor = visitor; 242 } 243 void visitCell(VisitorCell * oldCell, int x, int y, int z, Dir direction) { 244 if (x < -maxDist || x > maxDist || y < -maxDist || y > maxDist || z < -maxDist || z > maxDist) 245 return; // out of bounds 246 auto cell = cells.ptr(x, y, z); 247 if (!cell.visited) { 248 cell.chunk = world.getCellChunk(pos.x + (x << 3), pos.y + (y << 3), pos.z + (z << 3)); 249 cell.visited = true; 250 newcells.append(Vector3d(x, y, z)); 251 } 252 cell.dirFlags |= (1 << direction); 253 } 254 void visitChunks(Vector3d pos) { 255 this.pos = pos; 256 cells.reset(maxDist); 257 //cells[1,2,3] = VisitorCell.init; 258 newcells.clear(); 259 //oldcells.append(Vector3d(0, 0, 0)); 260 visitCell(null, 0,0,0, Dir.NORTH); 261 visitCell(null, 0,0,0, Dir.SOUTH); 262 visitCell(null, 0,0,0, Dir.WEST); 263 visitCell(null, 0,0,0, Dir.EAST); 264 visitCell(null, 0,0,0, Dir.UP); 265 visitCell(null, 0,0,0, Dir.DOWN); 266 newcells.swap(oldcells); 267 // call visitor for this newly visited cells 268 for (int i = 0; i < oldcells.length; i++) { 269 Vector3d pt = oldcells[i]; 270 auto cell = cells.ptr(pt.x, pt.y, pt.z); 271 if (cell.chunk) 272 visitor.visit(world, cell.chunk); 273 } 274 for (int dist = 0; dist < maxDist * 2; dist++) { 275 if (oldcells.length == 0) 276 break; 277 newcells.clear(); 278 for (int i = 0; i < oldcells.length; i++) { 279 Vector3d pt = oldcells[i]; 280 auto oldcell = cells.ptr(pt.x, pt.y, pt.z); 281 if (pt.x < 0) { 282 visitCell(oldcell, pt.x - 1, pt.y, pt.z, Dir.WEST); 283 } else if (pt.x > 0) { 284 visitCell(oldcell, pt.x + 1, pt.y, pt.z, Dir.EAST); 285 } else { 286 visitCell(oldcell, pt.x - 1, pt.y, pt.z, Dir.WEST); 287 visitCell(oldcell, pt.x + 1, pt.y, pt.z, Dir.EAST); 288 } 289 if (pt.y < 0) { 290 visitCell(oldcell, pt.x, pt.y - 1, pt.z, Dir.DOWN); 291 } else if (pt.y > 0) { 292 visitCell(oldcell, pt.x, pt.y + 1, pt.z, Dir.UP); 293 } else { 294 visitCell(oldcell, pt.x, pt.y - 1, pt.z, Dir.DOWN); 295 visitCell(oldcell, pt.x, pt.y + 1, pt.z, Dir.UP); 296 } 297 if (pt.z < 0) { 298 visitCell(oldcell, pt.x, pt.y, pt.z - 1, Dir.WEST); 299 } else if (pt.z > 0) { 300 visitCell(oldcell, pt.x, pt.y, pt.z + 1, Dir.EAST); 301 } else { 302 visitCell(oldcell, pt.x, pt.y, pt.z - 1, Dir.WEST); 303 visitCell(oldcell, pt.x, pt.y, pt.z + 1, Dir.EAST); 304 } 305 } 306 newcells.swap(oldcells); 307 // call visitor for this newly visited cells 308 for (int i = 0; i < oldcells.length; i++) { 309 Vector3d pt = oldcells[i]; 310 auto cell = cells.ptr(pt.x, pt.y, pt.z); 311 if (cell.chunk) 312 visitor.visit(world, cell.chunk); 313 } 314 } 315 } 316 } 317