terrain-editor/modules/terrain_editor/terrain_chunk.cpp

102 lines
2.7 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;
}
process_lod();
on_terrain_changed();
}
void TerrainChunk::on_terrain_changed() {
if (this->generator) {
Vector3 const position{ get_global_position() };
this->meshes.resize_zeroed(5);
size_t lod{ 0 };
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 * 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, MeshStatus{ 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() };
position.y = camera.y = 0;
float distance{ camera.distance_to(position) };
size_t lod{ size_t(Math::floor(distance / (this->lod4_distance / 5.f))) };
result = lod < 5 ? lod : 4;
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;
}