add VoxelStructure lua usertype

This commit is contained in:
MihailRis 2024-09-19 18:41:34 +03:00
parent bf9f81a98c
commit 88b0f8e3d6
17 changed files with 180 additions and 60 deletions

Binary file not shown.

View File

@ -1,3 +1,4 @@
-- use for engine development tests -- use for engine development tests
-- must be empty in release -- must be empty in release
-- must not be modified by content-packs -- must not be modified by content-packs
print(generation.load_structure("core:generators/default.files/tree0.vox"))

View File

@ -158,7 +158,7 @@ static int l_file_write_bytes(lua::State* L) {
fs::path path = resolve_path(lua::require_string(L, pathIndex)); fs::path path = resolve_path(lua::require_string(L, pathIndex));
if (auto bytearray = lua::touserdata<lua::Bytearray>(L, -1)) { if (auto bytearray = lua::touserdata<lua::LuaBytearray>(L, -1)) {
auto& bytes = bytearray->data(); auto& bytes = bytearray->data();
return lua::pushboolean( return lua::pushboolean(
L, files::write_bytes(path, bytes.data(), bytes.size()) L, files::write_bytes(path, bytes.data(), bytes.size())

View File

@ -5,6 +5,8 @@
#include "coders/binary_json.hpp" #include "coders/binary_json.hpp"
#include "world/Level.hpp" #include "world/Level.hpp"
#include "world/generator/VoxelStructure.hpp" #include "world/generator/VoxelStructure.hpp"
#include "engine.hpp"
#include "lua_custom_types.hpp"
using namespace scripting; using namespace scripting;
@ -25,6 +27,21 @@ static int l_save_structure(lua::State* L) {
return 0; return 0;
} }
static int l_load_structure(lua::State* L) {
auto paths = engine->getPaths();
auto filename = lua::require_string(L, 1);
auto path = paths->resolve(filename);
if (!std::filesystem::exists(path)) {
throw std::runtime_error("file "+path.u8string()+" does not exist");
}
auto map = files::read_binary_json(path);
auto structure = std::make_shared<VoxelStructure>();
structure->deserialize(map);
return lua::newuserdata<lua::LuaVoxelStructure>(L, std::move(structure));
}
const luaL_Reg generationlib[] = { const luaL_Reg generationlib[] = {
{"save_structure", lua::wrap<l_save_structure>}, {"save_structure", lua::wrap<l_save_structure>},
{"load_structure", lua::wrap<l_load_structure>},
{NULL, NULL}}; {NULL, NULL}};

View File

@ -4,9 +4,10 @@
#include <vector> #include <vector>
#include "lua_commons.hpp" #include "lua_commons.hpp"
#include "maths/Heightmap.hpp"
struct fnl_state; struct fnl_state;
class Heightmap;
class VoxelStructure;
namespace lua { namespace lua {
class Userdata { class Userdata {
@ -15,12 +16,12 @@ namespace lua {
virtual const std::string& getTypeName() const = 0; virtual const std::string& getTypeName() const = 0;
}; };
class Bytearray : public Userdata { class LuaBytearray : public Userdata {
std::vector<ubyte> buffer; std::vector<ubyte> buffer;
public: public:
Bytearray(size_t capacity); LuaBytearray(size_t capacity);
Bytearray(std::vector<ubyte> buffer); LuaBytearray(std::vector<ubyte> buffer);
virtual ~Bytearray(); virtual ~LuaBytearray();
const std::string& getTypeName() const override { const std::string& getTypeName() const override {
return TYPENAME; return TYPENAME;
@ -32,6 +33,7 @@ namespace lua {
static int createMetatable(lua::State*); static int createMetatable(lua::State*);
inline static std::string TYPENAME = "Bytearray"; inline static std::string TYPENAME = "Bytearray";
}; };
static_assert(!std::is_abstract<LuaBytearray>());
class LuaHeightmap : public Userdata { class LuaHeightmap : public Userdata {
std::shared_ptr<Heightmap> map; std::shared_ptr<Heightmap> map;
@ -41,21 +43,13 @@ namespace lua {
virtual ~LuaHeightmap(); virtual ~LuaHeightmap();
uint getWidth() const { uint getWidth() const;
return map->getWidth();
}
uint getHeight() const { uint getHeight() const;
return map->getHeight();
}
float* getValues() { float* getValues();
return map->getValues();
}
const float* getValues() const { const float* getValues() const;
return map->getValues();
}
const std::string& getTypeName() const override { const std::string& getTypeName() const override {
return TYPENAME; return TYPENAME;
@ -74,4 +68,25 @@ namespace lua {
static int createMetatable(lua::State*); static int createMetatable(lua::State*);
inline static std::string TYPENAME = "Heightmap"; inline static std::string TYPENAME = "Heightmap";
}; };
static_assert(!std::is_abstract<LuaHeightmap>());
class LuaVoxelStructure : public Userdata {
std::shared_ptr<VoxelStructure> structure;
public:
LuaVoxelStructure(std::shared_ptr<VoxelStructure> structure);
virtual ~LuaVoxelStructure();
std::shared_ptr<VoxelStructure> getStructure() const {
return structure;
}
const std::string& getTypeName() const override {
return TYPENAME;
}
static int createMetatable(lua::State*);
inline static std::string TYPENAME = "VoxelStructure";
};
static_assert(!std::is_abstract<LuaVoxelStructure>());
} }

View File

@ -96,8 +96,9 @@ void lua::initialize() {
initialize_libs_extends(L); initialize_libs_extends(L);
newusertype<Bytearray, Bytearray::createMetatable>(L, "Bytearray"); newusertype<LuaBytearray>(L);
newusertype<LuaHeightmap, LuaHeightmap::createMetatable>(L, "Heightmap"); newusertype<LuaHeightmap>(L);
newusertype<LuaVoxelStructure>(L);
} }
void lua::finalize() { void lua::finalize() {

View File

@ -248,10 +248,12 @@ namespace lua {
return 1; return 1;
} }
template <class T, lua_CFunction func> template <class T>
inline void newusertype(lua::State* L, const std::string& name) { inline std::enable_if_t<std::is_base_of_v<Userdata, T>>
newusertype(lua::State* L) {
const std::string& name = T::TYPENAME;
usertypeNames[typeid(T)] = name; usertypeNames[typeid(T)] = name;
func(L); T::createMetatable(L);
pushcfunction(L, userdata_destructor); pushcfunction(L, userdata_destructor);
setfield(L, "__gc"); setfield(L, "__gc");

View File

@ -1,23 +1,23 @@
#include "lua_custom_types.hpp" #include "../lua_custom_types.hpp"
#include <sstream> #include <sstream>
#include "lua_util.hpp" #include "../lua_util.hpp"
using namespace lua; using namespace lua;
Bytearray::Bytearray(size_t capacity) : buffer(capacity) { LuaBytearray::LuaBytearray(size_t capacity) : buffer(capacity) {
buffer.resize(capacity); buffer.resize(capacity);
} }
Bytearray::Bytearray(std::vector<ubyte> buffer) : buffer(std::move(buffer)) { LuaBytearray::LuaBytearray(std::vector<ubyte> buffer) : buffer(std::move(buffer)) {
} }
Bytearray::~Bytearray() { LuaBytearray::~LuaBytearray() {
} }
static int l_append(lua::State* L) { static int l_append(lua::State* L) {
if (auto buffer = touserdata<Bytearray>(L, 1)) { if (auto buffer = touserdata<LuaBytearray>(L, 1)) {
auto value = tointeger(L, 2); auto value = tointeger(L, 2);
buffer->data().push_back(static_cast<ubyte>(value)); buffer->data().push_back(static_cast<ubyte>(value));
} }
@ -25,7 +25,7 @@ static int l_append(lua::State* L) {
} }
static int l_insert(lua::State* L) { static int l_insert(lua::State* L) {
auto buffer = touserdata<Bytearray>(L, 1); auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) { if (buffer == nullptr) {
return 0; return 0;
} }
@ -40,7 +40,7 @@ static int l_insert(lua::State* L) {
} }
static int l_remove(lua::State* L) { static int l_remove(lua::State* L) {
auto buffer = touserdata<Bytearray>(L, 1); auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) { if (buffer == nullptr) {
return 0; return 0;
} }
@ -69,17 +69,17 @@ static int l_meta_meta_call(lua::State* L) {
buffer[i] = static_cast<ubyte>(tointeger(L, -1)); buffer[i] = static_cast<ubyte>(tointeger(L, -1));
pop(L); pop(L);
} }
return newuserdata<Bytearray>(L, std::move(buffer)); return newuserdata<LuaBytearray>(L, std::move(buffer));
} }
auto size = tointeger(L, 2); auto size = tointeger(L, 2);
if (size < 0) { if (size < 0) {
throw std::runtime_error("size can not be less than 0"); throw std::runtime_error("size can not be less than 0");
} }
return newuserdata<Bytearray>(L, static_cast<size_t>(size)); return newuserdata<LuaBytearray>(L, static_cast<size_t>(size));
} }
static int l_meta_index(lua::State* L) { static int l_meta_index(lua::State* L) {
auto buffer = touserdata<Bytearray>(L, 1); auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) { if (buffer == nullptr) {
return 0; return 0;
} }
@ -98,7 +98,7 @@ static int l_meta_index(lua::State* L) {
} }
static int l_meta_newindex(lua::State* L) { static int l_meta_newindex(lua::State* L) {
auto buffer = touserdata<Bytearray>(L, 1); auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) { if (buffer == nullptr) {
return 0; return 0;
} }
@ -116,14 +116,14 @@ static int l_meta_newindex(lua::State* L) {
} }
static int l_meta_len(lua::State* L) { static int l_meta_len(lua::State* L) {
if (auto buffer = touserdata<Bytearray>(L, 1)) { if (auto buffer = touserdata<LuaBytearray>(L, 1)) {
return pushinteger(L, buffer->data().size()); return pushinteger(L, buffer->data().size());
} }
return 0; return 0;
} }
static int l_meta_tostring(lua::State* L) { static int l_meta_tostring(lua::State* L) {
auto buffer = touserdata<Bytearray>(L, 1); auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) { if (buffer == nullptr) {
return 0; return 0;
} }
@ -147,8 +147,8 @@ static int l_meta_tostring(lua::State* L) {
} }
static int l_meta_add(lua::State* L) { static int l_meta_add(lua::State* L) {
auto bufferA = touserdata<Bytearray>(L, 1); auto bufferA = touserdata<LuaBytearray>(L, 1);
auto bufferB = touserdata<Bytearray>(L, 2); auto bufferB = touserdata<LuaBytearray>(L, 2);
if (bufferA == nullptr || bufferB == nullptr) { if (bufferA == nullptr || bufferB == nullptr) {
return 0; return 0;
} }
@ -159,10 +159,10 @@ static int l_meta_add(lua::State* L) {
ab.reserve(dataA.size() + dataB.size()); ab.reserve(dataA.size() + dataB.size());
ab.insert(ab.end(), dataA.begin(), dataA.end()); ab.insert(ab.end(), dataA.begin(), dataA.end());
ab.insert(ab.end(), dataB.begin(), dataB.end()); ab.insert(ab.end(), dataB.begin(), dataB.end());
return newuserdata<Bytearray>(L, std::move(ab)); return newuserdata<LuaBytearray>(L, std::move(ab));
} }
int Bytearray::createMetatable(lua::State* L) { int LuaBytearray::createMetatable(lua::State* L) {
createtable(L, 0, 6); createtable(L, 0, 6);
pushcfunction(L, lua::wrap<l_meta_index>); pushcfunction(L, lua::wrap<l_meta_index>);
setfield(L, "__index"); setfield(L, "__index");

View File

@ -1,4 +1,4 @@
#include "lua_custom_types.hpp" #include "../lua_custom_types.hpp"
#include <cstring> #include <cstring>
#include <sstream> #include <sstream>
@ -11,7 +11,8 @@
#include "coders/png.hpp" #include "coders/png.hpp"
#include "files/util.hpp" #include "files/util.hpp"
#include "graphics/core/ImageData.hpp" #include "graphics/core/ImageData.hpp"
#include "lua_util.hpp" #include "maths/Heightmap.hpp"
#include "../lua_util.hpp"
using namespace lua; using namespace lua;
@ -27,6 +28,22 @@ void LuaHeightmap::setSeed(int64_t seed) {
noise->seed = seed; noise->seed = seed;
} }
uint LuaHeightmap::getWidth() const {
return map->getWidth();
}
uint LuaHeightmap::getHeight() const {
return map->getHeight();
}
float* LuaHeightmap::getValues() {
return map->getValues();
}
const float* LuaHeightmap::getValues() const {
return map->getValues();
}
static int l_dump(lua::State* L) { static int l_dump(lua::State* L) {
if (auto heightmap = touserdata<LuaHeightmap>(L, 1)) { if (auto heightmap = touserdata<LuaHeightmap>(L, 1)) {
auto filename = require_string(L, 2); auto filename = require_string(L, 2);

View File

@ -0,0 +1,26 @@
#include "../lua_custom_types.hpp"
#include "../lua_util.hpp"
#include "world/generator/VoxelStructure.hpp"
#include "util/stringutil.hpp"
using namespace lua;
LuaVoxelStructure::LuaVoxelStructure(std::shared_ptr<VoxelStructure> structure)
: structure(std::move(structure)) {}
LuaVoxelStructure::~LuaVoxelStructure() {
}
static int l_meta_tostring(lua::State* L) {
return pushstring(L, "VoxelStructure(0x" + util::tohex(
reinterpret_cast<uint64_t>(topointer(L, 1)))+")");
}
int LuaVoxelStructure::createMetatable(lua::State* L) {
createtable(L, 0, 1);
pushcfunction(L, lua::wrap<l_meta_tostring>);
setfield(L, "__tostring");
return 1;
}

View File

@ -4,6 +4,7 @@
#include <string> #include <string>
#include "typedefs.hpp" #include "typedefs.hpp"
#include "maths/Heightmap.hpp"
enum class InterpolationType { enum class InterpolationType {
NEAREST, NEAREST,

View File

@ -312,13 +312,17 @@ std::string util::base64_encode(const ubyte* data, size_t size) {
return ss.str(); return ss.str();
} }
std::string util::mangleid(uint64_t value) { std::string util::tohex(uint64_t value) {
// todo: use base64
std::stringstream ss; std::stringstream ss;
ss << std::hex << value; ss << std::hex << value;
return ss.str(); return ss.str();
} }
std::string util::mangleid(uint64_t value) {
// todo: use base64
return tohex(value);
}
util::Buffer<ubyte> util::base64_decode(const char* str, size_t size) { util::Buffer<ubyte> util::base64_decode(const char* str, size_t size) {
util::Buffer<ubyte> bytes((size / 4) * 3); util::Buffer<ubyte> bytes((size / 4) * 3);
ubyte* dst = bytes.data(); ubyte* dst = bytes.data();

View File

@ -60,6 +60,8 @@ namespace util {
util::Buffer<ubyte> base64_decode(const char* str, size_t size); util::Buffer<ubyte> base64_decode(const char* str, size_t size);
util::Buffer<ubyte> base64_decode(const std::string& str); util::Buffer<ubyte> base64_decode(const std::string& str);
std::string tohex(uint64_t value);
std::string mangleid(uint64_t value); std::string mangleid(uint64_t value);
int replaceAll( int replaceAll(

View File

@ -66,11 +66,23 @@ dv::value VoxelStructure::serialize() const {
void VoxelStructure::deserialize(const dv::value& src) { void VoxelStructure::deserialize(const dv::value& src) {
size = glm::ivec3(); size = glm::ivec3();
dv::get_vec(src, "size", size); dv::get_vec(src, "size", size);
voxels.resize(size.x*size.y*size.z);
auto volume = size.x*size.y*size.z;
voxels.resize(volume);
const auto& voxelsArr = src["voxels"]; const auto& voxelsArr = src["voxels"];
for (size_t i = 0; i < size.x*size.y*size.z; i++) { for (size_t i = 0; i < volume; i++) {
voxels[i].id = voxelsArr[i * 2].asInteger(); voxels[i].id = voxelsArr[i * 2].asInteger();
voxels[i].state = int2blockstate(voxelsArr[i * 2 + 1].asInteger()); voxels[i].state = int2blockstate(voxelsArr[i * 2 + 1].asInteger());
} }
} }
void VoxelStructure::prepare(const Content& content) {
auto volume = size.x*size.y*size.z;
voxelsRuntime.resize(volume);
for (size_t i = 0; i < volume; i++) {
const auto& name = blockNames[voxels[i].id];
voxelsRuntime[i].id = content.blocks.require(name).rt.id;
voxelsRuntime[i].state = voxels[i].state;
}
}

View File

@ -11,7 +11,7 @@ inline constexpr int STRUCTURE_FORMAT_VERSION = 1;
class Level; class Level;
class Content; class Content;
struct VoxelStructure : public Serializable { class VoxelStructure : public Serializable {
glm::ivec3 size; glm::ivec3 size;
/// @brief Structure voxels indexed different to world content /// @brief Structure voxels indexed different to world content
@ -19,6 +19,9 @@ struct VoxelStructure : public Serializable {
/// @brief Block names are used for indexing /// @brief Block names are used for indexing
std::vector<std::string> blockNames; std::vector<std::string> blockNames;
/// @brief Structure voxels built on prepare(...) call
std::vector<voxel> voxelsRuntime;
public:
VoxelStructure() : size() {} VoxelStructure() : size() {}
VoxelStructure( VoxelStructure(
@ -33,6 +36,18 @@ struct VoxelStructure : public Serializable {
dv::value serialize() const override; dv::value serialize() const override;
void deserialize(const dv::value& src) override; void deserialize(const dv::value& src) override;
/// @brief Build runtime voxel indices
/// @param content world content
void prepare(const Content& content);
static std::unique_ptr<VoxelStructure> create( static std::unique_ptr<VoxelStructure> create(
Level* level, const glm::ivec3& a, const glm::ivec3& b, bool entities); Level* level, const glm::ivec3& a, const glm::ivec3& b, bool entities);
const glm::ivec3& getSize() const {
return size;
}
const std::vector<voxel>& getRuntimeVoxels() {
return voxelsRuntime;
}
}; };

View File

@ -7,7 +7,8 @@
#include "content/Content.hpp" #include "content/Content.hpp"
#include "voxels/Block.hpp" #include "voxels/Block.hpp"
#include "voxels/Chunk.hpp" #include "voxels/Chunk.hpp"
#include "world/generator/GeneratorDef.hpp" #include "GeneratorDef.hpp"
#include "VoxelStructure.hpp"
#include "util/timeutil.hpp" #include "util/timeutil.hpp"
#include "debug/Logger.hpp" #include "debug/Logger.hpp"
@ -47,6 +48,8 @@ WorldGenerator::WorldGenerator(
}); });
} }
WorldGenerator::~WorldGenerator() {}
static inline void generate_pole( static inline void generate_pole(
const BlocksLayers& layers, const BlocksLayers& layers,
int top, int bottom, int top, int bottom,
@ -119,8 +122,8 @@ std::unique_ptr<ChunkPrototype> WorldGenerator::generatePrototype(
} }
return std::make_unique<ChunkPrototype>( return std::make_unique<ChunkPrototype>(
ChunkPrototypeLevel::BIOMES, ChunkPrototypeLevel::BIOMES,
nullptr, std::move(chunkBiomes),
std::move(chunkBiomes)); nullptr);
} }
void WorldGenerator::generateHeightmap( void WorldGenerator::generateHeightmap(

View File

@ -14,6 +14,7 @@ class Content;
struct GeneratorDef; struct GeneratorDef;
class Heightmap; class Heightmap;
struct Biome; struct Biome;
class VoxelStructure;
enum class ChunkPrototypeLevel { enum class ChunkPrototypeLevel {
BIOMES, HEIGHTMAP BIOMES, HEIGHTMAP
@ -22,18 +23,19 @@ enum class ChunkPrototypeLevel {
struct ChunkPrototype { struct ChunkPrototype {
ChunkPrototypeLevel level; ChunkPrototypeLevel level;
/// @brief chunk heightmap
std::shared_ptr<Heightmap> heightmap;
/// @brief chunk biomes matrix /// @brief chunk biomes matrix
std::vector<const Biome*> biomes; std::vector<const Biome*> biomes;
/// @brief chunk heightmap
std::shared_ptr<Heightmap> heightmap;
ChunkPrototype( ChunkPrototype(
ChunkPrototypeLevel level, ChunkPrototypeLevel level,
std::shared_ptr<Heightmap> heightmap, std::vector<const Biome*> biomes,
std::vector<const Biome*> biomes std::shared_ptr<Heightmap> heightmap
) : level(level), ) : level(level),
heightmap(std::move(heightmap)), biomes(std::move(biomes)),
biomes(std::move(biomes)) {}; heightmap(std::move(heightmap)) {};
}; };
/// @brief High-level world generation controller /// @brief High-level world generation controller
@ -49,6 +51,8 @@ class WorldGenerator {
/// @brief Chunk prototypes loading surround map /// @brief Chunk prototypes loading surround map
SurroundMap surroundMap; SurroundMap surroundMap;
std::vector<std::unique_ptr<VoxelStructure>> structures;
/// @brief Generate chunk prototype (see ChunkPrototype) /// @brief Generate chunk prototype (see ChunkPrototype)
/// @param x chunk position X divided by CHUNK_W /// @param x chunk position X divided by CHUNK_W
/// @param z chunk position Y divided by CHUNK_D /// @param z chunk position Y divided by CHUNK_D
@ -61,15 +65,15 @@ public:
const Content* content, const Content* content,
uint64_t seed uint64_t seed
); );
virtual ~WorldGenerator() = default; ~WorldGenerator();
virtual void update(int centerX, int centerY, int loadDistance); void update(int centerX, int centerY, int loadDistance);
/// @brief Generate complete chunk voxels /// @brief Generate complete chunk voxels
/// @param voxels destinatiopn chunk voxels buffer /// @param voxels destinatiopn chunk voxels buffer
/// @param x chunk position X divided by CHUNK_W /// @param x chunk position X divided by CHUNK_W
/// @param z chunk position Y divided by CHUNK_D /// @param z chunk position Y divided by CHUNK_D
virtual void generate(voxel* voxels, int x, int z); void generate(voxel* voxels, int x, int z);
inline static std::string DEFAULT = "core:default"; inline static std::string DEFAULT = "core:default";
}; };