summaryrefslogtreecommitdiffstats
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/navigation_region_2d.cpp35
-rw-r--r--scene/2d/navigation_region_2d.h3
-rw-r--r--scene/2d/tile_map.cpp154
-rw-r--r--scene/2d/tile_map.h16
-rw-r--r--scene/3d/bone_attachment_3d.cpp2
-rw-r--r--scene/3d/lightmap_gi.cpp2
-rw-r--r--scene/gui/code_edit.cpp65
-rw-r--r--scene/gui/code_edit.h3
-rw-r--r--scene/main/scene_tree.cpp35
-rw-r--r--scene/register_scene_types.cpp2
-rw-r--r--scene/resources/navigation_mesh_source_geometry_data_2d.cpp138
-rw-r--r--scene/resources/navigation_mesh_source_geometry_data_2d.h78
-rw-r--r--scene/resources/navigation_polygon.cpp132
-rw-r--r--scene/resources/navigation_polygon.h51
14 files changed, 642 insertions, 74 deletions
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index 670a2c641c..706b26bd05 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -179,10 +179,6 @@ void NavigationRegion2D::_notification(int p_what) {
}
void NavigationRegion2D::set_navigation_polygon(const Ref<NavigationPolygon> &p_navigation_polygon) {
- if (p_navigation_polygon == navigation_polygon) {
- return;
- }
-
if (navigation_polygon.is_valid()) {
navigation_polygon->disconnect_changed(callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed));
}
@@ -226,6 +222,32 @@ RID NavigationRegion2D::get_navigation_map() const {
return RID();
}
+void NavigationRegion2D::bake_navigation_polygon(bool p_on_thread) {
+ ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
+ ERR_FAIL_COND_MSG(!navigation_polygon.is_valid(), "Baking the navigation polygon requires a valid `NavigationPolygon` resource.");
+
+ Ref<NavigationMeshSourceGeometryData2D> source_geometry_data;
+ source_geometry_data.instantiate();
+
+ NavigationServer2D::get_singleton()->parse_source_geometry_data(navigation_polygon, source_geometry_data, this);
+
+ if (p_on_thread) {
+ NavigationServer2D::get_singleton()->bake_from_source_geometry_data_async(navigation_polygon, source_geometry_data, callable_mp(this, &NavigationRegion2D::_bake_finished).bind(navigation_polygon));
+ } else {
+ NavigationServer2D::get_singleton()->bake_from_source_geometry_data(navigation_polygon, source_geometry_data, callable_mp(this, &NavigationRegion2D::_bake_finished).bind(navigation_polygon));
+ }
+}
+
+void NavigationRegion2D::_bake_finished(Ref<NavigationPolygon> p_navigation_polygon) {
+ if (!Thread::is_main_thread()) {
+ call_deferred(SNAME("_bake_finished"), p_navigation_polygon);
+ return;
+ }
+
+ set_navigation_polygon(p_navigation_polygon);
+ emit_signal(SNAME("bake_finished"));
+}
+
void NavigationRegion2D::_navigation_polygon_changed() {
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) {
queue_redraw();
@@ -290,6 +312,8 @@ void NavigationRegion2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationRegion2D::set_travel_cost);
ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationRegion2D::get_travel_cost);
+ ClassDB::bind_method(D_METHOD("bake_navigation_polygon", "on_thread"), &NavigationRegion2D::bake_navigation_polygon, DEFVAL(true));
+
ClassDB::bind_method(D_METHOD("_navigation_polygon_changed"), &NavigationRegion2D::_navigation_polygon_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_polygon", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon");
@@ -300,6 +324,9 @@ void NavigationRegion2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constrain_avoidance"), "set_constrain_avoidance", "get_constrain_avoidance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
+
+ ADD_SIGNAL(MethodInfo("navigation_polygon_changed"));
+ ADD_SIGNAL(MethodInfo("bake_finished"));
}
#ifndef DISABLE_DEPRECATED
diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h
index 0a48b10f47..36e889877a 100644
--- a/scene/2d/navigation_region_2d.h
+++ b/scene/2d/navigation_region_2d.h
@@ -114,6 +114,9 @@ public:
PackedStringArray get_configuration_warnings() const override;
+ void bake_navigation_polygon(bool p_on_thread);
+ void _bake_finished(Ref<NavigationPolygon> p_navigation_polygon);
+
NavigationRegion2D();
~NavigationRegion2D();
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 0c1aab6198..cbcd136438 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -178,15 +178,6 @@ void TileMapLayer::_debug_quadrants_update_cell(CellData &r_cell_data, SelfList<
#endif // DEBUG_ENABLED
/////////////////////////////// Rendering //////////////////////////////////////
-Vector2i TileMapLayer::_coords_to_rendering_quadrant_coords(const Vector2i &p_coords) const {
- int quad_size = get_effective_quadrant_size();
-
- // Rounding down, instead of simply rounding towards zero (truncating).
- return Vector2i(
- p_coords.x > 0 ? p_coords.x / quad_size : (p_coords.x - (quad_size - 1)) / quad_size,
- p_coords.y > 0 ? p_coords.y / quad_size : (p_coords.y - (quad_size - 1)) / quad_size);
-}
-
void TileMapLayer::_rendering_update() {
const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
RenderingServer *rs = RenderingServer::get_singleton();
@@ -239,8 +230,11 @@ void TileMapLayer::_rendering_update() {
// Check if anything changed that might change the quadrant shape.
// If so, recreate everything.
- if (forced_cleanup || dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_TILE_MAP_QUADRANT_SIZE]) {
- // Free all quadrants.
+ bool quandrant_shape_changed = dirty.flags[DIRTY_FLAGS_TILE_MAP_QUADRANT_SIZE] ||
+ (tile_map_node->is_y_sort_enabled() && y_sort_enabled && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_TILE_MAP_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_TILE_MAP_LOCAL_XFORM] || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]));
+
+ // Free all quadrants.
+ if (forced_cleanup || quandrant_shape_changed) {
for (const KeyValue<Vector2i, Ref<RenderingQuadrant>> &kv : rendering_quadrant_map) {
for (int i = 0; i < kv.value->canvas_items.size(); i++) {
const RID &ci = kv.value->canvas_items[i];
@@ -295,6 +289,14 @@ void TileMapLayer::_rendering_update() {
}
rendering_quadrant->canvas_items.clear();
+ // Sort the quadrant cells.
+ if (tile_map_node->is_y_sort_enabled() && is_y_sort_enabled()) {
+ // For compatibility reasons, we use another comparator for Y-sorted layers.
+ rendering_quadrant->cells.sort_custom<CellDataYSortedComparator>();
+ } else {
+ rendering_quadrant->cells.sort();
+ }
+
// Those allow to group cell per material or z-index.
Ref<Material> prev_material;
int prev_z_index = 0;
@@ -317,12 +319,6 @@ void TileMapLayer::_rendering_update() {
int tile_z_index = tile_data->get_z_index();
// Quandrant pos.
- Vector2i quadrant_coords = _coords_to_rendering_quadrant_coords(cell_data.coords);
- Vector2 ci_position = tile_map_node->map_to_local(quadrant_coords * get_effective_quadrant_size());
- if (tile_map_node->is_y_sort_enabled() && y_sort_enabled) {
- // When Y-sorting, the quandrant size is sure to be 1, we can thus offset the CanvasItem.
- ci_position.y += y_sort_origin + tile_data->get_y_sort_origin();
- }
// --- CanvasItems ---
RID ci;
@@ -337,7 +333,7 @@ void TileMapLayer::_rendering_update() {
rs->canvas_item_set_parent(ci, canvas_item);
rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid());
- Transform2D xform(0, ci_position);
+ Transform2D xform(0, rendering_quadrant->canvas_items_position);
rs->canvas_item_set_transform(ci, xform);
rs->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask());
@@ -370,7 +366,7 @@ void TileMapLayer::_rendering_update() {
}
// Drawing the tile in the canvas item.
- tile_map_node->draw_tile(ci, local_tile_pos - ci_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, tile_map_node->get_self_modulate(), tile_data, random_animation_offset);
+ tile_map_node->draw_tile(ci, local_tile_pos - rendering_quadrant->canvas_items_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, tile_map_node->get_self_modulate(), tile_data, random_animation_offset);
}
} else {
// Free the quadrant.
@@ -465,48 +461,85 @@ void TileMapLayer::_rendering_update() {
void TileMapLayer::_rendering_quadrants_update_cell(CellData &r_cell_data, SelfList<RenderingQuadrant>::List &r_dirty_rendering_quadrant_list) {
const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
- Vector2i quadrant_coords = _coords_to_rendering_quadrant_coords(r_cell_data.coords);
-
- if (rendering_quadrant_map.has(quadrant_coords)) {
- // Mark the quadrant as dirty.
- Ref<RenderingQuadrant> &rendering_quadrant = rendering_quadrant_map[quadrant_coords];
- if (!rendering_quadrant->dirty_quadrant_list_element.in_list()) {
- r_dirty_rendering_quadrant_list.add(&rendering_quadrant->dirty_quadrant_list_element);
- }
- } else {
- // Create a new quadrant and add it to the quadrant lists.
- Ref<RenderingQuadrant> new_quadrant;
- new_quadrant.instantiate();
- new_quadrant->quadrant_coords = quadrant_coords;
- rendering_quadrant_map[quadrant_coords] = new_quadrant;
- }
- // Check if the cell is valid.
+ // Check if the cell is valid and retrieve its y_sort_origin.
bool is_valid = false;
+ int tile_y_sort_origin = 0;
TileSetSource *source;
if (tile_set->has_source(r_cell_data.cell.source_id)) {
source = *tile_set->get_source(r_cell_data.cell.source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source && atlas_source->has_tile(r_cell_data.cell.get_atlas_coords()) && atlas_source->has_alternative_tile(r_cell_data.cell.get_atlas_coords(), r_cell_data.cell.alternative_tile)) {
is_valid = true;
+ tile_y_sort_origin = atlas_source->get_tile_data(r_cell_data.cell.get_atlas_coords(), r_cell_data.cell.alternative_tile)->get_y_sort_origin();
}
}
- // Add/Remove the cell to/from its quadrant.
- Ref<RenderingQuadrant> &rendering_quadrant = rendering_quadrant_map[quadrant_coords];
- if (r_cell_data.rendering_quadrant_list_element.in_list()) {
- if (!is_valid) {
+ if (is_valid) {
+ // Get the quadrant coords.
+ Vector2 canvas_items_position;
+ Vector2i quadrant_coords;
+ if (tile_map_node->is_y_sort_enabled() && is_y_sort_enabled()) {
+ canvas_items_position = Vector2(0, tile_map_node->map_to_local(r_cell_data.coords).y + tile_y_sort_origin + y_sort_origin);
+ quadrant_coords = canvas_items_position * 100;
+ } else {
+ int quad_size = tile_map_node->get_rendering_quadrant_size();
+ const Vector2i &coords = r_cell_data.coords;
+
+ // Rounding down, instead of simply rounding towards zero (truncating).
+ quadrant_coords = Vector2i(
+ coords.x > 0 ? coords.x / quad_size : (coords.x - (quad_size - 1)) / quad_size,
+ coords.y > 0 ? coords.y / quad_size : (coords.y - (quad_size - 1)) / quad_size);
+ canvas_items_position = quad_size * quadrant_coords;
+ }
+
+ Ref<RenderingQuadrant> rendering_quadrant;
+ if (rendering_quadrant_map.has(quadrant_coords)) {
+ // Reuse existing rendering quadrant.
+ rendering_quadrant = rendering_quadrant_map[quadrant_coords];
+ } else {
+ // Create a new rendering quadrant.
+ rendering_quadrant.instantiate();
+ rendering_quadrant->quadrant_coords = quadrant_coords;
+ rendering_quadrant->canvas_items_position = canvas_items_position;
+ rendering_quadrant_map[quadrant_coords] = rendering_quadrant;
+ }
+
+ // Mark the old quadrant as dirty (if it exists).
+ if (r_cell_data.rendering_quadrant.is_valid()) {
+ if (!r_cell_data.rendering_quadrant->dirty_quadrant_list_element.in_list()) {
+ r_dirty_rendering_quadrant_list.add(&r_cell_data.rendering_quadrant->dirty_quadrant_list_element);
+ }
+ }
+
+ // Remove the cell from that quadrant.
+ if (r_cell_data.rendering_quadrant_list_element.in_list()) {
r_cell_data.rendering_quadrant_list_element.remove_from_list();
}
+
+ // Add the cell to its new quadrant.
+ r_cell_data.rendering_quadrant = rendering_quadrant;
+ r_cell_data.rendering_quadrant->cells.add(&r_cell_data.rendering_quadrant_list_element);
+
+ // Add the new quadrant to the dirty quadrant list.
+ if (!rendering_quadrant->dirty_quadrant_list_element.in_list()) {
+ r_dirty_rendering_quadrant_list.add(&rendering_quadrant->dirty_quadrant_list_element);
+ }
} else {
- if (is_valid) {
- rendering_quadrant->cells.add(&r_cell_data.rendering_quadrant_list_element);
+ Ref<RenderingQuadrant> rendering_quadrant = r_cell_data.rendering_quadrant;
+
+ // Remove the cell from its quadrant.
+ r_cell_data.rendering_quadrant = Ref<RenderingQuadrant>();
+ if (r_cell_data.rendering_quadrant_list_element.in_list()) {
+ rendering_quadrant->cells.remove(&r_cell_data.rendering_quadrant_list_element);
}
- }
- // Add the quadrant to the dirty quadrant list.
- if (!rendering_quadrant->dirty_quadrant_list_element.in_list()) {
- r_dirty_rendering_quadrant_list.add(&rendering_quadrant->dirty_quadrant_list_element);
+ if (rendering_quadrant.is_valid()) {
+ // Add the quadrant to the dirty quadrant list.
+ if (!rendering_quadrant->dirty_quadrant_list_element.in_list()) {
+ r_dirty_rendering_quadrant_list.add(&rendering_quadrant->dirty_quadrant_list_element);
+ }
+ }
}
}
@@ -1831,15 +1864,6 @@ TileMapCell TileMapLayer::get_cell(const Vector2i &p_coords, bool p_use_proxies)
}
}
-int TileMapLayer::get_effective_quadrant_size() const {
- // When using YSort, the quadrant size is reduced to 1 to have one CanvasItem per quadrant.
- if (tile_map_node->is_y_sort_enabled() && is_y_sort_enabled()) {
- return 1;
- } else {
- return tile_map_node->get_rendering_quadrant_size();
- }
-}
-
void TileMapLayer::set_tile_data(TileMapLayer::DataFormat p_format, const Vector<int> &p_data) {
ERR_FAIL_COND(p_format > TileMapLayer::FORMAT_3);
@@ -2949,7 +2973,7 @@ void TileMap::_notification(int p_what) {
last_valid_transform = new_transform;
set_notify_local_transform(false);
set_global_transform(new_transform);
- set_notify_local_transform(true);
+ _update_notify_local_transform();
}
} break;
@@ -2979,7 +3003,7 @@ void TileMap::_notification(int p_what) {
// ... but then revert changes.
set_notify_local_transform(false);
set_global_transform(last_valid_transform);
- set_notify_local_transform(true);
+ _update_notify_local_transform();
}
} break;
}
@@ -3233,6 +3257,7 @@ Color TileMap::get_layer_modulate(int p_layer) const {
void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) {
TILEMAP_CALL_FOR_LAYER(p_layer, set_y_sort_enabled, p_y_sort_enabled);
+ _update_notify_local_transform();
}
bool TileMap::is_layer_y_sort_enabled(int p_layer) const {
@@ -3268,7 +3293,7 @@ void TileMap::set_collision_animatable(bool p_enabled) {
return;
}
collision_animatable = p_enabled;
- set_notify_local_transform(p_enabled);
+ _update_notify_local_transform();
set_physics_process_internal(p_enabled);
for (Ref<TileMapLayer> &layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_COLLISION_ANIMATABLE);
@@ -4622,9 +4647,22 @@ void TileMap::_tile_set_changed() {
update_configuration_warnings();
}
+void TileMap::_update_notify_local_transform() {
+ bool notify = collision_animatable || is_y_sort_enabled();
+ if (!notify) {
+ for (const Ref<TileMapLayer> &layer : layers) {
+ if (layer->is_y_sort_enabled()) {
+ notify = true;
+ break;
+ }
+ }
+ }
+ set_notify_local_transform(notify);
+}
+
TileMap::TileMap() {
set_notify_transform(true);
- set_notify_local_transform(false);
+ _update_notify_local_transform();
Ref<TileMapLayer> new_layer;
new_layer.instantiate();
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index f804f808cb..f98fd77a5c 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -121,6 +121,10 @@ struct CellData {
// List elements.
SelfList<CellData> dirty_list_element;
+ bool operator<(const CellData &p_other) const {
+ return coords < p_other.coords;
+ }
+
// For those, copy everything but SelfList elements.
void operator=(const CellData &p_other) {
coords = p_other.coords;
@@ -152,6 +156,13 @@ struct CellData {
}
};
+// For compatibility reasons, we use another comparator for Y-sorted layers.
+struct CellDataYSortedComparator {
+ _FORCE_INLINE_ bool operator()(const CellData &p_a, const CellData &p_b) const {
+ return p_a.coords.x == p_b.coords.x ? (p_a.coords.y < p_b.coords.y) : (p_a.coords.x > p_b.coords.x);
+ }
+};
+
#ifdef DEBUG_ENABLED
class DebugQuadrant : public RefCounted {
GDCLASS(DebugQuadrant, RefCounted);
@@ -199,6 +210,7 @@ public:
Vector2i quadrant_coords;
SelfList<CellData>::List cells;
List<RID> canvas_items;
+ Vector2 canvas_items_position;
SelfList<RenderingQuadrant> dirty_quadrant_list_element;
@@ -304,7 +316,6 @@ private:
#endif // DEBUG_ENABLED
HashMap<Vector2i, Ref<RenderingQuadrant>> rendering_quadrant_map;
- Vector2i _coords_to_rendering_quadrant_coords(const Vector2i &p_coords) const;
bool _rendering_was_cleaned_up = false;
void _rendering_update();
void _rendering_quadrants_update_cell(CellData &r_cell_data, SelfList<RenderingQuadrant>::List &r_dirty_rendering_quadrant_list);
@@ -361,7 +372,6 @@ public:
// Not exposed to users.
TileMapCell get_cell(const Vector2i &p_coords, bool p_use_proxies = false) const;
- int get_effective_quadrant_size() const;
// For TileMap node's use.
void set_tile_data(DataFormat p_format, const Vector<int> &p_data);
@@ -455,6 +465,8 @@ private:
void _tile_set_changed();
+ void _update_notify_local_transform();
+
// Polygons.
HashMap<Pair<Ref<Resource>, int>, Ref<Resource>, PairHash<Ref<Resource>, int>> polygon_cache;
PackedVector2Array _get_transformed_vertices(const PackedVector2Array &p_vertices, int p_alternative_id);
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index 261bd401d4..3b30bbf8b6 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -148,7 +148,7 @@ void BoneAttachment3D::_check_bind() {
bone_idx = sk->find_bone(bone_name);
}
if (bone_idx != -1) {
- sk->call_deferred(SNAME("connect"), "bone_pose_changed", callable_mp(this, &BoneAttachment3D::on_bone_pose_update));
+ sk->connect(SNAME("bone_pose_changed"), callable_mp(this, &BoneAttachment3D::on_bone_pose_update));
bound = true;
call_deferred(SNAME("on_bone_pose_update"), bone_idx);
}
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index 5dd1e6954d..3d40e9795e 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -372,7 +372,7 @@ void LightmapGI::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &m
Node3D *s = Object::cast_to<Node3D>(p_at_node);
if (!mi && s) {
- Array bmeshes = p_at_node->call("get_bake_bmeshes");
+ Array bmeshes = p_at_node->call("get_bake_meshes");
if (bmeshes.size() && (bmeshes.size() & 1) == 0) {
Transform3D xf = get_global_transform().affine_inverse() * s->get_global_transform();
for (int i = 0; i < bmeshes.size(); i += 2) {
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index d35d35d36d..e7a2a26a29 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -2399,6 +2399,68 @@ void CodeEdit::set_symbol_lookup_word_as_valid(bool p_valid) {
}
}
+/* Text manipulation */
+void CodeEdit::duplicate_lines() {
+ begin_complex_operation();
+
+ Vector<int> caret_edit_order = get_caret_index_edit_order();
+ for (const int &caret_index : caret_edit_order) {
+ // The text that will be inserted. All lines in one string.
+ String insert_text;
+
+ // The new line position of the caret after the operation.
+ int new_caret_line = get_caret_line(caret_index);
+ // The new column position of the caret after the operation.
+ int new_caret_column = get_caret_column(caret_index);
+ // The caret positions of the selection. Stays -1 if there is no selection.
+ int select_from_line = -1;
+ int select_to_line = -1;
+ int select_from_column = -1;
+ int select_to_column = -1;
+ // Number of lines of the selection.
+ int select_num_lines = -1;
+
+ if (has_selection(caret_index)) {
+ select_from_line = get_selection_from_line(caret_index);
+ select_to_line = get_selection_to_line(caret_index);
+ select_from_column = get_selection_from_column(caret_index);
+ select_to_column = get_selection_to_column(caret_index);
+ select_num_lines = select_to_line - select_from_line + 1;
+
+ for (int i = select_from_line; i <= select_to_line; i++) {
+ insert_text += "\n" + get_line(i);
+ unfold_line(i);
+ }
+ new_caret_line = select_to_line + select_num_lines;
+ } else {
+ insert_text = "\n" + get_line(new_caret_line);
+ new_caret_line++;
+
+ unfold_line(get_caret_line(caret_index));
+ }
+
+ // The text will be inserted at the end of the current line.
+ set_caret_column(get_line(get_caret_line(caret_index)).length(), false, caret_index);
+
+ deselect(caret_index);
+
+ insert_text_at_caret(insert_text, caret_index);
+ set_caret_line(new_caret_line, false, true, 0, caret_index);
+ set_caret_column(new_caret_column, true, caret_index);
+
+ if (select_from_line != -1) {
+ // Advance the selection by the number of duplicated lines.
+ select_from_line += select_num_lines;
+ select_to_line += select_num_lines;
+
+ select(select_from_line, select_from_column, select_to_line, select_to_column, caret_index);
+ }
+ }
+
+ end_complex_operation();
+ queue_redraw();
+}
+
/* Visual */
Color CodeEdit::_get_brace_mismatch_color() const {
return theme_cache.brace_mismatch_color;
@@ -2598,6 +2660,9 @@ void CodeEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_symbol_lookup_word_as_valid", "valid"), &CodeEdit::set_symbol_lookup_word_as_valid);
+ /* Text manipulation */
+ ClassDB::bind_method(D_METHOD("duplicate_lines"), &CodeEdit::duplicate_lines);
+
/* Inspector */
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "symbol_lookup_on_click"), "set_symbol_lookup_on_click_enabled", "is_symbol_lookup_on_click_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "line_folding"), "set_line_folding_enabled", "is_line_folding_enabled");
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index e688af2bda..97c435b52d 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -486,6 +486,9 @@ public:
void set_symbol_lookup_word_as_valid(bool p_valid);
+ /* Text manipulation */
+ void duplicate_lines();
+
CodeEdit();
~CodeEdit();
};
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index ba11084d3b..d357e35c1d 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -1483,15 +1483,18 @@ TypedArray<Tween> SceneTree::get_processed_tweens() {
Ref<MultiplayerAPI> SceneTree::get_multiplayer(const NodePath &p_for_path) const {
ERR_FAIL_COND_V_MSG(!Thread::is_main_thread(), Ref<MultiplayerAPI>(), "Multiplayer can only be manipulated from the main thread.");
- Ref<MultiplayerAPI> out = multiplayer;
+ if (p_for_path.is_empty()) {
+ return multiplayer;
+ }
+
+ const Vector<StringName> tnames = p_for_path.get_names();
+ const StringName *nptr = tnames.ptr();
for (const KeyValue<NodePath, Ref<MultiplayerAPI>> &E : custom_multiplayers) {
const Vector<StringName> snames = E.key.get_names();
- const Vector<StringName> tnames = p_for_path.get_names();
if (tnames.size() < snames.size()) {
continue;
}
const StringName *sptr = snames.ptr();
- const StringName *nptr = tnames.ptr();
bool valid = true;
for (int i = 0; i < snames.size(); i++) {
if (sptr[i] != nptr[i]) {
@@ -1500,11 +1503,11 @@ Ref<MultiplayerAPI> SceneTree::get_multiplayer(const NodePath &p_for_path) const
}
}
if (valid) {
- out = E.value;
- break;
+ return E.value;
}
}
- return out;
+
+ return multiplayer;
}
void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePath &p_root_path) {
@@ -1519,10 +1522,30 @@ void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePat
} else {
if (custom_multiplayers.has(p_root_path)) {
custom_multiplayers[p_root_path]->object_configuration_remove(nullptr, p_root_path);
+ } else if (p_multiplayer.is_valid()) {
+ const Vector<StringName> tnames = p_root_path.get_names();
+ const StringName *nptr = tnames.ptr();
+ for (const KeyValue<NodePath, Ref<MultiplayerAPI>> &E : custom_multiplayers) {
+ const Vector<StringName> snames = E.key.get_names();
+ if (tnames.size() < snames.size()) {
+ continue;
+ }
+ const StringName *sptr = snames.ptr();
+ bool valid = true;
+ for (int i = 0; i < snames.size(); i++) {
+ if (sptr[i] != nptr[i]) {
+ valid = false;
+ break;
+ }
+ }
+ ERR_FAIL_COND_MSG(valid, "Multiplayer is already configured for a parent of this path: '" + p_root_path + "' in '" + E.key + "'.");
+ }
}
if (p_multiplayer.is_valid()) {
custom_multiplayers[p_root_path] = p_multiplayer;
p_multiplayer->object_configuration_add(nullptr, p_root_path);
+ } else {
+ custom_multiplayers.erase(p_root_path);
}
}
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 19c4b62eaf..03085edb86 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -172,6 +172,7 @@
#include "scene/resources/mesh_texture.h"
#include "scene/resources/multimesh.h"
#include "scene/resources/navigation_mesh.h"
+#include "scene/resources/navigation_mesh_source_geometry_data_2d.h"
#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/navigation_polygon.h"
#include "scene/resources/packed_scene.h"
@@ -956,6 +957,7 @@ void register_scene_types() {
GDREGISTER_CLASS(PathFollow2D);
GDREGISTER_CLASS(NavigationMesh);
+ GDREGISTER_CLASS(NavigationMeshSourceGeometryData2D);
GDREGISTER_CLASS(NavigationMeshSourceGeometryData3D);
GDREGISTER_CLASS(NavigationPolygon);
GDREGISTER_CLASS(NavigationRegion2D);
diff --git a/scene/resources/navigation_mesh_source_geometry_data_2d.cpp b/scene/resources/navigation_mesh_source_geometry_data_2d.cpp
new file mode 100644
index 0000000000..3dde6dbff6
--- /dev/null
+++ b/scene/resources/navigation_mesh_source_geometry_data_2d.cpp
@@ -0,0 +1,138 @@
+/**************************************************************************/
+/* navigation_mesh_source_geometry_data_2d.cpp */
+/**************************************************************************/
+/* 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. */
+/**************************************************************************/
+
+#include "navigation_mesh_source_geometry_data_2d.h"
+
+#include "scene/resources/mesh.h"
+
+void NavigationMeshSourceGeometryData2D::clear() {
+ traversable_outlines.clear();
+ obstruction_outlines.clear();
+}
+
+void NavigationMeshSourceGeometryData2D::_set_traversable_outlines(const Vector<Vector<Vector2>> &p_traversable_outlines) {
+ traversable_outlines = p_traversable_outlines;
+}
+
+void NavigationMeshSourceGeometryData2D::_set_obstruction_outlines(const Vector<Vector<Vector2>> &p_obstruction_outlines) {
+ obstruction_outlines = p_obstruction_outlines;
+}
+
+void NavigationMeshSourceGeometryData2D::_add_traversable_outline(const Vector<Vector2> &p_shape_outline) {
+ if (p_shape_outline.size() > 1) {
+ traversable_outlines.push_back(p_shape_outline);
+ }
+}
+
+void NavigationMeshSourceGeometryData2D::_add_obstruction_outline(const Vector<Vector2> &p_shape_outline) {
+ if (p_shape_outline.size() > 1) {
+ obstruction_outlines.push_back(p_shape_outline);
+ }
+}
+
+void NavigationMeshSourceGeometryData2D::set_traversable_outlines(const TypedArray<Vector<Vector2>> &p_traversable_outlines) {
+ traversable_outlines.resize(p_traversable_outlines.size());
+ for (int i = 0; i < p_traversable_outlines.size(); i++) {
+ traversable_outlines.write[i] = p_traversable_outlines[i];
+ }
+}
+
+TypedArray<Vector<Vector2>> NavigationMeshSourceGeometryData2D::get_traversable_outlines() const {
+ TypedArray<Vector<Vector2>> typed_array_traversable_outlines;
+ typed_array_traversable_outlines.resize(traversable_outlines.size());
+ for (int i = 0; i < typed_array_traversable_outlines.size(); i++) {
+ typed_array_traversable_outlines[i] = traversable_outlines[i];
+ }
+
+ return typed_array_traversable_outlines;
+}
+
+void NavigationMeshSourceGeometryData2D::set_obstruction_outlines(const TypedArray<Vector<Vector2>> &p_obstruction_outlines) {
+ obstruction_outlines.resize(p_obstruction_outlines.size());
+ for (int i = 0; i < p_obstruction_outlines.size(); i++) {
+ obstruction_outlines.write[i] = p_obstruction_outlines[i];
+ }
+}
+
+TypedArray<Vector<Vector2>> NavigationMeshSourceGeometryData2D::get_obstruction_outlines() const {
+ TypedArray<Vector<Vector2>> typed_array_obstruction_outlines;
+ typed_array_obstruction_outlines.resize(obstruction_outlines.size());
+ for (int i = 0; i < typed_array_obstruction_outlines.size(); i++) {
+ typed_array_obstruction_outlines[i] = obstruction_outlines[i];
+ }
+
+ return typed_array_obstruction_outlines;
+}
+
+void NavigationMeshSourceGeometryData2D::add_traversable_outline(const PackedVector2Array &p_shape_outline) {
+ if (p_shape_outline.size() > 1) {
+ Vector<Vector2> traversable_outline;
+ traversable_outline.resize(p_shape_outline.size());
+ for (int i = 0; i < p_shape_outline.size(); i++) {
+ traversable_outline.write[i] = p_shape_outline[i];
+ }
+ traversable_outlines.push_back(traversable_outline);
+ }
+}
+
+void NavigationMeshSourceGeometryData2D::add_obstruction_outline(const PackedVector2Array &p_shape_outline) {
+ if (p_shape_outline.size() > 1) {
+ Vector<Vector2> obstruction_outline;
+ obstruction_outline.resize(p_shape_outline.size());
+ for (int i = 0; i < p_shape_outline.size(); i++) {
+ obstruction_outline.write[i] = p_shape_outline[i];
+ }
+ obstruction_outlines.push_back(obstruction_outline);
+ }
+}
+
+void NavigationMeshSourceGeometryData2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("clear"), &NavigationMeshSourceGeometryData2D::clear);
+ ClassDB::bind_method(D_METHOD("has_data"), &NavigationMeshSourceGeometryData2D::has_data);
+
+ ClassDB::bind_method(D_METHOD("set_traversable_outlines", "traversable_outlines"), &NavigationMeshSourceGeometryData2D::set_traversable_outlines);
+ ClassDB::bind_method(D_METHOD("get_traversable_outlines"), &NavigationMeshSourceGeometryData2D::get_traversable_outlines);
+
+ ClassDB::bind_method(D_METHOD("set_obstruction_outlines", "obstruction_outlines"), &NavigationMeshSourceGeometryData2D::set_obstruction_outlines);
+ ClassDB::bind_method(D_METHOD("get_obstruction_outlines"), &NavigationMeshSourceGeometryData2D::get_obstruction_outlines);
+
+ ClassDB::bind_method(D_METHOD("add_traversable_outline", "shape_outline"), &NavigationMeshSourceGeometryData2D::add_traversable_outline);
+ ClassDB::bind_method(D_METHOD("add_obstruction_outline", "shape_outline"), &NavigationMeshSourceGeometryData2D::add_obstruction_outline);
+
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "traversable_outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_traversable_outlines", "get_traversable_outlines");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "obstruction_outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_obstruction_outlines", "get_obstruction_outlines");
+}
+
+NavigationMeshSourceGeometryData2D::NavigationMeshSourceGeometryData2D() {
+}
+
+NavigationMeshSourceGeometryData2D::~NavigationMeshSourceGeometryData2D() {
+ clear();
+}
diff --git a/scene/resources/navigation_mesh_source_geometry_data_2d.h b/scene/resources/navigation_mesh_source_geometry_data_2d.h
new file mode 100644
index 0000000000..f26a4e9a2e
--- /dev/null
+++ b/scene/resources/navigation_mesh_source_geometry_data_2d.h
@@ -0,0 +1,78 @@
+/**************************************************************************/
+/* navigation_mesh_source_geometry_data_2d.h */
+/**************************************************************************/
+/* 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 NAVIGATION_MESH_SOURCE_GEOMETRY_DATA_2D_H
+#define NAVIGATION_MESH_SOURCE_GEOMETRY_DATA_2D_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/navigation_polygon.h"
+
+class NavigationMeshSourceGeometryData2D : public Resource {
+ GDCLASS(NavigationMeshSourceGeometryData2D, Resource);
+
+ Vector<Vector<Vector2>> traversable_outlines;
+ Vector<Vector<Vector2>> obstruction_outlines;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void _set_traversable_outlines(const Vector<Vector<Vector2>> &p_traversable_outlines);
+ const Vector<Vector<Vector2>> &_get_traversable_outlines() const { return traversable_outlines; }
+
+ void _set_obstruction_outlines(const Vector<Vector<Vector2>> &p_obstruction_outlines);
+ const Vector<Vector<Vector2>> &_get_obstruction_outlines() const { return obstruction_outlines; }
+
+ void _add_traversable_outline(const Vector<Vector2> &p_shape_outline);
+ void _add_obstruction_outline(const Vector<Vector2> &p_shape_outline);
+
+ // kept root node transform here on the geometry data
+ // if we add this transform to all exposed functions we need to break comp on all functions later
+ // when navmesh changes from global transform to relative to navregion
+ // but if it stays here we can just remove it and change the internal functions only
+ Transform2D root_node_transform;
+
+ void set_traversable_outlines(const TypedArray<Vector<Vector2>> &p_traversable_outlines);
+ TypedArray<Vector<Vector2>> get_traversable_outlines() const;
+
+ void set_obstruction_outlines(const TypedArray<Vector<Vector2>> &p_obstruction_outlines);
+ TypedArray<Vector<Vector2>> get_obstruction_outlines() const;
+
+ void add_traversable_outline(const PackedVector2Array &p_shape_outline);
+ void add_obstruction_outline(const PackedVector2Array &p_shape_outline);
+
+ bool has_data() { return traversable_outlines.size(); };
+ void clear();
+
+ NavigationMeshSourceGeometryData2D();
+ ~NavigationMeshSourceGeometryData2D();
+};
+
+#endif // NAVIGATION_MESH_SOURCE_GEOMETRY_DATA_2D_H
diff --git a/scene/resources/navigation_polygon.cpp b/scene/resources/navigation_polygon.cpp
index e521bfb2e0..6c0e1343ec 100644
--- a/scene/resources/navigation_polygon.cpp
+++ b/scene/resources/navigation_polygon.cpp
@@ -32,6 +32,7 @@
#include "core/math/geometry_2d.h"
#include "core/os/mutex.h"
+#include "servers/navigation_server_2d.h"
#include "thirdparty/misc/polypartition.h"
@@ -229,7 +230,11 @@ void NavigationPolygon::clear_outlines() {
rect_cache_dirty = true;
}
+#ifndef DISABLE_DEPRECATED
void NavigationPolygon::make_polygons_from_outlines() {
+ WARN_PRINT("Function make_polygons_from_outlines() is deprecated."
+ "\nUse NavigationServer2D.parse_source_geometry_data() and NavigationServer2D.bake_from_source_geometry_data() instead.");
+
{
MutexLock lock(navigation_mesh_generation);
navigation_mesh.unref();
@@ -331,6 +336,7 @@ void NavigationPolygon::make_polygons_from_outlines() {
emit_changed();
}
+#endif // DISABLE_DEPRECATED
void NavigationPolygon::set_cell_size(real_t p_cell_size) {
cell_size = p_cell_size;
@@ -341,6 +347,69 @@ real_t NavigationPolygon::get_cell_size() const {
return cell_size;
}
+void NavigationPolygon::set_parsed_geometry_type(ParsedGeometryType p_geometry_type) {
+ ERR_FAIL_INDEX(p_geometry_type, PARSED_GEOMETRY_MAX);
+ parsed_geometry_type = p_geometry_type;
+ notify_property_list_changed();
+}
+
+NavigationPolygon::ParsedGeometryType NavigationPolygon::get_parsed_geometry_type() const {
+ return parsed_geometry_type;
+}
+
+void NavigationPolygon::set_parsed_collision_mask(uint32_t p_mask) {
+ parsed_collision_mask = p_mask;
+}
+
+uint32_t NavigationPolygon::get_parsed_collision_mask() const {
+ return parsed_collision_mask;
+}
+
+void NavigationPolygon::set_parsed_collision_mask_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
+ uint32_t mask = get_parsed_collision_mask();
+ if (p_value) {
+ mask |= 1 << (p_layer_number - 1);
+ } else {
+ mask &= ~(1 << (p_layer_number - 1));
+ }
+ set_parsed_collision_mask(mask);
+}
+
+bool NavigationPolygon::get_parsed_collision_mask_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
+ return get_parsed_collision_mask() & (1 << (p_layer_number - 1));
+}
+
+void NavigationPolygon::set_source_geometry_mode(SourceGeometryMode p_geometry_mode) {
+ ERR_FAIL_INDEX(p_geometry_mode, SOURCE_GEOMETRY_MAX);
+ source_geometry_mode = p_geometry_mode;
+ notify_property_list_changed();
+}
+
+NavigationPolygon::SourceGeometryMode NavigationPolygon::get_source_geometry_mode() const {
+ return source_geometry_mode;
+}
+
+void NavigationPolygon::set_source_geometry_group_name(StringName p_group_name) {
+ source_geometry_group_name = p_group_name;
+}
+
+StringName NavigationPolygon::get_source_geometry_group_name() const {
+ return source_geometry_group_name;
+}
+
+void NavigationPolygon::set_agent_radius(real_t p_value) {
+ ERR_FAIL_COND(p_value < 0);
+ agent_radius = p_value;
+}
+
+real_t NavigationPolygon::get_agent_radius() const {
+ return agent_radius;
+}
+
void NavigationPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationPolygon::set_vertices);
ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationPolygon::get_vertices);
@@ -358,7 +427,9 @@ void NavigationPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_outline", "idx"), &NavigationPolygon::get_outline);
ClassDB::bind_method(D_METHOD("remove_outline", "idx"), &NavigationPolygon::remove_outline);
ClassDB::bind_method(D_METHOD("clear_outlines"), &NavigationPolygon::clear_outlines);
+#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("make_polygons_from_outlines"), &NavigationPolygon::make_polygons_from_outlines);
+#endif // DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationPolygon::_set_polygons);
ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationPolygon::_get_polygons);
@@ -369,10 +440,69 @@ void NavigationPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &NavigationPolygon::set_cell_size);
ClassDB::bind_method(D_METHOD("get_cell_size"), &NavigationPolygon::get_cell_size);
+ ClassDB::bind_method(D_METHOD("set_parsed_geometry_type", "geometry_type"), &NavigationPolygon::set_parsed_geometry_type);
+ ClassDB::bind_method(D_METHOD("get_parsed_geometry_type"), &NavigationPolygon::get_parsed_geometry_type);
+
+ ClassDB::bind_method(D_METHOD("set_parsed_collision_mask", "mask"), &NavigationPolygon::set_parsed_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_parsed_collision_mask"), &NavigationPolygon::get_parsed_collision_mask);
+
+ ClassDB::bind_method(D_METHOD("set_parsed_collision_mask_value", "layer_number", "value"), &NavigationPolygon::set_parsed_collision_mask_value);
+ ClassDB::bind_method(D_METHOD("get_parsed_collision_mask_value", "layer_number"), &NavigationPolygon::get_parsed_collision_mask_value);
+
+ ClassDB::bind_method(D_METHOD("set_source_geometry_mode", "geometry_mode"), &NavigationPolygon::set_source_geometry_mode);
+ ClassDB::bind_method(D_METHOD("get_source_geometry_mode"), &NavigationPolygon::get_source_geometry_mode);
+
+ ClassDB::bind_method(D_METHOD("set_source_geometry_group_name", "group_name"), &NavigationPolygon::set_source_geometry_group_name);
+ ClassDB::bind_method(D_METHOD("get_source_geometry_group_name"), &NavigationPolygon::get_source_geometry_group_name);
+
+ ClassDB::bind_method(D_METHOD("set_agent_radius", "agent_radius"), &NavigationPolygon::set_agent_radius);
+ ClassDB::bind_method(D_METHOD("get_agent_radius"), &NavigationPolygon::get_agent_radius);
+
ClassDB::bind_method(D_METHOD("clear"), &NavigationPolygon::clear);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "0.01,500.0,0.01,or_greater,suffix:px"), "set_cell_size", "get_cell_size");
+
+ ADD_GROUP("Geometry", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Meshes and Static Colliders"), "set_parsed_geometry_type", "get_parsed_geometry_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "parsed_collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_parsed_collision_mask", "get_parsed_collision_mask");
+ ADD_PROPERTY_DEFAULT("parsed_collision_mask", 0xFFFFFFFF);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "source_geometry_mode", PROPERTY_HINT_ENUM, "Root Node Children,Group With Children,Group Explicit"), "set_source_geometry_mode", "get_source_geometry_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_geometry_group_name"), "set_source_geometry_group_name", "get_source_geometry_group_name");
+ ADD_PROPERTY_DEFAULT("source_geometry_group_name", StringName("navigation_polygon_source_geometry_group"));
+ ADD_GROUP("Cells", "");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "1.0,50.0,1.0,or_greater,suffix:px"), "set_cell_size", "get_cell_size");
+ ADD_GROUP("Agents", "agent_");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_radius", PROPERTY_HINT_RANGE, "0.0,500.0,0.01,or_greater,suffix:px"), "set_agent_radius", "get_agent_radius");
+
+ BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES);
+ BIND_ENUM_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS);
+ BIND_ENUM_CONSTANT(PARSED_GEOMETRY_BOTH);
+ BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MAX);
+
+ BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_ROOT_NODE_CHILDREN);
+ BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN);
+ BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_EXPLICIT);
+ BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_MAX);
+}
+
+void NavigationPolygon::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name == "parsed_collision_mask") {
+ if (parsed_geometry_type == PARSED_GEOMETRY_MESH_INSTANCES) {
+ p_property.usage = PROPERTY_USAGE_NONE;
+ return;
+ }
+ }
+
+ if (p_property.name == "parsed_source_group_name") {
+ if (source_geometry_mode == SOURCE_GEOMETRY_ROOT_NODE_CHILDREN) {
+ p_property.usage = PROPERTY_USAGE_NONE;
+ return;
+ }
+ }
+}
+
+NavigationPolygon::NavigationPolygon() {
+ navigation_mesh.instantiate();
}
diff --git a/scene/resources/navigation_polygon.h b/scene/resources/navigation_polygon.h
index 7926709a9e..4a6a97e2e7 100644
--- a/scene/resources/navigation_polygon.h
+++ b/scene/resources/navigation_polygon.h
@@ -43,6 +43,7 @@ class NavigationPolygon : public Resource {
};
Vector<Polygon> polygons;
Vector<Vector<Vector2>> outlines;
+ Vector<Vector<Vector2>> baked_outlines;
mutable Rect2 item_rect;
mutable bool rect_cache_dirty = true;
@@ -55,6 +56,7 @@ class NavigationPolygon : public Resource {
protected:
static void _bind_methods();
+ void _validate_property(PropertyInfo &p_property) const;
void _set_polygons(const TypedArray<Vector<int32_t>> &p_array);
TypedArray<Vector<int32_t>> _get_polygons() const;
@@ -68,6 +70,28 @@ public:
bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
#endif
+ enum ParsedGeometryType {
+ PARSED_GEOMETRY_MESH_INSTANCES = 0,
+ PARSED_GEOMETRY_STATIC_COLLIDERS,
+ PARSED_GEOMETRY_BOTH,
+ PARSED_GEOMETRY_MAX
+ };
+
+ enum SourceGeometryMode {
+ SOURCE_GEOMETRY_ROOT_NODE_CHILDREN = 0,
+ SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN,
+ SOURCE_GEOMETRY_GROUPS_EXPLICIT,
+ SOURCE_GEOMETRY_MAX
+ };
+
+ real_t agent_radius = 10.0f;
+
+ ParsedGeometryType parsed_geometry_type = PARSED_GEOMETRY_BOTH;
+ uint32_t parsed_collision_mask = 0xFFFFFFFF;
+
+ SourceGeometryMode source_geometry_mode = SOURCE_GEOMETRY_ROOT_NODE_CHILDREN;
+ StringName source_geometry_group_name = "navigation_polygon_source_group";
+
void set_vertices(const Vector<Vector2> &p_vertices);
Vector<Vector2> get_vertices() const;
@@ -82,11 +106,33 @@ public:
int get_outline_count() const;
void clear_outlines();
+#ifndef DISABLE_DEPRECATED
void make_polygons_from_outlines();
+#endif // DISABLE_DEPRECATED
+ void set_polygons(const Vector<Vector<int>> &p_polygons);
+ const Vector<Vector<int>> &get_polygons() const;
Vector<int> get_polygon(int p_idx);
void clear_polygons();
+ void set_parsed_geometry_type(ParsedGeometryType p_geometry_type);
+ ParsedGeometryType get_parsed_geometry_type() const;
+
+ void set_parsed_collision_mask(uint32_t p_mask);
+ uint32_t get_parsed_collision_mask() const;
+
+ void set_parsed_collision_mask_value(int p_layer_number, bool p_value);
+ bool get_parsed_collision_mask_value(int p_layer_number) const;
+
+ void set_source_geometry_mode(SourceGeometryMode p_geometry_mode);
+ SourceGeometryMode get_source_geometry_mode() const;
+
+ void set_source_geometry_group_name(StringName p_group_name);
+ StringName get_source_geometry_group_name() const;
+
+ void set_agent_radius(real_t p_value);
+ real_t get_agent_radius() const;
+
Ref<NavigationMesh> get_navigation_mesh();
void set_cell_size(real_t p_cell_size);
@@ -94,8 +140,11 @@ public:
void clear();
- NavigationPolygon() {}
+ NavigationPolygon();
~NavigationPolygon() {}
};
+VARIANT_ENUM_CAST(NavigationPolygon::ParsedGeometryType);
+VARIANT_ENUM_CAST(NavigationPolygon::SourceGeometryMode);
+
#endif // NAVIGATION_POLYGON_H