1
0
Fork 0

Multiverse: Areas

No position for now
This commit is contained in:
May B. 2020-08-02 22:15:53 +02:00
parent f75afb5e1e
commit 5c6ccd48e5
33 changed files with 460 additions and 291 deletions

15
TODO.md
View File

@ -4,10 +4,9 @@
- [x] Generate noise
- [x] Density
- [x] Robin hood map
- [ ] In memory RLE
- [ ] Octree world
- [ ] Remove density
- [x] Serialize
- [ ] Variable length encoding
- [x] Group files
- [x] Zstd + custom grouping
- [~] Find best region size
@ -15,13 +14,15 @@
- [x] Low memory: Keep only ifstream
- [x] High memory: Save multiple
- [x] Unload unused
- [ ] In memory RLE
- [x] Edition
- [ ] Entity
- [ ] Planet
- [ ] CubicSphere
- [ ] Healpix
- [ ] Galaxy
- [ ] Area
- [ ] Offset
- [ ] Rotation
- [ ] Planet
- [ ] CubicSphere
- [ ] Healpix
- [ ] Galaxy
- [ ] Biomes
- https://imgur.com/kM8b5Zq
- https://imgur.com/a/bh2iy

View File

@ -15,13 +15,13 @@ namespace contouring {
/// Each frame ping.
/// Mostly used for cleanup and to flush buffers data using main thread
virtual void update(const voxel_pos &pos) = 0;
virtual void update(const voxel_pos &pos, const world::area_map &areas) = 0;
/// Chunk data change
virtual void onUpdate(const chunk_pos &pos, const world::chunk_map &data, geometry::Faces neighbors) = 0;
virtual void onUpdate(const area_<chunk_pos> &pos, const world::ChunkContainer &data, geometry::Faces neighbors) = 0;
/// Chunk existante ping
/// @note notify for chunks entering view while moving
virtual void onNotify(const chunk_pos &pos, const world::chunk_map &data) = 0;
virtual void onNotify(const area_<chunk_pos> &pos, const world::ChunkContainer &data) = 0;
/// Display ImGui config
virtual void onGui() = 0;
/// Get options

View File

@ -5,17 +5,30 @@
#include "../world/Chunk.hpp"
namespace contouring {
void AbstractFlat::update(const voxel_pos& pos) {
center = glm::divide(pos, chunk_voxel_pos(CHUNK_LENGTH));
auto it = buffers.begin();
while (it != buffers.end()) { // Remove out of range buffers
if (inKeepRange(it->first)) {
it++;
} else {
if(it->second != NULL)
delete it->second;
void AbstractFlat::update(const voxel_pos& pos, const world::area_map& areas) {
center = glm::divide(pos);
auto it_a = buffers.begin();
while (it_a != buffers.end()) { // Remove out of range buffers
if (const auto area = areas.find(it_a->first); area != areas.end()) {
//Update
auto it = it_a->second.begin();
while(it != it_a->second.end()) {
if (inKeepRange(it->first /*TODO: + area.second->getPosition() */)) {
++it;
} else {
if(it->second != NULL)
delete it->second;
it = buffers.erase(it);
it = it_a->second.erase(it);
}
}
++it_a;
} else {
for(auto& buffer: it_a->second) {
if(buffer.second != NULL)
delete buffer.second;
}
it_a = buffers.erase(it_a);
}
}
}
@ -41,10 +54,11 @@ namespace contouring {
void AbstractFlat::getModels(std::vector<std::pair<glm::mat4, buffer::Abstract *const>> &out, const std::optional<geometry::Frustum> &frustum, const glm::llvec3& offset, int density) {
const auto scaling = glm::scale(glm::mat4(1), glm::vec3(1.f / density));
for (const auto [pos, buffer] : buffers) {
const glm::vec3 fPos = glm::vec3(glm::llvec3(pos) * glm::llvec3(CHUNK_LENGTH) - offset * glm::llvec3(density)) / glm::vec3(density);
for (const auto [_, bfs] : buffers) {
for (const auto [pos, buffer] : bfs) {
const glm::vec3 fPos = glm::vec3(glm::multiply(pos /*TODO: + area.position*/) - offset * glm::llvec3(density)) / glm::vec3(density);
if (buffer != NULL && (!frustum.has_value() || frustum.value().contains(geometry::Box::fromMin(fPos, glm::vec3(CHUNK_LENGTH / (float)density)))))
out.emplace_back(glm::translate(scaling, fPos * (float)density), buffer);
}
}}
}
}

View File

@ -11,7 +11,7 @@ namespace contouring {
virtual ~AbstractFlat() {}
/// Each frame ping. Used to clear out of range
void update(const voxel_pos &) override;
void update(const voxel_pos &, const world::area_map &areas) override;
/// Display ImGui config
void onGui() override;
@ -30,7 +30,8 @@ namespace contouring {
return glm::length2(center - point) <= keepDistance * keepDistance;
}
robin_hood::unordered_map<chunk_pos, buffer::Abstract *> buffers;
//MAYBE: store position copy
robin_hood::unordered_flat_map<area_id, robin_hood::unordered_flat_map<chunk_pos, buffer::Abstract *>> buffers;
chunk_pos center = chunk_pos(INT_MAX);
int loadDistance = 3;

View File

@ -9,9 +9,9 @@ namespace contouring {
Dummy(): Abstract() { }
virtual ~Dummy() { }
void update(const voxel_pos &) override { }
void onUpdate(const chunk_pos &, const world::chunk_map &, geometry::Faces) override {}
void onNotify(const chunk_pos &, const world::chunk_map &) override { }
void update(const voxel_pos &, const world::area_map &) override {}
void onUpdate(const area_<chunk_pos> &, const world::ChunkContainer &, geometry::Faces) override {}
void onNotify(const area_<chunk_pos> &, const world::ChunkContainer &) override {}
void onGui() override { }
std::string getOptions() override { return ""; }
void getModels(std::vector<std::pair<glm::mat4, buffer::Abstract *const>> &, const std::optional<geometry::Frustum>&, const glm::llvec3&, int) override { }

View File

@ -16,7 +16,7 @@ namespace contouring {
for (size_t i = 1; i <= 2; i++) {
workers.emplace_back([&] {
while (running) {
std::pair<chunk_pos, surrounding::corners> ctx;
std::pair<area_<chunk_pos>, surrounding::corners> ctx;
loadQueue.wait();
if (loadQueue.pop(ctx)) {
rmt_ScopedCPUSample(ProcessContouring, 0);
@ -49,51 +49,52 @@ namespace contouring {
return ss.str();
}
void FlatDualMC::enqueue(const chunk_pos &pos, const world::chunk_map &data) {
void FlatDualMC::enqueue(const area_<chunk_pos> &pos, const world::ChunkContainer &data) {
rmt_ScopedCPUSample(EnqueueContouring, RMTSF_Aggregate);
const auto dist2 = glm::length2(pos - center);
const auto dist2 = glm::length2(center - pos.second); //TODO: - area.pos
if (dist2 <= loadDistance * loadDistance) {
surrounding::corners surrounding;
if(surrounding::load(surrounding, pos, data)) {
if(surrounding::load(surrounding, pos.second, data)) {
loadQueue.push(pos, surrounding, -dist2);
}
}
}
void FlatDualMC::onUpdate(const chunk_pos &pos, const world::chunk_map &data, geometry::Faces neighbors) {
void FlatDualMC::onUpdate(const area_<chunk_pos> &pos, const world::ChunkContainer &data, geometry::Faces neighbors) {
enqueue(pos, data);
if (neighbors && (geometry::Faces::Left | geometry::Faces::Down | geometry::Faces::Backward)) {
for (size_t i = 1; i < 8; i++) {
enqueue(pos - surrounding::g_corner_offsets[i], data);
enqueue(std::make_pair(pos.first, pos.second - surrounding::g_corner_offsets[i]), data);
}
}
}
void FlatDualMC::onNotify(const chunk_pos &pos, const world::chunk_map &data) {
if (buffers.find(pos) == buffers.end()) {
void FlatDualMC::onNotify(const area_<chunk_pos> &pos, const world::ChunkContainer &data) {
const auto it = buffers.find(pos.first);
if(it == buffers.end() || it->second.find(pos.second) == it->second.end()) {
enqueue(pos, data);
}
}
void FlatDualMC::update(const voxel_pos& pos) {
AbstractFlat::update(pos);
std::pair<chunk_pos, buffer::ShortIndexed::Data> out;
void FlatDualMC::update(const voxel_pos& pos, const world::area_map& areas) {
AbstractFlat::update(pos, areas);
std::pair<area_<chunk_pos>, buffer::ShortIndexed::Data> out;
reports.load.push(loadQueue.size());
//MAYBE: clear out of range loadQueue.trim(keepDistance * keepDistance)
reports.loaded.push(loadedQueue.size());
while(loadedQueue.pop(out)) {
const auto buffer = new buffer::ShortIndexed(GL_TRIANGLES, out.second);
const auto it = buffers.find(out.first);
if (it != buffers.end()) {
auto& bfs = buffers[out.first.first];
if (const auto it = bfs.find(out.first.second); it != bfs.end()) {
if(it->second != NULL)
delete it->second;
it->second = buffer;
} else {
buffers.emplace(out.first, buffer);
bfs.emplace(out.first.second, buffer);
}
}
reports.count.push(buffers.size());
reports.count.push(buffers.size()); //FIXME: get from AbstractFlat
}
void FlatDualMC::onGui() {
@ -116,7 +117,7 @@ namespace contouring {
for (int y = 0; y < SIZE; y++) {
for (int x = 0; x < SIZE; x++) {
const auto &chunk = surrounding[(z >= CHUNK_LENGTH) + (y >= CHUNK_LENGTH)*2 + (x >= CHUNK_LENGTH)*4];
const auto &voxel = chunk->getAt(chunk_voxel_pos(x % CHUNK_LENGTH, y % CHUNK_LENGTH, z % CHUNK_LENGTH));
const auto &voxel = chunk->get(glm::toIdx(x % CHUNK_LENGTH, y % CHUNK_LENGTH, z % CHUNK_LENGTH));
grid.emplace_back(voxel.density() * 1.f / world::Voxel::DENSITY_MAX, voxel.material());
}}}
}

View File

@ -19,21 +19,21 @@ namespace contouring {
FlatDualMC(const std::string&);
virtual ~FlatDualMC();
void update(const voxel_pos&) override;
void update(const voxel_pos&, const world::area_map&) override;
void onGui() override;
std::string getOptions() override;
/// Chunk data change
void onUpdate(const chunk_pos &, const world::chunk_map &, geometry::Faces) override;
void onUpdate(const area_<chunk_pos> &, const world::ChunkContainer &, geometry::Faces) override;
/// Chunk existante ping
/// @note notify for chunks entering view while moving
void onNotify(const chunk_pos &, const world::chunk_map &) override;
void onNotify(const area_<chunk_pos> &, const world::ChunkContainer &) override;
protected:
safe_priority_queue_map<chunk_pos, surrounding::corners, int> loadQueue;
safe_queue<std::pair<chunk_pos, buffer::ShortIndexed::Data>> loadedQueue;
safe_priority_queue_map<area_<chunk_pos>, surrounding::corners, int, area_hash> loadQueue;
safe_queue<std::pair<area_<chunk_pos>, buffer::ShortIndexed::Data>> loadedQueue;
struct report {
circular_buffer<float> count = circular_buffer<float>(REPORT_BUFFER_SIZE, 0); // MAYBE: store int
@ -44,7 +44,7 @@ namespace contouring {
bool running = true;
std::vector<std::thread> workers;
void enqueue(const chunk_pos &, const world::chunk_map &);
void enqueue(const area_<chunk_pos> &, const world::ChunkContainer &);
float iso = .1f;
bool manifold = true;

View File

@ -11,7 +11,7 @@ namespace contouring {
for (size_t i = 1; i <= 4; i++) {
workers.emplace_back([&] {
while (running) {
std::pair<chunk_pos, surrounding::faces> ctx;
std::pair<area_<chunk_pos>, surrounding::faces> ctx;
loadQueue.wait();
if (loadQueue.pop(ctx)) {
rmt_ScopedCPUSample(ProcessContouring, 0);
@ -36,63 +36,64 @@ namespace contouring {
}
}
void FlatSurroundingBox::enqueue(const chunk_pos &pos, const world::chunk_map &data) {
void FlatSurroundingBox::enqueue(const area_<chunk_pos> &pos, const world::ChunkContainer &data) {
rmt_ScopedCPUSample(EnqueueContouring, RMTSF_Aggregate);
const auto dist2 = glm::length2(pos - center);
const auto dist2 = glm::length2(center - pos.second /*TODO: - area.pos */);
if (dist2 <= loadDistance * loadDistance) {
surrounding::faces surrounding;
if(surrounding::load(surrounding, pos, data)) {
if(surrounding::load(surrounding, pos.second, data)) {
loadQueue.push(pos, surrounding, -dist2);
}
}
}
void FlatSurroundingBox::onUpdate(const chunk_pos &pos, const world::chunk_map &data, Faces neighbors) {
void FlatSurroundingBox::onUpdate(const area_<chunk_pos> &pos, const world::ChunkContainer &data, Faces neighbors) {
enqueue(pos, data);
if (neighbors && Faces::Right)
enqueue(pos + g_face_offsets[static_cast<int>(Face::Right)], data);
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Right)]), data);
if (neighbors && Faces::Left)
enqueue(pos + g_face_offsets[static_cast<int>(Face::Left)], data);
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Left)]), data);
if (neighbors && Faces::Up)
enqueue(pos + g_face_offsets[static_cast<int>(Face::Up)], data);
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Up)]), data);
if (neighbors && Faces::Down)
enqueue(pos + g_face_offsets[static_cast<int>(Face::Down)], data);
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Down)]), data);
if (neighbors && Faces::Forward)
enqueue(pos + g_face_offsets[static_cast<int>(Face::Forward)], data);
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Forward)]), data);
if (neighbors && Faces::Backward)
enqueue(pos + g_face_offsets[static_cast<int>(Face::Backward)], data);
enqueue(std::make_pair(pos.first, pos.second + g_face_offsets[static_cast<int>(Face::Backward)]), data);
}
void FlatSurroundingBox::onNotify(const chunk_pos &pos, const world::chunk_map &data) {
if (buffers.find(pos) == buffers.end()) {
void FlatSurroundingBox::onNotify(const area_<chunk_pos> &pos, const world::ChunkContainer &data) {
const auto it = buffers.find(pos.first);
if(it == buffers.end() || it->second.find(pos.second) == it->second.end()) {
enqueue(pos, data);
}
}
void FlatSurroundingBox::update(const voxel_pos& pos) {
AbstractFlat::update(pos);
std::pair<chunk_pos, buffer::ShortIndexed::Data> out;
void FlatSurroundingBox::update(const voxel_pos& pos, const world::area_map& areas) {
AbstractFlat::update(pos, areas);
std::pair<area_<chunk_pos>, buffer::ShortIndexed::Data> out;
reports.load.push(loadQueue.size());
//MAYBE: clear out of range loadQueue.trim(keepDistance * keepDistance)
reports.loaded.push(loadedQueue.size());
while(loadedQueue.pop(out)) {
const auto buffer = new buffer::ShortIndexed(GL_TRIANGLES, out.second);
const auto it = buffers.find(out.first);
if (it != buffers.end()) {
auto& bfs = buffers[out.first.first];
if (const auto it = bfs.find(out.first.second); it != bfs.end()) {
if(it->second != NULL)
delete it->second;
it->second = buffer;
} else {
buffers.emplace(out.first, buffer);
bfs.emplace(out.first.second, buffer);
}
}
reports.count.push(buffers.size());
reports.count.push(buffers.size()); //FIXME: get from AbstractFlat
}
void FlatSurroundingBox::onGui() {
@ -110,7 +111,7 @@ namespace contouring {
void FlatSurroundingBox::render(const surrounding::faces &surrounding, std::vector<buffer::VertexData> &vertices) {
const auto center = surrounding[surrounding::CENTER];
vertices.clear();
for (ushort i = 0; i < CHUNK_SIZE; i++) {
for (ushort i = 0; i < world::CHUNK_SIZE; i++) {
if (center->get(i).density() > 0) {
Faces faces = center->get(i).density() < world::Voxel::DENSITY_MAX ? Faces::All :
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Right)) & Faces::Right) |
@ -119,7 +120,7 @@ namespace contouring {
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Down)) & Faces::Down) |
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Forward)) & Faces::Forward) |
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Backward)) & Faces::Backward);
box::addCube(vertices, world::Chunk::getPosition(i), center->get(i).material(), faces, glm::vec3(center->get(i).density() * 1.f / world::Voxel::DENSITY_MAX));
box::addCube(vertices, glm::fromIdx(i), center->get(i).material(), faces, glm::vec3(center->get(i).density() * 1.f / world::Voxel::DENSITY_MAX));
}
}
}

View File

@ -19,19 +19,19 @@ namespace contouring {
FlatSurroundingBox(const std::string&);
virtual ~FlatSurroundingBox();
void update(const voxel_pos&) override;
void update(const voxel_pos&, const world::area_map&) override;
void onGui() override;
/// Chunk data change
void onUpdate(const chunk_pos &, const world::chunk_map &, geometry::Faces) override;
void onUpdate(const area_<chunk_pos> &, const world::ChunkContainer &, geometry::Faces) override;
/// Chunk existante ping
/// @note notify for chunks entering view while moving
void onNotify(const chunk_pos &, const world::chunk_map &) override;
void onNotify(const area_<chunk_pos> &, const world::ChunkContainer &) override;
protected:
safe_priority_queue_map<chunk_pos, surrounding::faces, int> loadQueue;
safe_queue<std::pair<chunk_pos, buffer::ShortIndexed::Data>> loadedQueue;
safe_priority_queue_map<area_<chunk_pos>, surrounding::faces, int, area_hash> loadQueue;
safe_queue<std::pair<area_<chunk_pos>, buffer::ShortIndexed::Data>> loadedQueue;
struct report {
circular_buffer<float> count = circular_buffer<float>(REPORT_BUFFER_SIZE, 0); // MAYBE: store int
@ -42,7 +42,7 @@ namespace contouring {
bool running = true;
std::vector<std::thread> workers;
void enqueue(const chunk_pos &, const world::chunk_map &);
void enqueue(const area_<chunk_pos> &, const world::ChunkContainer &);
private:
static inline bool isTransparent(const surrounding::faces &surrounding, const std::pair<ushort, ushort> &idx);

View File

@ -1,72 +1,79 @@
#include "surrounding.hpp"
#include "../world/Chunk.hpp"
#include "../world/Area.hpp"
#include "../data/math.hpp"
using namespace geometry;
namespace contouring::surrounding {
bool load(faces &out, const chunk_pos &chunkPos, const world::chunk_map &chunks) {
bool load(faces &out, const chunk_pos &chunkPos, const world::ChunkContainer &chunks) {
{
const auto it = chunks.find(chunkPos);
if (it == chunks.end())
const auto chunk = chunks.findInRange(chunkPos);
if (!chunk.has_value())
return false;
out[CENTER] = it->second;
out[CENTER] = chunk.value();
}
for (size_t i = 0; i < CENTER; i++) {
const auto it = chunks.find(chunkPos + g_face_offsets[i]);
if (it == chunks.end())
const auto chunk = chunks.findOrEmpty(chunkPos + g_face_offsets[i]);
if (!chunk.has_value())
return false;
out[i] = it->second;
out[i] = chunk.value();
}
return true;
}
std::pair<ushort, ushort> getNeighborIdx(ushort idx, Face face) {
std::pair<glm::idx, glm::idx> getNeighborIdx(glm::idx idx, Face face) {
switch (face) {
case Face::Forward:
if (idx % CHUNK_LENGTH >= CHUNK_LENGTH - 1)
return {static_cast<int>(Face::Forward), idx - (CHUNK_LENGTH - 1)};
if (idx % glm::IDX_LENGTH >= glm::IDX_LENGTH - 1)
return {static_cast<int>(Face::Forward), idx - (glm::IDX_LENGTH - 1)};
return {CENTER, idx + 1};
case Face::Backward:
if (idx % CHUNK_LENGTH <= 0)
return {static_cast<int>(Face::Backward), idx + (CHUNK_LENGTH - 1)};
if (idx % glm::IDX_LENGTH <= 0)
return {static_cast<int>(Face::Backward), idx + (glm::IDX_LENGTH - 1)};
return {CENTER, idx - 1};
case Face::Up:
if ((idx / CHUNK_LENGTH) % CHUNK_LENGTH >= CHUNK_LENGTH - 1)
return {static_cast<int>(Face::Up), idx - (CHUNK_LENGTH2 - CHUNK_LENGTH)};
return {CENTER, idx + CHUNK_LENGTH};
if ((idx / glm::IDX_LENGTH) % glm::IDX_LENGTH >= glm::IDX_LENGTH - 1)
return {static_cast<int>(Face::Up), idx - (glm::IDX_LENGTH2 - glm::IDX_LENGTH)};
return {CENTER, idx + glm::IDX_LENGTH};
case Face::Down:
if ((idx / CHUNK_LENGTH) % CHUNK_LENGTH <= 0)
return {static_cast<int>(Face::Down), idx + (CHUNK_LENGTH2 - CHUNK_LENGTH)};
return {CENTER, idx - CHUNK_LENGTH};
if ((idx / glm::IDX_LENGTH) % glm::IDX_LENGTH <= 0)
return {static_cast<int>(Face::Down), idx + (glm::IDX_LENGTH2 - glm::IDX_LENGTH)};
return {CENTER, idx - glm::IDX_LENGTH};
case Face::Right:
if (idx / CHUNK_LENGTH2 >= CHUNK_LENGTH - 1)
return {static_cast<int>(Face::Right), idx - (CHUNK_SIZE - CHUNK_LENGTH2)};
return {CENTER, idx + CHUNK_LENGTH2};
if (idx / glm::IDX_LENGTH2 >= glm::IDX_LENGTH - 1)
return {static_cast<int>(Face::Right), idx - (glm::IDX_SIZE - glm::IDX_LENGTH2)};
return {CENTER, idx + glm::IDX_LENGTH2};
case Face::Left:
if (idx / CHUNK_LENGTH2 <= 0)
return {static_cast<int>(Face::Left), idx + (CHUNK_SIZE - CHUNK_LENGTH2)};
return {CENTER, idx - CHUNK_LENGTH2};
if (idx / glm::IDX_LENGTH2 <= 0)
return {static_cast<int>(Face::Left), idx + (glm::IDX_SIZE - glm::IDX_LENGTH2)};
return {CENTER, idx - glm::IDX_LENGTH2};
default:
return {CENTER, idx};
}
}
bool load(corners &out, const chunk_pos &chunkPos, const world::chunk_map &chunks) {
for (size_t i = 0; i < 8; i++) {
const auto it = chunks.find(chunkPos + g_corner_offsets[i]);
if (it == chunks.end())
bool load(corners &out, const chunk_pos &chunkPos, const world::ChunkContainer &chunks) {
{
const auto chunk = chunks.findInRange(chunkPos);
if(!chunk.has_value())
return false;
out[i] = it->second;
out[0] = chunk.value();
}
for (size_t i = 1; i < 8; i++) {
const auto chunk = chunks.findOrEmpty(chunkPos + g_corner_offsets[i]);
if (!chunk.has_value())
return false;
out[i] = chunk.value();
}
return true;
}

View File

@ -10,7 +10,7 @@ namespace contouring::surrounding {
const auto CENTER = 6;
typedef std::array<std::shared_ptr<const world::Chunk>, CENTER+1> faces;
bool load(faces &out, const chunk_pos &chunkPos, const world::chunk_map &chunks);
bool load(faces &out, const chunk_pos &chunkPos, const world::ChunkContainer &chunks);
std::pair<ushort, ushort> getNeighborIdx(ushort idx, geometry::Face face);
@ -26,5 +26,5 @@ namespace contouring::surrounding {
chunk_pos(1, 1, 1),
};
bool load(corners &out, const chunk_pos &chunkPos, const world::chunk_map &chunks);
bool load(corners &out, const chunk_pos &chunkPos, const world::ChunkContainer &chunks);
}

View File

@ -11,7 +11,7 @@ namespace geometry {
};
Box(glm::vec3 min, glm::vec3 max): Min(min), Max(max) {
assert(("Min > Max", max.x >= min.x && max.y >= min.y && max.z >= min.z));
assert((max.x >= min.x && max.y >= min.y && max.z >= min.z) && "Min > Max");
}
inline static Box fromCenter(glm::vec3 center, float radius) { return Box(center - radius, center + radius); }
inline static Box fromMin(glm::vec3 min, glm::vec3 size) { return Box(min, min + size); }

View File

@ -7,4 +7,9 @@ namespace glm {
typedef vec<3, long> lvec3;
typedef vec<3, ushort> usvec3;
typedef vec<3, unsigned char> ucvec3;
const auto IDX_LENGTH = 32;
const auto IDX_LENGTH2 = IDX_LENGTH * IDX_LENGTH;
const auto IDX_SIZE = IDX_LENGTH2 * IDX_LENGTH;
using idx = glm::u16;
}

View File

@ -20,16 +20,32 @@ namespace glm {
constexpr long inline div(long long value, uint m) {
return value < 0 ? ((value+1) / (long long)m) - 1 : value / (long long)m;
}
constexpr ucvec3 inline modulo(const llvec3& value, const ucvec3& m) {
constexpr ucvec3 inline modulo(const llvec3& value, const ucvec3& m = ucvec3(IDX_LENGTH)) {
return ucvec3(rem(value.x, m.x), rem(value.y, m.y), rem(value.z, m.z));
}
constexpr lvec3 inline divide(const llvec3 &value, const ucvec3 &m) {
return lvec3(div(value.x, m.x), div(value.y, m.y), div(value.z, m.z));
}
constexpr lvec3 inline divide(const llvec3 &value, const uvec3 &m) {
return lvec3(div(value.x, m.x), div(value.y, m.y), div(value.z, m.z));
}
constexpr std::pair<lvec3, ucvec3> inline split(const llvec3 &value, const ucvec3 &m) {
constexpr lvec3 inline divide(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) {
return lvec3(div(value.x, m.x), div(value.y, m.y), div(value.z, m.z));
}
constexpr llvec3 inline multiply(const lvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) {
return llvec3(value) * llvec3(m);
}
constexpr std::pair<lvec3, ucvec3> inline split(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) {
return {divide(value, m), modulo(value, m)};
}
constexpr ucvec3 inline fromIdx(idx idx) {
assert(idx < IDX_SIZE);
return ucvec3(idx / IDX_LENGTH2, (idx / IDX_LENGTH) % IDX_LENGTH, idx % IDX_LENGTH);
}
constexpr idx inline toIdx(glm::u8 x, glm::u8 y, glm::u8 z) {
return (x * IDX_LENGTH + y) * IDX_LENGTH + z;
}
constexpr idx inline toIdx(ucvec3 pos) {
return toIdx(pos.x, pos.y, pos.z);
}
constexpr std::pair<lvec3, idx> inline splitIdx(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) {
return {divide(value, m), toIdx(rem(value.x, m.x), rem(value.y, m.y), rem(value.z, m.z))};
}
}

View File

@ -10,7 +10,7 @@
namespace data {
/// Thread safe queue with unique keys updating priority and value
template <class K, class V, class W>
template <class K, class V, class W, class hash = robin_hood::hash<K>>
class safe_priority_queue_map {
private:
static bool cmpByWeight(const std::pair<K, W> &a, const std::pair<K, W> &b) {
@ -18,7 +18,7 @@ namespace data {
}
std::vector<std::pair<K, W>> heap;
robin_hood::unordered_map<K, V> map;
robin_hood::unordered_map<K, V, hash> map;
std::mutex mutex;
std::condition_variable cv;

View File

@ -3,7 +3,6 @@
#include <queue>
#include <mutex>
#include <condition_variable>
#include <robin_hood.h>
namespace data {
/// Thread safe queue

View File

@ -7,11 +7,11 @@
namespace data {
/// Thread safe queue discarding duplicate values
template <class T>
template <class T, class hash = robin_hood::hash<T>>
class safe_unique_queue {
private:
std::queue<T> queue;
robin_hood::unordered_flat_set<T> set;
robin_hood::unordered_flat_set<T, hash> set;
std::mutex mutex;
std::condition_variable cv;

View File

@ -74,10 +74,10 @@ int main(int /*unused*/, char */*unused*/[]){
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
});
#if RMT_ENABLED
Remotery *rmt;
rmt_CreateGlobalInstance(&rmt);
rmt_BindOpenGL();
#if RMT_ENABLED
LOG("Profiling !");
#endif
@ -171,7 +171,7 @@ int main(int /*unused*/, char */*unused*/[]){
if(state.look_at.has_value()) {
lookProgram->useIt();
lookProgram->start(renderer);
const auto model = glm::scale(glm::translate(glm::scale(glm::mat4(1), 1.f / glm::vec3(options.voxel_density)), glm::vec3(state.look_at.value().first - offset * glm::llvec3(options.voxel_density)) - glm::vec3(.5 + options.tool.radius)), glm::vec3(1 + options.tool.radius * 2));
const auto model = glm::scale(glm::translate(glm::scale(glm::mat4(1), 1.f / glm::vec3(options.voxel_density)), glm::vec3(state.look_at.value().first.second - offset * glm::llvec3(options.voxel_density)) - glm::vec3(.5 + options.tool.radius)), glm::vec3(1 + options.tool.radius * 2));
lookBuffer.draw(lookProgram->setup(renderer, model));
}
renderer->postProcess();

View File

@ -166,8 +166,8 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
if (options.editor_show) {
ImGui::Begin("Editor", &options.editor_show, ImGuiWindowFlags_AlwaysAutoResize);
if (state.look_at.has_value()) {
ImGui::Text("Look at: (%lld, %lld, %lld) (%s, %.1f)", state.look_at.value().first.x, state.look_at.value().first.y, state.look_at.value().first.z, world::materials::textures[state.look_at.value().second.material()].c_str(), state.look_at.value().second.density() * 1. / world::Voxel::DENSITY_MAX);
ImGui::Text("(%.3f, %.3f, %.3f)", state.look_at.value().first.x * 1. / options.voxel_density, state.look_at.value().first.y * 1. / options.voxel_density, state.look_at.value().first.z * 1. / options.voxel_density);
ImGui::Text("Look at: (%ld: %lld, %lld, %lld) (%s, %.1f)", state.look_at.value().first.first, state.look_at.value().first.second.x, state.look_at.value().first.second.y, state.look_at.value().first.second.z, world::materials::textures[state.look_at.value().second.material()].c_str(), state.look_at.value().second.density() * 1. / world::Voxel::DENSITY_MAX);
ImGui::Text("(%.3f, %.3f, %.3f)", state.look_at.value().first.second.x * 1. / options.voxel_density, state.look_at.value().first.second.y * 1. / options.voxel_density, state.look_at.value().first.second.z * 1. / options.voxel_density);
} else {
ImGui::Text("Look at: none");
}

View File

@ -9,7 +9,7 @@
#define MIN_WIDTH 854
#define MIN_HEIGHT 480
void framebuffer_size_callback(GLFWwindow *window, int width, int height) {
void framebuffer_size_callback(GLFWwindow *, int width, int height) {
glViewport(0, 0, width, height);
}

View File

@ -181,7 +181,7 @@ struct options {
struct state {
bool capture_mouse = true;
camera_pos position = camera_pos(voxel_pos(0), 1);
std::optional<std::pair<voxel_pos, world::Voxel>> look_at = {};
std::optional<std::pair<area_<voxel_pos>, world::Voxel>> look_at = {};
std::shared_ptr<contouring::Abstract> contouring;

26
src/world/Area.cpp Normal file
View File

@ -0,0 +1,26 @@
#include "Area.hpp"
#include "Chunk.hpp"
using namespace world;
std::optional<std::shared_ptr<Chunk>> ChunkContainer::findInRange(const chunk_pos &pos) const {
const auto it = find(pos);
return it != end() ? std::make_optional(it->second) : std::nullopt;
}
std::optional<std::shared_ptr<const Chunk>> ChunkContainer::findOrEmpty(const chunk_pos &pos) const {
return inRange(pos) ? findInRange(pos) : std::make_optional(world::EMPTY_CHUNK);
}
std::shared_ptr<Region> Area::getRegion(const std::string& folderPath, const area_<region_pos>& pos) {
{ // Found
const auto shared = regions.lock_shared();
const auto it = shared->find(pos.second);
if(it != shared->end())
return it->second;
}
// Reading
const auto reg = std::make_shared<Region>(folderPath, pos);
const auto unique = regions.lock();
return unique->insert({pos.second, reg}).first->second;
}

47
src/world/Area.hpp Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include "forward.h"
#include <shared_mutex_guarded.h>
using namespace libguarded;
#include "region/index.hpp"
#include "Generator.hpp"
namespace world {
/// Chunk map with restricted access
struct ChunkContainer: robin_hood::unordered_map<chunk_pos, std::shared_ptr<Chunk>> {
private:
int radius;
public:
ChunkContainer(int radius): radius(radius) {
assert(radius < (1 << 22) / CHUNK_LENGTH);
}
inline bool inRange(const chunk_pos& pos) const {
return glm::abs(pos.x) < radius && glm::abs(pos.y) < radius && glm::abs(pos.z) < radius;
}
std::optional<std::shared_ptr<Chunk>> findInRange(const chunk_pos &pos) const;
std::optional<std::shared_ptr<const Chunk>> findOrEmpty(const chunk_pos &pos) const;
};
/// Area (aka big group of chunks)
struct Area {
public:
using regions_t = robin_hood::unordered_map<region_pos, std::shared_ptr<Region>>;
/// radius: size in chunk (length = radius * 2 + 1)
Area(int radius, int seed = 42): chunks(radius), generator(seed) { }
inline const ChunkContainer &getChunks() const { return chunks; }
inline ChunkContainer &setChunks() { return chunks; }
std::shared_ptr<Region> getRegion(const std::string& folderPath, const area_<region_pos> &);
shared_guarded<regions_t>::handle getRegions() { return regions.lock(); }
inline Generator &getGenerator() { return generator; }
private:
//TODO: position rotation
ChunkContainer chunks;
shared_guarded<regions_t> regions;
Generator generator;
};
}

View File

@ -2,6 +2,7 @@
#include "materials.hpp"
#include <algorithm>
#include "../data/math.hpp"
using namespace world;
@ -20,6 +21,7 @@ Chunk::Chunk(const chunk_pos& pos, Generator& rnd) {
rnd.free(densitySet);
rnd.free(materialSet);
}
#include <iostream>
Chunk::Chunk(std::istream& str, bool rle) {
if(rle) {
ushort i = 0;
@ -34,7 +36,7 @@ Chunk::Chunk(std::istream& str, bool rle) {
i++;
}
}
assert(("Mismatch data length", i == CHUNK_SIZE-1));
assert(i == CHUNK_SIZE && "Mismatch data length");
} else {
for(auto& voxel: voxels) {
str.read(reinterpret_cast<char *>(&voxel), sizeof(voxel));
@ -49,7 +51,7 @@ void Chunk::write(std::ostream& str, bool rle) const {
ushort counter = 1;
Voxel current = *it;
while(true) {
it++;
++it;
const auto end = (it == voxels.end());
if(end || current.value != it->value) {
str.write(reinterpret_cast<char *>(&counter), sizeof(counter));
@ -90,37 +92,37 @@ void Chunk::set(ushort idx, const Voxel& val) {
((!getNeighborIdx(idx, Face::Backward).has_value()) & Faces::Backward));
}
std::optional<ushort> Chunk::getNeighborIdx(ushort idx, Face dir) {
std::optional<chunk_voxel_idx> Chunk::getNeighborIdx(chunk_voxel_idx idx, Face dir) {
switch (dir) {
case Face::Forward:
if (idx % CHUNK_LENGTH >= CHUNK_LENGTH - 1)
if (idx % glm::IDX_LENGTH >= glm::IDX_LENGTH - 1)
return {};
return idx + 1;
case Face::Backward:
if (idx % CHUNK_LENGTH <= 0)
if (idx % glm::IDX_LENGTH <= 0)
return {};
return idx - 1;
case Face::Up:
if ((idx / CHUNK_LENGTH) % CHUNK_LENGTH >= CHUNK_LENGTH - 1)
if ((idx / glm::IDX_LENGTH) % glm::IDX_LENGTH >= glm::IDX_LENGTH - 1)
return {};
return idx + CHUNK_LENGTH;
return idx + glm::IDX_LENGTH;
case Face::Down:
if ((idx / CHUNK_LENGTH) % CHUNK_LENGTH <= 0)
if ((idx / glm::IDX_LENGTH) % glm::IDX_LENGTH <= 0)
return {};
return idx - CHUNK_LENGTH;
return idx - glm::IDX_LENGTH;
case Face::Right:
if (idx / CHUNK_LENGTH2 >= CHUNK_LENGTH - 1)
if (idx / glm::IDX_LENGTH2 >= glm::IDX_LENGTH - 1)
return {};
return idx + CHUNK_LENGTH2;
return idx + glm::IDX_LENGTH2;
case Face::Left:
if (idx / CHUNK_LENGTH2 <= 0)
if (idx / glm::IDX_LENGTH2 <= 0)
return {};
return idx - CHUNK_LENGTH2;
return idx - glm::IDX_LENGTH2;
default:
return {};

View File

@ -1,20 +1,23 @@
#pragma once
#include <memory>
#include <sstream>
#include "Generator.hpp"
#include "Voxel.hpp"
#include "../data/geometry/Faces.hpp"
#include <sstream>
#include "../data/math.hpp"
/// Chunk length
#define CHUNK_LENGTH2 (CHUNK_LENGTH * CHUNK_LENGTH)
#define CHUNK_SIZE (CHUNK_LENGTH2 * CHUNK_LENGTH)
namespace world {
const auto CHUNK_LENGTH2 = CHUNK_LENGTH * CHUNK_LENGTH;
const auto CHUNK_SIZE = CHUNK_LENGTH2 * CHUNK_LENGTH;
constexpr auto RLE = true; //NOTE: only 2.7% gain after zstd
using namespace geometry;
/// World part as linear 3d voxel array
class Chunk {
public:
Chunk() {}
Chunk(const chunk_pos& pos, Generator& rnd);
Chunk(std::istream& str, bool rle = RLE);
~Chunk();
@ -30,41 +33,31 @@ namespace world {
modified = true;
}
// Get voxel from index
inline const Voxel& get(ushort idx) const {
inline const Voxel& get(chunk_voxel_idx idx) const {
return voxels[idx];
}
// Get voxel from position
inline const Voxel& getAt(const chunk_voxel_pos& pos) const {
return get(getIdx(pos));
return get(glm::toIdx(pos));
}
// Set voxel from index
void set(ushort idx, const Voxel& val);
void set(chunk_voxel_idx idx, const Voxel& val);
// Set voxel from position
void setAt(const chunk_voxel_pos& pos, const Voxel& val) {
set(getIdx(pos), val);
set(glm::toIdx(pos), val);
}
// Break voxel
Item breakAt(const chunk_voxel_pos& pos, const Voxel& val) {
const auto idx = getIdx(pos);
std::optional<Item> replace(chunk_voxel_idx idx, const Voxel& val) {
const auto res = voxels[idx];
set(idx, val);
return Item{res.density(), res.material()};
return {Item{res.density(), res.material()}}; //TODO: materials break table
}
// Is player modified
inline bool isModified() const { return modified; }
// Write to file.
void write(std::ostream& str, bool rle = RLE) const;
static inline chunk_voxel_pos getPosition(ushort idx) {
return chunk_voxel_pos(idx / CHUNK_LENGTH2, (idx / CHUNK_LENGTH) % CHUNK_LENGTH, idx % CHUNK_LENGTH);
}
static inline ushort getIdx(chunk_voxel_pos pos) {
return getIdx(pos.x, pos.y, pos.z);
}
static inline ushort getIdx(uint x, uint y, uint z) {
return (x * CHUNK_LENGTH + y) * CHUNK_LENGTH + z;
}
static std::optional<ushort> getNeighborIdx(ushort idx, Face dir);
static std::optional<chunk_voxel_idx> getNeighborIdx(chunk_voxel_idx idx, Face dir);
private:
/// Chunk data
@ -76,4 +69,7 @@ namespace world {
/// Modified by player
bool modified = false;
};
/// Chunk full of air
static const std::shared_ptr<const Chunk> EMPTY_CHUNK = std::make_shared<Chunk>();
}

View File

@ -8,7 +8,9 @@
using namespace world;
Universe::Universe(const Universe::options &options): generator(42), dicts("content/zstd.dict"), contouring(std::make_shared<contouring::Dummy>()) {
const auto MAIN_AREA = 1;
Universe::Universe(const Universe::options &options): dicts("content/zstd.dict"), contouring(std::make_shared<contouring::Dummy>()) {
setOptions(options);
folderPath = options.folderPath;
struct vec_istream: std::streambuf {
@ -17,30 +19,33 @@ Universe::Universe(const Universe::options &options): generator(42), dicts("cont
}
};
running = true;
std::filesystem::create_directories(folderPath);
areas.emplace(MAIN_AREA, std::make_shared<Area>(1 << 2));
areas.emplace(2, std::make_shared<Area>(1 << 2, 43));
// Load workers
for (size_t i = 0; i < 4; i++) {
loadWorkers.emplace_back([&] {
const auto ctx = dicts.make_reader();
while (running) {
chunk_pos pos;
std::pair<area_<chunk_pos>, std::shared_ptr<Area>> task;
loadQueue.wait();
if (loadQueue.pop(pos)) {
if (loadQueue.pop(task)) {
//MAYBE: loadQueue.take to avoid duplicated work on fast move
rmt_ScopedCPUSample(ProcessLoad, 0);
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));
const auto reg = getRegion(rPos);
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));
Region::data data;
if(reg->read(cPos, ctx, data)) {
if(reg->read(rcPos.second, ctx, data)) {
rmt_ScopedCPUSample(ProcessRead, 0);
vec_istream idata(data);
std::istream iss(&idata);
loadedQueue.push({pos, std::make_shared<Chunk>(iss)});
} else {
rmt_ScopedCPUSample(ProcessGenerate, 0);
loadedQueue.push({pos, std::make_shared<Chunk>(pos, generator)});
loadedQueue.push({pos, std::make_shared<Chunk>(pos.second, task.second->getGenerator())});
}
}
}
@ -52,17 +57,16 @@ Universe::Universe(const Universe::options &options): generator(42), dicts("cont
saveWorkers.emplace_back([&] {
const auto ctx = dicts.make_writer();
while (running) {
robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>> task;
save_task_t task;
saveQueue.wait();
if (saveQueue.pop(task) && task.second->isModified()) {
if (saveQueue.pop(task) && task.second.second->isModified()) {
//MAYBE: queue.take to avoid concurent write or duplicated work on fast move
rmt_ScopedCPUSample(ProcessSave, 0);
std::ostringstream out;
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));
const auto reg = getRegion(rPos);
reg->write(cPos, ctx, out.str());
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());
}
}
});
@ -72,8 +76,9 @@ Universe::~Universe() {
contouring = NULL;
// Save all
for(auto& pair: chunks) {
saveQueue.push(pair);
for(auto& area: areas) {
for(auto& chunk: area.second->getChunks())
saveQueue.emplace(area, chunk);
}
if (auto size = saveQueue.size(); size > 0) {
std::cout << std::endl;
@ -99,88 +104,103 @@ Universe::~Universe() {
}
}
std::shared_ptr<Region> Universe::getRegion(const region_pos& pos) {
{ // Found
const auto shared = regions.lock_shared();
const auto it = shared->find(pos);
if(it != shared->end())
return it->second;
}
// Reading
const auto reg = std::make_shared<Region>(folderPath, pos);
const auto unique = regions.lock();
return unique->insert({pos, reg}).first->second;
}
void Universe::update(const voxel_pos& pos, Universe::report& rep) {
const chunk_pos newPos = glm::divide(pos, chunk_voxel_pos(CHUNK_LENGTH));
const chunk_pos newPos = glm::divide(pos);
const auto chunkChange = last_pos != newPos;
last_pos = newPos;
rmt_ScopedCPUSample(Universe, 0);
// Update alive chunks
// Update alive areas
{
rmt_ScopedCPUSample(Update, 0);
auto it = chunks.begin();
while (it != chunks.end()) {
if (glm::length2(last_pos - it->first) > keepDistance * keepDistance) {
saveQueue.push(*it);
it = chunks.erase(it);
size_t chunk_count = 0;
auto it = areas.begin();
while (it != areas.end()) {
auto diff = last_pos /*TODO: - it->second.position*/;
auto &chunks = it->second->setChunks();
if (glm::length2(diff) /*TODO: - it->second.radius * radius * CHUNK_SIZE * CHUNK_SIZE */ > keepDistance * keepDistance) {
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);
} 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);
auto it_c = chunks.begin();
while(it_c != chunks.end()) { // Update alive chunks
if (glm::length2(diff - it_c->first) > keepDistance * keepDistance) {
saveQueue.emplace(*it, *it_c);
it_c = chunks.erase(it_c);
}else {
const auto acPos = std::make_pair(it->first, it_c->first);
if (const auto neighbors = it_c->second->update()) {
contouring->onUpdate(acPos, chunks, neighbors.value()); //TODO: it->second.position
} else if (chunkChange) {
contouring->onNotify(acPos, chunks); //TODO: it->second.position
}
++it_c;
chunk_count++;
}
}
++it;
}
}
rep.chunk_count.push(chunk_count);
}
rep.chunk_unload.push(saveQueue.size());
{
rmt_ScopedCPUSample(Contouring, 0);
contouring->update(pos);
contouring->update(pos, areas);
//MAYBE: if(chunkChange) contouring->notify(chunks);
}
// Find missing chunks
if(chunkChange) {
rmt_ScopedCPUSample(ToLoad, 0);
//NOTE: 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 = last_pos + chunk_pos(x, y, z);
if (chunks.find(p) == chunks.end()) {
loadQueue.push(p, -dist2);
//TODO: for(auto& far: far_areas)
for(auto& area: areas) {
//NOTE: need dist so no easy sphere fill
auto& chunks = area.second->getChunks();
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 = last_pos + chunk_pos(x, y, z) /*TODO: + area.second->getPosition() */;
if (chunks.inRange(p) && chunks.find(p) == chunks.end()) {
loadQueue.push(std::make_pair(area.first, p), area.second, -dist2);
}
}
}
}}}
}}}
}
}
rep.chunk_load.push(loadQueue.size());
// Loaded chunks
// Store loaded chunks
{
rmt_ScopedCPUSample(Load, 0);
robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>> loaded;
robin_hood::pair<area_<chunk_pos>, std::shared_ptr<Chunk>> loaded;
while (loadedQueue.pop(loaded)) {
chunks.insert(loaded);
contouring->onUpdate(loaded.first, chunks, Faces::All);
if (const auto it = areas.find(loaded.first.first); it != areas.end()) {
auto &chunks = it->second->setChunks();
chunks.emplace(loaded.first.second, loaded.second);
contouring->onUpdate(loaded.first, chunks, Faces::All);
}
}
}
rep.chunk_count.push(chunks.size());
if(!rep.chunk_load.current() && !rep.chunk_unload.current()) {
rmt_ScopedCPUSample(Region, 0);
const auto unique = regions.lock(); // MAYBE: shared then unique
rep.region_count.push(unique->size());
const auto me = glm::divide(last_pos, glm::uvec3(REGION_LENGTH));
for (auto it = unique->begin(); it != unique->end(); it++) {
if (glm::length2(glm::lvec3(it->first) - me) > keepDistance) { //FIXME: probably a limit
unique->erase(it);
break; //NOTE: save one max per frame
for(auto& area: areas) {
const auto unique = area.second->getRegions(); // MAYBE: shared then unique
rep.region_count.push(unique->size());
const auto me = glm::divide(last_pos /*TODO: + position*/);
for (auto it = unique->begin(); it != unique->end(); ++it) {
if (glm::length2(glm::lvec3(it->first) - me) > keepDistance) { //FIXME: keepDistance^1 may suck with high values
unique->erase(it);
break; //NOTE: save one max per frame
}
}
}
}
@ -195,43 +215,63 @@ void Universe::setContouring(const std::shared_ptr<contouring::Abstract>& ct) {
last_pos = chunk_pos(INT_MAX); // trigger chunkChange on next update
}
std::optional<std::pair<voxel_pos, Voxel>> Universe::raycast(const Ray &ray) const {
std::optional<std::pair<area_<voxel_pos>, Voxel>> Universe::raycast(const Ray &ray) const {
std::vector<voxel_pos> points;
ray.grid(points);
std::shared_ptr<Chunk> chunk = NULL;
chunk_pos chunk_vec(INT_MAX);
for(auto point: points) {
const chunk_pos pos = glm::divide(point, glm::uvec3(CHUNK_LENGTH));
if(pos != chunk_vec) {
if(const auto& newChunk = at(pos)) {
chunk = newChunk.value();
chunk_vec = pos;
std::optional<std::pair<area_<voxel_pos>, Voxel>> target = std::nullopt;
size_t dist = points.size();
for(auto& area: areas) {
//TODO: if ray.insert(area.getIBox())
const auto &chunks = area.second->getChunks();
std::shared_ptr<Chunk> chunk = NULL;
chunk_pos chunk_vec(INT_MAX);
for (size_t i = 0; i < dist; i++) {
const auto pos = points[i] /*TODO: + area.position*/;
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 = NULL;
}
}
if(chunk != NULL) {
const auto voxel = chunk->getAt(glm::modulo(pos));
if(voxel.density() > 0) {
target = {{{area.first, pos}, voxel}};
dist = i;
i = points.size();
}
}
}
if(chunk != NULL) {
const auto voxel = chunk->getAt(glm::modulo(point, glm::uvec3(CHUNK_LENGTH)));
if(voxel.density() > 0)
return {{point, voxel}};
}
}
return target;
}
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)};
}
return {};
}
std::optional<Item> Universe::set(const voxel_pos& pos, const Voxel& val) {
const auto chunkPos = glm::divide(pos, glm::uvec3(CHUNK_LENGTH));
if(const auto& chunk = at(chunkPos)) {
return {chunk.value()->breakAt(glm::modulo(pos, glm::uvec3(CHUNK_LENGTH)), val)};
} else {
return {};
}
}
ItemList Universe::setCube(const voxel_pos& pos, const Voxel& val, int radius) {
ItemList Universe::setCube(const area_<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::llvec3(x, y, z), val));
}}}
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));
}}}
}
return list;
}

View File

@ -2,17 +2,14 @@
#include <string>
#include <thread>
#include <shared_mutex_guarded.h>
using namespace libguarded;
#include "../data/math.hpp"
#include "../data/safe_queue.hpp"
#include "../data/safe_priority_queue.hpp"
#include "../data/circular_buffer.hpp"
#include "../data/geometry/Ray.hpp"
#include "forward.h"
#include "Area.hpp"
#include "Voxel.hpp"
#include "region/index.hpp"
#include "Generator.hpp"
#define REPORT_BUFFER_SIZE 128
@ -57,11 +54,12 @@ namespace world {
/// Get nearest voxel colliding ray
/// @note ray in world scale
std::optional<std::pair<voxel_pos, Voxel>> raycast(const geometry::Ray &ray) const;
std::optional<std::pair<area_<voxel_pos>, Voxel>> raycast(const geometry::Ray &ray) const;
/// Set voxel at pos
std::optional<Item> set(const voxel_pos &pos, const Voxel &val);
std::optional<Item> set(const area_<voxel_pos> &pos, const Voxel &val);
/// Set cube of voxel with pos as center
ItemList setCube(const voxel_pos &pos, const Voxel &val, int radius);
/// MAYBE: allow set multi area
ItemList setCube(const area_<voxel_pos> &pos, const Voxel &val, int radius);
/// Change contouring worker
void setContouring(const std::shared_ptr<contouring::Abstract>& ct);
@ -73,33 +71,26 @@ namespace world {
private:
chunk_pos last_pos = chunk_pos(INT_MAX);
/// Data
chunk_map chunks;
/// Alive areas containing chunks
area_map areas;
using area_it_t = robin_hood::pair<area_id, std::shared_ptr<Area>>;
std::optional<std::shared_ptr<Chunk>> at(const chunk_pos& pos) const {
const auto it = chunks.find(pos);
if(it == chunks.end())
return {};
return {it->second};
}
Generator generator;
//TODO: far_areas: <pos, {id, size, seed}>
bool running = true;
std::vector<std::thread> loadWorkers;
safe_priority_queue<chunk_pos, int> loadQueue;
safe_queue<robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>>> loadedQueue;
safe_priority_queue_map<area_<chunk_pos>, std::shared_ptr<Area>, int, area_hash> loadQueue;
safe_queue<robin_hood::pair<area_<chunk_pos>, std::shared_ptr<Chunk>>> loadedQueue;
std::vector<std::thread> saveWorkers;
safe_queue<robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>>> saveQueue; //NOTE: consider const Chunk
using save_task_t = std::pair<area_it_t, robin_hood::pair<chunk_pos, std::shared_ptr<Chunk>>>;
data::safe_queue<save_task_t> saveQueue; //NOTE: consider const Area and Chunk
int loadDistance;
int keepDistance;
std::string folderPath;
shared_guarded<robin_hood::unordered_map<region_pos, std::shared_ptr<Region>>> regions;
dict_set dicts;
std::shared_ptr<Region> getRegion(const region_pos &);
/// Contouring worker
std::shared_ptr<contouring::Abstract> contouring;

View File

@ -1,9 +1,12 @@
#pragma once
#include "../data/safe_queue.hpp"
#include <robin_hood.h>
#include "position.h"
namespace world {
class Chunk;
typedef robin_hood::unordered_map<chunk_pos, std::shared_ptr<Chunk>> chunk_map;
class Area;
using area_map = robin_hood::unordered_map<area_id, std::shared_ptr<Area>>;
class ChunkContainer;
}

View File

@ -1,7 +1,9 @@
/**
* chunk_voxel_pos: u8 (contains CHUNK_LENGTH)
* chunk_voxel_pos: u8 (contains u5: CHUNK_LENGTH)
* chunk_voxel_idx: u16
* chunk_pos: i56
* region_chunk_pos: u8 (contains REGION_LENGTH)
* region_chunk_pos: u8 (contains u5: REGION_LENGTH)
* region_chunk_idx: u16
* region_pos: i48 (NOTE: trimmed to i32)
* voxel_pos: i64
*
@ -17,16 +19,33 @@
#pragma once
#include <functional>
#include "../data/glm.hpp"
const auto CHUNK_LENGTH = 32;
const auto REGION_LENGTH = 32;
const auto CHUNK_LENGTH = glm::IDX_LENGTH;
const auto REGION_LENGTH = glm::IDX_LENGTH;
using voxel_pos = glm::llvec3;
using chunk_pos = glm::lvec3;
using chunk_voxel_pos = glm::ucvec3;
using chunk_voxel_idx = glm::u16;
using region_pos = glm::ivec3;
using region_chunk_pos = glm::ucvec3;
using region_chunk_idx = glm::u16;
using area_id = uint64_t;
template <class pos>
using area_ = std::pair<area_id, pos>;
struct area_hash {
template <typename pos>
std::size_t operator()(area_<pos> const& a) const noexcept {
std::size_t h1 = std::hash<area_id>{}(a.first);
std::size_t h2 = std::hash<pos>{}(a.second);
return h1 ^ (h2 << 1);
}
};
using entity_id = uint64_t;
struct camera_pos {
using raw_t = region_pos;
using offset_t = glm::vec3;

View File

@ -6,9 +6,9 @@ using namespace world;
#define REMOVE_CORRUPTED 1
FileRegion::FileRegion(const std::string &folderPath, const region_pos &pos) {
path = folderPath + '/' + std::to_string(pos.x) + '.' +
std::to_string(pos.y) + '.' + std::to_string(pos.z) + ".map";
FileRegion::FileRegion(const std::string &folderPath, const area_<region_pos> &pos) {
path = folderPath + '/' + std::to_string(pos.first) + '.' + std::to_string(pos.second.x) + '.' +
std::to_string(pos.second.y) + '.' + std::to_string(pos.second.z) + ".map";
load();
}

View File

@ -11,7 +11,7 @@ namespace world {
///Group of chunks saved as a single file only pointer
class FileRegion {
public:
FileRegion(const std::string& folderPath, const region_pos &pos);
FileRegion(const std::string& folderPath, const area_<region_pos> &pos);
~FileRegion();
typedef std::vector<char> data;

View File

@ -5,9 +5,9 @@ using namespace world;
#define REMOVE_CORRUPTED 1
#define LAZYNESS 8
MemoryRegion::MemoryRegion(const std::string &folderPath, const region_pos &pos) {
path = folderPath + '/' + std::to_string(pos.x) + '.' +
std::to_string(pos.y) + '.' + std::to_string(pos.z) + ".map";
MemoryRegion::MemoryRegion(const std::string &folderPath, const area_<region_pos> &pos) {
path = folderPath + '/' + std::to_string(pos.first) + '.' + std::to_string(pos.second.x) + '.' +
std::to_string(pos.second.y) + '.' + std::to_string(pos.second.z) + ".map";
load();
}

View File

@ -12,7 +12,7 @@ namespace world {
///Group of chunks saved as a single file in memory
class MemoryRegion {
public:
MemoryRegion(const std::string& folderPath, const region_pos &pos);
MemoryRegion(const std::string& folderPath, const area_<region_pos> &pos);
~MemoryRegion();
typedef std::vector<char> data;