#include "terrain_mesh_generator.h" #include "core/math/math_funcs.h" #include "core/math/rect2.h" #include "core/object/class_db.h" #include "core/templates/local_vector.h" #include "scene/resources/surface_tool.h" #include "terrain_editor/macros.h" #include "terrain_editor/terrain_primitive.h" #include String const TerrainMeshGenerator::sig_primitives_changed{ "primitives_changed" }; String const TerrainMeshGenerator::sig_primitive_list_changed{ "primitive_list_changed" }; void TerrainMeshGenerator::_bind_methods() { BIND_HPROPERTY(Variant::ARRAY, primitives, PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:TerrainPrimitive", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE)); BIND_HPROPERTY(Variant::OBJECT, vertex_color_gradient, PROPERTY_HINT_RESOURCE_TYPE, "Gradient"); BIND_PROPERTY(Variant::FLOAT, color_gradient_start_height); BIND_PROPERTY(Variant::FLOAT, color_gradient_end_height); ADD_SIGNAL(MethodInfo(sig_primitives_changed)); ADD_SIGNAL(MethodInfo(sig_primitive_list_changed, PropertyInfo(Variant::ARRAY, "array", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:TerrainPrimitive", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE)))); ClassDB::bind_method(D_METHOD("generate_grid", "area", "out_mesh", "side_points"), &self_type::generate_grid); } void TerrainMeshGenerator::on_configuration_changed() { emit_signal(sig_primitives_changed); } Color TerrainMeshGenerator::color_at_height(float height) const { float const mapped{ Math::remap(height, this->color_gradient_start_height, this->color_gradient_end_height, 0.f, 1.f) }; return this->vertex_color_gradient.is_valid() ? this->vertex_color_gradient->get_color_at_offset(mapped) : Color{ mapped, mapped, mapped, 1.f }; } float TerrainMeshGenerator::evaluate_point(Vector2 at) const { float height{ -std::numeric_limits::infinity() }; for (Ref primitive : this->primitives) { if (primitive.is_valid()) { primitive->evaluate(at, height); } } return height; } void face_from(Ref surface, size_t tl, size_t side_points) { LocalVector &vertices{ surface->get_vertex_array() }; float d1{ vertices[tl].vertex.distance_to(vertices[tl + side_points + 1].vertex) }; float d2{ vertices[tl + 1].vertex.distance_to(vertices[tl + side_points].vertex) }; if (d1 < d2) { surface->add_index(tl); surface->add_index(tl + side_points + 1); surface->add_index(tl + 1); surface->add_index(tl); surface->add_index(tl + side_points); surface->add_index(tl + side_points + 1); } else { surface->add_index(tl + side_points); surface->add_index(tl + side_points + 1); surface->add_index(tl + 1); surface->add_index(tl + 1); surface->add_index(tl); surface->add_index(tl + side_points); } } void TerrainMeshGenerator::generate_grid(Rect2 area, Ref mesh, size_t side_points) { mesh->clear_surfaces(); surface->clear(); Vector2 point_distance{ area.size / (float)side_points }; Vector2 at{ area.position }; surface->begin(Mesh::PRIMITIVE_TRIANGLES); for (size_t xpoint{ 0 }; xpoint < side_points; ++xpoint) { for (size_t ypoint{ 0 }; ypoint < side_points; ++ypoint) { float const height{ evaluate_point(at) }; surface->set_color(color_at_height(height)); surface->set_uv({ at / area.size }); surface->add_vertex({ at.x, height, at.y }); at.y += point_distance.y; } at.y = area.position.y; at.x += point_distance.x; } for (size_t xface{ 0 }; xface < side_points - 1; ++xface) { for (size_t yface{ 0 }; yface < side_points - 1; ++yface) { face_from(surface, xface + yface * (size_t)side_points, side_points); } } surface->generate_normals(); surface->generate_tangents(); surface->commit(mesh); } void TerrainMeshGenerator::set_primitives(Array primitives) { for (Ref primitive : this->primitives) { if (primitive.is_valid()) { primitive->disconnect_changed(this->generation_changed); } } this->primitives.clear(); for (Variant var : primitives) { Ref primitive{ var }; this->primitives.push_back(primitive); if (primitive.is_valid()) { primitive->connect_changed(this->generation_changed); } } emit_signal(sig_primitive_list_changed, get_primitives()); on_configuration_changed(); } Array TerrainMeshGenerator::get_primitives() const { Array a; for (Ref primitive : this->primitives) { a.push_back(primitive); } return a; } void TerrainMeshGenerator::set_vertex_color_gradient(Ref gradient) { if (this->vertex_color_gradient.is_valid()) { this->vertex_color_gradient->disconnect_changed(this->generation_changed); } this->vertex_color_gradient = gradient; if (gradient.is_valid()) { this->vertex_color_gradient->connect_changed(this->generation_changed); } on_configuration_changed(); } Ref TerrainMeshGenerator::get_vertex_color_gradient() const { return this->vertex_color_gradient; } void TerrainMeshGenerator::set_color_gradient_start_height(float value) { this->color_gradient_start_height = value; on_configuration_changed(); } float TerrainMeshGenerator::get_color_gradient_start_height() const { return this->color_gradient_start_height; } void TerrainMeshGenerator::set_color_gradient_end_height(float value) { this->color_gradient_end_height = value; on_configuration_changed(); } float TerrainMeshGenerator::get_color_gradient_end_height() const { return this->color_gradient_end_height; }