+Chunk *World::ChunkLoaded(const Chunk::Pos &pos) {
+ for (Chunk &chunk : loaded) {
+ if (chunk.Position() == pos) {
+ return &chunk;
+ }
+ }
+ return nullptr;
+}
+
+Chunk *World::ChunkQueued(const Chunk::Pos &pos) {
+ for (Chunk &chunk : to_generate) {
+ if (chunk.Position() == pos) {
+ return &chunk;
+ }
+ }
+ return nullptr;
+}
+
+Chunk *World::ChunkAvailable(const Chunk::Pos &pos) {
+ Chunk *chunk = ChunkLoaded(pos);
+ if (chunk) return chunk;
+
+ return ChunkQueued(pos);
+}
+
+Chunk &World::Next(const Chunk &to, const glm::tvec3<int> &dir) {
+ const Chunk::Pos tgt_pos = to.Position() + dir;
+
+ Chunk *chunk = ChunkLoaded(tgt_pos);
+ if (chunk) {
+ return *chunk;
+ }
+
+ chunk = ChunkQueued(tgt_pos);
+ if (chunk) {
+ Generate(*chunk);
+ return *chunk;
+ }
+
+ loaded.emplace_back(blockType);
+ loaded.back().Position(tgt_pos);
+ Generate(loaded.back());
+ return loaded.back();
+}
+
+
+void World::Update(int dt) {
+ player.Update(dt);
+
+ CheckChunkGeneration();
+}
+
+void World::CheckChunkGeneration() {
+ if (player.ChunkCoords() != player_chunk) {
+ player_chunk = player.ChunkCoords();
+
+ constexpr int max_dist = 8;
+ // unload far away chunks
+ for (auto iter(loaded.begin()), end(loaded.end()); iter != end;) {
+ if (std::abs(player_chunk.x - iter->Position().x) > max_dist
+ || std::abs(player_chunk.y - iter->Position().y) > max_dist
+ || std::abs(player_chunk.z - iter->Position().z) > max_dist) {
+ auto saved = iter;
+ ++iter;
+ to_free.splice(to_free.end(), loaded, saved);
+ } else {
+ ++iter;
+ }
+ }
+ // abort far away queued chunks
+ for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end;) {
+ if (std::abs(player_chunk.x - iter->Position().x) > max_dist
+ || std::abs(player_chunk.y - iter->Position().y) > max_dist
+ || std::abs(player_chunk.z - iter->Position().z) > max_dist) {
+ iter = to_generate.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+ // add missing new chunks
+ const Chunk::Pos offset(max_dist, max_dist, max_dist);
+ Generate(player_chunk - offset, player_chunk + offset);
+ }
+
+ if (!to_generate.empty()) {
+ Generate(to_generate.front());
+ loaded.splice(loaded.end(), to_generate, to_generate.begin());
+ }
+
+ if (!to_free.empty()) {
+ to_free.pop_front();
+ }
+}
+
+
+void World::Render(DirectionalLighting &program) {
+ program.SetLightDirection({ -1.0f, -3.0f, -2.0f });
+ program.SetView(glm::inverse(player.Transform(player.ChunkCoords())));
+
+ for (Chunk &chunk : LoadedChunks()) {
+ glm::mat4 m(chunk.Transform(player.ChunkCoords()));
+ program.SetM(m);
+ glm::mat4 mvp(program.GetVP() * m);
+ if (!CullTest(Chunk::Bounds(), mvp)) {
+ chunk.Draw();