Skip to content

Commit

Permalink
Fix a few detail layer chunk issues when very large map_scale is used
Browse files Browse the repository at this point in the history
  • Loading branch information
Zylann committed May 11, 2024
1 parent 5c9a915 commit ea1a6b9
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ For a more detailed list of past and incoming changes, see the commit history.
- Fixed detail layers were not rendering correctly when terrain is in centered mode
- Fixed texture import dialog was opening a very tall window (issue #423)
- Fixed detail layers "lines" showing on top of grass quads when MSAA is enabled
- Fixed detail layer chunks disappearing unexpectedly when `map_scale` is very large


1.7.2
Expand Down
47 changes: 38 additions & 9 deletions addons/zylann.hterrain/hterrain_detail_layer.gd
Original file line number Diff line number Diff line change
Expand Up @@ -477,13 +477,23 @@ func process(delta: float, viewer_pos: Vector3):
cmax_x = clampi(cmax_x, 0, terrain_chunks_x + 1)
cmax_z = clampi(cmax_z, 0, terrain_chunks_z + 1)

# This algorithm isn't the most efficient ever.
# Maybe we could switch to a clipbox algorithm eventually, and updating only when the viewer
# changes chunk position?

if DEBUG and visible:
_debug_cubes.clear()
for cz in range(cmin_z, cmax_z):
for cx in range(cmin_x, cmax_x):
_add_debug_cube(terrain, _get_chunk_aabb(terrain, Vector3(cx, 0, cz) * CHUNK_SIZE),
terrain_transform_without_map_scale)

#for cz in range(cmin_z, cmax_z):
#for cx in range(cmin_x, cmax_x):
#_add_debug_cube(terrain, _get_chunk_aabb(terrain, Vector3(cx, 0, cz) * CHUNK_SIZE),
#terrain_transform_without_map_scale)
for k in _chunks:
var aabb := _get_chunk_aabb(terrain, Vector3(k.x, 0, k.y) * CHUNK_SIZE)
_add_debug_cube(terrain, aabb, terrain_transform_without_map_scale)
_add_debug_cube(terrain,
AABB(local_viewer_pos - Vector3(1,1,1), Vector3(2,2,2)),
terrain_transform_without_map_scale)

for cz in range(cmin_z, cmax_z):
for cx in range(cmin_x, cmax_x):

Expand All @@ -492,7 +502,7 @@ func process(delta: float, viewer_pos: Vector3):
continue

var aabb := _get_chunk_aabb(terrain, Vector3(cx, 0, cz) * CHUNK_SIZE)
var d := (aabb.position + 0.5 * aabb.size).distance_to(local_viewer_pos)
var d := _get_approx_distance_to_chunk_aabb(aabb, local_viewer_pos)

if d < view_distance:
_load_chunk(terrain_transform_without_map_scale, cx, cz, aabb)
Expand All @@ -502,7 +512,7 @@ func process(delta: float, viewer_pos: Vector3):
for k in _chunks:
var chunk = _chunks[k]
var aabb := _get_chunk_aabb(terrain, Vector3(k.x, 0, k.y) * CHUNK_SIZE)
var d := (aabb.position + 0.5 * aabb.size).distance_to(local_viewer_pos)
var d := _get_approx_distance_to_chunk_aabb(aabb, local_viewer_pos)
if d > view_distance:
to_recycle.append(k)

Expand All @@ -517,15 +527,34 @@ func process(delta: float, viewer_pos: Vector3):
_material.set_shader_parameter("u_ambient_wind", awp)


# It would be nice if Godot had "AABB.distance_squared_to(vec3)"...
# Using an approximation here cuz GDScript is slow.
static func _get_approx_distance_to_chunk_aabb(aabb: AABB, pos: Vector3) -> float:
# Distance to center is faster but has issues with very large map scale.
# Detail chunks are based on cached vertical bounds.
# Usually it works fine on moderate scales, but on huge map scales,
# vertical bound chunks cover such large areas that detail chunks often
# stay exceedingly tall, in turn causing their centers to be randomly
# far from the ground, and then getting unloaded because assumed too far away
#return aabb.get_center().distance_to(pos)

if aabb.has_point(pos):
return 0.0
var delta := (pos - aabb.get_center()).abs() - aabb.size * 0.5
return maxf(delta.x, maxf(delta.y, delta.z))


# Gets local-space AABB of a detail chunk.
# This only apply map_scale in Y, because details are not affected by X and Z map scale.
func _get_chunk_aabb(terrain, lpos: Vector3) -> AABB:
var terrain_scale = terrain.map_scale
var terrain_data = terrain.get_data()
var origin_cells_x := int(lpos.x / terrain_scale.x)
var origin_cells_z := int(lpos.z / terrain_scale.z)
var size_cells_x := int(CHUNK_SIZE / terrain_scale.x)
var size_cells_z := int(CHUNK_SIZE / terrain_scale.z)
# We must at least sample 1 cell, in cases where map_scale is greater than the size of our
# chunks. This is quite an extreme situation though
var size_cells_x := int(ceilf(CHUNK_SIZE / terrain_scale.x))
var size_cells_z := int(ceilf(CHUNK_SIZE / terrain_scale.z))

var aabb = terrain_data.get_region_aabb(
origin_cells_x, origin_cells_z, size_cells_x, size_cells_z)
Expand Down

0 comments on commit ea1a6b9

Please sign in to comment.