mirror of
https://github.com/Relintai/godot_voxel.git
synced 2025-01-27 08:29:19 +01:00
Clang-format
This commit is contained in:
parent
bc75ec863b
commit
51596fef95
64
vector3i.h
64
vector3i.h
@ -1,8 +1,8 @@
|
||||
#ifndef VOXEL_VECTOR3I_H
|
||||
#define VOXEL_VECTOR3I_H
|
||||
|
||||
#include <core/math/vector3.h>
|
||||
#include <core/hashfuncs.h>
|
||||
#include <core/math/vector3.h>
|
||||
|
||||
struct Vector3i {
|
||||
|
||||
@ -15,15 +15,17 @@ struct Vector3i {
|
||||
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;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Vector3i(const Vector3 & f) {
|
||||
_FORCE_INLINE_ Vector3i(const Vector3 &f) {
|
||||
x = Math::floor(f.x);
|
||||
y = Math::floor(f.y);
|
||||
z = Math::floor(f.z);
|
||||
@ -34,39 +36,39 @@ struct Vector3i {
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int volume() const {
|
||||
return x*y*z;
|
||||
return x * y * z;
|
||||
}
|
||||
|
||||
_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 {
|
||||
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;
|
||||
y = other.y;
|
||||
z = other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void operator+=(const Vector3i & other) {
|
||||
_FORCE_INLINE_ void operator+=(const Vector3i &other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void operator-=(const Vector3i & other) {
|
||||
_FORCE_INLINE_ void operator-=(const Vector3i &other) {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int & operator[](unsigned int i) {
|
||||
_FORCE_INLINE_ int &operator[](unsigned int i) {
|
||||
return coords[i];
|
||||
}
|
||||
|
||||
@ -80,70 +82,67 @@ struct Vector3i {
|
||||
if (z >= max.z) z = max.z - 1;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool is_contained_in(const Vector3i & min, const Vector3i & max) {
|
||||
return x >= min.x && y >= min.y && z >= min.z
|
||||
&& x < max.x && y < max.y && z < max.z;
|
||||
_FORCE_INLINE_ bool is_contained_in(const Vector3i &min, const Vector3i &max) {
|
||||
return x >= min.x && y >= min.y && z >= min.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(
|
||||
x % size.x,
|
||||
y % size.y,
|
||||
z % size.z
|
||||
);
|
||||
x % size.x,
|
||||
y % size.y,
|
||||
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.y, b.y);
|
||||
sort_min_max(a.z, b.z);
|
||||
}
|
||||
|
||||
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) {
|
||||
int temp = a;
|
||||
a = b;
|
||||
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);
|
||||
}
|
||||
|
||||
_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);
|
||||
}
|
||||
|
||||
_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);
|
||||
}
|
||||
|
||||
_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);
|
||||
}
|
||||
|
||||
_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);
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
_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();
|
||||
}
|
||||
|
||||
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);
|
||||
hash = hash_djb2_one_32(v.y, hash);
|
||||
return hash_djb2_one_32(v.z, hash);
|
||||
@ -151,4 +150,3 @@ struct Vector3iHasher {
|
||||
};
|
||||
|
||||
#endif // VOXEL_VECTOR3I_H
|
||||
|
||||
|
130
voxel.cpp
130
voxel.cpp
@ -2,13 +2,13 @@
|
||||
#include "voxel_library.h"
|
||||
#include "voxel_mesher.h"
|
||||
|
||||
Voxel::Voxel() : Reference(),
|
||||
_id(-1),
|
||||
_material_id(0),
|
||||
_is_transparent(false),
|
||||
_library(NULL),
|
||||
_color(1.f, 1.f, 1.f)
|
||||
{}
|
||||
Voxel::Voxel()
|
||||
: Reference(),
|
||||
_id(-1),
|
||||
_material_id(0),
|
||||
_is_transparent(false),
|
||||
_library(NULL),
|
||||
_color(1.f, 1.f, 1.f) {}
|
||||
|
||||
Ref<Voxel> Voxel::set_name(String name) {
|
||||
_name = name;
|
||||
@ -41,60 +41,48 @@ Ref<Voxel> Voxel::set_transparent(bool t) {
|
||||
|
||||
Ref<Voxel> Voxel::set_cube_geometry(float sy) {
|
||||
const Vector3 vertices[SIDE_COUNT][6] = {
|
||||
{
|
||||
// LEFT
|
||||
Vector3(0, 0, 0),
|
||||
Vector3(0, sy, 0),
|
||||
Vector3(0, sy, 1),
|
||||
Vector3(0, 0, 0),
|
||||
Vector3(0, sy, 1),
|
||||
Vector3(0, 0, 1),
|
||||
},
|
||||
{
|
||||
// RIGHT
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(1, sy, 1),
|
||||
Vector3(1, sy, 0),
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(1, 0, 1),
|
||||
Vector3(1, sy, 1)
|
||||
},
|
||||
{
|
||||
// BOTTOM
|
||||
Vector3(0, 0, 0),
|
||||
Vector3(1, 0, 1),
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(0, 0, 0),
|
||||
Vector3(0, 0, 1),
|
||||
Vector3(1, 0, 1)
|
||||
},
|
||||
{
|
||||
// TOP
|
||||
Vector3(0, sy, 0),
|
||||
Vector3(1, sy, 0),
|
||||
Vector3(1, sy, 1),
|
||||
Vector3(0, sy, 0),
|
||||
Vector3(1, sy, 1),
|
||||
Vector3(0, sy, 1)
|
||||
},
|
||||
{
|
||||
// BACK
|
||||
Vector3(0, 0, 0),
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(1, sy, 0),
|
||||
Vector3(0, 0, 0),
|
||||
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)
|
||||
}
|
||||
{ // LEFT
|
||||
Vector3(0, 0, 0),
|
||||
Vector3(0, sy, 0),
|
||||
Vector3(0, sy, 1),
|
||||
Vector3(0, 0, 0),
|
||||
Vector3(0, sy, 1),
|
||||
Vector3(0, 0, 1) },
|
||||
{ // RIGHT
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(1, sy, 1),
|
||||
Vector3(1, sy, 0),
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(1, 0, 1),
|
||||
Vector3(1, sy, 1) },
|
||||
{ // BOTTOM
|
||||
Vector3(0, 0, 0),
|
||||
Vector3(1, 0, 1),
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(0, 0, 0),
|
||||
Vector3(0, 0, 1),
|
||||
Vector3(1, 0, 1) },
|
||||
{ // TOP
|
||||
Vector3(0, sy, 0),
|
||||
Vector3(1, sy, 0),
|
||||
Vector3(1, sy, 1),
|
||||
Vector3(0, sy, 0),
|
||||
Vector3(1, sy, 1),
|
||||
Vector3(0, sy, 1) },
|
||||
{ // BACK
|
||||
Vector3(0, 0, 0),
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(1, sy, 0),
|
||||
Vector3(0, 0, 0),
|
||||
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) {
|
||||
@ -121,17 +109,17 @@ Ref<Voxel> Voxel::_set_cube_uv_sides(const Vector2 atlas_pos[6]) {
|
||||
|
||||
const int uv6[SIDE_COUNT][6] = {
|
||||
// LEFT
|
||||
{ 2,0,1,2,1,3 },
|
||||
{ 2, 0, 1, 2, 1, 3 },
|
||||
// RIGHT
|
||||
{ 2,1,0,2,3,1 },
|
||||
{ 2, 1, 0, 2, 3, 1 },
|
||||
// BOTTOM
|
||||
{ 0,3,1,0,2,3 },
|
||||
{ 0, 3, 1, 0, 2, 3 },
|
||||
// TOP
|
||||
{ 0,1,3,0,3,2 },
|
||||
{ 0, 1, 3, 0, 3, 2 },
|
||||
// BACK
|
||||
{ 2,3,1,2,1,0 },
|
||||
{ 2, 3, 1, 2, 1, 0 },
|
||||
// FRONT
|
||||
{ 3,2,1,2,0,1 }
|
||||
{ 3, 2, 1, 2, 0, 1 }
|
||||
};
|
||||
|
||||
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_tbs_sides", "top_atlas_pos", "side_atlas_pos", "bottom_atlas_pos"), &Voxel::set_cube_uv_tbs_sides);
|
||||
|
||||
BIND_CONSTANT( CHANNEL_TYPE )
|
||||
BIND_CONSTANT( CHANNEL_ISOLEVEL )
|
||||
BIND_CONSTANT( CHANNEL_DATA )
|
||||
|
||||
BIND_CONSTANT(CHANNEL_TYPE)
|
||||
BIND_CONSTANT(CHANNEL_ISOLEVEL)
|
||||
BIND_CONSTANT(CHANNEL_DATA)
|
||||
}
|
||||
|
||||
|
17
voxel.h
17
voxel.h
@ -60,13 +60,13 @@ public:
|
||||
|
||||
// Getters for native usage only
|
||||
|
||||
const PoolVector<Vector3> & get_model_vertices() const { return _model_vertices; }
|
||||
const PoolVector<Vector3> & get_model_normals() const { return _model_normals; }
|
||||
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<Vector2> & get_model_side_uv(unsigned int side) const { return _model_side_uv[side]; }
|
||||
const PoolVector<Vector3> &get_model_vertices() const { return _model_vertices; }
|
||||
const PoolVector<Vector3> &get_model_normals() const { return _model_normals; }
|
||||
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<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:
|
||||
Ref<Voxel> _set_cube_uv_sides(const Vector2 atlas_pos[6]);
|
||||
@ -74,7 +74,7 @@ protected:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
VoxelLibrary * _library;
|
||||
VoxelLibrary *_library;
|
||||
|
||||
// Identifiers
|
||||
int _id;
|
||||
@ -93,11 +93,8 @@ private:
|
||||
PoolVector<Vector2> _model_side_uv[SIDE_COUNT];
|
||||
|
||||
// TODO Child voxel types
|
||||
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(Voxel::ChannelMode)
|
||||
|
||||
|
||||
#endif // VOXEL_TYPE_H
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
#include "voxel_buffer.h"
|
||||
#include <string.h>
|
||||
#include <math_funcs.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
VoxelBuffer::VoxelBuffer() {
|
||||
|
||||
}
|
||||
|
||||
VoxelBuffer::~VoxelBuffer() {
|
||||
@ -18,7 +16,7 @@ void VoxelBuffer::create(int sx, int sy, int sz) {
|
||||
Vector3i new_size(sx, sy, sz);
|
||||
if (new_size != _size) {
|
||||
for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
|
||||
Channel & channel = _channels[i];
|
||||
Channel &channel = _channels[i];
|
||||
if (channel.data) {
|
||||
// TODO Optimize with realloc
|
||||
delete_channel(i);
|
||||
@ -31,7 +29,7 @@ void VoxelBuffer::create(int sx, int sy, int sz) {
|
||||
|
||||
void VoxelBuffer::clear() {
|
||||
for (unsigned int i = 0; i < MAX_CHANNELS; ++i) {
|
||||
Channel & channel = _channels[i];
|
||||
Channel &channel = _channels[i];
|
||||
if (channel.data) {
|
||||
delete_channel(i);
|
||||
}
|
||||
@ -40,13 +38,13 @@ void VoxelBuffer::clear() {
|
||||
|
||||
void VoxelBuffer::clear_channel(unsigned int channel_index, int clear_value) {
|
||||
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||
if(_channels[channel_index].data)
|
||||
if (_channels[channel_index].data)
|
||||
delete_channel(channel_index);
|
||||
_channels[channel_index].defval = clear_value;
|
||||
}
|
||||
|
||||
void VoxelBuffer::set_default_values(uint8_t values[VoxelBuffer::MAX_CHANNELS]) {
|
||||
for(unsigned int i = 0; i < MAX_CHANNELS; ++i) {
|
||||
for (unsigned int i = 0; i < MAX_CHANNELS; ++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 {
|
||||
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) {
|
||||
return channel.data[index(x,y,z)];
|
||||
}
|
||||
else {
|
||||
return channel.data[index(x, y, z)];
|
||||
} else {
|
||||
return channel.defval;
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelBuffer::set_voxel(int value, int x, int y, int z, unsigned int channel_index) {
|
||||
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||
ERR_FAIL_COND(!validate_pos(x, y, z));
|
||||
|
||||
Channel & channel = _channels[channel_index];
|
||||
Channel &channel = _channels[channel_index];
|
||||
|
||||
if (channel.data == NULL) {
|
||||
if (channel.defval != value) {
|
||||
create_channel(channel_index, _size);
|
||||
channel.data[index(x, y, z)] = value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
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) {
|
||||
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||
|
||||
Channel & channel = _channels[channel_index];
|
||||
Channel &channel = _channels[channel_index];
|
||||
if (channel.data == NULL && channel.defval == defval)
|
||||
return;
|
||||
else
|
||||
@ -104,10 +100,10 @@ void VoxelBuffer::fill_area(int defval, Vector3i min, Vector3i max, unsigned int
|
||||
Vector3i::sort_min_max(min, max);
|
||||
|
||||
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;
|
||||
|
||||
Channel & channel = _channels[channel_index];
|
||||
Channel &channel = _channels[channel_index];
|
||||
if (channel.data == NULL) {
|
||||
if (channel.defval == defval)
|
||||
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 {
|
||||
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)
|
||||
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_COND(other._size == _size);
|
||||
|
||||
Channel & channel = _channels[channel_index];
|
||||
const Channel & other_channel = other._channels[channel_index];
|
||||
Channel &channel = _channels[channel_index];
|
||||
const Channel &other_channel = other._channels[channel_index];
|
||||
|
||||
if (other_channel.data) {
|
||||
if (channel.data == NULL) {
|
||||
create_channel_noinit(channel_index, _size);
|
||||
}
|
||||
memcpy(channel.data, other_channel.data, get_volume() * sizeof(uint8_t));
|
||||
}
|
||||
else if(channel.data) {
|
||||
} else if (channel.data) {
|
||||
delete_channel(channel_index);
|
||||
}
|
||||
|
||||
channel.defval = other_channel.defval;
|
||||
}
|
||||
|
||||
void VoxelBuffer::copy_from(const VoxelBuffer & other, Vector3i src_min, Vector3i src_max, Vector3i dst_min, unsigned int channel_index) {
|
||||
void VoxelBuffer::copy_from(const VoxelBuffer &other, Vector3i src_min, Vector3i src_max, Vector3i dst_min, unsigned int channel_index) {
|
||||
|
||||
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||
|
||||
Channel & channel = _channels[channel_index];
|
||||
const Channel & other_channel = other._channels[channel_index];
|
||||
Channel &channel = _channels[channel_index];
|
||||
const Channel &other_channel = other._channels[channel_index];
|
||||
|
||||
Vector3i::sort_min_max(src_min, src_max);
|
||||
|
||||
src_min.clamp_to(Vector3i(0, 0, 0), other._size);
|
||||
src_max.clamp_to(Vector3i(0, 0, 0), other._size + Vector3i(1,1,1));
|
||||
src_max.clamp_to(Vector3i(0, 0, 0), other._size + Vector3i(1, 1, 1));
|
||||
|
||||
dst_min.clamp_to(Vector3i(0, 0, 0), _size);
|
||||
Vector3i area_size = src_max - src_min;
|
||||
@ -188,8 +183,7 @@ void VoxelBuffer::copy_from(const VoxelBuffer & other, Vector3i src_min, Vector3
|
||||
|
||||
if (area_size == _size) {
|
||||
copy_from(other, channel_index);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (other_channel.data) {
|
||||
if (channel.data == NULL) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (channel.defval != other_channel.defval) {
|
||||
} else if (channel.defval != other_channel.defval) {
|
||||
if (channel.data == NULL) {
|
||||
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) {
|
||||
Channel & channel = _channels[i];
|
||||
Channel &channel = _channels[i];
|
||||
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) {
|
||||
Channel & channel = _channels[i];
|
||||
Channel &channel = _channels[i];
|
||||
ERR_FAIL_COND(channel.data == NULL);
|
||||
memfree(channel.data);
|
||||
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("optimize"), &VoxelBuffer::optimize);
|
||||
|
||||
}
|
||||
|
||||
void VoxelBuffer::_copy_from_binding(Ref<VoxelBuffer> other, unsigned int channel) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
#ifndef VOXEL_BUFFER_H
|
||||
#define VOXEL_BUFFER_H
|
||||
|
||||
#include "vector3i.h"
|
||||
#include <reference.h>
|
||||
#include <vector.h>
|
||||
#include "vector3i.h"
|
||||
|
||||
// Dense voxels data storage.
|
||||
// Organized in 8-bit channels like images, all optional.
|
||||
@ -19,9 +19,9 @@ public:
|
||||
// Converts -1..1 float into 0..255 integer
|
||||
static inline int iso_to_byte(real_t iso) {
|
||||
int v = static_cast<int>(128.f * iso + 128.f);
|
||||
if(v > 255)
|
||||
if (v > 255)
|
||||
return 255;
|
||||
else if(v < 0)
|
||||
else if (v < 0)
|
||||
return 0;
|
||||
return v;
|
||||
}
|
||||
@ -36,17 +36,17 @@ public:
|
||||
|
||||
void create(int sx, int sy, int sz);
|
||||
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; }
|
||||
|
||||
void set_default_values(uint8_t values[MAX_CHANNELS]);
|
||||
|
||||
int get_voxel(int x, int y, int z, unsigned int channel_index=0) const;
|
||||
void set_voxel(int value, int x, int y, int z, unsigned int channel_index=0);
|
||||
int get_voxel(int x, int y, int z, unsigned int channel_index = 0) const;
|
||||
void set_voxel(int value, int x, int y, int z, unsigned int channel_index = 0);
|
||||
void set_voxel_v(int value, Vector3 pos, unsigned int channel_index = 0);
|
||||
_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_ 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_ 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); }
|
||||
|
||||
@ -57,13 +57,11 @@ public:
|
||||
|
||||
void optimize();
|
||||
|
||||
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, unsigned int channel_index = 0);
|
||||
void copy_from(const VoxelBuffer &other, Vector3i src_min, Vector3i src_max, Vector3i dst_min, unsigned int channel_index = 0);
|
||||
|
||||
_FORCE_INLINE_ bool validate_pos(unsigned int x, unsigned int y, unsigned int z) const {
|
||||
return x < _size.x
|
||||
&& y < _size.y
|
||||
&& z < _size.x;
|
||||
return x < _size.x && y < _size.y && z < _size.x;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ unsigned int index(unsigned int x, unsigned int y, unsigned int z) const {
|
||||
@ -80,7 +78,7 @@ public:
|
||||
|
||||
private:
|
||||
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);
|
||||
|
||||
protected:
|
||||
@ -95,18 +93,19 @@ protected:
|
||||
void _copy_from_binding(Ref<VoxelBuffer> other, unsigned int channel);
|
||||
void _copy_from_area_binding(Ref<VoxelBuffer> other, Vector3 src_min, Vector3 src_max, Vector3 dst_min, unsigned int channel);
|
||||
_FORCE_INLINE_ void _fill_area_binding(int defval, Vector3 min, Vector3 max, unsigned int channel_index) { fill_area(defval, Vector3i(min), Vector3i(max), channel_index); }
|
||||
_FORCE_INLINE_ void _set_voxel_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:
|
||||
struct Channel {
|
||||
// Allocated when the channel is populated.
|
||||
// Flat array, in order [z][x][y] because it allows faster vertical-wise access (the engine is Y-up).
|
||||
uint8_t * data;
|
||||
uint8_t *data;
|
||||
|
||||
// Default value when data is null
|
||||
uint8_t defval;
|
||||
|
||||
Channel() : data(NULL), defval(0) {}
|
||||
Channel()
|
||||
: data(NULL), defval(0) {}
|
||||
};
|
||||
|
||||
// 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.
|
||||
Vector3i _size;
|
||||
|
||||
};
|
||||
|
||||
#endif // VOXEL_BUFFER_H
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "voxel_library.h"
|
||||
|
||||
VoxelLibrary::VoxelLibrary() : Reference(), _atlas_size(1) {
|
||||
VoxelLibrary::VoxelLibrary()
|
||||
: Reference(), _atlas_size(1) {
|
||||
// Defaults
|
||||
create_voxel(0, "air")->set_transparent(true);
|
||||
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("set_atlas_size", "square_size"), &VoxelLibrary::set_atlas_size);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
#ifndef VOXEL_LIBRARY_H
|
||||
#define VOXEL_LIBRARY_H
|
||||
|
||||
#include <reference.h>
|
||||
#include "voxel.h"
|
||||
#include <reference.h>
|
||||
|
||||
class VoxelLibrary : public Reference {
|
||||
GDCLASS(VoxelLibrary, Reference)
|
||||
@ -22,7 +22,7 @@ public:
|
||||
// Internal getters
|
||||
|
||||
_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:
|
||||
static void _bind_methods();
|
||||
@ -32,8 +32,6 @@ protected:
|
||||
private:
|
||||
Ref<Voxel> _voxel_types[MAX_VOXEL_TYPES];
|
||||
int _atlas_size;
|
||||
|
||||
};
|
||||
|
||||
#endif // VOXEL_LIBRARY_H
|
||||
|
||||
|
119
voxel_map.cpp
119
voxel_map.cpp
@ -5,22 +5,22 @@
|
||||
// VoxelBlock
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
MeshInstance * VoxelBlock::get_mesh_instance(const Node & root) {
|
||||
MeshInstance *VoxelBlock::get_mesh_instance(const Node &root) {
|
||||
if (mesh_instance_path.is_empty())
|
||||
return NULL;
|
||||
Node * n = root.get_node(mesh_instance_path);
|
||||
Node *n = root.get_node(mesh_instance_path);
|
||||
if (n == NULL)
|
||||
return NULL;
|
||||
return n->cast_to<MeshInstance>();
|
||||
}
|
||||
|
||||
// Helper
|
||||
VoxelBlock * VoxelBlock::create(Vector3i bpos, Ref<VoxelBuffer> buffer) {
|
||||
VoxelBlock *VoxelBlock::create(Vector3i bpos, Ref<VoxelBuffer> buffer) {
|
||||
const int bs = VoxelBlock::SIZE;
|
||||
ERR_FAIL_COND_V(buffer.is_null(), 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->voxels = buffer;
|
||||
@ -28,14 +28,16 @@ VoxelBlock * VoxelBlock::create(Vector3i bpos, Ref<VoxelBuffer> buffer) {
|
||||
return block;
|
||||
}
|
||||
|
||||
VoxelBlock::VoxelBlock(): voxels(NULL) {
|
||||
VoxelBlock::VoxelBlock()
|
||||
: voxels(NULL) {
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// VoxelMap
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
VoxelMap::VoxelMap() : _last_accessed_block(NULL) {
|
||||
VoxelMap::VoxelMap()
|
||||
: _last_accessed_block(NULL) {
|
||||
for (unsigned int i = 0; i < VoxelBuffer::MAX_CHANNELS; ++i) {
|
||||
_default_voxel[i] = 0;
|
||||
}
|
||||
@ -47,7 +49,7 @@ VoxelMap::~VoxelMap() {
|
||||
|
||||
int VoxelMap::get_voxel(Vector3i pos, unsigned int c) {
|
||||
Vector3i bpos = voxel_to_block(pos);
|
||||
VoxelBlock * block = get_block(bpos);
|
||||
VoxelBlock *block = get_block(bpos);
|
||||
if (block == NULL) {
|
||||
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) {
|
||||
|
||||
Vector3i bpos = voxel_to_block(pos);
|
||||
VoxelBlock * block = get_block(bpos);
|
||||
VoxelBlock *block = get_block(bpos);
|
||||
|
||||
if (block == NULL) {
|
||||
|
||||
@ -83,11 +85,11 @@ int VoxelMap::get_default_voxel(unsigned int 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) {
|
||||
return _last_accessed_block;
|
||||
}
|
||||
VoxelBlock ** p = _blocks.getptr(bpos);
|
||||
VoxelBlock **p = _blocks.getptr(bpos);
|
||||
if (p) {
|
||||
_last_accessed_block = *p;
|
||||
return _last_accessed_block;
|
||||
@ -95,7 +97,7 @@ VoxelBlock * VoxelMap::get_block(Vector3i bpos) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void VoxelMap::set_block(Vector3i bpos, VoxelBlock * block) {
|
||||
void VoxelMap::set_block(Vector3i bpos, VoxelBlock *block) {
|
||||
ERR_FAIL_COND(block == NULL);
|
||||
if (_last_accessed_block == NULL || _last_accessed_block->pos == bpos) {
|
||||
_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) {
|
||||
ERR_FAIL_COND(buffer.is_null());
|
||||
VoxelBlock * block = get_block(bpos);
|
||||
VoxelBlock *block = get_block(bpos);
|
||||
if (block == NULL) {
|
||||
block = VoxelBlock::create(bpos, *buffer);
|
||||
set_block(bpos, block);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
block->voxels = buffer;
|
||||
}
|
||||
}
|
||||
@ -120,35 +121,35 @@ bool VoxelMap::has_block(Vector3i pos) const {
|
||||
}
|
||||
|
||||
Vector3i g_moore_neighboring_3d[26] = {
|
||||
Vector3i(-1,-1,-1),
|
||||
Vector3i(0,-1,-1),
|
||||
Vector3i(1,-1,-1),
|
||||
Vector3i(-1,-1,0),
|
||||
Vector3i(0,-1,0),
|
||||
Vector3i(1,-1,0),
|
||||
Vector3i(-1,-1,1),
|
||||
Vector3i(0,-1,1),
|
||||
Vector3i(1,-1,1),
|
||||
Vector3i(-1, -1, -1),
|
||||
Vector3i(0, -1, -1),
|
||||
Vector3i(1, -1, -1),
|
||||
Vector3i(-1, -1, 0),
|
||||
Vector3i(0, -1, 0),
|
||||
Vector3i(1, -1, 0),
|
||||
Vector3i(-1, -1, 1),
|
||||
Vector3i(0, -1, 1),
|
||||
Vector3i(1, -1, 1),
|
||||
|
||||
Vector3i(-1,0,-1),
|
||||
Vector3i(0,0,-1),
|
||||
Vector3i(1,0,-1),
|
||||
Vector3i(-1,0,0),
|
||||
Vector3i(-1, 0, -1),
|
||||
Vector3i(0, 0, -1),
|
||||
Vector3i(1, 0, -1),
|
||||
Vector3i(-1, 0, 0),
|
||||
//Vector3i(0,0,0),
|
||||
Vector3i(1,0,0),
|
||||
Vector3i(-1,0,1),
|
||||
Vector3i(0,0,1),
|
||||
Vector3i(1,0,1),
|
||||
Vector3i(1, 0, 0),
|
||||
Vector3i(-1, 0, 1),
|
||||
Vector3i(0, 0, 1),
|
||||
Vector3i(1, 0, 1),
|
||||
|
||||
Vector3i(-1,1,-1),
|
||||
Vector3i(0,1,-1),
|
||||
Vector3i(1,1,-1),
|
||||
Vector3i(-1,1,0),
|
||||
Vector3i(0,1,0),
|
||||
Vector3i(1,1,0),
|
||||
Vector3i(-1,1,1),
|
||||
Vector3i(0,1,1),
|
||||
Vector3i(1,1,1),
|
||||
Vector3i(-1, 1, -1),
|
||||
Vector3i(0, 1, -1),
|
||||
Vector3i(1, 1, -1),
|
||||
Vector3i(-1, 1, 0),
|
||||
Vector3i(0, 1, 0),
|
||||
Vector3i(1, 1, 0),
|
||||
Vector3i(-1, 1, 1),
|
||||
Vector3i(0, 1, 1),
|
||||
Vector3i(1, 1, 1),
|
||||
};
|
||||
|
||||
bool VoxelMap::is_block_surrounded(Vector3i pos) const {
|
||||
@ -161,17 +162,17 @@ bool VoxelMap::is_block_surrounded(Vector3i pos) const {
|
||||
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 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));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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.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) {
|
||||
|
||||
VoxelBuffer & src_buffer = **block->voxels;
|
||||
VoxelBuffer &src_buffer = **block->voxels;
|
||||
Vector3i offset = block_to_voxel(bpos);
|
||||
// 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);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Vector3i offset = block_to_voxel(bpos);
|
||||
dst_buffer.fill_area(
|
||||
_default_voxel[channel],
|
||||
offset - min_pos,
|
||||
offset - min_pos + Vector3i(VoxelBlock::SIZE,VoxelBlock::SIZE, VoxelBlock::SIZE)
|
||||
);
|
||||
_default_voxel[channel],
|
||||
offset - min_pos,
|
||||
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);
|
||||
|
||||
Vector<Vector3i> to_remove;
|
||||
const Vector3i * key = NULL;
|
||||
const Vector3i *key = NULL;
|
||||
|
||||
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
|
||||
|
||||
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() {
|
||||
const Vector3i * key = NULL;
|
||||
const Vector3i *key = NULL;
|
||||
while (key = _blocks.next(key)) {
|
||||
VoxelBlock * block_ref = _blocks.get(*key);
|
||||
if(block_ref == NULL) {
|
||||
VoxelBlock *block_ref = _blocks.get(*key);
|
||||
if (block_ref == NULL) {
|
||||
OS::get_singleton()->printerr("Unexpected NULL in VoxelMap::clear()");
|
||||
}
|
||||
memdelete(block_ref);
|
||||
@ -246,7 +243,6 @@ void VoxelMap::clear() {
|
||||
_last_accessed_block = NULL;
|
||||
}
|
||||
|
||||
|
||||
void VoxelMap::_bind_methods() {
|
||||
|
||||
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);
|
||||
|
||||
//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) {
|
||||
ERR_FAIL_COND(dst_buffer_ref.is_null());
|
||||
get_buffer_copy(Vector3i(pos), **dst_buffer_ref, channel);
|
||||
}
|
||||
|
||||
|
42
voxel_map.h
42
voxel_map.h
@ -1,12 +1,11 @@
|
||||
#ifndef VOXEL_MAP_H
|
||||
#define VOXEL_MAP_H
|
||||
|
||||
#include <scene/main/node.h>
|
||||
#include "voxel_buffer.h"
|
||||
#include <core/hash_map.h>
|
||||
#include <scene/3d/mesh_instance.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.
|
||||
class VoxelBlock {
|
||||
@ -19,16 +18,14 @@ public:
|
||||
Vector3i pos;
|
||||
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:
|
||||
VoxelBlock();
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Infinite voxel storage by means of octants like Gridmap
|
||||
class VoxelMap : public Reference {
|
||||
GDCLASS(VoxelMap, Reference)
|
||||
@ -36,18 +33,16 @@ public:
|
||||
// Converts voxel coodinates into block coordinates
|
||||
static _FORCE_INLINE_ Vector3i voxel_to_block(Vector3i pos) {
|
||||
return Vector3i(
|
||||
pos.x >> VoxelBlock::SIZE_POW2,
|
||||
pos.y >> VoxelBlock::SIZE_POW2,
|
||||
pos.z >> VoxelBlock::SIZE_POW2
|
||||
);
|
||||
pos.x >> VoxelBlock::SIZE_POW2,
|
||||
pos.y >> VoxelBlock::SIZE_POW2,
|
||||
pos.z >> VoxelBlock::SIZE_POW2);
|
||||
}
|
||||
|
||||
static _FORCE_INLINE_ Vector3i to_local(Vector3i pos) {
|
||||
return Vector3i(
|
||||
pos.x & VoxelBlock::SIZE_MASK,
|
||||
pos.y & VoxelBlock::SIZE_MASK,
|
||||
pos.z & VoxelBlock::SIZE_MASK
|
||||
);
|
||||
pos.x & VoxelBlock::SIZE_MASK,
|
||||
pos.y & VoxelBlock::SIZE_MASK,
|
||||
pos.z & VoxelBlock::SIZE_MASK);
|
||||
}
|
||||
|
||||
// Converts block coodinates into voxel coordinates
|
||||
@ -61,27 +56,26 @@ public:
|
||||
int get_voxel(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);
|
||||
int get_default_voxel(unsigned int channel=0);
|
||||
void set_default_voxel(int value, 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.
|
||||
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.
|
||||
void set_block_buffer(Vector3i bpos, Ref<VoxelBuffer> buffer);
|
||||
|
||||
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 is_block_surrounded(Vector3i pos) const;
|
||||
|
||||
void clear();
|
||||
|
||||
|
||||
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; }
|
||||
|
||||
@ -103,13 +97,11 @@ private:
|
||||
uint8_t _default_voxel[VoxelBuffer::MAX_CHANNELS];
|
||||
|
||||
// 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.
|
||||
// To prevent too much hashing, this reference is checked before.
|
||||
VoxelBlock * _last_accessed_block;
|
||||
|
||||
VoxelBlock *_last_accessed_block;
|
||||
};
|
||||
|
||||
#endif // VOXEL_MAP_H
|
||||
|
||||
|
405
voxel_mesher.cpp
405
voxel_mesher.cpp
@ -19,7 +19,7 @@
|
||||
// / | / |
|
||||
// o----8----o |
|
||||
// | o---2-|---o
|
||||
// 4 / 5 /
|
||||
// 4 / 5 /
|
||||
// | 3 | 1
|
||||
// |/ |/
|
||||
// o----0----o
|
||||
@ -31,44 +31,44 @@ static const unsigned int CORNER_COUNT = 8;
|
||||
static const unsigned int EDGE_COUNT = 12;
|
||||
|
||||
static const Vector3 g_corner_position[CORNER_COUNT] = {
|
||||
Vector3(0, 0, 0),
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(1, 0, 1),
|
||||
Vector3(0, 0, 1),
|
||||
Vector3(0, 1, 0),
|
||||
Vector3(1, 1, 0),
|
||||
Vector3(1, 1, 1),
|
||||
Vector3(0, 1, 1)
|
||||
Vector3(0, 0, 0),
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(1, 0, 1),
|
||||
Vector3(0, 0, 1),
|
||||
Vector3(0, 1, 0),
|
||||
Vector3(1, 1, 0),
|
||||
Vector3(1, 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_sign[Voxel::SIDE_COUNT] = { 0, 1, 0, 1, 0, 1 };
|
||||
|
||||
static const Vector3i g_side_normals[Voxel::SIDE_COUNT] = {
|
||||
Vector3i(-1, 0, 0),
|
||||
Vector3i(1, 0, 0),
|
||||
Vector3i(0, -1, 0),
|
||||
Vector3i(0, 1, 0),
|
||||
Vector3i(0, 0, -1),
|
||||
Vector3i(0, 0, 1),
|
||||
Vector3i(-1, 0, 0),
|
||||
Vector3i(1, 0, 0),
|
||||
Vector3i(0, -1, 0),
|
||||
Vector3i(0, 1, 0),
|
||||
Vector3i(0, 0, -1),
|
||||
Vector3i(0, 0, 1),
|
||||
};
|
||||
|
||||
static const unsigned int g_side_corners[Voxel::SIDE_COUNT][4] = {
|
||||
{ 0, 3, 7, 4 },
|
||||
{ 1, 2, 6, 5 },
|
||||
{ 0, 1, 2, 3 },
|
||||
{ 4, 5, 6, 7 },
|
||||
{ 0, 1, 5, 4 },
|
||||
{ 3, 2, 6, 7 }
|
||||
{ 0, 3, 7, 4 },
|
||||
{ 1, 2, 6, 5 },
|
||||
{ 0, 1, 2, 3 },
|
||||
{ 4, 5, 6, 7 },
|
||||
{ 0, 1, 5, 4 },
|
||||
{ 3, 2, 6, 7 }
|
||||
};
|
||||
|
||||
static const unsigned int g_side_edges[Voxel::SIDE_COUNT][4] = {
|
||||
{ 3, 7, 11, 4 },
|
||||
{ 1, 6, 9, 5 },
|
||||
{ 0, 1, 2, 3 },
|
||||
{ 8, 9, 10, 11 },
|
||||
{ 0, 5, 8, 4 },
|
||||
{ 2, 6, 10, 7 }
|
||||
{ 3, 7, 11, 4 },
|
||||
{ 1, 6, 9, 5 },
|
||||
{ 0, 1, 2, 3 },
|
||||
{ 8, 9, 10, 11 },
|
||||
{ 0, 5, 8, 4 },
|
||||
{ 2, 6, 10, 7 }
|
||||
};
|
||||
|
||||
// 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] = {
|
||||
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] = {
|
||||
Vector3i(0, -1, -1),
|
||||
Vector3i(1, -1, 0),
|
||||
Vector3i(0, -1, 1),
|
||||
Vector3i(-1, -1, 0),
|
||||
Vector3i(0, -1, -1),
|
||||
Vector3i(1, -1, 0),
|
||||
Vector3i(0, -1, 1),
|
||||
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(1, 1, 0),
|
||||
Vector3i(0, 1, 1),
|
||||
Vector3i(-1, 1, 0)
|
||||
Vector3i(0, 1, -1),
|
||||
Vector3i(1, 1, 0),
|
||||
Vector3i(0, 1, 1),
|
||||
Vector3i(-1, 1, 0)
|
||||
};
|
||||
|
||||
static const unsigned int g_edge_corners[EDGE_COUNT][2] = {
|
||||
{ 0, 1 }, { 1, 2 }, { 2, 3 }, {3, 0},
|
||||
{ 0, 4 }, { 1, 5 }, { 2, 6 }, {3, 7},
|
||||
{ 4, 5 }, { 5, 6 }, { 6, 7 }, {7, 4}
|
||||
{ 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 0 },
|
||||
{ 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 },
|
||||
{ 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 4 }
|
||||
};
|
||||
|
||||
|
||||
VoxelMesher::VoxelMesher():
|
||||
_baked_occlusion_darkness(0.75),
|
||||
_bake_occlusion(true)
|
||||
{}
|
||||
VoxelMesher::VoxelMesher()
|
||||
: _baked_occlusion_darkness(0.75),
|
||||
_bake_occlusion(true) {}
|
||||
|
||||
void VoxelMesher::set_library(Ref<VoxelLibrary> library) {
|
||||
ERR_FAIL_COND(library.is_null());
|
||||
_library = library;
|
||||
ERR_FAIL_COND(library.is_null());
|
||||
_library = library;
|
||||
}
|
||||
|
||||
void VoxelMesher::set_material(Ref<Material> material, unsigned int id) {
|
||||
ERR_FAIL_COND(id >= MAX_MATERIALS);
|
||||
_materials[id] = material;
|
||||
ERR_FAIL_COND(id >= MAX_MATERIALS);
|
||||
_materials[id] = material;
|
||||
}
|
||||
|
||||
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) {
|
||||
_baked_occlusion_darkness = darkness;
|
||||
if (_baked_occlusion_darkness < 0.0)
|
||||
_baked_occlusion_darkness = 0.0;
|
||||
else if (_baked_occlusion_darkness >= 1.0)
|
||||
_baked_occlusion_darkness = 1.0;
|
||||
_baked_occlusion_darkness = darkness;
|
||||
if (_baked_occlusion_darkness < 0.0)
|
||||
_baked_occlusion_darkness = 0.0;
|
||||
else if (_baked_occlusion_darkness >= 1.0)
|
||||
_baked_occlusion_darkness = 1.0;
|
||||
}
|
||||
|
||||
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 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 Color Color_greyscale(float c) {
|
||||
return Color(c, c, c);
|
||||
}
|
||||
|
||||
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;
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
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(channel >= VoxelBuffer::MAX_CHANNELS, Ref<ArrayMesh>());
|
||||
|
||||
const VoxelLibrary & library = **_library;
|
||||
const VoxelLibrary &library = **_library;
|
||||
|
||||
for (unsigned int i = 0; i < MAX_MATERIALS; ++i) {
|
||||
_surface_tool[i].begin(Mesh::PRIMITIVE_TRIANGLES);
|
||||
for (unsigned int i = 0; i < MAX_MATERIALS; ++i) {
|
||||
_surface_tool[i].begin(Mesh::PRIMITIVE_TRIANGLES);
|
||||
_surface_tool[i].set_material(_materials[i]);
|
||||
}
|
||||
|
||||
float baked_occlusion_darkness;
|
||||
if (_bake_occlusion)
|
||||
baked_occlusion_darkness = _baked_occlusion_darkness / 3.0;
|
||||
float baked_occlusion_darkness;
|
||||
if (_bake_occlusion)
|
||||
baked_occlusion_darkness = _baked_occlusion_darkness / 3.0;
|
||||
|
||||
// The technique is Culled faces.
|
||||
// Could be improved with greedy meshing: https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/
|
||||
// However I don't feel it's worth it yet:
|
||||
// - Not so much gain for organic worlds with lots of texture variations
|
||||
// - Works well with cubes but not with any shape
|
||||
// - Slower
|
||||
// => Could be implemented in a separate class?
|
||||
// The technique is Culled faces.
|
||||
// Could be improved with greedy meshing: https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/
|
||||
// However I don't feel it's worth it yet:
|
||||
// - Not so much gain for organic worlds with lots of texture variations
|
||||
// - Works well with cubes but not with any shape
|
||||
// - Slower
|
||||
// => Could be implemented in a separate class?
|
||||
|
||||
VOXEL_PROFILE_BEGIN("mesher_face_extraction")
|
||||
|
||||
// Data must be padded, hence the off-by-one
|
||||
Vector3i::sort_min_max(min, max);
|
||||
const Vector3i pad(1,1,1);
|
||||
const Vector3i pad(1, 1, 1);
|
||||
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.
|
||||
// This is the most intensive job in this class, so all required data should be as fit as possible.
|
||||
// Iterate 3D padded data to extract voxel faces.
|
||||
// This is the most intensive job in this class, so all required data should be as fit as possible.
|
||||
for (unsigned int z = min.z; z < max.z; ++z) {
|
||||
for (unsigned int x = min.x; x < max.x; ++x) {
|
||||
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,
|
||||
// and still allow voxels to have geometry that is not a cube
|
||||
// Hybrid approach: extract cube faces and decimate those that aren't visible,
|
||||
// and still allow voxels to have geometry that is not a cube
|
||||
|
||||
// Sides
|
||||
for (unsigned int side = 0; side < Voxel::SIDE_COUNT; ++side) {
|
||||
// Sides
|
||||
for (unsigned int side = 0; side < Voxel::SIDE_COUNT; ++side) {
|
||||
|
||||
const PoolVector<Vector3> & vertices = voxel.get_model_side_vertices(side);
|
||||
if (vertices.size() != 0) {
|
||||
const PoolVector<Vector3> &vertices = voxel.get_model_side_vertices(side);
|
||||
if (vertices.size() != 0) {
|
||||
|
||||
Vector3i normal = g_side_normals[side];
|
||||
unsigned nx = x + normal.x;
|
||||
unsigned ny = y + normal.y;
|
||||
unsigned nz = z + normal.z;
|
||||
Vector3i normal = g_side_normals[side];
|
||||
unsigned nx = x + normal.x;
|
||||
unsigned ny = y + normal.y;
|
||||
unsigned nz = z + normal.z;
|
||||
|
||||
int neighbor_voxel_id = buffer.get_voxel(nx, ny, nz, channel);
|
||||
// TODO Better face visibility test
|
||||
if (is_face_visible(library, voxel, neighbor_voxel_id)) {
|
||||
// TODO Better face visibility test
|
||||
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) {
|
||||
unsigned int edge = g_side_edges[side][j];
|
||||
Vector3i edge_normal = g_edge_inormals[edge];
|
||||
unsigned ex = x + edge_normal.x;
|
||||
unsigned ey = y + edge_normal.y;
|
||||
unsigned ez = z + edge_normal.z;
|
||||
if (!is_transparent(library, buffer.get_voxel(ex, ey, ez))) {
|
||||
shaded_corner[g_edge_corners[edge][0]] += 1;
|
||||
shaded_corner[g_edge_corners[edge][1]] += 1;
|
||||
}
|
||||
}
|
||||
for (unsigned int j = 0; j < 4; ++j) {
|
||||
unsigned int corner = g_side_corners[side][j];
|
||||
if (shaded_corner[corner] == 2) {
|
||||
shaded_corner[corner] = 3;
|
||||
}
|
||||
else {
|
||||
Vector3i corner_normal = g_corner_inormals[corner];
|
||||
unsigned int cx = x + corner_normal.x;
|
||||
unsigned int cy = y + corner_normal.y;
|
||||
unsigned int cz = z + corner_normal.z;
|
||||
if (!is_transparent(library, buffer.get_voxel(cx, cy, cz))) {
|
||||
shaded_corner[corner] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (unsigned int j = 0; j < 4; ++j) {
|
||||
unsigned int edge = g_side_edges[side][j];
|
||||
Vector3i edge_normal = g_edge_inormals[edge];
|
||||
unsigned ex = x + edge_normal.x;
|
||||
unsigned ey = y + edge_normal.y;
|
||||
unsigned ez = z + edge_normal.z;
|
||||
if (!is_transparent(library, buffer.get_voxel(ex, ey, ez))) {
|
||||
shaded_corner[g_edge_corners[edge][0]] += 1;
|
||||
shaded_corner[g_edge_corners[edge][1]] += 1;
|
||||
}
|
||||
}
|
||||
for (unsigned int j = 0; j < 4; ++j) {
|
||||
unsigned int corner = g_side_corners[side][j];
|
||||
if (shaded_corner[corner] == 2) {
|
||||
shaded_corner[corner] = 3;
|
||||
} else {
|
||||
Vector3i corner_normal = g_corner_inormals[corner];
|
||||
unsigned int cx = x + corner_normal.x;
|
||||
unsigned int cy = y + corner_normal.y;
|
||||
unsigned int cz = z + corner_normal.z;
|
||||
if (!is_transparent(library, buffer.get_voxel(cx, cy, cz))) {
|
||||
shaded_corner[corner] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PoolVector<Vector3>::Read rv = vertices.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) {
|
||||
Vector3 v = rv[i];
|
||||
for (unsigned int i = 0; i < vertices.size(); ++i) {
|
||||
Vector3 v = rv[i];
|
||||
|
||||
if (_bake_occlusion) {
|
||||
// General purpose occlusion colouring.
|
||||
// TODO Optimize for cubes
|
||||
// TODO Fix occlusion inconsistency caused by triangles orientation
|
||||
float shade = 0;
|
||||
for (unsigned int j = 0; j < 4; ++j) {
|
||||
unsigned int corner = g_side_corners[side][j];
|
||||
if (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);
|
||||
if (k < 0.0)
|
||||
k = 0.0;
|
||||
s *= k;
|
||||
if (s > shade)
|
||||
shade = s;
|
||||
}
|
||||
}
|
||||
float gs = 1.0 - shade;
|
||||
st.add_color(Color(gs, gs, gs));
|
||||
}
|
||||
if (_bake_occlusion) {
|
||||
// General purpose occlusion colouring.
|
||||
// TODO Optimize for cubes
|
||||
// TODO Fix occlusion inconsistency caused by triangles orientation
|
||||
float shade = 0;
|
||||
for (unsigned int j = 0; j < 4; ++j) {
|
||||
unsigned int corner = g_side_corners[side][j];
|
||||
if (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);
|
||||
if (k < 0.0)
|
||||
k = 0.0;
|
||||
s *= k;
|
||||
if (s > shade)
|
||||
shade = s;
|
||||
}
|
||||
}
|
||||
float gs = 1.0 - shade;
|
||||
st.add_color(Color(gs, gs, gs));
|
||||
}
|
||||
|
||||
st.add_normal(Vector3(normal.x, normal.y, normal.z));
|
||||
st.add_uv(rt[i]);
|
||||
st.add_vertex(v + pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
st.add_normal(Vector3(normal.x, normal.y, normal.z));
|
||||
st.add_uv(rt[i]);
|
||||
st.add_vertex(v + pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inside
|
||||
if (voxel.get_model_vertices().size() != 0) {
|
||||
// Inside
|
||||
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 rn = voxel.get_model_normals().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) {
|
||||
st.add_normal(rn[i]);
|
||||
st.add_uv(rt[i]);
|
||||
st.add_vertex(rv[i] + pos);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
for (unsigned int i = 0; i < vertices.size(); ++i) {
|
||||
st.add_normal(rn[i]);
|
||||
st.add_uv(rt[i]);
|
||||
st.add_vertex(rv[i] + pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VOXEL_PROFILE_END("mesher_face_extraction")
|
||||
|
||||
// Commit mesh
|
||||
// Commit mesh
|
||||
|
||||
Ref<ArrayMesh> mesh_ref = mesh;
|
||||
if(mesh.is_null())
|
||||
if (mesh.is_null())
|
||||
mesh_ref = Ref<ArrayMesh>(memnew(ArrayMesh));
|
||||
|
||||
for (unsigned int i = 0; i < MAX_MATERIALS; ++i) {
|
||||
if (_materials[i].is_valid()) {
|
||||
SurfaceTool & st = _surface_tool[i];
|
||||
|
||||
// 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!
|
||||
// VOXEL_PROFILE_BEGIN("mesher_surfacetool_index")
|
||||
// st.index();
|
||||
// VOXEL_PROFILE_END("mesher_surfacetool_index")
|
||||
for (unsigned int i = 0; i < MAX_MATERIALS; ++i) {
|
||||
if (_materials[i].is_valid()) {
|
||||
SurfaceTool &st = _surface_tool[i];
|
||||
|
||||
// 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!
|
||||
// VOXEL_PROFILE_BEGIN("mesher_surfacetool_index")
|
||||
// st.index();
|
||||
// VOXEL_PROFILE_END("mesher_surfacetool_index")
|
||||
|
||||
VOXEL_PROFILE_BEGIN("mesher_surfacetool_commit")
|
||||
mesh_ref = st.commit(mesh_ref);
|
||||
VOXEL_PROFILE_END("mesher_surfacetool_commit")
|
||||
|
||||
st.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mesh_ref;
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
#ifndef VOXEL_MESHER
|
||||
#define VOXEL_MESHER
|
||||
|
||||
#include <reference.h>
|
||||
#include <scene/resources/mesh.h>
|
||||
#include <scene/resources/surface_tool.h>
|
||||
#include "voxel.h"
|
||||
#include "voxel_buffer.h"
|
||||
#include "voxel_library.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
|
||||
class VoxelMesher : public Reference {
|
||||
@ -31,8 +30,8 @@ public:
|
||||
void set_occlusion_enabled(bool enable);
|
||||
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_ref(Ref<VoxelBuffer> buffer_ref, unsigned int channel, Ref<ArrayMesh> mesh=Ref<ArrayMesh>());
|
||||
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>());
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
@ -50,5 +49,4 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#endif // VOXEL_MESHER
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include "transvoxel_tables.cpp"
|
||||
#include <os/os.h>
|
||||
|
||||
|
||||
inline float tof(int8_t v) {
|
||||
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) {
|
||||
//return g_corner_dirs[mask] - Vector3(1,1,1);
|
||||
return Vector3i(
|
||||
-(dir & 1),
|
||||
-((dir >> 1) & 1),
|
||||
-((dir >> 2) & 1)
|
||||
);
|
||||
-(dir & 1),
|
||||
-((dir >> 1) & 1),
|
||||
-((dir >> 2) & 1));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void copy_to(PoolVector<T> & to, Vector<T> & from) {
|
||||
template <typename T>
|
||||
void copy_to(PoolVector<T> &to, Vector<T> &from) {
|
||||
|
||||
to.resize(from.size());
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
VoxelMesherSmooth::ReuseCell::ReuseCell() {
|
||||
case_index = 0;
|
||||
for(unsigned int i = 0; i < 4; ++i) {
|
||||
for (unsigned int i = 0; i < 4; ++i) {
|
||||
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>());
|
||||
|
||||
VoxelBuffer & voxels = **voxels_ref;
|
||||
VoxelBuffer &voxels = **voxels_ref;
|
||||
|
||||
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>());
|
||||
|
||||
@ -93,12 +91,12 @@ Ref<ArrayMesh> VoxelMesherSmooth::build(const VoxelBuffer & voxels, unsigned int
|
||||
m_output_indices.clear();
|
||||
|
||||
build_mesh(voxels, channel);
|
||||
// OS::get_singleton()->print("vertices: %i, normals: %i, indices: %i\n",
|
||||
// m_output_vertices.size(),
|
||||
// m_output_normals.size(),
|
||||
// m_output_indices.size());
|
||||
// OS::get_singleton()->print("vertices: %i, normals: %i, indices: %i\n",
|
||||
// m_output_vertices.size(),
|
||||
// m_output_normals.size(),
|
||||
// m_output_indices.size());
|
||||
|
||||
if(m_output_vertices.size() == 0) {
|
||||
if (m_output_vertices.size() == 0) {
|
||||
// The mesh can be empty
|
||||
return Ref<ArrayMesh>();
|
||||
}
|
||||
@ -114,12 +112,12 @@ Ref<ArrayMesh> VoxelMesherSmooth::build(const VoxelBuffer & voxels, unsigned int
|
||||
Array arrays;
|
||||
arrays.resize(Mesh::ARRAY_MAX);
|
||||
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_INDEX] = indices;
|
||||
|
||||
if(mesh.is_null())
|
||||
if (mesh.is_null())
|
||||
mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
|
||||
|
||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);
|
||||
@ -127,7 +125,7 @@ Ref<ArrayMesh> VoxelMesherSmooth::build(const VoxelBuffer & voxels, unsigned int
|
||||
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"
|
||||
|
||||
@ -139,8 +137,8 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
|
||||
// Prepare vertex reuse cache
|
||||
m_block_size = block_size;
|
||||
unsigned int deck_area = block_size.x * block_size.y;
|
||||
for(int i = 0; i < 2; ++i) {
|
||||
if(m_cache[i].size() != deck_area) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (m_cache[i].size() != deck_area) {
|
||||
m_cache[i].clear(); // Clear any previous data
|
||||
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)
|
||||
Vector3i pos;
|
||||
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.x = PAD.x; pos.x < block_size.x - 2; ++pos.x) {
|
||||
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.x = PAD.x; pos.x < block_size.x - 2; ++pos.x) {
|
||||
|
||||
// Get the value of cells.
|
||||
// Negative values are "solid" and positive are "air".
|
||||
// Due to raw cells being unsigned 8-bit, they get converted to signed.
|
||||
int8_t cell_samples[8] = {
|
||||
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, 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+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+1, pos.y+1, pos.z+1, 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, 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 + 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 + 1, pos.y + 1, pos.z + 1, channel))
|
||||
};
|
||||
|
||||
// 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.
|
||||
uint8_t case_code =
|
||||
sign(cell_samples[0])
|
||||
| (sign(cell_samples[1]) << 1)
|
||||
| (sign(cell_samples[2]) << 2)
|
||||
| (sign(cell_samples[3]) << 3)
|
||||
| (sign(cell_samples[4]) << 4)
|
||||
| (sign(cell_samples[5]) << 5)
|
||||
| (sign(cell_samples[6]) << 6)
|
||||
| (sign(cell_samples[7]) << 7);
|
||||
uint8_t case_code = sign(cell_samples[0]);
|
||||
case_code |= (sign(cell_samples[1]) << 1);
|
||||
case_code |= (sign(cell_samples[2]) << 2);
|
||||
case_code |= (sign(cell_samples[3]) << 3);
|
||||
case_code |= (sign(cell_samples[4]) << 4);
|
||||
case_code |= (sign(cell_samples[5]) << 5);
|
||||
case_code |= (sign(cell_samples[6]) << 6);
|
||||
case_code |= (sign(cell_samples[7]) << 7);
|
||||
|
||||
{
|
||||
ReuseCell & rc = get_reuse_cell(pos);
|
||||
ReuseCell &rc = get_reuse_cell(pos);
|
||||
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
|
||||
continue;
|
||||
}
|
||||
@ -191,18 +188,13 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
|
||||
// TODO We might not always need all of them
|
||||
// Compute normals
|
||||
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];
|
||||
|
||||
float nx = 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 nx = 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)));
|
||||
|
||||
corner_normals[i] = Vector3(nx, ny, nz);
|
||||
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
|
||||
// whether corresponding bits in a direction code are valid
|
||||
uint8_t direction_validity_mask =
|
||||
(pos.x > 1 ? 1 : 0)
|
||||
| ((pos.y > 1 ? 1 : 0) << 1)
|
||||
| ((pos.z > 1 ? 1 : 0) << 2);
|
||||
(pos.x > 1 ? 1 : 0) | ((pos.y > 1 ? 1 : 0) << 1) | ((pos.z > 1 ? 1 : 0) << 2);
|
||||
|
||||
uint8_t regular_cell_class_index = Transvoxel::regularCellClass[case_code];
|
||||
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];
|
||||
|
||||
// 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 low byte of each 16-bit code contains the corner indexes of the edge’s 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 p1 = pos + g_corner_dirs[v1];
|
||||
|
||||
if(t & 0xff) {
|
||||
if (t & 0xff) {
|
||||
//OS::get_singleton()->print("A");
|
||||
// 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;
|
||||
|
||||
if(can_reuse) {
|
||||
if (can_reuse) {
|
||||
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.
|
||||
cell_mesh_indices[i] = -1;
|
||||
} 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
|
||||
|
||||
cell_mesh_indices[i] = m_output_vertices.size();
|
||||
|
||||
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;
|
||||
|
||||
emit_vertex(primary, normal);
|
||||
|
||||
if (reuse_dir & 8) {
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
|
||||
} else if(t == 0 && v1 == 7) {
|
||||
} else if (t == 0 && v1 == 7) {
|
||||
|
||||
//OS::get_singleton()->print("B");
|
||||
// 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();
|
||||
|
||||
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;
|
||||
|
||||
emit_vertex(primary, normal);
|
||||
|
||||
ReuseCell & rc = get_reuse_cell(pos);
|
||||
ReuseCell &rc = get_reuse_cell(pos);
|
||||
rc.vertices[0] = cell_mesh_indices[i];
|
||||
|
||||
} 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;
|
||||
|
||||
// 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);
|
||||
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();
|
||||
|
||||
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;
|
||||
|
||||
emit_vertex(primary, normal);
|
||||
@ -377,23 +367,18 @@ void VoxelMesherSmooth::build_mesh(const VoxelBuffer & voxels, unsigned int chan
|
||||
//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 i = pos.y * m_block_size.y + pos.x;
|
||||
return m_cache[j][i];
|
||||
}
|
||||
|
||||
|
||||
void VoxelMesherSmooth::emit_vertex(Vector3 primary, Vector3 normal) {
|
||||
m_output_vertices.push_back(primary - PAD.to_vec3());
|
||||
m_output_normals.push_back(normal);
|
||||
}
|
||||
|
||||
|
||||
void VoxelMesherSmooth::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("build", "voxels", "channel", "existing_mesh"), &VoxelMesherSmooth::build_ref, DEFVAL(Variant()));
|
||||
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,8 @@ class VoxelMesherSmooth : public Reference {
|
||||
public:
|
||||
VoxelMesherSmooth();
|
||||
|
||||
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_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>());
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
@ -23,12 +23,12 @@ private:
|
||||
ReuseCell();
|
||||
};
|
||||
|
||||
void build_mesh(const VoxelBuffer & voxels, unsigned int channel);
|
||||
ReuseCell & get_reuse_cell(Vector3i pos);
|
||||
void build_mesh(const VoxelBuffer &voxels, unsigned int channel);
|
||||
ReuseCell &get_reuse_cell(Vector3i pos);
|
||||
void emit_vertex(Vector3 primary, Vector3 normal);
|
||||
|
||||
private:
|
||||
const Vector3i PAD = Vector3i(1,1,1);
|
||||
const Vector3i PAD = Vector3i(1, 1, 1);
|
||||
|
||||
Vector<ReuseCell> m_cache[2];
|
||||
Vector3i m_block_size;
|
||||
|
@ -1,15 +1,14 @@
|
||||
#include "voxel_provider.h"
|
||||
#include "voxel_map.h"
|
||||
|
||||
|
||||
void VoxelProvider::emerge_block(Ref<VoxelBuffer> out_buffer, Vector3i block_pos) {
|
||||
ERR_FAIL_COND(out_buffer.is_null());
|
||||
ScriptInstance * script = get_script_instance();
|
||||
if(script) {
|
||||
ScriptInstance *script = get_script_instance();
|
||||
if (script) {
|
||||
// Call script to generate buffer
|
||||
Variant arg1 = out_buffer;
|
||||
Variant arg2 = block_pos.to_vec3();
|
||||
const Variant * args[2] = { &arg1, &arg2 };
|
||||
const Variant *args[2] = { &arg1, &arg2 };
|
||||
//Variant::CallError err; // wut
|
||||
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) {
|
||||
ERR_FAIL_COND(buffer.is_null());
|
||||
ScriptInstance * script = get_script_instance();
|
||||
if(script) {
|
||||
ScriptInstance *script = get_script_instance();
|
||||
if (script) {
|
||||
// Call script to save buffer
|
||||
Variant arg1 = buffer;
|
||||
Variant arg2 = block_pos.to_vec3();
|
||||
const Variant * args[2] = { &arg1, &arg2 };
|
||||
const Variant *args[2] = { &arg1, &arg2 };
|
||||
//Variant::CallError err; // wut
|
||||
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("immerge_block", "buffer", "block_pos"), &VoxelProvider::_immerge_block);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include "reference.h"
|
||||
#include "voxel_buffer.h"
|
||||
|
||||
|
||||
class VoxelProvider : public Reference {
|
||||
GDCLASS(VoxelProvider, Reference)
|
||||
public:
|
||||
@ -18,5 +17,4 @@ protected:
|
||||
void _immerge_block(Ref<VoxelBuffer> buffer, Vector3 block_pos);
|
||||
};
|
||||
|
||||
|
||||
#endif // VOXEL_PROVIDER_H
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
VARIANT_ENUM_CAST(VoxelProviderTest::Mode)
|
||||
|
||||
|
||||
VoxelProviderTest::VoxelProviderTest() {
|
||||
_mode = MODE_FLAT;
|
||||
_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) {
|
||||
ERR_FAIL_COND(out_buffer.is_null());
|
||||
|
||||
switch(_mode) {
|
||||
switch (_mode) {
|
||||
|
||||
case MODE_FLAT:
|
||||
generate_block_flat(**out_buffer, block_pos);
|
||||
break;
|
||||
case MODE_FLAT:
|
||||
generate_block_flat(**out_buffer, block_pos);
|
||||
break;
|
||||
|
||||
case MODE_WAVES:
|
||||
generate_block_waves(**out_buffer, block_pos);
|
||||
break;
|
||||
case MODE_WAVES:
|
||||
generate_block_waves(**out_buffer, block_pos);
|
||||
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!
|
||||
Vector3i size = out_buffer.get_size();
|
||||
Vector3i origin = VoxelMap::block_to_voxel(block_pos);
|
||||
|
||||
int rh = _pattern_offset.y - origin.y;
|
||||
if(rh > size.y)
|
||||
if (rh > size.y)
|
||||
rh = size.y;
|
||||
|
||||
for(int rz = 0; rz < size.z; ++rz) {
|
||||
for(int rx = 0; rx < size.x; ++rx) {
|
||||
for(int ry = 0; ry < rh; ++ry) {
|
||||
for (int rz = 0; rz < size.z; ++rz) {
|
||||
for (int rx = 0; rx < size.x; ++rx) {
|
||||
for (int ry = 0; ry < rh; ++ry) {
|
||||
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 origin = VoxelMap::block_to_voxel(block_pos) + _pattern_offset;
|
||||
float amplitude = static_cast<float>(_pattern_size.y);
|
||||
float period_x = 1.f/static_cast<float>(_pattern_size.x);
|
||||
float period_z = 1.f/static_cast<float>(_pattern_size.z);
|
||||
float period_x = 1.f / static_cast<float>(_pattern_size.x);
|
||||
float period_z = 1.f / static_cast<float>(_pattern_size.z);
|
||||
|
||||
for(int rz = 0; rz < size.z; ++rz) {
|
||||
for(int rx = 0; rx < size.x; ++rx) {
|
||||
for (int rz = 0; rz < size.z; ++rz) {
|
||||
for (int rx = 0; rx < size.x; ++rx) {
|
||||
|
||||
float x = origin.x + rx;
|
||||
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;
|
||||
if(rh > size.y)
|
||||
if (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);
|
||||
}
|
||||
}
|
||||
@ -108,4 +107,3 @@ void VoxelProviderTest::_bind_methods() {
|
||||
BIND_CONSTANT(MODE_FLAT);
|
||||
BIND_CONSTANT(MODE_WAVES);
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
#include "voxel_provider.h"
|
||||
|
||||
|
||||
class VoxelProviderTest : public VoxelProvider {
|
||||
GDCLASS(VoxelProviderTest, VoxelProvider)
|
||||
|
||||
@ -30,8 +29,8 @@ public:
|
||||
void set_pattern_offset(Vector3i offset);
|
||||
|
||||
protected:
|
||||
void generate_block_flat(VoxelBuffer & out_buffer, Vector3i block_pos);
|
||||
void generate_block_waves(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);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
@ -48,6 +47,4 @@ private:
|
||||
Vector3i _pattern_size;
|
||||
};
|
||||
|
||||
|
||||
#endif // VOXEL_PROVIDER_TEST_H
|
||||
|
||||
|
@ -4,14 +4,13 @@
|
||||
const float g_infinite = 9999999;
|
||||
|
||||
bool voxel_raycast(
|
||||
Vector3 ray_origin,
|
||||
Vector3 ray_direction,
|
||||
VoxelPredicate predicate,
|
||||
void * predicate_context,
|
||||
real_t max_distance,
|
||||
Vector3i & out_hit_pos,
|
||||
Vector3i & out_prev_pos
|
||||
){
|
||||
Vector3 ray_origin,
|
||||
Vector3 ray_direction,
|
||||
VoxelPredicate predicate,
|
||||
void *predicate_context,
|
||||
real_t max_distance,
|
||||
Vector3i &out_hit_pos,
|
||||
Vector3i &out_prev_pos) {
|
||||
// Equation : p + v*t
|
||||
// p : ray start position (ray.pos)
|
||||
// v : ray orientation vector (ray.dir)
|
||||
@ -29,10 +28,9 @@ bool voxel_raycast(
|
||||
|
||||
// Voxel position
|
||||
Vector3i hit_pos(
|
||||
Math::floor(ray_origin.x),
|
||||
Math::floor(ray_origin.y),
|
||||
Math::floor(ray_origin.z)
|
||||
);
|
||||
Math::floor(ray_origin.x),
|
||||
Math::floor(ray_origin.y),
|
||||
Math::floor(ray_origin.z));
|
||||
Vector3i hit_prev_pos = hit_pos;
|
||||
|
||||
// Voxel step
|
||||
@ -51,91 +49,74 @@ bool voxel_raycast(
|
||||
real_t tcross_z; // At which value of T we will cross a depth line?
|
||||
|
||||
// X initialization
|
||||
if(xi_step != 0)
|
||||
{
|
||||
if(xi_step == 1)
|
||||
if (xi_step != 0) {
|
||||
if (xi_step == 1)
|
||||
tcross_x = (Math::ceil(ray_origin.x) - ray_origin.x) * tdelta_x;
|
||||
else
|
||||
tcross_x = (ray_origin.x - Math::floor(ray_origin.x)) * tdelta_x;
|
||||
}
|
||||
else
|
||||
} else
|
||||
tcross_x = g_infinite; // Will never cross on X
|
||||
|
||||
// Y initialization
|
||||
if(yi_step != 0)
|
||||
{
|
||||
if(yi_step == 1)
|
||||
if (yi_step != 0) {
|
||||
if (yi_step == 1)
|
||||
tcross_y = (Math::ceil(ray_origin.y) - ray_origin.y) * tdelta_y;
|
||||
else
|
||||
tcross_y = (ray_origin.y - Math::floor(ray_origin.y)) * tdelta_y;
|
||||
}
|
||||
else
|
||||
} else
|
||||
tcross_y = g_infinite; // Will never cross on X
|
||||
|
||||
// Z initialization
|
||||
if(zi_step != 0)
|
||||
{
|
||||
if(zi_step == 1)
|
||||
if (zi_step != 0) {
|
||||
if (zi_step == 1)
|
||||
tcross_z = (Math::ceil(ray_origin.z) - ray_origin.z) * tdelta_z;
|
||||
else
|
||||
tcross_z = (ray_origin.z - Math::floor(ray_origin.z)) * tdelta_z;
|
||||
}
|
||||
else
|
||||
} else
|
||||
tcross_z = g_infinite; // Will never cross on X
|
||||
|
||||
/* Iteration */
|
||||
|
||||
do
|
||||
{
|
||||
do {
|
||||
hit_prev_pos = hit_pos;
|
||||
if(tcross_x < tcross_y)
|
||||
{
|
||||
if(tcross_x < tcross_z)
|
||||
{
|
||||
if (tcross_x < tcross_y) {
|
||||
if (tcross_x < tcross_z) {
|
||||
// X collision
|
||||
//hit.prevPos.x = hit.pos.x;
|
||||
hit_pos.x += xi_step;
|
||||
if(tcross_x > max_distance)
|
||||
if (tcross_x > max_distance)
|
||||
return false;
|
||||
tcross_x += tdelta_x;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Z collision (duplicate code)
|
||||
//hit.prevPos.z = hit.pos.z;
|
||||
hit_pos.z += zi_step;
|
||||
if(tcross_z > max_distance)
|
||||
if (tcross_z > max_distance)
|
||||
return false;
|
||||
tcross_z += tdelta_z;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(tcross_y < tcross_z)
|
||||
{
|
||||
} else {
|
||||
if (tcross_y < tcross_z) {
|
||||
// Y collision
|
||||
//hit.prevPos.y = hit.pos.y;
|
||||
hit_pos.y += yi_step;
|
||||
if(tcross_y > max_distance)
|
||||
if (tcross_y > max_distance)
|
||||
return false;
|
||||
tcross_y += tdelta_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Z collision (duplicate code)
|
||||
//hit.prevPos.z = hit.pos.z;
|
||||
hit_pos.z += zi_step;
|
||||
if(tcross_z > max_distance)
|
||||
if (tcross_z > max_distance)
|
||||
return false;
|
||||
tcross_z += tdelta_z;
|
||||
}
|
||||
}
|
||||
|
||||
} while(!predicate(hit_pos, predicate_context));
|
||||
} while (!predicate(hit_pos, predicate_context));
|
||||
|
||||
out_hit_pos = hit_pos;
|
||||
out_prev_pos = hit_prev_pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,16 @@
|
||||
#include <vector3.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
|
||||
// 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(
|
||||
Vector3 ray_origin,
|
||||
Vector3 ray_direction,
|
||||
VoxelPredicate predicate,
|
||||
void * predicate_context, // Handle that one with care
|
||||
real_t max_distance,
|
||||
Vector3i & out_hit_pos,
|
||||
Vector3i & out_prev_pos
|
||||
);
|
||||
|
||||
Vector3 ray_origin,
|
||||
Vector3 ray_direction,
|
||||
VoxelPredicate predicate,
|
||||
void *predicate_context, // Handle that one with care
|
||||
real_t max_distance,
|
||||
Vector3i &out_hit_pos,
|
||||
Vector3i &out_prev_pos);
|
||||
|
@ -1,9 +1,10 @@
|
||||
#include "voxel_terrain.h"
|
||||
#include <scene/3d/mesh_instance.h>
|
||||
#include <os/os.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));
|
||||
_mesher = Ref<VoxelMesher>(memnew(VoxelMesher));
|
||||
@ -14,7 +15,7 @@ Vector3i g_viewer_block_pos; // TODO UGLY! Lambdas or pointers needed...
|
||||
|
||||
// Sorts distance to viewer
|
||||
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);
|
||||
}
|
||||
};
|
||||
@ -36,7 +37,7 @@ void VoxelTerrain::set_generate_collisions(bool enabled) {
|
||||
}
|
||||
|
||||
void VoxelTerrain::set_viewer_path(NodePath path) {
|
||||
if(!path.is_empty())
|
||||
if (!path.is_empty())
|
||||
ERR_FAIL_COND(get_viewer(path) == NULL);
|
||||
_viewer_path = path;
|
||||
}
|
||||
@ -45,11 +46,11 @@ NodePath VoxelTerrain::get_viewer_path() {
|
||||
return _viewer_path;
|
||||
}
|
||||
|
||||
Spatial * VoxelTerrain::get_viewer(NodePath path) {
|
||||
if(path.is_empty())
|
||||
Spatial *VoxelTerrain::get_viewer(NodePath path) {
|
||||
if (path.is_empty())
|
||||
return NULL;
|
||||
Node * node = get_node(path);
|
||||
if(node == NULL)
|
||||
Node *node = get_node(path);
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
return node->cast_to<Spatial>();
|
||||
}
|
||||
@ -61,7 +62,7 @@ Spatial * VoxelTerrain::get_viewer(NodePath path) {
|
||||
|
||||
void VoxelTerrain::make_block_dirty(Vector3i bpos) {
|
||||
// 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);
|
||||
_block_update_queue.push_back(bpos);
|
||||
_dirty_blocks[bpos] = true;
|
||||
@ -75,9 +76,9 @@ bool VoxelTerrain::is_block_dirty(Vector3i bpos) {
|
||||
void VoxelTerrain::make_blocks_dirty(Vector3i min, Vector3i size) {
|
||||
Vector3i max = min + size;
|
||||
Vector3i pos;
|
||||
for(pos.z = min.z; pos.z < max.z; ++pos.z) {
|
||||
for(pos.y = min.y; pos.y < max.y; ++pos.y) {
|
||||
for(pos.x = min.x; pos.x < max.x; ++pos.x) {
|
||||
for (pos.z = min.z; pos.z < max.z; ++pos.z) {
|
||||
for (pos.y = min.y; pos.y < max.y; ++pos.y) {
|
||||
for (pos.x = min.x; pos.x < max.x; ++pos.x) {
|
||||
make_block_dirty(pos);
|
||||
}
|
||||
}
|
||||
@ -101,28 +102,25 @@ void VoxelTerrain::make_voxel_dirty(Vector3i pos) {
|
||||
|
||||
bool check_corners = _mesher->get_occlusion_enabled();
|
||||
|
||||
const int max = VoxelBlock::SIZE-1;
|
||||
const int max = VoxelBlock::SIZE - 1;
|
||||
|
||||
if(rpos.x == 0)
|
||||
make_block_dirty(bpos - Vector3i(1,0,0));
|
||||
else
|
||||
if(rpos.x == max)
|
||||
make_block_dirty(bpos + Vector3i(1,0,0));
|
||||
if (rpos.x == 0)
|
||||
make_block_dirty(bpos - Vector3i(1, 0, 0));
|
||||
else if (rpos.x == max)
|
||||
make_block_dirty(bpos + Vector3i(1, 0, 0));
|
||||
|
||||
if(rpos.y == 0)
|
||||
make_block_dirty(bpos - Vector3i(0,1,0));
|
||||
else
|
||||
if(rpos.y == max)
|
||||
make_block_dirty(bpos + Vector3i(0,1,0));
|
||||
if (rpos.y == 0)
|
||||
make_block_dirty(bpos - Vector3i(0, 1, 0));
|
||||
else if (rpos.y == max)
|
||||
make_block_dirty(bpos + Vector3i(0, 1, 0));
|
||||
|
||||
if(rpos.z == 0)
|
||||
make_block_dirty(bpos - Vector3i(0,0,1));
|
||||
else
|
||||
if(rpos.z == max)
|
||||
make_block_dirty(bpos + Vector3i(0,0,1));
|
||||
if (rpos.z == 0)
|
||||
make_block_dirty(bpos - Vector3i(0, 0, 1));
|
||||
else if (rpos.z == max)
|
||||
make_block_dirty(bpos + Vector3i(0, 0, 1));
|
||||
|
||||
// We might want to update blocks in corners in order to update ambient occlusion
|
||||
if(check_corners) {
|
||||
if (check_corners) {
|
||||
|
||||
// 24------25------26
|
||||
// /| /|
|
||||
@ -143,17 +141,17 @@ void VoxelTerrain::make_voxel_dirty(Vector3i pos) {
|
||||
// I'm not good at writing piles of ifs
|
||||
|
||||
static const int normals[27][3] = {
|
||||
{-1,-1,-1}, { 0,-1,-1}, { 1,-1,-1},
|
||||
{-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, -1, 0 }, { 0, -1, 0 }, { 1, -1, 0 },
|
||||
{ -1, -1, 1 }, { 0, -1, 1 }, { 1, -1, 1 },
|
||||
|
||||
{-1, 0,-1}, { 0, 0,-1}, { 1, 0,-1},
|
||||
{-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, 0, 0 }, { 0, 0, 0 }, { 1, 0, 0 },
|
||||
{ -1, 0, 1 }, { 0, 0, 1 }, { 1, 0, 1 },
|
||||
|
||||
{-1, 1,-1}, { 0, 1,-1}, { 1, 1,-1},
|
||||
{-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, 1, 0 }, { 0, 1, 0 }, { 1, 1, 0 },
|
||||
{ -1, 1, 1 }, { 0, 1, 1 }, { 1, 1, 1 }
|
||||
};
|
||||
static const int ce_counts[27] = {
|
||||
4, 1, 4,
|
||||
@ -169,32 +167,30 @@ void VoxelTerrain::make_voxel_dirty(Vector3i pos) {
|
||||
4, 1, 4
|
||||
};
|
||||
static const int ce_indexes_lut[27][4] = {
|
||||
{0, 1, 3, 9}, {1}, {2, 1, 5, 11},
|
||||
{3}, {}, {5},
|
||||
{6, 3, 7, 15}, {7}, {8, 7, 5, 17},
|
||||
{ 0, 1, 3, 9 }, { 1 }, { 2, 1, 5, 11 },
|
||||
{ 3 }, {}, { 5 },
|
||||
{ 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},
|
||||
{21}, {}, {23},
|
||||
{24, 15, 21, 25}, {25}, {26, 17, 23, 25}
|
||||
{ 18, 9, 19, 21 }, { 19 }, { 20, 11, 19, 23 },
|
||||
{ 21 }, {}, { 23 },
|
||||
{ 24, 15, 21, 25 }, { 25 }, { 26, 17, 23, 25 }
|
||||
};
|
||||
|
||||
int m = get_border_index(rpos.x, max)
|
||||
+ 3*get_border_index(rpos.z, max)
|
||||
+ 9*get_border_index(rpos.y, max);
|
||||
int m = get_border_index(rpos.x, 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];
|
||||
//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,
|
||||
// we could optimize it even more by looking at neighbor voxels,
|
||||
// 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]);
|
||||
//OS::get_singleton()->print("Corner dirty (%i, %i, %i)\n", nbpos.x, nbpos.y, nbpos.z);
|
||||
make_block_dirty(nbpos);
|
||||
@ -210,19 +206,19 @@ void VoxelTerrain::_notification(int p_what) {
|
||||
|
||||
switch (p_what) {
|
||||
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
set_process(true);
|
||||
break;
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
set_process(true);
|
||||
break;
|
||||
|
||||
case NOTIFICATION_PROCESS:
|
||||
_process();
|
||||
break;
|
||||
case NOTIFICATION_PROCESS:
|
||||
_process();
|
||||
break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE:
|
||||
break;
|
||||
case NOTIFICATION_EXIT_TREE:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,11 +227,11 @@ void VoxelTerrain::_process() {
|
||||
}
|
||||
|
||||
void VoxelTerrain::update_blocks() {
|
||||
OS & os = *OS::get_singleton();
|
||||
OS &os = *OS::get_singleton();
|
||||
|
||||
// Get viewer location
|
||||
Spatial * viewer = get_viewer(_viewer_path);
|
||||
if(viewer)
|
||||
Spatial *viewer = get_viewer(_viewer_path);
|
||||
if (viewer)
|
||||
g_viewer_block_pos = VoxelMap::voxel_to_block(viewer->get_translation());
|
||||
else
|
||||
g_viewer_block_pos = Vector3i();
|
||||
@ -263,7 +259,7 @@ void VoxelTerrain::update_blocks() {
|
||||
|
||||
if (!_map->has_block(block_pos)) {
|
||||
// Create buffer
|
||||
if(!_provider.is_null()) {
|
||||
if (!_provider.is_null()) {
|
||||
|
||||
VOXEL_PROFILE_BEGIN("voxel_buffer_creation_gen")
|
||||
|
||||
@ -291,7 +287,7 @@ void VoxelTerrain::update_blocks() {
|
||||
|
||||
// Update views (mesh/collisions)
|
||||
|
||||
if(entire_block_changed) {
|
||||
if (entire_block_changed) {
|
||||
// All neighbors have to be checked
|
||||
Vector3i ndir;
|
||||
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
|
||||
update_block_mesh(block_pos);
|
||||
//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) {
|
||||
if(mesh_ref.is_null())
|
||||
if (mesh_ref.is_null())
|
||||
return true;
|
||||
Mesh & mesh = **mesh_ref;
|
||||
if(mesh.get_surface_count() == 0)
|
||||
Mesh &mesh = **mesh_ref;
|
||||
if (mesh.get_surface_count() == 0)
|
||||
return true;
|
||||
// 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 false;
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
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();
|
||||
|
||||
// TODO Re-use existing meshes to optimize memory cost
|
||||
// TODO Re-use existing meshes to optimize memory cost
|
||||
|
||||
// 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
|
||||
_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(mesh_instance) {
|
||||
if (is_mesh_empty(mesh)) {
|
||||
if (mesh_instance) {
|
||||
mesh_instance->set_mesh(Ref<Mesh>());
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// The mesh exist and it has vertices
|
||||
|
||||
// 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);
|
||||
add_child(mesh_instance);
|
||||
block->mesh_instance_path = mesh_instance->get_path();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Update mesh
|
||||
VOXEL_PROFILE_BEGIN("mesh_instance_set_mesh")
|
||||
mesh_instance->set_mesh(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 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 {
|
||||
VoxelTerrain & terrain;
|
||||
VoxelTerrain &terrain;
|
||||
//unsigned int channel_mask;
|
||||
};
|
||||
|
||||
static bool _raycast_binding_predicate(Vector3i pos, void *context_ptr) {
|
||||
|
||||
ERR_FAIL_COND_V(context_ptr == NULL, false);
|
||||
_VoxelTerrainRaycastContext * context = (_VoxelTerrainRaycastContext*)context_ptr;
|
||||
VoxelTerrain & terrain = context->terrain;
|
||||
_VoxelTerrainRaycastContext *context = (_VoxelTerrainRaycastContext *)context_ptr;
|
||||
VoxelTerrain &terrain = context->terrain;
|
||||
|
||||
//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);
|
||||
|
||||
Ref<VoxelLibrary> lib_ref = terrain.get_voxel_library();
|
||||
if(lib_ref.is_null())
|
||||
if (lib_ref.is_null())
|
||||
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;
|
||||
|
||||
const Voxel & voxel = lib.get_voxel_const(v0);
|
||||
if(voxel.is_transparent() == false)
|
||||
const Voxel &voxel = lib.get_voxel_const(v0);
|
||||
if (voxel.is_transparent() == false)
|
||||
return true;
|
||||
|
||||
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 };
|
||||
|
||||
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();
|
||||
hit["position"] = hit_pos.to_vec3();
|
||||
hit["prev_position"] = prev_pos.to_vec3();
|
||||
return hit;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return Variant(); // Null dictionary, no alloc
|
||||
}
|
||||
}
|
||||
@ -484,6 +474,4 @@ void VoxelTerrain::_bind_methods() {
|
||||
#ifdef VOXEL_PROFILING
|
||||
ClassDB::bind_method(D_METHOD("get_profiling_info"), &VoxelTerrain::get_profiling_info);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
#ifndef VOXEL_TERRAIN_H
|
||||
#define VOXEL_TERRAIN_H
|
||||
|
||||
#include <scene/main/node.h>
|
||||
#include "voxel_map.h"
|
||||
#include "voxel_mesher.h"
|
||||
#include "voxel_mesher_smooth.h"
|
||||
#include "voxel_provider.h"
|
||||
#include "zprofiling.h"
|
||||
#include <scene/main/node.h>
|
||||
|
||||
// Infinite static terrain made of voxels.
|
||||
// It is loaded around VoxelTerrainStreamers.
|
||||
@ -46,7 +46,7 @@ private:
|
||||
void update_blocks();
|
||||
void update_block_mesh(Vector3i block_pos);
|
||||
|
||||
Spatial * get_viewer(NodePath path);
|
||||
Spatial *get_viewer(NodePath path);
|
||||
|
||||
// Observer events
|
||||
//void block_removed(VoxelBlock & block);
|
||||
@ -90,8 +90,6 @@ private:
|
||||
ZProfiler _zprofiler;
|
||||
Dictionary get_profiling_info() { return _zprofiler.get_all_serialized_info(); }
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif // VOXEL_TERRAIN_H
|
||||
|
||||
|
@ -40,7 +40,7 @@ ZProfileVar::ZProfileVar() {
|
||||
total_time = 0.f;
|
||||
hits = 0;
|
||||
_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_time_index = 0;
|
||||
}
|
||||
@ -52,17 +52,14 @@ void ZProfileVar::begin(uint64_t time) {
|
||||
void ZProfileVar::end(uint64_t time) {
|
||||
instant_time = time - _begin_time;
|
||||
|
||||
if(hits == 0)
|
||||
{
|
||||
if (hits == 0) {
|
||||
min_time = instant_time;
|
||||
max_time = instant_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(instant_time < min_time)
|
||||
} else {
|
||||
if (instant_time < min_time)
|
||||
min_time = instant_time;
|
||||
|
||||
if(instant_time > max_time)
|
||||
if (instant_time > max_time)
|
||||
max_time = instant_time;
|
||||
}
|
||||
|
||||
@ -70,7 +67,7 @@ void ZProfileVar::end(uint64_t time) {
|
||||
|
||||
buffered_times[buffered_time_index] = instant_time;
|
||||
++buffered_time_index;
|
||||
if(buffered_time_index >= BUFFERED_TIME_COUNT)
|
||||
if (buffered_time_index >= BUFFERED_TIME_COUNT)
|
||||
buffered_time_index = 0;
|
||||
|
||||
++hits;
|
||||
@ -86,7 +83,7 @@ Dictionary ZProfileVar::serialize() {
|
||||
d["hits"] = hits;
|
||||
|
||||
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]);
|
||||
}
|
||||
d["buffered_times"] = a;
|
||||
@ -103,47 +100,44 @@ Dictionary ZProfileVar::serialize() {
|
||||
//}
|
||||
|
||||
ZProfiler::~ZProfiler() {
|
||||
const String * key = NULL;
|
||||
const String *key = NULL;
|
||||
while (key = _vars.next(key)) {
|
||||
ZProfileVar * v = _vars.get(*key);
|
||||
ZProfileVar *v = _vars.get(*key);
|
||||
memdelete(v);
|
||||
}
|
||||
}
|
||||
|
||||
ZProfileVar * ZProfiler::get_var(String key) {
|
||||
ZProfileVar * v = NULL;
|
||||
ZProfileVar ** pv = _vars.getptr(key);
|
||||
if(pv == NULL) {
|
||||
ZProfileVar *ZProfiler::get_var(String key) {
|
||||
ZProfileVar *v = NULL;
|
||||
ZProfileVar **pv = _vars.getptr(key);
|
||||
if (pv == NULL) {
|
||||
v = memnew(ZProfileVar);
|
||||
_vars[key] = v;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
v = *pv;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void ZProfiler::begin(String key) {
|
||||
ZProfileVar * v = get_var(key);
|
||||
ZProfileVar *v = get_var(key);
|
||||
v->begin(get_time());
|
||||
}
|
||||
|
||||
void ZProfiler::end(String key) {
|
||||
uint64_t time = get_time();
|
||||
ZProfileVar * v = get_var(key);
|
||||
ZProfileVar *v = get_var(key);
|
||||
v->end(time);
|
||||
}
|
||||
|
||||
Dictionary ZProfiler::get_all_serialized_info() const {
|
||||
Dictionary d;
|
||||
const String * key = NULL;
|
||||
const String *key = NULL;
|
||||
while (key = _vars.next(key)) {
|
||||
ZProfileVar * v = _vars.get(*key);
|
||||
ZProfileVar *v = _vars.get(*key);
|
||||
d[*key] = v->serialize();
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
#endif // VOXEL_PROFILING
|
||||
|
||||
|
@ -5,9 +5,9 @@
|
||||
|
||||
#ifdef VOXEL_PROFILING
|
||||
|
||||
#include <ustring.h>
|
||||
#include <dictionary.h>
|
||||
#include <hash_map.h>
|
||||
#include <ustring.h>
|
||||
|
||||
#define VOXEL_PROFILE_BEGIN(_key) _zprofiler.begin(_key);
|
||||
#define VOXEL_PROFILE_END(_key) _zprofiler.end(_key);
|
||||
@ -26,9 +26,9 @@ public:
|
||||
|
||||
private:
|
||||
//ZProfiler();
|
||||
ZProfileVar * get_var(String key);
|
||||
ZProfileVar *get_var(String key);
|
||||
|
||||
HashMap<String, ZProfileVar*> _vars;
|
||||
HashMap<String, ZProfileVar *> _vars;
|
||||
};
|
||||
|
||||
#else
|
||||
|
Loading…
Reference in New Issue
Block a user