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

283 lines
12 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-31 23:17:09 +00:00
#include <Remotery.h> // NOLINT
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-08-02 20:15:53 +00:00
const auto MAIN_AREA = 1;
Universe::Universe(const Universe::options &options): 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;
2020-08-02 20:15:53 +00:00
2020-07-30 16:35:13 +00:00
std::filesystem::create_directories(folderPath);
2020-08-03 17:55:03 +00:00
//areas.emplace(MAIN_AREA, std::make_shared<Area>(area_pos(glm::multiply(chunk_pos(1 << 8, 0, 0))), 1 << 20));
areas.emplace(2, std::make_shared<Area>(area_pos(voxel_pos(0)), 1 << 20));
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-08-02 20:15:53 +00:00
std::pair<area_<chunk_pos>, std::shared_ptr<Area>> task;
2020-07-22 20:55:13 +00:00
loadQueue.wait();
2020-08-02 20:15:53 +00:00
if (loadQueue.pop(task)) {
2020-07-26 20:53:14 +00:00
//MAYBE: loadQueue.take to avoid duplicated work on fast move
rmt_ScopedCPUSample(ProcessLoad, 0);
2020-08-02 20:15:53 +00:00
const auto &pos = task.first;
const auto rcPos = glm::split(pos.second);
const auto reg = task.second->getRegion(folderPath, std::make_pair(pos.first, rcPos.first));
2020-07-30 16:35:13 +00:00
Region::data data;
2020-08-02 20:15:53 +00:00
if(reg->read(rcPos.second, 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-08-02 20:15:53 +00:00
loadedQueue.push({pos, std::make_shared<Chunk>(pos.second, task.second->getGenerator())});
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-08-02 20:15:53 +00:00
save_task_t task;
2020-07-30 16:35:13 +00:00
saveQueue.wait();
2020-08-02 20:15:53 +00:00
if (saveQueue.pop(task) && task.second.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-08-02 20:15:53 +00:00
task.second.second->write(out);
const auto rcPos = glm::split(task.second.first);
const auto reg = task.first.second->getRegion(folderPath, std::make_pair(task.first.first, rcPos.first));
reg->write(rcPos.second, 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() {
2020-08-03 16:15:02 +00:00
contouring = nullptr;
2020-07-30 16:35:13 +00:00
// Save all
2020-08-02 20:15:53 +00:00
for(auto& area: areas) {
2020-08-03 16:15:02 +00:00
for(const auto& chunk: area.second->getChunks())
2020-08-02 20:15:53 +00:00
saveQueue.emplace(area, chunk);
2020-07-30 16:35:13 +00:00
}
2020-07-31 20:26:07 +00:00
if (auto size = saveQueue.size(); size > 0) {
2020-08-03 16:15:02 +00:00
LOG_I("Saving " << size << " chunks");
const auto SAVE_CHECK_TIME = 500;
2020-07-31 20:26:07 +00:00
do {
std::cout << "\rSaving... " << size << " " << std::flush;
2020-08-03 16:15:02 +00:00
std::this_thread::sleep_for(std::chrono::microseconds(SAVE_CHECK_TIME));
2020-07-31 20:26:07 +00:00
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-08-03 16:15:02 +00:00
LOG_D("Universe disappeared");
2020-07-26 20:53:14 +00:00
}
2020-07-30 16:35:13 +00:00
2020-08-03 17:55:03 +00:00
void Universe::update(const voxel_pos& pos, float deltaTime, Universe::report& rep) {
rmt_ScopedCPUSample(Universe, 0);
2020-08-02 20:15:53 +00:00
const chunk_pos newPos = glm::divide(pos);
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-08-03 16:15:02 +00:00
{ // Update alive areas
2020-07-18 12:54:07 +00:00
rmt_ScopedCPUSample(Update, 0);
2020-08-02 20:15:53 +00:00
size_t chunk_count = 0;
2020-08-03 16:15:02 +00:00
size_t region_count = 0;
const bool queuesEmpty = loadQueue.empty() && saveQueue.empty();
bool allLazy = true;
2020-08-02 20:15:53 +00:00
auto it = areas.begin();
while (it != areas.end()) {
2020-08-03 16:15:02 +00:00
rmt_ScopedCPUSample(Area, 0);
2020-08-03 17:55:03 +00:00
const bool chunkChangeArea = (it->first == 2 && it->second->move(glm::vec3(deltaTime))) || chunkChange; // TODO: area.velocity
const chunk_pos diff = glm::divide(pos - it->second->getOffset().as_voxel());
2020-08-02 20:15:53 +00:00
auto &chunks = it->second->setChunks();
2020-08-03 16:15:02 +00:00
if (glm::length2(diff) > glm::pow2(keepDistance + it->second->getChunks().getRadius())) {
2020-08-02 20:15:53 +00:00
auto it_c = chunks.begin();
while(it_c != chunks.end()) {
saveQueue.emplace(*it, *it_c);
it_c = chunks.erase(it_c);
}
it = areas.erase(it);
2020-07-26 20:53:14 +00:00
} else {
2020-08-03 16:15:02 +00:00
bool lazyArea = queuesEmpty;
{ // Update alive chunks
rmt_ScopedCPUSample(Alive, 0);
auto it_c = chunks.begin();
while(it_c != chunks.end()) {
if (glm::length2(diff - it_c->first) > glm::pow2(keepDistance)) {
saveQueue.emplace(*it, *it_c);
lazyArea = false;
it_c = chunks.erase(it_c);
}else {
const area_<chunk_pos> acPos = std::make_pair(it->first, it_c->first);
if (const auto neighbors = it_c->second->update()) {
contouring->onUpdate(acPos, diff, chunks, neighbors.value());
} else if (chunkChange || it->first == 2) {
contouring->onNotify(acPos, diff, chunks);
}
++it_c;
chunk_count++;
}
}
}
if (chunkChangeArea) { // Enqueue missing chunks
rmt_ScopedCPUSample(Missing, 0);
//TODO: need dist so no easy sphere fill
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 auto p = diff + chunk_pos(x, y, z);
if (chunks.inRange(p) && chunks.find(p) == chunks.end()) {
loadQueue.push(std::make_pair(it->first, p), it->second, -dist2);
lazyArea = false;
}
}
}}}
}
allLazy &= lazyArea;
if (lazyArea) { // Clear un-used regions
rmt_ScopedCPUSample(Region, 0);
const auto unique = it->second->getRegions(); // MAYBE: shared then unique
region_count += unique->size();
for (auto it_r = unique->begin(); it_r != unique->end(); ++it_r) {
if (glm::length2(diff - glm::lvec3(it_r->first) * glm::lvec3(REGION_LENGTH)) > glm::pow2(keepDistance + REGION_LENGTH * 2)) {
unique->erase(it_r); //FIXME: may wait for os file access (long)
break; //NOTE: save one only max per frame
2020-08-02 20:15:53 +00:00
}
}
2020-07-26 20:53:14 +00:00
}
++it;
2020-07-10 17:49:16 +00:00
}
}
2020-08-02 20:15:53 +00:00
rep.chunk_count.push(chunk_count);
2020-08-03 16:15:02 +00:00
rep.region_count.push(allLazy ? region_count : rep.region_count.current());
2020-07-10 17:49:16 +00:00
}
2020-08-03 16:15:02 +00:00
rep.chunk_load.push(loadQueue.size());
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);
2020-08-02 20:15:53 +00:00
contouring->update(pos, areas);
2020-07-26 20:53:14 +00:00
//MAYBE: if(chunkChange) contouring->notify(chunks);
2020-07-10 17:49:16 +00:00
}
2020-08-03 16:15:02 +00:00
{ // Store loaded chunks
2020-07-18 12:54:07 +00:00
rmt_ScopedCPUSample(Load, 0);
2020-08-02 20:15:53 +00:00
robin_hood::pair<area_<chunk_pos>, std::shared_ptr<Chunk>> loaded;
2020-07-30 16:35:13 +00:00
while (loadedQueue.pop(loaded)) {
2020-08-02 20:15:53 +00:00
if (const auto it = areas.find(loaded.first.first); it != areas.end()) {
auto &chunks = it->second->setChunks();
chunks.emplace(loaded.first.second, loaded.second);
2020-08-03 17:55:03 +00:00
const chunk_pos diff = glm::divide(pos - it->second->getOffset().as_voxel());
2020-08-03 16:15:02 +00:00
contouring->onUpdate(loaded.first, diff, chunks, Faces::All);
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-31 23:17:09 +00:00
void Universe::setContouring(const 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-08-03 16:15:02 +00:00
std::optional<Universe::ray_target> Universe::raycast(const Ray &ray) const {
2020-08-03 17:55:03 +00:00
//MAYBE: ray + offset to get float precision
2020-07-25 14:29:05 +00:00
std::vector<voxel_pos> points;
ray.grid(points);
2020-08-03 16:15:02 +00:00
std::optional<Universe::ray_target> target = std::nullopt;
2020-08-02 20:15:53 +00:00
size_t dist = points.size();
for(auto& area: areas) {
2020-08-03 16:15:02 +00:00
if(ray.intersect(area.second->getBounding()) != IBox::ContainmentType::Disjoint) {
2020-08-03 17:55:03 +00:00
const auto &offset = area.second->getOffset().as_voxel();
2020-08-03 16:15:02 +00:00
const auto &chunks = area.second->getChunks();
std::shared_ptr<Chunk> chunk = nullptr;
chunk_pos chunk_vec(INT_MAX);
for (size_t i = 0; i < dist; i++) {
const auto pos = points[i] - offset;
const chunk_pos cPos = glm::divide(pos);
if(cPos != chunk_vec) {
if (const auto it = chunks.find(cPos); it != chunks.end()) {
chunk = it->second;
chunk_vec = cPos;
} else {
chunk = nullptr;
}
2020-08-02 20:15:53 +00:00
}
2020-08-03 16:15:02 +00:00
if(chunk != nullptr) {
const auto voxel = chunk->getAt(glm::modulo(pos));
if(voxel.density() > 0) {
target = {ray_target{{area.first, pos}, voxel, offset}};
dist = i;
i = points.size();
}
2020-08-02 20:15:53 +00:00
}
2020-07-25 14:29:05 +00:00
}
}
}
2020-08-02 20:15:53 +00:00
return target;
2020-07-25 14:29:05 +00:00
}
2020-08-02 20:15:53 +00:00
std::optional<Item> Universe::set(const area_<voxel_pos>& pos, const Voxel& val) {
if(const auto it = areas.find(pos.first); it != areas.end()) {
auto &chunks = it->second->setChunks();
const auto split = glm::splitIdx(pos.second);
if(chunks.inRange(split.first))
if(const auto chunk = chunks.findInRange(split.first))
return {chunk.value()->replace(split.second, val)};
2020-07-25 14:29:05 +00:00
}
2020-08-02 20:15:53 +00:00
return {};
2020-07-25 14:29:05 +00:00
}
2020-08-02 20:15:53 +00:00
ItemList Universe::setCube(const area_<voxel_pos>& pos, const Voxel& val, int radius) {
2020-07-25 14:29:05 +00:00
ItemList list;
2020-08-02 20:15:53 +00:00
if(const auto it = areas.find(pos.first); it != areas.end()) {
auto& chunks = it->second->setChunks();
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)
const auto split = glm::splitIdx(pos.second + voxel_pos(x, y, z));
if(chunks.inRange(split.first))
if(const auto chunk = it->second->setChunks().findInRange(split.first))
list.add(chunk.value()->replace(split.second, val));
}}}
}
2020-07-25 14:29:05 +00:00
return list;
}