I'm new to godot and enjoying learning about it.
I am trying to understand more about procedural generated noise. I have followed a Youtube video by Codat showing how to get procedural terrain generated in chunks. It does ok for what i need it to do right now. The issue I am facing is when it comes to placing something like trees on the surface of the generated noise. As there is no set height map to use and when i try to print vertex.y above a number to console it lags badly.
The idea of my project is a star fox type of game. the player is stationary and the terrain moves toward the player on the z axis. the terrain continuously generates and unloads as it leaves behind the camera view. I am not sure about proceeding with this as the programming concepts are out of my league at the moment. but before starting over from scratch i thought i would ask anyway. Thanks for your time.
Here is the code to create chunks and world generation.
Chunk.gd
# Original Source: Codat Youtube
extends Spatial
class_name Chunk
var mesh_instance
var noise
var x
var z
var chunk_size
var should_remove = true
func _init(noise, x, z, chunk_size):
self.noise = noise
self.x = x
self.z = z
self.chunk_size = chunk_size
func _ready():
generate_chunk()
generate_water()
func generate_chunk():
var plane_mesh = PlaneMesh.new()
plane_mesh.size = Vector2(chunk_size, chunk_size)
plane_mesh.subdivide_depth = chunk_size * 0.5
plane_mesh.subdivide_width = chunk_size * 0.5
plane_mesh.material = preload("res://asset/material/terrain.material")
var surface_tool = SurfaceTool.new()
var data_tool = MeshDataTool.new()
surface_tool.create_from(plane_mesh, 0)
var array_plane = surface_tool.commit()
var error = data_tool.create_from_surface(array_plane, 0)
for i in range(data_tool.get_vertex_count()):
var vertex = data_tool.get_vertex(i)
# Chunk Height
vertex.y = noise.get_noise_3d(vertex.x + x, vertex.y, vertex.z +z) * 10 #80
data_tool.set_vertex(i, vertex)
for s in range(array_plane.get_surface_count()):
array_plane.surface_remove(s)
data_tool.commit_to_surface(array_plane)
surface_tool.begin(Mesh.PRIMITIVE_TRIANGLES)
surface_tool.create_from(array_plane, 0)
surface_tool.generate_normals()
mesh_instance = MeshInstance.new()
mesh_instance.mesh = surface_tool.commit()
mesh_instance.create_trimesh_collision()
mesh_instance.cast_shadow = GeometryInstance.SHADOW_CASTING_SETTING_OFF
add_child(mesh_instance)
func generate_water():
var plane_mesh = PlaneMesh.new()
plane_mesh.size = Vector2(chunk_size, chunk_size)
# To Do give it a material
plane_mesh.material = preload("res://asset/material/water.material")
var mesh_instance = MeshInstance.new()
mesh_instance.mesh = plane_mesh
add_child(mesh_instance)
World.gd
extends Spatial
const chunk_size = 32 #64 default
const chunk_amount = 16 #16 default
var noise
var chunks = {}
var unready_chunks = {}
var thread
func _ready():
randomize()
noise = OpenSimplexNoise.new()
noise.seed = randi()
noise.octaves = 6
noise.period = 80
thread = Thread.new()
func add_chunk(x, z):
var key = str(x) + "," + str(z)
if chunks.has(key) or unready_chunks.has(key):
return
if not thread.is_active():
thread.start(self, "load_chunk", [thread, x, z])
unready_chunks[key] = 1
func load_chunk(arr):
var thread = arr[0]
var x = arr[1]
var z = arr[2]
var chunk = Chunk.new(noise, x * chunk_size, z * chunk_size, chunk_size)
# Chunk Y axis Location
chunk.translation = Vector3(x * chunk_size, 0, z * chunk_size)
call_deferred("load_done", chunk, thread)
func load_done(chunk, thread):
add_child(chunk)
var key = str(chunk.x / chunk_size) + "," + str(chunk.z / chunk_size)
chunks[key] = chunk
unready_chunks.erase(key)
thread.wait_to_finish()
func get_chunk(x, z):
var key = str(x) + "," + str(z)
if chunks.has(key):
return chunks.get(key)
return null
func _process(delta):
update_chunks()
clean_up_chunks()
reset_chunks()
func update_chunks():
var player_translation = $Player.translation
var p_x = int(player_translation.x) / chunk_size
var p_z = int(player_translation.z) / chunk_size
# Chunk Location To Player
for x in range(p_x - chunk_amount * 1, p_x + chunk_amount * 1):
for z in range(p_z - chunk_amount, p_z + 1): #(p_z - chunk_amount * 0.5, p_z + chunk_amount * 0.5):
add_chunk(x, z)
var chunk = get_chunk(x, z)
if chunk != null:
chunk.should_remove = false
func clean_up_chunks():
for key in chunks:
var chunk = chunks[key]
if chunk.should_remove:
chunk.queue_free()
chunks.erase(key)
func reset_chunks():
for key in chunks:
chunks[key].should_remove = true
