This works reasonably fast for 16x16 chunks

This commit is contained in:
2026-01-08 13:20:03 +11:00
parent 192fb28ea9
commit 7e393e88ac
2 changed files with 62 additions and 30 deletions

View File

@@ -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