Attention | Topic was automatically imported from the old Question2Answer platform. | |
Asked By | Thummper |
Hello!
I have been following some tutorials on YouTube to generate landscape from height maps. As my code does not seem very performant - generating anything over like a 300x300 block crashes the algorithm, I wanted to implement threading to generate the landscape in the background.
I am not familiar with threading at all really, and I have created a rudimentary solution that seems to work okay, my only issue is that it seems to randomly crash with unhelpful error messages, or at least, error messages that I don’t know how to solve.
This is the code for my Terrain scene:
extends Spatial
export(OpenSimplexNoise) var noise
export(float) var noiseScale
export(bool) var regenerate setget regenSet
export(int) var chunkSize
export(int) var mapWidth
export(int) var mapHeight
var mapLoading = true
var mutex
var threads = []
var lazyThreads = []
var workList = []
var numberThreads = 8
func generateMap(cs, mw, mh):
mutex = Mutex.new()
# Make threads for map generator
for i in numberThreads:
var tempThread = Thread.new()
print("Made thread: ", tempThread.is_active())
lazyThreads.push_back(tempThread)
# Make data for map generator
for i in mh:
for j in mw:
var xoffset = i * cs
var yoffset = j * cs
var threadData = [cs, xoffset, yoffset, noise, noiseScale, mutex]
workList.push_front(threadData)
func assignLazyThreads():
var lazy = lazyThreads.pop_back()
if lazy != null:
# We have a thread
mutex.lock()
var thrdData = workList.pop_back()
mutex.unlock()
if thrdData != null:
print("Thread: ", lazy)
thrdData.push_back(lazy)
if lazy.is_active():
lazy.wait_to_finish()
lazy.start(self, "getChunk", thrdData)
func _process(_delta):
# 2 thread lists, lazy and active
assignLazyThreads()
func getChunk(data):
print("Getting chunk for data: ", data)
mutex.lock()
var MapGen = load("res://MapGenerator.gd").new()
mutex.unlock()
MapGen.setChunk(data[0])
var chunkData = MapGen.generateLandscape(data)
var chunkMesh = MeshInstance.new()
chunkMesh.mesh = chunkData[0]
call_deferred("add_child", chunkMesh)
print("Thread has finished")
mutex.lock()
lazyThreads.push_back(data[6])
mutex.unlock()
func clearMap():
for i in range(0, get_child_count()):
get_child(i).queue_free()
func _ready():
clearMap()
generateMap(chunkSize, mapWidth, mapHeight)
func regenSet(_regen):
regenerate = true
print("Regenerating map")
clearMap()
generateMap(chunkSize, mapWidth, mapHeight)
And the code for my MapGenerator:
extends Spatial
var chunkSize
var width
var height
var noise = null
var noiseScale
var tempMesh
var vertices
var UVs
var normals
var mutex
func createQuad(x, y):
var vert1 # vertex positions (Vector2)
var vert2
var vert3
var side1 # sides of each triangle (Vector3)
var side2
var normal # normal for each triangle (Vector3)
# triangle 1
vert1 = Vector3(x, getNoise(x, -y), -y)
vert2 = Vector3(x, getNoise(x, -y-1), -y-1)
vert3 = Vector3(x+1, getNoise(x+1, -y-1), -y-1)
vertices.push_back(vert1)
vertices.push_back(vert2)
vertices.push_back(vert3)
UVs.push_back(Vector2(vert1.x/10, -vert1.z/10))
UVs.push_back(Vector2(vert2.x/10, -vert2.z/10))
UVs.push_back(Vector2(vert3.x/10, -vert3.z/10))
side1 = vert2-vert1
side2 = vert2-vert3
normal = side1.cross(side2)
for _i in range(0,3):
normals.push_back(normal)
# triangle 2
vert1 = Vector3(x, getNoise(x, -y), -y)
vert2 = Vector3(x+1, getNoise(x+1, -y-1), -y-1)
vert3 = Vector3(x+1, getNoise(x+1, -y), -y)
vertices.push_back(vert1)
vertices.push_back(vert2)
vertices.push_back(vert3)
UVs.push_back(Vector2(vert1.x/10, -vert1.z/10))
UVs.push_back(Vector2(vert2.x/10, -vert2.z/10))
UVs.push_back(Vector2(vert3.x/10, -vert3.z/10))
side1 = vert2-vert1
side2 = vert2-vert3
normal = side1.cross(side2)
for _i in range(0,3):
normals.push_back(normal)
func getNoise(x, y):
if noise != null:
return noise.get_noise_2d(x, y) * noiseScale
else:
return 0
func setChunk(size):
chunkSize = size
func generateLandscape(chunkData):
var xoffset = chunkData[1]
var yoffset = chunkData[2]
noise = chunkData[3]
noiseScale = chunkData[4]
mutex = chunkData[5]
tempMesh = Mesh.new()
vertices = PoolVector3Array()
UVs = PoolVector2Array()
normals = PoolVector3Array()
for x in range(0, chunkSize):
for y in range(0, chunkSize):
createQuad(x + xoffset, y + yoffset)
var st = SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLES)
mutex.lock()
st.set_material(load("res://mat.tres"))
mutex.unlock()
for v in vertices.size():
st.add_uv(UVs[v])
st.add_normal(normals[v])
st.add_vertex(vertices[v])
st.commit(tempMesh)
var shape = ConcavePolygonShape.new()
shape.set_faces(tempMesh.get_faces())
return [tempMesh, shape]
My code is also hosted here: GitHub - thummper/Massive if you would like to download and run it - generally I get errors after changing the size of the chunks, of the size of the map and rerunning the code.
My errors look like this https://i.imgur.com/qczqVP9.png:
Any help in solving these errors would be appreciated, also any help with the structure of my code would be helpful as I have never done any multithreaded stuff before - initially I was just making a new thread each time I needed a new chunk, but that led to 100% CPU usage, so I thought limiting the threads would be a better solution.
Thanks.