From be630c05becc578eb99c058ad60efa84d6974bf5 Mon Sep 17 00:00:00 2001 From: Marc Gilleron Date: Thu, 5 May 2016 21:08:52 +0200 Subject: [PATCH] Fixed VoxelBuffer binding mistakes and added fill and copy functions --- vector3i.h | 51 +++++++++++++---- voxel_buffer.cpp | 124 ++++++++++++++++++++++++++++++++++++++--- voxel_buffer.h | 21 ++++++- voxel_mesh_builder.cpp | 8 +++ 4 files changed, 185 insertions(+), 19 deletions(-) diff --git a/vector3i.h b/vector3i.h index 0acfd59..69902a0 100644 --- a/vector3i.h +++ b/vector3i.h @@ -1,51 +1,82 @@ #ifndef VOXEL_VECTOR3I_H #define VOXEL_VECTOR3I_H +#include + 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 diff --git a/voxel_buffer.cpp b/voxel_buffer.cpp index e9f7e5d..41d2c57 100644 --- a/voxel_buffer.cpp +++ b/voxel_buffer.cpp @@ -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 other, unsigned int channel) { + ERR_FAIL_COND(other.is_null()); + copy_from(**other, channel); +} + +void VoxelBuffer::_copy_from_area_binding(Ref 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); +} diff --git a/voxel_buffer.h b/voxel_buffer.h index 2940271..db40b73 100644 --- a/voxel_buffer.h +++ b/voxel_buffer.h @@ -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 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 other, unsigned int channel); + void _copy_from_area_binding(Ref 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(); diff --git a/voxel_mesh_builder.cpp b/voxel_mesh_builder.cpp index aa32464..8c0ccc2 100644 --- a/voxel_mesh_builder.cpp +++ b/voxel_mesh_builder.cpp @@ -180,6 +180,14 @@ Ref VoxelMeshBuilder::build(Ref 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) {