1
0
Fork 0

Area position

This commit is contained in:
May B. 2020-08-03 18:15:02 +02:00
parent 5c6ccd48e5
commit f7b34f74f8
18 changed files with 282 additions and 162 deletions

View File

@ -18,10 +18,11 @@ namespace contouring {
virtual void update(const voxel_pos &pos, const world::area_map &areas) = 0;
/// Chunk data change
virtual void onUpdate(const area_<chunk_pos> &pos, const world::ChunkContainer &data, geometry::Faces neighbors) = 0;
/// @param offset priority position offset
virtual void onUpdate(const area_<chunk_pos> &pos, const chunk_pos &offset, const world::ChunkContainer &data, geometry::Faces neighbors) = 0;
/// Chunk existante ping
/// @note notify for chunks entering view while moving
virtual void onNotify(const area_<chunk_pos> &pos, const world::ChunkContainer &data) = 0;
virtual void onNotify(const area_<chunk_pos> &pos, const chunk_pos &offset, const world::ChunkContainer &data) = 0;
/// Display ImGui config
virtual void onGui() = 0;
/// Get options

View File

@ -3,34 +3,40 @@
#include <imgui.h> // NOLINT
#include <toml.h>
#include "../world/Chunk.hpp"
#include "../world/Area.hpp"
namespace contouring {
void AbstractFlat::update(const voxel_pos& pos, const world::area_map& areas) {
center = glm::divide(pos);
size_t AbstractFlat::clear(const voxel_pos& pos, const world::area_map& areas) {
size_t buffer_count = 0;
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 {
const auto& offset = it_a->second.first = area->second->getOffset();
const auto center = glm::divide(pos - offset);
auto &bfs = it_a->second.second;
auto it = bfs.begin();
while(it != bfs.end()) {
if (glm::length2(center - it->first) > glm::pow2(keepDistance)) {
if(it->second != NULL)
delete it->second;
it = it_a->second.erase(it);
it = bfs.erase(it);
} else {
buffer_count++;
++it;
}
}
++it_a;
} else {
for(auto& buffer: it_a->second) {
for(auto& buffer: it_a->second.second) {
if(buffer.second != NULL)
delete buffer.second;
}
it_a = buffers.erase(it_a);
}
}
return buffer_count;
}
AbstractFlat::AbstractFlat(const std::string& str): Abstract() {
@ -54,9 +60,9 @@ 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 [_, 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);
for (const auto [_, area] : buffers) {
for (const auto [pos, buffer] : area.second) {
const glm::vec3 fPos = glm::vec3(area.first + glm::multiply(pos) - 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

@ -10,9 +10,6 @@ namespace contouring {
AbstractFlat(const std::string &str);
virtual ~AbstractFlat() {}
/// Each frame ping. Used to clear out of range
void update(const voxel_pos &, const world::area_map &areas) override;
/// Display ImGui config
void onGui() override;
@ -23,16 +20,10 @@ namespace contouring {
void getModels(std::vector<std::pair<glm::mat4, buffer::Abstract *const>> &out, const std::optional<geometry::Frustum> &frustum, const glm::llvec3& offset, int density) override;
protected:
bool inline inLoadRange(const chunk_pos& point) const {
return glm::length2(center - point) <= loadDistance * loadDistance;
}
bool inline inKeepRange(const chunk_pos& point) const {
return glm::length2(center - point) <= keepDistance * keepDistance;
}
size_t clear(const voxel_pos &, const world::area_map &areas);
//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);
robin_hood::unordered_flat_map<area_id, robin_hood::pair<voxel_pos, robin_hood::unordered_flat_map<chunk_pos, buffer::Abstract *>>> buffers;
int loadDistance = 3;
int keepDistance = 4;

View File

@ -10,8 +10,8 @@ namespace contouring {
virtual ~Dummy() { }
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 onUpdate(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &, geometry::Faces) override {}
void onNotify(const area_<chunk_pos> &, const 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

@ -49,9 +49,9 @@ namespace contouring {
return ss.str();
}
void FlatDualMC::enqueue(const area_<chunk_pos> &pos, const world::ChunkContainer &data) {
void FlatDualMC::enqueue(const area_<chunk_pos> &pos, const chunk_pos &offset, const world::ChunkContainer &data) {
rmt_ScopedCPUSample(EnqueueContouring, RMTSF_Aggregate);
const auto dist2 = glm::length2(center - pos.second); //TODO: - area.pos
const auto dist2 = glm::length2(offset - pos.second);
if (dist2 <= loadDistance * loadDistance) {
surrounding::corners surrounding;
if(surrounding::load(surrounding, pos.second, data)) {
@ -60,31 +60,30 @@ namespace contouring {
}
}
void FlatDualMC::onUpdate(const area_<chunk_pos> &pos, const world::ChunkContainer &data, geometry::Faces neighbors) {
enqueue(pos, data);
void FlatDualMC::onUpdate(const area_<chunk_pos> &pos, const chunk_pos &offset, const world::ChunkContainer &data, geometry::Faces neighbors) {
enqueue(pos, offset, data);
if (neighbors && (geometry::Faces::Left | geometry::Faces::Down | geometry::Faces::Backward)) {
for (size_t i = 1; i < 8; i++) {
enqueue(std::make_pair(pos.first, pos.second - surrounding::g_corner_offsets[i]), data);
enqueue(std::make_pair(pos.first, pos.second - surrounding::g_corner_offsets[i]), offset, data);
}
}
}
void FlatDualMC::onNotify(const area_<chunk_pos> &pos, const world::ChunkContainer &data) {
void FlatDualMC::onNotify(const area_<chunk_pos> &pos, const chunk_pos &offset, 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);
if(it == buffers.end() || it->second.second.find(pos.second) == it->second.second.end()) {
enqueue(pos, offset, data);
}
}
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);
auto& bfs = buffers[out.first.first];
auto &bfs = buffers[out.first.first].second; //NOTE: buffer.first uninitialized (will be set in clear())
if (const auto it = bfs.find(out.first.second); it != bfs.end()) {
if(it->second != NULL)
delete it->second;
@ -94,7 +93,8 @@ namespace contouring {
bfs.emplace(out.first.second, buffer);
}
}
reports.count.push(buffers.size()); //FIXME: get from AbstractFlat
size_t count = AbstractFlat::clear(pos, areas);
reports.count.push(count);
}
void FlatDualMC::onGui() {

View File

@ -26,10 +26,10 @@ namespace contouring {
std::string getOptions() override;
/// Chunk data change
void onUpdate(const area_<chunk_pos> &, const world::ChunkContainer &, geometry::Faces) override;
void onUpdate(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &, geometry::Faces) override;
/// Chunk existante ping
/// @note notify for chunks entering view while moving
void onNotify(const area_<chunk_pos> &, const world::ChunkContainer &) override;
void onNotify(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &) override;
protected:
safe_priority_queue_map<area_<chunk_pos>, surrounding::corners, int, area_hash> loadQueue;
@ -44,7 +44,7 @@ namespace contouring {
bool running = true;
std::vector<std::thread> workers;
void enqueue(const area_<chunk_pos> &, const world::ChunkContainer &);
void enqueue(const area_<chunk_pos> &, const chunk_pos &offset, const world::ChunkContainer &);
float iso = .1f;
bool manifold = true;

View File

@ -36,9 +36,9 @@ namespace contouring {
}
}
void FlatSurroundingBox::enqueue(const area_<chunk_pos> &pos, const world::ChunkContainer &data) {
void FlatSurroundingBox::enqueue(const area_<chunk_pos> &pos, const chunk_pos& offset, const world::ChunkContainer &data) {
rmt_ScopedCPUSample(EnqueueContouring, RMTSF_Aggregate);
const auto dist2 = glm::length2(center - pos.second /*TODO: - area.pos */);
const auto dist2 = glm::length2(offset - pos.second);
if (dist2 <= loadDistance * loadDistance) {
surrounding::faces surrounding;
if(surrounding::load(surrounding, pos.second, data)) {
@ -47,43 +47,42 @@ namespace contouring {
}
}
void FlatSurroundingBox::onUpdate(const area_<chunk_pos> &pos, const world::ChunkContainer &data, Faces neighbors) {
enqueue(pos, data);
void FlatSurroundingBox::onUpdate(const area_<chunk_pos> &pos, const chunk_pos& offset, const world::ChunkContainer &data, Faces neighbors) {
enqueue(pos, offset, data);
if (neighbors && Faces::Right)
enqueue(std::make_pair(pos.first, pos.second + 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)]), offset, data);
if (neighbors && Faces::Left)
enqueue(std::make_pair(pos.first, pos.second + 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)]), offset, data);
if (neighbors && Faces::Up)
enqueue(std::make_pair(pos.first, pos.second + 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)]), offset, data);
if (neighbors && Faces::Down)
enqueue(std::make_pair(pos.first, pos.second + 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)]), offset, data);
if (neighbors && Faces::Forward)
enqueue(std::make_pair(pos.first, pos.second + 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)]), offset, data);
if (neighbors && Faces::Backward)
enqueue(std::make_pair(pos.first, pos.second + 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)]), offset, data);
}
void FlatSurroundingBox::onNotify(const area_<chunk_pos> &pos, const world::ChunkContainer &data) {
void FlatSurroundingBox::onNotify(const area_<chunk_pos> &pos, const chunk_pos& offset, 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);
if(it == buffers.end() || it->second.second.find(pos.second) == it->second.second.end()) {
enqueue(pos, offset, data);
}
}
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);
auto& bfs = buffers[out.first.first];
auto& bfs = buffers[out.first.first].second; //NOTE: buffer.first uninitialized
if (const auto it = bfs.find(out.first.second); it != bfs.end()) {
if(it->second != NULL)
delete it->second;
@ -93,7 +92,8 @@ namespace contouring {
bfs.emplace(out.first.second, buffer);
}
}
reports.count.push(buffers.size()); //FIXME: get from AbstractFlat
size_t count = AbstractFlat::clear(pos, areas);
reports.count.push(count);
}
void FlatSurroundingBox::onGui() {

View File

@ -24,10 +24,10 @@ namespace contouring {
void onGui() override;
/// Chunk data change
void onUpdate(const area_<chunk_pos> &, const world::ChunkContainer &, geometry::Faces) override;
void onUpdate(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &, geometry::Faces) override;
/// Chunk existante ping
/// @note notify for chunks entering view while moving
void onNotify(const area_<chunk_pos> &, const world::ChunkContainer &) override;
void onNotify(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &) override;
protected:
safe_priority_queue_map<area_<chunk_pos>, surrounding::faces, int, area_hash> loadQueue;
@ -42,7 +42,7 @@ namespace contouring {
bool running = true;
std::vector<std::thread> workers;
void enqueue(const area_<chunk_pos> &, const world::ChunkContainer &);
void enqueue(const area_<chunk_pos> &, const chunk_pos& offset, const world::ChunkContainer &);
private:
static inline bool isTransparent(const surrounding::faces &surrounding, const std::pair<ushort, ushort> &idx);

View File

@ -1,7 +1,6 @@
#pragma once
#include <glm/glm.hpp>
#include <iostream>
namespace geometry {
/// Axis Aligned Floating Box

View File

@ -0,0 +1,49 @@
#pragma once
#include <glm/glm.hpp>
namespace geometry {
/// Axis Aligned Long Box
struct IBox {
using pos_t = glm::llvec3;
enum class ContainmentType {
Disjoint = false, Intersects, Contains
};
IBox(pos_t min, pos_t max): Min(min), Max(max) {
assert((max.x >= min.x && max.y >= min.y && max.z >= min.z) && "Min > Max");
}
inline static IBox fromCenter(pos_t center, pos_t::value_type radius) { return IBox(center - radius, center + radius); }
inline static IBox fromMin(pos_t min, pos_t size) { return IBox(min, min + size); }
pos_t Min;
pos_t Max;
ContainmentType contains(const IBox& box) const {
//test if all corner is in the same side of a face by just checking min and max
if (box.Max.x < Min.x
|| box.Min.x > Max.x
|| box.Max.y < Min.y
|| box.Min.y > Max.y
|| box.Max.z < Min.z
|| box.Min.z > Max.z)
return ContainmentType::Disjoint;
if (box.Min.x >= Min.x
&& box.Max.x <= Max.x
&& box.Min.y >= Min.y
&& box.Max.y <= Max.y
&& box.Min.z >= Min.z
&& box.Max.z <= Max.z)
return ContainmentType::Contains;
return ContainmentType::Intersects;
}
constexpr bool contains(const pos_t& pos) const {
return pos.x >= Min.x && pos.x <= Max.x &&
pos.y >= Min.y && pos.y <= Max.y &&
pos.z >= Min.z && pos.z <= Max.z;
}
};
}

View File

@ -1,6 +1,6 @@
#pragma once
#include "Box.hpp"
#include "IBox.hpp"
#include "../../world/position.h"
namespace geometry {
@ -83,5 +83,44 @@ namespace geometry {
}
points.push_back(current);
}
IBox::ContainmentType intersect(const IBox& box) const {
const glm::llvec3 start = from.as_voxel();
if(box.contains(start))
return IBox::ContainmentType::Contains;
const auto inv = 1. / glm::dvec3(dir);
glm::f64 tmin = ((inv.x < 0 ? box.Max : box.Min).x - start.x) * inv.x;
glm::f64 tmax = ((inv.x < 0 ? box.Min : box.Max).x - start.x) * inv.x;
glm::f64 tymin = ((inv.y < 0 ? box.Max : box.Min).y - start.y) * inv.y;
glm::f64 tymax = ((inv.y < 0 ? box.Min : box.Max).y - start.y) * inv.y;
if ((tmin > tymax) || (tymin > tmax)){
return IBox::ContainmentType::Disjoint;
}
if (tymin > tmin) {
tmin = tymin;
}
if (tymax < tmax){
tmax = tymax;
}
glm::f64 tzmin = ((inv.z < 0 ? box.Max : box.Min).z - start.z) * inv.z;
glm::f64 tzmax = ((inv.z < 0 ? box.Min : box.Max).z - start.z) * inv.z;
if ((tmin > tzmax) || (tzmin > tmax)){
return IBox::ContainmentType::Disjoint;
}
if (tzmin > tmin){
tmin = tzmin;
}
if (tzmax < tmax){
tmax = tzmax;
}
// this last check is different from the 'ray' case in below references:
// we need to check that the segment is on the span of the line
// that intersects the box
return tmax<0.0f || tmin> 1.0f ? IBox::ContainmentType::Intersects : IBox::ContainmentType::Disjoint;
}
};
}

View File

@ -7,9 +7,12 @@ namespace glm {
constexpr ivec3 inline iround(const vec3& p) {
return ivec3(std::round<int>(p.x), std::round<int>(p.y), std::round<int>(p.z));
}
constexpr int inline length2(const ivec3& a) {
constexpr long inline length2(const ivec3& a) {
return a.x * a.x + a.y * a.y + a.z * a.z;
}
constexpr long inline pow2(int v) {
return v * v;
}
constexpr ivec3 inline diff(const ivec3& a, const ivec3& b) {
return glm::abs(glm::abs(a) - glm::abs(b));
}

View File

@ -100,9 +100,9 @@ int main(int /*unused*/, char */*unused*/[]){
state.look_at = world.raycast(camera.getRay() * options.voxel_density);
if (state.capture_mouse && state.look_at.has_value()) {
if (inputs.isPressing(Mouse::Left))
world.setCube(state.look_at.value().first, world::Voxel(0), options.tool.radius);
world.setCube(state.look_at.value().pos, world::Voxel(0), options.tool.radius);
else if (inputs.isPressing(Mouse::Right))
world.setCube(state.look_at.value().first, world::Voxel(options.tool.material, world::Voxel::DENSITY_MAX), options.tool.radius);
world.setCube(state.look_at.value().pos, world::Voxel(options.tool.material, world::Voxel::DENSITY_MAX), options.tool.radius);
}
world.update((state.position * options.voxel_density).as_voxel(), reports.world);
inputs.saveKeys();
@ -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.second - 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().pos.second + state.look_at.value().offset - 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,11 @@ 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: (%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);
const auto &look = state.look_at.value();
ImGui::Text("Look at: (%ld: %lld, %lld, %lld) (%s, %.1f)", look.pos.first, look.pos.second.x, look.pos.second.y, look.pos.second.z,
world::materials::textures[look.value.material()].c_str(), look.value.density() * 1. / world::Voxel::DENSITY_MAX);
const auto w_pos = look.pos.second + look.offset;
ImGui::Text("(%.3f, %.3f, %.3f)", w_pos.x * 1. / options.voxel_density, w_pos.y * 1. / options.voxel_density, w_pos.z * 1. / options.voxel_density);
} else {
ImGui::Text("Look at: none");
}

View File

@ -107,7 +107,7 @@ struct options {
{"mode", contouring::names[contouring_idx]},
{"options", toml::table()}
}));
for(auto opt: contouring_data) {
for(const auto opt: contouring_data) {
if(!opt.second.empty())
config["mesh"]["options"].as_table()->insert_or_assign(opt.first, opt.second);
}
@ -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<area_<voxel_pos>, world::Voxel>> look_at = {};
std::optional<world::Universe::ray_target> look_at = {};
std::shared_ptr<contouring::Abstract> contouring;

View File

@ -5,6 +5,7 @@
using namespace libguarded;
#include "region/index.hpp"
#include "Generator.hpp"
#include "../data/geometry/IBox.hpp"
namespace world {
/// Chunk map with restricted access
@ -13,8 +14,10 @@ namespace world {
int radius;
public:
/// Area radius in chunks
constexpr inline int getRadius() const { return radius; }
ChunkContainer(int radius): radius(radius) {
assert(radius < (1 << 22) / CHUNK_LENGTH);
assert(radius > 0 && 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;
@ -29,16 +32,32 @@ namespace world {
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) { }
Area(const voxel_pos& center, int radius, int seed = 42): center(center), chunks(radius), generator(seed) { }
inline const voxel_pos &getOffset() const { return center; }
inline geometry::IBox getBounding() const {
return geometry::IBox(center - voxel_pos(CHUNK_LENGTH * (chunks.getRadius() - 1)),
center + voxel_pos(CHUNK_LENGTH * chunks.getRadius()));
}
/// Move offset return if chunk_change
constexpr bool inline move(const voxel_pos &offset) {
const auto prev = glm::divide(center);
center += offset;
return prev != glm::divide(center);
}
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
voxel_pos center;
//TODO: rotation
ChunkContainer chunks;
shared_guarded<regions_t> regions;

View File

@ -21,8 +21,8 @@ Universe::Universe(const Universe::options &options): dicts("content/zstd.dict"
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));
areas.emplace(MAIN_AREA, std::make_shared<Area>(glm::multiply(chunk_pos(1 << 8, 0, 0)), 1 << 20));
areas.emplace(2, std::make_shared<Area>(voxel_pos(0), 1 << 20));
// Load workers
for (size_t i = 0; i < 4; i++) {
@ -73,18 +73,19 @@ Universe::Universe(const Universe::options &options): dicts("content/zstd.dict"
}
}
Universe::~Universe() {
contouring = NULL;
contouring = nullptr;
// Save all
for(auto& area: areas) {
for(auto& chunk: area.second->getChunks())
for(const auto& chunk: area.second->getChunks())
saveQueue.emplace(area, chunk);
}
if (auto size = saveQueue.size(); size > 0) {
std::cout << std::endl;
LOG_I("Saving " << size << " chunks");
const auto SAVE_CHECK_TIME = 500;
do {
std::cout << "\rSaving... " << size << " " << std::flush;
std::this_thread::sleep_for(std::chrono::microseconds(500));
std::this_thread::sleep_for(std::chrono::microseconds(SAVE_CHECK_TIME));
size = saveQueue.size();
} while (size > 0);
std::cout << std::endl;
@ -102,6 +103,7 @@ Universe::~Universe() {
if (worker.joinable())
worker.join();
}
LOG_D("Universe disappeared");
}
void Universe::update(const voxel_pos& pos, Universe::report& rep) {
@ -110,15 +112,19 @@ void Universe::update(const voxel_pos& pos, Universe::report& rep) {
last_pos = newPos;
rmt_ScopedCPUSample(Universe, 0);
// Update alive areas
{
{ // Update alive areas
rmt_ScopedCPUSample(Update, 0);
size_t chunk_count = 0;
size_t region_count = 0;
const bool queuesEmpty = loadQueue.empty() && saveQueue.empty();
bool allLazy = true;
auto it = areas.begin();
while (it != areas.end()) {
auto diff = last_pos /*TODO: - it->second.position*/;
rmt_ScopedCPUSample(Area, 0);
const bool chunkChangeArea = (it->first == 2 && it->second->move(voxel_pos(1, 0, 0))) || chunkChange; // TODO: area.velocity
const chunk_pos diff = glm::divide(pos - it->second->getOffset());
auto &chunks = it->second->setChunks();
if (glm::length2(diff) /*TODO: - it->second.radius * radius * CHUNK_SIZE * CHUNK_SIZE */ > keepDistance * keepDistance) {
if (glm::length2(diff) > glm::pow2(keepDistance + it->second->getChunks().getRadius())) {
auto it_c = chunks.begin();
while(it_c != chunks.end()) {
saveQueue.emplace(*it, *it_c);
@ -126,27 +132,62 @@ void Universe::update(const voxel_pos& pos, Universe::report& rep) {
}
it = areas.erase(it);
} else {
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
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
}
++it_c;
chunk_count++;
}
}
++it;
}
}
rep.chunk_count.push(chunk_count);
rep.region_count.push(allLazy ? region_count : rep.region_count.current());
}
rep.chunk_load.push(loadQueue.size());
rep.chunk_unload.push(saveQueue.size());
{
rmt_ScopedCPUSample(Contouring, 0);
@ -154,53 +195,15 @@ void Universe::update(const voxel_pos& pos, Universe::report& rep) {
//MAYBE: if(chunkChange) contouring->notify(chunks);
}
// Find missing chunks
if(chunkChange) {
rmt_ScopedCPUSample(ToLoad, 0);
//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());
// Store loaded chunks
{
{ // Store loaded chunks
rmt_ScopedCPUSample(Load, 0);
robin_hood::pair<area_<chunk_pos>, std::shared_ptr<Chunk>> loaded;
while (loadedQueue.pop(loaded)) {
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);
}
}
}
if(!rep.chunk_load.current() && !rep.chunk_unload.current()) {
rmt_ScopedCPUSample(Region, 0);
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
}
const chunk_pos diff = glm::divide(pos - it->second->getOffset());
contouring->onUpdate(loaded.first, diff, chunks, Faces::All);
}
}
}
@ -215,33 +218,35 @@ 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<area_<voxel_pos>, Voxel>> Universe::raycast(const Ray &ray) const {
std::optional<Universe::ray_target> Universe::raycast(const Ray &ray) const {
std::vector<voxel_pos> points;
ray.grid(points);
std::optional<std::pair<area_<voxel_pos>, Voxel>> target = std::nullopt;
std::optional<Universe::ray_target> 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(ray.intersect(area.second->getBounding()) != IBox::ContainmentType::Disjoint) {
const auto &offset = area.second->getOffset();
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;
}
}
}
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 != 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();
}
}
}
}

View File

@ -52,9 +52,14 @@ namespace world {
/// Apply new options
void setOptions(const options &);
struct ray_target {
area_<voxel_pos> pos;
Voxel value;
voxel_pos offset;
};
/// Get nearest voxel colliding ray
/// @note ray in world scale
std::optional<std::pair<area_<voxel_pos>, Voxel>> raycast(const geometry::Ray &ray) const;
std::optional<ray_target> raycast(const geometry::Ray &ray) const;
/// Set voxel at pos
std::optional<Item> set(const area_<voxel_pos> &pos, const Voxel &val);
/// Set cube of voxel with pos as center
@ -79,12 +84,12 @@ namespace world {
bool running = true;
std::vector<std::thread> loadWorkers;
safe_priority_queue_map<area_<chunk_pos>, std::shared_ptr<Area>, int, area_hash> loadQueue;
safe_priority_queue_map<area_<chunk_pos>, std::shared_ptr<Area>, int, area_hash> loadQueue; //NOTE: consider Area const (getRegion uses mutex)
safe_queue<robin_hood::pair<area_<chunk_pos>, std::shared_ptr<Chunk>>> loadedQueue;
std::vector<std::thread> saveWorkers;
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
data::safe_queue<save_task_t> saveQueue; //NOTE: consider Area and Chunk const
int loadDistance;
int keepDistance;