Voxel changed from 16 bit to 32 bit + new lua functions
This commit is contained in:
parent
460b08a006
commit
06f66ffd71
@ -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);
|
||||
|
||||
|
||||
@ -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<ubyte[]> data (wfile->getChunk(gx, gz));
|
||||
int gz = cz + z * REGION_SIZE;
|
||||
std::unique_ptr<ubyte[]> 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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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<LevelScreen>(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<LevelScreen>(engine, level));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
#include "Chunk.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user