]> git.localhorst.tv Git - blank.git/blobdiff - src/world/chunk.cpp
use collision structures for ray tests
[blank.git] / src / world / chunk.cpp
index c421be9d679fa45e0e715ab2c2feeddf7b3230cf..83c94e3403a14ddd97283739dfe34e0e50fa9696 100644 (file)
@@ -4,10 +4,11 @@
 
 #include "Generator.hpp"
 #include "WorldCollision.hpp"
+#include "../io/WorldSave.hpp"
 
 #include <algorithm>
-#include <iostream>
 #include <limits>
+#include <ostream>
 #include <queue>
 
 
@@ -26,7 +27,8 @@ Chunk::Chunk(const BlockTypeRegistry &types) noexcept
 , light{0}
 , model()
 , position(0, 0, 0)
-, dirty(false) {
+, dirty_model(false)
+, dirty_save(false) {
 
 }
 
@@ -34,7 +36,8 @@ Chunk::Chunk(Chunk &&other) noexcept
 : types(other.types)
 , model(std::move(other.model))
 , position(other.position)
-, dirty(other.dirty) {
+, dirty_model(other.dirty_model)
+, dirty_save(other.dirty_save) {
        std::copy(other.neighbor, other.neighbor + sizeof(neighbor), neighbor);
        std::copy(other.blocks, other.blocks + sizeof(blocks), blocks);
        std::copy(other.light, other.light + sizeof(light), light);
@@ -47,7 +50,8 @@ Chunk &Chunk::operator =(Chunk &&other) noexcept {
        std::copy(other.light, other.light + sizeof(light), light);
        model = std::move(other.model);
        position = other.position;
-       dirty = other.dirty;
+       dirty_model = other.dirty_save;
+       dirty_save = other.dirty_save;
        return *this;
 }
 
@@ -148,6 +152,7 @@ void Chunk::SetBlock(int index, const Block &block) noexcept {
        const BlockType &new_type = Type(block);
 
        blocks[index] = block;
+       Invalidate();
 
        if (&old_type == &new_type) return;
 
@@ -319,7 +324,7 @@ int Chunk::GetLight(int index) const noexcept {
        return light[index];
 }
 
-float Chunk::GetVertexLight(const Pos &pos, const BlockModel::Position &vtx, const Model::Normal &norm) const noexcept {
+float Chunk::GetVertexLight(const Pos &pos, const BlockModel::Position &vtx, const EntityModel::Normal &norm) const noexcept {
        int index = ToIndex(pos);
        float light = GetLight(index);
 
@@ -425,7 +430,7 @@ bool Chunk::IsSurface(const Pos &pos) const noexcept {
 
 
 void Chunk::Draw() noexcept {
-       if (dirty) {
+       if (ShouldUpdateModel()) {
                Update();
        }
        model.Draw();
@@ -435,13 +440,12 @@ void Chunk::Draw() noexcept {
 bool Chunk::Intersection(
        const Ray &ray,
        const glm::mat4 &M,
-       int &blkid,
-       float &dist,
-       glm::vec3 &normal
-) const noexcept {
+       WorldCollision &coll
+) noexcept {
        int idx = 0;
-       blkid = -1;
-       dist = std::numeric_limits<float>::infinity();
+       coll.chunk = this;
+       coll.block = -1;
+       coll.depth = std::numeric_limits<float>::infinity();
        for (int z = 0; z < depth; ++z) {
                for (int y = 0; y < height; ++y) {
                        for (int x = 0; x < width; ++x, ++idx) {
@@ -452,20 +456,20 @@ bool Chunk::Intersection(
                                float cur_dist;
                                glm::vec3 cur_norm;
                                if (type.shape->Intersects(ray, M * ToTransform(Pos(x, y, z), idx), cur_dist, cur_norm)) {
-                                       if (cur_dist < dist) {
-                                               blkid = idx;
-                                               dist = cur_dist;
-                                               normal = cur_norm;
+                                       if (cur_dist < coll.depth) {
+                                               coll.block = idx;
+                                               coll.depth = cur_dist;
+                                               coll.normal = cur_norm;
                                        }
                                }
                        }
                }
        }
 
-       if (blkid < 0) {
+       if (coll.block < 0) {
                return false;
        } else {
-               normal = glm::vec3(BlockAt(blkid).Transform() * glm::vec4(normal, 0.0f));
+               coll.normal = glm::vec3(BlockAt(coll.block).Transform() * glm::vec4(coll.normal, 0.0f));
                return true;
        }
 }
@@ -475,7 +479,7 @@ bool Chunk::Intersection(
        const glm::mat4 &Mbox,
        const glm::mat4 &Mchunk,
        std::vector<WorldCollision> &col
-) const noexcept {
+) noexcept {
        bool any = false;
        float penetration;
        glm::vec3 normal;
@@ -508,7 +512,7 @@ BlockModel::Buffer buf;
 }
 
 void Chunk::CheckUpdate() noexcept {
-       if (dirty) {
+       if (ShouldUpdateModel()) {
                Update();
        }
 }
@@ -549,7 +553,7 @@ void Chunk::Update() noexcept {
        }
 
        model.Update(buf);
-       dirty = false;
+       ClearModel();
 }
 
 Block::FaceSet Chunk::Obstructed(const Pos &pos) const noexcept {
@@ -639,12 +643,18 @@ BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p, Block::Face face) noexce
 }
 
 
-ChunkLoader::ChunkLoader(const Config &config, const BlockTypeRegistry &reg, const Generator &gen) noexcept
+ChunkLoader::ChunkLoader(
+       const Config &config,
+       const BlockTypeRegistry &reg,
+       const Generator &gen,
+       const WorldSave &save
+) noexcept
 : base(0, 0, 0)
 , reg(reg)
 , gen(gen)
+, save(save)
 , loaded()
-, to_generate()
+, to_load()
 , to_free()
 , gen_timer(config.gen_limit)
 , load_dist(config.load_dist)
@@ -673,7 +683,7 @@ struct ChunkLess {
 
 }
 
-void ChunkLoader::Generate(const Chunk::Pos &from, const Chunk::Pos &to) {
+void ChunkLoader::Queue(const Chunk::Pos &from, const Chunk::Pos &to) {
        for (int z = from.z; z < to.z; ++z) {
                for (int y = from.y; y < to.y; ++y) {
                        for (int x = from.x; x < to.x; ++x) {
@@ -681,7 +691,7 @@ void ChunkLoader::Generate(const Chunk::Pos &from, const Chunk::Pos &to) {
                                if (Known(pos)) {
                                        continue;
                                } else if (pos == base) {
-                                       Generate(pos);
+                                       Load(pos);
 
                                //      light testing
                                //      for (int i = 0; i < 16; ++i) {
@@ -728,19 +738,23 @@ void ChunkLoader::Generate(const Chunk::Pos &from, const Chunk::Pos &to) {
                                //      loaded.back().Invalidate();
                                //      loaded.back().CheckUpdate();
                                } else {
-                                       to_generate.emplace_back(pos);
+                                       to_load.emplace_back(pos);
                                }
                        }
                }
        }
-       to_generate.sort(ChunkLess(base));
+       to_load.sort(ChunkLess(base));
 }
 
-Chunk &ChunkLoader::Generate(const Chunk::Pos &pos) {
+Chunk &ChunkLoader::Load(const Chunk::Pos &pos) {
        loaded.emplace_back(reg);
        Chunk &chunk = loaded.back();
        chunk.Position(pos);
-       gen(chunk);
+       if (save.Exists(pos)) {
+               save.Read(chunk);
+       } else {
+               gen(chunk);
+       }
        Insert(chunk);
        return chunk;
 }
@@ -757,6 +771,10 @@ std::list<Chunk>::iterator ChunkLoader::Remove(std::list<Chunk>::iterator chunk)
        ++next;
        // unlink neighbors so they won't reference a dead chunk
        chunk->ClearNeighbors();
+       // if it should be saved, do it now
+       if (chunk->ShouldUpdateSave()) {
+               save.Write(*chunk);
+       }
        // and move it from loaded to free list
        to_free.splice(to_free.end(), loaded, chunk);
        return next;
@@ -772,7 +790,7 @@ Chunk *ChunkLoader::Loaded(const Chunk::Pos &pos) noexcept {
 }
 
 bool ChunkLoader::Queued(const Chunk::Pos &pos) noexcept {
-       for (const Chunk::Pos &chunk : to_generate) {
+       for (const Chunk::Pos &chunk : to_load) {
                if (chunk == pos) {
                        return true;
                }
@@ -791,14 +809,14 @@ Chunk &ChunkLoader::ForceLoad(const Chunk::Pos &pos) {
                return *chunk;
        }
 
-       for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end; ++iter) {
+       for (auto iter(to_load.begin()), end(to_load.end()); iter != end; ++iter) {
                if (*iter == pos) {
-                       to_generate.erase(iter);
+                       to_load.erase(iter);
                        break;
                }
        }
 
-       return Generate(pos);
+       return Load(pos);
 }
 
 bool ChunkLoader::OutOfRange(const Chunk::Pos &pos) const noexcept {
@@ -822,40 +840,75 @@ void ChunkLoader::Rebase(const Chunk::Pos &new_base) {
                }
        }
        // abort far away queued chunks
-       for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end;) {
+       for (auto iter(to_load.begin()), end(to_load.end()); iter != end;) {
                if (OutOfRange(*iter)) {
-                       iter = to_generate.erase(iter);
+                       iter = to_load.erase(iter);
                } else {
                        ++iter;
                }
        }
        // add missing new chunks
-       GenerateSurrounding(base);
+       QueueSurrounding(base);
 }
 
-void ChunkLoader::GenerateSurrounding(const Chunk::Pos &pos) {
+void ChunkLoader::QueueSurrounding(const Chunk::Pos &pos) {
        const Chunk::Pos offset(load_dist, load_dist, load_dist);
-       Generate(pos - offset, pos + offset);
+       Queue(pos - offset, pos + offset);
 }
 
 void ChunkLoader::Update(int dt) {
-       // check if a chunk generation is scheduled for this frame
-       // and if there's a chunk waiting to be generated
+       // check if a chunk load is scheduled for this frame
+       // and if there's chunks waiting to be loaded
        gen_timer.Update(dt);
-       if (!gen_timer.Hit() || to_generate.empty()) {
-               return;
+       if (gen_timer.Hit()) {
+               // we may
+               // load until one of load or generation limits was hit
+               constexpr int max_load = 10;
+               constexpr int max_gen = 1;
+               int loaded = 0;
+               int generated = 0;
+               while (!to_load.empty() && loaded < max_load && generated < max_gen) {
+                       if (LoadOne()) {
+                               ++generated;
+                       } else {
+                               ++loaded;
+                       }
+               }
        }
 
+       constexpr int max_save = 10;
+       int saved = 0;
+       for (Chunk &chunk : loaded) {
+               if (chunk.ShouldUpdateSave()) {
+                       save.Write(chunk);
+                       ++saved;
+                       if (saved >= max_save) {
+                               break;
+                       }
+               }
+       }
+}
+
+void ChunkLoader::LoadN(std::size_t n) {
+       std::size_t end = std::min(n, ToLoad());
+       for (std::size_t i = 0; i < end; ++i) {
+               LoadOne();
+       }
+}
+
+bool ChunkLoader::LoadOne() {
+       if (to_load.empty()) return false;
+
        // take position of next chunk in queue
-       Chunk::Pos pos(to_generate.front());
-       to_generate.pop_front();
+       Chunk::Pos pos(to_load.front());
+       to_load.pop_front();
 
        // look if the same chunk was already generated and still lingering
        for (auto iter(to_free.begin()), end(to_free.end()); iter != end; ++iter) {
                if (iter->Position() == pos) {
                        loaded.splice(loaded.end(), to_free, iter);
                        Insert(loaded.back());
-                       return;
+                       return false;
                }
        }
 
@@ -868,10 +921,17 @@ void ChunkLoader::Update(int dt) {
                loaded.splice(loaded.end(), to_free, to_free.begin());
        }
 
+       bool generated = false;
        Chunk &chunk = loaded.back();
        chunk.Position(pos);
-       gen(chunk);
+       if (save.Exists(pos)) {
+               save.Read(chunk);
+       } else {
+               gen(chunk);
+               generated = true;
+       }
        Insert(chunk);
+       return generated;
 }
 
 }