terrain-editor/modules/terrain_editor/terrain_chunk.cpp

107 lines
3 KiB
C++

#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<TerrainMeshGenerator>(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_zeroed(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;
}