Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # wave function collapse background
- extends Control
- export (int, 12, 128) var T_SIZE : int = 64 #px
- export var t_cont_expand : int = 2 #extra cells
- var clock = 0
- export var CLOCK_STEP = 0.01
- export var CLOCK_RESTART = 2.5
- onready var t_cont : Control = $tex_container
- var t_folder := "res://graphics/wfc_tileset01/"
- var t_instructions := "instructions.txt"
- var grid_size : Vector2
- var tiles = [] #[ 0 img, 1 rules[right, top, left, bottom], 2 [rot, flip_h, flip_v] ]
- var matrix_texrect := [] # TextureRect nodes (One dimensional array)
- var matrix_entropy := [] # It stores the ids of the cells that can possible be there (One dimensional array)
- var cell_tot : int
- var cell_activating := [] # (One dimensional bool array) cells activating
- var cell_activated := [] # (One dimensional bool array) cells activated
- var cell_collapsed := [] # (One dimensional bool array) cells collapsed
- var bmap_activating : BitMap
- var bmap_activated : BitMap
- #=================================== INIT ======================================
- func _ready():
- #===== TEMP
- # Mng.emit_signal("scene_changed")
- #===== TEMP
- yield(get_tree(), "idle_frame")
- create_tiles()
- create_grid()
- reset_matrix()
- func create_tiles():
- #--- read instructions
- var file = File.new()
- if not file.file_exists(t_folder + t_instructions):
- print("wfc bg: Cannot find instruction file for tileset @ %s"%[t_folder + t_instructions])
- return false
- file.open(t_folder + t_instructions, File.READ)
- #--- read original tiles
- while not file.eof_reached():
- var parsed = file.get_csv_line()
- var img = load(t_folder+parsed[0])
- var rules = [parsed[1], parsed[2], parsed[3], parsed[4]]
- #--- create new rotated / flipped tiles
- var variations_rules = []
- for rot in range (0, 360, 90):
- for h in range(2):
- for v in range(2):
- var variations = [rot, h == 1, v == 1]
- var new_rules = rotate_flip_rules(rules, variations)
- if not new_rules in variations_rules:
- variations_rules.append(new_rules)
- tiles.append([img, new_rules, variations])
- if false: # DEBUG
- for tile in tiles:
- print("%s, [%s, %s, %s, %s], [%s, %s, %s]"%[tile[0], tile[1][0], tile[1][1], tile[1][2], tile[1][3], tile[2][0], tile[2][1], tile[2][2]])
- func create_grid():
- #--- adjust cells container rect size
- t_cont.rect_size = rect_size + (Vector2.ONE * T_SIZE * t_cont_expand)
- t_cont.rect_size.x = ceil(t_cont.rect_size.x / T_SIZE) * T_SIZE
- t_cont.rect_size.y = ceil(t_cont.rect_size.y / T_SIZE) * T_SIZE
- #--- calculate grid size (cells)
- grid_size.x = ceil(t_cont.rect_size.x / T_SIZE)
- grid_size.y = ceil(t_cont.rect_size.y / T_SIZE)
- bmap_activating = BitMap.new()
- bmap_activated = BitMap.new()
- bmap_activating.resize(grid_size)
- bmap_activated.resize(grid_size)
- cell_tot = grid_size.x*grid_size.y
- #--- clear texture container
- for child in t_cont.get_children():
- child.queue_free()
- #--- initialize matrix with TextureRect and Entropy
- for _y in range(grid_size.y):
- for _x in range(grid_size.x):
- var tex = TextureRect.new()
- tex.expand = true
- tex.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
- t_cont.add_child(tex)
- matrix_texrect.append(tex)
- reset_matrix()
- func reset_matrix():
- #--- reset texrect
- for i in range(matrix_texrect.size()):
- var tex : TextureRect = matrix_texrect[i]
- tex.visible = false
- tex.modulate.a = 0
- tex.rect_size = Vector2.ONE * T_SIZE
- tex.rect_pivot_offset = tex.rect_size/2
- tex.rect_position = cell_id2unit_pos(i) * T_SIZE
- #--- reset entropy
- cell_activating = []
- cell_activated = []
- cell_collapsed = []
- matrix_entropy = []
- for _i in range(matrix_texrect.size()):
- matrix_entropy.append(range(tiles.size()))
- cell_activating.append(false)
- cell_activated.append(false)
- cell_collapsed.append(false)
- #--- DEBUG
- bmap_activating = BitMap.new()
- bmap_activated = BitMap.new()
- bmap_activating.resize(grid_size)
- bmap_activated.resize(grid_size)
- #=========================== WAVE FUNCTION COLLAPSE ============================
- func collapse_cell(cell_id):
- var poss : Array = matrix_entropy[cell_id]
- if poss.empty():
- print("wfc: error, cannot collapse cell")
- return
- var cell_tile = poss[randi()%poss.size()]
- matrix_entropy[cell_id] = [cell_tile]
- cell_collapsed[cell_id] = true
- set_cell_tile(cell_id, cell_tile)
- #--- reduce entropy from neighbours
- var neighbours = cell_neighbours(cell_id)
- for side in range(4):
- var n_id = neighbours[side] # neighbour id
- if n_id == -1 or cell_collapsed[n_id]:
- continue
- #--- remove options
- match side:
- 0: # right neighbour
- for i in range(matrix_entropy[n_id].size()-1, -1, -1):
- var possible = matrix_entropy[n_id][i]
- if tiles[cell_tile][1][0] != tiles[possible][1][2]:
- matrix_entropy[n_id].erase(possible)
- 1: # top neighbour
- for i in range(matrix_entropy[n_id].size()-1, -1, -1):
- var possible = matrix_entropy[n_id][i]
- if tiles[cell_tile][1][1] != tiles[possible][1][3]:
- # print("removing possibility %s from top neighbour"%possible)
- # print("this cell top_rule: %s -> possible tile %s cell, rules: %s"%[tiles[cell_tile][1][1], possible, tiles[possible][1]])
- matrix_entropy[n_id].erase(possible)
- 2: # left neighbour
- for i in range(matrix_entropy[n_id].size()-1, -1, -1):
- var possible = matrix_entropy[n_id][i]
- if tiles[cell_tile][1][2] != tiles[possible][1][0]:
- matrix_entropy[n_id].erase(possible)
- # if i== 0:
- # breakpoint
- 3: # bot neighbour
- for i in range(matrix_entropy[n_id].size()-1, -1, -1):
- var possible = matrix_entropy[n_id][i]
- if tiles[cell_tile][1][3] != tiles[possible][1][1]:
- matrix_entropy[n_id].erase(possible)
- func find_least_entropy_cells() -> Array:
- var cells = []
- var min_entropy = INF
- for cell_id in range(cell_tot):
- if cell_collapsed[cell_id] or matrix_entropy[cell_id].size() < 1:
- continue
- var entropy = matrix_entropy[cell_id].size()
- if entropy > min_entropy:
- continue
- elif entropy == min_entropy:
- cells.append(cell_id)
- else:
- min_entropy = entropy
- cells = [cell_id]
- return cells
- func cell_neighbours(cell_id) -> Array: # [right, top, right, bottom] returns -1 on the sides that are invalid
- var size_x = int(grid_size.x)
- var right : int = cell_id + 1 if ((cell_id+1)%size_x != 0) else -1
- var top : int = cell_id - size_x if (cell_id - size_x) >= 0 else -1
- var left : int = cell_id - 1 if (cell_id)%size_x != 0 else -1
- var bot : int = cell_id + size_x if (cell_id + size_x < matrix_entropy.size() ) else -1
- return [right, top, left, bot]
- func collapse_next():
- var least_entropy_cells = find_least_entropy_cells()
- if not least_entropy_cells.empty():
- var rand_id = randi()%(least_entropy_cells.size())
- collapse_cell(least_entropy_cells[rand_id])
- #============================== CANVAS ANIMATION ===============================
- func visualize_all_random_tiles():
- for i in range(matrix_texrect.size()):
- set_cell_tile(i, randi()%tiles.size())
- func set_cell_tile(cell_id, tile_id):
- var tex : TextureRect = matrix_texrect[cell_id]
- tex.visible = true
- tex.modulate.a = 0
- tex.texture = tiles[tile_id][0]
- tex.rect_rotation = tiles[tile_id][2][0]
- tex.flip_h = tiles[tile_id][2][1]
- tex.flip_v = tiles[tile_id][2][2]
- cell_activating[cell_id] = true
- bmap_activating.set_bit(cell_id2unit_pos(cell_id), true)
- var texture := ImageTexture.new()
- texture.create_from_image(bmap_activating.convert_to_image(), Image.FORMAT_L8)
- $vb/acting.texture = texture
- func _process(d):
- #--- automated cells collapse
- if true:
- clock += d
- if not wfc_completed():
- if clock >= CLOCK_STEP:
- clock = 0
- collapse_next()
- elif clock >= CLOCK_RESTART:
- clock = 0
- reset_matrix()
- #--- animate cells fade_in via bitmask
- for i in range(cell_activating.size()):
- if cell_activating[i]:
- var tex : TextureRect = matrix_texrect[i]
- tex.modulate.a = min(tex.modulate.a + d * 0.05/CLOCK_STEP, 1)
- if tex.modulate.a >= 1:
- cell_activating[i] = false
- cell_activated[i] = true
- bmap_activated.set_bit(cell_id2unit_pos(i), true)
- var texture := ImageTexture.new()
- texture.create_from_image(bmap_activated.convert_to_image(), Image.FORMAT_L8)
- $vb/act.texture = texture
- # move_t_cont_with_mouse()
- move_t_cont_with_gravity_vector()
- #--- debug
- var pos = t_cont.get_local_mouse_position() / T_SIZE
- pos.x = floor(pos.x)
- pos.y = floor(pos.y)
- var cell = unit_pos2cell_id(pos)
- if cell >= cell_collapsed.size():
- return
- $vb/nfo/values/id.text = "%03d"%cell
- if cell_collapsed[cell]:
- var tile = matrix_entropy[cell][0]
- $vb/nfo/values/rules.text = "%s"%[ tiles[tile][1] ]
- else:
- $vb/nfo/values/rules.text = ""
- $vb/nfo/values/neigh.text = "%s"%[cell_neighbours(cell)]
- $vb/nfo/values/entropy.text = "%s"%[matrix_entropy[cell]]
- $vb/nfo/values/collapsed.text = "%s"%cell_collapsed[cell]
- func move_t_cont_with_mouse():
- var diff : Vector2 = t_cont.rect_size - rect_size
- t_cont.rect_position.x = -(get_local_mouse_position().x / rect_size.x) * diff.x
- t_cont.rect_position.y = -(get_local_mouse_position().y / rect_size.y) * diff.y
- func move_t_cont_with_gravity_vector():
- var diff : Vector2 = t_cont.rect_size - rect_size
- var grav = Input.get_gravity().normalized()
- grav.x = (grav.x+1)/2
- grav.y = (grav.y+1)/2
- t_cont.rect_position.x = -grav.x * diff.x
- t_cont.rect_position.y = -grav.y * diff.y
- #"""
- #var noise := OpenSimplexNoise.new()
- #var canvas_vel : Vector2
- #const canvas_speed = 5 #px/s
- #func move_t_cont_with_vel(d):
- # canvas_vel.x += noise.get_noise_2d(OS.get_ticks_usec()*d/1000, 0) * canvas_speed * d
- # canvas_vel.y += noise.get_noise_2d(0, OS.get_ticks_usec()*d/1000) * canvas_speed * d
- # if not bounce_container():
- # t_cont.rect_position += canvas_vel
- #
- #
- #func bounce_container():
- # var pos : Vector2 = t_cont.rect_position
- # var diff : Vector2 = t_cont.rect_size - rect_size
- # var bound_right : bool = canvas_vel.x < 0 and pos.x <= -diff.x
- # var bound_left : bool = canvas_vel.x > 0 and pos.x >= 0
- # var bound_up : bool = canvas_vel.y < 0 and pos.y <= -diff.y
- # var bound_down : bool = canvas_vel.y > 0 and pos.y >= 0
- #
- # if bound_right or bound_left:
- # canvas_vel.x = -canvas_vel.x
- # return true
- # elif bound_up or bound_down:
- # canvas_vel.y = -canvas_vel.y
- # return true
- # return false
- #"""
- #================================== UTILS ======================================
- func cell_id2unit_pos(cell_id) -> Vector2:
- var pos := Vector2()
- pos.x = cell_id%int(grid_size.x)
- pos.y = floor(cell_id/grid_size.x)
- return pos
- func unit_pos2cell_id(pos : Vector2) -> int:
- var cell_id : int = (pos.x) + (pos.y * grid_size.x)
- return cell_id
- func reverse_string(string : String) -> String:
- var reversed := PoolStringArray([])
- for character in string:
- reversed.insert(0, character)
- return reversed.join("")
- func wfc_completed() -> bool:
- for val in cell_collapsed:
- if val == false:
- return false
- return true
- func rotate_flip_rules(rules : Array, variations : Array) -> Array:
- var new_rules : Array = rules.duplicate()
- #--- h_flip
- if variations[1]:
- var swap = new_rules[0]
- new_rules[0] = new_rules[2]
- new_rules[1] = reverse_string(new_rules[1])
- new_rules[2] = swap
- new_rules[3] = reverse_string(new_rules[3])
- #--- v_flip
- if variations[2]:
- var swap = new_rules[1]
- new_rules[0] = reverse_string(new_rules[0])
- new_rules[1] = new_rules[3]
- new_rules[2] = reverse_string(new_rules[2])
- new_rules[3] = swap
- match variations[0]:
- 0: new_rules = rules
- 90:
- new_rules[0] = rules[1]
- new_rules[1] = reverse_string(rules[2])
- new_rules[2] = rules[3]
- new_rules[3] = reverse_string(rules[0])
- 180:
- new_rules[0] = reverse_string(rules[2])
- new_rules[1] = reverse_string(rules[3])
- new_rules[2] = reverse_string(rules[0])
- new_rules[3] = reverse_string(rules[1])
- 270:
- new_rules[0] = reverse_string(rules[3])
- new_rules[1] = rules[0]
- new_rules[2] = reverse_string(rules[1])
- new_rules[3] = rules[2]
- return new_rules
- func _input(event):
- if event is InputEventMouseButton:
- if event.is_pressed():
- pass
- # collapse_next()
- #
- # var pos = t_cont.get_local_mouse_position() / T_SIZE
- # pos.x = floor(pos.x)
- # pos.y = floor(pos.y)
- # collapse_cell(unit_pos2cell_id(pos))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement