feat: lazy loading of terrain chunk LODs
This commit is contained in:
parent
90c46e30d2
commit
2a3eeef522
|
|
@ -21,11 +21,21 @@ void TerrainChunk::ready() {
|
||||||
process_lod();
|
process_lod();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TerrainChunk::generate_lod(size_t lod) {
|
||||||
|
MeshStatus status{ this->meshes.get(lod) };
|
||||||
|
size_t base_detail{ lod == 0 ? this->lod0_detail : this->lod0_detail / (2 * lod + lod) };
|
||||||
|
base_detail = base_detail > 1 ? base_detail : 1;
|
||||||
|
Vector3 const position{ get_global_position() };
|
||||||
|
this->generator->push_task({ { position.x, position.z }, { this->size, this->size } }, status.mesh, base_detail > 1 ? base_detail : 1, callable_mp(this, &self_type::lod_generated).bind(lod));
|
||||||
|
status.flag = MESH_DISPATCHED;
|
||||||
|
this->meshes.set(lod, status);
|
||||||
|
}
|
||||||
|
|
||||||
void TerrainChunk::on_terrain_changed() {
|
void TerrainChunk::on_terrain_changed() {
|
||||||
if (this->generator) {
|
if (this->generator) {
|
||||||
Vector3 const position{ get_global_position() };
|
if (this->meshes.size() != this->lod_count) {
|
||||||
this->meshes.resize_initialized(3);
|
this->meshes.resize_initialized(this->lod_count);
|
||||||
size_t lod{ 0 };
|
}
|
||||||
if (this->collisions) {
|
if (this->collisions) {
|
||||||
this->collisions->queue_free();
|
this->collisions->queue_free();
|
||||||
this->collisions = nullptr;
|
this->collisions = nullptr;
|
||||||
|
|
@ -34,28 +44,31 @@ void TerrainChunk::on_terrain_changed() {
|
||||||
if (!status.mesh.is_valid()) {
|
if (!status.mesh.is_valid()) {
|
||||||
status.mesh.instantiate();
|
status.mesh.instantiate();
|
||||||
}
|
}
|
||||||
size_t base_detail{ lod == 0 ? this->lod0_detail : this->lod0_detail / (2 * lod) };
|
status.flag = MESH_DIRTY;
|
||||||
status.dirty = true;
|
|
||||||
this->generator->push_task({ { position.x, position.z }, { this->size, this->size } }, status.mesh, base_detail > 1 ? base_detail : 1, callable_mp(this, &self_type::lod_generated).bind(lod));
|
|
||||||
lod++;
|
|
||||||
}
|
}
|
||||||
|
generate_lod(this->meshes.size() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerrainChunk::lod_generated(size_t lod) {
|
void TerrainChunk::lod_generated(size_t lod) {
|
||||||
this->meshes.set(lod, { this->meshes[lod].mesh, false });
|
this->meshes.set(lod, { this->meshes[lod].mesh, MESH_LOADED });
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerrainChunk::process_lod() {
|
void TerrainChunk::process_lod() {
|
||||||
size_t result{ (size_t)this->meshes.size() };
|
size_t result{ (size_t)this->meshes.size() };
|
||||||
if (is_ready() && this->meshes.size() > 0) {
|
if (is_ready() && this->meshes.size() > 0) {
|
||||||
Vector3 position{ get_global_position() };
|
Vector3 position{ get_global_position() };
|
||||||
|
position.y = 0;
|
||||||
Vector3 camera{ get_viewport()->get_camera_3d()->get_global_position() };
|
Vector3 camera{ get_viewport()->get_camera_3d()->get_global_position() };
|
||||||
float distance{ (position - camera).length() - this->size / 2.f };
|
camera.y = 0;
|
||||||
|
float distance{ (position - camera).length() - this->size };
|
||||||
distance = distance > 0.f ? distance : 0.f;
|
distance = distance > 0.f ? distance : 0.f;
|
||||||
size_t lod{ size_t(Math::floor(distance / (this->lod_end_distance / this->meshes.size()))) };
|
size_t lod{ size_t(Math::floor(distance / ((this->max_lod_distance * this->size) / this->meshes.size()))) };
|
||||||
result = lod < this->meshes.size() ? lod : (this->meshes.size() - 1);
|
result = lod < this->meshes.size() ? lod : (this->meshes.size() - 1);
|
||||||
while (this->meshes[result].dirty && result < (this->meshes.size() - 1)) {
|
if (this->meshes[result].flag == MESH_DIRTY) {
|
||||||
|
generate_lod(result);
|
||||||
|
}
|
||||||
|
while (this->meshes[result].flag != MESH_LOADED && result < (this->meshes.size() - 1)) {
|
||||||
result++;
|
result++;
|
||||||
}
|
}
|
||||||
if (this->meshes[result].mesh != this->get_mesh()) {
|
if (this->meshes[result].mesh != this->get_mesh()) {
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,17 @@ class TerrainMeshGenerator;
|
||||||
|
|
||||||
class TerrainChunk : public MeshInstance3D {
|
class TerrainChunk : public MeshInstance3D {
|
||||||
GDCLASS(TerrainChunk, MeshInstance3D);
|
GDCLASS(TerrainChunk, MeshInstance3D);
|
||||||
|
enum MeshStatusFlag {
|
||||||
|
MESH_DIRTY,
|
||||||
|
MESH_DISPATCHED,
|
||||||
|
MESH_LOADED
|
||||||
|
};
|
||||||
struct MeshStatus {
|
struct MeshStatus {
|
||||||
Ref<ArrayMesh> mesh;
|
Ref<ArrayMesh> mesh;
|
||||||
bool dirty;
|
MeshStatusFlag flag;
|
||||||
};
|
};
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
void generate_lod(size_t lod);
|
||||||
void ready();
|
void ready();
|
||||||
void on_terrain_changed();
|
void on_terrain_changed();
|
||||||
void lod_generated(size_t lod);
|
void lod_generated(size_t lod);
|
||||||
|
|
@ -27,10 +33,10 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Node *collisions{ nullptr };
|
Node *collisions{ nullptr };
|
||||||
size_t collisions_lod{ 0 };
|
size_t lod_count{ 3 };
|
||||||
Vector<MeshStatus> meshes{};
|
Vector<MeshStatus> meshes{};
|
||||||
int lod0_detail{ 200 };
|
int lod0_detail{ 200 };
|
||||||
float lod_end_distance{ 600 };
|
float max_lod_distance{ 6 };
|
||||||
float size{ 200 };
|
float size{ 200 };
|
||||||
TerrainMeshGenerator *generator{ nullptr };
|
TerrainMeshGenerator *generator{ nullptr };
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,5 @@
|
||||||
|
|
||||||
[node name="TerrainChunk" type="TerrainChunk" unique_id=1453572398]
|
[node name="TerrainChunk" type="TerrainChunk" unique_id=1453572398]
|
||||||
material_override = ExtResource("1_6vjd7")
|
material_override = ExtResource("1_6vjd7")
|
||||||
size = 50.0
|
size = 40.0
|
||||||
lod0_detail = 100
|
lod0_detail = 80
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ noise = SubResource("FastNoiseLite_b1cmn")
|
||||||
noise_amplitude = 5.0
|
noise_amplitude = 5.0
|
||||||
|
|
||||||
[sub_resource type="FastNoiseLite" id="FastNoiseLite_ba0ut"]
|
[sub_resource type="FastNoiseLite" id="FastNoiseLite_ba0ut"]
|
||||||
|
frequency = 0.0163
|
||||||
fractal_type = 2
|
fractal_type = 2
|
||||||
fractal_gain = 1.0
|
fractal_gain = 1.0
|
||||||
fractal_weighted_strength = 0.58
|
fractal_weighted_strength = 0.58
|
||||||
|
|
@ -132,7 +133,7 @@ load_path = "res://.godot/imported/point.svg-e68fd7c1e788d2c48d769cc58eba6e98.ct
|
||||||
primitives = [SubResource("PointPrimitive_5tm2q"), SubResource("NoisePrimitive_ba0ut"), SubResource("NoisePrimitive_pxqd5"), SubResource("NoisePrimitive_q68jb"), SubResource("PlanePrimitive_ba0ut")]
|
primitives = [SubResource("PointPrimitive_5tm2q"), SubResource("NoisePrimitive_ba0ut"), SubResource("NoisePrimitive_pxqd5"), SubResource("NoisePrimitive_q68jb"), SubResource("PlanePrimitive_ba0ut")]
|
||||||
vertex_color_gradient = SubResource("Gradient_b1cmn")
|
vertex_color_gradient = SubResource("Gradient_b1cmn")
|
||||||
color_gradient_end_height = 100.0
|
color_gradient_end_height = 100.0
|
||||||
chunk_count = 4
|
chunk_count = 8
|
||||||
chunk_scene = ExtResource("1_pxqd5")
|
chunk_scene = ExtResource("1_pxqd5")
|
||||||
point_primitive_object = ExtResource("1_b1cmn")
|
point_primitive_object = ExtResource("1_b1cmn")
|
||||||
|
|
||||||
|
|
@ -176,17 +177,6 @@ current_tab = 0
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
metadata/_tab_index = 0
|
metadata/_tab_index = 0
|
||||||
|
|
||||||
[node name="Tree" type="PrimitiveLayerList" parent="LeftPanel/VBoxContainer/Layers/Layers" unique_id=797700186 node_paths=PackedStringArray("terrain")]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_vertical = 3
|
|
||||||
scroll_horizontal_enabled = false
|
|
||||||
terrain = NodePath("../../../../../TerrainMeshEditor")
|
|
||||||
icons = {
|
|
||||||
&"NoisePrimitive": ExtResource("5_eqbpn"),
|
|
||||||
&"PlanePrimitive": ExtResource("4_xg7d5"),
|
|
||||||
&"PointPrimitive": ExtResource("4_5lcyj")
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="LeftPanel/VBoxContainer/Layers/Layers" unique_id=702489990]
|
[node name="HBoxContainer" type="HBoxContainer" parent="LeftPanel/VBoxContainer/Layers/Layers" unique_id=702489990]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 8
|
size_flags_vertical = 8
|
||||||
|
|
@ -222,6 +212,17 @@ expand_icon = true
|
||||||
primitive_blueprint = SubResource("NoisePrimitive_5lcyj")
|
primitive_blueprint = SubResource("NoisePrimitive_5lcyj")
|
||||||
terrain = NodePath("../../../../../../TerrainMeshEditor")
|
terrain = NodePath("../../../../../../TerrainMeshEditor")
|
||||||
|
|
||||||
|
[node name="Tree" type="PrimitiveLayerList" parent="LeftPanel/VBoxContainer/Layers/Layers" unique_id=797700186 node_paths=PackedStringArray("terrain")]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
scroll_horizontal_enabled = false
|
||||||
|
terrain = NodePath("../../../../../TerrainMeshEditor")
|
||||||
|
icons = {
|
||||||
|
&"NoisePrimitive": ExtResource("5_eqbpn"),
|
||||||
|
&"PlanePrimitive": ExtResource("4_xg7d5"),
|
||||||
|
&"PointPrimitive": ExtResource("4_5lcyj")
|
||||||
|
}
|
||||||
|
|
||||||
[node name="Inspector" type="TabContainer" parent="LeftPanel/VBoxContainer" unique_id=240272030]
|
[node name="Inspector" type="TabContainer" parent="LeftPanel/VBoxContainer" unique_id=240272030]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue