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();
|
||||
}
|
||||
|
||||
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() {
|
||||
if (this->generator) {
|
||||
Vector3 const position{ get_global_position() };
|
||||
this->meshes.resize_initialized(3);
|
||||
size_t lod{ 0 };
|
||||
if (this->meshes.size() != this->lod_count) {
|
||||
this->meshes.resize_initialized(this->lod_count);
|
||||
}
|
||||
if (this->collisions) {
|
||||
this->collisions->queue_free();
|
||||
this->collisions = nullptr;
|
||||
|
|
@ -34,28 +44,31 @@ void TerrainChunk::on_terrain_changed() {
|
|||
if (!status.mesh.is_valid()) {
|
||||
status.mesh.instantiate();
|
||||
}
|
||||
size_t base_detail{ lod == 0 ? this->lod0_detail : this->lod0_detail / (2 * lod) };
|
||||
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++;
|
||||
status.flag = MESH_DIRTY;
|
||||
}
|
||||
generate_lod(this->meshes.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
size_t result{ (size_t)this->meshes.size() };
|
||||
if (is_ready() && this->meshes.size() > 0) {
|
||||
Vector3 position{ get_global_position() };
|
||||
position.y = 0;
|
||||
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;
|
||||
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);
|
||||
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++;
|
||||
}
|
||||
if (this->meshes[result].mesh != this->get_mesh()) {
|
||||
|
|
|
|||
|
|
@ -6,11 +6,17 @@ class TerrainMeshGenerator;
|
|||
|
||||
class TerrainChunk : public MeshInstance3D {
|
||||
GDCLASS(TerrainChunk, MeshInstance3D);
|
||||
enum MeshStatusFlag {
|
||||
MESH_DIRTY,
|
||||
MESH_DISPATCHED,
|
||||
MESH_LOADED
|
||||
};
|
||||
struct MeshStatus {
|
||||
Ref<ArrayMesh> mesh;
|
||||
bool dirty;
|
||||
MeshStatusFlag flag;
|
||||
};
|
||||
static void _bind_methods();
|
||||
void generate_lod(size_t lod);
|
||||
void ready();
|
||||
void on_terrain_changed();
|
||||
void lod_generated(size_t lod);
|
||||
|
|
@ -27,10 +33,10 @@ public:
|
|||
|
||||
private:
|
||||
Node *collisions{ nullptr };
|
||||
size_t collisions_lod{ 0 };
|
||||
size_t lod_count{ 3 };
|
||||
Vector<MeshStatus> meshes{};
|
||||
int lod0_detail{ 200 };
|
||||
float lod_end_distance{ 600 };
|
||||
float max_lod_distance{ 6 };
|
||||
float size{ 200 };
|
||||
TerrainMeshGenerator *generator{ nullptr };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,5 +4,5 @@
|
|||
|
||||
[node name="TerrainChunk" type="TerrainChunk" unique_id=1453572398]
|
||||
material_override = ExtResource("1_6vjd7")
|
||||
size = 50.0
|
||||
lod0_detail = 100
|
||||
size = 40.0
|
||||
lod0_detail = 80
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ noise = SubResource("FastNoiseLite_b1cmn")
|
|||
noise_amplitude = 5.0
|
||||
|
||||
[sub_resource type="FastNoiseLite" id="FastNoiseLite_ba0ut"]
|
||||
frequency = 0.0163
|
||||
fractal_type = 2
|
||||
fractal_gain = 1.0
|
||||
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")]
|
||||
vertex_color_gradient = SubResource("Gradient_b1cmn")
|
||||
color_gradient_end_height = 100.0
|
||||
chunk_count = 4
|
||||
chunk_count = 8
|
||||
chunk_scene = ExtResource("1_pxqd5")
|
||||
point_primitive_object = ExtResource("1_b1cmn")
|
||||
|
||||
|
|
@ -176,17 +177,6 @@ current_tab = 0
|
|||
layout_mode = 2
|
||||
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]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 8
|
||||
|
|
@ -222,6 +212,17 @@ expand_icon = true
|
|||
primitive_blueprint = SubResource("NoisePrimitive_5lcyj")
|
||||
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]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
|
|
|||
Loading…
Reference in a new issue