From a102821076606f681cc1b6d176f6e82dbeaf03b8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 6 Dec 2023 16:15:44 +0300 Subject: [PATCH] Sky lights data saving (optimization) --- src/files/WorldFiles.cpp | 103 ++++++++++++++++++++++++--------- src/files/WorldFiles.h | 21 ++++++- src/files/settings_io.cpp | 1 + src/lighting/Lightmap.cpp | 21 ++++++- src/lighting/Lightmap.h | 5 ++ src/logic/ChunksController.cpp | 8 ++- src/settings.h | 1 + src/util/data_io.h | 2 +- src/voxels/Chunk.h | 5 ++ src/voxels/ChunksStorage.cpp | 6 ++ src/world/World.cpp | 11 +++- src/world/World.h | 1 + 12 files changed, 147 insertions(+), 38 deletions(-) diff --git a/src/files/WorldFiles.cpp b/src/files/WorldFiles.cpp index 459cdb5b..1ea0e007 100644 --- a/src/files/WorldFiles.cpp +++ b/src/files/WorldFiles.cpp @@ -13,6 +13,7 @@ #include "../typedefs.h" #include "../maths/voxmaths.h" #include "../world/World.h" +#include "../lighting/Lightmap.h" #include "../util/data_io.h" #include "../coders/json.h" @@ -87,8 +88,10 @@ uint WorldRegion::getSize(uint x, uint z) { return sizes[z * REGION_SIZE + x]; } -WorldFiles::WorldFiles(path directory, bool generatorTestMode) - : directory(directory), generatorTestMode(generatorTestMode) { +WorldFiles::WorldFiles(path directory, const DebugSettings& settings) + : directory(directory), + generatorTestMode(settings.generatorTestMode), + doWriteLights(settings.doWriteLights) { compressionBuffer = new ubyte[CHUNK_DATA_LEN * 2]; } @@ -108,6 +111,17 @@ WorldRegion* WorldFiles::getRegion(unordered_map& regions, return found->second; } +WorldRegion* WorldFiles::getOrCreateRegion( + unordered_map& regions, + int x, int z) { + WorldRegion* region = getRegion(regions, x, z); + if (region == nullptr) { + region = new WorldRegion(); + regions[ivec2(x, z)] = region; + } + return region; +} + ubyte* WorldFiles::compress(ubyte* src, size_t srclen, size_t& len) { len = extrle::encode(src, srclen, compressionBuffer); ubyte* data = new ubyte[len]; @@ -128,30 +142,38 @@ void WorldFiles::put(Chunk* chunk){ int regionX = floordiv(chunk->x, REGION_SIZE); int regionZ = floordiv(chunk->z, REGION_SIZE); - - WorldRegion* region = getRegion(regions, regionX, regionZ); - if (region == nullptr) { - region = new WorldRegion(); - regions[ivec2(regionX, regionZ)] = region; - } - region->setUnsaved(true); - int localX = chunk->x - (regionX * REGION_SIZE); int localZ = chunk->z - (regionZ * REGION_SIZE); - unique_ptr chunk_data (chunk->encode()); - size_t compressedSize; - ubyte* data = compress(chunk_data.get(), CHUNK_DATA_LEN, compressedSize); - region->put(localX, localZ, data, compressedSize); + /* Writing Voxels */ { + WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ); + region->setUnsaved(true); + unique_ptr chunk_data (chunk->encode()); + size_t compressedSize; + ubyte* data = compress(chunk_data.get(), CHUNK_DATA_LEN, compressedSize); + region->put(localX, localZ, data, compressedSize); + } + if (doWriteLights) { + WorldRegion* region = getOrCreateRegion(lights, regionX, regionZ); + region->setUnsaved(true); + unique_ptr light_data (chunk->lightmap->encode()); + size_t compressedSize; + ubyte* data = compress(light_data.get(), LIGHTMAP_DATA_LEN, compressedSize); + region->put(localX, localZ, data, compressedSize); + } } path WorldFiles::getRegionsFolder() const { return directory/path("regions"); } -path WorldFiles::getRegionFile(int x, int y) const { +path WorldFiles::getLightsFolder() const { + return directory/path("lights"); +} + +path WorldFiles::getRegionFilename(int x, int y) const { string filename = std::to_string(x) + "_" + std::to_string(y) + ".bin"; - return getRegionsFolder()/path(filename); + return path(filename); } path WorldFiles::getPlayerFile() const { @@ -175,6 +197,19 @@ path WorldFiles::getOldWorldFile() const { } ubyte* WorldFiles::getChunk(int x, int z){ + return getData(regions, getRegionsFolder(), x, z); +} + +light_t* WorldFiles::getLights(int x, int z) { + ubyte* data = getData(lights, getLightsFolder(), x, z); + if (data == nullptr) + return nullptr; + return Lightmap::decode(data); +} + +ubyte* WorldFiles::getData(unordered_map& regions, + const path& folder, + int x, int z) { int regionX = floordiv(x, REGION_SIZE); int regionZ = floordiv(z, REGION_SIZE); @@ -190,7 +225,8 @@ ubyte* WorldFiles::getChunk(int x, int z){ ubyte* data = region->get(localX, localZ); if (data == nullptr) { uint32_t size; - data = readChunkData(x, z, size, getRegionFile(regionX, regionZ)); + data = readChunkData(x, z, size, + folder/getRegionFilename(regionX, regionZ)); if (data) { region->put(localX, localZ, data, size); } @@ -278,21 +314,36 @@ void WorldFiles::writeRegion(int x, int y, WorldRegion* entry, path filename){ } } +void WorldFiles::writeRegions(unordered_map& regions, + const path& folder) { + for (auto it : regions){ + WorldRegion* region = it.second; + if (region->getChunks() == nullptr || !region->isUnsaved()) + continue; + ivec2 key = it.first; + writeRegion(key.x, key.y, region, folder/getRegionFilename(key.x, key.y)); + } +} + void WorldFiles::write(const World* world, const Content* content) { - path directory = getRegionsFolder(); - if (!fs::is_directory(directory)) { - fs::create_directories(directory); + { + path directory = getRegionsFolder(); + if (!fs::is_directory(directory)) { + fs::create_directories(directory); + } + } + { + path directory = getLightsFolder(); + if (!fs::is_directory(directory)) { + fs::create_directories(directory); + } } writeWorldInfo(world); if (generatorTestMode) return; writeIndices(content->indices); - for (auto it = regions.begin(); it != regions.end(); it++){ - if (it->second->getChunks() == nullptr || !it->second->isUnsaved()) - continue; - ivec2 key = it->first; - writeRegion(key.x, key.y, it->second, getRegionFile(key.x, key.y)); - } + writeRegions(regions, getRegionsFolder()); + writeRegions(lights, getLightsFolder()); } void WorldFiles::writeIndices(const ContentIndices* indices) { diff --git a/src/files/WorldFiles.h b/src/files/WorldFiles.h index d556547c..8d78c2e0 100644 --- a/src/files/WorldFiles.h +++ b/src/files/WorldFiles.h @@ -12,6 +12,7 @@ #include "glm/gtx/hash.hpp" #include "../typedefs.h" +#include "../settings.h" const uint REGION_SIZE_BIT = 5; const uint REGION_SIZE = (1 << (REGION_SIZE_BIT)); @@ -30,7 +31,7 @@ class World; class WorldRegion { ubyte** chunksData; uint32_t* sizes; - bool unsaved = true; + bool unsaved = false; public: WorldRegion(); ~WorldRegion(); @@ -49,7 +50,8 @@ public: class WorldFiles { void writeWorldInfo(const World* world); std::filesystem::path getRegionsFolder() const; - std::filesystem::path getRegionFile(int x, int y) const; + std::filesystem::path getLightsFolder() const; + std::filesystem::path getRegionFilename(int x, int y) const; std::filesystem::path getPlayerFile() const; std::filesystem::path getWorldFile() const; std::filesystem::path getIndicesFile() const; @@ -64,6 +66,10 @@ class WorldFiles { WorldRegion* getRegion(std::unordered_map& regions, int x, int z); + WorldRegion* getOrCreateRegion( + std::unordered_map& regions, + int x, int z); + /* Compress buffer with extrle @param src source buffer @param srclen length of source buffer @@ -80,18 +86,27 @@ class WorldFiles { ubyte* readChunkData(int x, int y, uint32_t& length, std::filesystem::path file); + + void writeRegions(std::unordered_map& regions, + const std::filesystem::path& folder); + + ubyte* getData(std::unordered_map& regions, + const std::filesystem::path& folder, + int x, int z); public: std::unordered_map regions; std::unordered_map lights; std::filesystem::path directory; ubyte* compressionBuffer; bool generatorTestMode; + bool doWriteLights; - WorldFiles(std::filesystem::path directory, bool generatorTestMode); + WorldFiles(std::filesystem::path directory, const DebugSettings& settings); ~WorldFiles(); void put(Chunk* chunk); ubyte* getChunk(int x, int y); + light_t* getLights(int x, int y); bool readWorldInfo(World* world); bool readPlayer(Player* player); diff --git a/src/files/settings_io.cpp b/src/files/settings_io.cpp index 0ecc4414..7185bfa9 100644 --- a/src/files/settings_io.cpp +++ b/src/files/settings_io.cpp @@ -38,6 +38,7 @@ toml::Wrapper create_wrapper(EngineSettings& settings) { toml::Section& debug = wrapper.add("debug"); debug.add("generator-test-mode", &settings.debug.generatorTestMode); debug.add("show-chunk-borders", &settings.debug.showChunkBorders); + debug.add("do-write-lights", &settings.debug.doWriteLights); return wrapper; } diff --git a/src/lighting/Lightmap.cpp b/src/lighting/Lightmap.cpp index 544d700c..f8698964 100644 --- a/src/lighting/Lightmap.cpp +++ b/src/lighting/Lightmap.cpp @@ -20,12 +20,27 @@ void Lightmap::set(const Lightmap* lightmap) { } } +void Lightmap::set(light_t* map) { + delete[] this->map; + this->map = map; +} + static_assert(sizeof(light_t) == 2, "replace dataio calls to new light_t"); ubyte* Lightmap::encode() const { - ubyte* buffer = new ubyte[CHUNK_VOL * sizeof(light_t)]; - for (uint i = 0; i < CHUNK_VOL; i++) { - dataio::write_int16_big(map[i], buffer, i * sizeof(light_t)); + ubyte* buffer = new ubyte[LIGHTMAP_DATA_LEN]; + for (uint i = 0; i < CHUNK_VOL; i+=2) { + buffer[i/2] = ((map[i] >> 12) & 0xF) | ((map[i+1] >> 8) & 0xF0); } return buffer; } + +light_t* Lightmap::decode(ubyte* buffer) { + light_t* lights = new light_t[CHUNK_VOL]; + for (uint i = 0; i < CHUNK_VOL; i+=2) { + ubyte b = buffer[i/2]; + lights[i] = ((b & 0xF) << 12); + lights[i+1] = ((b & 0xF0) << 8); + } + return lights; +} diff --git a/src/lighting/Lightmap.h b/src/lighting/Lightmap.h index c4077807..14fb9d7f 100644 --- a/src/lighting/Lightmap.h +++ b/src/lighting/Lightmap.h @@ -5,6 +5,8 @@ #include "../typedefs.h" #include "../voxels/Chunk.h" +const int LIGHTMAP_DATA_LEN = CHUNK_VOL/2; + // Lichtkarte class Lightmap { public: @@ -15,6 +17,8 @@ public: void set(const Lightmap* lightmap); + void set(light_t* map); + inline unsigned short get(int x, int y, int z){ return (map[y*CHUNK_D*CHUNK_W+z*CHUNK_W+x]); } @@ -81,6 +85,7 @@ public: } ubyte* encode() const; + static light_t* decode(ubyte* buffer); }; #endif /* LIGHTING_LIGHTMAP_H_ */ diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 8ff4ccdd..81d16e0e 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -79,7 +79,9 @@ bool ChunksController::loadVisible(){ } chunk->surrounding = surrounding; if (surrounding == MIN_SURROUNDING && !chunk->isLighted()) { - lighting->buildSkyLight(chunk->x, chunk->z); + if (!chunk->isLoadedLights()) { + lighting->buildSkyLight(chunk->x, chunk->z); + } lighting->onChunkLoaded(chunk->x, chunk->z); chunk->setLighted(true); return true; @@ -123,6 +125,8 @@ bool ChunksController::loadVisible(){ chunk->voxels[i].id = 11; } } - lighting->prebuildSkyLight(chunk->x, chunk->z); + if (!chunk->isLoadedLights()) { + lighting->prebuildSkyLight(chunk->x, chunk->z); + } return true; } diff --git a/src/settings.h b/src/settings.h index ceb7c303..69f9ca75 100644 --- a/src/settings.h +++ b/src/settings.h @@ -58,6 +58,7 @@ struct DebugSettings { /* Turns off chunks saving/loading */ bool generatorTestMode = false; bool showChunkBorders = false; + bool doWriteLights = true; }; struct EngineSettings { diff --git a/src/util/data_io.h b/src/util/data_io.h index 634e0cea..0f23a3dc 100644 --- a/src/util/data_io.h +++ b/src/util/data_io.h @@ -53,4 +53,4 @@ namespace dataio { } } -#endif // UTIL_DATA_IO_H_ \ No newline at end of file +#endif // UTIL_DATA_IO_H_ diff --git a/src/voxels/Chunk.h b/src/voxels/Chunk.h index 31c1cd32..690a732e 100644 --- a/src/voxels/Chunk.h +++ b/src/voxels/Chunk.h @@ -10,6 +10,7 @@ struct ChunkFlag{ static const int LOADED = 0x4; static const int LIGHTED = 0x8; static const int UNSAVED = 0x10; + static const int LOADED_LIGHTS = 0x20; }; #define CHUNK_DATA_LEN (CHUNK_VOL*2) @@ -57,6 +58,8 @@ public: inline bool isLoaded() const {return flags & ChunkFlag::LOADED;} + inline bool isLoadedLights() const {return flags & ChunkFlag::LOADED_LIGHTS;} + inline bool isReady() const {return flags & ChunkFlag::READY;} inline void setUnsaved(bool newState) {SETFLAGS(ChunkFlag::UNSAVED, newState);} @@ -65,6 +68,8 @@ public: inline void setLoaded(bool newState) {SETFLAGS(ChunkFlag::LOADED, newState);} + inline void setLoadedLights(bool newState) {SETFLAGS(ChunkFlag::LOADED_LIGHTS, newState);} + inline void setLighted(bool newState) {SETFLAGS(ChunkFlag::LIGHTED, newState);} inline void setReady(bool newState) {SETFLAGS(ChunkFlag::READY, newState);} diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index 7e17efcc..a59f7d9c 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -50,6 +50,12 @@ std::shared_ptr ChunksStorage::create(int x, int z) { chunk->decode(data.get()); chunk->setLoaded(true); } + + light_t* lights = level->world->wfile->getLights(chunk->x, chunk->z); + if (lights) { + chunk->lightmap->set(lights); + chunk->setLoadedLights(true); + } return chunk; } diff --git a/src/world/World.cpp b/src/world/World.cpp index 45fa3f03..fa437365 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -21,8 +21,10 @@ World::World(string name, path directory, uint64_t seed, EngineSettings& settings) - : name(name), seed(seed) { - wfile = new WorldFiles(directory, settings.debug.generatorTestMode); + : settings(settings), + name(name), + seed(seed) { + wfile = new WorldFiles(directory, settings.debug); } World::~World(){ @@ -41,7 +43,10 @@ void World::write(Level* level) { for (size_t i = 0; i < chunks->volume; i++) { shared_ptr chunk = chunks->chunks[i]; - if (chunk == nullptr || !chunk->isUnsaved()) + if (chunk == nullptr) + continue; + bool lightsUnsaved = !chunk->isLoadedLights() && settings.debug.doWriteLights; + if (!chunk->isUnsaved() && !lightsUnsaved) continue; wfile->put(chunk.get()); } diff --git a/src/world/World.h b/src/world/World.h index 95886d81..fc492468 100644 --- a/src/world/World.h +++ b/src/world/World.h @@ -14,6 +14,7 @@ class Level; class Player; class World { + EngineSettings& settings; public: std::string name; WorldFiles* wfile;