#include "terrain.h" #include "terrain/terrain_chunk.h" #include "terrain/terrain_modifier.h" void Terrain::_bind_methods() {} void Terrain::ready() { construct_chunk_grid(); generate_meshes(); } void Terrain::update_modifier_list() { bool editor{ Engine::get_singleton()->is_editor_hint() }; this->modifiers.clear(); for (Variant var : get_children()) { if (TerrainModifier * mod{ cast_to(var) }) { if (editor && !mod->is_connected(TerrainModifier::sig_changed, callable_mp(this, &self_type::on_terrain_changed))) { mod->connect(TerrainModifier::sig_changed, callable_mp(this, &self_type::on_terrain_changed)); } this->modifiers.push_back(mod); } } on_terrain_changed(); } void Terrain::child_entered(Node *node) { if (cast_to(node)) { update_modifier_list(); } } void Terrain::child_exiting(Node *node) { if (TerrainModifier * mod{ cast_to(node) }) { this->modifiers.erase(mod); if (Engine::get_singleton()->is_editor_hint() && !is_queued_for_deletion()) { mod->disconnect(TerrainModifier::sig_changed, callable_mp(this, &self_type::on_terrain_changed)); } on_terrain_changed(); } } void Terrain::on_terrain_changed() { if (is_queued_for_deletion() || !is_inside_tree()) { return; } generate_meshes(); } void Terrain::_notification(int what) { switch (what) { default: return; case NOTIFICATION_ENTER_TREE: if (!is_ready()) { connect("child_entered_tree", callable_mp(this, &self_type::child_entered)); connect("child_exiting_tree", callable_mp(this, &self_type::child_exiting)); connect("child_order_changed", callable_mp(this, &self_type::update_modifier_list)); } return; case NOTIFICATION_READY: ready(); return; } } void Terrain::construct_chunk_grid() { size_t const chunks_per_side{ this->side_length / this->chunk_size }; Vector3 const origin{ -(float)this->side_length / 2.f, 0.f, -(float)this->side_length / 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); } } } void Terrain::generate_meshes() { for (TerrainChunkMesh *mesh : this->meshes) { if (!mesh->is_inside_tree()) { return; } mesh->update_mesh(); } } 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(); generate_meshes(); } } 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(); generate_meshes(); } } void Terrain::set_detail(size_t detail) { this->detail = detail; if (is_inside_tree()) { construct_chunk_grid(); generate_meshes(); } } size_t Terrain::get_detail() const { return this->detail; }