Implement chunk compression and decompression methods
This commit is contained in:
parent
b02d83e72d
commit
21554417d1
@ -140,7 +140,6 @@ if(BUILD_CLIENT)
|
||||
src/game/window/keyboard.cpp
|
||||
src/game/window/mouse.cpp
|
||||
src/game/window/window.cpp
|
||||
src/game/world/chunk.cpp
|
||||
)
|
||||
if(RENDERER_GL)
|
||||
list(APPEND CLIENT_SOURCES
|
||||
|
@ -24,7 +24,7 @@ SOFTWARE.
|
||||
|
||||
#include "common/binary_utils.hpp"
|
||||
|
||||
static bool is_little_endian(){
|
||||
bool polygun::utils::is_little_endian(){
|
||||
unsigned x=0x76543210;
|
||||
char* c=(char*)&x;
|
||||
return *c==0x10;
|
||||
@ -86,6 +86,86 @@ void polygun::utils::uint32_to_bytes(uint32_t data,char* output){
|
||||
}
|
||||
}
|
||||
|
||||
int32_t polygun::utils::bytes_to_int32(const char* data){
|
||||
int32_t output;
|
||||
if(is_little_endian()){
|
||||
((char*)&output)[0]=data[0];
|
||||
((char*)&output)[1]=data[1];
|
||||
((char*)&output)[2]=data[2];
|
||||
((char*)&output)[3]=data[3];
|
||||
}
|
||||
else{
|
||||
((char*)&output)[3]=data[0];
|
||||
((char*)&output)[2]=data[1];
|
||||
((char*)&output)[1]=data[2];
|
||||
((char*)&output)[0]=data[3];
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
void polygun::utils::int32_to_bytes(int32_t data,char* output){
|
||||
if(is_little_endian()){
|
||||
output[0]=((char*)&data)[0];
|
||||
output[1]=((char*)&data)[1];
|
||||
output[2]=((char*)&data)[2];
|
||||
output[3]=((char*)&data)[3];
|
||||
}
|
||||
else{
|
||||
output[3]=((char*)&data)[0];
|
||||
output[2]=((char*)&data)[1];
|
||||
output[1]=((char*)&data)[2];
|
||||
output[0]=((char*)&data)[3];
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t polygun::utils::bytes_to_uint64(const char* data){
|
||||
uint64_t output;
|
||||
if(is_little_endian()){
|
||||
((char*)&output)[0]=data[0];
|
||||
((char*)&output)[1]=data[1];
|
||||
((char*)&output)[2]=data[2];
|
||||
((char*)&output)[3]=data[3];
|
||||
((char*)&output)[4]=data[4];
|
||||
((char*)&output)[5]=data[5];
|
||||
((char*)&output)[6]=data[6];
|
||||
((char*)&output)[7]=data[7];
|
||||
}
|
||||
else{
|
||||
((char*)&output)[7]=data[0];
|
||||
((char*)&output)[6]=data[1];
|
||||
((char*)&output)[5]=data[2];
|
||||
((char*)&output)[4]=data[3];
|
||||
((char*)&output)[3]=data[4];
|
||||
((char*)&output)[2]=data[5];
|
||||
((char*)&output)[1]=data[6];
|
||||
((char*)&output)[0]=data[7];
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
void polygun::utils::uint64_to_bytes(uint64_t data,char* output){
|
||||
if(is_little_endian()){
|
||||
output[0]=((char*)&data)[0];
|
||||
output[1]=((char*)&data)[1];
|
||||
output[2]=((char*)&data)[2];
|
||||
output[3]=((char*)&data)[3];
|
||||
output[4]=((char*)&data)[4];
|
||||
output[5]=((char*)&data)[5];
|
||||
output[6]=((char*)&data)[6];
|
||||
output[7]=((char*)&data)[7];
|
||||
}
|
||||
else{
|
||||
output[7]=((char*)&data)[0];
|
||||
output[6]=((char*)&data)[1];
|
||||
output[5]=((char*)&data)[2];
|
||||
output[4]=((char*)&data)[3];
|
||||
output[3]=((char*)&data)[4];
|
||||
output[2]=((char*)&data)[5];
|
||||
output[1]=((char*)&data)[6];
|
||||
output[0]=((char*)&data)[7];
|
||||
}
|
||||
}
|
||||
|
||||
float polygun::utils::bytes_to_float(const char* data) {
|
||||
float output;
|
||||
if(is_little_endian()){
|
||||
|
@ -28,10 +28,15 @@ SOFTWARE.
|
||||
#include <cstdint>
|
||||
|
||||
namespace polygun::utils {
|
||||
bool is_little_endian();
|
||||
uint16_t bytes_to_uint16(const char* data);
|
||||
void uint16_to_bytes(uint16_t data, char* output);
|
||||
uint32_t bytes_to_uint32(const char* data);
|
||||
void uint32_to_bytes(uint32_t data, char* output);
|
||||
int32_t bytes_to_int32(const char* data);
|
||||
void int32_to_bytes(int32_t data, char* output);
|
||||
uint64_t bytes_to_uint64(const char* data);
|
||||
void uint64_to_bytes(uint64_t data, char* output);
|
||||
float bytes_to_float(const char* data);
|
||||
void float_to_bytes(float data, char* output);
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ SOFTWARE.
|
||||
|
||||
namespace glm {
|
||||
typedef vec<4, uint8_t, packed_highp> vec4ub;
|
||||
typedef vec<3, uint8_t, packed_highp> vec3ub;
|
||||
typedef vec<3, int, packed_highp> vec3i;
|
||||
}
|
||||
|
||||
#endif // POLYGUN_UTILS_GLM_EXT_HPP
|
||||
|
192
src/common/world/chunk.cpp
Normal file
192
src/common/world/chunk.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
PolyGun
|
||||
|
||||
Copyright (c) 2023 kacperks https://kacperks.cubesoftware.xyz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "common/world/chunk.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/binary_utils.hpp"
|
||||
#include "common/logger.hpp"
|
||||
|
||||
static void huffman_encode(uint16_t num, std::vector<uint8_t>& output) {
|
||||
// FIXME: Make this code scalable because currently it can only encode number which takes 3 bytes maximum.
|
||||
uint8_t first_byte = num%0x80;
|
||||
output.push_back(first_byte|((num>0x7F)<<7));
|
||||
if(num>0x7F) {
|
||||
num-=first_byte;
|
||||
const uint16_t second_byte = num/0x80;
|
||||
output.push_back(static_cast<uint8_t>(second_byte)|((second_byte>0x7F)<<7));
|
||||
if(second_byte>0x7F)
|
||||
output.push_back(second_byte/0x80);
|
||||
}
|
||||
}
|
||||
|
||||
namespace polygun::world {
|
||||
Chunk::Chunk() :
|
||||
m_chunk_data()
|
||||
{
|
||||
memset(m_chunk_data, 0, sizeof(m_chunk_data));
|
||||
}
|
||||
|
||||
uint8_t Chunk::to_compressed_data(std::vector<uint8_t>& output) {
|
||||
// Try CHUNK_COMPRESSION_MODE_FILL
|
||||
const uint16_t match = get_node(glm::vec3ub(0, 0, 0));
|
||||
bool fill_success = true;
|
||||
for(unsigned char x = 0; x<CHUNK_SIZE; x++) {
|
||||
for(unsigned char y = 0; y<CHUNK_SIZE; y++) {
|
||||
for(unsigned char z = 0; z<CHUNK_SIZE; z++) {
|
||||
if(get_node(glm::vec3ub(x, y, z))!=match) {
|
||||
fill_success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(fill_success) {
|
||||
char temp[2];
|
||||
utils::uint16_to_bytes(match, temp);
|
||||
output.push_back(temp[0]);
|
||||
output.push_back(temp[1]);
|
||||
return ChunkCompressionMode::CHUNK_COMPRESSION_MODE_FILL;
|
||||
}
|
||||
|
||||
// Try CHUNK_COMPRESSION_MODE_HUFFMAN
|
||||
uint16_t current_count = 0;
|
||||
uint16_t current_id = 0;
|
||||
for(unsigned i = 0; i<sizeof(m_chunk_data)/2; i++) {
|
||||
if(m_chunk_data[i]!=current_id || i==sizeof(m_chunk_data)/2-1) {
|
||||
if(current_count>0) {
|
||||
huffman_encode(current_count, output);
|
||||
huffman_encode(current_id, output);
|
||||
current_count = 0;
|
||||
}
|
||||
current_id = m_chunk_data[i];
|
||||
}
|
||||
current_count++;
|
||||
}
|
||||
if(output.size()<sizeof(m_chunk_data))
|
||||
return ChunkCompressionMode::CHUNK_COMPRESSION_MODE_HUFFMAN;
|
||||
|
||||
// If none succeeded use CHUNK_COMPRESSION_MODE_NONE
|
||||
if(utils::is_little_endian())
|
||||
output.assign(reinterpret_cast<uint8_t*>(m_chunk_data), reinterpret_cast<uint8_t*>(m_chunk_data)+sizeof(m_chunk_data));
|
||||
else {
|
||||
output.resize(sizeof(m_chunk_data));
|
||||
for(uint16_t i = 0; i<sizeof(m_chunk_data)/2; i++)
|
||||
utils::uint16_to_bytes(m_chunk_data[i], reinterpret_cast<char*>(&output[i*2]));
|
||||
}
|
||||
return ChunkCompressionMode::CHUNK_COMPRESSION_MODE_NONE;
|
||||
}
|
||||
|
||||
void Chunk::load_from_compressed_data(const char* data, uint16_t data_size, uint8_t compression_mode) {
|
||||
switch(compression_mode) {
|
||||
default:
|
||||
LOG_ERROR("Unknown chunk compression mode, assuming none");
|
||||
case ChunkCompressionMode::CHUNK_COMPRESSION_MODE_NONE:
|
||||
for(uint16_t i = 0; i<data_size/2 && i<sizeof(m_chunk_data); i++)
|
||||
m_chunk_data[i] = utils::bytes_to_uint16(&data[i*2]);
|
||||
break;
|
||||
case ChunkCompressionMode::CHUNK_COMPRESSION_MODE_HUFFMAN: {
|
||||
uint16_t index = 0;
|
||||
uint16_t node_index = 0;
|
||||
while(index<data_size) {
|
||||
// Get 7 first bits of first count byte: 0xxxxxxx
|
||||
uint16_t count = data[index]&0x7F;
|
||||
unsigned char offset = 0;
|
||||
// Loop until last byte x0000000 is 1
|
||||
while(data[index++]&0x80 && index<data_size)
|
||||
count+=(++offset)*(data[index]&0x7F);
|
||||
// Same as above
|
||||
uint16_t id = data[index]&0x7F;
|
||||
offset = 1;
|
||||
while(data[index++]&0x80 && index<data_size)
|
||||
id+=(offset++)*(data[index]&0x7F);
|
||||
|
||||
for(uint16_t i = 0; i<count; i++)
|
||||
m_chunk_data[node_index++] = id;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ChunkCompressionMode::CHUNK_COMPRESSION_MODE_FILL: {
|
||||
const uint16_t id = utils::bytes_to_uint16(data);
|
||||
for(unsigned i = 0; i<sizeof(m_chunk_data)/2; i++)
|
||||
m_chunk_data[i] = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Chunk::add_node(uint16_t node_to_add, const glm::vec3ub& pos) {
|
||||
m_chunk_data[pos.z*CHUNK_SIZE*CHUNK_SIZE+pos.y*CHUNK_SIZE+pos.x] = node_to_add;
|
||||
}
|
||||
|
||||
bool Chunk::is_air(const glm::vec3ub& pos) {
|
||||
return get_node(pos) == 0;
|
||||
}
|
||||
|
||||
uint16_t Chunk::get_node(const glm::vec3ub& pos) {
|
||||
return m_chunk_data[pos.z*CHUNK_SIZE*CHUNK_SIZE+pos.y*CHUNK_SIZE+pos.x];
|
||||
}
|
||||
|
||||
bool Chunk::is_neighbour_air(const glm::vec3ub& pos) {
|
||||
if (pos.x+1 == CHUNK_SIZE) {
|
||||
return true;
|
||||
}
|
||||
if (pos.x-1 == CHUNK_SIZE) {
|
||||
return true;
|
||||
}
|
||||
if (pos.y+1 == CHUNK_SIZE) {
|
||||
return true;
|
||||
}
|
||||
if (pos.y-1 == CHUNK_SIZE) {
|
||||
return true;
|
||||
}
|
||||
if (pos.z+1 == CHUNK_SIZE) {
|
||||
return true;
|
||||
}
|
||||
if (pos.z-1 == CHUNK_SIZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pos.x+1 == 0) {
|
||||
return true;
|
||||
}
|
||||
if (pos.x-1 == 0) {
|
||||
return true;
|
||||
}
|
||||
if (pos.y+1 == 0) {
|
||||
return true;
|
||||
}
|
||||
if (pos.y-1 == 0) {
|
||||
return true;
|
||||
}
|
||||
if (pos.z+1 == 0) {
|
||||
return true;
|
||||
}
|
||||
if (pos.z-1 == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -24,25 +24,33 @@ SOFTWARE.
|
||||
#ifndef POLYGUN_WORLD_CHUNK_HPP
|
||||
#define POLYGUN_WORLD_CHUNK_HPP
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include "common/glm_ext.hpp"
|
||||
|
||||
namespace polygun::world {
|
||||
class Chunk {
|
||||
enum ChunkCompressionMode {
|
||||
CHUNK_COMPRESSION_MODE_NONE,
|
||||
CHUNK_COMPRESSION_MODE_HUFFMAN,
|
||||
CHUNK_COMPRESSION_MODE_FILL
|
||||
};
|
||||
|
||||
class Chunk final {
|
||||
public:
|
||||
Chunk();
|
||||
~Chunk() = default;
|
||||
|
||||
void add_node(int node_to_add, glm::vec3 pos);
|
||||
bool is_air(glm::vec3 pos);
|
||||
int get_node(glm::vec3 pos);
|
||||
bool is_neighbour_air(glm::vec3 pos);
|
||||
uint8_t to_compressed_data(std::vector<uint8_t>& output);
|
||||
void load_from_compressed_data(const char* data, uint16_t data_size, uint8_t compression_mode);
|
||||
void add_node(uint16_t node_to_add, const glm::vec3ub& pos);
|
||||
bool is_air(const glm::vec3ub& pos);
|
||||
uint16_t get_node(const glm::vec3ub& pos);
|
||||
bool is_neighbour_air(const glm::vec3ub& pos);
|
||||
|
||||
static const int CHUNK_SIZE = 32;
|
||||
|
||||
glm::vec3 chunk_pos;
|
||||
|
||||
private:
|
||||
int chunk_data[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE];
|
||||
uint16_t m_chunk_data[CHUNK_SIZE*CHUNK_SIZE*CHUNK_SIZE];
|
||||
};
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "greedy_meshing.hpp"
|
||||
#include "game/world/chunk.hpp"
|
||||
#include "common/world/chunk.hpp"
|
||||
|
||||
namespace polygun::engine {
|
||||
|
||||
@ -146,7 +146,7 @@ namespace polygun::engine {
|
||||
}
|
||||
|
||||
m_last_material = chunk_copy.get_node(glm::vec3(m_x, m_y, m_z));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -206,7 +206,7 @@ namespace polygun::engine {
|
||||
{1, 5, 7, 1, 7, 3}, // right
|
||||
{2, 7, 6, 2, 3, 7}, // front
|
||||
};
|
||||
|
||||
|
||||
static unsigned int slice_axises_indices[3][6] {
|
||||
{0, 3, 2, 5, 1, 4}, // bottom/top face check
|
||||
{1, 4, 2, 5, 0, 3}, // left/right face check
|
||||
@ -294,9 +294,9 @@ namespace polygun::engine {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
return {m_vertices, m_indices};
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ SOFTWARE.
|
||||
#include <config.hpp>
|
||||
|
||||
#include "game/engine/player_camera.hpp"
|
||||
#include "game/world/chunk.hpp"
|
||||
#include "common/world/chunk.hpp"
|
||||
#include "game/engine/greedy_meshing.hpp"
|
||||
#include "game/engine/resource_manager.hpp"
|
||||
#include "game/engine/content_registry.hpp"
|
||||
|
@ -1,93 +0,0 @@
|
||||
/*
|
||||
PolyGun
|
||||
|
||||
Copyright (c) 2023 kacperks https://kacperks.cubesoftware.xyz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "chunk.hpp"
|
||||
|
||||
#include "../engine/greedy_meshing.hpp"
|
||||
|
||||
namespace polygun::world {
|
||||
Chunk::Chunk() {
|
||||
for (int z = 0; z < CHUNK_SIZE; z++) {
|
||||
for (int y = 0; y < CHUNK_SIZE; y++) {
|
||||
for (int x = 0; x < CHUNK_SIZE; x++) {
|
||||
chunk_data[z][y][x] = 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Chunk::add_node(int node_to_add, glm::vec3 pos) {
|
||||
chunk_data[(int)pos.z][(int)pos.y][(int)pos.x] = node_to_add;
|
||||
}
|
||||
|
||||
bool Chunk::is_air(glm::vec3 pos) {
|
||||
return chunk_data[(int)pos.z][(int)pos.y][(int)pos.x] == 0;
|
||||
}
|
||||
|
||||
int Chunk::get_node(glm::vec3 pos) {
|
||||
return chunk_data[(int)pos.z][(int)pos.y][(int)pos.x];
|
||||
}
|
||||
|
||||
bool Chunk::is_neighbour_air(glm::vec3 pos) {
|
||||
if (pos.x+1 == CHUNK_SIZE) {
|
||||
return true;
|
||||
}
|
||||
if (pos.x-1 == CHUNK_SIZE) {
|
||||
return true;
|
||||
}
|
||||
if (pos.y+1 == CHUNK_SIZE) {
|
||||
return true;
|
||||
}
|
||||
if (pos.y-1 == CHUNK_SIZE) {
|
||||
return true;
|
||||
}
|
||||
if (pos.z+1 == CHUNK_SIZE) {
|
||||
return true;
|
||||
}
|
||||
if (pos.z-1 == CHUNK_SIZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pos.x+1 == 0) {
|
||||
return true;
|
||||
}
|
||||
if (pos.x-1 == 0) {
|
||||
return true;
|
||||
}
|
||||
if (pos.y+1 == 0) {
|
||||
return true;
|
||||
}
|
||||
if (pos.y-1 == 0) {
|
||||
return true;
|
||||
}
|
||||
if (pos.z+1 == 0) {
|
||||
return true;
|
||||
}
|
||||
if (pos.z-1 == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user