Make it easier to edit voxels one by one + a few fixes for that

master
Marc Gilleron 2017-03-29 22:36:42 +02:00
parent 4a24e28fce
commit 9cce2aabcb
5 changed files with 99 additions and 37 deletions

View File

@ -1,9 +1,6 @@
#include "voxel_buffer.h"
#include <string.h>
//#define VOXEL_AT(_data, x, y, z) data[z][x][y]
#define VOXEL_AT(_data, _x, _y, _z) _data[index(_x,_y,_z)]
VoxelBuffer::VoxelBuffer() {
@ -59,7 +56,7 @@ int VoxelBuffer::get_voxel(int x, int y, int z, unsigned int channel_index) cons
const Channel & channel = _channels[channel_index];
if (validate_pos(x, y, z) && channel.data) {
return VOXEL_AT(channel.data, x,y,z);
return channel.data[index(x,y,z)];
}
else {
return channel.defval;
@ -72,11 +69,14 @@ void VoxelBuffer::set_voxel(int value, int x, int y, int z, unsigned int channel
Channel & channel = _channels[channel_index];
if (channel.defval != value) {
if (channel.data == NULL) {
if (channel.data == NULL) {
if (channel.defval != value) {
create_channel(channel_index, _size);
channel.data[index(x, y, z)] = value;
}
VOXEL_AT(channel.data, x, y, z) = value;
}
else {
channel.data[index(x, y, z)] = value;
}
}
@ -170,6 +170,7 @@ void VoxelBuffer::copy_from(const VoxelBuffer & other, unsigned int channel_inde
}
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];

View File

@ -60,7 +60,7 @@ int VoxelMap::get_voxel(Vector3i pos, unsigned int c) {
if (block == NULL) {
return _default_voxel[c];
}
return block->voxels->get_voxel(pos - block_to_voxel(bpos), c);
return block->voxels->get_voxel(VoxelMap::to_local(pos), c);
}
void VoxelMap::set_voxel(int value, Vector3i pos, unsigned int c) {
@ -79,7 +79,7 @@ void VoxelMap::set_voxel(int value, Vector3i pos, unsigned int c) {
set_block(bpos, block);
}
block->voxels->set_voxel(value, pos - block_to_voxel(bpos), c);
block->voxels->set_voxel(value, VoxelMap::to_local(pos), c);
}
void VoxelMap::set_default_voxel(int value, unsigned int channel) {
@ -253,6 +253,8 @@ void VoxelMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_voxel", "x", "y", "z", "c"), &VoxelMap::_get_voxel_binding, DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_voxel", "value", "x", "y", "z", "c"), &VoxelMap::_set_voxel_binding, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_voxel_v", "pos", "c"), &VoxelMap::_get_voxel_v_binding, DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_voxel_v", "value", "pos", "c"), &VoxelMap::_set_voxel_v_binding, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_default_voxel", "channel"), &VoxelMap::get_default_voxel, DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_default_voxel", "value", "channel"), &VoxelMap::set_default_voxel, DEFVAL(0));
ClassDB::bind_method(D_METHOD("has_block", "x", "y", "z"), &VoxelMap::_has_block_binding);

View File

@ -13,6 +13,7 @@ class VoxelBlock {
public:
static const int SIZE_POW2 = 4; // 3=>8, 4=>16, 5=>32...
static const int SIZE = 1 << SIZE_POW2;
static const int SIZE_MASK = 0xf;
Ref<VoxelBuffer> voxels; // SIZE*SIZE*SIZE voxels
Vector3i pos;
@ -43,6 +44,14 @@ public:
);
}
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
);
}
// Converts block coodinates into voxel coordinates
static _FORCE_INLINE_ Vector3i block_to_voxel(Vector3i bpos) {
return bpos * VoxelBlock::SIZE;
@ -72,6 +81,7 @@ public:
void clear();
private:
void set_block(Vector3i bpos, VoxelBlock * block);
@ -81,6 +91,8 @@ private:
_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_ int _get_voxel_v_binding(Vector3 pos, unsigned int c = 0) { return get_voxel(Vector3i(pos), c); }
_FORCE_INLINE_ void _set_voxel_v_binding(int value, Vector3 pos, unsigned int c = 0) { set_voxel(value, Vector3i(pos), 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(); }

View File

@ -59,12 +59,18 @@ Spatial * VoxelTerrain::get_viewer(NodePath path) {
//}
void VoxelTerrain::make_block_dirty(Vector3i bpos) {
if(_dirty_blocks.has(bpos) == false) {
// TODO Immediate update viewer distance
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;
}
}
bool VoxelTerrain::is_block_dirty(Vector3i bpos) {
return _dirty_blocks.has(bpos);
}
void VoxelTerrain::make_blocks_dirty(Vector3i min, Vector3i size) {
Vector3i max = min + size;
Vector3i pos;
@ -77,6 +83,31 @@ void VoxelTerrain::make_blocks_dirty(Vector3i min, Vector3i size) {
}
}
void VoxelTerrain::make_voxel_dirty(Vector3i pos) {
// Update the block in which the voxel is
Vector3i bpos = VoxelMap::voxel_to_block(pos);
make_block_dirty(bpos);
// Update neighbor blocks if the voxel is touching a boundary
Vector3i rpos = VoxelMap::to_local(pos);
if(rpos.x == 0)
make_block_dirty(bpos - Vector3i(1,0,0));
if(rpos.y == 0)
make_block_dirty(bpos - Vector3i(0,1,0));
if(rpos.z == 0)
make_block_dirty(bpos - Vector3i(0,0,1));
if(rpos.x == VoxelBlock::SIZE-1)
make_block_dirty(bpos + Vector3i(1,0,0));
if(rpos.y == VoxelBlock::SIZE-1)
make_block_dirty(bpos + Vector3i(0,1,0));
if(rpos.z == VoxelBlock::SIZE-1)
make_block_dirty(bpos + Vector3i(0,0,1));
}
int VoxelTerrain::get_block_update_count() {
return _block_update_queue.size();
}
@ -125,6 +156,7 @@ void VoxelTerrain::update_blocks() {
while (!_block_update_queue.empty() && (os.get_ticks_msec() - time_before) < max_time) {
//printf("Remaining: %i\n", _block_update_queue.size());
//float time_before = os.get_ticks_usec();
// TODO Move this to a thread
// TODO Have VoxelTerrainGenerator in C++
@ -132,6 +164,8 @@ void VoxelTerrain::update_blocks() {
// Get request
Vector3i block_pos = _block_update_queue[_block_update_queue.size() - 1];
bool entire_block_changed = false;
if (!_map->has_block(block_pos)) {
// Create buffer
if(!_provider.is_null()) {
@ -147,23 +181,33 @@ void VoxelTerrain::update_blocks() {
// Store buffer
_map->set_block_buffer(block_pos, buffer_ref);
entire_block_changed = true;
}
}
// 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;
// TODO What if the map is really composed of empty blocks?
if (_map->is_block_surrounded(npos)) {
update_block_mesh(npos);
// Update views (mesh/collisions)
if(entire_block_changed) {
// All neighbors have to be checked
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;
// TODO What if the map is really composed of empty blocks?
if (_map->is_block_surrounded(npos)) {
update_block_mesh(npos);
}
}
}
}
}
//update_block_mesh(block_pos);
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);
}
// Pop request
_block_update_queue.resize(_block_update_queue.size() - 1);
@ -176,7 +220,19 @@ void VoxelTerrain::update_block_mesh(Vector3i block_pos) {
if (block == NULL) {
return;
}
if (block->voxels->is_uniform(0) && block->voxels->get_voxel(0, 0, 0, 0) == 0) {
// Optimization: the block contains nothing
MeshInstance * mesh_instance = block->get_mesh_instance(*this);
if(mesh_instance) {
mesh_instance->set_mesh(Ref<Mesh>());
}
StaticBody * body = block->get_physics_body(*this);
if(body) {
body->set_shape(0, Ref<Mesh>());
}
return;
}
@ -188,10 +244,10 @@ void VoxelTerrain::update_block_mesh(Vector3i block_pos) {
Vector3 block_node_pos = VoxelMap::block_to_voxel(block_pos).to_vec3();
// Build mesh (that part is the most CPU-intensive)
// TODO Re-use existing meshes to optimize memory cost
Ref<Mesh> mesh = _mesher->build(nbuffer);
// TODO Don't use nodes! Use servers directly, it's faster
MeshInstance * mesh_instance = block->get_mesh_instance(*this);
if (mesh_instance == NULL) {
// Create and spawn mesh
@ -274,19 +330,6 @@ Variant VoxelTerrain::_raycast_binding(Vector3 origin, Vector3 direction, real_t
}
}
//void VoxelTerrain::set_voxel_immediate(Vector3 pos, int value, int c) {
// Vector3i vpos = pos;
// _map->set_voxel(value, vpos, c);
//}
//int VoxelTerrain::get_voxel(Vector3 pos, int c) {
// return _map->get_voxel(pos, c);
//}
void VoxelTerrain::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_provider", "provider:VoxelProvider"), &VoxelTerrain::set_provider);
@ -301,7 +344,7 @@ void VoxelTerrain::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_viewer"), &VoxelTerrain::get_viewer_path);
ClassDB::bind_method(D_METHOD("set_viewer", "path"), &VoxelTerrain::set_viewer_path);
ClassDB::bind_method(D_METHOD("get_map:VoxelMap"), &VoxelTerrain::get_map);
ClassDB::bind_method(D_METHOD("get_storage:VoxelMap"), &VoxelTerrain::get_map);
// TODO Make those two static in VoxelMap?
ClassDB::bind_method(D_METHOD("voxel_to_block", "voxel_pos"), &VoxelTerrain::_voxel_to_block_binding);
@ -309,6 +352,7 @@ void VoxelTerrain::_bind_methods() {
ClassDB::bind_method(D_METHOD("make_block_dirty", "pos"), &VoxelTerrain::_make_block_dirty_binding);
ClassDB::bind_method(D_METHOD("make_blocks_dirty", "min", "size"), &VoxelTerrain::_make_blocks_dirty_binding);
ClassDB::bind_method(D_METHOD("make_voxel_dirty", "pos"), &VoxelTerrain::_make_voxel_dirty_binding);
ClassDB::bind_method(D_METHOD("raycast:Dictionary", "origin", "direction", "max_distance"), &VoxelTerrain::_raycast_binding, DEFVAL(100));

View File

@ -25,6 +25,8 @@ public:
void make_block_dirty(Vector3i bpos);
void make_blocks_dirty(Vector3i min, Vector3i size);
void make_voxel_dirty(Vector3i pos);
bool is_block_dirty(Vector3i bpos);
void set_generate_collisions(bool enabled);
bool get_generate_collisions() { return _generate_collisions; }
@ -58,11 +60,12 @@ private:
void _force_load_blocks_binding(Vector3 center, Vector3 extents) { force_load_blocks(center, extents); }
void _make_block_dirty_binding(Vector3 bpos) { make_block_dirty(bpos); }
void _make_blocks_dirty_binding(Vector3 min, Vector3 size) { make_blocks_dirty(min, size); }
void _make_voxel_dirty_binding(Vector3 pos) { make_voxel_dirty(pos); }
Variant _raycast_binding(Vector3 origin, Vector3 direction, real_t max_distance);
// void set_voxel(Vector3 pos, int value, int c);
// int get_voxel(Vector3 pos, int c);
void set_voxel(Vector3 pos, int value, int c);
int get_voxel(Vector3 pos, int c);
private:
// Parameters