Fixed VoxelBuffer binding mistakes and added fill and copy functions

This commit is contained in:
Marc Gilleron 2016-05-05 21:08:52 +02:00
parent ad124f418a
commit be630c05be
4 changed files with 185 additions and 19 deletions

View File

@ -1,51 +1,82 @@
#ifndef VOXEL_VECTOR3I_H
#define VOXEL_VECTOR3I_H
#include <core/math/vector3.h>
struct Vector3i {
int x;
int y;
int z;
Vector3i() : x(0), y(0), z(0) {}
_FORCE_INLINE_ Vector3i() : x(0), y(0), z(0) {}
Vector3i(int px, int py, int pz) : x(px), y(py), z(pz) {}
_FORCE_INLINE_ Vector3i(int px, int py, int pz) : x(px), y(py), z(pz) {}
Vector3i(const Vector3i & other) {
_FORCE_INLINE_ Vector3i(const Vector3i & other) {
*this = other;
}
Vector3i & operator=(const Vector3i & other) {
_FORCE_INLINE_ Vector3i(const Vector3 & f) {
x = Math::floor(f.x);
y = Math::floor(f.y);
z = Math::floor(f.z);
}
_FORCE_INLINE_ Vector3 to_vec3() {
return Vector3(x, y, z);
}
_FORCE_INLINE_ Vector3i & operator=(const Vector3i & other) {
x = other.x;
y = other.y;
z = other.z;
return *this;
}
Vector3i operator+(const Vector3i & other) {
_FORCE_INLINE_ Vector3i operator+(const Vector3i & other) const {
return Vector3i(x + other.x, y + other.y, z + other.z);
}
Vector3i operator-(const Vector3i & other) {
_FORCE_INLINE_ Vector3i operator-(const Vector3i & other) const {
return Vector3i(x - other.x, y - other.y, z - other.z);
}
Vector3i operator*(int n) {
_FORCE_INLINE_ Vector3i operator*(int n) const {
return Vector3i(x * n, y * n, z * n);
}
Vector3i operator/(int n) {
_FORCE_INLINE_ Vector3i operator/(int n) const {
return Vector3i(x / n, y / n, z / n);
}
bool operator==(const Vector3i & other) {
_FORCE_INLINE_ bool operator==(const Vector3i & other) const {
return x == other.x && y == other.y && z == other.z;
}
bool operator!=(const Vector3i & other) {
_FORCE_INLINE_ bool operator!=(const Vector3i & other) const {
return x != other.x && y != other.y && z != other.z;
}
void clamp_to(const Vector3i min, const Vector3i max) {
if (x < min.x) x = min.x;
if (y < min.y) y = min.y;
if (z < min.z) z = min.z;
if (x >= max.x) x = max.x;
if (y >= max.y) y = max.y;
if (z >= max.z) z = max.z;
}
};
struct Vector3iHasher {
static _FORCE_INLINE_ uint32_t hash(const Vector3i & v) {
uint32_t hash = hash_djb2_one_32(v.x);
hash = hash_djb2_one_32(v.y);
return hash_djb2_one_32(v.z);
}
};
#endif // VOXEL_VECTOR3I_H

View File

@ -101,11 +101,39 @@ 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];
if (channel.data == NULL && channel.defval == defval)
return;
else
create_channel_noinit(channel_index, _size);
unsigned int volume = get_volume();
memset(channel.data, defval, volume);
}
void VoxelBuffer::fill_area(int defval, Vector3i min, Vector3i max, unsigned int channel_index) {
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
min.clamp_to(Vector3i(0, 0, 0), _size);
max.clamp_to(Vector3i(0, 0, 0), _size);
Vector3i area_size = max - min;
Channel & channel = _channels[channel_index];
if (channel.data == NULL && channel.defval == defval)
return;
else
create_channel(channel_index, _size);
Vector3i pos;
for (pos.z = min.z; pos.z < max.z; ++pos.z) {
for (pos.x = min.x; pos.x < max.x; ++pos.x) {
unsigned int dst_ri = row_index(pos.x, pos.y, pos.z);
memset(&channel.data[dst_ri], defval, area_size.y * sizeof(uint8_t));
}
}
}
bool VoxelBuffer::is_uniform(unsigned int channel_index) {
ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, true);
@ -132,17 +160,86 @@ void VoxelBuffer::optimize() {
}
}
void VoxelBuffer::create_channel(int i, Vector3i size, uint8_t defval) {
void VoxelBuffer::copy_from(const VoxelBuffer & other, unsigned int channel_index) {
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
ERR_FAIL_COND(other._size == _size);
Channel & channel = _channels[channel_index];
const Channel & other_channel = other._channels[channel_index];
if (other_channel.data) {
if (channel.data == NULL) {
create_channel_noinit(channel_index, _size);
}
memcpy(channel.data, other_channel.data, get_volume() * sizeof(uint8_t));
}
else if(channel.data) {
delete_channel(channel_index, _size);
}
channel.defval = other_channel.defval;
}
void VoxelBuffer::copy_from(const VoxelBuffer & other, Vector3i src_min, Vector3i src_max, Vector3i dst_min, unsigned int channel_index) {
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
Channel & channel = _channels[channel_index];
const Channel & other_channel = other._channels[channel_index];
src_min.clamp_to(Vector3i(0, 0, 0), other._size);
src_max.clamp_to(Vector3i(0, 0, 0), other._size);
dst_min.clamp_to(Vector3i(0, 0, 0), _size);
Vector3i area_size = src_max - src_min;
Vector3i dst_max = src_min + area_size;
if (area_size == _size) {
copy_from(other, channel_index);
}
else {
if (other_channel.data) {
if (channel.data == NULL) {
create_channel(channel_index, _size);
}
// Copy row by row
Vector3i pos;
for (pos.z = 0; pos.z < area_size.z; ++pos.z) {
for (pos.x = 0; pos.x < area_size.x; ++pos.x) {
// Row direction is Y
unsigned int src_ri = other.row_index(pos.x + src_min.x, pos.y + src_min.y, pos.z + src_min.z);
unsigned int dst_ri = row_index(pos.x + dst_min.x, pos.y + dst_min.y, pos.z + dst_min.z);
memcpy(&channel.data[dst_ri], &other_channel.data[src_ri], area_size.y * sizeof(uint8_t));
}
}
}
else if (channel.defval != other_channel.defval) {
if (channel.data == NULL) {
create_channel(channel_index, _size);
}
// Set row by row
Vector3i pos;
for (pos.z = 0; pos.z < area_size.z; ++pos.z) {
for (pos.x = 0; pos.x < area_size.x; ++pos.x) {
unsigned int dst_ri = row_index(pos.x + dst_min.x, pos.y + dst_min.y, pos.z + dst_min.z);
memset(&channel.data[dst_ri], other_channel.defval, area_size.y * sizeof(uint8_t));
}
}
}
}
}
void VoxelBuffer::create_channel(int i, Vector3i size, uint8_t defval) {
create_channel_noinit(i, size);
memset(_channels[i].data, defval, get_volume() * sizeof(uint8_t));
}
void VoxelBuffer::create_channel_noinit(int i, Vector3i size) {
Channel & channel = _channels[i];
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];
memfree(channel.data);
channel.data = NULL;
@ -160,17 +257,30 @@ void VoxelBuffer::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_offset_x"), &VoxelBuffer::get_offset_x);
ObjectTypeDB::bind_method(_MD("get_offset_y"), &VoxelBuffer::get_offset_y);
ObjectTypeDB::bind_method(_MD("get_offset_z"), &VoxelBuffer::get_offset_z);
ObjectTypeDB::bind_method(_MD("get_offset"), &VoxelBuffer::_get_offset_binding);
ObjectTypeDB::bind_method(_MD("set_offset", "x", "y", "z"), &VoxelBuffer::set_offset);
ObjectTypeDB::bind_method(_MD("set_offset", "x", "y", "z"), &VoxelBuffer::_set_offset_binding);
ObjectTypeDB::bind_method(_MD("set_voxel", "value", "x", "y", "z", "channel"), &VoxelBuffer::set_voxel, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("set_voxel_v", "value", "pos", "channel"), &VoxelBuffer::set_voxel, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("get_voxel", "x", "y", "z", "channel"), &VoxelBuffer::set_voxel, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("set_voxel_v", "value", "pos", "channel"), &VoxelBuffer::set_voxel_v, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("get_voxel", "x", "y", "z", "channel"), &VoxelBuffer::get_voxel, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("fill", "value", "channel"), &VoxelBuffer::fill, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("fill_area", "value", "min", "max", "channel"), &VoxelBuffer::_fill_area_binding, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("copy_from", "other:VoxelBuffer", "channel"), &VoxelBuffer::_copy_from_binding, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("copy_from_area", "other:VoxelBuffer", "src_min", "src_max", "dst_min", "channel"), &VoxelBuffer::_copy_from_area_binding, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("is_uniform", "channel"), &VoxelBuffer::is_uniform, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("optimize"), &VoxelBuffer::optimize);
}
void VoxelBuffer::_copy_from_binding(Ref<VoxelBuffer> other, unsigned int channel) {
ERR_FAIL_COND(other.is_null());
copy_from(**other, channel);
}
void VoxelBuffer::_copy_from_area_binding(Ref<VoxelBuffer> other, Vector3 src_min, Vector3 src_max, Vector3 dst_min, unsigned int channel) {
ERR_FAIL_COND(other.is_null());
copy_from(**other, Vector3i(src_min), Vector3i(src_max), Vector3i(dst_min), channel);
}

View File

@ -12,9 +12,12 @@
class VoxelBuffer : public Reference {
OBJ_TYPE(VoxelBuffer, Reference)
public:
// Arbitrary value, 8 should be enough. Tweak for your needs.
static const int MAX_CHANNELS = 8;
private:
struct Channel {
// Allocated when the channel is populated.
// Flat array, in order [z][x][y] because it allows faster vertical-wise access (the engine is Y-up).
@ -48,12 +51,14 @@ public:
_FORCE_INLINE_ int get_size_x() const { return _size.x; }
_FORCE_INLINE_ int get_size_y() const { return _size.y; }
_FORCE_INLINE_ int get_size_z() const { return _size.z; }
_FORCE_INLINE_ Vector3i get_size() const { return _size; }
_FORCE_INLINE_ int get_offset_x() const { return _offset.x; }
_FORCE_INLINE_ int get_offset_y() const { return _offset.y; }
_FORCE_INLINE_ int get_offset_z() const { return _offset.z; }
_FORCE_INLINE_ void set_offset(int x, int y, int z) { _offset = Vector3i(x,y,z); }
_FORCE_INLINE_ void set_offset(Vector3i pos) { _offset = pos; }
int get_voxel(int x, int y, int z, unsigned int channel_index=0) const;
int get_voxel_local(int x, int y, int z, unsigned int channel_index=0) const;
@ -61,13 +66,14 @@ public:
void set_voxel_v(int value, Vector3 pos, unsigned int channel_index = 0);
void fill(int defval, unsigned int channel_index = 0);
//void fill_min_max(int value, int x0, int y0, int z0, int x1, int y1, int z1, unsigned int channel_index = 0);
void fill_area(int defval, Vector3i min, Vector3i max, unsigned int channel_index = 0);
bool is_uniform(unsigned int channel_index = 0);
void optimize();
//void copy_from(Ref<VoxelBuffer> other);
void copy_from(const VoxelBuffer & other, unsigned int channel_index=0);
void copy_from(const VoxelBuffer & other, Vector3i src_min, Vector3i src_max, Vector3i dst_min, unsigned int channel_index = 0);
_FORCE_INLINE_ bool validate_local_pos(unsigned int x, unsigned int y, unsigned int z) const {
return x < _size.x
@ -79,14 +85,25 @@ public:
return (z * _size.z + x) * _size.x + y;
}
_FORCE_INLINE_ unsigned int row_index(unsigned int x, unsigned int y, unsigned int z) const {
return (z * _size.z + x) * _size.x;
}
_FORCE_INLINE_ unsigned int get_volume() {
return _size.x * _size.y * _size.z;
}
private:
void create_channel_noinit(int i, Vector3i size);
void create_channel(int i, Vector3i size, uint8_t defval=0);
void delete_channel(int i, Vector3i size);
_FORCE_INLINE_ void _set_offset_binding(int x, int y, int z) { set_offset(x, y, z); }
_FORCE_INLINE_ Vector3 _get_offset_binding() const { return Vector3(_offset.x, _offset.y, _offset.z); }
void _copy_from_binding(Ref<VoxelBuffer> other, unsigned int channel);
void _copy_from_area_binding(Ref<VoxelBuffer> other, Vector3 src_min, Vector3 src_max, Vector3 dst_min, unsigned int channel);
_FORCE_INLINE_ void _fill_area_binding(int defval, Vector3 min, Vector3 max, unsigned int channel_index) { fill_area(defval, Vector3i(min), Vector3i(max), channel_index); }
protected:
static void _bind_methods();

View File

@ -180,6 +180,14 @@ Ref<Mesh> VoxelMeshBuilder::build(Ref<VoxelBuffer> buffer_ref) {
if (_bake_occlusion)
baked_occlusion_darkness = _baked_occlusion_darkness / 3.0;
// The technique is Culled faces.
// Could be improved with greedy meshing: https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/
// However I don't feel it's worth it yet:
// - Not so much gain for organic worlds with lots of texture variations
// - Works well with cubes but not with any shape
// - Slower
// => Could be implemented in a separate class?
// Iterate 3D padded data to extract voxel faces.
// This is the most intensive job in this class, so all required data should be as fit as possible.
for (unsigned int z = 1; z < buffer.get_size_z()-1; ++z) {