]> git.localhorst.tv Git - blank.git/blob - src/chunk.cpp
optimized block lookup a little
[blank.git] / src / chunk.cpp
1 #include "chunk.hpp"
2
3 #include "generator.hpp"
4
5 #include <limits>
6 #include <queue>
7 #include <glm/gtx/transform.hpp>
8
9
10 namespace blank {
11
12 Chunk::Chunk(const BlockTypeRegistry &types)
13 : types(&types)
14 , neighbor{ 0, 0, 0, 0, 0, 0 }
15 , blocks()
16 , light()
17 , model()
18 , position(0, 0, 0)
19 , dirty(false) {
20
21 }
22
23 Chunk::Chunk(Chunk &&other)
24 : types(other.types)
25 , blocks(std::move(other.blocks))
26 , light(std::move(other.light))
27 , model(std::move(other.model))
28 , position(other.position)
29 , dirty(other.dirty) {
30         for (size_t i = 0; i < Block::FACE_COUNT; ++i) {
31                 neighbor[i] = other.neighbor[i];
32         }
33 }
34
35 Chunk &Chunk::operator =(Chunk &&other) {
36         types = other.types;
37         for (size_t i = 0; i < Block::FACE_COUNT; ++i) {
38                 neighbor[i] = other.neighbor[i];
39         }
40         blocks = std::move(other.blocks);
41         light = std::move(other.light);
42         model = std::move(other.model);
43         position = other.position;
44         dirty = other.dirty;
45         return *this;
46 }
47
48
49 namespace {
50
51 struct SetNode {
52
53         Chunk *chunk;
54         Chunk::Pos pos;
55
56         SetNode(Chunk *chunk, Chunk::Pos pos)
57         : chunk(chunk), pos(pos) { }
58
59         int Get() const { return chunk->GetLight(pos); }
60         void Set(int level) { chunk->SetLight(pos, level); }
61
62         bool HasNext(Block::Face face) {
63                 const BlockLookup next(chunk, pos, face);
64                 return next && !next.GetType().block_light;
65         }
66         SetNode GetNext(Block::Face face) {
67                 const BlockLookup next(chunk, pos, face);
68                 return SetNode(&next.GetChunk(), next.GetBlockPos());
69         }
70
71 };
72
73 struct UnsetNode
74 : public SetNode {
75
76         int level;
77
78         UnsetNode(Chunk *chunk, Chunk::Pos pos)
79         : SetNode(chunk, pos), level(Get()) { }
80
81         UnsetNode(const SetNode &set)
82         : SetNode(set), level(Get()) { }
83
84
85         bool HasNext(Block::Face face) {
86                 const BlockLookup next(chunk, pos, face);
87                 return next;
88         }
89         UnsetNode GetNext(Block::Face face) { return UnsetNode(SetNode::GetNext(face)); }
90
91 };
92
93 std::queue<SetNode> light_queue;
94 std::queue<UnsetNode> dark_queue;
95
96 void work_light() {
97         while (!light_queue.empty()) {
98                 SetNode node = light_queue.front();
99                 light_queue.pop();
100
101                 int level = node.Get() - 1;
102                 for (int face = 0; face < Block::FACE_COUNT; ++face) {
103                         if (node.HasNext(Block::Face(face))) {
104                                 SetNode other = node.GetNext(Block::Face(face));
105                                 if (other.Get() < level) {
106                                         other.Set(level);
107                                         light_queue.emplace(other);
108                                 }
109                         }
110                 }
111         }
112 }
113
114 void work_dark() {
115         while (!dark_queue.empty()) {
116                 UnsetNode node = dark_queue.front();
117                 dark_queue.pop();
118
119                 for (int face = 0; face < Block::FACE_COUNT; ++face) {
120                         if (node.HasNext(Block::Face(face))) {
121                                 UnsetNode other = node.GetNext(Block::Face(face));
122                                 // TODO: if there a light source here with the same level this will err
123                                 if (other.Get() != 0 && other.Get() < node.level) {
124                                         other.Set(0);
125                                         dark_queue.emplace(other);
126                                 } else {
127                                         light_queue.emplace(other);
128                                 }
129                         }
130                 }
131         }
132 }
133
134 }
135
136 void Chunk::SetBlock(int index, const Block &block) {
137         const BlockType &old_type = Type(blocks[index]);
138         const BlockType &new_type = Type(block);
139
140         blocks[index] = block;
141
142         if (&old_type == &new_type) return;
143
144         if (new_type.luminosity > old_type.luminosity) {
145                 // light added
146                 SetLight(index, new_type.luminosity);
147                 light_queue.emplace(this, ToPos(index));
148                 work_light();
149         } else if (new_type.luminosity < old_type.luminosity) {
150                 // light removed
151                 dark_queue.emplace(this, ToPos(index));
152                 SetLight(index, 0);
153                 work_dark();
154                 SetLight(index, new_type.luminosity);
155                 light_queue.emplace(this, ToPos(index));
156                 work_light();
157         } else if (new_type.block_light && !old_type.block_light) {
158                 // obstacle added
159                 if (GetLight(index) > 0) {
160                         dark_queue.emplace(this, ToPos(index));
161                         SetLight(index, 0);
162                         work_dark();
163                         work_light();
164                 }
165         } else if (!new_type.block_light && old_type.block_light) {
166                 // obstacle removed
167                 int level = 0;
168                 for (int face = 0; face < Block::FACE_COUNT; ++face) {
169                         Pos next_pos(ToPos(index) + Block::FaceNormal(Block::Face(face)));
170                         int next_level = 0;
171                         if (InBounds(next_pos)) {
172                                 next_level = GetLight(next_pos);
173                         } else {
174                                 if (HasNeighbor(Block::Face(face))) {
175                                         next_pos -= (Block::FaceNormal(Block::Face(face)) * Chunk::Extent());
176                                         next_level = GetNeighbor(Block::Face(face)).GetLight(next_pos);
177                                 }
178                         }
179                         if (level < next_level) {
180                                 level = next_level;
181                         }
182                 }
183                 if (level > 1) {
184                         SetLight(index, level - 1);
185                         light_queue.emplace(this, ToPos(index));
186                         work_light();
187                 }
188         }
189 }
190
191 const Block *Chunk::FindNext(const Pos &pos, Block::Face face) const {
192         Pos next_pos(pos + Block::FaceNormal(face));
193         if (InBounds(next_pos)) {
194                 return &BlockAt(pos + Block::FaceNormal(face));
195         } else if (HasNeighbor(face)) {
196                 return &GetNeighbor(face).BlockAt(next_pos - (Block::FaceNormal(face) * Extent()));
197         } else {
198                 return nullptr;
199         }
200 }
201
202 void Chunk::SetNeighbor(Chunk &other) {
203         if (other.position == position + Pos(-1, 0, 0)) {
204                 if (neighbor[Block::FACE_LEFT] != &other) {
205                         neighbor[Block::FACE_LEFT] = &other;
206                         other.neighbor[Block::FACE_RIGHT] = this;
207                         for (int z = 0; z < Depth(); ++z) {
208                                 for (int y = 0; y < Height(); ++y) {
209                                         Pos my_pos(0, y, z);
210                                         Pos other_pos(Width() - 1, y, z);
211                                         if (GetLight(my_pos) > 0) {
212                                                 light_queue.emplace(this, my_pos);
213                                         }
214                                         if (other.GetLight(other_pos) > 0) {
215                                                 light_queue.emplace(&other, other_pos);
216                                         }
217                                 }
218                         }
219                         work_light();
220                 }
221         } else if (other.position == position + Pos(1, 0, 0)) {
222                 if (neighbor[Block::FACE_RIGHT] != &other) {
223                         neighbor[Block::FACE_RIGHT] = &other;
224                         other.neighbor[Block::FACE_LEFT] = this;
225                         for (int z = 0; z < Depth(); ++z) {
226                                 for (int y = 0; y < Height(); ++y) {
227                                         Pos my_pos(Width() - 1, y, z);
228                                         Pos other_pos(0, y, z);
229                                         if (GetLight(my_pos) > 0) {
230                                                 light_queue.emplace(this, my_pos);
231                                         }
232                                         if (other.GetLight(other_pos) > 0) {
233                                                 light_queue.emplace(&other, other_pos);
234                                         }
235                                 }
236                         }
237                         work_light();
238                 }
239         } else if (other.position == position + Pos(0, -1, 0)) {
240                 if (neighbor[Block::FACE_DOWN] != &other) {
241                         neighbor[Block::FACE_DOWN] = &other;
242                         other.neighbor[Block::FACE_UP] = this;
243                         for (int z = 0; z < Depth(); ++z) {
244                                 for (int x = 0; x < Width(); ++x) {
245                                         Pos my_pos(x, 0, z);
246                                         Pos other_pos(x, Height() - 1, z);
247                                         if (GetLight(my_pos) > 0) {
248                                                 light_queue.emplace(this, my_pos);
249                                         }
250                                         if (other.GetLight(other_pos) > 0) {
251                                                 light_queue.emplace(&other, other_pos);
252                                         }
253                                 }
254                         }
255                         work_light();
256                 }
257         } else if (other.position == position + Pos(0, 1, 0)) {
258                 if (neighbor[Block::FACE_UP] != &other) {
259                         neighbor[Block::FACE_UP] = &other;
260                         other.neighbor[Block::FACE_DOWN] = this;
261                         for (int z = 0; z < Depth(); ++z) {
262                                 for (int x = 0; x < Width(); ++x) {
263                                         Pos my_pos(x, Height() - 1, z);
264                                         Pos other_pos(x, 0, z);
265                                         if (GetLight(my_pos) > 0) {
266                                                 light_queue.emplace(this, my_pos);
267                                         }
268                                         if (other.GetLight(other_pos) > 0) {
269                                                 light_queue.emplace(&other, other_pos);
270                                         }
271                                 }
272                         }
273                         work_light();
274                 }
275         } else if (other.position == position + Pos(0, 0, -1)) {
276                 if (neighbor[Block::FACE_BACK] != &other) {
277                         neighbor[Block::FACE_BACK] = &other;
278                         other.neighbor[Block::FACE_FRONT] = this;
279                         for (int y = 0; y < Height(); ++y) {
280                                 for (int x = 0; x < Width(); ++x) {
281                                         Pos my_pos(x, y, 0);
282                                         Pos other_pos(x, y, Depth() - 1);
283                                         if (GetLight(my_pos) > 0) {
284                                                 light_queue.emplace(this, my_pos);
285                                         }
286                                         if (other.GetLight(other_pos) > 0) {
287                                                 light_queue.emplace(&other, other_pos);
288                                         }
289                                 }
290                         }
291                         work_light();
292                 }
293         } else if (other.position == position + Pos(0, 0, 1)) {
294                 if (neighbor[Block::FACE_FRONT] != &other) {
295                         neighbor[Block::FACE_FRONT] = &other;
296                         other.neighbor[Block::FACE_BACK] = this;
297                         for (int y = 0; y < Height(); ++y) {
298                                 for (int x = 0; x < Width(); ++x) {
299                                         Pos my_pos(x, y, Depth() - 1);
300                                         Pos other_pos(x, y, 0);
301                                         if (GetLight(my_pos) > 0) {
302                                                 light_queue.emplace(this, my_pos);
303                                         }
304                                         if (other.GetLight(other_pos) > 0) {
305                                                 light_queue.emplace(&other, other_pos);
306                                         }
307                                 }
308                         }
309                         work_light();
310                 }
311         }
312 }
313
314 void Chunk::ClearNeighbors() {
315         for (int i = 0; i < Block::FACE_COUNT; ++i) {
316                 neighbor[i] = nullptr;
317         }
318 }
319
320 void Chunk::Unlink() {
321         if (neighbor[Block::FACE_UP]) {
322                 neighbor[Block::FACE_UP]->neighbor[Block::FACE_DOWN] = nullptr;
323         }
324         if (neighbor[Block::FACE_DOWN]) {
325                 neighbor[Block::FACE_DOWN]->neighbor[Block::FACE_UP] = nullptr;
326         }
327         if (neighbor[Block::FACE_LEFT]) {
328                 neighbor[Block::FACE_LEFT]->neighbor[Block::FACE_RIGHT] = nullptr;
329         }
330         if (neighbor[Block::FACE_RIGHT]) {
331                 neighbor[Block::FACE_RIGHT]->neighbor[Block::FACE_LEFT] = nullptr;
332         }
333         if (neighbor[Block::FACE_FRONT]) {
334                 neighbor[Block::FACE_FRONT]->neighbor[Block::FACE_BACK] = nullptr;
335         }
336         if (neighbor[Block::FACE_BACK]) {
337                 neighbor[Block::FACE_BACK]->neighbor[Block::FACE_FRONT] = nullptr;
338         }
339 }
340
341 void Chunk::Relink() {
342         if (neighbor[Block::FACE_UP]) {
343                 neighbor[Block::FACE_UP]->neighbor[Block::FACE_DOWN] = this;
344         }
345         if (neighbor[Block::FACE_DOWN]) {
346                 neighbor[Block::FACE_DOWN]->neighbor[Block::FACE_UP] = this;
347         }
348         if (neighbor[Block::FACE_LEFT]) {
349                 neighbor[Block::FACE_LEFT]->neighbor[Block::FACE_RIGHT] = this;
350         }
351         if (neighbor[Block::FACE_RIGHT]) {
352                 neighbor[Block::FACE_RIGHT]->neighbor[Block::FACE_LEFT] = this;
353         }
354         if (neighbor[Block::FACE_FRONT]) {
355                 neighbor[Block::FACE_FRONT]->neighbor[Block::FACE_BACK] = this;
356         }
357         if (neighbor[Block::FACE_BACK]) {
358                 neighbor[Block::FACE_BACK]->neighbor[Block::FACE_FRONT] = this;
359         }
360 }
361
362
363 void Chunk::SetLight(int index, int level) {
364         if (light[index] != level) {
365                 light[index] = level;
366                 Invalidate();
367         }
368 }
369
370 int Chunk::GetLight(int index) const {
371         return light[index];
372 }
373
374 float Chunk::GetVertexLight(int index, const BlockModel::Position &vtx, const BlockModel::Normal &norm) const {
375         float light = GetLight(index);
376         Chunk::Pos pos(ToPos(index));
377
378         Block::Face direct_face(Block::NormalFace(norm));
379         // tis okay
380         BlockLookup direct(const_cast<Chunk *>(this), pos, Block::NormalFace(norm));
381         if (direct) {
382                 float direct_light = direct.GetLight();
383                 if (direct_light > light) {
384                         light = direct_light;
385                 }
386         }
387
388         // cheap alternative until AO etc are implemented
389         // to tell the faces apart
390
391         if (direct_face == Block::FACE_LEFT || direct_face == Block::FACE_RIGHT) {
392                 light -= 0.2;
393         } else if (direct_face == Block::FACE_FRONT || direct_face == Block::FACE_BACK) {
394                 light -= 0.4;
395         }
396
397         return light;
398 }
399
400
401 bool Chunk::IsSurface(const Pos &pos) const {
402         const Block &block = BlockAt(pos);
403         if (!Type(block).visible) {
404                 return false;
405         }
406         for (int face = 0; face < Block::FACE_COUNT; ++face) {
407                 const Block *next = FindNext(pos, Block::Face(face));
408                 if (!next || !Type(*next).visible) {
409                         return true;
410                 }
411         }
412         return false;
413 }
414
415
416 void Chunk::Allocate() {
417         blocks.resize(Size(), Block(0));
418         light.resize(Size(), 0);
419 }
420
421
422 void Chunk::Draw() {
423         if (dirty) {
424                 Update();
425         }
426         model.Draw();
427 }
428
429
430 bool Chunk::Intersection(
431         const Ray &ray,
432         const glm::mat4 &M,
433         int &blkid,
434         float &dist,
435         glm::vec3 &normal
436 ) const {
437         // TODO: should be possible to heavily optimize this
438         int id = 0;
439         blkid = -1;
440         dist = std::numeric_limits<float>::infinity();
441         for (int z = 0; z < Depth(); ++z) {
442                 for (int y = 0; y < Height(); ++y) {
443                         for (int x = 0; x < Width(); ++x, ++id) {
444                                 if (!Type(blocks[id]).visible) {
445                                         continue;
446                                 }
447                                 float cur_dist;
448                                 glm::vec3 cur_norm;
449                                 if (Type(blocks[id]).shape->Intersects(ray, M * ToTransform(id), cur_dist, cur_norm)) {
450                                         if (cur_dist < dist) {
451                                                 blkid = id;
452                                                 dist = cur_dist;
453                                                 normal = cur_norm;
454                                         }
455                                 }
456                         }
457                 }
458         }
459
460         if (blkid < 0) {
461                 return false;
462         } else {
463                 normal = glm::vec3(BlockAt(blkid).Transform() * glm::vec4(normal, 0.0f));
464                 return true;
465         }
466 }
467
468 void Chunk::Position(const Pos &pos) {
469         position = pos;
470 }
471
472 glm::mat4 Chunk::Transform(const Pos &offset) const {
473         return glm::translate((position - offset) * Extent());
474 }
475
476
477 namespace {
478
479 BlockModel::Buffer buf;
480
481 }
482
483 void Chunk::CheckUpdate() {
484         if (dirty) {
485                 Update();
486         }
487 }
488
489 void Chunk::Update() {
490         int vtx_count = 0, idx_count = 0;
491         for (const auto &block : blocks) {
492                 const Shape *shape = Type(block).shape;
493                 vtx_count += shape->VertexCount();
494                 idx_count += shape->VertexIndexCount();
495         }
496         buf.Clear();
497         buf.Reserve(vtx_count, idx_count);
498
499         BlockModel::Index vtx_counter = 0;
500         for (size_t i = 0; i < Size(); ++i) {
501                 const BlockType &type = Type(blocks[i]);
502
503                 if (!type.visible || Obstructed(i)) continue;
504
505                 type.FillBlockModel(buf, ToTransform(i), vtx_counter);
506                 size_t vtx_begin = vtx_counter;
507                 vtx_counter += type.shape->VertexCount();
508
509                 for (size_t vtx = vtx_begin; vtx < vtx_counter; ++vtx) {
510                         buf.lights.emplace_back(GetVertexLight(i, buf.vertices[vtx], buf.normals[vtx]));
511                 }
512         }
513
514         model.Update(buf);
515         dirty = false;
516 }
517
518 bool Chunk::Obstructed(int idx) const {
519         Chunk::Pos pos(ToPos(idx));
520
521         Chunk::Pos left_pos(pos + Chunk::Pos(-1, 0, 0));
522         const Block *left_block = nullptr;
523         if (InBounds(left_pos)) {
524                 left_block = &BlockAt(left_pos);
525         } else if (HasNeighbor(Block::FACE_LEFT)) {
526                 left_pos += Chunk::Pos(Width(), 0, 0);
527                 left_block = &GetNeighbor(Block::FACE_LEFT).BlockAt(left_pos);
528         } else {
529                 return false;
530         }
531         if (!Type(*left_block).FaceFilled(*left_block, Block::FACE_RIGHT)) {
532                 return false;
533         }
534
535         Chunk::Pos right_pos(pos + Chunk::Pos(1, 0, 0));
536         const Block *right_block = nullptr;
537         if (InBounds(right_pos)) {
538                 right_block = &BlockAt(right_pos);
539         } else if (HasNeighbor(Block::FACE_RIGHT)) {
540                 right_pos += Chunk::Pos(-Width(), 0, 0);
541                 right_block = &GetNeighbor(Block::FACE_RIGHT).BlockAt(right_pos);
542         } else {
543                 return false;
544         }
545         if (!Type(*right_block).FaceFilled(*right_block, Block::FACE_LEFT)) {
546                 return false;
547         }
548
549         Chunk::Pos down_pos(pos + Chunk::Pos(0, -1, 0));
550         const Block *down_block = nullptr;
551         if (InBounds(down_pos)) {
552                 down_block = &BlockAt(down_pos);
553         } else if (HasNeighbor(Block::FACE_DOWN)) {
554                 down_pos += Chunk::Pos(0, Height(), 0);
555                 down_block = &GetNeighbor(Block::FACE_DOWN).BlockAt(down_pos);
556         } else {
557                 return false;
558         }
559         if (!Type(*down_block).FaceFilled(*down_block, Block::FACE_UP)) {
560                 return false;
561         }
562
563         Chunk::Pos up_pos(pos + Chunk::Pos(0, 1, 0));
564         const Block *up_block = nullptr;
565         if (InBounds(up_pos)) {
566                 up_block = &BlockAt(up_pos);
567         } else if (HasNeighbor(Block::FACE_UP)) {
568                 up_pos += Chunk::Pos(0, -Height(), 0);
569                 up_block = &GetNeighbor(Block::FACE_UP).BlockAt(up_pos);
570         } else {
571                 return false;
572         }
573         if (!Type(*up_block).FaceFilled(*up_block, Block::FACE_DOWN)) {
574                 return false;
575         }
576
577         Chunk::Pos back_pos(pos + Chunk::Pos(0, 0, -1));
578         const Block *back_block = nullptr;
579         if (InBounds(back_pos)) {
580                 back_block = &BlockAt(back_pos);
581         } else if (HasNeighbor(Block::FACE_BACK)) {
582                 back_pos += Chunk::Pos(0, 0, Depth());
583                 back_block = &GetNeighbor(Block::FACE_BACK).BlockAt(back_pos);
584         } else {
585                 return false;
586         }
587         if (!Type(*back_block).FaceFilled(*back_block, Block::FACE_FRONT)) {
588                 return false;
589         }
590
591         Chunk::Pos front_pos(pos + Chunk::Pos(0, 0, 1));
592         const Block *front_block = nullptr;
593         if (InBounds(front_pos)) {
594                 front_block = &BlockAt(front_pos);
595         } else if (HasNeighbor(Block::FACE_FRONT)) {
596                 front_pos += Chunk::Pos(0, 0, -Depth());
597                 front_block = &GetNeighbor(Block::FACE_FRONT).BlockAt(front_pos);
598         } else {
599                 return false;
600         }
601         if (!Type(*front_block).FaceFilled(*front_block, Block::FACE_BACK)) {
602                 return false;
603         }
604
605         return true;
606 }
607
608 glm::mat4 Chunk::ToTransform(int idx) const {
609         return glm::translate(glm::mat4(1.0f), ToCoords(idx)) * blocks[idx].Transform();
610 }
611
612
613 BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p)
614 : chunk(c), pos(p) {
615         while (pos.x >= Chunk::Width()) {
616                 if (chunk->HasNeighbor(Block::FACE_RIGHT)) {
617                         chunk = &chunk->GetNeighbor(Block::FACE_RIGHT);
618                         pos.x -= Chunk::Width();
619                 } else {
620                         chunk = nullptr;
621                         return;
622                 }
623         }
624         while (pos.x < 0) {
625                 if (chunk->HasNeighbor(Block::FACE_LEFT)) {
626                         chunk = &chunk->GetNeighbor(Block::FACE_LEFT);
627                         pos.x += Chunk::Width();
628                 } else {
629                         chunk = nullptr;
630                         return;
631                 }
632         }
633         while (pos.y >= Chunk::Height()) {
634                 if (chunk->HasNeighbor(Block::FACE_UP)) {
635                         chunk = &chunk->GetNeighbor(Block::FACE_UP);
636                         pos.y -= Chunk::Height();
637                 } else {
638                         chunk = nullptr;
639                         return;
640                 }
641         }
642         while (pos.y < 0) {
643                 if (chunk->HasNeighbor(Block::FACE_DOWN)) {
644                         chunk = &chunk->GetNeighbor(Block::FACE_DOWN);
645                         pos.y += Chunk::Height();
646                 } else {
647                         chunk = nullptr;
648                         return;
649                 }
650         }
651         while (pos.z >= Chunk::Depth()) {
652                 if (chunk->HasNeighbor(Block::FACE_FRONT)) {
653                         chunk = &chunk->GetNeighbor(Block::FACE_FRONT);
654                         pos.z -= Chunk::Depth();
655                 } else {
656                         chunk = nullptr;
657                         return;
658                 }
659         }
660         while (pos.z < 0) {
661                 if (chunk->HasNeighbor(Block::FACE_BACK)) {
662                         chunk = &chunk->GetNeighbor(Block::FACE_BACK);
663                         pos.z += Chunk::Depth();
664                 } else {
665                         chunk = nullptr;
666                         return;
667                 }
668         }
669 }
670
671 BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p, Block::Face face)
672 : chunk(c), pos(p) {
673         pos += Block::FaceNormal(face);
674         if (!Chunk::InBounds(pos)) {
675                 pos -= Block::FaceNormal(face) * Chunk::Extent();
676                 chunk = &chunk->GetNeighbor(face);
677         }
678 }
679
680
681 ChunkLoader::ChunkLoader(const Config &config, const BlockTypeRegistry &reg, const Generator &gen)
682 : base(0, 0, 0)
683 , reg(reg)
684 , gen(gen)
685 , loaded()
686 , to_generate()
687 , to_free()
688 , load_dist(config.load_dist)
689 , unload_dist(config.unload_dist) {
690
691 }
692
693 namespace {
694
695 struct ChunkLess {
696
697         explicit ChunkLess(const Chunk::Pos &base)
698         : base(base) { }
699
700         bool operator ()(const Chunk::Pos &a, const Chunk::Pos &b) const {
701                 Chunk::Pos da(base - a);
702                 Chunk::Pos db(base - b);
703                 return
704                         da.x * da.x + da.y * da.y + da.z * da.z <
705                         db.x * db.x + db.y * db.y + db.z * db.z;
706         }
707
708         Chunk::Pos base;
709
710 };
711
712 }
713
714 void ChunkLoader::Generate(const Chunk::Pos &from, const Chunk::Pos &to) {
715         for (int z = from.z; z < to.z; ++z) {
716                 for (int y = from.y; y < to.y; ++y) {
717                         for (int x = from.x; x < to.x; ++x) {
718                                 Chunk::Pos pos(x, y, z);
719                                 if (Known(pos)) {
720                                         continue;
721                                 } else if (pos == base) {
722                                         Generate(pos);
723
724                                 //      light testing
725                                 //      for (int i = 0; i < 16; ++i) {
726                                 //              for (int j = 0; j < 16; ++j) {
727                                 //                      loaded.back().SetBlock(Chunk::Pos{  i, j,  0 }, Block(1));
728                                 //                      loaded.back().SetBlock(Chunk::Pos{  i, j, 15 }, Block(1));
729                                 //                      loaded.back().SetBlock(Chunk::Pos{  0, j,  i }, Block(1));
730                                 //                      loaded.back().SetBlock(Chunk::Pos{ 15, j,  i }, Block(1));
731                                 //              }
732                                 //      }
733                                 //      loaded.back().SetBlock(Chunk::Pos{  1,  0,  1 }, Block(13));
734                                 //      loaded.back().SetBlock(Chunk::Pos{ 14,  0,  1 }, Block(13));
735                                 //      loaded.back().SetBlock(Chunk::Pos{  1,  0, 14 }, Block(13));
736                                 //      loaded.back().SetBlock(Chunk::Pos{ 14,  0, 14 }, Block(13));
737                                 //      loaded.back().SetBlock(Chunk::Pos{  1, 15,  1 }, Block(13));
738                                 //      loaded.back().SetBlock(Chunk::Pos{ 14, 15,  1 }, Block(13));
739                                 //      loaded.back().SetBlock(Chunk::Pos{  1, 15, 14 }, Block(13));
740                                 //      loaded.back().SetBlock(Chunk::Pos{ 14, 15, 14 }, Block(13));
741                                 //      loaded.back().SetBlock(Chunk::Pos{  7,  7,  0 }, Block(13));
742                                 //      loaded.back().SetBlock(Chunk::Pos{  8,  7,  0 }, Block(13));
743                                 //      loaded.back().SetBlock(Chunk::Pos{  7,  8,  0 }, Block(13));
744                                 //      loaded.back().SetBlock(Chunk::Pos{  8,  8,  0 }, Block(13));
745                                 //      loaded.back().SetBlock(Chunk::Pos{  7,  7, 15 }, Block(13));
746                                 //      loaded.back().SetBlock(Chunk::Pos{  8,  7, 15 }, Block(13));
747                                 //      loaded.back().SetBlock(Chunk::Pos{  7,  8, 15 }, Block(13));
748                                 //      loaded.back().SetBlock(Chunk::Pos{  8,  8, 15 }, Block(13));
749                                 //      loaded.back().SetBlock(Chunk::Pos{  0,  7,  7 }, Block(13));
750                                 //      loaded.back().SetBlock(Chunk::Pos{  0,  7,  8 }, Block(13));
751                                 //      loaded.back().SetBlock(Chunk::Pos{  0,  8,  7 }, Block(13));
752                                 //      loaded.back().SetBlock(Chunk::Pos{  0,  8,  8 }, Block(13));
753                                 //      loaded.back().SetBlock(Chunk::Pos{ 15,  7,  7 }, Block(13));
754                                 //      loaded.back().SetBlock(Chunk::Pos{ 15,  7,  8 }, Block(13));
755                                 //      loaded.back().SetBlock(Chunk::Pos{ 15,  8,  7 }, Block(13));
756                                 //      loaded.back().SetBlock(Chunk::Pos{ 15,  8,  8 }, Block(13));
757                                 //      loaded.back().Invalidate();
758                                 //      loaded.back().CheckUpdate();
759
760                                 //      orientation testing
761                                 //      for (int i = 0; i < Block::FACE_COUNT; ++i) {
762                                 //              for (int j = 0; j < Block::TURN_COUNT; ++j) {
763                                 //                      loaded.back().BlockAt(512 * j + 2 * i) = Block(3 * (j + 1), Block::Face(i), Block::Turn(j));
764                                 //              }
765                                 //      }
766                                 //      loaded.back().Invalidate();
767                                 //      loaded.back().CheckUpdate();
768                                 } else {
769                                         to_generate.emplace_back(pos);
770                                 }
771                         }
772                 }
773         }
774         to_generate.sort(ChunkLess(base));
775 }
776
777 Chunk &ChunkLoader::Generate(const Chunk::Pos &pos) {
778         loaded.emplace_back(reg);
779         Chunk &chunk = loaded.back();
780         chunk.Position(pos);
781         chunk.Allocate();
782         gen(chunk);
783         Insert(chunk);
784         return chunk;
785 }
786
787 void ChunkLoader::Insert(Chunk &chunk) {
788         for (Chunk &other : loaded) {
789                 chunk.SetNeighbor(other);
790         }
791 }
792
793 void ChunkLoader::Remove(Chunk &chunk) {
794         chunk.Unlink();
795 }
796
797 Chunk *ChunkLoader::Loaded(const Chunk::Pos &pos) {
798         for (Chunk &chunk : loaded) {
799                 if (chunk.Position() == pos) {
800                         return &chunk;
801                 }
802         }
803         return nullptr;
804 }
805
806 bool ChunkLoader::Queued(const Chunk::Pos &pos) {
807         for (const Chunk::Pos &chunk : to_generate) {
808                 if (chunk == pos) {
809                         return true;
810                 }
811         }
812         return nullptr;
813 }
814
815 bool ChunkLoader::Known(const Chunk::Pos &pos) {
816         if (Loaded(pos)) return true;
817         return Queued(pos);
818 }
819
820 Chunk &ChunkLoader::ForceLoad(const Chunk::Pos &pos) {
821         Chunk *chunk = Loaded(pos);
822         if (chunk) {
823                 return *chunk;
824         }
825
826         for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end; ++iter) {
827                 if (*iter == pos) {
828                         to_generate.erase(iter);
829                         break;
830                 }
831         }
832
833         return Generate(pos);
834 }
835
836 void ChunkLoader::Rebase(const Chunk::Pos &new_base) {
837         if (new_base == base) {
838                 return;
839         }
840         base = new_base;
841
842         // unload far away chunks
843         for (auto iter(loaded.begin()), end(loaded.end()); iter != end;) {
844                 if (std::abs(base.x - iter->Position().x) > unload_dist
845                                 || std::abs(base.y - iter->Position().y) > unload_dist
846                                 || std::abs(base.z - iter->Position().z) > unload_dist) {
847                         auto saved = iter;
848                         Remove(*saved);
849                         ++iter;
850                         to_free.splice(to_free.end(), loaded, saved);
851                 } else {
852                         ++iter;
853                 }
854         }
855         // abort far away queued chunks
856         for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end;) {
857                 if (std::abs(base.x - iter->x) > unload_dist
858                                 || std::abs(base.y - iter->y) > unload_dist
859                                 || std::abs(base.z - iter->z) > unload_dist) {
860                         iter = to_generate.erase(iter);
861                 } else {
862                         ++iter;
863                 }
864         }
865         // add missing new chunks
866         GenerateSurrounding(base);
867 }
868
869 void ChunkLoader::GenerateSurrounding(const Chunk::Pos &pos) {
870         const Chunk::Pos offset(load_dist, load_dist, load_dist);
871         Generate(pos - offset, pos + offset);
872 }
873
874 void ChunkLoader::Update() {
875         bool reused = false;
876         if (!to_generate.empty()) {
877                 Chunk::Pos pos(to_generate.front());
878
879                 for (auto iter(to_free.begin()), end(to_free.end()); iter != end; ++iter) {
880                         if (iter->Position() == pos) {
881                                 iter->Relink();
882                                 loaded.splice(loaded.end(), to_free, iter);
883                                 reused = true;
884                                 break;
885                         }
886                 }
887
888                 if (!reused) {
889                         if (to_free.empty()) {
890                                 loaded.emplace_back(reg);
891                         } else {
892                                 to_free.front().ClearNeighbors();
893                                 loaded.splice(loaded.end(), to_free, to_free.begin());
894                                 reused = true;
895                         }
896                         Chunk &chunk = loaded.back();
897                         chunk.Position(pos);
898                         chunk.Allocate();
899                         gen(chunk);
900                         Insert(chunk);
901                 }
902                 to_generate.pop_front();
903         }
904
905         if (!reused && !to_free.empty()) {
906                 to_free.pop_front();
907         }
908 }
909
910 }