浏览代码

Implement missing TileMap brushes for terrains

Gilles Roudière 3 年之前
父节点
当前提交
c609d39214

文件差异内容过多而无法显示
+ 544 - 118
editor/plugins/tiles/tile_map_editor.cpp


+ 29 - 8
editor/plugins/tiles/tile_map_editor.h

@@ -75,14 +75,15 @@ private:
 	Button *line_tool_button;
 	Button *rect_tool_button;
 	Button *bucket_tool_button;
-	Button *picker_button;
 
 	HBoxContainer *tools_settings;
+
 	VSeparator *tools_settings_vsep;
+	Button *picker_button;
 	Button *erase_button;
-	CheckBox *bucket_continuous_checkbox;
 
 	VSeparator *tools_settings_vsep_2;
+	CheckBox *bucket_contiguous_checkbox;
 	CheckBox *random_tile_checkbox;
 	float scattering = 0.0;
 	Label *scatter_label;
@@ -108,17 +109,16 @@ private:
 		DRAG_TYPE_CLIPBOARD_PASTE,
 	};
 	DragType drag_type = DRAG_TYPE_NONE;
+	bool drag_erasing = false;
 	Vector2 drag_start_mouse_pos;
 	Vector2 drag_last_mouse_pos;
 	Map<Vector2i, TileMapCell> drag_modified;
-	bool rmb_erasing = false;
 
 	TileMapCell _pick_random_tile(Ref<TileMapPattern> p_pattern);
-	Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos);
-	Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell);
-	Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous);
+	Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos, bool p_erase);
+	Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
+	Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
 	void _stop_dragging();
-	bool _is_erasing() const;
 
 	///// Selection system. /////
 	Set<Vector2i> tile_map_selection;
@@ -220,32 +220,53 @@ private:
 
 	Ref<ButtonGroup> tool_buttons_group;
 	Button *paint_tool_button;
+	Button *line_tool_button;
+	Button *rect_tool_button;
+	Button *bucket_tool_button;
 
 	HBoxContainer *tools_settings;
+
 	VSeparator *tools_settings_vsep;
 	Button *picker_button;
 	Button *erase_button;
 
+	VSeparator *tools_settings_vsep_2;
+	CheckBox *bucket_contiguous_checkbox;
 	void _update_toolbar();
 
 	// Main vbox.
 	VBoxContainer *main_vbox_container;
 
 	// TileMap editing.
+	bool has_mouse = false;
+	void _mouse_exited_viewport();
+
 	enum DragType {
 		DRAG_TYPE_NONE = 0,
 		DRAG_TYPE_PAINT,
+		DRAG_TYPE_LINE,
+		DRAG_TYPE_RECT,
+		DRAG_TYPE_BUCKET,
 		DRAG_TYPE_PICK,
 	};
 	DragType drag_type = DRAG_TYPE_NONE;
+	bool drag_erasing = false;
 	Vector2 drag_start_mouse_pos;
 	Vector2 drag_last_mouse_pos;
 	Map<Vector2i, TileMapCell> drag_modified;
 
 	// Painting
 	Map<Vector2i, TileMapCell> _draw_terrains(const Map<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const;
+	Map<Vector2i, TileMapCell> _draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
+	Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
+	Set<Vector2i> _get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous);
+	Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
 	void _stop_dragging();
 
+	int selected_terrain_set = -1;
+	TileSet::TerrainsPattern selected_terrains_pattern;
+	void _update_selection();
+
 	// Bottom panel.
 	Tree *terrains_tree;
 	ItemList *terrains_tile_list;
@@ -265,7 +286,7 @@ private:
 public:
 	virtual Vector<TabData> get_tabs() const override;
 	virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
-	//virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
+	virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
 
 	TileMapEditorTerrainsPlugin();
 	~TileMapEditorTerrainsPlugin();

+ 8 - 9
scene/2d/tile_map.cpp

@@ -2146,18 +2146,16 @@ Set<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constrai
 	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]);
+				TerrainConstraint terrain_bit_constraint = TerrainConstraint(this, p_position, bit, terrain_pattern.get_terrain(bit));
 				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++;
 			}
 		}
 
@@ -2249,13 +2247,11 @@ Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_added_tile
 
 	// 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]);
+			TerrainConstraint c = TerrainConstraint(this, p_position, side, p_terrains_pattern.get_terrain(side));
 			output.insert(c);
-			in_pattern_count++;
 		}
 	}
 
@@ -2312,8 +2308,11 @@ Map<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse(
 		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]);
+			for (int 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)) {
+					terrains.insert(pattern.get_terrain(side));
+				}
 			}
 			min_terrain_count = MIN(min_terrain_count, terrains.size());
 			terrains_counts.push_back(terrains.size());
@@ -2369,7 +2368,7 @@ void TileMap::set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector
 
 	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);
+		TileMapCell cell = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value);
 		set_cell(p_layer, kv.key, cell.source_id, cell.get_atlas_coords(), cell.alternative_tile);
 	}
 }

+ 95 - 13
scene/resources/tile_set.cpp

@@ -225,6 +225,90 @@ void TileMapPattern::_bind_methods() {
 
 /////////////////////////////// TileSet //////////////////////////////////////
 
+bool TileSet::TerrainsPattern::is_valid() const {
+	return valid;
+}
+
+bool TileSet::TerrainsPattern::is_erase_pattern() const {
+	return not_empty_terrains_count == 0;
+}
+
+bool TileSet::TerrainsPattern::operator<(const TerrainsPattern &p_terrains_pattern) const {
+	for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+		if (is_valid_bit[i] != p_terrains_pattern.is_valid_bit[i]) {
+			return is_valid_bit[i] < p_terrains_pattern.is_valid_bit[i];
+		}
+	}
+	for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+		if (is_valid_bit[i] && bits[i] != p_terrains_pattern.bits[i]) {
+			return bits[i] < p_terrains_pattern.bits[i];
+		}
+	}
+	return false;
+}
+
+bool TileSet::TerrainsPattern::operator==(const TerrainsPattern &p_terrains_pattern) const {
+	for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+		if (is_valid_bit[i] != p_terrains_pattern.is_valid_bit[i]) {
+			return false;
+		}
+		if (is_valid_bit[i] && bits[i] != p_terrains_pattern.bits[i]) {
+			return false;
+		}
+	}
+	return true;
+}
+
+void TileSet::TerrainsPattern::set_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain) {
+	ERR_FAIL_COND(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX);
+	ERR_FAIL_COND(!is_valid_bit[p_peering_bit]);
+	ERR_FAIL_COND(p_terrain < -1);
+
+	// Update the "is_erase_pattern" status.
+	if (p_terrain >= 0 && bits[p_peering_bit] < 0) {
+		not_empty_terrains_count++;
+	} else if (p_terrain < 0 && bits[p_peering_bit] >= 0) {
+		not_empty_terrains_count--;
+	}
+
+	bits[p_peering_bit] = p_terrain;
+}
+
+int TileSet::TerrainsPattern::get_terrain(TileSet::CellNeighbor p_peering_bit) const {
+	ERR_FAIL_COND_V(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX, -1);
+	ERR_FAIL_COND_V(!is_valid_bit[p_peering_bit], -1);
+	return bits[p_peering_bit];
+}
+
+void TileSet::TerrainsPattern::set_terrains_from_array(Array p_terrains) {
+	int in_array_index = 0;
+	for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+		if (is_valid_bit[i]) {
+			ERR_FAIL_COND(in_array_index >= p_terrains.size());
+			set_terrain(TileSet::CellNeighbor(i), p_terrains[in_array_index]);
+			in_array_index++;
+		}
+	}
+}
+
+Array TileSet::TerrainsPattern::get_terrains_as_array() const {
+	Array output;
+	for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+		if (is_valid_bit[i]) {
+			output.push_back(bits[i]);
+		}
+	}
+	return output;
+}
+TileSet::TerrainsPattern::TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set) {
+	ERR_FAIL_COND(p_terrain_set < 0);
+	for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+		is_valid_bit[i] = (p_tile_set->is_valid_peering_bit_terrain(p_terrain_set, TileSet::CellNeighbor(i)));
+		bits[i] = -1;
+	}
+	valid = true;
+}
+
 const int TileSet::INVALID_SOURCE = -1;
 
 const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = {
@@ -330,10 +414,13 @@ void TileSet::_update_terrains_cache() {
 							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);
+							for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+								CellNeighbor bit = CellNeighbor(i);
+								if (is_valid_peering_bit_terrain(terrain_set, bit)) {
+									int terrain = terrains_pattern.get_terrain(bit);
+									if (terrain >= 0) {
+										per_terrain_pattern_tiles[terrain_set][terrains_pattern].insert(cell);
+									}
 								}
 							}
 						}
@@ -344,12 +431,7 @@ void TileSet::_update_terrains_cache() {
 
 		// 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);
-				}
-			}
+			TileSet::TerrainsPattern empty_pattern(this, i);
 
 			TileMapCell empty_cell;
 			empty_cell.source_id = TileSet::INVALID_SOURCE;
@@ -1283,7 +1365,7 @@ Set<TileMapCell> TileSet::get_tiles_for_terrains_pattern(int p_terrain_set, Terr
 	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) {
+TileMapCell TileSet::get_random_tile_from_terrains_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();
 
@@ -4915,10 +4997,10 @@ bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit)
 TileSet::TerrainsPattern TileData::get_terrains_pattern() const {
 	ERR_FAIL_COND_V(!tile_set, TileSet::TerrainsPattern());
 
-	TileSet::TerrainsPattern output;
+	TileSet::TerrainsPattern output(tile_set, terrain_set);
 	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)));
+			output.set_terrain(TileSet::CellNeighbor(i), get_peering_bit_terrain(TileSet::CellNeighbor(i)));
 		}
 	}
 	return output;

+ 25 - 2
scene/resources/tile_set.h

@@ -253,7 +253,30 @@ public:
 		Ref<PackedScene> scene;
 		Vector2 offset;
 	};
-	typedef Array TerrainsPattern;
+
+	class TerrainsPattern {
+		bool valid = false;
+		int bits[TileSet::CELL_NEIGHBOR_MAX];
+		bool is_valid_bit[TileSet::CELL_NEIGHBOR_MAX];
+
+		int not_empty_terrains_count = 0;
+
+	public:
+		bool is_valid() const;
+		bool is_erase_pattern() const;
+
+		bool operator<(const TerrainsPattern &p_terrains_pattern) const;
+		bool operator==(const TerrainsPattern &p_terrains_pattern) const;
+
+		void set_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain);
+		int get_terrain(TileSet::CellNeighbor p_peering_bit) const;
+
+		void set_terrains_from_array(Array p_terrains);
+		Array get_terrains_as_array() const;
+
+		TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set);
+		TerrainsPattern() {}
+	};
 
 protected:
 	bool _set(const StringName &p_name, const Variant &p_value);
@@ -478,7 +501,7 @@ public:
 	// 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);
+	TileMapCell get_random_tile_from_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
 
 	// Helpers
 	Vector<Vector2> get_tile_shape_polygon();

部分文件因为文件数量过多而无法显示