VoxelBuffer storage is now flat to reduce memory usage and simplify code

This commit is contained in:
Marc Gilleron 2016-05-03 00:09:07 +02:00
parent 3d3d996ed9
commit 68cf221d1a
4 changed files with 63 additions and 83 deletions

View File

@ -22,6 +22,22 @@ struct Vector3i {
return *this;
}
Vector3i operator+(const Vector3i & other) {
return Vector3i(x + other.x, y + other.y, z + other.z);
}
Vector3i operator-(const Vector3i & other) {
return Vector3i(x - other.x, y - other.y, z - other.z);
}
Vector3i operator*(int n) {
return Vector3i(x * n, y * n, z * n);
}
Vector3i operator/(int n) {
return Vector3i(x / n, y / n, z / n);
}
bool operator==(const Vector3i & other) {
return x == other.x && y == other.y && z == other.z;
}

View File

@ -103,36 +103,18 @@ Ref<Voxel> Voxel::_set_cube_uv_sides(const Vector2 atlas_pos[6]) {
};
const int uv6[SIDE_COUNT][6] = {
{
// LEFT
//0,1,3,0,1,2
2,0,1,2,1,3
},
{
// RIGHT
//0,3,1,0,2,3
2,1,0,2,3,1
},
{
// BOTTOM
//0,3,1,0,2,3
0,3,1,0,2,3
},
{
// TOP
//0,1,3,0,3,2
0,1,3,0,3,2
},
{
// BACK
//0,1,3,0,3,2
2,3,1,2,1,0
},
{
// FRONT
//1,0,3,0,2,3
3,2,1,2,0,1
}
// LEFT
{ 2,0,1,2,1,3 },
// RIGHT
{ 2,1,0,2,3,1 },
// BOTTOM
{ 0,3,1,0,2,3 },
// TOP
{ 0,1,3,0,3,2 },
// BACK
{ 2,3,1,2,1,0 },
// FRONT
{ 3,2,1,2,0,1 }
};
float s = 1.0 / (float)_library->get_atlas_size();

View File

@ -1,4 +1,8 @@
#include "voxel_buffer.h"
#include <string.h>
//#define VOXEL_AT(_data, x, y, z) data[z][x][y]
#define VOXEL_AT(_data, _x, _y, _z) _data[index(_x,_y,_z)]
VoxelBuffer::VoxelBuffer() {
@ -52,7 +56,7 @@ int VoxelBuffer::get_voxel(int x, int y, int z, unsigned int channel_index) cons
const Channel & channel = _channels[channel_index];
if (validate_local_pos(x, y, z) && channel.data) {
return channel.data[z][x][y];
return VOXEL_AT(channel.data, x,y,z);
}
else {
return channel.defval;
@ -65,7 +69,7 @@ int VoxelBuffer::get_voxel_local(int x, int y, int z, unsigned int channel_index
const Channel & channel = _channels[channel_index];
if (validate_local_pos(x, y, z) && channel.data) {
return channel.data[z][x][y];
return VOXEL_AT(channel.data, x, y, z);
}
else {
return channel.defval;
@ -87,7 +91,7 @@ void VoxelBuffer::set_voxel(int value, int x, int y, int z, unsigned int channel
if (channel.data == NULL) {
create_channel(channel_index, _size);
}
channel.data[z][x][y] = value;
VOXEL_AT(channel.data, x, y, z) = value;
}
}
@ -97,17 +101,9 @@ void VoxelBuffer::set_voxel_v(int value, Vector3 pos, unsigned int channel_index
void VoxelBuffer::fill(int defval, unsigned int channel_index) {
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
Channel & channel = _channels[channel_index];
for (unsigned int z = 0; z < _size.z; ++z) {
for (unsigned int x = 0; x < _size.x; ++x) {
uint8_t * column = channel.data[z][x];
for (unsigned int y = 0; y < _size.y; ++y) {
column[y] = defval;
}
}
}
Channel & channel = _channels[channel_index];
unsigned int volume = get_volume();
memset(channel.data, defval, volume);
}
bool VoxelBuffer::is_uniform(unsigned int channel_index) {
@ -116,24 +112,22 @@ bool VoxelBuffer::is_uniform(unsigned int channel_index) {
Channel & channel = _channels[channel_index];
if (channel.data == NULL)
return true;
uint8_t voxel = channel.data[0][0][0];
for (unsigned int z = 0; z < _size.z; ++z) {
for (unsigned int x = 0; x < _size.x; ++x) {
uint8_t * column = channel.data[z][x];
for (unsigned int y = 0; y < _size.y; ++y) {
if (column[y] != voxel) {
return false;
}
}
uint8_t voxel = channel.data[0];
unsigned int volume = get_volume();
for (unsigned int i = 0; i < volume; ++i) {
if (channel.data[i] != voxel) {
return false;
}
}
return true;
}
void VoxelBuffer::optimize() {
for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
if (_channels[i].data && is_uniform(i)) {
clear_channel(i, _channels[i].data[0][0][0]);
clear_channel(i, _channels[i].data[0]);
}
}
}
@ -141,35 +135,15 @@ void VoxelBuffer::optimize() {
void VoxelBuffer::create_channel(int i, Vector3i size, uint8_t defval) {
Channel & channel = _channels[i];
channel.data = (uint8_t***)memalloc(size.z * sizeof(uint8_t**));
for (unsigned int z = 0; z < size.z; ++z) {
uint8_t ** plane = (uint8_t**)memalloc(size.x * sizeof(uint8_t*));
channel.data[z] = plane;
for (unsigned int x = 0; x < size.x; ++x) {
uint8_t * column = (uint8_t*)memalloc(size.y * sizeof(uint8_t));
plane[x] = column;
for (unsigned int y = 0; y < size.y; ++y) {
column[y] = defval;
}
}
}
unsigned int volume = size.x * size.y * size.z;
channel.data = (uint8_t*)memalloc(volume * sizeof(uint8_t));
memset(channel.data, defval, volume);
}
void VoxelBuffer::delete_channel(int i, Vector3i size) {
Channel & channel = _channels[i];
for (unsigned int z = 0; z < size.z; ++z) {
for (unsigned int x = 0; x < size.x; ++x) {
memfree(channel.data[z][x]);
}
memfree(channel.data[z]);
}
Channel & channel = _channels[i];
memfree(channel.data);
channel.data = NULL;
}

View File

@ -17,11 +17,11 @@ class VoxelBuffer : public Reference {
struct Channel {
// Allocated when the channel is populated.
// Array of array of arrays, in order [z][x][y] because it makes vertical-wise access faster (the engine is Y-up).
// SUGG: move to flat storage?
uint8_t *** data;
// Flat array, in order [z][x][y] because it allows faster vertical-wise access (the engine is Y-up).
uint8_t * data;
uint8_t defval; // Default value when data is null
// Default value when data is null
uint8_t defval;
Channel() : data(NULL), defval(0) {}
};
@ -75,6 +75,14 @@ public:
&& z < _size.x;
}
_FORCE_INLINE_ unsigned int index(unsigned int x, unsigned int y, unsigned int z) const {
return (z * _size.z + x) * _size.x + y;
}
_FORCE_INLINE_ unsigned int get_volume() {
return _size.x * _size.y * _size.z;
}
private:
void create_channel(int i, Vector3i size, uint8_t defval=0);
void delete_channel(int i, Vector3i size);