Added VoxelMap and VoxelTerrain + various fixes

This commit is contained in:
Marc Gilleron 2016-05-10 01:59:54 +02:00
parent e990286b54
commit 5662dfde7b
9 changed files with 700 additions and 20 deletions

View File

@ -2,6 +2,8 @@
#include "voxel_buffer.h"
#include "voxel_mesher.h"
#include "voxel_library.h"
#include "voxel_map.h"
#include "voxel_terrain.h"
void register_voxel_types() {
@ -9,6 +11,8 @@ void register_voxel_types() {
ObjectTypeDB::register_type<VoxelBuffer>();
ObjectTypeDB::register_type<VoxelMesher>();
ObjectTypeDB::register_type<VoxelLibrary>();
ObjectTypeDB::register_type<VoxelMap>();
ObjectTypeDB::register_type<VoxelTerrain>();
}

View File

@ -28,10 +28,22 @@ struct Vector3i {
z = Math::floor(f.z);
}
_FORCE_INLINE_ Vector3 to_vec3() {
_FORCE_INLINE_ Vector3 to_vec3() const {
return Vector3(x, y, z);
}
_FORCE_INLINE_ int volume() const {
return x*y*z;
}
_FORCE_INLINE_ int length_sq() const {
return x*x + y*y + z*z;
}
_FORCE_INLINE_ float length() const {
return Math::sqrt(length_sq());
}
_FORCE_INLINE_ Vector3i & operator=(const Vector3i & other) {
x = other.x;
y = other.y;
@ -60,9 +72,9 @@ struct Vector3i {
if (y < min.y) y = min.y;
if (z < min.z) z = min.z;
if (x >= max.x) x = max.x;
if (y >= max.y) y = max.y;
if (z >= max.z) z = max.z;
if (x >= max.x) x = max.x - 1;
if (y >= max.y) y = max.y - 1;
if (z >= max.z) z = max.z - 1;
}
_FORCE_INLINE_ bool is_contained_in(const Vector3i & min, const Vector3i & max) {
@ -78,6 +90,21 @@ struct Vector3i {
);
}
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) {
if (a > b) {
int temp = a;
a = b;
b = a;
}
}
};
_FORCE_INLINE_ Vector3i operator+(const Vector3i a, const Vector3i & b) {

View File

@ -93,20 +93,24 @@ void VoxelBuffer::fill(int defval, unsigned int channel_index) {
void VoxelBuffer::fill_area(int defval, Vector3i min, Vector3i max, unsigned int channel_index) {
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
Vector3i::sort_min_max(min, max);
min.clamp_to(Vector3i(0, 0, 0), _size);
max.clamp_to(Vector3i(0, 0, 0), _size);
max.clamp_to(Vector3i(0, 0, 0), _size + Vector3i(1,1,1));
Vector3i area_size = max - min;
Channel & channel = _channels[channel_index];
if (channel.data == NULL && channel.defval == defval)
return;
else
create_channel(channel_index, _size);
if (channel.data == NULL) {
if (channel.defval == defval)
return;
else
create_channel(channel_index, _size);
}
Vector3i pos;
for (pos.z = min.z; pos.z < max.z; ++pos.z) {
for (pos.x = min.x; pos.x < max.x; ++pos.x) {
unsigned int dst_ri = row_index(pos.x, pos.y, pos.z);
unsigned int dst_ri = index(pos.x, pos.y, pos.z);
memset(&channel.data[dst_ri], defval, area_size.y * sizeof(uint8_t));
}
}
@ -164,12 +168,14 @@ void VoxelBuffer::copy_from(const VoxelBuffer & other, Vector3i src_min, Vector3
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);
src_max.clamp_to(Vector3i(0, 0, 0), other._size + Vector3i(1,1,1));
dst_min.clamp_to(Vector3i(0, 0, 0), _size);
Vector3i area_size = src_max - src_min;
Vector3i dst_max = src_min + area_size;
//Vector3i dst_max = dst_min + area_size;
if (area_size == _size) {
copy_from(other, channel_index);
@ -184,8 +190,8 @@ void VoxelBuffer::copy_from(const VoxelBuffer & other, Vector3i src_min, Vector3
for (pos.z = 0; pos.z < area_size.z; ++pos.z) {
for (pos.x = 0; pos.x < area_size.x; ++pos.x) {
// Row direction is Y
unsigned int src_ri = other.row_index(pos.x + src_min.x, pos.y + src_min.y, pos.z + src_min.z);
unsigned int dst_ri = row_index(pos.x + dst_min.x, pos.y + dst_min.y, pos.z + dst_min.z);
unsigned int src_ri = other.index(pos.x + src_min.x, pos.y + src_min.y, pos.z + src_min.z);
unsigned int dst_ri = index(pos.x + dst_min.x, pos.y + dst_min.y, pos.z + dst_min.z);
memcpy(&channel.data[dst_ri], &other_channel.data[src_ri], area_size.y * sizeof(uint8_t));
}
}
@ -198,7 +204,7 @@ void VoxelBuffer::copy_from(const VoxelBuffer & other, Vector3i src_min, Vector3
Vector3i pos;
for (pos.z = 0; pos.z < area_size.z; ++pos.z) {
for (pos.x = 0; pos.x < area_size.x; ++pos.x) {
unsigned int dst_ri = row_index(pos.x + dst_min.x, pos.y + dst_min.y, pos.z + dst_min.z);
unsigned int dst_ri = index(pos.x + dst_min.x, pos.y + dst_min.y, pos.z + dst_min.z);
memset(&channel.data[dst_ri], other_channel.defval, area_size.y * sizeof(uint8_t));
}
}

236
voxel_map.cpp Normal file
View File

@ -0,0 +1,236 @@
#include "voxel_map.h"
VoxelMap::VoxelMap() : _last_accessed_block(NULL) {
for (unsigned int i = 0; i < VoxelBuffer::MAX_CHANNELS; ++i) {
_default_voxel[i] = 0;
}
}
VoxelMap::~VoxelMap() {
}
int VoxelMap::get_voxel(Vector3i pos, unsigned int c) {
Vector3i bpos = voxel_to_block(pos);
VoxelBlock * block = get_block(bpos);
if (block == NULL) {
return _default_voxel[c];
}
return block->voxels->get_voxel(pos - block_to_voxel(bpos), c);
}
MeshInstance * VoxelBlock::get_mesh_instance(const Node & root) {
if (mesh_instance_path.is_empty())
return NULL;
Node * n = root.get_node(mesh_instance_path);
if (n == NULL)
return NULL;
return n->cast_to<MeshInstance>();
}
VoxelBlock::~VoxelBlock() {
}
// Helper
VoxelBlock * VoxelBlock::create(VoxelMap & map, Vector3i bpos, VoxelBuffer * buffer) {
VoxelBlock * block = memnew(VoxelBlock);
block->pos = bpos;
if (buffer) {
const int bs = VoxelBlock::SIZE;
ERR_FAIL_COND_V(buffer->get_size() != Vector3i(bs, bs, bs), NULL);
}
else {
buffer = memnew(VoxelBuffer);
}
ERR_FAIL_COND_V(buffer == NULL, NULL);
block->voxels = Ref<VoxelBuffer>(buffer);
//block->map = &map;
return block;
}
void VoxelMap::set_voxel(int value, Vector3i pos, unsigned int c) {
Vector3i bpos = voxel_to_block(pos);
VoxelBlock * block = get_block(pos);
if (block == NULL) {
set_block(bpos, VoxelBlock::create(*this, bpos));
}
block->voxels->set_voxel(value, pos - block_to_voxel(bpos), c);
}
void VoxelMap::set_default_voxel(int value, unsigned int channel) {
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
_default_voxel[channel] = value;
}
int VoxelMap::get_default_voxel(unsigned int channel) {
ERR_FAIL_INDEX_V(channel, VoxelBuffer::MAX_CHANNELS, 0);
return _default_voxel[channel];
}
VoxelBlock * VoxelMap::get_block(Vector3i bpos) {
if (_last_accessed_block && _last_accessed_block->pos == bpos) {
return _last_accessed_block;
}
Ref<VoxelBlock> * p = _blocks.getptr(bpos);
if (p) {
_last_accessed_block = p->ptr();
return _last_accessed_block;
}
return NULL;
}
void VoxelMap::set_block(Vector3i bpos, VoxelBlock * block) {
if (_last_accessed_block == NULL || _last_accessed_block->pos == bpos) {
_last_accessed_block = block;
}
_blocks.set(bpos, block);
}
void VoxelMap::set_block_buffer(Vector3i bpos, Ref<VoxelBuffer> buffer) {
ERR_FAIL_COND(buffer.is_null());
VoxelBlock * block = get_block(bpos);
if (block == NULL) {
block = VoxelBlock::create(*this, bpos, *buffer);
set_block(bpos, block);
}
else {
block->voxels = buffer;
}
}
bool VoxelMap::has_block(Vector3i pos) const {
return /*(_last_accessed_block != NULL && _last_accessed_block->pos == pos) ||*/ _blocks.has(pos);
}
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,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,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 {
for (unsigned int i = 0; i < 26; ++i) {
Vector3i bpos = pos + g_moore_neighboring_3d[i];
if (!has_block(bpos)) {
return false;
}
}
return true;
}
void VoxelMap::get_buffer_copy(Vector3i min_pos, VoxelBuffer & dst_buffer, unsigned int channel) {
ERR_FAIL_INDEX(channel, VoxelBuffer::MAX_CHANNELS);
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);
ERR_FAIL_COND((max_block_pos - min_block_pos) != Vector3(3, 3, 3));
Vector3i bpos;
for (bpos.z = min_block_pos.z; bpos.z < max_block_pos.z; ++bpos.z) {
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);
if (block) {
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 {
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)
);
}
}
}
}
}
void VoxelMap::remove_blocks_not_in_area(Vector3i min, Vector3i max) {
Vector3i::sort_min_max(min, max);
Vector<Vector3i> to_remove;
Vector3i * key = NULL;
while (_blocks.next(key)) {
Ref<VoxelBlock> & block_ref = _blocks.get(*key);
ERR_FAIL_COND(block_ref.is_null()); // Should never trigger
VoxelBlock & block = **block_ref;
if (!block.pos.is_contained_in(min, max)) {
//if (_observer)
// _observer->block_removed(block);
to_remove.push_back(*key);
if (&block == _last_accessed_block)
_last_accessed_block = NULL;
}
}
for (unsigned int i = 0; i < to_remove.size(); ++i) {
_blocks.erase(to_remove[i]);
}
}
void VoxelMap::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_voxel", "x", "y", "z", "c"), &VoxelMap::_get_voxel_binding, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("set_voxel", "value", "x", "y", "z", "c"), &VoxelMap::_set_voxel_binding, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("get_default_voxel", "channel"), &VoxelMap::get_default_voxel, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("set_default_voxel", "value", "channel"), &VoxelMap::set_default_voxel, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("has_block", "x", "y", "z"), &VoxelMap::_has_block_binding);
ObjectTypeDB::bind_method(_MD("get_buffer_copy", "min_pos", "out_buffer:VoxelBuffer", "channel"), &VoxelMap::_get_buffer_copy_binding, DEFVAL(0));
ObjectTypeDB::bind_method(_MD("set_block_buffer", "block_pos", "buffer:VoxelBuffer"), &VoxelMap::_set_block_buffer_binding);
ObjectTypeDB::bind_method(_MD("voxel_to_block", "voxel_pos"), &VoxelMap::_voxel_to_block_binding);
ObjectTypeDB::bind_method(_MD("block_to_voxel", "block_pos"), &VoxelMap::_block_to_voxel_binding);
ObjectTypeDB::bind_method(_MD("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);
}

155
voxel_map.h Normal file
View File

@ -0,0 +1,155 @@
#ifndef VOXEL_MAP_H
#define VOXEL_MAP_H
#include <scene/main/node.h>
#include <core/hash_map.h>
#include <scene/3d/mesh_instance.h>
#include "voxel_buffer.h"
class VoxelMap;
// Fixed-size voxel container used in VoxelMap. Used internally.
class VoxelBlock : public Reference {
OBJ_TYPE(VoxelBlock, Reference)
public:
static const int SIZE_POW2 = 4; // 3=>8, 4=>16, 5=>32...
static const int SIZE = 1 << SIZE_POW2;
Ref<VoxelBuffer> voxels; // SIZE*SIZE*SIZE voxels
Vector3i pos;
NodePath mesh_instance_path;
//VoxelMap * map;
MeshInstance * get_mesh_instance(const Node & root);
static VoxelBlock * create(VoxelMap & map, Vector3i bpos, VoxelBuffer * buffer = 0);
~VoxelBlock();
private:
VoxelBlock() : Reference(), voxels(NULL) {}
};
//class IVoxelMapObserver {
//public:
// virtual void block_removed(VoxelBlock & block) = 0;
//};
// Infinite voxel storage by means of octants like Gridmap
class VoxelMap : public Reference {
OBJ_TYPE(VoxelMap, Reference)
// Voxel values that will be returned if access is out of map bounds
uint8_t _default_voxel[VoxelBuffer::MAX_CHANNELS];
// Blocks stored with a spatial hash in all 3D directions
HashMap<Vector3i, Ref<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;
//IVoxelMapObserver * _observer;
public:
VoxelMap();
~VoxelMap();
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);
// Converts voxel coodinates into block coordinates
static _FORCE_INLINE_ Vector3i voxel_to_block(Vector3i pos) {
return Vector3i(
//pos.x < 0 ? (pos.x + 1) / VoxelBlock::SIZE - 1 : pos.x / VoxelBlock::SIZE,
//pos.y < 0 ? (pos.y + 1) / VoxelBlock::SIZE - 1 : pos.y / VoxelBlock::SIZE,
//pos.z < 0 ? (pos.z + 1) / VoxelBlock::SIZE - 1 : pos.z / VoxelBlock::SIZE
pos.x >> VoxelBlock::SIZE_POW2,
pos.y >> VoxelBlock::SIZE_POW2,
pos.z >> VoxelBlock::SIZE_POW2
);
}
// Converts block coodinates into voxel coordinates
static _FORCE_INLINE_ Vector3i block_to_voxel(Vector3i bpos) {
return bpos * VoxelBlock::SIZE;
}
// 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 channel = 0);
// 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);
_FORCE_INLINE_ Ref<VoxelBlock> get_block_ref(Vector3i bpos) { return get_block(bpos); }
bool has_block(Vector3i pos) const;
bool is_block_surrounded(Vector3i pos) const;
//void set_observer(IVoxelMapObserver * observer) { _observer = observer; }
private:
VoxelBlock * get_block(Vector3i bpos);
void set_block(Vector3i bpos, VoxelBlock * block);
_FORCE_INLINE_ int get_block_size() const { return VoxelBlock::SIZE; }
static void _bind_methods();
_FORCE_INLINE_ int _get_voxel_binding(int x, int y, int z, unsigned int c = 0) { return get_voxel(Vector3i(x, y, z), c); }
_FORCE_INLINE_ void _set_voxel_binding(int value, int x, int y, int z, unsigned int c = 0) { set_voxel(value, Vector3i(x, y, z), c); }
_FORCE_INLINE_ bool _has_block_binding(int x, int y, int z) { return has_block(Vector3i(x, y, z)); }
_FORCE_INLINE_ Vector3 _voxel_to_block_binding(Vector3 pos) const { return voxel_to_block(Vector3i(pos)).to_vec3(); }
_FORCE_INLINE_ Vector3 _block_to_voxel_binding(Vector3 pos) const { return block_to_voxel(Vector3i(pos)).to_vec3(); }
bool _is_block_surrounded(Vector3 pos) const { return is_block_surrounded(Vector3i(pos)); }
void _get_buffer_copy_binding(Vector3 pos, Ref<VoxelBuffer> dst_buffer_ref, unsigned int channel = 0);
void _set_block_buffer_binding(Vector3 bpos, Ref<VoxelBuffer> buffer) { set_block_buffer(Vector3i(bpos), buffer); }
};
//class VoxelSector {
//public:
// static const int SIZE = 16;
//
//private:
// Ref<VoxelBlock> blocks[SIZE * SIZE * SIZE];
//};
//template <unsigned int P>
//struct VoxelTree {
//
// const int SIZE = 1 << P;
// const int VOLUME = P*P*P;
//
// uint8_t get_voxel(int x, int y, int z) {
// unsigned int i = index(x / SIZE, y / SIZE, z / SIZE);
// ERR_FAIL_COND_V(i >= VOLUME, 0);
// if (subtrees) {
// VoxelTree<N> * subtree = subtrees[i];
// if (subtree) {
// return subtree->get_voxel(x, y, z);
// }
// }
// else if (blocks[i].is_valid()) {
// return blocks[i]->voxels.get_voxel(x, y, z);
// }
// return 0;
// }
//
// unsigned int index(int x, int y, int z) {
// return (pos.z * SIZE + pos.x) * SIZE + pos.y;
// }
//
// Ref<VoxelBlock> * blocks;
// VoxelTree<N> * subtrees;
// int level;
//};
#endif // VOXEL_MAP_H

View File

@ -165,11 +165,14 @@ inline bool is_transparent(const VoxelLibrary & lib, int voxel_id) {
return true;
}
Ref<Mesh> VoxelMesher::build(Ref<VoxelBuffer> buffer_ref) {
Ref<Mesh> VoxelMesher::build_ref(Ref<VoxelBuffer> buffer_ref) {
ERR_FAIL_COND_V(buffer_ref.is_null(), Ref<Mesh>());
return build(**buffer_ref);
}
Ref<Mesh> VoxelMesher::build(const VoxelBuffer & buffer) {
ERR_FAIL_COND_V(_library.is_null(), Ref<Mesh>());
const VoxelBuffer & buffer = **buffer_ref;
const VoxelLibrary & library = **_library;
for (unsigned int i = 0; i < MAX_MATERIALS; ++i) {
@ -335,7 +338,6 @@ void VoxelMesher::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_library", "voxel_library"), &VoxelMesher::set_library);
ObjectTypeDB::bind_method(_MD("set_occlusion_enabled", "enable"), &VoxelMesher::set_occlusion_enabled);
ObjectTypeDB::bind_method(_MD("set_occlusion_darkness", "value"), &VoxelMesher::set_occlusion_darkness);
ObjectTypeDB::bind_method(_MD("build", "voxel_buffer"), &VoxelMesher::build);
ObjectTypeDB::bind_method(_MD("build", "voxel_buffer"), &VoxelMesher::build_ref);
}

View File

@ -33,7 +33,8 @@ public:
void set_occlusion_enabled(bool enable);
Ref<Mesh> build(Ref<VoxelBuffer> buffer_ref);
Ref<Mesh> build(const VoxelBuffer & buffer_ref);
Ref<Mesh> build_ref(Ref<VoxelBuffer> buffer_ref);
protected:

197
voxel_terrain.cpp Normal file
View File

@ -0,0 +1,197 @@
#include "voxel_terrain.h"
#include <scene/3d/mesh_instance.h>
#include <os/os.h>
VoxelTerrain::VoxelTerrain(): Node(), _min_y(-4), _max_y(4) {
_map = Ref<VoxelMap>(memnew(VoxelMap));
_mesher = Ref<VoxelMesher>(memnew(VoxelMesher));
}
struct BlockUpdateComparator0 {
inline bool operator()(const Vector3i & a, const Vector3i & b) const {
return a.length_sq() > b.length_sq();
}
};
void VoxelTerrain::force_load_blocks(Vector3i center, Vector3i extents) {
//Vector3i min = center - extents;
//Vector3i max = center + extents + Vector3i(1,1,1);
//Vector3i size = max - min;
_block_update_queue.clear();
Vector3i pos;
for (pos.z = -extents.z; pos.z <= extents.z; ++pos.z) {
for (pos.x = -extents.x; pos.x <= extents.x; ++pos.x) {
for (pos.y = -extents.y; pos.y <= extents.y; ++pos.y) {
_block_update_queue.push_back(pos);
}
}
}
_block_update_queue.sort_custom<BlockUpdateComparator0>();
}
int VoxelTerrain::get_block_update_count() {
return _block_update_queue.size();
}
void VoxelTerrain::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
set_process(true);
break;
case NOTIFICATION_PROCESS:
_process();
break;
case NOTIFICATION_EXIT_TREE:
break;
default:
break;
}
}
void VoxelTerrain::_process() {
update_blocks();
}
void VoxelTerrain::update_blocks() {
OS & os = *OS::get_singleton();
uint32_t time_before = os.get_ticks_msec();
uint32_t max_time = 1000 / 60;
while (!_block_update_queue.empty() && (os.get_ticks_msec() - time_before) < max_time) {
// TODO Move this to a thread
// TODO Have VoxelTerrainGenerator in C++
// TODO Keep track of MeshInstances!
// Get request
Vector3i block_pos = _block_update_queue[_block_update_queue.size() - 1];
if (!_map->has_block(block_pos)) {
// Get script
ScriptInstance * script = get_script_instance();
if (script == NULL) {
return;
}
// Create buffer
Ref<VoxelBuffer> buffer_ref = Ref<VoxelBuffer>(memnew(VoxelBuffer));
const Vector3i block_size(VoxelBlock::SIZE, VoxelBlock::SIZE, VoxelBlock::SIZE);
buffer_ref->create(block_size.x, block_size.y, block_size.y);
// Call script to generate buffer
Variant arg1 = buffer_ref;
Variant arg2 = block_pos.to_vec3();
const Variant * args[2] = { &arg1, &arg2 };
Variant::CallError err; // wut
script->call_multilevel("_generate_block", args, 2);
// Check script return
ERR_FAIL_COND(buffer_ref->get_size() != block_size);
// Store buffer
_map->set_block_buffer(block_pos, buffer_ref);
// Update meshes
Vector3i ndir;
for (ndir.z = -1; ndir.z < 2; ++ndir.z) {
for (ndir.x = -1; ndir.x < 2; ++ndir.x) {
for (ndir.y = -1; ndir.y < 2; ++ndir.y) {
Vector3i npos = block_pos + ndir;
if (_map->is_block_surrounded(npos)) {
update_block_mesh(npos);
}
}
}
}
//update_block_mesh(block_pos);
}
// Pop request
_block_update_queue.resize(_block_update_queue.size() - 1);
}
}
void VoxelTerrain::update_block_mesh(Vector3i block_pos) {
Ref<VoxelBlock> block_ref = _map->get_block_ref(block_pos);
if (block_ref.is_null()) {
return;
}
if (block_ref->voxels->is_uniform(0) && block_ref->voxels->get_voxel(0, 0, 0, 0) == 0) {
return;
}
// Create buffer padded with neighbor voxels
VoxelBuffer nbuffer;
nbuffer.create(VoxelBlock::SIZE + 2, VoxelBlock::SIZE + 2, VoxelBlock::SIZE + 2);
_map->get_buffer_copy(VoxelMap::block_to_voxel(block_pos) - Vector3i(1, 1, 1), nbuffer);
// TEST
//if (block_pos == Vector3i(0, 0, 0)) {
// printf(">>>\n");
// String os;
// for (unsigned int y = 0; y < nbuffer.get_size().y; ++y) {
// for (unsigned int z = 0; z < nbuffer.get_size().z; ++z) {
// for (unsigned int x = 0; x < nbuffer.get_size().x; ++x) {
// if (nbuffer.get_voxel(x, y, z) == 0)
// os += '-';
// else
// os += 'O';
// }
// os += '\n';
// }
// os += '\n';
// }
// wprintf(os.c_str());
//}
// Build mesh (that part is the most CPU-intensive)
Ref<Mesh> mesh = _mesher->build(nbuffer);
MeshInstance * mesh_instance = block_ref->get_mesh_instance(*this);
if (mesh_instance == NULL) {
// Create and spawn mesh
mesh_instance = memnew(MeshInstance);
mesh_instance->set_mesh(mesh);
mesh_instance->set_translation(VoxelMap::block_to_voxel(block_pos).to_vec3());
add_child(mesh_instance);
block_ref->mesh_instance_path = mesh_instance->get_path();
}
else {
// Update mesh
mesh_instance->set_mesh(mesh);
}
}
//void VoxelTerrain::block_removed(VoxelBlock & block) {
// MeshInstance * mesh_instance = block.get_mesh_instance(*this);
// if (mesh_instance) {
// mesh_instance->queue_delete();
// }
//}
void VoxelTerrain::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_block_update_count"), &VoxelTerrain::get_block_update_count);
ObjectTypeDB::bind_method(_MD("get_mesher:VoxelMesher"), &VoxelTerrain::get_mesher);
// TODO Make those two static in VoxelMap?
ObjectTypeDB::bind_method(_MD("voxel_to_block", "voxel_pos"), &VoxelTerrain::_voxel_to_block_binding);
ObjectTypeDB::bind_method(_MD("block_to_voxel", "block_pos"), &VoxelTerrain::_block_to_voxel_binding);
ObjectTypeDB::bind_method(_MD("force_load_blocks", "center", "extents"), &VoxelTerrain::_force_load_blocks_binding);
}

52
voxel_terrain.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef VOXEL_TERRAIN_H
#define VOXEL_TERRAIN_H
#include <scene/main/node.h>
#include "voxel_map.h"
#include "voxel_mesher.h"
// Infinite static terrain made of voxels.
// It is loaded around VoxelTerrainStreamers.
class VoxelTerrain : public Node /*, public IVoxelMapObserver*/ {
OBJ_TYPE(VoxelTerrain, Node)
// Parameters
int _min_y; // In blocks, not voxels
int _max_y;
// Voxel storage
Ref<VoxelMap> _map;
Vector<Vector3i> _block_update_queue;
Ref<VoxelMesher> _mesher;
public:
VoxelTerrain();
void force_load_blocks(Vector3i center, Vector3i extents);
int get_block_update_count();
Ref<VoxelMesher> get_mesher() { return _mesher; }
protected:
void _notification(int p_what);
void _process();
void update_blocks();
void update_block_mesh(Vector3i block_pos);
// Observer events
//void block_removed(VoxelBlock & block);
static void _bind_methods();
// Convenience
Vector3 _voxel_to_block_binding(Vector3 pos) { return Vector3i(VoxelMap::voxel_to_block(pos)).to_vec3(); }
Vector3 _block_to_voxel_binding(Vector3 pos) { return Vector3i(VoxelMap::block_to_voxel(pos)).to_vec3(); }
void _force_load_blocks_binding(Vector3 center, Vector3 extents) { force_load_blocks(center, extents); }
};
#endif // VOXEL_TERRAIN_H