Sfoglia il codice sorgente

Merge pull request #54073 from groud/terrains_for_procgen

Rémi Verschelde 3 anni fa
parent
commit
01afa39d21

+ 11 - 0
doc/classes/TileMap.xml

@@ -204,6 +204,17 @@
 				- The alternative tile identifier [code]alternative_tile[/code] identifies a tile alternative the source is a [TileSetAtlasSource], and the scene for a [TileSetScenesCollectionSource].
 			</description>
 		</method>
+		<method name="set_cells_from_surrounding_terrains">
+			<return type="void" />
+			<argument index="0" name="layer" type="int" />
+			<argument index="1" name="cells" type="Vector2i[]" />
+			<argument index="2" name="terrain_set" type="int" />
+			<argument index="3" name="ignore_empty_terrains" type="bool" default="true" />
+			<description>
+				Updates all the cells in the [code]cells[/code] coordinates array and replace them by tiles that matches the surrounding cells terrains. Only cells form the given [code]terrain_set[/code] are considered.
+				If [code]ignore_empty_terrains[/code] is true, zones with no terrain defined are ignored to select the tiles.
+			</description>
+		</method>
 		<method name="set_layer_enabled">
 			<return type="void" />
 			<argument index="0" name="layer" type="int" />

+ 86 - 700
editor/plugins/tiles/tile_map_editor.cpp

@@ -2219,569 +2219,7 @@ Vector<TileMapEditorPlugin::TabData> TileMapEditorTerrainsPlugin::get_tabs() con
 	return tabs;
 }
 
-Map<Vector2i, TileSet::CellNeighbor> TileMapEditorTerrainsPlugin::Constraint::get_overlapping_coords_and_peering_bits() const {
-	Map<Vector2i, TileSet::CellNeighbor> output;
-	Ref<TileSet> tile_set = tile_map->get_tileset();
-	ERR_FAIL_COND_V(!tile_set.is_valid(), output);
-
-	TileSet::TileShape shape = tile_set->get_tile_shape();
-	if (shape == TileSet::TILE_SHAPE_SQUARE) {
-		switch (bit) {
-			case 0:
-				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
-				break;
-			case 1:
-				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
-				break;
-			case 2:
-				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
-				break;
-			case 3:
-				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
-				break;
-			default:
-				ERR_FAIL_V(output);
-		}
-	} else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
-		switch (bit) {
-			case 0:
-				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
-				break;
-			case 1:
-				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
-				break;
-			case 2:
-				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
-				break;
-			case 3:
-				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
-				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
-				break;
-			default:
-				ERR_FAIL_V(output);
-		}
-	} else {
-		// Half offset shapes.
-		TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
-		if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
-			switch (bit) {
-				case 0:
-					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
-					break;
-				case 1:
-					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
-					break;
-				case 2:
-					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
-					break;
-				case 3:
-					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
-					break;
-				case 4:
-					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
-					break;
-				default:
-					ERR_FAIL_V(output);
-			}
-		} else {
-			switch (bit) {
-				case 0:
-					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
-					break;
-				case 1:
-					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
-					break;
-				case 2:
-					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
-					break;
-				case 3:
-					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
-					break;
-				case 4:
-					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
-					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
-					break;
-				default:
-					ERR_FAIL_V(output);
-			}
-		}
-	}
-	return output;
-}
-
-TileMapEditorTerrainsPlugin::Constraint::Constraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) {
-	// The way we build the constraint make it easy to detect conflicting constraints.
-	tile_map = p_tile_map;
-
-	Ref<TileSet> tile_set = tile_map->get_tileset();
-	ERR_FAIL_COND(!tile_set.is_valid());
-
-	TileSet::TileShape shape = tile_set->get_tile_shape();
-	if (shape == TileSet::TILE_SHAPE_SQUARE || shape == TileSet::TILE_SHAPE_ISOMETRIC) {
-		switch (p_bit) {
-			case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
-			case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
-				bit = 0;
-				base_cell_coords = p_position;
-				break;
-			case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
-			case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
-				bit = 1;
-				base_cell_coords = p_position;
-				break;
-			case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
-			case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
-				bit = 2;
-				base_cell_coords = p_position;
-				break;
-			case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
-			case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
-				bit = 3;
-				base_cell_coords = p_position;
-				break;
-			case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
-			case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
-				bit = 0;
-				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
-				break;
-			case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
-			case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
-				bit = 1;
-				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
-				break;
-			case TileSet::CELL_NEIGHBOR_TOP_SIDE:
-			case TileSet::CELL_NEIGHBOR_TOP_CORNER:
-				bit = 2;
-				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
-				break;
-			case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
-			case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
-				bit = 3;
-				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
-				break;
-			default:
-				ERR_FAIL();
-				break;
-		}
-	} else {
-		// Half-offset shapes
-		TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
-		if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
-			switch (p_bit) {
-				case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
-					bit = 0;
-					base_cell_coords = p_position;
-					break;
-				case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
-					bit = 1;
-					base_cell_coords = p_position;
-					break;
-				case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
-					bit = 2;
-					base_cell_coords = p_position;
-					break;
-				case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
-					bit = 3;
-					base_cell_coords = p_position;
-					break;
-				case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
-					bit = 4;
-					base_cell_coords = p_position;
-					break;
-				case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
-					bit = 1;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
-					break;
-				case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
-					bit = 0;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
-					break;
-				case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
-					bit = 3;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
-					break;
-				case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
-					bit = 2;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
-					break;
-				case TileSet::CELL_NEIGHBOR_TOP_CORNER:
-					bit = 1;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
-					break;
-				case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
-					bit = 4;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
-					break;
-				case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
-					bit = 3;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
-					break;
-				default:
-					ERR_FAIL();
-					break;
-			}
-		} else {
-			switch (p_bit) {
-				case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
-					bit = 0;
-					base_cell_coords = p_position;
-					break;
-				case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
-					bit = 1;
-					base_cell_coords = p_position;
-					break;
-				case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
-					bit = 2;
-					base_cell_coords = p_position;
-					break;
-				case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
-					bit = 3;
-					base_cell_coords = p_position;
-					break;
-				case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
-					bit = 0;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
-					break;
-				case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
-					bit = 4;
-					base_cell_coords = p_position;
-					break;
-				case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
-					bit = 2;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
-					break;
-				case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
-					bit = 1;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
-					break;
-				case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
-					bit = 0;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
-					break;
-				case TileSet::CELL_NEIGHBOR_TOP_SIDE:
-					bit = 3;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
-					break;
-				case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
-					bit = 2;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
-					break;
-				case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
-					bit = 4;
-					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
-					break;
-				default:
-					ERR_FAIL();
-					break;
-			}
-		}
-	}
-	terrain = p_terrain;
-}
-
-Set<TileMapEditorTerrainsPlugin::TerrainsTilePattern> TileMapEditorTerrainsPlugin::_get_valid_terrains_tile_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<Constraint> p_constraints) const {
-	TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
-	if (!tile_map) {
-		return Set<TerrainsTilePattern>();
-	}
-
-	Ref<TileSet> tile_set = tile_map->get_tileset();
-	if (!tile_set.is_valid()) {
-		return Set<TerrainsTilePattern>();
-	}
-
-	// Returns all tiles compatible with the given constraints.
-	Set<TerrainsTilePattern> compatible_terrain_tile_patterns;
-	for (const KeyValue<TerrainsTilePattern, Set<TileMapCell>> &E : per_terrain_terrains_tile_patterns_tiles[p_terrain_set]) {
-		int valid = true;
-		int in_pattern_count = 0;
-		for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
-			TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
-			if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
-				// Check if the bit is compatible with the constraints.
-				Constraint terrain_bit_constraint = Constraint(tile_map, p_position, bit, E.key[in_pattern_count]);
-
-				Set<Constraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
-				if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) {
-					valid = false;
-					break;
-				}
-				in_pattern_count++;
-			}
-		}
-
-		if (valid) {
-			compatible_terrain_tile_patterns.insert(E.key);
-		}
-	}
-
-	return compatible_terrain_tile_patterns;
-}
-
-Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_constraints_from_removed_cells_list(const Set<Vector2i> &p_to_replace, int p_terrain_set) const {
-	TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
-	if (!tile_map) {
-		return Set<Constraint>();
-	}
-
-	Ref<TileSet> tile_set = tile_map->get_tileset();
-	if (!tile_set.is_valid()) {
-		return Set<Constraint>();
-	}
-
-	ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), Set<Constraint>());
-	ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), Set<Constraint>());
-
-	// Build a set of dummy constraints get the constrained points.
-	Set<Constraint> dummy_constraints;
-	for (Set<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) {
-		for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over sides.
-			TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
-			if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
-				dummy_constraints.insert(Constraint(tile_map, E->get(), bit, -1));
-			}
-		}
-	}
-
-	// For each constrained point, we get all overlapping tiles, and select the most adequate terrain for it.
-	Set<Constraint> constraints;
-	for (Set<Constraint>::Element *E = dummy_constraints.front(); E; E = E->next()) {
-		Constraint c = E->get();
-
-		Map<int, int> terrain_count;
-
-		// Count the number of occurrences per terrain.
-		Map<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits();
-		for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) {
-			if (!p_to_replace.has(E_overlapping.key)) {
-				TileMapCell neighbor_cell = tile_map->get_cell(tile_map_layer, E_overlapping.key);
-				TileData *neighbor_tile_data = nullptr;
-				if (terrain_tiles.has(neighbor_cell) && terrain_tiles[neighbor_cell]->get_terrain_set() == p_terrain_set) {
-					neighbor_tile_data = terrain_tiles[neighbor_cell];
-				}
-
-				int terrain = neighbor_tile_data ? neighbor_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(E_overlapping.value)) : -1;
-				if (terrain_count.has(terrain)) {
-					terrain_count[terrain] = 0;
-				}
-				terrain_count[terrain] += 1;
-			}
-		}
-
-		// Get the terrain with the max number of occurrences.
-		int max = 0;
-		int max_terrain = -1;
-		for (const KeyValue<int, int> &E_terrain_count : terrain_count) {
-			if (E_terrain_count.value > max) {
-				max = E_terrain_count.value;
-				max_terrain = E_terrain_count.key;
-			}
-		}
-
-		// Set the adequate terrain.
-		if (max > 0) {
-			c.set_terrain(max_terrain);
-			constraints.insert(c);
-		}
-	}
-
-	return constraints;
-}
-
-Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TerrainsTilePattern p_terrains_tile_pattern) const {
-	TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
-	if (!tile_map) {
-		return Set<TileMapEditorTerrainsPlugin::Constraint>();
-	}
-
-	Ref<TileSet> tile_set = tile_map->get_tileset();
-	if (!tile_set.is_valid()) {
-		return Set<TileMapEditorTerrainsPlugin::Constraint>();
-	}
-
-	// Compute the constraints needed from the surrounding tiles.
-	Set<TileMapEditorTerrainsPlugin::Constraint> output;
-	int in_pattern_count = 0;
-	for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
-		TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
-		if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) {
-			Constraint c = Constraint(tile_map, p_position, side, p_terrains_tile_pattern[in_pattern_count]);
-			output.insert(c);
-			in_pattern_count++;
-		}
-	}
-
-	return output;
-}
-
-Map<Vector2i, TileMapEditorTerrainsPlugin::TerrainsTilePattern> TileMapEditorTerrainsPlugin::_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const {
-	TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
-	if (!tile_map) {
-		return Map<Vector2i, TerrainsTilePattern>();
-	}
-
-	Ref<TileSet> tile_set = tile_map->get_tileset();
-	if (!tile_set.is_valid()) {
-		return Map<Vector2i, TileMapEditorTerrainsPlugin::TerrainsTilePattern>();
-	}
-
-	// Copy the constraints set.
-	Set<TileMapEditorTerrainsPlugin::Constraint> constraints = p_constraints;
-
-	// Compute all acceptable tiles for each cell.
-	Map<Vector2i, Set<TerrainsTilePattern>> per_cell_acceptable_tiles;
-	for (Set<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) {
-		per_cell_acceptable_tiles[E->get()] = _get_valid_terrains_tile_patterns_for_constraints(p_terrain_set, E->get(), constraints);
-	}
-
-	// Output map.
-	Map<Vector2i, TerrainsTilePattern> output;
-
-	// Add all positions to a set.
-	Set<Vector2i> to_replace = Set<Vector2i>(p_to_replace);
-	while (!to_replace.is_empty()) {
-		// Compute the minimum number of tile possibilities for each cell.
-		int min_nb_possibilities = 100000000;
-		for (const KeyValue<Vector2i, Set<TerrainsTilePattern>> &E : per_cell_acceptable_tiles) {
-			min_nb_possibilities = MIN(min_nb_possibilities, E.value.size());
-		}
-
-		// Get the set of possible cells to fill.
-		LocalVector<Vector2i> to_choose_from;
-		for (const KeyValue<Vector2i, Set<TerrainsTilePattern>> &E : per_cell_acceptable_tiles) {
-			if (E.value.size() == min_nb_possibilities) {
-				to_choose_from.push_back(E.key);
-			}
-		}
-
-		// Randomly pick a tile out of the most constrained.
-		Vector2i selected_cell_to_replace = to_choose_from[Math::random(0, to_choose_from.size() - 1)];
-
-		// Randomly select a tile out of them the put it in the grid.
-		Set<TerrainsTilePattern> valid_tiles = per_cell_acceptable_tiles[selected_cell_to_replace];
-		if (valid_tiles.is_empty()) {
-			// No possibilities :/
-			break;
-		}
-		int random_terrain_tile_pattern_index = Math::random(0, valid_tiles.size() - 1);
-		Set<TerrainsTilePattern>::Element *E = valid_tiles.front();
-		for (int i = 0; i < random_terrain_tile_pattern_index; i++) {
-			E = E->next();
-		}
-		TerrainsTilePattern selected_terrain_tile_pattern = E->get();
-
-		// Set the selected cell into the output.
-		output[selected_cell_to_replace] = selected_terrain_tile_pattern;
-		to_replace.erase(selected_cell_to_replace);
-		per_cell_acceptable_tiles.erase(selected_cell_to_replace);
-
-		// Add the new constraints from the added tiles.
-		Set<TileMapEditorTerrainsPlugin::Constraint> new_constraints = _get_constraints_from_added_tile(selected_cell_to_replace, p_terrain_set, selected_terrain_tile_pattern);
-		for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E_constraint = new_constraints.front(); E_constraint; E_constraint = E_constraint->next()) {
-			constraints.insert(E_constraint->get());
-		}
-
-		// Compute valid tiles again for neighbors.
-		for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
-			TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
-			if (tile_map->is_existing_neighbor(side)) {
-				Vector2i neighbor = tile_map->get_neighbor_cell(selected_cell_to_replace, side);
-				if (to_replace.has(neighbor)) {
-					per_cell_acceptable_tiles[neighbor] = _get_valid_terrains_tile_patterns_for_constraints(p_terrain_set, neighbor, constraints);
-				}
-			}
-		}
-	}
-	return output;
-}
-
-TileMapCell TileMapEditorTerrainsPlugin::_get_random_tile_from_pattern(int p_terrain_set, TerrainsTilePattern p_terrain_tile_pattern) const {
-	TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
-	if (!tile_map) {
-		return TileMapCell();
-	}
-
-	Ref<TileSet> tile_set = tile_map->get_tileset();
-	if (!tile_set.is_valid()) {
-		return TileMapCell();
-	}
-
-	// Count the sum of probabilities.
-	double sum = 0.0;
-	Set<TileMapCell> set = per_terrain_terrains_tile_patterns_tiles[p_terrain_set][p_terrain_tile_pattern];
-	for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
-		if (E->get().source_id >= 0) {
-			Ref<TileSetSource> source = tile_set->get_source(E->get().source_id);
-
-			Ref<TileSetAtlasSource> atlas_source = source;
-			if (atlas_source.is_valid()) {
-				TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
-				sum += tile_data->get_probability();
-			} else {
-				sum += 1.0;
-			}
-		} else {
-			sum += 1.0;
-		}
-	}
-
-	// Generate a random number.
-	double count = 0.0;
-	double picked = Math::random(0.0, sum);
-
-	// Pick the tile.
-	for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
-		if (E->get().source_id >= 0) {
-			Ref<TileSetSource> source = tile_set->get_source(E->get().source_id);
-
-			Ref<TileSetAtlasSource> atlas_source = source;
-			if (atlas_source.is_valid()) {
-				TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
-				count += tile_data->get_probability();
-			} else {
-				count += 1.0;
-			}
-		} else {
-			count += 1.0;
-		}
-
-		if (count >= picked) {
-			return E->get();
-		}
-	}
-
-	ERR_FAIL_V(TileMapCell());
-}
-
-Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map<Vector2i, TerrainsTilePattern> &p_to_paint, int p_terrain_set) const {
+Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const {
 	TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
 	if (!tile_map) {
 		return Map<Vector2i, TileMapCell>();
@@ -2795,20 +2233,20 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
 	Map<Vector2i, TileMapCell> output;
 
 	// Add the constraints from the added tiles.
-	Set<TileMapEditorTerrainsPlugin::Constraint> added_tiles_constraints_set;
-	for (const KeyValue<Vector2i, TerrainsTilePattern> &E_to_paint : p_to_paint) {
+	Set<TileMap::TerrainConstraint> added_tiles_constraints_set;
+	for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
 		Vector2i coords = E_to_paint.key;
-		TerrainsTilePattern terrains_tile_pattern = E_to_paint.value;
+		TileSet::TerrainsPattern terrains_pattern = E_to_paint.value;
 
-		Set<TileMapEditorTerrainsPlugin::Constraint> cell_constraints = _get_constraints_from_added_tile(coords, p_terrain_set, terrains_tile_pattern);
-		for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E = cell_constraints.front(); E; E = E->next()) {
+		Set<TileMap::TerrainConstraint> cell_constraints = tile_map->get_terrain_constraints_from_added_tile(coords, p_terrain_set, terrains_pattern);
+		for (Set<TileMap::TerrainConstraint>::Element *E = cell_constraints.front(); E; E = E->next()) {
 			added_tiles_constraints_set.insert(E->get());
 		}
 	}
 
 	// Build the list of potential tiles to replace.
 	Set<Vector2i> potential_to_replace;
-	for (const KeyValue<Vector2i, TerrainsTilePattern> &E_to_paint : p_to_paint) {
+	for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
 		Vector2i coords = E_to_paint.key;
 		for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
 			if (tile_map->is_existing_neighbor(TileSet::CellNeighbor(i))) {
@@ -2823,42 +2261,42 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
 	// Set of tiles to replace
 	Set<Vector2i> to_replace;
 
-	// Add the central tiles to the one to replace. TODO: maybe change that.
-	for (const KeyValue<Vector2i, TerrainsTilePattern> &E_to_paint : p_to_paint) {
+	// Add the central tiles to the one to replace.
+	for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
 		to_replace.insert(E_to_paint.key);
 	}
 
 	// Add the constraints from the surroundings of the modified areas.
-	Set<TileMapEditorTerrainsPlugin::Constraint> removed_cells_constraints_set;
+	Set<TileMap::TerrainConstraint> removed_cells_constraints_set;
 	bool to_replace_modified = true;
 	while (to_replace_modified) {
 		// Get the constraints from the removed cells.
-		removed_cells_constraints_set = _get_constraints_from_removed_cells_list(to_replace, p_terrain_set);
+		removed_cells_constraints_set = tile_map->get_terrain_constraints_from_removed_cells_list(tile_map_layer, to_replace, p_terrain_set);
 
 		// Filter the sources to make sure they are in the potential_to_replace.
-		Map<Constraint, Set<Vector2i>> source_tiles_of_constraint;
-		for (Set<Constraint>::Element *E = removed_cells_constraints_set.front(); E; E = E->next()) {
+		Map<TileMap::TerrainConstraint, Set<Vector2i>> per_constraint_tiles;
+		for (Set<TileMap::TerrainConstraint>::Element *E = removed_cells_constraints_set.front(); E; E = E->next()) {
 			Map<Vector2i, TileSet::CellNeighbor> sources_of_constraint = E->get().get_overlapping_coords_and_peering_bits();
 			for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_source_tile_of_constraint : sources_of_constraint) {
 				if (potential_to_replace.has(E_source_tile_of_constraint.key)) {
-					source_tiles_of_constraint[E->get()].insert(E_source_tile_of_constraint.key);
+					per_constraint_tiles[E->get()].insert(E_source_tile_of_constraint.key);
 				}
 			}
 		}
 
 		to_replace_modified = false;
-		for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
-			Constraint c = E->get();
+		for (Set<TileMap::TerrainConstraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
+			TileMap::TerrainConstraint c = E->get();
 			// Check if we have a conflict in constraints.
 			if (removed_cells_constraints_set.has(c) && removed_cells_constraints_set.find(c)->get().get_terrain() != c.get_terrain()) {
 				// If we do, we search for a neighbor to remove.
-				if (source_tiles_of_constraint.has(c) && !source_tiles_of_constraint[c].is_empty()) {
+				if (per_constraint_tiles.has(c) && !per_constraint_tiles[c].is_empty()) {
 					// Remove it.
-					Vector2i to_add_to_remove = source_tiles_of_constraint[c].front()->get();
+					Vector2i to_add_to_remove = per_constraint_tiles[c].front()->get();
 					potential_to_replace.erase(to_add_to_remove);
 					to_replace.insert(to_add_to_remove);
 					to_replace_modified = true;
-					for (KeyValue<Constraint, Set<Vector2i>> &E_source_tiles_of_constraint : source_tiles_of_constraint) {
+					for (KeyValue<TileMap::TerrainConstraint, Set<Vector2i>> &E_source_tiles_of_constraint : per_constraint_tiles) {
 						E_source_tiles_of_constraint.value.erase(to_add_to_remove);
 					}
 					break;
@@ -2868,22 +2306,27 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
 	}
 
 	// Combine all constraints together.
-	Set<TileMapEditorTerrainsPlugin::Constraint> constraints = removed_cells_constraints_set;
-	for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
+	Set<TileMap::TerrainConstraint> constraints = removed_cells_constraints_set;
+	for (Set<TileMap::TerrainConstraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
 		constraints.insert(E->get());
 	}
 
+	// Remove the central tiles from the ones to replace.
+	for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
+		to_replace.erase(E_to_paint.key);
+	}
+
 	// Run WFC to fill the holes with the constraints.
-	Map<Vector2i, TerrainsTilePattern> wfc_output = _wave_function_collapse(to_replace, p_terrain_set, constraints);
+	Map<Vector2i, TileSet::TerrainsPattern> wfc_output = tile_map->terrain_wave_function_collapse(to_replace, p_terrain_set, constraints);
 
-	// Use the WFC run for the output.
-	for (const KeyValue<Vector2i, TerrainsTilePattern> &E : wfc_output) {
-		output[E.key] = _get_random_tile_from_pattern(p_terrain_set, E.value);
+	// Actually paint the tiles.
+	for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
+		output[E_to_paint.key] = tile_set->get_random_tile_from_pattern(p_terrain_set, E_to_paint.value);
 	}
 
-	// Override the WFC results to make sure at least the painted tiles are actually painted.
-	for (const KeyValue<Vector2i, TerrainsTilePattern> &E_to_paint : p_to_paint) {
-		output[E_to_paint.key] = _get_random_tile_from_pattern(p_terrain_set, E_to_paint.value);
+	// Use the WFC run for the output.
+	for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : wfc_output) {
+		output[E.key] = tile_set->get_random_tile_from_pattern(p_terrain_set, E.value);
 	}
 
 	return output;
@@ -2901,10 +2344,23 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
 	switch (drag_type) {
 		case DRAG_TYPE_PICK: {
 			Vector2i coords = tile_map->world_to_map(mpos);
-			TileMapCell tile = tile_map->get_cell(tile_map_layer, coords);
+			TileMapCell cell = tile_map->get_cell(tile_map_layer, coords);
+
+			Ref<TileSet> tile_set = tile_map->get_tileset();
+			if (!tile_set.is_valid()) {
+				return;
+			}
 
-			if (terrain_tiles.has(tile)) {
-				Array terrains_tile_pattern = _build_terrains_tile_pattern(terrain_tiles[tile]);
+			TileData *tile_data = nullptr;
+
+			Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
+			Ref<TileSetAtlasSource> atlas_source = source;
+			if (atlas_source.is_valid()) {
+				tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile));
+			}
+
+			if (tile_data) {
+				Array terrains_pattern = tile_data->get_terrains_pattern();
 
 				// Find the tree item for the right terrain set.
 				bool need_tree_item_switch = true;
@@ -2914,7 +2370,7 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
 					if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
 						int terrain_set = metadata_dict["terrain_set"];
 						int terrain_id = metadata_dict["terrain_id"];
-						if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) {
+						if (per_terrain_terrains_patterns[terrain_set][terrain_id].has(terrains_pattern)) {
 							need_tree_item_switch = false;
 						}
 					}
@@ -2926,7 +2382,7 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
 						if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
 							int terrain_set = metadata_dict["terrain_set"];
 							int terrain_id = metadata_dict["terrain_id"];
-							if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) {
+							if (per_terrain_terrains_patterns[terrain_set][terrain_id].has(terrains_pattern)) {
 								// Found
 								tree_item->select(0);
 								_update_tiles_list();
@@ -2940,10 +2396,10 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
 				if (tree_item) {
 					for (int i = 0; i < terrains_tile_list->get_item_count(); i++) {
 						Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i);
-						TerrainsTilePattern in_meta_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"];
+						TileSet::TerrainsPattern in_meta_terrains_pattern = metadata_dict["terrains_pattern"];
 						bool equals = true;
-						for (int j = 0; j < terrains_tile_pattern.size(); j++) {
-							if (terrains_tile_pattern[j] != in_meta_terrains_tile_pattern[j]) {
+						for (int j = 0; j < terrains_pattern.size(); j++) {
+							if (terrains_pattern[j] != in_meta_terrains_pattern[j]) {
 								equals = false;
 								break;
 							}
@@ -2999,7 +2455,7 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
 	ERR_FAIL_COND_V(tile_map_layer >= tile_map->get_layers_count(), false);
 
 	// Get the selected terrain.
-	TerrainsTilePattern selected_terrains_tile_pattern;
+	TileSet::TerrainsPattern selected_terrains_pattern;
 	int selected_terrain_set = -1;
 
 	TreeItem *selected_tree_item = terrains_tree->get_selected();
@@ -3010,16 +2466,16 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
 
 		// Selected tile
 		if (erase_button->is_pressed()) {
-			selected_terrains_tile_pattern.clear();
+			selected_terrains_pattern.clear();
 			for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
 				TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
 				if (tile_set->is_valid_peering_bit_terrain(selected_terrain_set, side)) {
-					selected_terrains_tile_pattern.push_back(-1);
+					selected_terrains_pattern.push_back(-1);
 				}
 			}
 		} else if (terrains_tile_list->is_anything_selected()) {
 			metadata_dict = terrains_tile_list->get_item_metadata(terrains_tile_list->get_selected_items()[0]);
-			selected_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"];
+			selected_terrains_pattern = metadata_dict["terrains_pattern"];
 		}
 	}
 
@@ -3032,9 +2488,9 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
 			case DRAG_TYPE_PAINT: {
 				if (selected_terrain_set >= 0) {
 					Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
-					Map<Vector2i, TerrainsTilePattern> to_draw;
+					Map<Vector2i, TileSet::TerrainsPattern> to_draw;
 					for (int i = 0; i < line.size(); i++) {
-						to_draw[line[i]] = selected_terrains_tile_pattern;
+						to_draw[line[i]] = selected_terrains_pattern;
 					}
 					Map<Vector2i, TileMapCell> modified = _draw_terrains(to_draw, selected_terrain_set);
 					for (const KeyValue<Vector2i, TileMapCell> &E : modified) {
@@ -3066,14 +2522,14 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
 					drag_type = DRAG_TYPE_PICK;
 				} else {
 					// Paint otherwise.
-					if (selected_terrain_set >= 0 && !selected_terrains_tile_pattern.is_empty() && tool_buttons_group->get_pressed_button() == paint_tool_button) {
+					if (selected_terrain_set >= 0 && !selected_terrains_pattern.is_empty() && tool_buttons_group->get_pressed_button() == paint_tool_button) {
 						drag_type = DRAG_TYPE_PAINT;
 						drag_start_mouse_pos = mpos;
 
 						drag_modified.clear();
 
-						Map<Vector2i, TerrainsTilePattern> terrains_to_draw;
-						terrains_to_draw[tile_map->world_to_map(mpos)] = selected_terrains_tile_pattern;
+						Map<Vector2i, TileSet::TerrainsPattern> terrains_to_draw;
+						terrains_to_draw[tile_map->world_to_map(mpos)] = selected_terrains_pattern;
 
 						Map<Vector2i, TileMapCell> to_draw = _draw_terrains(terrains_to_draw, selected_terrain_set);
 						for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
@@ -3097,26 +2553,6 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
 	return false;
 }
 
-TileMapEditorTerrainsPlugin::TerrainsTilePattern TileMapEditorTerrainsPlugin::_build_terrains_tile_pattern(TileData *p_tile_data) {
-	TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
-	if (!tile_map) {
-		return TerrainsTilePattern();
-	}
-
-	Ref<TileSet> tile_set = tile_map->get_tileset();
-	if (!tile_set.is_valid()) {
-		return TerrainsTilePattern();
-	}
-
-	TerrainsTilePattern output;
-	for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
-		if (tile_set->is_valid_peering_bit_terrain(p_tile_data->get_terrain_set(), TileSet::CellNeighbor(i))) {
-			output.push_back(p_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(i)));
-		}
-	}
-	return output;
-}
-
 void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
 	TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
 	if (!tile_map) {
@@ -3128,45 +2564,12 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
 		return;
 	}
 
-	// Compute the tile sides.
-	tile_sides.clear();
-	TileSet::TileShape shape = tile_set->get_tile_shape();
-	if (shape == TileSet::TILE_SHAPE_SQUARE) {
-		tile_sides.push_back(TileSet::CELL_NEIGHBOR_RIGHT_SIDE);
-		tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE);
-		tile_sides.push_back(TileSet::CELL_NEIGHBOR_LEFT_SIDE);
-		tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_SIDE);
-	} else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
-		tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
-		tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
-		tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
-		tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
-	} else {
-		if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
-			tile_sides.push_back(TileSet::CELL_NEIGHBOR_RIGHT_SIDE);
-			tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
-			tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
-			tile_sides.push_back(TileSet::CELL_NEIGHBOR_LEFT_SIDE);
-			tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
-			tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
-		} else {
-			tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
-			tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE);
-			tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
-			tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
-			tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_SIDE);
-			tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
-		}
-	}
-
 	// Organizes tiles into structures.
-	per_terrain_terrains_tile_patterns_tiles.resize(tile_set->get_terrain_sets_count());
-	per_terrain_terrains_tile_patterns.resize(tile_set->get_terrain_sets_count());
+	per_terrain_terrains_patterns.resize(tile_set->get_terrain_sets_count());
 	for (int i = 0; i < tile_set->get_terrain_sets_count(); i++) {
-		per_terrain_terrains_tile_patterns_tiles[i].clear();
-		per_terrain_terrains_tile_patterns[i].resize(tile_set->get_terrains_count(i));
-		for (int j = 0; j < (int)per_terrain_terrains_tile_patterns[i].size(); j++) {
-			per_terrain_terrains_tile_patterns[i][j].clear();
+		per_terrain_terrains_patterns[i].resize(tile_set->get_terrains_count(i));
+		for (int j = 0; j < (int)per_terrain_terrains_patterns[i].size(); j++) {
+			per_terrain_terrains_patterns[i][j].clear();
 		}
 	}
 
@@ -3184,22 +2587,20 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
 					TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
 					int terrain_set = tile_data->get_terrain_set();
 					if (terrain_set >= 0) {
-						ERR_FAIL_INDEX(terrain_set, (int)per_terrain_terrains_tile_patterns.size());
+						ERR_FAIL_INDEX(terrain_set, (int)per_terrain_terrains_patterns.size());
 
 						TileMapCell cell;
 						cell.source_id = source_id;
 						cell.set_atlas_coords(tile_id);
 						cell.alternative_tile = alternative_id;
 
-						TerrainsTilePattern terrains_tile_pattern = _build_terrains_tile_pattern(tile_data);
+						TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
 
 						// Terrain bits.
-						for (int i = 0; i < terrains_tile_pattern.size(); i++) {
-							int terrain = terrains_tile_pattern[i];
-							if (terrain >= 0 && terrain < (int)per_terrain_terrains_tile_patterns[terrain_set].size()) {
-								per_terrain_terrains_tile_patterns[terrain_set][terrain].insert(terrains_tile_pattern);
-								terrain_tiles[cell] = tile_data;
-								per_terrain_terrains_tile_patterns_tiles[terrain_set][terrains_tile_pattern].insert(cell);
+						for (int i = 0; i < terrains_pattern.size(); i++) {
+							int terrain = terrains_pattern[i];
+							if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
+								per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
 							}
 						}
 					}
@@ -3207,22 +2608,6 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
 			}
 		}
 	}
-
-	// Add the empty cell in the possible patterns and cells.
-	for (int i = 0; i < tile_set->get_terrain_sets_count(); i++) {
-		TerrainsTilePattern empty_pattern;
-		for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
-			if (tile_set->is_valid_peering_bit_terrain(i, TileSet::CellNeighbor(j))) {
-				empty_pattern.push_back(-1);
-			}
-		}
-
-		TileMapCell empty_cell;
-		empty_cell.source_id = TileSet::INVALID_SOURCE;
-		empty_cell.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
-		empty_cell.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
-		per_terrain_terrains_tile_patterns_tiles[i][empty_pattern].insert(empty_cell);
-	}
 }
 
 void TileMapEditorTerrainsPlugin::_update_terrains_tree() {
@@ -3291,12 +2676,13 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
 		Dictionary metadata_dict = selected_tree_item->get_metadata(0);
 		int selected_terrain_set = metadata_dict["terrain_set"];
 		int selected_terrain_id = metadata_dict["terrain_id"];
-		ERR_FAIL_INDEX(selected_terrain_set, (int)per_terrain_terrains_tile_patterns.size());
-		ERR_FAIL_INDEX(selected_terrain_id, (int)per_terrain_terrains_tile_patterns[selected_terrain_set].size());
+		ERR_FAIL_INDEX(selected_terrain_set, tile_set->get_terrain_sets_count());
+		ERR_FAIL_INDEX(selected_terrain_id, tile_set->get_terrains_count(selected_terrain_set));
 
 		// Sort the items in a map by the number of corresponding terrains.
-		Map<int, Set<TerrainsTilePattern>> sorted;
-		for (Set<TerrainsTilePattern>::Element *E = per_terrain_terrains_tile_patterns[selected_terrain_set][selected_terrain_id].front(); E; E = E->next()) {
+		Map<int, Set<TileSet::TerrainsPattern>> sorted;
+
+		for (Set<TileSet::TerrainsPattern>::Element *E = per_terrain_terrains_patterns[selected_terrain_set][selected_terrain_id].front(); E; E = E->next()) {
 			// Count the number of matching sides/terrains.
 			int count = 0;
 
@@ -3308,9 +2694,9 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
 			sorted[count].insert(E->get());
 		}
 
-		for (Map<int, Set<TerrainsTilePattern>>::Element *E_set = sorted.back(); E_set; E_set = E_set->prev()) {
-			for (Set<TerrainsTilePattern>::Element *E = E_set->get().front(); E; E = E->next()) {
-				TerrainsTilePattern terrains_tile_pattern = E->get();
+		for (Map<int, Set<TileSet::TerrainsPattern>>::Element *E_set = sorted.back(); E_set; E_set = E_set->prev()) {
+			for (Set<TileSet::TerrainsPattern>::Element *E = E_set->get().front(); E; E = E->next()) {
+				TileSet::TerrainsPattern terrains_pattern = E->get();
 
 				// Get the icon.
 				Ref<Texture2D> icon;
@@ -3318,15 +2704,15 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
 				bool transpose = false;
 
 				double max_probability = -1.0;
-				for (Set<TileMapCell>::Element *E_tile_map_cell = per_terrain_terrains_tile_patterns_tiles[selected_terrain_set][terrains_tile_pattern].front(); E_tile_map_cell; E_tile_map_cell = E_tile_map_cell->next()) {
-					Ref<TileSetSource> source = tile_set->get_source(E_tile_map_cell->get().source_id);
+				for (const TileMapCell &cell : tile_set->get_tiles_for_terrains_pattern(selected_terrain_set, terrains_pattern)) {
+					Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
 
 					Ref<TileSetAtlasSource> atlas_source = source;
 					if (atlas_source.is_valid()) {
-						TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E_tile_map_cell->get().get_atlas_coords(), E_tile_map_cell->get().alternative_tile));
+						TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile));
 						if (tile_data->get_probability() > max_probability) {
 							icon = atlas_source->get_texture();
-							region = atlas_source->get_tile_texture_region(E_tile_map_cell->get().get_atlas_coords());
+							region = atlas_source->get_tile_texture_region(cell.get_atlas_coords());
 							if (tile_data->get_flip_h()) {
 								region.position.x += region.size.x;
 								region.size.x = -region.size.x;
@@ -3347,7 +2733,7 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
 				terrains_tile_list->set_item_icon_region(item_index, region);
 				terrains_tile_list->set_item_icon_transposed(item_index, transpose);
 				Dictionary list_metadata_dict;
-				list_metadata_dict["terrains_tile_pattern"] = terrains_tile_pattern;
+				list_metadata_dict["terrains_pattern"] = terrains_pattern;
 				terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
 			}
 		}

+ 4 - 54
editor/plugins/tiles/tile_map_editor.h

@@ -243,66 +243,16 @@ private:
 	Map<Vector2i, TileMapCell> drag_modified;
 
 	// Painting
-	class Constraint {
-	private:
-		const TileMap *tile_map;
-		Vector2i base_cell_coords = Vector2i();
-		int bit = -1;
-		int terrain = -1;
-
-	public:
-		// TODO implement difference operator.
-		bool operator<(const Constraint &p_other) const {
-			if (base_cell_coords == p_other.base_cell_coords) {
-				return bit < p_other.bit;
-			}
-			return base_cell_coords < p_other.base_cell_coords;
-		}
-
-		String to_string() const {
-			return vformat("Constraint {pos:%s, bit:%d, terrain:%d}", base_cell_coords, bit, terrain);
-		}
-
-		Vector2i get_base_cell_coords() const {
-			return base_cell_coords;
-		}
-
-		Map<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
-
-		void set_terrain(int p_terrain) {
-			terrain = p_terrain;
-		}
-
-		int get_terrain() const {
-			return terrain;
-		}
-
-		Constraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain);
-		Constraint() {}
-	};
-
-	typedef Array TerrainsTilePattern;
-
-	Set<TerrainsTilePattern> _get_valid_terrains_tile_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const;
-	Set<TileMapEditorTerrainsPlugin::Constraint> _get_constraints_from_removed_cells_list(const Set<Vector2i> &p_to_replace, int p_terrain_set) const;
-	Set<TileMapEditorTerrainsPlugin::Constraint> _get_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TerrainsTilePattern p_terrains_tile_pattern) const;
-	Map<Vector2i, TerrainsTilePattern> _wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const;
-	TileMapCell _get_random_tile_from_pattern(int p_terrain_set, TerrainsTilePattern p_terrain_tile_pattern) const;
-	Map<Vector2i, TileMapCell> _draw_terrains(const Map<Vector2i, TerrainsTilePattern> &p_to_paint, int p_terrain_set) const;
+	Map<Vector2i, TileMapCell> _draw_terrains(const Map<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const;
 	void _stop_dragging();
 
-	// Cached data.
-	TerrainsTilePattern _build_terrains_tile_pattern(TileData *p_tile_data);
-	LocalVector<Map<TerrainsTilePattern, Set<TileMapCell>>> per_terrain_terrains_tile_patterns_tiles;
-	LocalVector<LocalVector<Set<TerrainsTilePattern>>> per_terrain_terrains_tile_patterns;
-
-	Map<TileMapCell, TileData *> terrain_tiles;
-	LocalVector<TileSet::CellNeighbor> tile_sides;
-
 	// Bottom panel.
 	Tree *terrains_tree;
 	ItemList *terrains_tile_list;
 
+	// Cache.
+	LocalVector<LocalVector<Set<TileSet::TerrainsPattern>>> per_terrain_terrains_patterns;
+
 	// Update functions.
 	void _update_terrains_cache();
 	void _update_terrains_tree();

+ 557 - 0
scene/2d/tile_map.cpp

@@ -34,6 +34,324 @@
 
 #include "servers/navigation_server_2d.h"
 
+Map<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlapping_coords_and_peering_bits() const {
+	Map<Vector2i, TileSet::CellNeighbor> output;
+	Ref<TileSet> tile_set = tile_map->get_tileset();
+	ERR_FAIL_COND_V(!tile_set.is_valid(), output);
+
+	TileSet::TileShape shape = tile_set->get_tile_shape();
+	if (shape == TileSet::TILE_SHAPE_SQUARE) {
+		switch (bit) {
+			case 0:
+				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
+				break;
+			case 1:
+				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
+				break;
+			case 2:
+				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
+				break;
+			case 3:
+				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
+				break;
+			default:
+				ERR_FAIL_V(output);
+		}
+	} else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+		switch (bit) {
+			case 0:
+				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
+				break;
+			case 1:
+				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
+				break;
+			case 2:
+				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
+				break;
+			case 3:
+				output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
+				output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+				break;
+			default:
+				ERR_FAIL_V(output);
+		}
+	} else {
+		// Half offset shapes.
+		TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
+		if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+			switch (bit) {
+				case 0:
+					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
+					break;
+				case 1:
+					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
+					break;
+				case 2:
+					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
+					break;
+				case 3:
+					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
+					break;
+				case 4:
+					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+					break;
+				default:
+					ERR_FAIL_V(output);
+			}
+		} else {
+			switch (bit) {
+				case 0:
+					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+					break;
+				case 1:
+					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
+					break;
+				case 2:
+					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+					break;
+				case 3:
+					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
+					break;
+				case 4:
+					output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
+					output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+					break;
+				default:
+					ERR_FAIL_V(output);
+			}
+		}
+	}
+	return output;
+}
+
+TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) {
+	// The way we build the constraint make it easy to detect conflicting constraints.
+	tile_map = p_tile_map;
+
+	Ref<TileSet> tile_set = tile_map->get_tileset();
+	ERR_FAIL_COND(!tile_set.is_valid());
+
+	TileSet::TileShape shape = tile_set->get_tile_shape();
+	if (shape == TileSet::TILE_SHAPE_SQUARE) {
+		switch (p_bit) {
+			case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+				bit = 0;
+				base_cell_coords = p_position;
+				break;
+			case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+				bit = 1;
+				base_cell_coords = p_position;
+				break;
+			case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+				bit = 2;
+				base_cell_coords = p_position;
+				break;
+			case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+				bit = 1;
+				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+				break;
+			case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+				bit = 0;
+				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+				break;
+			case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+				bit = 1;
+				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER);
+				break;
+			case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+				bit = 2;
+				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+				break;
+			case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+				bit = 1;
+				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+				break;
+			default:
+				ERR_FAIL();
+				break;
+		}
+	} else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+		switch (p_bit) {
+			case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+				bit = 1;
+				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+				break;
+			case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+				bit = 0;
+				base_cell_coords = p_position;
+				break;
+			case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+				bit = 1;
+				base_cell_coords = p_position;
+				break;
+			case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+				bit = 2;
+				base_cell_coords = p_position;
+				break;
+			case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+				bit = 1;
+				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+				break;
+			case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+				bit = 0;
+				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+				break;
+			case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+				bit = 1;
+				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_CORNER);
+				break;
+			case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+				bit = 2;
+				base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+				break;
+			default:
+				ERR_FAIL();
+				break;
+		}
+	} else {
+		// Half-offset shapes
+		TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
+		if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+			switch (p_bit) {
+				case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+					bit = 0;
+					base_cell_coords = p_position;
+					break;
+				case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+					bit = 1;
+					base_cell_coords = p_position;
+					break;
+				case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+					bit = 2;
+					base_cell_coords = p_position;
+					break;
+				case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+					bit = 3;
+					base_cell_coords = p_position;
+					break;
+				case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+					bit = 4;
+					base_cell_coords = p_position;
+					break;
+				case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+					bit = 1;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+					break;
+				case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+					bit = 0;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+					break;
+				case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+					bit = 3;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+					break;
+				case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+					bit = 2;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+					break;
+				case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+					bit = 1;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+					break;
+				case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+					bit = 4;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+					break;
+				case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+					bit = 3;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+					break;
+				default:
+					ERR_FAIL();
+					break;
+			}
+		} else {
+			switch (p_bit) {
+				case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+					bit = 0;
+					base_cell_coords = p_position;
+					break;
+				case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+					bit = 1;
+					base_cell_coords = p_position;
+					break;
+				case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+					bit = 2;
+					base_cell_coords = p_position;
+					break;
+				case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+					bit = 3;
+					base_cell_coords = p_position;
+					break;
+				case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+					bit = 0;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
+					break;
+				case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+					bit = 4;
+					base_cell_coords = p_position;
+					break;
+				case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+					bit = 2;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+					break;
+				case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+					bit = 1;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+					break;
+				case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+					bit = 0;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+					break;
+				case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+					bit = 3;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+					break;
+				case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+					bit = 2;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+					break;
+				case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+					bit = 4;
+					base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+					break;
+				default:
+					ERR_FAIL();
+					break;
+			}
+		}
+	}
+	terrain = p_terrain;
+}
+
 Vector2i TileMap::transform_coords_layout(Vector2i p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout) {
 	// Transform to stacked layout.
 	Vector2i output = p_coords;
@@ -1784,6 +2102,243 @@ void TileMap::set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPat
 	}
 }
 
+Set<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TerrainConstraint> p_constraints) {
+	if (!tile_set.is_valid()) {
+		return Set<TileSet::TerrainsPattern>();
+	}
+
+	// Returns all tiles compatible with the given constraints.
+	Set<TileSet::TerrainsPattern> compatible_terrain_tile_patterns;
+	for (TileSet::TerrainsPattern &terrain_pattern : tile_set->get_terrains_pattern_set(p_terrain_set)) {
+		int valid = true;
+		int in_pattern_count = 0;
+		for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+			TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+			if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
+				// Check if the bit is compatible with the constraints.
+				TerrainConstraint terrain_bit_constraint = TerrainConstraint(this, p_position, bit, terrain_pattern[in_pattern_count]);
+				Set<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
+				if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) {
+					valid = false;
+					break;
+				}
+				in_pattern_count++;
+			}
+		}
+
+		if (valid) {
+			compatible_terrain_tile_patterns.insert(terrain_pattern);
+		}
+	}
+
+	return compatible_terrain_tile_patterns;
+}
+
+Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_cells_list(int p_layer, const Set<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains) const {
+	if (!tile_set.is_valid()) {
+		return Set<TerrainConstraint>();
+	}
+
+	ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), Set<TerrainConstraint>());
+	ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Set<TerrainConstraint>());
+
+	// Build a set of dummy constraints get the constrained points.
+	Set<TerrainConstraint> dummy_constraints;
+	for (Set<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) {
+		for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over sides.
+			TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+			if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
+				dummy_constraints.insert(TerrainConstraint(this, E->get(), bit, -1));
+			}
+		}
+	}
+
+	// For each constrained point, we get all overlapping tiles, and select the most adequate terrain for it.
+	Set<TerrainConstraint> constraints;
+	for (Set<TerrainConstraint>::Element *E = dummy_constraints.front(); E; E = E->next()) {
+		TerrainConstraint c = E->get();
+
+		Map<int, int> terrain_count;
+
+		// Count the number of occurrences per terrain.
+		Map<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits();
+		for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) {
+			if (!p_to_replace.has(E_overlapping.key)) {
+				TileData *neighbor_tile_data = nullptr;
+				TileMapCell neighbor_cell = get_cell(p_layer, E_overlapping.key);
+				if (neighbor_cell.source_id != TileSet::INVALID_SOURCE) {
+					Ref<TileSetSource> source = tile_set->get_source(neighbor_cell.source_id);
+					Ref<TileSetAtlasSource> atlas_source = source;
+					if (atlas_source.is_valid()) {
+						TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(neighbor_cell.get_atlas_coords(), neighbor_cell.alternative_tile));
+						if (tile_data && tile_data->get_terrain_set() == p_terrain_set) {
+							neighbor_tile_data = tile_data;
+						}
+					}
+				}
+
+				int terrain = neighbor_tile_data ? neighbor_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(E_overlapping.value)) : -1;
+				if (!p_ignore_empty_terrains || terrain >= 0) {
+					if (!terrain_count.has(terrain)) {
+						terrain_count[terrain] = 0;
+					}
+					terrain_count[terrain] += 1;
+				}
+			}
+		}
+
+		// Get the terrain with the max number of occurrences.
+		int max = 0;
+		int max_terrain = -1;
+		for (const KeyValue<int, int> &E_terrain_count : terrain_count) {
+			if (E_terrain_count.value > max) {
+				max = E_terrain_count.value;
+				max_terrain = E_terrain_count.key;
+			}
+		}
+
+		// Set the adequate terrain.
+		if (max > 0) {
+			c.set_terrain(max_terrain);
+			constraints.insert(c);
+		}
+	}
+
+	return constraints;
+}
+
+Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
+	if (!tile_set.is_valid()) {
+		return Set<TerrainConstraint>();
+	}
+
+	// Compute the constraints needed from the surrounding tiles.
+	Set<TerrainConstraint> output;
+	int in_pattern_count = 0;
+	for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+		TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
+		if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) {
+			TerrainConstraint c = TerrainConstraint(this, p_position, side, p_terrains_pattern[in_pattern_count]);
+			output.insert(c);
+			in_pattern_count++;
+		}
+	}
+
+	return output;
+}
+
+Map<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TerrainConstraint> p_constraints) {
+	if (!tile_set.is_valid()) {
+		return Map<Vector2i, TileSet::TerrainsPattern>();
+	}
+
+	// Copy the constraints set.
+	Set<TerrainConstraint> constraints = p_constraints;
+
+	// Compute all acceptable patterns for each cell.
+	Map<Vector2i, Set<TileSet::TerrainsPattern>> per_cell_acceptable_tiles;
+	for (Vector2i cell : p_to_replace) {
+		per_cell_acceptable_tiles[cell] = _get_valid_terrains_patterns_for_constraints(p_terrain_set, cell, constraints);
+	}
+
+	// Output map.
+	Map<Vector2i, TileSet::TerrainsPattern> output;
+
+	// Add all positions to a set.
+	Set<Vector2i> to_replace = Set<Vector2i>(p_to_replace);
+	while (!to_replace.is_empty()) {
+		// Compute the minimum number of tile possibilities for each cell.
+		int min_nb_possibilities = 100000000;
+		for (const KeyValue<Vector2i, Set<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) {
+			min_nb_possibilities = MIN(min_nb_possibilities, E.value.size());
+		}
+
+		// Get the set of possible cells to fill, out of the most constrained ones.
+		LocalVector<Vector2i> to_choose_from;
+		for (const KeyValue<Vector2i, Set<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) {
+			if (E.value.size() == min_nb_possibilities) {
+				to_choose_from.push_back(E.key);
+			}
+		}
+
+		// Randomly a cell to fill out of the most constrained.
+		Vector2i selected_cell_to_replace = to_choose_from[Math::random(0, to_choose_from.size() - 1)];
+
+		// Get the list of acceptable pattens for the given cell.
+		Set<TileSet::TerrainsPattern> valid_tiles = per_cell_acceptable_tiles[selected_cell_to_replace];
+		if (valid_tiles.is_empty()) {
+			break; // No possibilities :/
+		}
+
+		// Out of the possible patterns, prioritize the one which have the least amount of different terrains.
+		LocalVector<TileSet::TerrainsPattern> valid_tiles_with_least_amount_of_terrains;
+		int min_terrain_count = 10000;
+		LocalVector<int> terrains_counts;
+		int pattern_index = 0;
+		for (const TileSet::TerrainsPattern &pattern : valid_tiles) {
+			Set<int> terrains;
+			for (int i = 0; i < pattern.size(); i++) {
+				terrains.insert(pattern[i]);
+			}
+			min_terrain_count = MIN(min_terrain_count, terrains.size());
+			terrains_counts.push_back(terrains.size());
+			pattern_index++;
+		}
+		pattern_index = 0;
+		for (const TileSet::TerrainsPattern &pattern : valid_tiles) {
+			if (terrains_counts[pattern_index] == min_terrain_count) {
+				valid_tiles_with_least_amount_of_terrains.push_back(pattern);
+			}
+			pattern_index++;
+		}
+
+		// Randomly select a pattern out of the remaining ones.
+		TileSet::TerrainsPattern selected_terrain_tile_pattern = valid_tiles_with_least_amount_of_terrains[Math::random(0, valid_tiles_with_least_amount_of_terrains.size() - 1)];
+
+		// Set the selected cell into the output.
+		output[selected_cell_to_replace] = selected_terrain_tile_pattern;
+		to_replace.erase(selected_cell_to_replace);
+		per_cell_acceptable_tiles.erase(selected_cell_to_replace);
+
+		// Add the new constraints from the added tiles.
+		Set<TerrainConstraint> new_constraints = get_terrain_constraints_from_added_tile(selected_cell_to_replace, p_terrain_set, selected_terrain_tile_pattern);
+		for (Set<TerrainConstraint>::Element *E_constraint = new_constraints.front(); E_constraint; E_constraint = E_constraint->next()) {
+			constraints.insert(E_constraint->get());
+		}
+
+		// Compute valid tiles again for neighbors.
+		for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+			TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
+			if (is_existing_neighbor(side)) {
+				Vector2i neighbor = get_neighbor_cell(selected_cell_to_replace, side);
+				if (to_replace.has(neighbor)) {
+					per_cell_acceptable_tiles[neighbor] = _get_valid_terrains_patterns_for_constraints(p_terrain_set, neighbor, constraints);
+				}
+			}
+		}
+	}
+	return output;
+}
+
+void TileMap::set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector2i> p_coords_array, int p_terrain_set, bool p_ignore_empty_terrains) {
+	ERR_FAIL_COND(!tile_set.is_valid());
+	ERR_FAIL_INDEX(p_layer, (int)layers.size());
+	ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count());
+
+	Set<Vector2i> coords_set;
+	for (int i = 0; i < p_coords_array.size(); i++) {
+		coords_set.insert(p_coords_array[i]);
+	}
+
+	Set<TileMap::TerrainConstraint> constraints = get_terrain_constraints_from_removed_cells_list(p_layer, coords_set, p_terrain_set, p_ignore_empty_terrains);
+
+	Map<Vector2i, TileSet::TerrainsPattern> wfc_output = terrain_wave_function_collapse(coords_set, p_terrain_set, constraints);
+	for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : wfc_output) {
+		TileMapCell cell = tile_set->get_random_tile_from_pattern(p_terrain_set, kv.value);
+		set_cell(p_layer, kv.key, cell.source_id, cell.get_atlas_coords(), cell.alternative_tile);
+	}
+}
+
 TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
 	ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileMapCell());
 	const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
@@ -2989,6 +3544,8 @@ void TileMap::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("map_pattern", "position_in_tilemap", "coords_in_pattern", "pattern"), &TileMap::map_pattern);
 	ClassDB::bind_method(D_METHOD("set_pattern", "layer", "position", "pattern"), &TileMap::set_pattern);
 
+	ClassDB::bind_method(D_METHOD("set_cells_from_surrounding_terrains", "layer", "cells", "terrain_set", "ignore_empty_terrains"), &TileMap::set_cells_from_surrounding_terrains, DEFVAL(true));
+
 	ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles);
 	ClassDB::bind_method(D_METHOD("clear_layer", "layer"), &TileMap::clear_layer);
 	ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear);

+ 49 - 0
scene/2d/tile_map.h

@@ -109,6 +109,43 @@ class TileMap : public Node2D {
 	GDCLASS(TileMap, Node2D);
 
 public:
+	class TerrainConstraint {
+	private:
+		const TileMap *tile_map;
+		Vector2i base_cell_coords = Vector2i();
+		int bit = -1;
+		int terrain = -1;
+
+	public:
+		bool operator<(const TerrainConstraint &p_other) const {
+			if (base_cell_coords == p_other.base_cell_coords) {
+				return bit < p_other.bit;
+			}
+			return base_cell_coords < p_other.base_cell_coords;
+		}
+
+		String to_string() const {
+			return vformat("Constraint {pos:%s, bit:%d, terrain:%d}", base_cell_coords, bit, terrain);
+		}
+
+		Vector2i get_base_cell_coords() const {
+			return base_cell_coords;
+		}
+
+		Map<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
+
+		void set_terrain(int p_terrain) {
+			terrain = p_terrain;
+		}
+
+		int get_terrain() const {
+			return terrain;
+		}
+
+		TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain);
+		TerrainConstraint() {}
+	};
+
 	enum VisibilityMode {
 		VISIBILITY_MODE_DEFAULT,
 		VISIBILITY_MODE_FORCE_SHOW,
@@ -209,6 +246,9 @@ private:
 	void _scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant);
 	void _scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
 
+	// Terrains.
+	Set<TileSet::TerrainsPattern> _get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TerrainConstraint> p_constraints);
+
 	// Set and get tiles from data arrays.
 	void _set_tile_data(int p_layer, const Vector<int> &p_data);
 	Vector<int> _get_tile_data(int p_layer) const;
@@ -267,21 +307,30 @@ public:
 	void set_collision_animatable(bool p_enabled);
 	bool is_collision_animatable() const;
 
+	// Debug visibility modes.
 	void set_collision_visibility_mode(VisibilityMode p_show_collision);
 	VisibilityMode get_collision_visibility_mode();
 
 	void set_navigation_visibility_mode(VisibilityMode p_show_navigation);
 	VisibilityMode get_navigation_visibility_mode();
 
+	// Cells accessors.
 	void set_cell(int p_layer, const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE);
 	int get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
 	Vector2i get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
 	int get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
 
+	// Patterns.
 	Ref<TileMapPattern> get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array);
 	Vector2i map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, Ref<TileMapPattern> p_pattern);
 	void set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPattern> p_pattern);
 
+	// Terrains.
+	Set<TerrainConstraint> get_terrain_constraints_from_removed_cells_list(int p_layer, const Set<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains = true) const; // Not exposed.
+	Set<TerrainConstraint> get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const; // Not exposed.
+	Map<Vector2i, TileSet::TerrainsPattern> terrain_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TerrainConstraint> p_constraints); // Not exposed.
+	void set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector2i> p_coords_array, int p_terrain_set, bool p_ignore_empty_terrains = true);
+
 	// Not exposed to users
 	TileMapCell get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
 	Map<Vector2i, TileMapQuadrant> *get_quadrant_map(int p_layer);

+ 150 - 0
scene/resources/tile_set.cpp

@@ -301,6 +301,66 @@ int TileSet::get_next_source_id() const {
 	return next_source_id;
 }
 
+void TileSet::_update_terrains_cache() {
+	if (terrains_cache_dirty) {
+		// Organizes tiles into structures.
+		per_terrain_pattern_tiles.resize(terrain_sets.size());
+		for (int i = 0; i < (int)per_terrain_pattern_tiles.size(); i++) {
+			per_terrain_pattern_tiles[i].clear();
+		}
+
+		for (const KeyValue<int, Ref<TileSetSource>> &kv : sources) {
+			Ref<TileSetSource> source = kv.value;
+			Ref<TileSetAtlasSource> atlas_source = source;
+			if (atlas_source.is_valid()) {
+				for (int tile_index = 0; tile_index < source->get_tiles_count(); tile_index++) {
+					Vector2i tile_id = source->get_tile_id(tile_index);
+					for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) {
+						int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index);
+
+						// Executed for each tile_data.
+						TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
+						int terrain_set = tile_data->get_terrain_set();
+						if (terrain_set >= 0) {
+							TileMapCell cell;
+							cell.source_id = kv.key;
+							cell.set_atlas_coords(tile_id);
+							cell.alternative_tile = alternative_id;
+
+							TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
+
+							// Terrain bits.
+							for (int i = 0; i < terrains_pattern.size(); i++) {
+								int terrain = terrains_pattern[i];
+								if (terrain >= 0) {
+									per_terrain_pattern_tiles[terrain_set][terrains_pattern].insert(cell);
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+
+		// Add the empty cell in the possible patterns and cells.
+		for (int i = 0; i < terrain_sets.size(); i++) {
+			TileSet::TerrainsPattern empty_pattern;
+			for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
+				if (is_valid_peering_bit_terrain(i, TileSet::CellNeighbor(j))) {
+					empty_pattern.push_back(-1);
+				}
+			}
+
+			TileMapCell empty_cell;
+			empty_cell.source_id = TileSet::INVALID_SOURCE;
+			empty_cell.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
+			empty_cell.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
+			per_terrain_pattern_tiles[i][empty_pattern].insert(empty_cell);
+		}
+		terrains_cache_dirty = false;
+	}
+}
+
 void TileSet::_compute_next_source_id() {
 	while (sources.has(next_source_id)) {
 		next_source_id = (next_source_id + 1) % 1073741824; // 2 ** 30
@@ -321,6 +381,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source
 
 	sources[new_source_id]->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed));
 
+	terrains_cache_dirty = true;
 	emit_changed();
 
 	return new_source_id;
@@ -336,6 +397,7 @@ void TileSet::remove_source(int p_source_id) {
 	source_ids.erase(p_source_id);
 	source_ids.sort();
 
+	terrains_cache_dirty = true;
 	emit_changed();
 }
 
@@ -357,6 +419,7 @@ void TileSet::set_source_id(int p_source_id, int p_new_source_id) {
 
 	_compute_next_source_id();
 
+	terrains_cache_dirty = true;
 	emit_changed();
 }
 
@@ -545,6 +608,7 @@ void TileSet::add_terrain_set(int p_index) {
 	}
 
 	notify_property_list_changed();
+	terrains_cache_dirty = true;
 	emit_changed();
 }
 
@@ -557,6 +621,7 @@ void TileSet::move_terrain_set(int p_from_index, int p_to_pos) {
 		source.value->move_terrain_set(p_from_index, p_to_pos);
 	}
 	notify_property_list_changed();
+	terrains_cache_dirty = true;
 	emit_changed();
 }
 
@@ -567,6 +632,7 @@ void TileSet::remove_terrain_set(int p_index) {
 		source.value->remove_terrain_set(p_index);
 	}
 	notify_property_list_changed();
+	terrains_cache_dirty = true;
 	emit_changed();
 }
 
@@ -578,6 +644,7 @@ void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode
 	}
 
 	notify_property_list_changed();
+	terrains_cache_dirty = true;
 	emit_changed();
 }
 
@@ -612,6 +679,7 @@ void TileSet::add_terrain(int p_terrain_set, int p_index) {
 	}
 
 	notify_property_list_changed();
+	terrains_cache_dirty = true;
 	emit_changed();
 }
 
@@ -627,6 +695,7 @@ void TileSet::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
 		source.value->move_terrain(p_terrain_set, p_from_index, p_to_pos);
 	}
 	notify_property_list_changed();
+	terrains_cache_dirty = true;
 	emit_changed();
 }
 
@@ -640,6 +709,7 @@ void TileSet::remove_terrain(int p_terrain_set, int p_index) {
 		source.value->remove_terrain(p_terrain_set, p_index);
 	}
 	notify_property_list_changed();
+	terrains_cache_dirty = true;
 	emit_changed();
 }
 
@@ -1196,6 +1266,73 @@ int TileSet::get_patterns_count() {
 	return patterns.size();
 }
 
+Set<TileSet::TerrainsPattern> TileSet::get_terrains_pattern_set(int p_terrain_set) {
+	ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileSet::TerrainsPattern>());
+	_update_terrains_cache();
+
+	Set<TileSet::TerrainsPattern> output;
+	for (KeyValue<TileSet::TerrainsPattern, Set<TileMapCell>> kv : per_terrain_pattern_tiles[p_terrain_set]) {
+		output.insert(kv.key);
+	}
+	return output;
+}
+
+Set<TileMapCell> TileSet::get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) {
+	ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileMapCell>());
+	_update_terrains_cache();
+	return per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern];
+}
+
+TileMapCell TileSet::get_random_tile_from_pattern(int p_terrain_set, TileSet::TerrainsPattern p_terrain_tile_pattern) {
+	ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), TileMapCell());
+	_update_terrains_cache();
+
+	// Count the sum of probabilities.
+	double sum = 0.0;
+	Set<TileMapCell> set = per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern];
+	for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
+		if (E->get().source_id >= 0) {
+			Ref<TileSetSource> source = sources[E->get().source_id];
+			Ref<TileSetAtlasSource> atlas_source = source;
+			if (atlas_source.is_valid()) {
+				TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+				sum += tile_data->get_probability();
+			} else {
+				sum += 1.0;
+			}
+		} else {
+			sum += 1.0;
+		}
+	}
+
+	// Generate a random number.
+	double count = 0.0;
+	double picked = Math::random(0.0, sum);
+
+	// Pick the tile.
+	for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
+		if (E->get().source_id >= 0) {
+			Ref<TileSetSource> source = sources[E->get().source_id];
+
+			Ref<TileSetAtlasSource> atlas_source = source;
+			if (atlas_source.is_valid()) {
+				TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+				count += tile_data->get_probability();
+			} else {
+				count += 1.0;
+			}
+		} else {
+			count += 1.0;
+		}
+
+		if (count >= picked) {
+			return E->get();
+		}
+	}
+
+	ERR_FAIL_V(TileMapCell());
+}
+
 Vector<Vector2> TileSet::get_tile_shape_polygon() {
 	Vector<Vector2> points;
 	if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
@@ -1510,6 +1647,7 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) {
 }
 
 void TileSet::_source_changed() {
+	terrains_cache_dirty = true;
 	emit_changed();
 }
 
@@ -4743,6 +4881,18 @@ bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit)
 	return tile_set->is_valid_peering_bit_terrain(terrain_set, p_peering_bit);
 }
 
+TileSet::TerrainsPattern TileData::get_terrains_pattern() const {
+	ERR_FAIL_COND_V(!tile_set, TileSet::TerrainsPattern());
+
+	TileSet::TerrainsPattern output;
+	for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+		if (tile_set->is_valid_peering_bit_terrain(terrain_set, TileSet::CellNeighbor(i))) {
+			output.push_back(get_peering_bit_terrain(TileSet::CellNeighbor(i)));
+		}
+	}
+	return output;
+}
+
 // Navigation
 void TileData::set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon) {
 	ERR_FAIL_INDEX(p_layer_id, navigation.size());

+ 12 - 0
scene/resources/tile_set.h

@@ -253,6 +253,7 @@ public:
 		Ref<PackedScene> scene;
 		Vector2 offset;
 	};
+	typedef Array TerrainsPattern;
 
 protected:
 	bool _set(const StringName &p_name, const Variant &p_value);
@@ -303,6 +304,10 @@ private:
 	Map<TerrainMode, Map<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes;
 	bool terrain_bits_meshes_dirty = true;
 
+	LocalVector<Map<TileSet::TerrainsPattern, Set<TileMapCell>>> per_terrain_pattern_tiles; // Cached data.
+	bool terrains_cache_dirty = true;
+	void _update_terrains_cache();
+
 	// Navigation
 	struct NavigationLayer {
 		uint32_t layers = 1;
@@ -470,6 +475,11 @@ public:
 	void remove_pattern(int p_index);
 	int get_patterns_count();
 
+	// Terrains.
+	Set<TerrainsPattern> get_terrains_pattern_set(int p_terrain_set);
+	Set<TileMapCell> get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
+	TileMapCell get_random_tile_from_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
+
 	// Helpers
 	Vector<Vector2> get_tile_shape_polygon();
 	void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>());
@@ -831,6 +841,8 @@ public:
 	int get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
 	bool is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
 
+	TileSet::TerrainsPattern get_terrains_pattern() const; // Not exposed.
+
 	// Navigation
 	void set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon);
 	Ref<NavigationPolygon> get_navigation_polygon(int p_layer_id) const;