Make it easier to edit voxels one by one + a few fixes for that
This commit is contained in:
parent
4a24e28fce
commit
9cce2aabcb
@ -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];
|
||||
|
@ -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);
|
||||
|
12
voxel_map.h
12
voxel_map.h
@ -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(); }
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user