diff options
Diffstat (limited to 'scene')
| -rw-r--r-- | scene/2d/navigation_region_2d.cpp | 35 | ||||
| -rw-r--r-- | scene/2d/navigation_region_2d.h | 3 | ||||
| -rw-r--r-- | scene/2d/tile_map.cpp | 154 | ||||
| -rw-r--r-- | scene/2d/tile_map.h | 16 | ||||
| -rw-r--r-- | scene/3d/bone_attachment_3d.cpp | 2 | ||||
| -rw-r--r-- | scene/3d/lightmap_gi.cpp | 2 | ||||
| -rw-r--r-- | scene/gui/code_edit.cpp | 65 | ||||
| -rw-r--r-- | scene/gui/code_edit.h | 3 | ||||
| -rw-r--r-- | scene/main/scene_tree.cpp | 35 | ||||
| -rw-r--r-- | scene/register_scene_types.cpp | 2 | ||||
| -rw-r--r-- | scene/resources/navigation_mesh_source_geometry_data_2d.cpp | 138 | ||||
| -rw-r--r-- | scene/resources/navigation_mesh_source_geometry_data_2d.h | 78 | ||||
| -rw-r--r-- | scene/resources/navigation_polygon.cpp | 132 | ||||
| -rw-r--r-- | scene/resources/navigation_polygon.h | 51 |
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 |
