Added in 2 more classes and the updated vector3i from https://github.com/Zylann/godot_voxel. Also added their license to the top of these files.

This commit is contained in:
Relintai 2019-05-31 23:28:33 +02:00
parent 4d73c7e9c6
commit e2c8c5c2ca
8 changed files with 661 additions and 11 deletions

View File

@ -1 +0,0 @@
#include "vector3i.h"

View File

@ -2,7 +2,7 @@
#define VOXEL_H #define VOXEL_H
#include "core/reference.h" #include "core/reference.h"
#include "../collections/vector3i.h" #include "../math/vector3i.h"
#include "core/math/quat.h" #include "core/math/quat.h"
#include "core/resource.h" #include "core/resource.h"
#include "core/vector.h" #include "core/vector.h"

View File

@ -5,7 +5,7 @@
#include "core/reference.h" #include "core/reference.h"
#include "core/vector.h" #include "core/vector.h"
#include "../collections/vector3i.h" #include "../math/vector3i.h"
class VoxelLight : public Reference { class VoxelLight : public Reference {
GDCLASS(VoxelLight, Reference); GDCLASS(VoxelLight, Reference);

99
math/rect3i.h Normal file
View File

@ -0,0 +1,99 @@
/**
*
* Voxel Tools for Godot Engine
*
* Copyright(c) 2016 Marc Gilleron
*
* 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.
*
*/
#ifndef RECT3I_H
#define RECT3I_H
#include "vector3i.h"
#include <core/variant.h>
class Rect3i {
public:
Vector3i pos;
Vector3i size;
Rect3i() {}
Rect3i(Vector3i p_pos, Vector3i p_size) :
pos(p_pos),
size(p_size) {}
Rect3i(const Rect3i &other) :
pos(other.pos),
size(other.size) {}
static inline Rect3i from_center_extents(Vector3i center, Vector3i extents) {
return Rect3i(center - extents, 2 * extents);
}
static inline Rect3i get_bounding_box(Rect3i a, Rect3i b) {
Rect3i box;
box.pos.x = MIN(a.pos.x, b.pos.x);
box.pos.y = MIN(a.pos.y, b.pos.y);
box.pos.z = MIN(a.pos.z, b.pos.z);
Vector3i max_a = a.pos + a.size;
Vector3i max_b = b.pos + b.size;
box.size.x = MAX(max_a.x, max_b.x) - box.pos.x;
box.size.y = MAX(max_a.y, max_b.y) - box.pos.y;
box.size.z = MAX(max_a.z, max_b.z) - box.pos.z;
return box;
}
bool inline contains(Vector3i p_pos) const {
Vector3i end = pos + size;
return p_pos.x >= pos.x &&
p_pos.y >= pos.y &&
p_pos.z >= pos.z &&
p_pos.x < end.x &&
p_pos.y < end.y &&
p_pos.z < end.z;
}
String to_string() const {
return String("(o:{0}, s:{1})").format(varray(pos.to_vec3(), size.to_vec3()));
}
bool intersects(Rect3i other) {
if (pos.x > other.pos.x + other.size.x)
return false;
if (pos.y > other.pos.y + other.size.y)
return false;
if (pos.z > other.pos.z + other.size.z)
return false;
if (other.pos.x > pos.x + size.x)
return false;
if (other.pos.y > pos.y + size.y)
return false;
if (other.pos.z > pos.z + size.z)
return false;
return true;
}
};
inline bool operator!=(const Rect3i &a, const Rect3i &b) {
return a.pos != b.pos || a.size != b.size;
}
#endif // RECT3I_H

View File

@ -1,3 +1,22 @@
/**
*
* Voxel Tools for Godot Engine
*
* Copyright(c) 2016 Marc Gilleron
*
* 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.
*
*/
#ifndef VOXEL_VECTOR3I_H #ifndef VOXEL_VECTOR3I_H
#define VOXEL_VECTOR3I_H #define VOXEL_VECTOR3I_H
@ -65,13 +84,6 @@ struct Vector3i {
return *this; return *this;
} }
_FORCE_INLINE_ Vector3i &operator=(const Vector3 &other) {
x = (int) other.x;
y = (int) other.y;
z = (int) other.z;
return *this;
}
_FORCE_INLINE_ void operator+=(const Vector3i &other) { _FORCE_INLINE_ void operator+=(const Vector3i &other) {
x += other.x; x += other.x;
y += other.y; y += other.y;
@ -162,6 +174,27 @@ _FORCE_INLINE_ bool operator!=(const Vector3i &a, const Vector3i &b) {
return a.x != b.x || a.y != b.y || a.z != b.z; return a.x != b.x || a.y != b.y || a.z != b.z;
} }
_FORCE_INLINE_ Vector3i operator<<(const Vector3i &a, int b) {
return Vector3i(a.x << b, a.y << b, a.z << b);
}
_FORCE_INLINE_ Vector3i operator>>(const Vector3i &a, int b) {
return Vector3i(a.x >> b, a.y >> b, a.z >> b);
}
_FORCE_INLINE_ bool operator<(const Vector3i &a, const Vector3i &b) {
if (a.x == b.x) {
if (a.y == b.y) {
return a.z < b.z;
} else {
return a.y < b.y;
}
} else {
return a.x < b.x;
}
}
_FORCE_INLINE_ int Vector3i::distance_sq(const Vector3i &other) const { _FORCE_INLINE_ int Vector3i::distance_sq(const Vector3i &other) const {
return (other - *this).length_sq(); return (other - *this).length_sq();
} }

View File

@ -16,7 +16,7 @@
#include "scene/resources/concave_polygon_shape.h" #include "scene/resources/concave_polygon_shape.h"
#include "../library/voxelman_library.h" #include "../library/voxelman_library.h"
#include "../collections/vector3i.h" #include "../math/vector3i.h"
#include "../data/voxel.h" #include "../data/voxel.h"
#include "../library/voxelman_library.h" #include "../library/voxelman_library.h"
#include "../utility/marching_cubes_voxel_query.h" #include "../utility/marching_cubes_voxel_query.h"

349
world/voxel_buffer.cpp Normal file
View File

@ -0,0 +1,349 @@
/**
*
* Voxel Tools for Godot Engine
*
* Copyright(c) 2016 Marc Gilleron
*
* 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 "voxel_buffer.h"
#include <core/math/math_funcs.h>
#include <string.h>
const char *VoxelBuffer::CHANNEL_ID_HINT_STRING = "Type,Sdf,Data2,Data3,Data4,Data5,Data6,Data7";
VoxelBuffer::VoxelBuffer() {
_channels[CHANNEL_ISOLEVEL].defval = 255;
}
VoxelBuffer::~VoxelBuffer() {
clear();
}
void VoxelBuffer::create(int sx, int sy, int sz) {
if (sx <= 0 || sy <= 0 || sz <= 0) {
return;
}
Vector3i new_size(sx, sy, sz);
if (new_size != _size) {
for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
Channel &channel = _channels[i];
if (channel.data) {
// Channel already contained data
// TODO Optimize with realloc
delete_channel(i);
create_channel(i, new_size, channel.defval);
}
}
_size = new_size;
}
}
void VoxelBuffer::clear() {
for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
Channel &channel = _channels[i];
if (channel.data) {
delete_channel(i);
}
}
}
void VoxelBuffer::clear_channel(unsigned int channel_index, int clear_value) {
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
if (_channels[channel_index].data) {
delete_channel(channel_index);
}
_channels[channel_index].defval = clear_value;
}
void VoxelBuffer::set_default_values(uint8_t values[VoxelBuffer::MAX_CHANNELS]) {
for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
_channels[i].defval = values[i];
}
}
int VoxelBuffer::get_voxel(int x, int y, int z, unsigned int channel_index) const {
ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, 0);
const Channel &channel = _channels[channel_index];
if (validate_pos(x, y, z) && channel.data) {
return channel.data[index(x, y, z)];
} else {
return channel.defval;
}
}
void VoxelBuffer::set_voxel(int value, int x, int y, int z, unsigned int channel_index) {
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
ERR_FAIL_COND(!validate_pos(x, y, z));
Channel &channel = _channels[channel_index];
if (channel.data == NULL) {
if (channel.defval != value) {
// Allocate channel with same initial values as defval
create_channel(channel_index, _size, channel.defval);
channel.data[index(x, y, z)] = value;
}
} else {
channel.data[index(x, y, z)] = value;
}
}
// This version does not cause errors if out of bounds. Use only if it's okay to be outside.
void VoxelBuffer::try_set_voxel(int x, int y, int z, int value, unsigned int channel_index) {
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
if (!validate_pos(x, y, z)) {
return;
}
Channel &channel = _channels[channel_index];
if (channel.data == NULL) {
if (channel.defval != value) {
create_channel(channel_index, _size, channel.defval);
channel.data[index(x, y, z)] = value;
}
} else {
channel.data[index(x, y, z)] = value;
}
}
void VoxelBuffer::set_voxel_v(int value, Vector3 pos, unsigned int channel_index) {
set_voxel(value, pos.x, pos.y, pos.z, 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 is already optimized and uniform
if (channel.defval == defval) {
// No change
return;
} else {
// Just change default value
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);
Vector3i::sort_min_max(min, max);
min.clamp_to(Vector3i(0, 0, 0), _size + Vector3i(1, 1, 1));
max.clamp_to(Vector3i(0, 0, 0), _size + Vector3i(1, 1, 1));
Vector3i area_size = max - min;
if (area_size.x == 0 || area_size.y == 0 || area_size.z == 0) {
return;
}
Channel &channel = _channels[channel_index];
if (channel.data == NULL) {
if (channel.defval == defval) {
return;
} else {
create_channel(channel_index, _size, channel.defval);
}
}
Vector3i pos;
int volume = get_volume();
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 = index(pos.x, pos.y + min.y, pos.z);
CRASH_COND(dst_ri >= volume);
memset(&channel.data[dst_ri], defval, area_size.y * sizeof(uint8_t));
}
}
}
bool VoxelBuffer::is_uniform(unsigned int channel_index) const {
ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, true);
const Channel &channel = _channels[channel_index];
if (channel.data == NULL) {
// Channel has been optimized
return true;
}
// Channel isn't optimized, so must look at each voxel
uint8_t voxel = channel.data[0];
unsigned int volume = get_volume();
for (unsigned int i = 1; i < volume; ++i) {
if (channel.data[i] != voxel) {
return false;
}
}
return true;
}
void VoxelBuffer::compress_uniform_channels() {
for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
if (_channels[i].data && is_uniform(i)) {
clear_channel(i, _channels[i].data[0]);
}
}
}
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);
}
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];
Vector3i::sort_min_max(src_min, src_max);
src_min.clamp_to(Vector3i(0, 0, 0), other._size);
src_max.clamp_to(Vector3i(0, 0, 0), other._size + Vector3i(1, 1, 1));
dst_min.clamp_to(Vector3i(0, 0, 0), _size);
Vector3i area_size = src_max - src_min;
//Vector3i dst_max = dst_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, channel.defval);
}
// 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.index(pos.x + src_min.x, pos.y + src_min.y, pos.z + src_min.z);
unsigned int dst_ri = 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, channel.defval);
}
// 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 = 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));
}
}
}
}
}
uint8_t *VoxelBuffer::get_channel_raw(unsigned int channel_index) const {
ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, NULL);
const Channel &channel = _channels[channel_index];
return channel.data;
}
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));
}
void VoxelBuffer::delete_channel(int i) {
Channel &channel = _channels[i];
ERR_FAIL_COND(channel.data == NULL);
memfree(channel.data);
channel.data = NULL;
}
void VoxelBuffer::_bind_methods() {
ClassDB::bind_method(D_METHOD("create", "sx", "sy", "sz"), &VoxelBuffer::create);
ClassDB::bind_method(D_METHOD("clear"), &VoxelBuffer::clear);
ClassDB::bind_method(D_METHOD("get_size"), &VoxelBuffer::_get_size_binding);
ClassDB::bind_method(D_METHOD("get_size_x"), &VoxelBuffer::get_size_x);
ClassDB::bind_method(D_METHOD("get_size_y"), &VoxelBuffer::get_size_y);
ClassDB::bind_method(D_METHOD("get_size_z"), &VoxelBuffer::get_size_z);
ClassDB::bind_method(D_METHOD("set_voxel", "value", "x", "y", "z", "channel"), &VoxelBuffer::_set_voxel_binding, DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_voxel_f", "value", "x", "y", "z", "channel"), &VoxelBuffer::_set_voxel_f_binding, DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_voxel_v", "value", "pos", "channel"), &VoxelBuffer::set_voxel_v, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_voxel", "x", "y", "z", "channel"), &VoxelBuffer::_get_voxel_binding, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_voxel_f", "x", "y", "z", "channel"), &VoxelBuffer::get_voxel_f, DEFVAL(0));
ClassDB::bind_method(D_METHOD("fill", "value", "channel"), &VoxelBuffer::fill, DEFVAL(0));
ClassDB::bind_method(D_METHOD("fill_f", "value", "channel"), &VoxelBuffer::fill_f, DEFVAL(0));
ClassDB::bind_method(D_METHOD("fill_area", "value", "min", "max", "channel"), &VoxelBuffer::_fill_area_binding, DEFVAL(0));
ClassDB::bind_method(D_METHOD("copy_from", "other", "channel"), &VoxelBuffer::_copy_from_binding, DEFVAL(0));
ClassDB::bind_method(D_METHOD("copy_from_area", "other", "src_min", "src_max", "dst_min", "channel"), &VoxelBuffer::_copy_from_area_binding, DEFVAL(0));
ClassDB::bind_method(D_METHOD("is_uniform", "channel"), &VoxelBuffer::is_uniform);
ClassDB::bind_method(D_METHOD("optimize"), &VoxelBuffer::compress_uniform_channels);
BIND_ENUM_CONSTANT(CHANNEL_TYPE);
BIND_ENUM_CONSTANT(CHANNEL_ISOLEVEL);
BIND_ENUM_CONSTANT(CHANNEL_DATA2);
BIND_ENUM_CONSTANT(CHANNEL_DATA3);
BIND_ENUM_CONSTANT(CHANNEL_DATA4);
BIND_ENUM_CONSTANT(CHANNEL_DATA5);
BIND_ENUM_CONSTANT(CHANNEL_DATA6);
BIND_ENUM_CONSTANT(CHANNEL_DATA7);
BIND_ENUM_CONSTANT(MAX_CHANNELS);
}
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);
}

170
world/voxel_buffer.h Normal file
View File

@ -0,0 +1,170 @@
/**
*
* Voxel Tools for Godot Engine
*
* Copyright(c) 2016 Marc Gilleron
*
* 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.
*
*/
#ifndef VOXEL_BUFFER_H
#define VOXEL_BUFFER_H
#include "math/vector3i.h"
#include <core/reference.h>
#include <core/vector.h>
// Dense voxels data storage.
// Organized in 8-bit channels like images, all optional.
// Note: for float storage (marching cubes for example), you can map [0..256] to [0..1] and save 3 bytes per cell
class VoxelBuffer : public Reference {
GDCLASS(VoxelBuffer, Reference)
public:
enum ChannelId {
CHANNEL_TYPE = 0,
CHANNEL_ISOLEVEL,
CHANNEL_DATA2,
CHANNEL_DATA3,
CHANNEL_DATA4,
CHANNEL_DATA5,
CHANNEL_DATA6,
CHANNEL_DATA7,
// Arbitrary value, 8 should be enough. Tweak for your needs.
MAX_CHANNELS
};
// TODO use C++17 inline to initialize right here...
static const char *CHANNEL_ID_HINT_STRING;
// TODO Quantification options
// enum ChannelFormat {
// FORMAT_I8_Q256U, // 0..255 integer
// FORMAT_F8_Q1S, // -1..1 float stored in 8 bits
// FORMAT_F16_Q128S // -128..128 float stored in 16 bits
// };
// Converts -1..1 float into 0..255 integer
static inline int iso_to_byte(real_t iso) {
int v = static_cast<int>(128.f * iso + 128.f);
if (v > 255)
return 255;
else if (v < 0)
return 0;
return v;
}
// Converts 0..255 integer into -1..1 float
static inline real_t byte_to_iso(int b) {
return static_cast<float>(b - 128) / 128.f;
}
VoxelBuffer();
~VoxelBuffer();
void create(int sx, int sy, int sz);
void clear();
void clear_channel(unsigned int channel_index, int clear_value = 0);
_FORCE_INLINE_ void clear_channel_f(unsigned int channel_index, float clear_value = 0) { clear_channel(channel_index, iso_to_byte(clear_value)); }
_FORCE_INLINE_ const Vector3i &get_size() const { return _size; }
void set_default_values(uint8_t values[MAX_CHANNELS]);
int get_voxel(int x, int y, int z, unsigned int channel_index = 0) const;
void set_voxel(int value, int x, int y, int z, unsigned int channel_index = 0);
void set_voxel_v(int value, Vector3 pos, unsigned int channel_index = 0);
void try_set_voxel(int x, int y, int z, int value, unsigned int channel_index = 0);
_FORCE_INLINE_ void set_voxel_f(real_t value, int x, int y, int z, unsigned int channel_index = 0) { set_voxel(iso_to_byte(value), x, y, z, channel_index); }
_FORCE_INLINE_ real_t get_voxel_f(int x, int y, int z, unsigned int channel_index = 0) const { return byte_to_iso(get_voxel(x, y, z, channel_index)); }
_FORCE_INLINE_ int get_voxel(const Vector3i pos, unsigned int channel_index = 0) const { return get_voxel(pos.x, pos.y, pos.z, channel_index); }
_FORCE_INLINE_ void set_voxel(int value, const Vector3i pos, unsigned int channel_index = 0) { set_voxel(value, pos.x, pos.y, pos.z, channel_index); }
void fill(int defval, unsigned int channel_index = 0);
_FORCE_INLINE_ void fill_f(float value, unsigned int channel = 0) { fill(iso_to_byte(value), channel); }
void fill_area(int defval, Vector3i min, Vector3i max, unsigned int channel_index = 0);
bool is_uniform(unsigned int channel_index) const;
void compress_uniform_channels();
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_pos(unsigned int x, unsigned int y, unsigned int z) const {
return x < _size.x && y < _size.y && z < _size.z;
}
_FORCE_INLINE_ unsigned int index(unsigned int x, unsigned int y, unsigned int z) const {
return y + _size.y * (x + _size.x * z);
}
// _FORCE_INLINE_ unsigned int row_index(unsigned int x, unsigned int y, unsigned int z) const {
// return _size.y * (x + _size.x * z);
// }
_FORCE_INLINE_ unsigned int get_volume() const {
return _size.x * _size.y * _size.z;
}
uint8_t *get_channel_raw(unsigned int channel_index) const;
private:
void create_channel_noinit(int i, Vector3i size);
void create_channel(int i, Vector3i size, uint8_t defval);
void delete_channel(int i);
protected:
static void _bind_methods();
_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_ Vector3 _get_size_binding() const { return _size.to_vec3(); }
_FORCE_INLINE_ int _get_voxel_binding(int x, int y, int z, unsigned int channel) const { return get_voxel(x, y, z, channel); }
_FORCE_INLINE_ void _set_voxel_binding(int value, int x, int y, int z, unsigned int channel) { set_voxel(value, x, y, z, channel); }
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); }
_FORCE_INLINE_ void _set_voxel_f_binding(real_t value, int x, int y, int z, unsigned int channel) { set_voxel_f(value, x, y, z, channel); }
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).
uint8_t *data;
// Default value when data is null
uint8_t defval;
Channel() :
data(NULL),
defval(0) {}
};
// Each channel can store arbitary data.
// For example, you can decide to store colors (R, G, B, A), gameplay types (type, state, light) or both.
Channel _channels[MAX_CHANNELS];
// How many voxels are there in the three directions. All populated channels have the same size.
Vector3i _size;
};
VARIANT_ENUM_CAST(VoxelBuffer::ChannelId)
#endif // VOXEL_BUFFER_H