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