#include "terrain_chunk.h" #include "core/config/engine.h" #include "macros.h" #include "scene/3d/camera_3d.h" #include "scene/main/viewport.h" #include "terrain_editor/terrain_mesh_generator.h" void TerrainChunk::_bind_methods() { BIND_PROPERTY(Variant::FLOAT, size); BIND_PROPERTY(Variant::INT, lod0_detail); } void TerrainChunk::ready() { if ((this->generator = cast_to(get_parent()))) { this->generator->connect(TerrainMeshGenerator::sig_primitives_changed, callable_mp(this, &self_type::on_terrain_changed)); } else { print_error(vformat("Chunk %s ready without generator.", get_path())); return; } on_terrain_changed(); process_lod(); } void TerrainChunk::on_terrain_changed() { if (this->generator) { Vector3 const position{ get_global_position() }; this->meshes.resize_initialized(3); size_t lod{ 0 }; if (this->collisions) { this->collisions->queue_free(); this->collisions = nullptr; } for (MeshStatus &status : this->meshes) { if (!status.mesh.is_valid()) { status.mesh.instantiate(); } size_t base_detail{ lod == 0 ? this->lod0_detail : this->lod0_detail / (2 * lod) }; status.dirty = true; this->generator->push_task({ { position.x, position.z }, { this->size, this->size } }, status.mesh, base_detail > 1 ? base_detail : 1, callable_mp(this, &self_type::lod_generated).bind(lod)); lod++; } } } void TerrainChunk::lod_generated(size_t lod) { this->meshes.set(lod, { this->meshes[lod].mesh, false }); } void TerrainChunk::process_lod() { size_t result{ (size_t)this->meshes.size() }; if (is_ready() && this->meshes.size() > 0) { Vector3 position{ get_global_position() }; Vector3 camera{ get_viewport()->get_camera_3d()->get_global_position() }; Vector3 diff{ (position - camera).abs() }; float distance{ (diff.x < diff.z ? diff.z : diff.x) - this->size / 2.f }; distance = distance > 0.f ? distance : 0.f; size_t lod{ size_t(Math::floor(distance / (this->lod_end_distance / this->meshes.size()))) }; result = lod < this->meshes.size() ? lod : (this->meshes.size() - 1); while (this->meshes[result].dirty && result < (this->meshes.size() - 1)) { result++; } if (this->meshes[result].mesh != this->get_mesh()) { this->set_mesh(this->meshes[result].mesh); } } } void TerrainChunk::_notification(int what) { if (Engine::get_singleton()->is_editor_hint()) { return; } switch (what) { default: return; case NOTIFICATION_READY: set_process_thread_group(ProcessThreadGroup::PROCESS_THREAD_GROUP_SUB_THREAD); set_process(true); ready(); return; case NOTIFICATION_PROCESS: process_lod(); return; } } void TerrainChunk::set_size(float size) { this->size = size; if (is_ready()) { on_terrain_changed(); } } float TerrainChunk::get_size() const { return this->size; } void TerrainChunk::set_lod0_detail(int detail) { this->lod0_detail = detail; if (is_ready()) { on_terrain_changed(); } } int TerrainChunk::get_lod0_detail() const { return this->lod0_detail; }