Skip to content

Commit

Permalink
Adapt Terrain collision to TerrainRenderer
Browse files Browse the repository at this point in the history
Most code was adapted from the previous implementation, but it was simplified and streamlined.

This may be moved to Geodot entirely in the future for better performance and because it's a pretty basic feature.
  • Loading branch information
kb173 committed Feb 24, 2021
1 parent c870907 commit b9c5c4d
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 5 deletions.
89 changes: 89 additions & 0 deletions Layers/Renderers/Terrain/CollisionMeshCreator.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
extends StaticBody

#
# Can be added as a child to a TerrainLOD to generate a collision StaticBody based on its data and
# shape.
#


export(int) var subdivision = 16


func _ready():
connect("visibility_changed", self, "_on_visibility_changed")


# When this node becomes invisible (e.g. due to being hidden via the UI) disable the collider
func _on_visibility_changed():
if is_visible_in_tree():
$CollisionShape.disabled = false
else:
$CollisionShape.disabled = true


func create_mesh(heightmap_texture: ImageTexture, size: float):
$CollisionShape.shape = create_collision_shape(heightmap_texture.get_data(), size)

# Reset the scale of this node, as scaled colliders may yield wrong results
global_transform = Transform.IDENTITY


# Returns the height on the image at the given pixel position in meters.
func _get_height_from_image(pix_pos, image):
pix_pos.x = clamp(pix_pos.x, 0, image.get_width() - 1)
pix_pos.y = clamp(pix_pos.y, 0, image.get_height() - 1)

# Locking the image and using get_pixel takes about as long as manually
# getting the height from the PoolByteArray data, so this more intuitive
# way is used instead of a custom implementation, as previously.
image.lock()
var height = image.get_pixel(pix_pos.x, pix_pos.y).r
image.unlock()

return height


# Helper function for create_collision_shape - turns x and y coordinates from the loop to a real
# position including the correct height.
func _get_3d_position(normalized_position, source_resolution, image, size):
var local_pos = Vector3(
-size/2 + normalized_position.x * size,
0,
-size/2 + normalized_position.y * size)

var height = _get_height_from_image(normalized_position * source_resolution, image)

return Vector3(local_pos.x, height, local_pos.z)


# Create a ConcavePolygonShape based on the given heightmap, with the given size as the extent.
func create_collision_shape(image, size):
var shape = ConcavePolygonShape.new()
var vecs = PoolVector3Array()
var source_resolution = image.get_size()

# Build a mesh with subdivision * subdivision vectors with the height at each position coming
# from the heightmap
for x in range(0, subdivision):
for y in range(0, subdivision):
var mesh_position = Vector2(x, y)
var normalized_position = mesh_position / subdivision

var add = 1.0 / subdivision

var top_left = _get_3d_position(normalized_position, source_resolution, image, size)
var top_right = _get_3d_position(normalized_position + Vector2(add, 0), source_resolution, image, size)
var bottom_left = _get_3d_position(normalized_position + Vector2(0, add), source_resolution, image, size)
var bottom_right = _get_3d_position(normalized_position + Vector2(add, add), source_resolution, image, size)

vecs.append(bottom_left)
vecs.append(top_left)
vecs.append(top_right)

vecs.append(top_right)
vecs.append(bottom_right)
vecs.append(bottom_left)

shape.set_faces(vecs)

return shape
14 changes: 12 additions & 2 deletions Layers/Renderers/Terrain/TerrainLOD.gd
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,21 @@ export(bool) var is_inner

export(float) var size = 100

export(int) var heightmap_resolution = 100
export(int) var ortho_resolution = 1000

var position_x
var position_y

var height_layer
var texture_layer

var current_heightmap_raw
var current_heightmap
var current_texture

signal updated_data


func _ready():
visible = false
Expand Down Expand Up @@ -45,19 +51,20 @@ func build():
top_left_x,
top_left_y,
size,
100,
heightmap_resolution,
1
)

if current_height_image.is_valid():
current_heightmap = current_height_image.get_image_texture()
current_heightmap_raw = current_height_image.get_image()


var current_ortho_image = texture_layer.get_image(
top_left_x,
top_left_y,
size,
1000,
ortho_resolution,
1
)

Expand All @@ -71,3 +78,6 @@ func apply_textures():
material_override.set_shader_param("tex", current_texture)

visible = true

if has_node("CollisionMeshCreator"):
$CollisionMeshCreator.create_mesh(current_heightmap, size)
2 changes: 1 addition & 1 deletion Layers/Renderers/Terrain/TerrainRenderer.gd
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func _process(delta):
# we only want to update once _everything_ is ready!
if done:
for lod in get_children():
lod.call_deferred("apply_textures")
lod.apply_textures()

done = false

Expand Down
10 changes: 9 additions & 1 deletion Layers/Renderers/Terrain/TerrainRenderer.tscn
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
[gd_scene load_steps=8 format=2]
[gd_scene load_steps=9 format=2]

[ext_resource path="res://Layers/Renderers/Terrain/TerrainShader.shader" type="Shader" id=1]
[ext_resource path="res://Layers/Renderers/Terrain/TerrainLOD.tscn" type="PackedScene" id=2]
[ext_resource path="res://Layers/LayerRenderer.tscn" type="PackedScene" id=3]
[ext_resource path="res://Layers/Renderers/Terrain/TerrainRenderer.gd" type="Script" id=4]
[ext_resource path="res://Layers/Renderers/Terrain/CollisionMeshCreator.gd" type="Script" id=5]

[sub_resource type="ShaderMaterial" id=1]
resource_local_to_scene = true
Expand Down Expand Up @@ -34,3 +35,10 @@ size = 500.0
[node name="TerrainLOD2" parent="." index="2" instance=ExtResource( 2 )]
material_override = SubResource( 3 )
size = 2500.0

[node name="CollisionMeshCreator" type="StaticBody" parent="TerrainLOD2" index="0"]
collision_layer = 16387
script = ExtResource( 5 )
subdivision = 32

[node name="CollisionShape" type="CollisionShape" parent="TerrainLOD2/CollisionMeshCreator" index="0"]
1 change: 0 additions & 1 deletion Perspectives/PC/FirstPersonPC.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,3 @@ rotate = true
[node name="CollisionShape" type="CollisionShape" parent="."]
transform = Transform( 1, 0, 1.77636e-15, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 1.65595, 0 )
shape = SubResource( 1 )
disabled = true

0 comments on commit b9c5c4d

Please sign in to comment.