2019-05-04 00:02:10 +01:00
|
|
|
#include "voxel_lod_terrain.h"
|
2022-03-20 22:57:53 +00:00
|
|
|
#include "../../constants/voxel_string_names.h"
|
|
|
|
#include "../../edition/voxel_tool_lod_terrain.h"
|
|
|
|
#include "../../meshers/transvoxel/voxel_mesher_transvoxel.h"
|
|
|
|
#include "../../server/voxel_server_gd.h"
|
|
|
|
#include "../../server/voxel_server_updater.h"
|
|
|
|
#include "../../util/funcs.h"
|
|
|
|
#include "../../util/godot/funcs.h"
|
2022-04-09 15:00:34 +01:00
|
|
|
#include "../../util/log.h"
|
2022-03-20 22:57:53 +00:00
|
|
|
#include "../../util/profiling.h"
|
|
|
|
#include "../../util/profiling_clock.h"
|
|
|
|
#include "../../util/tasks/async_dependency_tracker.h"
|
|
|
|
#include "../instancing/voxel_instancer.h"
|
2022-03-15 00:29:39 +00:00
|
|
|
#include "voxel_lod_terrain_update_task.h"
|
2020-01-17 20:43:28 +00:00
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
#include <core/config/engine.h>
|
2019-08-24 01:44:27 +01:00
|
|
|
#include <core/core_string_names.h>
|
2021-12-13 21:38:10 +00:00
|
|
|
#include <scene/3d/mesh_instance_3d.h>
|
2020-11-21 18:31:28 +00:00
|
|
|
#include <scene/resources/packed_scene.h>
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2022-01-09 22:13:10 +00:00
|
|
|
namespace zylann::voxel {
|
2022-01-03 23:14:18 +00:00
|
|
|
|
2019-12-31 16:48:46 +00:00
|
|
|
namespace {
|
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
Ref<ArrayMesh> build_mesh(
|
2022-02-06 22:26:30 +00:00
|
|
|
Span<const Array> surfaces, Mesh::PrimitiveType primitive, int flags, Ref<Material> material) {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE();
|
2019-12-31 16:48:46 +00:00
|
|
|
Ref<ArrayMesh> mesh;
|
|
|
|
|
|
|
|
unsigned int surface_index = 0;
|
2022-02-06 22:26:30 +00:00
|
|
|
for (unsigned int i = 0; i < surfaces.size(); ++i) {
|
2019-12-31 16:48:46 +00:00
|
|
|
Array surface = surfaces[i];
|
2020-10-25 21:21:37 +00:00
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
if (surface.is_empty()) {
|
2019-12-31 16:48:46 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
CRASH_COND(surface.size() != Mesh::ARRAY_MAX);
|
|
|
|
if (!is_surface_triangulated(surface)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-03-15 00:17:42 +00:00
|
|
|
if (mesh.is_null()) {
|
|
|
|
mesh.instantiate();
|
|
|
|
}
|
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
// TODO Use `add_surface`, it's about 20% faster after measuring in Tracy (though we may see if Godot 4 expects
|
|
|
|
// the same)
|
|
|
|
mesh->add_surface_from_arrays(primitive, surface, Array(), Dictionary(), flags);
|
2019-12-31 16:48:46 +00:00
|
|
|
mesh->surface_set_material(surface_index, material);
|
|
|
|
// No multi-material supported yet
|
|
|
|
++surface_index;
|
|
|
|
}
|
|
|
|
|
2021-05-03 00:13:36 +01:00
|
|
|
// Debug code to highlight vertex sharing
|
|
|
|
/*if (mesh->get_surface_count() > 0) {
|
|
|
|
Array wireframe_surface = generate_debug_seams_wireframe_surface(mesh, 0);
|
|
|
|
if (wireframe_surface.size() > 0) {
|
|
|
|
const int wireframe_surface_index = mesh->get_surface_count();
|
|
|
|
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, wireframe_surface);
|
|
|
|
Ref<SpatialMaterial> line_material;
|
|
|
|
line_material.instance();
|
|
|
|
line_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
|
|
|
|
line_material->set_albedo(Color(1.0, 0.0, 1.0));
|
|
|
|
mesh->surface_set_material(wireframe_surface_index, line_material);
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
|
2022-04-07 19:44:29 +01:00
|
|
|
if (mesh.is_valid() && is_mesh_empty(**mesh)) {
|
2019-12-31 16:48:46 +00:00
|
|
|
mesh = Ref<Mesh>();
|
|
|
|
}
|
|
|
|
|
|
|
|
return mesh;
|
|
|
|
}
|
|
|
|
|
2021-04-03 20:39:37 +01:00
|
|
|
struct BeforeUnloadMeshAction {
|
2021-05-31 17:10:54 +01:00
|
|
|
std::vector<Ref<ShaderMaterial>> &shader_material_pool;
|
2021-04-03 20:39:37 +01:00
|
|
|
|
2022-03-20 22:04:53 +00:00
|
|
|
void operator()(VoxelMeshBlockVLT &block) {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE_NAMED("Recycle material");
|
2021-04-03 20:39:37 +01:00
|
|
|
// Recycle material
|
2022-03-20 19:16:58 +00:00
|
|
|
Ref<ShaderMaterial> sm = block.get_shader_material();
|
2021-04-03 20:39:37 +01:00
|
|
|
if (sm.is_valid()) {
|
|
|
|
shader_material_pool.push_back(sm);
|
2022-03-20 19:16:58 +00:00
|
|
|
block.set_shader_material(Ref<ShaderMaterial>());
|
2021-04-03 20:39:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-12-29 22:10:31 +00:00
|
|
|
struct ScheduleSaveAction {
|
2022-03-19 19:43:33 +00:00
|
|
|
std::vector<VoxelLodTerrainUpdateData::BlockToSave> &blocks_to_save;
|
2020-12-29 22:10:31 +00:00
|
|
|
|
2022-03-20 19:28:50 +00:00
|
|
|
void operator()(VoxelDataBlock &block) {
|
2020-12-29 22:10:31 +00:00
|
|
|
// Save if modified
|
|
|
|
// TODO Don't ask for save if the stream doesn't support it!
|
2022-03-20 19:28:50 +00:00
|
|
|
if (block.is_modified()) {
|
2020-12-29 22:10:31 +00:00
|
|
|
//print_line(String("Scheduling save for block {0}").format(varray(block->position.to_vec3())));
|
2022-03-19 19:43:33 +00:00
|
|
|
VoxelLodTerrainUpdateData::BlockToSave b;
|
2020-12-29 22:10:31 +00:00
|
|
|
|
2022-04-10 20:10:33 +01:00
|
|
|
b.voxels = make_shared_instance<VoxelBufferInternal>();
|
2021-09-26 04:14:50 +01:00
|
|
|
{
|
2022-03-20 19:28:50 +00:00
|
|
|
RWLockRead lock(block.get_voxels().get_lock());
|
|
|
|
block.get_voxels_const().duplicate_to(*b.voxels, true);
|
2021-09-26 04:14:50 +01:00
|
|
|
}
|
2020-12-29 22:10:31 +00:00
|
|
|
|
2022-03-20 19:28:50 +00:00
|
|
|
b.position = block.position;
|
|
|
|
b.lod = block.lod_index;
|
2020-12-29 22:10:31 +00:00
|
|
|
blocks_to_save.push_back(b);
|
2022-03-20 19:28:50 +00:00
|
|
|
block.set_modified(false);
|
2020-12-29 22:10:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-02-18 19:50:47 +00:00
|
|
|
static inline uint64_t get_ticks_msec() {
|
2021-12-29 17:06:24 +00:00
|
|
|
return Time::get_singleton()->get_ticks_msec();
|
2021-02-18 19:50:47 +00:00
|
|
|
}
|
|
|
|
|
2019-12-31 16:48:46 +00:00
|
|
|
} // namespace
|
|
|
|
|
2020-01-07 20:32:36 +00:00
|
|
|
VoxelLodTerrain::VoxelLodTerrain() {
|
2019-08-24 01:44:27 +01:00
|
|
|
// Note: don't do anything heavy in the constructor.
|
|
|
|
// Godot may create and destroy dozens of instances of all node types on startup,
|
|
|
|
// due to how ClassDB gets its default values.
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2022-04-06 23:26:54 +01:00
|
|
|
ZN_PRINT_VERBOSE("Construct VoxelLodTerrain");
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2022-04-10 20:10:33 +01:00
|
|
|
_data = make_shared_instance<VoxelDataLodMap>();
|
|
|
|
_update_data = make_shared_instance<VoxelLodTerrainUpdateData>();
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->task_is_complete = true;
|
2022-04-10 20:10:33 +01:00
|
|
|
_streaming_dependency = make_shared_instance<StreamingDependency>();
|
|
|
|
_meshing_dependency = make_shared_instance<MeshingDependency>();
|
2021-10-13 20:28:20 +01:00
|
|
|
|
2020-10-24 03:22:02 +01:00
|
|
|
set_notify_transform(true);
|
|
|
|
|
2020-10-30 19:02:00 +00:00
|
|
|
// Doing this to setup the defaults
|
2021-12-14 00:10:09 +00:00
|
|
|
set_process_callback(_process_callback);
|
2020-10-30 19:02:00 +00:00
|
|
|
|
2020-10-22 22:43:31 +01:00
|
|
|
// Infinite by default
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->settings.bounds_in_voxels =
|
|
|
|
Box3i::from_center_extents(Vector3i(), Vector3iUtil::create(constants::MAX_VOLUME_EXTENT));
|
2020-10-22 22:43:31 +01:00
|
|
|
|
2022-01-09 22:13:10 +00:00
|
|
|
struct ApplyMeshUpdateTask : public ITimeSpreadTask {
|
2022-03-15 00:29:39 +00:00
|
|
|
void run(TimeSpreadTaskContext &ctx) override {
|
2022-04-08 23:54:04 +01:00
|
|
|
if (!VoxelServer::get_singleton().is_volume_valid(volume_id)) {
|
2021-09-25 00:02:41 +01:00
|
|
|
// The node can have been destroyed while this task was still pending
|
2022-04-06 23:26:54 +01:00
|
|
|
ZN_PRINT_VERBOSE("Cancelling ApplyMeshUpdateTask, volume_id is invalid");
|
2021-09-25 00:02:41 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
self->apply_mesh_update(data);
|
|
|
|
}
|
|
|
|
uint32_t volume_id = 0;
|
|
|
|
VoxelLodTerrain *self = nullptr;
|
|
|
|
VoxelServer::BlockMeshOutput data;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Mesh updates are spread over frames by scheduling them in a task runner of VoxelServer,
|
|
|
|
// but instead of using a reception buffer we use a callback,
|
|
|
|
// because this kind of task scheduling would otherwise delay the update by 1 frame
|
2021-10-13 20:28:20 +01:00
|
|
|
VoxelServer::VolumeCallbacks callbacks;
|
|
|
|
callbacks.data = this;
|
|
|
|
callbacks.mesh_output_callback = [](void *cb_data, const VoxelServer::BlockMeshOutput &ob) {
|
2021-09-25 00:02:41 +01:00
|
|
|
VoxelLodTerrain *self = reinterpret_cast<VoxelLodTerrain *>(cb_data);
|
|
|
|
ApplyMeshUpdateTask *task = memnew(ApplyMeshUpdateTask);
|
|
|
|
task->volume_id = self->get_volume_id();
|
|
|
|
task->self = self;
|
|
|
|
task->data = ob;
|
2022-04-08 23:54:04 +01:00
|
|
|
VoxelServer::get_singleton().push_main_thread_time_spread_task(task);
|
2021-09-25 00:02:41 +01:00
|
|
|
};
|
2021-10-13 20:28:20 +01:00
|
|
|
callbacks.data_output_callback = [](void *cb_data, VoxelServer::BlockDataOutput &ob) {
|
|
|
|
VoxelLodTerrain *self = reinterpret_cast<VoxelLodTerrain *>(cb_data);
|
|
|
|
self->apply_data_block_response(ob);
|
|
|
|
};
|
2021-09-25 00:02:41 +01:00
|
|
|
|
2022-04-08 23:54:04 +01:00
|
|
|
_volume_id = VoxelServer::get_singleton().add_volume(callbacks, VoxelServer::VOLUME_SPARSE_OCTREE);
|
|
|
|
VoxelServer::get_singleton().set_volume_octree_lod_distance(_volume_id, get_lod_distance());
|
2020-08-31 21:51:30 +01:00
|
|
|
|
2019-09-03 22:54:40 +01:00
|
|
|
// TODO Being able to set a LOD smaller than the stream is probably a bad idea,
|
|
|
|
// Because it prevents edits from propagating up to the last one, they will be left out of sync
|
2019-08-29 22:55:02 +01:00
|
|
|
set_lod_count(4);
|
2021-03-27 16:38:20 +00:00
|
|
|
|
|
|
|
set_lod_distance(48.f);
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
VoxelLodTerrain::~VoxelLodTerrain() {
|
2022-04-06 23:26:54 +01:00
|
|
|
ZN_PRINT_VERBOSE("Destroy VoxelLodTerrain");
|
2021-10-04 19:20:36 +01:00
|
|
|
abort_async_edits();
|
2022-04-08 23:54:04 +01:00
|
|
|
VoxelServer::get_singleton().remove_volume(_volume_id);
|
2020-12-29 22:25:22 +00:00
|
|
|
// Instancer can take care of itself
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Ref<Material> VoxelLodTerrain::get_material() const {
|
|
|
|
return _material;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelLodTerrain::set_material(Ref<Material> p_material) {
|
2022-03-26 22:28:18 +00:00
|
|
|
// TODO Update existing block surfaces
|
2019-05-04 00:02:10 +01:00
|
|
|
_material = p_material;
|
|
|
|
}
|
|
|
|
|
2021-04-03 20:39:37 +01:00
|
|
|
unsigned int VoxelLodTerrain::get_data_block_size() const {
|
2021-10-13 20:28:20 +01:00
|
|
|
return _data->lods[0].map.get_block_size();
|
2021-04-03 20:39:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int VoxelLodTerrain::get_data_block_size_pow2() const {
|
2021-10-13 20:28:20 +01:00
|
|
|
return _data->lods[0].map.get_block_size_pow2();
|
2021-04-03 20:39:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int VoxelLodTerrain::get_mesh_block_size_pow2() const {
|
2022-03-19 19:43:33 +00:00
|
|
|
return _update_data->settings.mesh_block_size_po2;
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2021-04-03 20:39:37 +01:00
|
|
|
unsigned int VoxelLodTerrain::get_mesh_block_size() const {
|
2022-03-19 19:43:33 +00:00
|
|
|
return 1 << _update_data->settings.mesh_block_size_po2;
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2019-05-25 16:07:38 +01:00
|
|
|
void VoxelLodTerrain::set_stream(Ref<VoxelStream> p_stream) {
|
2022-03-19 22:43:57 +00:00
|
|
|
if (p_stream == _stream) {
|
2019-08-24 01:44:27 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_stream = p_stream;
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_streaming_dependency->valid = false;
|
2022-04-10 20:10:33 +01:00
|
|
|
_streaming_dependency = make_shared_instance<StreamingDependency>();
|
2022-03-15 00:29:39 +00:00
|
|
|
_streaming_dependency->stream = _stream;
|
|
|
|
_streaming_dependency->generator = _generator;
|
|
|
|
_streaming_dependency->valid = true;
|
|
|
|
|
2020-08-14 20:33:09 +01:00
|
|
|
#ifdef TOOLS_ENABLED
|
2022-03-15 00:29:39 +00:00
|
|
|
if (p_stream.is_valid()) {
|
2020-08-14 20:33:09 +01:00
|
|
|
if (Engine::get_singleton()->is_editor_hint()) {
|
2022-03-15 00:29:39 +00:00
|
|
|
Ref<Script> script = p_stream->get_script();
|
2021-01-17 17:18:05 +00:00
|
|
|
if (script.is_valid()) {
|
2020-08-14 20:33:09 +01:00
|
|
|
// Safety check. It's too easy to break threads by making a script reload.
|
|
|
|
// You can turn it back on, but be careful.
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->settings.run_stream_in_editor = false;
|
2021-12-13 21:38:10 +00:00
|
|
|
notify_property_list_changed();
|
2020-08-14 20:33:09 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-24 01:44:27 +01:00
|
|
|
}
|
2020-08-14 20:33:09 +01:00
|
|
|
#endif
|
2019-08-24 01:44:27 +01:00
|
|
|
|
|
|
|
_on_stream_params_changed();
|
|
|
|
}
|
|
|
|
|
2021-01-17 17:18:05 +00:00
|
|
|
Ref<VoxelStream> VoxelLodTerrain::get_stream() const {
|
2022-03-15 00:29:39 +00:00
|
|
|
return _streaming_dependency->stream;
|
2021-01-17 17:18:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelLodTerrain::set_generator(Ref<VoxelGenerator> p_generator) {
|
2022-03-19 22:43:57 +00:00
|
|
|
if (p_generator == _generator) {
|
2021-01-17 17:18:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-19 22:43:57 +00:00
|
|
|
_generator = p_generator;
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_meshing_dependency->valid = false;
|
2022-04-10 20:10:33 +01:00
|
|
|
_meshing_dependency = make_shared_instance<MeshingDependency>();
|
2022-03-15 00:29:39 +00:00
|
|
|
_meshing_dependency->mesher = _mesher;
|
|
|
|
_meshing_dependency->generator = p_generator;
|
|
|
|
_meshing_dependency->valid = true;
|
|
|
|
|
|
|
|
_streaming_dependency->valid = false;
|
2022-04-10 20:10:33 +01:00
|
|
|
_streaming_dependency = make_shared_instance<StreamingDependency>();
|
2022-03-15 00:29:39 +00:00
|
|
|
_streaming_dependency->stream = _stream;
|
|
|
|
_streaming_dependency->generator = p_generator;
|
|
|
|
_streaming_dependency->valid = true;
|
2021-01-17 17:18:05 +00:00
|
|
|
|
|
|
|
#ifdef TOOLS_ENABLED
|
2022-03-15 00:29:39 +00:00
|
|
|
if (p_generator.is_valid()) {
|
2021-01-17 17:18:05 +00:00
|
|
|
if (Engine::get_singleton()->is_editor_hint()) {
|
2022-03-15 00:29:39 +00:00
|
|
|
Ref<Script> script = p_generator->get_script();
|
2021-01-17 17:18:05 +00:00
|
|
|
if (script.is_valid()) {
|
|
|
|
// Safety check. It's too easy to break threads by making a script reload.
|
|
|
|
// You can turn it back on, but be careful.
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->settings.run_stream_in_editor = false;
|
2021-12-13 21:38:10 +00:00
|
|
|
notify_property_list_changed();
|
2021-01-17 17:18:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
_on_stream_params_changed();
|
|
|
|
}
|
|
|
|
|
|
|
|
Ref<VoxelGenerator> VoxelLodTerrain::get_generator() const {
|
|
|
|
return _generator;
|
2020-12-18 21:01:50 +00:00
|
|
|
}
|
|
|
|
|
2021-12-16 00:11:11 +00:00
|
|
|
void VoxelLodTerrain::_on_gi_mode_changed() {
|
|
|
|
const GIMode gi_mode = get_gi_mode();
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < _update_data->state.lods.size(); ++lod_index) {
|
2022-03-20 22:04:53 +00:00
|
|
|
_mesh_maps_per_lod[lod_index].for_each_block([gi_mode](VoxelMeshBlockVLT &block) { //
|
2022-03-20 19:16:58 +00:00
|
|
|
block.set_gi_mode(DirectMeshInstance::GIMode(gi_mode));
|
2021-12-16 00:11:11 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-18 21:01:50 +00:00
|
|
|
void VoxelLodTerrain::set_mesher(Ref<VoxelMesher> p_mesher) {
|
|
|
|
if (_mesher == p_mesher) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
stop_updater();
|
|
|
|
|
|
|
|
_mesher = p_mesher;
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_meshing_dependency->valid = false;
|
2022-04-10 20:10:33 +01:00
|
|
|
_meshing_dependency = make_shared_instance<MeshingDependency>();
|
2022-03-15 00:29:39 +00:00
|
|
|
_meshing_dependency->mesher = _mesher;
|
|
|
|
_meshing_dependency->generator = _generator;
|
|
|
|
_meshing_dependency->valid = true;
|
|
|
|
|
2020-12-18 21:01:50 +00:00
|
|
|
if (_mesher.is_valid()) {
|
|
|
|
start_updater();
|
|
|
|
remesh_all_blocks();
|
|
|
|
}
|
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
update_configuration_warnings();
|
2020-12-18 21:01:50 +00:00
|
|
|
}
|
|
|
|
|
2021-01-17 17:18:05 +00:00
|
|
|
Ref<VoxelMesher> VoxelLodTerrain::get_mesher() const {
|
|
|
|
return _mesher;
|
|
|
|
}
|
|
|
|
|
2019-08-24 01:44:27 +01:00
|
|
|
void VoxelLodTerrain::_on_stream_params_changed() {
|
|
|
|
stop_streamer();
|
|
|
|
stop_updater();
|
|
|
|
|
2021-01-17 17:18:05 +00:00
|
|
|
if (_stream.is_valid()) {
|
2021-10-13 20:28:20 +01:00
|
|
|
//const int stream_block_size_po2 = _stream->get_block_size_po2();
|
|
|
|
//_set_block_size_po2(stream_block_size_po2);
|
2019-08-24 01:44:27 +01:00
|
|
|
|
2021-02-07 17:04:22 +00:00
|
|
|
// TODO We have to figure out streams that have a LOD requirement
|
|
|
|
// const int stream_lod_count = _stream->get_lod_count();
|
|
|
|
// _set_lod_count(min(stream_lod_count, get_lod_count()));
|
2021-10-03 01:48:07 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
if (_update_data->settings.full_load_mode && !_stream->supports_loading_all_blocks()) {
|
2021-10-03 01:48:07 +01:00
|
|
|
ERR_PRINT("The chosen stream does not supports loading all blocks. Full load mode cannot be used.");
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
_update_data->settings.full_load_mode = false;
|
2021-10-03 01:48:07 +01:00
|
|
|
#ifdef TOOLS_ENABLED
|
2021-12-13 21:38:10 +00:00
|
|
|
notify_property_list_changed();
|
2021-10-03 01:48:07 +01:00
|
|
|
#endif
|
|
|
|
}
|
2019-08-24 01:44:27 +01:00
|
|
|
}
|
|
|
|
|
2022-04-08 23:54:04 +01:00
|
|
|
VoxelServer::get_singleton().set_volume_data_block_size(_volume_id, get_data_block_size());
|
|
|
|
VoxelServer::get_singleton().set_volume_render_block_size(_volume_id, get_mesh_block_size());
|
2020-08-31 21:51:30 +01:00
|
|
|
|
2020-08-14 20:33:09 +01:00
|
|
|
reset_maps();
|
2021-10-13 20:28:20 +01:00
|
|
|
// TODO Size other than 16 is not really supported though.
|
|
|
|
// also this code isn't right, it doesnt update the other lods
|
|
|
|
//_data->lods[0].map.create(p_block_size_po2, 0);
|
2020-08-14 20:33:09 +01:00
|
|
|
|
2021-01-17 17:18:05 +00:00
|
|
|
if ((_stream.is_valid() || _generator.is_valid()) &&
|
2022-03-15 00:29:39 +00:00
|
|
|
(Engine::get_singleton()->is_editor_hint() == false || _update_data->settings.run_stream_in_editor)) {
|
2019-08-24 01:44:27 +01:00
|
|
|
start_streamer();
|
|
|
|
start_updater();
|
|
|
|
}
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
2022-03-26 15:59:03 +00:00
|
|
|
_update_data->state.force_update_octrees_next_update = true;
|
2022-03-15 00:29:39 +00:00
|
|
|
|
2019-08-24 01:44:27 +01:00
|
|
|
// The whole map might change, so make all area dirty
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int i = 0; i < _update_data->settings.lod_count; ++i) {
|
|
|
|
VoxelLodTerrainUpdateData::Lod &lod = _update_data->state.lods[i];
|
2021-04-03 20:39:37 +01:00
|
|
|
lod.last_view_distance_data_blocks = 0;
|
|
|
|
lod.last_view_distance_mesh_blocks = 0;
|
2019-09-03 22:54:40 +01:00
|
|
|
}
|
2020-02-13 00:48:18 +08:00
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
update_configuration_warnings();
|
2019-08-24 01:44:27 +01:00
|
|
|
}
|
|
|
|
|
2021-04-03 23:51:11 +01:00
|
|
|
void VoxelLodTerrain::set_mesh_block_size(unsigned int mesh_block_size) {
|
2021-10-28 23:55:46 +01:00
|
|
|
// Mesh block size cannot be smaller than data block size, for now
|
2022-01-09 03:06:58 +00:00
|
|
|
mesh_block_size = math::clamp(mesh_block_size, get_data_block_size(), constants::MAX_BLOCK_SIZE);
|
2021-04-03 23:51:11 +01:00
|
|
|
|
2021-10-04 19:20:36 +01:00
|
|
|
// Only these sizes are allowed at the moment. This stuff is still not supported in a generic way yet,
|
|
|
|
// some code still exploits the fact it's a multiple of data block size, for performance
|
2021-04-03 23:51:11 +01:00
|
|
|
unsigned int po2;
|
|
|
|
switch (mesh_block_size) {
|
|
|
|
case 16:
|
|
|
|
po2 = 4;
|
|
|
|
break;
|
|
|
|
case 32:
|
|
|
|
po2 = 5;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
mesh_block_size = 16;
|
|
|
|
po2 = 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (mesh_block_size == get_mesh_block_size()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
2022-03-19 19:43:33 +00:00
|
|
|
_update_data->settings.mesh_block_size_po2 = po2;
|
2022-03-26 15:59:03 +00:00
|
|
|
_update_data->state.force_update_octrees_next_update = true;
|
2022-03-15 00:29:39 +00:00
|
|
|
|
|
|
|
VoxelLodTerrainUpdateData::State &state = _update_data->state;
|
|
|
|
const unsigned int lod_count = _update_data->settings.lod_count;
|
|
|
|
|
2021-04-03 23:51:11 +01:00
|
|
|
// Reset mesh maps
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < lod_count; ++lod_index) {
|
|
|
|
VoxelLodTerrainUpdateData::Lod &lod = state.lods[lod_index];
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
2021-04-03 23:51:11 +01:00
|
|
|
if (_instancer != nullptr) {
|
|
|
|
// Unload instances
|
|
|
|
VoxelInstancer *instancer = _instancer;
|
2022-03-20 22:04:53 +00:00
|
|
|
mesh_map.for_each_block([lod_index, instancer](VoxelMeshBlockVLT &block) {
|
2022-03-20 19:16:58 +00:00
|
|
|
instancer->on_mesh_block_exit(block.position, lod_index);
|
2021-04-03 23:51:11 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
// Unload mesh blocks
|
2022-03-19 19:43:33 +00:00
|
|
|
mesh_map.for_each_block(BeforeUnloadMeshAction{ _shader_material_pool });
|
2022-03-20 22:04:53 +00:00
|
|
|
mesh_map.clear();
|
2021-04-03 23:51:11 +01:00
|
|
|
// Reset view distance cache so they will be re-entered
|
|
|
|
lod.last_view_distance_mesh_blocks = 0;
|
|
|
|
}
|
|
|
|
|
2022-04-09 20:40:03 +01:00
|
|
|
// Doing this after because `on_mesh_block_exit` may use the old size
|
|
|
|
if (_instancer != nullptr) {
|
|
|
|
_instancer->set_mesh_block_size_po2(mesh_block_size);
|
|
|
|
}
|
|
|
|
|
2021-04-03 23:51:11 +01:00
|
|
|
// Reset LOD octrees
|
|
|
|
LodOctree::NoDestroyAction nda;
|
2022-03-15 00:29:39 +00:00
|
|
|
for (Map<Vector3i, VoxelLodTerrainUpdateData::OctreeItem>::Element *E = state.lod_octrees.front(); E;
|
|
|
|
E = E->next()) {
|
|
|
|
VoxelLodTerrainUpdateData::OctreeItem &item = E->value();
|
|
|
|
item.octree.create(lod_count, nda);
|
2021-04-03 23:51:11 +01:00
|
|
|
}
|
|
|
|
|
2022-04-08 23:54:04 +01:00
|
|
|
VoxelServer::get_singleton().set_volume_render_block_size(_volume_id, mesh_block_size);
|
2021-04-05 03:47:25 +01:00
|
|
|
|
|
|
|
// Update voxel bounds because block size change can affect octree size
|
2022-03-15 00:29:39 +00:00
|
|
|
set_voxel_bounds(_update_data->settings.bounds_in_voxels);
|
2021-04-03 23:51:11 +01:00
|
|
|
}
|
|
|
|
|
2021-10-03 01:48:07 +01:00
|
|
|
void VoxelLodTerrain::set_full_load_mode_enabled(bool enabled) {
|
2022-03-15 00:29:39 +00:00
|
|
|
if (enabled != _update_data->settings.full_load_mode) {
|
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
_update_data->settings.full_load_mode = enabled;
|
2022-03-26 15:59:03 +00:00
|
|
|
_update_data->state.force_update_octrees_next_update = true;
|
2021-10-03 01:48:07 +01:00
|
|
|
_on_stream_params_changed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VoxelLodTerrain::is_full_load_mode_enabled() const {
|
2022-03-15 00:29:39 +00:00
|
|
|
return _update_data->settings.full_load_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelLodTerrain::set_threaded_update_enabled(bool enabled) {
|
|
|
|
if (enabled != _threaded_update_enabled) {
|
|
|
|
if (_threaded_update_enabled) {
|
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
}
|
|
|
|
_threaded_update_enabled = enabled;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VoxelLodTerrain::is_threaded_update_enabled() const {
|
|
|
|
return _threaded_update_enabled;
|
2021-10-03 01:48:07 +01:00
|
|
|
}
|
|
|
|
|
2022-03-20 22:04:53 +00:00
|
|
|
void VoxelLodTerrain::set_mesh_block_active(VoxelMeshBlockVLT &block, bool active) {
|
2021-03-02 22:49:42 +00:00
|
|
|
if (block.active == active) {
|
|
|
|
return;
|
|
|
|
}
|
2021-03-12 23:35:51 +00:00
|
|
|
|
2021-03-02 22:49:42 +00:00
|
|
|
block.active = active;
|
2021-03-12 23:35:51 +00:00
|
|
|
|
|
|
|
if (_lod_fade_duration == 0.f) {
|
|
|
|
block.set_visible(active);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshBlockVLT::FadingState fading_state;
|
2021-03-12 23:35:51 +00:00
|
|
|
// Initial progress has to be set too because it sometimes happens that a LOD must appear before its parent
|
|
|
|
// finished fading in. So the parent will have to fade out from solid with the same duration.
|
|
|
|
float initial_progress;
|
2021-03-02 22:49:42 +00:00
|
|
|
if (active) {
|
|
|
|
block.set_visible(true);
|
2022-03-20 22:04:53 +00:00
|
|
|
fading_state = VoxelMeshBlockVLT::FADING_IN;
|
2021-03-12 23:35:51 +00:00
|
|
|
initial_progress = 0.f;
|
2021-03-02 22:49:42 +00:00
|
|
|
} else {
|
2022-03-20 22:04:53 +00:00
|
|
|
fading_state = VoxelMeshBlockVLT::FADING_OUT;
|
2021-03-12 23:35:51 +00:00
|
|
|
initial_progress = 1.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (block.fading_state != fading_state) {
|
2022-03-20 22:04:53 +00:00
|
|
|
if (block.fading_state == VoxelMeshBlockVLT::FADING_NONE) {
|
|
|
|
Map<Vector3i, VoxelMeshBlockVLT *> &fading_blocks = _fading_blocks_per_lod[block.lod_index];
|
2021-03-12 23:35:51 +00:00
|
|
|
// Must not have duplicates
|
2022-03-15 00:29:39 +00:00
|
|
|
ERR_FAIL_COND(fading_blocks.has(block.position));
|
|
|
|
fading_blocks.insert(block.position, &block);
|
2021-03-02 22:49:42 +00:00
|
|
|
}
|
2021-03-12 23:35:51 +00:00
|
|
|
block.fading_state = fading_state;
|
|
|
|
block.fading_progress = initial_progress;
|
2021-03-02 22:49:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-17 20:01:15 +01:00
|
|
|
bool VoxelLodTerrain::is_area_editable(Box3i p_voxel_box) const {
|
2022-03-15 00:29:39 +00:00
|
|
|
if (_update_data->settings.full_load_mode) {
|
2021-10-03 01:48:07 +01:00
|
|
|
return true;
|
|
|
|
}
|
2022-03-15 00:29:39 +00:00
|
|
|
const Box3i voxel_box = p_voxel_box.clipped(_update_data->settings.bounds_in_voxels);
|
2021-10-13 20:28:20 +01:00
|
|
|
VoxelDataLodMap::Lod &data_lod0 = _data->lods[0];
|
|
|
|
{
|
|
|
|
RWLockRead rlock(data_lod0.map_lock);
|
|
|
|
const bool all_blocks_present = data_lod0.map.is_area_fully_loaded(voxel_box);
|
|
|
|
return all_blocks_present;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline std::shared_ptr<VoxelBufferInternal> try_get_voxel_buffer_with_lock(
|
|
|
|
const VoxelDataLodMap::Lod &data_lod, Vector3i block_pos) {
|
|
|
|
RWLockRead rlock(data_lod.map_lock);
|
|
|
|
const VoxelDataBlock *block = data_lod.map.get_block(block_pos);
|
|
|
|
if (block == nullptr) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return block->get_voxels_shared();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline VoxelSingleValue get_voxel_with_lock(VoxelBufferInternal &vb, Vector3i pos, unsigned int channel) {
|
|
|
|
VoxelSingleValue v;
|
|
|
|
if (channel == VoxelBufferInternal::CHANNEL_SDF) {
|
|
|
|
RWLockRead rlock(vb.get_lock());
|
|
|
|
v.f = vb.get_voxel_f(pos.x, pos.y, pos.z, channel);
|
|
|
|
} else {
|
|
|
|
RWLockRead rlock(vb.get_lock());
|
|
|
|
v.i = vb.get_voxel(pos, channel);
|
|
|
|
}
|
|
|
|
return v;
|
2021-09-17 20:01:15 +01:00
|
|
|
}
|
|
|
|
|
2021-10-03 01:48:07 +01:00
|
|
|
VoxelSingleValue VoxelLodTerrain::get_voxel(Vector3i pos, unsigned int channel, VoxelSingleValue defval) {
|
2022-03-15 00:29:39 +00:00
|
|
|
if (!_update_data->settings.bounds_in_voxels.contains(pos)) {
|
2021-10-03 01:48:07 +01:00
|
|
|
return defval;
|
|
|
|
}
|
|
|
|
|
2021-09-17 20:01:15 +01:00
|
|
|
Vector3i block_pos = pos >> get_data_block_size_pow2();
|
2021-10-03 01:48:07 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
if (_update_data->settings.full_load_mode) {
|
2021-10-13 20:28:20 +01:00
|
|
|
const VoxelDataLodMap::Lod &data_lod0 = _data->lods[0];
|
|
|
|
std::shared_ptr<VoxelBufferInternal> voxels = try_get_voxel_buffer_with_lock(data_lod0, block_pos);
|
|
|
|
if (voxels == nullptr) {
|
2021-10-03 01:48:07 +01:00
|
|
|
if (_generator.is_valid()) {
|
|
|
|
return _generator->generate_single(pos, channel);
|
|
|
|
}
|
2021-10-04 19:20:36 +01:00
|
|
|
} else {
|
2021-10-13 20:28:20 +01:00
|
|
|
const Vector3i rpos = data_lod0.map.to_local(pos);
|
2021-10-04 19:20:36 +01:00
|
|
|
VoxelSingleValue v;
|
2021-10-13 20:28:20 +01:00
|
|
|
RWLockRead rlock(voxels->get_lock());
|
2021-10-04 19:20:36 +01:00
|
|
|
if (channel == VoxelBufferInternal::CHANNEL_SDF) {
|
2021-10-13 20:28:20 +01:00
|
|
|
v.f = voxels->get_voxel_f(rpos.x, rpos.y, rpos.z, channel);
|
2021-10-04 19:20:36 +01:00
|
|
|
} else {
|
2021-10-13 20:28:20 +01:00
|
|
|
v.i = voxels->get_voxel(rpos, channel);
|
2021-10-04 19:20:36 +01:00
|
|
|
}
|
|
|
|
return v;
|
2021-10-03 01:48:07 +01:00
|
|
|
}
|
|
|
|
return defval;
|
|
|
|
|
|
|
|
} else {
|
2021-10-13 20:28:20 +01:00
|
|
|
Vector3i voxel_pos = pos;
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < _update_data->settings.lod_count; ++lod_index) {
|
2021-10-13 20:28:20 +01:00
|
|
|
const VoxelDataLodMap::Lod &data_lod = _data->lods[lod_index];
|
|
|
|
std::shared_ptr<VoxelBufferInternal> voxels = try_get_voxel_buffer_with_lock(data_lod, block_pos);
|
|
|
|
if (voxels != nullptr) {
|
|
|
|
return get_voxel_with_lock(*voxels, data_lod.map.to_local(voxel_pos), channel);
|
2021-10-03 01:48:07 +01:00
|
|
|
}
|
|
|
|
// Fallback on lower LOD
|
|
|
|
block_pos = block_pos >> 1;
|
2021-10-13 20:28:20 +01:00
|
|
|
voxel_pos = voxel_pos >> 1;
|
2021-09-17 20:01:15 +01:00
|
|
|
}
|
2021-10-03 01:48:07 +01:00
|
|
|
return defval;
|
2021-09-17 20:01:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VoxelLodTerrain::try_set_voxel_without_update(Vector3i pos, unsigned int channel, uint64_t value) {
|
|
|
|
const Vector3i block_pos_lod0 = pos >> get_data_block_size_pow2();
|
2021-10-13 20:28:20 +01:00
|
|
|
VoxelDataLodMap::Lod &data_lod0 = _data->lods[0];
|
|
|
|
const Vector3i block_pos = data_lod0.map.voxel_to_block(pos);
|
|
|
|
std::shared_ptr<VoxelBufferInternal> voxels = try_get_voxel_buffer_with_lock(data_lod0, block_pos);
|
|
|
|
if (voxels == nullptr) {
|
2022-03-15 00:29:39 +00:00
|
|
|
if (!_update_data->settings.full_load_mode) {
|
2021-10-03 01:48:07 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (_generator.is_valid()) {
|
2022-04-10 20:10:33 +01:00
|
|
|
voxels = make_shared_instance<VoxelBufferInternal>();
|
2021-12-13 21:38:10 +00:00
|
|
|
voxels->create(Vector3iUtil::create(get_data_block_size()));
|
2022-02-12 23:37:02 +00:00
|
|
|
VoxelGenerator::VoxelQueryData q{ *voxels, pos, 0 };
|
|
|
|
_generator->generate_block(q);
|
2021-10-13 20:28:20 +01:00
|
|
|
RWLockWrite wlock(data_lod0.map_lock);
|
|
|
|
if (data_lod0.map.has_block(block_pos_lod0)) {
|
|
|
|
// A block was loaded by another thread, cancel our edit.
|
|
|
|
return false;
|
|
|
|
}
|
2022-02-12 23:37:02 +00:00
|
|
|
data_lod0.map.set_block_buffer(block_pos_lod0, voxels, true);
|
2021-10-03 01:48:07 +01:00
|
|
|
}
|
2021-09-17 20:01:15 +01:00
|
|
|
}
|
2021-10-13 20:28:20 +01:00
|
|
|
// If it turns out to be a problem, use CoW?
|
|
|
|
RWLockWrite lock(voxels->get_lock());
|
|
|
|
voxels->set_voxel(value, data_lod0.map.to_local(pos), channel);
|
2021-10-03 01:48:07 +01:00
|
|
|
return true;
|
2021-09-17 20:01:15 +01:00
|
|
|
}
|
|
|
|
|
2021-10-03 01:48:07 +01:00
|
|
|
void VoxelLodTerrain::copy(Vector3i p_origin_voxels, VoxelBufferInternal &dst_buffer, uint8_t channels_mask) {
|
2021-10-13 20:28:20 +01:00
|
|
|
const VoxelDataLodMap::Lod &data_lod0 = _data->lods[0];
|
2022-03-15 00:29:39 +00:00
|
|
|
if (_update_data->settings.full_load_mode && _generator.is_valid()) {
|
2021-10-13 20:28:20 +01:00
|
|
|
RWLockRead rlock(data_lod0.map_lock);
|
2021-12-12 00:16:18 +00:00
|
|
|
data_lod0.map.copy(p_origin_voxels, dst_buffer, channels_mask, *_generator,
|
2021-10-03 01:48:07 +01:00
|
|
|
[](void *callback_data, VoxelBufferInternal &voxels, Vector3i pos) {
|
|
|
|
VoxelGenerator *generator = reinterpret_cast<VoxelGenerator *>(callback_data);
|
2022-02-12 23:37:02 +00:00
|
|
|
VoxelGenerator::VoxelQueryData q{ voxels, pos, 0 };
|
|
|
|
generator->generate_block(q);
|
2021-10-03 01:48:07 +01:00
|
|
|
});
|
|
|
|
} else {
|
2021-10-13 20:28:20 +01:00
|
|
|
RWLockRead rlock(data_lod0.map_lock);
|
|
|
|
data_lod0.map.copy(p_origin_voxels, dst_buffer, channels_mask);
|
2021-10-03 01:48:07 +01:00
|
|
|
}
|
2021-09-17 20:01:15 +01:00
|
|
|
}
|
|
|
|
|
2019-09-03 22:54:40 +01:00
|
|
|
// Marks intersecting blocks in the area as modified, updates LODs and schedules remeshing.
|
|
|
|
// The provided box must be at LOD0 coordinates.
|
2021-05-31 17:10:54 +01:00
|
|
|
void VoxelLodTerrain::post_edit_area(Box3i p_box) {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE();
|
2021-07-25 19:48:51 +01:00
|
|
|
// TODO Better decoupling is needed here.
|
|
|
|
// In the past this padding was necessary for mesh blocks because visuals depend on neighbor voxels.
|
|
|
|
// So when editing voxels at the boundary of two mesh blocks, both must update.
|
|
|
|
// However on data blocks it doesn't make sense, neighbors are not affected (at least for now).
|
|
|
|
// this can cause false positive errors as if we were editing a block that's not loaded (coming up as null).
|
|
|
|
// For now, this is worked around by ignoring cases where blocks are null,
|
|
|
|
// But it might mip more lods than necessary when editing on borders.
|
2021-05-31 17:10:54 +01:00
|
|
|
const Box3i box = p_box.padded(1);
|
|
|
|
const Box3i bbox = box.downscaled(get_data_block_size());
|
2019-09-03 22:54:40 +01:00
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
VoxelDataLodMap::Lod &data_lod0 = _data->lods[0];
|
|
|
|
{
|
|
|
|
RWLockRead rlock(data_lod0.map_lock);
|
2022-03-15 00:29:39 +00:00
|
|
|
MutexLock lock(_update_data->state.blocks_pending_lodding_lod0_mutex);
|
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
bbox.for_each_cell([this, &data_lod0](Vector3i block_pos_lod0) {
|
|
|
|
VoxelDataBlock *block = data_lod0.map.get_block(block_pos_lod0);
|
|
|
|
//ERR_FAIL_COND(block == nullptr);
|
|
|
|
if (block == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
2021-07-25 19:48:51 +01:00
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
//RWLockWrite wlock(block->get_voxels_shared()->get_lock());
|
|
|
|
block->set_modified(true);
|
2021-07-25 19:48:51 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
// TODO That boolean is also modified by the threaded update task (always set to false)
|
2021-10-13 20:28:20 +01:00
|
|
|
if (!block->get_needs_lodding()) {
|
|
|
|
block->set_needs_lodding(true);
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->state.blocks_pending_lodding_lod0.push_back(block_pos_lod0);
|
2021-10-13 20:28:20 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2020-12-30 20:11:46 +00:00
|
|
|
|
|
|
|
if (_instancer != nullptr) {
|
|
|
|
_instancer->on_area_edited(p_box);
|
|
|
|
}
|
2019-09-03 22:54:40 +01:00
|
|
|
}
|
|
|
|
|
2022-01-09 22:13:10 +00:00
|
|
|
void VoxelLodTerrain::push_async_edit(IThreadedTask *task, Box3i box, std::shared_ptr<AsyncDependencyTracker> tracker) {
|
2021-10-13 20:28:20 +01:00
|
|
|
CRASH_COND(task == nullptr);
|
|
|
|
CRASH_COND(tracker == nullptr);
|
2022-03-15 00:29:39 +00:00
|
|
|
|
|
|
|
VoxelLodTerrainUpdateData::AsyncEdit e;
|
2021-10-13 20:28:20 +01:00
|
|
|
e.box = box;
|
|
|
|
e.task = task;
|
|
|
|
e.task_tracker = tracker;
|
2022-03-15 00:29:39 +00:00
|
|
|
|
|
|
|
VoxelLodTerrainUpdateData::State &state = _update_data->state;
|
|
|
|
MutexLock lock(state.pending_async_edits_mutex);
|
|
|
|
state.pending_async_edits.push_back(e);
|
2021-10-04 19:20:36 +01:00
|
|
|
}
|
|
|
|
|
2019-09-03 22:54:40 +01:00
|
|
|
Ref<VoxelTool> VoxelLodTerrain::get_voxel_tool() {
|
2021-09-17 20:01:15 +01:00
|
|
|
VoxelToolLodTerrain *vt = memnew(VoxelToolLodTerrain(this));
|
2019-09-06 23:24:56 +01:00
|
|
|
// Set to most commonly used channel on this kind of terrain
|
2021-09-26 04:14:50 +01:00
|
|
|
vt->set_channel(VoxelBufferInternal::CHANNEL_SDF);
|
2019-09-06 23:24:56 +01:00
|
|
|
return Ref<VoxelTool>(vt);
|
2019-09-03 22:54:40 +01:00
|
|
|
}
|
|
|
|
|
2019-05-04 00:02:10 +01:00
|
|
|
int VoxelLodTerrain::get_view_distance() const {
|
2022-03-15 00:29:39 +00:00
|
|
|
return _update_data->settings.view_distance_voxels;
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2020-10-29 22:58:49 +00:00
|
|
|
// TODO Needs to be clamped dynamically, to avoid the user accidentally setting blowing up memory.
|
|
|
|
// It used to be clamped to a hardcoded value, but now it may depend on LOD count and boundaries
|
2019-09-03 22:56:57 +01:00
|
|
|
void VoxelLodTerrain::set_view_distance(int p_distance_in_voxels) {
|
|
|
|
ERR_FAIL_COND(p_distance_in_voxels <= 0);
|
2019-08-29 22:55:02 +01:00
|
|
|
// Note: this is a hint distance, the terrain will attempt to have this radius filled with loaded voxels.
|
|
|
|
// It is possible for blocks to still load beyond that distance.
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
_update_data->settings.view_distance_voxels = p_distance_in_voxels;
|
2022-03-26 15:59:03 +00:00
|
|
|
_update_data->state.force_update_octrees_next_update = true;
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 01:44:27 +01:00
|
|
|
void VoxelLodTerrain::start_updater() {
|
2021-05-15 23:41:19 +01:00
|
|
|
Ref<VoxelMesherBlocky> blocky_mesher = _mesher;
|
|
|
|
if (blocky_mesher.is_valid()) {
|
2022-01-09 23:23:59 +00:00
|
|
|
Ref<VoxelBlockyLibrary> library = blocky_mesher->get_library();
|
2021-05-15 23:41:19 +01:00
|
|
|
if (library.is_valid()) {
|
|
|
|
// TODO Any way to execute this function just after the TRES resource loader has finished to load?
|
|
|
|
// VoxelLibrary should be baked ahead of time, like MeshLibrary
|
|
|
|
library->bake();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-08 23:54:04 +01:00
|
|
|
VoxelServer::get_singleton().set_volume_mesher(_volume_id, _mesher);
|
2019-08-24 01:44:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelLodTerrain::stop_updater() {
|
2022-04-08 23:54:04 +01:00
|
|
|
VoxelServer::get_singleton().set_volume_mesher(_volume_id, Ref<VoxelMesher>());
|
2019-08-24 01:44:27 +01:00
|
|
|
|
2021-09-25 00:02:41 +01:00
|
|
|
// TODO We can still receive a few mesh delayed mesh updates after this. Is it a problem?
|
|
|
|
//_reception_buffers.mesh_output.clear();
|
2019-08-24 01:44:27 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < _update_data->state.lods.size(); ++i) {
|
|
|
|
VoxelLodTerrainUpdateData::Lod &lod = _update_data->state.lods[i];
|
2019-08-24 01:44:27 +01:00
|
|
|
lod.blocks_pending_update.clear();
|
|
|
|
|
2022-03-19 19:43:33 +00:00
|
|
|
for (auto it = lod.mesh_map_state.map.begin(); it != lod.mesh_map_state.map.end(); ++it) {
|
|
|
|
VoxelLodTerrainUpdateData::MeshBlockState &mesh_block = it->second;
|
|
|
|
if (mesh_block.state == VoxelLodTerrainUpdateData::MESH_UPDATE_SENT) {
|
|
|
|
mesh_block.state = VoxelLodTerrainUpdateData::MESH_UPDATE_NOT_SENT;
|
|
|
|
}
|
|
|
|
}
|
2019-08-24 01:44:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelLodTerrain::start_streamer() {
|
2022-04-08 23:54:04 +01:00
|
|
|
VoxelServer::get_singleton().set_volume_stream(_volume_id, _stream);
|
|
|
|
VoxelServer::get_singleton().set_volume_generator(_volume_id, _generator);
|
2021-10-03 01:48:07 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
if (_update_data->settings.full_load_mode && _stream.is_valid()) {
|
2021-10-03 01:48:07 +01:00
|
|
|
// TODO May want to defer this to be sure it's not done multiple times.
|
|
|
|
// This would be a side-effect of setting properties one by one, either by scene loader or by script
|
2022-04-08 23:54:04 +01:00
|
|
|
VoxelServer::get_singleton().request_all_stream_blocks(_volume_id);
|
2021-10-03 01:48:07 +01:00
|
|
|
}
|
2019-08-24 01:44:27 +01:00
|
|
|
}
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2019-08-24 01:44:27 +01:00
|
|
|
void VoxelLodTerrain::stop_streamer() {
|
2022-04-08 23:54:04 +01:00
|
|
|
VoxelServer::get_singleton().set_volume_stream(_volume_id, Ref<VoxelStream>());
|
|
|
|
VoxelServer::get_singleton().set_volume_generator(_volume_id, Ref<VoxelGenerator>());
|
2019-08-24 01:44:27 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < _update_data->state.lods.size(); ++i) {
|
|
|
|
VoxelLodTerrainUpdateData::Lod &lod = _update_data->state.lods[i];
|
2020-08-30 21:38:38 +01:00
|
|
|
lod.loading_blocks.clear();
|
2019-08-24 01:44:27 +01:00
|
|
|
}
|
2020-08-31 21:51:30 +01:00
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
//_reception_buffers.data_output.clear();
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2021-03-27 16:38:20 +00:00
|
|
|
void VoxelLodTerrain::set_lod_distance(float p_lod_distance) {
|
2022-03-15 00:29:39 +00:00
|
|
|
if (p_lod_distance == _update_data->settings.lod_distance) {
|
2019-08-29 22:55:02 +01:00
|
|
|
return;
|
|
|
|
}
|
2022-03-15 00:29:39 +00:00
|
|
|
|
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
|
2022-02-12 18:09:46 +00:00
|
|
|
// Distance must be greater than a threshold,
|
|
|
|
// otherwise lods will decimate too fast and it will look messy
|
2022-04-09 20:40:03 +01:00
|
|
|
const float lod_distance =
|
2022-03-15 00:29:39 +00:00
|
|
|
math::clamp(p_lod_distance, constants::MINIMUM_LOD_DISTANCE, constants::MAXIMUM_LOD_DISTANCE);
|
2022-04-09 20:40:03 +01:00
|
|
|
_update_data->settings.lod_distance = lod_distance;
|
2022-03-26 15:59:03 +00:00
|
|
|
_update_data->state.force_update_octrees_next_update = true;
|
2022-04-08 23:54:04 +01:00
|
|
|
VoxelServer::get_singleton().set_volume_octree_lod_distance(_volume_id, get_lod_distance());
|
2022-04-09 20:40:03 +01:00
|
|
|
|
|
|
|
if (_instancer != nullptr) {
|
|
|
|
_instancer->set_mesh_lod_distance(lod_distance);
|
|
|
|
}
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2021-03-27 16:38:20 +00:00
|
|
|
float VoxelLodTerrain::get_lod_distance() const {
|
2022-03-15 00:29:39 +00:00
|
|
|
return _update_data->settings.lod_distance;
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 01:44:27 +01:00
|
|
|
void VoxelLodTerrain::set_lod_count(int p_lod_count) {
|
2022-01-09 03:06:58 +00:00
|
|
|
ERR_FAIL_COND(p_lod_count >= (int)constants::MAX_LOD);
|
2019-08-29 22:55:02 +01:00
|
|
|
ERR_FAIL_COND(p_lod_count < 1);
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2021-05-07 22:39:06 +01:00
|
|
|
if (get_lod_count() != p_lod_count) {
|
2019-08-24 01:44:27 +01:00
|
|
|
_set_lod_count(p_lod_count);
|
|
|
|
}
|
|
|
|
}
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2019-08-24 01:44:27 +01:00
|
|
|
void VoxelLodTerrain::_set_lod_count(int p_lod_count) {
|
2022-01-09 03:06:58 +00:00
|
|
|
CRASH_COND(p_lod_count >= (int)constants::MAX_LOD);
|
2019-08-29 22:55:02 +01:00
|
|
|
CRASH_COND(p_lod_count < 1);
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
|
|
|
|
_update_data->settings.lod_count = p_lod_count;
|
2022-03-26 15:59:03 +00:00
|
|
|
_update_data->state.force_update_octrees_next_update = true;
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2020-01-23 00:37:13 +00:00
|
|
|
LodOctree::NoDestroyAction nda;
|
2019-08-29 22:55:02 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
for (Map<Vector3i, VoxelLodTerrainUpdateData::OctreeItem>::Element *E = _update_data->state.lod_octrees.front(); E;
|
|
|
|
E = E->next()) {
|
|
|
|
VoxelLodTerrainUpdateData::OctreeItem &item = E->value();
|
2022-02-12 18:09:46 +00:00
|
|
|
item.octree.create(p_lod_count, nda);
|
2019-08-29 22:55:02 +01:00
|
|
|
}
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2020-10-19 23:22:23 +01:00
|
|
|
// Not entirely required, but changing LOD count at runtime is rarely needed
|
2019-08-24 01:44:27 +01:00
|
|
|
reset_maps();
|
|
|
|
}
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2019-08-24 01:44:27 +01:00
|
|
|
void VoxelLodTerrain::reset_maps() {
|
2019-08-29 22:55:02 +01:00
|
|
|
// Clears all blocks and reconfigures maps to account for new LOD count and block sizes
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2020-08-14 20:33:09 +01:00
|
|
|
// Don't reset while streaming, the result can be dirty?
|
|
|
|
//CRASH_COND(_stream_thread != nullptr);
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
|
|
|
|
const unsigned int lod_count = _update_data->settings.lod_count;
|
|
|
|
VoxelLodTerrainUpdateData::State &state = _update_data->state;
|
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
// Make a new one, so if threads still reference the old one it will be a different copy
|
2022-04-10 20:10:33 +01:00
|
|
|
_data = make_shared_instance<VoxelDataLodMap>();
|
2022-03-15 00:29:39 +00:00
|
|
|
_data->lod_count = lod_count;
|
2021-10-13 20:28:20 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < state.lods.size(); ++lod_index) {
|
2021-10-13 20:28:20 +01:00
|
|
|
VoxelDataLodMap::Lod &data_lod = _data->lods[lod_index];
|
|
|
|
// Instance new maps if we have more lods, or clear them otherwise
|
2022-03-15 00:29:39 +00:00
|
|
|
if (lod_index < lod_count) {
|
2021-10-13 20:28:20 +01:00
|
|
|
data_lod.map.create(data_lod.map.get_block_size_pow2(), lod_index);
|
|
|
|
} else {
|
|
|
|
data_lod.map.clear();
|
|
|
|
}
|
|
|
|
}
|
2019-08-24 01:44:27 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < state.lods.size(); ++lod_index) {
|
|
|
|
VoxelLodTerrainUpdateData::Lod &lod = state.lods[lod_index];
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
2022-03-19 19:43:33 +00:00
|
|
|
|
2019-08-24 01:44:27 +01:00
|
|
|
// Instance new maps if we have more lods, or clear them otherwise
|
2022-03-15 00:29:39 +00:00
|
|
|
if (lod_index < lod_count) {
|
2022-03-20 22:04:53 +00:00
|
|
|
mesh_map.clear();
|
2022-03-19 19:43:33 +00:00
|
|
|
// Reset view distance cache so blocks will be re-entered due to the difference
|
2021-04-03 20:39:37 +01:00
|
|
|
lod.last_view_distance_data_blocks = 0;
|
|
|
|
lod.last_view_distance_mesh_blocks = 0;
|
2020-08-14 20:33:09 +01:00
|
|
|
} else {
|
2022-03-19 19:43:33 +00:00
|
|
|
mesh_map.clear();
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
2022-03-19 19:43:33 +00:00
|
|
|
|
|
|
|
lod.mesh_map_state.map.clear();
|
|
|
|
|
|
|
|
lod.mesh_blocks_to_activate.clear();
|
|
|
|
lod.mesh_blocks_to_deactivate.clear();
|
|
|
|
lod.mesh_blocks_to_unload.clear();
|
|
|
|
lod.mesh_blocks_to_update_transitions.clear();
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_deferred_collision_updates_per_lod[lod_index].clear();
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
2020-08-14 20:33:09 +01:00
|
|
|
|
2021-10-04 19:20:36 +01:00
|
|
|
abort_async_edits();
|
|
|
|
|
2020-08-14 20:33:09 +01:00
|
|
|
// Reset previous state caches to force rebuilding the view area
|
2022-03-15 00:29:39 +00:00
|
|
|
state.last_octree_region_box = Box3i();
|
|
|
|
state.lod_octrees.clear();
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2021-05-07 22:39:06 +01:00
|
|
|
int VoxelLodTerrain::get_lod_count() const {
|
2022-03-15 00:29:39 +00:00
|
|
|
return _update_data->settings.lod_count;
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2019-08-25 13:04:49 +01:00
|
|
|
void VoxelLodTerrain::set_generate_collisions(bool enabled) {
|
|
|
|
_generate_collisions = enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelLodTerrain::set_collision_lod_count(int lod_count) {
|
2021-04-18 01:29:26 +01:00
|
|
|
ERR_FAIL_COND(lod_count < 0);
|
2022-01-03 23:14:18 +00:00
|
|
|
_collision_lod_count = static_cast<unsigned int>(math::min(lod_count, get_lod_count()));
|
2019-08-25 13:04:49 +01:00
|
|
|
}
|
|
|
|
|
2021-05-08 17:15:15 +01:00
|
|
|
int VoxelLodTerrain::get_collision_lod_count() const {
|
2019-08-25 13:04:49 +01:00
|
|
|
return _collision_lod_count;
|
|
|
|
}
|
|
|
|
|
2021-05-09 20:49:45 +01:00
|
|
|
void VoxelLodTerrain::set_collision_layer(int layer) {
|
2022-03-15 00:29:39 +00:00
|
|
|
const unsigned int lod_count = _update_data->settings.lod_count;
|
|
|
|
|
2021-05-09 20:49:45 +01:00
|
|
|
_collision_layer = layer;
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < lod_count; ++lod_index) {
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
|
|
|
mesh_map.for_each_block([layer](VoxelMeshBlockVLT &block) { //
|
2022-03-20 19:16:58 +00:00
|
|
|
block.set_collision_layer(layer);
|
2022-03-15 00:29:39 +00:00
|
|
|
});
|
2021-05-09 20:49:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int VoxelLodTerrain::get_collision_layer() const {
|
|
|
|
return _collision_layer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelLodTerrain::set_collision_mask(int mask) {
|
2022-03-15 00:29:39 +00:00
|
|
|
const unsigned int lod_count = _update_data->settings.lod_count;
|
|
|
|
|
2021-05-09 20:49:45 +01:00
|
|
|
_collision_mask = mask;
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < lod_count; ++lod_index) {
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
|
|
|
mesh_map.for_each_block([mask](VoxelMeshBlockVLT &block) { //
|
2022-03-20 19:16:58 +00:00
|
|
|
block.set_collision_mask(mask);
|
2022-03-15 00:29:39 +00:00
|
|
|
});
|
2021-05-09 20:49:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int VoxelLodTerrain::get_collision_mask() const {
|
|
|
|
return _collision_mask;
|
|
|
|
}
|
|
|
|
|
2021-07-10 22:14:17 +01:00
|
|
|
void VoxelLodTerrain::set_collision_margin(float margin) {
|
2022-03-15 00:29:39 +00:00
|
|
|
const unsigned int lod_count = _update_data->settings.lod_count;
|
|
|
|
|
2021-07-10 22:14:17 +01:00
|
|
|
_collision_margin = margin;
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < lod_count; ++lod_index) {
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
|
|
|
mesh_map.for_each_block([margin](VoxelMeshBlockVLT &block) { //
|
2022-03-20 19:16:58 +00:00
|
|
|
block.set_collision_margin(margin);
|
2022-03-15 00:29:39 +00:00
|
|
|
});
|
2021-07-10 22:14:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
float VoxelLodTerrain::get_collision_margin() const {
|
|
|
|
return _collision_margin;
|
|
|
|
}
|
|
|
|
|
2021-04-03 20:39:37 +01:00
|
|
|
int VoxelLodTerrain::get_data_block_region_extent() const {
|
2022-03-15 00:29:39 +00:00
|
|
|
return VoxelServer::get_octree_lod_block_region_extent(_update_data->settings.lod_distance, get_data_block_size());
|
2021-04-03 20:39:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int VoxelLodTerrain::get_mesh_block_region_extent() const {
|
2022-03-15 00:29:39 +00:00
|
|
|
return VoxelServer::get_octree_lod_block_region_extent(_update_data->settings.lod_distance, get_mesh_block_size());
|
2019-05-08 00:57:34 +01:00
|
|
|
}
|
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
Vector3i VoxelLodTerrain::voxel_to_data_block_position(Vector3 vpos, int lod_index) const {
|
|
|
|
ERR_FAIL_COND_V(lod_index < 0, Vector3i());
|
|
|
|
ERR_FAIL_COND_V(lod_index >= get_lod_count(), Vector3i());
|
2021-10-13 20:28:20 +01:00
|
|
|
const VoxelDataLodMap::Lod &lod = _data->lods[lod_index];
|
2022-03-19 19:43:33 +00:00
|
|
|
const Vector3i bpos = lod.map.voxel_to_block(Vector3iUtil::from_floored(vpos)) >> lod_index;
|
2021-12-13 21:38:10 +00:00
|
|
|
return bpos;
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
Vector3i VoxelLodTerrain::voxel_to_mesh_block_position(Vector3 vpos, int lod_index) const {
|
|
|
|
ERR_FAIL_COND_V(lod_index < 0, Vector3i());
|
|
|
|
ERR_FAIL_COND_V(lod_index >= get_lod_count(), Vector3i());
|
2022-03-19 19:43:33 +00:00
|
|
|
const unsigned int mesh_block_size_po2 = _update_data->settings.mesh_block_size_po2;
|
2022-03-20 22:04:53 +00:00
|
|
|
const Vector3i bpos = (Vector3iUtil::from_floored(vpos) >> mesh_block_size_po2) >> lod_index;
|
2021-12-13 21:38:10 +00:00
|
|
|
return bpos;
|
2021-04-05 03:44:52 +01:00
|
|
|
}
|
|
|
|
|
2021-12-14 00:10:09 +00:00
|
|
|
void VoxelLodTerrain::set_process_callback(ProcessCallback mode) {
|
|
|
|
_process_callback = mode;
|
|
|
|
set_process(_process_callback == PROCESS_CALLBACK_IDLE);
|
|
|
|
set_physics_process(_process_callback == PROCESS_CALLBACK_PHYSICS);
|
2020-10-30 19:02:00 +00:00
|
|
|
}
|
|
|
|
|
2019-05-04 00:02:10 +01:00
|
|
|
void VoxelLodTerrain::_notification(int p_what) {
|
|
|
|
switch (p_what) {
|
2020-10-30 19:02:00 +00:00
|
|
|
// TODO Should use NOTIFICATION_INTERNAL_PROCESS instead?
|
2019-05-04 00:02:10 +01:00
|
|
|
case NOTIFICATION_PROCESS:
|
2021-12-14 00:10:09 +00:00
|
|
|
if (_process_callback == PROCESS_CALLBACK_IDLE) {
|
2020-10-30 19:02:00 +00:00
|
|
|
// 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());
|
2021-03-02 22:49:42 +00:00
|
|
|
_process(get_process_delta_time());
|
2020-10-30 19:02:00 +00:00
|
|
|
}
|
2019-05-04 00:02:10 +01:00
|
|
|
break;
|
|
|
|
|
2020-10-30 19:02:00 +00:00
|
|
|
// TODO Should use NOTIFICATION_INTERNAL_PHYSICS_PROCESS instead?
|
|
|
|
case NOTIFICATION_PHYSICS_PROCESS:
|
2021-12-14 00:10:09 +00:00
|
|
|
if (_process_callback == PROCESS_CALLBACK_PHYSICS) {
|
2020-10-30 19:02:00 +00:00
|
|
|
// 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());
|
2021-03-02 22:49:42 +00:00
|
|
|
_process(get_physics_process_delta_time());
|
2020-10-30 19:02:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-05-04 00:02:10 +01:00
|
|
|
case NOTIFICATION_EXIT_TREE:
|
|
|
|
break;
|
|
|
|
|
2019-08-15 01:21:45 +01:00
|
|
|
case NOTIFICATION_ENTER_WORLD: {
|
2021-12-13 21:38:10 +00:00
|
|
|
World3D *world = *get_world_3d();
|
2022-03-15 00:29:39 +00:00
|
|
|
VoxelLodTerrainUpdateData::State &state = _update_data->state;
|
|
|
|
for (unsigned int lod_index = 0; lod_index < state.lods.size(); ++lod_index) {
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
|
|
|
mesh_map.for_each_block([world](VoxelMeshBlockVLT &block) { //
|
2022-03-20 19:16:58 +00:00
|
|
|
block.set_world(world);
|
2020-11-21 18:28:06 +00:00
|
|
|
});
|
2019-10-03 18:55:13 +01:00
|
|
|
}
|
2020-10-24 00:08:14 +01:00
|
|
|
#ifdef TOOLS_ENABLED
|
|
|
|
if (is_showing_gizmos()) {
|
|
|
|
_debug_renderer.set_world(is_visible_in_tree() ? world : nullptr);
|
|
|
|
}
|
|
|
|
#endif
|
2021-04-05 03:47:25 +01:00
|
|
|
// DEBUG
|
|
|
|
//set_show_gizmos(true);
|
2019-08-15 01:21:45 +01:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case NOTIFICATION_EXIT_WORLD: {
|
2022-03-15 00:29:39 +00:00
|
|
|
VoxelLodTerrainUpdateData::State &state = _update_data->state;
|
|
|
|
for (unsigned int lod_index = 0; lod_index < state.lods.size(); ++lod_index) {
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
|
|
|
mesh_map.for_each_block([](VoxelMeshBlockVLT &block) { //
|
2022-03-20 19:16:58 +00:00
|
|
|
block.set_world(nullptr);
|
2020-11-21 18:28:06 +00:00
|
|
|
});
|
2019-10-03 18:55:13 +01:00
|
|
|
}
|
2020-10-24 00:08:14 +01:00
|
|
|
#ifdef TOOLS_ENABLED
|
|
|
|
_debug_renderer.set_world(nullptr);
|
|
|
|
#endif
|
2019-08-15 01:21:45 +01:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case NOTIFICATION_VISIBILITY_CHANGED: {
|
2020-10-25 00:55:57 +01:00
|
|
|
const bool visible = is_visible();
|
2022-03-15 00:29:39 +00:00
|
|
|
VoxelLodTerrainUpdateData::State &state = _update_data->state;
|
|
|
|
|
|
|
|
for (unsigned int lod_index = 0; lod_index < state.lods.size(); ++lod_index) {
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
|
|
|
mesh_map.for_each_block([visible](VoxelMeshBlockVLT &block) { //
|
2022-03-20 19:16:58 +00:00
|
|
|
block.set_parent_visible(visible);
|
2022-03-15 00:29:39 +00:00
|
|
|
});
|
2019-10-03 18:55:13 +01:00
|
|
|
}
|
2022-03-15 00:29:39 +00:00
|
|
|
|
2020-10-24 00:08:14 +01:00
|
|
|
#ifdef TOOLS_ENABLED
|
|
|
|
if (is_showing_gizmos()) {
|
2021-12-13 21:38:10 +00:00
|
|
|
_debug_renderer.set_world(is_visible_in_tree() ? *get_world_3d() : nullptr);
|
2020-10-24 00:08:14 +01:00
|
|
|
}
|
|
|
|
#endif
|
2019-08-15 01:21:45 +01:00
|
|
|
} break;
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2020-10-24 03:22:02 +01:00
|
|
|
case NOTIFICATION_TRANSFORM_CHANGED: {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE_NAMED("VoxelLodTerrain::NOTIFICATION_TRANSFORM_CHANGED");
|
2020-10-29 00:53:04 +00:00
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
const Transform3D transform = get_global_transform();
|
2022-04-08 23:54:04 +01:00
|
|
|
VoxelServer::get_singleton().set_volume_transform(_volume_id, transform);
|
2020-10-25 00:55:57 +01:00
|
|
|
|
2020-10-24 03:22:02 +01:00
|
|
|
if (!is_inside_tree()) {
|
|
|
|
// The transform and other properties can be set by the scene loader,
|
|
|
|
// before we enter the tree
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
VoxelLodTerrainUpdateData::State &state = _update_data->state;
|
|
|
|
|
|
|
|
for (unsigned int lod_index = 0; lod_index < state.lods.size(); ++lod_index) {
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
|
|
|
mesh_map.for_each_block([&transform](VoxelMeshBlockVLT &block) { //
|
2022-03-20 19:16:58 +00:00
|
|
|
block.set_parent_transform(transform);
|
2022-03-15 00:29:39 +00:00
|
|
|
});
|
2020-10-24 03:22:02 +01:00
|
|
|
}
|
|
|
|
} break;
|
2019-05-04 00:02:10 +01:00
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-25 21:21:37 +00:00
|
|
|
Vector3 VoxelLodTerrain::get_local_viewer_pos() const {
|
2021-04-03 20:39:37 +01:00
|
|
|
// Pick this by default
|
2022-03-15 00:29:39 +00:00
|
|
|
Vector3 pos = _update_data->state.lods[0].last_viewer_data_block_pos << get_data_block_size_pow2();
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2020-12-19 23:16:10 +00:00
|
|
|
// TODO Support for multiple viewers, this is a placeholder implementation
|
2022-04-08 23:54:04 +01:00
|
|
|
VoxelServer::get_singleton().for_each_viewer( //
|
2021-12-13 21:38:10 +00:00
|
|
|
[&pos](const VoxelServer::Viewer &viewer, uint32_t viewer_id) { //
|
|
|
|
pos = viewer.world_position;
|
|
|
|
});
|
2020-10-25 21:21:37 +00:00
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
const Transform3D world_to_local = get_global_transform().affine_inverse();
|
2020-12-19 23:16:10 +00:00
|
|
|
pos = world_to_local.xform(pos);
|
|
|
|
return pos;
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
inline bool check_block_sizes(int data_block_size, int mesh_block_size) {
|
|
|
|
return (data_block_size == 16 || data_block_size == 32) && (mesh_block_size == 16 || mesh_block_size == 32) &&
|
|
|
|
mesh_block_size >= data_block_size;
|
2021-10-06 18:34:02 +01:00
|
|
|
}
|
2019-09-07 21:19:12 +01:00
|
|
|
|
2022-03-19 19:43:33 +00:00
|
|
|
// void VoxelLodTerrain::send_block_save_requests(Span<BlockToSave> blocks_to_save) {
|
|
|
|
// for (unsigned int i = 0; i < blocks_to_save.size(); ++i) {
|
|
|
|
// BlockToSave &b = blocks_to_save[i];
|
2022-04-06 23:26:54 +01:00
|
|
|
// ZN_PRINT_VERBOSE(String("Requesting save of block {0} lod {1}").format(varray(b.position, b.lod)));
|
2022-04-08 23:54:04 +01:00
|
|
|
// VoxelServer::get_singleton().request_voxel_block_save(_volume_id, b.voxels, b.position, b.lod);
|
2022-03-19 19:43:33 +00:00
|
|
|
// }
|
|
|
|
// }
|
2019-09-07 21:19:12 +01:00
|
|
|
|
2021-03-02 22:49:42 +00:00
|
|
|
void VoxelLodTerrain::_process(float delta) {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE();
|
2020-01-02 20:29:02 +00:00
|
|
|
|
2022-03-20 18:30:18 +00:00
|
|
|
_stats.dropped_block_loads = 0;
|
|
|
|
_stats.dropped_block_meshs = 0;
|
|
|
|
|
2019-05-04 00:02:10 +01:00
|
|
|
if (get_lod_count() == 0) {
|
|
|
|
// If there isn't a LOD 0, there is nothing to load
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-10-05 20:47:28 +01:00
|
|
|
// Get block loading responses
|
|
|
|
// Note: if block loading is too fast, this can cause stutters.
|
|
|
|
// It should only happen on first load, though.
|
2021-10-13 20:28:20 +01:00
|
|
|
//process_block_loading_responses();
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2021-10-05 20:47:28 +01:00
|
|
|
process_fading_blocks(delta);
|
2019-09-06 23:24:56 +01:00
|
|
|
|
2021-10-05 20:47:28 +01:00
|
|
|
// TODO This could go into time spread tasks too
|
2022-04-08 23:54:04 +01:00
|
|
|
process_deferred_collision_updates(VoxelServer::get_singleton().get_main_thread_time_budget_usec());
|
2021-10-05 20:47:28 +01:00
|
|
|
|
|
|
|
#ifdef TOOLS_ENABLED
|
|
|
|
if (is_showing_gizmos() && is_visible_in_tree()) {
|
|
|
|
update_gizmos();
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
2021-10-05 20:47:28 +01:00
|
|
|
#endif
|
2021-04-03 20:39:37 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
if (_update_data->task_is_complete) {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE();
|
2021-04-03 20:39:37 +01:00
|
|
|
|
2022-03-16 00:05:04 +00:00
|
|
|
apply_main_thread_update_tasks();
|
2021-04-03 20:39:37 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
// Get viewer location in voxel space
|
|
|
|
const Vector3 viewer_pos = get_local_viewer_pos();
|
2021-04-03 20:39:37 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
// TODO Optimization: pool tasks instead of allocating?
|
|
|
|
VoxelLodTerrainUpdateTask *task = memnew(VoxelLodTerrainUpdateTask(_data, _update_data, _streaming_dependency,
|
2022-04-08 23:54:04 +01:00
|
|
|
_meshing_dependency, VoxelServer::get_singleton().get_shared_viewers_data_from_default_world(),
|
2022-03-15 00:29:39 +00:00
|
|
|
viewer_pos, _instancer != nullptr, _volume_id, get_global_transform()));
|
2021-10-05 20:47:28 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->task_is_complete = false;
|
2021-10-05 20:47:28 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
if (_threaded_update_enabled) {
|
|
|
|
// Schedule task at the end, so it is less likely to have contention with other logic than if it was done at
|
|
|
|
// the beginnning of `_process`
|
2022-04-08 23:54:04 +01:00
|
|
|
VoxelServer::get_singleton().push_async_task(task);
|
2021-04-03 20:39:37 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
} else {
|
|
|
|
task->run(ThreadedTaskContext{ 0 });
|
|
|
|
memdelete(task);
|
2022-03-16 00:05:04 +00:00
|
|
|
apply_main_thread_update_tasks();
|
2021-04-03 20:39:37 +01:00
|
|
|
}
|
|
|
|
}
|
2021-10-05 20:47:28 +01:00
|
|
|
}
|
2021-04-03 20:39:37 +01:00
|
|
|
|
2022-03-16 00:05:04 +00:00
|
|
|
void VoxelLodTerrain::apply_main_thread_update_tasks() {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE();
|
2022-03-15 00:29:39 +00:00
|
|
|
// Dequeue outputs of the threadable part of the update for actions taking place on the main thread
|
2019-08-29 22:55:02 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
CRASH_COND(_update_data->task_is_complete == false);
|
2019-08-29 22:55:02 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
VoxelLodTerrainUpdateData::State &state = _update_data->state;
|
2019-08-29 22:55:02 +01:00
|
|
|
|
2022-03-19 19:43:33 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < _update_data->settings.lod_count; ++lod_index) {
|
|
|
|
VoxelLodTerrainUpdateData::Lod &lod = _update_data->state.lods[lod_index];
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
2022-03-19 19:43:33 +00:00
|
|
|
|
|
|
|
for (unsigned int i = 0; i < lod.mesh_blocks_to_activate.size(); ++i) {
|
|
|
|
const Vector3i bpos = lod.mesh_blocks_to_activate[i];
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshBlockVLT *block = mesh_map.get_block(bpos);
|
2022-03-19 19:43:33 +00:00
|
|
|
// Can be null if there is actually no surface at this location
|
|
|
|
if (block == nullptr) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//ERR_CONTINUE(block == nullptr);
|
2022-03-15 00:29:39 +00:00
|
|
|
set_mesh_block_active(*block, true);
|
2021-10-05 20:47:28 +01:00
|
|
|
}
|
|
|
|
|
2022-03-19 19:43:33 +00:00
|
|
|
for (unsigned int i = 0; i < lod.mesh_blocks_to_deactivate.size(); ++i) {
|
|
|
|
const Vector3i bpos = lod.mesh_blocks_to_deactivate[i];
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshBlockVLT *block = mesh_map.get_block(bpos);
|
2022-03-19 19:43:33 +00:00
|
|
|
// Can be null if there is actually no surface at this location
|
|
|
|
if (block == nullptr) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//ERR_CONTINUE(block == nullptr);
|
2022-03-15 00:29:39 +00:00
|
|
|
set_mesh_block_active(*block, false);
|
2021-10-05 20:47:28 +01:00
|
|
|
}
|
2019-08-29 22:55:02 +01:00
|
|
|
|
2022-03-19 19:43:33 +00:00
|
|
|
lod.mesh_blocks_to_activate.clear();
|
|
|
|
lod.mesh_blocks_to_deactivate.clear();
|
2019-08-29 22:55:02 +01:00
|
|
|
|
2022-03-26 15:59:03 +00:00
|
|
|
/*
|
2022-03-15 00:29:39 +00:00
|
|
|
#ifdef DEBUG_ENABLED
|
|
|
|
std::unordered_set<Vector3i> debug_removed_blocks;
|
|
|
|
#endif
|
2022-03-26 15:59:03 +00:00
|
|
|
*/
|
2019-08-29 22:55:02 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int i = 0; i < lod.mesh_blocks_to_unload.size(); ++i) {
|
|
|
|
const Vector3i bpos = lod.mesh_blocks_to_unload[i];
|
2022-03-19 19:43:33 +00:00
|
|
|
mesh_map.remove_block(bpos, BeforeUnloadMeshAction{ _shader_material_pool });
|
2020-01-23 00:37:13 +00:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_fading_blocks_per_lod[lod_index].erase(bpos);
|
2021-10-05 20:47:28 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
if (_instancer != nullptr) {
|
|
|
|
_instancer->on_mesh_block_exit(bpos, lod_index);
|
2021-10-05 20:47:28 +01:00
|
|
|
}
|
2022-03-26 15:59:03 +00:00
|
|
|
/*
|
2022-03-15 00:29:39 +00:00
|
|
|
#ifdef DEBUG_ENABLED
|
|
|
|
debug_removed_blocks.insert(bpos);
|
|
|
|
#endif
|
2022-03-26 15:59:03 +00:00
|
|
|
*/
|
2022-03-15 00:29:39 +00:00
|
|
|
// Blocks in the update queue will be cancelled in _process,
|
|
|
|
// because it's too expensive to linear-search all blocks for each block
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < lod.mesh_blocks_to_update_transitions.size(); ++i) {
|
|
|
|
const VoxelLodTerrainUpdateData::TransitionUpdate tu = lod.mesh_blocks_to_update_transitions[i];
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshBlockVLT *block = mesh_map.get_block(tu.block_position);
|
2022-03-19 19:43:33 +00:00
|
|
|
// Can be null if there is actually no surface at this location
|
2022-03-15 00:29:39 +00:00
|
|
|
if (block == nullptr) {
|
2022-03-19 19:43:33 +00:00
|
|
|
/*
|
2022-03-15 00:29:39 +00:00
|
|
|
#ifdef DEBUG_ENABLED
|
|
|
|
// If the block was removed for a different reason then it is unexpected
|
|
|
|
ERR_CONTINUE(debug_removed_blocks.find(tu.block_position) == debug_removed_blocks.end());
|
|
|
|
#endif
|
2022-04-06 23:26:54 +01:00
|
|
|
ZN_PRINT_VERBOSE(String("Skipping TransitionUpdate at {0} lod {1}, block not found")
|
2022-03-15 00:29:39 +00:00
|
|
|
.format(varray(tu.block_position, lod_index)));
|
2022-03-19 19:43:33 +00:00
|
|
|
*/
|
2022-03-15 00:29:39 +00:00
|
|
|
continue;
|
2021-09-09 18:53:22 +01:00
|
|
|
}
|
2022-03-15 00:29:39 +00:00
|
|
|
//CRASH_COND(block == nullptr);
|
|
|
|
if (block->active) {
|
|
|
|
block->set_transition_mask(tu.transition_mask);
|
2021-09-09 18:53:22 +01:00
|
|
|
}
|
2021-10-05 20:47:28 +01:00
|
|
|
}
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
lod.mesh_blocks_to_unload.clear();
|
|
|
|
lod.mesh_blocks_to_update_transitions.clear();
|
|
|
|
}
|
2019-08-29 22:55:02 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
// Remove completed async edits
|
|
|
|
unordered_remove_if(state.running_async_edits, [this](VoxelLodTerrainUpdateData::RunningAsyncEdit &e) {
|
|
|
|
if (e.tracker->is_complete()) {
|
|
|
|
if (e.tracker->has_next_tasks()) {
|
|
|
|
ERR_PRINT("Completed async edit had next tasks?");
|
2021-10-05 20:47:28 +01:00
|
|
|
}
|
2022-03-15 00:29:39 +00:00
|
|
|
post_edit_area(e.box);
|
|
|
|
return true;
|
2022-02-12 18:09:46 +00:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
} else if (e.tracker->is_aborted()) {
|
|
|
|
return true;
|
|
|
|
}
|
2019-12-31 16:48:46 +00:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
return false;
|
|
|
|
});
|
2022-03-20 18:30:18 +00:00
|
|
|
|
|
|
|
_stats.blocked_lods = state.stats.blocked_lods;
|
|
|
|
_stats.time_detect_required_blocks = state.stats.time_detect_required_blocks;
|
|
|
|
_stats.time_io_requests = state.stats.time_io_requests;
|
|
|
|
_stats.time_mesh_requests = state.stats.time_mesh_requests;
|
|
|
|
_stats.time_update_task = state.stats.time_total;
|
2022-03-15 00:29:39 +00:00
|
|
|
}
|
2019-05-08 20:53:51 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
template <typename T>
|
|
|
|
bool thread_safe_contains(const std::unordered_set<T> &set, T v, BinaryMutex &mutex) {
|
|
|
|
MutexLock lock(mutex);
|
2022-03-19 20:56:13 +00:00
|
|
|
typename std::unordered_set<T>::const_iterator it = set.find(v);
|
2022-03-15 00:29:39 +00:00
|
|
|
return it != set.end();
|
2021-10-05 20:47:28 +01:00
|
|
|
}
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
void VoxelLodTerrain::apply_data_block_response(VoxelServer::BlockDataOutput &ob) {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE();
|
2019-05-05 01:09:12 +01:00
|
|
|
|
2022-01-31 21:23:39 +00:00
|
|
|
if (ob.type == VoxelServer::BlockDataOutput::TYPE_SAVED) {
|
2021-10-13 20:28:20 +01:00
|
|
|
// 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
|
2020-01-02 20:29:02 +00:00
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
// TODO Now that's the case. Use version? Or just keep copying?
|
|
|
|
return;
|
|
|
|
}
|
2020-01-02 20:29:02 +00:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
if (ob.lod >= _update_data->settings.lod_count) {
|
2021-10-13 20:28:20 +01:00
|
|
|
// That block was requested at a time where LOD was higher... drop it
|
|
|
|
++_stats.dropped_block_loads;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initial load will be true when we requested data without specifying specific positions,
|
|
|
|
// so we wouldn't know which ones to expect. This is the case of full load mode.
|
2022-03-15 00:29:39 +00:00
|
|
|
VoxelLodTerrainUpdateData::Lod &lod = _update_data->state.lods[ob.lod];
|
2021-10-13 20:28:20 +01:00
|
|
|
if (!ob.initial_load) {
|
2022-03-15 00:29:39 +00:00
|
|
|
if (!thread_safe_contains(lod.loading_blocks, ob.position, lod.loading_blocks_mutex)) {
|
2021-10-13 20:28:20 +01:00
|
|
|
// That block was not requested, or is no longer needed. drop it...
|
2022-04-09 15:00:34 +01:00
|
|
|
ZN_PRINT_VERBOSE(format("Ignoring block {} lod {}, it was not in loading blocks", ob.position, ob.lod));
|
2021-10-05 20:47:28 +01:00
|
|
|
++_stats.dropped_block_loads;
|
2021-10-13 20:28:20 +01:00
|
|
|
return;
|
2021-10-05 20:47:28 +01:00
|
|
|
}
|
2021-10-13 20:28:20 +01:00
|
|
|
}
|
2020-02-04 15:37:52 +01:00
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
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...
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
// print_line(String("Received a block loading drop while we were still expecting it: lod{0} ({1},
|
|
|
|
//{2}, {3})") .format(varray(ob.lod, ob.position.x, ob.position.y,
|
|
|
|
//ob.position.z)));
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
++_stats.dropped_block_loads;
|
|
|
|
return;
|
|
|
|
}
|
2019-05-09 22:00:54 +01:00
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
if (ob.voxels != nullptr) {
|
|
|
|
VoxelDataLodMap::Lod &data_lod = _data->lods[ob.lod];
|
2020-01-15 21:04:23 +00:00
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
if (ob.voxels->get_size() != Vector3iUtil::create(data_lod.map.get_block_size())) {
|
2021-10-13 20:28:20 +01:00
|
|
|
// Voxel block size is incorrect, drop it
|
|
|
|
ERR_PRINT("Block size obtained from stream is different from expected size");
|
2021-10-05 20:47:28 +01:00
|
|
|
++_stats.dropped_block_loads;
|
2021-10-13 20:28:20 +01:00
|
|
|
return;
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
2021-02-08 01:22:09 +00:00
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
// Store buffer
|
|
|
|
RWLockWrite wlock(data_lod.map_lock);
|
|
|
|
VoxelDataBlock *block = data_lod.map.set_block_buffer(ob.position, ob.voxels, false);
|
|
|
|
CRASH_COND(block == nullptr);
|
2022-01-31 21:23:39 +00:00
|
|
|
block->set_edited(ob.type == VoxelServer::BlockDataOutput::TYPE_LOADED);
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
{
|
2022-03-19 19:43:33 +00:00
|
|
|
// We have to do this after adding the block to the map, otherwise there would be a small period of time where
|
|
|
|
// the threaded update task could request the block again needlessly
|
2022-03-15 00:29:39 +00:00
|
|
|
MutexLock lock(lod.loading_blocks_mutex);
|
|
|
|
lod.loading_blocks.erase(ob.position);
|
2021-10-13 20:28:20 +01:00
|
|
|
}
|
2020-01-02 20:29:02 +00:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
if (_instancer != nullptr && ob.instances != nullptr) {
|
|
|
|
_instancer->on_data_block_loaded(ob.position, ob.lod, std::move(ob.instances));
|
2021-09-25 00:02:41 +01:00
|
|
|
}
|
|
|
|
}
|
2020-10-24 03:22:02 +01:00
|
|
|
|
2021-09-25 00:02:41 +01:00
|
|
|
void VoxelLodTerrain::apply_mesh_update(const VoxelServer::BlockMeshOutput &ob) {
|
|
|
|
// 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?
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE();
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2021-09-25 00:02:41 +01:00
|
|
|
ERR_FAIL_COND(!is_inside_tree());
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
CRASH_COND(_update_data == nullptr);
|
|
|
|
VoxelLodTerrainUpdateData &update_data = *_update_data;
|
|
|
|
|
|
|
|
if (ob.lod >= update_data.settings.lod_count) {
|
2021-09-25 00:02:41 +01:00
|
|
|
// Sorry, LOD configuration changed, drop that mesh
|
|
|
|
++_stats.dropped_block_meshs;
|
|
|
|
return;
|
|
|
|
}
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2022-03-19 19:43:33 +00:00
|
|
|
uint8_t transition_mask;
|
2022-03-20 22:03:19 +00:00
|
|
|
bool active;
|
2022-03-15 00:29:39 +00:00
|
|
|
{
|
2022-03-19 19:43:33 +00:00
|
|
|
VoxelLodTerrainUpdateData::Lod &lod = update_data.state.lods[ob.lod];
|
|
|
|
RWLockRead rlock(lod.mesh_map_state.map_lock);
|
|
|
|
auto mesh_block_state_it = lod.mesh_map_state.map.find(ob.position);
|
|
|
|
if (mesh_block_state_it == lod.mesh_map_state.map.end()) {
|
|
|
|
// That block is no longer loaded in the update map, drop the result
|
2022-03-15 00:29:39 +00:00
|
|
|
++_stats.dropped_block_meshs;
|
|
|
|
return;
|
|
|
|
}
|
2022-03-19 19:43:33 +00:00
|
|
|
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
|
2022-04-06 23:26:54 +01:00
|
|
|
ZN_PRINT_VERBOSE("Received a block mesh drop while we were still expecting it");
|
2022-03-19 19:43:33 +00:00
|
|
|
++_stats.dropped_block_meshs;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
transition_mask = mesh_block_state_it->second.transition_mask;
|
|
|
|
// The update task could be running at the same time, so we need to do this atomically.
|
|
|
|
// The state can become "up to date" only if no other unsent update was pending.
|
|
|
|
VoxelLodTerrainUpdateData::MeshState expected = VoxelLodTerrainUpdateData::MESH_UPDATE_SENT;
|
|
|
|
mesh_block_state_it->second.state.compare_exchange_strong(expected, VoxelLodTerrainUpdateData::MESH_UP_TO_DATE);
|
2022-03-20 22:03:19 +00:00
|
|
|
active = mesh_block_state_it->second.active;
|
2021-09-25 00:02:41 +01:00
|
|
|
}
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
// -------- Part where we invoke Godot functions ---------
|
|
|
|
// As far as I know, this is not yet threadable efficiently, for the most part.
|
|
|
|
// By that, I mean being able to call into RenderingServer and PhysicsServer,
|
|
|
|
// without inducing a stall of the main thread.
|
2019-05-09 19:11:17 +01:00
|
|
|
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[ob.lod];
|
|
|
|
VoxelMeshBlockVLT *block = mesh_map.get_block(ob.position);
|
2022-03-19 19:43:33 +00:00
|
|
|
|
2022-02-06 22:26:30 +00:00
|
|
|
const VoxelMesher::Output &mesh_data = ob.surfaces;
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2022-02-06 22:26:30 +00:00
|
|
|
Ref<ArrayMesh> mesh =
|
|
|
|
build_mesh(to_span_const(mesh_data.surfaces), mesh_data.primitive_type, mesh_data.mesh_flags, _material);
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2022-03-19 19:43:33 +00:00
|
|
|
if (mesh.is_null()) {
|
|
|
|
if (block != nullptr) {
|
|
|
|
// No surface anymore in this block, destroy it
|
|
|
|
// TODO Factor removal in a function, it's done in a few places
|
|
|
|
mesh_map.remove_block(ob.position, BeforeUnloadMeshAction{ _shader_material_pool });
|
|
|
|
if (_instancer != nullptr) {
|
|
|
|
_instancer->on_mesh_block_exit(ob.position, ob.lod);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (block == nullptr) {
|
2022-03-20 22:04:53 +00:00
|
|
|
block = memnew(VoxelMeshBlockVLT(ob.position, get_mesh_block_size(), ob.lod));
|
2022-03-20 22:03:19 +00:00
|
|
|
block->active = active;
|
|
|
|
block->set_visible(active);
|
2022-03-19 19:43:33 +00:00
|
|
|
mesh_map.set_block(ob.position, block);
|
|
|
|
}
|
|
|
|
|
2021-09-25 00:02:41 +01:00
|
|
|
bool has_collision = _generate_collisions;
|
|
|
|
if (has_collision && _collision_lod_count != 0) {
|
|
|
|
has_collision = ob.lod < _collision_lod_count;
|
|
|
|
}
|
2020-12-29 22:25:22 +00:00
|
|
|
|
2022-03-19 19:43:33 +00:00
|
|
|
// TODO Is this boolean needed anymore now that we create blocks only if a surface is present?
|
2021-09-25 00:02:41 +01:00
|
|
|
if (block->got_first_mesh_update == false) {
|
|
|
|
block->got_first_mesh_update = true;
|
2021-04-03 20:39:37 +01:00
|
|
|
|
2021-09-25 00:02:41 +01:00
|
|
|
// TODO Need a more generic API for this kind of stuff
|
|
|
|
if (_instancer != nullptr && ob.surfaces.surfaces.size() > 0) {
|
|
|
|
// TODO The mesh could come from an edited region!
|
|
|
|
// We would have to know if specific voxels got edited, or different from the generator
|
|
|
|
_instancer->on_mesh_block_enter(ob.position, ob.lod, ob.surfaces.surfaces[0]);
|
|
|
|
}
|
2021-04-03 20:39:37 +01:00
|
|
|
|
2021-09-25 00:02:41 +01:00
|
|
|
// Lazy initialization
|
2021-04-03 20:39:37 +01:00
|
|
|
|
2021-09-25 00:02:41 +01:00
|
|
|
//print_line(String("Adding block {0} at lod {1}").format(varray(eo.block_position.to_vec3(), eo.lod)));
|
|
|
|
//set_mesh_block_active(*block, false);
|
|
|
|
block->set_parent_visible(is_visible());
|
2021-12-13 21:38:10 +00:00
|
|
|
block->set_world(get_world_3d());
|
2021-04-03 20:39:37 +01:00
|
|
|
|
2021-09-25 00:02:41 +01:00
|
|
|
Ref<ShaderMaterial> shader_material = _material;
|
|
|
|
if (shader_material.is_valid() && block->get_shader_material().is_null()) {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE_NAMED("Add ShaderMaterial");
|
2021-04-03 20:39:37 +01:00
|
|
|
|
2021-09-25 00:02:41 +01:00
|
|
|
// Pooling shader materials is necessary for now, to avoid stuttering in the editor.
|
|
|
|
// Due to a signal used to keep the inspector up to date, even though these
|
|
|
|
// material copies will never be seen in the inspector
|
|
|
|
// See https://github.com/godotengine/godot/issues/34741
|
|
|
|
Ref<ShaderMaterial> sm;
|
|
|
|
if (_shader_material_pool.size() > 0) {
|
|
|
|
sm = _shader_material_pool.back();
|
|
|
|
// The joys of pooling materials
|
2022-04-09 15:15:22 +01:00
|
|
|
sm->set_shader_param(VoxelStringNames::get_singleton().u_transition_mask, 0);
|
2021-09-25 00:02:41 +01:00
|
|
|
_shader_material_pool.pop_back();
|
|
|
|
} else {
|
|
|
|
sm = shader_material->duplicate(false);
|
2020-12-29 22:25:22 +00:00
|
|
|
}
|
|
|
|
|
2021-09-25 00:02:41 +01:00
|
|
|
// Set individual shader material, because each block can have dynamic parameters,
|
|
|
|
// used to smooth seams without re-uploading meshes and allow to implement LOD fading
|
|
|
|
block->set_shader_material(sm);
|
|
|
|
}
|
2022-03-19 19:43:33 +00:00
|
|
|
|
|
|
|
block->set_transition_mask(transition_mask);
|
2021-09-25 00:02:41 +01:00
|
|
|
}
|
2020-10-24 03:22:02 +01:00
|
|
|
|
2021-12-16 00:11:11 +00:00
|
|
|
block->set_mesh(mesh, DirectMeshInstance::GIMode(get_gi_mode()));
|
2021-09-25 00:02:41 +01:00
|
|
|
{
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE_NAMED("Transition meshes");
|
2021-09-25 00:02:41 +01:00
|
|
|
for (unsigned int dir = 0; dir < mesh_data.transition_surfaces.size(); ++dir) {
|
2022-02-06 22:26:30 +00:00
|
|
|
Ref<ArrayMesh> transition_mesh = build_mesh(to_span_const(mesh_data.transition_surfaces[dir]),
|
|
|
|
mesh_data.primitive_type, mesh_data.mesh_flags, _material);
|
2021-02-18 19:50:47 +00:00
|
|
|
|
2021-12-16 00:11:11 +00:00
|
|
|
block->set_transition_mesh(transition_mesh, dir, DirectMeshInstance::GIMode(get_gi_mode()));
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
2021-09-25 00:02:41 +01:00
|
|
|
}
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2021-09-25 00:02:41 +01:00
|
|
|
const uint32_t now = get_ticks_msec();
|
|
|
|
if (has_collision) {
|
|
|
|
if (_collision_update_delay == 0 ||
|
|
|
|
static_cast<int>(now - block->last_collider_update_time) > _collision_update_delay) {
|
2022-02-06 21:26:48 +00:00
|
|
|
block->set_collision_mesh(to_span_const(mesh_data.surfaces), get_tree()->is_debugging_collisions_hint(),
|
|
|
|
this, _collision_margin);
|
2021-09-25 00:02:41 +01:00
|
|
|
block->set_collision_layer(_collision_layer);
|
|
|
|
block->set_collision_mask(_collision_mask);
|
|
|
|
block->last_collider_update_time = now;
|
|
|
|
block->has_deferred_collider_update = false;
|
|
|
|
block->deferred_collider_data.clear();
|
|
|
|
} else {
|
|
|
|
if (!block->has_deferred_collider_update) {
|
2022-03-15 00:29:39 +00:00
|
|
|
_deferred_collision_updates_per_lod[ob.lod].push_back(ob.position);
|
2021-09-25 00:02:41 +01:00
|
|
|
block->has_deferred_collider_update = true;
|
|
|
|
}
|
2022-02-06 22:26:30 +00:00
|
|
|
// TODO Optimization: could avoid the small allocation.
|
|
|
|
// It's usually a small vectors with a handful of elements.
|
|
|
|
// The caller providing `mesh_data` doesnt use `mesh_data` later so we could have moved the vector,
|
|
|
|
// but at the moment it's passed with `const` so that isn't possible. Indeed we are only going to read the
|
|
|
|
// data, but `const` also means the structure holding it is read-only as well.
|
2021-09-25 00:02:41 +01:00
|
|
|
block->deferred_collider_data = mesh_data.surfaces;
|
2020-01-02 20:29:02 +00:00
|
|
|
}
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2021-09-25 00:02:41 +01:00
|
|
|
block->set_parent_transform(get_global_transform());
|
2019-05-05 01:09:12 +01:00
|
|
|
}
|
|
|
|
|
2021-02-18 19:50:47 +00:00
|
|
|
void VoxelLodTerrain::process_deferred_collision_updates(uint32_t timeout_msec) {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE();
|
2021-02-18 19:50:47 +00:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
const unsigned int lod_count = _update_data->settings.lod_count;
|
2021-02-18 19:50:47 +00:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < lod_count; ++lod_index) {
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
2022-03-15 00:29:39 +00:00
|
|
|
std::vector<Vector3i> &deferred_collision_updates = _deferred_collision_updates_per_lod[lod_index];
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < deferred_collision_updates.size(); ++i) {
|
|
|
|
const Vector3i block_pos = deferred_collision_updates[i];
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshBlockVLT *block = mesh_map.get_block(block_pos);
|
2021-02-18 19:50:47 +00:00
|
|
|
|
|
|
|
if (block == nullptr || block->has_deferred_collider_update == false) {
|
|
|
|
// Block was unloaded or no longer needs a collision update
|
2022-03-15 00:29:39 +00:00
|
|
|
unordered_remove(deferred_collision_updates, i);
|
2021-02-18 19:50:47 +00:00
|
|
|
--i;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint32_t now = get_ticks_msec();
|
|
|
|
|
2021-02-19 01:51:06 +00:00
|
|
|
if (static_cast<int>(now - block->last_collider_update_time) > _collision_update_delay) {
|
2022-02-06 21:26:48 +00:00
|
|
|
block->set_collision_mesh(to_span_const(block->deferred_collider_data),
|
|
|
|
get_tree()->is_debugging_collisions_hint(), this, _collision_margin);
|
2021-05-09 20:49:45 +01:00
|
|
|
block->set_collision_layer(_collision_layer);
|
|
|
|
block->set_collision_mask(_collision_mask);
|
2021-02-18 19:50:47 +00:00
|
|
|
block->last_collider_update_time = now;
|
|
|
|
block->has_deferred_collider_update = false;
|
|
|
|
block->deferred_collider_data.clear();
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
unordered_remove(deferred_collision_updates, i);
|
2021-02-18 19:50:47 +00:00
|
|
|
--i;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We always process at least one, then we to check the timeout
|
|
|
|
if (get_ticks_msec() >= timeout_msec) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-04 19:20:36 +01:00
|
|
|
void VoxelLodTerrain::abort_async_edits() {
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
VoxelLodTerrainUpdateData::State &state = _update_data->state;
|
|
|
|
|
|
|
|
for (auto it = state.pending_async_edits.begin(); it != state.pending_async_edits.end(); ++it) {
|
|
|
|
VoxelLodTerrainUpdateData::AsyncEdit &e = *it;
|
2021-10-13 20:28:20 +01:00
|
|
|
CRASH_COND(e.task == nullptr);
|
|
|
|
memdelete(e.task);
|
2021-10-04 19:20:36 +01:00
|
|
|
}
|
2022-03-15 00:29:39 +00:00
|
|
|
state.pending_async_edits.clear();
|
|
|
|
state.running_async_edits.clear();
|
2021-10-13 20:28:20 +01:00
|
|
|
// Can't cancel edits which are already running on the thread pool,
|
|
|
|
// so the caller of this function must ensure none of them are running, or none will have an effect
|
2021-10-04 19:20:36 +01:00
|
|
|
}
|
|
|
|
|
2021-03-02 22:49:42 +00:00
|
|
|
void VoxelLodTerrain::process_fading_blocks(float delta) {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE();
|
2021-03-02 22:49:42 +00:00
|
|
|
|
|
|
|
const float speed = _lod_fade_duration < 0.001f ? 99999.f : delta / _lod_fade_duration;
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < _fading_blocks_per_lod.size(); ++lod_index) {
|
2022-03-20 22:04:53 +00:00
|
|
|
Map<Vector3i, VoxelMeshBlockVLT *> &fading_blocks = _fading_blocks_per_lod[lod_index];
|
2021-03-02 22:49:42 +00:00
|
|
|
|
2022-03-20 22:04:53 +00:00
|
|
|
Map<Vector3i, VoxelMeshBlockVLT *>::Element *e = fading_blocks.front();
|
2021-03-02 22:49:42 +00:00
|
|
|
|
|
|
|
while (e != nullptr) {
|
2022-03-20 22:04:53 +00:00
|
|
|
VoxelMeshBlockVLT *block = e->value();
|
2021-03-12 23:35:51 +00:00
|
|
|
// The collection of fading blocks must only contain fading blocks
|
2022-03-20 22:04:53 +00:00
|
|
|
ERR_FAIL_COND(block->fading_state == VoxelMeshBlockVLT::FADING_NONE);
|
2021-03-12 23:35:51 +00:00
|
|
|
|
2021-03-02 22:49:42 +00:00
|
|
|
const bool finished = block->update_fading(speed);
|
|
|
|
|
|
|
|
if (finished) {
|
2022-03-20 22:04:53 +00:00
|
|
|
Map<Vector3i, VoxelMeshBlockVLT *>::Element *next = e->next();
|
2022-03-15 00:29:39 +00:00
|
|
|
fading_blocks.erase(e);
|
2021-03-02 22:49:42 +00:00
|
|
|
e = next;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
e = e->next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-29 22:33:30 +00:00
|
|
|
void VoxelLodTerrain::set_instancer(VoxelInstancer *instancer) {
|
|
|
|
if (_instancer != nullptr && instancer != nullptr) {
|
|
|
|
ERR_FAIL_COND_MSG(_instancer != nullptr, "No more than one VoxelInstancer per terrain");
|
|
|
|
}
|
|
|
|
_instancer = instancer;
|
|
|
|
}
|
|
|
|
|
2021-02-14 21:55:31 +00:00
|
|
|
// This function is primarily intented for editor use cases at the moment.
|
|
|
|
// It will be slower than using the instancing generation events,
|
|
|
|
// because it has to query VisualServer, which then allocates and decodes vertex buffers (assuming they are cached).
|
2021-04-03 20:39:37 +01:00
|
|
|
Array VoxelLodTerrain::get_mesh_block_surface(Vector3i block_pos, int lod_index) const {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE();
|
2022-03-15 00:29:39 +00:00
|
|
|
|
|
|
|
const int lod_count = _update_data->settings.lod_count;
|
|
|
|
ERR_FAIL_COND_V(lod_index < 0 || lod_index >= lod_count, Array());
|
|
|
|
|
2022-03-20 22:04:53 +00:00
|
|
|
const VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
2022-03-15 00:29:39 +00:00
|
|
|
|
|
|
|
Ref<Mesh> mesh;
|
|
|
|
{
|
2022-03-20 22:04:53 +00:00
|
|
|
const VoxelMeshBlockVLT *block = mesh_map.get_block(block_pos);
|
2022-03-15 00:29:39 +00:00
|
|
|
if (block != nullptr) {
|
|
|
|
mesh = block->get_mesh();
|
2021-02-14 21:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
2022-03-15 00:29:39 +00:00
|
|
|
|
|
|
|
if (mesh.is_valid()) {
|
|
|
|
return mesh->surface_get_arrays(0);
|
|
|
|
}
|
|
|
|
|
2021-02-15 18:25:48 +00:00
|
|
|
return Array();
|
2021-02-14 21:55:31 +00:00
|
|
|
}
|
|
|
|
|
2022-02-06 21:26:48 +00:00
|
|
|
void VoxelLodTerrain::get_meshed_block_positions_at_lod(int lod_index, std::vector<Vector3i> &out_positions) const {
|
2022-03-15 00:29:39 +00:00
|
|
|
const int lod_count = _update_data->settings.lod_count;
|
|
|
|
ERR_FAIL_COND(lod_index < 0 || lod_index >= lod_count);
|
|
|
|
|
2022-03-20 22:04:53 +00:00
|
|
|
const VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
2022-03-15 00:29:39 +00:00
|
|
|
|
2022-03-20 22:04:53 +00:00
|
|
|
mesh_map.for_each_block([&out_positions](const VoxelMeshBlockVLT &block) {
|
2022-03-20 19:16:58 +00:00
|
|
|
if (block.has_mesh()) {
|
|
|
|
out_positions.push_back(block.position);
|
2021-02-15 21:45:12 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-09-07 21:19:12 +01:00
|
|
|
void VoxelLodTerrain::save_all_modified_blocks(bool with_copy) {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE();
|
2022-03-15 00:29:39 +00:00
|
|
|
|
|
|
|
// This is often called before quitting the game or forcing a global save.
|
|
|
|
// This could be part of the update task if async, but here we want it to be immediate.
|
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
|
|
|
|
VoxelLodTerrainUpdateTask::flush_pending_lod_edits(
|
2022-03-19 19:43:33 +00:00
|
|
|
_update_data->state, *_data, _generator, _update_data->settings.full_load_mode, get_mesh_block_size());
|
2019-09-07 21:19:12 +01:00
|
|
|
|
2022-03-19 19:43:33 +00:00
|
|
|
std::vector<VoxelLodTerrainUpdateData::BlockToSave> blocks_to_save;
|
2021-10-06 18:34:02 +01:00
|
|
|
|
2021-01-23 23:01:02 +00:00
|
|
|
if (_stream.is_valid()) {
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int i = 0; i < _data->lod_count; ++i) {
|
2021-10-13 20:28:20 +01:00
|
|
|
VoxelDataLodMap::Lod &data_lod = _data->lods[i];
|
|
|
|
RWLockRead rlock(data_lod.map_lock);
|
2021-01-23 23:01:02 +00:00
|
|
|
// That may cause a stutter, so should be used when the player won't notice
|
2022-02-20 21:35:35 +00:00
|
|
|
data_lod.map.for_each_block(ScheduleSaveAction{ blocks_to_save });
|
2021-01-23 23:01:02 +00:00
|
|
|
}
|
2021-02-07 17:22:50 +00:00
|
|
|
|
|
|
|
if (_instancer != nullptr && _stream->supports_instance_blocks()) {
|
|
|
|
_instancer->save_all_modified_blocks();
|
|
|
|
}
|
2019-09-07 21:19:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// And flush immediately
|
2022-03-22 20:51:56 +00:00
|
|
|
BufferedTaskScheduler task_scheduler;
|
2022-03-19 19:43:33 +00:00
|
|
|
VoxelLodTerrainUpdateTask::send_block_save_requests(
|
2022-03-22 20:51:56 +00:00
|
|
|
_volume_id, to_span(blocks_to_save), _streaming_dependency, get_data_block_size(), task_scheduler);
|
|
|
|
task_scheduler.flush();
|
2019-09-03 22:54:40 +01:00
|
|
|
}
|
|
|
|
|
2020-12-25 17:08:40 +00:00
|
|
|
const VoxelLodTerrain::Stats &VoxelLodTerrain::get_stats() const {
|
|
|
|
return _stats;
|
|
|
|
}
|
|
|
|
|
|
|
|
Dictionary VoxelLodTerrain::_b_get_statistics() const {
|
2019-05-05 01:09:12 +01:00
|
|
|
Dictionary d;
|
2019-08-25 18:47:43 +01:00
|
|
|
|
2022-04-06 01:37:47 +01:00
|
|
|
// const unsigned int lod_count = _update_data->settings.lod_count;
|
2022-03-15 00:29:39 +00:00
|
|
|
|
2022-04-06 01:31:51 +01:00
|
|
|
// int deferred_collision_updates = 0;
|
|
|
|
// for (unsigned int lod_index = 0; lod_index < lod_count; ++lod_index) {
|
|
|
|
// deferred_collision_updates += _deferred_collision_updates_per_lod[lod_index].size();
|
|
|
|
// }
|
2021-02-18 19:50:47 +00:00
|
|
|
|
2022-03-20 18:30:18 +00:00
|
|
|
// Breakdown of information and time spent in _process and the update task.
|
|
|
|
|
|
|
|
// Update task
|
2019-08-25 18:47:43 +01:00
|
|
|
d["time_detect_required_blocks"] = _stats.time_detect_required_blocks;
|
2022-03-20 18:30:18 +00:00
|
|
|
d["time_io_requests"] = _stats.time_io_requests;
|
|
|
|
d["time_mesh_requests"] = _stats.time_mesh_requests;
|
|
|
|
d["time_update_task"] = _stats.time_update_task;
|
|
|
|
d["blocked_lods"] = _stats.blocked_lods;
|
2019-08-25 18:47:43 +01:00
|
|
|
|
2022-03-20 18:30:18 +00:00
|
|
|
// Process
|
2019-05-12 16:30:45 +01:00
|
|
|
d["dropped_block_loads"] = _stats.dropped_block_loads;
|
|
|
|
d["dropped_block_meshs"] = _stats.dropped_block_meshs;
|
2019-05-05 01:09:12 +01:00
|
|
|
|
|
|
|
return d;
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
|
|
|
|
2020-08-14 20:33:09 +01:00
|
|
|
void VoxelLodTerrain::set_run_stream_in_editor(bool enable) {
|
2022-03-15 00:29:39 +00:00
|
|
|
if (enable == _update_data->settings.run_stream_in_editor) {
|
2020-08-14 20:33:09 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
_update_data->settings.run_stream_in_editor = enable;
|
2020-08-14 20:33:09 +01:00
|
|
|
|
|
|
|
if (Engine::get_singleton()->is_editor_hint()) {
|
2022-03-15 00:29:39 +00:00
|
|
|
if (enable) {
|
2020-08-14 20:33:09 +01:00
|
|
|
_on_stream_params_changed();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// This is expected to block the main thread until the streaming thread is done.
|
|
|
|
stop_streamer();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VoxelLodTerrain::is_stream_running_in_editor() const {
|
2022-03-15 00:29:39 +00:00
|
|
|
return _update_data->settings.run_stream_in_editor;
|
2020-08-14 20:33:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelLodTerrain::restart_stream() {
|
|
|
|
_on_stream_params_changed();
|
|
|
|
}
|
|
|
|
|
2020-12-18 21:01:50 +00:00
|
|
|
void VoxelLodTerrain::remesh_all_blocks() {
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
for (unsigned int lod_index = 0; lod_index < _update_data->settings.lod_count; ++lod_index) {
|
|
|
|
VoxelLodTerrainUpdateData::Lod &lod = _update_data->state.lods[lod_index];
|
2022-03-19 19:43:33 +00:00
|
|
|
for (auto it = lod.mesh_map_state.map.begin(); it != lod.mesh_map_state.map.end(); ++it) {
|
|
|
|
VoxelLodTerrainUpdateTask::schedule_mesh_update(it->second, it->first, lod.blocks_pending_update);
|
|
|
|
}
|
2020-12-18 21:01:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-31 17:10:54 +01:00
|
|
|
void VoxelLodTerrain::set_voxel_bounds(Box3i p_box) {
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
Box3i bounds_in_voxels =
|
2022-01-09 03:06:58 +00:00
|
|
|
p_box.clipped(Box3i::from_center_extents(Vector3i(), Vector3iUtil::create(constants::MAX_VOLUME_EXTENT)));
|
2020-10-22 22:43:31 +01:00
|
|
|
// Round to octree size
|
2021-04-03 20:39:37 +01:00
|
|
|
const int octree_size = get_mesh_block_size() << (get_lod_count() - 1);
|
2022-03-15 00:29:39 +00:00
|
|
|
bounds_in_voxels = bounds_in_voxels.snapped(octree_size);
|
2020-10-22 22:43:31 +01:00
|
|
|
// Can't have a smaller region than one octree
|
2021-12-13 21:38:10 +00:00
|
|
|
for (unsigned i = 0; i < Vector3iUtil::AXIS_COUNT; ++i) {
|
2022-03-15 00:29:39 +00:00
|
|
|
if (bounds_in_voxels.size[i] < octree_size) {
|
|
|
|
bounds_in_voxels.size[i] = octree_size;
|
2020-10-22 22:43:31 +01:00
|
|
|
}
|
|
|
|
}
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->settings.bounds_in_voxels = bounds_in_voxels;
|
2022-03-26 15:59:03 +00:00
|
|
|
_update_data->state.force_update_octrees_next_update = true;
|
2020-10-22 22:43:31 +01:00
|
|
|
}
|
|
|
|
|
2021-02-18 19:50:47 +00:00
|
|
|
void VoxelLodTerrain::set_collision_update_delay(int delay_msec) {
|
2022-01-03 23:14:18 +00:00
|
|
|
_collision_update_delay = math::clamp(delay_msec, 0, 4000);
|
2021-02-18 19:50:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int VoxelLodTerrain::get_collision_update_delay() const {
|
|
|
|
return _collision_update_delay;
|
|
|
|
}
|
|
|
|
|
2021-03-02 22:49:42 +00:00
|
|
|
void VoxelLodTerrain::set_lod_fade_duration(float seconds) {
|
2022-01-03 23:14:18 +00:00
|
|
|
_lod_fade_duration = math::clamp(seconds, 0.f, 1.f);
|
2021-03-02 22:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
float VoxelLodTerrain::get_lod_fade_duration() const {
|
|
|
|
return _lod_fade_duration;
|
|
|
|
}
|
|
|
|
|
2022-03-06 01:10:47 +00:00
|
|
|
#ifdef TOOLS_ENABLED
|
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
TypedArray<String> VoxelLodTerrain::get_configuration_warnings() const {
|
|
|
|
TypedArray<String> warnings = VoxelNode::get_configuration_warnings();
|
|
|
|
if (!warnings.is_empty()) {
|
|
|
|
return warnings;
|
2021-05-15 23:41:19 +01:00
|
|
|
}
|
|
|
|
Ref<VoxelMesher> mesher = get_mesher();
|
|
|
|
if (mesher.is_valid() && !mesher->supports_lod()) {
|
2021-12-13 21:38:10 +00:00
|
|
|
warnings.append(TTR("The assigned mesher does not support level of detail (LOD), results may be unexpected."));
|
2021-05-15 23:41:19 +01:00
|
|
|
}
|
2021-12-13 21:38:10 +00:00
|
|
|
return warnings;
|
2021-05-15 23:41:19 +01:00
|
|
|
}
|
|
|
|
|
2022-03-06 01:10:47 +00:00
|
|
|
#endif // TOOLS_ENABLED
|
|
|
|
|
2020-07-25 16:29:16 +01:00
|
|
|
void VoxelLodTerrain::_b_save_modified_blocks() {
|
|
|
|
save_all_modified_blocks(true);
|
|
|
|
}
|
|
|
|
|
2020-10-22 22:43:31 +01:00
|
|
|
void VoxelLodTerrain::_b_set_voxel_bounds(AABB aabb) {
|
2022-01-03 23:14:18 +00:00
|
|
|
ERR_FAIL_COND(!math::is_valid_size(aabb.size));
|
2021-12-13 21:38:10 +00:00
|
|
|
set_voxel_bounds(Box3i(Vector3iUtil::from_rounded(aabb.position), Vector3iUtil::from_rounded(aabb.size)));
|
2020-10-22 22:43:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
AABB VoxelLodTerrain::_b_get_voxel_bounds() const {
|
2021-05-31 17:10:54 +01:00
|
|
|
const Box3i b = get_voxel_bounds();
|
2021-12-13 21:38:10 +00:00
|
|
|
return AABB(b.pos, b.size);
|
2020-10-22 22:43:31 +01:00
|
|
|
}
|
|
|
|
|
2020-01-15 21:04:23 +00:00
|
|
|
// DEBUG LAND
|
|
|
|
|
2021-04-03 20:39:37 +01:00
|
|
|
Array VoxelLodTerrain::debug_raycast_mesh_block(Vector3 world_origin, Vector3 world_direction) const {
|
2021-12-13 21:38:10 +00:00
|
|
|
const Transform3D world_to_local = get_global_transform().affine_inverse();
|
2020-10-25 00:55:57 +01:00
|
|
|
Vector3 pos = world_to_local.xform(world_origin);
|
|
|
|
const Vector3 dir = world_to_local.basis.xform(world_direction);
|
|
|
|
const float max_distance = 256;
|
|
|
|
const float step = 2.f;
|
2020-01-05 23:27:43 +00:00
|
|
|
float distance = 0.f;
|
2022-03-15 00:29:39 +00:00
|
|
|
const unsigned int lod_count = _update_data->settings.lod_count;
|
2022-03-20 22:04:53 +00:00
|
|
|
const unsigned int mesh_block_size_po2 = _update_data->settings.mesh_block_size_po2;
|
2020-01-05 23:27:43 +00:00
|
|
|
|
|
|
|
Array hits;
|
|
|
|
while (distance < max_distance && hits.size() == 0) {
|
2022-03-20 22:04:53 +00:00
|
|
|
const Vector3i posi = Vector3iUtil::from_floored(pos);
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < lod_count; ++lod_index) {
|
2022-03-20 22:04:53 +00:00
|
|
|
const VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
|
|
|
const Vector3i bpos = (posi << mesh_block_size_po2) >> lod_index;
|
|
|
|
const VoxelMeshBlockVLT *block = mesh_map.get_block(bpos);
|
2020-01-05 23:27:43 +00:00
|
|
|
if (block != nullptr && block->is_visible() && block->has_mesh()) {
|
|
|
|
Dictionary d;
|
2021-12-13 21:38:10 +00:00
|
|
|
d["position"] = block->position;
|
2020-01-05 23:27:43 +00:00
|
|
|
d["lod"] = block->lod_index;
|
|
|
|
hits.append(d);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
distance += step;
|
|
|
|
pos += dir * step;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hits;
|
|
|
|
}
|
|
|
|
|
2021-04-05 03:46:15 +01:00
|
|
|
Dictionary VoxelLodTerrain::debug_get_data_block_info(Vector3 fbpos, int lod_index) const {
|
|
|
|
Dictionary d;
|
|
|
|
ERR_FAIL_COND_V(lod_index < 0, d);
|
2021-05-07 22:39:06 +01:00
|
|
|
ERR_FAIL_COND_V(lod_index >= get_lod_count(), d);
|
2021-04-05 03:46:15 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
const VoxelLodTerrainUpdateData::Lod &lod = _update_data->state.lods[lod_index];
|
2021-10-13 20:28:20 +01:00
|
|
|
|
|
|
|
const VoxelDataLodMap::Lod &data_lod = _data->lods[lod_index];
|
2021-12-13 21:38:10 +00:00
|
|
|
Vector3i bpos = Vector3iUtil::from_floored(fbpos);
|
2021-04-05 03:46:15 +01:00
|
|
|
|
|
|
|
int loading_state = 0;
|
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
bool has_block = false;
|
|
|
|
{
|
|
|
|
RWLockRead rlock(data_lod.map_lock);
|
|
|
|
has_block = data_lod.map.has_block(bpos);
|
|
|
|
}
|
|
|
|
if (has_block) {
|
2021-04-05 03:46:15 +01:00
|
|
|
loading_state = 2;
|
2022-03-15 00:29:39 +00:00
|
|
|
} else {
|
|
|
|
MutexLock lock(lod.loading_blocks_mutex);
|
|
|
|
if (lod.has_loading_block(bpos)) {
|
|
|
|
loading_state = 1;
|
|
|
|
}
|
2021-04-05 03:46:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
d["loading_state"] = loading_state;
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2021-04-03 20:39:37 +01:00
|
|
|
Dictionary VoxelLodTerrain::debug_get_mesh_block_info(Vector3 fbpos, int lod_index) const {
|
2020-01-15 21:04:23 +00:00
|
|
|
Dictionary d;
|
|
|
|
ERR_FAIL_COND_V(lod_index < 0, d);
|
2022-03-15 00:29:39 +00:00
|
|
|
const int lod_count = get_lod_count();
|
|
|
|
ERR_FAIL_COND_V(lod_index >= lod_count, d);
|
2020-01-15 21:04:23 +00:00
|
|
|
|
2022-03-19 19:43:33 +00:00
|
|
|
const Vector3i bpos = Vector3iUtil::from_floored(fbpos);
|
2020-01-15 21:04:23 +00:00
|
|
|
|
2021-04-05 03:46:15 +01:00
|
|
|
bool loaded = false;
|
2020-01-15 21:04:23 +00:00
|
|
|
bool meshed = false;
|
|
|
|
bool visible = false;
|
2021-04-05 03:46:15 +01:00
|
|
|
bool active = false;
|
2022-03-20 22:04:53 +00:00
|
|
|
int mesh_state = VoxelLodTerrainUpdateData::MESH_NEVER_UPDATED;
|
2022-03-15 00:29:39 +00:00
|
|
|
|
2022-03-20 22:04:53 +00:00
|
|
|
const VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
|
|
|
const VoxelMeshBlockVLT *block = mesh_map.get_block(bpos);
|
2020-01-15 21:04:23 +00:00
|
|
|
|
2021-04-05 03:46:15 +01:00
|
|
|
if (block != nullptr) {
|
2022-03-19 19:43:33 +00:00
|
|
|
int recomputed_transition_mask;
|
|
|
|
{
|
|
|
|
const VoxelLodTerrainUpdateData::Lod &lod = _update_data->state.lods[lod_index];
|
|
|
|
RWLockRead rlock(lod.mesh_map_state.map_lock);
|
|
|
|
recomputed_transition_mask = VoxelLodTerrainUpdateTask::get_transition_mask(
|
2022-03-20 22:04:53 +00:00
|
|
|
_update_data->state, bpos, block->lod_index, lod_count);
|
|
|
|
auto it = lod.mesh_map_state.map.find(bpos);
|
|
|
|
if (it != lod.mesh_map_state.map.end()) {
|
|
|
|
mesh_state = it->second.state;
|
|
|
|
}
|
2022-03-19 19:43:33 +00:00
|
|
|
}
|
|
|
|
|
2021-04-05 03:46:15 +01:00
|
|
|
loaded = true;
|
|
|
|
meshed = block->has_mesh();
|
2020-01-15 21:04:23 +00:00
|
|
|
visible = block->is_visible();
|
2021-04-05 03:46:15 +01:00
|
|
|
active = block->active;
|
2020-01-15 21:04:23 +00:00
|
|
|
d["transition_mask"] = block->get_transition_mask();
|
2021-04-05 03:46:15 +01:00
|
|
|
// This can highlight possible bugs between the current state and what it should be
|
2022-03-19 19:43:33 +00:00
|
|
|
d["recomputed_transition_mask"] = recomputed_transition_mask;
|
2020-01-15 21:04:23 +00:00
|
|
|
}
|
|
|
|
|
2021-04-05 03:46:15 +01:00
|
|
|
d["loaded"] = loaded;
|
2020-01-15 21:04:23 +00:00
|
|
|
d["meshed"] = meshed;
|
2021-04-05 03:46:15 +01:00
|
|
|
d["mesh_state"] = mesh_state;
|
2020-01-15 21:04:23 +00:00
|
|
|
d["visible"] = visible;
|
2021-04-05 03:46:15 +01:00
|
|
|
d["active"] = active;
|
2020-01-15 21:04:23 +00:00
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2021-03-11 22:55:52 +00:00
|
|
|
Array VoxelLodTerrain::debug_get_octree_positions() const {
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
2020-01-23 00:37:13 +00:00
|
|
|
Array positions;
|
2022-03-15 00:29:39 +00:00
|
|
|
const Map<Vector3i, VoxelLodTerrainUpdateData::OctreeItem> &octrees = _update_data->state.lod_octrees;
|
|
|
|
positions.resize(octrees.size());
|
2020-01-23 00:37:13 +00:00
|
|
|
int i = 0;
|
2022-03-15 00:29:39 +00:00
|
|
|
for (Map<Vector3i, VoxelLodTerrainUpdateData::OctreeItem>::Element *e = octrees.front(); e; e = e->next()) {
|
|
|
|
positions[i++] = e->key();
|
2020-01-23 00:37:13 +00:00
|
|
|
}
|
|
|
|
return positions;
|
|
|
|
}
|
|
|
|
|
2021-03-11 22:55:52 +00:00
|
|
|
Array VoxelLodTerrain::debug_get_octrees_detailed() const {
|
|
|
|
// [
|
|
|
|
// Vector3,
|
|
|
|
// Octree,
|
|
|
|
// ...
|
|
|
|
// ]
|
|
|
|
// Octree [
|
|
|
|
// state: State,
|
|
|
|
// Octree[8] or null
|
|
|
|
// ]
|
|
|
|
// State {
|
|
|
|
// 0: no block
|
|
|
|
// 1: no mesh
|
|
|
|
// 2: mesh
|
|
|
|
// }
|
|
|
|
|
|
|
|
struct L {
|
|
|
|
static void read_node(const LodOctree &octree, const LodOctree::Node *node, Vector3i position, int lod_index,
|
2022-03-15 00:29:39 +00:00
|
|
|
const VoxelLodTerrainUpdateData::State &state, Array &out_data) {
|
2021-03-11 22:55:52 +00:00
|
|
|
ERR_FAIL_COND(lod_index < 0);
|
2022-03-15 00:29:39 +00:00
|
|
|
Variant node_state;
|
2021-03-11 22:55:52 +00:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
const VoxelLodTerrainUpdateData::Lod &lod = state.lods[lod_index];
|
2022-03-19 19:43:33 +00:00
|
|
|
auto mesh_block_it = lod.mesh_map_state.map.find(position);
|
|
|
|
if (mesh_block_it == lod.mesh_map_state.map.end()) {
|
2022-03-15 00:29:39 +00:00
|
|
|
node_state = 0;
|
2021-03-11 22:55:52 +00:00
|
|
|
} else {
|
2022-03-19 19:43:33 +00:00
|
|
|
if (mesh_block_it->second.state == VoxelLodTerrainUpdateData::MESH_UP_TO_DATE) {
|
2022-03-15 00:29:39 +00:00
|
|
|
node_state = 2;
|
2021-03-11 22:55:52 +00:00
|
|
|
} else {
|
2022-03-15 00:29:39 +00:00
|
|
|
node_state = 1;
|
2021-03-11 22:55:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
out_data.append(node_state);
|
2021-03-11 22:55:52 +00:00
|
|
|
|
|
|
|
if (node->has_children()) {
|
|
|
|
Array children_data;
|
|
|
|
for (unsigned int i = 0; i < 8; ++i) {
|
|
|
|
Array child_data;
|
|
|
|
const LodOctree::Node *child = octree.get_child(node, i);
|
|
|
|
const Vector3i child_pos = LodOctree::get_child_position(position, i);
|
2022-03-15 00:29:39 +00:00
|
|
|
read_node(octree, child, child_pos, lod_index - 1, state, child_data);
|
2021-03-11 22:55:52 +00:00
|
|
|
children_data.append(child_data);
|
|
|
|
}
|
|
|
|
out_data.append(children_data);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
out_data.append(Variant());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
_update_data->wait_for_end_of_task();
|
|
|
|
|
|
|
|
const Map<Vector3i, VoxelLodTerrainUpdateData::OctreeItem> &octrees = _update_data->state.lod_octrees;
|
|
|
|
|
2021-03-11 22:55:52 +00:00
|
|
|
Array forest_data;
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
for (const Map<Vector3i, VoxelLodTerrainUpdateData::OctreeItem>::Element *e = octrees.front(); e; e = e->next()) {
|
2021-03-11 22:55:52 +00:00
|
|
|
const LodOctree &octree = e->value().octree;
|
|
|
|
const LodOctree::Node *root = octree.get_root();
|
|
|
|
Array root_data;
|
|
|
|
const Vector3i octree_pos = e->key();
|
2022-03-15 00:29:39 +00:00
|
|
|
L::read_node(octree, root, octree_pos, get_lod_count() - 1, _update_data->state, root_data);
|
2021-12-13 21:38:10 +00:00
|
|
|
forest_data.append(octree_pos);
|
2021-03-11 22:55:52 +00:00
|
|
|
forest_data.append(root_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return forest_data;
|
|
|
|
}
|
|
|
|
|
2020-10-24 00:08:14 +01:00
|
|
|
#ifdef TOOLS_ENABLED
|
|
|
|
|
|
|
|
void VoxelLodTerrain::update_gizmos() {
|
2022-04-09 15:33:08 +01:00
|
|
|
ZN_PROFILE_SCOPE();
|
2020-10-24 00:08:14 +01:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
// Hopefully this should not be skipped most of the time, because the task is started at the end of `_process`,
|
|
|
|
// and gizmos update before. So the task has about 16ms to complete. If it takes longer, it will skip.
|
|
|
|
// This allows us to avoid locking data structures.
|
|
|
|
if (_update_data->task_is_complete == false) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const VoxelLodTerrainUpdateData::State &state = _update_data->state;
|
|
|
|
|
2022-01-04 22:57:21 +00:00
|
|
|
DebugRenderer &dr = _debug_renderer;
|
2020-10-24 00:08:14 +01:00
|
|
|
dr.begin();
|
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
const Transform3D parent_transform = get_global_transform();
|
2022-03-15 00:29:39 +00:00
|
|
|
const unsigned int lod_count = get_lod_count();
|
2020-10-24 03:22:02 +01:00
|
|
|
|
2021-09-16 21:54:04 +01:00
|
|
|
// Octree bounds
|
|
|
|
if (_show_octree_bounds_gizmos) {
|
2022-03-15 00:29:39 +00:00
|
|
|
const int octree_size = 1 << LodOctree::get_octree_size_po2(get_mesh_block_size_pow2(), get_lod_count());
|
2021-09-16 21:54:04 +01:00
|
|
|
const Basis local_octree_basis = Basis().scaled(Vector3(octree_size, octree_size, octree_size));
|
2022-03-15 00:29:39 +00:00
|
|
|
for (Map<Vector3i, VoxelLodTerrainUpdateData::OctreeItem>::Element *e = state.lod_octrees.front(); e;
|
|
|
|
e = e->next()) {
|
2021-12-13 21:38:10 +00:00
|
|
|
const Transform3D local_transform(local_octree_basis, e->key() * octree_size);
|
2022-01-04 22:57:21 +00:00
|
|
|
dr.draw_box(parent_transform * local_transform, DebugColors::ID_OCTREE_BOUNDS);
|
2021-09-16 21:54:04 +01:00
|
|
|
}
|
2020-10-24 00:08:14 +01:00
|
|
|
}
|
|
|
|
|
2021-09-16 21:54:04 +01:00
|
|
|
// Volume bounds
|
|
|
|
if (_show_volume_bounds_gizmos) {
|
2022-03-15 00:29:39 +00:00
|
|
|
const Box3i bounds_in_voxels = get_voxel_bounds();
|
|
|
|
const float bounds_in_voxels_len = Vector3(bounds_in_voxels.size).length();
|
2021-09-16 21:54:04 +01:00
|
|
|
if (bounds_in_voxels_len < 10000) {
|
|
|
|
const Vector3 margin = Vector3(1, 1, 1) * bounds_in_voxels_len * 0.0025f;
|
2022-03-15 00:29:39 +00:00
|
|
|
const Vector3 size = bounds_in_voxels.size;
|
2021-12-13 21:38:10 +00:00
|
|
|
const Transform3D local_transform(
|
2022-03-15 00:29:39 +00:00
|
|
|
Basis().scaled(size + margin * 2.f), Vector3(bounds_in_voxels.pos) - margin);
|
2022-01-04 22:57:21 +00:00
|
|
|
dr.draw_box(parent_transform * local_transform, DebugColors::ID_VOXEL_BOUNDS);
|
2021-09-16 21:54:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Octree nodes
|
|
|
|
if (_show_octree_node_gizmos) {
|
|
|
|
// That can be expensive to draw
|
|
|
|
const int mesh_block_size = get_mesh_block_size();
|
2022-03-15 00:29:39 +00:00
|
|
|
const float lod_count_f = lod_count;
|
|
|
|
for (Map<Vector3i, VoxelLodTerrainUpdateData::OctreeItem>::Element *e = state.lod_octrees.front(); e;
|
|
|
|
e = e->next()) {
|
2021-09-16 21:54:04 +01:00
|
|
|
const LodOctree &octree = e->value().octree;
|
|
|
|
|
|
|
|
const Vector3i block_pos_maxlod = e->key();
|
2022-03-15 00:29:39 +00:00
|
|
|
const Vector3i block_offset_lod0 = block_pos_maxlod << (lod_count - 1);
|
2021-09-16 21:54:04 +01:00
|
|
|
|
2021-09-18 19:54:54 +01:00
|
|
|
octree.for_each_leaf([&dr, block_offset_lod0, mesh_block_size, parent_transform, lod_count_f](
|
2021-09-16 21:54:04 +01:00
|
|
|
Vector3i node_pos, int lod_index, const LodOctree::NodeData &data) {
|
|
|
|
//
|
|
|
|
const int size = mesh_block_size << lod_index;
|
|
|
|
const Vector3i voxel_pos = mesh_block_size * ((node_pos << lod_index) + block_offset_lod0);
|
2021-12-13 21:38:10 +00:00
|
|
|
const Transform3D local_transform(Basis().scaled(Vector3(size, size, size)), voxel_pos);
|
|
|
|
const Transform3D t = parent_transform * local_transform;
|
2021-09-18 19:54:54 +01:00
|
|
|
// Squaring because lower lod indexes are more interesting to see, so we give them more contrast.
|
|
|
|
// Also this might be better with sRGB?
|
2022-01-03 23:14:18 +00:00
|
|
|
const float g = math::squared(math::max(1.f - float(lod_index) / lod_count_f, 0.f));
|
2021-09-18 19:54:54 +01:00
|
|
|
dr.draw_box_mm(t, Color8(255, uint8_t(g * 254.f), 0, 255));
|
2021-09-16 21:54:04 +01:00
|
|
|
});
|
|
|
|
}
|
2020-10-24 00:08:14 +01:00
|
|
|
}
|
|
|
|
|
2021-10-03 01:48:07 +01:00
|
|
|
// Edited blocks
|
2022-03-15 00:29:39 +00:00
|
|
|
if (_show_edited_blocks && _edited_blocks_gizmos_lod_index < lod_count) {
|
2021-10-13 20:28:20 +01:00
|
|
|
const VoxelDataLodMap::Lod &data_lod = _data->lods[_edited_blocks_gizmos_lod_index];
|
2021-10-04 19:20:36 +01:00
|
|
|
const int data_block_size = get_data_block_size() << _edited_blocks_gizmos_lod_index;
|
2021-10-03 15:47:56 +01:00
|
|
|
const Basis basis(Basis().scaled(Vector3(data_block_size, data_block_size, data_block_size)));
|
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
RWLockRead rlock(data_lod.map_lock);
|
2022-03-20 19:28:50 +00:00
|
|
|
data_lod.map.for_each_block([&dr, parent_transform, data_block_size, basis](const VoxelDataBlock &block) {
|
|
|
|
const Transform3D local_transform(basis, block.position * data_block_size);
|
2021-12-13 21:38:10 +00:00
|
|
|
const Transform3D t = parent_transform * local_transform;
|
2022-03-20 19:28:50 +00:00
|
|
|
const Color8 c = Color8(block.is_modified() ? 255 : 0, 255, 0, 255);
|
2021-10-03 15:47:56 +01:00
|
|
|
dr.draw_box_mm(t, c);
|
2021-10-03 01:48:07 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-10-24 00:08:14 +01:00
|
|
|
dr.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelLodTerrain::set_show_gizmos(bool enable) {
|
|
|
|
_show_gizmos_enabled = enable;
|
|
|
|
if (_show_gizmos_enabled) {
|
2021-12-13 21:38:10 +00:00
|
|
|
_debug_renderer.set_world(is_visible_in_tree() ? *get_world_3d() : nullptr);
|
2020-10-24 00:08:14 +01:00
|
|
|
} else {
|
|
|
|
_debug_renderer.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-13 01:19:21 +00:00
|
|
|
void VoxelLodTerrain::set_show_octree_gizmos(bool enable) {
|
|
|
|
_show_octree_node_gizmos = enable;
|
|
|
|
}
|
|
|
|
|
2020-10-24 00:08:14 +01:00
|
|
|
#endif
|
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
// This copies at multiple LOD levels to debug mips
|
2021-12-13 21:38:10 +00:00
|
|
|
Array VoxelLodTerrain::_b_debug_print_sdf_top_down(Vector3i center, Vector3i extents) {
|
2022-01-03 23:14:18 +00:00
|
|
|
ERR_FAIL_COND_V(!math::is_valid_size(extents), Array());
|
2021-10-28 23:51:30 +01:00
|
|
|
|
2020-02-05 21:09:14 +01:00
|
|
|
Array image_array;
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < _data->lod_count; ++lod_index) {
|
2021-12-13 21:38:10 +00:00
|
|
|
const Box3i world_box = Box3i::from_center_extents(center >> lod_index, extents >> lod_index);
|
2020-02-05 21:09:14 +01:00
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
if (Vector3iUtil::get_volume(world_box.size) == 0) {
|
2020-02-05 21:09:14 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-09-26 04:14:50 +01:00
|
|
|
VoxelBufferInternal buffer;
|
2020-02-05 21:09:14 +01:00
|
|
|
buffer.create(world_box.size);
|
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
const VoxelDataLodMap::Lod &data_lod = _data->lods[lod_index];
|
2020-02-05 21:09:14 +01:00
|
|
|
|
2021-10-13 20:28:20 +01:00
|
|
|
world_box.for_each_cell([&data_lod, &buffer, world_box](const Vector3i &world_pos) {
|
2021-12-13 21:38:10 +00:00
|
|
|
std::shared_ptr<VoxelBufferInternal> voxels =
|
|
|
|
try_get_voxel_buffer_with_lock(data_lod, data_lod.map.voxel_to_block(world_pos));
|
2021-10-13 20:28:20 +01:00
|
|
|
if (voxels == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
2022-02-03 00:02:10 +00:00
|
|
|
const float v =
|
|
|
|
get_voxel_with_lock(*voxels, data_lod.map.to_local(world_pos), VoxelBufferInternal::CHANNEL_SDF).f;
|
2020-02-05 21:09:14 +01:00
|
|
|
const Vector3i rpos = world_pos - world_box.pos;
|
2022-02-03 00:02:10 +00:00
|
|
|
buffer.set_voxel_f(v, rpos.x, rpos.y, rpos.z, VoxelBufferInternal::CHANNEL_SDF);
|
2020-02-05 21:09:14 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
Ref<Image> image = buffer.debug_print_sdf_to_image_top_down();
|
2022-02-27 02:54:35 +00:00
|
|
|
image_array.append(image);
|
2020-02-05 21:09:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return image_array;
|
|
|
|
}
|
|
|
|
|
2021-04-03 20:39:37 +01:00
|
|
|
int VoxelLodTerrain::_b_debug_get_mesh_block_count() const {
|
|
|
|
int sum = 0;
|
2022-03-15 00:29:39 +00:00
|
|
|
const unsigned int lod_count = get_lod_count();
|
|
|
|
for (unsigned int lod_index = 0; lod_index < lod_count; ++lod_index) {
|
2022-03-20 22:04:53 +00:00
|
|
|
const VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
2022-03-19 19:43:33 +00:00
|
|
|
sum += mesh_map.get_block_count();
|
2021-04-03 20:39:37 +01:00
|
|
|
}
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
|
|
|
int VoxelLodTerrain::_b_debug_get_data_block_count() const {
|
2020-10-29 00:49:12 +00:00
|
|
|
int sum = 0;
|
2022-03-15 00:29:39 +00:00
|
|
|
for (unsigned int lod_index = 0; lod_index < _data->lod_count; ++lod_index) {
|
2021-10-13 20:28:20 +01:00
|
|
|
const VoxelDataLodMap::Lod &data_lod = _data->lods[lod_index];
|
|
|
|
RWLockRead rlock(data_lod.map_lock);
|
|
|
|
sum += data_lod.map.get_block_count();
|
2020-10-29 00:49:12 +00:00
|
|
|
}
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
2021-12-30 04:25:42 +00:00
|
|
|
Error VoxelLodTerrain::_b_debug_dump_as_scene(String fpath, bool include_instancer) const {
|
2021-12-13 21:38:10 +00:00
|
|
|
Node3D *root = memnew(Node3D);
|
2020-11-21 18:31:28 +00:00
|
|
|
root->set_name(get_name());
|
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
const unsigned int lod_count = get_lod_count();
|
|
|
|
|
|
|
|
for (unsigned int lod_index = 0; lod_index < lod_count; ++lod_index) {
|
2022-03-20 22:04:53 +00:00
|
|
|
const VoxelMeshMap<VoxelMeshBlockVLT> &mesh_map = _mesh_maps_per_lod[lod_index];
|
2020-11-21 18:31:28 +00:00
|
|
|
|
2022-03-20 22:04:53 +00:00
|
|
|
mesh_map.for_each_block([root](const VoxelMeshBlockVLT &block) {
|
2022-03-20 19:16:58 +00:00
|
|
|
block.for_each_mesh_instance_with_transform([root, &block](const DirectMeshInstance &dmi, Transform3D t) {
|
2020-11-21 18:31:28 +00:00
|
|
|
Ref<Mesh> mesh = dmi.get_mesh();
|
|
|
|
|
|
|
|
if (mesh.is_valid()) {
|
2021-12-13 21:38:10 +00:00
|
|
|
MeshInstance3D *mi = memnew(MeshInstance3D);
|
2020-11-21 18:31:28 +00:00
|
|
|
mi->set_mesh(mesh);
|
|
|
|
mi->set_transform(t);
|
|
|
|
// TODO Transition mesh visibility?
|
2022-03-20 19:16:58 +00:00
|
|
|
mi->set_visible(block.is_visible());
|
2020-11-21 18:31:28 +00:00
|
|
|
root->add_child(mi);
|
|
|
|
// The owner must be set after adding to parent
|
|
|
|
mi->set_owner(root);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-12-30 04:25:42 +00:00
|
|
|
if (include_instancer && _instancer != nullptr) {
|
|
|
|
Node *instances_root = _instancer->debug_dump_as_nodes();
|
|
|
|
if (instances_root != nullptr) {
|
|
|
|
root->add_child(instances_root);
|
|
|
|
set_nodes_owner(instances_root, root);
|
|
|
|
}
|
2020-11-21 18:31:28 +00:00
|
|
|
}
|
|
|
|
|
2021-12-30 04:25:42 +00:00
|
|
|
Ref<PackedScene> scene;
|
|
|
|
scene.instantiate();
|
|
|
|
const Error pack_result = scene->pack(root);
|
2020-11-21 18:31:28 +00:00
|
|
|
memdelete(root);
|
2021-12-30 04:25:42 +00:00
|
|
|
if (pack_result != OK) {
|
|
|
|
return pack_result;
|
|
|
|
}
|
2020-11-21 18:31:28 +00:00
|
|
|
|
2021-12-30 04:25:42 +00:00
|
|
|
const Error save_result = ResourceSaver::save(fpath, scene, ResourceSaver::FLAG_BUNDLE_RESOURCES);
|
|
|
|
return save_result;
|
2020-11-21 18:31:28 +00:00
|
|
|
}
|
|
|
|
|
2019-05-04 00:02:10 +01:00
|
|
|
void VoxelLodTerrain::_bind_methods() {
|
2019-05-05 19:54:47 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("set_material", "material"), &VoxelLodTerrain::set_material);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_material"), &VoxelLodTerrain::get_material);
|
|
|
|
|
2019-05-04 00:02:10 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("set_view_distance", "distance_in_voxels"), &VoxelLodTerrain::set_view_distance);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_view_distance"), &VoxelLodTerrain::get_view_distance);
|
|
|
|
|
2019-08-25 13:04:49 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("get_generate_collisions"), &VoxelLodTerrain::get_generate_collisions);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_generate_collisions", "enabled"), &VoxelLodTerrain::set_generate_collisions);
|
|
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("get_collision_lod_count"), &VoxelLodTerrain::get_collision_lod_count);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_collision_lod_count", "count"), &VoxelLodTerrain::set_collision_lod_count);
|
|
|
|
|
2021-05-09 20:49:45 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("get_collision_layer"), &VoxelLodTerrain::get_collision_layer);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &VoxelLodTerrain::set_collision_layer);
|
|
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("get_collision_mask"), &VoxelLodTerrain::get_collision_mask);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &VoxelLodTerrain::set_collision_mask);
|
|
|
|
|
2021-07-10 22:14:17 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("get_collision_margin"), &VoxelLodTerrain::get_collision_margin);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_collision_margin", "margin"), &VoxelLodTerrain::set_collision_margin);
|
|
|
|
|
2021-02-18 19:50:47 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_collision_update_delay"), &VoxelLodTerrain::get_collision_update_delay);
|
2021-12-13 21:38:10 +00:00
|
|
|
ClassDB::bind_method(
|
|
|
|
D_METHOD("set_collision_update_delay", "delay_msec"), &VoxelLodTerrain::set_collision_update_delay);
|
2021-02-18 19:50:47 +00:00
|
|
|
|
2021-03-02 22:49:42 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_lod_fade_duration"), &VoxelLodTerrain::get_lod_fade_duration);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_lod_fade_duration", "seconds"), &VoxelLodTerrain::set_lod_fade_duration);
|
|
|
|
|
2019-05-04 00:02:10 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("set_lod_count", "lod_count"), &VoxelLodTerrain::set_lod_count);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_lod_count"), &VoxelLodTerrain::get_lod_count);
|
|
|
|
|
2021-03-27 16:38:20 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_lod_distance", "lod_distance"), &VoxelLodTerrain::set_lod_distance);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_lod_distance"), &VoxelLodTerrain::get_lod_distance);
|
2019-05-05 19:54:47 +01:00
|
|
|
|
2021-04-03 20:39:37 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("get_mesh_block_size"), &VoxelLodTerrain::get_mesh_block_size);
|
2021-04-03 23:51:11 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("set_mesh_block_size"), &VoxelLodTerrain::set_mesh_block_size);
|
|
|
|
|
2021-04-03 20:39:37 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("get_data_block_size"), &VoxelLodTerrain::get_data_block_size);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_data_block_region_extent"), &VoxelLodTerrain::get_data_block_region_extent);
|
2021-04-03 23:51:11 +01:00
|
|
|
|
2021-10-03 01:48:07 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("set_full_load_mode_enabled"), &VoxelLodTerrain::set_full_load_mode_enabled);
|
|
|
|
ClassDB::bind_method(D_METHOD("is_full_load_mode_enabled"), &VoxelLodTerrain::is_full_load_mode_enabled);
|
|
|
|
|
2020-12-25 17:08:40 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_statistics"), &VoxelLodTerrain::_b_get_statistics);
|
2021-12-13 21:38:10 +00:00
|
|
|
ClassDB::bind_method(
|
|
|
|
D_METHOD("voxel_to_data_block_position", "lod_index"), &VoxelLodTerrain::voxel_to_data_block_position);
|
|
|
|
ClassDB::bind_method(
|
|
|
|
D_METHOD("voxel_to_mesh_block_position", "lod_index"), &VoxelLodTerrain::voxel_to_mesh_block_position);
|
2019-05-04 00:02:10 +01:00
|
|
|
|
2019-09-03 22:54:40 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("get_voxel_tool"), &VoxelLodTerrain::get_voxel_tool);
|
2020-07-25 16:29:16 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("save_modified_blocks"), &VoxelLodTerrain::_b_save_modified_blocks);
|
2019-09-03 22:54:40 +01:00
|
|
|
|
2020-08-14 20:33:09 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("set_run_stream_in_editor"), &VoxelLodTerrain::set_run_stream_in_editor);
|
|
|
|
ClassDB::bind_method(D_METHOD("is_stream_running_in_editor"), &VoxelLodTerrain::is_stream_running_in_editor);
|
|
|
|
|
2020-10-22 22:43:31 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("set_voxel_bounds"), &VoxelLodTerrain::_b_set_voxel_bounds);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_voxel_bounds"), &VoxelLodTerrain::_b_get_voxel_bounds);
|
|
|
|
|
2021-12-14 00:10:09 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_process_callback", "mode"), &VoxelLodTerrain::set_process_callback);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_process_callback"), &VoxelLodTerrain::get_process_callback);
|
2020-10-30 19:02:00 +00:00
|
|
|
|
2022-03-15 00:29:39 +00:00
|
|
|
ClassDB::bind_method(
|
|
|
|
D_METHOD("set_threaded_update_enabled", "enabled"), &VoxelLodTerrain::set_threaded_update_enabled);
|
|
|
|
ClassDB::bind_method(D_METHOD("is_threaded_update_enabled"), &VoxelLodTerrain::is_threaded_update_enabled);
|
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
ClassDB::bind_method(
|
|
|
|
D_METHOD("debug_raycast_mesh_block", "origin", "dir"), &VoxelLodTerrain::debug_raycast_mesh_block);
|
|
|
|
ClassDB::bind_method(
|
|
|
|
D_METHOD("debug_get_data_block_info", "block_pos", "lod"), &VoxelLodTerrain::debug_get_data_block_info);
|
|
|
|
ClassDB::bind_method(
|
|
|
|
D_METHOD("debug_get_mesh_block_info", "block_pos", "lod"), &VoxelLodTerrain::debug_get_mesh_block_info);
|
2021-03-11 22:55:52 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("debug_get_octrees_detailed"), &VoxelLodTerrain::debug_get_octrees_detailed);
|
2021-12-13 21:38:10 +00:00
|
|
|
ClassDB::bind_method(
|
|
|
|
D_METHOD("debug_print_sdf_top_down", "center", "extents"), &VoxelLodTerrain::_b_debug_print_sdf_top_down);
|
2021-04-03 20:39:37 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("debug_get_mesh_block_count"), &VoxelLodTerrain::_b_debug_get_mesh_block_count);
|
|
|
|
ClassDB::bind_method(D_METHOD("debug_get_data_block_count"), &VoxelLodTerrain::_b_debug_get_data_block_count);
|
2021-12-30 04:25:42 +00:00
|
|
|
ClassDB::bind_method(
|
|
|
|
D_METHOD("debug_dump_as_scene", "path", "include_instancer"), &VoxelLodTerrain::_b_debug_dump_as_scene);
|
2020-01-05 23:27:43 +00:00
|
|
|
|
2020-08-14 20:33:09 +01:00
|
|
|
//ClassDB::bind_method(D_METHOD("_on_stream_params_changed"), &VoxelLodTerrain::_on_stream_params_changed);
|
2019-08-24 01:44:27 +01:00
|
|
|
|
2021-12-14 00:10:09 +00:00
|
|
|
BIND_ENUM_CONSTANT(PROCESS_CALLBACK_IDLE);
|
|
|
|
BIND_ENUM_CONSTANT(PROCESS_CALLBACK_PHYSICS);
|
|
|
|
BIND_ENUM_CONSTANT(PROCESS_CALLBACK_DISABLED);
|
2020-10-30 19:02:00 +00:00
|
|
|
|
2021-03-27 16:38:52 +00:00
|
|
|
ADD_GROUP("Bounds", "");
|
|
|
|
|
2019-05-04 00:02:10 +01:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "view_distance"), "set_view_distance", "get_view_distance");
|
2021-03-02 22:49:42 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::AABB, "voxel_bounds"), "set_voxel_bounds", "get_voxel_bounds");
|
|
|
|
|
2021-03-27 16:38:52 +00:00
|
|
|
ADD_GROUP("Level of detail", "");
|
|
|
|
|
2019-05-05 19:54:47 +01:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_count"), "set_lod_count", "get_lod_count");
|
2021-12-13 21:38:10 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_distance"), "set_lod_distance", "get_lod_distance");
|
2021-03-02 22:49:42 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_fade_duration"), "set_lod_fade_duration", "get_lod_fade_duration");
|
|
|
|
|
2021-03-27 16:38:52 +00:00
|
|
|
ADD_GROUP("Material", "");
|
|
|
|
|
2022-01-16 16:04:25 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, Material::get_class_static()),
|
|
|
|
"set_material", "get_material");
|
2021-03-02 22:49:42 +00:00
|
|
|
|
2021-03-27 16:38:52 +00:00
|
|
|
ADD_GROUP("Collisions", "");
|
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
ADD_PROPERTY(
|
|
|
|
PropertyInfo(Variant::BOOL, "generate_collisions"), "set_generate_collisions", "get_generate_collisions");
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer",
|
|
|
|
"get_collision_layer");
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask",
|
|
|
|
"get_collision_mask");
|
|
|
|
ADD_PROPERTY(
|
|
|
|
PropertyInfo(Variant::INT, "collision_lod_count"), "set_collision_lod_count", "get_collision_lod_count");
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_update_delay"), "set_collision_update_delay",
|
|
|
|
"get_collision_update_delay");
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_margin"), "set_collision_margin", "get_collision_margin");
|
2021-03-02 22:49:42 +00:00
|
|
|
|
2021-03-27 16:38:52 +00:00
|
|
|
ADD_GROUP("Advanced", "");
|
|
|
|
|
|
|
|
// TODO Probably should be in parent class?
|
2021-12-13 21:38:10 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "run_stream_in_editor"), "set_run_stream_in_editor",
|
|
|
|
"is_stream_running_in_editor");
|
2021-04-03 23:51:11 +01:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "mesh_block_size"), "set_mesh_block_size", "get_mesh_block_size");
|
2021-12-13 21:38:10 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "full_load_mode_enabled"), "set_full_load_mode_enabled",
|
|
|
|
"is_full_load_mode_enabled");
|
2022-03-15 00:29:39 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "threaded_update_enabled"), "set_threaded_update_enabled",
|
|
|
|
"is_threaded_update_enabled");
|
2019-05-04 00:02:10 +01:00
|
|
|
}
|
2022-01-09 22:13:10 +00:00
|
|
|
|
|
|
|
} // namespace zylann::voxel
|