#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: this->safe_position = get_global_position(); float const sizef{ (float)get_size() }; this->bounds.position = { this->safe_position.x - sizef / 2.f, this->safe_position.z - sizef / 2.f }; this->bounds.size = { sizef, sizef }; return; } } void TerrainChunkMesh::apply_new_mesh() { this->lock.lock(); set_mesh(this->new_mesh); this->lock.unlock(); } 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->lock.lock(); this->surface = memnew(SurfaceTool); this->surface->begin(Mesh::PRIMITIVE_TRIANGLES); generate_vertices(); generate_faces(); this->surface->generate_normals(); this->surface->generate_tangents(); this->new_mesh = memnew(ArrayMesh); this->surface->commit(this->new_mesh); this->lock.unlock(); this->terrain->mesh_dirty(this); } size_t TerrainChunkMesh::points_per_side() const { return this->size * this->detail; }