1
0
Fork 0
Univerxel/src/world/Universe.cpp

238 lines
8.2 KiB
C++
Raw Normal View History

2020-07-25 16:45:03 +00:00
#include "Universe.hpp"
2020-07-10 17:49:16 +00:00
2020-07-18 12:54:07 +00:00
#include <Remotery.h>
2020-07-26 20:53:14 +00:00
#include <filesystem>
2020-07-31 17:09:44 +00:00
#include "../contouring/Dummy.hpp"
#include "Chunk.hpp"
2020-07-10 17:49:16 +00:00
2020-07-25 16:45:03 +00:00
using namespace world;
2020-07-31 22:11:08 +00:00
Universe::Universe(const Universe::options &options): generator(42), dicts("content/zstd.dict"), contouring(std::make_shared<contouring::Dummy>()) {
2020-07-10 17:49:16 +00:00
setOptions(options);
2020-07-30 16:35:13 +00:00
folderPath = options.folderPath;
struct vec_istream: std::streambuf {
vec_istream(std::vector<char> &vec) {
this->setg(&vec[0], &vec[0], &vec[0] + vec.size());
}
};
running = true;
std::filesystem::create_directories(folderPath);
2020-07-10 17:49:16 +00:00
2020-07-30 16:35:13 +00:00
// Load workers
for (size_t i = 0; i < 4; i++) {
2020-07-31 17:09:44 +00:00
loadWorkers.emplace_back([&] {
2020-07-31 22:11:08 +00:00
const auto ctx = dicts.make_reader();
2020-07-22 20:55:13 +00:00
while (running) {
2020-07-31 20:26:07 +00:00
chunk_pos pos;
2020-07-22 20:55:13 +00:00
loadQueue.wait();
2020-07-31 20:26:07 +00:00
if (loadQueue.pop(pos)) {
2020-07-26 20:53:14 +00:00
//MAYBE: loadQueue.take to avoid duplicated work on fast move
rmt_ScopedCPUSample(ProcessLoad, 0);
2020-07-31 20:26:07 +00:00
const region_pos rPos = glm::divide(pos, region_chunk_pos(REGION_LENGTH));
const region_chunk_pos cPos = glm::modulo(pos, region_chunk_pos(REGION_LENGTH));
2020-07-30 16:35:13 +00:00
const auto reg = getRegion(rPos);
Region::data data;
2020-07-31 20:26:07 +00:00
if(reg->read(cPos, ctx, data)) {
2020-07-26 20:53:14 +00:00
rmt_ScopedCPUSample(ProcessRead, 0);
2020-07-30 16:35:13 +00:00
vec_istream idata(data);
std::istream iss(&idata);
2020-07-31 20:26:07 +00:00
loadedQueue.push({pos, std::make_shared<Chunk>(iss)});
2020-07-26 20:53:14 +00:00
} else {
rmt_ScopedCPUSample(ProcessGenerate, 0);
2020-07-31 20:26:07 +00:00
loadedQueue.push({pos, std::make_shared<Chunk>(pos, generator)});
2020-07-26 20:53:14 +00:00
}
2020-07-22 20:55:13 +00:00
}
}
2020-07-31 17:09:44 +00:00
});
2020-07-22 20:55:13 +00:00
}
2020-07-30 16:35:13 +00:00
// Save workers
for (size_t i = 0; i < 2; i++) {
2020-07-31 17:09:44 +00:00
saveWorkers.emplace_back([&] {
2020-07-31 22:11:08 +00:00
const auto ctx = dicts.make_writer();
2020-07-26 20:53:14 +00:00
while (running) {
2020-07-31 20:26:07 +00:00
robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>> task;
2020-07-30 16:35:13 +00:00
saveQueue.wait();
2020-07-31 20:26:07 +00:00
if (saveQueue.pop(task) && task.second->isModified()) {
2020-07-26 20:53:14 +00:00
//MAYBE: queue.take to avoid concurent write or duplicated work on fast move
rmt_ScopedCPUSample(ProcessSave, 0);
2020-07-30 16:35:13 +00:00
std::ostringstream out;
2020-07-31 20:26:07 +00:00
task.second->write(out);
const region_pos rPos = glm::divide(task.first, region_chunk_pos(REGION_LENGTH));
const region_chunk_pos cPos = glm::modulo(task.first, region_chunk_pos(REGION_LENGTH));
2020-07-30 16:35:13 +00:00
const auto reg = getRegion(rPos);
2020-07-31 20:26:07 +00:00
reg->write(cPos, ctx, out.str());
2020-07-26 20:53:14 +00:00
}
}
2020-07-31 17:09:44 +00:00
});
2020-07-26 20:53:14 +00:00
}
}
2020-07-30 16:35:13 +00:00
Universe::~Universe() {
contouring = NULL;
// Save all
for(auto& pair: chunks) {
saveQueue.push(pair);
}
2020-07-31 20:26:07 +00:00
if (auto size = saveQueue.size(); size > 0) {
std::cout << std::endl;
do {
std::cout << "\rSaving... " << size << " " << std::flush;
std::this_thread::sleep_for(std::chrono::microseconds(500));
size = saveQueue.size();
} while (size > 0);
std::cout << std::endl;
2020-07-30 16:35:13 +00:00
}
2020-07-26 20:53:14 +00:00
running = false;
2020-07-30 16:35:13 +00:00
loadQueue.notify();
saveQueue.notify();
2020-07-26 20:53:14 +00:00
2020-07-30 16:35:13 +00:00
for (auto &worker: loadWorkers) {
if (worker.joinable())
worker.join();
}
for (auto &worker: saveWorkers) {
2020-07-26 20:53:14 +00:00
if (worker.joinable())
worker.join();
}
}
2020-07-30 16:35:13 +00:00
std::shared_ptr<Region> Universe::getRegion(const region_pos& pos) {
2020-07-31 22:11:08 +00:00
{ // Found
const auto shared = regions.lock_shared();
const auto it = shared->find(pos);
if(it != shared->end())
return it->second;
2020-07-30 16:35:13 +00:00
}
2020-07-31 22:11:08 +00:00
// Reading
const auto reg = std::make_shared<Region>(folderPath, pos);
const auto unique = regions.lock();
return unique->insert({pos, reg}).first->second;
2020-07-30 16:35:13 +00:00
}
2020-07-26 20:53:14 +00:00
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) {
2020-07-30 16:35:13 +00:00
saveQueue.push(*it);
2020-07-26 20:53:14 +00:00
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-30 16:35:13 +00:00
rep.chunk_unload.push(saveQueue.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-30 16:35:13 +00:00
loadQueue.push(p, -dist2);
2020-07-10 17:49:16 +00:00
}
}
}}}
}
2020-07-30 16:35:13 +00:00
rep.chunk_load.push(loadQueue.size());
2020-07-22 20:55:13 +00:00
// 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-30 16:35:13 +00:00
while (loadedQueue.pop(loaded)) {
2020-07-22 20:55:13 +00:00
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-31 20:26:07 +00:00
{
rmt_ScopedCPUSample(Region, 0);
2020-07-31 22:11:08 +00:00
const auto unique = regions.lock(); // MAYBE: shared then unique
rep.region_count.push(unique->size());
2020-07-31 20:26:07 +00:00
const auto me = glm::divide(last_pos, glm::ivec3(REGION_LENGTH));
2020-07-31 22:11:08 +00:00
for (auto it = unique->begin(); it != unique->end(); it++) {
2020-07-31 20:26:07 +00:00
if (glm::length2(it->first - me) > keepDistance) {
2020-07-31 22:11:08 +00:00
LOG_D("Remove region");
unique->erase(it);
break; //NOTE: save one max per frame
2020-07-31 20:26:07 +00:00
}
}
}
2020-07-10 17:49:16 +00:00
}
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;
}