From f0270d33914627c6e86222b3c5b664f227bc046b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 5 Jul 2024 05:16:31 +0300 Subject: [PATCH] feat: saving entities (WIP) --- res/content/base/entities/drop.json | 4 +- res/content/base/scripts/hud.lua | 2 +- res/modules/internal/stdcomp.lua | 1 + src/assets/assetload_funcs.cpp | 2 +- src/coders/binary_json.cpp | 7 +++ src/coders/binary_json.hpp | 7 +-- src/content/ContentLoader.cpp | 4 +- src/data/dynamic.hpp | 27 ++-------- src/data/dynamic_fwd.hpp | 31 +++++++++++ src/data/dynamic_util.hpp | 30 +++++++++++ src/files/WorldRegions.cpp | 49 +++++++++++------- src/files/WorldRegions.hpp | 5 +- src/logic/scripting/scripting.cpp | 11 ++++ src/logic/scripting/scripting.hpp | 1 + src/maths/aabb.hpp | 2 +- src/objects/Entities.cpp | 73 ++++++++++++++++++++++++-- src/objects/Entities.hpp | 4 ++ src/objects/EntityDef.hpp | 12 ++++- src/objects/rigging.cpp | 9 ++-- src/objects/rigging.hpp | 11 +++- src/voxels/Chunks.cpp | 79 +++++++++++++++++++---------- src/voxels/Chunks.hpp | 9 ++-- src/world/Level.cpp | 2 +- src/world/World.cpp | 9 +--- 24 files changed, 290 insertions(+), 101 deletions(-) create mode 100644 src/data/dynamic_fwd.hpp create mode 100644 src/data/dynamic_util.hpp diff --git a/res/content/base/entities/drop.json b/res/content/base/entities/drop.json index 0be03b85..9bf3dac7 100644 --- a/res/content/base/entities/drop.json +++ b/res/content/base/entities/drop.json @@ -6,5 +6,7 @@ "triggers": [ ["aabb", -0.2, -0.2, -0.2, 0.2, 0.2, 0.2], ["radius", 1.6] - ] + ], + "save-rig-textures": true, + "save-rig-pose": true } diff --git a/res/content/base/scripts/hud.lua b/res/content/base/scripts/hud.lua index 0505d941..010fec67 100644 --- a/res/content/base/scripts/hud.lua +++ b/res/content/base/scripts/hud.lua @@ -19,6 +19,6 @@ function on_hud_open() count=1 }}) local velocity = vec3.add(throw_force, vec3.add(pvel, DROP_INIT_VEL)) - drop.get_component("rigidbody"):set_vel(velocity) + drop.rigidbody:set_vel(velocity) end) end diff --git a/res/modules/internal/stdcomp.lua b/res/modules/internal/stdcomp.lua index 75e72306..de9f9a09 100644 --- a/res/modules/internal/stdcomp.lua +++ b/res/modules/internal/stdcomp.lua @@ -74,6 +74,7 @@ return { if callback then local result, err = pcall(callback) if err then + --// TODO: replace with error logging print(err) end end diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index 690422cb..bb5c0b6d 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -238,7 +238,7 @@ assetload::postfunc assetload::rig( auto path = paths->find(file+".json"); auto text = files::read_string(path); try { - auto rig = rigging::RigConfig::parse(text, path.u8string()).release(); + auto rig = rigging::RigConfig::parse(text, path.u8string(), name).release(); return [=](Assets* assets) { // TODO: add models loading assets->store(std::unique_ptr(rig), name); diff --git a/src/coders/binary_json.cpp b/src/coders/binary_json.cpp index 8c3236e8..f1f87af6 100644 --- a/src/coders/binary_json.cpp +++ b/src/coders/binary_json.cpp @@ -83,6 +83,13 @@ std::vector json::to_binary(const Map* obj, bool compress) { return builder.build(); } +std::vector json::to_binary(const Value& value, bool compress) { + if (auto map = std::get_if(&value)) { + return to_binary(map->get(), compress); + } + throw std::runtime_error("map is only supported as the root element"); +} + static Value value_from_binary(ByteReader& reader) { ubyte typecode = reader.get(); switch (typecode) { diff --git a/src/coders/binary_json.hpp b/src/coders/binary_json.hpp index c79a7d4d..0cb9945b 100644 --- a/src/coders/binary_json.hpp +++ b/src/coders/binary_json.hpp @@ -1,7 +1,7 @@ #ifndef CODERS_BINARY_JSON_HPP_ #define CODERS_BINARY_JSON_HPP_ -#include "../typedefs.hpp" +#include "../data/dynamic_fwd.hpp" #include #include @@ -26,8 +26,9 @@ namespace json { inline constexpr int BJSON_TYPE_NULL = 0xC; inline constexpr int BJSON_TYPE_CDOCUMENT = 0x1F; - extern std::vector to_binary(const dynamic::Map* obj, bool compress=false); - extern std::shared_ptr from_binary(const ubyte* src, size_t size); + std::vector to_binary(const dynamic::Map* obj, bool compress=false); + std::vector to_binary(const dynamic::Value& obj, bool compress=false); + std::shared_ptr from_binary(const ubyte* src, size_t size); } #endif // CODERS_BINARY_JSON_HPP_ diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index ab23be7c..d62277c7 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -337,7 +337,9 @@ void ContentLoader::loadEntity(EntityDef& def, const std::string& name, const fs } } } - std::cout << "loading entity " << name << " from " << file.u8string() << std::endl; + root->flag("save", def.save.enabled); + root->flag("save-rig-pose", def.save.rig.pose); + root->flag("save-rig-textures", def.save.rig.textures); } void ContentLoader::loadEntity(EntityDef& def, const std::string& full, const std::string& name) { diff --git a/src/data/dynamic.hpp b/src/data/dynamic.hpp index 5dc5dbed..1153697f 100644 --- a/src/data/dynamic.hpp +++ b/src/data/dynamic.hpp @@ -1,42 +1,22 @@ #ifndef DATA_DYNAMIC_HPP_ #define DATA_DYNAMIC_HPP_ -#include "../typedefs.hpp" +#include "dynamic_fwd.hpp" #include #include #include #include #include -#include + #include #include namespace dynamic { - class Map; - class List; - enum class Type { none=0, map, list, string, number, boolean, integer }; - using Map_sptr = std::shared_ptr; - using List_sptr = std::shared_ptr; - - struct none {}; - - inline constexpr none NONE = {}; - - using Value = std::variant< - none, - Map_sptr, - List_sptr, - std::string, - number_t, - bool, - integer_t - >; - const std::string& type_name(const Value& value); List_sptr create_list(std::initializer_list values={}); Map_sptr create_map(std::initializer_list> entries={}); @@ -153,6 +133,9 @@ namespace dynamic { Map& put(std::string key, int64_t value) { return put(key, Value(static_cast(value))); } + Map& put(std::string key, uint64_t value) { + return put(key, Value(static_cast(value))); + } Map& put(std::string key, float value) { return put(key, Value(static_cast(value))); } diff --git a/src/data/dynamic_fwd.hpp b/src/data/dynamic_fwd.hpp new file mode 100644 index 00000000..ee5f552f --- /dev/null +++ b/src/data/dynamic_fwd.hpp @@ -0,0 +1,31 @@ +#ifndef DATA_DYNAMIC_FWD_HPP_ +#define DATA_DYNAMIC_FWD_HPP_ + +#include "../typedefs.hpp" + +#include +#include + +namespace dynamic { + class Map; + class List; + + using Map_sptr = std::shared_ptr; + using List_sptr = std::shared_ptr; + + struct none {}; + + inline constexpr none NONE = {}; + + using Value = std::variant< + none, + Map_sptr, + List_sptr, + std::string, + number_t, + bool, + integer_t + >; +} + +#endif // DATA_DYNAMIC_FWD_HPP_ diff --git a/src/data/dynamic_util.hpp b/src/data/dynamic_util.hpp new file mode 100644 index 00000000..3a10a123 --- /dev/null +++ b/src/data/dynamic_util.hpp @@ -0,0 +1,30 @@ +#ifndef DATA_DYNAMIC_UTIL_HPP_ +#define DATA_DYNAMIC_UTIL_HPP_ + +#include "dynamic.hpp" + +#include + +namespace dynamic { + template + inline dynamic::List_sptr to_value(glm::vec vec) { + auto list = dynamic::create_list(); + for (size_t i = 0; i < n; i++) { + list->put(vec[i]); + } + return list; + } + + template + inline dynamic::List_sptr to_value(glm::mat mat) { + auto list = dynamic::create_list(); + for (size_t i = 0; i < n; i++) { + for (size_t j = 0; j < m; j++) { + list->put(mat[i][j]); + } + } + return list; + } +} + +#endif // DATA_DYNAMIC_UTIL_HPP_ diff --git a/src/files/WorldRegions.cpp b/src/files/WorldRegions.cpp index 44aa8449..9bec7921 100644 --- a/src/files/WorldRegions.cpp +++ b/src/files/WorldRegions.cpp @@ -9,6 +9,7 @@ #include #include +#include #define REGION_FORMAT_MAGIC ".VOXREG" @@ -36,17 +37,17 @@ std::unique_ptr regfile::read(int index, uint32_t& length) { uint32_t offset; file.seekg(table_offset + index * 4); - file.read((char*)(&offset), 4); - offset = dataio::read_int32_big((const ubyte*)(&offset), 0); + file.read(reinterpret_cast(&offset), 4); + offset = dataio::read_int32_big(reinterpret_cast(&offset), 0); if (offset == 0){ return nullptr; } file.seekg(offset); - file.read((char*)(&offset), 4); - length = dataio::read_int32_big((const ubyte*)(&offset), 0); + file.read(reinterpret_cast(&offset), 4); + length = dataio::read_int32_big(reinterpret_cast(&offset), 0); auto data = std::make_unique(length); - file.read((char*)data.get(), length); + file.read(reinterpret_cast(data.get()), length); return data; } @@ -88,12 +89,13 @@ uint WorldRegion::getChunkDataSize(uint x, uint z) { } WorldRegions::WorldRegions(const fs::path& directory) : directory(directory) { - for (uint i = 0; i < sizeof(layers)/sizeof(RegionsLayer); i++) { + for (size_t i = 0; i < sizeof(layers)/sizeof(RegionsLayer); i++) { layers[i].layer = i; } layers[REGION_LAYER_VOXELS].folder = directory/fs::path("regions"); layers[REGION_LAYER_LIGHTS].folder = directory/fs::path("lights"); layers[REGION_LAYER_INVENTORIES].folder = directory/fs::path("inventories"); + layers[REGION_LAYER_ENTITIES].folder = directory/fs::path("entities"); } WorldRegions::~WorldRegions() { @@ -123,7 +125,7 @@ WorldRegion* WorldRegions::getOrCreateRegion(int x, int z, int layer) { std::unique_ptr WorldRegions::compress(const ubyte* src, size_t srclen, size_t& len) { auto buffer = bufferPool.get(); - ubyte* bytes = buffer.get(); + auto bytes = buffer.get(); len = extrle::encode(src, srclen, bytes); auto data = std::make_unique(len); @@ -150,7 +152,7 @@ inline void calc_reg_coords( std::unique_ptr WorldRegions::readChunkData( int x, int z, uint32_t& length, regfile* rfile -){ +) { int regionX, regionZ, localX, localZ; calc_reg_coords(x, z, regionX, regionZ, localX, localZ); int chunkIndex = localZ * REGION_SIZE + localX; @@ -171,10 +173,7 @@ void WorldRegions::fetchChunks(WorldRegion* region, int x, int z, regfile* file) } } -ubyte* WorldRegions::getData( - int x, int z, int layer, - uint32_t& size -) { +ubyte* WorldRegions::getData(int x, int z, int layer, uint32_t& size) { if (generatorTestMode) { return nullptr; } @@ -301,7 +300,7 @@ void WorldRegions::writeRegion(int x, int z, int layer, WorldRegion* entry){ offset += 4 + compressedSize; file.write(intbuf, 4); - file.write((const char*)chunk, compressedSize); + file.write(reinterpret_cast(chunk), compressedSize); } } for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) { @@ -313,8 +312,9 @@ void WorldRegions::writeRegion(int x, int z, int layer, WorldRegion* entry){ void WorldRegions::writeRegions(int layer) { for (auto& it : layers[layer].regions){ WorldRegion* region = it.second.get(); - if (region->getChunks() == nullptr || !region->isUnsaved()) + if (region->getChunks() == nullptr || !region->isUnsaved()) { continue; + } glm::ivec2 key = it.first; writeRegion(key[0], key[1], layer, region); } @@ -354,7 +354,7 @@ static std::unique_ptr write_inventories(Chunk* chunk, uint& datasize) } /// @brief Store chunk data (voxels and lights) in region (existing or new) -void WorldRegions::put(Chunk* chunk){ +void WorldRegions::put(Chunk* chunk, std::vector entitiesData){ assert(chunk != nullptr); if (!chunk->flags.lighted) { return; @@ -376,12 +376,21 @@ void WorldRegions::put(Chunk* chunk){ chunk->lightmap.encode(), LIGHTMAP_DATA_LEN, true); } // Writing block inventories - if (!chunk->inventories.empty()){ + if (!chunk->inventories.empty()) { uint datasize; auto data = write_inventories(chunk, datasize); put(chunk->x, chunk->z, REGION_LAYER_INVENTORIES, std::move(data), datasize, false); } + // Writing entities + if (!entitiesData.empty()) { + auto data = std::make_unique(entitiesData.size()); + for (size_t i = 0; i < entitiesData.size(); i++) { + data[i] = entitiesData[i]; + } + put(chunk->x, chunk->z, REGION_LAYER_ENTITIES, + std::move(data), entitiesData.size(), false); + } } std::unique_ptr WorldRegions::getChunk(int x, int z){ @@ -398,8 +407,9 @@ std::unique_ptr WorldRegions::getChunk(int x, int z){ std::unique_ptr WorldRegions::getLights(int x, int z) { uint32_t size; auto* bytes = getData(x, z, REGION_LAYER_LIGHTS, size); - if (bytes == nullptr) + if (bytes == nullptr) { return nullptr; + } auto data = decompress(bytes, size, LIGHTMAP_DATA_LEN); return Lightmap::decode(data.get()); } @@ -412,7 +422,7 @@ chunk_inventories_map WorldRegions::fetchInventories(int x, int z) { return meta; } ByteReader reader(data, bytesSize); - int count = reader.getInt32(); + auto count = reader.getInt32(); for (int i = 0; i < count; i++) { uint index = reader.getInt32(); uint size = reader.getInt32(); @@ -439,8 +449,9 @@ void WorldRegions::processRegionVoxels(int x, int z, const regionproc& func) { int gz = cz + z * REGION_SIZE; uint32_t length; auto data = readChunkData(gx, gz, length, regfile.get()); - if (data == nullptr) + if (data == nullptr) { continue; + } data = decompress(data.get(), length, CHUNK_DATA_LEN); if (func(data.get())) { put(gx, gz, REGION_LAYER_VOXELS, std::move(data), CHUNK_DATA_LEN, true); diff --git a/src/files/WorldRegions.hpp b/src/files/WorldRegions.hpp index 7ad16037..f96477de 100644 --- a/src/files/WorldRegions.hpp +++ b/src/files/WorldRegions.hpp @@ -24,6 +24,7 @@ inline constexpr uint REGION_HEADER_SIZE = 10; inline constexpr uint REGION_LAYER_VOXELS = 0; inline constexpr uint REGION_LAYER_LIGHTS = 1; inline constexpr uint REGION_LAYER_INVENTORIES = 2; +inline constexpr uint REGION_LAYER_ENTITIES = 3; inline constexpr uint REGION_SIZE_BIT = 5; inline constexpr uint REGION_SIZE = (1 << (REGION_SIZE_BIT)); @@ -119,7 +120,7 @@ class WorldRegions { std::unordered_map> openRegFiles; std::mutex regFilesMutex; std::condition_variable regFilesCv; - RegionsLayer layers[3] {}; + RegionsLayer layers[4] {}; util::BufferPool bufferPool { std::max(CHUNK_DATA_LEN, LIGHTMAP_DATA_LEN) * 2 }; @@ -170,7 +171,7 @@ public: ~WorldRegions(); /// @brief Put all chunk data to regions - void put(Chunk* chunk); + void put(Chunk* chunk, std::vector entitiesData); /// @brief Store data in specified region /// @param x chunk.x diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index b6d0ec9e..9d9a8146 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -295,6 +295,7 @@ void scripting::on_entity_spawn( funcsset.on_despawn = lua::hasfield(L, "on_despawn"); funcsset.on_trigger_enter = lua::hasfield(L, "on_trigger_enter"); funcsset.on_trigger_exit = lua::hasfield(L, "on_trigger_exit"); + funcsset.on_save = lua::hasfield(L, "on_save"); lua::pop(L, 2); component->env = compenv; @@ -355,6 +356,16 @@ bool scripting::on_entity_fall(const Entity& entity) { return true; } +bool scripting::on_entity_save(const Entity& entity) { + const auto& script = entity.getScripting(); + for (auto& component : script.components) { + if (component->funcsset.on_save) { + process_entity_callback(component->env, "on_save", nullptr); + } + } + return true; +} + void scripting::on_trigger_enter(const Entity& entity, size_t index, entityid_t oid) { const auto& script = entity.getScripting(); for (auto& component : script.components) { diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index 9bdf7367..cb063acc 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -85,6 +85,7 @@ namespace scripting { bool on_entity_despawn(const EntityDef& def, const Entity& entity); bool on_entity_grounded(const Entity& entity, float force); bool on_entity_fall(const Entity& entity); + bool on_entity_save(const Entity& entity); void on_entities_update(); void on_trigger_enter(const Entity& entity, size_t index, entityid_t oid); void on_trigger_exit(const Entity& entity, size_t index, entityid_t oid); diff --git a/src/maths/aabb.hpp b/src/maths/aabb.hpp index 38c829ce..67a3f05d 100644 --- a/src/maths/aabb.hpp +++ b/src/maths/aabb.hpp @@ -13,7 +13,7 @@ struct AABB { AABB(glm::vec3 size) : a(0.0f), b(size) { } - AABB(glm::vec3 pos, glm::vec3 size) : a(pos), b(size) { + AABB(glm::vec3 a, glm::vec3 b) : a(a), b(b) { } /// @brief Get AABB point with minimal x,y,z diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 3e3f9afe..2b18a46b 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -1,5 +1,6 @@ #include "Entities.hpp" +#include "../data/dynamic_util.hpp" #include "../assets/Assets.hpp" #include "../world/Level.hpp" #include "../physics/Hitbox.hpp" @@ -78,8 +79,7 @@ entityid_t Entities::spawn( body.triggers[i] = Trigger { true, TriggerType::AABB, i, id, params, params, {}, {}, create_trigger_callback(this), - create_trigger_callback(this) - }; + create_trigger_callback(this)}; } for (auto& [i, radius] : def.radialTriggers) { TriggerParams params {}; @@ -87,11 +87,11 @@ entityid_t Entities::spawn( body.triggers[i] = Trigger { true, TriggerType::RADIUS, i, id, params, params, {}, {}, create_trigger_callback(this), - create_trigger_callback(this) - }; + create_trigger_callback(this)}; } auto& scripting = registry.emplace(entity); entities[id] = entity; + uids[entity] = id; registry.emplace(entity, rig->instance()); for (auto& componentName : def.components) { auto component = std::make_unique( @@ -112,11 +112,59 @@ void Entities::despawn(entityid_t id) { } } +dynamic::Value Entities::serialize(const Entity& entity) { + auto root = dynamic::create_map(); + auto& eid = entity.getID(); + auto& def = eid.def; + root->put("def", def.name); + root->put("uid", eid.uid); + { + auto& transform = entity.getTransform(); + auto& tsfmap = root->putMap("transform"); + tsfmap.put("pos", dynamic::to_value(transform.pos)); + if (transform.size != glm::vec3(1.0f)) { + tsfmap.put("size", dynamic::to_value(transform.size)); + } + tsfmap.put("rot", dynamic::to_value(transform.rot)); + } + { + auto& rigidbody = entity.getRigidbody(); + auto& bodymap = root->putMap("rigidbody"); + if (!rigidbody.enabled) { + bodymap.put("enabled", rigidbody.enabled); + } + bodymap.put("vel", dynamic::to_value(rigidbody.hitbox.velocity)); + bodymap.put("damping", rigidbody.hitbox.linearDamping); + } + auto& rig = entity.getModeltree(); + if (rig.config->getName() != def.rigName) { + root->put("rig", rig.config->getName()); + } + + if (def.save.rig.pose || def.save.rig.textures) { + auto& rigmap = root->putMap("rig"); + if (def.save.rig.textures) { + auto& map = rigmap.putMap("textures"); + for (auto& entry : rig.textures) { + map.put(entry.first, entry.second); + } + } + if (def.save.rig.pose) { + auto& list = rigmap.putList("pose"); + for (auto& mat : rig.pose.matrices) { + list.put(dynamic::to_value(mat)); + } + } + } + return root; +} + void Entities::clean() { for (auto it = entities.begin(); it != entities.end();) { if (!registry.get(it->second).destroyFlag) { ++it; } else { + uids.erase(it->second); registry.destroy(it->second); it = entities.erase(it); } @@ -249,3 +297,20 @@ void Entities::render(Assets* assets, ModelBatch& batch, const Frustum& frustum) } } } + +std::vector Entities::getAllInside(AABB aabb) { + std::vector collected; + auto view = registry.view(); + for (auto [entity, transform] : view.each()) { + if (aabb.contains(transform.pos)) { + const auto& found = uids.find(entity); + if (found == uids.end()) { + continue; + } + if (auto entity = get(found->second)) { + collected.push_back(*entity); + } + } + } + return collected; +} diff --git a/src/objects/Entities.hpp b/src/objects/Entities.hpp index 1aca686b..3c0c3c95 100644 --- a/src/objects/Entities.hpp +++ b/src/objects/Entities.hpp @@ -21,6 +21,7 @@ struct entity_funcs_set { bool on_fall : 1; bool on_trigger_enter : 1; bool on_trigger_exit : 1; + bool on_save : 1; }; struct EntityDef; @@ -154,6 +155,7 @@ class Entities { entt::registry registry; Level* level; std::unordered_map entities; + std::unordered_map uids; entityid_t nextID = 1; void preparePhysics(); @@ -181,7 +183,9 @@ public: return std::nullopt; } + std::vector getAllInside(AABB aabb); void despawn(entityid_t id); + dynamic::Value serialize(const Entity& entity); inline size_t size() const { return entities.size(); diff --git a/src/objects/EntityDef.hpp b/src/objects/EntityDef.hpp index d8b87b87..1abfca14 100644 --- a/src/objects/EntityDef.hpp +++ b/src/objects/EntityDef.hpp @@ -15,14 +15,22 @@ namespace rigging { struct EntityDef { /// @brief Entity string id (with prefix included) std::string const name; - + std::vector components; glm::vec3 hitbox {0.5f}; std::vector> boxTriggers {}; std::vector> radialTriggers {}; std::string rigName = name.substr(name.find(":")+1); - + + struct { + bool enabled = true; + struct { + bool textures = false; + bool pose = false; + } rig; + } save {}; + struct { entityid_t id; rigging::RigConfig* rig; diff --git a/src/objects/rigging.cpp b/src/objects/rigging.cpp index baadc8bf..275577aa 100644 --- a/src/objects/rigging.cpp +++ b/src/objects/rigging.cpp @@ -30,8 +30,8 @@ static void get_all_nodes(std::vector& nodes, RigNode* node) { } } -RigConfig::RigConfig(std::unique_ptr root, size_t nodesCount) - : root(std::move(root)), nodes(nodesCount) { +RigConfig::RigConfig(const std::string& name, std::unique_ptr root, size_t nodesCount) + : name(name), root(std::move(root)), nodes(nodesCount) { get_all_nodes(nodes, this->root.get()); } @@ -106,7 +106,8 @@ static std::tuple> read_node( std::unique_ptr RigConfig::parse( std::string_view src, - std::string_view file + std::string_view file, + std::string_view name ) { auto root = json::parse(file, src); auto rootNodeMap = root->map("root"); @@ -114,5 +115,5 @@ std::unique_ptr RigConfig::parse( throw std::runtime_error("missing 'root' element"); } auto [count, rootNode] = read_node(rootNodeMap, 0); - return std::make_unique(std::move(rootNode), count); + return std::make_unique(std::string(name), std::move(rootNode), count); } diff --git a/src/objects/rigging.hpp b/src/objects/rigging.hpp index 797ec20e..6ab1b8f3 100644 --- a/src/objects/rigging.hpp +++ b/src/objects/rigging.hpp @@ -68,6 +68,7 @@ namespace rigging { }; class RigConfig { + std::string name; std::unique_ptr root; std::unordered_map indices; @@ -85,7 +86,8 @@ namespace rigging { RigNode* node, glm::mat4 matrix) const; public: - RigConfig(std::unique_ptr root, size_t nodesCount); + RigConfig(const std::string& name, std::unique_ptr root, + size_t nodesCount); void update(Rig& rig, glm::mat4 matrix) const; void setup(const Assets* assets, RigNode* node=nullptr) const; @@ -103,12 +105,17 @@ namespace rigging { static std::unique_ptr parse( std::string_view src, - std::string_view file + std::string_view file, + std::string_view name ); const std::vector& getNodes() const { return nodes; } + + const std::string& getName() const { + return name; + } }; }; diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 9edc4e16..2b02b974 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -7,10 +7,14 @@ #include "../lighting/Lightmap.hpp" #include "../files/WorldFiles.hpp" #include "../world/LevelEvents.hpp" +#include "../world/Level.hpp" +#include "../objects/Entities.hpp" #include "../graphics/core/Mesh.hpp" #include "../maths/voxmaths.hpp" #include "../maths/aabb.hpp" #include "../maths/rays.hpp" +#include "../coders/byte_utils.hpp" +#include "../coders/json.hpp" #include #include @@ -18,17 +22,16 @@ #include Chunks::Chunks( - uint32_t w, uint32_t d, - int32_t ox, int32_t oz, - WorldFiles* wfile, - LevelEvents* events, - const Content* content -) : indices(content->getIndices()), + uint32_t w, uint32_t d, + int32_t ox, int32_t oz, + WorldFiles* wfile, + Level* level +) : level(level), + indices(level->content->getIndices()), chunks(w*d), chunksSecond(w*d), w(w), d(d), ox(ox), oz(oz), - worldFiles(wfile), - events(events) + worldFiles(wfile) { volume = static_cast(w)*static_cast(d); chunksCount = 0; @@ -424,13 +427,13 @@ voxel* Chunks::rayCast( int steppedIndex = -1; - while (t <= maxDist){ + while (t <= maxDist) { voxel* voxel = get(ix, iy, iz); if (voxel == nullptr){ return nullptr; } const auto def = indices->blocks.get(voxel->id); - if (def->selectable){ + if (def->selectable) { end.x = px + t * dx; end.y = py + t * dy; end.z = pz + t * dz; @@ -517,9 +520,9 @@ voxel* Chunks::rayCast( } glm::vec3 Chunks::rayCastToObstacle(glm::vec3 start, glm::vec3 dir, float maxDist) { - float px = start.x; - float py = start.y; - float pz = start.z; + const float px = start.x; + const float py = start.y; + const float pz = start.z; float dx = dir.x; float dy = dir.y; @@ -620,7 +623,6 @@ void Chunks::setCenter(int32_t x, int32_t z) { } void Chunks::translate(int32_t dx, int32_t dz) { - auto& regions = worldFiles->getRegions(); for (uint i = 0; i < volume; i++){ chunksSecond[i] = nullptr; } @@ -631,9 +633,9 @@ void Chunks::translate(int32_t dx, int32_t dz) { int nz = z - dz; if (chunk == nullptr) continue; - if (nx < 0 || nz < 0 || nx >= int(w) || nz >= int(d)) { - events->trigger(EVT_CHUNK_HIDDEN, chunk.get()); - regions.put(chunk.get()); + if (nx < 0 || nz < 0 || nx >= static_cast(w) || nz >= static_cast(d)) { + level->events->trigger(EVT_CHUNK_HIDDEN, chunk.get()); + save(chunk.get()); chunksCount--; continue; } @@ -662,8 +664,8 @@ void Chunks::resize(uint32_t newW, uint32_t newD) { const int newVolume = newW * newD; std::vector> newChunks(newVolume); std::vector> newChunksSecond(newVolume); - for (int z = 0; z < int(d) && z < int(newD); z++) { - for (int x = 0; x < int(w) && x < int(newW); x++) { + for (int z = 0; z < static_cast(d) && z < static_cast(newD); z++) { + for (int x = 0; x < static_cast(w) && x < static_cast(newW); x++) { newChunks[z * newW + x] = chunks[z * w + x]; } } @@ -684,21 +686,46 @@ bool Chunks::putChunk(const std::shared_ptr& chunk) { int z = chunk->z; x -= ox; z -= oz; - if (x < 0 || z < 0 || x >= int(w) || z >= int(d)) + if (x < 0 || z < 0 || x >= static_cast(w) || z >= static_cast(d)) { return false; + } chunks[z * w + x] = chunk; chunksCount++; return true; } -void Chunks::saveAndClear(){ - auto& regions = worldFiles->getRegions(); +void Chunks::saveAndClear() { for (size_t i = 0; i < volume; i++){ - Chunk* chunk = chunks[i].get(); + auto chunk = chunks[i].get(); chunks[i] = nullptr; - if (chunk) { - regions.put(chunk); - } + save(chunk); } chunksCount = 0; } + +void Chunks::save(Chunk* chunk) { + if (chunk != nullptr) { + AABB aabb ( + glm::vec3(chunk->x * CHUNK_W, -16, chunk->z * CHUNK_D), + glm::vec3((chunk->x+1) * CHUNK_W, CHUNK_H+32, (chunk->z + 1) * CHUNK_D) + ); + auto entities = level->entities->getAllInside(aabb); + auto root = dynamic::create_map(); + auto& list = root->putList("data"); + for (auto& entity : entities) { + list.put(level->entities->serialize(entity)); + } + if (!entities.empty()) { + chunk->flags.unsaved = true; + } + worldFiles->getRegions().put(chunk, json::to_binary(root, true)); + } +} + +void Chunks::saveAll() { + for (size_t i = 0; i < volume; i++) { + if (auto& chunk = chunks[i]) { + save(chunk.get()); + } + } +} diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index fb8185d4..860d3da0 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -18,9 +18,11 @@ class Chunk; class WorldFiles; class LevelEvents; class Block; +class Level; -/* Player-centred chunks matrix */ +/// Player-centred chunks matrix class Chunks { + Level* level; const ContentIndices* const indices; void eraseSegments(const Block* def, blockstate state, int x, int y, int z); @@ -35,10 +37,9 @@ public: uint32_t w, d; int32_t ox, oz; WorldFiles* worldFiles; - LevelEvents* events; Chunks(uint32_t w, uint32_t d, int32_t ox, int32_t oz, - WorldFiles* worldFiles, LevelEvents* events, const Content* content); + WorldFiles* worldFiles, Level* level); ~Chunks() = default; bool putChunk(const std::shared_ptr& chunk); @@ -95,6 +96,8 @@ public: void resize(uint32_t newW, uint32_t newD); void saveAndClear(); + void save(Chunk* chunk); + void saveAll(); }; #endif // VOXELS_CHUNKS_HPP_ diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 84b10742..e057708d 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -38,7 +38,7 @@ Level::Level( settings.chunks.padding.get() ) * 2; chunks = std::make_unique( - matrixSize, matrixSize, 0, 0, this->world->wfile.get(), events.get(), content + matrixSize, matrixSize, 0, 0, this->world->wfile.get(), this ); lighting = std::make_unique(content, chunks.get()); diff --git a/src/world/World.cpp b/src/world/World.cpp index 3e92ccb6..e16b5e6e 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -51,14 +51,7 @@ void World::updateTimers(float delta) { void World::write(Level* level) { const Content* content = level->content; - Chunks* chunks = level->chunks.get(); - auto& regions = wfile->getRegions(); - - for (size_t i = 0; i < chunks->volume; i++) { - if (auto chunk = chunks->chunks[i]) { - regions.put(chunk.get()); - } - } + level->chunks->saveAll(); wfile->write(this, content); auto playerFile = dynamic::Map();