mirror of
https://github.com/Relintai/godot_voxel.git
synced 2024-12-25 16:27:16 +01:00
Fixed VoxelBuffer binding mistakes and added fill and copy functions
This commit is contained in:
parent
ad124f418a
commit
be630c05be
51
vector3i.h
51
vector3i.h
@ -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
|
||||
|
124
voxel_buffer.cpp
124
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<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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user