2016-05-09 16:59:54 -07:00
|
|
|
#include "voxel_terrain.h"
|
|
|
|
#include <scene/3d/mesh_instance.h>
|
|
|
|
#include <os/os.h>
|
|
|
|
|
|
|
|
VoxelTerrain::VoxelTerrain(): Node(), _min_y(-4), _max_y(4) {
|
|
|
|
|
2016-12-31 19:40:16 -08:00
|
|
|
_map = Ref<VoxelMap>(memnew(VoxelMap));
|
|
|
|
_mesher = Ref<VoxelMesher>(memnew(VoxelMesher));
|
2016-05-09 16:59:54 -07:00
|
|
|
}
|
|
|
|
|
2017-01-01 17:15:57 -08:00
|
|
|
// Sorts distance to world origin
|
|
|
|
// TODO Use distance to camera
|
2016-05-09 16:59:54 -07:00
|
|
|
struct BlockUpdateComparator0 {
|
|
|
|
inline bool operator()(const Vector3i & a, const Vector3i & b) const {
|
2016-12-31 19:40:16 -08:00
|
|
|
return a.length_sq() > b.length_sq();
|
|
|
|
}
|
2016-05-09 16:59:54 -07:00
|
|
|
};
|
|
|
|
|
2017-01-01 17:15:57 -08:00
|
|
|
void VoxelTerrain::set_provider(Ref<VoxelProvider> provider) {
|
|
|
|
_provider = provider;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ref<VoxelProvider> VoxelTerrain::get_provider() {
|
|
|
|
return _provider;
|
|
|
|
}
|
|
|
|
|
2016-05-09 16:59:54 -07:00
|
|
|
void VoxelTerrain::force_load_blocks(Vector3i center, Vector3i extents) {
|
2016-12-31 19:40:16 -08:00
|
|
|
//Vector3i min = center - extents;
|
|
|
|
//Vector3i max = center + extents + Vector3i(1,1,1);
|
|
|
|
//Vector3i size = max - min;
|
2016-05-09 16:59:54 -07:00
|
|
|
|
2016-12-31 19:40:16 -08:00
|
|
|
_block_update_queue.clear();
|
2016-05-09 16:59:54 -07:00
|
|
|
|
2016-12-31 19:40:16 -08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-05-09 16:59:54 -07:00
|
|
|
|
2016-12-31 19:40:16 -08:00
|
|
|
_block_update_queue.sort_custom<BlockUpdateComparator0>();
|
2016-05-09 16:59:54 -07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int VoxelTerrain::get_block_update_count() {
|
2016-12-31 19:40:16 -08:00
|
|
|
return _block_update_queue.size();
|
2016-05-09 16:59:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelTerrain::_notification(int p_what) {
|
|
|
|
|
2016-12-31 19:40:16 -08:00
|
|
|
switch (p_what) {
|
2016-05-09 16:59:54 -07:00
|
|
|
|
2016-12-31 19:40:16 -08:00
|
|
|
case NOTIFICATION_ENTER_TREE:
|
|
|
|
set_process(true);
|
|
|
|
break;
|
2016-05-09 16:59:54 -07:00
|
|
|
|
2016-12-31 19:40:16 -08:00
|
|
|
case NOTIFICATION_PROCESS:
|
|
|
|
_process();
|
|
|
|
break;
|
2016-05-09 16:59:54 -07:00
|
|
|
|
2016-12-31 19:40:16 -08:00
|
|
|
case NOTIFICATION_EXIT_TREE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2016-05-09 16:59:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelTerrain::_process() {
|
2016-12-31 19:40:16 -08:00
|
|
|
update_blocks();
|
2016-05-09 16:59:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelTerrain::update_blocks() {
|
2016-12-31 19:40:16 -08:00
|
|
|
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)) {
|
|
|
|
|
|
|
|
// Create buffer
|
|
|
|
Ref<VoxelBuffer> buffer_ref = Ref<VoxelBuffer>(memnew(VoxelBuffer));
|
|
|
|
const Vector3i block_size(VoxelBlock::SIZE, VoxelBlock::SIZE, VoxelBlock::SIZE);
|
2017-01-01 17:15:57 -08:00
|
|
|
buffer_ref->create(block_size.x, block_size.y, block_size.z);
|
2016-12-31 19:40:16 -08:00
|
|
|
|
2017-01-01 17:15:57 -08:00
|
|
|
// Query voxel provider
|
|
|
|
if(!_provider.is_null()) {
|
|
|
|
_provider->emerge_block(buffer_ref, block_pos);
|
|
|
|
}
|
2016-12-31 19:40:16 -08:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
2016-05-09 16:59:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelTerrain::update_block_mesh(Vector3i block_pos) {
|
2017-01-02 17:50:19 -08:00
|
|
|
VoxelBlock * block = _map->get_block(block_pos);
|
|
|
|
if (block == NULL) {
|
2016-12-31 19:40:16 -08:00
|
|
|
return;
|
|
|
|
}
|
2017-01-02 17:50:19 -08:00
|
|
|
if (block->voxels->is_uniform(0) && block->voxels->get_voxel(0, 0, 0, 0) == 0) {
|
2016-12-31 19:40:16 -08:00
|
|
|
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);
|
|
|
|
|
2017-01-02 17:50:19 -08:00
|
|
|
MeshInstance * mesh_instance = block->get_mesh_instance(*this);
|
2016-12-31 19:40:16 -08:00
|
|
|
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);
|
2017-01-02 17:50:19 -08:00
|
|
|
block->mesh_instance_path = mesh_instance->get_path();
|
2016-12-31 19:40:16 -08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Update mesh
|
|
|
|
mesh_instance->set_mesh(mesh);
|
|
|
|
}
|
2016-05-09 16:59:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
//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() {
|
|
|
|
|
2017-01-01 17:15:57 -08:00
|
|
|
ObjectTypeDB::bind_method(_MD("set_provider", "provider:VoxelProvider"), &VoxelTerrain::set_provider);
|
|
|
|
ObjectTypeDB::bind_method(_MD("get_provider:VoxelProvider"), &VoxelTerrain::get_provider);
|
|
|
|
|
2016-12-31 19:40:16 -08:00
|
|
|
ObjectTypeDB::bind_method(_MD("get_block_update_count"), &VoxelTerrain::get_block_update_count);
|
|
|
|
ObjectTypeDB::bind_method(_MD("get_mesher:VoxelMesher"), &VoxelTerrain::get_mesher);
|
2016-05-09 16:59:54 -07:00
|
|
|
|
2016-12-31 19:40:16 -08:00
|
|
|
// 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);
|
2016-05-09 16:59:54 -07:00
|
|
|
|
2016-12-31 19:40:16 -08:00
|
|
|
ObjectTypeDB::bind_method(_MD("force_load_blocks", "center", "extents"), &VoxelTerrain::_force_load_blocks_binding);
|
2016-05-09 16:59:54 -07:00
|
|
|
|
|
|
|
}
|
|
|
|
|