authority/modules/terrain/terrain_chunk.cpp

89 lines
3.4 KiB
C++

#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<SurfaceTool::Vertex> &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;
}