#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::generate_lod(size_t lod) { MeshStatus status{ this->meshes.get(lod) }; size_t base_detail{ lod == 0 ? this->lod0_detail : this->lod0_detail / (2 * lod + lod) }; base_detail = base_detail > 1 ? base_detail : 1; Vector3 const position{ get_global_position() }; 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)); status.flag = MESH_DISPATCHED; this->meshes.set(lod, status); } void TerrainChunk::on_terrain_changed() { if (this->generator) { if (this->meshes.size() != this->lod_count) { this->meshes.resize_initialized(this->lod_count); } if (this->collisions) { this->collisions->queue_free(); this->collisions = nullptr; } for (MeshStatus &status : this->meshes) { if (!status.mesh.is_valid()) { status.mesh.instantiate(); } status.flag = MESH_DIRTY; } generate_lod(this->meshes.size() - 1); } } void TerrainChunk::lod_generated(size_t lod) { this->meshes.set(lod, { this->meshes[lod].mesh, MESH_LOADED }); } void TerrainChunk::process_lod() { if (!is_ready() || this->meshes.size() == 0) { return; } Vector3 position{ get_global_position() }; position.y = 0; Vector3 camera{ get_viewport()->get_camera_3d()->get_global_position() }; camera.y = 0; float distance{ (position - camera).length() }; distance = distance > this->size ? distance - this->size : 0.f; size_t lod{ size_t(Math::floor(distance / ((this->max_lod_distance * this->size) / this->meshes.size()))) }; lod = lod < this->meshes.size() ? lod : (this->meshes.size() - 1); Vector3 pos{ get_position() }; pos.y = (this->lod_count - lod) * 0.5f; set_position(pos); if (this->meshes[lod].flag == MESH_DIRTY) { generate_lod(lod); } while (this->meshes[lod].flag != MESH_LOADED && lod < (this->meshes.size() - 1)) { lod++; } if (this->meshes[lod].mesh != this->get_mesh()) { this->set_mesh(this->meshes[lod].mesh); } } void TerrainChunk::_notification(int what) { if (Engine::get_singleton()->is_editor_hint()) { return; } switch (what) { default: return; case NOTIFICATION_READY: set_process_mode(ProcessMode::PROCESS_MODE_ALWAYS); set_process_thread_group(ProcessThreadGroup::PROCESS_THREAD_GROUP_MAIN_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; }