diff --git a/doc/classes/TileData.xml b/doc/classes/TileData.xml index 8aa5f1398a87..9e833e0236d0 100644 --- a/doc/classes/TileData.xml +++ b/doc/classes/TileData.xml @@ -70,15 +70,23 @@ + + + Returns the navigation polygon of the tile for the TileSet navigation layer with index [param layer_id]. + [param flip_h], [param flip_v], and [param transpose] allow transforming the returned polygon. + + + Returns the occluder polygon of the tile for the TileSet occlusion layer with index [param layer_id]. + [param flip_h], [param flip_v], and [param transpose] allow transforming the returned polygon. diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index ad4844fe3e26..221833d45026 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -1421,8 +1421,8 @@ void TileDataOcclusionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Tra Variant TileDataOcclusionShapeEditor::_get_painted_value() { Ref occluder_polygon; - occluder_polygon.instantiate(); if (polygon_editor->get_polygon_count() >= 1) { + occluder_polygon.instantiate(); occluder_polygon->set_polygon(polygon_editor->get_polygon(0)); } return occluder_polygon; diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected index 0538195e9e11..8a31d0fbc8e0 100644 --- a/misc/extension_api_validation/4.2-stable.expected +++ b/misc/extension_api_validation/4.2-stable.expected @@ -7,3 +7,10 @@ should instead be used to justify these changes and describe how users should wo Add new entries at the end of the file. ## Changes between 4.2-stable and 4.3-stable + +GH-84660 +-------- +Validate extension JSON: Error: Field 'classes/TileData/methods/get_navigation_polygon/arguments': size changed value in new API, from 1 to 4. +Validate extension JSON: Error: Field 'classes/TileData/methods/get_occluder/arguments': size changed value in new API, from 1 to 4. + +Added optional argument to get_navigation_polygon and get_occluder to specify a polygon transform. diff --git a/modules/navigation/nav_mesh_generator_2d.cpp b/modules/navigation/nav_mesh_generator_2d.cpp index f8c12935b484..6dfafa4e91e5 100644 --- a/modules/navigation/nav_mesh_generator_2d.cpp +++ b/modules/navigation/nav_mesh_generator_2d.cpp @@ -591,13 +591,19 @@ void NavMeshGenerator2D::generator_parse_tilemap_node(const Refget_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 navigation_polygon = tile_data->get_navigation_polygon(tilemap_layer); + Ref 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 &navigation_polygon_outline = navigation_polygon->get_outline(outline_index); @@ -622,11 +628,15 @@ void NavMeshGenerator2D::generator_parse_tilemap_node(const Ref 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++) { - const Vector &collision_polygon_points = tile_data->get_collision_polygon_points(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()); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 04e362c42688..f2be91cbf308 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -447,11 +447,12 @@ void TileMapLayer::_rendering_update() { for (KeyValue &kv : tile_map) { CellData &cell_data = kv.value; for (const RID &occluder : cell_data.occluders) { - if (occluder.is_valid()) { - Transform2D xform(0, tile_map_node->map_to_local(kv.key)); - rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas()); - rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform); + if (occluder.is_null()) { + continue; } + Transform2D xform(0, tile_map_node->map_to_local(kv.key)); + rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas()); + rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform); } } } @@ -591,6 +592,11 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) { tile_data = atlas_source->get_tile_data(r_cell_data.cell.get_atlas_coords(), r_cell_data.cell.alternative_tile); } + // Transform flags. + bool flip_h = (r_cell_data.cell.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H); + bool flip_v = (r_cell_data.cell.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V); + bool transpose = (r_cell_data.cell.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + // Create, update or clear occluders. for (uint32_t occlusion_layer_index = 0; occlusion_layer_index < r_cell_data.occluders.size(); occlusion_layer_index++) { Ref occluder_polygon = tile_data->get_occluder(occlusion_layer_index); @@ -606,7 +612,7 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) { } rs->canvas_light_occluder_set_enabled(occluder, node_visible); rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform); - rs->canvas_light_occluder_set_polygon(occluder, tile_map_node->get_transformed_polygon(Ref(tile_data->get_occluder(occlusion_layer_index)), r_cell_data.cell.alternative_tile)->get_rid()); + rs->canvas_light_occluder_set_polygon(occluder, tile_data->get_occluder(occlusion_layer_index, flip_h, flip_v, transpose)->get_rid()); rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas()); rs->canvas_light_occluder_set_light_mask(occluder, tile_set->get_occlusion_layer_light_mask(occlusion_layer_index)); } else { @@ -800,6 +806,11 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) { tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); } + // Transform flags. + bool flip_h = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H); + bool flip_v = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V); + bool transpose = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + // Free unused bodies then resize the bodies array. for (uint32_t i = tile_set->get_physics_layers_count(); i < r_cell_data.bodies.size(); i++) { RID body = r_cell_data.bodies[i]; @@ -864,8 +875,7 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) { int shapes_count = tile_data->get_collision_polygon_shapes_count(tile_set_physics_layer, polygon_index); for (int shape_index = 0; shape_index < shapes_count; shape_index++) { // Add decomposed convex shapes. - Ref shape = tile_data->get_collision_polygon_shape(tile_set_physics_layer, polygon_index, shape_index); - shape = tile_map_node->get_transformed_polygon(Ref(shape), c.alternative_tile); + Ref shape = tile_data->get_collision_polygon_shape(tile_set_physics_layer, polygon_index, shape_index, flip_h, flip_v, transpose); ps->body_add_shape(body, shape->get_rid()); ps->body_set_shape_as_one_way_collision(body, body_shape_index, one_way_collision, one_way_collision_margin); @@ -1053,6 +1063,11 @@ void TileMapLayer::_navigation_update_cell(CellData &r_cell_data) { tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); } + // Transform flags. + bool flip_h = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H); + bool flip_v = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V); + bool transpose = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + // Free unused regions then resize the regions array. for (uint32_t i = tile_set->get_navigation_layers_count(); i < r_cell_data.navigation_regions.size(); i++) { RID ®ion = r_cell_data.navigation_regions[i]; @@ -1066,9 +1081,7 @@ void TileMapLayer::_navigation_update_cell(CellData &r_cell_data) { // Create, update or clear regions. for (uint32_t navigation_layer_index = 0; navigation_layer_index < r_cell_data.navigation_regions.size(); navigation_layer_index++) { - Ref navigation_polygon; - navigation_polygon = tile_data->get_navigation_polygon(navigation_layer_index); - navigation_polygon = tile_map_node->get_transformed_polygon(Ref(navigation_polygon), c.alternative_tile); + Ref navigation_polygon = tile_data->get_navigation_polygon(navigation_layer_index, flip_h, flip_v, transpose); RID ®ion = r_cell_data.navigation_regions[navigation_layer_index]; @@ -1161,9 +1174,11 @@ void TileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, const V rs->canvas_item_add_set_transform(p_canvas_item, cell_to_quadrant); for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { - Ref navigation_polygon = tile_data->get_navigation_polygon(layer_index); + bool flip_h = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H); + bool flip_v = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V); + bool transpose = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + Ref navigation_polygon = tile_data->get_navigation_polygon(layer_index, flip_h, flip_v, transpose); if (navigation_polygon.is_valid()) { - navigation_polygon = tile_map_node->get_transformed_polygon(Ref(navigation_polygon), c.alternative_tile); Vector navigation_polygon_vertices = navigation_polygon->get_vertices(); if (navigation_polygon_vertices.size() < 3) { continue; @@ -3119,11 +3134,6 @@ void TileMap::_internal_update() { return; } - // FIXME: This should only clear polygons that are no longer going to be used, but since it's difficult to determine, - // the cache is never cleared at runtime to prevent invalidating used polygons. - if (Engine::get_singleton()->is_editor_hint()) { - polygon_cache.clear(); - } // Update dirty quadrants on layers. for (Ref &layer : layers) { layer->internal_update(); @@ -3608,37 +3618,6 @@ Rect2 TileMap::_edit_get_rect() const { } #endif -PackedVector2Array TileMap::_get_transformed_vertices(const PackedVector2Array &p_vertices, int p_alternative_id) { - const Vector2 *r = p_vertices.ptr(); - int size = p_vertices.size(); - - PackedVector2Array new_points; - new_points.resize(size); - Vector2 *w = new_points.ptrw(); - - bool flip_h = (p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H); - bool flip_v = (p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V); - bool transpose = (p_alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE); - - for (int i = 0; i < size; i++) { - Vector2 v; - if (transpose) { - v = Vector2(r[i].y, r[i].x); - } else { - v = r[i]; - } - - if (flip_h) { - v.x *= -1; - } - if (flip_v) { - v.y *= -1; - } - w[i] = v; - } - return new_points; -} - bool TileMap::_set(const StringName &p_name, const Variant &p_value) { Vector components = String(p_name).split("/", true, 2); if (p_name == "format") { @@ -4637,57 +4616,6 @@ void TileMap::draw_cells_outline(Control *p_control, const RBSet &p_ce #undef DRAW_SIDE_IF_NEEDED } -Ref TileMap::get_transformed_polygon(Ref p_polygon, int p_alternative_id) { - if (!bool(p_alternative_id & (TileSetAtlasSource::TRANSFORM_FLIP_H | TileSetAtlasSource::TRANSFORM_FLIP_V | TileSetAtlasSource::TRANSFORM_TRANSPOSE))) { - return p_polygon; - } - - { - HashMap, int>, Ref, PairHash, int>>::Iterator E = polygon_cache.find(Pair, int>(p_polygon, p_alternative_id)); - if (E) { - return E->value; - } - } - - Ref col = p_polygon; - if (col.is_valid()) { - Ref ret; - ret.instantiate(); - ret->set_points(_get_transformed_vertices(col->get_points(), p_alternative_id)); - polygon_cache[Pair, int>(p_polygon, p_alternative_id)] = ret; - return ret; - } - - Ref nav = p_polygon; - if (nav.is_valid()) { - PackedVector2Array new_points = _get_transformed_vertices(nav->get_vertices(), p_alternative_id); - Ref ret; - ret.instantiate(); - ret->set_vertices(new_points); - - PackedInt32Array indices; - indices.resize(new_points.size()); - int *w = indices.ptrw(); - for (int i = 0; i < new_points.size(); i++) { - w[i] = i; - } - ret->add_polygon(indices); - polygon_cache[Pair, int>(p_polygon, p_alternative_id)] = ret; - return ret; - } - - Ref ocd = p_polygon; - if (ocd.is_valid()) { - Ref ret; - ret.instantiate(); - ret->set_polygon(_get_transformed_vertices(ocd->get_polygon(), p_alternative_id)); - polygon_cache[Pair, int>(p_polygon, p_alternative_id)] = ret; - return ret; - } - - return p_polygon; -} - PackedStringArray TileMap::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 9a847a7ebb8f..1e4c6d0e6657 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -472,10 +472,6 @@ private: void _update_notify_local_transform(); - // Polygons. - HashMap, int>, Ref, PairHash, int>> polygon_cache; - PackedVector2Array _get_transformed_vertices(const PackedVector2Array &p_vertices, int p_alternative_id); - protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -619,7 +615,6 @@ public: // Helpers? TypedArray get_surrounding_cells(const Vector2i &coords); void draw_cells_outline(Control *p_control, const RBSet &p_cells, Color p_color, Transform2D p_transform = Transform2D()); - Ref get_transformed_polygon(Ref p_polygon, int p_alternative_id); // Virtual function to modify the TileData at runtime. GDVIRTUAL2R(bool, _use_tile_data_runtime_update, int, Vector2i); diff --git a/scene/resources/tile_set.compat.inc b/scene/resources/tile_set.compat.inc new file mode 100644 index 000000000000..873ae3aa93f5 --- /dev/null +++ b/scene/resources/tile_set.compat.inc @@ -0,0 +1,48 @@ +/**************************************************************************/ +/* tile_set.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +#include "tile_set.h" + +Ref TileData::_get_navigation_polygon_bind_compat_84660(int p_layer_id) const { + return get_navigation_polygon(p_layer_id, false, false, false); +} + +Ref TileData::_get_occluder_bind_compat_84660(int p_layer_id) const { + return get_occluder(p_layer_id, false, false, false); +} + +void TileData::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("get_navigation_polygon"), &TileData::_get_navigation_polygon_bind_compat_84660); + ClassDB::bind_compatibility_method(D_METHOD("get_occluder"), &TileData::_get_occluder_bind_compat_84660); +} + +#endif diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 422cd4fa2c06..2de7cce5c7b0 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "tile_set.h" +#include "tile_set.compat.inc" #include "core/io/marshalls.h" #include "core/math/geometry_2d.h" @@ -5104,7 +5105,7 @@ void TileData::add_occlusion_layer(int p_to_pos) { p_to_pos = occluders.size(); } ERR_FAIL_INDEX(p_to_pos, occluders.size() + 1); - occluders.insert(p_to_pos, Ref()); + occluders.insert(p_to_pos, OcclusionLayerTileData()); } void TileData::move_occlusion_layer(int p_from_index, int p_to_pos) { @@ -5219,7 +5220,7 @@ void TileData::add_navigation_layer(int p_to_pos) { p_to_pos = navigation.size(); } ERR_FAIL_INDEX(p_to_pos, navigation.size() + 1); - navigation.insert(p_to_pos, Ref()); + navigation.insert(p_to_pos, NavigationLayerTileData()); } void TileData::move_navigation_layer(int p_from_index, int p_to_pos) { @@ -5365,13 +5366,35 @@ int TileData::get_y_sort_origin() const { void TileData::set_occluder(int p_layer_id, Ref p_occluder_polygon) { ERR_FAIL_INDEX(p_layer_id, occluders.size()); - occluders.write[p_layer_id] = p_occluder_polygon; + occluders.write[p_layer_id].occluder = p_occluder_polygon; + occluders.write[p_layer_id].transformed_occluders.clear(); emit_signal(SNAME("changed")); } -Ref TileData::get_occluder(int p_layer_id) const { +Ref TileData::get_occluder(int p_layer_id, bool p_flip_h, bool p_flip_v, bool p_transpose) const { ERR_FAIL_INDEX_V(p_layer_id, occluders.size(), Ref()); - return occluders[p_layer_id]; + + const OcclusionLayerTileData &layer_tile_data = occluders[p_layer_id]; + + int key = int(p_flip_h) | int(p_flip_v) << 1 | int(p_transpose) << 2; + if (key == 0) { + return layer_tile_data.occluder; + } + + if (layer_tile_data.occluder.is_null()) { + return Ref(); + } + + HashMap>::Iterator I = layer_tile_data.transformed_occluders.find(key); + if (!I) { + Ref transformed_polygon; + transformed_polygon.instantiate(); + transformed_polygon->set_polygon(get_transformed_vertices(layer_tile_data.occluder->get_polygon(), p_flip_h, p_flip_v, p_transpose)); + layer_tile_data.transformed_occluders[key] = transformed_polygon; + return transformed_polygon; + } else { + return I->value; + } } // Physics @@ -5431,22 +5454,25 @@ void TileData::set_collision_polygon_points(int p_layer_id, int p_polygon_index, ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); ERR_FAIL_COND_MSG(p_polygon.size() != 0 && p_polygon.size() < 3, "Invalid polygon. Needs either 0 or more than 3 points."); + TileData::PhysicsLayerTileData::PolygonShapeTileData &polygon_shape_tile_data = physics.write[p_layer_id].polygons.write[p_polygon_index]; + if (p_polygon.is_empty()) { - physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.clear(); + polygon_shape_tile_data.shapes.clear(); } else { // Decompose into convex shapes. Vector> decomp = Geometry2D::decompose_polygon_in_convex(p_polygon); ERR_FAIL_COND_MSG(decomp.is_empty(), "Could not decompose the polygon into convex shapes."); - physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.resize(decomp.size()); + polygon_shape_tile_data.shapes.resize(decomp.size()); for (int i = 0; i < decomp.size(); i++) { Ref shape; shape.instantiate(); shape->set_points(decomp[i]); - physics.write[p_layer_id].polygons.write[p_polygon_index].shapes[i] = shape; + polygon_shape_tile_data.shapes[i] = shape; } } - physics.write[p_layer_id].polygons.write[p_polygon_index].polygon = p_polygon; + polygon_shape_tile_data.transformed_shapes.clear(); + polygon_shape_tile_data.polygon = p_polygon; emit_signal(SNAME("changed")); } @@ -5488,11 +5514,36 @@ int TileData::get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_i return physics[p_layer_id].polygons[p_polygon_index].shapes.size(); } -Ref TileData::get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const { +Ref TileData::get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index, bool p_flip_h, bool p_flip_v, bool p_transpose) const { ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Ref()); ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Ref()); ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[p_polygon_index].shapes.size(), Ref()); - return physics[p_layer_id].polygons[p_polygon_index].shapes[shape_index]; + + const PhysicsLayerTileData &layer_tile_data = physics[p_layer_id]; + const PhysicsLayerTileData::PolygonShapeTileData &shapes_data = layer_tile_data.polygons[p_polygon_index]; + + int key = int(p_flip_h) | int(p_flip_v) << 1 | int(p_transpose) << 2; + if (key == 0) { + return shapes_data.shapes[shape_index]; + } + if (shapes_data.shapes[shape_index].is_null()) { + return Ref(); + } + + HashMap>>::Iterator I = shapes_data.transformed_shapes.find(key); + if (!I) { + int size = shapes_data.shapes.size(); + shapes_data.transformed_shapes[key].resize(size); + for (int i = 0; i < size; i++) { + Ref transformed_polygon; + transformed_polygon.instantiate(); + transformed_polygon->set_points(get_transformed_vertices(shapes_data.shapes[shape_index]->get_points(), p_flip_h, p_flip_v, p_transpose)); + shapes_data.transformed_shapes[key][i] = transformed_polygon; + } + return shapes_data.transformed_shapes[key][shape_index]; + } else { + return I->value[shape_index]; + } } // Terrain @@ -5570,13 +5621,50 @@ TileSet::TerrainsPattern TileData::get_terrains_pattern() const { // Navigation void TileData::set_navigation_polygon(int p_layer_id, Ref p_navigation_polygon) { ERR_FAIL_INDEX(p_layer_id, navigation.size()); - navigation.write[p_layer_id] = p_navigation_polygon; + navigation.write[p_layer_id].navigation_polygon = p_navigation_polygon; + navigation.write[p_layer_id].transformed_navigation_polygon.clear(); emit_signal(SNAME("changed")); } -Ref TileData::get_navigation_polygon(int p_layer_id) const { +Ref TileData::get_navigation_polygon(int p_layer_id, bool p_flip_h, bool p_flip_v, bool p_transpose) const { ERR_FAIL_INDEX_V(p_layer_id, navigation.size(), Ref()); - return navigation[p_layer_id]; + + const NavigationLayerTileData &layer_tile_data = navigation[p_layer_id]; + + int key = int(p_flip_h) | int(p_flip_v) << 1 | int(p_transpose) << 2; + if (key == 0) { + return layer_tile_data.navigation_polygon; + } + + if (layer_tile_data.navigation_polygon.is_null()) { + return Ref(); + } + + HashMap>::Iterator I = layer_tile_data.transformed_navigation_polygon.find(key); + if (!I) { + Ref transformed_polygon; + transformed_polygon.instantiate(); + + PackedVector2Array new_points = get_transformed_vertices(layer_tile_data.navigation_polygon->get_vertices(), p_flip_h, p_flip_v, p_transpose); + transformed_polygon->set_vertices(new_points); + + for (int i = 0; i < layer_tile_data.navigation_polygon->get_outline_count(); i++) { + PackedVector2Array new_outline = get_transformed_vertices(layer_tile_data.navigation_polygon->get_outline(i), p_flip_h, p_flip_v, p_transpose); + transformed_polygon->add_outline(new_outline); + } + + PackedInt32Array indices; + indices.resize(new_points.size()); + int *w = indices.ptrw(); + for (int i = 0; i < new_points.size(); i++) { + w[i] = i; + } + transformed_polygon->add_polygon(indices); + layer_tile_data.transformed_navigation_polygon[key] = transformed_polygon; + return transformed_polygon; + } else { + return I->value; + } } // Misc @@ -5615,6 +5703,33 @@ Variant TileData::get_custom_data_by_layer_id(int p_layer_id) const { return custom_data[p_layer_id]; } +PackedVector2Array TileData::get_transformed_vertices(const PackedVector2Array &p_vertices, bool p_flip_h, bool p_flip_v, bool p_transpose) { + const Vector2 *r = p_vertices.ptr(); + int size = p_vertices.size(); + + PackedVector2Array new_points; + new_points.resize(size); + Vector2 *w = new_points.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 new_points; +} + bool TileData::_set(const StringName &p_name, const Variant &p_value) { #ifndef DISABLE_DEPRECATED if (p_name == "texture_offset") { @@ -5845,7 +5960,7 @@ void TileData::_get_property_list(List *p_list) const { for (int i = 0; i < occluders.size(); i++) { // occlusion_layer_%d/polygon property_info = PropertyInfo(Variant::OBJECT, vformat("occlusion_layer_%d/%s", i, PNAME("polygon")), PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_DEFAULT); - if (!occluders[i].is_valid()) { + if (occluders[i].occluder.is_null()) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } p_list->push_back(property_info); @@ -5901,7 +6016,7 @@ void TileData::_get_property_list(List *p_list) const { p_list->push_back(PropertyInfo(Variant::NIL, GNAME("Navigation", ""), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (int i = 0; i < navigation.size(); i++) { property_info = PropertyInfo(Variant::OBJECT, vformat("navigation_layer_%d/%s", i, PNAME("polygon")), PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon", PROPERTY_USAGE_DEFAULT); - if (!navigation[i].is_valid()) { + if (navigation[i].navigation_polygon.is_null()) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } p_list->push_back(property_info); @@ -5942,7 +6057,7 @@ void TileData::_bind_methods() { ClassDB::bind_method(D_METHOD("get_y_sort_origin"), &TileData::get_y_sort_origin); ClassDB::bind_method(D_METHOD("set_occluder", "layer_id", "occluder_polygon"), &TileData::set_occluder); - ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder); + ClassDB::bind_method(D_METHOD("get_occluder", "layer_id", "flip_h", "flip_v", "transpose"), &TileData::get_occluder, DEFVAL(false), DEFVAL(false), DEFVAL(false)); // Physics. ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "layer_id", "velocity"), &TileData::set_constant_linear_velocity); @@ -5970,7 +6085,7 @@ void TileData::_bind_methods() { // Navigation ClassDB::bind_method(D_METHOD("set_navigation_polygon", "layer_id", "navigation_polygon"), &TileData::set_navigation_polygon); - ClassDB::bind_method(D_METHOD("get_navigation_polygon", "layer_id"), &TileData::get_navigation_polygon); + ClassDB::bind_method(D_METHOD("get_navigation_polygon", "layer_id", "flip_h", "flip_v", "transpose"), &TileData::get_navigation_polygon, DEFVAL(false), DEFVAL(false), DEFVAL(false)); // Misc. ClassDB::bind_method(D_METHOD("set_probability", "probability"), &TileData::set_probability); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 313c4df65de3..a71982cd566c 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -815,13 +815,18 @@ private: Color modulate = Color(1.0, 1.0, 1.0, 1.0); int z_index = 0; int y_sort_origin = 0; - Vector> occluders; + struct OcclusionLayerTileData { + Ref occluder; + mutable HashMap> transformed_occluders; + }; + Vector occluders; // Physics struct PhysicsLayerTileData { struct PolygonShapeTileData { LocalVector polygon; LocalVector> shapes; + mutable HashMap>> transformed_shapes; bool one_way = false; float one_way_margin = 1.0; }; @@ -839,7 +844,11 @@ private: int terrain_peering_bits[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; // Navigation - Vector> navigation; + struct NavigationLayerTileData { + Ref navigation_polygon; + mutable HashMap> transformed_navigation_polygon; + }; + Vector navigation; // Misc double probability = 1.0; @@ -853,6 +862,13 @@ protected: void _get_property_list(List *p_list) const; static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + Ref _get_navigation_polygon_bind_compat_84660(int p_layer_id) const; + Ref _get_occluder_bind_compat_84660(int p_layer_id) const; + + static void _bind_compatibility_methods(); +#endif + public: // Not exposed. void set_tile_set(const TileSet *p_tile_set); @@ -901,7 +917,7 @@ public: int get_y_sort_origin() const; void set_occluder(int p_layer_id, Ref p_occluder_polygon); - Ref get_occluder(int p_layer_id) const; + Ref get_occluder(int p_layer_id, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const; // Physics void set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity); @@ -919,7 +935,7 @@ public: void set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin); float get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const; int get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_index) const; - Ref get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const; + Ref get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const; // Terrain void set_terrain_set(int p_terrain_id); @@ -934,7 +950,7 @@ public: // Navigation void set_navigation_polygon(int p_layer_id, Ref p_navigation_polygon); - Ref get_navigation_polygon(int p_layer_id) const; + Ref get_navigation_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); @@ -945,6 +961,9 @@ public: Variant get_custom_data(String p_layer_name) const; void set_custom_data_by_layer_id(int p_layer_id, Variant p_value); Variant get_custom_data_by_layer_id(int p_layer_id) const; + + // Polygons. + static PackedVector2Array get_transformed_vertices(const PackedVector2Array &p_vertices, bool p_flip_h, bool p_flip_v, bool p_transpose); }; VARIANT_ENUM_CAST(TileSet::CellNeighbor);