fix: no gaps in terrain collision time/space

Terrain collision is generated before the first _process

Terrain collision is generated from and to the edges of chunks with no
gaps inbetween
This commit is contained in:
Sara Gerretsen 2026-03-09 11:28:17 +01:00
parent 9ea423a2cb
commit da0419bc13
7 changed files with 104 additions and 39 deletions

View file

@ -55,6 +55,32 @@ void Terrain::update_threads() {
}
}
void Terrain::synchronous_generate_terrain() {
this->workload_lock.lock();
this->threads_stop = false;
this->workload.clear();
this->workload.append_array(this->meshes);
for (Thread &thread : this->threads) {
if (!thread.is_started()) {
thread.start(&Terrain::generate_meshes_thread, this);
}
}
do {
this->workload_lock.unlock();
Thread::yield();
this->workload_lock.lock();
} while (!this->workload.is_empty());
this->threads_stop = true;
this->workload_lock.unlock();
for (Thread &thread : this->threads) {
thread.wait_to_finish();
}
for (TerrainChunkMesh *mesh : this->dirty_meshes) {
mesh->apply_new_mesh();
}
this->dirty_meshes.clear();
}
void Terrain::_notification(int what) {
switch (what) {
default:
@ -66,6 +92,7 @@ void Terrain::_notification(int what) {
return;
case NOTIFICATION_READY:
construct_chunk_grid();
synchronous_generate_terrain();
return;
case NOTIFICATION_PROCESS:
update_meshes();

View file

@ -15,6 +15,7 @@ class Terrain : public Node {
void child_order_changed();
void update_meshes();
void update_threads();
void synchronous_generate_terrain();
protected:
void _notification(int what);

View file

@ -1,22 +1,54 @@
#include "terrain_chunk.h"
#include "core/math/math_defs.h"
#include "core/math/math_funcs.h"
#include "core/variant/variant.h"
#include "scene/3d/physics/collision_shape_3d.h"
#include "scene/3d/physics/static_body_3d.h"
#include "scene/resources/3d/height_map_shape_3d.h"
#include "scene/resources/surface_tool.h"
#include "terrain/terrain.h"
void TerrainChunkMesh::_bind_methods() {}
void TerrainChunkMesh::ready() {
this->position_buffer = get_global_position();
float const sizef{ (float)get_size() };
this->bounds.position = { this->position_buffer.x - sizef / 2.f, this->position_buffer.z - sizef / 2.f };
this->bounds.size = { sizef, sizef };
add_child(this->body = memnew(StaticBody3D));
ERR_FAIL_COND_EDMSG(this->body == nullptr, "Failed to instantiate StaticBody3D");
this->body->add_child(this->collider = memnew(CollisionShape3D));
this->body->set_owner(this);
ERR_FAIL_COND_EDMSG(this->collider == nullptr, "Failed to instantiate CollisionShape3D");
this->collider->set_owner(this);
this->shape = memnew(HeightMapShape3D);
ERR_FAIL_COND_EDMSG(!this->shape.is_valid(), "Failed to instantiate HeightMapShape3D");
this->collider->set_shape(this->shape);
this->shape->set_map_depth(heightmap_side_length());
this->shape->set_map_width(heightmap_side_length());
}
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 } };
Vector3 origin{ this->position_buffer - 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 });
float height{ this->terrain->height_at(coordinate) };
this->surface->add_vertex({ coordinate.x - this->position_buffer.x, height, coordinate.y - this->position_buffer.z });
}
}
this->heightmap.resize_initialized(heightmap_side_length() * heightmap_side_length());
for (size_t x{ 0 }; x < heightmap_side_length(); x++) {
for (size_t y{ 0 }; y < heightmap_side_length(); y++) {
Vector2 coordinate{ origin.x + x, origin.z + y };
this->heightmap.set(x + y * heightmap_side_length(), this->terrain->height_at(coordinate));
}
}
}
@ -54,10 +86,7 @@ void TerrainChunkMesh::_notification(int 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 };
ready();
return;
}
}
@ -65,6 +94,11 @@ void TerrainChunkMesh::_notification(int what) {
void TerrainChunkMesh::apply_new_mesh() {
this->lock.lock();
set_mesh(this->new_mesh);
if (this->shape->get_map_depth() != heightmap_side_length() || this->shape->get_map_width() != heightmap_side_length()) {
this->shape->set_map_depth(heightmap_side_length());
this->shape->set_map_width(heightmap_side_length());
}
this->shape->set_map_data(heightmap);
this->lock.unlock();
}
@ -87,3 +121,7 @@ void TerrainChunkMesh::update_mesh() {
size_t TerrainChunkMesh::points_per_side() const {
return this->size * this->detail;
}
int TerrainChunkMesh::heightmap_side_length() const {
return get_size() + 1;
}

View file

@ -3,6 +3,9 @@
#include "core/math/rect2.h"
#include "macros.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/physics/collision_shape_3d.h"
#include "scene/3d/physics/static_body_3d.h"
#include "scene/resources/3d/height_map_shape_3d.h"
#include "scene/resources/mesh.h"
#include "scene/resources/surface_tool.h"
class Terrain;
@ -10,6 +13,7 @@ class Terrain;
class TerrainChunkMesh : public MeshInstance3D {
GDCLASS(TerrainChunkMesh, MeshInstance3D);
static void _bind_methods();
void ready();
void generate_vertices();
void generate_faces();
@ -20,10 +24,11 @@ public:
void apply_new_mesh();
void update_mesh();
size_t points_per_side() const;
int heightmap_side_length() const;
private:
Mutex lock{};
Vector3 safe_position{};
Vector3 position_buffer{};
Ref<SurfaceTool> surface{};
Ref<ArrayMesh> new_mesh{};
Terrain *terrain{ nullptr };
@ -31,6 +36,11 @@ private:
size_t size{ 1 };
Rect2 bounds{};
Ref<HeightMapShape3D> shape{};
StaticBody3D *body{ nullptr };
CollisionShape3D *collider{ nullptr };
Vector<real_t> heightmap{};
public:
GET_SET_FNS(Rect2, bounds);
GET_SET_FNS(Terrain *, terrain);

View file

@ -48,8 +48,8 @@ shader_parameter/floor_3_tiling = 500.0
shader_parameter/floor_3_specular = 0.5
shader_parameter/floor_3_metallic = 0.5
shader_parameter/region_blending = 1.0
shader_parameter/slope_threshold = 0.355
shader_parameter/slope_blend_distance = 0.028
shader_parameter/slope_threshold = 0.3
shader_parameter/slope_blend_distance = 0.0
shader_parameter/slope_albedo = SubResource("NoiseTexture2D_pkl7k")
shader_parameter/slope_tiling = 0.106
shader_parameter/slope_roughness = 0.7

View file

@ -2,9 +2,7 @@
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_e6s0j"]
render_priority = 1
transparency = 2
alpha_scissor_threshold = 1.0
alpha_antialiasing_mode = 0
transparency = 1
shading_mode = 0
albedo_color = Color(0, 0, 0, 1)
grow = true

View file

@ -45,7 +45,7 @@ point_count = 2
[sub_resource type="Curve" id="Curve_4kj3c"]
_limits = [0.0, 1.0, 0.0, 4.0]
_data = [Vector2(0.78173465, 1), 0.0, 0.0, 0, 0, Vector2(3.0802917, 0), 0.0, 0.0, 0, 0]
_data = [Vector2(1.1431929, 1), 0.0, 0.0, 0, 0, Vector2(3.4741035, 0), 0.0, 0.0, 0, 0]
point_count = 2
[sub_resource type="BoxMesh" id="BoxMesh_kbmr5"]
@ -96,8 +96,8 @@ dof_blur_amount = 0.15
[sub_resource type="SphereMesh" id="SphereMesh_o3i6r"]
flip_faces = true
radius = 1000.0
height = 2000.0
radius = 750.0
height = 1500.0
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_chm2y"]
shading_mode = 0
@ -106,7 +106,7 @@ specular_mode = 2
disable_ambient_light = true
disable_fog = true
disable_specular_occlusion = true
albedo_color = Color(0.3, 0.42, 0.6, 1)
albedo_color = Color(0.055481255, 0.09639085, 0.15917248, 1)
[node name="Node3D" type="Node3D" unique_id=289500437]
@ -376,7 +376,7 @@ transform = Transform3D(1.0000001, 0, 0, 0, 1, 0, 0, 0, 1.0000001, -9.991547, -6
gizmo_extents = 5.0
[node name="TerrainModifierPathPoint23" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath16" unique_id=1120921338]
transform = Transform3D(1.0000004, 0, 0, 0, 1, 0, 0, 0, 1.0000004, -14.902985, -4.79982, -20.752014)
transform = Transform3D(1.0000007, 0, 0, 0, 1, 0, 0, 0, 1.0000007, -14.680634, -6.0471573, -22.171997)
gizmo_extents = 5.0
[node name="TerrainModifierPath19" type="TerrainModifierPath" parent="Terrain" unique_id=1676642694]
@ -451,11 +451,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.441101, 0.8837204, 19.30413
[node name="TerrainModifierPathPoint21" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=1607100253]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.441101, 2.210205, 14.580994)
[node name="TerrainModifierPathPoint5" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=1820799746]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8.534241, 5.0986557, 4.118469)
[node name="TerrainModifierPathPoint6" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=2116612387]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6.9170227, 8.299988, -11.543091)
[node name="TerrainModifierPathPoint23" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=2000472843]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9.022736, 6.2285767, -4.9683228)
[node name="TerrainModifierPathPoint7" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=1272225499]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.5455017, 10.130386, -23.116821)
@ -470,7 +467,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8.5961, 11.085152, -18.563782
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13.502441, 12.257561, -3.0429688)
[node name="TerrainModifierPathPoint11" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=43577796]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13.093262, 12.385696, 10.487976)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12.110291, 13.22065, 16.20935)
[node name="TerrainModifierPathPoint12" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=1921440501]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 11.920105, 13.9776535, 22.744324)
@ -479,35 +476,26 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 11.920105, 13.9776535, 22.744
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 15.705017, 14.232437, 23.40039)
[node name="TerrainModifierPathPoint14" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=22432767]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 17.276093, 16.732018, 12.025452)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 16.127441, 15.883354, 15.772705)
[node name="TerrainModifierPathPoint16" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=159801860]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 17.724762, 18.037773, 3.9212646)
[node name="TerrainModifierPathPoint15" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=2059353099]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 17.2388, 19.639542, -3.52417)
[node name="TerrainModifierPathPoint17" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=1928936301]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 16.626587, 21.457283, -9.916443)
[node name="TerrainModifierPathPoint18" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=2071609159]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 15.835022, 22.994705, -16.712402)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 16.948792, 21.457283, -11.136475)
[node name="TerrainModifierPathPoint19" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=2055368136]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 14.000671, 24.554016, -23.884766)
[node name="TerrainModifierPathPoint20" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=1567587602]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 18.630096, 24.856354, -23.92749)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 14.000671, 24.39434, -23.884766)
[node name="TerrainModifierPathPoint22" type="TerrainModifierPathPoint" parent="Terrain/TerrainModifierPath17" unique_id=1457562552]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 24.136475, 25.229156, -20.645813)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 26.076569, 24.770523, -26.303467)
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1089775425]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 430.6339, 81.57795, 686.2748)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 415.88525, 69.94918, 697.9054)
mesh = SubResource("BoxMesh_kbmr5")
[node name="PlayerVehicle" parent="." unique_id=2037675333 instance=ExtResource("2_o3i6r")]
transform = Transform3D(-0.1691032, -0.0145671945, -0.9854907, -0.0020476654, 0.99989396, -0.014428737, 0.9855962, -0.00042199076, -0.16911517, 542.1524, 107.626686, 693.53375)
transform = Transform3D(-0.9999769, -0.002140658, -0.006432563, -0.0020476652, 0.9998938, -0.014428736, 0.006462857, -0.014415228, -0.99987507, 433.92926, 89.578415, 688.07916)
[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=1509403058]
environment = SubResource("Environment_1b7vx")
@ -515,6 +503,9 @@ camera_attributes = SubResource("CameraAttributesPractical_7eqww")
compositor = SubResource("Compositor_n0ica")
[node name="SkySphere" type="MeshInstance3D" parent="." unique_id=56834935]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 482.60254, 3.9775167, 432.92352)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 543.9732, 3.9775777, 555.09717)
mesh = SubResource("SphereMesh_o3i6r")
surface_material_override/0 = SubResource("StandardMaterial3D_chm2y")
[node name="Camera3D" type="Camera3D" parent="." unique_id=241002743]
transform = Transform3D(-0.13388503, 0.50896627, -0.8503106, 0.0791636, 0.86078644, 0.5027721, 0.9878299, 0, -0.15553802, 388.02707, 99.978836, 697.3086)