From 06f66ffd7130a5b6860497b0f8506ab11aad1725 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 4 Jan 2024 15:27:07 +0300 Subject: [PATCH] Voxel changed from 16 bit to 32 bit + new lua functions --- src/constants.h | 3 + src/files/WorldConverter.cpp | 26 ++++---- src/files/WorldFiles.cpp | 33 +++++++++-- src/files/WorldFiles.h | 9 +++ src/frontend/menu.cpp | 16 ++++- src/logic/scripting/api_lua.cpp | 99 +++++++++++++++++++++++++++++++ src/logic/scripting/scripting.cpp | 6 ++ src/typedefs.h | 4 +- src/voxels/Chunk.cpp | 66 ++++++++++++++++----- src/voxels/Chunk.h | 3 +- 10 files changed, 229 insertions(+), 36 deletions(-) diff --git a/src/constants.h b/src/constants.h index cf560440..0b135aa4 100644 --- a/src/constants.h +++ b/src/constants.h @@ -11,6 +11,9 @@ const int CHUNK_W = 16; const int CHUNK_H = 256; const int CHUNK_D = 16; +const uint VOXEL_USER_BITS = 8; +constexpr uint VOXEL_USER_BITS_OFFSET = sizeof(blockstate_t)*8-VOXEL_USER_BITS; + /* Chunk volume (count of voxels per Chunk) */ constexpr int CHUNK_VOL = (CHUNK_W * CHUNK_H * CHUNK_D); diff --git a/src/files/WorldConverter.cpp b/src/files/WorldConverter.cpp index 3c8c7b35..de171ab3 100644 --- a/src/files/WorldConverter.cpp +++ b/src/files/WorldConverter.cpp @@ -8,18 +8,15 @@ #include "../content/ContentLUT.h" namespace fs = std::filesystem; -using std::string; -using std::unique_ptr; -using fs::path; -WorldConverter::WorldConverter(path folder, +WorldConverter::WorldConverter(fs::path folder, const Content* content, const ContentLUT* lut) : lut(lut), content(content) { DebugSettings settings; wfile = new WorldFiles(folder, settings); - path regionsFolder = wfile->getRegionsFolder(); + fs::path regionsFolder = wfile->getRegionsFolder(); if (!fs::is_directory(regionsFolder)) { std::cerr << "nothing to convert" << std::endl; return; @@ -41,13 +38,13 @@ void WorldConverter::convertNext() { if (!hasNext()) { throw std::runtime_error("no more regions to convert"); } - path regfile = regions.front(); + fs::path regfile = regions.front(); regions.pop(); if (!fs::is_regular_file(regfile)) return; - int x, y; - string name = regfile.stem().string(); - if (!WorldFiles::parseRegionFilename(name, x, y)) { + int x, z; + std::string name = regfile.stem().string(); + if (!WorldFiles::parseRegionFilename(name, x, z)) { std::cerr << "could not parse name " << name << std::endl; return; } @@ -55,11 +52,16 @@ void WorldConverter::convertNext() { for (uint cz = 0; cz < REGION_SIZE; cz++) { for (uint cx = 0; cx < REGION_SIZE; cx++) { int gx = cx + x * REGION_SIZE; - int gz = cz + y * REGION_SIZE; - unique_ptr data (wfile->getChunk(gx, gz)); + int gz = cz + z * REGION_SIZE; + std::unique_ptr data (wfile->getChunk(gx, gz)); if (data == nullptr) continue; - Chunk::convert(data.get(), lut); + if (wfile->getVoxelRegionVersion(x, z) != REGION_FORMAT_VERSION) { + Chunk::fromOld(data.get()); + } + if (lut) { + Chunk::convert(data.get(), lut); + } wfile->put(gx, gz, data.get()); } } diff --git a/src/files/WorldFiles.cpp b/src/files/WorldFiles.cpp index 9b1645f3..6c092a48 100644 --- a/src/files/WorldFiles.cpp +++ b/src/files/WorldFiles.cpp @@ -38,8 +38,8 @@ regfile::regfile(fs::path filename) : file(filename) { throw std::runtime_error("invalid region file magic number"); } version = header[8]; - if (version > 2) { - throw std::runtime_error( + if (version > REGION_FORMAT_VERSION) { + throw illegal_region_format( "region format "+std::to_string(version)+" is not supported"); } } @@ -130,6 +130,31 @@ ubyte* WorldFiles::decompress(const ubyte* src, size_t srclen, size_t dstlen) { return decompressed; } +int WorldFiles::getVoxelRegionVersion(int x, int z) { + regfile* rf = getRegFile(glm::ivec3(x, z, REGION_LAYER_VOXELS), getRegionsFolder()); + if (rf == nullptr) { + return 0; + } + return rf->version; +} + +int WorldFiles::getVoxelRegionsVersion() { + fs::path regionsFolder = getRegionsFolder(); + if (!fs::is_directory(regionsFolder)) { + return REGION_FORMAT_VERSION; + } + for (auto file : fs::directory_iterator(regionsFolder)) { + int x; + int z; + if (!parseRegionFilename(file.path().stem().string(), x, z)) { + continue; + } + regfile* rf = getRegFile(glm::ivec3(x, z, REGION_LAYER_VOXELS), regionsFolder); + return rf->version; + } + return REGION_FORMAT_VERSION; +} + /* * Compress and store chunk voxels data in region * @param x chunk.x @@ -190,8 +215,7 @@ fs::path WorldFiles::getLightsFolder() const { } fs::path WorldFiles::getRegionFilename(int x, int z) const { - std::string filename = std::to_string(x) + "_" + std::to_string(z) + ".bin"; - return fs::path(filename); + return fs::path(std::to_string(x) + "_" + std::to_string(z) + ".bin"); } /* @@ -254,7 +278,6 @@ ubyte* WorldFiles::getData(regionsmap& regions, const fs::path& folder, int localZ = z - (regionZ * REGION_SIZE); WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ); - ubyte* data = region->getChunkData(localX, localZ); if (data == nullptr) { uint32_t size; diff --git a/src/files/WorldFiles.h b/src/files/WorldFiles.h index d288a26c..d85b0221 100644 --- a/src/files/WorldFiles.h +++ b/src/files/WorldFiles.h @@ -34,6 +34,12 @@ class Content; class ContentIndices; class World; +class illegal_region_format : public std::runtime_error { +public: + illegal_region_format(const std::string& message) + : std::runtime_error(message) {} +}; + class WorldRegion { ubyte** chunksData; uint32_t* sizes; @@ -125,6 +131,9 @@ public: void put(Chunk* chunk); void put(int x, int z, const ubyte* voxelData); + int getVoxelRegionVersion(int x, int z); + int getVoxelRegionsVersion(); + ubyte* getChunk(int x, int z); light_t* getLights(int x, int z); diff --git a/src/frontend/menu.cpp b/src/frontend/menu.cpp index 6484bc0c..e5600e50 100644 --- a/src/frontend/menu.cpp +++ b/src/frontend/menu.cpp @@ -14,7 +14,9 @@ #include "../util/stringutil.h" #include "../files/engine_paths.h" #include "../files/WorldConverter.h" +#include "../files/WorldFiles.h" #include "../world/World.h" +#include "../world/Level.h" #include "../window/Events.h" #include "../window/Window.h" #include "../engine.h" @@ -175,8 +177,18 @@ void open_world(std::string name, Engine* engine) { show_convert_request(engine, content, lut, folder); } } else { - Level* level = World::load(folder, settings, content, packs); - engine->setScreen(std::make_shared(engine, level)); + // TODO: remove in 0.18 + int version; + { + WorldFiles wfile(folder, settings.debug); + version = wfile.getVoxelRegionsVersion(); + } + if (version != REGION_FORMAT_VERSION) { + show_convert_request(engine, content, lut, folder); + } else { + Level* level = World::load(folder, settings, content, packs); + engine->setScreen(std::make_shared(engine, level)); + } } } diff --git a/src/logic/scripting/api_lua.cpp b/src/logic/scripting/api_lua.cpp index e6fe52ce..e39df1ee 100644 --- a/src/logic/scripting/api_lua.cpp +++ b/src/logic/scripting/api_lua.cpp @@ -59,6 +59,64 @@ int l_get_block(lua_State* L) { return 1; } +inline int lua_pushivec3(lua_State* L, int x, int y, int z) { + lua_pushinteger(L, x); + lua_pushinteger(L, y); + lua_pushinteger(L, z); + return 3; +} + +int l_get_block_x(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + voxel* vox = scripting::level->chunks->get(x, y, z); + if (vox == nullptr) { + return lua_pushivec3(L, 1, 0, 0); + } + const Block* def = scripting::level->content->indices->getBlockDef(vox->id); + if (!def->rotatable) { + return lua_pushivec3(L, 1, 0, 0); + } else { + const CoordSystem& rot = def->rotations.variants[vox->rotation()]; + return lua_pushivec3(L, rot.axisX.x, rot.axisX.y, rot.axisX.z); + } +} + +int l_get_block_y(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + voxel* vox = scripting::level->chunks->get(x, y, z); + if (vox == nullptr) { + return lua_pushivec3(L, 0, 1, 0); + } + const Block* def = scripting::level->content->indices->getBlockDef(vox->id); + if (!def->rotatable) { + return lua_pushivec3(L, 0, 1, 0); + } else { + const CoordSystem& rot = def->rotations.variants[vox->rotation()]; + return lua_pushivec3(L, rot.axisY.x, rot.axisY.y, rot.axisY.z); + } +} + +int l_get_block_z(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + voxel* vox = scripting::level->chunks->get(x, y, z); + if (vox == nullptr) { + return lua_pushivec3(L, 0, 0, 1); + } + const Block* def = scripting::level->content->indices->getBlockDef(vox->id); + if (!def->rotatable) { + return lua_pushivec3(L, 0, 0, 1); + } else { + const CoordSystem& rot = def->rotations.variants[vox->rotation()]; + return lua_pushivec3(L, rot.axisZ.x, rot.axisZ.y, rot.axisZ.z); + } +} + int l_get_player_pos(lua_State* L) { glm::vec3 pos = scripting::level->player->hitbox->position; lua_pushnumber(L, pos.x); @@ -101,6 +159,42 @@ int l_get_block_states(lua_State* L) { return 1; } +int l_get_block_script_states(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + int offset = lua_tointeger(L, 4) + VOXEL_USER_BITS_OFFSET; + int bits = lua_tointeger(L, 5); + + voxel* vox = scripting::level->chunks->get(x, y, z); + if (vox == nullptr) { + lua_pushinteger(L, 0); + return 1; + } + uint mask = ((1 << bits) - 1) << offset; + uint data = (vox->states & mask) >> offset; + lua_pushinteger(L, data); + return 1; +} + +int l_set_block_script_states(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + int offset = lua_tointeger(L, 4) + VOXEL_USER_BITS_OFFSET; + int bits = lua_tointeger(L, 5); + + uint mask = (1 << bits) - 1; + int value = lua_tointeger(L, 6) & mask; + + voxel* vox = scripting::level->chunks->get(x, y, z); + if (vox == nullptr) { + return 0; + } + vox->states = (vox->states & (~mask)) | (value << offset); + return 0; +} + int l_is_replaceable_at(lua_State* L) { int x = lua_tointeger(L, 1); int y = lua_tointeger(L, 2); @@ -121,9 +215,14 @@ void apilua::create_funcs(lua_State* L) { lua_addfunc(L, l_is_replaceable_at, "is_replaceable_at"); lua_addfunc(L, l_set_block, "set_block"); lua_addfunc(L, l_get_block, "get_block"); + lua_addfunc(L, l_get_block_x, "get_block_X"); + lua_addfunc(L, l_get_block_y, "get_block_Y"); + lua_addfunc(L, l_get_block_z, "get_block_Z"); lua_addfunc(L, l_get_player_pos, "get_player_pos"); lua_addfunc(L, l_set_player_pos, "set_player_pos"); lua_addfunc(L, l_get_player_rot, "get_player_rot"); lua_addfunc(L, l_set_player_rot, "set_player_rot"); lua_addfunc(L, l_get_block_states, "get_block_states"); + lua_addfunc(L, l_get_block_script_states, "get_block_script_states"); + lua_addfunc(L, l_set_block_script_states, "set_block_script_states"); } diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index f7d19121..e4e1c355 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -53,8 +53,14 @@ void scripting::initialize(EnginePaths* paths) { if (L == nullptr) { throw std::runtime_error("could not to initialize Lua"); } + + // Allowed standard libraries luaopen_base(L); luaopen_math(L); + luaopen_string(L); + luaopen_table(L); + + // io-manipulations will be implemented via api functions std::cout << LUA_VERSION << std::endl; # ifdef LUAJIT_VERSION diff --git a/src/typedefs.h b/src/typedefs.h index ac519572..f1915c2f 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -9,8 +9,8 @@ typedef unsigned int uint; // use for bytes arrays typedef uint8_t ubyte; -typedef uint8_t blockid_t; -typedef uint8_t blockstate_t; +typedef uint16_t blockid_t; +typedef uint16_t blockstate_t; typedef uint16_t light_t; #endif diff --git a/src/voxels/Chunk.cpp b/src/voxels/Chunk.cpp index f1efaf33..a9afb823 100644 --- a/src/voxels/Chunk.cpp +++ b/src/voxels/Chunk.cpp @@ -1,4 +1,7 @@ #include "Chunk.h" + +#include + #include "voxel.h" #include "../content/ContentLUT.h" #include "../lighting/Lightmap.h" @@ -22,7 +25,7 @@ Chunk::~Chunk(){ bool Chunk::isEmpty(){ int id = -1; - for (int i = 0; i < CHUNK_VOL; i++){ + for (size_t i = 0; i < CHUNK_VOL; i++){ if (voxels[i].id != id){ if (id != -1) return false; @@ -34,14 +37,14 @@ bool Chunk::isEmpty(){ } void Chunk::updateHeights() { - for (int i = 0; i < CHUNK_VOL; i++) { + for (size_t i = 0; i < CHUNK_VOL; i++) { if (voxels[i].id != 0) { bottom = i / (CHUNK_D * CHUNK_W); break; } } - for (int i = CHUNK_VOL - 1; i > -1; i--) { + for (int i = CHUNK_VOL - 1; i >= 0; i--) { if (voxels[i].id != 0) { top = i / (CHUNK_D * CHUNK_W) + 1; break; @@ -51,7 +54,7 @@ void Chunk::updateHeights() { Chunk* Chunk::clone() const { Chunk* other = new Chunk(x,z); - for (int i = 0; i < CHUNK_VOL; i++) + for (size_t i = 0; i < CHUNK_VOL; i++) other->voxels[i] = voxels[i]; other->lightmap->set(lightmap); return other; @@ -59,32 +62,67 @@ Chunk* Chunk::clone() const { /** Current chunk format: - [voxel_ids...][voxel_states...]; + - byte-order: big-endian + - [don't panic!] first and second bytes are separated for RLE efficiency + + ```cpp + uint8_t voxel_id_first_byte[CHUNK_VOL]; + uint8_t voxel_id_second_byte[CHUNK_VOL]; + uint8_t voxel_states_first_byte[CHUNK_VOL]; + uint8_t voxel_states_second_byte[CHUNK_VOL]; + ``` + + Total size: (CHUNK_VOL * 4) bytes */ ubyte* Chunk::encode() const { ubyte* buffer = new ubyte[CHUNK_DATA_LEN]; for (size_t i = 0; i < CHUNK_VOL; i++) { - buffer[i] = voxels[i].id; - buffer[CHUNK_VOL + i] = voxels[i].states; + buffer[i] = voxels[i].id >> 8; + buffer[CHUNK_VOL+i] = voxels[i].id & 0xFF; + buffer[CHUNK_VOL*2 + i] = voxels[i].states >> 8; + buffer[CHUNK_VOL*3 + i] = voxels[i].states & 0xFF; } return buffer; } /** - @return true if all is fine -*/ + * @return true if all is fine + **/ bool Chunk::decode(ubyte* data) { for (size_t i = 0; i < CHUNK_VOL; i++) { voxel& vox = voxels[i]; - vox.id = data[i]; - vox.states = data[CHUNK_VOL + i]; + + ubyte bid1 = data[i]; + ubyte bid2 = data[CHUNK_VOL + i]; + + ubyte bst1 = data[CHUNK_VOL*2 + i]; + ubyte bst2 = data[CHUNK_VOL*3 + i]; + + vox.id = (blockid_t(bid1) << 8) | (blockid_t(bid2)); + vox.states = (blockstate_t(bst1) << 8) | (blockstate_t(bst2)); } return true; } -void Chunk::convert(ubyte* data, const ContentLUT* lut) { +/* + * Convert chunk voxels data from 16 bit to 32 bit + */ +void Chunk::fromOld(ubyte* data) { for (size_t i = 0; i < CHUNK_VOL; i++) { - blockid_t id = data[i]; - data[i] = lut->getBlockId(id); + data[i + CHUNK_VOL*3] = data[i + CHUNK_VOL]; + data[i + CHUNK_VOL] = data[i]; + data[i + CHUNK_VOL*2] = 0; + data[i] = 0; + } +} + +void Chunk::convert(ubyte* data, const ContentLUT* lut) { + for (size_t i = 0; i < CHUNK_VOL; i++) { + // see encode method to understand what the hell is going on here + blockid_t id = ((blockid_t(data[i]) << 8) | + blockid_t(data[CHUNK_VOL+i])); + blockid_t replacement = lut->getBlockId(id); + data[i] = replacement >> 8; + data[CHUNK_VOL+i] = replacement & 0xFF; } } diff --git a/src/voxels/Chunk.h b/src/voxels/Chunk.h index 8d915754..f92dc836 100644 --- a/src/voxels/Chunk.h +++ b/src/voxels/Chunk.h @@ -12,7 +12,7 @@ struct ChunkFlag{ static const int UNSAVED = 0x10; static const int LOADED_LIGHTS = 0x20; }; -#define CHUNK_DATA_LEN (CHUNK_VOL*2) +#define CHUNK_DATA_LEN (CHUNK_VOL*4) struct voxel; class Lightmap; @@ -78,6 +78,7 @@ public: ubyte* encode() const; bool decode(ubyte* data); + static void fromOld(ubyte* data); static void convert(ubyte* data, const ContentLUT* lut); };