Optimizations
Reorder chunk_voxel_idx Region exp saving ratio Single level chunk tree
This commit is contained in:
parent
6288606505
commit
765e9da05f
4
TODO.md
4
TODO.md
|
@ -73,6 +73,10 @@ Released as `0.0.1`: `Pre alpha 1`
|
||||||
- [ ] Inventory
|
- [ ] Inventory
|
||||||
- [ ] Octree
|
- [ ] Octree
|
||||||
- [ ] Surface aware LOD
|
- [ ] Surface aware LOD
|
||||||
|
- [ ] All levels (0-5)
|
||||||
|
- [ ] Tree fill
|
||||||
|
- [ ] Big edit queue
|
||||||
|
- [ ] Update region level average
|
||||||
- [ ] Area converter
|
- [ ] Area converter
|
||||||
- [ ] Minecraft import
|
- [ ] Minecraft import
|
||||||
- [ ] Base unit size change
|
- [ ] Base unit size change
|
||||||
|
|
|
@ -240,7 +240,7 @@ public:
|
||||||
float opacity = .8f;
|
float opacity = .8f;
|
||||||
} console;
|
} console;
|
||||||
|
|
||||||
struct {
|
struct overlay_t {
|
||||||
bool visible = true;
|
bool visible = true;
|
||||||
int corner = 3;
|
int corner = 3;
|
||||||
} overlay;
|
} overlay;
|
||||||
|
|
|
@ -50,6 +50,9 @@ namespace contouring {
|
||||||
/// Get pending elements
|
/// Get pending elements
|
||||||
virtual size_t getQueueSize() = 0;
|
virtual size_t getQueueSize() = 0;
|
||||||
|
|
||||||
|
/// Get max visible distance in chunks
|
||||||
|
virtual uint16_t getVisibleDist() const = 0;
|
||||||
|
|
||||||
struct area_info {
|
struct area_info {
|
||||||
world::transform absolute;
|
world::transform absolute;
|
||||||
world::cell_pos::value_type radius;
|
world::cell_pos::value_type radius;
|
||||||
|
|
|
@ -311,24 +311,26 @@ namespace contouring {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlatDualMC::render(const surrounding::corners &surrounding, render::Model::Data::indices_t &out, std::vector<render::VertexData> &tmp, Layer layer) const {
|
void FlatDualMC::render(const surrounding::corners &surrounding, render::Model::Data::indices_t &out, std::vector<render::VertexData> &tmp, Layer layer) const {
|
||||||
const int SIZE = world::CHUNK_LENGTH + 3;
|
constexpr uint8_t SIZE = world::CHUNK_LENGTH + 3;
|
||||||
std::array<dualmc::DualMC<float>::Point, SIZE * SIZE * SIZE> grid;
|
std::array<dualmc::DualMC<float>::Point, SIZE * SIZE * SIZE> grid;
|
||||||
const auto &materials = world::module::Registry::Get()->getMaterials();
|
const auto &materials = world::module::Registry::Get()->getMaterials();
|
||||||
{
|
{
|
||||||
ZoneScopedN("Load");
|
ZoneScopedN("Load");
|
||||||
const auto setCell = [&](int x, int y, int z, const world::Voxel &voxel) {
|
const auto setCell = [&](glm::idx i, const world::Voxel &voxel) {
|
||||||
auto &cell = grid[((z * SIZE) + y) * SIZE + x];
|
auto &cell = grid[i];
|
||||||
cell.w = voxel.material();
|
cell.w = voxel.material();
|
||||||
cell.x = voxel.density_ratio() * (!materials.invisibilities[cell.w] &&
|
cell.x = voxel.density_ratio() * (!materials.invisibilities[cell.w] &&
|
||||||
((materials.transparencies[cell.w] && (layer && Layer::Transparent)) ||
|
((materials.transparencies[cell.w] && (layer && Layer::Transparent)) ||
|
||||||
(!materials.transparencies[cell.w] && (layer && Layer::Solid))));
|
(!materials.transparencies[cell.w] && (layer && Layer::Solid))));
|
||||||
};
|
};
|
||||||
for (int z = 0; z < SIZE; z++) {
|
size_t i = 0;
|
||||||
for (int y = 0; y < SIZE; y++) {
|
for (uint8_t z = 0; z < SIZE; z++) {
|
||||||
for (int x = 0; x < SIZE; x++) {
|
for (uint8_t y = 0; y < SIZE; y++) {
|
||||||
|
for (uint8_t x = 0; x < SIZE; x++) {
|
||||||
const auto &chunk = surrounding[(z >= world::CHUNK_LENGTH) + (y >= world::CHUNK_LENGTH)*2 + (x >= world::CHUNK_LENGTH)*4];
|
const auto &chunk = surrounding[(z >= world::CHUNK_LENGTH) + (y >= world::CHUNK_LENGTH)*2 + (x >= world::CHUNK_LENGTH)*4];
|
||||||
const auto &voxel = chunk->get(glm::toIdx(x % world::CHUNK_LENGTH, y % world::CHUNK_LENGTH, z % world::CHUNK_LENGTH));
|
const auto &voxel = chunk->get(glm::toIdx(x & glm::IDX_MASK, y & glm::IDX_MASK, z & glm::IDX_MASK));
|
||||||
setCell(x, y, z, voxel);
|
setCell(i, voxel);
|
||||||
|
i++;
|
||||||
}}}
|
}}}
|
||||||
|
|
||||||
for (size_t i = 0; i < surrounding.size(); i++) {
|
for (size_t i = 0; i < surrounding.size(); i++) {
|
||||||
|
@ -337,7 +339,7 @@ namespace contouring {
|
||||||
for (auto it = edits.begin(); it != edits.end(); ++it) {
|
for (auto it = edits.begin(); it != edits.end(); ++it) {
|
||||||
auto p = offset + glm::ivec3(glm::fromIdx(it->first));
|
auto p = offset + glm::ivec3(glm::fromIdx(it->first));
|
||||||
if(p.x < SIZE && p.y < SIZE && p.z < SIZE) {
|
if(p.x < SIZE && p.y < SIZE && p.z < SIZE) {
|
||||||
setCell(p.x, p.y, p.z, it->second.value);
|
setCell(glm::toIdx(p, glm::ucvec3(SIZE)), it->second.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,8 @@ namespace contouring {
|
||||||
render::Model* getModel(const world::model_id&) const override;
|
render::Model* getModel(const world::model_id&) const override;
|
||||||
void getInstancedModels(instanced_draw_call draw, const world::Elements&, const std::optional<geometry::Frustum>& frustum, const world::cell_pos& offset, world::node_id ignore) const override;
|
void getInstancedModels(instanced_draw_call draw, const world::Elements&, const std::optional<geometry::Frustum>& frustum, const world::cell_pos& offset, world::node_id ignore) const override;
|
||||||
|
|
||||||
|
uint16_t getVisibleDist() const override { return keepDistance; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct area_models {
|
struct area_models {
|
||||||
std::unique_ptr<render::LodModel> main;
|
std::unique_ptr<render::LodModel> main;
|
||||||
|
|
|
@ -395,10 +395,7 @@ void DualMC<T>::calculateDualPoint(int32_t const cx, int32_t const cy, int32_t c
|
||||||
|
|
||||||
// compute the dual point as the mean of the face vertices belonging to the
|
// compute the dual point as the mean of the face vertices belonging to the
|
||||||
// original marching cubes face
|
// original marching cubes face
|
||||||
Vertex p;
|
Vertex p(0, 0, 0, 0);
|
||||||
p.x = 0;
|
|
||||||
p.y = 0;
|
|
||||||
p.z = 0;
|
|
||||||
int points = 0;
|
int points = 0;
|
||||||
T max = 0;
|
T max = 0;
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include <imgui_stdlib.h>
|
#include <imgui_stdlib.h>
|
||||||
#include "../state.hpp"
|
#include "../state.hpp"
|
||||||
#include "Window.hpp"
|
#include "Window.hpp"
|
||||||
|
#include "core/utils/os.hpp"
|
||||||
|
#include "api/Buffers.hpp"
|
||||||
#include "core/world/Elements.hpp"
|
#include "core/world/Elements.hpp"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
@ -115,38 +117,47 @@ void UI::Unload() {
|
||||||
sInstance = nullptr;
|
sInstance = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
UI::Actions drawMenu(config::client::options &options, state::state &state, const std::vector<std::string>& packs) {
|
template<typename F>
|
||||||
const ImGuiIO &io = ImGui::GetIO();
|
void drawOverlay(config::client::options::overlay_t& overlay, F inner) {
|
||||||
|
if (overlay.visible) {
|
||||||
if (options.overlay.visible) {
|
const ImGuiIO &io = ImGui::GetIO();
|
||||||
if (options.overlay.corner != -1) {
|
if (overlay.corner != -1) {
|
||||||
ImVec2 window_pos = ImVec2((options.overlay.corner & 1) ? io.DisplaySize.x - UI_MARGIN : UI_MARGIN, (options.overlay.corner & 2) ? io.DisplaySize.y - UI_MARGIN : UI_MARGIN);
|
ImVec2 window_pos = ImVec2((overlay.corner & 1) ? io.DisplaySize.x - UI_MARGIN : UI_MARGIN, (overlay.corner & 2) ? io.DisplaySize.y - UI_MARGIN : UI_MARGIN);
|
||||||
ImVec2 window_pos_pivot = ImVec2((options.overlay.corner & 1) ? 1.0f : 0.0f, (options.overlay.corner & 2) ? 1.0f : 0.0f);
|
ImVec2 window_pos_pivot = ImVec2((overlay.corner & 1) ? 1.0f : 0.0f, (overlay.corner & 2) ? 1.0f : 0.0f);
|
||||||
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
|
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
|
||||||
}
|
}
|
||||||
ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
|
ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
|
||||||
ImGui::Begin("Overlay", &options.overlay.visible, (options.overlay.corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav);
|
ImGui::Begin("Overlay", &overlay.visible, (overlay.corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav);
|
||||||
ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||||
|
inner();
|
||||||
|
ImGui::Text("RAM: %.3f/%.3f GB", os::GetProcess().memused / 1000000000.f, os::GetGlobal().ram.total / 1000000000.);
|
||||||
|
|
||||||
if (ImGui::BeginPopupContextWindow()) {
|
if (ImGui::BeginPopupContextWindow()) {
|
||||||
if (ImGui::MenuItem("Custom", NULL, options.overlay.corner == -1))
|
if (ImGui::MenuItem("Custom", NULL, overlay.corner == -1))
|
||||||
options.overlay.corner = -1;
|
overlay.corner = -1;
|
||||||
if (ImGui::MenuItem("Top-left", NULL, options.overlay.corner == 0))
|
if (ImGui::MenuItem("Top-left", NULL, overlay.corner == 0))
|
||||||
options.overlay.corner = 0;
|
overlay.corner = 0;
|
||||||
if (ImGui::MenuItem("Top-right", NULL, options.overlay.corner == 1))
|
if (ImGui::MenuItem("Top-right", NULL, overlay.corner == 1))
|
||||||
options.overlay.corner = 1;
|
overlay.corner = 1;
|
||||||
if (ImGui::MenuItem("Bottom-left", NULL, options.overlay.corner == 2))
|
if (ImGui::MenuItem("Bottom-left", NULL, overlay.corner == 2))
|
||||||
options.overlay.corner = 2;
|
overlay.corner = 2;
|
||||||
if (ImGui::MenuItem("Bottom-right", NULL, options.overlay.corner == 3))
|
if (ImGui::MenuItem("Bottom-right", NULL, overlay.corner == 3))
|
||||||
options.overlay.corner = 3;
|
overlay.corner = 3;
|
||||||
if (options.overlay.visible && ImGui::MenuItem("Close"))
|
if (overlay.visible && ImGui::MenuItem("Close"))
|
||||||
options.overlay.visible = false;
|
overlay.visible = false;
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UI::Actions drawMenu(config::client::options &options, state::state &state, const std::vector<std::string>& packs) {
|
||||||
|
|
||||||
|
drawOverlay(options.overlay, []{});
|
||||||
|
|
||||||
auto actions = drawCommon(options, state, packs);
|
auto actions = drawCommon(options, state, packs);
|
||||||
|
|
||||||
|
const ImGuiIO &io = ImGui::GetIO();
|
||||||
ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x / 2, io.DisplaySize.y / 2), ImGuiCond_Always, ImVec2(.5f, .5f));
|
ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x / 2, io.DisplaySize.y / 2), ImGuiCond_Always, ImVec2(.5f, .5f));
|
||||||
ImGui::Begin((std::string("Univerxel ") + UNIVERXEL_VERSION).c_str(), NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoCollapse);
|
ImGui::Begin((std::string("Univerxel ") + UNIVERXEL_VERSION).c_str(), NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoCollapse);
|
||||||
state.login.name.resize(128);
|
state.login.name.resize(128);
|
||||||
|
@ -327,33 +338,9 @@ UI::Actions drawInGame(config::client::options &options, state::state &state, co
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.overlay.visible) {
|
drawOverlay(options.overlay, [&]{
|
||||||
if (options.overlay.corner != -1) {
|
|
||||||
ImVec2 window_pos = ImVec2((options.overlay.corner & 1) ? io.DisplaySize.x - UI_MARGIN : UI_MARGIN, (options.overlay.corner & 2) ? io.DisplaySize.y - UI_MARGIN : UI_MARGIN);
|
|
||||||
ImVec2 window_pos_pivot = ImVec2((options.overlay.corner & 1) ? 1.0f : 0.0f, (options.overlay.corner & 2) ? 1.0f : 0.0f);
|
|
||||||
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
|
|
||||||
}
|
|
||||||
ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
|
|
||||||
ImGui::Begin("Overlay", &options.overlay.visible, (options.overlay.corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav);
|
|
||||||
ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
|
||||||
ImGui::Text("%ld tris(%ld models)", reports.tris_count, reports.models_count);
|
ImGui::Text("%ld tris(%ld models)", reports.tris_count, reports.models_count);
|
||||||
if (ImGui::BeginPopupContextWindow()) {
|
});
|
||||||
if (ImGui::MenuItem("Custom", NULL, options.overlay.corner == -1))
|
|
||||||
options.overlay.corner = -1;
|
|
||||||
if (ImGui::MenuItem("Top-left", NULL, options.overlay.corner == 0))
|
|
||||||
options.overlay.corner = 0;
|
|
||||||
if (ImGui::MenuItem("Top-right", NULL, options.overlay.corner == 1))
|
|
||||||
options.overlay.corner = 1;
|
|
||||||
if (ImGui::MenuItem("Bottom-left", NULL, options.overlay.corner == 2))
|
|
||||||
options.overlay.corner = 2;
|
|
||||||
if (ImGui::MenuItem("Bottom-right", NULL, options.overlay.corner == 3))
|
|
||||||
options.overlay.corner = 3;
|
|
||||||
if (options.overlay.visible && ImGui::MenuItem("Close"))
|
|
||||||
options.overlay.visible = false;
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,8 @@ struct ServerFactory: public world::AbstractServerFactory {
|
||||||
{
|
{
|
||||||
int load = options.world.loadDistance;
|
int load = options.world.loadDistance;
|
||||||
int keep = options.world.keepDistance;
|
int keep = options.world.keepDistance;
|
||||||
if (ImGui::SliderInt("Load distance", &load, 1, options.world.keepDistance) |
|
if (ImGui::SliderInt("Load distance##srv", &load, 1, options.world.keepDistance) |
|
||||||
ImGui::SliderInt("Keep distance", &keep, options.world.loadDistance + 1, 21)) {
|
ImGui::SliderInt("Keep distance##srv", &keep, options.world.loadDistance + 1, 21)) {
|
||||||
options.world.loadDistance = load;
|
options.world.loadDistance = load;
|
||||||
options.world.keepDistance = keep;
|
options.world.keepDistance = keep;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,9 @@ class Chunk final: public world::EdittableChunk {
|
||||||
public:
|
public:
|
||||||
Chunk(std::istream &is): EdittableChunk(new ChunkFutureEdits(this), is) { }
|
Chunk(std::istream &is): EdittableChunk(new ChunkFutureEdits(this), is) { }
|
||||||
/// Create from average
|
/// Create from average
|
||||||
Chunk(Voxel val): EdittableChunk(new ChunkFutureEdits(this)), isAverage(true), isMajorant(val.swap()) { voxels.fill(Voxel(val.material(), val.density())); }
|
Chunk(Voxel val): EdittableChunk(new ChunkFutureEdits(this), Voxel(val.material(), val.density())), isMajorant(val.swap()) { }
|
||||||
Chunk(): EdittableChunk(new ChunkFutureEdits(this)) { }
|
|
||||||
|
|
||||||
constexpr bool isTrusted(bool allowMajorant) const { return !isAverage || (allowMajorant && isMajorant); }
|
inline bool isTrusted(bool allowMajorant) const noexcept { return isHeavy() || (allowMajorant && isMajorant); }
|
||||||
void unsetMajorant() {
|
void unsetMajorant() {
|
||||||
assert(isMajorant);
|
assert(isMajorant);
|
||||||
isMajorant = false;
|
isMajorant = false;
|
||||||
|
@ -51,13 +50,11 @@ public:
|
||||||
ChunkFutureEdits &setEdits() { assert(edits); return *(ChunkFutureEdits*)edits.get(); }
|
ChunkFutureEdits &setEdits() { assert(edits); return *(ChunkFutureEdits*)edits.get(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Is temporary average
|
|
||||||
const bool isAverage = false;
|
|
||||||
/// Is temporary full valued
|
/// Is temporary full valued
|
||||||
bool isMajorant = false;
|
bool isMajorant = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Chunk full of air
|
/// Chunk full of void
|
||||||
static const std::shared_ptr<const Chunk> EMPTY_CHUNK = std::make_shared<Chunk>();
|
static const std::shared_ptr<const Chunk> EMPTY_CHUNK = std::make_shared<Chunk>(Voxel());
|
||||||
|
|
||||||
}
|
}
|
|
@ -351,7 +351,7 @@ bool DistantUniverse::onPacket(const memory::read_view& buf, net::PacketFlags) {
|
||||||
}
|
}
|
||||||
if (auto ck = readChunk()) {
|
if (auto ck = readChunk()) {
|
||||||
part->allocate();
|
part->allocate();
|
||||||
part->chunks.at(part->getIdx(pos.chunk)) = ck;
|
part->emplace(pos.chunk, ck);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ void LocalUniverse::update(cell_pos pos, float deltaTime) {
|
||||||
|
|
||||||
const auto lock = getElements();
|
const auto lock = getElements();
|
||||||
const auto &elements = *lock;
|
const auto &elements = *lock;
|
||||||
|
const auto visibleDist2 = glm::pow2<glm::l>(contouring->getVisibleDist());
|
||||||
{ // Update alive areas
|
{ // Update alive areas
|
||||||
ZoneScopedN("World");
|
ZoneScopedN("World");
|
||||||
auto rng = std::mt19937(std::rand());
|
auto rng = std::mt19937(std::rand());
|
||||||
|
@ -60,7 +61,8 @@ void LocalUniverse::update(cell_pos pos, float deltaTime) {
|
||||||
//MAYBE: compute absolute manually from chunk_pos(1) * pivot
|
//MAYBE: compute absolute manually from chunk_pos(1) * pivot
|
||||||
const cell_pos chunkPos = area_tf.computeChild(glm::multiply(chunk.first));
|
const cell_pos chunkPos = area_tf.computeChild(glm::multiply(chunk.first));
|
||||||
const auto chunkDist = glm::length2(glm::divide(chunkPos - pos));
|
const auto chunkDist = glm::length2(glm::divide(chunkPos - pos));
|
||||||
//MAYBE: if in load range
|
if (chunkDist > visibleDist2) // FIXME: World still > 20ms with big contouring.keepDist
|
||||||
|
continue;
|
||||||
|
|
||||||
if(const auto neighbors = chunk.second->setEdits()->update(deltaTime, rng() < contouringThreshold)) {
|
if(const auto neighbors = chunk.second->setEdits()->update(deltaTime, rng() < contouringThreshold)) {
|
||||||
contouring->onUpdate(area_chunk_pos{id, chunk.first}, chunkDist, chunks, neighbors.value());
|
contouring->onUpdate(area_chunk_pos{id, chunk.first}, chunkDist, chunks, neighbors.value());
|
||||||
|
|
|
@ -16,7 +16,9 @@ namespace glm {
|
||||||
typedef vec<3, uc> ucvec3;
|
typedef vec<3, uc> ucvec3;
|
||||||
|
|
||||||
const auto IDX_LENGTH = 32;
|
const auto IDX_LENGTH = 32;
|
||||||
|
const auto IDX_SHIFT = 5;
|
||||||
const auto IDX_LENGTH2 = IDX_LENGTH * IDX_LENGTH;
|
const auto IDX_LENGTH2 = IDX_LENGTH * IDX_LENGTH;
|
||||||
const auto IDX_SIZE = IDX_LENGTH2 * IDX_LENGTH;
|
const auto IDX_SIZE = IDX_LENGTH2 * IDX_LENGTH;
|
||||||
using idx = glm::u16;
|
using idx = glm::u16;
|
||||||
|
const idx IDX_MASK = (1u<<IDX_SHIFT)-1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@ namespace glm {
|
||||||
constexpr U inline pow2(U v) {
|
constexpr U inline pow2(U v) {
|
||||||
return v * v;
|
return v * v;
|
||||||
}
|
}
|
||||||
|
template<typename U>
|
||||||
|
constexpr U inline pow3(U v) {
|
||||||
|
return v * v * v;
|
||||||
|
}
|
||||||
constexpr ll inline length2(const llvec3& v) {
|
constexpr ll inline length2(const llvec3& v) {
|
||||||
return pow2(v.x) + pow2(v.y) + pow2(v.z);
|
return pow2(v.x) + pow2(v.y) + pow2(v.z);
|
||||||
}
|
}
|
||||||
|
@ -64,32 +68,43 @@ namespace glm {
|
||||||
constexpr std::pair<lvec3, ucvec3> inline split(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) {
|
constexpr std::pair<lvec3, ucvec3> inline split(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) {
|
||||||
return {divide(value, m), modulo(value, m)};
|
return {divide(value, m), modulo(value, m)};
|
||||||
}
|
}
|
||||||
|
/// idx order is z-y-x
|
||||||
constexpr ucvec3 inline fromIdx(idx idx) {
|
constexpr ucvec3 inline fromIdx(idx idx) {
|
||||||
assert(idx < IDX_SIZE);
|
assert(idx < IDX_SIZE);
|
||||||
return ucvec3(idx / IDX_LENGTH2, (idx / IDX_LENGTH) % IDX_LENGTH, idx % IDX_LENGTH);
|
return ucvec3(idx % IDX_LENGTH, (idx / IDX_LENGTH) % IDX_LENGTH, idx / IDX_LENGTH2);
|
||||||
|
}
|
||||||
|
template <idx S = IDX_SHIFT>
|
||||||
|
constexpr ucvec3 inline fromIdxShift(idx idx) {
|
||||||
|
assert(idx < IDX_SIZE);
|
||||||
|
constexpr glm::idx MASK = (1u<<S)-1u;
|
||||||
|
return ucvec3(idx & MASK, (idx >> S) & MASK, (idx >> (2*S)) & MASK);
|
||||||
}
|
}
|
||||||
constexpr usvec3 inline fromIdx(size_t idx, lvec3 size) {
|
constexpr usvec3 inline fromIdx(size_t idx, lvec3 size) {
|
||||||
assert(size.x>=0 && size.y>=0 && size.z>=0);
|
assert(size.x>=0 && size.y>=0 && size.z>=0);
|
||||||
assert((lvec3::value_type)idx < size.x * size.y * size.z);
|
assert((lvec3::value_type)idx < size.x * size.y * size.z);
|
||||||
return usvec3(idx / (size.y * size.z), (idx / size.z) % size.y, idx % size.z);
|
return usvec3(idx % size.x, (idx / size.x) % size.y, idx / (size.y * size.x));
|
||||||
|
}
|
||||||
|
template <idx S = IDX_SHIFT>
|
||||||
|
constexpr idx inline toIdxShift(glm::us x, glm::us y, glm::us z) {
|
||||||
|
return (z << (2*S)) | (y << S) | x;
|
||||||
}
|
}
|
||||||
constexpr idx inline toIdx(glm::uc x, glm::uc y, glm::uc z) {
|
constexpr idx inline toIdx(glm::uc x, glm::uc y, glm::uc z) {
|
||||||
return (x * IDX_LENGTH + y) * IDX_LENGTH + z;
|
return (z * IDX_LENGTH + y) * IDX_LENGTH + x;
|
||||||
}
|
}
|
||||||
constexpr idx inline toIdx(glm::uc x, glm::uc y, glm::uc z, glm::uc sy, glm::uc sz) {
|
constexpr idx inline toIdx(glm::uc x, glm::uc y, glm::uc z, glm::uc sy, glm::uc sx) {
|
||||||
return x * sy * sz + y * sz + z;
|
return z * sy * sx + y * sx + x;
|
||||||
}
|
}
|
||||||
constexpr idx inline toIdx(ucvec3 pos) {
|
constexpr idx inline toIdx(ucvec3 pos) {
|
||||||
return toIdx(pos.x, pos.y, pos.z);
|
return toIdx(pos.x, pos.y, pos.z);
|
||||||
}
|
}
|
||||||
constexpr idx inline toIdx(ucvec3 pos, ucvec3 size) {
|
constexpr idx inline toIdx(ucvec3 pos, ucvec3 size) {
|
||||||
return toIdx(pos.x, pos.y, pos.z, size.y, size.z);
|
return toIdx(pos.x, pos.y, pos.z, size.y, size.x);
|
||||||
}
|
}
|
||||||
constexpr l inline toIdx(lvec3 pos) {
|
constexpr l inline toIdx(lvec3 pos) {
|
||||||
return toIdx(pos.x, pos.y, pos.z);
|
return toIdx(pos.x, pos.y, pos.z);
|
||||||
}
|
}
|
||||||
constexpr l inline toIdx(lvec3 pos, lvec3 size) {
|
constexpr l inline toIdx(lvec3 pos, lvec3 size) {
|
||||||
return toIdx(pos.x, pos.y, pos.z, size.y, size.z);
|
return toIdx(pos.x, pos.y, pos.z, size.y, size.x);
|
||||||
}
|
}
|
||||||
constexpr std::pair<lvec3, idx> inline splitIdx(const llvec3 &value, const ucvec3 &m = ucvec3(IDX_LENGTH)) {
|
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))};
|
return {divide(value, m), toIdx(rem(value.x, m.x), rem(value.y, m.y), rem(value.z, m.z))};
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
#include "os.hpp"
|
||||||
|
|
||||||
|
#ifdef _WINDOWS
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <psapi.h>
|
||||||
|
|
||||||
|
os::global os::GetGlobal() {
|
||||||
|
global out{};
|
||||||
|
|
||||||
|
MEMORYSTATUSEX memInfo;
|
||||||
|
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
|
||||||
|
GlobalMemoryStatusEx(&memInfo);
|
||||||
|
DWORDLONG totalVirtualMem = memInfo.ullTotalPageFile;
|
||||||
|
|
||||||
|
out.ram.total = memInfo.ullTotalPhys;
|
||||||
|
out.ram.free = memInfo.ullAvailPhys
|
||||||
|
out.swap.total = memInfo.ullTotalPageFile;
|
||||||
|
out.swap.total -= out.ram.total;
|
||||||
|
out.swap.free = memInfo.ullAvailPageFile;
|
||||||
|
out.swap.free -= out.ram.free;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
os::process os::GetProcess() {
|
||||||
|
PROCESS_MEMORY_COUNTERS_EX pmc;
|
||||||
|
GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc));
|
||||||
|
process out{};
|
||||||
|
out.memused = pmc.PrivateUsage;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/sysinfo.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
os::global os::GetGlobal() {
|
||||||
|
struct sysinfo memInfo;
|
||||||
|
sysinfo(&memInfo);
|
||||||
|
global out{};
|
||||||
|
out.ram.total = memInfo.totalram;
|
||||||
|
out.ram.total *= memInfo.mem_unit;
|
||||||
|
out.ram.free = memInfo.freeram;
|
||||||
|
out.ram.free *= memInfo.mem_unit;
|
||||||
|
out.swap.total = memInfo.totalswap;
|
||||||
|
out.swap.total *= memInfo.mem_unit;
|
||||||
|
out.swap.free = memInfo.freeswap;
|
||||||
|
out.swap.free *= memInfo.mem_unit;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
os::process os::GetProcess() {
|
||||||
|
struct rusage r_usage;
|
||||||
|
process out{};
|
||||||
|
getrusage(RUSAGE_SELF, &r_usage);
|
||||||
|
out.memused = r_usage.ru_maxrss;
|
||||||
|
out.memused *= 1000;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace os {
|
||||||
|
|
||||||
|
struct global {
|
||||||
|
struct memory {
|
||||||
|
/// In bytes
|
||||||
|
unsigned long long total;
|
||||||
|
unsigned long long free;
|
||||||
|
};
|
||||||
|
memory ram;
|
||||||
|
memory swap;
|
||||||
|
};
|
||||||
|
global GetGlobal();
|
||||||
|
|
||||||
|
struct process {
|
||||||
|
unsigned long long memused;
|
||||||
|
};
|
||||||
|
process GetProcess();
|
||||||
|
}
|
|
@ -5,8 +5,9 @@
|
||||||
|
|
||||||
using namespace world;
|
using namespace world;
|
||||||
|
|
||||||
Chunk::Chunk(std::istream& str, bool rle) {
|
Chunk::Chunk(std::istream& str) {
|
||||||
if(rle) {
|
voxels.resize(CHUNK_SIZE);
|
||||||
|
if constexpr(RLE) {
|
||||||
uint16_t i = 0;
|
uint16_t i = 0;
|
||||||
while(!str.eof()) {
|
while(!str.eof()) {
|
||||||
uint16_t count;
|
uint16_t count;
|
||||||
|
@ -19,13 +20,18 @@ Chunk::Chunk(std::istream& str, bool rle) {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(i == CHUNK_SIZE && "Mismatch data length");
|
assert((i == 1 || i == CHUNK_SIZE) && "Mismatch data length");
|
||||||
|
if (i == 1) {
|
||||||
|
voxels.resize(1);
|
||||||
|
voxels.shrink_to_fit();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for(auto& voxel: voxels) {
|
for(auto& voxel: voxels) {
|
||||||
str.read(reinterpret_cast<char *>(&voxel), sizeof(voxel));
|
str.read(reinterpret_cast<char *>(&voxel), sizeof(voxel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Chunk::Chunk(Voxel v): voxels({v}) { }
|
||||||
Chunk::~Chunk() { }
|
Chunk::~Chunk() { }
|
||||||
|
|
||||||
const Voxel &Chunk::getAt(const chunk_voxel_pos &pos) const {
|
const Voxel &Chunk::getAt(const chunk_voxel_pos &pos) const {
|
||||||
|
|
|
@ -6,22 +6,28 @@
|
||||||
namespace world {
|
namespace world {
|
||||||
constexpr auto RLE = true; //NOTE: only ~2.5% gain after zstd
|
constexpr auto RLE = true; //NOTE: only ~2.5% gain after zstd
|
||||||
|
|
||||||
/// World part as linear 3d voxel array
|
/// World part as linear 3d voxel array or just a single one (32x LOD)
|
||||||
class Chunk {
|
class Chunk {
|
||||||
public:
|
public:
|
||||||
Chunk(std::istream& str, bool rle = RLE);
|
Chunk(std::istream& str);
|
||||||
virtual ~Chunk();
|
virtual ~Chunk();
|
||||||
|
|
||||||
/// Get voxel from index
|
/// Get voxel from index
|
||||||
inline const Voxel& get(chunk_voxel_idx idx) const {
|
inline const Voxel& get(chunk_voxel_idx idx) const {
|
||||||
return voxels[idx];
|
assert(isAllocated());
|
||||||
|
return voxels[isHeavy() ? idx : 0];
|
||||||
}
|
}
|
||||||
/// Get voxel from position
|
/// Get voxel from position
|
||||||
const Voxel &getAt(const chunk_voxel_pos &pos) const;
|
const Voxel &getAt(const chunk_voxel_pos &pos) const;
|
||||||
|
|
||||||
|
inline bool isAllocated() const noexcept { return !voxels.empty(); }
|
||||||
|
inline bool isHeavy() const noexcept { return voxels.size() > 1; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
Chunk(Voxel v);
|
||||||
|
/// NOTE: voxels must be allocated by child constructor
|
||||||
Chunk() { }
|
Chunk() { }
|
||||||
/// Chunk data
|
/// Chunk data
|
||||||
std::array<Voxel, CHUNK_SIZE> voxels;
|
std::vector<Voxel> voxels; //MAYBE: manual handle
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -6,16 +6,31 @@
|
||||||
|
|
||||||
using namespace world;
|
using namespace world;
|
||||||
|
|
||||||
EdittableChunk::EdittableChunk(owner<ChunkEdits*> edits, std::istream& str, bool rle): world::Chunk(str, rle), edits(edits) { }
|
EdittableChunk::EdittableChunk(owner<ChunkEdits*> edits, std::istream& str): world::Chunk(str), edits(edits) { }
|
||||||
|
EdittableChunk::EdittableChunk(owner<ChunkEdits*> edits, Voxel v): world::Chunk(v), edits(edits) { }
|
||||||
EdittableChunk::EdittableChunk(owner<ChunkEdits*> edits): world::Chunk(), edits(edits) { }
|
EdittableChunk::EdittableChunk(owner<ChunkEdits*> edits): world::Chunk(), edits(edits) { }
|
||||||
EdittableChunk::~EdittableChunk() { }
|
EdittableChunk::~EdittableChunk() { }
|
||||||
|
|
||||||
|
void EdittableChunk::set(chunk_voxel_idx idx, const Voxel& voxel) {
|
||||||
|
if (!isHeavy()) {
|
||||||
|
if (get(idx).value == voxel.value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
flatten();
|
||||||
|
}
|
||||||
|
voxels[idx] = voxel;
|
||||||
|
}
|
||||||
void EdittableChunk::setAt(const chunk_voxel_pos &pos, const Voxel& voxel) {
|
void EdittableChunk::setAt(const chunk_voxel_pos &pos, const Voxel& voxel) {
|
||||||
return set(glm::toIdx(pos), voxel);
|
return set(glm::toIdx(pos), voxel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EdittableChunk::flatten() {
|
||||||
|
assert(isAllocated());
|
||||||
|
voxels.resize(CHUNK_SIZE, voxels.front());
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<Faces> ChunkEdits::update(float deltaTime, bool animate) {
|
std::optional<Faces> ChunkEdits::update(float deltaTime, bool animate) {
|
||||||
ZoneScopedN("Chunk");
|
//ZoneScopedN("Chunk");
|
||||||
for(auto it = edits.begin(); it != edits.end();) {
|
for(auto it = edits.begin(); it != edits.end();) {
|
||||||
it->second.delay -= deltaTime;
|
it->second.delay -= deltaTime;
|
||||||
if(it->second.delay <= 0 && animate) {
|
if(it->second.delay <= 0 && animate) {
|
||||||
|
|
|
@ -61,14 +61,21 @@ namespace world {
|
||||||
const ChunkEdits& getEdits() const { assert(edits); return *edits.get(); }
|
const ChunkEdits& getEdits() const { assert(edits); return *edits.get(); }
|
||||||
|
|
||||||
/// Direct set without update of voxel at index
|
/// Direct set without update of voxel at index
|
||||||
inline void set(chunk_voxel_idx idx, const Voxel& voxel) {
|
void set(chunk_voxel_idx idx, const Voxel &voxel);
|
||||||
voxels[idx] = voxel;
|
|
||||||
}
|
|
||||||
/// Direct set without update of voxel at position
|
/// Direct set without update of voxel at position
|
||||||
void setAt(const chunk_voxel_pos &pos, const Voxel& voxel);
|
void setAt(const chunk_voxel_pos &pos, const Voxel& voxel);
|
||||||
|
|
||||||
|
/// Convert light to heavy
|
||||||
|
/// Faster to edit
|
||||||
|
void flatten();
|
||||||
|
/// Convert heavy to light
|
||||||
|
/// Uses less memory
|
||||||
|
void shrink();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
EdittableChunk(owner<ChunkEdits*>, std::istream& str, bool rle = RLE);
|
EdittableChunk(owner<ChunkEdits*>, std::istream& str);
|
||||||
|
EdittableChunk(owner<ChunkEdits*>, Voxel);
|
||||||
|
/// NOTE: voxels must be allocated by child constructor
|
||||||
EdittableChunk(owner<ChunkEdits*>);
|
EdittableChunk(owner<ChunkEdits*>);
|
||||||
const std::unique_ptr<ChunkEdits> edits;
|
const std::unique_ptr<ChunkEdits> edits;
|
||||||
};
|
};
|
||||||
|
|
|
@ -141,10 +141,18 @@ namespace world {
|
||||||
assert(inRange(c));
|
assert(inRange(c));
|
||||||
const auto idx = getIdx(c);
|
const auto idx = getIdx(c);
|
||||||
if (idx < chunks.size())
|
if (idx < chunks.size())
|
||||||
return chunks.at(idx);
|
return chunks[idx];
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
void emplace(const chunk_pos& c, const std::shared_ptr<world::EdittableChunk>& ck) {
|
||||||
|
allocate();
|
||||||
|
chunks[getIdx(c)] = ck;
|
||||||
|
}
|
||||||
|
void emplace(const chunk_pos& c, std::shared_ptr<world::EdittableChunk>&& ck) {
|
||||||
|
allocate();
|
||||||
|
chunks[getIdx(c)] = std::move(ck);
|
||||||
|
}
|
||||||
|
|
||||||
glm::usvec3 size;
|
glm::usvec3 size;
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,8 +8,11 @@ namespace world::generator {
|
||||||
/// Abstract Noise generator
|
/// Abstract Noise generator
|
||||||
class Abstract {
|
class Abstract {
|
||||||
public:
|
public:
|
||||||
|
using voxels = std::vector<Voxel>;
|
||||||
|
|
||||||
/// Generate chunk voxels
|
/// Generate chunk voxels
|
||||||
virtual void generate(const chunk_pos &at, std::array<Voxel, CHUNK_SIZE> &out) = 0;
|
/// level 5: upto 32^3 voxels
|
||||||
|
virtual void generate5(const voxel_pos &min, voxels &out) = 0;
|
||||||
/// Get gravity vector at given point
|
/// Get gravity vector at given point
|
||||||
virtual glm::vec3 getGravity(const voxel_pos& point) const = 0;
|
virtual glm::vec3 getGravity(const voxel_pos& point) const = 0;
|
||||||
/// Area visual curvature
|
/// Area visual curvature
|
||||||
|
@ -22,8 +25,8 @@ namespace world::generator {
|
||||||
struct Params { };
|
struct Params { };
|
||||||
Void() = default;
|
Void() = default;
|
||||||
|
|
||||||
void generate(const chunk_pos &, std::array<Voxel, CHUNK_SIZE> &out) override {
|
void generate5(const voxel_pos &, voxels &out) override {
|
||||||
out.fill(Voxel());
|
out = {Voxel()};
|
||||||
}
|
}
|
||||||
glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(0); }
|
glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(0); }
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,6 +6,25 @@
|
||||||
namespace world::generator {
|
namespace world::generator {
|
||||||
/// Endless cave network
|
/// Endless cave network
|
||||||
class Cave: public Abstract {
|
class Cave: public Abstract {
|
||||||
|
private:
|
||||||
|
template<uint32_t level>
|
||||||
|
inline void generate(const voxel_pos& min, voxels& out) const {
|
||||||
|
constexpr uint32_t LENGTH = 1u << level;
|
||||||
|
constexpr size_t SIZE = glm::pow3(LENGTH);
|
||||||
|
constexpr auto VEC = glm::ivec3(LENGTH);
|
||||||
|
const auto densitySet = density.getRaw(min, VEC);
|
||||||
|
const auto materialSet = material.getRaw(min, VEC);
|
||||||
|
constexpr auto MATERIAL_SKIP = 2;
|
||||||
|
const int materialMax = module::Registry::Get()->getMaterials().names.size() - (MATERIAL_SKIP + 1);
|
||||||
|
out.resize(SIZE);
|
||||||
|
for (size_t i = 0; i < SIZE; i++) {
|
||||||
|
const auto density = std::clamp((densitySet.get()[i] + params.density) * params.granularity, 0.f, 1.f) * Voxel::DENSITY_MAX;
|
||||||
|
const auto material = density > 0 ? MATERIAL_SKIP + std::clamp(static_cast<int>(std::lrint((materialSet.get()[i] + 1) / 2 * materialMax)),
|
||||||
|
0, materialMax) : 1; //NOTE: map (approx -1, 1) to (1, mat_max)
|
||||||
|
out[i] = Voxel(material, density);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct Params {
|
struct Params {
|
||||||
Params(int seed = 42, float density = 0, float gran = 30): seed(seed), density(density), granularity(gran) { }
|
Params(int seed = 42, float density = 0, float gran = 30): seed(seed), density(density), granularity(gran) { }
|
||||||
|
@ -19,17 +38,8 @@ namespace world::generator {
|
||||||
};
|
};
|
||||||
Cave(const Params p): params(p), density(Noise::SimplexFractal(p.seed)), material(Noise::Cellular(p.seed * 5, .1)) { }
|
Cave(const Params p): params(p), density(Noise::SimplexFractal(p.seed)), material(Noise::Cellular(p.seed * 5, .1)) { }
|
||||||
|
|
||||||
void generate(const chunk_pos &pos, std::array<Voxel, CHUNK_SIZE> &out) override {
|
void generate5(const voxel_pos& min, voxels& out) override {
|
||||||
const auto densitySet = density.getBlock(pos, CHUNK_LENGTH);
|
generate<5>(min, out);
|
||||||
const auto materialSet = material.getBlock(pos, CHUNK_LENGTH);
|
|
||||||
constexpr auto MATERIAL_SKIP = 2;
|
|
||||||
const int materialMax = module::Registry::Get()->getMaterials().names.size() - (MATERIAL_SKIP + 1);
|
|
||||||
for (size_t i = 0; i < CHUNK_SIZE; i++) {
|
|
||||||
const auto density = std::clamp((densitySet.get()[i] + params.density) * params.granularity, 0.f, 1.f) * Voxel::DENSITY_MAX;
|
|
||||||
const auto material = density > 0 ? MATERIAL_SKIP + std::clamp(static_cast<int>(std::lrint((materialSet.get()[i] + 1) / 2 * materialMax)),
|
|
||||||
0, materialMax) : 1; //NOTE: map (approx -1, 1) to (1, mat_max)
|
|
||||||
out[i] = Voxel(material, density);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(-1, 0, 0); }
|
glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(-1, 0, 0); }
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -10,7 +10,54 @@ namespace world::generator {
|
||||||
|
|
||||||
/// Abstract shaped planet generator
|
/// Abstract shaped planet generator
|
||||||
template <PlanetShape PS>
|
template <PlanetShape PS>
|
||||||
class Planet: public Abstract {
|
class Planet final: public Abstract {
|
||||||
|
private:
|
||||||
|
template<uint32_t level>
|
||||||
|
inline void generate(const voxel_pos& min, voxels& out) const {
|
||||||
|
constexpr uint32_t LENGTH = 1u << level;
|
||||||
|
constexpr size_t SIZE = glm::pow3(LENGTH);
|
||||||
|
constexpr auto VEC = glm::i32vec3(LENGTH);
|
||||||
|
const auto densitySet = density.getRaw(min, VEC);
|
||||||
|
const auto displacementSet = displacement.getRaw(min, VEC);
|
||||||
|
const auto& IDS = world::module::core::Core::Get()->ids;
|
||||||
|
const auto genAt = [&](size_t i) -> Voxel {
|
||||||
|
const auto heightRatio = ((float)getHeight(min + voxel_pos(glm::fromIdx(i))) - params.height) / params.height;
|
||||||
|
|
||||||
|
const auto verticalDensityOffset = heightRatio / (heightRatio >= 0 ? params.surface_roughness : params.depth_roughness);
|
||||||
|
const auto density = std::min((densitySet.get()[i] + params.density - verticalDensityOffset) * params.granularity, 1.f) * Voxel::DENSITY_MAX;
|
||||||
|
|
||||||
|
if (density > 0) {
|
||||||
|
const auto material = [&]() -> int {
|
||||||
|
const auto noisedHeightRatio = heightRatio - displacementSet.get()[i] * params.beach_displacement;
|
||||||
|
if(noisedHeightRatio >= 0) {
|
||||||
|
return densitySet.get()[i] + params.density < ((heightRatio + 0.007f) / params.surface_roughness) ? IDS.GRASS : IDS.DIRT;
|
||||||
|
} else {
|
||||||
|
return noisedHeightRatio >= -params.beach_depth ? IDS.SAND : IDS.ROCK;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
return Voxel(material, density);
|
||||||
|
} else {
|
||||||
|
if (heightRatio >= 0) {
|
||||||
|
return Voxel(IDS.AIR, (heightRatio < params.surface_roughness) * Voxel::DENSITY_MAX);
|
||||||
|
} else {
|
||||||
|
return Voxel(IDS.WATER, std::clamp<double>(-heightRatio * params.height, 0.f, 1.f) * Voxel::DENSITY_MAX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto sample = genAt(0);
|
||||||
|
bool unique = true;
|
||||||
|
out.resize(SIZE);
|
||||||
|
for (size_t i = 0; i < SIZE; i++) {
|
||||||
|
out[i] = genAt(i);
|
||||||
|
if (unique && out[i].value != sample.value)
|
||||||
|
unique = false;
|
||||||
|
}
|
||||||
|
if (unique) {
|
||||||
|
out = voxels{sample};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct Params: Cave::Params {
|
struct Params: Cave::Params {
|
||||||
Params(voxel_pos::value_type height, int seed = 42, float surface_roughness = .1, float depth_roughness = .05, float density = 0, float gran = 30):
|
Params(voxel_pos::value_type height, int seed = 42, float surface_roughness = .1, float depth_roughness = .05, float density = 0, float gran = 30):
|
||||||
|
@ -29,39 +76,8 @@ namespace world::generator {
|
||||||
};
|
};
|
||||||
Planet(const Params& p) : params(p), density(Noise::SimplexFractal(p.seed)), displacement(Noise::Simplex(p.seed * 5, .01)) {}
|
Planet(const Params& p) : params(p), density(Noise::SimplexFractal(p.seed)), displacement(Noise::Simplex(p.seed * 5, .01)) {}
|
||||||
|
|
||||||
void generate(const chunk_pos &pos, std::array<Voxel, CHUNK_SIZE> &out) override {
|
void generate5(const voxel_pos &min, voxels &out) override {
|
||||||
auto densitySet = density.getBlock(pos, CHUNK_LENGTH);
|
generate<5>(min, out);
|
||||||
auto displacementSet = displacement.getBlock(pos, CHUNK_LENGTH);
|
|
||||||
const auto& IDS = world::module::core::Core::Get()->ids;
|
|
||||||
for (size_t i = 0; i < CHUNK_SIZE; i++) {
|
|
||||||
const auto vPos = glm::fromIdx(i);
|
|
||||||
const auto noise_i = glm::toIdx(vPos.z, vPos.y, vPos.x);
|
|
||||||
|
|
||||||
const auto heightRatio = ((float)getHeight(glm::multiply(pos) + glm::llvec3(vPos)) - params.height) / params.height;
|
|
||||||
|
|
||||||
const auto verticalDensityOffset = heightRatio / (heightRatio >= 0 ? params.surface_roughness : params.depth_roughness);
|
|
||||||
const auto density = std::min((densitySet.get()[noise_i] + params.density - verticalDensityOffset) * params.granularity, 1.f) * Voxel::DENSITY_MAX;
|
|
||||||
|
|
||||||
out[i] = [&]() -> Voxel {
|
|
||||||
if (density > 0) {
|
|
||||||
const auto material = [&]() -> int {
|
|
||||||
const auto noisedHeightRatio = heightRatio - displacementSet.get()[noise_i] * params.beach_displacement;
|
|
||||||
if(noisedHeightRatio >= 0) {
|
|
||||||
return densitySet.get()[noise_i] + params.density < ((heightRatio + 0.007f) / params.surface_roughness) ? IDS.GRASS : IDS.DIRT;
|
|
||||||
} else {
|
|
||||||
return noisedHeightRatio >= -params.beach_depth ? IDS.SAND : IDS.ROCK;
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
return Voxel(material, density);
|
|
||||||
} else {
|
|
||||||
if (heightRatio >= 0) {
|
|
||||||
return Voxel(IDS.AIR, (heightRatio < params.surface_roughness) * Voxel::DENSITY_MAX);
|
|
||||||
} else {
|
|
||||||
return Voxel(IDS.WATER, std::clamp<double>(-heightRatio * params.height, 0.f, 1.f) * Voxel::DENSITY_MAX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
glm::vec3 getGravity(const voxel_pos& pos) const override {
|
glm::vec3 getGravity(const voxel_pos& pos) const override {
|
||||||
const auto heightRatio = static_cast<float>((getHeight(pos) - params.height) / params.height);
|
const auto heightRatio = static_cast<float>((getHeight(pos) - params.height) / params.height);
|
||||||
|
|
|
@ -7,14 +7,14 @@
|
||||||
|
|
||||||
using namespace world::server;
|
using namespace world::server;
|
||||||
|
|
||||||
Chunk::Chunk(owner<world::ChunkEdits*> edits): world::EdittableChunk(edits) { }
|
Chunk::Chunk(owner<world::ChunkEdits*> edits): world::EdittableChunk(edits, Voxel(0)) { }
|
||||||
Chunk::Chunk(owner<world::ChunkEdits*> edits, const chunk_pos &pos, const std::unique_ptr<generator::Abstract> &rnd): world::EdittableChunk(edits) {
|
Chunk::Chunk(owner<world::ChunkEdits*> edits, const chunk_pos &pos, const std::unique_ptr<generator::Abstract> &rnd): world::EdittableChunk(edits) {
|
||||||
rnd->generate(pos, voxels);
|
rnd->generate5(glm::multiply(pos), voxels);
|
||||||
}
|
}
|
||||||
Chunk::Chunk(owner<world::ChunkEdits*> edits, std::istream& str, bool rle): world::EdittableChunk(edits, str, rle) { }
|
Chunk::Chunk(owner<world::ChunkEdits*> edits, std::istream& str): world::EdittableChunk(edits, str) { }
|
||||||
Chunk::~Chunk() { }
|
Chunk::~Chunk() { }
|
||||||
|
|
||||||
world::Voxel Chunk::write(std::ostream& str, bool rle) const {
|
world::Voxel Chunk::write(std::ostream& str) const {
|
||||||
Voxel::material_t majMat = UINT16_MAX;
|
Voxel::material_t majMat = UINT16_MAX;
|
||||||
uint16_t majCounter = 1;
|
uint16_t majCounter = 1;
|
||||||
size_t visibleDensity = 0;
|
size_t visibleDensity = 0;
|
||||||
|
@ -30,7 +30,7 @@ world::Voxel Chunk::write(std::ostream& str, bool rle) const {
|
||||||
}
|
}
|
||||||
visibleDensity += current.density() * current.is_visible();
|
visibleDensity += current.density() * current.is_visible();
|
||||||
};
|
};
|
||||||
if (rle) {
|
if constexpr(RLE) {
|
||||||
auto it = voxels.begin();
|
auto it = voxels.begin();
|
||||||
uint16_t counter = 1;
|
uint16_t counter = 1;
|
||||||
Voxel current = *it;
|
Voxel current = *it;
|
||||||
|
@ -63,14 +63,21 @@ world::Voxel Chunk::write(std::ostream& str, bool rle) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chunk::set(uint16_t idx, const world::Voxel& val) {
|
void Chunk::set(uint16_t idx, const world::Voxel& val) {
|
||||||
modified = modified || (voxels[idx].value != val.value);
|
const auto same = get(idx).value == val.value;
|
||||||
|
if (!isHeavy()) {
|
||||||
|
if (same)
|
||||||
|
return;
|
||||||
|
|
||||||
|
flatten();
|
||||||
|
}
|
||||||
|
modified = modified || !same;
|
||||||
voxels[idx] = val;
|
voxels[idx] = val;
|
||||||
}
|
}
|
||||||
void Chunk::setAt(const chunk_voxel_pos& pos, const world::Voxel& val) {
|
void Chunk::setAt(const chunk_voxel_pos& pos, const world::Voxel& val) {
|
||||||
set(glm::toIdx(pos), val);
|
set(glm::toIdx(pos), val);
|
||||||
}
|
}
|
||||||
std::optional<world::Item> Chunk::replace(chunk_voxel_idx idx, const world::Voxel& val, float) {
|
std::optional<world::Item> Chunk::replace(chunk_voxel_idx idx, const world::Voxel& val, float) {
|
||||||
const auto res = voxels[idx];
|
const auto res = get(idx);
|
||||||
set(idx, val);
|
set(idx, val);
|
||||||
return {world::Item{res.density(), res.material()}};
|
return {world::Item{res.density(), res.material()}};
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace world::server {
|
||||||
public:
|
public:
|
||||||
Chunk(owner<world::ChunkEdits*>);
|
Chunk(owner<world::ChunkEdits*>);
|
||||||
Chunk(owner<world::ChunkEdits*>, const chunk_pos &pos, const std::unique_ptr<generator::Abstract> &rnd);
|
Chunk(owner<world::ChunkEdits*>, const chunk_pos &pos, const std::unique_ptr<generator::Abstract> &rnd);
|
||||||
Chunk(owner<world::ChunkEdits*>, std::istream& str, bool rle = RLE);
|
Chunk(owner<world::ChunkEdits*>, std::istream& str);
|
||||||
virtual ~Chunk();
|
virtual ~Chunk();
|
||||||
|
|
||||||
/// Set voxel from index
|
/// Set voxel from index
|
||||||
|
@ -28,7 +28,7 @@ namespace world::server {
|
||||||
inline bool isModified() const { return modified; }
|
inline bool isModified() const { return modified; }
|
||||||
/// Write to file and return average (majorant material)
|
/// Write to file and return average (majorant material)
|
||||||
/// Voxel swap bit indicate perfect majority
|
/// Voxel swap bit indicate perfect majority
|
||||||
Voxel write(std::ostream& str, bool rle = RLE) const;
|
Voxel write(std::ostream& str) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Modified by player
|
/// Modified by player
|
||||||
|
@ -36,8 +36,11 @@ namespace world::server {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ChunkFactory {
|
struct ChunkFactory {
|
||||||
|
/// Dynamic chunk using rnd
|
||||||
virtual std::shared_ptr<Chunk> create(const chunk_pos &pos, const std::unique_ptr<generator::Abstract> &rnd) const = 0;
|
virtual std::shared_ptr<Chunk> create(const chunk_pos &pos, const std::unique_ptr<generator::Abstract> &rnd) const = 0;
|
||||||
|
/// Dynamic chunk from stream
|
||||||
virtual std::shared_ptr<Chunk> create(std::istream &str) const = 0;
|
virtual std::shared_ptr<Chunk> create(std::istream &str) const = 0;
|
||||||
|
/// Light void chunk
|
||||||
virtual std::shared_ptr<Chunk> create() const = 0;
|
virtual std::shared_ptr<Chunk> create() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
using namespace world::server;
|
using namespace world::server;
|
||||||
|
|
||||||
#define REMOVE_CORRUPTED 1
|
#define REMOVE_CORRUPTED 1
|
||||||
#define LAZYNESS 8
|
Region::save_throttler::save_throttler(): nextSaveExponent(2) { }
|
||||||
|
|
||||||
Region::Region(const std::string &folderPath, const area_region_ref &id) {
|
Region::Region(const std::string &folderPath, const area_region_ref &id) {
|
||||||
path = folderPath + '/' + std::to_string(id.area.val) + '.' + std::to_string(id.region.x) + '.' +
|
path = folderPath + '/' + std::to_string(id.area.val) + '.' + std::to_string(id.region.x) + '.' +
|
||||||
|
@ -19,7 +19,7 @@ Region::Region(const std::string &folderPath, const area_region_ref &id) {
|
||||||
}
|
}
|
||||||
Region::~Region() {
|
Region::~Region() {
|
||||||
if(!content.empty())
|
if(!content.empty())
|
||||||
save(changed);
|
save(saveThrottler.isUnsaved());
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Region::Read(const std::string &path, const std::function<void(const region_chunk_pos &, const std::optional<Voxel> &, std::vector<char> &&)>& out) {
|
size_t Region::Read(const std::string &path, const std::function<void(const region_chunk_pos &, const std::optional<Voxel> &, std::vector<char> &&)>& out) {
|
||||||
|
@ -197,13 +197,13 @@ void Region::write(const region_chunk_pos& pos, const zstd::write_ctx& ctx, cons
|
||||||
} else {
|
} else {
|
||||||
content.emplace(pos, node(avg, std::move(buffer)));
|
content.emplace(pos, node(avg, std::move(buffer)));
|
||||||
}
|
}
|
||||||
changed = true;
|
saveThrottler.change();
|
||||||
}
|
}
|
||||||
save(false);
|
save(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Region::save(bool force) {
|
void Region::save(bool force) {
|
||||||
if(!force && rand() % LAZYNESS == 0)
|
if(!(force || saveThrottler.mustSave()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock(mutex);
|
||||||
|
@ -260,5 +260,5 @@ void Region::save(bool force) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
changed = false;
|
saveThrottler.save();
|
||||||
}
|
}
|
|
@ -54,7 +54,23 @@ namespace world::server {
|
||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
};
|
};
|
||||||
robin_hood::unordered_map<region_chunk_pos, node> content;
|
robin_hood::unordered_map<region_chunk_pos, node> content;
|
||||||
bool changed = false;
|
|
||||||
|
struct save_throttler {
|
||||||
|
save_throttler();
|
||||||
|
/// Counter of currently unsaved
|
||||||
|
size_t changes = 0;
|
||||||
|
/// Save when changes > (1 << it)
|
||||||
|
/// MAYBE: decrease with time
|
||||||
|
size_t nextSaveExponent;
|
||||||
|
|
||||||
|
inline void change() { changes++; }
|
||||||
|
inline constexpr bool isUnsaved() const { return changes > 0; }
|
||||||
|
inline constexpr bool mustSave() const { return changes > (1ull << nextSaveExponent); }
|
||||||
|
inline void save() {
|
||||||
|
changes = 0;
|
||||||
|
nextSaveExponent++;
|
||||||
|
}
|
||||||
|
} saveThrottler;
|
||||||
|
|
||||||
void load();
|
void load();
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,13 +9,13 @@ namespace world::server {
|
||||||
public:
|
public:
|
||||||
SharedChunk(): Chunk(new world::ChunkEdits(this)) { }
|
SharedChunk(): Chunk(new world::ChunkEdits(this)) { }
|
||||||
SharedChunk(const chunk_pos &pos, const std::unique_ptr<generator::Abstract> &rnd): Chunk(new world::ChunkEdits(this), pos, rnd) { }
|
SharedChunk(const chunk_pos &pos, const std::unique_ptr<generator::Abstract> &rnd): Chunk(new world::ChunkEdits(this), pos, rnd) { }
|
||||||
SharedChunk(std::istream &str, bool rle = RLE): Chunk(new world::ChunkEdits(this), str, rle) { }
|
SharedChunk(std::istream &str): Chunk(new world::ChunkEdits(this), str) { }
|
||||||
|
|
||||||
/// Break voxel
|
/// Break voxel
|
||||||
std::optional<Item> replace(chunk_voxel_idx idx, const Voxel &val, float delay = 0) override {
|
std::optional<Item> replace(chunk_voxel_idx idx, const Voxel &val, float delay = 0) override {
|
||||||
const auto res = voxels[idx];
|
const auto res = get(idx);
|
||||||
set(idx, val);
|
set(idx, val);
|
||||||
if (res.value != voxels[idx].value) {
|
if (res.value != get(idx).value) {
|
||||||
setEdits()->add(world::ChunkEdits::Edit{res, delay, idx});
|
setEdits()->add(world::ChunkEdits::Edit{res, delay, idx});
|
||||||
}
|
}
|
||||||
return {Item{res.density(), res.material()}};
|
return {Item{res.density(), res.material()}};
|
||||||
|
|
|
@ -370,14 +370,13 @@ void Universe::upgrade() {
|
||||||
|
|
||||||
{ // Store loaded chunks
|
{ // Store loaded chunks
|
||||||
ZoneScopedN("Load");
|
ZoneScopedN("Load");
|
||||||
loadedQueue.extractor([&](const std::pair<node_chunk_pos, std::shared_ptr<Chunk>> &loaded) {
|
loadedQueue.extractor([&](std::pair<node_chunk_pos, std::shared_ptr<Chunk>> &loaded) {
|
||||||
if (Elements::Is<Elements::Type::Area>(loaded.first.node)) {
|
if (Elements::Is<Elements::Type::Area>(loaded.first.node)) {
|
||||||
if (const auto area = elements.findArea(loaded.first.node.val))
|
if (const auto area = elements.findArea(loaded.first.node.val))
|
||||||
area->get()->setChunks().emplace(loaded.first.chunk, loaded.second);
|
area->get()->setChunks().emplace(loaded.first.chunk, std::move(loaded.second));
|
||||||
} else {
|
} else {
|
||||||
if (const auto part = elements.findPart(loaded.first.node.val)) {
|
if (const auto part = elements.findPart(loaded.first.node.val))
|
||||||
part->get()->chunks[part->get()->getIdx(loaded.first.chunk)] = loaded.second;
|
part->get()->emplace(loaded.first.chunk, std::move(loaded.second));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -404,12 +403,12 @@ void Universe::loadIndex() {
|
||||||
elements->createInstance(Elements::start_point(p.val, transform(glm::vec3(1000, 0, 0))), generational::id(0));
|
elements->createInstance(Elements::start_point(p.val, transform(glm::vec3(1000, 0, 0))), generational::id(0));
|
||||||
//TODO: generate universe
|
//TODO: generate universe
|
||||||
{
|
{
|
||||||
const auto radius = 1 << 4;
|
const auto radius = 1 << 6;
|
||||||
generator::RoundPlanet::Params opts(radius * CHUNK_LENGTH * 3 / 4);
|
const auto surface = radius * CHUNK_LENGTH * 3 / 4;
|
||||||
|
generator::RoundPlanet::Params opts(surface);
|
||||||
Area::params params{radius, 0, std::vector<char>(sizeof(opts))};
|
Area::params params{radius, 0, std::vector<char>(sizeof(opts))};
|
||||||
memcpy(params.params.data(), &opts, params.params.size());
|
memcpy(params.params.data(), &opts, params.params.size());
|
||||||
const auto a = elements->createArea(Elements::start_point(generational::id(), transform(glm::multiply(voxel_pos(radius, 0, 0)), glm::angleAxis(.5f, glm::vec3(0, 1, 0))), velocity(offset_pos(), glm::angleAxis(.1f, glm::vec3(0, 1, 0)))), params);
|
elements->createArea(Elements::start_point(generational::id(), transform(world_pos(-surface, 0, 0)), velocity(offset_pos(), glm::angleAxis(.01f, glm::vec3(0, 1, 0)))), params);
|
||||||
elements->createInstance(Elements::start_point(a.val, transform(glm::vec3(-radius * CHUNK_LENGTH + 100, 0, 0))), generational::id(0));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
index.close();
|
index.close();
|
||||||
|
|
Loading…
Reference in New Issue