Implement navigation obstacle support for LayeredTileMapLayers.

This commit is contained in:
Relintai 2024-05-11 19:29:43 +02:00
parent 01342ca5fd
commit d59353d48e
4 changed files with 169 additions and 10 deletions

View File

@ -217,18 +217,18 @@ void LayeredTileMapLayer::_rendering_update(bool p_force_cleanup) {
// If so, recreate everything.
bool quandrant_shape_changed = dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE] ||
((is_sort_enabled() && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED])) ||
dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] ||
dirty.flags[DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM] ||
dirty.flags[DIRTY_FLAGS_TILE_SET]
dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] ||
dirty.flags[DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM] ||
dirty.flags[DIRTY_FLAGS_TILE_SET]
#ifdef MODULE_VERTEX_LIGHTS_2D_ENABLED
|| dirty.flags[DIRTY_FLAGS_LAYER_VERTEX_LIGHTS]
|| dirty.flags[DIRTY_FLAGS_LAYER_VERTEX_LIGHTS]
#endif
#ifdef MODULE_FASTNOISE_ENABLED
|| dirty.flags[DIRTY_FLAGS_LAYER_RAO]
|| dirty.flags[DIRTY_FLAGS_LAYER_RAO]
#endif
);
);
// Free all quadrants.
if (forced_cleanup || quandrant_shape_changed) {
@ -1071,6 +1071,13 @@ void LayeredTileMapLayer::_navigation_notification(int p_what) {
Transform2D tilemap_xform = get_global_transform();
for (HashMap<Vector2i, CellData>::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) {
const CellData &cell_data = kv->value();
Transform2D tile_transform;
tile_transform.set_origin(tile_set->map_to_local(kv->key()));
Transform2D ctf = tilemap_xform * tile_transform;
Vector2 pos = ctf.xform(Vector2());
// Update navigation regions transform.
for (uint32_t i = 0; i < cell_data.navigation_regions.size(); ++i) {
const RID &region = cell_data.navigation_regions[i];
@ -1079,9 +1086,18 @@ void LayeredTileMapLayer::_navigation_notification(int p_what) {
continue;
}
Transform2D tile_transform;
tile_transform.set_origin(tile_set->map_to_local(kv->key()));
Navigation2DServer::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform);
Navigation2DServer::get_singleton()->region_set_transform(region, ctf);
}
// Update navigation obstacle transform.
for (uint32_t i = 0; i < cell_data.obstacles.size(); ++i) {
const RID &obstacle = cell_data.obstacles[i];
if (!obstacle.is_valid()) {
continue;
}
Navigation2DServer::get_singleton()->obstacle_set_position(obstacle, pos);
}
}
}
@ -1099,6 +1115,16 @@ void LayeredTileMapLayer::_navigation_clear_cell(CellData &r_cell_data) {
}
}
r_cell_data.navigation_regions.clear();
// Clear navigation obstacles.
for (uint32_t i = 0; i < r_cell_data.obstacles.size(); i++) {
const RID &obstacle = r_cell_data.obstacles[i];
if (obstacle.is_valid()) {
ns->obstacle_set_map(obstacle, RID());
ns->free(obstacle);
}
}
r_cell_data.obstacles.clear();
}
void LayeredTileMapLayer::_navigation_update_cell(CellData &r_cell_data) {
@ -1168,6 +1194,46 @@ void LayeredTileMapLayer::_navigation_update_cell(CellData &r_cell_data) {
}
}
// Free unused obstacles then resize the obstacles array.
for (uint32_t i = tile_set->get_avoidance_layers_count(); i < r_cell_data.obstacles.size(); i++) {
RID &obstacle = r_cell_data.obstacles[i];
if (obstacle.is_valid()) {
ns->obstacle_set_map(obstacle, RID());
ns->free(obstacle);
obstacle = RID();
}
}
r_cell_data.obstacles.resize(tile_set->get_avoidance_layers_count());
// Create, update or clear obstacles.
for (uint32_t avoidance_layer_index = 0; avoidance_layer_index < r_cell_data.obstacles.size(); avoidance_layer_index++) {
Vector<Vector2> polygon = tile_data->get_transformed_avoidance_polygon(avoidance_layer_index, flip_h, flip_v, transpose);
RID &obstacle = r_cell_data.obstacles[avoidance_layer_index];
if (polygon.size() >= 3) {
// Create or update regions.
Vector2 pos = tile_set->map_to_local(r_cell_data.coords);
if (!obstacle.is_valid()) {
obstacle = ns->obstacle_create();
}
ns->obstacle_set_map(obstacle, navigation_map);
ns->obstacle_set_vertices(obstacle, polygon);
ns->obstacle_set_radius(obstacle, tile_data->get_avoidance_radius(avoidance_layer_index));
ns->obstacle_set_position(obstacle, pos + tile_data->get_avoidance_position(avoidance_layer_index));
ns->obstacle_set_avoidance_layers(obstacle, tile_set->get_avoidance_layer_layers(avoidance_layer_index));
} else {
// Clear region.
if (obstacle.is_valid()) {
ns->obstacle_set_map(obstacle, RID());
ns->free(obstacle);
obstacle = RID();
}
}
}
return;
}
}
@ -1277,6 +1343,59 @@ void LayeredTileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item,
}
}
}
if (r_cell_data.obstacles.empty()) {
return;
}
Color pushin_face_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_face_color();
Color pushout_face_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_face_color();
Color pushin_edge_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_edge_color();
Color pushout_edge_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_edge_color();
for (int layer_index = 0; layer_index < tile_set->get_avoidance_layers_count(); layer_index++) {
bool flip_h = (c.alternative_tile & LayeredTileSetAtlasSource::TRANSFORM_FLIP_H);
bool flip_v = (c.alternative_tile & LayeredTileSetAtlasSource::TRANSFORM_FLIP_V);
bool transpose = (c.alternative_tile & LayeredTileSetAtlasSource::TRANSFORM_TRANSPOSE);
Vector<Vector2> polygon = tile_data->get_transformed_avoidance_polygon(layer_index, flip_h, flip_v, transpose);
if (polygon.size() >= 3) {
bool obstacle_pushes_inward = Geometry::is_polygon_clockwise(polygon);
Color debug_static_obstacle_face_color;
if (obstacle_pushes_inward) {
debug_static_obstacle_face_color = pushin_face_color;
} else {
debug_static_obstacle_face_color = pushout_face_color;
}
Vector<Color> debug_obstacle_polygon_colors;
debug_obstacle_polygon_colors.resize(polygon.size());
debug_obstacle_polygon_colors.fill(debug_static_obstacle_face_color);
RS::get_singleton()->canvas_item_add_polygon(p_canvas_item, polygon, debug_obstacle_polygon_colors);
Color debug_static_obstacle_edge_color;
if (obstacle_pushes_inward) {
debug_static_obstacle_edge_color = pushin_edge_color;
} else {
debug_static_obstacle_edge_color = pushout_edge_color;
}
Vector<Vector2> debug_obstacle_line_vertices = polygon;
debug_obstacle_line_vertices.push_back(debug_obstacle_line_vertices[0]);
debug_obstacle_line_vertices.resize(debug_obstacle_line_vertices.size());
Vector<Color> debug_obstacle_line_colors;
debug_obstacle_line_colors.resize(debug_obstacle_line_vertices.size());
debug_obstacle_line_colors.fill(debug_static_obstacle_edge_color);
RS::get_singleton()->canvas_item_add_polyline(p_canvas_item, debug_obstacle_line_vertices, debug_obstacle_line_colors, 4.0);
}
}
}
}
}

View File

@ -134,6 +134,9 @@ struct CellData {
// Navigation.
LocalVector<RID> navigation_regions;
// Avoidance.
LocalVector<RID> obstacles;
// Scenes.
String scene;
@ -154,6 +157,7 @@ struct CellData {
occluders = p_other.occluders;
bodies = p_other.bodies;
navigation_regions = p_other.navigation_regions;
obstacles = p_other.obstacles;
scene = p_other.scene;
runtime_tile_data_cache = p_other.runtime_tile_data_cache;
@ -175,6 +179,7 @@ struct CellData {
occluders = p_other.occluders;
bodies = p_other.bodies;
navigation_regions = p_other.navigation_regions;
obstacles = p_other.obstacles;
scene = p_other.scene;
runtime_tile_data_cache = p_other.runtime_tile_data_cache;

View File

@ -6807,6 +6807,39 @@ Vector<Vector2> LayeredTileData::get_avoidance_polygon_points(int p_layer_id) co
return avoidance[p_layer_id].polygon;
}
Vector<Vector2> LayeredTileData::get_transformed_avoidance_polygon(int p_layer_id, bool p_flip_h, bool p_flip_v, bool p_transpose) const {
ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Vector<Vector2>());
const Vector<Vector2> &polygon = avoidance[p_layer_id].polygon;
const Vector2 *r = polygon.ptr();
int size = polygon.size();
Vector<Vector2> transformed_polygon;
transformed_polygon.resize(polygon.size());
Vector2 *w = transformed_polygon.ptrw();
for (int i = 0; i < size; i++) {
Vector2 v;
if (p_transpose) {
v = Vector2(r[i].y, r[i].x);
} else {
v = r[i];
}
if (p_flip_h) {
v.x *= -1;
}
if (p_flip_v) {
v.y *= -1;
}
w[i] = v;
}
return transformed_polygon;
}
// Misc
void LayeredTileData::set_probability(float p_probability) {
ERR_FAIL_COND(p_probability < 0.0);

View File

@ -1034,6 +1034,8 @@ public:
void set_avoidance_polygon_points(int p_layer_id, Vector<Vector2> p_polygon);
Vector<Vector2> get_avoidance_polygon_points(int p_layer_id) const;
Vector<Vector2> get_transformed_avoidance_polygon(int p_layer_id, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const;
// Misc
void set_probability(float p_probability);
float get_probability() const;