Clang-format

master
Marc Gilleron 2017-08-13 01:19:39 +02:00
parent bc75ec863b
commit 51596fef95
23 changed files with 668 additions and 788 deletions

View File

@ -1,8 +1,8 @@
#ifndef VOXEL_VECTOR3I_H #ifndef VOXEL_VECTOR3I_H
#define VOXEL_VECTOR3I_H #define VOXEL_VECTOR3I_H
#include <core/math/vector3.h>
#include <core/hashfuncs.h> #include <core/hashfuncs.h>
#include <core/math/vector3.h>
struct Vector3i { struct Vector3i {
@ -15,15 +15,17 @@ struct Vector3i {
int coords[3]; int coords[3];
}; };
_FORCE_INLINE_ Vector3i() : x(0), y(0), z(0) {} _FORCE_INLINE_ Vector3i()
: x(0), y(0), z(0) {}
_FORCE_INLINE_ 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) {}
_FORCE_INLINE_ Vector3i(const Vector3i & other) { _FORCE_INLINE_ Vector3i(const Vector3i &other) {
*this = other; *this = other;
} }
_FORCE_INLINE_ Vector3i(const Vector3 & f) { _FORCE_INLINE_ Vector3i(const Vector3 &f) {
x = Math::floor(f.x); x = Math::floor(f.x);
y = Math::floor(f.y); y = Math::floor(f.y);
z = Math::floor(f.z); z = Math::floor(f.z);
@ -34,39 +36,39 @@ struct Vector3i {
} }
_FORCE_INLINE_ int volume() const { _FORCE_INLINE_ int volume() const {
return x*y*z; return x * y * z;
} }
_FORCE_INLINE_ int length_sq() const { _FORCE_INLINE_ int length_sq() const {
return x*x + y*y + z*z; return x * x + y * y + z * z;
} }
_FORCE_INLINE_ real_t length() const { _FORCE_INLINE_ real_t length() const {
return Math::sqrt((real_t)length_sq()); return Math::sqrt((real_t)length_sq());
} }
_FORCE_INLINE_ int distance_sq(const Vector3i & other) const; _FORCE_INLINE_ int distance_sq(const Vector3i &other) const;
_FORCE_INLINE_ Vector3i & operator=(const Vector3i & other) { _FORCE_INLINE_ Vector3i &operator=(const Vector3i &other) {
x = other.x; x = other.x;
y = other.y; y = other.y;
z = other.z; z = other.z;
return *this; 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;
z += other.z; z += other.z;
} }
_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;
z -= other.z; z -= other.z;
} }
_FORCE_INLINE_ int & operator[](unsigned int i) { _FORCE_INLINE_ int &operator[](unsigned int i) {
return coords[i]; return coords[i];
} }
@ -80,70 +82,67 @@ struct Vector3i {
if (z >= max.z) z = max.z - 1; if (z >= max.z) z = max.z - 1;
} }
_FORCE_INLINE_ bool is_contained_in(const Vector3i & min, const Vector3i & max) { _FORCE_INLINE_ bool is_contained_in(const Vector3i &min, const Vector3i &max) {
return x >= min.x && y >= min.y && z >= min.z return x >= min.x && y >= min.y && z >= min.z && x < max.x && y < max.y && z < max.z;
&& x < max.x && y < max.y && z < max.z;
} }
_FORCE_INLINE_ Vector3i wrap(const Vector3i & size) { _FORCE_INLINE_ Vector3i wrap(const Vector3i &size) {
return Vector3i( return Vector3i(
x % size.x, x % size.x,
y % size.y, y % size.y,
z % size.z z % size.z);
);
} }
static void sort_min_max(Vector3i & a, Vector3i & b) { static void sort_min_max(Vector3i &a, Vector3i &b) {
sort_min_max(a.x, b.x); sort_min_max(a.x, b.x);
sort_min_max(a.y, b.y); sort_min_max(a.y, b.y);
sort_min_max(a.z, b.z); sort_min_max(a.z, b.z);
} }
private: private:
static _FORCE_INLINE_ void sort_min_max(int & a, int & b) { static _FORCE_INLINE_ void sort_min_max(int &a, int &b) {
if (a > b) { if (a > b) {
int temp = a; int temp = a;
a = b; a = b;
b = temp; b = temp;
} }
} }
}; };
_FORCE_INLINE_ Vector3i operator+(const Vector3i a, const Vector3i & b) { _FORCE_INLINE_ Vector3i operator+(const Vector3i a, const Vector3i &b) {
return Vector3i(a.x + b.x, a.y + b.y, a.z + b.z); return Vector3i(a.x + b.x, a.y + b.y, a.z + b.z);
} }
_FORCE_INLINE_ Vector3i operator-(const Vector3i & a, const Vector3i & b) { _FORCE_INLINE_ Vector3i operator-(const Vector3i &a, const Vector3i &b) {
return Vector3i(a.x - b.x, a.y - b.y, a.z - b.z); return Vector3i(a.x - b.x, a.y - b.y, a.z - b.z);
} }
_FORCE_INLINE_ Vector3i operator*(const Vector3i & a, int n) { _FORCE_INLINE_ Vector3i operator*(const Vector3i &a, int n) {
return Vector3i(a.x * n, a.y * n, a.z * n); return Vector3i(a.x * n, a.y * n, a.z * n);
} }
_FORCE_INLINE_ Vector3i operator*(int n, const Vector3i & a) { _FORCE_INLINE_ Vector3i operator*(int n, const Vector3i &a) {
return Vector3i(a.x * n, a.y * n, a.z * n); return Vector3i(a.x * n, a.y * n, a.z * n);
} }
_FORCE_INLINE_ Vector3i operator/(const Vector3i & a, int n) { _FORCE_INLINE_ Vector3i operator/(const Vector3i &a, int n) {
return Vector3i(a.x / n, a.y / n, a.z / n); return Vector3i(a.x / n, a.y / n, a.z / n);
} }
_FORCE_INLINE_ bool operator==(const Vector3i & a, const Vector3i & b) { _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_ bool operator!=(const Vector3i & a, const Vector3i & b) { _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_ 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();
} }
struct Vector3iHasher { struct Vector3iHasher {
static _FORCE_INLINE_ uint32_t hash(const Vector3i & v) { static _FORCE_INLINE_ uint32_t hash(const Vector3i &v) {
uint32_t hash = hash_djb2_one_32(v.x); uint32_t hash = hash_djb2_one_32(v.x);
hash = hash_djb2_one_32(v.y, hash); hash = hash_djb2_one_32(v.y, hash);
return hash_djb2_one_32(v.z, hash); return hash_djb2_one_32(v.z, hash);
@ -151,4 +150,3 @@ struct Vector3iHasher {
}; };
#endif // VOXEL_VECTOR3I_H #endif // VOXEL_VECTOR3I_H

130
voxel.cpp
View File

@ -2,13 +2,13 @@
#include "voxel_library.h" #include "voxel_library.h"
#include "voxel_mesher.h" #include "voxel_mesher.h"
Voxel::Voxel() : Reference(), Voxel::Voxel()
_id(-1), : Reference(),
_material_id(0), _id(-1),
_is_transparent(false), _material_id(0),
_library(NULL), _is_transparent(false),
_color(1.f, 1.f, 1.f) _library(NULL),
{} _color(1.f, 1.f, 1.f) {}
Ref<Voxel> Voxel::set_name(String name) { Ref<Voxel> Voxel::set_name(String name) {
_name = name; _name = name;
@ -41,60 +41,48 @@ Ref<Voxel> Voxel::set_transparent(bool t) {
Ref<Voxel> Voxel::set_cube_geometry(float sy) { Ref<Voxel> Voxel::set_cube_geometry(float sy) {
const Vector3 vertices[SIDE_COUNT][6] = { const Vector3 vertices[SIDE_COUNT][6] = {
{ { // LEFT
// LEFT Vector3(0, 0, 0),
Vector3(0, 0, 0), Vector3(0, sy, 0),
Vector3(0, sy, 0), Vector3(0, sy, 1),
Vector3(0, sy, 1), Vector3(0, 0, 0),
Vector3(0, 0, 0), Vector3(0, sy, 1),
Vector3(0, sy, 1), Vector3(0, 0, 1) },
Vector3(0, 0, 1), { // RIGHT
}, Vector3(1, 0, 0),
{ Vector3(1, sy, 1),
// RIGHT Vector3(1, sy, 0),
Vector3(1, 0, 0), Vector3(1, 0, 0),
Vector3(1, sy, 1), Vector3(1, 0, 1),
Vector3(1, sy, 0), Vector3(1, sy, 1) },
Vector3(1, 0, 0), { // BOTTOM
Vector3(1, 0, 1), Vector3(0, 0, 0),
Vector3(1, sy, 1) Vector3(1, 0, 1),
}, Vector3(1, 0, 0),
{ Vector3(0, 0, 0),
// BOTTOM Vector3(0, 0, 1),
Vector3(0, 0, 0), Vector3(1, 0, 1) },
Vector3(1, 0, 1), { // TOP
Vector3(1, 0, 0), Vector3(0, sy, 0),
Vector3(0, 0, 0), Vector3(1, sy, 0),
Vector3(0, 0, 1), Vector3(1, sy, 1),
Vector3(1, 0, 1) Vector3(0, sy, 0),
}, Vector3(1, sy, 1),
{ Vector3(0, sy, 1) },
// TOP { // BACK
Vector3(0, sy, 0), Vector3(0, 0, 0),
Vector3(1, sy, 0), Vector3(1, 0, 0),
Vector3(1, sy, 1), Vector3(1, sy, 0),
Vector3(0, sy, 0), Vector3(0, 0, 0),
Vector3(1, sy, 1), Vector3(1, sy, 0),
Vector3(0, sy, 1) Vector3(0, sy, 0) },
}, { // FRONT
{ Vector3(1, 0, 1),
// BACK Vector3(0, 0, 1),
Vector3(0, 0, 0), Vector3(1, sy, 1),
Vector3(1, 0, 0), Vector3(0, 0, 1),
Vector3(1, sy, 0), Vector3(0, sy, 1),
Vector3(0, 0, 0), Vector3(1, sy, 1) }
Vector3(1, sy, 0),
Vector3(0, sy, 0),
},
{
// FRONT
Vector3(1, 0, 1),
Vector3(0, 0, 1),
Vector3(1, sy, 1),
Vector3(0, 0, 1),
Vector3(0, sy, 1),
Vector3(1, sy, 1)
}
}; };
for (unsigned int side = 0; side < SIDE_COUNT; ++side) { for (unsigned int side = 0; side < SIDE_COUNT; ++side) {
@ -121,17 +109,17 @@ Ref<Voxel> Voxel::_set_cube_uv_sides(const Vector2 atlas_pos[6]) {
const int uv6[SIDE_COUNT][6] = { const int uv6[SIDE_COUNT][6] = {
// LEFT // LEFT
{ 2,0,1,2,1,3 }, { 2, 0, 1, 2, 1, 3 },
// RIGHT // RIGHT
{ 2,1,0,2,3,1 }, { 2, 1, 0, 2, 3, 1 },
// BOTTOM // BOTTOM
{ 0,3,1,0,2,3 }, { 0, 3, 1, 0, 2, 3 },
// TOP // TOP
{ 0,1,3,0,3,2 }, { 0, 1, 3, 0, 3, 2 },
// BACK // BACK
{ 2,3,1,2,1,0 }, { 2, 3, 1, 2, 1, 0 },
// FRONT // FRONT
{ 3,2,1,2,0,1 } { 3, 2, 1, 2, 0, 1 }
}; };
float s = 1.0 / (float)_library->get_atlas_size(); float s = 1.0 / (float)_library->get_atlas_size();
@ -197,9 +185,7 @@ void Voxel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cube_uv_all_sides", "atlas_pos"), &Voxel::set_cube_uv_all_sides); ClassDB::bind_method(D_METHOD("set_cube_uv_all_sides", "atlas_pos"), &Voxel::set_cube_uv_all_sides);
ClassDB::bind_method(D_METHOD("set_cube_uv_tbs_sides", "top_atlas_pos", "side_atlas_pos", "bottom_atlas_pos"), &Voxel::set_cube_uv_tbs_sides); ClassDB::bind_method(D_METHOD("set_cube_uv_tbs_sides", "top_atlas_pos", "side_atlas_pos", "bottom_atlas_pos"), &Voxel::set_cube_uv_tbs_sides);
BIND_CONSTANT( CHANNEL_TYPE ) BIND_CONSTANT(CHANNEL_TYPE)
BIND_CONSTANT( CHANNEL_ISOLEVEL ) BIND_CONSTANT(CHANNEL_ISOLEVEL)
BIND_CONSTANT( CHANNEL_DATA ) BIND_CONSTANT(CHANNEL_DATA)
} }

17
voxel.h
View File

@ -60,13 +60,13 @@ public:
// Getters for native usage only // Getters for native usage only
const PoolVector<Vector3> & get_model_vertices() const { return _model_vertices; } const PoolVector<Vector3> &get_model_vertices() const { return _model_vertices; }
const PoolVector<Vector3> & get_model_normals() const { return _model_normals; } const PoolVector<Vector3> &get_model_normals() const { return _model_normals; }
const PoolVector<Vector2> & get_model_uv() const { return _model_uv; } const PoolVector<Vector2> &get_model_uv() const { return _model_uv; }
const PoolVector<Vector3> & get_model_side_vertices(unsigned int side) const { return _model_side_vertices[side]; } const PoolVector<Vector3> &get_model_side_vertices(unsigned int side) const { return _model_side_vertices[side]; }
const PoolVector<Vector2> & get_model_side_uv(unsigned int side) const { return _model_side_uv[side]; } const PoolVector<Vector2> &get_model_side_uv(unsigned int side) const { return _model_side_uv[side]; }
void set_library_ptr(VoxelLibrary * lib) { _library = lib; } void set_library_ptr(VoxelLibrary *lib) { _library = lib; }
protected: protected:
Ref<Voxel> _set_cube_uv_sides(const Vector2 atlas_pos[6]); Ref<Voxel> _set_cube_uv_sides(const Vector2 atlas_pos[6]);
@ -74,7 +74,7 @@ protected:
static void _bind_methods(); static void _bind_methods();
private: private:
VoxelLibrary * _library; VoxelLibrary *_library;
// Identifiers // Identifiers
int _id; int _id;
@ -93,11 +93,8 @@ private:
PoolVector<Vector2> _model_side_uv[SIDE_COUNT]; PoolVector<Vector2> _model_side_uv[SIDE_COUNT];
// TODO Child voxel types // TODO Child voxel types
}; };
VARIANT_ENUM_CAST(Voxel::ChannelMode) VARIANT_ENUM_CAST(Voxel::ChannelMode)
#endif // VOXEL_TYPE_H #endif // VOXEL_TYPE_H

View File

@ -1,10 +1,8 @@
#include "voxel_buffer.h" #include "voxel_buffer.h"
#include <string.h>
#include <math_funcs.h> #include <math_funcs.h>
#include <string.h>
VoxelBuffer::VoxelBuffer() { VoxelBuffer::VoxelBuffer() {
} }
VoxelBuffer::~VoxelBuffer() { VoxelBuffer::~VoxelBuffer() {
@ -18,7 +16,7 @@ void VoxelBuffer::create(int sx, int sy, int sz) {
Vector3i new_size(sx, sy, sz); Vector3i new_size(sx, sy, sz);
if (new_size != _size) { if (new_size != _size) {
for (unsigned int i = 0; i < MAX_CHANNELS; ++i) { for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
Channel & channel = _channels[i]; Channel &channel = _channels[i];
if (channel.data) { if (channel.data) {
// TODO Optimize with realloc // TODO Optimize with realloc
delete_channel(i); delete_channel(i);
@ -31,7 +29,7 @@ void VoxelBuffer::create(int sx, int sy, int sz) {
void VoxelBuffer::clear() { void VoxelBuffer::clear() {
for (unsigned int i = 0; i < MAX_CHANNELS; ++i) { for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
Channel & channel = _channels[i]; Channel &channel = _channels[i];
if (channel.data) { if (channel.data) {
delete_channel(i); delete_channel(i);
} }
@ -40,13 +38,13 @@ void VoxelBuffer::clear() {
void VoxelBuffer::clear_channel(unsigned int channel_index, int clear_value) { void VoxelBuffer::clear_channel(unsigned int channel_index, int clear_value) {
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS); ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
if(_channels[channel_index].data) if (_channels[channel_index].data)
delete_channel(channel_index); delete_channel(channel_index);
_channels[channel_index].defval = clear_value; _channels[channel_index].defval = clear_value;
} }
void VoxelBuffer::set_default_values(uint8_t values[VoxelBuffer::MAX_CHANNELS]) { void VoxelBuffer::set_default_values(uint8_t values[VoxelBuffer::MAX_CHANNELS]) {
for(unsigned int i = 0; i < MAX_CHANNELS; ++i) { for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
_channels[i].defval = values[i]; _channels[i].defval = values[i];
} }
} }
@ -54,29 +52,27 @@ void VoxelBuffer::set_default_values(uint8_t values[VoxelBuffer::MAX_CHANNELS])
int VoxelBuffer::get_voxel(int x, int y, int z, unsigned int channel_index) const { int VoxelBuffer::get_voxel(int x, int y, int z, unsigned int channel_index) const {
ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, 0); ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, 0);
const Channel & channel = _channels[channel_index]; const Channel &channel = _channels[channel_index];
if (validate_pos(x, y, z) && channel.data) { if (validate_pos(x, y, z) && channel.data) {
return channel.data[index(x,y,z)]; return channel.data[index(x, y, z)];
} } else {
else {
return channel.defval; return channel.defval;
} }
} }
void VoxelBuffer::set_voxel(int value, int x, int y, int z, unsigned int channel_index) { 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_INDEX(channel_index, MAX_CHANNELS);
ERR_FAIL_COND(!validate_pos(x, y, z)); ERR_FAIL_COND(!validate_pos(x, y, z));
Channel & channel = _channels[channel_index]; Channel &channel = _channels[channel_index];
if (channel.data == NULL) { if (channel.data == NULL) {
if (channel.defval != value) { if (channel.defval != value) {
create_channel(channel_index, _size); create_channel(channel_index, _size);
channel.data[index(x, y, z)] = value; channel.data[index(x, y, z)] = value;
} }
} } else {
else {
channel.data[index(x, y, z)] = value; channel.data[index(x, y, z)] = value;
} }
} }
@ -88,7 +84,7 @@ void VoxelBuffer::set_voxel_v(int value, Vector3 pos, unsigned int channel_index
void VoxelBuffer::fill(int defval, unsigned int channel_index) { void VoxelBuffer::fill(int defval, unsigned int channel_index) {
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS); ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
Channel & channel = _channels[channel_index]; Channel &channel = _channels[channel_index];
if (channel.data == NULL && channel.defval == defval) if (channel.data == NULL && channel.defval == defval)
return; return;
else else
@ -104,10 +100,10 @@ void VoxelBuffer::fill_area(int defval, Vector3i min, Vector3i max, unsigned int
Vector3i::sort_min_max(min, max); Vector3i::sort_min_max(min, max);
min.clamp_to(Vector3i(0, 0, 0), _size); min.clamp_to(Vector3i(0, 0, 0), _size);
max.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; Vector3i area_size = max - min;
Channel & channel = _channels[channel_index]; Channel &channel = _channels[channel_index];
if (channel.data == NULL) { if (channel.data == NULL) {
if (channel.defval == defval) if (channel.defval == defval)
return; return;
@ -127,7 +123,7 @@ void VoxelBuffer::fill_area(int defval, Vector3i min, Vector3i max, unsigned int
bool VoxelBuffer::is_uniform(unsigned int channel_index) const { bool VoxelBuffer::is_uniform(unsigned int channel_index) const {
ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, true); ERR_FAIL_INDEX_V(channel_index, MAX_CHANNELS, true);
const Channel & channel = _channels[channel_index]; const Channel &channel = _channels[channel_index];
if (channel.data == NULL) if (channel.data == NULL)
return true; return true;
@ -150,37 +146,36 @@ void VoxelBuffer::optimize() {
} }
} }
void VoxelBuffer::copy_from(const VoxelBuffer & other, unsigned int channel_index) { void VoxelBuffer::copy_from(const VoxelBuffer &other, unsigned int channel_index) {
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS); ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
ERR_FAIL_COND(other._size == _size); ERR_FAIL_COND(other._size == _size);
Channel & channel = _channels[channel_index]; Channel &channel = _channels[channel_index];
const Channel & other_channel = other._channels[channel_index]; const Channel &other_channel = other._channels[channel_index];
if (other_channel.data) { if (other_channel.data) {
if (channel.data == NULL) { if (channel.data == NULL) {
create_channel_noinit(channel_index, _size); create_channel_noinit(channel_index, _size);
} }
memcpy(channel.data, other_channel.data, get_volume() * sizeof(uint8_t)); memcpy(channel.data, other_channel.data, get_volume() * sizeof(uint8_t));
} } else if (channel.data) {
else if(channel.data) {
delete_channel(channel_index); delete_channel(channel_index);
} }
channel.defval = other_channel.defval; 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) { 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); ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
Channel & channel = _channels[channel_index]; Channel &channel = _channels[channel_index];
const Channel & other_channel = other._channels[channel_index]; const Channel &other_channel = other._channels[channel_index];
Vector3i::sort_min_max(src_min, src_max); Vector3i::sort_min_max(src_min, src_max);
src_min.clamp_to(Vector3i(0, 0, 0), other._size); src_min.clamp_to(Vector3i(0, 0, 0), other._size);
src_max.clamp_to(Vector3i(0, 0, 0), other._size + Vector3i(1,1,1)); src_max.clamp_to(Vector3i(0, 0, 0), other._size + Vector3i(1, 1, 1));
dst_min.clamp_to(Vector3i(0, 0, 0), _size); dst_min.clamp_to(Vector3i(0, 0, 0), _size);
Vector3i area_size = src_max - src_min; Vector3i area_size = src_max - src_min;
@ -188,8 +183,7 @@ void VoxelBuffer::copy_from(const VoxelBuffer & other, Vector3i src_min, Vector3
if (area_size == _size) { if (area_size == _size) {
copy_from(other, channel_index); copy_from(other, channel_index);
} } else {
else {
if (other_channel.data) { if (other_channel.data) {
if (channel.data == NULL) { if (channel.data == NULL) {
create_channel(channel_index, _size); create_channel(channel_index, _size);
@ -204,8 +198,7 @@ void VoxelBuffer::copy_from(const VoxelBuffer & other, Vector3i src_min, Vector3
memcpy(&channel.data[dst_ri], &other_channel.data[src_ri], area_size.y * sizeof(uint8_t)); memcpy(&channel.data[dst_ri], &other_channel.data[src_ri], area_size.y * sizeof(uint8_t));
} }
} }
} } else if (channel.defval != other_channel.defval) {
else if (channel.defval != other_channel.defval) {
if (channel.data == NULL) { if (channel.data == NULL) {
create_channel(channel_index, _size); create_channel(channel_index, _size);
} }
@ -227,13 +220,13 @@ void VoxelBuffer::create_channel(int i, Vector3i size, uint8_t defval) {
} }
void VoxelBuffer::create_channel_noinit(int i, Vector3i size) { void VoxelBuffer::create_channel_noinit(int i, Vector3i size) {
Channel & channel = _channels[i]; Channel &channel = _channels[i];
unsigned int volume = size.x * size.y * size.z; unsigned int volume = size.x * size.y * size.z;
channel.data = (uint8_t*)memalloc(volume * sizeof(uint8_t)); channel.data = (uint8_t *)memalloc(volume * sizeof(uint8_t));
} }
void VoxelBuffer::delete_channel(int i) { void VoxelBuffer::delete_channel(int i) {
Channel & channel = _channels[i]; Channel &channel = _channels[i];
ERR_FAIL_COND(channel.data == NULL); ERR_FAIL_COND(channel.data == NULL);
memfree(channel.data); memfree(channel.data);
channel.data = NULL; channel.data = NULL;
@ -261,7 +254,6 @@ void VoxelBuffer::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_uniform", "channel"), &VoxelBuffer::is_uniform, DEFVAL(0)); ClassDB::bind_method(D_METHOD("is_uniform", "channel"), &VoxelBuffer::is_uniform, DEFVAL(0));
ClassDB::bind_method(D_METHOD("optimize"), &VoxelBuffer::optimize); ClassDB::bind_method(D_METHOD("optimize"), &VoxelBuffer::optimize);
} }
void VoxelBuffer::_copy_from_binding(Ref<VoxelBuffer> other, unsigned int channel) { void VoxelBuffer::_copy_from_binding(Ref<VoxelBuffer> other, unsigned int channel) {

View File

@ -1,9 +1,9 @@
#ifndef VOXEL_BUFFER_H #ifndef VOXEL_BUFFER_H
#define VOXEL_BUFFER_H #define VOXEL_BUFFER_H
#include "vector3i.h"
#include <reference.h> #include <reference.h>
#include <vector.h> #include <vector.h>
#include "vector3i.h"
// Dense voxels data storage. // Dense voxels data storage.
// Organized in 8-bit channels like images, all optional. // Organized in 8-bit channels like images, all optional.
@ -19,9 +19,9 @@ public:
// Converts -1..1 float into 0..255 integer // Converts -1..1 float into 0..255 integer
static inline int iso_to_byte(real_t iso) { static inline int iso_to_byte(real_t iso) {
int v = static_cast<int>(128.f * iso + 128.f); int v = static_cast<int>(128.f * iso + 128.f);
if(v > 255) if (v > 255)
return 255; return 255;
else if(v < 0) else if (v < 0)
return 0; return 0;
return v; return v;
} }
@ -36,17 +36,17 @@ public:
void create(int sx, int sy, int sz); void create(int sx, int sy, int sz);
void clear(); void clear();
void clear_channel(unsigned int channel_index, int clear_value=0); void clear_channel(unsigned int channel_index, int clear_value = 0);
_FORCE_INLINE_ Vector3i get_size() const { return _size; } _FORCE_INLINE_ Vector3i get_size() const { return _size; }
void set_default_values(uint8_t values[MAX_CHANNELS]); void set_default_values(uint8_t values[MAX_CHANNELS]);
int get_voxel(int x, int y, int z, unsigned int channel_index=0) const; 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(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 set_voxel_v(int value, Vector3 pos, unsigned int channel_index = 0);
_FORCE_INLINE_ void set_voxel_iso(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_ void set_voxel_iso(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_iso(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_ real_t get_voxel_iso(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_ 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); } _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); }
@ -57,13 +57,11 @@ public:
void optimize(); void optimize();
void copy_from(const VoxelBuffer & other, unsigned int channel_index=0); 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); 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 { _FORCE_INLINE_ bool validate_pos(unsigned int x, unsigned int y, unsigned int z) const {
return x < _size.x return x < _size.x && y < _size.y && z < _size.x;
&& y < _size.y
&& z < _size.x;
} }
_FORCE_INLINE_ unsigned int index(unsigned int x, unsigned int y, unsigned int z) const { _FORCE_INLINE_ unsigned int index(unsigned int x, unsigned int y, unsigned int z) const {
@ -80,7 +78,7 @@ public:
private: private:
void create_channel_noinit(int i, Vector3i size); void create_channel_noinit(int i, Vector3i size);
void create_channel(int i, Vector3i size, uint8_t defval=0); void create_channel(int i, Vector3i size, uint8_t defval = 0);
void delete_channel(int i); void delete_channel(int i);
protected: protected:
@ -95,18 +93,19 @@ protected:
void _copy_from_binding(Ref<VoxelBuffer> other, unsigned int 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); 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 _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_iso_binding(real_t value, int x, int y, int z, unsigned int channel) { set_voxel_iso(value, x,y,z, channel); } _FORCE_INLINE_ void _set_voxel_iso_binding(real_t value, int x, int y, int z, unsigned int channel) { set_voxel_iso(value, x, y, z, channel); }
private: private:
struct Channel { struct Channel {
// Allocated when the channel is populated. // 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). // Flat array, in order [z][x][y] because it allows faster vertical-wise access (the engine is Y-up).
uint8_t * data; uint8_t *data;
// Default value when data is null // Default value when data is null
uint8_t defval; uint8_t defval;
Channel() : data(NULL), defval(0) {} Channel()
: data(NULL), defval(0) {}
}; };
// Each channel can store arbitary data. // Each channel can store arbitary data.
@ -115,8 +114,6 @@ private:
// How many voxels are there in the three directions. All populated channels have the same size. // How many voxels are there in the three directions. All populated channels have the same size.
Vector3i _size; Vector3i _size;
}; };
#endif // VOXEL_BUFFER_H #endif // VOXEL_BUFFER_H

View File

@ -1,6 +1,7 @@
#include "voxel_library.h" #include "voxel_library.h"
VoxelLibrary::VoxelLibrary() : Reference(), _atlas_size(1) { VoxelLibrary::VoxelLibrary()
: Reference(), _atlas_size(1) {
// Defaults // Defaults
create_voxel(0, "air")->set_transparent(true); create_voxel(0, "air")->set_transparent(true);
create_voxel(1, "solid")->set_transparent(false)->set_cube_geometry(); create_voxel(1, "solid")->set_transparent(false)->set_cube_geometry();
@ -40,6 +41,4 @@ void VoxelLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_voxel", "id"), &VoxelLibrary::_get_voxel_bind); ClassDB::bind_method(D_METHOD("get_voxel", "id"), &VoxelLibrary::_get_voxel_bind);
ClassDB::bind_method(D_METHOD("set_atlas_size", "square_size"), &VoxelLibrary::set_atlas_size); ClassDB::bind_method(D_METHOD("set_atlas_size", "square_size"), &VoxelLibrary::set_atlas_size);
} }

View File

@ -1,8 +1,8 @@
#ifndef VOXEL_LIBRARY_H #ifndef VOXEL_LIBRARY_H
#define VOXEL_LIBRARY_H #define VOXEL_LIBRARY_H
#include <reference.h>
#include "voxel.h" #include "voxel.h"
#include <reference.h>
class VoxelLibrary : public Reference { class VoxelLibrary : public Reference {
GDCLASS(VoxelLibrary, Reference) GDCLASS(VoxelLibrary, Reference)
@ -22,7 +22,7 @@ public:
// Internal getters // Internal getters
_FORCE_INLINE_ bool has_voxel(int id) const { return _voxel_types[id].is_valid(); } _FORCE_INLINE_ bool has_voxel(int id) const { return _voxel_types[id].is_valid(); }
_FORCE_INLINE_ const Voxel & get_voxel_const(int id) const { return **_voxel_types[id]; } _FORCE_INLINE_ const Voxel &get_voxel_const(int id) const { return **_voxel_types[id]; }
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -32,8 +32,6 @@ protected:
private: private:
Ref<Voxel> _voxel_types[MAX_VOXEL_TYPES]; Ref<Voxel> _voxel_types[MAX_VOXEL_TYPES];
int _atlas_size; int _atlas_size;
}; };
#endif // VOXEL_LIBRARY_H #endif // VOXEL_LIBRARY_H

View File

@ -5,22 +5,22 @@
// VoxelBlock // VoxelBlock
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
MeshInstance * VoxelBlock::get_mesh_instance(const Node & root) { MeshInstance *VoxelBlock::get_mesh_instance(const Node &root) {
if (mesh_instance_path.is_empty()) if (mesh_instance_path.is_empty())
return NULL; return NULL;
Node * n = root.get_node(mesh_instance_path); Node *n = root.get_node(mesh_instance_path);
if (n == NULL) if (n == NULL)
return NULL; return NULL;
return n->cast_to<MeshInstance>(); return n->cast_to<MeshInstance>();
} }
// Helper // Helper
VoxelBlock * VoxelBlock::create(Vector3i bpos, Ref<VoxelBuffer> buffer) { VoxelBlock *VoxelBlock::create(Vector3i bpos, Ref<VoxelBuffer> buffer) {
const int bs = VoxelBlock::SIZE; const int bs = VoxelBlock::SIZE;
ERR_FAIL_COND_V(buffer.is_null(), NULL); ERR_FAIL_COND_V(buffer.is_null(), NULL);
ERR_FAIL_COND_V(buffer->get_size() != Vector3i(bs, bs, bs), NULL); ERR_FAIL_COND_V(buffer->get_size() != Vector3i(bs, bs, bs), NULL);
VoxelBlock * block = memnew(VoxelBlock); VoxelBlock *block = memnew(VoxelBlock);
block->pos = bpos; block->pos = bpos;
block->voxels = buffer; block->voxels = buffer;
@ -28,14 +28,16 @@ VoxelBlock * VoxelBlock::create(Vector3i bpos, Ref<VoxelBuffer> buffer) {
return block; return block;
} }
VoxelBlock::VoxelBlock(): voxels(NULL) { VoxelBlock::VoxelBlock()
: voxels(NULL) {
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// VoxelMap // VoxelMap
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
VoxelMap::VoxelMap() : _last_accessed_block(NULL) { VoxelMap::VoxelMap()
: _last_accessed_block(NULL) {
for (unsigned int i = 0; i < VoxelBuffer::MAX_CHANNELS; ++i) { for (unsigned int i = 0; i < VoxelBuffer::MAX_CHANNELS; ++i) {
_default_voxel[i] = 0; _default_voxel[i] = 0;
} }
@ -47,7 +49,7 @@ VoxelMap::~VoxelMap() {
int VoxelMap::get_voxel(Vector3i pos, unsigned int c) { int VoxelMap::get_voxel(Vector3i pos, unsigned int c) {
Vector3i bpos = voxel_to_block(pos); Vector3i bpos = voxel_to_block(pos);
VoxelBlock * block = get_block(bpos); VoxelBlock *block = get_block(bpos);
if (block == NULL) { if (block == NULL) {
return _default_voxel[c]; return _default_voxel[c];
} }
@ -57,7 +59,7 @@ int VoxelMap::get_voxel(Vector3i pos, unsigned int c) {
void VoxelMap::set_voxel(int value, Vector3i pos, unsigned int c) { void VoxelMap::set_voxel(int value, Vector3i pos, unsigned int c) {
Vector3i bpos = voxel_to_block(pos); Vector3i bpos = voxel_to_block(pos);
VoxelBlock * block = get_block(bpos); VoxelBlock *block = get_block(bpos);
if (block == NULL) { if (block == NULL) {
@ -83,11 +85,11 @@ int VoxelMap::get_default_voxel(unsigned int channel) {
return _default_voxel[channel]; return _default_voxel[channel];
} }
VoxelBlock * VoxelMap::get_block(Vector3i bpos) { VoxelBlock *VoxelMap::get_block(Vector3i bpos) {
if (_last_accessed_block && _last_accessed_block->pos == bpos) { if (_last_accessed_block && _last_accessed_block->pos == bpos) {
return _last_accessed_block; return _last_accessed_block;
} }
VoxelBlock ** p = _blocks.getptr(bpos); VoxelBlock **p = _blocks.getptr(bpos);
if (p) { if (p) {
_last_accessed_block = *p; _last_accessed_block = *p;
return _last_accessed_block; return _last_accessed_block;
@ -95,7 +97,7 @@ VoxelBlock * VoxelMap::get_block(Vector3i bpos) {
return NULL; return NULL;
} }
void VoxelMap::set_block(Vector3i bpos, VoxelBlock * block) { void VoxelMap::set_block(Vector3i bpos, VoxelBlock *block) {
ERR_FAIL_COND(block == NULL); ERR_FAIL_COND(block == NULL);
if (_last_accessed_block == NULL || _last_accessed_block->pos == bpos) { if (_last_accessed_block == NULL || _last_accessed_block->pos == bpos) {
_last_accessed_block = block; _last_accessed_block = block;
@ -105,12 +107,11 @@ void VoxelMap::set_block(Vector3i bpos, VoxelBlock * block) {
void VoxelMap::set_block_buffer(Vector3i bpos, Ref<VoxelBuffer> buffer) { void VoxelMap::set_block_buffer(Vector3i bpos, Ref<VoxelBuffer> buffer) {
ERR_FAIL_COND(buffer.is_null()); ERR_FAIL_COND(buffer.is_null());
VoxelBlock * block = get_block(bpos); VoxelBlock *block = get_block(bpos);
if (block == NULL) { if (block == NULL) {
block = VoxelBlock::create(bpos, *buffer); block = VoxelBlock::create(bpos, *buffer);
set_block(bpos, block); set_block(bpos, block);
} } else {
else {
block->voxels = buffer; block->voxels = buffer;
} }
} }
@ -120,35 +121,35 @@ bool VoxelMap::has_block(Vector3i pos) const {
} }
Vector3i g_moore_neighboring_3d[26] = { Vector3i g_moore_neighboring_3d[26] = {
Vector3i(-1,-1,-1), Vector3i(-1, -1, -1),
Vector3i(0,-1,-1), Vector3i(0, -1, -1),
Vector3i(1,-1,-1), Vector3i(1, -1, -1),
Vector3i(-1,-1,0), Vector3i(-1, -1, 0),
Vector3i(0,-1,0), Vector3i(0, -1, 0),
Vector3i(1,-1,0), Vector3i(1, -1, 0),
Vector3i(-1,-1,1), Vector3i(-1, -1, 1),
Vector3i(0,-1,1), Vector3i(0, -1, 1),
Vector3i(1,-1,1), Vector3i(1, -1, 1),
Vector3i(-1,0,-1), Vector3i(-1, 0, -1),
Vector3i(0,0,-1), Vector3i(0, 0, -1),
Vector3i(1,0,-1), Vector3i(1, 0, -1),
Vector3i(-1,0,0), Vector3i(-1, 0, 0),
//Vector3i(0,0,0), //Vector3i(0,0,0),
Vector3i(1,0,0), Vector3i(1, 0, 0),
Vector3i(-1,0,1), Vector3i(-1, 0, 1),
Vector3i(0,0,1), Vector3i(0, 0, 1),
Vector3i(1,0,1), Vector3i(1, 0, 1),
Vector3i(-1,1,-1), Vector3i(-1, 1, -1),
Vector3i(0,1,-1), Vector3i(0, 1, -1),
Vector3i(1,1,-1), Vector3i(1, 1, -1),
Vector3i(-1,1,0), Vector3i(-1, 1, 0),
Vector3i(0,1,0), Vector3i(0, 1, 0),
Vector3i(1,1,0), Vector3i(1, 1, 0),
Vector3i(-1,1,1), Vector3i(-1, 1, 1),
Vector3i(0,1,1), Vector3i(0, 1, 1),
Vector3i(1,1,1), Vector3i(1, 1, 1),
}; };
bool VoxelMap::is_block_surrounded(Vector3i pos) const { bool VoxelMap::is_block_surrounded(Vector3i pos) const {
@ -161,17 +162,17 @@ bool VoxelMap::is_block_surrounded(Vector3i pos) const {
return true; return true;
} }
void VoxelMap::get_buffer_copy(Vector3i min_pos, VoxelBuffer & dst_buffer, unsigned int channels_mask) { void VoxelMap::get_buffer_copy(Vector3i min_pos, VoxelBuffer &dst_buffer, unsigned int channels_mask) {
Vector3i max_pos = min_pos + dst_buffer.get_size(); Vector3i max_pos = min_pos + dst_buffer.get_size();
Vector3i min_block_pos = voxel_to_block(min_pos); Vector3i min_block_pos = voxel_to_block(min_pos);
Vector3i max_block_pos = voxel_to_block(max_pos - Vector3i(1,1,1)) + Vector3i(1,1,1); Vector3i max_block_pos = voxel_to_block(max_pos - Vector3i(1, 1, 1)) + Vector3i(1, 1, 1);
ERR_FAIL_COND((max_block_pos - min_block_pos) != Vector3(3, 3, 3)); ERR_FAIL_COND((max_block_pos - min_block_pos) != Vector3(3, 3, 3));
for(unsigned int channel = 0; channel < VoxelBuffer::MAX_CHANNELS; ++channel) { for (unsigned int channel = 0; channel < VoxelBuffer::MAX_CHANNELS; ++channel) {
if(((1 << channel) & channels_mask) == 0) { if (((1 << channel) & channels_mask) == 0) {
continue; continue;
} }
@ -180,27 +181,23 @@ void VoxelMap::get_buffer_copy(Vector3i min_pos, VoxelBuffer & dst_buffer, unsig
for (bpos.x = min_block_pos.x; bpos.x < max_block_pos.x; ++bpos.x) { for (bpos.x = min_block_pos.x; bpos.x < max_block_pos.x; ++bpos.x) {
for (bpos.y = min_block_pos.y; bpos.y < max_block_pos.y; ++bpos.y) { for (bpos.y = min_block_pos.y; bpos.y < max_block_pos.y; ++bpos.y) {
VoxelBlock * block = get_block(bpos); VoxelBlock *block = get_block(bpos);
if (block) { if (block) {
VoxelBuffer & src_buffer = **block->voxels; VoxelBuffer &src_buffer = **block->voxels;
Vector3i offset = block_to_voxel(bpos); Vector3i offset = block_to_voxel(bpos);
// Note: copy_from takes care of clamping the area if it's on an edge // Note: copy_from takes care of clamping the area if it's on an edge
dst_buffer.copy_from(src_buffer, min_pos - offset, max_pos - offset, offset - min_pos, channel); dst_buffer.copy_from(src_buffer, min_pos - offset, max_pos - offset, offset - min_pos, channel);
} } else {
else {
Vector3i offset = block_to_voxel(bpos); Vector3i offset = block_to_voxel(bpos);
dst_buffer.fill_area( dst_buffer.fill_area(
_default_voxel[channel], _default_voxel[channel],
offset - min_pos, offset - min_pos,
offset - min_pos + Vector3i(VoxelBlock::SIZE,VoxelBlock::SIZE, VoxelBlock::SIZE) offset - min_pos + Vector3i(VoxelBlock::SIZE, VoxelBlock::SIZE, VoxelBlock::SIZE));
);
} }
} }
} }
} }
} }
} }
@ -209,11 +206,11 @@ void VoxelMap::remove_blocks_not_in_area(Vector3i min, Vector3i max) {
Vector3i::sort_min_max(min, max); Vector3i::sort_min_max(min, max);
Vector<Vector3i> to_remove; Vector<Vector3i> to_remove;
const Vector3i * key = NULL; const Vector3i *key = NULL;
while (key = _blocks.next(key)) { while (key = _blocks.next(key)) {
VoxelBlock * block_ref = _blocks.get(*key); VoxelBlock *block_ref = _blocks.get(*key);
ERR_FAIL_COND(block_ref == NULL); // Should never trigger ERR_FAIL_COND(block_ref == NULL); // Should never trigger
if (block_ref->pos.is_contained_in(min, max)) { if (block_ref->pos.is_contained_in(min, max)) {
@ -234,10 +231,10 @@ void VoxelMap::remove_blocks_not_in_area(Vector3i min, Vector3i max) {
} }
void VoxelMap::clear() { void VoxelMap::clear() {
const Vector3i * key = NULL; const Vector3i *key = NULL;
while (key = _blocks.next(key)) { while (key = _blocks.next(key)) {
VoxelBlock * block_ref = _blocks.get(*key); VoxelBlock *block_ref = _blocks.get(*key);
if(block_ref == NULL) { if (block_ref == NULL) {
OS::get_singleton()->printerr("Unexpected NULL in VoxelMap::clear()"); OS::get_singleton()->printerr("Unexpected NULL in VoxelMap::clear()");
} }
memdelete(block_ref); memdelete(block_ref);
@ -246,7 +243,6 @@ void VoxelMap::clear() {
_last_accessed_block = NULL; _last_accessed_block = NULL;
} }
void VoxelMap::_bind_methods() { void VoxelMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_voxel", "x", "y", "z", "c"), &VoxelMap::_get_voxel_binding, DEFVAL(0)); ClassDB::bind_method(D_METHOD("get_voxel", "x", "y", "z", "c"), &VoxelMap::_get_voxel_binding, DEFVAL(0));
@ -263,12 +259,9 @@ void VoxelMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_block_size"), &VoxelMap::get_block_size); ClassDB::bind_method(D_METHOD("get_block_size"), &VoxelMap::get_block_size);
//ADD_PROPERTY(PropertyInfo(Variant::INT, "iterations"), _SCS("set_iterations"), _SCS("get_iterations")); //ADD_PROPERTY(PropertyInfo(Variant::INT, "iterations"), _SCS("set_iterations"), _SCS("get_iterations"));
} }
void VoxelMap::_get_buffer_copy_binding(Vector3 pos, Ref<VoxelBuffer> dst_buffer_ref, unsigned int channel) { void VoxelMap::_get_buffer_copy_binding(Vector3 pos, Ref<VoxelBuffer> dst_buffer_ref, unsigned int channel) {
ERR_FAIL_COND(dst_buffer_ref.is_null()); ERR_FAIL_COND(dst_buffer_ref.is_null());
get_buffer_copy(Vector3i(pos), **dst_buffer_ref, channel); get_buffer_copy(Vector3i(pos), **dst_buffer_ref, channel);
} }

View File

@ -1,12 +1,11 @@
#ifndef VOXEL_MAP_H #ifndef VOXEL_MAP_H
#define VOXEL_MAP_H #define VOXEL_MAP_H
#include <scene/main/node.h> #include "voxel_buffer.h"
#include <core/hash_map.h> #include <core/hash_map.h>
#include <scene/3d/mesh_instance.h> #include <scene/3d/mesh_instance.h>
#include <scene/3d/physics_body.h> #include <scene/3d/physics_body.h>
#include "voxel_buffer.h" #include <scene/main/node.h>
// Fixed-size voxel container used in VoxelMap. Used internally. // Fixed-size voxel container used in VoxelMap. Used internally.
class VoxelBlock { class VoxelBlock {
@ -19,16 +18,14 @@ public:
Vector3i pos; Vector3i pos;
NodePath mesh_instance_path; NodePath mesh_instance_path;
static VoxelBlock * create(Vector3i bpos, Ref<VoxelBuffer> buffer); static VoxelBlock *create(Vector3i bpos, Ref<VoxelBuffer> buffer);
MeshInstance * get_mesh_instance(const Node & root); MeshInstance *get_mesh_instance(const Node &root);
private: private:
VoxelBlock(); VoxelBlock();
}; };
// Infinite voxel storage by means of octants like Gridmap // Infinite voxel storage by means of octants like Gridmap
class VoxelMap : public Reference { class VoxelMap : public Reference {
GDCLASS(VoxelMap, Reference) GDCLASS(VoxelMap, Reference)
@ -36,18 +33,16 @@ public:
// Converts voxel coodinates into block coordinates // Converts voxel coodinates into block coordinates
static _FORCE_INLINE_ Vector3i voxel_to_block(Vector3i pos) { static _FORCE_INLINE_ Vector3i voxel_to_block(Vector3i pos) {
return Vector3i( return Vector3i(
pos.x >> VoxelBlock::SIZE_POW2, pos.x >> VoxelBlock::SIZE_POW2,
pos.y >> VoxelBlock::SIZE_POW2, pos.y >> VoxelBlock::SIZE_POW2,
pos.z >> VoxelBlock::SIZE_POW2 pos.z >> VoxelBlock::SIZE_POW2);
);
} }
static _FORCE_INLINE_ Vector3i to_local(Vector3i pos) { static _FORCE_INLINE_ Vector3i to_local(Vector3i pos) {
return Vector3i( return Vector3i(
pos.x & VoxelBlock::SIZE_MASK, pos.x & VoxelBlock::SIZE_MASK,
pos.y & VoxelBlock::SIZE_MASK, pos.y & VoxelBlock::SIZE_MASK,
pos.z & VoxelBlock::SIZE_MASK pos.z & VoxelBlock::SIZE_MASK);
);
} }
// Converts block coodinates into voxel coordinates // Converts block coodinates into voxel coordinates
@ -61,27 +56,26 @@ public:
int get_voxel(Vector3i pos, unsigned int c = 0); int get_voxel(Vector3i pos, unsigned int c = 0);
void set_voxel(int value, Vector3i pos, unsigned int c = 0); void set_voxel(int value, Vector3i pos, unsigned int c = 0);
void set_default_voxel(int value, unsigned int channel=0); void set_default_voxel(int value, unsigned int channel = 0);
int get_default_voxel(unsigned int channel=0); int get_default_voxel(unsigned int channel = 0);
// Gets a copy of all voxels in the area starting at min_pos having the same size as dst_buffer. // Gets a copy of all voxels in the area starting at min_pos having the same size as dst_buffer.
void get_buffer_copy(Vector3i min_pos, VoxelBuffer & dst_buffer, unsigned int channels_mask = 1); void get_buffer_copy(Vector3i min_pos, VoxelBuffer &dst_buffer, unsigned int channels_mask = 1);
// Moves the given buffer into a block of the map. The buffer is referenced, no copy is made. // Moves the given buffer into a block of the map. The buffer is referenced, no copy is made.
void set_block_buffer(Vector3i bpos, Ref<VoxelBuffer> buffer); void set_block_buffer(Vector3i bpos, Ref<VoxelBuffer> buffer);
void remove_blocks_not_in_area(Vector3i min, Vector3i max); void remove_blocks_not_in_area(Vector3i min, Vector3i max);
VoxelBlock * get_block(Vector3i bpos); VoxelBlock *get_block(Vector3i bpos);
bool has_block(Vector3i pos) const; bool has_block(Vector3i pos) const;
bool is_block_surrounded(Vector3i pos) const; bool is_block_surrounded(Vector3i pos) const;
void clear(); void clear();
private: private:
void set_block(Vector3i bpos, VoxelBlock * block); void set_block(Vector3i bpos, VoxelBlock *block);
_FORCE_INLINE_ int get_block_size() const { return VoxelBlock::SIZE; } _FORCE_INLINE_ int get_block_size() const { return VoxelBlock::SIZE; }
@ -103,13 +97,11 @@ private:
uint8_t _default_voxel[VoxelBuffer::MAX_CHANNELS]; uint8_t _default_voxel[VoxelBuffer::MAX_CHANNELS];
// Blocks stored with a spatial hash in all 3D directions // Blocks stored with a spatial hash in all 3D directions
HashMap<Vector3i, VoxelBlock*, Vector3iHasher> _blocks; HashMap<Vector3i, VoxelBlock *, Vector3iHasher> _blocks;
// Voxel access will most frequently be in contiguous areas, so the same blocks are accessed. // Voxel access will most frequently be in contiguous areas, so the same blocks are accessed.
// To prevent too much hashing, this reference is checked before. // To prevent too much hashing, this reference is checked before.
VoxelBlock * _last_accessed_block; VoxelBlock *_last_accessed_block;
}; };
#endif // VOXEL_MAP_H #endif // VOXEL_MAP_H

View File

@ -19,7 +19,7 @@
// / | / | // / | / |
// o----8----o | // o----8----o |
// | o---2-|---o // | o---2-|---o
// 4 / 5 / // 4 / 5 /
// | 3 | 1 // | 3 | 1
// |/ |/ // |/ |/
// o----0----o // o----0----o
@ -31,44 +31,44 @@ static const unsigned int CORNER_COUNT = 8;
static const unsigned int EDGE_COUNT = 12; static const unsigned int EDGE_COUNT = 12;
static const Vector3 g_corner_position[CORNER_COUNT] = { static const Vector3 g_corner_position[CORNER_COUNT] = {
Vector3(0, 0, 0), Vector3(0, 0, 0),
Vector3(1, 0, 0), Vector3(1, 0, 0),
Vector3(1, 0, 1), Vector3(1, 0, 1),
Vector3(0, 0, 1), Vector3(0, 0, 1),
Vector3(0, 1, 0), Vector3(0, 1, 0),
Vector3(1, 1, 0), Vector3(1, 1, 0),
Vector3(1, 1, 1), Vector3(1, 1, 1),
Vector3(0, 1, 1) Vector3(0, 1, 1)
}; };
static const unsigned int g_side_coord[Voxel::SIDE_COUNT] = { 0, 0, 1, 1, 2, 2 }; static const unsigned int g_side_coord[Voxel::SIDE_COUNT] = { 0, 0, 1, 1, 2, 2 };
static const unsigned int g_side_sign[Voxel::SIDE_COUNT] = { 0, 1, 0, 1, 0, 1 }; static const unsigned int g_side_sign[Voxel::SIDE_COUNT] = { 0, 1, 0, 1, 0, 1 };
static const Vector3i g_side_normals[Voxel::SIDE_COUNT] = { static const Vector3i g_side_normals[Voxel::SIDE_COUNT] = {
Vector3i(-1, 0, 0), Vector3i(-1, 0, 0),
Vector3i(1, 0, 0), Vector3i(1, 0, 0),
Vector3i(0, -1, 0), Vector3i(0, -1, 0),
Vector3i(0, 1, 0), Vector3i(0, 1, 0),
Vector3i(0, 0, -1), Vector3i(0, 0, -1),
Vector3i(0, 0, 1), Vector3i(0, 0, 1),
}; };
static const unsigned int g_side_corners[Voxel::SIDE_COUNT][4] = { static const unsigned int g_side_corners[Voxel::SIDE_COUNT][4] = {
{ 0, 3, 7, 4 }, { 0, 3, 7, 4 },
{ 1, 2, 6, 5 }, { 1, 2, 6, 5 },
{ 0, 1, 2, 3 }, { 0, 1, 2, 3 },
{ 4, 5, 6, 7 }, { 4, 5, 6, 7 },
{ 0, 1, 5, 4 }, { 0, 1, 5, 4 },
{ 3, 2, 6, 7 } { 3, 2, 6, 7 }
}; };
static const unsigned int g_side_edges[Voxel::SIDE_COUNT][4] = { static const unsigned int g_side_edges[Voxel::SIDE_COUNT][4] = {
{ 3, 7, 11, 4 }, { 3, 7, 11, 4 },
{ 1, 6, 9, 5 }, { 1, 6, 9, 5 },
{ 0, 1, 2, 3 }, { 0, 1, 2, 3 },
{ 8, 9, 10, 11 }, { 8, 9, 10, 11 },
{ 0, 5, 8, 4 }, { 0, 5, 8, 4 },
{ 2, 6, 10, 7 } { 2, 6, 10, 7 }
}; };
// 3---2 // 3---2
@ -84,54 +84,52 @@ static const unsigned int g_side_edges[Voxel::SIDE_COUNT][4] = {
//}; //};
static const Vector3i g_corner_inormals[CORNER_COUNT] = { static const Vector3i g_corner_inormals[CORNER_COUNT] = {
Vector3i(-1, -1, -1), Vector3i(-1, -1, -1),
Vector3i(1, -1, -1), Vector3i(1, -1, -1),
Vector3i(1, -1, 1), Vector3i(1, -1, 1),
Vector3i(-1, -1, 1), Vector3i(-1, -1, 1),
Vector3i(-1, 1, -1), Vector3i(-1, 1, -1),
Vector3i(1, 1, -1), Vector3i(1, 1, -1),
Vector3i(1, 1, 1), Vector3i(1, 1, 1),
Vector3i(-1, 1, 1) Vector3i(-1, 1, 1)
}; };
static const Vector3i g_edge_inormals[EDGE_COUNT] = { static const Vector3i g_edge_inormals[EDGE_COUNT] = {
Vector3i(0, -1, -1), Vector3i(0, -1, -1),
Vector3i(1, -1, 0), Vector3i(1, -1, 0),
Vector3i(0, -1, 1), Vector3i(0, -1, 1),
Vector3i(-1, -1, 0), Vector3i(-1, -1, 0),
Vector3i(-1, 0, -1), Vector3i(-1, 0, -1),
Vector3i(1, 0, -1), Vector3i(1, 0, -1),
Vector3i(1, 0, 1), Vector3i(1, 0, 1),
Vector3i(-1, 0, 1), Vector3i(-1, 0, 1),
Vector3i(0, 1, -1), Vector3i(0, 1, -1),
Vector3i(1, 1, 0), Vector3i(1, 1, 0),
Vector3i(0, 1, 1), Vector3i(0, 1, 1),
Vector3i(-1, 1, 0) Vector3i(-1, 1, 0)
}; };
static const unsigned int g_edge_corners[EDGE_COUNT][2] = { static const unsigned int g_edge_corners[EDGE_COUNT][2] = {
{ 0, 1 }, { 1, 2 }, { 2, 3 }, {3, 0}, { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 },
{ 0, 4 }, { 1, 5 }, { 2, 6 }, {3, 7}, { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 },
{ 4, 5 }, { 5, 6 }, { 6, 7 }, {7, 4} { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 4 }
}; };
VoxelMesher::VoxelMesher()
VoxelMesher::VoxelMesher(): : _baked_occlusion_darkness(0.75),
_baked_occlusion_darkness(0.75), _bake_occlusion(true) {}
_bake_occlusion(true)
{}
void VoxelMesher::set_library(Ref<VoxelLibrary> library) { void VoxelMesher::set_library(Ref<VoxelLibrary> library) {
ERR_FAIL_COND(library.is_null()); ERR_FAIL_COND(library.is_null());
_library = library; _library = library;
} }
void VoxelMesher::set_material(Ref<Material> material, unsigned int id) { void VoxelMesher::set_material(Ref<Material> material, unsigned int id) {
ERR_FAIL_COND(id >= MAX_MATERIALS); ERR_FAIL_COND(id >= MAX_MATERIALS);
_materials[id] = material; _materials[id] = material;
} }
Ref<Material> VoxelMesher::get_material(unsigned int id) const { Ref<Material> VoxelMesher::get_material(unsigned int id) const {
@ -140,224 +138,223 @@ Ref<Material> VoxelMesher::get_material(unsigned int id) const {
} }
void VoxelMesher::set_occlusion_darkness(float darkness) { void VoxelMesher::set_occlusion_darkness(float darkness) {
_baked_occlusion_darkness = darkness; _baked_occlusion_darkness = darkness;
if (_baked_occlusion_darkness < 0.0) if (_baked_occlusion_darkness < 0.0)
_baked_occlusion_darkness = 0.0; _baked_occlusion_darkness = 0.0;
else if (_baked_occlusion_darkness >= 1.0) else if (_baked_occlusion_darkness >= 1.0)
_baked_occlusion_darkness = 1.0; _baked_occlusion_darkness = 1.0;
} }
void VoxelMesher::set_occlusion_enabled(bool enable) { void VoxelMesher::set_occlusion_enabled(bool enable) {
_bake_occlusion = enable; _bake_occlusion = enable;
} }
inline Color Color_greyscale(float c) { return Color(c, c, c); } inline Color Color_greyscale(float c) {
return Color(c, c, c);
inline bool is_face_visible(const VoxelLibrary & lib, const Voxel & vt, int other_voxel_id) {
if (other_voxel_id == 0) // air
return true;
if (lib.has_voxel(other_voxel_id)) {
const Voxel & other_vt = lib.get_voxel_const(other_voxel_id);
return other_vt.is_transparent() && vt.get_id() != other_voxel_id;
}
return true;
} }
inline bool is_transparent(const VoxelLibrary & lib, int voxel_id) { inline bool is_face_visible(const VoxelLibrary &lib, const Voxel &vt, int other_voxel_id) {
if (lib.has_voxel(voxel_id)) if (other_voxel_id == 0) // air
return lib.get_voxel_const(voxel_id).is_transparent(); return true;
return true; if (lib.has_voxel(other_voxel_id)) {
const Voxel &other_vt = lib.get_voxel_const(other_voxel_id);
return other_vt.is_transparent() && vt.get_id() != other_voxel_id;
}
return true;
}
inline bool is_transparent(const VoxelLibrary &lib, int voxel_id) {
if (lib.has_voxel(voxel_id))
return lib.get_voxel_const(voxel_id).is_transparent();
return true;
} }
Ref<ArrayMesh> VoxelMesher::build_ref(Ref<VoxelBuffer> buffer_ref, unsigned int channel, Ref<ArrayMesh> mesh) { Ref<ArrayMesh> VoxelMesher::build_ref(Ref<VoxelBuffer> buffer_ref, unsigned int channel, Ref<ArrayMesh> mesh) {
ERR_FAIL_COND_V(buffer_ref.is_null(), Ref<ArrayMesh>()); ERR_FAIL_COND_V(buffer_ref.is_null(), Ref<ArrayMesh>());
VoxelBuffer & buffer = **buffer_ref; VoxelBuffer &buffer = **buffer_ref;
mesh = build(buffer, channel, Vector3i(), buffer.get_size(), mesh); mesh = build(buffer, channel, Vector3i(), buffer.get_size(), mesh);
return mesh; return mesh;
} }
Ref<ArrayMesh> VoxelMesher::build(const VoxelBuffer & buffer, unsigned int channel, Vector3i min, Vector3i max, Ref<ArrayMesh> mesh) { Ref<ArrayMesh> VoxelMesher::build(const VoxelBuffer &buffer, unsigned int channel, Vector3i min, Vector3i max, Ref<ArrayMesh> mesh) {
ERR_FAIL_COND_V(_library.is_null(), Ref<ArrayMesh>()); ERR_FAIL_COND_V(_library.is_null(), Ref<ArrayMesh>());
ERR_FAIL_COND_V(channel >= VoxelBuffer::MAX_CHANNELS, Ref<ArrayMesh>()); ERR_FAIL_COND_V(channel >= VoxelBuffer::MAX_CHANNELS, Ref<ArrayMesh>());
const VoxelLibrary & library = **_library; const VoxelLibrary &library = **_library;
for (unsigned int i = 0; i < MAX_MATERIALS; ++i) { for (unsigned int i = 0; i < MAX_MATERIALS; ++i) {
_surface_tool[i].begin(Mesh::PRIMITIVE_TRIANGLES); _surface_tool[i].begin(Mesh::PRIMITIVE_TRIANGLES);
_surface_tool[i].set_material(_materials[i]); _surface_tool[i].set_material(_materials[i]);
} }
float baked_occlusion_darkness; float baked_occlusion_darkness;
if (_bake_occlusion) if (_bake_occlusion)
baked_occlusion_darkness = _baked_occlusion_darkness / 3.0; baked_occlusion_darkness = _baked_occlusion_darkness / 3.0;
// The technique is Culled faces. // The technique is Culled faces.
// Could be improved with greedy meshing: https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/ // 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: // However I don't feel it's worth it yet:
// - Not so much gain for organic worlds with lots of texture variations // - Not so much gain for organic worlds with lots of texture variations
// - Works well with cubes but not with any shape // - Works well with cubes but not with any shape
// - Slower // - Slower
// => Could be implemented in a separate class? // => Could be implemented in a separate class?
VOXEL_PROFILE_BEGIN("mesher_face_extraction") VOXEL_PROFILE_BEGIN("mesher_face_extraction")
// Data must be padded, hence the off-by-one // Data must be padded, hence the off-by-one
Vector3i::sort_min_max(min, max); Vector3i::sort_min_max(min, max);
const Vector3i pad(1,1,1); const Vector3i pad(1, 1, 1);
min.clamp_to(pad, max); min.clamp_to(pad, max);
max.clamp_to(min, buffer.get_size()-pad); max.clamp_to(min, buffer.get_size() - pad);
// Iterate 3D padded data to extract voxel faces. // 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. // This is the most intensive job in this class, so all required data should be as fit as possible.
for (unsigned int z = min.z; z < max.z; ++z) { for (unsigned int z = min.z; z < max.z; ++z) {
for (unsigned int x = min.x; x < max.x; ++x) { for (unsigned int x = min.x; x < max.x; ++x) {
for (unsigned int y = min.y; y < max.y; ++y) { for (unsigned int y = min.y; y < max.y; ++y) {
int voxel_id = buffer.get_voxel(x, y, z, 0); int voxel_id = buffer.get_voxel(x, y, z, 0);
if (voxel_id != 0 && library.has_voxel(voxel_id)) { if (voxel_id != 0 && library.has_voxel(voxel_id)) {
const Voxel & voxel = library.get_voxel_const(voxel_id); const Voxel &voxel = library.get_voxel_const(voxel_id);
SurfaceTool & st = _surface_tool[voxel.get_material_id()]; SurfaceTool &st = _surface_tool[voxel.get_material_id()];
// Hybrid approach: extract cube faces and decimate those that aren't visible, // Hybrid approach: extract cube faces and decimate those that aren't visible,
// and still allow voxels to have geometry that is not a cube // and still allow voxels to have geometry that is not a cube
// Sides // Sides
for (unsigned int side = 0; side < Voxel::SIDE_COUNT; ++side) { for (unsigned int side = 0; side < Voxel::SIDE_COUNT; ++side) {
const PoolVector<Vector3> & vertices = voxel.get_model_side_vertices(side); const PoolVector<Vector3> &vertices = voxel.get_model_side_vertices(side);
if (vertices.size() != 0) { if (vertices.size() != 0) {
Vector3i normal = g_side_normals[side]; Vector3i normal = g_side_normals[side];
unsigned nx = x + normal.x; unsigned nx = x + normal.x;
unsigned ny = y + normal.y; unsigned ny = y + normal.y;
unsigned nz = z + normal.z; unsigned nz = z + normal.z;
int neighbor_voxel_id = buffer.get_voxel(nx, ny, nz, channel); int neighbor_voxel_id = buffer.get_voxel(nx, ny, nz, channel);
// TODO Better face visibility test // TODO Better face visibility test
if (is_face_visible(library, voxel, neighbor_voxel_id)) { if (is_face_visible(library, voxel, neighbor_voxel_id)) {
// The face is visible // The face is visible
int shaded_corner[8] = { 0 }; int shaded_corner[8] = { 0 };
if (_bake_occlusion) { if (_bake_occlusion) {
// Combinatory solution for https://0fps.net/2013/07/03/ambient-occlusion-for-minecraft-like-worlds/ // Combinatory solution for https://0fps.net/2013/07/03/ambient-occlusion-for-minecraft-like-worlds/
for (unsigned int j = 0; j < 4; ++j) { for (unsigned int j = 0; j < 4; ++j) {
unsigned int edge = g_side_edges[side][j]; unsigned int edge = g_side_edges[side][j];
Vector3i edge_normal = g_edge_inormals[edge]; Vector3i edge_normal = g_edge_inormals[edge];
unsigned ex = x + edge_normal.x; unsigned ex = x + edge_normal.x;
unsigned ey = y + edge_normal.y; unsigned ey = y + edge_normal.y;
unsigned ez = z + edge_normal.z; unsigned ez = z + edge_normal.z;
if (!is_transparent(library, buffer.get_voxel(ex, ey, ez))) { if (!is_transparent(library, buffer.get_voxel(ex, ey, ez))) {
shaded_corner[g_edge_corners[edge][0]] += 1; shaded_corner[g_edge_corners[edge][0]] += 1;
shaded_corner[g_edge_corners[edge][1]] += 1; shaded_corner[g_edge_corners[edge][1]] += 1;
} }
} }
for (unsigned int j = 0; j < 4; ++j) { for (unsigned int j = 0; j < 4; ++j) {
unsigned int corner = g_side_corners[side][j]; unsigned int corner = g_side_corners[side][j];
if (shaded_corner[corner] == 2) { if (shaded_corner[corner] == 2) {
shaded_corner[corner] = 3; shaded_corner[corner] = 3;
} } else {
else { Vector3i corner_normal = g_corner_inormals[corner];
Vector3i corner_normal = g_corner_inormals[corner]; unsigned int cx = x + corner_normal.x;
unsigned int cx = x + corner_normal.x; unsigned int cy = y + corner_normal.y;
unsigned int cy = y + corner_normal.y; unsigned int cz = z + corner_normal.z;
unsigned int cz = z + corner_normal.z; if (!is_transparent(library, buffer.get_voxel(cx, cy, cz))) {
if (!is_transparent(library, buffer.get_voxel(cx, cy, cz))) { shaded_corner[corner] += 1;
shaded_corner[corner] += 1; }
} }
} }
} }
}
PoolVector<Vector3>::Read rv = vertices.read(); PoolVector<Vector3>::Read rv = vertices.read();
PoolVector<Vector2>::Read rt = voxel.get_model_side_uv(side).read(); PoolVector<Vector2>::Read rt = voxel.get_model_side_uv(side).read();
Vector3 pos(x - 1, y - 1, z - 1); Vector3 pos(x - 1, y - 1, z - 1);
for (unsigned int i = 0; i < vertices.size(); ++i) { for (unsigned int i = 0; i < vertices.size(); ++i) {
Vector3 v = rv[i]; Vector3 v = rv[i];
if (_bake_occlusion) { if (_bake_occlusion) {
// General purpose occlusion colouring. // General purpose occlusion colouring.
// TODO Optimize for cubes // TODO Optimize for cubes
// TODO Fix occlusion inconsistency caused by triangles orientation // TODO Fix occlusion inconsistency caused by triangles orientation
float shade = 0; float shade = 0;
for (unsigned int j = 0; j < 4; ++j) { for (unsigned int j = 0; j < 4; ++j) {
unsigned int corner = g_side_corners[side][j]; unsigned int corner = g_side_corners[side][j];
if (shaded_corner[corner]) { if (shaded_corner[corner]) {
float s = baked_occlusion_darkness * static_cast<float>(shaded_corner[corner]); float s = baked_occlusion_darkness * static_cast<float>(shaded_corner[corner]);
float k = 1.0 - g_corner_position[corner].distance_to(v); float k = 1.0 - g_corner_position[corner].distance_to(v);
if (k < 0.0) if (k < 0.0)
k = 0.0; k = 0.0;
s *= k; s *= k;
if (s > shade) if (s > shade)
shade = s; shade = s;
} }
} }
float gs = 1.0 - shade; float gs = 1.0 - shade;
st.add_color(Color(gs, gs, gs)); st.add_color(Color(gs, gs, gs));
} }
st.add_normal(Vector3(normal.x, normal.y, normal.z)); st.add_normal(Vector3(normal.x, normal.y, normal.z));
st.add_uv(rt[i]); st.add_uv(rt[i]);
st.add_vertex(v + pos); st.add_vertex(v + pos);
} }
} }
} }
} }
// Inside // Inside
if (voxel.get_model_vertices().size() != 0) { if (voxel.get_model_vertices().size() != 0) {
const PoolVector<Vector3> & vertices = voxel.get_model_vertices(); const PoolVector<Vector3> &vertices = voxel.get_model_vertices();
PoolVector<Vector3>::Read rv = voxel.get_model_vertices().read(); PoolVector<Vector3>::Read rv = voxel.get_model_vertices().read();
PoolVector<Vector3>::Read rn = voxel.get_model_normals().read(); PoolVector<Vector3>::Read rn = voxel.get_model_normals().read();
PoolVector<Vector2>::Read rt = voxel.get_model_uv().read(); PoolVector<Vector2>::Read rt = voxel.get_model_uv().read();
Vector3 pos(x - 1, y - 1, z - 1); Vector3 pos(x - 1, y - 1, z - 1);
for (unsigned int i = 0; i < vertices.size(); ++i) { for (unsigned int i = 0; i < vertices.size(); ++i) {
st.add_normal(rn[i]); st.add_normal(rn[i]);
st.add_uv(rt[i]); st.add_uv(rt[i]);
st.add_vertex(rv[i] + pos); st.add_vertex(rv[i] + pos);
} }
} }
}
} }
}
} }
}
}
VOXEL_PROFILE_END("mesher_face_extraction") VOXEL_PROFILE_END("mesher_face_extraction")
// Commit mesh // Commit mesh
Ref<ArrayMesh> mesh_ref = mesh; Ref<ArrayMesh> mesh_ref = mesh;
if(mesh.is_null()) if (mesh.is_null())
mesh_ref = Ref<ArrayMesh>(memnew(ArrayMesh)); mesh_ref = Ref<ArrayMesh>(memnew(ArrayMesh));
for (unsigned int i = 0; i < MAX_MATERIALS; ++i) { for (unsigned int i = 0; i < MAX_MATERIALS; ++i) {
if (_materials[i].is_valid()) { if (_materials[i].is_valid()) {
SurfaceTool & st = _surface_tool[i]; SurfaceTool &st = _surface_tool[i];
// Index mesh to reduce memory usage and make upload to VRAM faster // Index mesh to reduce memory usage and make upload to VRAM faster
// TODO actually, we could make it indexed from the ground up without using SurfaceTool, so we also save time! // TODO actually, we could make it indexed from the ground up without using SurfaceTool, so we also save time!
// VOXEL_PROFILE_BEGIN("mesher_surfacetool_index") // VOXEL_PROFILE_BEGIN("mesher_surfacetool_index")
// st.index(); // st.index();
// VOXEL_PROFILE_END("mesher_surfacetool_index") // VOXEL_PROFILE_END("mesher_surfacetool_index")
VOXEL_PROFILE_BEGIN("mesher_surfacetool_commit") VOXEL_PROFILE_BEGIN("mesher_surfacetool_commit")
mesh_ref = st.commit(mesh_ref); mesh_ref = st.commit(mesh_ref);
VOXEL_PROFILE_END("mesher_surfacetool_commit") VOXEL_PROFILE_END("mesher_surfacetool_commit")
st.clear(); st.clear();
} }
} }
return mesh_ref; return mesh_ref;
} }

View File

@ -1,14 +1,13 @@
#ifndef VOXEL_MESHER #ifndef VOXEL_MESHER
#define VOXEL_MESHER #define VOXEL_MESHER
#include <reference.h>
#include <scene/resources/mesh.h>
#include <scene/resources/surface_tool.h>
#include "voxel.h" #include "voxel.h"
#include "voxel_buffer.h" #include "voxel_buffer.h"
#include "voxel_library.h" #include "voxel_library.h"
#include "zprofiling.h" #include "zprofiling.h"
#include <reference.h>
#include <scene/resources/mesh.h>
#include <scene/resources/surface_tool.h>
// TODO Should be renamed VoxelMesherCubic or something like that // TODO Should be renamed VoxelMesherCubic or something like that
class VoxelMesher : public Reference { class VoxelMesher : public Reference {
@ -31,8 +30,8 @@ public:
void set_occlusion_enabled(bool enable); void set_occlusion_enabled(bool enable);
bool get_occlusion_enabled() const { return _bake_occlusion; } bool get_occlusion_enabled() const { return _bake_occlusion; }
Ref<ArrayMesh> build(const VoxelBuffer & buffer_ref, unsigned int channel, Vector3i min, Vector3i max, Ref<ArrayMesh> mesh=Ref<Mesh>()); Ref<ArrayMesh> build(const VoxelBuffer &buffer_ref, unsigned int channel, Vector3i min, Vector3i max, Ref<ArrayMesh> mesh = Ref<Mesh>());
Ref<ArrayMesh> build_ref(Ref<VoxelBuffer> buffer_ref, unsigned int channel, Ref<ArrayMesh> mesh=Ref<ArrayMesh>()); Ref<ArrayMesh> build_ref(Ref<VoxelBuffer> buffer_ref, unsigned int channel, Ref<ArrayMesh> mesh = Ref<ArrayMesh>());
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -50,5 +49,4 @@ private:
#endif #endif
}; };
#endif // VOXEL_MESHER #endif // VOXEL_MESHER

View File

@ -3,7 +3,6 @@
#include "transvoxel_tables.cpp" #include "transvoxel_tables.cpp"
#include <os/os.h> #include <os/os.h>
inline float tof(int8_t v) { inline float tof(int8_t v) {
return static_cast<float>(v) / 256.f; return static_cast<float>(v) / 256.f;
} }
@ -42,27 +41,26 @@ const Vector3i g_corner_dirs[8] = {
inline Vector3i dir_to_prev_vec(uint8_t dir) { inline Vector3i dir_to_prev_vec(uint8_t dir) {
//return g_corner_dirs[mask] - Vector3(1,1,1); //return g_corner_dirs[mask] - Vector3(1,1,1);
return Vector3i( return Vector3i(
-(dir & 1), -(dir & 1),
-((dir >> 1) & 1), -((dir >> 1) & 1),
-((dir >> 2) & 1) -((dir >> 2) & 1));
);
} }
template<typename T> template <typename T>
void copy_to(PoolVector<T> & to, Vector<T> & from) { void copy_to(PoolVector<T> &to, Vector<T> &from) {
to.resize(from.size()); to.resize(from.size());
PoolVector<T>::Write w = to.write(); PoolVector<T>::Write w = to.write();
for(unsigned int i = 0; i < from.size(); ++i) { for (unsigned int i = 0; i < from.size(); ++i) {
w[i] = from[i]; w[i] = from[i];
} }
} }
VoxelMesherSmooth::ReuseCell::ReuseCell() { VoxelMesherSmooth::ReuseCell::ReuseCell() {
case_index = 0; case_index = 0;
for(unsigned int i = 0; i < 4; ++i) { for (unsigned int i = 0; i < 4; ++i) {
vertices[i] = -1; vertices[i] = -1;
} }
} }
@ -74,12 +72,12 @@ Ref<ArrayMesh> VoxelMesherSmooth::build_ref(Ref<VoxelBuffer> voxels_ref, unsigne
ERR_FAIL_COND_V(voxels_ref.is_null(), Ref<ArrayMesh>()); ERR_FAIL_COND_V(voxels_ref.is_null(), Ref<ArrayMesh>());
VoxelBuffer & voxels = **voxels_ref; VoxelBuffer &voxels = **voxels_ref;
return build(voxels, channel, mesh); return build(voxels, channel, mesh);
} }
Ref<ArrayMesh> VoxelMesherSmooth::build(const VoxelBuffer & voxels, unsigned int channel, Ref<ArrayMesh> mesh) { Ref<ArrayMesh> VoxelMesherSmooth::build(const VoxelBuffer &voxels, unsigned int channel, Ref<ArrayMesh> mesh) {
ERR_FAIL_COND_V(channel >= VoxelBuffer::MAX_CHANNELS, Ref<ArrayMesh>()); ERR_FAIL_COND_V(channel >= VoxelBuffer::MAX_CHANNELS, Ref<ArrayMesh>());
@ -93,12 +91,12 @@ Ref<ArrayMesh> VoxelMesherSmooth::build(const VoxelBuffer & voxels, unsigned int
m_output_indices.clear(); m_output_indices.clear();
build_mesh(voxels, channel); build_mesh(voxels, channel);
// OS::get_singleton()->print("vertices: %i, normals: %i, indices: %i\n", // OS::get_singleton()->print("vertices: %i, normals: %i, indices: %i\n",
// m_output_vertices.size(), // m_output_vertices.size(),
// m_output_normals.size(), // m_output_normals.size(),
// m_output_indices.size()); // m_output_indices.size());
if(m_output_vertices.size() == 0) { if (m_output_vertices.size() == 0) {
// The mesh can be empty // The mesh can be empty
return Ref<ArrayMesh>(); return Ref<ArrayMesh>();
} }
@ -114,12 +112,12 @@ Ref<ArrayMesh> VoxelMesherSmooth::build(const VoxelBuffer & voxels, unsigned int
Array arrays; Array arrays;
arrays.resize(Mesh::ARRAY_MAX); arrays.resize(Mesh::ARRAY_MAX);
arrays[Mesh::ARRAY_VERTEX] = vertices; arrays[Mesh::ARRAY_VERTEX] = vertices;
if(m_output_normals.size() != 0) { if (m_output_normals.size() != 0) {
arrays[Mesh::ARRAY_NORMAL] = normals; arrays[Mesh::ARRAY_NORMAL] = normals;
} }
arrays[Mesh::ARRAY_INDEX] = indices; arrays[Mesh::ARRAY_INDEX] = indices;
if(mesh.is_null()) if (mesh.is_null())
mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays); mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);
@ -127,7 +125,7 @@ Ref<ArrayMesh> VoxelMesherSmooth::build(const VoxelBuffer & voxels, unsigned int
return mesh; return mesh;
} }
void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int channel) { void VoxelMesherSmooth::build_mesh(const VoxelBuffer &voxels, unsigned int channel) {
// Each 2x2 voxel group is a "cell" // Each 2x2 voxel group is a "cell"
@ -139,8 +137,8 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
// Prepare vertex reuse cache // Prepare vertex reuse cache
m_block_size = block_size; m_block_size = block_size;
unsigned int deck_area = block_size.x * block_size.y; unsigned int deck_area = block_size.x * block_size.y;
for(int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
if(m_cache[i].size() != deck_area) { if (m_cache[i].size() != deck_area) {
m_cache[i].clear(); // Clear any previous data m_cache[i].clear(); // Clear any previous data
m_cache[i].resize(deck_area); m_cache[i].resize(deck_area);
} }
@ -148,42 +146,41 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
// Iterate all cells with padding (expected to be neighbors) // Iterate all cells with padding (expected to be neighbors)
Vector3i pos; Vector3i pos;
for(pos.z = PAD.z; pos.z < block_size.z - 2; ++pos.z) { for (pos.z = PAD.z; pos.z < block_size.z - 2; ++pos.z) {
for(pos.y = PAD.y; pos.y < block_size.y - 2; ++pos.y) { for (pos.y = PAD.y; pos.y < block_size.y - 2; ++pos.y) {
for(pos.x = PAD.x; pos.x < block_size.x - 2; ++pos.x) { for (pos.x = PAD.x; pos.x < block_size.x - 2; ++pos.x) {
// Get the value of cells. // Get the value of cells.
// Negative values are "solid" and positive are "air". // Negative values are "solid" and positive are "air".
// Due to raw cells being unsigned 8-bit, they get converted to signed. // Due to raw cells being unsigned 8-bit, they get converted to signed.
int8_t cell_samples[8] = { int8_t cell_samples[8] = {
tos(voxels.get_voxel( pos.x, pos.y, pos.z, channel )), tos(voxels.get_voxel(pos.x, pos.y, pos.z, channel)),
tos(voxels.get_voxel( pos.x+1, pos.y, pos.z, channel )), tos(voxels.get_voxel(pos.x + 1, pos.y, pos.z, channel)),
tos(voxels.get_voxel( pos.x, pos.y+1, pos.z, channel )), tos(voxels.get_voxel(pos.x, pos.y + 1, pos.z, channel)),
tos(voxels.get_voxel( pos.x+1, pos.y+1, pos.z, channel )), tos(voxels.get_voxel(pos.x + 1, pos.y + 1, pos.z, channel)),
tos(voxels.get_voxel( pos.x, pos.y, pos.z+1, channel )), tos(voxels.get_voxel(pos.x, pos.y, pos.z + 1, channel)),
tos(voxels.get_voxel( pos.x+1, pos.y, pos.z+1, channel )), tos(voxels.get_voxel(pos.x + 1, pos.y, pos.z + 1, channel)),
tos(voxels.get_voxel( pos.x, pos.y+1, pos.z+1, channel )), tos(voxels.get_voxel(pos.x, pos.y + 1, pos.z + 1, channel)),
tos(voxels.get_voxel( pos.x+1, pos.y+1, pos.z+1, channel )) tos(voxels.get_voxel(pos.x + 1, pos.y + 1, pos.z + 1, channel))
}; };
// Concatenate the sign of cell values to obtain the case code. // Concatenate the sign of cell values to obtain the case code.
// Index 0 is the less significant bit, and index 7 is the most significant bit. // Index 0 is the less significant bit, and index 7 is the most significant bit.
uint8_t case_code = uint8_t case_code = sign(cell_samples[0]);
sign(cell_samples[0]) case_code |= (sign(cell_samples[1]) << 1);
| (sign(cell_samples[1]) << 1) case_code |= (sign(cell_samples[2]) << 2);
| (sign(cell_samples[2]) << 2) case_code |= (sign(cell_samples[3]) << 3);
| (sign(cell_samples[3]) << 3) case_code |= (sign(cell_samples[4]) << 4);
| (sign(cell_samples[4]) << 4) case_code |= (sign(cell_samples[5]) << 5);
| (sign(cell_samples[5]) << 5) case_code |= (sign(cell_samples[6]) << 6);
| (sign(cell_samples[6]) << 6) case_code |= (sign(cell_samples[7]) << 7);
| (sign(cell_samples[7]) << 7);
{ {
ReuseCell & rc = get_reuse_cell(pos); ReuseCell &rc = get_reuse_cell(pos);
rc.case_index = case_code; rc.case_index = case_code;
} }
if(case_code == 0 || case_code == 255) { if (case_code == 0 || case_code == 255) {
// If the case_code is 0 or 255, there is no triangulation to do // If the case_code is 0 or 255, there is no triangulation to do
continue; continue;
} }
@ -191,18 +188,13 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
// TODO We might not always need all of them // TODO We might not always need all of them
// Compute normals // Compute normals
Vector3 corner_normals[8]; Vector3 corner_normals[8];
for(unsigned int i = 0; i < 8; ++i) { for (unsigned int i = 0; i < 8; ++i) {
Vector3i p = pos + g_corner_dirs[i]; Vector3i p = pos + g_corner_dirs[i];
float nx = tof(tos(voxels.get_voxel(p - Vector3i(1,0,0), channel))) float nx = tof(tos(voxels.get_voxel(p - Vector3i(1, 0, 0), channel))) - tof(tos(voxels.get_voxel(p + Vector3i(1, 0, 0), channel)));
- tof(tos(voxels.get_voxel(p + Vector3i(1,0,0), channel))); float ny = tof(tos(voxels.get_voxel(p - Vector3i(0, 1, 0), channel))) - tof(tos(voxels.get_voxel(p + Vector3i(0, 1, 0), channel)));
float nz = tof(tos(voxels.get_voxel(p - Vector3i(0, 0, 1), channel))) - tof(tos(voxels.get_voxel(p + Vector3i(0, 0, 1), channel)));
float ny = tof(tos(voxels.get_voxel(p - Vector3i(0,1,0), channel)))
- tof(tos(voxels.get_voxel(p + Vector3i(0,1,0), channel)));
float nz = tof(tos(voxels.get_voxel(p - Vector3i(0,0,1), channel)))
- tof(tos(voxels.get_voxel(p + Vector3i(0,0,1), channel)));
corner_normals[i] = Vector3(nx, ny, nz); corner_normals[i] = Vector3(nx, ny, nz);
corner_normals[i].normalize(); corner_normals[i].normalize();
@ -214,9 +206,7 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
// While iterating through the cells in a block, a 3-bit mask is maintained whose bits indicate // While iterating through the cells in a block, a 3-bit mask is maintained whose bits indicate
// whether corresponding bits in a direction code are valid // whether corresponding bits in a direction code are valid
uint8_t direction_validity_mask = uint8_t direction_validity_mask =
(pos.x > 1 ? 1 : 0) (pos.x > 1 ? 1 : 0) | ((pos.y > 1 ? 1 : 0) << 1) | ((pos.z > 1 ? 1 : 0) << 2);
| ((pos.y > 1 ? 1 : 0) << 1)
| ((pos.z > 1 ? 1 : 0) << 2);
uint8_t regular_cell_class_index = Transvoxel::regularCellClass[case_code]; uint8_t regular_cell_class_index = Transvoxel::regularCellClass[case_code];
Transvoxel::RegularCellData regular_cell_class = Transvoxel::regularCellData[regular_cell_class_index]; Transvoxel::RegularCellData regular_cell_class = Transvoxel::regularCellData[regular_cell_class_index];
@ -226,7 +216,7 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
int cell_mesh_indices[12]; int cell_mesh_indices[12];
// For each vertex in the case // For each vertex in the case
for(unsigned int i = 0; i < vertex_count; ++i) { for (unsigned int i = 0; i < vertex_count; ++i) {
// The case index maps to a list of 16-bit codes providing information about the edges on which the vertices lie. // The case index maps to a list of 16-bit codes providing information about the edges on which the vertices lie.
// The low byte of each 16-bit code contains the corner indexes of the edges endpoints in one nibble each, // The low byte of each 16-bit code contains the corner indexes of the edges endpoints in one nibble each,
@ -260,7 +250,7 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
Vector3i p0 = pos + g_corner_dirs[v0]; Vector3i p0 = pos + g_corner_dirs[v0];
Vector3i p1 = pos + g_corner_dirs[v1]; Vector3i p1 = pos + g_corner_dirs[v1];
if(t & 0xff) { if (t & 0xff) {
//OS::get_singleton()->print("A"); //OS::get_singleton()->print("A");
// Vertex lies in the interior of the edge. // Vertex lies in the interior of the edge.
@ -275,11 +265,11 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
bool can_reuse = (reuse_dir & direction_validity_mask) == reuse_dir; bool can_reuse = (reuse_dir & direction_validity_mask) == reuse_dir;
if(can_reuse) { if (can_reuse) {
Vector3i cache_pos = pos + dir_to_prev_vec(reuse_dir); Vector3i cache_pos = pos + dir_to_prev_vec(reuse_dir);
ReuseCell & prev_cell = get_reuse_cell(cache_pos); ReuseCell &prev_cell = get_reuse_cell(cache_pos);
if(prev_cell.case_index == 0 || prev_cell.case_index == 255) { if (prev_cell.case_index == 0 || prev_cell.case_index == 255) {
// TODO I don't think this can happen for non-corner vertices. // TODO I don't think this can happen for non-corner vertices.
cell_mesh_indices[i] = -1; cell_mesh_indices[i] = -1;
} else { } else {
@ -288,26 +278,26 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
} }
} }
if(!can_reuse || cell_mesh_indices[i] == -1) { if (!can_reuse || cell_mesh_indices[i] == -1) {
// Going to create a new vertice // Going to create a new vertice
cell_mesh_indices[i] = m_output_vertices.size(); cell_mesh_indices[i] = m_output_vertices.size();
Vector3 pi = p0.to_vec3() * t0 + p1.to_vec3() * t1; Vector3 pi = p0.to_vec3() * t0 + p1.to_vec3() * t1;
Vector3 primary = pi;//pos.to_vec3() + pi; Vector3 primary = pi; //pos.to_vec3() + pi;
Vector3 normal = corner_normals[v0] * t0 + corner_normals[v1] * t1; Vector3 normal = corner_normals[v0] * t0 + corner_normals[v1] * t1;
emit_vertex(primary, normal); emit_vertex(primary, normal);
if (reuse_dir & 8) { if (reuse_dir & 8) {
// Store the generated vertex so that other cells can reuse it. // Store the generated vertex so that other cells can reuse it.
ReuseCell & rc = get_reuse_cell(pos); ReuseCell &rc = get_reuse_cell(pos);
rc.vertices[reuse_vertex_index] = cell_mesh_indices[i]; rc.vertices[reuse_vertex_index] = cell_mesh_indices[i];
} }
} }
} else if(t == 0 && v1 == 7) { } else if (t == 0 && v1 == 7) {
//OS::get_singleton()->print("B"); //OS::get_singleton()->print("B");
// This cell owns the vertex, so it should be created. // This cell owns the vertex, so it should be created.
@ -315,12 +305,12 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
cell_mesh_indices[i] = m_output_vertices.size(); cell_mesh_indices[i] = m_output_vertices.size();
Vector3 pi = p0.to_vec3() * t0 + p1.to_vec3() * t1; Vector3 pi = p0.to_vec3() * t0 + p1.to_vec3() * t1;
Vector3 primary = pi;//pos.to_vec3() + pi; Vector3 primary = pi; //pos.to_vec3() + pi;
Vector3 normal = corner_normals[v0] * t0 + corner_normals[v1] * t1; Vector3 normal = corner_normals[v0] * t0 + corner_normals[v1] * t1;
emit_vertex(primary, normal); emit_vertex(primary, normal);
ReuseCell & rc = get_reuse_cell(pos); ReuseCell &rc = get_reuse_cell(pos);
rc.vertices[0] = cell_mesh_indices[i]; rc.vertices[0] = cell_mesh_indices[i];
} else { } else {
@ -335,7 +325,7 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
bool can_reuse = (reuse_dir & direction_validity_mask) == reuse_dir; bool can_reuse = (reuse_dir & direction_validity_mask) == reuse_dir;
// Note: the only difference with similar code above is that we take vertice 0 in the `else` // Note: the only difference with similar code above is that we take vertice 0 in the `else`
if(can_reuse) { if (can_reuse) {
Vector3i cache_pos = pos + dir_to_prev_vec(reuse_dir); Vector3i cache_pos = pos + dir_to_prev_vec(reuse_dir);
ReuseCell prev_cell = get_reuse_cell(cache_pos); ReuseCell prev_cell = get_reuse_cell(cache_pos);
@ -348,11 +338,11 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
} }
} }
if(!can_reuse || cell_mesh_indices[i] < 0) { if (!can_reuse || cell_mesh_indices[i] < 0) {
cell_mesh_indices[i] = m_output_vertices.size(); cell_mesh_indices[i] = m_output_vertices.size();
Vector3 pi = p0.to_vec3() * t0 + p1.to_vec3() * t1; Vector3 pi = p0.to_vec3() * t0 + p1.to_vec3() * t1;
Vector3 primary = pi;//pos.to_vec3() + pi; Vector3 primary = pi; //pos.to_vec3() + pi;
Vector3 normal = corner_normals[v0] * t0 + corner_normals[v1] * t1; Vector3 normal = corner_normals[v0] * t0 + corner_normals[v1] * t1;
emit_vertex(primary, normal); emit_vertex(primary, normal);
@ -377,23 +367,18 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
//OS::get_singleton()->print("\n"); //OS::get_singleton()->print("\n");
} }
VoxelMesherSmooth::ReuseCell &VoxelMesherSmooth::get_reuse_cell(Vector3i pos) {
VoxelMesherSmooth::ReuseCell & VoxelMesherSmooth::get_reuse_cell(Vector3i pos) {
int j = pos.z & 1; int j = pos.z & 1;
int i = pos.y * m_block_size.y + pos.x; int i = pos.y * m_block_size.y + pos.x;
return m_cache[j][i]; return m_cache[j][i];
} }
void VoxelMesherSmooth::emit_vertex(Vector3 primary, Vector3 normal) { void VoxelMesherSmooth::emit_vertex(Vector3 primary, Vector3 normal) {
m_output_vertices.push_back(primary - PAD.to_vec3()); m_output_vertices.push_back(primary - PAD.to_vec3());
m_output_normals.push_back(normal); m_output_normals.push_back(normal);
} }
void VoxelMesherSmooth::_bind_methods() { void VoxelMesherSmooth::_bind_methods() {
ClassDB::bind_method(D_METHOD("build", "voxels", "channel", "existing_mesh"), &VoxelMesherSmooth::build_ref, DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("build", "voxels", "channel", "existing_mesh"), &VoxelMesherSmooth::build_ref, DEFVAL(Variant()));
} }

View File

@ -10,8 +10,8 @@ class VoxelMesherSmooth : public Reference {
public: public:
VoxelMesherSmooth(); VoxelMesherSmooth();
Ref<ArrayMesh> build_ref(Ref<VoxelBuffer> voxels_ref, unsigned int channel, Ref<ArrayMesh> mesh=Ref<ArrayMesh>()); Ref<ArrayMesh> build_ref(Ref<VoxelBuffer> voxels_ref, unsigned int channel, Ref<ArrayMesh> mesh = Ref<ArrayMesh>());
Ref<ArrayMesh> build(const VoxelBuffer & voxels, unsigned int channel, Ref<ArrayMesh> mesh=Ref<ArrayMesh>()); Ref<ArrayMesh> build(const VoxelBuffer &voxels, unsigned int channel, Ref<ArrayMesh> mesh = Ref<ArrayMesh>());
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -23,12 +23,12 @@ private:
ReuseCell(); ReuseCell();
}; };
void build_mesh(const VoxelBuffer & voxels, unsigned int channel); void build_mesh(const VoxelBuffer &voxels, unsigned int channel);
ReuseCell & get_reuse_cell(Vector3i pos); ReuseCell &get_reuse_cell(Vector3i pos);
void emit_vertex(Vector3 primary, Vector3 normal); void emit_vertex(Vector3 primary, Vector3 normal);
private: private:
const Vector3i PAD = Vector3i(1,1,1); const Vector3i PAD = Vector3i(1, 1, 1);
Vector<ReuseCell> m_cache[2]; Vector<ReuseCell> m_cache[2];
Vector3i m_block_size; Vector3i m_block_size;

View File

@ -1,15 +1,14 @@
#include "voxel_provider.h" #include "voxel_provider.h"
#include "voxel_map.h" #include "voxel_map.h"
void VoxelProvider::emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i block_pos) { void VoxelProvider::emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i block_pos) {
ERR_FAIL_COND(out_buffer.is_null()); ERR_FAIL_COND(out_buffer.is_null());
ScriptInstance * script = get_script_instance(); ScriptInstance *script = get_script_instance();
if(script) { if (script) {
// Call script to generate buffer // Call script to generate buffer
Variant arg1 = out_buffer; Variant arg1 = out_buffer;
Variant arg2 = block_pos.to_vec3(); Variant arg2 = block_pos.to_vec3();
const Variant * args[2] = { &arg1, &arg2 }; const Variant *args[2] = { &arg1, &arg2 };
//Variant::CallError err; // wut //Variant::CallError err; // wut
script->call_multilevel("emerge_block", args, 2); script->call_multilevel("emerge_block", args, 2);
} }
@ -17,12 +16,12 @@ void VoxelProvider::emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i block_pos
void VoxelProvider::immerge_block(Ref<VoxelBuffer> buffer, Vector3i block_pos) { void VoxelProvider::immerge_block(Ref<VoxelBuffer> buffer, Vector3i block_pos) {
ERR_FAIL_COND(buffer.is_null()); ERR_FAIL_COND(buffer.is_null());
ScriptInstance * script = get_script_instance(); ScriptInstance *script = get_script_instance();
if(script) { if (script) {
// Call script to save buffer // Call script to save buffer
Variant arg1 = buffer; Variant arg1 = buffer;
Variant arg2 = block_pos.to_vec3(); Variant arg2 = block_pos.to_vec3();
const Variant * args[2] = { &arg1, &arg2 }; const Variant *args[2] = { &arg1, &arg2 };
//Variant::CallError err; // wut //Variant::CallError err; // wut
script->call_multilevel("immerge_block", args, 2); script->call_multilevel("immerge_block", args, 2);
} }
@ -40,7 +39,4 @@ void VoxelProvider::_bind_methods() {
ClassDB::bind_method(D_METHOD("emerge_block", "out_buffer", "block_pos"), &VoxelProvider::_emerge_block); ClassDB::bind_method(D_METHOD("emerge_block", "out_buffer", "block_pos"), &VoxelProvider::_emerge_block);
ClassDB::bind_method(D_METHOD("immerge_block", "buffer", "block_pos"), &VoxelProvider::_immerge_block); ClassDB::bind_method(D_METHOD("immerge_block", "buffer", "block_pos"), &VoxelProvider::_immerge_block);
} }

View File

@ -4,7 +4,6 @@
#include "reference.h" #include "reference.h"
#include "voxel_buffer.h" #include "voxel_buffer.h"
class VoxelProvider : public Reference { class VoxelProvider : public Reference {
GDCLASS(VoxelProvider, Reference) GDCLASS(VoxelProvider, Reference)
public: public:
@ -18,5 +17,4 @@ protected:
void _immerge_block(Ref<VoxelBuffer> buffer, Vector3 block_pos); void _immerge_block(Ref<VoxelBuffer> buffer, Vector3 block_pos);
}; };
#endif // VOXEL_PROVIDER_H #endif // VOXEL_PROVIDER_H

View File

@ -3,7 +3,6 @@
VARIANT_ENUM_CAST(VoxelProviderTest::Mode) VARIANT_ENUM_CAST(VoxelProviderTest::Mode)
VoxelProviderTest::VoxelProviderTest() { VoxelProviderTest::VoxelProviderTest() {
_mode = MODE_FLAT; _mode = MODE_FLAT;
_voxel_type = 1; _voxel_type = 1;
@ -34,57 +33,57 @@ void VoxelProviderTest::set_pattern_offset(Vector3i offset) {
void VoxelProviderTest::emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i block_pos) { void VoxelProviderTest::emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i block_pos) {
ERR_FAIL_COND(out_buffer.is_null()); ERR_FAIL_COND(out_buffer.is_null());
switch(_mode) { switch (_mode) {
case MODE_FLAT: case MODE_FLAT:
generate_block_flat(**out_buffer, block_pos); generate_block_flat(**out_buffer, block_pos);
break; break;
case MODE_WAVES: case MODE_WAVES:
generate_block_waves(**out_buffer, block_pos); generate_block_waves(**out_buffer, block_pos);
break; break;
} }
} }
void VoxelProviderTest::generate_block_flat(VoxelBuffer & out_buffer, Vector3i block_pos) { void VoxelProviderTest::generate_block_flat(VoxelBuffer &out_buffer, Vector3i block_pos) {
// TODO Don't expect a block pos, but a voxel pos! // TODO Don't expect a block pos, but a voxel pos!
Vector3i size = out_buffer.get_size(); Vector3i size = out_buffer.get_size();
Vector3i origin = VoxelMap::block_to_voxel(block_pos); Vector3i origin = VoxelMap::block_to_voxel(block_pos);
int rh = _pattern_offset.y - origin.y; int rh = _pattern_offset.y - origin.y;
if(rh > size.y) if (rh > size.y)
rh = size.y; rh = size.y;
for(int rz = 0; rz < size.z; ++rz) { for (int rz = 0; rz < size.z; ++rz) {
for(int rx = 0; rx < size.x; ++rx) { for (int rx = 0; rx < size.x; ++rx) {
for(int ry = 0; ry < rh; ++ry) { for (int ry = 0; ry < rh; ++ry) {
out_buffer.set_voxel(_voxel_type, rx, ry, rz, 0); out_buffer.set_voxel(_voxel_type, rx, ry, rz, 0);
} }
} }
} }
} }
void VoxelProviderTest::generate_block_waves(VoxelBuffer & out_buffer, Vector3i block_pos) { void VoxelProviderTest::generate_block_waves(VoxelBuffer &out_buffer, Vector3i block_pos) {
Vector3i size = out_buffer.get_size(); Vector3i size = out_buffer.get_size();
Vector3i origin = VoxelMap::block_to_voxel(block_pos) + _pattern_offset; Vector3i origin = VoxelMap::block_to_voxel(block_pos) + _pattern_offset;
float amplitude = static_cast<float>(_pattern_size.y); float amplitude = static_cast<float>(_pattern_size.y);
float period_x = 1.f/static_cast<float>(_pattern_size.x); float period_x = 1.f / static_cast<float>(_pattern_size.x);
float period_z = 1.f/static_cast<float>(_pattern_size.z); float period_z = 1.f / static_cast<float>(_pattern_size.z);
for(int rz = 0; rz < size.z; ++rz) { for (int rz = 0; rz < size.z; ++rz) {
for(int rx = 0; rx < size.x; ++rx) { for (int rx = 0; rx < size.x; ++rx) {
float x = origin.x + rx; float x = origin.x + rx;
float z = origin.z + rz; float z = origin.z + rz;
int h = _pattern_offset.y + amplitude * (Math::cos(x*period_x) + Math::sin(z*period_z)); int h = _pattern_offset.y + amplitude * (Math::cos(x * period_x) + Math::sin(z * period_z));
int rh = h - origin.y; int rh = h - origin.y;
if(rh > size.y) if (rh > size.y)
rh = size.y; rh = size.y;
for(int ry = 0; ry < rh; ++ry) { for (int ry = 0; ry < rh; ++ry) {
out_buffer.set_voxel(_voxel_type, rx, ry, rz, 0); out_buffer.set_voxel(_voxel_type, rx, ry, rz, 0);
} }
} }
@ -108,4 +107,3 @@ void VoxelProviderTest::_bind_methods() {
BIND_CONSTANT(MODE_FLAT); BIND_CONSTANT(MODE_FLAT);
BIND_CONSTANT(MODE_WAVES); BIND_CONSTANT(MODE_WAVES);
} }

View File

@ -3,7 +3,6 @@
#include "voxel_provider.h" #include "voxel_provider.h"
class VoxelProviderTest : public VoxelProvider { class VoxelProviderTest : public VoxelProvider {
GDCLASS(VoxelProviderTest, VoxelProvider) GDCLASS(VoxelProviderTest, VoxelProvider)
@ -30,8 +29,8 @@ public:
void set_pattern_offset(Vector3i offset); void set_pattern_offset(Vector3i offset);
protected: protected:
void generate_block_flat(VoxelBuffer & out_buffer, Vector3i block_pos); void generate_block_flat(VoxelBuffer &out_buffer, Vector3i block_pos);
void generate_block_waves(VoxelBuffer & out_buffer, Vector3i block_pos); void generate_block_waves(VoxelBuffer &out_buffer, Vector3i block_pos);
static void _bind_methods(); static void _bind_methods();
@ -48,6 +47,4 @@ private:
Vector3i _pattern_size; Vector3i _pattern_size;
}; };
#endif // VOXEL_PROVIDER_TEST_H #endif // VOXEL_PROVIDER_TEST_H

View File

@ -4,14 +4,13 @@
const float g_infinite = 9999999; const float g_infinite = 9999999;
bool voxel_raycast( bool voxel_raycast(
Vector3 ray_origin, Vector3 ray_origin,
Vector3 ray_direction, Vector3 ray_direction,
VoxelPredicate predicate, VoxelPredicate predicate,
void * predicate_context, void *predicate_context,
real_t max_distance, real_t max_distance,
Vector3i & out_hit_pos, Vector3i &out_hit_pos,
Vector3i & out_prev_pos Vector3i &out_prev_pos) {
){
// Equation : p + v*t // Equation : p + v*t
// p : ray start position (ray.pos) // p : ray start position (ray.pos)
// v : ray orientation vector (ray.dir) // v : ray orientation vector (ray.dir)
@ -29,10 +28,9 @@ bool voxel_raycast(
// Voxel position // Voxel position
Vector3i hit_pos( Vector3i hit_pos(
Math::floor(ray_origin.x), Math::floor(ray_origin.x),
Math::floor(ray_origin.y), Math::floor(ray_origin.y),
Math::floor(ray_origin.z) Math::floor(ray_origin.z));
);
Vector3i hit_prev_pos = hit_pos; Vector3i hit_prev_pos = hit_pos;
// Voxel step // Voxel step
@ -51,91 +49,74 @@ bool voxel_raycast(
real_t tcross_z; // At which value of T we will cross a depth line? real_t tcross_z; // At which value of T we will cross a depth line?
// X initialization // X initialization
if(xi_step != 0) if (xi_step != 0) {
{ if (xi_step == 1)
if(xi_step == 1)
tcross_x = (Math::ceil(ray_origin.x) - ray_origin.x) * tdelta_x; tcross_x = (Math::ceil(ray_origin.x) - ray_origin.x) * tdelta_x;
else else
tcross_x = (ray_origin.x - Math::floor(ray_origin.x)) * tdelta_x; tcross_x = (ray_origin.x - Math::floor(ray_origin.x)) * tdelta_x;
} } else
else
tcross_x = g_infinite; // Will never cross on X tcross_x = g_infinite; // Will never cross on X
// Y initialization // Y initialization
if(yi_step != 0) if (yi_step != 0) {
{ if (yi_step == 1)
if(yi_step == 1)
tcross_y = (Math::ceil(ray_origin.y) - ray_origin.y) * tdelta_y; tcross_y = (Math::ceil(ray_origin.y) - ray_origin.y) * tdelta_y;
else else
tcross_y = (ray_origin.y - Math::floor(ray_origin.y)) * tdelta_y; tcross_y = (ray_origin.y - Math::floor(ray_origin.y)) * tdelta_y;
} } else
else
tcross_y = g_infinite; // Will never cross on X tcross_y = g_infinite; // Will never cross on X
// Z initialization // Z initialization
if(zi_step != 0) if (zi_step != 0) {
{ if (zi_step == 1)
if(zi_step == 1)
tcross_z = (Math::ceil(ray_origin.z) - ray_origin.z) * tdelta_z; tcross_z = (Math::ceil(ray_origin.z) - ray_origin.z) * tdelta_z;
else else
tcross_z = (ray_origin.z - Math::floor(ray_origin.z)) * tdelta_z; tcross_z = (ray_origin.z - Math::floor(ray_origin.z)) * tdelta_z;
} } else
else
tcross_z = g_infinite; // Will never cross on X tcross_z = g_infinite; // Will never cross on X
/* Iteration */ /* Iteration */
do do {
{
hit_prev_pos = hit_pos; hit_prev_pos = hit_pos;
if(tcross_x < tcross_y) if (tcross_x < tcross_y) {
{ if (tcross_x < tcross_z) {
if(tcross_x < tcross_z)
{
// X collision // X collision
//hit.prevPos.x = hit.pos.x; //hit.prevPos.x = hit.pos.x;
hit_pos.x += xi_step; hit_pos.x += xi_step;
if(tcross_x > max_distance) if (tcross_x > max_distance)
return false; return false;
tcross_x += tdelta_x; tcross_x += tdelta_x;
} } else {
else
{
// Z collision (duplicate code) // Z collision (duplicate code)
//hit.prevPos.z = hit.pos.z; //hit.prevPos.z = hit.pos.z;
hit_pos.z += zi_step; hit_pos.z += zi_step;
if(tcross_z > max_distance) if (tcross_z > max_distance)
return false; return false;
tcross_z += tdelta_z; tcross_z += tdelta_z;
} }
} } else {
else if (tcross_y < tcross_z) {
{
if(tcross_y < tcross_z)
{
// Y collision // Y collision
//hit.prevPos.y = hit.pos.y; //hit.prevPos.y = hit.pos.y;
hit_pos.y += yi_step; hit_pos.y += yi_step;
if(tcross_y > max_distance) if (tcross_y > max_distance)
return false; return false;
tcross_y += tdelta_y; tcross_y += tdelta_y;
} } else {
else
{
// Z collision (duplicate code) // Z collision (duplicate code)
//hit.prevPos.z = hit.pos.z; //hit.prevPos.z = hit.pos.z;
hit_pos.z += zi_step; hit_pos.z += zi_step;
if(tcross_z > max_distance) if (tcross_z > max_distance)
return false; return false;
tcross_z += tdelta_z; tcross_z += tdelta_z;
} }
} }
} while(!predicate(hit_pos, predicate_context)); } while (!predicate(hit_pos, predicate_context));
out_hit_pos = hit_pos; out_hit_pos = hit_pos;
out_prev_pos = hit_prev_pos; out_prev_pos = hit_prev_pos;
return true; return true;
} }

View File

@ -1,18 +1,16 @@
#include <vector3.h>
#include "vector3i.h" #include "vector3i.h"
#include <vector3.h>
// TODO Having a C++11 lambda would be nice... // TODO that could be a template function
// pos: voxel position // pos: voxel position
// context: arguments to carry (as a lamdbda capture) // context: arguments to carry (as a lamdbda capture)
typedef bool(*VoxelPredicate)(Vector3i pos, void * context); typedef bool (*VoxelPredicate)(Vector3i pos, void *context);
bool voxel_raycast( bool voxel_raycast(
Vector3 ray_origin, Vector3 ray_origin,
Vector3 ray_direction, Vector3 ray_direction,
VoxelPredicate predicate, VoxelPredicate predicate,
void * predicate_context, // Handle that one with care void *predicate_context, // Handle that one with care
real_t max_distance, real_t max_distance,
Vector3i & out_hit_pos, Vector3i &out_hit_pos,
Vector3i & out_prev_pos Vector3i &out_prev_pos);
);

View File

@ -1,9 +1,10 @@
#include "voxel_terrain.h" #include "voxel_terrain.h"
#include <scene/3d/mesh_instance.h>
#include <os/os.h>
#include "voxel_raycast.h" #include "voxel_raycast.h"
#include <os/os.h>
#include <scene/3d/mesh_instance.h>
VoxelTerrain::VoxelTerrain(): Node(), _generate_collisions(true) { VoxelTerrain::VoxelTerrain()
: Node(), _generate_collisions(true) {
_map = Ref<VoxelMap>(memnew(VoxelMap)); _map = Ref<VoxelMap>(memnew(VoxelMap));
_mesher = Ref<VoxelMesher>(memnew(VoxelMesher)); _mesher = Ref<VoxelMesher>(memnew(VoxelMesher));
@ -14,7 +15,7 @@ Vector3i g_viewer_block_pos; // TODO UGLY! Lambdas or pointers needed...
// Sorts distance to viewer // Sorts distance to viewer
struct BlockUpdateComparator { struct BlockUpdateComparator {
inline bool operator()(const Vector3i & a, const Vector3i & b) const { inline bool operator()(const Vector3i &a, const Vector3i &b) const {
return a.distance_sq(g_viewer_block_pos) > b.distance_sq(g_viewer_block_pos); return a.distance_sq(g_viewer_block_pos) > b.distance_sq(g_viewer_block_pos);
} }
}; };
@ -36,7 +37,7 @@ void VoxelTerrain::set_generate_collisions(bool enabled) {
} }
void VoxelTerrain::set_viewer_path(NodePath path) { void VoxelTerrain::set_viewer_path(NodePath path) {
if(!path.is_empty()) if (!path.is_empty())
ERR_FAIL_COND(get_viewer(path) == NULL); ERR_FAIL_COND(get_viewer(path) == NULL);
_viewer_path = path; _viewer_path = path;
} }
@ -45,11 +46,11 @@ NodePath VoxelTerrain::get_viewer_path() {
return _viewer_path; return _viewer_path;
} }
Spatial * VoxelTerrain::get_viewer(NodePath path) { Spatial *VoxelTerrain::get_viewer(NodePath path) {
if(path.is_empty()) if (path.is_empty())
return NULL; return NULL;
Node * node = get_node(path); Node *node = get_node(path);
if(node == NULL) if (node == NULL)
return NULL; return NULL;
return node->cast_to<Spatial>(); return node->cast_to<Spatial>();
} }
@ -61,7 +62,7 @@ Spatial * VoxelTerrain::get_viewer(NodePath path) {
void VoxelTerrain::make_block_dirty(Vector3i bpos) { void VoxelTerrain::make_block_dirty(Vector3i bpos) {
// TODO Immediate update viewer distance // TODO Immediate update viewer distance
if(is_block_dirty(bpos) == false) { if (is_block_dirty(bpos) == false) {
//OS::get_singleton()->print("Dirty (%i, %i, %i)", bpos.x, bpos.y, bpos.z); //OS::get_singleton()->print("Dirty (%i, %i, %i)", bpos.x, bpos.y, bpos.z);
_block_update_queue.push_back(bpos); _block_update_queue.push_back(bpos);
_dirty_blocks[bpos] = true; _dirty_blocks[bpos] = true;
@ -75,9 +76,9 @@ bool VoxelTerrain::is_block_dirty(Vector3i bpos) {
void VoxelTerrain::make_blocks_dirty(Vector3i min, Vector3i size) { void VoxelTerrain::make_blocks_dirty(Vector3i min, Vector3i size) {
Vector3i max = min + size; Vector3i max = min + size;
Vector3i pos; Vector3i pos;
for(pos.z = min.z; pos.z < max.z; ++pos.z) { for (pos.z = min.z; pos.z < max.z; ++pos.z) {
for(pos.y = min.y; pos.y < max.y; ++pos.y) { for (pos.y = min.y; pos.y < max.y; ++pos.y) {
for(pos.x = min.x; pos.x < max.x; ++pos.x) { for (pos.x = min.x; pos.x < max.x; ++pos.x) {
make_block_dirty(pos); make_block_dirty(pos);
} }
} }
@ -101,28 +102,25 @@ void VoxelTerrain::make_voxel_dirty(Vector3i pos) {
bool check_corners = _mesher->get_occlusion_enabled(); bool check_corners = _mesher->get_occlusion_enabled();
const int max = VoxelBlock::SIZE-1; const int max = VoxelBlock::SIZE - 1;
if(rpos.x == 0) if (rpos.x == 0)
make_block_dirty(bpos - Vector3i(1,0,0)); make_block_dirty(bpos - Vector3i(1, 0, 0));
else else if (rpos.x == max)
if(rpos.x == max) make_block_dirty(bpos + Vector3i(1, 0, 0));
make_block_dirty(bpos + Vector3i(1,0,0));
if(rpos.y == 0) if (rpos.y == 0)
make_block_dirty(bpos - Vector3i(0,1,0)); make_block_dirty(bpos - Vector3i(0, 1, 0));
else else if (rpos.y == max)
if(rpos.y == max) make_block_dirty(bpos + Vector3i(0, 1, 0));
make_block_dirty(bpos + Vector3i(0,1,0));
if(rpos.z == 0) if (rpos.z == 0)
make_block_dirty(bpos - Vector3i(0,0,1)); make_block_dirty(bpos - Vector3i(0, 0, 1));
else else if (rpos.z == max)
if(rpos.z == max) make_block_dirty(bpos + Vector3i(0, 0, 1));
make_block_dirty(bpos + Vector3i(0,0,1));
// We might want to update blocks in corners in order to update ambient occlusion // We might want to update blocks in corners in order to update ambient occlusion
if(check_corners) { if (check_corners) {
// 24------25------26 // 24------25------26
// /| /| // /| /|
@ -143,17 +141,17 @@ void VoxelTerrain::make_voxel_dirty(Vector3i pos) {
// I'm not good at writing piles of ifs // I'm not good at writing piles of ifs
static const int normals[27][3] = { static const int normals[27][3] = {
{-1,-1,-1}, { 0,-1,-1}, { 1,-1,-1}, { -1, -1, -1 }, { 0, -1, -1 }, { 1, -1, -1 },
{-1,-1, 0}, { 0,-1, 0}, { 1,-1, 0}, { -1, -1, 0 }, { 0, -1, 0 }, { 1, -1, 0 },
{-1,-1, 1}, { 0,-1, 1}, { 1,-1, 1}, { -1, -1, 1 }, { 0, -1, 1 }, { 1, -1, 1 },
{-1, 0,-1}, { 0, 0,-1}, { 1, 0,-1}, { -1, 0, -1 }, { 0, 0, -1 }, { 1, 0, -1 },
{-1, 0, 0}, { 0, 0, 0}, { 1, 0, 0}, { -1, 0, 0 }, { 0, 0, 0 }, { 1, 0, 0 },
{-1, 0, 1}, { 0, 0, 1}, { 1, 0, 1}, { -1, 0, 1 }, { 0, 0, 1 }, { 1, 0, 1 },
{-1, 1,-1}, { 0, 1,-1}, { 1, 1,-1}, { -1, 1, -1 }, { 0, 1, -1 }, { 1, 1, -1 },
{-1, 1, 0}, { 0, 1, 0}, { 1, 1, 0}, { -1, 1, 0 }, { 0, 1, 0 }, { 1, 1, 0 },
{-1, 1, 1}, { 0, 1, 1}, { 1, 1, 1} { -1, 1, 1 }, { 0, 1, 1 }, { 1, 1, 1 }
}; };
static const int ce_counts[27] = { static const int ce_counts[27] = {
4, 1, 4, 4, 1, 4,
@ -169,32 +167,30 @@ void VoxelTerrain::make_voxel_dirty(Vector3i pos) {
4, 1, 4 4, 1, 4
}; };
static const int ce_indexes_lut[27][4] = { static const int ce_indexes_lut[27][4] = {
{0, 1, 3, 9}, {1}, {2, 1, 5, 11}, { 0, 1, 3, 9 }, { 1 }, { 2, 1, 5, 11 },
{3}, {}, {5}, { 3 }, {}, { 5 },
{6, 3, 7, 15}, {7}, {8, 7, 5, 17}, { 6, 3, 7, 15 }, { 7 }, { 8, 7, 5, 17 },
{9}, {}, {11}, { 9 }, {}, { 11 },
{}, {}, {}, {}, {}, {},
{15}, {}, {17}, { 15 }, {}, { 17 },
{18, 9, 19, 21}, {19}, {20, 11, 19, 23}, { 18, 9, 19, 21 }, { 19 }, { 20, 11, 19, 23 },
{21}, {}, {23}, { 21 }, {}, { 23 },
{24, 15, 21, 25}, {25}, {26, 17, 23, 25} { 24, 15, 21, 25 }, { 25 }, { 26, 17, 23, 25 }
}; };
int m = get_border_index(rpos.x, max) int m = get_border_index(rpos.x, max) + 3 * get_border_index(rpos.z, max) + 9 * get_border_index(rpos.y, max);
+ 3*get_border_index(rpos.z, max)
+ 9*get_border_index(rpos.y, max);
const int * ce_indexes = ce_indexes_lut[m]; const int *ce_indexes = ce_indexes_lut[m];
int ce_count = ce_counts[m]; int ce_count = ce_counts[m];
//OS::get_singleton()->print("m=%i, rpos=(%i, %i, %i)\n", m, rpos.x, rpos.y, rpos.z); //OS::get_singleton()->print("m=%i, rpos=(%i, %i, %i)\n", m, rpos.x, rpos.y, rpos.z);
for(int i = 0; i < ce_count; ++i) { for (int i = 0; i < ce_count; ++i) {
// TODO Because it's about ambient occlusion across 1 voxel only, // TODO Because it's about ambient occlusion across 1 voxel only,
// we could optimize it even more by looking at neighbor voxels, // we could optimize it even more by looking at neighbor voxels,
// and discard the update if we know it won't change anything // and discard the update if we know it won't change anything
const int * normal = normals[ce_indexes[i]]; const int *normal = normals[ce_indexes[i]];
Vector3i nbpos(bpos.x + normal[0], bpos.y + normal[1], bpos.z + normal[2]); Vector3i nbpos(bpos.x + normal[0], bpos.y + normal[1], bpos.z + normal[2]);
//OS::get_singleton()->print("Corner dirty (%i, %i, %i)\n", nbpos.x, nbpos.y, nbpos.z); //OS::get_singleton()->print("Corner dirty (%i, %i, %i)\n", nbpos.x, nbpos.y, nbpos.z);
make_block_dirty(nbpos); make_block_dirty(nbpos);
@ -210,19 +206,19 @@ void VoxelTerrain::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_ENTER_TREE: case NOTIFICATION_ENTER_TREE:
set_process(true); set_process(true);
break; break;
case NOTIFICATION_PROCESS: case NOTIFICATION_PROCESS:
_process(); _process();
break; break;
case NOTIFICATION_EXIT_TREE: case NOTIFICATION_EXIT_TREE:
break; break;
default: default:
break; break;
} }
} }
@ -231,11 +227,11 @@ void VoxelTerrain::_process() {
} }
void VoxelTerrain::update_blocks() { void VoxelTerrain::update_blocks() {
OS & os = *OS::get_singleton(); OS &os = *OS::get_singleton();
// Get viewer location // Get viewer location
Spatial * viewer = get_viewer(_viewer_path); Spatial *viewer = get_viewer(_viewer_path);
if(viewer) if (viewer)
g_viewer_block_pos = VoxelMap::voxel_to_block(viewer->get_translation()); g_viewer_block_pos = VoxelMap::voxel_to_block(viewer->get_translation());
else else
g_viewer_block_pos = Vector3i(); g_viewer_block_pos = Vector3i();
@ -263,7 +259,7 @@ void VoxelTerrain::update_blocks() {
if (!_map->has_block(block_pos)) { if (!_map->has_block(block_pos)) {
// Create buffer // Create buffer
if(!_provider.is_null()) { if (!_provider.is_null()) {
VOXEL_PROFILE_BEGIN("voxel_buffer_creation_gen") VOXEL_PROFILE_BEGIN("voxel_buffer_creation_gen")
@ -291,7 +287,7 @@ void VoxelTerrain::update_blocks() {
// Update views (mesh/collisions) // Update views (mesh/collisions)
if(entire_block_changed) { if (entire_block_changed) {
// All neighbors have to be checked // All neighbors have to be checked
Vector3i ndir; Vector3i ndir;
for (ndir.z = -1; ndir.z < 2; ++ndir.z) { for (ndir.z = -1; ndir.z < 2; ++ndir.z) {
@ -305,8 +301,7 @@ void VoxelTerrain::update_blocks() {
} }
} }
} }
} } else {
else {
// Only update the block, neighbors will probably follow if needed // Only update the block, neighbors will probably follow if needed
update_block_mesh(block_pos); update_block_mesh(block_pos);
//OS::get_singleton()->print("Update (%i, %i, %i)\n", block_pos.x, block_pos.y, block_pos.z); //OS::get_singleton()->print("Update (%i, %i, %i)\n", block_pos.x, block_pos.y, block_pos.z);
@ -318,23 +313,21 @@ void VoxelTerrain::update_blocks() {
} }
} }
static inline bool is_mesh_empty(Ref<Mesh> mesh_ref) { static inline bool is_mesh_empty(Ref<Mesh> mesh_ref) {
if(mesh_ref.is_null()) if (mesh_ref.is_null())
return true; return true;
Mesh & mesh = **mesh_ref; Mesh &mesh = **mesh_ref;
if(mesh.get_surface_count() == 0) if (mesh.get_surface_count() == 0)
return true; return true;
// TODO Shouldn't it have an index to the surface rather than just the type? Oo // TODO Shouldn't it have an index to the surface rather than just the type? Oo
if(mesh.surface_get_array_len(Mesh::ARRAY_VERTEX) == 0) if (mesh.surface_get_array_len(Mesh::ARRAY_VERTEX) == 0)
return true; return true;
return false; return false;
} }
void VoxelTerrain::update_block_mesh(Vector3i block_pos) { void VoxelTerrain::update_block_mesh(Vector3i block_pos) {
VoxelBlock * block = _map->get_block(block_pos); VoxelBlock *block = _map->get_block(block_pos);
if (block == NULL) { if (block == NULL) {
return; return;
} }
@ -354,21 +347,20 @@ void VoxelTerrain::update_block_mesh(Vector3i block_pos) {
Vector3 block_node_pos = VoxelMap::block_to_voxel(block_pos).to_vec3(); Vector3 block_node_pos = VoxelMap::block_to_voxel(block_pos).to_vec3();
// TODO Re-use existing meshes to optimize memory cost // TODO Re-use existing meshes to optimize memory cost
// Build cubic parts of the mesh // Build cubic parts of the mesh
Ref<ArrayMesh> mesh = _mesher->build(nbuffer, Voxel::CHANNEL_TYPE, Vector3i(0,0,0), nbuffer.get_size()-Vector3(1,1,1)); Ref<ArrayMesh> mesh = _mesher->build(nbuffer, Voxel::CHANNEL_TYPE, Vector3i(0, 0, 0), nbuffer.get_size() - Vector3(1, 1, 1));
// Build smooth parts of the mesh // Build smooth parts of the mesh
_mesher_smooth->build(nbuffer, Voxel::CHANNEL_ISOLEVEL, mesh); _mesher_smooth->build(nbuffer, Voxel::CHANNEL_ISOLEVEL, mesh);
MeshInstance * mesh_instance = block->get_mesh_instance(*this); MeshInstance *mesh_instance = block->get_mesh_instance(*this);
if(is_mesh_empty(mesh)) { if (is_mesh_empty(mesh)) {
if(mesh_instance) { if (mesh_instance) {
mesh_instance->set_mesh(Ref<Mesh>()); mesh_instance->set_mesh(Ref<Mesh>());
} }
} } else {
else {
// The mesh exist and it has vertices // The mesh exist and it has vertices
// TODO Don't use nodes! Use servers directly, it's faster // TODO Don't use nodes! Use servers directly, it's faster
@ -379,15 +371,14 @@ void VoxelTerrain::update_block_mesh(Vector3i block_pos) {
mesh_instance->set_translation(block_node_pos); mesh_instance->set_translation(block_node_pos);
add_child(mesh_instance); add_child(mesh_instance);
block->mesh_instance_path = mesh_instance->get_path(); block->mesh_instance_path = mesh_instance->get_path();
} } else {
else {
// Update mesh // Update mesh
VOXEL_PROFILE_BEGIN("mesh_instance_set_mesh") VOXEL_PROFILE_BEGIN("mesh_instance_set_mesh")
mesh_instance->set_mesh(mesh); mesh_instance->set_mesh(mesh);
VOXEL_PROFILE_END("mesh_instance_set_mesh") VOXEL_PROFILE_END("mesh_instance_set_mesh")
} }
if(get_tree()->is_editor_hint() == false && _generate_collisions) { if (get_tree()->is_editor_hint() == false && _generate_collisions) {
// TODO Generate collisions using PhysicsServer // TODO Generate collisions using PhysicsServer
// TODO Need to select only specific surfaces because some may not have collisions // TODO Need to select only specific surfaces because some may not have collisions
@ -403,15 +394,15 @@ void VoxelTerrain::update_block_mesh(Vector3i block_pos) {
//} //}
struct _VoxelTerrainRaycastContext { struct _VoxelTerrainRaycastContext {
VoxelTerrain & terrain; VoxelTerrain &terrain;
//unsigned int channel_mask; //unsigned int channel_mask;
}; };
static bool _raycast_binding_predicate(Vector3i pos, void *context_ptr) { static bool _raycast_binding_predicate(Vector3i pos, void *context_ptr) {
ERR_FAIL_COND_V(context_ptr == NULL, false); ERR_FAIL_COND_V(context_ptr == NULL, false);
_VoxelTerrainRaycastContext * context = (_VoxelTerrainRaycastContext*)context_ptr; _VoxelTerrainRaycastContext *context = (_VoxelTerrainRaycastContext *)context_ptr;
VoxelTerrain & terrain = context->terrain; VoxelTerrain &terrain = context->terrain;
//unsigned int channel = context->channel; //unsigned int channel = context->channel;
@ -419,15 +410,15 @@ static bool _raycast_binding_predicate(Vector3i pos, void *context_ptr) {
int v0 = map->get_voxel(pos, Voxel::CHANNEL_TYPE); int v0 = map->get_voxel(pos, Voxel::CHANNEL_TYPE);
Ref<VoxelLibrary> lib_ref = terrain.get_voxel_library(); Ref<VoxelLibrary> lib_ref = terrain.get_voxel_library();
if(lib_ref.is_null()) if (lib_ref.is_null())
return false; return false;
const VoxelLibrary & lib = **lib_ref; const VoxelLibrary &lib = **lib_ref;
if(lib.has_voxel(v0) == false) if (lib.has_voxel(v0) == false)
return false; return false;
const Voxel & voxel = lib.get_voxel_const(v0); const Voxel &voxel = lib.get_voxel_const(v0);
if(voxel.is_transparent() == false) if (voxel.is_transparent() == false)
return true; return true;
int v1 = map->get_voxel(pos, Voxel::CHANNEL_ISOLEVEL); int v1 = map->get_voxel(pos, Voxel::CHANNEL_ISOLEVEL);
@ -443,14 +434,13 @@ Variant VoxelTerrain::_raycast_binding(Vector3 origin, Vector3 direction, real_t
_VoxelTerrainRaycastContext context = { *this }; _VoxelTerrainRaycastContext context = { *this };
if(voxel_raycast(origin, direction, _raycast_binding_predicate, &context, max_distance, hit_pos, prev_pos)) { if (voxel_raycast(origin, direction, _raycast_binding_predicate, &context, max_distance, hit_pos, prev_pos)) {
Dictionary hit = Dictionary(); Dictionary hit = Dictionary();
hit["position"] = hit_pos.to_vec3(); hit["position"] = hit_pos.to_vec3();
hit["prev_position"] = prev_pos.to_vec3(); hit["prev_position"] = prev_pos.to_vec3();
return hit; return hit;
} } else {
else {
return Variant(); // Null dictionary, no alloc return Variant(); // Null dictionary, no alloc
} }
} }
@ -484,6 +474,4 @@ void VoxelTerrain::_bind_methods() {
#ifdef VOXEL_PROFILING #ifdef VOXEL_PROFILING
ClassDB::bind_method(D_METHOD("get_profiling_info"), &VoxelTerrain::get_profiling_info); ClassDB::bind_method(D_METHOD("get_profiling_info"), &VoxelTerrain::get_profiling_info);
#endif #endif
} }

View File

@ -1,12 +1,12 @@
#ifndef VOXEL_TERRAIN_H #ifndef VOXEL_TERRAIN_H
#define VOXEL_TERRAIN_H #define VOXEL_TERRAIN_H
#include <scene/main/node.h>
#include "voxel_map.h" #include "voxel_map.h"
#include "voxel_mesher.h" #include "voxel_mesher.h"
#include "voxel_mesher_smooth.h" #include "voxel_mesher_smooth.h"
#include "voxel_provider.h" #include "voxel_provider.h"
#include "zprofiling.h" #include "zprofiling.h"
#include <scene/main/node.h>
// Infinite static terrain made of voxels. // Infinite static terrain made of voxels.
// It is loaded around VoxelTerrainStreamers. // It is loaded around VoxelTerrainStreamers.
@ -46,7 +46,7 @@ private:
void update_blocks(); void update_blocks();
void update_block_mesh(Vector3i block_pos); void update_block_mesh(Vector3i block_pos);
Spatial * get_viewer(NodePath path); Spatial *get_viewer(NodePath path);
// Observer events // Observer events
//void block_removed(VoxelBlock & block); //void block_removed(VoxelBlock & block);
@ -90,8 +90,6 @@ private:
ZProfiler _zprofiler; ZProfiler _zprofiler;
Dictionary get_profiling_info() { return _zprofiler.get_all_serialized_info(); } Dictionary get_profiling_info() { return _zprofiler.get_all_serialized_info(); }
#endif #endif
}; };
#endif // VOXEL_TERRAIN_H #endif // VOXEL_TERRAIN_H

View File

@ -40,7 +40,7 @@ ZProfileVar::ZProfileVar() {
total_time = 0.f; total_time = 0.f;
hits = 0; hits = 0;
_begin_time = 0.f; _begin_time = 0.f;
for(unsigned int i = 0; i < BUFFERED_TIME_COUNT; ++i) for (unsigned int i = 0; i < BUFFERED_TIME_COUNT; ++i)
buffered_times[i] = 0; buffered_times[i] = 0;
buffered_time_index = 0; buffered_time_index = 0;
} }
@ -52,17 +52,14 @@ void ZProfileVar::begin(uint64_t time) {
void ZProfileVar::end(uint64_t time) { void ZProfileVar::end(uint64_t time) {
instant_time = time - _begin_time; instant_time = time - _begin_time;
if(hits == 0) if (hits == 0) {
{
min_time = instant_time; min_time = instant_time;
max_time = instant_time; max_time = instant_time;
} } else {
else if (instant_time < min_time)
{
if(instant_time < min_time)
min_time = instant_time; min_time = instant_time;
if(instant_time > max_time) if (instant_time > max_time)
max_time = instant_time; max_time = instant_time;
} }
@ -70,7 +67,7 @@ void ZProfileVar::end(uint64_t time) {
buffered_times[buffered_time_index] = instant_time; buffered_times[buffered_time_index] = instant_time;
++buffered_time_index; ++buffered_time_index;
if(buffered_time_index >= BUFFERED_TIME_COUNT) if (buffered_time_index >= BUFFERED_TIME_COUNT)
buffered_time_index = 0; buffered_time_index = 0;
++hits; ++hits;
@ -86,7 +83,7 @@ Dictionary ZProfileVar::serialize() {
d["hits"] = hits; d["hits"] = hits;
Array a; Array a;
for(unsigned int i = 0; i < BUFFERED_TIME_COUNT; ++i) { for (unsigned int i = 0; i < BUFFERED_TIME_COUNT; ++i) {
a.append(buffered_times[i]); a.append(buffered_times[i]);
} }
d["buffered_times"] = a; d["buffered_times"] = a;
@ -103,47 +100,44 @@ Dictionary ZProfileVar::serialize() {
//} //}
ZProfiler::~ZProfiler() { ZProfiler::~ZProfiler() {
const String * key = NULL; const String *key = NULL;
while (key = _vars.next(key)) { while (key = _vars.next(key)) {
ZProfileVar * v = _vars.get(*key); ZProfileVar *v = _vars.get(*key);
memdelete(v); memdelete(v);
} }
} }
ZProfileVar * ZProfiler::get_var(String key) { ZProfileVar *ZProfiler::get_var(String key) {
ZProfileVar * v = NULL; ZProfileVar *v = NULL;
ZProfileVar ** pv = _vars.getptr(key); ZProfileVar **pv = _vars.getptr(key);
if(pv == NULL) { if (pv == NULL) {
v = memnew(ZProfileVar); v = memnew(ZProfileVar);
_vars[key] = v; _vars[key] = v;
} } else {
else {
v = *pv; v = *pv;
} }
return v; return v;
} }
void ZProfiler::begin(String key) { void ZProfiler::begin(String key) {
ZProfileVar * v = get_var(key); ZProfileVar *v = get_var(key);
v->begin(get_time()); v->begin(get_time());
} }
void ZProfiler::end(String key) { void ZProfiler::end(String key) {
uint64_t time = get_time(); uint64_t time = get_time();
ZProfileVar * v = get_var(key); ZProfileVar *v = get_var(key);
v->end(time); v->end(time);
} }
Dictionary ZProfiler::get_all_serialized_info() const { Dictionary ZProfiler::get_all_serialized_info() const {
Dictionary d; Dictionary d;
const String * key = NULL; const String *key = NULL;
while (key = _vars.next(key)) { while (key = _vars.next(key)) {
ZProfileVar * v = _vars.get(*key); ZProfileVar *v = _vars.get(*key);
d[*key] = v->serialize(); d[*key] = v->serialize();
} }
return d; return d;
} }
#endif // VOXEL_PROFILING #endif // VOXEL_PROFILING

View File

@ -5,9 +5,9 @@
#ifdef VOXEL_PROFILING #ifdef VOXEL_PROFILING
#include <ustring.h>
#include <dictionary.h> #include <dictionary.h>
#include <hash_map.h> #include <hash_map.h>
#include <ustring.h>
#define VOXEL_PROFILE_BEGIN(_key) _zprofiler.begin(_key); #define VOXEL_PROFILE_BEGIN(_key) _zprofiler.begin(_key);
#define VOXEL_PROFILE_END(_key) _zprofiler.end(_key); #define VOXEL_PROFILE_END(_key) _zprofiler.end(_key);
@ -26,9 +26,9 @@ public:
private: private:
//ZProfiler(); //ZProfiler();
ZProfileVar * get_var(String key); ZProfileVar *get_var(String key);
HashMap<String, ZProfileVar*> _vars; HashMap<String, ZProfileVar *> _vars;
}; };
#else #else