This works reasonably fast for 16x16 chunks
This commit is contained in:
@@ -4,9 +4,9 @@ extends Node
|
|||||||
var _chunks: Array[Chunk] = []
|
var _chunks: Array[Chunk] = []
|
||||||
|
|
||||||
#Create some test samples - these samples must ALWAYS be 9-elements long, 5th element is the result
|
#Create some test samples - these samples must ALWAYS be 9-elements long, 5th element is the result
|
||||||
var _samples: Array[PackedInt32Array] = [
|
#var _samples: Array[PackedInt32Array] = [
|
||||||
[1,1,1, 1,1,1, 1,1,1],
|
#[1,1,1, 1,1,1, 1,1,1],
|
||||||
]
|
#]
|
||||||
|
|
||||||
@onready
|
@onready
|
||||||
var wfc: Node = get_node("../Generator")
|
var wfc: Node = get_node("../Generator")
|
||||||
@@ -15,6 +15,8 @@ func _ready() -> void:
|
|||||||
var ruleset: PackedInt32Array = read_sample_ruleset("sample1.png")
|
var ruleset: PackedInt32Array = read_sample_ruleset("sample1.png")
|
||||||
var samples: Array[PackedInt32Array] = parse_samples_from_ruleset(ruleset)
|
var samples: Array[PackedInt32Array] = parse_samples_from_ruleset(ruleset)
|
||||||
var c = wfc.generate_chunk_at(0,0,_chunks,samples)
|
var c = wfc.generate_chunk_at(0,0,_chunks,samples)
|
||||||
|
#TODO: build a visual layout for the results
|
||||||
|
#TODO: handle the chunk-edges
|
||||||
|
|
||||||
#print(ruleset)
|
#print(ruleset)
|
||||||
#print(samples)
|
#print(samples)
|
||||||
|
|||||||
@@ -1,48 +1,78 @@
|
|||||||
extends Node
|
extends Node
|
||||||
## The generator works with pure numbers, you'll need something else to convert it to actual tiles
|
## 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
|
## Entropy for tiles far away from activity shouldn't be recalculated, so cache them
|
||||||
func generate_chunk_at(_x: int, _y: int, _chunk_array: Array[Chunk], _samples: Array[PackedInt32Array]) -> Chunk:
|
var entropy_valid: Array[Array] = [] ##Options for each tile
|
||||||
#TODO: fix the chunk-edges
|
var entropy_total: PackedInt32Array = [] ##Total number of options, negative means unknown
|
||||||
var chunk: Chunk = Chunk.new(_x, _y)
|
|
||||||
|
|
||||||
while _set_lowest_entropy_tile(chunk, _samples) > 0: pass
|
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)
|
||||||
|
|
||||||
_chunk_array.append(chunk)
|
func _update_entropy(chunk: Chunk, samples: Array[PackedInt32Array]) -> void:
|
||||||
return chunk
|
#iterate over indeterminate tiles to find their valid neighbours
|
||||||
|
|
||||||
## 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)
|
|
||||||
|
|
||||||
#iterate over unset tiles with valid neighbours
|
|
||||||
for x in range(Chunk.CHUNK_WIDTH):
|
for x in range(Chunk.CHUNK_WIDTH):
|
||||||
for y in range((Chunk.CHUNK_HEIGHT)):
|
for y in range((Chunk.CHUNK_HEIGHT)):
|
||||||
if chunk.data[y * Chunk.CHUNK_HEIGHT + x] == 0: #direct access, to skip extra checks
|
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[y * Chunk.CHUNK_HEIGHT + x] = _find_valid_samples(chunk, x, y, _samples)
|
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
|
#find the lowest-entropy tile
|
||||||
var lowest: int = -1
|
var lowest: int = -1
|
||||||
for i in range(Chunk.CHUNK_WIDTH * Chunk.CHUNK_HEIGHT):
|
|
||||||
if entropy[i].size() == 0: continue #no options
|
|
||||||
|
|
||||||
|
for i in range(Chunk.CHUNK_WIDTH * Chunk.CHUNK_HEIGHT):
|
||||||
if lowest < 0:
|
if lowest < 0:
|
||||||
|
if entropy_total[i] <= 0: continue #no options
|
||||||
lowest = i
|
lowest = i
|
||||||
continue
|
continue
|
||||||
if entropy[i].size() < entropy[lowest].size():
|
|
||||||
|
if entropy_total[i] > 0 and entropy_total[i] < entropy_total[lowest]:
|
||||||
lowest = i
|
lowest = i
|
||||||
|
|
||||||
#finished
|
#finished
|
||||||
if lowest < 0: return lowest
|
return lowest
|
||||||
|
|
||||||
#finally, set the tile from the sample
|
func _find_valid_samples_at(chunk: Chunk, tile_x: int, tile_y: int, _samples: Array[PackedInt32Array]) -> Array[PackedInt32Array]:
|
||||||
var s: PackedInt32Array = entropy[lowest].pick_random()
|
|
||||||
chunk.data[lowest] = s[4]
|
|
||||||
return entropy.size()
|
|
||||||
|
|
||||||
## Returns the valid samples
|
|
||||||
func _find_valid_samples(chunk: Chunk, tile_x: int, tile_y: int, _samples: Array[PackedInt32Array]) -> Array[PackedInt32Array]:
|
|
||||||
var valid: Array[PackedInt32Array] = []
|
var valid: Array[PackedInt32Array] = []
|
||||||
|
|
||||||
#use a lambda for easy reading below
|
#use a lambda for easy reading below
|
||||||
|
|||||||
Reference in New Issue
Block a user