Optimizations
Reorder chunk_voxel_idx Region exp saving ratio Single level chunk treemaster
parent
6288606505
commit
765e9da05f
4
TODO.md
4
TODO.md
|
@ -73,6 +73,10 @@ Released as `0.0.1`: `Pre alpha 1`
|
|||
- [ ] Inventory
|
||||
- [ ] Octree
|
||||
- [ ] Surface aware LOD
|
||||
- [ ] All levels (0-5)
|
||||
- [ ] Tree fill
|
||||
- [ ] Big edit queue
|
||||
- [ ] Update region level average
|
||||
- [ ] Area converter
|
||||
- [ ] Minecraft import
|
||||
- [ ] Base unit size change
|
||||
|
|
|
@ -240,7 +240,7 @@ public:
|
|||
float opacity = .8f;
|
||||
} console;
|
||||
|
||||
struct {
|
||||
struct overlay_t {
|
||||
bool visible = true;
|
||||
int corner = 3;
|
||||
} overlay;
|
||||
|
|
|
@ -50,6 +50,9 @@ namespace contouring {
|
|||
/// Get pending elements
|
||||
virtual size_t getQueueSize() = 0;
|
||||
|
||||
/// Get max visible distance in chunks
|
||||
virtual uint16_t getVisibleDist() const = 0;
|
||||
|
||||
struct area_info {
|
||||
world::transform absolute;
|
||||
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 {
|
||||
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;
|
||||
const auto &materials = world::module::Registry::Get()->getMaterials();
|
||||
{
|
||||
ZoneScopedN("Load");
|
||||
const auto setCell = [&](int x, int y, int z, const world::Voxel &voxel) {
|
||||
auto &cell = grid[((z * SIZE) + y) * SIZE + x];
|
||||
const auto setCell = [&](glm::idx i, const world::Voxel &voxel) {
|
||||
auto &cell = grid[i];
|
||||
cell.w = voxel.material();
|
||||
cell.x = voxel.density_ratio() * (!materials.invisibilities[cell.w] &&
|
||||
((materials.transparencies[cell.w] && (layer && Layer::Transparent)) ||
|
||||
(!materials.transparencies[cell.w] && (layer && Layer::Solid))));
|
||||
};
|
||||
for (int z = 0; z < SIZE; z++) {
|
||||
for (int y = 0; y < SIZE; y++) {
|
||||
for (int x = 0; x < SIZE; x++) {
|
||||
size_t i = 0;
|
||||
for (uint8_t z = 0; z < SIZE; z++) {
|
||||
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 &voxel = chunk->get(glm::toIdx(x % world::CHUNK_LENGTH, y % world::CHUNK_LENGTH, z % world::CHUNK_LENGTH));
|
||||
setCell(x, y, z, voxel);
|
||||
const auto &voxel = chunk->get(glm::toIdx(x & glm::IDX_MASK, y & glm::IDX_MASK, z & glm::IDX_MASK));
|
||||
setCell(i, voxel);
|
||||
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) {
|
||||
auto p = offset + glm::ivec3(glm::fromIdx(it->first));
|
||||
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;
|
||||
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:
|
||||
struct area_models {
|
||||
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
|
||||
// original marching cubes face
|
||||
Vertex p;
|
||||
p.x = 0;
|
||||
p.y = 0;
|
||||
p.z = 0;
|
||||
Vertex p(0, 0, 0, 0);
|
||||
int points = 0;
|
||||
T max = 0;
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <imgui_stdlib.h>
|
||||
#include "../state.hpp"
|
||||
#include "Window.hpp"
|
||||
#include "core/utils/os.hpp"
|
||||
#include "api/Buffers.hpp"
|
||||
#include "core/world/Elements.hpp"
|
||||
#include "version.h"
|
||||
#include <filesystem>
|
||||
|
@ -115,38 +117,47 @@ void UI::Unload() {
|
|||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
UI::Actions drawMenu(config::client::options &options, state::state &state, const std::vector<std::string>& packs) {
|
||||
const ImGuiIO &io = ImGui::GetIO();
|
||||
|
||||
if (options.overlay.visible) {
|
||||
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);
|
||||
template<typename F>
|
||||
void drawOverlay(config::client::options::overlay_t& overlay, F inner) {
|
||||
if (overlay.visible) {
|
||||
const ImGuiIO &io = ImGui::GetIO();
|
||||
if (overlay.corner != -1) {
|
||||
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((overlay.corner & 1) ? 1.0f : 0.0f, (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::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);
|
||||
inner();
|
||||
ImGui::Text("RAM: %.3f/%.3f GB", os::GetProcess().memused / 1000000000.f, os::GetGlobal().ram.total / 1000000000.);
|
||||
|
||||
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;
|
||||
if (ImGui::MenuItem("Custom", NULL, overlay.corner == -1))
|
||||
overlay.corner = -1;
|
||||
if (ImGui::MenuItem("Top-left", NULL, overlay.corner == 0))
|
||||
overlay.corner = 0;
|
||||
if (ImGui::MenuItem("Top-right", NULL, overlay.corner == 1))
|
||||
overlay.corner = 1;
|
||||
if (ImGui::MenuItem("Bottom-left", NULL, overlay.corner == 2))
|
||||
overlay.corner = 2;
|
||||
if (ImGui::MenuItem("Bottom-right", NULL, overlay.corner == 3))
|
||||
overlay.corner = 3;
|
||||
if (overlay.visible && ImGui::MenuItem("Close"))
|
||||
overlay.visible = false;
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
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);
|
||||
|
||||
const ImGuiIO &io = ImGui::GetIO();
|
||||
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);
|
||||
state.login.name.resize(128);
|
||||
|
@ -327,33 +338,9 @@ UI::Actions drawInGame(config::client::options &options, state::state &state, co
|
|||
ImGui::End();
|
||||
}
|
||||
|
||||
if (options.overlay.visible) {
|
||||
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);
|
||||
drawOverlay(options.overlay, [&]{
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -60,8 +60,8 @@ struct ServerFactory: public world::AbstractServerFactory {
|
|||
{
|
||||
int load = options.world.loadDistance;
|
||||
int keep = options.world.keepDistance;
|
||||
if (ImGui::SliderInt("Load distance", &load, 1, options.world.keepDistance) |
|
||||
ImGui::SliderInt("Keep distance", &keep, options.world.loadDistance + 1, 21)) {
|
||||
if (ImGui::SliderInt("Load distance##srv", &load, 1, options.world.keepDistance) |
|
||||
ImGui::SliderInt("Keep distance##srv", &keep, options.world.loadDistance + 1, 21)) {
|
||||
options.world.loadDistance = load;
|
||||
options.world.keepDistance = keep;
|
||||
}
|
||||
|
|
|
@ -39,10 +39,9 @@ class Chunk final: public world::EdittableChunk {
|
|||
public:
|
||||
Chunk(std::istream &is): EdittableChunk(new ChunkFutureEdits(this), is) { }
|
||||
/// Create from average
|
||||
Chunk(Voxel val): EdittableChunk(new ChunkFutureEdits(this)), isAverage(true), isMajorant(val.swap()) { voxels.fill(Voxel(val.material(), val.density())); }
|
||||
Chunk(): EdittableChunk(new ChunkFutureEdits(this)) { }
|
||||
Chunk(Voxel val): EdittableChunk(new ChunkFutureEdits(this), Voxel(val.material(), val.density())), isMajorant(val.swap()) { }
|
||||
|
||||
constexpr bool isTrusted(bool allowMajorant) const { return !isAverage || (allowMajorant && isMajorant); }
|
||||
inline bool isTrusted(bool allowMajorant) const noexcept { return isHeavy() || (allowMajorant && isMajorant); }
|
||||
void unsetMajorant() {
|
||||
assert(isMajorant);
|
||||
isMajorant = false;
|
||||
|
@ -51,13 +50,11 @@ public:
|
|||
ChunkFutureEdits &setEdits() { assert(edits); return *(ChunkFutureEdits*)edits.get(); }
|
||||
|
||||
private:
|
||||
/// Is temporary average
|
||||
const bool isAverage = false;
|
||||
/// Is temporary full valued
|
||||
bool isMajorant = false;
|
||||
};
|
||||
|
||||
/// Chunk full of air
|
||||
static const std::shared_ptr<const Chunk> EMPTY_CHUNK = std::make_shared<Chunk>();
|
||||
/// Chunk full of void
|
||||
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()) {
|
||||
part->allocate();
|
||||
part->chunks.at(part->getIdx(pos.chunk)) = ck;
|
||||
part->emplace(pos.chunk, ck);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ void LocalUniverse::update(cell_pos pos, float deltaTime) {
|
|||
|
||||
const auto lock = getElements();
|
||||
const auto &elements = *lock;
|
||||
const auto visibleDist2 = glm::pow2<glm::l>(contouring->getVisibleDist());
|
||||
{ // Update alive areas
|
||||
ZoneScopedN("World");
|
||||
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
|
||||
const cell_pos chunkPos = area_tf.computeChild(glm::multiply(chunk.first));
|
||||
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)) {
|
||||
contouring->onUpdate(area_chunk_pos{id, chunk.first}, chunkDist, chunks, neighbors.value());
|
||||
|
|
|
@ -16,7 +16,9 @@ namespace glm {
|
|||
typedef vec<3, uc> ucvec3;
|
||||
|
||||
const auto IDX_LENGTH = 32;
|
||||
const auto IDX_SHIFT = 5;
|
||||
const auto IDX_LENGTH2 = IDX_LENGTH * IDX_LENGTH;
|
||||
const auto IDX_SIZE = IDX_LENGTH2 * IDX_LENGTH;
|
||||
using idx = glm::u16;
|
||||
const idx IDX_MASK = (1u<<IDX_SHIFT)-1;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,10 @@ namespace glm {
|
|||
constexpr U inline pow2(U v) {
|
||||
return v * v;
|
||||
}
|
||||
template<typename U>
|
||||
constexpr U inline pow3(U v) {
|
||||
return v * v * v;
|
||||
}
|
||||
constexpr ll inline length2(const llvec3& v) {
|
||||
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)) {
|
||||
return {divide(value, m), modulo(value, m)};
|
||||
}
|
||||
/// idx order is z-y-x
|
||||
constexpr ucvec3 inline fromIdx(idx idx) {
|
||||
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) {
|
||||
assert(size.x>=0 && size.y>=0 && size.z>=0);
|
||||
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) {
|
||||
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) {
|
||||
return x * sy * sz + y * sz + z;
|
||||
constexpr idx inline toIdx(glm::uc x, glm::uc y, glm::uc z, glm::uc sy, glm::uc sx) {
|
||||
return z * sy * sx + y * sx + x;
|
||||
}
|
||||
constexpr idx inline toIdx(ucvec3 pos) {
|
||||
return toIdx(pos.x, pos.y, pos.z);
|
||||
}
|
||||
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) {
|
||||
return toIdx(pos.x, pos.y, pos.z);
|
||||
}
|
||||
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)) {
|
||||
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;
|
||||
|
||||
Chunk::Chunk(std::istream& str, bool rle) {
|
||||
if(rle) {
|
||||
Chunk::Chunk(std::istream& str) {
|
||||
voxels.resize(CHUNK_SIZE);
|
||||
if constexpr(RLE) {
|
||||
uint16_t i = 0;
|
||||
while(!str.eof()) {
|
||||
uint16_t count;
|
||||
|
@ -19,13 +20,18 @@ Chunk::Chunk(std::istream& str, bool rle) {
|
|||
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 {
|
||||
for(auto& voxel: voxels) {
|
||||
str.read(reinterpret_cast<char *>(&voxel), sizeof(voxel));
|
||||
}
|
||||
}
|
||||
}
|
||||
Chunk::Chunk(Voxel v): voxels({v}) { }
|
||||
Chunk::~Chunk() { }
|
||||
|
||||
const Voxel &Chunk::getAt(const chunk_voxel_pos &pos) const {
|
||||
|
|
|
@ -6,22 +6,28 @@
|
|||
namespace world {
|
||||
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 {
|
||||
public:
|
||||
Chunk(std::istream& str, bool rle = RLE);
|
||||
Chunk(std::istream& str);
|
||||
virtual ~Chunk();
|
||||
|
||||
/// Get voxel from index
|
||||
inline const Voxel& get(chunk_voxel_idx idx) const {
|
||||
return voxels[idx];
|
||||
assert(isAllocated());
|
||||
return voxels[isHeavy() ? idx : 0];
|
||||
}
|
||||
/// Get voxel from position
|
||||
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:
|
||||
Chunk(Voxel v);
|
||||
/// NOTE: voxels must be allocated by child constructor
|
||||
Chunk() { }
|
||||
/// Chunk data
|
||||
std::array<Voxel, CHUNK_SIZE> voxels;
|
||||
std::vector<Voxel> voxels; //MAYBE: manual handle
|
||||
};
|
||||
}
|
|
@ -6,16 +6,31 @@
|
|||
|
||||
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() { }
|
||||
|
||||
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) {
|
||||
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) {
|
||||
ZoneScopedN("Chunk");
|
||||
//ZoneScopedN("Chunk");
|
||||
for(auto it = edits.begin(); it != edits.end();) {
|
||||
it->second.delay -= deltaTime;
|
||||
if(it->second.delay <= 0 && animate) {
|
||||
|
|
|
@ -61,14 +61,21 @@ namespace world {
|
|||
const ChunkEdits& getEdits() const { assert(edits); return *edits.get(); }
|
||||
|
||||
/// Direct set without update of voxel at index
|
||||
inline void set(chunk_voxel_idx idx, const Voxel& voxel) {
|
||||
voxels[idx] = voxel;
|
||||
}
|
||||
void set(chunk_voxel_idx idx, const Voxel &voxel);
|
||||
/// Direct set without update of voxel at position
|
||||
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:
|
||||
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*>);
|
||||
const std::unique_ptr<ChunkEdits> edits;
|
||||
};
|
||||
|
|
|
@ -141,10 +141,18 @@ namespace world {
|
|||
assert(inRange(c));
|
||||
const auto idx = getIdx(c);
|
||||
if (idx < chunks.size())
|
||||
return chunks.at(idx);
|
||||
return chunks[idx];
|
||||
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -8,8 +8,11 @@ namespace world::generator {
|
|||
/// Abstract Noise generator
|
||||
class Abstract {
|
||||
public:
|
||||
using voxels = std::vector<Voxel>;
|
||||
|
||||
/// 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
|
||||
virtual glm::vec3 getGravity(const voxel_pos& point) const = 0;
|
||||
/// Area visual curvature
|
||||
|
@ -22,8 +25,8 @@ namespace world::generator {
|
|||
struct Params { };
|
||||
Void() = default;
|
||||
|
||||
void generate(const chunk_pos &, std::array<Voxel, CHUNK_SIZE> &out) override {
|
||||
out.fill(Voxel());
|
||||
void generate5(const voxel_pos &, voxels &out) override {
|
||||
out = {Voxel()};
|
||||
}
|
||||
glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(0); }
|
||||
};
|
||||
|
|
|
@ -6,6 +6,25 @@
|
|||
namespace world::generator {
|
||||
/// Endless cave network
|
||||
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:
|
||||
struct Params {
|
||||
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)) { }
|
||||
|
||||
void generate(const chunk_pos &pos, std::array<Voxel, CHUNK_SIZE> &out) override {
|
||||
const auto densitySet = density.getBlock(pos, CHUNK_LENGTH);
|
||||
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);
|
||||
}
|
||||
void generate5(const voxel_pos& min, voxels& out) override {
|
||||
generate<5>(min, out);
|
||||
}
|
||||
glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(-1, 0, 0); }
|
||||
private:
|
||||
|
|
|
@ -10,7 +10,54 @@ namespace world::generator {
|
|||
|
||||
/// Abstract shaped planet generator
|
||||
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:
|
||||
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):
|
||||
|
@ -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)) {}
|
||||
|
||||
void generate(const chunk_pos &pos, std::array<Voxel, CHUNK_SIZE> &out) override {
|
||||
auto densitySet = density.getBlock(pos, CHUNK_LENGTH);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}();
|
||||
}
|
||||
void generate5(const voxel_pos &min, voxels &out) override {
|
||||
generate<5>(min, out);
|
||||
}
|
||||
glm::vec3 getGravity(const voxel_pos& pos) const override {
|
||||
const auto heightRatio = static_cast<float>((getHeight(pos) - params.height) / params.height);
|
||||
|
|
|
@ -7,14 +7,14 @@
|
|||
|
||||
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) {
|
||||
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() { }
|
||||
|
||||
world::Voxel Chunk::write(std::ostream& str, bool rle) const {
|
||||
world::Voxel Chunk::write(std::ostream& str) const {
|
||||
Voxel::material_t majMat = UINT16_MAX;
|
||||
uint16_t majCounter = 1;
|
||||
size_t visibleDensity = 0;
|
||||
|
@ -30,7 +30,7 @@ world::Voxel Chunk::write(std::ostream& str, bool rle) const {
|
|||
}
|
||||
visibleDensity += current.density() * current.is_visible();
|
||||
};
|
||||
if (rle) {
|
||||
if constexpr(RLE) {
|
||||
auto it = voxels.begin();
|
||||
uint16_t counter = 1;
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
void Chunk::setAt(const chunk_voxel_pos& pos, const world::Voxel& val) {
|
||||
set(glm::toIdx(pos), val);
|
||||
}
|
||||
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);
|
||||
return {world::Item{res.density(), res.material()}};
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace world::server {
|
|||
public:
|
||||
Chunk(owner<world::ChunkEdits*>);
|
||||
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();
|
||||
|
||||
/// Set voxel from index
|
||||
|
@ -28,7 +28,7 @@ namespace world::server {
|
|||
inline bool isModified() const { return modified; }
|
||||
/// Write to file and return average (majorant material)
|
||||
/// Voxel swap bit indicate perfect majority
|
||||
Voxel write(std::ostream& str, bool rle = RLE) const;
|
||||
Voxel write(std::ostream& str) const;
|
||||
|
||||
private:
|
||||
/// Modified by player
|
||||
|
@ -36,8 +36,11 @@ namespace world::server {
|
|||
};
|
||||
|
||||
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;
|
||||
/// Dynamic chunk from stream
|
||||
virtual std::shared_ptr<Chunk> create(std::istream &str) const = 0;
|
||||
/// Light void chunk
|
||||
virtual std::shared_ptr<Chunk> create() const = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
using namespace world::server;
|
||||
|
||||
#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) {
|
||||
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() {
|
||||
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) {
|
||||
|
@ -197,13 +197,13 @@ void Region::write(const region_chunk_pos& pos, const zstd::write_ctx& ctx, cons
|
|||
} else {
|
||||
content.emplace(pos, node(avg, std::move(buffer)));
|
||||
}
|
||||
changed = true;
|
||||
saveThrottler.change();
|
||||
}
|
||||
save(false);
|
||||
}
|
||||
|
||||
void Region::save(bool force) {
|
||||
if(!force && rand() % LAZYNESS == 0)
|
||||
if(!(force || saveThrottler.mustSave()))
|
||||
return;
|
||||
|
||||
std::unique_lock lock(mutex);
|
||||
|
@ -260,5 +260,5 @@ void Region::save(bool force) {
|
|||
return;
|
||||
}
|
||||
file.close();
|
||||
changed = false;
|
||||
saveThrottler.save();
|
||||
}
|
|
@ -54,7 +54,23 @@ namespace world::server {
|
|||
std::vector<char> data;
|
||||
};
|
||||
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();
|
||||
};
|
||||
|
|
|
@ -9,13 +9,13 @@ namespace world::server {
|
|||
public:
|
||||
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(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
|
||||
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);
|
||||
if (res.value != voxels[idx].value) {
|
||||
if (res.value != get(idx).value) {
|
||||
setEdits()->add(world::ChunkEdits::Edit{res, delay, idx});
|
||||
}
|
||||
return {Item{res.density(), res.material()}};
|
||||
|
|
|
@ -370,14 +370,13 @@ void Universe::upgrade() {
|
|||
|
||||
{ // Store loaded chunks
|
||||
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 (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 {
|
||||
if (const auto part = elements.findPart(loaded.first.node.val)) {
|
||||
part->get()->chunks[part->get()->getIdx(loaded.first.chunk)] = loaded.second;
|
||||
}
|
||||
if (const auto part = elements.findPart(loaded.first.node.val))
|
||||
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));
|
||||
//TODO: generate universe
|
||||
{
|
||||
const auto radius = 1 << 4;
|
||||
generator::RoundPlanet::Params opts(radius * CHUNK_LENGTH * 3 / 4);
|
||||
const auto radius = 1 << 6;
|
||||
const auto surface = radius * CHUNK_LENGTH * 3 / 4;
|
||||
generator::RoundPlanet::Params opts(surface);
|
||||
Area::params params{radius, 0, std::vector<char>(sizeof(opts))};
|
||||
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->createInstance(Elements::start_point(a.val, transform(glm::vec3(-radius * CHUNK_LENGTH + 100, 0, 0))), generational::id(0));
|
||||
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);
|
||||
}
|
||||
}
|
||||
index.close();
|
||||
|
|
Loading…
Reference in New Issue