Skip to content

Commit

Permalink
Merge pull request #437 from MihailRis/chunks-manipulations
Browse files Browse the repository at this point in the history
Chunks manipulations
  • Loading branch information
MihailRis authored Jan 12, 2025
2 parents d7ad7ff + cafaa3a commit cfa6b5e
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 121 deletions.
14 changes: 14 additions & 0 deletions doc/en/scripting/builtins/libworld.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,18 @@ world.is_night() -> bool

-- Returns the total number of chunks loaded into memory
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
```
14 changes: 14 additions & 0 deletions doc/ru/scripting/builtins/libworld.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,18 @@ world.is_night() -> bool

-- Возвращает общее количество загруженных в память чанков
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
```
12 changes: 10 additions & 2 deletions src/graphics/render/ChunksRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ const Mesh* ChunksRenderer::getOrRender(
if (found == meshes.end()) {
return render(chunk, important);
}
if (chunk->flags.modified) {
if (chunk->flags.modified && chunk->flags.lighted) {
render(chunk, important);
}
return found->second.mesh.get();
Expand All @@ -149,9 +149,17 @@ const Mesh* ChunksRenderer::retrieveChunk(
size_t index, const Camera& camera, Shader& shader, bool culling
) {
auto chunk = chunks.getChunks()[index];
if (chunk == nullptr || !chunk->flags.lighted) {
if (chunk == 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(
camera.position,
glm::vec3(
Expand Down
142 changes: 38 additions & 104 deletions src/logic/scripting/lua/libs/libworld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

#include "api_lua.hpp"
#include "assets/AssetsLoader.hpp"
#include "coders/compression.hpp"
#include "coders/gzip.hpp"
#include "coders/json.hpp"
#include "engine/Engine.hpp"
#include "files/engine_paths.hpp"
Expand All @@ -14,6 +12,7 @@
#include "voxels/Chunk.hpp"
#include "voxels/Chunks.hpp"
#include "voxels/GlobalChunks.hpp"
#include "voxels/compressed_chunks.hpp"
#include "world/Level.hpp"
#include "world/World.hpp"
#include "logic/LevelController.hpp"
Expand Down Expand Up @@ -123,122 +122,57 @@ static int l_get_generator(lua::State* L) {
}

static int l_get_chunk_data(lua::State* L) {
int x = (int)lua::tointeger(L, 1);
int y = (int)lua::tointeger(L, 2);
const auto& chunk = level->chunks->getChunk(x, y);
int x = static_cast<int>(lua::tointeger(L, 1));
int z = static_cast<int>(lua::tointeger(L, 2));
const auto& chunk = level->chunks->getChunk(x, z);
if (chunk == nullptr) {
lua::pushnil(L);
return 0;
}
auto chunkData = compressed_chunks::encode(*chunk);
return lua::newuserdata<lua::LuaBytearray>(L, std::move(chunkData));
}

bool compress = false;
if (lua::gettop(L) >= 3) {
compress = lua::toboolean(L, 3);
}
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
);
static void integrate_chunk_client(Chunk& chunk) {
int x = chunk.x;
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);
}
}
}
return lua::newuserdata<lua::LuaBytearray>(L, chunk_data);
}

static int l_set_chunk_data(lua::State* L) {
int x = (int)lua::tointeger(L, 1);
int y = (int)lua::tointeger(L, 2);
int x = static_cast<int>(lua::tointeger(L, 1));
int z = static_cast<int>(lua::tointeger(L, 2));
auto buffer = lua::touserdata<lua::LuaBytearray>(L, 3);
bool is_compressed = false;
if (lua::gettop(L) >= 4) {
is_compressed = lua::toboolean(L, 4);
}
auto chunk = level->chunks->getChunk(x, y);
auto chunk = level->chunks->getChunk(x, z);
if (chunk == nullptr) {
return 0;
}
if (is_compressed) {
std::vector<ubyte>& raw_data = buffer->data();
size_t gzip_decompressed_size =
dataio::le2h(*(size_t*)(raw_data.data()));
const auto& rle_data = compression::decompress(
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());
}

auto chunksController = controller->getChunksController();
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);
compressed_chunks::decode(
*chunk, buffer->data().data(), buffer->data().size()
);
if (controller->getChunksController()->lighting == nullptr) {
return lua::pushboolean(L, 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;
integrate_chunk_client(*chunk);
return lua::pushboolean(L, true);
}

static int l_count_chunks(lua::State* L) {
Expand Down
13 changes: 0 additions & 13 deletions src/voxels/Chunk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,6 @@ Chunk::Chunk(int xpos, int zpos) : x(xpos), z(zpos) {
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() {
for (uint i = 0; i < CHUNK_VOL; i++) {
if (voxels[i].id != 0) {
Expand Down
4 changes: 2 additions & 2 deletions src/voxels/Chunk.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "maths/aabb.hpp"
#include "voxel.hpp"

/// @brief Total bytes number of chunk voxel data
inline constexpr int CHUNK_DATA_LEN = CHUNK_VOL * 4;

class ContentReport;
Expand Down Expand Up @@ -45,8 +46,7 @@ class Chunk {

Chunk(int x, int z);

bool isEmpty() const;

/// @brief Refresh `bottom` and `top` values
void updateHeights();

// unused
Expand Down
61 changes: 61 additions & 0 deletions src/voxels/compressed_chunks.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 12 additions & 0 deletions src/voxels/compressed_chunks.hpp
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit cfa6b5e

Please sign in to comment.