From 23b9a9f2ded161fddc76f0ba576edd4b3c8afffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gilles=20Roudi=C3=A8re?= Date: Thu, 2 May 2024 17:35:36 +0200 Subject: [PATCH] Fix TileMapLayer navmesh baking --- .../navigation/2d/nav_mesh_generator_2d.cpp | 179 ++++++++---------- modules/navigation/2d/nav_mesh_generator_2d.h | 2 +- 2 files changed, 75 insertions(+), 106 deletions(-) diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp index 15a645816cd..2198158f9ca 100644 --- a/modules/navigation/2d/nav_mesh_generator_2d.cpp +++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp @@ -243,7 +243,7 @@ void NavMeshGenerator2D::generator_parse_geometry_node(Ref 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 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(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(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 &p_navigation_mesh, Ref p_source_geometry_data, Node *p_node) { - TileMap *tilemap = Object::cast_to(p_node); - - if (tilemap == nullptr) { +void NavMeshGenerator2D::generator_parse_tile_map_layer_node(const Ref &p_navigation_mesh, Ref p_source_geometry_data, Node *p_node) { + TileMapLayer *tile_map_layer = Object::cast_to(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 tile_set = tilemap->get_tileset(); + Ref 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 cells_with_navigation_polygon; - HashSet 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 * tilemap->get_global_transform(); + const Transform2D tilemap_xform = p_source_geometry_data->root_node_transform * tile_map_layer->get_global_transform(); -#ifdef DEBUG_ENABLED - int error_print_counter = 0; - int error_print_max = 10; -#endif // DEBUG_ENABLED + TypedArray 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]; - for (int tilemap_layer = 0; tilemap_layer < tilemap->get_layers_count(); tilemap_layer++) { - TypedArray used_cells = tilemap->get_used_cells(tilemap_layer); + const TileData *tile_data = tile_map_layer->get_cell_tile_data(cell); + if (tile_data == nullptr) { + continue; + } - for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) { - const Vector2i &cell = used_cells[used_cell_index]; + // 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); - const TileData *tile_data = tilemap->get_cell_tile_data(tilemap_layer, cell, false); - if (tile_data == nullptr) { - continue; - } + Transform2D tile_transform; + tile_transform.set_origin(tile_map_layer->map_to_local(cell)); - // 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); + const Transform2D tile_transform_offset = tilemap_xform * tile_transform; - 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 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 &navigation_polygon_outline = navigation_polygon->get_outline(outline_index); - if (navigation_polygon_outline.size() == 0) { - continue; - } - - Vector 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); - } + // Parse traversable polygons. + for (int navigation_layer = 0; navigation_layer < navigation_layers_count; navigation_layer++) { + Ref 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 &navigation_polygon_outline = navigation_polygon->get_outline(outline_index); + if (navigation_polygon_outline.is_empty()) { + continue; } + + Vector 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); } } + } - 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 obstruction_outline; - obstruction_outline.resize(collision_polygon_points.size()); - - const Vector2 *collision_polygon_points_ptr = collision_polygon_points.ptr(); - Vector2 *obstruction_outline_ptrw = obstruction_outline.ptrw(); - - 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); + if (flip_h || flip_v || transpose) { + collision_polygon_points = TileData::get_transformed_vertices(collision_polygon_points, flip_h, flip_v, transpose); } + + Vector obstruction_outline; + obstruction_outline.resize(collision_polygon_points.size()); + + const Vector2 *collision_polygon_points_ptr = collision_polygon_points.ptr(); + Vector2 *obstruction_outline_ptrw = obstruction_outline.ptrw(); + + 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 &p_navigation_mesh, Ref p_source_geometry_data, Node *p_node) { diff --git a/modules/navigation/2d/nav_mesh_generator_2d.h b/modules/navigation/2d/nav_mesh_generator_2d.h index 235a84d5487..d5f9694242a 100644 --- a/modules/navigation/2d/nav_mesh_generator_2d.h +++ b/modules/navigation/2d/nav_mesh_generator_2d.h @@ -89,7 +89,7 @@ class NavMeshGenerator2D : public Object { static void generator_parse_multimeshinstance2d_node(const Ref &p_navigation_mesh, Ref p_source_geometry_data, Node *p_node); static void generator_parse_polygon2d_node(const Ref &p_navigation_mesh, Ref p_source_geometry_data, Node *p_node); static void generator_parse_staticbody2d_node(const Ref &p_navigation_mesh, Ref p_source_geometry_data, Node *p_node); - static void generator_parse_tilemap_node(const Ref &p_navigation_mesh, Ref p_source_geometry_data, Node *p_node); + static void generator_parse_tile_map_layer_node(const Ref &p_navigation_mesh, Ref p_source_geometry_data, Node *p_node); static void generator_parse_navigationobstacle_node(const Ref &p_navigation_mesh, Ref p_source_geometry_data, Node *p_node); static bool generator_emit_callback(const Callable &p_callback);