Merge pull request #437 from MihailRis/chunks-manipulations
Chunks manipulations
This commit is contained in:
commit
cfa6b5e578
@ -47,4 +47,18 @@ world.is_night() -> bool
|
|||||||
|
|
||||||
-- Returns the total number of chunks loaded into memory
|
-- Returns the total number of chunks loaded into memory
|
||||||
world.count_chunks() -> int
|
world.count_chunks() -> int
|
||||||
|
|
||||||
|
-- Returns the compressed chunk data to send.
|
||||||
|
-- Currently includes:
|
||||||
|
-- 1. Voxel data (id and state)
|
||||||
|
-- 2. Voxel metadata (fields)
|
||||||
|
world.get_chunk_data(x: int, z: int) -> Bytearray
|
||||||
|
|
||||||
|
-- Modifies the chunk based on the compressed data.
|
||||||
|
-- Returns true if the chunk exists.
|
||||||
|
world.set_chunk_data(
|
||||||
|
x: int, z: int,
|
||||||
|
-- compressed chunk data
|
||||||
|
data: Bytearray
|
||||||
|
) -> bool
|
||||||
```
|
```
|
||||||
|
|||||||
@ -46,4 +46,18 @@ world.is_night() -> bool
|
|||||||
|
|
||||||
-- Возвращает общее количество загруженных в память чанков
|
-- Возвращает общее количество загруженных в память чанков
|
||||||
world.count_chunks() -> int
|
world.count_chunks() -> int
|
||||||
|
|
||||||
|
-- Возвращает сжатые данные чанка для отправки.
|
||||||
|
-- На данный момент включает:
|
||||||
|
-- 1. Данные вокселей (id и состояние)
|
||||||
|
-- 2. Метаданные (поля) вокселей
|
||||||
|
world.get_chunk_data(x: int, z: int) -> Bytearray
|
||||||
|
|
||||||
|
-- Изменяет чанк на основе сжатых данных.
|
||||||
|
-- Возвращает true если чанк существует.
|
||||||
|
world.set_chunk_data(
|
||||||
|
x: int, z: int,
|
||||||
|
-- сжатые данные чанка
|
||||||
|
data: Bytearray
|
||||||
|
) -> bool
|
||||||
```
|
```
|
||||||
|
|||||||
@ -135,7 +135,7 @@ const Mesh* ChunksRenderer::getOrRender(
|
|||||||
if (found == meshes.end()) {
|
if (found == meshes.end()) {
|
||||||
return render(chunk, important);
|
return render(chunk, important);
|
||||||
}
|
}
|
||||||
if (chunk->flags.modified) {
|
if (chunk->flags.modified && chunk->flags.lighted) {
|
||||||
render(chunk, important);
|
render(chunk, important);
|
||||||
}
|
}
|
||||||
return found->second.mesh.get();
|
return found->second.mesh.get();
|
||||||
@ -149,9 +149,17 @@ const Mesh* ChunksRenderer::retrieveChunk(
|
|||||||
size_t index, const Camera& camera, Shader& shader, bool culling
|
size_t index, const Camera& camera, Shader& shader, bool culling
|
||||||
) {
|
) {
|
||||||
auto chunk = chunks.getChunks()[index];
|
auto chunk = chunks.getChunks()[index];
|
||||||
if (chunk == nullptr || !chunk->flags.lighted) {
|
if (chunk == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
if (!chunk->flags.lighted) {
|
||||||
|
const auto& found = meshes.find({chunk->x, chunk->z});
|
||||||
|
if (found == meshes.end()) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
return found->second.mesh.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
float distance = glm::distance(
|
float distance = glm::distance(
|
||||||
camera.position,
|
camera.position,
|
||||||
glm::vec3(
|
glm::vec3(
|
||||||
|
|||||||
@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
#include "api_lua.hpp"
|
#include "api_lua.hpp"
|
||||||
#include "assets/AssetsLoader.hpp"
|
#include "assets/AssetsLoader.hpp"
|
||||||
#include "coders/compression.hpp"
|
|
||||||
#include "coders/gzip.hpp"
|
|
||||||
#include "coders/json.hpp"
|
#include "coders/json.hpp"
|
||||||
#include "engine/Engine.hpp"
|
#include "engine/Engine.hpp"
|
||||||
#include "files/engine_paths.hpp"
|
#include "files/engine_paths.hpp"
|
||||||
@ -14,6 +12,7 @@
|
|||||||
#include "voxels/Chunk.hpp"
|
#include "voxels/Chunk.hpp"
|
||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/Chunks.hpp"
|
||||||
#include "voxels/GlobalChunks.hpp"
|
#include "voxels/GlobalChunks.hpp"
|
||||||
|
#include "voxels/compressed_chunks.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "world/World.hpp"
|
#include "world/World.hpp"
|
||||||
#include "logic/LevelController.hpp"
|
#include "logic/LevelController.hpp"
|
||||||
@ -123,122 +122,57 @@ static int l_get_generator(lua::State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_get_chunk_data(lua::State* L) {
|
static int l_get_chunk_data(lua::State* L) {
|
||||||
int x = (int)lua::tointeger(L, 1);
|
int x = static_cast<int>(lua::tointeger(L, 1));
|
||||||
int y = (int)lua::tointeger(L, 2);
|
int z = static_cast<int>(lua::tointeger(L, 2));
|
||||||
const auto& chunk = level->chunks->getChunk(x, y);
|
const auto& chunk = level->chunks->getChunk(x, z);
|
||||||
if (chunk == nullptr) {
|
if (chunk == nullptr) {
|
||||||
lua::pushnil(L);
|
lua::pushnil(L);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
auto chunkData = compressed_chunks::encode(*chunk);
|
||||||
|
return lua::newuserdata<lua::LuaBytearray>(L, std::move(chunkData));
|
||||||
|
}
|
||||||
|
|
||||||
bool compress = false;
|
static void integrate_chunk_client(Chunk& chunk) {
|
||||||
if (lua::gettop(L) >= 3) {
|
int x = chunk.x;
|
||||||
compress = lua::toboolean(L, 3);
|
int z = chunk.z;
|
||||||
|
auto chunksController = controller->getChunksController();
|
||||||
|
Lighting& lighting = *chunksController->lighting;
|
||||||
|
chunk.flags.loadedLights = false;
|
||||||
|
chunk.flags.lighted = false;
|
||||||
|
|
||||||
|
Lighting::prebuildSkyLight(chunk, *indices);
|
||||||
|
lighting.onChunkLoaded(x, z, true);
|
||||||
|
|
||||||
|
for (int lz = -1; lz <= 1; lz++) {
|
||||||
|
for (int lx = -1; lx <= 1; lx++) {
|
||||||
|
if (std::abs(lx) + std::abs(lz) != 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (auto other = level->chunks->getChunk(x + lx, z + lz)) {
|
||||||
|
other->flags.modified = true;
|
||||||
|
lighting.onChunkLoaded(x - 1, z, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
std::vector<ubyte> chunk_data;
|
|
||||||
if (compress) {
|
|
||||||
size_t rle_compressed_size;
|
|
||||||
size_t gzip_compressed_size;
|
|
||||||
const auto& data_ptr = chunk->encode();
|
|
||||||
ubyte* data = data_ptr.get();
|
|
||||||
const auto& rle_compressed_data_ptr = compression::compress(
|
|
||||||
data,
|
|
||||||
CHUNK_DATA_LEN,
|
|
||||||
rle_compressed_size,
|
|
||||||
compression::Method::EXTRLE16
|
|
||||||
);
|
|
||||||
const auto& gzip_compressed_data = compression::compress(
|
|
||||||
rle_compressed_data_ptr.get(),
|
|
||||||
rle_compressed_size,
|
|
||||||
gzip_compressed_size,
|
|
||||||
compression::Method::GZIP
|
|
||||||
);
|
|
||||||
auto tmp = dataio::h2le(rle_compressed_size);
|
|
||||||
chunk_data.reserve(gzip_compressed_size + sizeof(tmp));
|
|
||||||
chunk_data.insert(
|
|
||||||
chunk_data.begin() + 0, (char*)&tmp, ((char*)&tmp) + sizeof(tmp)
|
|
||||||
);
|
|
||||||
chunk_data.insert(
|
|
||||||
chunk_data.begin() + sizeof(tmp),
|
|
||||||
gzip_compressed_data.get(),
|
|
||||||
gzip_compressed_data.get() + gzip_compressed_size
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const auto& data = chunk->encode();
|
|
||||||
chunk_data.reserve(CHUNK_DATA_LEN);
|
|
||||||
chunk_data.insert(
|
|
||||||
chunk_data.begin(), data.get(), data.get() + CHUNK_DATA_LEN
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return lua::newuserdata<lua::LuaBytearray>(L, chunk_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_set_chunk_data(lua::State* L) {
|
static int l_set_chunk_data(lua::State* L) {
|
||||||
int x = (int)lua::tointeger(L, 1);
|
int x = static_cast<int>(lua::tointeger(L, 1));
|
||||||
int y = (int)lua::tointeger(L, 2);
|
int z = static_cast<int>(lua::tointeger(L, 2));
|
||||||
auto buffer = lua::touserdata<lua::LuaBytearray>(L, 3);
|
auto buffer = lua::touserdata<lua::LuaBytearray>(L, 3);
|
||||||
bool is_compressed = false;
|
auto chunk = level->chunks->getChunk(x, z);
|
||||||
if (lua::gettop(L) >= 4) {
|
|
||||||
is_compressed = lua::toboolean(L, 4);
|
|
||||||
}
|
|
||||||
auto chunk = level->chunks->getChunk(x, y);
|
|
||||||
if (chunk == nullptr) {
|
if (chunk == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (is_compressed) {
|
compressed_chunks::decode(
|
||||||
std::vector<ubyte>& raw_data = buffer->data();
|
*chunk, buffer->data().data(), buffer->data().size()
|
||||||
size_t gzip_decompressed_size =
|
);
|
||||||
dataio::le2h(*(size_t*)(raw_data.data()));
|
if (controller->getChunksController()->lighting == nullptr) {
|
||||||
const auto& rle_data = compression::decompress(
|
return lua::pushboolean(L, true);
|
||||||
raw_data.data() + sizeof(gzip_decompressed_size),
|
|
||||||
buffer->data().size() - sizeof(gzip_decompressed_size),
|
|
||||||
gzip_decompressed_size,
|
|
||||||
compression::Method::GZIP
|
|
||||||
);
|
|
||||||
const auto& data = compression::decompress(
|
|
||||||
rle_data.get(),
|
|
||||||
gzip_decompressed_size,
|
|
||||||
CHUNK_DATA_LEN,
|
|
||||||
compression::Method::EXTRLE16
|
|
||||||
);
|
|
||||||
chunk->decode(data.get());
|
|
||||||
} else {
|
|
||||||
chunk->decode(buffer->data().data());
|
|
||||||
}
|
}
|
||||||
|
integrate_chunk_client(*chunk);
|
||||||
auto chunksController = controller->getChunksController();
|
return lua::pushboolean(L, true);
|
||||||
if (chunksController == nullptr) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Lighting& lighting = *chunksController->lighting;
|
|
||||||
chunk->updateHeights();
|
|
||||||
lighting.buildSkyLight(x, y);
|
|
||||||
chunk->flags.modified = true;
|
|
||||||
lighting.onChunkLoaded(x, y, true);
|
|
||||||
|
|
||||||
chunk = level->chunks->getChunk(x - 1, y);
|
|
||||||
if (chunk != nullptr) {
|
|
||||||
chunk->flags.modified = true;
|
|
||||||
lighting.onChunkLoaded(x - 1, y, true);
|
|
||||||
}
|
|
||||||
chunk = level->chunks->getChunk(x + 1, y);
|
|
||||||
if (chunk != nullptr) {
|
|
||||||
chunk->flags.modified = true;
|
|
||||||
lighting.onChunkLoaded(x + 1, y, true);
|
|
||||||
}
|
|
||||||
chunk = level->chunks->getChunk(x, y - 1);
|
|
||||||
if (chunk != nullptr) {
|
|
||||||
chunk->flags.modified = true;
|
|
||||||
lighting.onChunkLoaded(x, y - 1, true);
|
|
||||||
}
|
|
||||||
chunk = level->chunks->getChunk(x, y + 1);
|
|
||||||
if (chunk != nullptr) {
|
|
||||||
chunk->flags.modified = true;
|
|
||||||
lighting.onChunkLoaded(x, y + 1, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_count_chunks(lua::State* L) {
|
static int l_count_chunks(lua::State* L) {
|
||||||
|
|||||||
@ -13,19 +13,6 @@ Chunk::Chunk(int xpos, int zpos) : x(xpos), z(zpos) {
|
|||||||
top = CHUNK_H;
|
top = CHUNK_H;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chunk::isEmpty() const {
|
|
||||||
int id = -1;
|
|
||||||
for (uint i = 0; i < CHUNK_VOL; i++) {
|
|
||||||
if (voxels[i].id != id) {
|
|
||||||
if (id != -1)
|
|
||||||
return false;
|
|
||||||
else
|
|
||||||
id = voxels[i].id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Chunk::updateHeights() {
|
void Chunk::updateHeights() {
|
||||||
for (uint i = 0; i < CHUNK_VOL; i++) {
|
for (uint i = 0; i < CHUNK_VOL; i++) {
|
||||||
if (voxels[i].id != 0) {
|
if (voxels[i].id != 0) {
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#include "maths/aabb.hpp"
|
#include "maths/aabb.hpp"
|
||||||
#include "voxel.hpp"
|
#include "voxel.hpp"
|
||||||
|
|
||||||
|
/// @brief Total bytes number of chunk voxel data
|
||||||
inline constexpr int CHUNK_DATA_LEN = CHUNK_VOL * 4;
|
inline constexpr int CHUNK_DATA_LEN = CHUNK_VOL * 4;
|
||||||
|
|
||||||
class ContentReport;
|
class ContentReport;
|
||||||
@ -45,8 +46,7 @@ public:
|
|||||||
|
|
||||||
Chunk(int x, int z);
|
Chunk(int x, int z);
|
||||||
|
|
||||||
bool isEmpty() const;
|
/// @brief Refresh `bottom` and `top` values
|
||||||
|
|
||||||
void updateHeights();
|
void updateHeights();
|
||||||
|
|
||||||
// unused
|
// unused
|
||||||
|
|||||||
61
src/voxels/compressed_chunks.cpp
Normal file
61
src/voxels/compressed_chunks.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include "compressed_chunks.hpp"
|
||||||
|
|
||||||
|
#include "coders/rle.hpp"
|
||||||
|
#include "coders/gzip.hpp"
|
||||||
|
#include "coders/byte_utils.hpp"
|
||||||
|
#include "voxels/Chunk.hpp"
|
||||||
|
|
||||||
|
inline constexpr int HAS_VOXELS = 0x1;
|
||||||
|
inline constexpr int HAS_METADATA = 0x2;
|
||||||
|
|
||||||
|
std::vector<ubyte> compressed_chunks::encode(const Chunk& chunk) {
|
||||||
|
auto data = chunk.encode();
|
||||||
|
|
||||||
|
/// world.get_chunk_data is only available in the main Lua state
|
||||||
|
static util::Buffer<ubyte> rleBuffer;
|
||||||
|
if (rleBuffer.size() < CHUNK_DATA_LEN * 2) {
|
||||||
|
rleBuffer = util::Buffer<ubyte>(CHUNK_DATA_LEN * 2);
|
||||||
|
}
|
||||||
|
size_t rleCompressedSize =
|
||||||
|
extrle::encode16(data.get(), CHUNK_DATA_LEN, rleBuffer.data());
|
||||||
|
|
||||||
|
const auto gzipCompressedData = gzip::compress(
|
||||||
|
rleBuffer.data(), rleCompressedSize
|
||||||
|
);
|
||||||
|
auto metadataBytes = chunk.blocksMetadata.serialize();
|
||||||
|
|
||||||
|
ByteBuilder builder(2 + 8 + gzipCompressedData.size() + metadataBytes.size());
|
||||||
|
builder.put(HAS_VOXELS | HAS_METADATA); // flags
|
||||||
|
builder.put(0); // reserved
|
||||||
|
builder.putInt32(gzipCompressedData.size());
|
||||||
|
builder.put(gzipCompressedData.data(), gzipCompressedData.size());
|
||||||
|
builder.putInt32(metadataBytes.size());
|
||||||
|
builder.put(metadataBytes.data(), metadataBytes.size());
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
void compressed_chunks::decode(Chunk& chunk, const ubyte* src, size_t size) {
|
||||||
|
ByteReader reader(src, size);
|
||||||
|
|
||||||
|
ubyte flags = reader.get();
|
||||||
|
reader.skip(1); // reserved byte
|
||||||
|
|
||||||
|
if (flags & HAS_VOXELS) {
|
||||||
|
size_t gzipCompressedSize = reader.getInt32();
|
||||||
|
|
||||||
|
auto rleData = gzip::decompress(reader.pointer(), gzipCompressedSize);
|
||||||
|
reader.skip(gzipCompressedSize);
|
||||||
|
|
||||||
|
/// world.get_chunk_data is only available in the main Lua state
|
||||||
|
static util::Buffer<ubyte> voxelData (CHUNK_DATA_LEN);
|
||||||
|
extrle::decode16(rleData.data(), rleData.size(), voxelData.data());
|
||||||
|
chunk.decode(voxelData.data());
|
||||||
|
chunk.updateHeights();
|
||||||
|
}
|
||||||
|
if (flags & HAS_METADATA) {
|
||||||
|
size_t metadataSize = reader.getInt32();
|
||||||
|
chunk.blocksMetadata.deserialize(reader.pointer(), metadataSize);
|
||||||
|
reader.skip(metadataSize);
|
||||||
|
}
|
||||||
|
chunk.setModifiedAndUnsaved();
|
||||||
|
}
|
||||||
12
src/voxels/compressed_chunks.hpp
Normal file
12
src/voxels/compressed_chunks.hpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "typedefs.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Chunk;
|
||||||
|
|
||||||
|
namespace compressed_chunks {
|
||||||
|
std::vector<ubyte> encode(const Chunk& chunk);
|
||||||
|
void decode(Chunk& chunk, const ubyte* src, size_t size);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user