2020-07-25 16:45:03 +00:00
|
|
|
#include "Universe.hpp"
|
2020-07-10 17:49:16 +00:00
|
|
|
|
2020-07-18 15:42:45 +00:00
|
|
|
#include "../contouring/Dummy.hpp"
|
2020-07-26 20:53:14 +00:00
|
|
|
#include "../data/geometry/Sphere.hpp"
|
2020-07-18 12:54:07 +00:00
|
|
|
#include <Remotery.h>
|
2020-07-26 20:53:14 +00:00
|
|
|
#include <filesystem>
|
|
|
|
#include <fstream>
|
2020-07-10 17:49:16 +00:00
|
|
|
|
2020-07-25 16:45:03 +00:00
|
|
|
using namespace world;
|
|
|
|
|
2020-07-26 20:53:14 +00:00
|
|
|
Universe::Universe(const Universe::options &options): loadPool(4, options.folderPath), savePool(2, options.folderPath),
|
|
|
|
contouring(std::make_shared<contouring::Dummy>()) {
|
2020-07-10 17:49:16 +00:00
|
|
|
setOptions(options);
|
|
|
|
}
|
2020-07-25 16:45:03 +00:00
|
|
|
Universe::~Universe() {
|
2020-07-18 15:42:45 +00:00
|
|
|
contouring = NULL;
|
2020-07-26 20:53:14 +00:00
|
|
|
|
|
|
|
// Save all
|
|
|
|
for(auto& pair: chunks) {
|
|
|
|
savePool.push(pair);
|
|
|
|
}
|
|
|
|
if(savePool.size() > 0) {
|
|
|
|
std::cout << "Saving..." << std::endl;
|
|
|
|
}
|
|
|
|
while(savePool.size() > 0) {
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
}
|
2020-07-18 15:42:45 +00:00
|
|
|
}
|
2020-07-10 17:49:16 +00:00
|
|
|
|
2020-07-26 20:53:14 +00:00
|
|
|
Universe::LoadPool::LoadPool(size_t count, const std::string& folderPath): folderPath(folderPath) {
|
2020-07-22 20:55:13 +00:00
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
workers.push_back(std::thread([&] {
|
|
|
|
while (running) {
|
|
|
|
chunk_pos ctx;
|
|
|
|
loadQueue.wait();
|
|
|
|
if (loadQueue.pop(ctx)) {
|
2020-07-26 20:53:14 +00:00
|
|
|
//MAYBE: loadQueue.take to avoid duplicated work on fast move
|
|
|
|
rmt_ScopedCPUSample(ProcessLoad, 0);
|
|
|
|
const auto path = folderPath + '/' + std::to_string(ctx.x) + '.' +
|
|
|
|
std::to_string(ctx.y) + '.' + std::to_string(ctx.z) + ".map";
|
|
|
|
std::ifstream str(path);
|
|
|
|
if(str.good()) {
|
|
|
|
rmt_ScopedCPUSample(ProcessRead, 0);
|
|
|
|
loadedQueue.push({ctx, std::make_shared<Chunk>(str)});
|
|
|
|
if(str.bad()) {
|
|
|
|
std::cout << "Fail reading '" << path << "'" << std::endl;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rmt_ScopedCPUSample(ProcessGenerate, 0);
|
|
|
|
loadedQueue.push({ctx, std::make_shared<Chunk>(ctx, generator)});
|
|
|
|
}
|
2020-07-22 20:55:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
2020-07-25 16:45:03 +00:00
|
|
|
Universe::LoadPool::~LoadPool() {
|
2020-07-22 20:55:13 +00:00
|
|
|
running = false;
|
|
|
|
loadQueue.notify();
|
|
|
|
|
|
|
|
for (auto &worker : workers) {
|
|
|
|
if (worker.joinable())
|
|
|
|
worker.join();
|
|
|
|
}
|
|
|
|
}
|
2020-07-25 16:45:03 +00:00
|
|
|
inline void Universe::LoadPool::push(const chunk_pos &pos, int weight) { loadQueue.push(pos, weight); }
|
|
|
|
inline bool Universe::LoadPool::pop(robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>> &out) { return loadedQueue.pop(out); }
|
|
|
|
inline size_t Universe::LoadPool::size() { return loadQueue.size(); }
|
2020-07-22 20:55:13 +00:00
|
|
|
|
2020-07-26 20:53:14 +00:00
|
|
|
Universe::SavePool::SavePool(size_t count, const std::string& folderPath): folderPath(folderPath) {
|
|
|
|
std::filesystem::create_directories(folderPath);
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
workers.push_back(std::thread([&] {
|
|
|
|
while (running) {
|
|
|
|
robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>> ctx;
|
|
|
|
queue.wait();
|
|
|
|
if (queue.pop(ctx) && ctx.second->isModified()) {
|
|
|
|
//MAYBE: queue.take to avoid concurent write or duplicated work on fast move
|
|
|
|
rmt_ScopedCPUSample(ProcessSave, 0);
|
|
|
|
const auto path = folderPath + '/' + std::to_string(ctx.first.x) + '.' +
|
|
|
|
std::to_string(ctx.first.y) + '.' + std::to_string(ctx.first.z) + ".map";
|
|
|
|
std::ofstream ofs(path);
|
|
|
|
if(!ofs.good()) {
|
|
|
|
std::cout << "Fail opening '" << path << "' to save" << std::endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ctx.second->write(ofs);
|
|
|
|
ofs.flush();
|
|
|
|
ofs.close();
|
|
|
|
if(!ofs.good()) {
|
|
|
|
std::cout << "Fail closing '" << path << "' to save" << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Universe::SavePool::~SavePool() {
|
|
|
|
running = false;
|
|
|
|
queue.notify();
|
|
|
|
|
|
|
|
for (auto &worker : workers) {
|
|
|
|
if (worker.joinable())
|
|
|
|
worker.join();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
inline void Universe::SavePool::push(const robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>> &pos) { queue.push(pos); }
|
|
|
|
inline size_t Universe::SavePool::size() { return queue.size(); }
|
|
|
|
|
2020-07-25 16:45:03 +00:00
|
|
|
void Universe::update(const camera_pos& pos, Universe::report& rep) {
|
2020-07-10 17:49:16 +00:00
|
|
|
const chunk_pos newPos = glm::divide(pos, chunk_voxel_pos(CHUNK_LENGTH));
|
2020-07-26 20:53:14 +00:00
|
|
|
const auto chunkChange = last_pos != newPos;
|
2020-07-10 17:49:16 +00:00
|
|
|
last_pos = newPos;
|
2020-07-25 16:45:03 +00:00
|
|
|
rmt_ScopedCPUSample(Universe, 0);
|
2020-07-10 17:49:16 +00:00
|
|
|
|
|
|
|
// Update alive chunks
|
|
|
|
{
|
2020-07-18 12:54:07 +00:00
|
|
|
rmt_ScopedCPUSample(Update, 0);
|
2020-07-26 20:53:14 +00:00
|
|
|
auto it = chunks.begin();
|
|
|
|
while (it != chunks.end()) {
|
|
|
|
if (glm::length2(last_pos - it->first) > keepDistance * keepDistance) {
|
|
|
|
savePool.push(*it);
|
|
|
|
it = chunks.erase(it);
|
|
|
|
} else {
|
|
|
|
if (const auto neighbors = it->second->update()) {
|
|
|
|
contouring->onUpdate(it->first, chunks, neighbors.value()); //TODO: get update update_type(simple(pos), complex)
|
|
|
|
} else if (chunkChange) {
|
|
|
|
contouring->onNotify(it->first, chunks);
|
|
|
|
}
|
|
|
|
++it;
|
2020-07-10 17:49:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-26 20:53:14 +00:00
|
|
|
rep.chunk_unload.push(savePool.size());
|
2020-07-22 20:55:13 +00:00
|
|
|
{
|
|
|
|
rmt_ScopedCPUSample(Contouring, 0);
|
|
|
|
contouring->update(pos);
|
2020-07-26 20:53:14 +00:00
|
|
|
//MAYBE: if(chunkChange) contouring->notify(chunks);
|
2020-07-10 17:49:16 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 20:55:13 +00:00
|
|
|
// Find missing chunks
|
2020-07-10 17:49:16 +00:00
|
|
|
if(chunkChange) {
|
2020-07-18 12:54:07 +00:00
|
|
|
rmt_ScopedCPUSample(ToLoad, 0);
|
2020-07-26 20:53:14 +00:00
|
|
|
//NOTE: need dist so no easy sphere fill
|
2020-07-10 17:49:16 +00:00
|
|
|
for (int x = -loadDistance; x <= loadDistance; x++) {
|
|
|
|
for (int y = -loadDistance; y <= loadDistance; y++) {
|
|
|
|
for (int z = -loadDistance; z <= loadDistance; z++) {
|
2020-07-22 20:55:13 +00:00
|
|
|
const auto dist2 = x * x + y * y + z * z;
|
|
|
|
if (dist2 <= loadDistance * loadDistance) {
|
2020-07-10 17:49:16 +00:00
|
|
|
const chunk_pos p = last_pos + glm::ivec3(x, y, z);
|
|
|
|
if (chunks.find(p) == chunks.end()) {
|
2020-07-22 20:55:13 +00:00
|
|
|
loadPool.push(p, -dist2);
|
2020-07-10 17:49:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}}}
|
|
|
|
}
|
|
|
|
|
2020-07-22 20:55:13 +00:00
|
|
|
rep.chunk_load.push(loadPool.size());
|
|
|
|
// Loaded chunks
|
2020-07-18 12:54:07 +00:00
|
|
|
{
|
|
|
|
rmt_ScopedCPUSample(Load, 0);
|
2020-07-23 19:39:08 +00:00
|
|
|
robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>> loaded;
|
2020-07-22 20:55:13 +00:00
|
|
|
while (loadPool.pop(loaded)) {
|
|
|
|
chunks.insert(loaded);
|
|
|
|
contouring->onUpdate(loaded.first, chunks, Faces::All);
|
2020-07-14 17:03:32 +00:00
|
|
|
}
|
2020-07-10 17:49:16 +00:00
|
|
|
}
|
|
|
|
rep.chunk_count.push(chunks.size());
|
|
|
|
}
|
2020-07-25 16:45:03 +00:00
|
|
|
void Universe::setOptions(const Universe::options& options) {
|
2020-07-10 17:49:16 +00:00
|
|
|
loadDistance = options.loadDistance;
|
|
|
|
keepDistance = options.keepDistance;
|
|
|
|
}
|
|
|
|
|
2020-07-25 16:45:03 +00:00
|
|
|
void Universe::setContouring(std::shared_ptr<contouring::Abstract> ct) {
|
2020-07-18 15:42:45 +00:00
|
|
|
contouring = ct;
|
|
|
|
last_pos = chunk_pos(INT_MAX); // trigger chunkChange on next update
|
2020-07-10 19:37:49 +00:00
|
|
|
}
|
2020-07-25 14:29:05 +00:00
|
|
|
|
2020-07-25 16:45:03 +00:00
|
|
|
std::optional<std::pair<voxel_pos, Voxel>> Universe::raycast(const Ray &ray) const {
|
2020-07-25 14:29:05 +00:00
|
|
|
std::vector<voxel_pos> points;
|
|
|
|
ray.grid(points);
|
|
|
|
std::shared_ptr<Chunk> 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 {};
|
|
|
|
}
|
|
|
|
|
2020-07-25 16:45:03 +00:00
|
|
|
std::optional<Item> Universe::set(const voxel_pos& pos, const Voxel& val) {
|
2020-07-25 14:29:05 +00:00
|
|
|
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 {};
|
|
|
|
}
|
|
|
|
}
|
2020-07-25 16:45:03 +00:00
|
|
|
ItemList Universe::setCube(const voxel_pos& pos, const Voxel& val, int radius) {
|
2020-07-25 14:29:05 +00:00
|
|
|
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;
|
|
|
|
}
|