2020-07-25 16:45:03 +00:00
|
|
|
#include "Universe.hpp"
|
2020-07-10 17:49:16 +00:00
|
|
|
|
2020-08-07 17:08:02 +00:00
|
|
|
#include <Tracy.hpp> //NOLINT
|
|
|
|
#include <common/TracySystem.hpp> // 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-03 19:42:09 +00:00
|
|
|
const auto AREAS_FILE = "/areas.idx";
|
2020-08-02 20:15:53 +00:00
|
|
|
|
|
|
|
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 19:42:09 +00:00
|
|
|
{
|
|
|
|
std::ifstream index(folderPath + AREAS_FILE);
|
|
|
|
if(index.good()) {
|
|
|
|
size_t size = 0;
|
|
|
|
index.read(reinterpret_cast<char *>(&size), sizeof(size));
|
2020-08-07 17:08:02 +00:00
|
|
|
robin_hood::unordered_map<size_t, Area::params> tmp;
|
2020-08-03 19:42:09 +00:00
|
|
|
while(!index.eof()) {
|
2020-08-04 15:44:53 +00:00
|
|
|
size_t id = UINT32_MAX;
|
|
|
|
index.read(reinterpret_cast<char *>(&id), sizeof(size_t));
|
2020-08-03 19:42:09 +00:00
|
|
|
Area::params params{voxel_pos(0), 0};
|
|
|
|
index.read(reinterpret_cast<char *>(¶ms.center.x), sizeof(voxel_pos::value_type));
|
|
|
|
index.read(reinterpret_cast<char *>(¶ms.center.y), sizeof(voxel_pos::value_type));
|
|
|
|
index.read(reinterpret_cast<char *>(¶ms.center.z), sizeof(voxel_pos::value_type));
|
|
|
|
index.read(reinterpret_cast<char *>(¶ms.radius), sizeof(int));
|
|
|
|
index.read(reinterpret_cast<char *>(¶ms.seed), sizeof(int));
|
2020-08-04 15:44:53 +00:00
|
|
|
[[maybe_unused]]
|
|
|
|
auto ok = tmp.emplace(id, params).second;
|
2020-08-03 19:42:09 +00:00
|
|
|
assert(ok && "Duplicated area");
|
|
|
|
index.peek();
|
|
|
|
}
|
2020-08-04 15:44:53 +00:00
|
|
|
assert(tmp.size() == size && "Corrupted areas index");
|
|
|
|
far_areas = data::generational::vector<Area::params>(tmp);
|
2020-08-03 19:42:09 +00:00
|
|
|
LOG_D(far_areas.size() << " areas loaded");
|
|
|
|
} else {
|
|
|
|
LOG_E("No index file!!! Probably a new world...");
|
|
|
|
//TODO: generate universe
|
2020-08-04 15:44:53 +00:00
|
|
|
far_areas.emplace(Area::params{voxel_pos(0), 1 << 20});
|
2020-08-07 17:08:02 +00:00
|
|
|
//far_areas.emplace(Area::params{voxel_pos(0), 1, 43});
|
2020-08-03 19:42:09 +00:00
|
|
|
}
|
|
|
|
index.close();
|
|
|
|
}
|
2020-07-10 17:49:16 +00:00
|
|
|
|
2020-08-04 18:34:47 +00:00
|
|
|
entities.emplace(nullptr, glm::vec3(1), glm::vec3(2));
|
|
|
|
|
2020-08-05 13:14:57 +00:00
|
|
|
// Workers
|
2020-07-30 16:35:13 +00:00
|
|
|
for (size_t i = 0; i < 4; i++) {
|
2020-08-05 13:14:57 +00:00
|
|
|
workers.emplace_back([&] {
|
2020-08-07 17:08:02 +00:00
|
|
|
#if TRACY_ENABLE
|
|
|
|
tracy::SetThreadName("Chunks");
|
|
|
|
#endif
|
2020-08-05 13:14:57 +00:00
|
|
|
const auto read_ctx = dicts.make_reader();
|
|
|
|
const auto write_ctx = dicts.make_writer();
|
2020-07-22 20:55:13 +00:00
|
|
|
while (running) {
|
2020-08-05 13:14:57 +00:00
|
|
|
if (std::pair<area_<chunk_pos>, std::shared_ptr<Area>> task; loadQueue.pop(task)) {
|
2020-07-26 20:53:14 +00:00
|
|
|
//MAYBE: loadQueue.take to avoid duplicated work on fast move
|
2020-08-07 17:08:02 +00:00
|
|
|
ZoneScopedN("ProcessLoad");
|
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-05 13:14:57 +00:00
|
|
|
if(reg->read(rcPos.second, read_ctx, data)) {
|
2020-08-07 17:08:02 +00:00
|
|
|
ZoneScopedN("ProcessRead");
|
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 {
|
2020-08-07 17:08:02 +00:00
|
|
|
ZoneScopedN("ProcessGenerate");
|
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-08-05 13:14:57 +00:00
|
|
|
} else if(save_task_t task; saveQueue.pop(task)) {
|
2020-07-26 20:53:14 +00:00
|
|
|
//MAYBE: queue.take to avoid concurent write or duplicated work on fast move
|
2020-08-07 17:08:02 +00:00
|
|
|
ZoneScopedN("ProcessSave");
|
2020-08-05 13:14:57 +00:00
|
|
|
if(task.second.second->isModified()) {
|
|
|
|
std::ostringstream out;
|
|
|
|
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, write_ctx, out.str());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
loadQueue.wait();
|
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-05 13:14:57 +00:00
|
|
|
for(const auto& chunk: area.second->getChunks()) {
|
2020-08-02 20:15:53 +00:00
|
|
|
saveQueue.emplace(area, chunk);
|
2020-08-05 13:14:57 +00:00
|
|
|
}
|
2020-07-30 16:35:13 +00:00
|
|
|
}
|
2020-08-05 13:14:57 +00:00
|
|
|
loadQueue.notify_all();
|
|
|
|
|
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-08-03 19:42:09 +00:00
|
|
|
saveAreas();
|
2020-07-30 16:35:13 +00:00
|
|
|
|
2020-07-26 20:53:14 +00:00
|
|
|
running = false;
|
2020-08-05 13:14:57 +00:00
|
|
|
loadQueue.notify_all();
|
2020-07-26 20:53:14 +00:00
|
|
|
|
2020-08-05 13:14:57 +00:00
|
|
|
for (auto &worker: workers) {
|
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 19:42:09 +00:00
|
|
|
// Write areas index (warn: file io)
|
|
|
|
void Universe::saveAreas() const {
|
|
|
|
std::ofstream index(folderPath + AREAS_FILE, std::ios::out | std::ios::binary);
|
|
|
|
if(!index.good()) {
|
|
|
|
LOG_E("Areas index write error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
size_t size = areas.size() + far_areas.size();
|
|
|
|
index.write(reinterpret_cast<char *>(&size), sizeof(size));
|
|
|
|
}
|
2020-08-04 15:44:53 +00:00
|
|
|
std::function write = [&](area_id id, Area::params params) {
|
|
|
|
auto idx = id.index;
|
|
|
|
index.write(reinterpret_cast<char *>(&idx), sizeof(size_t));
|
2020-08-03 19:42:09 +00:00
|
|
|
index.write(reinterpret_cast<char *>(¶ms.center.x), sizeof(voxel_pos::value_type));
|
|
|
|
index.write(reinterpret_cast<char *>(¶ms.center.y), sizeof(voxel_pos::value_type));
|
|
|
|
index.write(reinterpret_cast<char *>(¶ms.center.z), sizeof(voxel_pos::value_type));
|
|
|
|
index.write(reinterpret_cast<char *>(¶ms.radius), sizeof(int));
|
|
|
|
index.write(reinterpret_cast<char *>(¶ms.seed), sizeof(int));
|
2020-08-04 15:44:53 +00:00
|
|
|
};
|
|
|
|
for(const auto& area: areas) {
|
|
|
|
write(area.first, area.second->getParams());
|
2020-08-03 19:42:09 +00:00
|
|
|
}
|
2020-08-04 15:44:53 +00:00
|
|
|
far_areas.iter(write);
|
2020-08-03 19:42:09 +00:00
|
|
|
if(!index.good())
|
|
|
|
LOG_E("Areas index write error");
|
|
|
|
|
|
|
|
index.close();
|
|
|
|
}
|
|
|
|
|
2020-08-07 17:08:02 +00:00
|
|
|
void Universe::update(const voxel_pos& pos, float deltaTime) {
|
|
|
|
ZoneScopedN("Universe");
|
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 19:42:09 +00:00
|
|
|
if(chunkChange) {
|
2020-08-07 17:08:02 +00:00
|
|
|
ZoneScopedN("Far");
|
2020-08-04 15:44:53 +00:00
|
|
|
far_areas.extract([&](area_id id, Area::params params){
|
|
|
|
if (const chunk_pos diff = glm::divide(pos - params.center);
|
|
|
|
glm::length2(diff) > glm::pow2(loadDistance + params.radius))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
LOG_I("Load area " << id.index);
|
|
|
|
areas.emplace(id, std::make_shared<Area>(params));
|
|
|
|
return true;
|
|
|
|
});
|
2020-08-03 19:42:09 +00:00
|
|
|
}
|
2020-08-03 16:15:02 +00:00
|
|
|
{ // Update alive areas
|
2020-08-07 17:08:02 +00:00
|
|
|
ZoneScopedN("World");
|
|
|
|
#if TRACY_ENABLE
|
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;
|
2020-08-07 17:08:02 +00:00
|
|
|
#endif
|
2020-08-03 16:15:02 +00:00
|
|
|
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-07 17:08:02 +00:00
|
|
|
ZoneScopedN("Area");
|
2020-08-04 15:44:53 +00:00
|
|
|
const bool chunkChangeArea = (false && it->first == 1 && it->second->move(glm::vec3(deltaTime))) || chunkChange; // TODO: area.velocity
|
2020-08-03 17:55:03 +00:00
|
|
|
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);
|
|
|
|
}
|
2020-08-04 15:44:53 +00:00
|
|
|
LOG_I("Unload area " << it->first.index);
|
|
|
|
[[maybe_unused]]
|
|
|
|
auto ok = far_areas.put(it->first, it->second->getParams());
|
|
|
|
assert(ok);
|
2020-08-02 20:15:53 +00:00
|
|
|
it = areas.erase(it);
|
2020-08-03 19:42:09 +00:00
|
|
|
saveAreas();
|
2020-07-26 20:53:14 +00:00
|
|
|
} else {
|
2020-08-03 16:15:02 +00:00
|
|
|
bool lazyArea = queuesEmpty;
|
|
|
|
{ // Update alive chunks
|
2020-08-07 17:08:02 +00:00
|
|
|
ZoneScopedN("Alive");
|
2020-08-03 16:15:02 +00:00
|
|
|
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());
|
2020-08-04 15:44:53 +00:00
|
|
|
} else if (chunkChangeArea) {
|
2020-08-03 16:15:02 +00:00
|
|
|
contouring->onNotify(acPos, diff, chunks);
|
|
|
|
}
|
|
|
|
++it_c;
|
2020-08-07 17:08:02 +00:00
|
|
|
#if TRACY_ENABLE
|
2020-08-03 16:15:02 +00:00
|
|
|
chunk_count++;
|
2020-08-07 17:08:02 +00:00
|
|
|
#endif
|
2020-08-03 16:15:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (chunkChangeArea) { // Enqueue missing chunks
|
2020-08-07 17:08:02 +00:00
|
|
|
ZoneScopedN("Missing");
|
2020-08-03 16:15:02 +00:00
|
|
|
//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
|
2020-08-07 17:08:02 +00:00
|
|
|
ZoneScopedN("Region");
|
2020-08-03 16:15:02 +00:00
|
|
|
const auto unique = it->second->getRegions(); // MAYBE: shared then unique
|
2020-08-07 17:08:02 +00:00
|
|
|
#if TRACY_ENABLE
|
2020-08-03 16:15:02 +00:00
|
|
|
region_count += unique->size();
|
2020-08-07 17:08:02 +00:00
|
|
|
#endif
|
2020-08-03 16:15:02 +00:00
|
|
|
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-07 17:08:02 +00:00
|
|
|
#if TRACY_ENABLE
|
|
|
|
TracyPlot("ChunkCount", static_cast<int64_t>(chunk_count));
|
|
|
|
if(allLazy) {
|
|
|
|
TracyPlot("Region", static_cast<int64_t>(region_count));
|
|
|
|
}
|
|
|
|
TracyPlot("ChunkLoad", static_cast<int64_t>(loadQueue.size()));
|
|
|
|
TracyPlot("ChunkUnload", static_cast<int64_t>(saveQueue.size()));
|
|
|
|
#endif
|
2020-07-10 17:49:16 +00:00
|
|
|
}
|
2020-07-22 20:55:13 +00:00
|
|
|
{
|
2020-08-07 17:08:02 +00:00
|
|
|
ZoneScopedN("Contouring");
|
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-04 18:34:47 +00:00
|
|
|
{ // Update entities
|
2020-08-07 17:08:02 +00:00
|
|
|
ZoneScopedN("Entities");
|
|
|
|
#if TRACY_ENABLE
|
2020-08-04 18:34:47 +00:00
|
|
|
size_t entity_count = 0;
|
2020-08-07 17:08:02 +00:00
|
|
|
#endif
|
2020-08-04 18:34:47 +00:00
|
|
|
entities.for_each([&](entity_id, Entity &val) {
|
|
|
|
val.instances.remove([&](entity_id, Entity::Instance &inst) {
|
2020-08-07 17:08:02 +00:00
|
|
|
#if TRACY_ENABLE
|
2020-08-04 18:34:47 +00:00
|
|
|
entity_count++;
|
2020-08-07 17:08:02 +00:00
|
|
|
#endif
|
2020-08-04 18:34:47 +00:00
|
|
|
inst.pos += inst.velocity * deltaTime;
|
|
|
|
return glm::length2(glm::divide(pos - inst.pos.as_voxel())) > glm::pow2(keepDistance);
|
|
|
|
});
|
|
|
|
});
|
2020-08-07 17:08:02 +00:00
|
|
|
#if TRACY_ENABLE
|
|
|
|
TracyPlot("EntityCount", static_cast<int64_t>(entity_count));
|
|
|
|
#endif
|
2020-08-04 18:34:47 +00:00
|
|
|
}
|
2020-07-10 17:49:16 +00:00
|
|
|
|
2020-08-03 16:15:02 +00:00
|
|
|
{ // Store loaded chunks
|
2020-08-07 17:08:02 +00:00
|
|
|
ZoneScopedN("Load");
|
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;
|
2020-08-04 18:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
entity_instance_id Universe::addEntity(entity_id type, const Entity::Instance &instance) {
|
|
|
|
return std::make_pair(type, entities.at(type).instances.push(instance));
|
|
|
|
}
|
|
|
|
void Universe::getEntitiesModels(std::vector<std::pair<std::vector<glm::mat4>, buffer::Abstract *const>> &buffers, const std::optional<geometry::Frustum> &frustum, const glm::llvec3 &offset, int density) {
|
|
|
|
entities.iter([&](entity_id, const Entity &entity) {
|
|
|
|
std::vector<glm::mat4> mats;
|
|
|
|
entity.instances.iter([&](entity_id, const Entity::Instance &inst) {
|
|
|
|
const glm::vec3 fPos = (glm::vec3(inst.pos.raw_as_long() - offset * glm::llvec3(density)) + inst.pos.offset) / glm::vec3(density);
|
|
|
|
if (!frustum.has_value() || frustum.value().contains(geometry::Box::fromMin(fPos, entity.size)))
|
|
|
|
mats.emplace_back(glm::scale(glm::translate(glm::mat4(1), fPos * (float)density), entity.scale));
|
|
|
|
});
|
|
|
|
if(!mats.empty())
|
|
|
|
buffers.emplace_back(mats, entity.buffer);
|
|
|
|
});
|
2020-07-25 14:29:05 +00:00
|
|
|
}
|