terrain-editor/modules/terrain_editor/terrain_chunk.cpp

123 lines
3.4 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::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;
}