Switch VoxelLodTerrain to VoxelServer
parent
7a37770e84
commit
b8c97ebc55
|
@ -23,9 +23,11 @@
|
|||
#include "terrain/voxel_lod_terrain.h"
|
||||
#include "terrain/voxel_map.h"
|
||||
#include "terrain/voxel_terrain.h"
|
||||
#include "util/macros.h"
|
||||
#include "voxel_buffer.h"
|
||||
#include "voxel_memory_pool.h"
|
||||
#include "voxel_string_names.h"
|
||||
#include <core/engine.h>
|
||||
|
||||
void register_voxel_types() {
|
||||
VoxelMemoryPool::create_singleton();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "../meshers/transvoxel/voxel_mesher_transvoxel.h"
|
||||
#include "../util/macros.h"
|
||||
#include "../util/profiling.h"
|
||||
#include "../voxel_constants.h"
|
||||
#include <core/os/memory.h>
|
||||
#include <scene/main/viewport.h>
|
||||
|
||||
|
@ -103,7 +104,7 @@ void VoxelServer::wait_and_clear_all_tasks(bool warn) {
|
|||
});
|
||||
}
|
||||
|
||||
int VoxelServer::get_priority(const BlockRequestPriorityDependency &dep) {
|
||||
int VoxelServer::get_priority(const BlockRequestPriorityDependency &dep, uint8_t lod) {
|
||||
const std::vector<Vector3> &viewer_positions = dep.viewers->positions;
|
||||
const Vector3 block_position = dep.world_position;
|
||||
|
||||
|
@ -120,7 +121,13 @@ int VoxelServer::get_priority(const BlockRequestPriorityDependency &dep) {
|
|||
}
|
||||
}
|
||||
|
||||
return static_cast<int>(closest_distance_sq);
|
||||
int priority = static_cast<int>(closest_distance_sq);
|
||||
|
||||
// Higher lod indexes come first to allow the octree to subdivide.
|
||||
// Then comes distance, which is modified by how much in view the block is
|
||||
priority += (VoxelConstants::MAX_LOD - lod) * 10000;
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
uint32_t VoxelServer::add_volume(ReceptionBuffers *buffers) {
|
||||
|
@ -174,6 +181,10 @@ void VoxelServer::invalidate_volume_mesh_requests(uint32_t volume_id) {
|
|||
volume.meshing_dependency->library = volume.voxel_library;
|
||||
}
|
||||
|
||||
static inline Vector3i get_block_center(Vector3i pos, int bs, int lod) {
|
||||
return (pos << lod) * bs + Vector3i(bs / 2);
|
||||
}
|
||||
|
||||
void VoxelServer::request_block_mesh(uint32_t volume_id, BlockMeshInput &input) {
|
||||
const Volume &volume = _world.volumes.get(volume_id);
|
||||
ERR_FAIL_COND(volume.stream.is_null());
|
||||
|
@ -196,7 +207,7 @@ void VoxelServer::request_block_mesh(uint32_t volume_id, BlockMeshInput &input)
|
|||
|
||||
r->meshing_dependency = volume.meshing_dependency;
|
||||
|
||||
const Vector3i voxel_pos = (input.position << input.lod) * volume.block_size;
|
||||
const Vector3i voxel_pos = get_block_center(input.position, volume.block_size, input.lod);
|
||||
r->priority_dependency.world_position = volume.transform.xform(voxel_pos.to_vec3());
|
||||
r->priority_dependency.viewers = _world.viewers_for_priority;
|
||||
|
||||
|
@ -218,7 +229,7 @@ void VoxelServer::request_block_load(uint32_t volume_id, Vector3i block_pos, int
|
|||
|
||||
r.stream_dependency = volume.stream_dependency;
|
||||
|
||||
const Vector3i voxel_pos = (block_pos << lod) * volume.block_size;
|
||||
const Vector3i voxel_pos = get_block_center(block_pos, volume.block_size, lod);
|
||||
r.priority_dependency.world_position = volume.transform.xform(voxel_pos.to_vec3());
|
||||
r.priority_dependency.viewers = _world.viewers_for_priority;
|
||||
|
||||
|
@ -297,6 +308,7 @@ void VoxelServer::process() {
|
|||
Volume *volume = _world.volumes.try_get(r->volume_id);
|
||||
|
||||
if (volume != nullptr) {
|
||||
// TODO Comparing pointer may not be guaranteed
|
||||
// The request response must match the dependency it would have been requested with.
|
||||
// If it doesn't match, we are no longer interested in the result.
|
||||
if (r->stream_dependency == volume->stream_dependency) {
|
||||
|
@ -336,6 +348,7 @@ void VoxelServer::process() {
|
|||
Volume *volume = _world.volumes.try_get(r->volume_id);
|
||||
|
||||
if (volume != nullptr) {
|
||||
// TODO Comparing pointer may not be guaranteed
|
||||
// The request response must match the dependency it would have been requested with.
|
||||
// If it doesn't match, we are no longer interested in the result.
|
||||
if (volume->meshing_dependency == r->meshing_dependency) {
|
||||
|
@ -443,11 +456,13 @@ void VoxelServer::BlockDataRequest::run(VoxelTaskContext ctx) {
|
|||
Ref<VoxelStream> stream = stream_dependency->streams[ctx.thread_index];
|
||||
CRASH_COND(stream.is_null());
|
||||
|
||||
const Vector3i origin_in_voxels = (position << lod) * block_size;
|
||||
|
||||
switch (type) {
|
||||
case TYPE_LOAD:
|
||||
voxels.instance();
|
||||
voxels->create(block_size, block_size, block_size);
|
||||
stream->emerge_block(voxels, position * block_size, lod);
|
||||
stream->emerge_block(voxels, origin_in_voxels, lod);
|
||||
break;
|
||||
|
||||
case TYPE_SAVE: {
|
||||
|
@ -457,7 +472,7 @@ void VoxelServer::BlockDataRequest::run(VoxelTaskContext ctx) {
|
|||
voxels_copy = voxels->duplicate(true);
|
||||
}
|
||||
voxels.unref();
|
||||
stream->immerge_block(voxels_copy, position * block_size, lod);
|
||||
stream->immerge_block(voxels_copy, origin_in_voxels, lod);
|
||||
} break;
|
||||
|
||||
default:
|
||||
|
@ -468,7 +483,7 @@ void VoxelServer::BlockDataRequest::run(VoxelTaskContext ctx) {
|
|||
}
|
||||
|
||||
int VoxelServer::BlockDataRequest::get_priority() {
|
||||
return type == TYPE_LOAD ? VoxelServer::get_priority(priority_dependency) : 0;
|
||||
return type == TYPE_LOAD ? VoxelServer::get_priority(priority_dependency, lod) : 0;
|
||||
}
|
||||
|
||||
bool VoxelServer::BlockDataRequest::is_cancelled() {
|
||||
|
@ -560,7 +575,7 @@ void VoxelServer::BlockMeshRequest::run(VoxelTaskContext ctx) {
|
|||
}
|
||||
|
||||
int VoxelServer::BlockMeshRequest::get_priority() {
|
||||
return VoxelServer::get_priority(priority_dependency);
|
||||
return VoxelServer::get_priority(priority_dependency, lod);
|
||||
}
|
||||
|
||||
bool VoxelServer::BlockMeshRequest::is_cancelled() {
|
||||
|
@ -569,15 +584,27 @@ bool VoxelServer::BlockMeshRequest::is_cancelled() {
|
|||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
bool g_updater_created = false;
|
||||
}
|
||||
|
||||
VoxelServerUpdater::VoxelServerUpdater() {
|
||||
PRINT_VERBOSE("Creating VoxelServerUpdater");
|
||||
set_process(true);
|
||||
g_updater_created = true;
|
||||
}
|
||||
|
||||
VoxelServerUpdater::~VoxelServerUpdater() {
|
||||
g_updater_created = false;
|
||||
}
|
||||
|
||||
void VoxelServerUpdater::ensure_existence(SceneTree *st) {
|
||||
if (st == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (g_updater_created) {
|
||||
return;
|
||||
}
|
||||
Viewport *root = st->get_root();
|
||||
for (int i = 0; i < root->get_child_count(); ++i) {
|
||||
VoxelServerUpdater *u = Object::cast_to<VoxelServerUpdater>(root->get_child(i));
|
||||
|
|
|
@ -147,7 +147,7 @@ private:
|
|||
Vector3 world_position; // TODO Won't update while in queue. Can it be bad?
|
||||
};
|
||||
|
||||
static int get_priority(const BlockRequestPriorityDependency &dep);
|
||||
static int get_priority(const BlockRequestPriorityDependency &dep, uint8_t lod);
|
||||
|
||||
class BlockDataRequest : public IVoxelTask {
|
||||
public:
|
||||
|
@ -210,6 +210,7 @@ private:
|
|||
class VoxelServerUpdater : public Node {
|
||||
GDCLASS(VoxelServerUpdater, Node)
|
||||
public:
|
||||
~VoxelServerUpdater();
|
||||
static void ensure_existence(SceneTree *st);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -89,7 +89,7 @@ private:
|
|||
Ref<Material> _debug_transition_material;
|
||||
#endif
|
||||
|
||||
int _mesh_update_count = 0;
|
||||
int _mesh_update_count = 0; // TODO Never really used?
|
||||
bool _visible = true;
|
||||
bool _parent_visible = true;
|
||||
MeshState _mesh_state = MESH_NEVER_UPDATED;
|
||||
|
|
|
@ -56,6 +56,8 @@ VoxelLodTerrain::VoxelLodTerrain() {
|
|||
|
||||
PRINT_VERBOSE("Construct VoxelLodTerrain");
|
||||
|
||||
_volume_id = VoxelServer::get_singleton()->add_volume(&_reception_buffers);
|
||||
|
||||
_lods[0].map.instance();
|
||||
|
||||
// TODO Being able to set a LOD smaller than the stream is probably a bad idea,
|
||||
|
@ -67,17 +69,13 @@ VoxelLodTerrain::VoxelLodTerrain() {
|
|||
VoxelLodTerrain::~VoxelLodTerrain() {
|
||||
PRINT_VERBOSE("Destroy VoxelLodTerrain");
|
||||
|
||||
if (_stream_thread) {
|
||||
if (_stream.is_valid()) {
|
||||
// Schedule saving of all modified blocks,
|
||||
// without copy because we are destroying the map anyways
|
||||
save_all_modified_blocks(false);
|
||||
|
||||
memdelete(_stream_thread);
|
||||
}
|
||||
|
||||
if (_block_updater) {
|
||||
memdelete(_block_updater);
|
||||
}
|
||||
VoxelServer::get_singleton()->remove_volume(_volume_id);
|
||||
}
|
||||
|
||||
String VoxelLodTerrain::get_configuration_warning() const {
|
||||
|
@ -145,8 +143,6 @@ void VoxelLodTerrain::set_stream(Ref<VoxelStream> p_stream) {
|
|||
|
||||
void VoxelLodTerrain::_on_stream_params_changed() {
|
||||
stop_streamer();
|
||||
|
||||
const bool was_updater_running = _block_updater != nullptr;
|
||||
stop_updater();
|
||||
|
||||
Ref<VoxelStreamFile> file_stream = _stream;
|
||||
|
@ -158,12 +154,12 @@ void VoxelLodTerrain::_on_stream_params_changed() {
|
|||
_set_lod_count(min(stream_lod_count, get_lod_count()));
|
||||
}
|
||||
|
||||
VoxelServer::get_singleton()->set_volume_block_size(_volume_id, get_block_size());
|
||||
|
||||
reset_maps();
|
||||
|
||||
if (_stream.is_valid() && (!Engine::get_singleton()->is_editor_hint() || _run_stream_in_editor)) {
|
||||
start_streamer();
|
||||
}
|
||||
if (was_updater_running) {
|
||||
start_updater();
|
||||
}
|
||||
|
||||
|
@ -257,13 +253,6 @@ Spatial *VoxelLodTerrain::get_viewer() const {
|
|||
}
|
||||
|
||||
void VoxelLodTerrain::start_updater() {
|
||||
ERR_FAIL_COND(_block_updater != nullptr);
|
||||
|
||||
// TODO Thread-safe way to change those parameters
|
||||
VoxelMeshUpdater::MeshingParams params;
|
||||
params.smooth_surface = true;
|
||||
|
||||
_block_updater = memnew(VoxelMeshUpdater(2, params));
|
||||
}
|
||||
|
||||
void VoxelLodTerrain::stop_updater() {
|
||||
|
@ -275,12 +264,9 @@ void VoxelLodTerrain::stop_updater() {
|
|||
}
|
||||
};
|
||||
|
||||
if (_block_updater) {
|
||||
memdelete(_block_updater);
|
||||
_block_updater = nullptr;
|
||||
}
|
||||
VoxelServer::get_singleton()->invalidate_volume_mesh_requests(_volume_id);
|
||||
|
||||
_blocks_pending_main_thread_update.clear();
|
||||
_reception_buffers.mesh_output.clear();
|
||||
|
||||
for (unsigned int i = 0; i < _lods.size(); ++i) {
|
||||
Lod &lod = _lods[i];
|
||||
|
@ -294,23 +280,19 @@ void VoxelLodTerrain::stop_updater() {
|
|||
}
|
||||
|
||||
void VoxelLodTerrain::start_streamer() {
|
||||
ERR_FAIL_COND(_stream_thread != nullptr);
|
||||
ERR_FAIL_COND(_stream.is_null());
|
||||
|
||||
_stream_thread = memnew(VoxelDataLoader(1, _stream, get_block_size_pow2()));
|
||||
VoxelServer::get_singleton()->set_volume_stream(_volume_id, _stream);
|
||||
}
|
||||
|
||||
void VoxelLodTerrain::stop_streamer() {
|
||||
if (_stream_thread) {
|
||||
memdelete(_stream_thread);
|
||||
_stream_thread = nullptr;
|
||||
}
|
||||
VoxelServer::get_singleton()->set_volume_stream(_volume_id, Ref<VoxelStream>());
|
||||
|
||||
for (unsigned int i = 0; i < _lods.size(); ++i) {
|
||||
Lod &lod = _lods[i];
|
||||
lod.loading_blocks.clear();
|
||||
lod.blocks_to_load.clear();
|
||||
}
|
||||
|
||||
_reception_buffers.data_output.clear();
|
||||
}
|
||||
|
||||
void VoxelLodTerrain::set_lod_split_scale(float p_lod_split_scale) {
|
||||
|
@ -430,13 +412,15 @@ Vector3 VoxelLodTerrain::voxel_to_block_position(Vector3 vpos, int lod_index) co
|
|||
void VoxelLodTerrain::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
if (_block_updater == nullptr) {
|
||||
start_updater();
|
||||
}
|
||||
set_process(true);
|
||||
break;
|
||||
|
||||
case NOTIFICATION_PROCESS:
|
||||
// Can't do that in enter tree because Godot is "still setting up children".
|
||||
// Can't do that in ready either because Godot says node state is locked.
|
||||
// This hack is quite miserable.
|
||||
VoxelServerUpdater::ensure_existence(get_tree());
|
||||
|
||||
_process();
|
||||
break;
|
||||
|
||||
|
@ -574,40 +558,27 @@ bool VoxelLodTerrain::check_block_mesh_updated(VoxelBlock *block) {
|
|||
}
|
||||
|
||||
void VoxelLodTerrain::send_block_data_requests() {
|
||||
VoxelDataLoader::Input input;
|
||||
|
||||
Vector3 viewer_pos;
|
||||
get_viewer_pos_and_direction(viewer_pos, input.priority_direction);
|
||||
input.priority_position = _lods[0].map->voxel_to_block(Vector3i(viewer_pos));
|
||||
|
||||
input.use_exclusive_region = true;
|
||||
// The last LOD may spread until end of view distance, it should not be discarded
|
||||
input.exclusive_region_max_lod = get_lod_count() - 1;
|
||||
input.exclusive_region_extent = get_block_region_extent();
|
||||
|
||||
// Blocks to load
|
||||
for (int lod_index = 0; lod_index < get_lod_count(); ++lod_index) {
|
||||
Lod &lod = _lods[lod_index];
|
||||
|
||||
for (unsigned int i = 0; i < lod.blocks_to_load.size(); ++i) {
|
||||
VoxelDataLoader::InputBlock input_block;
|
||||
input_block.position = lod.blocks_to_load[i];
|
||||
input_block.lod = lod_index;
|
||||
input.blocks.push_back(input_block);
|
||||
const Vector3i block_pos = lod.blocks_to_load[i];
|
||||
VoxelServer::get_singleton()->request_block_load(_volume_id, block_pos, lod_index);
|
||||
}
|
||||
|
||||
lod.blocks_to_load.clear();
|
||||
}
|
||||
|
||||
// Blocks to save
|
||||
for (unsigned int i = 0; i < _blocks_to_save.size(); ++i) {
|
||||
PRINT_VERBOSE(String("Requesting save of block {0} lod {1}")
|
||||
.format(varray(_blocks_to_save[i].position.to_vec3(), _blocks_to_save[i].lod)));
|
||||
input.blocks.push_back(_blocks_to_save[i]);
|
||||
const BlockToSave &b = _blocks_to_save[i];
|
||||
VoxelServer::get_singleton()->request_block_save(_volume_id, b.voxels, b.position, b.lod);
|
||||
}
|
||||
|
||||
_blocks_to_save.clear();
|
||||
|
||||
//print_line(String("Sending {0}").format(varray(input.blocks_to_emerge.size())));
|
||||
_stream_thread->push(input);
|
||||
}
|
||||
|
||||
void VoxelLodTerrain::_process() {
|
||||
|
@ -618,8 +589,6 @@ void VoxelLodTerrain::_process() {
|
|||
return;
|
||||
}
|
||||
|
||||
OS &os = *OS::get_singleton();
|
||||
|
||||
// Get viewer location
|
||||
// TODO Transform to local (Spatial Transform)
|
||||
Vector3 viewer_pos;
|
||||
|
@ -943,7 +912,7 @@ void VoxelLodTerrain::_process() {
|
|||
_stats.time_detect_required_blocks = profiling_clock.restart();
|
||||
|
||||
// It's possible the user didn't set a stream yet, or it is turned off
|
||||
if (_stream_thread != nullptr) {
|
||||
if (_stream.is_valid() && (Engine::get_singleton()->is_editor_hint() == false || _run_stream_in_editor)) {
|
||||
send_block_data_requests();
|
||||
}
|
||||
|
||||
|
@ -952,25 +921,19 @@ void VoxelLodTerrain::_process() {
|
|||
// Get block loading responses
|
||||
// Note: if block loading is too fast, this can cause stutters.
|
||||
// It should only happen on first load, though.
|
||||
if (_stream_thread != nullptr) {
|
||||
{
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
VoxelDataLoader::Output output;
|
||||
_stream_thread->pop(output);
|
||||
_stats.stream = output.stats;
|
||||
|
||||
//print_line(String("Loaded {0} blocks").format(varray(output.emerged_blocks.size())));
|
||||
|
||||
for (int i = 0; i < output.blocks.size(); ++i) {
|
||||
|
||||
for (size_t i = 0; i < _reception_buffers.data_output.size(); ++i) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
const VoxelServer::BlockDataOutput &ob = _reception_buffers.data_output[i];
|
||||
|
||||
const VoxelDataLoader::OutputBlock &ob = output.blocks[i];
|
||||
|
||||
if (ob.data.type == VoxelDataLoader::TYPE_SAVE) {
|
||||
if (ob.type == VoxelServer::BlockDataOutput::TYPE_SAVE) {
|
||||
// That's a save confirmation event.
|
||||
// Note: in the future, if blocks don't get copied before being sent for saving,
|
||||
// we will need to use block versionning to know when we can reset the `modified` flag properly
|
||||
|
||||
// TODO Now that's the case. Use version? Or just keep copying?
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -993,7 +956,7 @@ void VoxelLodTerrain::_process() {
|
|||
lod.loading_blocks.erase(E);
|
||||
}
|
||||
|
||||
if (ob.drop_hint) {
|
||||
if (ob.dropped) {
|
||||
// That block was dropped by the data loader thread, but we were still expecting it...
|
||||
// This is most likely caused by the loader not keeping up with the speed at which the player is moving.
|
||||
// We should recover with the removal from `loading_blocks` so it will be re-queried again later...
|
||||
|
@ -1005,7 +968,7 @@ void VoxelLodTerrain::_process() {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (ob.data.voxels_loaded->get_size() != Vector3i(lod.map->get_block_size())) {
|
||||
if (ob.voxels->get_size() != Vector3i(lod.map->get_block_size())) {
|
||||
// Voxel block size is incorrect, drop it
|
||||
ERR_PRINT("Block size obtained from stream is different from expected size");
|
||||
++_stats.dropped_block_loads;
|
||||
|
@ -1013,7 +976,7 @@ void VoxelLodTerrain::_process() {
|
|||
}
|
||||
|
||||
// Store buffer
|
||||
VoxelBlock *block = lod.map->set_block_buffer(ob.position, ob.data.voxels_loaded);
|
||||
VoxelBlock *block = lod.map->set_block_buffer(ob.position, ob.voxels);
|
||||
//print_line(String("Adding block {0} at lod {1}").format(varray(eo.block_position.to_vec3(), eo.lod)));
|
||||
// The block will be made visible and meshed only by LodOctree
|
||||
block->set_visible(false);
|
||||
|
@ -1051,100 +1014,61 @@ void VoxelLodTerrain::_process() {
|
|||
{
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
VoxelMeshUpdater::Input input;
|
||||
input.priority_position = viewer_block_pos;
|
||||
input.priority_direction = viewer_direction;
|
||||
input.use_exclusive_region = true;
|
||||
input.exclusive_region_max_lod = get_lod_count() - 1;
|
||||
input.exclusive_region_extent = get_block_region_extent();
|
||||
|
||||
for (int lod_index = 0; lod_index < get_lod_count(); ++lod_index) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
Lod &lod = _lods[lod_index];
|
||||
|
||||
for (unsigned int i = 0; i < lod.blocks_pending_update.size(); ++i) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
Vector3i block_pos = lod.blocks_pending_update[i];
|
||||
const Vector3i block_pos = lod.blocks_pending_update[i];
|
||||
|
||||
VoxelBlock *block = lod.map->get_block(block_pos);
|
||||
CRASH_COND(block == nullptr);
|
||||
// All blocks we get here must be in the scheduled state
|
||||
CRASH_COND(block->get_mesh_state() != VoxelBlock::MESH_UPDATE_NOT_SENT);
|
||||
|
||||
// TODO Perhaps we could do a bit of early-rejection before spending time in buffer copy?
|
||||
|
||||
// Create buffer padded with neighbor voxels
|
||||
Ref<VoxelBuffer> nbuffer;
|
||||
nbuffer.instance();
|
||||
|
||||
// TODO Make the buffer re-usable, or pool memory
|
||||
unsigned int min_padding = _block_updater->get_minimum_padding();
|
||||
unsigned int max_padding = _block_updater->get_maximum_padding();
|
||||
{
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
unsigned int block_size = lod.map->get_block_size();
|
||||
nbuffer->create(Vector3i(block_size + min_padding + max_padding));
|
||||
// Get block and its neighbors
|
||||
VoxelServer::BlockMeshInput mi;
|
||||
mi.position = block_pos;
|
||||
mi.lod = lod_index;
|
||||
for (unsigned int i = 0; i < Cube::MOORE_AREA_3D_COUNT; ++i) {
|
||||
const Vector3i npos = block_pos + Cube::g_ordered_moore_area_3d[i];
|
||||
VoxelBlock *nblock = lod.map->get_block(npos);
|
||||
// The block can actually be null on some occasions. Not sure yet if it's that bad
|
||||
//CRASH_COND(nblock == nullptr);
|
||||
if (nblock == nullptr) {
|
||||
continue;
|
||||
}
|
||||
mi.blocks[i] = nblock->voxels;
|
||||
}
|
||||
|
||||
{
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
unsigned int channels_mask = (1 << VoxelBuffer::CHANNEL_SDF);
|
||||
lod.map->get_buffer_copy(lod.map->block_to_voxel(block_pos) - Vector3i(min_padding), **nbuffer, channels_mask);
|
||||
}
|
||||
|
||||
VoxelMeshUpdater::InputBlock iblock;
|
||||
iblock.data.voxels = nbuffer;
|
||||
iblock.position = block_pos;
|
||||
iblock.lod = lod_index;
|
||||
input.blocks.push_back(iblock);
|
||||
VoxelServer::get_singleton()->request_block_mesh(_volume_id, mi);
|
||||
|
||||
block->set_mesh_state(VoxelBlock::MESH_UPDATE_SENT);
|
||||
}
|
||||
|
||||
lod.blocks_pending_update.clear();
|
||||
}
|
||||
|
||||
//print_line(String("Sending {0} updates").format(varray(input.blocks.size())));
|
||||
_block_updater->push(input);
|
||||
}
|
||||
|
||||
_stats.time_request_blocks_to_update = profiling_clock.restart();
|
||||
|
||||
// Receive mesh updates
|
||||
{
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
{
|
||||
VoxelMeshUpdater::Output output;
|
||||
_block_updater->pop(output);
|
||||
VOXEL_PROFILE_SCOPE_NAMED("Receive mesh updates");
|
||||
|
||||
_stats.updater = output.stats;
|
||||
_stats.updated_blocks = output.blocks.size();
|
||||
|
||||
for (int i = 0; i < output.blocks.size(); ++i) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
const VoxelMeshUpdater::OutputBlock &ob = output.blocks[i];
|
||||
|
||||
if (ob.lod >= get_lod_count()) {
|
||||
// Sorry, LOD configuration changed, drop that mesh
|
||||
++_stats.dropped_block_meshs;
|
||||
continue;
|
||||
}
|
||||
|
||||
_blocks_pending_main_thread_update.push_back(ob);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t timeout = os.get_ticks_msec() + MAIN_THREAD_MESHING_BUDGET_MS; // Allocate milliseconds max to upload meshes
|
||||
unsigned int queue_index = 0;
|
||||
// Allocate milliseconds max to upload meshes
|
||||
const OS &os = *OS::get_singleton();
|
||||
const uint32_t timeout = os.get_ticks_msec() + MAIN_THREAD_MESHING_BUDGET_MS;
|
||||
|
||||
// The following is done on the main thread because Godot doesn't really support multithreaded Mesh allocation.
|
||||
// This also proved to be very slow compared to the meshing process itself...
|
||||
// hopefully Vulkan will allow us to upload graphical resources without stalling rendering as they upload?
|
||||
|
||||
for (; queue_index < _blocks_pending_main_thread_update.size() && os.get_ticks_msec() < timeout; ++queue_index) {
|
||||
size_t queue_index = 0;
|
||||
for (; queue_index < _reception_buffers.mesh_output.size() && os.get_ticks_msec() < timeout; ++queue_index) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
const VoxelMeshUpdater::OutputBlock &ob = _blocks_pending_main_thread_update[queue_index];
|
||||
const VoxelServer::BlockMeshOutput &ob = _reception_buffers.mesh_output[queue_index];
|
||||
|
||||
if (ob.lod >= get_lod_count()) {
|
||||
// Sorry, LOD configuration changed, drop that mesh
|
||||
|
@ -1161,7 +1085,7 @@ void VoxelLodTerrain::_process() {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (ob.drop_hint) {
|
||||
if (ob.type == VoxelServer::BlockMeshOutput::TYPE_DROPPED) {
|
||||
// That block is loaded, but its meshing request was dropped.
|
||||
// TODO Not sure what to do in this case, the code sending update queries has to be tweaked
|
||||
PRINT_VERBOSE("Received a block mesh drop while we were still expecting it");
|
||||
|
@ -1173,7 +1097,7 @@ void VoxelLodTerrain::_process() {
|
|||
block->set_mesh_state(VoxelBlock::MESH_UP_TO_DATE);
|
||||
}
|
||||
|
||||
const VoxelMesher::Output mesh_data = ob.data.smooth_surfaces;
|
||||
const VoxelMesher::Output mesh_data = ob.smooth_surfaces;
|
||||
|
||||
Ref<ArrayMesh> mesh = build_mesh(
|
||||
mesh_data.surfaces,
|
||||
|
@ -1205,7 +1129,7 @@ void VoxelLodTerrain::_process() {
|
|||
|
||||
{
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
shift_up(_blocks_pending_main_thread_update, queue_index);
|
||||
shift_up(_reception_buffers.mesh_output, queue_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1302,13 +1226,14 @@ void VoxelLodTerrain::flush_pending_lod_edits() {
|
|||
|
||||
namespace {
|
||||
struct ScheduleSaveAction {
|
||||
std::vector<VoxelDataLoader::InputBlock> &blocks_to_save;
|
||||
std::vector<VoxelLodTerrain::BlockToSave> &blocks_to_save;
|
||||
std::vector<Ref<ShaderMaterial> > &shader_materials;
|
||||
bool with_copy;
|
||||
|
||||
void operator()(VoxelBlock *block) {
|
||||
Ref<ShaderMaterial> sm = block->get_shader_material();
|
||||
if (sm.is_valid()) {
|
||||
// Recycle material
|
||||
shader_materials.push_back(sm);
|
||||
block->set_shader_material(Ref<ShaderMaterial>());
|
||||
}
|
||||
|
@ -1316,17 +1241,16 @@ struct ScheduleSaveAction {
|
|||
// TODO Don't ask for save if the stream doesn't support it!
|
||||
if (block->is_modified()) {
|
||||
//print_line(String("Scheduling save for block {0}").format(varray(block->position.to_vec3())));
|
||||
VoxelDataLoader::InputBlock b;
|
||||
VoxelLodTerrain::BlockToSave b;
|
||||
|
||||
if (with_copy) {
|
||||
RWLockRead lock(block->voxels->get_lock());
|
||||
b.data.voxels_to_save = block->voxels->duplicate(true);
|
||||
b.voxels = block->voxels->duplicate(true);
|
||||
} else {
|
||||
b.data.voxels_to_save = block->voxels;
|
||||
b.voxels = block->voxels;
|
||||
}
|
||||
|
||||
b.position = block->position;
|
||||
b.can_be_discarded = false;
|
||||
b.lod = block->lod_index;
|
||||
blocks_to_save.push_back(b);
|
||||
block->set_modified(false);
|
||||
|
@ -1356,8 +1280,6 @@ void VoxelLodTerrain::immerge_block(Vector3i block_pos, int lod_index) {
|
|||
}
|
||||
|
||||
void VoxelLodTerrain::save_all_modified_blocks(bool with_copy) {
|
||||
ERR_FAIL_COND(_stream_thread == nullptr);
|
||||
|
||||
flush_pending_lod_edits();
|
||||
|
||||
for (int i = 0; i < _lod_count; ++i) {
|
||||
|
@ -1494,8 +1416,6 @@ uint8_t VoxelLodTerrain::get_transition_mask(Vector3i block_pos, int lod_index)
|
|||
|
||||
Dictionary VoxelLodTerrain::get_statistics() const {
|
||||
Dictionary d;
|
||||
d["stream"] = VoxelDataLoader::Mgr::to_dictionary(_stats.stream);
|
||||
d["updater"] = VoxelMeshUpdater::Mgr::to_dictionary(_stats.updater);
|
||||
|
||||
// Breakdown of time spent in _process
|
||||
d["time_detect_required_blocks"] = _stats.time_detect_required_blocks;
|
||||
|
@ -1504,7 +1424,7 @@ Dictionary VoxelLodTerrain::get_statistics() const {
|
|||
d["time_request_blocks_to_update"] = _stats.time_request_blocks_to_update;
|
||||
d["time_process_update_responses"] = _stats.time_process_update_responses;
|
||||
|
||||
d["remaining_main_thread_blocks"] = (int)_blocks_pending_main_thread_update.size();
|
||||
d["remaining_main_thread_blocks"] = (int)_reception_buffers.mesh_output.size();
|
||||
d["dropped_block_loads"] = _stats.dropped_block_loads;
|
||||
d["dropped_block_meshs"] = _stats.dropped_block_meshs;
|
||||
d["updated_blocks"] = _stats.updated_blocks;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#ifndef VOXEL_LOD_TERRAIN_HPP
|
||||
#define VOXEL_LOD_TERRAIN_HPP
|
||||
|
||||
#include "../server/voxel_server.h"
|
||||
#include "lod_octree.h"
|
||||
#include "voxel_data_loader.h"
|
||||
#include "voxel_mesh_updater.h"
|
||||
#include <core/set.h>
|
||||
#include <scene/3d/spatial.h>
|
||||
|
||||
|
@ -62,8 +61,6 @@ public:
|
|||
Ref<VoxelTool> get_voxel_tool();
|
||||
|
||||
struct Stats {
|
||||
VoxelMeshUpdater::Stats updater;
|
||||
VoxelDataLoader::Stats stream;
|
||||
int blocked_lods = 0;
|
||||
int updated_blocks = 0;
|
||||
int dropped_block_loads = 0;
|
||||
|
@ -82,6 +79,12 @@ public:
|
|||
|
||||
void restart_stream();
|
||||
|
||||
struct BlockToSave {
|
||||
Ref<VoxelBuffer> voxels;
|
||||
Vector3i position;
|
||||
uint8_t lod;
|
||||
};
|
||||
|
||||
Array debug_raycast_block(Vector3 world_origin, Vector3 world_direction) const;
|
||||
Dictionary debug_get_block_info(Vector3 fbpos, int lod_index) const;
|
||||
Array debug_get_octrees() const;
|
||||
|
@ -138,10 +141,9 @@ private:
|
|||
NodePath _viewer_path;
|
||||
|
||||
Ref<VoxelStream> _stream;
|
||||
VoxelDataLoader *_stream_thread = nullptr;
|
||||
VoxelMeshUpdater *_block_updater = nullptr;
|
||||
std::vector<VoxelMeshUpdater::OutputBlock> _blocks_pending_main_thread_update;
|
||||
std::vector<VoxelDataLoader::InputBlock> _blocks_to_save;
|
||||
std::vector<BlockToSave> _blocks_to_save;
|
||||
VoxelServer::ReceptionBuffers _reception_buffers;
|
||||
uint32_t _volume_id = 0;
|
||||
|
||||
// Only populated and then cleared inside _process, so lifetime of pointers should be valid
|
||||
std::vector<VoxelBlock *> _blocks_pending_transition_update;
|
||||
|
|
|
@ -607,7 +607,7 @@ void VoxelTerrain::_notification(int p_what) {
|
|||
case NOTIFICATION_PROCESS:
|
||||
// Can't do that in enter tree because Godot is "still setting up children".
|
||||
// Can't do that in ready either because Godot says node state is locked.
|
||||
// This hack is really getting miserable.
|
||||
// This hack is quite miserable.
|
||||
VoxelServerUpdater::ensure_existence(get_tree());
|
||||
|
||||
_process();
|
||||
|
|
Loading…
Reference in New Issue