ソースを参照

Merge pull request #91464 from groud/fix_tilemap_layer_navmesh_baking

Fix TileMapLayer navmesh baking
Rémi Verschelde 1 年間 前
コミット
7fef538223

+ 72 - 103
modules/navigation/2d/nav_mesh_generator_2d.cpp

@@ -243,7 +243,7 @@ void NavMeshGenerator2D::generator_parse_geometry_node(Ref<NavigationPolygon> p_
 	generator_parse_multimeshinstance2d_node(p_navigation_mesh, p_source_geometry_data, p_node);
 	generator_parse_polygon2d_node(p_navigation_mesh, p_source_geometry_data, p_node);
 	generator_parse_staticbody2d_node(p_navigation_mesh, p_source_geometry_data, p_node);
-	generator_parse_tilemap_node(p_navigation_mesh, p_source_geometry_data, p_node);
+	generator_parse_tile_map_layer_node(p_navigation_mesh, p_source_geometry_data, p_node);
 	generator_parse_navigationobstacle_node(p_navigation_mesh, p_source_geometry_data, p_node);
 
 	generator_rid_rwlock.read_lock();
@@ -259,6 +259,14 @@ void NavMeshGenerator2D::generator_parse_geometry_node(Ref<NavigationPolygon> p_
 		for (int i = 0; i < p_node->get_child_count(); i++) {
 			generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, p_node->get_child(i), p_recurse_children);
 		}
+	} else if (Object::cast_to<TileMap>(p_node)) {
+		// Special case for TileMap, so that internal layer get parsed even if p_recurse_children is false.
+		for (int i = 0; i < p_node->get_child_count(); i++) {
+			TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_node->get_child(i));
+			if (tile_map_layer->get_index_in_tile_map() >= 0) {
+				generator_parse_tile_map_layer_node(p_navigation_mesh, p_source_geometry_data, tile_map_layer);
+			}
+		}
 	}
 }
 
@@ -580,141 +588,102 @@ void NavMeshGenerator2D::generator_parse_staticbody2d_node(const Ref<NavigationP
 	}
 }
 
-void NavMeshGenerator2D::generator_parse_tilemap_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
-	TileMap *tilemap = Object::cast_to<TileMap>(p_node);
-
-	if (tilemap == nullptr) {
+void NavMeshGenerator2D::generator_parse_tile_map_layer_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
+	TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_node);
+	if (tile_map_layer == nullptr) {
 		return;
 	}
 
-	NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
-	uint32_t parsed_collision_mask = p_navigation_mesh->get_parsed_collision_mask();
-
-	if (tilemap->get_layers_count() <= 0) {
-		return;
-	}
-
-	Ref<TileSet> tile_set = tilemap->get_tileset();
+	Ref<TileSet> tile_set = tile_map_layer->get_tile_set();
 	if (!tile_set.is_valid()) {
 		return;
 	}
 
 	int physics_layers_count = tile_set->get_physics_layers_count();
 	int navigation_layers_count = tile_set->get_navigation_layers_count();
-
 	if (physics_layers_count <= 0 && navigation_layers_count <= 0) {
 		return;
 	}
 
-	HashSet<Vector2i> cells_with_navigation_polygon;
-	HashSet<Vector2i> cells_with_collision_polygon;
+	NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
+	uint32_t parsed_collision_mask = p_navigation_mesh->get_parsed_collision_mask();
+
+	const Transform2D tilemap_xform = p_source_geometry_data->root_node_transform * tile_map_layer->get_global_transform();
 
-	const Transform2D tilemap_xform = p_source_geometry_data->root_node_transform * tilemap->get_global_transform();
+	TypedArray<Vector2i> used_cells = tile_map_layer->get_used_cells();
+	for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) {
+		const Vector2i &cell = used_cells[used_cell_index];
 
-#ifdef DEBUG_ENABLED
-	int error_print_counter = 0;
-	int error_print_max = 10;
-#endif // DEBUG_ENABLED
+		const TileData *tile_data = tile_map_layer->get_cell_tile_data(cell);
+		if (tile_data == nullptr) {
+			continue;
+		}
 
-	for (int tilemap_layer = 0; tilemap_layer < tilemap->get_layers_count(); tilemap_layer++) {
-		TypedArray<Vector2i> used_cells = tilemap->get_used_cells(tilemap_layer);
+		// Transform flags.
+		const int alternative_id = tile_map_layer->get_cell_alternative_tile(cell);
+		bool flip_h = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H);
+		bool flip_v = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V);
+		bool transpose = (alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
+
+		Transform2D tile_transform;
+		tile_transform.set_origin(tile_map_layer->map_to_local(cell));
+
+		const Transform2D tile_transform_offset = tilemap_xform * tile_transform;
+
+		// Parse traversable polygons.
+		for (int navigation_layer = 0; navigation_layer < navigation_layers_count; navigation_layer++) {
+			Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(navigation_layer, flip_h, flip_v, transpose);
+			if (navigation_polygon.is_valid()) {
+				for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) {
+					const Vector<Vector2> &navigation_polygon_outline = navigation_polygon->get_outline(outline_index);
+					if (navigation_polygon_outline.is_empty()) {
+						continue;
+					}
 
-		for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) {
-			const Vector2i &cell = used_cells[used_cell_index];
+					Vector<Vector2> traversable_outline;
+					traversable_outline.resize(navigation_polygon_outline.size());
 
-			const TileData *tile_data = tilemap->get_cell_tile_data(tilemap_layer, cell, false);
-			if (tile_data == nullptr) {
-				continue;
-			}
+					const Vector2 *navigation_polygon_outline_ptr = navigation_polygon_outline.ptr();
+					Vector2 *traversable_outline_ptrw = traversable_outline.ptrw();
 
-			// Transform flags.
-			const int alternative_id = tilemap->get_cell_alternative_tile(tilemap_layer, cell, false);
-			bool flip_h = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H);
-			bool flip_v = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V);
-			bool transpose = (alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
-
-			Transform2D tile_transform;
-			tile_transform.set_origin(tilemap->map_to_local(cell));
-
-			const Transform2D tile_transform_offset = tilemap_xform * tile_transform;
-
-			if (navigation_layers_count > 0) {
-				Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(tilemap_layer, flip_h, flip_v, transpose);
-				if (navigation_polygon.is_valid()) {
-					if (cells_with_navigation_polygon.has(cell)) {
-#ifdef DEBUG_ENABLED
-						error_print_counter++;
-						if (error_print_counter <= error_print_max) {
-							WARN_PRINT(vformat("TileMap navigation mesh baking error. The TileMap cell key Vector2i(%s, %s) has navigation mesh from 2 or more different TileMap layers assigned. This can cause unexpected navigation mesh baking results. The duplicated cell data was ignored.", cell.x, cell.y));
-						}
-#endif // DEBUG_ENABLED
-					} else {
-						cells_with_navigation_polygon.insert(cell);
-
-						for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) {
-							const Vector<Vector2> &navigation_polygon_outline = navigation_polygon->get_outline(outline_index);
-							if (navigation_polygon_outline.size() == 0) {
-								continue;
-							}
-
-							Vector<Vector2> traversable_outline;
-							traversable_outline.resize(navigation_polygon_outline.size());
-
-							const Vector2 *navigation_polygon_outline_ptr = navigation_polygon_outline.ptr();
-							Vector2 *traversable_outline_ptrw = traversable_outline.ptrw();
-
-							for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) {
-								traversable_outline_ptrw[traversable_outline_index] = tile_transform_offset.xform(navigation_polygon_outline_ptr[traversable_outline_index]);
-							}
-
-							p_source_geometry_data->_add_traversable_outline(traversable_outline);
-						}
+					for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) {
+						traversable_outline_ptrw[traversable_outline_index] = tile_transform_offset.xform(navigation_polygon_outline_ptr[traversable_outline_index]);
 					}
+
+					p_source_geometry_data->_add_traversable_outline(traversable_outline);
 				}
 			}
+		}
 
-			if (physics_layers_count > 0 && (parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH) && (tile_set->get_physics_layer_collision_layer(tilemap_layer) & parsed_collision_mask)) {
-				if (cells_with_collision_polygon.has(cell)) {
-#ifdef DEBUG_ENABLED
-					error_print_counter++;
-					if (error_print_counter <= error_print_max) {
-						WARN_PRINT(vformat("TileMap navigation mesh baking error. The cell key Vector2i(%s, %s) has collision polygons from 2 or more different TileMap layers assigned that all match the parsed collision mask. This can cause unexpected navigation mesh baking results. The duplicated cell data was ignored.", cell.x, cell.y));
+		// Parse obstacles.
+		for (int physics_layer = 0; physics_layer < physics_layers_count; physics_layer++) {
+			if ((parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH) &&
+					(tile_set->get_physics_layer_collision_layer(physics_layer) & parsed_collision_mask)) {
+				for (int collision_polygon_index = 0; collision_polygon_index < tile_data->get_collision_polygons_count(physics_layer); collision_polygon_index++) {
+					PackedVector2Array collision_polygon_points = tile_data->get_collision_polygon_points(physics_layer, collision_polygon_index);
+					if (collision_polygon_points.is_empty()) {
+						continue;
 					}
-#endif // DEBUG_ENABLED
-				} else {
-					cells_with_collision_polygon.insert(cell);
-
-					for (int collision_polygon_index = 0; collision_polygon_index < tile_data->get_collision_polygons_count(tilemap_layer); collision_polygon_index++) {
-						PackedVector2Array collision_polygon_points = tile_data->get_collision_polygon_points(tilemap_layer, collision_polygon_index);
-						if (collision_polygon_points.size() == 0) {
-							continue;
-						}
-
-						if (flip_h || flip_v || transpose) {
-							collision_polygon_points = TileData::get_transformed_vertices(collision_polygon_points, flip_h, flip_v, transpose);
-						}
 
-						Vector<Vector2> obstruction_outline;
-						obstruction_outline.resize(collision_polygon_points.size());
+					if (flip_h || flip_v || transpose) {
+						collision_polygon_points = TileData::get_transformed_vertices(collision_polygon_points, flip_h, flip_v, transpose);
+					}
 
-						const Vector2 *collision_polygon_points_ptr = collision_polygon_points.ptr();
-						Vector2 *obstruction_outline_ptrw = obstruction_outline.ptrw();
+					Vector<Vector2> obstruction_outline;
+					obstruction_outline.resize(collision_polygon_points.size());
 
-						for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) {
-							obstruction_outline_ptrw[obstruction_outline_index] = tile_transform_offset.xform(collision_polygon_points_ptr[obstruction_outline_index]);
-						}
+					const Vector2 *collision_polygon_points_ptr = collision_polygon_points.ptr();
+					Vector2 *obstruction_outline_ptrw = obstruction_outline.ptrw();
 
-						p_source_geometry_data->_add_obstruction_outline(obstruction_outline);
+					for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) {
+						obstruction_outline_ptrw[obstruction_outline_index] = tile_transform_offset.xform(collision_polygon_points_ptr[obstruction_outline_index]);
 					}
+
+					p_source_geometry_data->_add_obstruction_outline(obstruction_outline);
 				}
 			}
 		}
 	}
-#ifdef DEBUG_ENABLED
-	if (error_print_counter > error_print_max) {
-		ERR_PRINT(vformat("TileMap navigation mesh baking error. A total of %s cells with navigation or collision polygons from 2 or more different TileMap layers overlap. This can cause unexpected navigation mesh baking results. The duplicated cell data was ignored.", error_print_counter));
-	}
-#endif // DEBUG_ENABLED
 }
 
 void NavMeshGenerator2D::generator_parse_navigationobstacle_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {

+ 1 - 1
modules/navigation/2d/nav_mesh_generator_2d.h

@@ -89,7 +89,7 @@ class NavMeshGenerator2D : public Object {
 	static void generator_parse_multimeshinstance2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
 	static void generator_parse_polygon2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
 	static void generator_parse_staticbody2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
-	static void generator_parse_tilemap_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
+	static void generator_parse_tile_map_layer_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
 	static void generator_parse_navigationobstacle_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
 
 	static bool generator_emit_callback(const Callable &p_callback);