diff --git a/src/constants.h b/src/constants.h index fc0ae25f..bd40c278 100644 --- a/src/constants.h +++ b/src/constants.h @@ -6,9 +6,12 @@ #define CHUNK_W 16 #define CHUNK_H 256 #define CHUNK_D 16 + +/* Chunk volume (count of voxels per Chunk) */ #define CHUNK_VOL (CHUNK_W * CHUNK_H * CHUNK_D) -#define BLOCK_VOID (blockid_t)((2 << (sizeof(blockid_t)*8)) - 1) +/* BLOCK_VOID is block id used to mark non-existing voxel (voxel of missing chunk) */ +#define BLOCK_VOID (blockid_t)((2 << (sizeof(blockid_t)*CHAR_BIT)) - 1) inline uint vox_index(int x, int y, int z, int w, int d) { return (y * d + z) * w + x; diff --git a/src/declarations.cpp b/src/declarations.cpp index 2f89985d..a6984798 100644 --- a/src/declarations.cpp +++ b/src/declarations.cpp @@ -20,8 +20,9 @@ void initialize_assets(AssetsLoader* loader) { // All in-game definitions (blocks, items, etc..) void setup_definitions() { - for (size_t i = 0; i < 256; i++) + for (size_t i = 0; i < 256; i++) { Block::blocks[i] = nullptr; + } Block* block = new Block(BLOCK_AIR, 0); block->drawGroup = 1; diff --git a/src/files/WorldFiles.cpp b/src/files/WorldFiles.cpp index 681b2896..94798429 100644 --- a/src/files/WorldFiles.cpp +++ b/src/files/WorldFiles.cpp @@ -1,6 +1,7 @@ #include "WorldFiles.h" #include "files.h" +#include "rle.h" #include "../window/Camera.h" #include "../objects/Player.h" #include "../physics/Hitbox.h" @@ -12,6 +13,7 @@ #include #include #include +#include #include #include @@ -20,12 +22,11 @@ #define SECTION_FLAGS 3 #define PLAYER_FLAG_FLIGHT 0x1 #define PLAYER_FLAG_NOCLIP 0x2 -#define CHUNK_DATA_LEN (CHUNK_VOL*sizeof(voxel)) using glm::ivec2; using glm::vec3; - -int64_t WorldFiles::totalCompressed = 0; +using std::ios; +using std::unique_ptr; int bytes2Int(const ubyte* src, size_t offset){ return (src[offset] << 24) | (src[offset+1] << 16) | (src[offset+2] << 8) | (src[offset+3]); @@ -55,18 +56,18 @@ float bytes2Float(ubyte* src, uint offset){ } WorldFiles::WorldFiles(std::string directory, size_t mainBufferCapacity) : directory(directory){ - mainBufferIn = new ubyte[CHUNK_DATA_LEN]; - mainBufferOut = new ubyte[mainBufferCapacity]; + mainBufferIn = new ubyte[CHUNK_DATA_LEN * 2]; + compressionBuffer = new ubyte[CHUNK_DATA_LEN * 2]; } WorldFiles::~WorldFiles(){ delete[] mainBufferIn; - delete[] mainBufferOut; + delete[] compressionBuffer; for (auto it = regions.begin(); it != regions.end(); it++){ WorldRegion region = it->second; if (region.chunksData == nullptr) continue; - for (unsigned int i = 0; i < REGION_VOL; i++){ + for (size_t i = 0; i < REGION_VOL; i++){ delete[] region.chunksData[i]; } delete[] region.chunksData; @@ -74,37 +75,44 @@ WorldFiles::~WorldFiles(){ regions.clear(); } -void WorldFiles::put(const ubyte* chunkData, int x, int y){ - assert(chunkData != nullptr); +void WorldFiles::put(Chunk* chunk){ + assert(chunk != nullptr); - int regionX = floordiv(x, REGION_SIZE); - int regionY = floordiv(y, REGION_SIZE); + int regionX = floordiv(chunk->x, REGION_SIZE); + int regionY = floordiv(chunk->z, REGION_SIZE); - int localX = x - (regionX * REGION_SIZE); - int localY = y - (regionY * REGION_SIZE); + int localX = chunk->x - (regionX * REGION_SIZE); + int localY = chunk->z - (regionY * REGION_SIZE); ivec2 key(regionX, regionY); auto found = regions.find(key); if (found == regions.end()) { ubyte** chunksData = new ubyte*[REGION_VOL]; + uint32_t* compressedSizes = new uint32_t[REGION_VOL]; for (uint i = 0; i < REGION_VOL; i++) { chunksData[i] = nullptr; } - regions[key] = { chunksData, true }; + regions[key] = { chunksData, compressedSizes, true }; } WorldRegion& region = regions[key]; region.unsaved = true; - ubyte* targetChunk = region.chunksData[localY * REGION_SIZE + localX]; - if (targetChunk == nullptr){ - targetChunk = new ubyte[CHUNK_DATA_LEN]; - region.chunksData[localY * REGION_SIZE + localX] = targetChunk; - totalCompressed += CHUNK_DATA_LEN; + size_t chunk_index = localY * REGION_SIZE + localX; + ubyte* target_chunk = region.chunksData[chunk_index]; + if (target_chunk) { + delete[] target_chunk; } - for (uint i = 0; i < CHUNK_DATA_LEN; i++) - targetChunk[i] = chunkData[i]; + ubyte* chunk_data = chunk->encode(); + size_t compressedSize = extrle::encode(chunk_data, CHUNK_DATA_LEN, compressionBuffer); + delete[] chunk_data; + ubyte* data = new ubyte[compressedSize]; + for (size_t i = 0; i < compressedSize; i++) { + data[i] = compressionBuffer[i]; + } + region.chunksData[chunk_index] = data; + region.compressedSizes[chunk_index] = compressedSize; } std::string WorldFiles::getRegionFile(int x, int y) { @@ -115,9 +123,7 @@ std::string WorldFiles::getPlayerFile() { return directory + "/player.bin"; } -bool WorldFiles::getChunk(int x, int y, ubyte* out){ - assert(out != nullptr); - +ubyte* WorldFiles::getChunk(int x, int y){ int regionX = floordiv(x, REGION_SIZE); int regionY = floordiv(y, REGION_SIZE); @@ -131,21 +137,31 @@ bool WorldFiles::getChunk(int x, int y, ubyte* out){ auto found = regions.find(key); if (found == regions.end()) { - return readChunk(x, y, out); + ubyte** chunksData = new ubyte * [REGION_VOL]; + uint32_t* compressedSizes = new uint32_t[REGION_VOL]; + for (uint i = 0; i < REGION_VOL; i++) { + chunksData[i] = nullptr; + } + regions[key] = { chunksData, compressedSizes, true }; } - WorldRegion& region = found->second; - ubyte* chunk = region.chunksData[chunkIndex]; - if (chunk == nullptr) - return readChunk(x,y,out); - for (uint i = 0; i < CHUNK_DATA_LEN; i++) - out[i] = chunk[i]; - return true; + WorldRegion& region = regions[key]; + ubyte* data = region.chunksData[chunkIndex]; + if (data == nullptr) { + data = readChunkData(x, y, region.compressedSizes[chunkIndex]); + if (data) { + region.chunksData[chunkIndex] = data; + } + } + if (data) { + ubyte* decompressed = new ubyte[CHUNK_DATA_LEN]; + extrle::decode(data, region.compressedSizes[chunkIndex], decompressed); + return decompressed; + } + return nullptr; } -bool WorldFiles::readChunk(int x, int y, ubyte* out){ - assert(out != nullptr); - +ubyte* WorldFiles::readChunkData(int x, int y, uint32_t& length){ int regionX = floordiv(x, REGION_SIZE); int regionY = floordiv(y, REGION_SIZE); @@ -157,39 +173,35 @@ bool WorldFiles::readChunk(int x, int y, ubyte* out){ std::string filename = getRegionFile(regionX, regionY); std::ifstream input(filename, std::ios::binary); if (!input.is_open()){ - return false; + return nullptr; } + input.seekg(0, ios::end); + size_t file_size = input.tellg(); + size_t table_offset = file_size - REGION_VOL * 4; uint32_t offset; - input.seekg(chunkIndex*4); + input.seekg(table_offset + chunkIndex * 4); input.read((char*)(&offset), 4); - // Ordering bytes from big-endian to machine order (any, just reading) - offset = bytes2Int((const unsigned char*)(&offset), 0); - //assert (offset < 1000000); + offset = bytes2Int((const ubyte*)(&offset), 0); if (offset == 0){ input.close(); - return false; + return nullptr; } input.seekg(offset); input.read((char*)(&offset), 4); - size_t compressedSize = bytes2Int((const ubyte*)(&offset), 0); - input.read((char*)mainBufferIn, compressedSize); + length = bytes2Int((const ubyte*)(&offset), 0); + ubyte* data = new ubyte[length]; + input.read((char*)data, length); input.close(); - - decompressRLE((ubyte*)mainBufferIn, compressedSize, (ubyte*)out, CHUNK_DATA_LEN); - - return true; + return data; } void WorldFiles::write(){ for (auto it = regions.begin(); it != regions.end(); it++){ if (it->second.chunksData == nullptr || !it->second.unsaved) continue; - ivec2 key = it->first; - - unsigned int size = writeRegion(mainBufferOut, key.x, key.y, it->second.chunksData); - write_binary_file(getRegionFile(key.x, key.y), (const char*)mainBufferOut, size); + writeRegion(key.x, key.y, it->second); } } @@ -238,7 +250,7 @@ bool WorldFiles::readPlayer(Player* player) { break; case SECTION_FLAGS: { - unsigned char flags = data[offset++]; + ubyte flags = data[offset++]; player->flight = flags & PLAYER_FLAG_FLIGHT; player->noclip = flags & PLAYER_FLAG_NOCLIP; } @@ -250,39 +262,45 @@ bool WorldFiles::readPlayer(Player* player) { return true; } -uint WorldFiles::writeRegion(ubyte* out, int x, int y, ubyte** region){ - uint offset = REGION_VOL * 4; - for (uint i = 0; i < offset; i++) - out[i] = 0; - ubyte* compressed = new ubyte[CHUNK_DATA_LEN]; - for (int i = 0; i < REGION_VOL; i++){ - ubyte* chunk = region[i]; - if (chunk == nullptr){ - chunk = new ubyte[CHUNK_DATA_LEN]; - if (readChunk((i % REGION_SIZE) + x * REGION_SIZE, (i / REGION_SIZE) + y * REGION_SIZE, chunk)){ - region[i] = chunk; - totalCompressed += CHUNK_DATA_LEN; - } else { - delete[] chunk; - chunk = nullptr; - } - } - - if (chunk == nullptr){ - int2Bytes(0, out, i*4); - } else { - int2Bytes(offset, out, i*4); - - uint compressedSize = compressRLE(chunk, CHUNK_DATA_LEN, compressed); - - int2Bytes(compressedSize, out, offset); - offset += 4; - - for (uint j = 0; j < compressedSize; j++) - out[offset++] = compressed[j]; +void WorldFiles::writeRegion(int x, int y, WorldRegion& entry){ + ubyte** region = entry.chunksData; + uint32_t* sizes = entry.compressedSizes; + for (size_t i = 0; i < REGION_VOL; i++) { + int chunk_x = (i % REGION_SIZE) + x * REGION_SIZE; + int chunk_y = (i / REGION_SIZE) + y * REGION_SIZE; + if (region[i] == nullptr) { + region[i] = readChunkData(chunk_x, chunk_y, sizes[i]); } } - delete[] compressed; - return offset; + + char header[10] = ".VOXREG"; + header[8] = REGION_FORMAT_VERSION; + header[9] = 0; // flags + std::ofstream file(getRegionFile(x, y), ios::out | ios::binary); + file.write(header, 10); + + size_t offset = 10; + char intbuf[4]{}; + uint offsets[REGION_VOL]{}; + + for (size_t i = 0; i < REGION_VOL; i++) { + ubyte* chunk = region[i]; + if (chunk == nullptr){ + offsets[i] = 0; + } else { + offsets[i] = offset; + + size_t compressedSize = sizes[i]; + int2Bytes(compressedSize, (ubyte*)intbuf, 0); + offset += 4 + compressedSize; + + file.write(intbuf, 4); + file.write((const char*)chunk, compressedSize); + } + } + for (size_t i = 0; i < REGION_VOL; i++) { + int2Bytes(offsets[i], (ubyte*)intbuf, 0); + file.write(intbuf, 4); + } } diff --git a/src/files/WorldFiles.h b/src/files/WorldFiles.h index 20628d95..221058da 100644 --- a/src/files/WorldFiles.h +++ b/src/files/WorldFiles.h @@ -11,34 +11,36 @@ #include "../typedefs.h" -class Player; - #define REGION_SIZE_BIT 5 #define REGION_SIZE (1 << (REGION_SIZE_BIT)) #define REGION_VOL ((REGION_SIZE) * (REGION_SIZE)) +#define REGION_FORMAT_VERSION 1 + +class Player; +class Chunk; struct WorldRegion { ubyte** chunksData; + uint32_t* compressedSizes; bool unsaved; }; class WorldFiles { public: - static int64_t totalCompressed; std::unordered_map regions; std::string directory; ubyte* mainBufferIn; - ubyte* mainBufferOut; + ubyte* compressionBuffer; WorldFiles(std::string directory, size_t mainBufferCapacity); ~WorldFiles(); - void put(const ubyte* chunkData, int x, int y); + void put(Chunk* chunk); bool readPlayer(Player* player); - bool readChunk(int x, int y, ubyte* out); - bool getChunk(int x, int y, ubyte* out); - uint writeRegion(ubyte* out, int x, int y, ubyte** region); + ubyte* readChunkData(int x, int y, uint32_t& length); + ubyte* getChunk(int x, int y); + void writeRegion(int x, int y, WorldRegion& entry); void writePlayer(Player* player); void write(); diff --git a/src/files/files.cpp b/src/files/files.cpp index 573d1f4b..51e3b558 100644 --- a/src/files/files.cpp +++ b/src/files/files.cpp @@ -55,56 +55,3 @@ char* read_binary_file(std::string filename, size_t& length) { input.close(); return data.release(); } - -// returns decompressed length -size_t decompressRLE(const ubyte* src, size_t length, ubyte* dst, size_t targetLength){ - size_t offset = 0; - for (size_t i = 0; i < length;){ - unsigned char counter = src[i++]; - unsigned char c = src[i++]; - for (unsigned int j = 0; j <= counter; j++){ - dst[offset++] = c; - } - } - return offset; -} - -size_t calcRLE(const ubyte* src, size_t length) { - size_t offset = 0; - size_t counter = 0; - ubyte c = src[0]; - for (size_t i = 0; i < length; i++){ - ubyte cnext = src[i]; - if (cnext != c || counter == 255){ - offset += 2; - c = cnext; - counter = 0; - } else { - counter++; - } - } - return offset + 2; -} - -// max result size = length * 2; returns compressed length -size_t compressRLE(const ubyte* src, size_t length, ubyte* dst) { - if (length == 0) - return 0; - size_t offset = 0; - uint counter = 0; - ubyte c = src[0]; - for (size_t i = 1; i < length; i++){ - ubyte cnext = src[i]; - if (cnext != c || counter == 255){ - dst[offset++] = counter; - dst[offset++] = c; - c = cnext; - counter = 0; - } else { - counter++; - } - } - dst[offset++] = counter; - dst[offset++] = c; - return offset; -} diff --git a/src/files/files.h b/src/files/files.h index b760529d..87ca7d4d 100644 --- a/src/files/files.h +++ b/src/files/files.h @@ -7,11 +7,6 @@ extern bool write_binary_file(std::string filename, const char* data, size_t size); extern unsigned int append_binary_file(std::string filename, const char* data, size_t size); extern bool read_binary_file(std::string filename, char* data, size_t size); -extern bool read_binary_file(std::string filename, char* data, size_t offset, size_t size); extern char* read_binary_file(std::string filename, size_t& length); -extern size_t calcRLE(const ubyte* src, size_t length); -extern size_t compressRLE(const ubyte* src, size_t length, ubyte* dst); -extern size_t decompressRLE(const ubyte* src, size_t length, ubyte* dst, size_t targetLength); - #endif /* FILES_FILES_H_ */ diff --git a/src/files/rle.cpp b/src/files/rle.cpp new file mode 100644 index 00000000..5a5c5714 --- /dev/null +++ b/src/files/rle.cpp @@ -0,0 +1,90 @@ +#include "rle.h" + +size_t rle::decode(const ubyte* src, size_t srclen, ubyte* dst) { + size_t offset = 0; + for (size_t i = 0; i < srclen;) { + ubyte len = src[i++]; + ubyte c = src[i++]; + for (size_t j = 0; j <= len; j++) { + dst[offset++] = c; + } + } + return offset; +} + +size_t rle::encode(const ubyte* src, size_t srclen, ubyte* dst) { + if (srclen == 0) { + return 0; + } + size_t offset = 0; + ubyte counter = 0; + ubyte c = src[0]; + for (size_t i = 1; i < srclen; i++) { + ubyte cnext = src[i]; + if (cnext != c || counter == 255) { + dst[offset++] = counter; + dst[offset++] = c; + c = cnext; + counter = 0; + } + else { + counter++; + } + } + dst[offset++] = counter; + dst[offset++] = c; + return offset; +} + + +size_t extrle::decode(const ubyte* src, size_t srclen, ubyte* dst) { + size_t offset = 0; + for (size_t i = 0; i < srclen;) { + uint len = src[i++]; + if (len & 0x80) { + len &= 0x7F; + len |= ((uint)src[i++]) << 7; + } + ubyte c = src[i++]; + for (size_t j = 0; j <= len; j++) { + dst[offset++] = c; + } + } + return offset; +} + +size_t extrle::encode(const ubyte* src, size_t srclen, ubyte* dst) { + if (srclen == 0) { + return 0; + } + size_t offset = 0; + uint counter = 0; + ubyte c = src[0]; + for (size_t i = 1; i < srclen; i++) { + ubyte cnext = src[i]; + if (cnext != c || counter == max_sequence) { + if (counter >= 0x80) { + dst[offset++] = 0x80 | (counter & 0x7F); + dst[offset++] = counter >> 7; + } + else { + dst[offset++] = counter; + } + dst[offset++] = c; + c = cnext; + counter = 0; + } + else { + counter++; + } + } + if (counter >= 0x80) { + dst[offset++] = 0x80 | (counter & 0x7F); + dst[offset++] = counter >> 7; + } + else { + dst[offset++] = counter; + } + dst[offset++] = c; + return offset; +} diff --git a/src/files/rle.h b/src/files/rle.h new file mode 100644 index 00000000..b12a1adc --- /dev/null +++ b/src/files/rle.h @@ -0,0 +1,17 @@ +#ifndef FILES_RLE_H_ +#define FILES_RLE_H_ + +#include "../typedefs.h" + +namespace rle { + size_t encode(const ubyte* src, size_t length, ubyte* dst); + size_t decode(const ubyte* src, size_t length, ubyte* dst); +} + +namespace extrle { + constexpr uint max_sequence = 0x7FFF; + size_t encode(const ubyte* src, size_t length, ubyte* dst); + size_t decode(const ubyte* src, size_t length, ubyte* dst); +} + +#endif // FILES_RLE_H_ \ No newline at end of file diff --git a/src/voxels/Chunk.cpp b/src/voxels/Chunk.cpp index 6fecb180..fc372437 100644 --- a/src/voxels/Chunk.cpp +++ b/src/voxels/Chunk.cpp @@ -4,8 +4,10 @@ Chunk::Chunk(int xpos, int zpos) : x(xpos), z(zpos){ voxels = new voxel[CHUNK_VOL]; - for (unsigned int i = 0; i < CHUNK_VOL; i++) - voxels[i].id = 1; + for (size_t i = 0; i < CHUNK_VOL; i++) { + voxels[i].id = 2; + voxels[i].states = 0; + } lightmap = new Lightmap(); renderData.vertices = nullptr; } @@ -36,3 +38,27 @@ Chunk* Chunk::clone() const { return other; } +/** + Current chunk format: + [voxel_ids...][voxel_states...]; +*/ +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; + } + return buffer; +} + +/** + @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]; + } + return true; +} diff --git a/src/voxels/Chunk.h b/src/voxels/Chunk.h index 329dbed8..b3b8e2b6 100644 --- a/src/voxels/Chunk.h +++ b/src/voxels/Chunk.h @@ -9,6 +9,7 @@ #define CHUNK_LOADED 0x4 #define CHUNK_LIGHTED 0x8 #define CHUNK_UNSAVED 0x10 +#define CHUNK_DATA_LEN (CHUNK_VOL*2) struct voxel; class Lightmap; @@ -59,6 +60,9 @@ public: inline void setLighted(bool flag) {BITSET(flags, CHUNK_LIGHTED, flag);} inline void setReady(bool flag) {BITSET(flags, CHUNK_READY, flag);} + + ubyte* encode() const; + bool decode(ubyte* data); }; #endif /* VOXELS_CHUNK_H_ */ diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 3a5d8961..165a90ab 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -5,6 +5,7 @@ #include "WorldGenerator.h" #include "../lighting/Lightmap.h" #include "../files/WorldFiles.h" +#include "../world/LevelEvents.h" #include "../graphics/Mesh.h" #include "../voxmaths.h" @@ -15,8 +16,8 @@ using glm::vec3; using std::shared_ptr; -Chunks::Chunks(int w, int d, int ox, int oz) : w(w), d(d), ox(ox), oz(oz){ - volume = w*d; +Chunks::Chunks(int w, int d, int ox, int oz, LevelEvents* events) : w(w), d(d), ox(ox), oz(oz), events(events) { + volume = (size_t)w*(size_t)d; chunks = new shared_ptr[volume]; chunksSecond = new shared_ptr[volume]; @@ -258,7 +259,8 @@ void Chunks::translate(WorldFiles* worldFiles, int dx, int dz){ if (chunk == nullptr) continue; if (nx < 0 || nz < 0 || nx >= w || nz >= d){ - worldFiles->put((const ubyte*)chunk->voxels, chunk->x, chunk->z); + events->trigger(EVT_CHUNK_HIDDEN, chunk.get()); + worldFiles->put(chunk.get()); chunksCount--; continue; } @@ -292,6 +294,10 @@ bool Chunks::putChunk(shared_ptr chunk) { void Chunks::clear(){ for (size_t i = 0; i < volume; i++){ + Chunk* chunk = chunks[i].get(); + if (chunk) { + events->trigger(EVT_CHUNK_HIDDEN, chunk); + } chunks[i] = nullptr; } chunksCount = 0; diff --git a/src/voxels/Chunks.h b/src/voxels/Chunks.h index 509b7671..2e871c18 100644 --- a/src/voxels/Chunks.h +++ b/src/voxels/Chunks.h @@ -11,6 +11,7 @@ class VoxelRenderer; class Chunk; class voxel; class WorldFiles; +class LevelEvents; class Chunks { public: @@ -20,8 +21,9 @@ public: size_t chunksCount; int w,d; int ox,oz; + LevelEvents* events; - Chunks(int w, int d, int ox, int oz); + Chunks(int w, int d, int ox, int oz, LevelEvents* events); ~Chunks(); bool putChunk(std::shared_ptr chunk); diff --git a/src/voxels/ChunksController.cpp b/src/voxels/ChunksController.cpp index c697d3c3..ae13c0f0 100644 --- a/src/voxels/ChunksController.cpp +++ b/src/voxels/ChunksController.cpp @@ -79,9 +79,12 @@ bool ChunksController::loadVisible(WorldFiles* worldFiles){ chunk = shared_ptr(new Chunk(nearX+ox, nearZ+oz)); level->chunksStorage->store(chunk); - if (worldFiles->getChunk(chunk->x, chunk->z, (ubyte*)chunk->voxels)) + ubyte* data = worldFiles->getChunk(chunk->x, chunk->z); + if (data) { + chunk->decode(data); chunk->setLoaded(true); - + delete[] data; + } chunks->putChunk(chunk); if (!chunk->isLoaded()) { @@ -90,8 +93,9 @@ bool ChunksController::loadVisible(WorldFiles* worldFiles){ } for (size_t i = 0; i < CHUNK_VOL; i++) { - if (Block::blocks[chunk->voxels[i].id] == nullptr) { - std::cout << "corruped block detected at " << i << " of chunk " << chunk->x << "x" << chunk->z << std::endl; + blockid_t id = chunk->voxels[i].id; + if (Block::blocks[id] == nullptr) { + std::cout << "corruped block detected at " << i << " of chunk " << chunk->x << "x" << chunk->z << " -> " << (int)id << std::endl; chunk->voxels[i].id = 11; } } diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index ec7f7e1b..dba41672 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -29,6 +29,13 @@ shared_ptr ChunksStorage::get(int x, int z) const { return found->second; } +void ChunksStorage::remove(int x, int z) { + auto found = chunksMap.find(ivec2(x, z)); + if (found != chunksMap.end()) { + chunksMap.erase(found->first); + } +} + // some magic code void ChunksStorage::getVoxels(VoxelsVolume* volume) const { voxel* voxels = volume->getVoxels(); diff --git a/src/voxels/ChunksStorage.h b/src/voxels/ChunksStorage.h index d15c99c4..24c415b7 100644 --- a/src/voxels/ChunksStorage.h +++ b/src/voxels/ChunksStorage.h @@ -20,6 +20,7 @@ public: std::shared_ptr get(int x, int y) const; void store(std::shared_ptr chunk); + void remove(int x, int y); void getVoxels(VoxelsVolume* volume) const; light_t getLight(int x, int y, int z, ubyte channel) const; diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 2b7d9db3..c9be21ba 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -1,6 +1,8 @@ #include "Level.h" #include "World.h" +#include "LevelEvents.h" #include "../lighting/Lighting.h" +#include "../voxels/Chunk.h" #include "../voxels/Chunks.h" #include "../voxels/ChunksController.h" #include "../voxels/ChunksStorage.h" @@ -9,19 +11,24 @@ #include "../physics/PhysicsSolver.h" #include "../objects/Player.h" -Level::Level(World* world, Player* player, Chunks* chunks, ChunksStorage* chunksStorage, PhysicsSolver* physics) : - world(world), - player(player), - chunks(chunks), - chunksStorage(chunksStorage), - physics(physics) { +Level::Level(World* world, Player* player, Chunks* chunks, ChunksStorage* chunksStorage, PhysicsSolver* physics, LevelEvents* events) : + world(world), + player(player), + chunks(chunks), + chunksStorage(chunksStorage), + physics(physics), + events(events) { lighting = new Lighting(chunks); chunksController = new ChunksController(this, chunks, lighting); playerController = new PlayerController(this); + events->listen(EVT_CHUNK_HIDDEN, [this](lvl_event_type type, Chunk* chunk) { + this->chunksStorage->remove(chunk->x, chunk->z); + }); } Level::~Level(){ delete chunks; + delete events; delete physics; delete player; delete lighting; diff --git a/src/world/Level.h b/src/world/Level.h index 6c981fea..03d2079e 100644 --- a/src/world/Level.h +++ b/src/world/Level.h @@ -4,6 +4,7 @@ class World; class Player; class Chunks; +class LevelEvents; class Lighting; class PhysicsSolver; class ChunksController; @@ -20,7 +21,8 @@ public: Lighting* lighting; ChunksController* chunksController; PlayerController* playerController; - Level(World* world, Player* player, Chunks* chunks, ChunksStorage* chunksStorage, PhysicsSolver* physics); + LevelEvents* events; + Level(World* world, Player* player, Chunks* chunks, ChunksStorage* chunksStorage, PhysicsSolver* physics, LevelEvents* events); ~Level(); void update(float delta, bool interactions); diff --git a/src/world/LevelEvents.cpp b/src/world/LevelEvents.cpp new file mode 100644 index 00000000..260aecb5 --- /dev/null +++ b/src/world/LevelEvents.cpp @@ -0,0 +1,16 @@ +#include "LevelEvents.h" +#include "../voxels/Chunk.h" + +using std::vector; + +void LevelEvents::listen(lvl_event_type type, chunk_event_func func) { + auto& callbacks = chunk_callbacks[type]; + callbacks.push_back(func); +} + +void LevelEvents::trigger(lvl_event_type type, Chunk* chunk) { + auto& callbacks = chunk_callbacks[type]; + for (chunk_event_func func : callbacks) { + func(type, chunk); + } +} \ No newline at end of file diff --git a/src/world/LevelEvents.h b/src/world/LevelEvents.h new file mode 100644 index 00000000..ae4362b9 --- /dev/null +++ b/src/world/LevelEvents.h @@ -0,0 +1,23 @@ +#ifndef WORLD_LEVEL_EVENTS_H_ +#define WORLD_LEVEL_EVENTS_H_ + +#include +#include +#include + +class Chunk; + +enum lvl_event_type { + EVT_CHUNK_HIDDEN, +}; + +typedef std::function chunk_event_func; + +class LevelEvents { + std::unordered_map> chunk_callbacks; +public: + void listen(lvl_event_type type, chunk_event_func func); + void trigger(lvl_event_type type, Chunk* chunk); +}; + +#endif // WORLD_LEVEL_EVENTS_H_ \ No newline at end of file diff --git a/src/world/World.cpp b/src/world/World.cpp index adaa578d..0bd357c5 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -10,11 +10,12 @@ #include "../objects/Player.h" #include "../physics/PhysicsSolver.h" #include "../window/Camera.h" +#include "../world/LevelEvents.h" using std::shared_ptr; World::World(std::string name, std::string directory, int seed) : name(name), seed(seed) { - wfile = new WorldFiles(directory, REGION_VOL * (CHUNK_VOL * 2 + 8)); + wfile = new WorldFiles(directory, REGION_VOL * (CHUNK_DATA_LEN * 2 + 8)); } World::~World(){ @@ -24,11 +25,11 @@ World::~World(){ void World::write(Level* level) { Chunks* chunks = level->chunks; - for (unsigned int i = 0; i < chunks->volume; i++) { + for (size_t i = 0; i < chunks->volume; i++) { shared_ptr chunk = chunks->chunks[i]; if (chunk == nullptr || !chunk->isUnsaved()) continue; - wfile->put((const ubyte*)chunk->voxels, chunk->x, chunk->z); + wfile->put(chunk.get()); } wfile->write(); @@ -37,7 +38,8 @@ void World::write(Level* level) { Level* World::loadLevel(Player* player) { ChunksStorage* storage = new ChunksStorage(); - Level* level = new Level(this, player, new Chunks(16, 16, 0, 0), storage, new PhysicsSolver(vec3(0, -19.6f, 0))); + LevelEvents* events = new LevelEvents(); + Level* level = new Level(this, player, new Chunks(16, 16, 0, 0, events), storage, new PhysicsSolver(vec3(0, -19.6f, 0)), events); wfile->readPlayer(player); Camera* camera = player->camera; diff --git a/src/world_render.cpp b/src/world_render.cpp index 6d0a2b5f..19b8ba1d 100644 --- a/src/world_render.cpp +++ b/src/world_render.cpp @@ -17,6 +17,7 @@ #include "voxels/Block.h" #include "world/World.h" #include "world/Level.h" +#include "world/LevelEvents.h" #include "objects/Player.h" #include "Assets.h" #include "player_control.h" @@ -30,6 +31,9 @@ WorldRenderer::WorldRenderer(Level* level, Assets* assets) : assets(assets), lev lineBatch = new LineBatch(4096); batch3d = new Batch3D(1024); renderer = new ChunksRenderer(level); + level->events->listen(EVT_CHUNK_HIDDEN, [this](lvl_event_type type, Chunk* chunk) { + renderer->unload(chunk); + }); } WorldRenderer::~WorldRenderer() {