diff options
Diffstat (limited to 'modules/navigation/2d/nav_mesh_generator_2d.cpp')
-rw-r--r-- | modules/navigation/2d/nav_mesh_generator_2d.cpp | 210 |
1 files changed, 153 insertions, 57 deletions
diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp index 069cc0f4a8..13399b858e 100644 --- a/modules/navigation/2d/nav_mesh_generator_2d.cpp +++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp @@ -43,9 +43,9 @@ #include "scene/resources/2d/circle_shape_2d.h" #include "scene/resources/2d/concave_polygon_shape_2d.h" #include "scene/resources/2d/convex_polygon_shape_2d.h" +#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h" +#include "scene/resources/2d/navigation_polygon.h" #include "scene/resources/2d/rectangle_shape_2d.h" -#include "scene/resources/navigation_mesh_source_geometry_data_2d.h" -#include "scene/resources/navigation_polygon.h" #include "thirdparty/clipper2/include/clipper2/clipper.h" #include "thirdparty/misc/polypartition.h" @@ -53,11 +53,14 @@ NavMeshGenerator2D *NavMeshGenerator2D::singleton = nullptr; Mutex NavMeshGenerator2D::baking_navmesh_mutex; Mutex NavMeshGenerator2D::generator_task_mutex; +RWLock NavMeshGenerator2D::generator_rid_rwlock; bool NavMeshGenerator2D::use_threads = true; bool NavMeshGenerator2D::baking_use_multiple_threads = true; bool NavMeshGenerator2D::baking_use_high_priority_threads = true; HashSet<Ref<NavigationPolygon>> NavMeshGenerator2D::baking_navmeshes; HashMap<WorkerThreadPool::TaskID, NavMeshGenerator2D::NavMeshGeneratorTask2D *> NavMeshGenerator2D::generator_tasks; +RID_Owner<NavMeshGenerator2D::NavMeshGeometryParser2D> NavMeshGenerator2D::generator_parser_owner; +LocalVector<NavMeshGenerator2D::NavMeshGeometryParser2D *> NavMeshGenerator2D::generator_parsers; NavMeshGenerator2D *NavMeshGenerator2D::get_singleton() { return singleton; @@ -72,7 +75,7 @@ NavMeshGenerator2D::NavMeshGenerator2D() { // Using threads might cause problems on certain exports or with the Editor on certain devices. // This is the main switch to turn threaded navmesh baking off should the need arise. - use_threads = baking_use_multiple_threads && !Engine::get_singleton()->is_editor_hint(); + use_threads = baking_use_multiple_threads; } NavMeshGenerator2D::~NavMeshGenerator2D() { @@ -126,6 +129,13 @@ void NavMeshGenerator2D::cleanup() { } generator_tasks.clear(); + generator_rid_rwlock.write_lock(); + for (NavMeshGeometryParser2D *parser : generator_parsers) { + generator_parser_owner.free(parser->self); + } + generator_parsers.clear(); + generator_rid_rwlock.write_unlock(); + generator_task_mutex.unlock(); baking_navmesh_mutex.unlock(); } @@ -236,6 +246,15 @@ void NavMeshGenerator2D::generator_parse_geometry_node(Ref<NavigationPolygon> p_ generator_parse_tilemap_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(); + for (const NavMeshGeometryParser2D *parser : generator_parsers) { + if (!parser->callback.is_valid()) { + continue; + } + parser->callback.call(p_navigation_mesh, p_source_geometry_data, p_node); + } + generator_rid_rwlock.read_unlock(); + if (p_recurse_children) { 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); @@ -575,8 +594,6 @@ void NavMeshGenerator2D::generator_parse_tilemap_node(const Ref<NavigationPolygo return; } - int tilemap_layer = 0; // only main tile map layer is supported - Ref<TileSet> tile_set = tilemap->get_tileset(); if (!tile_set.is_valid()) { return; @@ -589,77 +606,115 @@ void NavMeshGenerator2D::generator_parse_tilemap_node(const Ref<NavigationPolygo return; } - const Transform2D tilemap_xform = p_source_geometry_data->root_node_transform * tilemap->get_global_transform(); - TypedArray<Vector2i> used_cells = tilemap->get_used_cells(tilemap_layer); + HashSet<Vector2i> cells_with_navigation_polygon; + HashSet<Vector2i> cells_with_collision_polygon; - for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) { - const Vector2i &cell = used_cells[used_cell_index]; + const Transform2D tilemap_xform = p_source_geometry_data->root_node_transform * tilemap->get_global_transform(); - const TileData *tile_data = tilemap->get_cell_tile_data(tilemap_layer, cell, false); - if (tile_data == nullptr) { - continue; - } +#ifdef DEBUG_ENABLED + int error_print_counter = 0; + int error_print_max = 10; +#endif // DEBUG_ENABLED - // 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); + for (int tilemap_layer = 0; tilemap_layer < tilemap->get_layers_count(); tilemap_layer++) { + TypedArray<Vector2i> used_cells = tilemap->get_used_cells(tilemap_layer); - Transform2D tile_transform; - tile_transform.set_origin(tilemap->map_to_local(cell)); + for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) { + const Vector2i &cell = used_cells[used_cell_index]; - const Transform2D tile_transform_offset = tilemap_xform * tile_transform; + const TileData *tile_data = tilemap->get_cell_tile_data(tilemap_layer, cell, false); + if (tile_data == nullptr) { + continue; + } - 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()) { - 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; + // 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); + } } + } + } - 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]); + 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)); } +#endif // DEBUG_ENABLED + } else { + cells_with_collision_polygon.insert(cell); - p_source_geometry_data->_add_traversable_outline(traversable_outline); - } - } - } + 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 (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)) { - 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); + } - 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()); - Vector<Vector2> 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(); - 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]); + } - 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); + } } - - 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) { @@ -777,6 +832,47 @@ bool NavMeshGenerator2D::generator_emit_callback(const Callable &p_callback) { return ce.error == Callable::CallError::CALL_OK; } +RID NavMeshGenerator2D::source_geometry_parser_create() { + RWLockWrite write_lock(generator_rid_rwlock); + + RID rid = generator_parser_owner.make_rid(); + + NavMeshGeometryParser2D *parser = generator_parser_owner.get_or_null(rid); + parser->self = rid; + + generator_parsers.push_back(parser); + + return rid; +} + +void NavMeshGenerator2D::source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) { + RWLockWrite write_lock(generator_rid_rwlock); + + NavMeshGeometryParser2D *parser = generator_parser_owner.get_or_null(p_parser); + ERR_FAIL_NULL(parser); + + parser->callback = p_callback; +} + +bool NavMeshGenerator2D::owns(RID p_object) { + RWLockRead read_lock(generator_rid_rwlock); + return generator_parser_owner.owns(p_object); +} + +void NavMeshGenerator2D::free(RID p_object) { + RWLockWrite write_lock(generator_rid_rwlock); + + if (generator_parser_owner.owns(p_object)) { + NavMeshGeometryParser2D *parser = generator_parser_owner.get_or_null(p_object); + + generator_parsers.erase(parser); + + generator_parser_owner.free(p_object); + } else { + ERR_PRINT("Attempted to free a NavMeshGenerator2D RID that did not exist (or was already freed)."); + } +} + void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data) { if (p_navigation_mesh.is_null() || p_source_geometry_data.is_null()) { return; |