diff --git a/src/client/config.hpp b/src/client/config.hpp index fcf0f75..d00f67c 100644 --- a/src/client/config.hpp +++ b/src/client/config.hpp @@ -68,6 +68,8 @@ public: world.trustMajorant = config["world"]["trust_majorant"].value_or(world.trustMajorant); world.editPrediction = config["world"]["edit_prediction"].value_or(world.editPrediction); world.editHandling = config["world"]["edit_handling"].value_or(world.editHandling); + world.movePrediction = config["world"]["move_prediction"].value_or(world.movePrediction); + world.moveCollision = config["world"]["move_collision"].value_or(world.moveCollision); voxel_density = config["world"]["voxel_density"].value_or(voxel_density); #ifndef STANDALONE @@ -148,6 +150,8 @@ public: {"trust_majorant", world.trustMajorant}, {"edit_prediction", world.editPrediction}, {"edit_handling", world.editHandling}, + {"move_prediction", world.movePrediction}, + {"move_collision", world.moveCollision}, {"voxel_density", voxel_density} })); if(connection.has_value()) { diff --git a/src/client/world/DistantUniverse.cpp b/src/client/world/DistantUniverse.cpp index 20b18e4..da765a5 100644 --- a/src/client/world/DistantUniverse.cpp +++ b/src/client/world/DistantUniverse.cpp @@ -128,6 +128,19 @@ void DistantUniverse::update(voxel_pos pos, float deltaTime) { } } } + if (options.movePrediction) { + entities.remove([&](size_t eId, Entity &entity) { + entity.instances.remove([&](size_t iId, Universe::Entity::Instance &inst) { + if (eId == PLAYER_ENTITY_ID.index && iId == playerId) + return false; + + // MAYBE: leap after entities packet + inst.pos += inst.velocity * deltaTime; + return false; + }); + return false; + }); + } contouring->update(pos, areas); } @@ -172,7 +185,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { } case server_packet_type::COMPRESSION: { - const auto remain = packet.readAll(); + const auto remain = packet.readRemaining(); dict.emplace(remain.data(), remain.size()); LOG_T("Compression dictionnary loaded"); mayQueryChunks = true; @@ -187,13 +200,13 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { } case server_packet_type::MESSAGE: { - const auto remain = packet.readAll(); + const auto remain = packet.readRemaining(); onMessage(std::string((const char*)remain.data(), remain.size())); break; } case server_packet_type::AREAS: { - while(!packet.isFull()) { + while(!packet.isDone()) { area_id id; world::Area::params p; if(!packet.read(id) || !packet.read(p)) @@ -228,7 +241,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { // MAYBE: then create virtual or non chunk of single material uint16_t full = 0; uint16_t total = 0; - while (!packet.isFull()) { + while (!packet.isDone()) { region_chunk_pos cpos; Voxel voxel; if (!packet.read(cpos) || !packet.read(voxel)) @@ -247,7 +260,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { if (!dict.has_value()) break; - if (packet.isFull()) { + if (packet.isDone()) { mayQueryChunks = true; break; } @@ -267,7 +280,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { } std::vector buffer; - if(auto err = dict.value().decompress(packet.readAll(), buffer)) { + if(auto err = dict.value().decompress(packet.readRemaining(), buffer)) { LOG_E("Corrupted chunk packet " << err.value()); break; } @@ -313,14 +326,14 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { break; } - while(!packet.isFull()) { + while(!packet.isDone()) { chunk_pos pos = chunk_pos(INT_MAX); packet.read(pos); chunk_voxel_idx count = 0; packet.read(count); if (auto ptr = it->second->setChunks().findInRange(pos)) { auto chunk = std::dynamic_pointer_cast(ptr.value()); - for (auto i = 0; i < count && !packet.isFull(); i++) { + for (auto i = 0; i < count && !packet.isDone(); i++) { Chunk::Edit edit; packet.read(edit); chunk->apply(edit); @@ -334,7 +347,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { case server_packet_type::ENTITY_TYPES: { entities.clear(); - while(!packet.isFull()) { + while(!packet.isDone()) { size_t index; glm::usvec3 size; glm::vec3 scale; @@ -351,7 +364,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { case server_packet_type::ENTITIES: { entities.remove([](size_t, Entity &entity) { entity.instances.clear(); return false; }); - while(!packet.isFull()) { + while(!packet.isDone()) { size_t entity_idx; size_t count; if (!(packet.read(entity_idx) && packet.read(count))) @@ -360,7 +373,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { if (auto entity = entities.get(entity_idx)) { if (count == 0) continue; - for (size_t i = 0; i < count && !packet.isFull(); i++) { + for (size_t i = 0; i < count && !packet.isDone(); i++) { size_t idx; glm::ifvec3 pos; glm::vec3 vel; @@ -410,15 +423,9 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { auto area = std::get_if(&it->shape); assert(area); area->clear(); - glm::i64 i = 0; - while (!packet.isFull()) { - i++; - size_t size = 0; - if (!packet.read(size)) - break; - + while (!packet.isDone()) { std::vector buffer; - if(auto err = dict.value().decompress(packet.readPart(size), buffer)) { + if(auto err = dict.value().decompress(packet.readSizedPart(), buffer)) { LOG_E("Corrupted entity chunk packet " << err.value()); break; } @@ -427,15 +434,15 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { area->push_back(std::make_shared(iss)); } auto scale = glm::lvec3(1) + glm::divide(it->size); - if (i != scale.x * scale.y * scale.z) { - LOG_E("Corrupted entity area"); + if ((long)area->size() != scale.x * scale.y * scale.z) { + LOG_E("Corrupted entity area " << area->size() << "!=" << (scale.x * scale.y * scale.z)); break; } contouring->onEntityLoad(id, scale, *area); } else { auto model = std::get_if(&it->shape); assert(model); - auto remain = packet.readAll(); + auto remain = packet.readRemaining(); model->resize(remain.size()); //MAYBE: avoid storing model memcpy(model->data(), remain.data(), remain.size()); diff --git a/src/client/world/Universe.hpp b/src/client/world/Universe.hpp index 9554640..8f26716 100644 --- a/src/client/world/Universe.hpp +++ b/src/client/world/Universe.hpp @@ -29,6 +29,10 @@ namespace world::client { bool editPrediction = true; /// Get only edits commands and compute locally bool editHandling = true; + /// Apply velocity locally. Smooth entities positions + bool movePrediction = true; + /// Check for entities collisions + bool moveCollision = false; }; struct connection: net::address { connection(): net::address{"127.0.0.1", 4242} { } diff --git a/src/core/data/glm.hpp b/src/core/data/glm.hpp index f5d0483..78172eb 100644 --- a/src/core/data/glm.hpp +++ b/src/core/data/glm.hpp @@ -42,6 +42,7 @@ namespace glm { } inline ifvec3 operator+(const offset_t& v) const { return ifvec3(raw, offset + v); } inline ifvec3 operator-(const offset_t &v) const { return ifvec3(raw, offset - v); } + inline ifvec3 operator-(const ifvec3 &v) const { return ifvec3(raw - v.raw, offset - v.offset); } inline ifvec3 operator/(int i) const { return ifvec3(raw / i, offset / (i * 1.f), false); } inline ifvec3 operator*(int i) const { return ifvec3(raw * i, offset * (i * 1.f)); } }; diff --git a/src/core/data/math.hpp b/src/core/data/math.hpp index 0031192..c5ab075 100644 --- a/src/core/data/math.hpp +++ b/src/core/data/math.hpp @@ -76,7 +76,8 @@ namespace glm { constexpr T inline max_axis(const vec<3, T> &v) { return std::max({v.x, v.y, v.z}); } - ivec3 inline inormalize(const ivec3 &v) { + template + ivec3 inline inormalize(const vec<3, T> &v) { const auto a = glm::abs(v); return a.x >= a.y ? (a.x >= a.z ? ivec3(glm::sign(v.x), 0, 0) : ivec3(0, 0, glm::sign(v.z))) : (a.y >= a.z ? ivec3(0, glm::sign(v.y), 0) : ivec3(0, 0, glm::sign(v.z))); diff --git a/src/core/data/mem.hpp b/src/core/data/mem.hpp index 235c267..4650a47 100644 --- a/src/core/data/mem.hpp +++ b/src/core/data/mem.hpp @@ -24,6 +24,131 @@ struct view { constexpr size_t size() const { return siz; } constexpr bool isDone() const { return cur >= siz; } }; +// Abstract data writer +struct in_view: view { + in_view(uint8_t *ptr, size_t size): view(size), ptr(ptr) { + assert(ptr != nullptr); + } + + uint8_t* ptr; + uint8_t* writeTo(size_t len) { + assert(cur + len <= siz); + auto p = ptr + cur; + cur += len; + return p; + } + void write(const uint8_t* data, size_t len) { + memcpy(writeTo(len), data, len); + } +}; +/// Writer with Dynamic allocation and subparts +struct in_writer: public in_view { +private: + static constexpr auto EMPTY_OFFSET = 0x1000; + static uint8_t* alloc(size_t size) { return (uint8_t*)(size > 0 ? ::malloc(size) : reinterpret_cast(EMPTY_OFFSET)); } + static uint8_t* realloc(uint8_t* ptr, size_t size) { return (uint8_t*)::realloc(ptr, size); } + static void free(uint8_t* ptr) { ::free(ptr); } + + inline void auto_size(size_t len) { + if (cur + len > siz) + resize(cur + len); + } + +public: + in_writer(size_t init_size = 0): in_view(alloc(init_size), init_size), vis_siz(init_size) { } + ~in_writer() { if (ptr != reinterpret_cast(EMPTY_OFFSET)) free(ptr); } + + size_t vis_siz = 0; + + uint8_t* data() { return ptr; } + constexpr size_t size() const { return vis_siz; } + + void write(const void* data, size_t len) { + in_view::write((uint8_t*)data, len); + } + /// Write without resize + template + void write(const D& d) { + write(&d, sizeof(d)); + } + + uint8_t* pushTo(size_t len) { + auto_size(len); + return writeTo(len); + } + void push(const void* data, size_t len) { + auto_size(len); + in_view::write((uint8_t*)data, len); + } + template + void push(const D& d) { + push(&d, sizeof(d)); + } + + size_t getCursor() const { return cur; } + void writeAt(size_t pos, const void* data, size_t len) { + assert(pos + len <= vis_siz); + memcpy(ptr + pos, data, len); + } + template + void writeAt(size_t pos, const D& d) { + writeAt(pos, &d, sizeof(d)); + } + + void reserve(size_t target) { + if (target >= siz) { + ptr = realloc(ptr, target); + siz = target; + } + } + void resize(size_t target) { + reserve(target); + vis_siz = target; + } + + bool isFull() const { + return cur >= vis_siz; + } + + // Must take ptr ownership before + void reset() { + ptr = reinterpret_cast(EMPTY_OFFSET); + cur = 0; + siz = 0; + vis_siz = 0; + } + + struct part { + part(in_writer* parent): up(parent), start(parent->cur) { } + in_writer* up; + size_t start; + + uint8_t* data() { return up->data() + start; } + constexpr size_t size() const { return up->size() - start; } + + void reserve(size_t size) { + up->reserve(start + size); + } + void resize(size_t size) { + up->resize(start + size); + } + }; + template + size_t pushPart(const CB& callback) { + const auto prev = cur; + callback(part(this)); + cur = vis_siz; + return cur - prev; + } + template + size_t pushSizedPart(const CB& callback) { + const auto size_cur = cur; + push(0); + const size_t size = pushPart(callback); + writeAt(size_cur, size); + return size; + } +}; /// Vector with in_view interface struct in_vector { /// 512 Mébibits @@ -44,23 +169,6 @@ struct in_vector { memcpy(writeTo(len), data, len); } }; -// Abstract data writer -struct in_view: view { - in_view(uint8_t *ptr, size_t size): view(size), ptr(ptr) { - assert(ptr != nullptr); - } - - uint8_t* ptr; - uint8_t* writeTo(size_t len) { - assert(cur + len <= siz); - auto p = ptr + cur; - cur += len; - return p; - } - void write(const uint8_t* data, size_t len) { - memcpy(writeTo(len), data, len); - } -}; /// Abstract data reader struct out_view: view { out_view(): out_view(nullptr, 0) { } @@ -80,6 +188,42 @@ struct out_view: view { constexpr const uint8_t* data() const { return ptr; } constexpr size_t remaining() const { return size() - cur; } }; +/// Helper to read from out_view +struct out_reader: public out_view { + out_reader(const out_view& buf): out_view(buf.ptr, buf.siz) { cur = buf.cur; } + + template + const D* read() { + return cur + sizeof(D) <= size() ? + (const D*)readFrom(sizeof(D)) : nullptr; + } + template + bool read(D& out) { + const auto ptr = read(); + if (ptr == nullptr) + return false; + + out = *ptr; + return true; + } + + bool skip(size_t size) { + readFrom(size); + return !isDone(); + } + + data::out_view readPart(size_t size) { + return data::out_view(readFrom(size), size); + } + data::out_view readSizedPart() { + size_t size = 0; + read(size); + return readPart(size); + } + data::out_view readRemaining() { + return readPart(remaining()); + } +}; /// Pointer to opaque owned memory using handle_t = std::shared_ptr; /// out_view with owned data @@ -92,6 +236,9 @@ struct out_buffer: out_view { out_buffer(view, std::shared_ptr(view.ptr)) { } /// Take ptr ownership out_buffer(const uint8_t *ptr, size_t size): out_buffer(out_view(ptr, size)) { } + /// Take ptr ownership (release writer) + out_buffer(in_writer& writer): + out_buffer(writer.data(), writer.size()) { writer.reset(); } handle_t handle = nullptr; }; diff --git a/src/core/net/io.hpp b/src/core/net/io.hpp index d84984c..7583897 100644 --- a/src/core/net/io.hpp +++ b/src/core/net/io.hpp @@ -8,100 +8,19 @@ namespace net { //TODO: preallocate static data pool /// Helper to write allocated packets -class PacketWriter final { +class PacketWriter final: public data::in_writer { public: - PacketWriter(size_t size): buffer((uint8_t*)malloc(size), size), visible_size(size) { } + PacketWriter(size_t size): data::in_writer(size) { } PacketWriter(uint8_t type, size_t data_size): PacketWriter(sizeof(type) + data_size) { write(type); } PacketWriter(net::client_packet_type type, size_t data_size): PacketWriter((uint8_t)type, data_size) { } PacketWriter(net::server_packet_type type, size_t data_size): PacketWriter((uint8_t)type, data_size) { } - ~PacketWriter() { - if (buffer.ptr != nullptr) - free(buffer.ptr); - } - - void write(const void* data, size_t len) { - buffer.write((uint8_t*)data, len); - } - template - void write(const D& d) { - write(&d, sizeof(d)); - } - - constexpr size_t getCursor() const { return buffer.cur; } - void writeAt(size_t pos, const void* data, size_t len) { - assert(pos + len <= buffer.siz); - memcpy(buffer.ptr + pos, data, len); - } - template - void writeAt(size_t pos, const D& d) { - writeAt(pos, &d, sizeof(d)); - } - - void reserve(size_t target) { - if (target >= buffer.siz - buffer.cur) { - const auto size = target + buffer.cur; - buffer.ptr = (uint8_t *)realloc(buffer.ptr, size); - buffer.siz = size; - } - } - void resize(size_t target) { - reserve(target); - visible_size = target; - } - - void writePush(const void* data, size_t len) { - if (buffer.cur + len > buffer.siz) - resize(buffer.cur + len); - write(data, len); - } - template - void writePush(const D& d) { - writePush(&d, sizeof(d)); - } - - struct varying_part { - varying_part(data::in_view &buffer, size_t& p_size): buffer(buffer), parent_size(p_size), visible_size(0) { } - ~varying_part() { - buffer.cur += visible_size; - parent_size += visible_size; - } - - data::in_view &buffer; - size_t &parent_size; - size_t visible_size; - - constexpr size_t size() const { return visible_size; } - void* data() { return buffer.writeTo(0); } - void reserve(size_t target) { - if (target >= buffer.siz - buffer.cur) { - const auto size = target + buffer.cur; - buffer.ptr = (uint8_t *)realloc(buffer.ptr, size); - buffer.siz = size; - } - } - void resize(size_t target) { - reserve(target); - visible_size = target; - } - }; - /// Only from resize, write, resize down - varying_part varying() { - return varying_part(buffer, visible_size); - } - - bool isFull() const { - return buffer.cur >= visible_size; - } [[nodiscard]] data::out_buffer finish() { assert(isFull()); - data::out_buffer cpy(buffer.ptr, visible_size); - buffer.ptr = nullptr; - buffer.siz = 0; - return cpy; + return data::out_buffer(*this); } [[nodiscard]] @@ -126,50 +45,12 @@ public: static data::out_buffer Of(net::server_packet_type type, const D& data) { return Of(type, &data, sizeof(data)); } - -private: - data::in_view buffer; - size_t visible_size; }; /// Helper to read out_view -class PacketReader final { +class PacketReader final: public data::out_reader { public: - PacketReader(const data::out_view& buf): buffer(buf) { } - - template - const D* read() { - return buffer.cur + sizeof(D) <= buffer.size() ? - (const D*)buffer.readFrom(sizeof(D)) : nullptr; - } - template - bool read(D& out) { - const auto ptr = read(); - if (ptr == nullptr) - return false; - - out = *ptr; - return true; - } - - bool skip(size_t size) { - buffer.readFrom(size); - return !isFull(); - } - - data::out_view readPart(size_t size) { - return data::out_view(buffer.readFrom(size), size); - } - data::out_view readAll() { - return readPart(buffer.size() - buffer.cur); - } - - bool isFull() const { - return buffer.isDone(); - } - -private: - data::out_view buffer; + PacketReader(const data::out_view& buf): data::out_reader(buf) { } }; } \ No newline at end of file diff --git a/src/server/world/Area.hpp b/src/server/world/Area.hpp index 4083494..f07fd70 100644 --- a/src/server/world/Area.hpp +++ b/src/server/world/Area.hpp @@ -30,6 +30,8 @@ namespace world::server { std::optional getCurvature() const override { return GetCurvature(generator_properties); } static std::optional GetCurvature(const generator::params &); + inline const glm::vec3 getGravity(const voxel_pos& p) const { return generator->getGravity(p); } + private: shared_guarded regions; diff --git a/src/server/world/Universe.cpp b/src/server/world/Universe.cpp index df50f67..db23737 100644 --- a/src/server/world/Universe.cpp +++ b/src/server/world/Universe.cpp @@ -306,7 +306,7 @@ void Universe::update(float deltaTime) { const auto areaDiff = glm::divide(it->second->getOffset().as_voxel()); std::vector inAreaPlayers; - players.iter([&](entity_id, Entity::Instance player) { + players.iter([&](const entity_id&, const Entity::Instance& player) { const chunk_pos diff = glm::divide(player.pos.as_voxel() - it->second->getOffset().as_voxel()); if (glm::length2(diff) <= areaRange) inAreaPlayers.push_back(diff); @@ -414,6 +414,9 @@ void Universe::update(float deltaTime) { ZoneScopedN("Entities"); size_t item_count = 0; entities.remove([&](entity_id type, Entity &val) { + //TODO: find mass center + const auto OFFSET = glm::vec3(val.size) * val.scale / 2.f; + const auto DIST2 = glm::pow2(loadDistance); //NOTE: must be < keepDistance + glm::length(OFFSET) to avoid collision miss val.instances.remove([&](entity_id, Entity::Instance &inst) { if (type == PLAYER_ENTITY_ID) { //MAYBE: update players ? @@ -421,14 +424,25 @@ void Universe::update(float deltaTime) { return false; } - inst.pos += inst.velocity * deltaTime; - if (true /*FIXME: remove far entities ? glm::length2(glm::divide(pos - inst.pos.as_voxel())) <= glm::pow2(keepDistance);*/) { + //TODO: partition space + auto pos = inst.pos + OFFSET; + for(const auto& area: areas) { + //NOTE: consider entity mass negligible + inst.velocity += area.second->getGravity((pos - area.second->getOffset()).as_voxel()) * deltaTime; + } + //MAYBE: friction + if (move(pos, inst.velocity, 1, glm::max_axis(OFFSET))) { + inst.velocity = glm::vec3(0); + } + inst.pos = pos - OFFSET; + if (entities.at(PLAYER_ENTITY_ID).instances.contains([&](const entity_id&, const Entity::Instance& player) { + return glm::length2(glm::divide((player.pos - inst.pos).as_voxel())) <= DIST2; + })) { item_count++; return false; } + //TODO: Save to files if !temporary return true; - //MAYBE: Store in region ? - //MAYBE: Save to files }); return !val.permanant && val.instances.empty(); }); @@ -558,7 +572,7 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net: break; } case client_packet_type::MESSAGE: { - const auto ref = packet.readAll(); + const auto ref = packet.readRemaining(); broadcastMessage("Player" + std::to_string(peer->getCtx()->instanceId.index) + ": " + std::string((const char*)ref.data(), ref.size())); break; @@ -573,7 +587,7 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net: break; if (auto area = areas.find(id); area != areas.end()) { const chunk_pos areaOffset = glm::divide(pos - area->second->getOffset().as_voxel()); - while (!packet.isFull()) { + while (!packet.isDone()) { region_pos rpos; if (!packet.read(rpos)) break; @@ -582,10 +596,8 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net: if (glm::length2(areaOffset - glm::lvec3(it_r->first) * glm::lvec3(REGION_LENGTH)) <= glm::pow2(loadDistance + REGION_LENGTH * 2)) { auto packet = PacketWriter(server_packet_type::REGION, sizeof(area_)); packet.write>(std::make_pair(id, rpos)); - { - auto vec = packet.varying(); - it_r->second->averages(vec); - } + packet.pushPart([&](PacketWriter::part part) { + it_r->second->averages(part); }); peer->send(packet.finish(), net::server::CHUNK); } else { LOG_T("Request out of range region"); @@ -611,7 +623,7 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net: auto &chunks = area->second->getChunks(); const chunk_pos areaOffset = glm::divide(pos - area->second->getOffset().as_voxel()); - for (size_t i = 0; !packet.isFull() && i < MAX_PENDING_CHUNK_COUNT; i++) { + for (size_t i = 0; !packet.isDone() && i < MAX_PENDING_CHUNK_COUNT; i++) { chunk_pos cpos; if (!packet.read(cpos)) break; @@ -642,15 +654,8 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net: for (auto it = area->begin(); it != area->end(); ++it) { std::ostringstream out; std::dynamic_pointer_cast(*it)->write(out); - auto size_pos = packet.getCursor(); - size_t size = 0; - packet.writePush(size); - { - auto vec = packet.varying(); - dict_write_ctx.compress(out.str(), vec); - size = vec.size(); - } - packet.writeAt(size_pos, size); + packet.pushSizedPart([&](PacketWriter::part part) { + dict_write_ctx.compress(out.str(), part); }); } peer->send(packet.finish(), net::server::CHUNK); } else { @@ -691,10 +696,8 @@ data::out_buffer Universe::serializeChunk(area_ id, const std::shared data->write(out); auto packet = net::PacketWriter(net::server_packet_type::CHUNK, sizeof(id)); packet.write(id); - { - auto vec = packet.varying(); - dict_write_ctx.compress(out.str(), vec); - } + packet.pushPart([&](net::PacketWriter::part part){ + dict_write_ctx.compress(out.str(), part); }); return packet.finish(); } void Universe::broadcastMessage(const std::string& text) { diff --git a/src/server/world/generator.hpp b/src/server/world/generator.hpp index 23a1ad7..437f9f6 100644 --- a/src/server/world/generator.hpp +++ b/src/server/world/generator.hpp @@ -13,6 +13,8 @@ namespace world::generator { /// Generate chunk voxels /// MAYBE: use template to avoid virtual virtual void generate(const chunk_pos &at, std::array &out) = 0; + /// Get gravity vector at given point + virtual glm::vec3 getGravity(const voxel_pos& point) const = 0; }; // Generate empty space @@ -24,6 +26,7 @@ namespace world::generator { void generate(const chunk_pos &, std::array &out) override { out.fill(Voxel()); } + glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(0); } }; /// Endless cave network @@ -51,6 +54,7 @@ namespace world::generator { out[i] = Voxel(material, density); } } + glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(-1, 0, 0); } private: Params params; Noise density; @@ -110,6 +114,12 @@ namespace world::generator { }(); } } + glm::vec3 getGravity(const voxel_pos& pos) const override { + const auto heightRatio = static_cast((getHeight(pos) - params.height) / params.height); + const auto scale = params.height >> 7; + const auto mass = scale * scale * scale; //FIXME: average material density + return -getSurfaceDir(pos) * std::max(0.f, 1.f - heightRatio * heightRatio) * glm::vec3(mass); + } private: static constexpr PlanetShape shape = PS; static constexpr glm::f64 getHeight(const voxel_pos &p) { @@ -119,6 +129,15 @@ namespace world::generator { return glm::length(glm::dvec3(p)); } } + /// Centre inverse direction + // NOTE: So center point is consider mass center + static constexpr glm::vec3 getSurfaceDir(const voxel_pos &p) { + if constexpr(shape == PlanetShape::Cube) { + return glm::inormalize(p); + } else { + return glm::normalize(glm::dvec3(p)); + } + } Params params; Noise density; Noise displacement;