107 lines
3 KiB
C++
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_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;
|
|
}
|