#include "World.hpp" #include "../contouring/Dummy.hpp" #include World::World(const World::options &options): loadPool(2), contouring(std::make_shared()) { setOptions(options); } World::~World() { contouring = NULL; } World::LoadPool::LoadPool(size_t count) { for (size_t i = 0; i < count; i++) { workers.push_back(std::thread([&] { while (running) { chunk_pos ctx; loadQueue.wait(); if (loadQueue.pop(ctx)) { rmt_ScopedCPUSample(ProcessGenerate, 0); loadedQueue.push({ctx, std::make_shared(ctx, generator)}); } } })); } } World::LoadPool::~LoadPool() { running = false; loadQueue.notify(); for (auto &worker : workers) { if (worker.joinable()) worker.join(); } } inline void World::LoadPool::push(const chunk_pos &pos, int weight) { loadQueue.push(pos, weight); } inline bool World::LoadPool::pop(robin_hood::pair> &out) { return loadedQueue.pop(out); } inline size_t World::LoadPool::size() { return loadQueue.size(); } void World::update(const camera_pos& pos, World::report& rep) { const chunk_pos newPos = glm::divide(pos, chunk_voxel_pos(CHUNK_LENGTH)); const auto chunkChange = newPos != last_pos; last_pos = newPos; rmt_ScopedCPUSample(World, 0); // Update alive chunks { rmt_ScopedCPUSample(Update, 0); for (auto &[chunkPos, chunk]: chunks) { if (glm::length2(last_pos - chunkPos) > keepDistance * keepDistance && unloadQueue.push(chunkPos)) { //TODO: unloadCount++; continue; } if (const auto neighbors = chunk->update()) { contouring->onUpdate(chunkPos, chunks, neighbors.value()); //TODO: get update update_type(simple(pos), complex) } else if (chunkChange) { //NOTE: must be solved before octrees contouring->onNotify(chunkPos, chunks); } } } { rmt_ScopedCPUSample(Contouring, 0); contouring->update(pos); } rep.chunk_unload.push(unloadQueue.size()); // Unload dead chunks { rmt_ScopedCPUSample(Unload, 0); for (size_t i = 0; i < 256 && !unloadQueue.empty(); i++) { chunks.erase(unloadQueue.pop()); //TODO: save to file } } // Find missing chunks if(chunkChange) { rmt_ScopedCPUSample(ToLoad, 0); //TODO: circle point algo for (int x = -loadDistance; x <= loadDistance; x++) { for (int y = -loadDistance; y <= loadDistance; y++) { for (int z = -loadDistance; z <= loadDistance; z++) { const auto dist2 = x * x + y * y + z * z; if (dist2 <= loadDistance * loadDistance) { const chunk_pos p = last_pos + glm::ivec3(x, y, z); if (chunks.find(p) == chunks.end()) { loadPool.push(p, -dist2); } } }}} } rep.chunk_load.push(loadPool.size()); // Loaded chunks { rmt_ScopedCPUSample(Load, 0); robin_hood::pair> loaded; while (loadPool.pop(loaded)) { chunks.insert(loaded); contouring->onUpdate(loaded.first, chunks, Faces::All); } } rep.chunk_count.push(chunks.size()); } void World::setOptions(const World::options& options) { loadDistance = options.loadDistance; keepDistance = options.keepDistance; } void World::setContouring(std::shared_ptr ct) { contouring = ct; last_pos = chunk_pos(INT_MAX); // trigger chunkChange on next update } std::optional> World::raycast(const Ray &ray) const { std::vector points; ray.grid(points); std::shared_ptr chunk = NULL; chunk_pos chunk_pos(INT_MAX); for(auto point: points) { const auto pos = glm::divide(point, glm::ivec3(CHUNK_LENGTH)); if(pos != chunk_pos) { if(const auto& newChunk = at(pos)) { chunk = newChunk.value(); chunk_pos = pos; } } if(chunk != NULL) { const auto voxel = chunk->getAt(glm::modulo(point, glm::uvec3(CHUNK_LENGTH))); if(voxel.Density > 0) return {{point, voxel}}; } } return {}; } std::optional World::set(const voxel_pos& pos, const Voxel& val) { const auto chunkPos = glm::divide(pos, glm::ivec3(CHUNK_LENGTH)); if(const auto& chunk = at(chunkPos)) { return {chunk.value()->breakAt(glm::modulo(pos, glm::ivec3(CHUNK_LENGTH)), val)}; } else { return {}; } } ItemList World::setCube(const voxel_pos& pos, const Voxel& val, int radius) { ItemList list; for (int z = -radius; z <= radius; z++) { for (int y = -radius; y <= radius; y++) { for (int x = -radius; x <= radius; x++) { //TODO: list.pop(val) list.add(set(pos + glm::lvec3(x, y, z), val)); }}} return list; }