#include "terrain_chunk.h" #include "core/variant/variant.h" #include "scene/resources/surface_tool.h" #include "terrain/terrain.h" void TerrainChunkMesh::_bind_methods() {} void TerrainChunkMesh::generate_vertices() { ERR_FAIL_COND_EDMSG(this->terrain == nullptr, "TerrainChunkMesh::generate_vertices: no terrain assigned"); ERR_FAIL_COND_EDMSG(this->size <= 0.f, "TerrainChunkMesh::generate_vertices: size <= 0"); ERR_FAIL_COND_EDMSG(points_per_side() <= 0, "TerrainChunkMesh::generate_vertices: points per side <= 0"); float const half_extent{ (float)this->size / 2.f }; float const point_distance{ (float)this->size / ((float)points_per_side() - 1) }; Vector3 origin{ this->safe_position - Vector3{ half_extent, 0, half_extent } }; for (size_t x{ 0 }; x < points_per_side(); ++x) { for (size_t y{ 0 }; y < points_per_side(); ++y) { Vector2 const coordinate{ origin.x + point_distance * x, origin.z + point_distance * y }; this->surface->set_uv({ (float)x / (float)points_per_side(), (float)y / (float)points_per_side() }); this->surface->add_vertex({ coordinate.x - this->safe_position.x, this->terrain->height_at(coordinate), coordinate.y - this->safe_position.z }); } } } void TerrainChunkMesh::generate_faces() { LocalVector &verts{ this->surface->get_vertex_array() }; ERR_FAIL_COND_EDMSG(verts.size() == 0, "TerrainChunkMesh::generate_faces: no vertices in surface, call generate_vertices first"); size_t const faces_per_side{ points_per_side() - 1 }; for (size_t x{ 0 }; x < faces_per_side; ++x) { for (size_t y{ 0 }; y < faces_per_side; ++y) { size_t const tl{ x + y * points_per_side() }; float tl_br{ verts[tl].vertex.distance_to(verts[tl + points_per_side() + 1].vertex) }; float tr_bl{ verts[tl + 1].vertex.distance_to(verts[tl + points_per_side()].vertex) }; if (tl_br < tr_bl) { surface->add_index(tl); surface->add_index(tl + points_per_side() + 1); surface->add_index(tl + 1); surface->add_index(tl); surface->add_index(tl + points_per_side()); surface->add_index(tl + points_per_side() + 1); } else { surface->add_index(tl + points_per_side()); surface->add_index(tl + points_per_side() + 1); surface->add_index(tl + 1); surface->add_index(tl + 1); surface->add_index(tl); surface->add_index(tl + points_per_side()); } } } } void TerrainChunkMesh::_notification(int what) { switch (what) { default: return; case NOTIFICATION_READY: if (!get_mesh().is_valid()) { set_mesh(memnew(ArrayMesh)); } this->safe_position = get_global_position(); return; } } void TerrainChunkMesh::update_mesh() { ERR_FAIL_COND_EDMSG(this->size <= 0.f, "TerrainChunkMesh::generate: size <= 0"); ERR_FAIL_COND_EDMSG(points_per_side() <= 0, "TerrainChunkMesh::generate: points per side <= 0"); this->surface->clear(); this->surface->begin(Mesh::PRIMITIVE_TRIANGLES); generate_vertices(); generate_faces(); this->surface->generate_normals(); this->surface->generate_tangents(); callable_mp(Ref(this->mesh).ptr(), &ArrayMesh::clear_surfaces).call_deferred(); callable_mp(Ref(this->mesh).ptr(), &ArrayMesh::add_surface_from_arrays).call_deferred(Mesh::PRIMITIVE_TRIANGLES, this->surface->commit_to_arrays(), TypedArray(), Dictionary(), 0); } size_t TerrainChunkMesh::points_per_side() const { return this->size * this->detail; }