This works reasonably fast for 16x16 chunks
This commit is contained in:
@@ -1,48 +1,78 @@
|
||||
extends Node
|
||||
## The generator works with pure numbers, you'll need something else to convert it to actual tiles
|
||||
|
||||
## Makes a new chunk, derived from the given WFC samples
|
||||
func generate_chunk_at(_x: int, _y: int, _chunk_array: Array[Chunk], _samples: Array[PackedInt32Array]) -> Chunk:
|
||||
#TODO: fix the chunk-edges
|
||||
var chunk: Chunk = Chunk.new(_x, _y)
|
||||
|
||||
while _set_lowest_entropy_tile(chunk, _samples) > 0: pass
|
||||
|
||||
_chunk_array.append(chunk)
|
||||
return chunk
|
||||
## Entropy for tiles far away from activity shouldn't be recalculated, so cache them
|
||||
var entropy_valid: Array[Array] = [] ##Options for each tile
|
||||
var entropy_total: PackedInt32Array = [] ##Total number of options, negative means unknown
|
||||
|
||||
## Returns the entropy of the selected tile
|
||||
func _set_lowest_entropy_tile(chunk: Chunk, _samples: Array[PackedInt32Array]) -> int:
|
||||
var entropy: Array[Array] = []
|
||||
entropy.resize(Chunk.CHUNK_WIDTH * Chunk.CHUNK_HEIGHT)
|
||||
func _reset_entropy() -> void:
|
||||
entropy_valid.clear()
|
||||
entropy_valid.resize(Chunk.CHUNK_WIDTH * Chunk.CHUNK_HEIGHT)
|
||||
entropy_valid.fill([])
|
||||
entropy_total.clear()
|
||||
entropy_total.resize(Chunk.CHUNK_WIDTH * Chunk.CHUNK_HEIGHT)
|
||||
entropy_total.fill(-1)
|
||||
|
||||
#iterate over unset tiles with valid neighbours
|
||||
func _update_entropy(chunk: Chunk, samples: Array[PackedInt32Array]) -> void:
|
||||
#iterate over indeterminate tiles to find their valid neighbours
|
||||
for x in range(Chunk.CHUNK_WIDTH):
|
||||
for y in range((Chunk.CHUNK_HEIGHT)):
|
||||
if chunk.data[y * Chunk.CHUNK_HEIGHT + x] == 0: #direct access, to skip extra checks
|
||||
entropy[y * Chunk.CHUNK_HEIGHT + x] = _find_valid_samples(chunk, x, y, _samples)
|
||||
if entropy_total[y * Chunk.CHUNK_WIDTH + x] < 0: #There's some excess multiplication in this code, but I want to get it right first
|
||||
entropy_valid[y * Chunk.CHUNK_WIDTH + x] = _find_valid_samples_at(chunk, x, y, samples) #this overrides any pre-existing cached data
|
||||
entropy_total[y * Chunk.CHUNK_WIDTH + x] = entropy_valid[y * Chunk.CHUNK_WIDTH + x].size() #the entropy is stored separately or faster processing
|
||||
|
||||
func _clear_entropy_at(index: int) -> void:
|
||||
var clear_at := func (dx: int, dy: int) -> void:
|
||||
var check = index + (dy * Chunk.CHUNK_WIDTH) + (dx)
|
||||
if check >= 0 and check < Chunk.CHUNK_WIDTH * Chunk.CHUNK_HEIGHT:
|
||||
if entropy_total[check] > 0: entropy_total[check] = -1 #unset surrounding tiles
|
||||
clear_at.call(-1, -1)
|
||||
clear_at.call( 0, -1)
|
||||
clear_at.call( 1, -1)
|
||||
clear_at.call(-1, 0)
|
||||
entropy_total[index] = 0 #shortcut
|
||||
clear_at.call( 1, 0)
|
||||
clear_at.call(-1, +1)
|
||||
clear_at.call( 0, +1)
|
||||
clear_at.call( 1, +1)
|
||||
|
||||
## Creates a new chunk, derived from the given WFC samples
|
||||
func generate_chunk_at(_x: int, _y: int, chunk_array: Array[Chunk], samples: Array[PackedInt32Array]) -> Chunk:
|
||||
var chunk: Chunk = Chunk.new(_x, _y)
|
||||
_reset_entropy()
|
||||
_update_entropy(chunk, samples)
|
||||
|
||||
while true:
|
||||
var index = _find_lowest_entropy_tile_index()
|
||||
if index < 0: break #none found, finished
|
||||
|
||||
var s: PackedInt32Array = entropy_valid[index].pick_random()
|
||||
chunk.data[index] = s[4]
|
||||
|
||||
_clear_entropy_at(index)
|
||||
_update_entropy(chunk, samples)
|
||||
|
||||
chunk_array.append(chunk)
|
||||
return chunk
|
||||
|
||||
## Returns the index of a tile with the lowest entropy, without being zero
|
||||
func _find_lowest_entropy_tile_index() -> int:
|
||||
#find the lowest-entropy tile
|
||||
var lowest: int = -1
|
||||
|
||||
for i in range(Chunk.CHUNK_WIDTH * Chunk.CHUNK_HEIGHT):
|
||||
if entropy[i].size() == 0: continue #no options
|
||||
|
||||
if lowest < 0:
|
||||
if entropy_total[i] <= 0: continue #no options
|
||||
lowest = i
|
||||
continue
|
||||
if entropy[i].size() < entropy[lowest].size():
|
||||
|
||||
if entropy_total[i] > 0 and entropy_total[i] < entropy_total[lowest]:
|
||||
lowest = i
|
||||
|
||||
#finished
|
||||
if lowest < 0: return lowest
|
||||
|
||||
#finally, set the tile from the sample
|
||||
var s: PackedInt32Array = entropy[lowest].pick_random()
|
||||
chunk.data[lowest] = s[4]
|
||||
return entropy.size()
|
||||
return lowest
|
||||
|
||||
## Returns the valid samples
|
||||
func _find_valid_samples(chunk: Chunk, tile_x: int, tile_y: int, _samples: Array[PackedInt32Array]) -> Array[PackedInt32Array]:
|
||||
func _find_valid_samples_at(chunk: Chunk, tile_x: int, tile_y: int, _samples: Array[PackedInt32Array]) -> Array[PackedInt32Array]:
|
||||
var valid: Array[PackedInt32Array] = []
|
||||
|
||||
#use a lambda for easy reading below
|
||||
|
||||
Reference in New Issue
Block a user