1 #include "BlockLookup.hpp"
3 #include "ChunkIndex.hpp"
4 #include "ChunkLoader.hpp"
5 #include "ChunkRenderer.hpp"
6 #include "ChunkStore.hpp"
8 #include "Generator.hpp"
9 #include "WorldCollision.hpp"
10 #include "../app/Assets.hpp"
11 #include "../graphics/BlockLighting.hpp"
12 #include "../graphics/Viewport.hpp"
13 #include "../io/WorldSave.hpp"
14 #include "../model/BlockModel.hpp"
24 constexpr int Chunk::width;
25 constexpr int Chunk::height;
26 constexpr int Chunk::depth;
27 constexpr int Chunk::size;
30 Chunk::Chunk(const BlockTypeRegistry &types) noexcept
42 Chunk::Chunk(Chunk &&other) noexcept
44 , position(other.position)
45 , dirty_model(other.dirty_model)
46 , dirty_save(other.dirty_save) {
47 std::copy(other.neighbor, other.neighbor + sizeof(neighbor), neighbor);
48 std::copy(other.blocks, other.blocks + sizeof(blocks), blocks);
49 std::copy(other.light, other.light + sizeof(light), light);
52 Chunk &Chunk::operator =(Chunk &&other) noexcept {
54 std::copy(other.neighbor, other.neighbor + sizeof(neighbor), neighbor);
55 std::copy(other.blocks, other.blocks + sizeof(blocks), blocks);
56 std::copy(other.light, other.light + sizeof(light), light);
57 position = other.position;
58 dirty_model = other.dirty_save;
59 dirty_save = other.dirty_save;
71 SetNode(Chunk *chunk, Chunk::Pos pos)
72 : chunk(chunk), pos(pos) { }
74 int Get() const noexcept { return chunk->GetLight(pos); }
75 void Set(int level) noexcept { chunk->SetLight(pos, level); }
77 const BlockType &GetType() const noexcept { return chunk->Type(Chunk::ToIndex(pos)); }
79 bool HasNext(Block::Face face) noexcept {
80 const BlockType &type = GetType();
81 if (type.block_light && !type.luminosity) return false;
82 const BlockLookup next(chunk, pos, face);
85 SetNode GetNext(Block::Face face) noexcept {
86 const BlockLookup next(chunk, pos, face);
87 return SetNode(&next.GetChunk(), next.GetBlockPos());
97 UnsetNode(Chunk *chunk, Chunk::Pos pos)
98 : SetNode(chunk, pos), level(Get()) { }
100 UnsetNode(const SetNode &set)
101 : SetNode(set), level(Get()) { }
104 bool HasNext(Block::Face face) noexcept {
105 const BlockLookup next(chunk, pos, face);
108 UnsetNode GetNext(Block::Face face) noexcept { return UnsetNode(SetNode::GetNext(face)); }
112 std::queue<SetNode> light_queue;
113 std::queue<UnsetNode> dark_queue;
115 void work_light() noexcept {
116 while (!light_queue.empty()) {
117 SetNode node = light_queue.front();
120 int level = node.Get() - 1;
121 for (int face = 0; face < Block::FACE_COUNT; ++face) {
122 if (node.HasNext(Block::Face(face))) {
123 SetNode other = node.GetNext(Block::Face(face));
124 if (other.Get() < level) {
126 light_queue.emplace(other);
133 void work_dark() noexcept {
134 while (!dark_queue.empty()) {
135 UnsetNode node = dark_queue.front();
138 for (int face = 0; face < Block::FACE_COUNT; ++face) {
139 if (node.HasNext(Block::Face(face))) {
140 UnsetNode other = node.GetNext(Block::Face(face));
141 // TODO: if there a light source here with the same level this will err
142 if (other.Get() != 0 && other.Get() < node.level) {
144 dark_queue.emplace(other);
146 light_queue.emplace(other);
155 void Chunk::SetBlock(int index, const Block &block) noexcept {
156 const BlockType &old_type = Type(blocks[index]);
157 const BlockType &new_type = Type(block);
159 blocks[index] = block;
162 if (&old_type == &new_type) return;
164 if (new_type.luminosity > old_type.luminosity) {
166 SetLight(index, new_type.luminosity);
167 light_queue.emplace(this, ToPos(index));
169 } else if (new_type.luminosity < old_type.luminosity) {
171 dark_queue.emplace(this, ToPos(index));
174 SetLight(index, new_type.luminosity);
175 light_queue.emplace(this, ToPos(index));
177 } else if (new_type.block_light && !old_type.block_light) {
179 if (GetLight(index) > 0) {
180 dark_queue.emplace(this, ToPos(index));
185 } else if (!new_type.block_light && old_type.block_light) {
188 Pos pos(ToPos(index));
189 for (int face = 0; face < Block::FACE_COUNT; ++face) {
190 BlockLookup next_block(this, pos, Block::Face(face));
192 level = std::max(level, next_block.GetLight());
196 SetLight(index, level - 1);
197 light_queue.emplace(this, pos);
205 // propagate light from a's edge to b
207 Chunk &a, const Chunk::Pos &a_pos,
208 Chunk &b, const Chunk::Pos &b_pos
210 if (a.GetLight(a_pos) > 1) {
211 const BlockType &b_type = b.Type(Chunk::ToIndex(b_pos));
212 if (!b_type.block_light) {
213 light_queue.emplace(&a, a_pos);
215 if (b_type.visible) {
223 void Chunk::SetNeighbor(Block::Face face, Chunk &other) noexcept {
224 neighbor[face] = &other;
225 other.neighbor[Block::Opposite(face)] = this;
233 case Block::FACE_LEFT:
234 for (int z = 0; z < depth; ++z) {
235 for (int y = 0; y < height; ++y) {
237 Pos other_pos(width - 1, y, z);
238 edge_light(*this, my_pos, other, other_pos);
239 edge_light(other, other_pos, *this, my_pos);
244 case Block::FACE_RIGHT:
245 for (int z = 0; z < depth; ++z) {
246 for (int y = 0; y < height; ++y) {
247 Pos my_pos(width - 1, y, z);
248 Pos other_pos(0, y, z);
249 edge_light(*this, my_pos, other, other_pos);
250 edge_light(other, other_pos, *this, my_pos);
255 case Block::FACE_DOWN:
256 for (int z = 0; z < depth; ++z) {
257 for (int x = 0; x < width; ++x) {
259 Pos other_pos(x, height - 1, z);
260 edge_light(*this, my_pos, other, other_pos);
261 edge_light(other, other_pos, *this, my_pos);
267 for (int z = 0; z < depth; ++z) {
268 for (int x = 0; x < width; ++x) {
269 Pos my_pos(x, height - 1, z);
270 Pos other_pos(x, 0, z);
271 edge_light(*this, my_pos, other, other_pos);
272 edge_light(other, other_pos, *this, my_pos);
277 case Block::FACE_BACK:
278 for (int y = 0; y < height; ++y) {
279 for (int x = 0; x < width; ++x) {
281 Pos other_pos(x, y, depth - 1);
282 edge_light(*this, my_pos, other, other_pos);
283 edge_light(other, other_pos, *this, my_pos);
288 case Block::FACE_FRONT:
289 for (int y = 0; y < height; ++y) {
290 for (int x = 0; x < width; ++x) {
291 Pos my_pos(x, y, depth - 1);
292 Pos other_pos(x, y, 0);
293 edge_light(*this, my_pos, other, other_pos);
294 edge_light(other, other_pos, *this, my_pos);
303 void Chunk::Unlink() noexcept {
304 for (int face = 0; face < Block::FACE_COUNT; ++face) {
305 if (neighbor[face]) {
306 neighbor[face]->neighbor[Block::Opposite(Block::Face(face))] = nullptr;
307 neighbor[face] = nullptr;
313 void Chunk::SetLight(int index, int level) noexcept {
314 if (light[index] != level) {
315 light[index] = level;
320 int Chunk::GetLight(int index) const noexcept {
324 float Chunk::GetVertexLight(const Pos &pos, const BlockModel::Position &vtx, const EntityModel::Normal &norm) const noexcept {
325 int index = ToIndex(pos);
326 float light = GetLight(index);
328 Block::Face direct_face(Block::NormalFace(norm));
330 BlockLookup direct(const_cast<Chunk *>(this), pos, Block::NormalFace(norm));
332 float direct_light = direct.GetLight();
333 if (direct_light > light) {
334 light = direct_light;
340 if (Type(BlockAt(index)).luminosity > 0 || direct.GetType().block_light) {
345 switch (Block::Axis(direct_face)) {
347 edge[0] = (vtx.y - pos.y) > 0.5f ? Block::FACE_UP : Block::FACE_DOWN;
348 edge[1] = (vtx.z - pos.z) > 0.5f ? Block::FACE_FRONT : Block::FACE_BACK;
351 edge[0] = (vtx.z - pos.z) > 0.5f ? Block::FACE_FRONT : Block::FACE_BACK;
352 edge[1] = (vtx.x - pos.x) > 0.5f ? Block::FACE_RIGHT : Block::FACE_LEFT;
355 edge[0] = (vtx.x - pos.x) > 0.5f ? Block::FACE_RIGHT : Block::FACE_LEFT;
356 edge[1] = (vtx.y - pos.y) > 0.5f ? Block::FACE_UP : Block::FACE_DOWN;
363 BlockLookup next[2] = {
364 direct.Next(edge[0]),
365 direct.Next(edge[1]),
369 if (next[0].GetType().block_light) {
372 light += next[0].GetLight();
377 if (next[1].GetType().block_light) {
380 light += next[1].GetLight();
386 BlockLookup corner = next[0].Next(edge[1]);
388 if (corner.GetType().block_light) {
391 light += corner.GetLight();
395 } else if (next[1]) {
396 BlockLookup corner = next[1].Next(edge[0]);
398 if (corner.GetType().block_light) {
401 light += corner.GetLight();
410 return (light / num) - (occlusion * 0.8f);
414 bool Chunk::IsSurface(const Pos &pos) const noexcept {
415 const Block &block = BlockAt(pos);
416 if (!Type(block).visible) {
419 for (int face = 0; face < Block::FACE_COUNT; ++face) {
420 BlockLookup next = BlockLookup(const_cast<Chunk *>(this), pos, Block::Face(face));
421 if (!next || !next.GetType().visible) {
429 bool Chunk::Intersection(
437 coll.depth = std::numeric_limits<float>::infinity();
438 for (int z = 0; z < depth; ++z) {
439 for (int y = 0; y < height; ++y) {
440 for (int x = 0; x < width; ++x, ++idx) {
441 const BlockType &type = Type(idx);
447 if (type.shape->Intersects(ray, M * ToTransform(Pos(x, y, z), idx), cur_dist, cur_norm)) {
448 if (cur_dist < coll.depth) {
450 coll.depth = cur_dist;
451 coll.normal = cur_norm;
458 if (coll.block < 0) {
461 coll.normal = glm::vec3(BlockAt(coll.block).Transform() * glm::vec4(coll.normal, 0.0f));
466 bool Chunk::Intersection(
468 const glm::mat4 &Mbox,
469 const glm::mat4 &Mchunk,
470 std::vector<WorldCollision> &col
476 if (!blank::Intersection(box, Mbox, Bounds(), Mchunk, penetration, normal)) {
479 for (int idx = 0, z = 0; z < depth; ++z) {
480 for (int y = 0; y < height; ++y) {
481 for (int x = 0; x < width; ++x, ++idx) {
482 const BlockType &type = Type(idx);
483 if (!type.collision) {
486 if (type.shape->Intersects(Mchunk * ToTransform(Pos(x, y, z), idx), box, Mbox, penetration, normal)) {
487 col.emplace_back(this, idx, penetration, normal);
499 BlockModel::Buffer buf;
503 void Chunk::Update(BlockModel &model) noexcept {
504 int vtx_count = 0, idx_count = 0;
505 for (const auto &block : blocks) {
506 const Shape *shape = Type(block).shape;
507 vtx_count += shape->VertexCount();
508 idx_count += shape->VertexIndexCount();
511 buf.Reserve(vtx_count, idx_count);
514 BlockModel::Index vtx_counter = 0;
515 for (size_t z = 0; z < depth; ++z) {
516 for (size_t y = 0; y < height; ++y) {
517 for (size_t x = 0; x < width; ++x, ++idx) {
518 const BlockType &type = Type(BlockAt(idx));
519 const Pos pos(x, y, z);
521 if (!type.visible || Obstructed(pos).All()) continue;
523 type.FillBlockModel(buf, ToTransform(pos, idx), vtx_counter);
524 size_t vtx_begin = vtx_counter;
525 vtx_counter += type.shape->VertexCount();
527 for (size_t vtx = vtx_begin; vtx < vtx_counter; ++vtx) {
528 buf.lights.emplace_back(GetVertexLight(
531 type.shape->VertexNormal(vtx - vtx_begin, BlockAt(idx).Transform())
542 Block::FaceSet Chunk::Obstructed(const Pos &pos) const noexcept {
543 Block::FaceSet result;
545 for (int f = 0; f < Block::FACE_COUNT; ++f) {
546 Block::Face face = Block::Face(f);
547 BlockLookup next(const_cast<Chunk *>(this), pos, face);
548 if (next && next.GetType().FaceFilled(next.GetBlock(), Block::Opposite(face))) {
556 glm::mat4 Chunk::ToTransform(const Pos &pos, int idx) const noexcept {
557 return glm::translate(ToCoords(pos)) * BlockAt(idx).Transform();
561 BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p) noexcept
563 while (pos.x >= Chunk::width) {
564 if (chunk->HasNeighbor(Block::FACE_RIGHT)) {
565 chunk = &chunk->GetNeighbor(Block::FACE_RIGHT);
566 pos.x -= Chunk::width;
573 if (chunk->HasNeighbor(Block::FACE_LEFT)) {
574 chunk = &chunk->GetNeighbor(Block::FACE_LEFT);
575 pos.x += Chunk::width;
581 while (pos.y >= Chunk::height) {
582 if (chunk->HasNeighbor(Block::FACE_UP)) {
583 chunk = &chunk->GetNeighbor(Block::FACE_UP);
584 pos.y -= Chunk::height;
591 if (chunk->HasNeighbor(Block::FACE_DOWN)) {
592 chunk = &chunk->GetNeighbor(Block::FACE_DOWN);
593 pos.y += Chunk::height;
599 while (pos.z >= Chunk::depth) {
600 if (chunk->HasNeighbor(Block::FACE_FRONT)) {
601 chunk = &chunk->GetNeighbor(Block::FACE_FRONT);
602 pos.z -= Chunk::depth;
609 if (chunk->HasNeighbor(Block::FACE_BACK)) {
610 chunk = &chunk->GetNeighbor(Block::FACE_BACK);
611 pos.z += Chunk::depth;
619 BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p, Block::Face face) noexcept
621 pos += Block::FaceNormal(face);
622 if (!Chunk::InBounds(pos)) {
623 pos -= Block::FaceNormal(face) * Chunk::Extent();
624 chunk = &chunk->GetNeighbor(face);
629 ChunkLoader::ChunkLoader(
631 const Generator &gen,
632 const WorldSave &save
640 void ChunkLoader::Update(int dt) {
641 // check if there's chunks waiting to be loaded
642 // load until one of load or generation limits was hit
643 constexpr int max_load = 10;
644 constexpr int max_gen = 1;
647 while (loaded < max_load && generated < max_gen && store.HasMissing()) {
655 // store a few chunks as well
656 constexpr int max_save = 10;
658 for (Chunk &chunk : store) {
659 if (chunk.ShouldUpdateSave()) {
662 if (saved >= max_save) {
669 int ChunkLoader::ToLoad() const noexcept {
670 return store.EstimateMissing();
673 bool ChunkLoader::LoadOne() {
674 if (!store.HasMissing()) return false;
676 Chunk::Pos pos = store.NextMissing();
677 Chunk *chunk = store.Allocate(pos);
679 // chunk store corrupted?
683 if (save.Exists(pos)) {
692 void ChunkLoader::LoadN(std::size_t n) {
693 std::size_t end = std::min(n, std::size_t(ToLoad()));
694 for (std::size_t i = 0; i < end && store.HasMissing(); ++i) {
700 ChunkRenderer::ChunkRenderer(ChunkIndex &index)
702 , models(index.TotalChunks())
704 , fog_density(0.0f) {
708 ChunkRenderer::~ChunkRenderer() {
712 int ChunkRenderer::MissingChunks() const noexcept {
713 return index.MissingChunks();
716 void ChunkRenderer::LoadTextures(const AssetLoader &loader, const TextureIndex &tex_index) {
718 loader.LoadTextures(tex_index, block_tex);
719 block_tex.FilterNearest();
722 void ChunkRenderer::Update(int dt) {
723 for (int i = 0, updates = 0; updates < dt && i < index.TotalChunks(); ++i) {
724 if (index[i] && index[i]->ShouldUpdateModel()) {
725 index[i]->Update(models[i]);
731 void ChunkRenderer::Render(Viewport &viewport) {
732 BlockLighting &chunk_prog = viewport.ChunkProgram();
733 chunk_prog.SetTexture(block_tex);
734 chunk_prog.SetFogDensity(fog_density);
736 for (int i = 0; i < index.TotalChunks(); ++i) {
737 if (!index[i]) continue;
738 glm::mat4 m(index[i]->Transform(index.Base()));
739 glm::mat4 mvp(chunk_prog.GetVP() * m);
740 if (!CullTest(Chunk::Bounds(), mvp)) {
741 if (index[i]->ShouldUpdateModel()) {
742 index[i]->Update(models[i]);
751 ChunkIndex::ChunkIndex(ChunkStore &store, const Chunk::Pos &base, int extent)
755 , side_length(2 * extent + 1)
756 , total_length(side_length * side_length * side_length)
759 , stride(1, side_length, side_length * side_length)
760 , chunks(total_length, nullptr) {
764 ChunkIndex::~ChunkIndex() {
768 bool ChunkIndex::InRange(const Chunk::Pos &pos) const noexcept {
769 return manhattan_radius(pos - base) <= extent;
772 int ChunkIndex::IndexOf(const Chunk::Pos &pos) const noexcept {
778 return mod_pos.x * stride.x
779 + mod_pos.y * stride.y
780 + mod_pos.z * stride.z;
783 Chunk::Pos ChunkIndex::PositionOf(int i) const noexcept {
785 (i / stride.x) % side_length,
786 (i / stride.y) % side_length,
787 (i / stride.z) % side_length
789 Chunk::Pos zero_base(
794 Chunk::Pos base_relative(zero_pos - zero_base);
795 if (base_relative.x > extent) base_relative.x -= side_length;
796 else if (base_relative.x < -extent) base_relative.x += side_length;
797 if (base_relative.y > extent) base_relative.y -= side_length;
798 else if (base_relative.y < -extent) base_relative.y += side_length;
799 if (base_relative.z > extent) base_relative.z -= side_length;
800 else if (base_relative.z < -extent) base_relative.z += side_length;
801 return base + base_relative;
804 Chunk *ChunkIndex::Get(const Chunk::Pos &pos) noexcept {
806 return chunks[IndexOf(pos)];
812 const Chunk *ChunkIndex::Get(const Chunk::Pos &pos) const noexcept {
814 return chunks[IndexOf(pos)];
820 void ChunkIndex::Rebase(const Chunk::Pos &new_base) {
821 if (new_base == base) return;
823 Chunk::Pos diff(new_base - base);
825 if (manhattan_radius(diff) > extent) {
826 // that's more than half, so probably not worth shifting
835 Shift(Block::FACE_RIGHT);
839 Shift(Block::FACE_LEFT);
843 Shift(Block::FACE_UP);
847 Shift(Block::FACE_DOWN);
851 Shift(Block::FACE_FRONT);
855 Shift(Block::FACE_BACK);
861 int ChunkIndex::GetCol(int c) const noexcept {
863 if (c < 0) c += side_length;
867 void ChunkIndex::Shift(Block::Face f) {
868 int a_axis = Block::Axis(f);
869 int b_axis = (a_axis + 1) % 3;
870 int c_axis = (a_axis + 2) % 3;
871 int dir = Block::Direction(f);
873 int a = GetCol(base[a_axis] + (extent * dir));
874 int a_stride = a * stride[a_axis];
875 for (int b = 0; b < side_length; ++b) {
876 int b_stride = b * stride[b_axis];
877 for (int c = 0; c < side_length; ++c) {
878 int bc_stride = b_stride + c * stride[c_axis];
879 int index = a_stride + bc_stride;
881 int neighbor = ((a - dir + side_length) % side_length) * stride[a_axis] + bc_stride;
882 if (chunks[neighbor] && chunks[neighbor]->HasNeighbor(f)) {
883 Set(index, chunks[neighbor]->GetNeighbor(f));
889 void ChunkIndex::Clear() noexcept {
890 for (int i = 0; i < total_length && total_indexed > 0; ++i) {
895 void ChunkIndex::Scan() noexcept {
896 for (Chunk &chunk : store) {
901 void ChunkIndex::Register(Chunk &chunk) noexcept {
902 if (InRange(chunk.Position())) {
903 Set(IndexOf(chunk.Position()), chunk);
907 void ChunkIndex::Set(int index, Chunk &chunk) noexcept {
909 chunks[index] = &chunk;
914 void ChunkIndex::Unset(int index) noexcept {
916 chunks[index]->UnRef();
917 chunks[index] = nullptr;
922 Chunk::Pos ChunkIndex::NextMissing() noexcept {
923 int roundtrip = last_missing;
924 while (chunks[last_missing]) {
926 last_missing %= total_length;
927 if (last_missing == roundtrip) {
931 return PositionOf(last_missing);
935 ChunkStore::ChunkStore(const BlockTypeRegistry &types)
943 ChunkStore::~ChunkStore() {
947 ChunkIndex &ChunkStore::MakeIndex(const Chunk::Pos &pos, int extent) {
948 indices.emplace_back(*this, pos, extent);
949 return indices.back();
952 void ChunkStore::UnregisterIndex(ChunkIndex &index) {
953 for (auto i = indices.begin(), end = indices.end(); i != end; ++i) {
963 Chunk *ChunkStore::Get(const Chunk::Pos &pos) {
964 for (ChunkIndex &index : indices) {
965 Chunk *chunk = index.Get(pos);
973 Chunk *ChunkStore::Allocate(const Chunk::Pos &pos) {
974 Chunk *chunk = Get(pos);
979 loaded.emplace(loaded.begin(), types);
981 loaded.splice(loaded.begin(), free, free.begin());
982 loaded.front().Unlink();
984 chunk = &loaded.front();
985 chunk->Position(pos);
986 for (ChunkIndex &index : indices) {
987 if (index.InRange(pos)) {
988 index.Register(*chunk);
991 for (int i = 0; i < Block::FACE_COUNT; ++i) {
992 Block::Face face = Block::Face(i);
993 Chunk::Pos neighbor_pos(pos + Block::FaceNormal(face));
994 Chunk *neighbor = Get(neighbor_pos);
996 chunk->SetNeighbor(face, *neighbor);
1002 bool ChunkStore::HasMissing() const noexcept {
1003 for (const ChunkIndex &index : indices) {
1004 if (index.MissingChunks() > 0) {
1011 int ChunkStore::EstimateMissing() const noexcept {
1013 for (const ChunkIndex &index : indices) {
1014 missing += index.MissingChunks();
1019 Chunk::Pos ChunkStore::NextMissing() noexcept {
1020 for (ChunkIndex &index : indices) {
1021 if (index.MissingChunks()) {
1022 return index.NextMissing();
1025 return Chunk::Pos(0, 0, 0);
1028 void ChunkStore::Clean() {
1029 for (auto i = loaded.begin(), end = loaded.end(); i != end;) {
1030 if (i->Referenced()) {
1035 free.splice(free.end(), loaded, chunk);
1037 chunk->InvalidateModel();