#include "terrain.h" #include "terrain/terrain_chunk.h" #include "terrain/terrain_modifier.h" void Terrain::_bind_methods() { BIND_PROPERTY(Variant::INT, side_length); BIND_PROPERTY(Variant::INT, chunk_size); BIND_PROPERTY(Variant::INT, detail); BIND_PROPERTY(Variant::INT, thread_count); } void Terrain::ready() { construct_chunk_grid(); } void Terrain::_notification(int what) { switch (what) { default: return; case NOTIFICATION_READY: ready(); return; case NOTIFICATION_EXIT_TREE: this->workload_lock.lock(); this->threads_stop = true; this->workload_lock.unlock(); for (Thread &thread : this->threads) { thread.wait_to_finish(); } return; } } void Terrain::generate_meshes_thread(void *terrain) { Terrain *self{ static_cast(terrain) }; print_line("thread", Thread::get_caller_id(), "start"); for (;;) { self->workload_lock.lock(); if (self->threads_stop) { self->workload_lock.unlock(); print_line(Thread::get_caller_id(), "exiting"); break; } if (self->workload.is_empty()) { self->workload_lock.unlock(); Thread::yield(); continue; } TerrainChunkMesh *mesh{ self->workload[0] }; self->workload.remove_at(0); self->workload_lock.unlock(); if (!mesh->is_inside_tree()) { print_line(Thread::get_caller_id(), "mesh is outside tree, exiting"); break; } mesh->update_mesh(); Thread::yield(); } print_line(Thread::get_caller_id(), "done"); return; } void Terrain::construct_chunk_grid() { for (TerrainChunkMesh *mesh : this->meshes) { mesh->queue_free(); } this->meshes.clear(); size_t const chunks_per_side{ this->side_length / this->chunk_size }; Vector3 const origin{ (float)this->chunk_size / 2.f, 0.f, (float)this->chunk_size / 2.f }; for (size_t y{ 0 }; y < chunks_per_side; ++y) { for (size_t x{ 0 }; x < chunks_per_side; ++x) { TerrainChunkMesh *chunk{ memnew(TerrainChunkMesh) }; chunk->set_size(this->chunk_size); chunk->set_detail(this->detail); chunk->set_terrain(this); chunk->set_position(origin + Vector3{ (float)this->chunk_size * (float)x, 0.f, (float)this->chunk_size * (float)y }); add_child(chunk); chunk->set_owner(this); this->meshes.push_back(chunk); } } } float Terrain::height_at(Vector2 world_coordinate) { float height{ 0 }; for (TerrainModifier *mod : this->modifiers) { if (!mod->is_inside_tree()) { return height; } height = mod->evaluate_at(world_coordinate, height); } return height; } void Terrain::set_side_length(size_t length) { this->side_length = length; if (is_inside_tree()) { construct_chunk_grid(); } } size_t Terrain::get_side_length() const { return this->side_length; } void Terrain::set_chunk_size(size_t size) { this->chunk_size = size; if (is_inside_tree()) { construct_chunk_grid(); } } size_t Terrain::get_chunk_size() const { return this->chunk_size; } void Terrain::set_detail(size_t detail) { this->detail = detail; if (is_inside_tree()) { construct_chunk_grid(); } } size_t Terrain::get_detail() const { return this->detail; } void Terrain::set_thread_count(size_t num) { this->workload_lock.lock(); this->threads_stop = true; this->workload_lock.unlock(); for (Thread &thread : this->threads) { thread.wait_to_finish(); } this->threads_stop = false; this->threads.resize_initialized(num); for (Thread &thread : this->threads) { thread.start(&self_type::generate_meshes_thread, this); } } size_t Terrain::get_thread_count() const { return this->threads.size(); }