summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml17
-rw-r--r--COPYRIGHT.txt5
-rw-r--r--README.md1
-rw-r--r--SConstruct1
-rw-r--r--core/io/logger.cpp12
-rw-r--r--core/variant_parser.cpp10
-rw-r--r--doc/classes/Navigation2DServer.xml274
-rw-r--r--doc/classes/NavigationAgent.xml163
-rw-r--r--doc/classes/NavigationAgent2D.xml157
-rw-r--r--doc/classes/NavigationMeshInstance.xml23
-rw-r--r--doc/classes/NavigationObstacle.xml29
-rw-r--r--doc/classes/NavigationObstacle2D.xml29
-rw-r--r--doc/classes/NavigationServer.xml323
-rw-r--r--doc/classes/OS.xml2
-rw-r--r--drivers/unix/os_unix.cpp26
-rw-r--r--editor/animation_track_editor.cpp4
-rw-r--r--editor/editor_file_system.cpp30
-rw-r--r--editor/editor_file_system.h1
-rw-r--r--editor/editor_node.cpp6
-rw-r--r--editor/filesystem_dock.cpp8
-rw-r--r--editor/find_in_files.cpp4
-rw-r--r--editor/icons/icon_animation_tree_player.svg1
-rw-r--r--editor/icons/icon_godot_docs.svg1
-rw-r--r--editor/icons/icon_particle_attractor_2d.svg1
-rw-r--r--editor/inspector_dock.cpp2
-rw-r--r--editor/plugins/animation_tree_player_editor_plugin.cpp1451
-rw-r--r--editor/plugins/animation_tree_player_editor_plugin.h184
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp19
-rw-r--r--editor/plugins/asset_library_editor_plugin.h1
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp48
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h2
-rw-r--r--editor/plugins/mesh_instance_editor_plugin.cpp72
-rw-r--r--editor/plugins/mesh_instance_editor_plugin.h4
-rw-r--r--editor/plugins/mesh_library_editor_plugin.cpp2
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp47
-rw-r--r--editor/plugins/spatial_editor_plugin.h2
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp4
-rw-r--r--editor/project_manager.cpp36
-rw-r--r--editor/project_settings_editor.cpp38
-rw-r--r--editor/spatial_editor_gizmos.cpp2
-rw-r--r--main/main.cpp21
-rw-r--r--modules/gdnative/include/pluginscript/godot_pluginscript.h7
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.cpp6
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.cpp3
-rw-r--r--modules/gdnavigation/SCsub50
-rw-r--r--modules/gdnavigation/config.py (renamed from modules/recast/config.py)2
-rw-r--r--modules/gdnavigation/gd_navigation_server.cpp472
-rw-r--r--modules/gdnavigation/gd_navigation_server.h134
-rw-r--r--modules/gdnavigation/nav_map.cpp665
-rw-r--r--modules/gdnavigation/nav_map.h137
-rw-r--r--modules/gdnavigation/nav_region.cpp136
-rw-r--r--modules/gdnavigation/nav_region.h89
-rw-r--r--modules/gdnavigation/nav_rid.h48
-rw-r--r--modules/gdnavigation/nav_utils.h169
-rw-r--r--modules/gdnavigation/navigation_mesh_editor_plugin.cpp (renamed from modules/recast/navigation_mesh_editor_plugin.cpp)15
-rw-r--r--modules/gdnavigation/navigation_mesh_editor_plugin.h (renamed from modules/recast/navigation_mesh_editor_plugin.h)13
-rw-r--r--modules/gdnavigation/navigation_mesh_generator.cpp (renamed from modules/recast/navigation_mesh_generator.cpp)139
-rw-r--r--modules/gdnavigation/navigation_mesh_generator.h (renamed from modules/recast/navigation_mesh_generator.h)38
-rw-r--r--modules/gdnavigation/register_types.cpp (renamed from modules/recast/register_types.cpp)45
-rw-r--r--modules/gdnavigation/register_types.h (renamed from modules/recast/register_types.h)8
-rw-r--r--modules/gdnavigation/rvo_agent.cpp84
-rw-r--r--modules/gdnavigation/rvo_agent.h77
-rw-r--r--modules/gridmap/grid_map.cpp41
-rw-r--r--modules/gridmap/grid_map.h2
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp24
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h1
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs58
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs6
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs70
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs46
-rw-r--r--modules/recast/SCsub33
-rw-r--r--modules/visual_script/visual_script_nodes.cpp23
-rw-r--r--platform/iphone/export/export.cpp191
-rw-r--r--platform/javascript/os_javascript.cpp21
-rw-r--r--platform/javascript/os_javascript.h4
-rw-r--r--platform/osx/os_osx.mm35
-rw-r--r--platform/windows/windows_terminal_logger.cpp68
-rw-r--r--platform/x11/os_x11.cpp98
-rw-r--r--scene/2d/navigation_2d.cpp714
-rw-r--r--scene/2d/navigation_2d.h137
-rw-r--r--scene/2d/navigation_agent_2d.cpp341
-rw-r--r--scene/2d/navigation_agent_2d.h150
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp154
-rw-r--r--scene/2d/navigation_obstacle_2d.h71
-rw-r--r--scene/2d/navigation_polygon.cpp90
-rw-r--r--scene/2d/navigation_polygon.h12
-rw-r--r--scene/2d/tile_map.cpp16
-rw-r--r--scene/2d/tile_map.h2
-rw-r--r--scene/3d/navigation.cpp704
-rw-r--r--scene/3d/navigation.h147
-rw-r--r--scene/3d/navigation_agent.cpp361
-rw-r--r--scene/3d/navigation_agent.h162
-rw-r--r--scene/3d/navigation_mesh_instance.cpp258
-rw-r--r--scene/3d/navigation_mesh_instance.h75
-rw-r--r--scene/3d/navigation_obstacle.cpp163
-rw-r--r--scene/3d/navigation_obstacle.h71
-rw-r--r--scene/3d/physics_body.cpp28
-rw-r--r--scene/3d/physics_body.h9
-rw-r--r--scene/animation/animation_tree_player.cpp1866
-rw-r--r--scene/animation/animation_tree_player.h487
-rw-r--r--scene/gui/text_edit.cpp73
-rw-r--r--scene/main/scene_tree.cpp2
-rw-r--r--scene/register_scene_types.cpp27
-rw-r--r--scene/resources/box_shape.cpp4
-rw-r--r--scene/resources/box_shape.h1
-rw-r--r--scene/resources/capsule_shape.cpp4
-rw-r--r--scene/resources/capsule_shape.h1
-rw-r--r--scene/resources/capsule_shape_2d.cpp4
-rw-r--r--scene/resources/capsule_shape_2d.h1
-rw-r--r--scene/resources/circle_shape_2d.cpp4
-rw-r--r--scene/resources/circle_shape_2d.h1
-rw-r--r--scene/resources/concave_polygon_shape.cpp10
-rw-r--r--scene/resources/concave_polygon_shape.h3
-rw-r--r--scene/resources/concave_polygon_shape_2d.cpp10
-rw-r--r--scene/resources/concave_polygon_shape_2d.h1
-rw-r--r--scene/resources/convex_polygon_shape.cpp10
-rw-r--r--scene/resources/convex_polygon_shape.h1
-rw-r--r--scene/resources/convex_polygon_shape_2d.cpp8
-rw-r--r--scene/resources/convex_polygon_shape_2d.h1
-rw-r--r--scene/resources/cylinder_shape.cpp4
-rw-r--r--scene/resources/cylinder_shape.h1
-rw-r--r--scene/resources/height_map_shape.cpp4
-rw-r--r--scene/resources/height_map_shape.h1
-rw-r--r--scene/resources/line_shape_2d.cpp4
-rw-r--r--scene/resources/line_shape_2d.h1
-rw-r--r--scene/resources/mesh_library.h2
-rw-r--r--scene/resources/navigation_mesh.cpp (renamed from scene/3d/navigation_mesh.cpp)196
-rw-r--r--scene/resources/navigation_mesh.h (renamed from scene/3d/navigation_mesh.h)32
-rw-r--r--scene/resources/plane_shape.h4
-rw-r--r--scene/resources/ray_shape.cpp4
-rw-r--r--scene/resources/ray_shape.h1
-rw-r--r--scene/resources/rectangle_shape_2d.cpp4
-rw-r--r--scene/resources/rectangle_shape_2d.h1
-rw-r--r--scene/resources/segment_shape_2d.cpp8
-rw-r--r--scene/resources/segment_shape_2d.h2
-rw-r--r--scene/resources/shape.h3
-rw-r--r--scene/resources/shape_2d.h2
-rw-r--r--scene/resources/sphere_shape.cpp4
-rw-r--r--scene/resources/sphere_shape.h1
-rw-r--r--scene/resources/world.cpp1
-rw-r--r--servers/navigation_2d_server.cpp218
-rw-r--r--servers/navigation_2d_server.h160
-rw-r--r--servers/navigation_server.cpp103
-rw-r--r--servers/navigation_server.h192
-rw-r--r--servers/register_server_types.cpp4
-rw-r--r--thirdparty/README.md18
-rw-r--r--thirdparty/rvo2/LICENSE202
-rw-r--r--thirdparty/rvo2/README.md32
-rw-r--r--thirdparty/rvo2/src/API.h71
-rw-r--r--thirdparty/rvo2/src/Agent.cpp425
-rw-r--r--thirdparty/rvo2/src/Agent.h121
-rw-r--r--thirdparty/rvo2/src/Definitions.h55
-rw-r--r--thirdparty/rvo2/src/KdTree.cpp152
-rw-r--r--thirdparty/rvo2/src/KdTree.h124
-rw-r--r--thirdparty/rvo2/src/RVO.h406
-rw-r--r--thirdparty/rvo2/src/Vector3.h335
157 files changed, 8888 insertions, 6318 deletions
diff --git a/.travis.yml b/.travis.yml
index c8b123c79c..d39d6383ea 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -72,12 +72,20 @@ matrix:
env: PLATFORM=osx TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-clang EXTRA_ARGS="warnings=extra werror=yes"
os: osx
compiler: clang
+ addons:
+ homebrew:
+ packages:
+ - scons
- name: iOS export template (debug, Clang)
stage: build
env: PLATFORM=iphone TOOLS=no TARGET=debug CACHE_NAME=${PLATFORM}-clang
os: osx
compiler: clang
+ addons:
+ homebrew:
+ packages:
+ - scons
- name: Linux headless editor (release_debug, GCC 9, testing project exporting and script running)
stage: build
@@ -109,16 +117,17 @@ before_install:
fi
install:
- - pip install --user scons;
+ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then
+ pyenv global 3.7.1 system;
+ pip3 install --user scons;
+ fi
+ - scons --version
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$PLATFORM" = "android" ]; then
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64;
export PATH=/usr/lib/jvm/java-8-openjdk-amd64/jre/bin:${PATH};
java -version;
misc/travis/android-tools-linux.sh;
fi
- - if [ "$TRAVIS_OS_NAME" = "osx" ]; then
- export PATH=${PATH}:/Users/travis/Library/Python/2.7/bin;
- fi
before_script:
- if [ "$PLATFORM" = "android" ]; then
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index e5330770d1..4a0dec3815 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -332,6 +332,11 @@ Comment: Recast
Copyright: 2009, Mikko Mononen
License: Zlib
+Files: ./thirdparty/rvo2/
+Comment: RVO2
+Copyright: 2016, University of North Carolina at Chapel Hill
+License: Apache 2.0
+
Files: ./thirdparty/squish/
Comment: libSquish
Copyright: 2006, Simon Brown
diff --git a/README.md b/README.md
index 9e772fa89f..1202dabc8c 100644
--- a/README.md
+++ b/README.md
@@ -70,3 +70,4 @@ for more info.
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/bfiihqq6byxsjxxh/branch/master?svg=true)](https://ci.appveyor.com/project/akien-mga/godot)
[![Code Triagers Badge](https://www.codetriage.com/godotengine/godot/badges/users.svg)](https://www.codetriage.com/godotengine/godot)
[![Translate on Weblate](https://hosted.weblate.org/widgets/godot-engine/-/godot/svg-badge.svg)](https://hosted.weblate.org/engage/godot-engine/?utm_source=widget)
+[![Total alerts on LGTM](https://img.shields.io/lgtm/alerts/g/godotengine/godot.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/godotengine/godot/alerts)
diff --git a/SConstruct b/SConstruct
index 6af363090b..a8a4acbcdb 100644
--- a/SConstruct
+++ b/SConstruct
@@ -152,6 +152,7 @@ opts.Add(BoolVariable('builtin_opus', "Use the built-in Opus library", True))
opts.Add(BoolVariable('builtin_pcre2', "Use the built-in PCRE2 library", True))
opts.Add(BoolVariable('builtin_pcre2_with_jit', "Use JIT compiler for the built-in PCRE2 library", True))
opts.Add(BoolVariable('builtin_recast', "Use the built-in Recast library", True))
+opts.Add(BoolVariable('builtin_rvo2', "Use the built-in RVO2 library", True))
opts.Add(BoolVariable('builtin_squish', "Use the built-in squish library", True))
opts.Add(BoolVariable('builtin_xatlas', "Use the built-in xatlas library", True))
opts.Add(BoolVariable('builtin_zlib', "Use the built-in zlib library", True))
diff --git a/core/io/logger.cpp b/core/io/logger.cpp
index c3ea0d024e..4d732332d5 100644
--- a/core/io/logger.cpp
+++ b/core/io/logger.cpp
@@ -58,12 +58,12 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c
return;
}
- const char *err_type = "**ERROR**";
+ const char *err_type = "ERROR";
switch (p_type) {
- case ERR_ERROR: err_type = "**ERROR**"; break;
- case ERR_WARNING: err_type = "**WARNING**"; break;
- case ERR_SCRIPT: err_type = "**SCRIPT ERROR**"; break;
- case ERR_SHADER: err_type = "**SHADER ERROR**"; break;
+ case ERR_ERROR: err_type = "ERROR"; break;
+ case ERR_WARNING: err_type = "WARNING"; break;
+ case ERR_SCRIPT: err_type = "SCRIPT ERROR"; break;
+ case ERR_SHADER: err_type = "SHADER ERROR"; break;
default: ERR_PRINT("Unknown error type"); break;
}
@@ -74,7 +74,7 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c
err_details = p_code;
logf_error("%s: %s\n", err_type, err_details);
- logf_error(" At: %s:%i:%s() - %s\n", p_file, p_line, p_function, p_code);
+ logf_error(" at: %s (%s:%i) - %s\n", p_function, p_file, p_line, p_code);
}
void Logger::logf(const char *p_format, ...) {
diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp
index 6ca9d6c246..4ce33b0123 100644
--- a/core/variant_parser.cpp
+++ b/core/variant_parser.cpp
@@ -51,10 +51,16 @@ bool VariantParser::StreamFile::is_eof() const {
CharType VariantParser::StreamString::get_char() {
- if (pos >= s.length())
+ if (pos > s.length()) {
return 0;
- else
+ } else if (pos == s.length()) {
+ // You need to try to read again when you have reached the end for EOF to be reported,
+ // so this works the same as files (like StreamFile does)
+ pos++;
+ return 0;
+ } else {
return s[pos++];
+ }
}
bool VariantParser::StreamString::is_utf8() const {
diff --git a/doc/classes/Navigation2DServer.xml b/doc/classes/Navigation2DServer.xml
new file mode 100644
index 0000000000..105d6aabf8
--- /dev/null
+++ b/doc/classes/Navigation2DServer.xml
@@ -0,0 +1,274 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="Navigation2DServer" inherits="Object" category="Core" version="3.2">
+ <brief_description>
+ Server interface for low-level 2D navigation access
+ </brief_description>
+ <description>
+ Navigation2DServer is the server responsible for all 2D navigation. It creates the agents, maps, and regions for navigation to work as expected. This keeps tracks of any call and executes them during the sync phase. This means that you can request any change to the map, using any thread, without worrying.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="agent_create" qualifiers="const">
+ <return type="RID">
+ </return>
+ <description>
+ Creates the agent.
+ </description>
+ </method>
+ <method name="agent_is_map_changed">
+ <return type="bool" qualifiers="const">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <description>
+ Returns true if the map got changed the previous frame.
+ </description>
+ </method>
+ <method name="agent_set_callback" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="receiver" type="Object">
+ </argument>
+ <argument index="2" name="method" type="String">
+ </argument>
+ <argument index="3" name="userdata" type="Variant" default="null">
+ </argument>
+ <description>
+ Callback called at the end of the RVO process.
+ </description>
+ </method>
+ <method name="agent_set_map" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="map" type="RID">
+ </argument>
+ <description>
+ Puts the agent in the map.
+ </description>
+ </method>
+ <method name="agent_set_max_neighbors" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="count" type="int">
+ </argument>
+ <description>
+ Sets the maximum number of other agents the agent takes into account in the navigation. The larger this number, the longer the running time of the simulation. If the number is too low, the simulation will not be safe.
+ </description>
+ </method>
+ <method name="agent_set_max_speed" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="max_speed" type="float">
+ </argument>
+ <description>
+ Sets the maximum speed of the agent. Must be positive.
+ </description>
+ </method>
+ <method name="agent_set_neighbor_dist" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="dist" type="float">
+ </argument>
+ <description>
+ Sets the maximum distance to other agents this agent takes into account in the navigation. The larger this number, the longer the running time of the simulation. If the number is too low, the simulation will not be safe.
+ </description>
+ </method>
+ <method name="agent_set_position" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="position" type="Vector3">
+ </argument>
+ <description>
+ Sets the position of the agent in world space.
+ </description>
+ </method>
+ <method name="agent_set_radius" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="radius" type="float">
+ </argument>
+ <description>
+ Sets the radius of the agent.
+ </description>
+ </method>
+ <method name="agent_set_target_velocity" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="target_velocity" type="Vector3">
+ </argument>
+ <description>
+ Sets the new target velocity.
+ </description>
+ </method>
+ <method name="agent_set_time_horizon" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="time" type="float">
+ </argument>
+ <description>
+ The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. Must be positive.
+ </description>
+ </method>
+ <method name="agent_set_velocity" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="velocity" type="Vector3">
+ </argument>
+ <description>
+ Sets the current velocity of the agent.
+ </description>
+ </method>
+ <method name="free" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="object" type="RID">
+ </argument>
+ <description>
+ Destroy the RID
+ </description>
+ </method>
+ <method name="map_create" qualifiers="const">
+ <return type="RID">
+ </return>
+ <description>
+ Create a new map.
+ </description>
+ </method>
+ <method name="map_get_cell_size">
+ <return type="float">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <description>
+ Returns the map cell size.
+ </description>
+ </method>
+ <method name="map_get_edge_connection_margin" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <description>
+ Returns the edge connection margin of the map. The edge connection margin is a distance used to connect two regions.
+ </description>
+ </method>
+ <method name="map_get_path" qualifiers="const">
+ <return type="PoolVector2Array">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <argument index="1" name="origin" type="Vector2">
+ </argument>
+ <argument index="2" name="destination" type="Vector2">
+ </argument>
+ <argument index="3" name="optimize" type="bool">
+ </argument>
+ <description>
+ Returns the navigation path to reach the destination from the origin, while avoiding static obstacles.
+ </description>
+ </method>
+ <method name="map_is_active" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <description>
+ Returns true if the map is active.
+ </description>
+ </method>
+ <method name="map_set_active" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <argument index="1" name="active" type="bool">
+ </argument>
+ <description>
+ Sets the map active.
+ </description>
+ </method>
+ <method name="map_set_cell_size" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <argument index="1" name="cell_size" type="float">
+ </argument>
+ <description>
+ Set the map cell size used to weld the navigation mesh polygons.
+ </description>
+ </method>
+ <method name="map_set_edge_connection_margin" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <argument index="1" name="margin" type="float">
+ </argument>
+ <description>
+ Set the map edge connection margin used to weld the compatible region edges.
+ </description>
+ </method>
+ <method name="region_create" qualifiers="const">
+ <return type="RID">
+ </return>
+ <description>
+ Creates a new region.
+ </description>
+ </method>
+ <method name="region_set_map" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="region" type="RID">
+ </argument>
+ <argument index="1" name="map" type="RID">
+ </argument>
+ <description>
+ Sets the map for the region.
+ </description>
+ </method>
+ <method name="region_set_navpoly" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="region" type="RID">
+ </argument>
+ <argument index="1" name="nav_poly" type="NavigationPolygon">
+ </argument>
+ <description>
+ Sets the navigation mesh for the region.
+ </description>
+ </method>
+ <method name="region_set_transform" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="region" type="RID">
+ </argument>
+ <argument index="1" name="transform" type="Transform">
+ </argument>
+ <description>
+ Sets the global transformation for the region.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/NavigationAgent.xml b/doc/classes/NavigationAgent.xml
new file mode 100644
index 0000000000..d0e2a60fbf
--- /dev/null
+++ b/doc/classes/NavigationAgent.xml
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="NavigationAgent" inherits="Node" category="Core" version="3.2">
+ <brief_description>
+ 3D Agent used in navigation for collision avoidance.
+ </brief_description>
+ <description>
+ 3D Agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO collision avoidance. The agent needs navigation data to work correctly. This can be done by having the agent as a child of a [Navigation] node, or using [method set_navigation]. [NavigationAgent] is physics safe.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="distance_to_target" qualifiers="const">
+ <return type="float">
+ </return>
+ <description>
+ Returns the distance to the target location, using the agent's global position. The user must set the target location with [method set_target_location] in order for this to be accurate.
+ </description>
+ </method>
+ <method name="get_final_location">
+ <return type="Vector3">
+ </return>
+ <description>
+ Returns the reachable final location in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame.
+ </description>
+ </method>
+ <method name="get_nav_path" qualifiers="const">
+ <return type="PoolVector3Array">
+ </return>
+ <description>
+ Returns the path from start to finish in global coordinates.
+ </description>
+ </method>
+ <method name="get_nav_path_index" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns which index the agent is currently on in the navigation path's [PoolVector3Array].
+ </description>
+ </method>
+ <method name="get_navigation" qualifiers="const">
+ <return type="Node">
+ </return>
+ <description>
+ Returns the [Navigation] node that the agent is using for its navigation system.
+ </description>
+ </method>
+ <method name="get_next_location">
+ <return type="Vector3">
+ </return>
+ <description>
+ Returns a [Vector3] in global coordinates, that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the origin of the agent's parent.
+ </description>
+ </method>
+ <method name="get_target_location" qualifiers="const">
+ <return type="Vector3">
+ </return>
+ <description>
+ Returns the user defined [Vector3] after setting the target location.
+ </description>
+ </method>
+ <method name="is_navigation_finished">
+ <return type="bool">
+ </return>
+ <description>
+ Returns true if the navigation path's final location has been reached.
+ </description>
+ </method>
+ <method name="is_target_reachable">
+ <return type="bool">
+ </return>
+ <description>
+ Returns true if the target location is reachable. The target location is set using [method set_target_location].
+ </description>
+ </method>
+ <method name="is_target_reached" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ Returns true if the target location is reached. The target location is set using [method set_target_location]. It may not always be possible to reach the target location. It should always be possible to reach the final location though. See [method get_final_location].
+ </description>
+ </method>
+ <method name="set_navigation">
+ <return type="void">
+ </return>
+ <argument index="0" name="navigation" type="Node">
+ </argument>
+ <description>
+ Sets the [Navigation] node used by the agent. Useful when you don't want to make the agent a child of a [Navigation] node.
+ </description>
+ </method>
+ <method name="set_target_location">
+ <return type="void">
+ </return>
+ <argument index="0" name="location" type="Vector3">
+ </argument>
+ <description>
+ Sets the user desired final location. This will clear the current navigation path.
+ </description>
+ </method>
+ <method name="set_velocity">
+ <return type="void">
+ </return>
+ <argument index="0" name="velocity" type="Vector3">
+ </argument>
+ <description>
+ Sends the passed in velocity to the collision avoidance algorithm. It will adjust the velocity to avoid collisions. Once the adjustment to the velocity is complete, it will emit the [signal velocity_computed] signal.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="target_desired_distance" type="float" setter="set_target_desired_distance" getter="get_target_desired_distance" default="1.0">
+ The distance threshold before a target is considered to be reached. This will allow an agent to not have to hit a point on the path exactly, but in the area.
+ </member>
+ <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
+ The radius of the agent.
+ </member>
+ <member name="agent_height_offset" type="float" setter="set_agent_height_offset" getter="get_agent_height_offset" default="0.0">
+ The agent height offset to match the navigation mesh height.
+ </member>
+ <member name="neighbor_dist" type="float" setter="set_neighbor_dist" getter="get_neighbor_dist" default="50.0">
+ The distance to search for other agents.
+ </member>
+ <member name="max_neighbors" type="int" setter="set_max_neighbors" getter="get_max_neighbors" default="10">
+ The maximum number of neighbors for the agent to consider.
+ </member>
+ <member name="time_horizon" type="float" setter="set_time_horizon" getter="get_time_horizon" default="5.0">
+ The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithim, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. Must be positive.
+ </member>
+ <member name="max_speed" type="float" setter="set_max_speed" getter="get_max_speed" default="10.0">
+ The maximum speed that an agent can move.
+ </member>
+ <member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="3.0">
+ The maximum distance the agent is allowed away from the ideal path to the final location. This can happen due to trying to avoid collisions. When the maximum distance is exceded, it recalculates the ideal path.
+ </member>
+ <member name="ignore_y" type="bool" setter="set_ignore_y" getter="get_ignore_y" default="true">
+ Ignores collisions on the Y axis. Must be true to move on a horizontal plane.
+ </member>
+ </members>
+ <signals>
+ <signal name="navigation_finished">
+ <description>
+ Notifies when the final location is reached.
+ </description>
+ </signal>
+ <signal name="path_changed">
+ <description>
+ Notifies when the navigation path changes.
+ </description>
+ </signal>
+ <signal name="target_reached">
+ <description>
+ Notifies when the player defined target, set with [method set_target_location], is reached.
+ </description>
+ </signal>
+ <signal name="velocity_computed">
+ <argument index="0" name="safe_velocity" type="Vector2">
+ </argument>
+ <description>
+ Notifies when the collision avoidance velocity is calculated. Emitted by [method set_velocity].
+ </description>
+ </signal>
+ </signals>
+</class>
diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml
new file mode 100644
index 0000000000..dd3a8aa011
--- /dev/null
+++ b/doc/classes/NavigationAgent2D.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="NavigationAgent2D" inherits="Node" category="Core" version="3.2">
+ <brief_description>
+ 2D Agent used in navigation for collision avoidance.
+ </brief_description>
+ <description>
+ 2D Agent that is used in navigation to reach a location while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO collision avoidance. The agent needs navigation data to work correctly. This can be done by having the agent as a child of a [Navigation2D] node, or using [method set_navigation]. [NavigationAgent2D] is physics safe.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="distance_to_target" qualifiers="const">
+ <return type="float">
+ </return>
+ <description>
+ Returns the distance to the target location, using the agent's global position. The user must set the target location with [method set_target_location] in order for this to be accurate.
+ </description>
+ </method>
+ <method name="get_final_location">
+ <return type="Vector2">
+ </return>
+ <description>
+ Returns the reachable final location in global coordinates. This can change if the navigation path is altered in any way.
+ </description>
+ </method>
+ <method name="get_nav_path" qualifiers="const">
+ <return type="PoolVector2Array">
+ </return>
+ <description>
+ Returns the path from start to finish in global coordinates.
+ </description>
+ </method>
+ <method name="get_nav_path_index" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns which index the agent is currently on in the navigation path's [PoolVector2Array].
+ </description>
+ </method>
+ <method name="get_navigation" qualifiers="const">
+ <return type="Node">
+ </return>
+ <description>
+ Returns the [Navigation2D] node that the agent is using for its navigation system.
+ </description>
+ </method>
+ <method name="get_next_location">
+ <return type="Vector2">
+ </return>
+ <description>
+ Returns a [Vector2] in global coordinates, that can be moved to, making sure that there are no static objects in the way. If the agent does not have a navigation path, it will return the position of the agent's parent.
+ </description>
+ </method>
+ <method name="get_target_location" qualifiers="const">
+ <return type="Vector2">
+ </return>
+ <description>
+ Returns the user defined [Vector2] after setting the target location.
+ </description>
+ </method>
+ <method name="is_navigation_finished">
+ <return type="bool">
+ </return>
+ <description>
+ Returns true if the navigation path's final location has been reached.
+ </description>
+ </method>
+ <method name="is_target_reachable">
+ <return type="bool">
+ </return>
+ <description>
+ Returns true if the target location is reachable. The target location is set using [method set_target_location].
+ </description>
+ </method>
+ <method name="is_target_reached" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ Returns true if the target location is reached. The target location is set using [method set_target_location]. It may not always be possible to reach the target location. It should always be possible to reach the final location though. See [method get_final_location].
+ </description>
+ </method>
+ <method name="set_navigation">
+ <return type="void">
+ </return>
+ <argument index="0" name="navigation" type="Node">
+ </argument>
+ <description>
+ Sets the [Navigation2D] node used by the agent. Useful when you don't want to make the agent a child of a [Navigation2D] node.
+ </description>
+ </method>
+ <method name="set_target_location">
+ <return type="void">
+ </return>
+ <argument index="0" name="location" type="Vector2">
+ </argument>
+ <description>
+ Sets the user desired final location. This will clear the current navigation path.
+ </description>
+ </method>
+ <method name="set_velocity">
+ <return type="void">
+ </return>
+ <argument index="0" name="velocity" type="Vector2">
+ </argument>
+ <description>
+ Sends the passed in velocity to the collision avoidance algorithm. It will adjust the velocity to avoid collisions. Once the adjustment to the velocity is complete, it will emit the [signal velocity_computed] signal.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="target_desired_distance" type="float" setter="set_target_desired_distance" getter="get_target_desired_distance" default="1.0">
+ The distance threshold before a target is considered to be reached. This will allow an agent to not have to hit a point on the path exactly, but in the area.
+ </member>
+ <member name="radius" type="float" setter="set_radius" getter="get_radius" default="10.0">
+ The radius of the agent.
+ </member>
+ <member name="neighbor_dist" type="float" setter="set_neighbor_dist" getter="get_neighbor_dist" default="500.0">
+ The distance to search for other agents.
+ </member>
+ <member name="max_neighbors" type="int" setter="set_max_neighbors" getter="get_max_neighbors" default="10">
+ The maximum number of neighbors for the agent to consider.
+ </member>
+ <member name="time_horizon" type="float" setter="set_time_horizon" getter="get_time_horizon" default="20.0">
+ The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithim, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. Must be positive.
+ </member>
+ <member name="max_speed" type="float" setter="set_max_speed" getter="get_max_speed" default="200.0">
+ The maximum speed that an agent can move.
+ </member>
+ <member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="3.0">
+ The maximum distance the agent is allowed away from the ideal path to the final location. This can happen due to trying to avoid collisions. When the maximum distance is exceded, it recalculates the ideal path.
+ </member>
+ </members>
+ <signals>
+ <signal name="navigation_finished">
+ <description>
+ Notifies when the final location is reached.
+ </description>
+ </signal>
+ <signal name="path_changed">
+ <description>
+ Notifies when the navigation path changes.
+ </description>
+ </signal>
+ <signal name="target_reached">
+ <description>
+ Notifies when the player defined target, set with [method set_target_location], is reached.
+ </description>
+ </signal>
+ <signal name="velocity_computed">
+ <argument index="0" name="safe_velocity" type="Vector2">
+ </argument>
+ <description>
+ Notifies when the collision avoidance velocity is calculated. Emitted by [method set_velocity].
+ </description>
+ </signal>
+ </signals>
+</class>
diff --git a/doc/classes/NavigationMeshInstance.xml b/doc/classes/NavigationMeshInstance.xml
index a348740fdc..75bd62e278 100644
--- a/doc/classes/NavigationMeshInstance.xml
+++ b/doc/classes/NavigationMeshInstance.xml
@@ -1,19 +1,42 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NavigationMeshInstance" inherits="Spatial" version="4.0">
<brief_description>
+ An instance of a [NavigationMesh].
</brief_description>
<description>
+ An instance of a [NavigationMesh]. It tells the [Navigation] node what can be navigated and what cannot, based on the [NavigationMesh] resource. This should be a child of a [Navigation] node.
</description>
<tutorials>
</tutorials>
<methods>
+ <method name="bake_navigation_mesh">
+ <return type="void">
+ </return>
+ <description>
+ Bakes the [NavigationMesh]. The baking is done in a seperate thread because navigation baking is not a cheap operation. This can be done at runtime. When it is completed, it automatically sets the new [NavigationMesh].
+ </description>
+ </method>
</methods>
<members>
<member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true">
+ Determines if the [NavigationMeshInstance] is enabled or disabled.
</member>
<member name="navmesh" type="NavigationMesh" setter="set_navigation_mesh" getter="get_navigation_mesh">
+ The [NavigationMesh] resource to use.
</member>
</members>
+ <signals>
+ <signal name="bake_finished">
+ <description>
+ Notifies when the navigation mesh bake operation is completed.
+ </description>
+ </signal>
+ <signal name="navigation_mesh_changed">
+ <description>
+ Notifies when the [NavigationMesh] has changed.
+ </description>
+ </signal>
+ </signals>
<constants>
</constants>
</class>
diff --git a/doc/classes/NavigationObstacle.xml b/doc/classes/NavigationObstacle.xml
new file mode 100644
index 0000000000..307538b77b
--- /dev/null
+++ b/doc/classes/NavigationObstacle.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="NavigationObstacle" inherits="Node" category="Core" version="3.2">
+ <brief_description>
+ 3D Obstacle used in navigation for collision avoidance.
+ </brief_description>
+ <description>
+ 3D Obstacle used in navigation for collision avoidance. The obstacle needs navigation data to work correctly. This can be done by having the obstacle as a child of a [Navigation] node, or using [method set_navigation]. [NavigationObstacle] is physics safe.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_navigation" qualifiers="const">
+ <return type="Node">
+ </return>
+ <description>
+ Returns the [Navigation] node that the obstacle is using for its navigation system.
+ </description>
+ </method>
+ <method name="set_navigation">
+ <return type="void">
+ </return>
+ <argument index="0" name="navigation" type="Node">
+ </argument>
+ <description>
+ Sets the [Navigation] node used by the obstacle. Useful when you don't want to make the obstacle a child of a [Navigation] node.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/NavigationObstacle2D.xml b/doc/classes/NavigationObstacle2D.xml
new file mode 100644
index 0000000000..cf5dd2fa10
--- /dev/null
+++ b/doc/classes/NavigationObstacle2D.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="NavigationObstacle2D" inherits="Node" category="Core" version="3.2">
+ <brief_description>
+ 2D Obstacle used in navigation for collision avoidance.
+ </brief_description>
+ <description>
+ 2D Obstacle used in navigation for collision avoidance. The obstacle needs navigation data to work correctly. This can be done by having the obstacle as a child of a [Navigation2D] node, or using [method set_navigation]. [NavigationObstacle] is physics safe.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_navigation" qualifiers="const">
+ <return type="Node">
+ </return>
+ <description>
+ Returns the [Navigation2D] node that the obstacle is using for its navigation system.
+ </description>
+ </method>
+ <method name="set_navigation">
+ <return type="void">
+ </return>
+ <argument index="0" name="navigation" type="Node">
+ </argument>
+ <description>
+ Sets the [Navigation2D] node used by the obstacle. Useful when you don't want to make the obstacle a child of a [Navigation2D] node.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/NavigationServer.xml b/doc/classes/NavigationServer.xml
new file mode 100644
index 0000000000..894a46b8a7
--- /dev/null
+++ b/doc/classes/NavigationServer.xml
@@ -0,0 +1,323 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="NavigationServer" inherits="Object" category="Core" version="3.2">
+ <brief_description>
+ Server interface for low-level 3D navigation access
+ </brief_description>
+ <description>
+ NavigationServer is the server responsible for all 3D navigation. It creates the agents, maps, and regions for navigation to work as expected. This keeps tracks of any call and executes them during the sync phase. This means that you can request any change to the map, using any thread, without worrying.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="agent_create" qualifiers="const">
+ <return type="RID">
+ </return>
+ <description>
+ Creates the agent.
+ </description>
+ </method>
+ <method name="agent_is_map_changed">
+ <return type="bool" qualifiers="const">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <description>
+ Returns true if the map got changed the previous frame.
+ </description>
+ </method>
+ <method name="agent_set_callback" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="receiver" type="Object">
+ </argument>
+ <argument index="2" name="method" type="String">
+ </argument>
+ <argument index="3" name="userdata" type="Variant" default="null">
+ </argument>
+ <description>
+ Callback called at the end of the RVO process.
+ </description>
+ </method>
+ <method name="agent_set_map" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="map" type="RID">
+ </argument>
+ <description>
+ Puts the agent in the map.
+ </description>
+ </method>
+ <method name="agent_set_max_neighbors" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="count" type="int">
+ </argument>
+ <description>
+ Sets the maximum number of other agents the agent takes into account in the navigation. The larger this number, the longer the running time of the simulation. If the number is too low, the simulation will not be safe.
+ </description>
+ </method>
+ <method name="agent_set_max_speed" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="max_speed" type="float">
+ </argument>
+ <description>
+ Sets the maximum speed of the agent. Must be positive.
+ </description>
+ </method>
+ <method name="agent_set_neighbor_dist" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="dist" type="float">
+ </argument>
+ <description>
+ Sets the maximum distance to other agents this agent takes into account in the navigation. The larger this number, the longer the running time of the simulation. If the number is too low, the simulation will not be safe.
+ </description>
+ </method>
+ <method name="agent_set_position" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="position" type="Vector3">
+ </argument>
+ <description>
+ Sets the position of the agent in world space.
+ </description>
+ </method>
+ <method name="agent_set_radius" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="radius" type="float">
+ </argument>
+ <description>
+ Sets the radius of the agent.
+ </description>
+ </method>
+ <method name="agent_set_target_velocity" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="target_velocity" type="Vector3">
+ </argument>
+ <description>
+ Sets the new target velocity.
+ </description>
+ </method>
+ <method name="agent_set_time_horizon" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="time" type="float">
+ </argument>
+ <description>
+ The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. Must be positive.
+ </description>
+ </method>
+ <method name="agent_set_velocity" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="agent" type="RID">
+ </argument>
+ <argument index="1" name="velocity" type="Vector3">
+ </argument>
+ <description>
+ Sets the current velocity of the agent.
+ </description>
+ </method>
+ <method name="free" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="object" type="RID">
+ </argument>
+ <description>
+ Destroy the RID
+ </description>
+ </method>
+ <method name="map_create" qualifiers="const">
+ <return type="RID">
+ </return>
+ <description>
+ Create a new map.
+ </description>
+ </method>
+ <method name="map_get_cell_size">
+ <return type="float">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <description>
+ Returns the map cell size.
+ </description>
+ </method>
+ <method name="map_get_edge_connection_margin" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <description>
+ Returns the edge connection margin of the map.
+ </description>
+ </method>
+ <method name="map_get_path" qualifiers="const">
+ <return type="PoolVector3Array">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <argument index="1" name="origin" type="Vector3">
+ </argument>
+ <argument index="2" name="destination" type="Vector3">
+ </argument>
+ <argument index="3" name="optimize" type="bool">
+ </argument>
+ <description>
+ Returns the navigation path to reach the destination from the origin.
+ </description>
+ </method>
+ <method name="map_get_up" qualifiers="const">
+ <return type="Vector3">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <description>
+ Returns the map's up direction.
+ </description>
+ </method>
+ <method name="map_is_active" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <description>
+ Returns true if the map is active.
+ </description>
+ </method>
+ <method name="map_set_active" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <argument index="1" name="active" type="bool">
+ </argument>
+ <description>
+ Sets the map active.
+ </description>
+ </method>
+ <method name="map_set_cell_size" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <argument index="1" name="cell_size" type="float">
+ </argument>
+ <description>
+ Set the map cell size used to weld the navigation mesh polygons.
+ </description>
+ </method>
+ <method name="map_set_edge_connection_margin" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <argument index="1" name="margin" type="float">
+ </argument>
+ <description>
+ Set the map edge connection margein used to weld the compatible region edges.
+ </description>
+ </method>
+ <method name="map_set_up" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="map" type="RID">
+ </argument>
+ <argument index="1" name="up" type="Vector3">
+ </argument>
+ <description>
+ Sets the map up direction.
+ </description>
+ </method>
+ <method name="region_bake_navmesh" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="mesh" type="NavigationMesh">
+ </argument>
+ <argument index="1" name="node" type="Node">
+ </argument>
+ <description>
+ Bakes the navigation mesh.
+ </description>
+ </method>
+ <method name="region_create" qualifiers="const">
+ <return type="RID">
+ </return>
+ <description>
+ Creates a new region.
+ </description>
+ </method>
+ <method name="region_set_map" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="region" type="RID">
+ </argument>
+ <argument index="1" name="map" type="RID">
+ </argument>
+ <description>
+ Sets the map for the region.
+ </description>
+ </method>
+ <method name="region_set_navmesh" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="region" type="RID">
+ </argument>
+ <argument index="1" name="nav_mesh" type="NavigationMesh">
+ </argument>
+ <description>
+ Sets the navigation mesh for the region.
+ </description>
+ </method>
+ <method name="region_set_transform" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="region" type="RID">
+ </argument>
+ <argument index="1" name="transform" type="Transform">
+ </argument>
+ <description>
+ Sets the global transformation for the region.
+ </description>
+ </method>
+ <method name="set_active" qualifiers="const">
+ <return type="void">
+ </return>
+ <argument index="0" name="active" type="bool">
+ </argument>
+ <description>
+ Control activation of this server.
+ </description>
+ </method>
+ <method name="step">
+ <return type="void">
+ </return>
+ <argument index="0" name="delta_time" type="float">
+ </argument>
+ <description>
+ Steps the server. This is not threadsafe and must be called in single thread.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index 21c8dcd20a..e0f73df1bf 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -960,7 +960,7 @@
If [code]true[/code], the window background is transparent and window frame is removed.
Use [code]get_tree().get_root().set_transparent_background(true)[/code] to disable main viewport background rendering.
[b]Note:[/b] This property has no effect if [b]Project &gt; Project Settings &gt; Display &gt; Window &gt; Per-pixel transparency &gt; Allowed[/b] setting is disabled.
- [b]Note:[/b] This property is implemented on Linux, macOS and Windows.
+ [b]Note:[/b] This property is implemented on HTML5, Linux, macOS and Windows.
</member>
<member name="window_position" type="Vector2" setter="set_window_position" getter="get_window_position" default="Vector2( 0, 0 )">
The window position relative to the screen, the origin is the top left corner, +Y axis goes to the bottom and +X axis goes to the right.
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index c5eb343cc8..2d8d37b2f1 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -559,34 +559,34 @@ void UnixTerminalLogger::log_error(const char *p_function, const char *p_file, i
// This prevents Godot from writing ANSI escape codes when redirecting
// stdout and stderr to a file.
const bool tty = isatty(fileno(stdout));
- const char *red = tty ? "\E[0;31m" : "";
+ const char *gray = tty ? "\E[0;90m" : "";
+ const char *red = tty ? "\E[0;91m" : "";
const char *red_bold = tty ? "\E[1;31m" : "";
- const char *yellow = tty ? "\E[0;33m" : "";
+ const char *yellow = tty ? "\E[0;93m" : "";
const char *yellow_bold = tty ? "\E[1;33m" : "";
- const char *magenta = tty ? "\E[0;35m" : "";
+ const char *magenta = tty ? "\E[0;95m" : "";
const char *magenta_bold = tty ? "\E[1;35m" : "";
- const char *cyan = tty ? "\E[0;36m" : "";
+ const char *cyan = tty ? "\E[0;96m" : "";
const char *cyan_bold = tty ? "\E[1;36m" : "";
const char *reset = tty ? "\E[0m" : "";
- const char *bold = tty ? "\E[1m" : "";
switch (p_type) {
case ERR_WARNING:
- logf_error("%sWARNING: %s: %s%s%s\n", yellow_bold, p_function, reset, bold, err_details);
- logf_error("%s At: %s:%i.%s\n", yellow, p_file, p_line, reset);
+ logf_error("%sWARNING:%s %s\n", yellow_bold, yellow, err_details);
+ logf_error("%s at: %s (%s:%i)%s\n", gray, p_function, p_file, p_line, reset);
break;
case ERR_SCRIPT:
- logf_error("%sSCRIPT ERROR: %s: %s%s%s\n", magenta_bold, p_function, reset, bold, err_details);
- logf_error("%s At: %s:%i.%s\n", magenta, p_file, p_line, reset);
+ logf_error("%sSCRIPT ERROR:%s %s\n", magenta_bold, magenta, err_details);
+ logf_error("%s at: %s (%s:%i)%s\n", gray, p_function, p_file, p_line, reset);
break;
case ERR_SHADER:
- logf_error("%sSHADER ERROR: %s: %s%s%s\n", cyan_bold, p_function, reset, bold, err_details);
- logf_error("%s At: %s:%i.%s\n", cyan, p_file, p_line, reset);
+ logf_error("%sSHADER ERROR:%s %s\n", cyan_bold, cyan, err_details);
+ logf_error("%s at: %s (%s:%i)%s\n", gray, p_function, p_file, p_line, reset);
break;
case ERR_ERROR:
default:
- logf_error("%sERROR: %s: %s%s%s\n", red_bold, p_function, reset, bold, err_details);
- logf_error("%s At: %s:%i.%s\n", red, p_file, p_line, reset);
+ logf_error("%sERROR:%s %s\n", red_bold, red, err_details);
+ logf_error("%s at: %s (%s:%i)%s\n", gray, p_function, p_file, p_line, reset);
break;
}
}
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index e9719f8618..2edfcced60 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -1727,7 +1727,7 @@ void AnimationTimelineEdit::update_values() {
time_icon->set_tooltip(TTR("Animation length (frames)"));
} else {
length->set_value(animation->get_length());
- length->set_step(0.01);
+ length->set_step(0.001);
length->set_tooltip(TTR("Animation length (seconds)"));
time_icon->set_tooltip(TTR("Animation length (seconds)"));
}
@@ -1890,7 +1890,7 @@ AnimationTimelineEdit::AnimationTimelineEdit() {
length = memnew(EditorSpinSlider);
length->set_min(0.001);
length->set_max(36000);
- length->set_step(0.01);
+ length->set_step(0.001);
length->set_allow_greater(true);
length->set_custom_minimum_size(Vector2(70 * EDSCALE, 0));
length->set_hide_slider(true);
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 04fe6e5ce6..107ecac3b0 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -675,9 +675,12 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess
if (f == "")
break;
+ if (da->current_is_hidden())
+ continue;
+
if (da->current_is_dir()) {
- if (f.begins_with(".")) //ignore hidden and . / ..
+ if (f.begins_with(".")) // Ignore special and . / ..
continue;
if (FileAccess::exists(cd.plus_file(f).plus_file("project.godot"))) // skip if another project inside this
@@ -871,9 +874,12 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const
if (f == "")
break;
+ if (da->current_is_hidden())
+ continue;
+
if (da->current_is_dir()) {
- if (f.begins_with(".")) //ignore hidden and . / ..
+ if (f.begins_with(".")) // Ignore special and . / ..
continue;
int idx = p_dir->find_dir_index(f);
@@ -1059,8 +1065,12 @@ void EditorFileSystem::get_changed_sources(List<String> *r_changed) {
void EditorFileSystem::scan_changes() {
- if (scanning || scanning_changes || thread)
+ if (first_scan || // Prevent a premature changes scan from inhibiting the first full scan
+ scanning || scanning_changes || thread) {
+ scan_changes_pending = true;
+ set_process(true);
return;
+ }
_update_extensions();
sources_changed.clear();
@@ -1105,15 +1115,17 @@ void EditorFileSystem::_notification(int p_what) {
} break;
case NOTIFICATION_EXIT_TREE: {
- if (use_threads && thread) {
+ Thread *active_thread = thread ? thread : thread_sources;
+ if (use_threads && active_thread) {
//abort thread if in progress
abort_scan = true;
while (scanning) {
OS::get_singleton()->delay_usec(1000);
}
- Thread::wait_to_finish(thread);
- memdelete(thread);
+ Thread::wait_to_finish(active_thread);
+ memdelete(active_thread);
thread = NULL;
+ thread_sources = NULL;
WARN_PRINT("Scan thread aborted...");
set_process(false);
}
@@ -1164,6 +1176,11 @@ void EditorFileSystem::_notification(int p_what) {
_queue_update_script_classes();
first_scan = false;
}
+
+ if (!is_processing() && scan_changes_pending) {
+ scan_changes_pending = false;
+ scan_changes();
+ }
}
} break;
}
@@ -2138,6 +2155,7 @@ EditorFileSystem::EditorFileSystem() {
scan_total = 0;
update_script_classes_queued = false;
first_scan = true;
+ scan_changes_pending = false;
revalidate_import_files = false;
}
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index ce9936f983..381acc0fe2 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -145,6 +145,7 @@ class EditorFileSystem : public Node {
bool scanning;
bool importing;
bool first_scan;
+ bool scan_changes_pending;
float scan_total;
String filesystem_settings_version_for_import;
bool revalidate_import_files;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 7d0601e8db..d6dcbf7e84 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -62,6 +62,8 @@
#include "scene/gui/texture_progress.h"
#include "scene/gui/tool_button.h"
#include "scene/resources/packed_scene.h"
+#include "servers/navigation_2d_server.h"
+#include "servers/navigation_server.h"
#include "servers/physics_2d_server.h"
#include "editor/audio_stream_preview.h"
@@ -110,7 +112,6 @@
#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor/plugins/animation_state_machine_editor.h"
#include "editor/plugins/animation_tree_editor_plugin.h"
-#include "editor/plugins/animation_tree_player_editor_plugin.h"
#include "editor/plugins/asset_library_editor_plugin.h"
#include "editor/plugins/audio_stream_editor_plugin.h"
#include "editor/plugins/baked_lightmap_editor_plugin.h"
@@ -5584,6 +5585,8 @@ EditorNode::EditorNode() {
VisualServer::get_singleton()->textures_keep_original(true);
VisualServer::get_singleton()->set_debug_generate_wireframes(true);
+ NavigationServer::get_singleton()->set_active(false); // no nav by default if editor
+
PhysicsServer::get_singleton()->set_active(false); // no physics by default if editor
Physics2DServer::get_singleton()->set_active(false); // no physics by default if editor
ScriptServer::set_scripting_enabled(false); // no scripting by default if editor
@@ -6646,7 +6649,6 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(MultiMeshEditorPlugin(this)));
add_editor_plugin(memnew(MeshInstanceEditorPlugin(this)));
add_editor_plugin(memnew(AnimationTreeEditorPlugin(this)));
- add_editor_plugin(memnew(AnimationTreePlayerEditorPlugin(this)));
add_editor_plugin(memnew(MeshLibraryEditorPlugin(this)));
add_editor_plugin(memnew(StyleBoxEditorPlugin(this)));
add_editor_plugin(memnew(SpriteEditorPlugin(this)));
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 5041441ac3..684942dbad 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -1782,6 +1782,14 @@ void FileSystemDock::_resource_created() const {
Resource *r = Object::cast_to<Resource>(c);
ERR_FAIL_COND(!r);
+ PackedScene *scene = Object::cast_to<PackedScene>(r);
+ if (scene) {
+ Node *node = memnew(Node);
+ node->set_name("Node");
+ scene->pack(node);
+ memdelete(node);
+ }
+
REF res(r);
editor->push_item(c);
diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp
index 4ab90ad3e4..b24a5c38f2 100644
--- a/editor/find_in_files.cpp
+++ b/editor/find_in_files.cpp
@@ -235,9 +235,11 @@ void FindInFiles::_scan_dir(String path, PoolStringArray &out_folders) {
if (file == "")
break;
- // Ignore special dirs and hidden dirs (such as .git and .import)
+ // Ignore special dirs (such as .git and .import)
if (file == "." || file == ".." || file.begins_with("."))
continue;
+ if (dir->current_is_hidden())
+ continue;
if (dir->current_is_dir())
out_folders.append(file);
diff --git a/editor/icons/icon_animation_tree_player.svg b/editor/icons/icon_animation_tree_player.svg
deleted file mode 100644
index 718eaac2d2..0000000000
--- a/editor/icons/icon_animation_tree_player.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v14h1.166v-2h1.834v2h8v-2h2v2h1v-14h-1v2h-2v-2h-8v2h-1.834v-2zm4 3h2v1 1h1 3v2h-2v1 1h1 1v2h-1-2a1.0001 1.0001 0 0 1 -1-1v-1-2h-1a1.0001 1.0001 0 0 1 -1-1v-1-1zm-2.834 1h1.834v2h-1.834zm9.834 0h2v2h-2zm-9.834 4h1.834v2h-1.834zm9.834 0h2v2h-2z" fill="#cea4f1"/></svg> \ No newline at end of file
diff --git a/editor/icons/icon_godot_docs.svg b/editor/icons/icon_godot_docs.svg
deleted file mode 100644
index e38885aed9..0000000000
--- a/editor/icons/icon_godot_docs.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke-width=".32031" transform="matrix(.017241 0 0 .017241 -.82759 -2.7)"><path d="m0 0s-.325 1.994-.515 1.976l-36.182-3.491c-2.879-.278-5.115-2.574-5.317-5.459l-.994-14.247-27.992-1.997-1.904 12.912c-.424 2.872-2.932 5.037-5.835 5.037h-38.188c-2.902 0-5.41-2.165-5.834-5.037l-1.905-12.912-27.992 1.997-.994 14.247c-.202 2.886-2.438 5.182-5.317 5.46l-36.2 3.49c-.187.018-.324-1.978-.511-1.978l-.049-7.83 30.658-4.944 1.004-14.374c.203-2.91 2.551-5.263 5.463-5.472l38.551-2.75c.146-.01.29-.016.434-.016 2.897 0 5.401 2.166 5.825 5.038l1.959 13.286h28.005l1.959-13.286c.423-2.871 2.93-5.037 5.831-5.037.142 0 .284.005.423.015l38.556 2.75c2.911.209 5.26 2.562 5.463 5.472l1.003 14.374 30.645 4.966z" fill="#fff" transform="matrix(4.1626 0 0 -4.1626 919.24 771.67)"/><path d="m0 0v-59.041c.108-.001.216-.005.323-.015l36.196-3.49c1.896-.183 3.382-1.709 3.514-3.609l1.116-15.978 31.574-2.253 2.175 14.747c.282 1.912 1.922 3.329 3.856 3.329h38.188c1.933 0 3.573-1.417 3.855-3.329l2.175-14.747 31.575 2.253 1.115 15.978c.133 1.9 1.618 3.425 3.514 3.609l36.182 3.49c.107.01.214.014.322.015v4.711l.015.005v54.325h.134c4.795 6.12 9.232 12.569 13.487 19.449-5.651 9.62-12.575 18.217-19.976 26.182-6.864-3.455-13.531-7.369-19.828-11.534-3.151 3.132-6.7 5.694-10.186 8.372-3.425 2.751-7.285 4.768-10.946 7.118 1.09 8.117 1.629 16.108 1.846 24.448-9.446 4.754-19.519 7.906-29.708 10.17-4.068-6.837-7.788-14.241-11.028-21.479-3.842.642-7.702.88-11.567.926v.006c-.027 0-.052-.006-.075-.006-.024 0-.049.006-.073.006v-.006c-3.872-.046-7.729-.284-11.572-.926-3.238 7.238-6.956 14.642-11.03 21.479-10.184-2.264-20.258-5.416-29.703-10.17.216-8.34.755-16.331 1.848-24.448-3.668-2.35-7.523-4.367-10.949-7.118-3.481-2.678-7.036-5.24-10.188-8.372-6.297 4.165-12.962 8.079-19.828 11.534-7.401-7.965-14.321-16.562-19.974-26.182 4.253-6.88 8.693-13.329 13.487-19.449z" fill="#478cbf" transform="matrix(4.1626 0 0 -4.1626 104.7 525.91)"/><path d="m0 0-1.121-16.063c-.135-1.936-1.675-3.477-3.611-3.616l-38.555-2.751c-.094-.007-.188-.01-.281-.01-1.916 0-3.569 1.406-3.852 3.33l-2.211 14.994h-31.459l-2.211-14.994c-.297-2.018-2.101-3.469-4.133-3.32l-38.555 2.751c-1.936.139-3.476 1.68-3.611 3.616l-1.121 16.063-32.547 3.138c.015-3.498.06-7.33.06-8.093 0-34.374 43.605-50.896 97.781-51.086h.133c54.176.19 97.766 16.712 97.766 51.086 0 .777.047 4.593.063 8.093z" fill="#478cbf" transform="matrix(4.1626 0 0 -4.1626 784.07 817.24)"/><path d="m0 0c0-12.052-9.765-21.815-21.813-21.815-12.042 0-21.81 9.763-21.81 21.815 0 12.044 9.768 21.802 21.81 21.802 12.048 0 21.813-9.758 21.813-21.802" fill="#fff" transform="matrix(4.1626 0 0 -4.1626 389.21 625.67)"/><path d="m0 0c0-7.994-6.479-14.473-14.479-14.473-7.996 0-14.479 6.479-14.479 14.473s6.483 14.479 14.479 14.479c8 0 14.479-6.485 14.479-14.479" fill="#414042" transform="matrix(4.1626 0 0 -4.1626 367.37 631.06)"/><path d="m0 0c-3.878 0-7.021 2.858-7.021 6.381v20.081c0 3.52 3.143 6.381 7.021 6.381s7.028-2.861 7.028-6.381v-20.081c0-3.523-3.15-6.381-7.028-6.381" fill="#fff" transform="matrix(4.1626 0 0 -4.1626 511.99 724.74)"/><path d="m0 0c0-12.052 9.765-21.815 21.815-21.815 12.041 0 21.808 9.763 21.808 21.815 0 12.044-9.767 21.802-21.808 21.802-12.05 0-21.815-9.758-21.815-21.802" fill="#fff" transform="matrix(4.1626 0 0 -4.1626 634.79 625.67)"/><path d="m0 0c0-7.994 6.477-14.473 14.471-14.473 8.002 0 14.479 6.479 14.479 14.473s-6.477 14.479-14.479 14.479c-7.994 0-14.471-6.485-14.471-14.479" fill="#414042" transform="matrix(4.1626 0 0 -4.1626 656.64 631.06)"/></g><path d="m4 5a3 3 0 0 0 -3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3h2a3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0 -3-3 3 3 0 0 0 -2.8262 2h-2.3496a3 3 0 0 0 -2.8242-2zm0 1a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2-2 2 2 0 0 1 2-2zm8 0a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2-2 2 2 0 0 1 2-2z"/></svg> \ No newline at end of file
diff --git a/editor/icons/icon_particle_attractor_2d.svg b/editor/icons/icon_particle_attractor_2d.svg
deleted file mode 100644
index 85f289dc4b..0000000000
--- a/editor/icons/icon_particle_attractor_2d.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1a3 7 0 0 0 -2.0801 1.9668 7 3 45 0 0 -2.8691.083984 7 3 45 0 0 -.080078 2.8633 7 3 0 0 0 -1.9707 2.0859 7 3 0 0 0 1.9668 2.0801 3 7 45 0 0 .083984 2.8691 3 7 45 0 0 2.8633.080078 3 7 0 0 0 2.0859 1.9707 3 7 0 0 0 2.0801-1.9668 7 3 45 0 0 2.8691-.083984 7 3 45 0 0 .080078-2.8633 7 3 0 0 0 1.9707-2.0859 7 3 0 0 0 -1.9668-2.0801 3 7 45 0 0 -.083984-2.8691 3 7 45 0 0 -2.8633-.080078 3 7 0 0 0 -2.0859-1.9707zm0 1a2 6 0 0 1 1.2598 1.3438 3 7 45 0 0 -1.2578.75977 7 3 45 0 0 -1.2637-.75781 2 6 0 0 1 1.2617-1.3457zm-3.6348 1.5293a6 2 45 0 1 1.2344.28906 3 7 0 0 0 -.35352 1.4238 7 3 0 0 0 -1.4297.35742 6 2 45 0 1 -.058594-1.8418 6 2 45 0 1 .60742-.22852zm7.0762.0039062a2 6 45 0 1 .80078.22461 2 6 45 0 1 -.060547 1.8418 7 3 0 0 0 -1.4238-.35352 3 7 0 0 0 -.35742-1.4297 2 6 45 0 1 1.041-.2832zm-4.998.70703a6 2 45 0 1 .74023.4707 3 7 45 0 0 -.41211.33984 7 3 0 0 0 -.52344.048828 2 6 0 0 1 .19531-.85938zm3.1152.0019531a2 6 0 0 1 .18945.85547 7 3 0 0 0 -.5293-.050781 7 3 45 0 0 -.4043-.33594 2 6 45 0 1 .74414-.46875zm-1.5586 1.7578a6 2 0 0 1 .82031.021484 6 2 45 0 1 .59375.56445 6 2 45 0 1 .56445.59375 2 6 0 0 1 .021484.82031 2 6 0 0 1 -.021484.82031 2 6 45 0 1 -.56445.59375 2 6 45 0 1 -.59375.56445 6 2 0 0 1 -.82031.021484 6 2 0 0 1 -.82031-.021484 6 2 45 0 1 -.59375-.56445 6 2 45 0 1 -.56445-.59375 2 6 0 0 1 -.021484-.82031 2 6 0 0 1 .021484-.82031 2 6 45 0 1 .56445-.59375 2 6 45 0 1 .59375-.56445 6 2 0 0 1 .82031-.021484zm2.9004.24805a6 2 0 0 1 .85938.19531 2 6 45 0 1 -.4707.74023 7 3 45 0 0 -.33984-.41211 3 7 0 0 0 -.048828-.52344zm-5.8027.0039062a3 7 0 0 0 -.050781.5293 3 7 45 0 0 -.33594.4043 6 2 45 0 1 -.46875-.74414 6 2 0 0 1 .85547-.18945zm7.5566.48633a6 2 0 0 1 1.3457 1.2617 6 2 0 0 1 -1.3438 1.2598 7 3 45 0 0 -.75977-1.2578 3 7 45 0 0 .75781-1.2637zm-9.3105.0019532a7 3 45 0 0 .75977 1.2578 3 7 45 0 0 -.75781 1.2637 6 2 0 0 1 -1.3457-1.2617 6 2 0 0 1 1.3438-1.2598zm4.6562.25977a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm3.2891 1.8145a6 2 45 0 1 .46875.74414 6 2 0 0 1 -.85547.18945 3 7 0 0 0 .050781-.5293 3 7 45 0 0 .33594-.4043zm-6.5781.0019531a7 3 45 0 0 .33984.41211 3 7 0 0 0 .048828.52344 6 2 0 0 1 -.85938-.19531 2 6 45 0 1 .4707-.74023zm-.89258 1.584a7 3 0 0 0 1.4238.35352 3 7 0 0 0 .35742 1.4297 2 6 45 0 1 -1.8418.058594 2 6 45 0 1 .060547-1.8418zm8.3652 0a6 2 45 0 1 .058594 1.8418 6 2 45 0 1 -1.8418-.060547 3 7 0 0 0 .35352-1.4238 7 3 0 0 0 1.4297-.35742zm-2.4316.5a2 6 0 0 1 -.19531.85938 6 2 45 0 1 -.74023-.4707 3 7 45 0 0 .41211-.33984 7 3 0 0 0 .52344-.048828zm-3.5.001953a7 3 0 0 0 .5293.050781 7 3 45 0 0 .4043.33594 2 6 45 0 1 -.74414.46875 2 6 0 0 1 -.18945-.85547zm1.7461.99414a7 3 45 0 0 1.2637.75781 2 6 0 0 1 -1.2617 1.3457 2 6 0 0 1 -1.2598-1.3438 3 7 45 0 0 1.2578-.75977z" fill="#a5b7f3" fill-opacity=".98824"/></svg> \ No newline at end of file
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index b65482cc6b..ecd1e341d1 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -337,6 +337,7 @@ void InspectorDock::_notification(int p_what) {
history_menu->set_icon(get_icon("History", "EditorIcons"));
object_menu->set_icon(get_icon("Tools", "EditorIcons"));
warning->set_icon(get_icon("NodeWarning", "EditorIcons"));
+ warning->add_color_override("font_color", get_color("warning_color", "Editor"));
} break;
}
}
@@ -584,6 +585,7 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
add_child(warning);
warning->set_text(TTR("Changes may be lost!"));
warning->set_icon(get_icon("NodeWarning", "EditorIcons"));
+ warning->add_color_override("font_color", get_color("warning_color", "Editor"));
warning->set_clip_text(true);
warning->hide();
warning->connect("pressed", this, "_warning_pressed");
diff --git a/editor/plugins/animation_tree_player_editor_plugin.cpp b/editor/plugins/animation_tree_player_editor_plugin.cpp
deleted file mode 100644
index 2b365feec5..0000000000
--- a/editor/plugins/animation_tree_player_editor_plugin.cpp
+++ /dev/null
@@ -1,1451 +0,0 @@
-/*************************************************************************/
-/* animation_tree_player_editor_plugin.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 "animation_tree_player_editor_plugin.h"
-
-#include "core/io/resource_loader.h"
-#include "core/os/input.h"
-#include "core/os/keyboard.h"
-#include "core/project_settings.h"
-#include "editor/editor_scale.h"
-#include "scene/gui/menu_button.h"
-#include "scene/gui/panel.h"
-#include "scene/main/viewport.h"
-
-void AnimationTreePlayerEditor::edit(AnimationTreePlayer *p_anim_tree) {
-
- anim_tree = p_anim_tree;
-
- if (!anim_tree) {
- hide();
- } else {
- order.clear();
- p_anim_tree->get_node_list(&order);
- /*
- for(List<StringName>::Element* E=order.front();E;E=E->next()) {
-
- if (E->get() >= (int)last_id)
- last_id=E->get()+1;
- }*/
- play_button->set_pressed(p_anim_tree->is_active());
- //read the orders
- }
-}
-
-Size2 AnimationTreePlayerEditor::_get_maximum_size() {
-
- Size2 max;
-
- for (List<StringName>::Element *E = order.front(); E; E = E->next()) {
-
- Point2 pos = anim_tree->node_get_position(E->get());
-
- if (click_type == CLICK_NODE && click_node == E->get()) {
-
- pos += click_motion - click_pos;
- }
- pos += get_node_size(E->get());
- if (pos.x > max.x)
- max.x = pos.x;
- if (pos.y > max.y)
- max.y = pos.y;
- }
-
- return max;
-}
-
-const char *AnimationTreePlayerEditor::_node_type_names[] = { "Output", "Animation", "OneShot", "Mix", "Blend2", "Blend3", "Blend4", "TimeScale", "TimeSeek", "Transition" };
-
-Size2 AnimationTreePlayerEditor::get_node_size(const StringName &p_node) const {
-
- AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node);
-
- Ref<StyleBox> style = get_stylebox("panel", "PopupMenu");
- Ref<Font> font = get_font("font", "PopupMenu");
-
- Size2 size = style->get_minimum_size();
-
- int count = 2; // title and name
- int inputs = anim_tree->node_get_input_count(p_node);
- count += inputs ? inputs : 1;
- String name = p_node;
-
- float name_w = font->get_string_size(name).width;
- float type_w = font->get_string_size(String(_node_type_names[type])).width;
- float max_w = MAX(name_w, type_w);
-
- switch (type) {
- case AnimationTreePlayer::NODE_TIMESEEK:
- case AnimationTreePlayer::NODE_OUTPUT: {
- } break;
- case AnimationTreePlayer::NODE_ANIMATION:
- case AnimationTreePlayer::NODE_ONESHOT:
- case AnimationTreePlayer::NODE_MIX:
- case AnimationTreePlayer::NODE_BLEND2:
- case AnimationTreePlayer::NODE_BLEND3:
- case AnimationTreePlayer::NODE_BLEND4:
- case AnimationTreePlayer::NODE_TIMESCALE:
- case AnimationTreePlayer::NODE_TRANSITION: {
-
- size.height += font->get_height();
- } break;
- case AnimationTreePlayer::NODE_MAX: {
- }
- }
-
- size.x += max_w + 20;
- size.y += count * (font->get_height() + get_constant("vseparation", "PopupMenu"));
-
- return size;
-}
-
-void AnimationTreePlayerEditor::_edit_dialog_changede(String) {
-
- edit_dialog->hide();
-}
-
-void AnimationTreePlayerEditor::_edit_dialog_changeds(String s) {
-
- _edit_dialog_changed();
-}
-
-void AnimationTreePlayerEditor::_edit_dialog_changedf(float) {
-
- _edit_dialog_changed();
-}
-
-void AnimationTreePlayerEditor::_edit_dialog_changed() {
-
- if (updating_edit)
- return;
-
- if (renaming_edit) {
-
- if (anim_tree->node_rename(edited_node, edit_line[0]->get_text()) == OK) {
- for (List<StringName>::Element *E = order.front(); E; E = E->next()) {
-
- if (E->get() == edited_node)
- E->get() = edit_line[0]->get_text();
- }
- edited_node = edit_line[0]->get_text();
- }
- update();
- return;
- }
-
- AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node);
-
- switch (type) {
-
- case AnimationTreePlayer::NODE_TIMESCALE:
- anim_tree->timescale_node_set_scale(edited_node, edit_line[0]->get_text().to_double());
- break;
- case AnimationTreePlayer::NODE_ONESHOT:
- anim_tree->oneshot_node_set_fadein_time(edited_node, edit_line[0]->get_text().to_double());
- anim_tree->oneshot_node_set_fadeout_time(edited_node, edit_line[1]->get_text().to_double());
- anim_tree->oneshot_node_set_autorestart_delay(edited_node, edit_line[2]->get_text().to_double());
- anim_tree->oneshot_node_set_autorestart_random_delay(edited_node, edit_line[3]->get_text().to_double());
- anim_tree->oneshot_node_set_autorestart(edited_node, edit_check->is_pressed());
- anim_tree->oneshot_node_set_mix_mode(edited_node, edit_option->get_selected());
-
- break;
-
- case AnimationTreePlayer::NODE_MIX:
-
- anim_tree->mix_node_set_amount(edited_node, edit_scroll[0]->get_value());
- break;
- case AnimationTreePlayer::NODE_BLEND2:
- anim_tree->blend2_node_set_amount(edited_node, edit_scroll[0]->get_value());
-
- break;
-
- case AnimationTreePlayer::NODE_BLEND3:
- anim_tree->blend3_node_set_amount(edited_node, edit_scroll[0]->get_value());
-
- break;
- case AnimationTreePlayer::NODE_BLEND4:
-
- anim_tree->blend4_node_set_amount(edited_node, Point2(edit_scroll[0]->get_value(), edit_scroll[1]->get_value()));
-
- break;
-
- case AnimationTreePlayer::NODE_TRANSITION: {
- anim_tree->transition_node_set_xfade_time(edited_node, edit_line[0]->get_text().to_double());
- if (anim_tree->transition_node_get_current(edited_node) != edit_option->get_selected())
- anim_tree->transition_node_set_current(edited_node, edit_option->get_selected());
- } break;
- default: {
- }
- }
-}
-
-void AnimationTreePlayerEditor::_edit_dialog_animation_changed() {
-
- Ref<Animation> anim = property_editor->get_variant().operator RefPtr();
- anim_tree->animation_node_set_animation(edited_node, anim);
- update();
-}
-
-void AnimationTreePlayerEditor::_edit_dialog_edit_animation() {
-
- if (Engine::get_singleton()->is_editor_hint()) {
- get_tree()->get_root()->get_child(0)->call("_resource_selected", property_editor->get_variant().operator RefPtr());
- };
-};
-
-void AnimationTreePlayerEditor::_edit_oneshot_start() {
-
- anim_tree->oneshot_node_start(edited_node);
-}
-
-void AnimationTreePlayerEditor::_play_toggled() {
-
- anim_tree->set_active(play_button->is_pressed());
-}
-
-void AnimationTreePlayerEditor::_master_anim_menu_item(int p_item) {
-
- if (p_item == 0)
- _edit_filters();
- else {
-
- String str = master_anim_popup->get_item_text(p_item);
- anim_tree->animation_node_set_master_animation(edited_node, str);
- }
- update();
-}
-
-void AnimationTreePlayerEditor::_popup_edit_dialog() {
-
- updating_edit = true;
-
- for (int i = 0; i < 2; i++)
- edit_scroll[i]->hide();
-
- for (int i = 0; i < 4; i++) {
-
- edit_line[i]->hide();
- edit_label[i]->hide();
- }
-
- edit_option->hide();
- edit_button->hide();
- filter_button->hide();
- edit_check->hide();
-
- Point2 pos = anim_tree->node_get_position(edited_node) - Point2(h_scroll->get_value(), v_scroll->get_value());
- Ref<StyleBox> style = get_stylebox("panel", "PopupMenu");
- Size2 size = get_node_size(edited_node);
- Point2 popup_pos(pos.x + style->get_margin(MARGIN_LEFT), pos.y + size.y - style->get_margin(MARGIN_BOTTOM));
- popup_pos += get_global_position();
-
- if (renaming_edit) {
-
- edit_label[0]->set_text(TTR("New name:"));
- edit_label[0]->set_position(Point2(5, 5));
- edit_label[0]->show();
- edit_line[0]->set_begin(Point2(15, 25));
- edit_line[0]->set_text(edited_node);
- edit_line[0]->show();
- edit_dialog->set_size(Size2(150, 50));
-
- } else {
-
- AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node);
-
- switch (type) {
-
- case AnimationTreePlayer::NODE_ANIMATION:
-
- if (anim_tree->get_master_player() != NodePath() && anim_tree->has_node(anim_tree->get_master_player()) && Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player()))) {
-
- AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player()));
- master_anim_popup->clear();
- master_anim_popup->add_item(TTR("Edit Filters"));
- master_anim_popup->add_separator();
- List<StringName> sn;
- ap->get_animation_list(&sn);
- sn.sort_custom<StringName::AlphCompare>();
- for (List<StringName>::Element *E = sn.front(); E; E = E->next()) {
- master_anim_popup->add_item(E->get());
- }
-
- master_anim_popup->set_position(popup_pos);
- master_anim_popup->popup();
- } else {
- property_editor->edit(this, "", Variant::OBJECT, anim_tree->animation_node_get_animation(edited_node), PROPERTY_HINT_RESOURCE_TYPE, "Animation");
- property_editor->set_position(popup_pos);
- property_editor->popup();
- updating_edit = false;
- }
- return;
- case AnimationTreePlayer::NODE_TIMESCALE:
- edit_label[0]->set_text(TTR("Scale:"));
- edit_label[0]->set_position(Point2(5, 5));
- edit_label[0]->show();
- edit_line[0]->set_begin(Point2(15, 25));
- edit_line[0]->set_text(rtos(anim_tree->timescale_node_get_scale(edited_node)));
- edit_line[0]->show();
- edit_dialog->set_size(Size2(150, 50));
- break;
- case AnimationTreePlayer::NODE_ONESHOT:
- edit_label[0]->set_text(TTR("Fade In (s):"));
- edit_label[0]->set_position(Point2(5, 5));
- edit_label[0]->show();
- edit_line[0]->set_begin(Point2(15, 25));
- edit_line[0]->set_text(rtos(anim_tree->oneshot_node_get_fadein_time(edited_node)));
- edit_line[0]->show();
- edit_label[1]->set_text(TTR("Fade Out (s):"));
- edit_label[1]->set_position(Point2(5, 55));
- edit_label[1]->show();
- edit_line[1]->set_begin(Point2(15, 75));
- edit_line[1]->set_text(rtos(anim_tree->oneshot_node_get_fadeout_time(edited_node)));
- edit_line[1]->show();
-
- edit_option->clear();
- edit_option->add_item(TTR("Blend"), 0);
- edit_option->add_item(TTR("Mix"), 1);
- edit_option->set_begin(Point2(15, 105));
-
- edit_option->select(anim_tree->oneshot_node_get_mix_mode(edited_node));
- edit_option->show();
-
- edit_check->set_text(TTR("Auto Restart:"));
- edit_check->set_begin(Point2(15, 125));
- edit_check->set_pressed(anim_tree->oneshot_node_has_autorestart(edited_node));
- edit_check->show();
-
- edit_label[2]->set_text(TTR("Restart (s):"));
- edit_label[2]->set_position(Point2(5, 145));
- edit_label[2]->show();
- edit_line[2]->set_begin(Point2(15, 165));
- edit_line[2]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_delay(edited_node)));
- edit_line[2]->show();
- edit_label[3]->set_text(TTR("Random Restart (s):"));
- edit_label[3]->set_position(Point2(5, 195));
- edit_label[3]->show();
- edit_line[3]->set_begin(Point2(15, 215));
- edit_line[3]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_random_delay(edited_node)));
- edit_line[3]->show();
-
- filter_button->set_begin(Point2(10, 245));
- filter_button->show();
-
- edit_button->set_begin(Point2(10, 268));
- edit_button->set_text(TTR("Start!"));
-
- edit_button->show();
-
- edit_dialog->set_size(Size2(180, 293));
-
- break;
-
- case AnimationTreePlayer::NODE_MIX:
-
- edit_label[0]->set_text(TTR("Amount:"));
- edit_label[0]->set_position(Point2(5, 5));
- edit_label[0]->show();
- edit_scroll[0]->set_min(0);
- edit_scroll[0]->set_max(1);
- edit_scroll[0]->set_step(0.01);
- edit_scroll[0]->set_value(anim_tree->mix_node_get_amount(edited_node));
- edit_scroll[0]->set_begin(Point2(15, 25));
- edit_scroll[0]->show();
- edit_dialog->set_size(Size2(150, 50));
-
- break;
- case AnimationTreePlayer::NODE_BLEND2:
- edit_label[0]->set_text(TTR("Blend:"));
- edit_label[0]->set_position(Point2(5, 5));
- edit_label[0]->show();
- edit_scroll[0]->set_min(0);
- edit_scroll[0]->set_max(1);
- edit_scroll[0]->set_step(0.01);
- edit_scroll[0]->set_value(anim_tree->blend2_node_get_amount(edited_node));
- edit_scroll[0]->set_begin(Point2(15, 25));
- edit_scroll[0]->show();
- filter_button->set_begin(Point2(10, 47));
- filter_button->show();
- edit_dialog->set_size(Size2(150, 74));
-
- break;
-
- case AnimationTreePlayer::NODE_BLEND3:
- edit_label[0]->set_text(TTR("Blend:"));
- edit_label[0]->set_position(Point2(5, 5));
- edit_label[0]->show();
- edit_scroll[0]->set_min(-1);
- edit_scroll[0]->set_max(1);
- edit_scroll[0]->set_step(0.01);
- edit_scroll[0]->set_value(anim_tree->blend3_node_get_amount(edited_node));
- edit_scroll[0]->set_begin(Point2(15, 25));
- edit_scroll[0]->show();
- edit_dialog->set_size(Size2(150, 50));
-
- break;
- case AnimationTreePlayer::NODE_BLEND4:
-
- edit_label[0]->set_text(TTR("Blend 0:"));
- edit_label[0]->set_position(Point2(5, 5));
- edit_label[0]->show();
- edit_scroll[0]->set_min(0);
- edit_scroll[0]->set_max(1);
- edit_scroll[0]->set_step(0.01);
- edit_scroll[0]->set_value(anim_tree->blend4_node_get_amount(edited_node).x);
- edit_scroll[0]->set_begin(Point2(15, 25));
- edit_scroll[0]->show();
- edit_label[1]->set_text(TTR("Blend 1:"));
- edit_label[1]->set_position(Point2(5, 55));
- edit_label[1]->show();
- edit_scroll[1]->set_min(0);
- edit_scroll[1]->set_max(1);
- edit_scroll[1]->set_step(0.01);
- edit_scroll[1]->set_value(anim_tree->blend4_node_get_amount(edited_node).y);
- edit_scroll[1]->set_begin(Point2(15, 75));
- edit_scroll[1]->show();
- edit_dialog->set_size(Size2(150, 100));
-
- break;
-
- case AnimationTreePlayer::NODE_TRANSITION: {
-
- edit_label[0]->set_text(TTR("X-Fade Time (s):"));
- edit_label[0]->set_position(Point2(5, 5));
- edit_label[0]->show();
- edit_line[0]->set_begin(Point2(15, 25));
- edit_line[0]->set_text(rtos(anim_tree->transition_node_get_xfade_time(edited_node)));
- edit_line[0]->show();
-
- edit_label[1]->set_text(TTR("Current:"));
- edit_label[1]->set_position(Point2(5, 55));
- edit_label[1]->show();
- edit_option->set_begin(Point2(15, 75));
-
- edit_option->clear();
-
- for (int i = 0; i < anim_tree->transition_node_get_input_count(edited_node); i++) {
- edit_option->add_item(itos(i), i);
- }
-
- edit_option->select(anim_tree->transition_node_get_current(edited_node));
- edit_option->show();
- edit_dialog->set_size(Size2(150, 100));
-
- } break;
- default: {
- }
- }
- }
-
- edit_dialog->set_position(popup_pos);
- edit_dialog->popup();
-
- updating_edit = false;
-}
-
-void AnimationTreePlayerEditor::_draw_node(const StringName &p_node) {
-
- RID ci = get_canvas_item();
- AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node);
-
- Ref<StyleBox> style = get_stylebox("panel", "PopupMenu");
- Ref<Font> font = get_font("font", "PopupMenu");
- Color font_color = get_color("font_color", "PopupMenu");
- Color font_color_title = get_color("font_color_hover", "PopupMenu");
- font_color_title.a *= 0.8;
- Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons");
-
- Size2 size = get_node_size(p_node);
- Point2 pos = anim_tree->node_get_position(p_node);
- if (click_type == CLICK_NODE && click_node == p_node) {
-
- pos += click_motion - click_pos;
- if (pos.x < 5)
- pos.x = 5;
- if (pos.y < 5)
- pos.y = 5;
- }
-
- pos -= Point2(h_scroll->get_value(), v_scroll->get_value());
-
- style->draw(ci, Rect2(pos, size));
-
- float w = size.width - style->get_minimum_size().width;
- float h = font->get_height() + get_constant("vseparation", "PopupMenu");
-
- Point2 ofs = style->get_offset() + pos;
- Point2 ascofs(0, font->get_ascent());
-
- Color bx = font_color_title;
- bx.a *= 0.1;
- draw_rect(Rect2(ofs, Size2(size.width - style->get_minimum_size().width, font->get_height())), bx);
- font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, String(_node_type_names[type]), font_color_title);
-
- ofs.y += h;
- font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, p_node, font_color);
- ofs.y += h;
-
- int inputs = anim_tree->node_get_input_count(p_node);
-
- float icon_h_ofs = Math::floor((font->get_height() - slot_icon->get_height()) / 2.0) + 1;
-
- if (type != AnimationTreePlayer::NODE_OUTPUT)
- slot_icon->draw(ci, ofs + Point2(w, icon_h_ofs)); //output
-
- if (inputs) {
- for (int i = 0; i < inputs; i++) {
-
- slot_icon->draw(ci, ofs + Point2(-slot_icon->get_width(), icon_h_ofs));
- String text;
- switch (type) {
-
- case AnimationTreePlayer::NODE_TIMESCALE:
- case AnimationTreePlayer::NODE_TIMESEEK: text = "in"; break;
- case AnimationTreePlayer::NODE_OUTPUT: text = "out"; break;
- case AnimationTreePlayer::NODE_ANIMATION: break;
- case AnimationTreePlayer::NODE_ONESHOT: text = (i == 0 ? "in" : "add"); break;
- case AnimationTreePlayer::NODE_BLEND2:
- case AnimationTreePlayer::NODE_MIX: text = (i == 0 ? "a" : "b"); break;
- case AnimationTreePlayer::NODE_BLEND3:
- switch (i) {
- case 0: text = "b-"; break;
- case 1: text = "a"; break;
- case 2: text = "b+"; break;
- }
- break;
-
- case AnimationTreePlayer::NODE_BLEND4:
- switch (i) {
- case 0: text = "a0"; break;
- case 1: text = "b0"; break;
- case 2: text = "a1"; break;
- case 3: text = "b1"; break;
- }
- break;
-
- case AnimationTreePlayer::NODE_TRANSITION:
- text = itos(i);
- if (anim_tree->transition_node_has_input_auto_advance(p_node, i))
- text += "->";
-
- break;
- default: {
- }
- }
- font->draw(ci, ofs + ascofs + Point2(3, 0), text, font_color);
-
- ofs.y += h;
- }
- } else {
- ofs.y += h;
- }
-
- Ref<StyleBox> pg_bg = get_stylebox("bg", "ProgressBar");
- Ref<StyleBox> pg_fill = get_stylebox("fill", "ProgressBar");
- Rect2 pg_rect(ofs, Size2(w, h));
-
- bool editable = true;
- switch (type) {
- case AnimationTreePlayer::NODE_ANIMATION: {
-
- Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node);
- String text;
- if (anim_tree->animation_node_get_master_animation(p_node) != "")
- text = anim_tree->animation_node_get_master_animation(p_node);
- else if (anim.is_null())
- text = "load...";
- else
- text = anim->get_name();
-
- font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, text, font_color_title);
-
- } break;
- case AnimationTreePlayer::NODE_ONESHOT:
- case AnimationTreePlayer::NODE_MIX:
- case AnimationTreePlayer::NODE_BLEND2:
- case AnimationTreePlayer::NODE_BLEND3:
- case AnimationTreePlayer::NODE_BLEND4:
- case AnimationTreePlayer::NODE_TIMESCALE:
- case AnimationTreePlayer::NODE_TRANSITION: {
-
- font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit...", font_color_title);
- } break;
- default: editable = false;
- }
-
- if (editable) {
-
- Ref<Texture> arrow = get_icon("GuiDropdown", "EditorIcons");
- Point2 arrow_ofs(w - arrow->get_width(), Math::floor((h - arrow->get_height()) / 2));
- arrow->draw(ci, ofs + arrow_ofs);
- }
-}
-
-AnimationTreePlayerEditor::ClickType AnimationTreePlayerEditor::_locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const {
-
- Ref<StyleBox> style = get_stylebox("panel", "PopupMenu");
- Ref<Font> font = get_font("font", "PopupMenu");
-
- float h = (font->get_height() + get_constant("vseparation", "PopupMenu"));
-
- for (const List<StringName>::Element *E = order.back(); E; E = E->prev()) {
-
- const StringName &node = E->get();
-
- AnimationTreePlayer::NodeType type = anim_tree->node_get_type(node);
-
- Point2 pos = anim_tree->node_get_position(node);
- Size2 size = get_node_size(node);
-
- pos -= Point2(h_scroll->get_value(), v_scroll->get_value());
-
- if (!Rect2(pos, size).has_point(p_click))
- continue;
-
- if (p_node_id)
- *p_node_id = node;
-
- pos = p_click - pos;
-
- float y = pos.y - style->get_offset().height;
-
- if (y < 2 * h)
- return CLICK_NODE;
- y -= 2 * h;
-
- int inputs = anim_tree->node_get_input_count(node);
- int count = MAX(inputs, 1);
-
- if (inputs == 0 || (pos.x > size.width / 2 && type != AnimationTreePlayer::NODE_OUTPUT)) {
-
- if (y < count * h) {
-
- if (p_slot_index)
- *p_slot_index = 0;
- return CLICK_OUTPUT_SLOT;
- }
- }
-
- for (int i = 0; i < count; i++) {
-
- if (y < h) {
- if (p_slot_index)
- *p_slot_index = i;
- return CLICK_INPUT_SLOT;
- }
- y -= h;
- }
-
- bool has_parameters = type != AnimationTreePlayer::NODE_OUTPUT && type != AnimationTreePlayer::NODE_TIMESEEK;
- return has_parameters ? CLICK_PARAMETER : CLICK_NODE;
- }
-
- return CLICK_NONE;
-}
-
-Point2 AnimationTreePlayerEditor::_get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot) {
-
- Ref<StyleBox> style = get_stylebox("panel", "PopupMenu");
- Ref<Font> font = get_font("font", "PopupMenu");
- Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons");
-
- Size2 size = get_node_size(p_node_id);
- Point2 pos = anim_tree->node_get_position(p_node_id);
-
- if (click_type == CLICK_NODE && click_node == p_node_id) {
-
- pos += click_motion - click_pos;
- if (pos.x < 5)
- pos.x = 5;
- if (pos.y < 5)
- pos.y = 5;
- }
-
- pos -= Point2(h_scroll->get_value(), v_scroll->get_value());
-
- float w = size.width - style->get_minimum_size().width;
- float h = font->get_height() + get_constant("vseparation", "PopupMenu");
-
- pos += style->get_offset();
-
- pos.y += h * 2;
-
- pos.y += h * p_slot;
-
- pos += Point2(-slot_icon->get_width() / 2.0, h / 2.0).floor();
-
- if (!p_input) {
- pos.x += w + slot_icon->get_width();
- }
-
- return pos;
-}
-
-void AnimationTreePlayerEditor::_gui_input(Ref<InputEvent> p_event) {
-
- Ref<InputEventMouseButton> mb = p_event;
-
- if (mb.is_valid()) {
-
- if (mb->is_pressed()) {
-
- if (mb->get_button_index() == 1) {
- click_pos = Point2(mb->get_position().x, mb->get_position().y);
- click_motion = click_pos;
- click_type = _locate_click(click_pos, &click_node, &click_slot);
- if (click_type != CLICK_NONE) {
-
- order.erase(click_node);
- order.push_back(click_node);
- update();
- }
-
- switch (click_type) {
- case CLICK_INPUT_SLOT: {
- click_pos = _get_slot_pos(click_node, true, click_slot);
- } break;
- case CLICK_OUTPUT_SLOT: {
- click_pos = _get_slot_pos(click_node, false, click_slot);
- } break;
- case CLICK_PARAMETER: {
-
- edited_node = click_node;
- renaming_edit = false;
- _popup_edit_dialog();
- //open editor
- //_node_edit_property(click_node);
- } break;
- default: {
- }
- }
- }
- if (mb->get_button_index() == 2) {
-
- if (click_type != CLICK_NONE) {
- click_type = CLICK_NONE;
- update();
- } else {
- // try to disconnect/remove
-
- Point2 rclick_pos = Point2(mb->get_position().x, mb->get_position().y);
- rclick_type = _locate_click(rclick_pos, &rclick_node, &rclick_slot);
- if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) {
-
- node_popup->clear();
- node_popup->set_size(Size2(1, 1));
- node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT);
- if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) {
- node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT);
- if (rclick_type == CLICK_INPUT_SLOT) {
- if (anim_tree->transition_node_has_input_auto_advance(rclick_node, rclick_slot))
- node_popup->add_item(TTR("Clear Auto-Advance"), NODE_CLEAR_AUTOADVANCE);
- else
- node_popup->add_item(TTR("Set Auto-Advance"), NODE_SET_AUTOADVANCE);
- node_popup->add_item(TTR("Delete Input"), NODE_DELETE_INPUT);
- }
- }
-
- node_popup->set_position(rclick_pos + get_global_position());
- node_popup->popup();
- }
-
- if (rclick_type == CLICK_NODE) {
- node_popup->clear();
- node_popup->set_size(Size2(1, 1));
- node_popup->add_item(TTR("Rename"), NODE_RENAME);
- node_popup->add_item(TTR("Remove"), NODE_ERASE);
- if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION)
- node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT);
- node_popup->set_position(rclick_pos + get_global_position());
- node_popup->popup();
- }
- }
- }
- } else {
-
- if (mb->get_button_index() == 1 && click_type != CLICK_NONE) {
-
- switch (click_type) {
- case CLICK_INPUT_SLOT:
- case CLICK_OUTPUT_SLOT: {
-
- Point2 dst_click_pos = Point2(mb->get_position().x, mb->get_position().y);
- StringName id;
- int slot;
- ClickType dst_click_type = _locate_click(dst_click_pos, &id, &slot);
-
- if (dst_click_type == CLICK_INPUT_SLOT && click_type == CLICK_OUTPUT_SLOT) {
-
- anim_tree->connect_nodes(click_node, id, slot);
- }
- if (click_type == CLICK_INPUT_SLOT && dst_click_type == CLICK_OUTPUT_SLOT) {
-
- anim_tree->connect_nodes(id, click_node, click_slot);
- }
-
- } break;
- case CLICK_NODE: {
- Point2 new_pos = anim_tree->node_get_position(click_node) + (click_motion - click_pos);
- if (new_pos.x < 5)
- new_pos.x = 5;
- if (new_pos.y < 5)
- new_pos.y = 5;
- anim_tree->node_set_position(click_node, new_pos);
-
- } break;
- default: {
- }
- }
-
- click_type = CLICK_NONE;
- update();
- }
- }
- }
-
- Ref<InputEventMouseMotion> mm = p_event;
-
- if (mm.is_valid()) {
-
- if (mm->get_button_mask() & 1 && click_type != CLICK_NONE) {
-
- click_motion = Point2(mm->get_position().x, mm->get_position().y);
- update();
- }
- if (mm->get_button_mask() & 4 || Input::get_singleton()->is_key_pressed(KEY_SPACE)) {
-
- h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x);
- v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y);
- update();
- }
- }
-}
-
-void AnimationTreePlayerEditor::_draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color) {
-
- static const int steps = 20;
-
- Rect2 r;
- r.position = p_from;
- r.expand_to(p_to);
- Vector2 sign = Vector2((p_from.x < p_to.x) ? 1 : -1, (p_from.y < p_to.y) ? 1 : -1);
- bool flip = sign.x * sign.y < 0;
-
- Vector2 prev;
- for (int i = 0; i <= steps; i++) {
-
- float d = i / float(steps);
- float c = -Math::cos(d * Math_PI) * 0.5 + 0.5;
- if (flip)
- c = 1.0 - c;
- Vector2 p = r.position + Vector2(d * r.size.width, c * r.size.height);
-
- if (i > 0) {
-
- draw_line(prev, p, p_color, 2);
- }
-
- prev = p;
- }
-}
-
-void AnimationTreePlayerEditor::_notification(int p_what) {
-
- switch (p_what) {
-
- case NOTIFICATION_ENTER_TREE: {
-
- play_button->set_icon(get_icon("Play", "EditorIcons"));
- add_menu->set_icon(get_icon("Add", "EditorIcons"));
- } break;
- case NOTIFICATION_DRAW: {
-
- _update_scrollbars();
- //VisualServer::get_singleton()->canvas_item_add_rect(get_canvas_item(),Rect2(Point2(),get_size()),Color(0,0,0,1));
- get_stylebox("bg", "Tree")->draw(get_canvas_item(), Rect2(Point2(), get_size()));
-
- for (List<StringName>::Element *E = order.front(); E; E = E->next()) {
-
- _draw_node(E->get());
- }
-
- if (click_type == CLICK_INPUT_SLOT || click_type == CLICK_OUTPUT_SLOT) {
-
- _draw_cos_line(click_pos, click_motion, Color(0.5, 1, 0.5, 0.8));
- }
-
- List<AnimationTreePlayer::Connection> connections;
- anim_tree->get_connection_list(&connections);
-
- for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) {
-
- const AnimationTreePlayer::Connection &c = E->get();
- Point2 source = _get_slot_pos(c.src_node, false, 0);
- Point2 dest = _get_slot_pos(c.dst_node, true, c.dst_input);
- Color col = Color(1, 1, 0.5, 0.8);
- /*
- if (click_type==CLICK_NODE && click_node==c.src_node) {
-
- source+=click_motion-click_pos;
- }
-
- if (click_type==CLICK_NODE && click_node==c.dst_node) {
-
- dest+=click_motion-click_pos;
- }*/
-
- _draw_cos_line(source, dest, col);
- }
-
- const Ref<Font> f = get_font("font", "Label");
- const Point2 status_offset = Point2(5, 25) * EDSCALE + Point2(0, f->get_ascent());
-
- switch (anim_tree->get_last_error()) {
-
- case AnimationTreePlayer::CONNECT_OK: {
-
- f->draw(get_canvas_item(), status_offset, TTR("Animation tree is valid."), Color(0, 1, 0.6, 0.8));
- } break;
- default: {
-
- f->draw(get_canvas_item(), status_offset, TTR("Animation tree is invalid."), Color(1, 0.6, 0.0, 0.8));
- } break;
- }
-
- } break;
- }
-}
-
-void AnimationTreePlayerEditor::_update_scrollbars() {
-
- Size2 size = get_size();
- Size2 hmin = h_scroll->get_combined_minimum_size();
- Size2 vmin = v_scroll->get_combined_minimum_size();
-
- v_scroll->set_begin(Point2(size.width - vmin.width, 0));
- v_scroll->set_end(Point2(size.width, size.height));
-
- h_scroll->set_begin(Point2(0, size.height - hmin.height));
- h_scroll->set_end(Point2(size.width - vmin.width, size.height));
-
- Size2 min = _get_maximum_size();
-
- if (min.height < size.height - hmin.height) {
-
- v_scroll->hide();
- offset.y = 0;
- } else {
-
- v_scroll->show();
- v_scroll->set_max(min.height);
- v_scroll->set_page(size.height - hmin.height);
- offset.y = v_scroll->get_value();
- }
-
- if (min.width < size.width - vmin.width) {
-
- h_scroll->hide();
- offset.x = 0;
- } else {
-
- h_scroll->show();
- h_scroll->set_max(min.width);
- h_scroll->set_page(size.width - vmin.width);
- offset.x = h_scroll->get_value();
- }
-}
-
-void AnimationTreePlayerEditor::_scroll_moved(float) {
-
- offset.x = h_scroll->get_value();
- offset.y = v_scroll->get_value();
- update();
-}
-
-void AnimationTreePlayerEditor::_node_menu_item(int p_item) {
-
- switch (p_item) {
-
- case NODE_DISCONNECT: {
-
- if (rclick_type == CLICK_INPUT_SLOT) {
-
- anim_tree->disconnect_nodes(rclick_node, rclick_slot);
- update();
- }
-
- if (rclick_type == CLICK_OUTPUT_SLOT) {
-
- List<AnimationTreePlayer::Connection> connections;
- anim_tree->get_connection_list(&connections);
-
- for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) {
-
- const AnimationTreePlayer::Connection &c = E->get();
- if (c.dst_node == rclick_node) {
-
- anim_tree->disconnect_nodes(c.dst_node, c.dst_input);
- }
- }
- update();
- }
-
- } break;
- case NODE_RENAME: {
-
- renaming_edit = true;
- edited_node = rclick_node;
- _popup_edit_dialog();
-
- } break;
- case NODE_ADD_INPUT: {
-
- anim_tree->transition_node_set_input_count(rclick_node, anim_tree->transition_node_get_input_count(rclick_node) + 1);
- update();
- } break;
- case NODE_DELETE_INPUT: {
-
- anim_tree->transition_node_delete_input(rclick_node, rclick_slot);
- update();
- } break;
- case NODE_SET_AUTOADVANCE: {
-
- anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, true);
- update();
-
- } break;
- case NODE_CLEAR_AUTOADVANCE: {
-
- anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, false);
- update();
-
- } break;
-
- case NODE_ERASE: {
-
- if (rclick_node == "out")
- break;
- order.erase(rclick_node);
- anim_tree->remove_node(rclick_node);
- update();
- } break;
- }
-}
-
-StringName AnimationTreePlayerEditor::_add_node(int p_item) {
-
- static const char *bname[] = {
- "out",
- "anim",
- "oneshot",
- "mix",
- "blend2",
- "blend3",
- "blend4",
- "scale",
- "seek",
- "transition"
- };
-
- String name;
- int idx = 1;
-
- while (true) {
-
- name = bname[p_item];
- if (idx > 1)
- name += " " + itos(idx);
- if (anim_tree->node_exists(name))
- idx++;
- else
- break;
- }
-
- anim_tree->add_node((AnimationTreePlayer::NodeType)p_item, name);
- anim_tree->node_set_position(name, Point2(last_x, last_y));
- order.push_back(name);
- last_x += 10;
- last_y += 10;
- last_x = last_x % (int)get_size().width;
- last_y = last_y % (int)get_size().height;
- update();
-
- return name;
-};
-
-void AnimationTreePlayerEditor::_file_dialog_selected(String p_path) {
-
- switch (file_op) {
-
- case MENU_IMPORT_ANIMATIONS: {
- Vector<String> files = file_dialog->get_selected_files();
-
- for (int i = 0; i < files.size(); i++) {
-
- StringName node = _add_node(AnimationTreePlayer::NODE_ANIMATION);
-
- RES anim = ResourceLoader::load(files[i]);
- anim_tree->animation_node_set_animation(node, anim);
- //anim_tree->node_set_name(node, files[i].get_file());
- };
- } break;
-
- default:
- break;
- };
-};
-
-void AnimationTreePlayerEditor::_add_menu_item(int p_item) {
-
- if (p_item == MENU_GRAPH_CLEAR) {
-
- //clear
- } else if (p_item == MENU_IMPORT_ANIMATIONS) {
-
- file_op = MENU_IMPORT_ANIMATIONS;
- file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
- file_dialog->popup_centered_ratio();
-
- } else {
-
- _add_node(p_item);
- }
-}
-
-Size2 AnimationTreePlayerEditor::get_minimum_size() const {
-
- return Size2(10, 200);
-}
-
-void AnimationTreePlayerEditor::_find_paths_for_filter(const StringName &p_node, Set<String> &paths) {
-
- ERR_FAIL_COND(!anim_tree->node_exists(p_node));
-
- for (int i = 0; i < anim_tree->node_get_input_count(p_node); i++) {
-
- StringName port = anim_tree->node_get_input_source(p_node, i);
- if (port == StringName())
- continue;
- _find_paths_for_filter(port, paths);
- }
-
- if (anim_tree->node_get_type(p_node) == AnimationTreePlayer::NODE_ANIMATION) {
-
- Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node);
- if (anim.is_valid()) {
-
- for (int i = 0; i < anim->get_track_count(); i++) {
- paths.insert(anim->track_get_path(i));
- }
- }
- }
-}
-
-void AnimationTreePlayerEditor::_filter_edited() {
-
- TreeItem *ed = filter->get_edited();
- if (!ed)
- return;
-
- if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) {
- anim_tree->oneshot_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0));
- } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) {
- anim_tree->blend2_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0));
- } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) {
- anim_tree->animation_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0));
- }
-}
-
-void AnimationTreePlayerEditor::_edit_filters() {
-
- filter_dialog->popup_centered_ratio();
- filter->clear();
-
- Set<String> npb;
- _find_paths_for_filter(edited_node, npb);
-
- TreeItem *root = filter->create_item();
- filter->set_hide_root(true);
- Map<String, TreeItem *> pm;
-
- Node *base = anim_tree->get_node(anim_tree->get_base_path());
-
- for (Set<String>::Element *E = npb.front(); E; E = E->next()) {
-
- TreeItem *parent = root;
- String descr = E->get();
- if (base) {
- NodePath np = E->get();
-
- if (np.get_subname_count() == 1) {
- Node *n = base->get_node(np);
- Skeleton *s = Object::cast_to<Skeleton>(n);
- if (s) {
-
- String skelbase = E->get().substr(0, E->get().find(":"));
-
- int bidx = s->find_bone(np.get_subname(0));
-
- if (bidx != -1) {
- int bparent = s->get_bone_parent(bidx);
- //
- if (bparent != -1) {
-
- String bpn = skelbase + ":" + s->get_bone_name(bparent);
- if (pm.has(bpn)) {
- parent = pm[bpn];
- descr = np.get_subname(0);
- }
- } else {
-
- if (pm.has(skelbase)) {
- parent = pm[skelbase];
- }
- }
- }
- }
- }
- }
-
- TreeItem *it = filter->create_item(parent);
- it->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
- it->set_text(0, descr);
- it->set_metadata(0, NodePath(E->get()));
- it->set_editable(0, true);
- if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) {
- it->set_checked(0, anim_tree->oneshot_node_is_path_filtered(edited_node, E->get()));
- } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) {
- it->set_checked(0, anim_tree->blend2_node_is_path_filtered(edited_node, E->get()));
- } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) {
- it->set_checked(0, anim_tree->animation_node_is_path_filtered(edited_node, E->get()));
- }
- pm[E->get()] = it;
- }
-}
-
-void AnimationTreePlayerEditor::_bind_methods() {
-
- ClassDB::bind_method("_add_menu_item", &AnimationTreePlayerEditor::_add_menu_item);
- ClassDB::bind_method("_node_menu_item", &AnimationTreePlayerEditor::_node_menu_item);
- ClassDB::bind_method("_gui_input", &AnimationTreePlayerEditor::_gui_input);
- //ClassDB::bind_method( "_node_param_changed", &AnimationTreeEditor::_node_param_changed );
- ClassDB::bind_method("_scroll_moved", &AnimationTreePlayerEditor::_scroll_moved);
- ClassDB::bind_method("_edit_dialog_changeds", &AnimationTreePlayerEditor::_edit_dialog_changeds);
- ClassDB::bind_method("_edit_dialog_changede", &AnimationTreePlayerEditor::_edit_dialog_changede);
- ClassDB::bind_method("_edit_dialog_changedf", &AnimationTreePlayerEditor::_edit_dialog_changedf);
- ClassDB::bind_method("_edit_dialog_changed", &AnimationTreePlayerEditor::_edit_dialog_changed);
- ClassDB::bind_method("_edit_dialog_animation_changed", &AnimationTreePlayerEditor::_edit_dialog_animation_changed);
- ClassDB::bind_method("_edit_dialog_edit_animation", &AnimationTreePlayerEditor::_edit_dialog_edit_animation);
- ClassDB::bind_method("_play_toggled", &AnimationTreePlayerEditor::_play_toggled);
- ClassDB::bind_method("_edit_oneshot_start", &AnimationTreePlayerEditor::_edit_oneshot_start);
- ClassDB::bind_method("_file_dialog_selected", &AnimationTreePlayerEditor::_file_dialog_selected);
- ClassDB::bind_method("_master_anim_menu_item", &AnimationTreePlayerEditor::_master_anim_menu_item);
- ClassDB::bind_method("_edit_filters", &AnimationTreePlayerEditor::_edit_filters);
- ClassDB::bind_method("_filter_edited", &AnimationTreePlayerEditor::_filter_edited);
-}
-
-AnimationTreePlayerEditor::AnimationTreePlayerEditor() {
-
- set_focus_mode(FOCUS_ALL);
-
- PopupMenu *p;
- List<PropertyInfo> defaults;
-
- add_menu = memnew(MenuButton);
- //add_menu->set_
- add_menu->set_position(Point2(0, 0));
- add_menu->set_size(Point2(25, 15));
- add_child(add_menu);
-
- p = add_menu->get_popup();
- p->add_item(TTR("Animation Node"), AnimationTreePlayer::NODE_ANIMATION);
- p->add_item(TTR("OneShot Node"), AnimationTreePlayer::NODE_ONESHOT);
- p->add_item(TTR("Mix Node"), AnimationTreePlayer::NODE_MIX);
- p->add_item(TTR("Blend2 Node"), AnimationTreePlayer::NODE_BLEND2);
- p->add_item(TTR("Blend3 Node"), AnimationTreePlayer::NODE_BLEND3);
- p->add_item(TTR("Blend4 Node"), AnimationTreePlayer::NODE_BLEND4);
- p->add_item(TTR("TimeScale Node"), AnimationTreePlayer::NODE_TIMESCALE);
- p->add_item(TTR("TimeSeek Node"), AnimationTreePlayer::NODE_TIMESEEK);
- p->add_item(TTR("Transition Node"), AnimationTreePlayer::NODE_TRANSITION);
- p->add_separator();
- p->add_item(TTR("Import Animations..."), MENU_IMPORT_ANIMATIONS); // wtf
- p->add_separator();
- p->add_item(TTR("Clear"), MENU_GRAPH_CLEAR);
-
- p->connect("id_pressed", this, "_add_menu_item");
-
- play_button = memnew(Button);
- play_button->set_position(Point2(25, 0) * EDSCALE);
- play_button->set_size(Point2(25, 15));
- add_child(play_button);
- play_button->set_toggle_mode(true);
- play_button->connect("pressed", this, "_play_toggled");
-
- last_x = 50;
- last_y = 50;
-
- property_editor = memnew(CustomPropertyEditor);
- add_child(property_editor);
- property_editor->connect("variant_changed", this, "_edit_dialog_animation_changed");
- property_editor->connect("resource_edit_request", this, "_edit_dialog_edit_animation");
-
- h_scroll = memnew(HScrollBar);
- v_scroll = memnew(VScrollBar);
-
- add_child(h_scroll);
- add_child(v_scroll);
-
- h_scroll->connect("value_changed", this, "_scroll_moved");
- v_scroll->connect("value_changed", this, "_scroll_moved");
-
- node_popup = memnew(PopupMenu);
- add_child(node_popup);
- node_popup->set_as_toplevel(true);
-
- master_anim_popup = memnew(PopupMenu);
- add_child(master_anim_popup);
- master_anim_popup->connect("id_pressed", this, "_master_anim_menu_item");
-
- node_popup->connect("id_pressed", this, "_node_menu_item");
-
- updating_edit = false;
-
- edit_dialog = memnew(PopupPanel);
- //edit_dialog->get_ok()->hide();
- //edit_dialog->get_cancel()->hide();
- add_child(edit_dialog);
-
- edit_option = memnew(OptionButton);
- edit_option->set_anchor(MARGIN_RIGHT, ANCHOR_END);
- edit_option->set_margin(MARGIN_RIGHT, -10);
- edit_dialog->add_child(edit_option);
- edit_option->connect("item_selected", this, "_edit_dialog_changedf");
- edit_option->hide();
-
- for (int i = 0; i < 2; i++) {
- edit_scroll[i] = memnew(HSlider);
- edit_scroll[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END);
- edit_scroll[i]->set_margin(MARGIN_RIGHT, -10);
- edit_dialog->add_child(edit_scroll[i]);
- edit_scroll[i]->hide();
- edit_scroll[i]->connect("value_changed", this, "_edit_dialog_changedf");
- }
- for (int i = 0; i < 4; i++) {
- edit_line[i] = memnew(LineEdit);
- edit_line[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END);
- edit_line[i]->set_margin(MARGIN_RIGHT, -10);
- edit_dialog->add_child(edit_line[i]);
- edit_line[i]->hide();
- edit_line[i]->connect("text_changed", this, "_edit_dialog_changeds");
- edit_line[i]->connect("text_entered", this, "_edit_dialog_changede");
- edit_label[i] = memnew(Label);
- edit_dialog->add_child(edit_label[i]);
- edit_label[i]->hide();
- }
-
- edit_button = memnew(Button);
- edit_button->set_anchor(MARGIN_RIGHT, ANCHOR_END);
- edit_button->set_margin(MARGIN_RIGHT, -10);
- edit_dialog->add_child(edit_button);
- edit_button->hide();
- edit_button->connect("pressed", this, "_edit_oneshot_start");
-
- edit_check = memnew(CheckButton);
- edit_check->set_anchor(MARGIN_RIGHT, ANCHOR_END);
- edit_check->set_margin(MARGIN_RIGHT, -10);
- edit_dialog->add_child(edit_check);
- edit_check->hide();
- edit_check->connect("pressed", this, "_edit_dialog_changed");
-
- file_dialog = memnew(EditorFileDialog);
- file_dialog->set_enable_multiple_selection(true);
- file_dialog->set_current_dir(ProjectSettings::get_singleton()->get_resource_path());
- add_child(file_dialog);
- file_dialog->connect("file_selected", this, "_file_dialog_selected");
-
- filter_dialog = memnew(AcceptDialog);
- filter_dialog->set_title(TTR("Edit Node Filters"));
- add_child(filter_dialog);
-
- filter = memnew(Tree);
- filter_dialog->add_child(filter);
- //filter_dialog->set_child_rect(filter);
- filter->connect("item_edited", this, "_filter_edited");
-
- filter_button = memnew(Button);
- filter_button->set_anchor(MARGIN_RIGHT, ANCHOR_END);
- filter_button->set_margin(MARGIN_RIGHT, -10);
- edit_dialog->add_child(filter_button);
- filter_button->hide();
- filter_button->set_text(TTR("Filters..."));
- filter_button->connect("pressed", this, "_edit_filters");
-
- set_clip_contents(true);
-}
-
-void AnimationTreePlayerEditorPlugin::edit(Object *p_object) {
-
- anim_tree_editor->edit(Object::cast_to<AnimationTreePlayer>(p_object));
-}
-
-bool AnimationTreePlayerEditorPlugin::handles(Object *p_object) const {
-
- return p_object->is_class("AnimationTreePlayer");
-}
-
-void AnimationTreePlayerEditorPlugin::make_visible(bool p_visible) {
-
- if (p_visible) {
- //editor->hide_animation_player_editors();
- //editor->animation_panel_make_visible(true);
- button->show();
- editor->make_bottom_panel_item_visible(anim_tree_editor);
- anim_tree_editor->set_physics_process(true);
- } else {
-
- if (anim_tree_editor->is_visible_in_tree())
- editor->hide_bottom_panel();
- button->hide();
- anim_tree_editor->set_physics_process(false);
- }
-}
-
-AnimationTreePlayerEditorPlugin::AnimationTreePlayerEditorPlugin(EditorNode *p_node) {
-
- editor = p_node;
- anim_tree_editor = memnew(AnimationTreePlayerEditor);
- anim_tree_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE);
-
- button = editor->add_bottom_panel_item(TTR("AnimationTree"), anim_tree_editor);
- button->hide();
-}
-
-AnimationTreePlayerEditorPlugin::~AnimationTreePlayerEditorPlugin() {
-}
diff --git a/editor/plugins/animation_tree_player_editor_plugin.h b/editor/plugins/animation_tree_player_editor_plugin.h
deleted file mode 100644
index d3fd6ae362..0000000000
--- a/editor/plugins/animation_tree_player_editor_plugin.h
+++ /dev/null
@@ -1,184 +0,0 @@
-/*************************************************************************/
-/* animation_tree_player_editor_plugin.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 ANIMATION_TREE_PLAYER_EDITOR_PLUGIN_H
-#define ANIMATION_TREE_PLAYER_EDITOR_PLUGIN_H
-
-#include "editor/editor_node.h"
-#include "editor/editor_plugin.h"
-#include "editor/property_editor.h"
-#include "scene/animation/animation_tree_player.h"
-#include "scene/gui/button.h"
-#include "scene/gui/popup.h"
-#include "scene/gui/tree.h"
-
-class AnimationTreePlayerEditor : public Control {
-
- GDCLASS(AnimationTreePlayerEditor, Control);
-
- static const char *_node_type_names[];
-
- enum ClickType {
- CLICK_NONE,
- CLICK_NAME,
- CLICK_NODE,
- CLICK_INPUT_SLOT,
- CLICK_OUTPUT_SLOT,
- CLICK_PARAMETER
- };
-
- enum {
-
- MENU_GRAPH_CLEAR = 100,
- MENU_IMPORT_ANIMATIONS = 101,
- NODE_DISCONNECT,
- NODE_RENAME,
- NODE_ERASE,
- NODE_ADD_INPUT,
- NODE_DELETE_INPUT,
- NODE_SET_AUTOADVANCE,
- NODE_CLEAR_AUTOADVANCE
- };
-
- bool renaming_edit;
- StringName edited_node;
- bool updating_edit;
- Popup *edit_dialog;
- HSlider *edit_scroll[2];
- LineEdit *edit_line[4];
- OptionButton *edit_option;
- Label *edit_label[4];
- Button *edit_button;
- Button *filter_button;
- CheckButton *edit_check;
- EditorFileDialog *file_dialog;
- int file_op;
-
- void _popup_edit_dialog();
-
- void _setup_edit_dialog(const StringName &p_node);
- PopupMenu *master_anim_popup;
- PopupMenu *node_popup;
- PopupMenu *add_popup;
- HScrollBar *h_scroll;
- VScrollBar *v_scroll;
- MenuButton *add_menu;
-
- CustomPropertyEditor *property_editor;
-
- AnimationTreePlayer *anim_tree;
- List<StringName> order;
- Set<StringName> active_nodes;
-
- int last_x, last_y;
-
- Point2 offset;
- ClickType click_type;
- Point2 click_pos;
- StringName click_node;
- int click_slot;
- Point2 click_motion;
- ClickType rclick_type;
- StringName rclick_node;
- int rclick_slot;
-
- Button *play_button;
-
- Size2 _get_maximum_size();
- Size2 get_node_size(const StringName &p_node) const;
- void _draw_node(const StringName &p_node);
-
- AcceptDialog *filter_dialog;
- Tree *filter;
-
- void _draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color);
- void _update_scrollbars();
- void _scroll_moved(float);
- void _play_toggled();
- /*
- void _node_param_changed();
- void _node_add_callback();
- void _node_add(VisualServer::AnimationTreeNodeType p_type);
- void _node_edit_property(const StringName& p_node);
-*/
-
- void _master_anim_menu_item(int p_item);
- void _node_menu_item(int p_item);
- void _add_menu_item(int p_item);
-
- void _filter_edited();
- void _find_paths_for_filter(const StringName &p_node, Set<String> &paths);
- void _edit_filters();
-
- void _edit_oneshot_start();
- void _edit_dialog_animation_changed();
- void _edit_dialog_edit_animation();
- void _edit_dialog_changeds(String);
- void _edit_dialog_changede(String);
- void _edit_dialog_changedf(float);
- void _edit_dialog_changed();
- void _dialog_changed() const;
- ClickType _locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const;
- Point2 _get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot);
-
- StringName _add_node(int p_item);
- void _file_dialog_selected(String p_path);
-
-protected:
- void _notification(int p_what);
- void _gui_input(Ref<InputEvent> p_event);
- static void _bind_methods();
-
-public:
- virtual Size2 get_minimum_size() const;
- void edit(AnimationTreePlayer *p_anim_tree);
- AnimationTreePlayerEditor();
-};
-
-class AnimationTreePlayerEditorPlugin : public EditorPlugin {
-
- GDCLASS(AnimationTreePlayerEditorPlugin, EditorPlugin);
-
- AnimationTreePlayerEditor *anim_tree_editor;
- EditorNode *editor;
- Button *button;
-
-public:
- virtual String get_name() const { return "AnimTree"; }
- bool has_main_screen() const { return false; }
- virtual void edit(Object *p_object);
- virtual bool handles(Object *p_object) const;
- virtual void make_visible(bool p_visible);
-
- AnimationTreePlayerEditorPlugin(EditorNode *p_node);
- ~AnimationTreePlayerEditorPlugin();
-};
-
-#endif // ANIMATION_TREE_EDITOR_PLUGIN_H
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 2428bf82d4..d88a716122 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -31,6 +31,8 @@
#include "asset_library_editor_plugin.h"
#include "core/io/json.h"
+#include "core/os/input.h"
+#include "core/os/keyboard.h"
#include "core/version.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
@@ -620,6 +622,21 @@ void EditorAssetLibrary::_notification(int p_what) {
}
}
+void EditorAssetLibrary::_unhandled_input(const Ref<InputEvent> &p_event) {
+
+ const Ref<InputEventKey> key = p_event;
+
+ if (key.is_valid() && key->is_pressed()) {
+
+ if (key->get_scancode_with_modifiers() == (KEY_MASK_CMD | KEY_F) && is_visible_in_tree()) {
+
+ filter->grab_focus();
+ filter->select_all();
+ accept_event();
+ }
+ }
+}
+
void EditorAssetLibrary::_install_asset() {
ERR_FAIL_COND(!description);
@@ -1320,6 +1337,7 @@ void EditorAssetLibrary::disable_community_support() {
void EditorAssetLibrary::_bind_methods() {
+ ClassDB::bind_method("_unhandled_input", &EditorAssetLibrary::_unhandled_input);
ClassDB::bind_method("_http_request_completed", &EditorAssetLibrary::_http_request_completed);
ClassDB::bind_method("_select_asset", &EditorAssetLibrary::_select_asset);
ClassDB::bind_method("_select_author", &EditorAssetLibrary::_select_author);
@@ -1502,6 +1520,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
description = NULL;
set_process(true);
+ set_process_unhandled_input(true);
downloads_scroll = memnew(ScrollContainer);
downloads_scroll->set_enable_h_scroll(true);
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index aa3c735810..8852125b8b 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -300,6 +300,7 @@ class EditorAssetLibrary : public PanelContainer {
protected:
static void _bind_methods();
void _notification(int p_what);
+ void _unhandled_input(const Ref<InputEvent> &p_event);
public:
void disable_community_support();
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index bf21abb455..ab816dc7f3 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -606,7 +606,7 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no
}
}
-void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items) {
+void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, bool p_allow_locked) {
Node *scene = editor->get_edited_scene();
@@ -621,14 +621,16 @@ void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_Sel
node = node->get_parent();
};
- // Replace the node by the group if grouped
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(node);
- while (node && node != scene->get_parent()) {
- CanvasItem *canvas_item_tmp = Object::cast_to<CanvasItem>(node);
- if (canvas_item_tmp && node->has_meta("_edit_group_")) {
- canvas_item = canvas_item_tmp;
+ if (!p_allow_locked) {
+ // Replace the node by the group if grouped
+ while (node && node != scene->get_parent()) {
+ CanvasItem *canvas_item_tmp = Object::cast_to<CanvasItem>(node);
+ if (canvas_item_tmp && node->has_meta("_edit_group_")) {
+ canvas_item = canvas_item_tmp;
+ }
+ node = node->get_parent();
}
- node = node->get_parent();
}
// Check if the canvas item is already in the list (for groups or scenes)
@@ -641,7 +643,7 @@ void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_Sel
}
//Remove the item if invalid
- if (!canvas_item || duplicate || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || _is_node_locked(canvas_item)) {
+ if (!canvas_item || duplicate || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || (!p_allow_locked && _is_node_locked(canvas_item))) {
r_items.remove(i);
i--;
} else {
@@ -1987,7 +1989,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
if (selection.size() > 0) {
drag_type = DRAG_MOVE;
- CanvasItem *canvas_item = drag_selection[0];
+ CanvasItem *canvas_item = selection[0];
Transform2D parent_xform = canvas_item->get_global_transform_with_canvas() * canvas_item->get_transform().affine_inverse();
Transform2D unscaled_transform = (transform * parent_xform * canvas_item->_edit_get_transform()).orthonormalized();
Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
@@ -2106,7 +2108,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
}
// Move the canvas items with the arrow keys
- if (k.is_valid() && k->is_pressed() && tool == TOOL_SELECT &&
+ if (k.is_valid() && k->is_pressed() && (tool == TOOL_SELECT || tool == TOOL_MOVE) &&
(k->get_scancode() == KEY_UP || k->get_scancode() == KEY_DOWN || k->get_scancode() == KEY_LEFT || k->get_scancode() == KEY_RIGHT)) {
if (!k->is_echo()) {
// Start moving the canvas items with the keyboard
@@ -2223,7 +2225,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
// Popup the selection menu list
Point2 click = transform.affine_inverse().xform(b->get_position());
- _get_canvas_items_at_pos(click, selection_results);
+ _get_canvas_items_at_pos(click, selection_results, b->get_alt() && tool != TOOL_LIST_SELECT);
if (selection_results.size() == 1) {
CanvasItem *item = selection_results[0].item;
@@ -2245,7 +2247,29 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
Ref<Texture> icon = EditorNode::get_singleton()->get_object_icon(item, "Node");
String node_path = "/" + root_name + "/" + root_path.rel_path_to(item->get_path());
- selection_menu->add_item(item->get_name());
+ int locked = 0;
+ if (_is_node_locked(item)) {
+ locked = 1;
+ } else {
+ Node *scene = editor->get_edited_scene();
+ Node *node = item;
+
+ while (node && node != scene->get_parent()) {
+ CanvasItem *canvas_item_tmp = Object::cast_to<CanvasItem>(node);
+ if (canvas_item_tmp && node->has_meta("_edit_group_")) {
+ locked = 2;
+ }
+ node = node->get_parent();
+ }
+ }
+
+ String suffix = String();
+ if (locked == 1) {
+ suffix = " (" + TTR("Locked") + ")";
+ } else if (locked == 2) {
+ suffix = " (" + TTR("Grouped") + ")";
+ }
+ selection_menu->add_item((String)item->get_name() + suffix);
selection_menu->set_item_icon(i, icon);
selection_menu->set_item_metadata(i, node_path);
selection_menu->set_item_tooltip(i, String(item->get_name()) + "\nType: " + item->get_class() + "\nPath: " + node_path);
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 37bc47ecd4..eb6e621c93 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -418,7 +418,7 @@ private:
bool _is_node_locked(const Node *p_node);
bool _is_node_movable(const Node *p_node, bool p_popup_warning = false);
void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
- void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items);
+ void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, bool p_allow_locked = false);
void _get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items);
void _find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp
index 25329906a9..aaba6406c7 100644
--- a/editor/plugins/mesh_instance_editor_plugin.cpp
+++ b/editor/plugins/mesh_instance_editor_plugin.cpp
@@ -32,7 +32,7 @@
#include "editor/editor_scale.h"
#include "scene/3d/collision_shape.h"
-#include "scene/3d/navigation_mesh.h"
+#include "scene/3d/navigation_mesh_instance.h"
#include "scene/3d/physics_body.h"
#include "scene/gui/box_container.h"
#include "spatial_editor_plugin.h"
@@ -60,10 +60,7 @@ void MeshInstanceEditor::_menu_option(int p_option) {
}
switch (p_option) {
- case MENU_OPTION_CREATE_STATIC_TRIMESH_BODY:
- case MENU_OPTION_CREATE_STATIC_CONVEX_BODY: {
-
- bool trimesh_shape = (p_option == MENU_OPTION_CREATE_STATIC_TRIMESH_BODY);
+ case MENU_OPTION_CREATE_STATIC_TRIMESH_BODY: {
EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
@@ -71,9 +68,12 @@ void MeshInstanceEditor::_menu_option(int p_option) {
List<Node *> selection = editor_selection->get_selected_node_list();
if (selection.empty()) {
- Ref<Shape> shape = trimesh_shape ? mesh->create_trimesh_shape() : mesh->create_convex_shape();
- if (shape.is_null())
+ Ref<Shape> shape = mesh->create_trimesh_shape();
+ if (shape.is_null()) {
+ err_dialog->set_text(TTR("Couldn't create a Trimesh collision shape."));
+ err_dialog->popup_centered_minsize();
return;
+ }
CollisionShape *cshape = memnew(CollisionShape);
cshape->set_shape(shape);
@@ -82,11 +82,7 @@ void MeshInstanceEditor::_menu_option(int p_option) {
Node *owner = node == get_tree()->get_edited_scene_root() ? node : node->get_owner();
- if (trimesh_shape)
- ur->create_action(TTR("Create Static Trimesh Body"));
- else
- ur->create_action(TTR("Create Static Convex Body"));
-
+ ur->create_action(TTR("Create Static Trimesh Body"));
ur->add_do_method(node, "add_child", body);
ur->add_do_method(body, "set_owner", owner);
ur->add_do_method(cshape, "set_owner", owner);
@@ -108,7 +104,7 @@ void MeshInstanceEditor::_menu_option(int p_option) {
if (m.is_null())
continue;
- Ref<Shape> shape = trimesh_shape ? m->create_trimesh_shape() : m->create_convex_shape();
+ Ref<Shape> shape = m->create_trimesh_shape();
if (shape.is_null())
continue;
@@ -158,10 +154,44 @@ void MeshInstanceEditor::_menu_option(int p_option) {
ur->add_undo_method(node->get_parent(), "remove_child", cshape);
ur->commit_action();
} break;
- case MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE: {
+ case MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE: {
if (node == get_tree()->get_edited_scene_root()) {
- err_dialog->set_text(TTR("This doesn't work on scene root!"));
+ err_dialog->set_text(TTR("Can't create a single convex collision shape for the scene root."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ Ref<Shape> shape = mesh->create_convex_shape();
+
+ if (shape.is_null()) {
+ err_dialog->set_text(TTR("Couldn't create a single convex collision shape."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+
+ ur->create_action(TTR("Create Single Convex Shape"));
+
+ CollisionShape *cshape = memnew(CollisionShape);
+ cshape->set_shape(shape);
+ cshape->set_transform(node->get_transform());
+
+ Node *owner = node->get_owner();
+
+ ur->add_do_method(node->get_parent(), "add_child", cshape);
+ ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1);
+ ur->add_do_method(cshape, "set_owner", owner);
+ ur->add_do_reference(cshape);
+ ur->add_undo_method(node->get_parent(), "remove_child", cshape);
+
+ ur->commit_action();
+
+ } break;
+ case MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES: {
+
+ if (node == get_tree()->get_edited_scene_root()) {
+ err_dialog->set_text(TTR("Can't create multiple convex collision shapes for the scene root."));
err_dialog->popup_centered_minsize();
return;
}
@@ -169,13 +199,13 @@ void MeshInstanceEditor::_menu_option(int p_option) {
Vector<Ref<Shape> > shapes = mesh->convex_decompose();
if (!shapes.size()) {
- err_dialog->set_text(TTR("Failed creating shapes!"));
+ err_dialog->set_text(TTR("Couldn't create any collision shapes."));
err_dialog->popup_centered_minsize();
return;
}
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
- ur->create_action(TTR("Create Convex Shape(s)"));
+ ur->create_action(TTR("Create Multiple Convex Shapes"));
for (int i = 0; i < shapes.size(); i++) {
@@ -421,13 +451,19 @@ MeshInstanceEditor::MeshInstanceEditor() {
options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MeshInstance", "EditorIcons"));
options->get_popup()->add_item(TTR("Create Trimesh Static Body"), MENU_OPTION_CREATE_STATIC_TRIMESH_BODY);
+ options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a StaticBody and assigns a polygon-based collision shape to it automatically.\nThis is the most accurate (but slowest) option for collision detection."));
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Create Trimesh Collision Sibling"), MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE);
- options->get_popup()->add_item(TTR("Create Convex Collision Sibling(s)"), MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE);
+ options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a polygon-based collision shape.\nThis is the most accurate (but slowest) option for collision detection."));
+ options->get_popup()->add_item(TTR("Create Single Convex Collision Siblings"), MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE);
+ options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a single convex collision shape.\nThis is the fastest (but least accurate) option for collision detection."));
+ options->get_popup()->add_item(TTR("Create Multiple Convex Collision Siblings"), MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES);
+ options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a polygon-based collision shape.\nThis is a performance middle-ground between the two above options."));
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Create Outline Mesh..."), MENU_OPTION_CREATE_OUTLINE_MESH);
+ options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a static outline mesh. The outline mesh will have its normals flipped automatically.\nThis can be used instead of the SpatialMaterial Grow property when using that property isn't possible."));
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("View UV1"), MENU_OPTION_DEBUG_UV1);
options->get_popup()->add_item(TTR("View UV2"), MENU_OPTION_DEBUG_UV2);
diff --git a/editor/plugins/mesh_instance_editor_plugin.h b/editor/plugins/mesh_instance_editor_plugin.h
index 5c95676fc4..5ca9aa3fec 100644
--- a/editor/plugins/mesh_instance_editor_plugin.h
+++ b/editor/plugins/mesh_instance_editor_plugin.h
@@ -43,9 +43,9 @@ class MeshInstanceEditor : public Control {
enum Menu {
MENU_OPTION_CREATE_STATIC_TRIMESH_BODY,
- MENU_OPTION_CREATE_STATIC_CONVEX_BODY,
MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE,
- MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE,
+ MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE,
+ MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES,
MENU_OPTION_CREATE_NAVMESH,
MENU_OPTION_CREATE_OUTLINE_MESH,
MENU_OPTION_CREATE_UV2,
diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp
index 92898ff9c3..42859e9190 100644
--- a/editor/plugins/mesh_library_editor_plugin.cpp
+++ b/editor/plugins/mesh_library_editor_plugin.cpp
@@ -34,7 +34,7 @@
#include "editor/editor_settings.h"
#include "main/main.h"
#include "scene/3d/mesh_instance.h"
-#include "scene/3d/navigation_mesh.h"
+#include "scene/3d/navigation_mesh_instance.h"
#include "scene/3d/physics_body.h"
#include "scene/main/viewport.h"
#include "scene/resources/packed_scene.h"
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index 31e6b65640..8f7ff04b2c 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -250,7 +250,7 @@ void SpatialEditorViewport::_clear_selected() {
editor_selection->clear();
}
-void SpatialEditorViewport::_select_clicked(bool p_append, bool p_single) {
+void SpatialEditorViewport::_select_clicked(bool p_append, bool p_single, bool p_allow_locked) {
if (!clicked)
return;
@@ -260,17 +260,20 @@ void SpatialEditorViewport::_select_clicked(bool p_append, bool p_single) {
if (!selected)
return;
- // Replace the node by the group if grouped
- while (node && node != editor->get_edited_scene()->get_parent()) {
- Spatial *selected_tmp = Object::cast_to<Spatial>(node);
- if (selected_tmp && node->has_meta("_edit_group_")) {
- selected = selected_tmp;
+ if (!p_allow_locked) {
+ // Replace the node by the group if grouped
+ while (node && node != editor->get_edited_scene()->get_parent()) {
+ Spatial *selected_tmp = Object::cast_to<Spatial>(node);
+ if (selected_tmp && node->has_meta("_edit_group_")) {
+ selected = selected_tmp;
+ }
+ node = node->get_parent();
}
- node = node->get_parent();
}
- if (!_is_node_locked(selected))
+ if (p_allow_locked || !_is_node_locked(selected)) {
_select(selected, clicked_wants_append, true);
+ }
}
void SpatialEditorViewport::_select(Node *p_node, bool p_append, bool p_single) {
@@ -848,7 +851,7 @@ void SpatialEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
selection_results.clear();
if (clicked) {
- _select_clicked(clicked_wants_append, true);
+ _select_clicked(clicked_wants_append, true, spatial_editor->get_tool_mode() != SpatialEditor::TOOL_MODE_LIST_SELECT);
clicked = 0;
}
@@ -865,7 +868,29 @@ void SpatialEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
String node_path = "/" + root_name + "/" + root_path.rel_path_to(spat->get_path());
- selection_menu->add_item(spat->get_name());
+ int locked = 0;
+ if (_is_node_locked(spat)) {
+ locked = 1;
+ } else {
+ Node *ed_scene = editor->get_edited_scene();
+ Node *node = spat;
+
+ while (node && node != ed_scene->get_parent()) {
+ Spatial *selected_tmp = Object::cast_to<Spatial>(node);
+ if (selected_tmp && node->has_meta("_edit_group_")) {
+ locked = 2;
+ }
+ node = node->get_parent();
+ }
+ }
+
+ String suffix = String();
+ if (locked == 1) {
+ suffix = " (" + TTR("Locked") + ")";
+ } else if (locked == 2) {
+ suffix = " (" + TTR("Grouped") + ")";
+ }
+ selection_menu->add_item((String)spat->get_name() + suffix);
selection_menu->set_item_icon(i, icon);
selection_menu->set_item_metadata(i, node_path);
selection_menu->set_item_tooltip(i, String(spat->get_name()) + "\nType: " + spat->get_class() + "\nPath: " + node_path);
@@ -2892,7 +2917,7 @@ void SpatialEditorViewport::_selection_result_pressed(int p_result) {
clicked = selection_results[p_result].item->get_instance_id();
if (clicked) {
- _select_clicked(clicked_wants_append, true);
+ _select_clicked(clicked_wants_append, true, spatial_editor->get_tool_mode() != SpatialEditor::TOOL_MODE_LIST_SELECT);
clicked = 0;
}
}
diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h
index 5cc2b24cbb..269e1715ef 100644
--- a/editor/plugins/spatial_editor_plugin.h
+++ b/editor/plugins/spatial_editor_plugin.h
@@ -234,7 +234,7 @@ private:
void _update_name();
void _compute_edit(const Point2 &p_point);
void _clear_selected();
- void _select_clicked(bool p_append, bool p_single);
+ void _select_clicked(bool p_append, bool p_single, bool p_allow_locked = false);
void _select(Node *p_node, bool p_append, bool p_single);
ObjectID _select_ray(const Point2 &p_pos, bool p_append, bool &r_includes_current, int *r_gizmo_handle = NULL, bool p_alt_select = false);
void _find_items_at_pos(const Point2 &p_pos, bool &r_includes_current, Vector<_RayResult> &results, bool p_alt_select = false);
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
index f889228f87..0c3e29028c 100644
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -2026,13 +2026,13 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
toolbar->add_child(bucket_fill_button);
picker_button = memnew(ToolButton);
- picker_button->set_shortcut(ED_SHORTCUT("tile_map_editor/pick_tile", TTR("Pick Tile"), KEY_CONTROL));
+ picker_button->set_shortcut(ED_SHORTCUT("tile_map_editor/pick_tile", TTR("Pick Tile"), KEY_I));
picker_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_PICKING));
picker_button->set_toggle_mode(true);
toolbar->add_child(picker_button);
select_button = memnew(ToolButton);
- select_button->set_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_MASK_CMD + KEY_B));
+ select_button->set_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_M));
select_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_SELECTING));
select_button->set_toggle_mode(true);
toolbar->add_child(select_button);
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index ee434aaac2..9cc2a79399 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -885,7 +885,8 @@ public:
rs_button->set_pressed(true);
rvb->add_child(rs_button);
l = memnew(Label);
- l->set_text(TTR("Higher visual quality\nAll features available\nIncompatible with older hardware\nNot recommended for web games"));
+ l->set_text(TTR("- Higher visual quality\n- All features available\n- Incompatible with older hardware\n- Not recommended for web games"));
+ l->set_modulate(Color(1, 1, 1, 0.7));
rvb->add_child(l);
rshb->add_child(memnew(VSeparator));
@@ -899,12 +900,17 @@ public:
rs_button->set_meta("driver_name", "GLES2");
rvb->add_child(rs_button);
l = memnew(Label);
- l->set_text(TTR("Lower visual quality\nSome features not available\nWorks on most hardware\nRecommended for web games"));
+ l->set_text(TTR("- Lower visual quality\n- Some features not available\n- Works on most hardware\n- Recommended for web games"));
+ l->set_modulate(Color(1, 1, 1, 0.7));
rvb->add_child(l);
l = memnew(Label);
- l->set_text(TTR("Renderer can be changed later, but scenes may need to be adjusted."));
+ l->set_text(TTR("The renderer can be changed later, but scenes may need to be adjusted."));
+ // Add some extra spacing to separate it from the list above and the buttons below.
+ l->set_custom_minimum_size(Size2(0, 40) * EDSCALE);
l->set_align(Label::ALIGN_CENTER);
+ l->set_valign(Label::VALIGN_CENTER);
+ l->set_modulate(Color(1, 1, 1, 0.7));
rasterizer_container->add_child(l);
fdialog = memnew(FileDialog);
@@ -1037,6 +1043,7 @@ public:
void sort_projects();
int get_project_count() const;
void select_project(int p_index);
+ void select_first_visible_project();
void erase_selected_projects();
Vector<Item> get_selected_projects() const;
const Set<String> &get_selected_project_keys() const;
@@ -1629,6 +1636,23 @@ void ProjectList::select_project(int p_index) {
toggle_select(p_index);
}
+void ProjectList::select_first_visible_project() {
+ bool found = false;
+
+ for (int i = 0; i < _projects.size(); i++) {
+ if (_projects[i].control->is_visible()) {
+ select_project(i);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ // Deselect all projects if there are no visible projects in the list.
+ _selected_project_keys.clear();
+ }
+}
+
inline void sort(int &a, int &b) {
if (a > b) {
int temp = a;
@@ -2347,6 +2371,12 @@ void ProjectManager::_on_order_option_changed() {
void ProjectManager::_on_filter_option_changed() {
_project_list->set_search_term(project_filter->get_search_term());
_project_list->sort_projects();
+
+ // Select the first visible project in the list.
+ // This makes it possible to open a project without ever touching the mouse,
+ // as the search field is automatically focused on startup.
+ _project_list->select_first_visible_project();
+ _update_project_buttons();
}
void ProjectManager::_bind_methods() {
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index b656fd647f..6635f5cb47 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -719,9 +719,18 @@ void ProjectSettingsEditor::_update_actions() {
item->set_range(1, action["deadzone"]);
item->set_custom_bg_color(1, get_color("prop_subsection", "Editor"));
+ const bool is_builtin_input = ProjectSettings::get_singleton()->get_input_presets().find(pi.name) != NULL;
+ const String tooltip = is_builtin_input ? TTR("Built-in actions can't be removed as they're used for UI navigation.") : TTR("Remove");
item->add_button(2, get_icon("Add", "EditorIcons"), 1, false, TTR("Add Event"));
- if (!ProjectSettings::get_singleton()->get_input_presets().find(pi.name)) {
- item->add_button(2, get_icon("Remove", "EditorIcons"), 2, false, TTR("Remove"));
+ item->add_button(2, get_icon("Remove", "EditorIcons"), 2, false, tooltip);
+
+ if (is_builtin_input) {
+ // Built-in action (like `ui_up`). Make the action not removable,
+ // but still display the button so the "Add" button is at the same
+ // horizontal position as for custom actions.
+ item->set_button_disabled(2, 1, true);
+ } else {
+ // Not a built-in action. Make the action name editable.
item->set_editable(0, true);
}
@@ -747,10 +756,9 @@ void ProjectSettingsEditor::_update_actions() {
if (jb.is_valid()) {
String str = _get_device_string(jb->get_device()) + ", " + TTR("Button") + " " + itos(jb->get_button_index());
- if (jb->get_button_index() >= 0 && jb->get_button_index() < JOY_BUTTON_MAX)
- str += String() + " (" + _button_names[jb->get_button_index()] + ").";
- else
- str += ".";
+ if (jb->get_button_index() >= 0 && jb->get_button_index() < JOY_BUTTON_MAX) {
+ str += String() + " (" + _button_names[jb->get_button_index()] + ")";
+ }
action2->set_text(0, str);
action2->set_icon(0, get_icon("JoyButton", "EditorIcons"));
@@ -761,12 +769,12 @@ void ProjectSettingsEditor::_update_actions() {
if (mb.is_valid()) {
String str = _get_device_string(mb->get_device()) + ", ";
switch (mb->get_button_index()) {
- case BUTTON_LEFT: str += TTR("Left Button."); break;
- case BUTTON_RIGHT: str += TTR("Right Button."); break;
- case BUTTON_MIDDLE: str += TTR("Middle Button."); break;
- case BUTTON_WHEEL_UP: str += TTR("Wheel Up."); break;
- case BUTTON_WHEEL_DOWN: str += TTR("Wheel Down."); break;
- default: str += TTR("Button") + " " + itos(mb->get_button_index()) + ".";
+ case BUTTON_LEFT: str += TTR("Left Button"); break;
+ case BUTTON_RIGHT: str += TTR("Right Button"); break;
+ case BUTTON_MIDDLE: str += TTR("Middle Button"); break;
+ case BUTTON_WHEEL_UP: str += TTR("Wheel Up"); break;
+ case BUTTON_WHEEL_DOWN: str += TTR("Wheel Down"); break;
+ default: str += vformat(TTR("%d Button"), mb->get_button_index());
}
action2->set_text(0, str);
@@ -780,7 +788,7 @@ void ProjectSettingsEditor::_update_actions() {
int ax = jm->get_axis();
int n = 2 * ax + (jm->get_axis_value() < 0 ? 0 : 1);
String desc = _axis_names[n];
- String str = _get_device_string(jm->get_device()) + ", " + TTR("Axis") + " " + itos(ax) + " " + (jm->get_axis_value() < 0 ? "-" : "+") + desc + ".";
+ String str = _get_device_string(jm->get_device()) + ", " + TTR("Axis") + " " + itos(ax) + " " + (jm->get_axis_value() < 0 ? "-" : "+") + desc;
action2->set_text(0, str);
action2->set_icon(0, get_icon("JoyAxis", "EditorIcons"));
}
@@ -789,6 +797,10 @@ void ProjectSettingsEditor::_update_actions() {
action2->add_button(2, get_icon("Edit", "EditorIcons"), 3, false, TTR("Edit"));
action2->add_button(2, get_icon("Remove", "EditorIcons"), 2, false, TTR("Remove"));
+ // Fade out the individual event buttons slightly to make the
+ // Add/Remove buttons stand out more.
+ action2->set_button_color(2, 0, Color(1, 1, 1, 0.75));
+ action2->set_button_color(2, 1, Color(1, 1, 1, 0.75));
}
}
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index c94b0eeab0..d012b2f821 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -41,7 +41,7 @@
#include "scene/3d/light.h"
#include "scene/3d/listener.h"
#include "scene/3d/mesh_instance.h"
-#include "scene/3d/navigation_mesh.h"
+#include "scene/3d/navigation_mesh_instance.h"
#include "scene/3d/particles.h"
#include "scene/3d/physics_joint.h"
#include "scene/3d/position_3d.h"
diff --git a/main/main.cpp b/main/main.cpp
index 650a680d87..162d5568d4 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -66,6 +66,8 @@
#include "servers/arvr_server.h"
#include "servers/audio_server.h"
#include "servers/camera_server.h"
+#include "servers/navigation_2d_server.h"
+#include "servers/navigation_server.h"
#include "servers/physics_2d_server.h"
#include "servers/physics_server.h"
#include "servers/register_server_types.h"
@@ -103,6 +105,8 @@ static CameraServer *camera_server = NULL;
static ARVRServer *arvr_server = NULL;
static PhysicsServer *physics_server = NULL;
static Physics2DServer *physics_2d_server = NULL;
+static NavigationServer *navigation_server = NULL;
+static Navigation2DServer *navigation_2d_server = NULL;
// We error out if setup2() doesn't turn this true
static bool _start_success = false;
@@ -197,6 +201,19 @@ void finalize_physics() {
memdelete(physics_2d_server);
}
+void initialize_navigation_server() {
+ ERR_FAIL_COND(navigation_server != NULL);
+ navigation_server = NavigationServerManager::new_default_server();
+ navigation_2d_server = memnew(Navigation2DServer);
+}
+
+void finalize_navigation_server() {
+ memdelete(navigation_server);
+ navigation_server = NULL;
+ memdelete(navigation_2d_server);
+ navigation_2d_server = NULL;
+}
+
//#define DEBUG_INIT
#ifdef DEBUG_INIT
#define MAIN_PRINT(m_txt) print_line(m_txt)
@@ -1356,6 +1373,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
camera_server = CameraServer::create();
initialize_physics();
+ initialize_navigation_server();
register_server_singletons();
register_driver_types();
@@ -2011,6 +2029,8 @@ bool Main::iteration() {
message_queue->flush();
+ NavigationServer::get_singleton_mut()->step(frame_slice * time_scale);
+
PhysicsServer::get_singleton()->step(frame_slice * time_scale);
Physics2DServer::get_singleton()->end_sync();
@@ -2202,6 +2222,7 @@ void Main::cleanup() {
OS::get_singleton()->finalize();
finalize_physics();
+ finalize_navigation_server();
if (packed_data)
memdelete(packed_data);
diff --git a/modules/gdnative/include/pluginscript/godot_pluginscript.h b/modules/gdnative/include/pluginscript/godot_pluginscript.h
index e8822fc1ec..210d3f7756 100644
--- a/modules/gdnative/include/pluginscript/godot_pluginscript.h
+++ b/modules/gdnative/include/pluginscript/godot_pluginscript.h
@@ -44,13 +44,12 @@ typedef void godot_pluginscript_language_data;
// --- Instance ---
-// TODO: use godot_string_name for faster lookup ?
typedef struct {
godot_pluginscript_instance_data *(*init)(godot_pluginscript_script_data *p_data, godot_object *p_owner);
void (*finish)(godot_pluginscript_instance_data *p_data);
- godot_bool (*set_prop)(godot_pluginscript_instance_data *p_data, const godot_string *p_name, const godot_variant *p_value);
- godot_bool (*get_prop)(godot_pluginscript_instance_data *p_data, const godot_string *p_name, godot_variant *r_ret);
+ godot_bool (*set_prop)(godot_pluginscript_instance_data *p_data, const godot_string_name *p_name, const godot_variant *p_value);
+ godot_bool (*get_prop)(godot_pluginscript_instance_data *p_data, const godot_string_name *p_name, godot_variant *r_ret);
godot_variant (*call_method)(godot_pluginscript_instance_data *p_data,
const godot_string_name *p_method, const godot_variant **p_args,
@@ -136,7 +135,7 @@ typedef struct {
godot_error (*complete_code)(godot_pluginscript_language_data *p_data, const godot_string *p_code, const godot_string *p_path, godot_object *p_owner, godot_array *r_options, godot_bool *r_force, godot_string *r_call_hint);
void (*auto_indent_code)(godot_pluginscript_language_data *p_data, godot_string *p_code, int p_from_line, int p_to_line);
- void (*add_global_constant)(godot_pluginscript_language_data *p_data, const godot_string *p_variable, const godot_variant *p_value);
+ void (*add_global_constant)(godot_pluginscript_language_data *p_data, const godot_string_name *p_variable, const godot_variant *p_value);
godot_string (*debug_get_error)(godot_pluginscript_language_data *p_data);
int (*debug_get_stack_level_count)(godot_pluginscript_language_data *p_data);
int (*debug_get_stack_level_line)(godot_pluginscript_language_data *p_data, int p_level);
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.cpp b/modules/gdnative/pluginscript/pluginscript_instance.cpp
index c64a00a4d9..0d6dac3268 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_instance.cpp
@@ -39,13 +39,11 @@
#include "pluginscript_script.h"
bool PluginScriptInstance::set(const StringName &p_name, const Variant &p_value) {
- String name = String(p_name);
- return _desc->set_prop(_data, (const godot_string *)&name, (const godot_variant *)&p_value);
+ return _desc->set_prop(_data, (const godot_string_name *)&p_name, (const godot_variant *)&p_value);
}
bool PluginScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
- String name = String(p_name);
- return _desc->get_prop(_data, (const godot_string *)&name, (godot_variant *)&r_ret);
+ return _desc->get_prop(_data, (const godot_string_name *)&p_name, (godot_variant *)&r_ret);
}
Ref<Script> PluginScriptInstance::get_script() const {
diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp
index 41f0e34243..421d6e0a89 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_language.cpp
@@ -187,8 +187,7 @@ void PluginScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int
}
void PluginScriptLanguage::add_global_constant(const StringName &p_variable, const Variant &p_value) {
- const String variable = String(p_variable);
- _desc.add_global_constant(_data, (godot_string *)&variable, (godot_variant *)&p_value);
+ _desc.add_global_constant(_data, (godot_string_name *)&p_variable, (godot_variant *)&p_value);
}
/* LOADER FUNCTIONS */
diff --git a/modules/gdnavigation/SCsub b/modules/gdnavigation/SCsub
new file mode 100644
index 0000000000..9d462f92a7
--- /dev/null
+++ b/modules/gdnavigation/SCsub
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_navigation = env_modules.Clone()
+
+# Recast Thirdparty source files
+if env['builtin_recast']:
+ thirdparty_dir = "#thirdparty/recastnavigation/Recast/"
+ thirdparty_sources = [
+ "Source/Recast.cpp",
+ "Source/RecastAlloc.cpp",
+ "Source/RecastArea.cpp",
+ "Source/RecastAssert.cpp",
+ "Source/RecastContour.cpp",
+ "Source/RecastFilter.cpp",
+ "Source/RecastLayers.cpp",
+ "Source/RecastMesh.cpp",
+ "Source/RecastMeshDetail.cpp",
+ "Source/RecastRasterization.cpp",
+ "Source/RecastRegion.cpp",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_navigation.Prepend(CPPPATH=[thirdparty_dir + "/Include"])
+
+ env_thirdparty = env_navigation.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
+
+# RVO Thirdparty source files
+if env['builtin_rvo2']:
+ thirdparty_dir = "#thirdparty/rvo2"
+ thirdparty_sources = [
+ "/src/Agent.cpp",
+ "/src/KdTree.cpp",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_navigation.Prepend(CPPPATH=[thirdparty_dir + "/src"])
+
+ env_thirdparty = env_navigation.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+
+
+# Godot source files
+env_navigation.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/recast/config.py b/modules/gdnavigation/config.py
index 098f1eafa9..1c8cd12a2d 100644
--- a/modules/recast/config.py
+++ b/modules/gdnavigation/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return env['tools']
+ return True
def configure(env):
pass
diff --git a/modules/gdnavigation/gd_navigation_server.cpp b/modules/gdnavigation/gd_navigation_server.cpp
new file mode 100644
index 0000000000..f3ffd93c9c
--- /dev/null
+++ b/modules/gdnavigation/gd_navigation_server.cpp
@@ -0,0 +1,472 @@
+/*************************************************************************/
+/* gd_navigation_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 "gd_navigation_server.h"
+
+#include "core/os/mutex.h"
+
+#ifndef _3D_DISABLED
+#include "navigation_mesh_generator.h"
+#endif
+
+/**
+ @author AndreaCatania
+*/
+
+/// Creates a struct for each function and a function that once called creates
+/// an instance of that struct with the submited parameters.
+/// Then, that struct is stored in an array; the `sync` function consume that array.
+
+#define COMMAND_1(F_NAME, T_0, D_0) \
+ struct MERGE(F_NAME, _command) : public SetCommand { \
+ T_0 d_0; \
+ MERGE(F_NAME, _command) \
+ (T_0 p_d_0) : \
+ d_0(p_d_0) {} \
+ virtual void exec(GdNavigationServer *server) { \
+ server->MERGE(_cmd_, F_NAME)(d_0); \
+ } \
+ }; \
+ void GdNavigationServer::F_NAME(T_0 D_0) const { \
+ auto cmd = memnew(MERGE(F_NAME, _command)( \
+ D_0)); \
+ add_command(cmd); \
+ } \
+ void GdNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0)
+
+#define COMMAND_2(F_NAME, T_0, D_0, T_1, D_1) \
+ struct MERGE(F_NAME, _command) : public SetCommand { \
+ T_0 d_0; \
+ T_1 d_1; \
+ MERGE(F_NAME, _command) \
+ ( \
+ T_0 p_d_0, \
+ T_1 p_d_1) : \
+ d_0(p_d_0), \
+ d_1(p_d_1) {} \
+ virtual void exec(GdNavigationServer *server) { \
+ server->MERGE(_cmd_, F_NAME)(d_0, d_1); \
+ } \
+ }; \
+ void GdNavigationServer::F_NAME(T_0 D_0, T_1 D_1) const { \
+ auto cmd = memnew(MERGE(F_NAME, _command)( \
+ D_0, \
+ D_1)); \
+ add_command(cmd); \
+ } \
+ void GdNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1)
+
+#define COMMAND_4(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3) \
+ struct MERGE(F_NAME, _command) : public SetCommand { \
+ T_0 d_0; \
+ T_1 d_1; \
+ T_2 d_2; \
+ T_3 d_3; \
+ MERGE(F_NAME, _command) \
+ ( \
+ T_0 p_d_0, \
+ T_1 p_d_1, \
+ T_2 p_d_2, \
+ T_3 p_d_3) : \
+ d_0(p_d_0), \
+ d_1(p_d_1), \
+ d_2(p_d_2), \
+ d_3(p_d_3) {} \
+ virtual void exec(GdNavigationServer *server) { \
+ server->MERGE(_cmd_, F_NAME)(d_0, d_1, d_2, d_3); \
+ } \
+ }; \
+ void GdNavigationServer::F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) const { \
+ auto cmd = memnew(MERGE(F_NAME, _command)( \
+ D_0, \
+ D_1, \
+ D_2, \
+ D_3)); \
+ add_command(cmd); \
+ } \
+ void GdNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3)
+
+GdNavigationServer::GdNavigationServer() :
+ NavigationServer(),
+ active(true) {
+ commands_mutex = Mutex::create();
+ operations_mutex = Mutex::create();
+}
+
+GdNavigationServer::~GdNavigationServer() {}
+
+void GdNavigationServer::add_command(SetCommand *command) const {
+ auto mut_this = const_cast<GdNavigationServer *>(this);
+ commands_mutex->lock();
+ mut_this->commands.push_back(command);
+ commands_mutex->unlock();
+}
+
+RID GdNavigationServer::map_create() const {
+ auto mut_this = const_cast<GdNavigationServer *>(this);
+ mut_this->operations_mutex->lock();
+ NavMap *space = memnew(NavMap);
+ RID rid = map_owner.make_rid(space);
+ space->set_self(rid);
+ mut_this->operations_mutex->unlock();
+ return rid;
+}
+
+COMMAND_2(map_set_active, RID, p_map, bool, p_active) {
+ NavMap *map = map_owner.get(p_map);
+ ERR_FAIL_COND(map == NULL);
+
+ if (p_active) {
+ if (!map_is_active(p_map)) {
+ active_maps.push_back(map);
+ }
+ } else {
+ active_maps.erase(map);
+ }
+}
+
+bool GdNavigationServer::map_is_active(RID p_map) const {
+ NavMap *map = map_owner.get(p_map);
+ ERR_FAIL_COND_V(map == NULL, false);
+
+ return active_maps.find(map) >= 0;
+}
+
+COMMAND_2(map_set_up, RID, p_map, Vector3, p_up) {
+ NavMap *map = map_owner.get(p_map);
+ ERR_FAIL_COND(map == NULL);
+
+ map->set_up(p_up);
+}
+
+Vector3 GdNavigationServer::map_get_up(RID p_map) const {
+ NavMap *map = map_owner.get(p_map);
+ ERR_FAIL_COND_V(map == NULL, Vector3());
+
+ return map->get_up();
+}
+
+COMMAND_2(map_set_cell_size, RID, p_map, real_t, p_cell_size) {
+ NavMap *map = map_owner.get(p_map);
+ ERR_FAIL_COND(map == NULL);
+
+ map->set_cell_size(p_cell_size);
+}
+
+real_t GdNavigationServer::map_get_cell_size(RID p_map) const {
+ NavMap *map = map_owner.get(p_map);
+ ERR_FAIL_COND_V(map == NULL, 0);
+
+ return map->get_cell_size();
+}
+
+COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin) {
+ NavMap *map = map_owner.get(p_map);
+ ERR_FAIL_COND(map == NULL);
+
+ map->set_edge_connection_margin(p_connection_margin);
+}
+
+real_t GdNavigationServer::map_get_edge_connection_margin(RID p_map) const {
+ NavMap *map = map_owner.get(p_map);
+ ERR_FAIL_COND_V(map == NULL, 0);
+
+ return map->get_edge_connection_margin();
+}
+
+Vector<Vector3> GdNavigationServer::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize) const {
+ NavMap *map = map_owner.get(p_map);
+ ERR_FAIL_COND_V(map == NULL, Vector<Vector3>());
+
+ return map->get_path(p_origin, p_destination, p_optimize);
+}
+
+RID GdNavigationServer::region_create() const {
+ auto mut_this = const_cast<GdNavigationServer *>(this);
+ mut_this->operations_mutex->lock();
+ NavRegion *reg = memnew(NavRegion);
+ RID rid = region_owner.make_rid(reg);
+ reg->set_self(rid);
+ mut_this->operations_mutex->unlock();
+ return rid;
+}
+
+COMMAND_2(region_set_map, RID, p_region, RID, p_map) {
+ NavRegion *region = region_owner.get(p_region);
+ ERR_FAIL_COND(region == NULL);
+
+ if (region->get_map() != NULL) {
+
+ if (region->get_map()->get_self() == p_map)
+ return; // Pointless
+
+ region->get_map()->remove_region(region);
+ region->set_map(NULL);
+ }
+
+ if (p_map.is_valid()) {
+ NavMap *map = map_owner.get(p_map);
+ ERR_FAIL_COND(map == NULL);
+
+ map->add_region(region);
+ region->set_map(map);
+ }
+}
+
+COMMAND_2(region_set_transform, RID, p_region, Transform, p_transform) {
+ NavRegion *region = region_owner.get(p_region);
+ ERR_FAIL_COND(region == NULL);
+
+ region->set_transform(p_transform);
+}
+
+COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh) {
+ NavRegion *region = region_owner.get(p_region);
+ ERR_FAIL_COND(region == NULL);
+
+ region->set_mesh(p_nav_mesh);
+}
+
+void GdNavigationServer::region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const {
+ ERR_FAIL_COND(r_mesh.is_null());
+ ERR_FAIL_COND(p_node == NULL);
+
+#ifndef _3D_DISABLED
+ NavigationMeshGenerator::get_singleton()->clear(r_mesh);
+ NavigationMeshGenerator::get_singleton()->bake(r_mesh, p_node);
+#endif
+}
+
+RID GdNavigationServer::agent_create() const {
+ auto mut_this = const_cast<GdNavigationServer *>(this);
+ mut_this->operations_mutex->lock();
+ RvoAgent *agent = memnew(RvoAgent());
+ RID rid = agent_owner.make_rid(agent);
+ agent->set_self(rid);
+ mut_this->operations_mutex->unlock();
+ return rid;
+}
+
+COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
+ RvoAgent *agent = agent_owner.get(p_agent);
+ ERR_FAIL_COND(agent == NULL);
+
+ if (agent->get_map()) {
+ if (agent->get_map()->get_self() == p_map)
+ return; // Pointless
+
+ agent->get_map()->remove_agent(agent);
+ }
+
+ agent->set_map(NULL);
+
+ if (p_map.is_valid()) {
+ NavMap *map = map_owner.get(p_map);
+ ERR_FAIL_COND(map == NULL);
+
+ agent->set_map(map);
+ map->add_agent(agent);
+
+ if (agent->has_callback()) {
+ map->set_agent_as_controlled(agent);
+ }
+ }
+}
+
+COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist) {
+ RvoAgent *agent = agent_owner.get(p_agent);
+ ERR_FAIL_COND(agent == NULL);
+
+ agent->get_agent()->neighborDist_ = p_dist;
+}
+
+COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) {
+ RvoAgent *agent = agent_owner.get(p_agent);
+ ERR_FAIL_COND(agent == NULL);
+
+ agent->get_agent()->maxNeighbors_ = p_count;
+}
+
+COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time) {
+ RvoAgent *agent = agent_owner.get(p_agent);
+ ERR_FAIL_COND(agent == NULL);
+
+ agent->get_agent()->timeHorizon_ = p_time;
+}
+
+COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius) {
+ RvoAgent *agent = agent_owner.get(p_agent);
+ ERR_FAIL_COND(agent == NULL);
+
+ agent->get_agent()->radius_ = p_radius;
+}
+
+COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed) {
+ RvoAgent *agent = agent_owner.get(p_agent);
+ ERR_FAIL_COND(agent == NULL);
+
+ agent->get_agent()->maxSpeed_ = p_max_speed;
+}
+
+COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity) {
+ RvoAgent *agent = agent_owner.get(p_agent);
+ ERR_FAIL_COND(agent == NULL);
+
+ agent->get_agent()->velocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
+}
+
+COMMAND_2(agent_set_target_velocity, RID, p_agent, Vector3, p_velocity) {
+ RvoAgent *agent = agent_owner.get(p_agent);
+ ERR_FAIL_COND(agent == NULL);
+
+ agent->get_agent()->prefVelocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
+}
+
+COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position) {
+ RvoAgent *agent = agent_owner.get(p_agent);
+ ERR_FAIL_COND(agent == NULL);
+
+ agent->get_agent()->position_ = RVO::Vector3(p_position.x, p_position.y, p_position.z);
+}
+
+COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore) {
+ RvoAgent *agent = agent_owner.get(p_agent);
+ ERR_FAIL_COND(agent == NULL);
+
+ agent->get_agent()->ignore_y_ = p_ignore;
+}
+
+bool GdNavigationServer::agent_is_map_changed(RID p_agent) const {
+ RvoAgent *agent = agent_owner.get(p_agent);
+ ERR_FAIL_COND_V(agent == NULL, false);
+
+ return agent->is_map_changed();
+}
+
+COMMAND_4(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_method, Variant, p_udata) {
+ RvoAgent *agent = agent_owner.get(p_agent);
+ ERR_FAIL_COND(agent == NULL);
+
+ agent->set_callback(p_receiver == NULL ? 0 : p_receiver->get_instance_id(), p_method, p_udata);
+
+ if (agent->get_map()) {
+ if (p_receiver == NULL) {
+ agent->get_map()->remove_agent_as_controlled(agent);
+ } else {
+ agent->get_map()->set_agent_as_controlled(agent);
+ }
+ }
+}
+
+COMMAND_1(free, RID, p_object) {
+ if (map_owner.owns(p_object)) {
+ NavMap *map = map_owner.get(p_object);
+
+ // Removes any assigned region
+ std::vector<NavRegion *> regions = map->get_regions();
+ for (size_t i(0); i < regions.size(); i++) {
+ map->remove_region(regions[i]);
+ regions[i]->set_map(NULL);
+ }
+
+ // Remove any assigned agent
+ std::vector<RvoAgent *> agents = map->get_agents();
+ for (size_t i(0); i < agents.size(); i++) {
+ map->remove_agent(agents[i]);
+ agents[i]->set_map(NULL);
+ }
+
+ active_maps.erase(map);
+ map_owner.free(p_object);
+ memdelete(map);
+
+ } else if (region_owner.owns(p_object)) {
+ NavRegion *region = region_owner.get(p_object);
+
+ // Removes this region from the map if assigned
+ if (region->get_map() != NULL) {
+ region->get_map()->remove_region(region);
+ region->set_map(NULL);
+ }
+
+ region_owner.free(p_object);
+ memdelete(region);
+
+ } else if (agent_owner.owns(p_object)) {
+ RvoAgent *agent = agent_owner.get(p_object);
+
+ // Removes this agent from the map if assigned
+ if (agent->get_map() != NULL) {
+ agent->get_map()->remove_agent(agent);
+ agent->set_map(NULL);
+ }
+
+ agent_owner.free(p_object);
+ memdelete(agent);
+
+ } else {
+ ERR_FAIL_COND("Invalid ID.");
+ }
+}
+
+void GdNavigationServer::set_active(bool p_active) const {
+ auto mut_this = const_cast<GdNavigationServer *>(this);
+ mut_this->operations_mutex->lock();
+ mut_this->active = p_active;
+ mut_this->operations_mutex->unlock();
+}
+
+void GdNavigationServer::step(real_t p_delta_time) {
+ if (!active) {
+ return;
+ }
+
+ // With c++ we can't be 100% sure this is called in single thread so use the mutex.
+ commands_mutex->lock();
+ operations_mutex->lock();
+ for (size_t i(0); i < commands.size(); i++) {
+ commands[i]->exec(this);
+ memdelete(commands[i]);
+ }
+ commands.clear();
+ operations_mutex->unlock();
+ commands_mutex->unlock();
+
+ // These are internal operations so don't need to be shielded.
+ for (int i(0); i < active_maps.size(); i++) {
+ active_maps[i]->sync();
+ active_maps[i]->step(p_delta_time);
+ active_maps[i]->dispatch_callbacks();
+ }
+}
+
+#undef COMMAND_1
+#undef COMMAND_2
+#undef COMMAND_4
diff --git a/modules/gdnavigation/gd_navigation_server.h b/modules/gdnavigation/gd_navigation_server.h
new file mode 100644
index 0000000000..80ba06880c
--- /dev/null
+++ b/modules/gdnavigation/gd_navigation_server.h
@@ -0,0 +1,134 @@
+/*************************************************************************/
+/* gd_navigation_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 GD_NAVIGATION_SERVER_H
+#define GD_NAVIGATION_SERVER_H
+
+#include "servers/navigation_server.h"
+
+#include "nav_map.h"
+#include "nav_region.h"
+#include "rvo_agent.h"
+
+/**
+ @author AndreaCatania
+*/
+
+/// The commands are functions executed during the `sync` phase.
+
+#define MERGE_INTERNAL(A, B) A##B
+#define MERGE(A, B) MERGE_INTERNAL(A, B)
+
+#define COMMAND_1(F_NAME, T_0, D_0) \
+ virtual void F_NAME(T_0 D_0) const; \
+ void MERGE(_cmd_, F_NAME)(T_0 D_0)
+
+#define COMMAND_2(F_NAME, T_0, D_0, T_1, D_1) \
+ virtual void F_NAME(T_0 D_0, T_1 D_1) const; \
+ void MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1)
+
+#define COMMAND_4_DEF(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, D_3_DEF) \
+ virtual void F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3 = D_3_DEF) const; \
+ void MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3)
+
+class GdNavigationServer;
+class Mutex;
+
+struct SetCommand {
+ virtual ~SetCommand() {}
+ virtual void exec(GdNavigationServer *server) = 0;
+};
+
+class GdNavigationServer : public NavigationServer {
+ Mutex *commands_mutex;
+ /// Mutex used to make any operation threadsafe.
+ Mutex *operations_mutex;
+
+ std::vector<SetCommand *> commands;
+
+ mutable RID_Owner<NavMap> map_owner;
+ mutable RID_Owner<NavRegion> region_owner;
+ mutable RID_Owner<RvoAgent> agent_owner;
+
+ bool active;
+ Vector<NavMap *> active_maps;
+
+public:
+ GdNavigationServer();
+ virtual ~GdNavigationServer();
+
+ void add_command(SetCommand *command) const;
+
+ virtual RID map_create() const;
+ COMMAND_2(map_set_active, RID, p_map, bool, p_active);
+ virtual bool map_is_active(RID p_map) const;
+
+ COMMAND_2(map_set_up, RID, p_map, Vector3, p_up);
+ virtual Vector3 map_get_up(RID p_map) const;
+
+ COMMAND_2(map_set_cell_size, RID, p_map, real_t, p_cell_size);
+ virtual real_t map_get_cell_size(RID p_map) const;
+
+ COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin);
+ virtual real_t map_get_edge_connection_margin(RID p_map) const;
+
+ virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize) const;
+
+ virtual RID region_create() const;
+ COMMAND_2(region_set_map, RID, p_region, RID, p_map);
+ COMMAND_2(region_set_transform, RID, p_region, Transform, p_transform);
+ COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh);
+ virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const;
+
+ virtual RID agent_create() const;
+ COMMAND_2(agent_set_map, RID, p_agent, RID, p_map);
+ COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist);
+ COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count);
+ COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time);
+ COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius);
+ COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed);
+ COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity);
+ COMMAND_2(agent_set_target_velocity, RID, p_agent, Vector3, p_velocity);
+ COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position);
+ COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore);
+ virtual bool agent_is_map_changed(RID p_agent) const;
+ COMMAND_4_DEF(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_method, Variant, p_udata, Variant());
+
+ COMMAND_1(free, RID, p_object);
+
+ virtual void set_active(bool p_active) const;
+ virtual void step(real_t p_delta_time);
+};
+
+#undef COMMAND_1
+#undef COMMAND_2
+#undef COMMAND_4_DEF
+
+#endif // GD_NAVIGATION_SERVER_H
diff --git a/modules/gdnavigation/nav_map.cpp b/modules/gdnavigation/nav_map.cpp
new file mode 100644
index 0000000000..3d0e02c77d
--- /dev/null
+++ b/modules/gdnavigation/nav_map.cpp
@@ -0,0 +1,665 @@
+/*************************************************************************/
+/* rvo_space.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 "nav_map.h"
+
+#include "core/os/threaded_array_processor.h"
+#include "nav_region.h"
+#include "rvo_agent.h"
+#include <algorithm>
+
+/**
+ @author AndreaCatania
+*/
+
+#define USE_ENTRY_POINT
+
+NavMap::NavMap() :
+ up(0, 1, 0),
+ cell_size(0.3),
+ edge_connection_margin(5.0),
+ regenerate_polygons(true),
+ regenerate_links(true),
+ agents_dirty(false),
+ deltatime(0.0),
+ map_update_id(0) {}
+
+void NavMap::set_up(Vector3 p_up) {
+ up = p_up;
+ regenerate_polygons = true;
+}
+
+void NavMap::set_cell_size(float p_cell_size) {
+ cell_size = p_cell_size;
+ regenerate_polygons = true;
+}
+
+void NavMap::set_edge_connection_margin(float p_edge_connection_margin) {
+ edge_connection_margin = p_edge_connection_margin;
+ regenerate_links = true;
+}
+
+gd::PointKey NavMap::get_point_key(const Vector3 &p_pos) const {
+ const int x = int(Math::floor(p_pos.x / cell_size));
+ const int y = int(Math::floor(p_pos.y / cell_size));
+ const int z = int(Math::floor(p_pos.z / cell_size));
+
+ gd::PointKey p;
+ p.key = 0;
+ p.x = x;
+ p.y = y;
+ p.z = z;
+ return p;
+}
+
+Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize) const {
+
+ const gd::Polygon *begin_poly = NULL;
+ const gd::Polygon *end_poly = NULL;
+ Vector3 begin_point;
+ Vector3 end_point;
+ float begin_d = 1e20;
+ float end_d = 1e20;
+
+ // Find the initial poly and the end poly on this map.
+ for (size_t i(0); i < polygons.size(); i++) {
+ const gd::Polygon &p = polygons[i];
+
+ // For each point cast a face and check the distance between the origin/destination
+ for (size_t point_id = 2; point_id < p.points.size(); point_id++) {
+
+ Face3 f(p.points[point_id - 2].pos, p.points[point_id - 1].pos, p.points[point_id].pos);
+ Vector3 spoint = f.get_closest_point_to(p_origin);
+ float dpoint = spoint.distance_to(p_origin);
+ if (dpoint < begin_d) {
+ begin_d = dpoint;
+ begin_poly = &p;
+ begin_point = spoint;
+ }
+
+ spoint = f.get_closest_point_to(p_destination);
+ dpoint = spoint.distance_to(p_destination);
+ if (dpoint < end_d) {
+ end_d = dpoint;
+ end_poly = &p;
+ end_point = spoint;
+ }
+ }
+ }
+
+ if (!begin_poly || !end_poly) {
+ // No path
+ return Vector<Vector3>();
+ }
+
+ if (begin_poly == end_poly) {
+
+ Vector<Vector3> path;
+ path.resize(2);
+ path.write[0] = begin_point;
+ path.write[1] = end_point;
+ return path;
+ }
+
+ std::vector<gd::NavigationPoly> navigation_polys;
+ navigation_polys.reserve(polygons.size() * 0.75);
+
+ // The elements indices in the `navigation_polys`.
+ int least_cost_id(-1);
+ List<uint32_t> open_list;
+ bool found_route = false;
+
+ navigation_polys.push_back(gd::NavigationPoly(begin_poly));
+ {
+ least_cost_id = 0;
+ gd::NavigationPoly *least_cost_poly = &navigation_polys[least_cost_id];
+ least_cost_poly->self_id = least_cost_id;
+ least_cost_poly->entry = begin_point;
+ }
+
+ open_list.push_back(0);
+
+ const gd::Polygon *reachable_end = NULL;
+ float reachable_d = 1e30;
+ bool is_reachable = true;
+
+ while (found_route == false) {
+
+ {
+ // Takes the current least_cost_poly neighbors and compute the traveled_distance of each
+ for (size_t i = 0; i < navigation_polys[least_cost_id].poly->edges.size(); i++) {
+ gd::NavigationPoly *least_cost_poly = &navigation_polys[least_cost_id];
+
+ const gd::Edge &edge = least_cost_poly->poly->edges[i];
+ if (!edge.other_polygon)
+ continue;
+
+#ifdef USE_ENTRY_POINT
+ Vector3 edge_line[2] = {
+ least_cost_poly->poly->points[i].pos,
+ least_cost_poly->poly->points[(i + 1) % least_cost_poly->poly->points.size()].pos
+ };
+
+ const Vector3 new_entry = Geometry::get_closest_point_to_segment(least_cost_poly->entry, edge_line);
+ const float new_distance = least_cost_poly->entry.distance_to(new_entry) + least_cost_poly->traveled_distance;
+#else
+ const float new_distance = least_cost_poly->poly->center.distance_to(edge.other_polygon->center) + least_cost_poly->traveled_distance;
+#endif
+
+ auto it = std::find(
+ navigation_polys.begin(),
+ navigation_polys.end(),
+ gd::NavigationPoly(edge.other_polygon));
+
+ if (it != navigation_polys.end()) {
+ // Oh this was visited already, can we win the cost?
+ if (it->traveled_distance > new_distance) {
+
+ it->prev_navigation_poly_id = least_cost_id;
+ it->back_navigation_edge = edge.other_edge;
+ it->traveled_distance = new_distance;
+#ifdef USE_ENTRY_POINT
+ it->entry = new_entry;
+#endif
+ }
+ } else {
+ // Add to open neighbours
+
+ navigation_polys.push_back(gd::NavigationPoly(edge.other_polygon));
+ gd::NavigationPoly *np = &navigation_polys[navigation_polys.size() - 1];
+
+ np->self_id = navigation_polys.size() - 1;
+ np->prev_navigation_poly_id = least_cost_id;
+ np->back_navigation_edge = edge.other_edge;
+ np->traveled_distance = new_distance;
+#ifdef USE_ENTRY_POINT
+ np->entry = new_entry;
+#endif
+ open_list.push_back(navigation_polys.size() - 1);
+ }
+ }
+ }
+
+ // Removes the least cost polygon from the open list so we can advance.
+ open_list.erase(least_cost_id);
+
+ if (open_list.size() == 0) {
+ // When the open list is empty at this point the End Polygon is not reachable
+ // so use the further reachable polygon
+ ERR_BREAK_MSG(is_reachable == false, "It's not expect to not find the most reachable polygons");
+ is_reachable = false;
+ if (reachable_end == NULL) {
+ // The path is not found and there is not a way out.
+ break;
+ }
+
+ // Set as end point the furthest reachable point.
+ end_poly = reachable_end;
+ end_d = 1e20;
+ for (size_t point_id = 2; point_id < end_poly->points.size(); point_id++) {
+ Face3 f(end_poly->points[point_id - 2].pos, end_poly->points[point_id - 1].pos, end_poly->points[point_id].pos);
+ Vector3 spoint = f.get_closest_point_to(p_destination);
+ float dpoint = spoint.distance_to(p_destination);
+ if (dpoint < end_d) {
+ end_point = spoint;
+ end_d = dpoint;
+ }
+ }
+
+ // Reset open and navigation_polys
+ gd::NavigationPoly np = navigation_polys[0];
+ navigation_polys.clear();
+ navigation_polys.push_back(np);
+ open_list.clear();
+ open_list.push_back(0);
+
+ reachable_end = NULL;
+
+ continue;
+ }
+
+ // Now take the new least_cost_poly from the open list.
+ least_cost_id = -1;
+ float least_cost = 1e30;
+
+ for (auto element = open_list.front(); element != NULL; element = element->next()) {
+ gd::NavigationPoly *np = &navigation_polys[element->get()];
+ float cost = np->traveled_distance;
+#ifdef USE_ENTRY_POINT
+ cost += np->entry.distance_to(end_point);
+#else
+ cost += np->poly->center.distance_to(end_point);
+#endif
+ if (cost < least_cost) {
+ least_cost_id = np->self_id;
+ least_cost = cost;
+ }
+ }
+
+ // Stores the further reachable end polygon, in case our goal is not reachable.
+ if (is_reachable) {
+ float d = navigation_polys[least_cost_id].entry.distance_to(p_destination);
+ if (reachable_d > d) {
+ reachable_d = d;
+ reachable_end = navigation_polys[least_cost_id].poly;
+ }
+ }
+
+ ERR_BREAK(least_cost_id == -1);
+
+ // Check if we reached the end
+ if (navigation_polys[least_cost_id].poly == end_poly) {
+ // Yep, done!!
+ found_route = true;
+ break;
+ }
+ }
+
+ if (found_route) {
+
+ Vector<Vector3> path;
+ if (p_optimize) {
+
+ // String pulling
+
+ gd::NavigationPoly *apex_poly = &navigation_polys[least_cost_id];
+ Vector3 apex_point = end_point;
+ Vector3 portal_left = apex_point;
+ Vector3 portal_right = apex_point;
+ gd::NavigationPoly *left_poly = apex_poly;
+ gd::NavigationPoly *right_poly = apex_poly;
+ gd::NavigationPoly *p = apex_poly;
+
+ path.push_back(end_point);
+
+ while (p) {
+
+ Vector3 left;
+ Vector3 right;
+
+#define CLOCK_TANGENT(m_a, m_b, m_c) (((m_a) - (m_c)).cross((m_a) - (m_b)))
+
+ if (p->poly == begin_poly) {
+ left = begin_point;
+ right = begin_point;
+ } else {
+ int prev = p->back_navigation_edge;
+ int prev_n = (p->back_navigation_edge + 1) % p->poly->points.size();
+ left = p->poly->points[prev].pos;
+ right = p->poly->points[prev_n].pos;
+
+ //if (CLOCK_TANGENT(apex_point,left,(left+right)*0.5).dot(up) < 0){
+ if (p->poly->clockwise) {
+ SWAP(left, right);
+ }
+ }
+
+ bool skip = false;
+
+ if (CLOCK_TANGENT(apex_point, portal_left, left).dot(up) >= 0) {
+ //process
+ if (portal_left == apex_point || CLOCK_TANGENT(apex_point, left, portal_right).dot(up) > 0) {
+ left_poly = p;
+ portal_left = left;
+ } else {
+
+ clip_path(navigation_polys, path, apex_poly, portal_right, right_poly);
+
+ apex_point = portal_right;
+ p = right_poly;
+ left_poly = p;
+ apex_poly = p;
+ portal_left = apex_point;
+ portal_right = apex_point;
+ path.push_back(apex_point);
+ skip = true;
+ }
+ }
+
+ if (!skip && CLOCK_TANGENT(apex_point, portal_right, right).dot(up) <= 0) {
+ //process
+ if (portal_right == apex_point || CLOCK_TANGENT(apex_point, right, portal_left).dot(up) < 0) {
+ right_poly = p;
+ portal_right = right;
+ } else {
+
+ clip_path(navigation_polys, path, apex_poly, portal_left, left_poly);
+
+ apex_point = portal_left;
+ p = left_poly;
+ right_poly = p;
+ apex_poly = p;
+ portal_right = apex_point;
+ portal_left = apex_point;
+ path.push_back(apex_point);
+ }
+ }
+
+ if (p->prev_navigation_poly_id != -1)
+ p = &navigation_polys[p->prev_navigation_poly_id];
+ else
+ // The end
+ p = NULL;
+ }
+
+ if (path[path.size() - 1] != begin_point)
+ path.push_back(begin_point);
+
+ path.invert();
+
+ } else {
+ path.push_back(end_point);
+
+ // Add mid points
+ int np_id = least_cost_id;
+ while (np_id != -1) {
+
+#ifdef USE_ENTRY_POINT
+ Vector3 point = navigation_polys[np_id].entry;
+#else
+ int prev = navigation_polys[np_id].back_navigation_edge;
+ int prev_n = (navigation_polys[np_id].back_navigation_edge + 1) % navigation_polys[np_id].poly->points.size();
+ Vector3 point = (navigation_polys[np_id].poly->points[prev].pos + navigation_polys[np_id].poly->points[prev_n].pos) * 0.5;
+#endif
+
+ path.push_back(point);
+ np_id = navigation_polys[np_id].prev_navigation_poly_id;
+ }
+
+ path.invert();
+ }
+
+ return path;
+ }
+ return Vector<Vector3>();
+}
+
+void NavMap::add_region(NavRegion *p_region) {
+ regions.push_back(p_region);
+ regenerate_links = true;
+}
+
+void NavMap::remove_region(NavRegion *p_region) {
+ regions.push_back(p_region);
+ regenerate_links = true;
+}
+
+bool NavMap::has_agent(RvoAgent *agent) const {
+ return std::find(agents.begin(), agents.end(), agent) != agents.end();
+}
+
+void NavMap::add_agent(RvoAgent *agent) {
+ if (!has_agent(agent)) {
+ agents.push_back(agent);
+ agents_dirty = true;
+ }
+}
+
+void NavMap::remove_agent(RvoAgent *agent) {
+ remove_agent_as_controlled(agent);
+ auto it = std::find(agents.begin(), agents.end(), agent);
+ if (it != agents.end()) {
+ agents.erase(it);
+ agents_dirty = true;
+ }
+}
+
+void NavMap::set_agent_as_controlled(RvoAgent *agent) {
+ const bool exist = std::find(controlled_agents.begin(), controlled_agents.end(), agent) != controlled_agents.end();
+ if (!exist) {
+ ERR_FAIL_COND(!has_agent(agent));
+ controlled_agents.push_back(agent);
+ }
+}
+
+void NavMap::remove_agent_as_controlled(RvoAgent *agent) {
+ auto it = std::find(controlled_agents.begin(), controlled_agents.end(), agent);
+ if (it != controlled_agents.end()) {
+ controlled_agents.erase(it);
+ }
+}
+
+void NavMap::sync() {
+
+ if (regenerate_polygons) {
+ for (size_t r(0); r < regions.size(); r++) {
+ regions[r]->scratch_polygons();
+ }
+ regenerate_links = true;
+ }
+
+ for (size_t r(0); r < regions.size(); r++) {
+ if (regions[r]->sync()) {
+ regenerate_links = true;
+ }
+ }
+
+ if (regenerate_links) {
+ // Copy all region polygons in the map.
+ int count = 0;
+ for (size_t r(0); r < regions.size(); r++) {
+ count += regions[r]->get_polygons().size();
+ }
+
+ polygons.resize(count);
+ count = 0;
+
+ for (size_t r(0); r < regions.size(); r++) {
+ std::copy(
+ regions[r]->get_polygons().data(),
+ regions[r]->get_polygons().data() + regions[r]->get_polygons().size(),
+ polygons.begin() + count);
+
+ count += regions[r]->get_polygons().size();
+ }
+
+ // Connects the `Edges` of all the `Polygons` of all `Regions` each other.
+ Map<gd::EdgeKey, gd::Connection> connections;
+
+ for (size_t poly_id(0); poly_id < polygons.size(); poly_id++) {
+ gd::Polygon &poly(polygons[poly_id]);
+
+ for (size_t p(0); p < poly.points.size(); p++) {
+ int next_point = (p + 1) % poly.points.size();
+ gd::EdgeKey ek(poly.points[p].key, poly.points[next_point].key);
+
+ Map<gd::EdgeKey, gd::Connection>::Element *connection = connections.find(ek);
+ if (!connection) {
+ // Nothing yet
+ gd::Connection c;
+ c.A = &poly;
+ c.A_edge = p;
+ c.B = NULL;
+ c.B_edge = -1;
+ connections[ek] = c;
+
+ } else if (connection->get().B == NULL) {
+ CRASH_COND(connection->get().A == NULL); // Unreachable
+
+ // Connect the two Polygons by this edge
+ connection->get().B = &poly;
+ connection->get().B_edge = p;
+
+ connection->get().A->edges[connection->get().A_edge].this_edge = connection->get().A_edge;
+ connection->get().A->edges[connection->get().A_edge].other_polygon = connection->get().B;
+ connection->get().A->edges[connection->get().A_edge].other_edge = connection->get().B_edge;
+
+ connection->get().B->edges[connection->get().B_edge].this_edge = connection->get().B_edge;
+ connection->get().B->edges[connection->get().B_edge].other_polygon = connection->get().A;
+ connection->get().B->edges[connection->get().B_edge].other_edge = connection->get().A_edge;
+ } else {
+ // The edge is already connected with another edge, skip.
+ }
+ }
+ }
+
+ // Takes all the free edges.
+ std::vector<gd::FreeEdge> free_edges;
+ free_edges.reserve(connections.size());
+
+ for (auto connection_element = connections.front(); connection_element; connection_element = connection_element->next()) {
+ if (connection_element->get().B == NULL) {
+ CRASH_COND(connection_element->get().A == NULL); // Unreachable
+ CRASH_COND(connection_element->get().A_edge < 0); // Unreachable
+
+ // This is a free edge
+ uint32_t id(free_edges.size());
+ free_edges.push_back(gd::FreeEdge());
+ free_edges[id].is_free = true;
+ free_edges[id].poly = connection_element->get().A;
+ free_edges[id].edge_id = connection_element->get().A_edge;
+ uint32_t point_0(free_edges[id].edge_id);
+ uint32_t point_1((free_edges[id].edge_id + 1) % free_edges[id].poly->points.size());
+ Vector3 pos_0 = free_edges[id].poly->points[point_0].pos;
+ Vector3 pos_1 = free_edges[id].poly->points[point_1].pos;
+ Vector3 relative = pos_1 - pos_0;
+ free_edges[id].edge_center = (pos_0 + pos_1) / 2.0;
+ free_edges[id].edge_dir = relative.normalized();
+ free_edges[id].edge_len_squared = relative.length_squared();
+ }
+ }
+
+ const float ecm_squared(edge_connection_margin * edge_connection_margin);
+#define LEN_TOLLERANCE 0.1
+#define DIR_TOLLERANCE 0.9
+ // In front of tollerance
+#define IFO_TOLLERANCE 0.5
+
+ // Find the compatible near edges.
+ //
+ // Note:
+ // Considering that the edges must be compatible (for obvious reasons)
+ // to be connected, create new polygons to remove that small gap is
+ // not really useful and would result in wasteful computation during
+ // connection, integration and path finding.
+ for (size_t i(0); i < free_edges.size(); i++) {
+ if (!free_edges[i].is_free) {
+ continue;
+ }
+ gd::FreeEdge &edge = free_edges[i];
+ for (size_t y(0); y < free_edges.size(); y++) {
+ gd::FreeEdge &other_edge = free_edges[y];
+ if (i == y || !other_edge.is_free || edge.poly->owner == other_edge.poly->owner) {
+ continue;
+ }
+
+ Vector3 rel_centers = other_edge.edge_center - edge.edge_center;
+ if (ecm_squared > rel_centers.length_squared() // Are enough closer?
+ && ABS(edge.edge_len_squared - other_edge.edge_len_squared) < LEN_TOLLERANCE // Are the same length?
+ && ABS(edge.edge_dir.dot(other_edge.edge_dir)) > DIR_TOLLERANCE // Are alligned?
+ && ABS(rel_centers.normalized().dot(edge.edge_dir)) < IFO_TOLLERANCE // Are one in front the other?
+ ) {
+ // The edges can be connected
+ edge.is_free = false;
+ other_edge.is_free = false;
+
+ edge.poly->edges[edge.edge_id].this_edge = edge.edge_id;
+ edge.poly->edges[edge.edge_id].other_edge = other_edge.edge_id;
+ edge.poly->edges[edge.edge_id].other_polygon = other_edge.poly;
+
+ other_edge.poly->edges[other_edge.edge_id].this_edge = other_edge.edge_id;
+ other_edge.poly->edges[other_edge.edge_id].other_edge = edge.edge_id;
+ other_edge.poly->edges[other_edge.edge_id].other_polygon = edge.poly;
+ }
+ }
+ }
+ }
+
+ if (regenerate_links) {
+ map_update_id = map_update_id + 1 % 9999999;
+ }
+
+ if (agents_dirty) {
+ std::vector<RVO::Agent *> raw_agents;
+ raw_agents.reserve(agents.size());
+ for (size_t i(0); i < agents.size(); i++)
+ raw_agents.push_back(agents[i]->get_agent());
+ rvo.buildAgentTree(raw_agents);
+ }
+
+ regenerate_polygons = false;
+ regenerate_links = false;
+ agents_dirty = false;
+}
+
+void NavMap::compute_single_step(uint32_t index, RvoAgent **agent) {
+ (*(agent + index))->get_agent()->computeNeighbors(&rvo);
+ (*(agent + index))->get_agent()->computeNewVelocity(deltatime);
+}
+
+void NavMap::step(real_t p_deltatime) {
+ deltatime = p_deltatime;
+ if (controlled_agents.size() > 0) {
+ thread_process_array(
+ controlled_agents.size(),
+ this,
+ &NavMap::compute_single_step,
+ controlled_agents.data());
+ }
+}
+
+void NavMap::dispatch_callbacks() {
+ for (int i(0); i < static_cast<int>(controlled_agents.size()); i++) {
+ controlled_agents[i]->dispatch_callback();
+ }
+}
+
+void NavMap::clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const {
+ Vector3 from = path[path.size() - 1];
+
+ if (from.distance_to(p_to_point) < CMP_EPSILON)
+ return;
+ Plane cut_plane;
+ cut_plane.normal = (from - p_to_point).cross(up);
+ if (cut_plane.normal == Vector3())
+ return;
+ cut_plane.normal.normalize();
+ cut_plane.d = cut_plane.normal.dot(from);
+
+ while (from_poly != p_to_poly) {
+
+ int back_nav_edge = from_poly->back_navigation_edge;
+ Vector3 a = from_poly->poly->points[back_nav_edge].pos;
+ Vector3 b = from_poly->poly->points[(back_nav_edge + 1) % from_poly->poly->points.size()].pos;
+
+ ERR_FAIL_COND(from_poly->prev_navigation_poly_id == -1);
+ from_poly = &p_navigation_polys[from_poly->prev_navigation_poly_id];
+
+ if (a.distance_to(b) > CMP_EPSILON) {
+
+ Vector3 inters;
+ if (cut_plane.intersects_segment(a, b, &inters)) {
+ if (inters.distance_to(p_to_point) > CMP_EPSILON && inters.distance_to(path[path.size() - 1]) > CMP_EPSILON) {
+ path.push_back(inters);
+ }
+ }
+ }
+ }
+}
diff --git a/modules/gdnavigation/nav_map.h b/modules/gdnavigation/nav_map.h
new file mode 100644
index 0000000000..636c07e437
--- /dev/null
+++ b/modules/gdnavigation/nav_map.h
@@ -0,0 +1,137 @@
+/*************************************************************************/
+/* rvo_space.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 RVO_SPACE_H
+#define RVO_SPACE_H
+
+#include "nav_rid.h"
+
+#include "core/math/math_defs.h"
+#include "nav_utils.h"
+#include <KdTree.h>
+
+/**
+ @author AndreaCatania
+*/
+
+class NavRegion;
+class RvoAgent;
+class NavRegion;
+
+class NavMap : public NavRid {
+
+ /// Map Up
+ Vector3 up;
+
+ /// To find the polygons edges the vertices are displaced in a grid where
+ /// each cell has the following cell_size.
+ real_t cell_size;
+
+ /// This value is used to detect the near edges to connect.
+ real_t edge_connection_margin;
+
+ bool regenerate_polygons;
+ bool regenerate_links;
+
+ std::vector<NavRegion *> regions;
+
+ /// Map polygons
+ std::vector<gd::Polygon> polygons;
+
+ /// Rvo world
+ RVO::KdTree rvo;
+
+ /// Is agent array modified?
+ bool agents_dirty;
+
+ /// All the Agents (even the controlled one)
+ std::vector<RvoAgent *> agents;
+
+ /// Controlled agents
+ std::vector<RvoAgent *> controlled_agents;
+
+ /// Physics delta time
+ real_t deltatime;
+
+ /// Change the id each time the map is updated.
+ uint32_t map_update_id;
+
+public:
+ NavMap();
+
+ void set_up(Vector3 p_up);
+ Vector3 get_up() const {
+ return up;
+ }
+
+ void set_cell_size(float p_cell_size);
+ float get_cell_size() const {
+ return cell_size;
+ }
+
+ void set_edge_connection_margin(float p_edge_connection_margin);
+ float get_edge_connection_margin() const {
+ return edge_connection_margin;
+ }
+
+ gd::PointKey get_point_key(const Vector3 &p_pos) const;
+
+ Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize) const;
+
+ void add_region(NavRegion *p_region);
+ void remove_region(NavRegion *p_region);
+ const std::vector<NavRegion *> &get_regions() const {
+ return regions;
+ }
+
+ bool has_agent(RvoAgent *agent) const;
+ void add_agent(RvoAgent *agent);
+ void remove_agent(RvoAgent *agent);
+ const std::vector<RvoAgent *> &get_agents() const {
+ return agents;
+ }
+
+ void set_agent_as_controlled(RvoAgent *agent);
+ void remove_agent_as_controlled(RvoAgent *agent);
+
+ uint32_t get_map_update_id() const {
+ return map_update_id;
+ }
+
+ void sync();
+ void step(real_t p_deltatime);
+ void dispatch_callbacks();
+
+private:
+ void compute_single_step(uint32_t index, RvoAgent **agent);
+ void clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const;
+};
+
+#endif // RVO_SPACE_H
diff --git a/modules/gdnavigation/nav_region.cpp b/modules/gdnavigation/nav_region.cpp
new file mode 100644
index 0000000000..03477085ce
--- /dev/null
+++ b/modules/gdnavigation/nav_region.cpp
@@ -0,0 +1,136 @@
+/*************************************************************************/
+/* nav_region.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 "nav_region.h"
+
+#include "nav_map.h"
+
+/**
+ @author AndreaCatania
+*/
+
+NavRegion::NavRegion() :
+ map(NULL),
+ polygons_dirty(true) {
+}
+
+void NavRegion::set_map(NavMap *p_map) {
+ map = p_map;
+ polygons_dirty = true;
+}
+
+void NavRegion::set_transform(Transform p_transform) {
+ transform = p_transform;
+ polygons_dirty = true;
+}
+
+void NavRegion::set_mesh(Ref<NavigationMesh> p_mesh) {
+ mesh = p_mesh;
+ polygons_dirty = true;
+}
+
+bool NavRegion::sync() {
+ bool something_changed = polygons_dirty /* || something_dirty? */;
+
+ update_polygons();
+
+ return something_changed;
+}
+
+void NavRegion::update_polygons() {
+ if (!polygons_dirty) {
+ return;
+ }
+ polygons.clear();
+ polygons_dirty = false;
+
+ if (map == NULL) {
+ return;
+ }
+
+ if (mesh.is_null())
+ return;
+
+ PoolVector<Vector3> vertices = mesh->get_vertices();
+ int len = vertices.size();
+ if (len == 0)
+ return;
+
+ PoolVector<Vector3>::Read vertices_r = vertices.read();
+
+ polygons.resize(mesh->get_polygon_count());
+
+ // Build
+ for (size_t i(0); i < polygons.size(); i++) {
+
+ gd::Polygon &p = polygons[i];
+ p.owner = this;
+
+ Vector<int> mesh_poly = mesh->get_polygon(i);
+ const int *indices = mesh_poly.ptr();
+ bool valid(true);
+ p.points.resize(mesh_poly.size());
+ p.edges.resize(mesh_poly.size());
+
+ Vector3 center;
+ float sum(0);
+
+ for (int j(0); j < mesh_poly.size(); j++) {
+
+ int idx = indices[j];
+ if (idx < 0 || idx >= len) {
+ valid = false;
+ break;
+ }
+
+ Vector3 point_position = transform.xform(vertices_r[idx]);
+ p.points[j].pos = point_position;
+ p.points[j].key = map->get_point_key(point_position);
+
+ center += point_position; // Composing the center of the polygon
+
+ if (j >= 2) {
+ Vector3 epa = transform.xform(vertices_r[indices[j - 2]]);
+ Vector3 epb = transform.xform(vertices_r[indices[j - 1]]);
+
+ sum += map->get_up().dot((epb - epa).cross(point_position - epa));
+ }
+ }
+
+ if (!valid) {
+ ERR_BREAK_MSG(!valid, "The navigation mesh set in this region is not valid!");
+ }
+
+ p.clockwise = sum > 0;
+ if (mesh_poly.size() != 0) {
+ p.center = center / float(mesh_poly.size());
+ }
+ }
+}
diff --git a/modules/gdnavigation/nav_region.h b/modules/gdnavigation/nav_region.h
new file mode 100644
index 0000000000..c961257163
--- /dev/null
+++ b/modules/gdnavigation/nav_region.h
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* nav_region.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 NAV_REGION_H
+#define NAV_REGION_H
+
+#include "nav_rid.h"
+
+#include "nav_utils.h"
+#include "scene/3d/navigation.h"
+#include <vector>
+
+/**
+ @author AndreaCatania
+*/
+
+class NavMap;
+class NavRegion;
+
+class NavRegion : public NavRid {
+ NavMap *map;
+ Transform transform;
+ Ref<NavigationMesh> mesh;
+
+ bool polygons_dirty;
+
+ /// Cache
+ std::vector<gd::Polygon> polygons;
+
+public:
+ NavRegion();
+
+ void scratch_polygons() {
+ polygons_dirty = true;
+ }
+
+ void set_map(NavMap *p_map);
+ NavMap *get_map() const {
+ return map;
+ }
+
+ void set_transform(Transform transform);
+ const Transform &get_transform() const {
+ return transform;
+ }
+
+ void set_mesh(Ref<NavigationMesh> p_mesh);
+ const Ref<NavigationMesh> get_mesh() const {
+ return mesh;
+ }
+
+ std::vector<gd::Polygon> const &get_polygons() const {
+ return polygons;
+ }
+
+ bool sync();
+
+private:
+ void update_polygons();
+};
+
+#endif // NAV_REGION_H
diff --git a/modules/gdnavigation/nav_rid.h b/modules/gdnavigation/nav_rid.h
new file mode 100644
index 0000000000..96e22800c2
--- /dev/null
+++ b/modules/gdnavigation/nav_rid.h
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* rvo_rid.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 NAV_RID_H
+#define NAV_RID_H
+
+#include "core/rid.h"
+
+/**
+ @author AndreaCatania
+*/
+
+class NavRid : public RID_Data {
+ RID self;
+
+public:
+ _FORCE_INLINE_ void set_self(const RID &p_self) { self = p_self; }
+ _FORCE_INLINE_ RID get_self() const { return self; }
+};
+
+#endif // NAV_RID_H
diff --git a/modules/gdnavigation/nav_utils.h b/modules/gdnavigation/nav_utils.h
new file mode 100644
index 0000000000..1048f58294
--- /dev/null
+++ b/modules/gdnavigation/nav_utils.h
@@ -0,0 +1,169 @@
+/*************************************************************************/
+/* utils.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 NAV_UTILS_H
+#define NAV_UTILS_H
+
+#include "core/math/vector3.h"
+#include <vector>
+
+/**
+ @author AndreaCatania
+*/
+
+class NavRegion;
+
+namespace gd {
+struct Polygon;
+
+union PointKey {
+
+ struct {
+ int64_t x : 21;
+ int64_t y : 22;
+ int64_t z : 21;
+ };
+
+ uint64_t key;
+ bool operator<(const PointKey &p_key) const { return key < p_key.key; }
+};
+
+struct EdgeKey {
+
+ PointKey a;
+ PointKey b;
+
+ bool operator<(const EdgeKey &p_key) const {
+ return (a.key == p_key.a.key) ? (b.key < p_key.b.key) : (a.key < p_key.a.key);
+ }
+
+ EdgeKey(const PointKey &p_a = PointKey(), const PointKey &p_b = PointKey()) :
+ a(p_a),
+ b(p_b) {
+ if (a.key > b.key) {
+ SWAP(a, b);
+ }
+ }
+};
+
+struct Point {
+ Vector3 pos;
+ PointKey key;
+};
+
+struct Edge {
+ /// This edge ID
+ int this_edge;
+
+ /// Other Polygon
+ Polygon *other_polygon;
+
+ /// The other `Polygon` at this edge id has this `Polygon`.
+ int other_edge;
+
+ Edge() {
+ this_edge = -1;
+ other_polygon = NULL;
+ other_edge = -1;
+ }
+};
+
+struct Polygon {
+ NavRegion *owner;
+
+ /// The points of this `Polygon`
+ std::vector<Point> points;
+
+ /// Are the points clockwise ?
+ bool clockwise;
+
+ /// The edges of this `Polygon`
+ std::vector<Edge> edges;
+
+ /// The center of this `Polygon`
+ Vector3 center;
+};
+
+struct Connection {
+
+ Polygon *A;
+ int A_edge;
+ Polygon *B;
+ int B_edge;
+
+ Connection() {
+ A = NULL;
+ B = NULL;
+ A_edge = -1;
+ B_edge = -1;
+ }
+};
+
+struct NavigationPoly {
+ uint32_t self_id;
+ /// This poly.
+ const Polygon *poly;
+ /// The previous navigation poly (id in the `navigation_poly` array).
+ int prev_navigation_poly_id;
+ /// The edge id in this `Poly` to reach the `prev_navigation_poly_id`.
+ uint32_t back_navigation_edge;
+ /// The entry location of this poly.
+ Vector3 entry;
+ /// The distance to the destination.
+ float traveled_distance;
+
+ NavigationPoly(const Polygon *p_poly) :
+ self_id(0),
+ poly(p_poly),
+ prev_navigation_poly_id(-1),
+ back_navigation_edge(0),
+ traveled_distance(0.0) {
+ }
+
+ bool operator==(const NavigationPoly &other) const {
+ return this->poly == other.poly;
+ }
+
+ bool operator!=(const NavigationPoly &other) const {
+ return !operator==(other);
+ }
+};
+
+struct FreeEdge {
+ bool is_free;
+ Polygon *poly;
+ uint32_t edge_id;
+ Vector3 edge_center;
+ Vector3 edge_dir;
+ float edge_len_squared;
+};
+} // namespace gd
+
+#endif // NAV_UTILS_H
diff --git a/modules/recast/navigation_mesh_editor_plugin.cpp b/modules/gdnavigation/navigation_mesh_editor_plugin.cpp
index 6e68dba8ee..13c74d5706 100644
--- a/modules/recast/navigation_mesh_editor_plugin.cpp
+++ b/modules/gdnavigation/navigation_mesh_editor_plugin.cpp
@@ -28,10 +28,12 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifdef TOOLS_ENABLED
#include "navigation_mesh_editor_plugin.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
+#include "navigation_mesh_generator.h"
#include "scene/3d/mesh_instance.h"
#include "scene/gui/box_container.h"
@@ -57,15 +59,14 @@ void NavigationMeshEditor::_bake_pressed() {
button_bake->set_pressed(false);
ERR_FAIL_COND(!node);
- const String conf_warning = node->get_configuration_warning();
- if (!conf_warning.empty()) {
- err_dialog->set_text(conf_warning);
+ if (!node->get_navigation_mesh().is_valid()) {
+ err_dialog->set_text(TTR("A NavigationMesh resource must be set or created for this node to work."));
err_dialog->popup_centered_minsize();
return;
}
- EditorNavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
- EditorNavigationMeshGenerator::get_singleton()->bake(node->get_navigation_mesh(), node);
+ NavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
+ NavigationMeshGenerator::get_singleton()->bake(node->get_navigation_mesh(), node);
node->update_gizmo();
}
@@ -73,7 +74,7 @@ void NavigationMeshEditor::_bake_pressed() {
void NavigationMeshEditor::_clear_pressed() {
if (node)
- EditorNavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
+ NavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
button_bake->set_pressed(false);
bake_info->set_text("");
@@ -160,3 +161,5 @@ NavigationMeshEditorPlugin::NavigationMeshEditorPlugin(EditorNode *p_node) {
NavigationMeshEditorPlugin::~NavigationMeshEditorPlugin() {
}
+
+#endif
diff --git a/modules/recast/navigation_mesh_editor_plugin.h b/modules/gdnavigation/navigation_mesh_editor_plugin.h
index 09c8673b43..f5833f3d7f 100644
--- a/modules/recast/navigation_mesh_editor_plugin.h
+++ b/modules/gdnavigation/navigation_mesh_editor_plugin.h
@@ -28,12 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef NAVIGATION_MESH_GENERATOR_PLUGIN_H
-#define NAVIGATION_MESH_GENERATOR_PLUGIN_H
+#ifndef NAVIGATION_MESH_EDITOR_PLUGIN_H
+#define NAVIGATION_MESH_EDITOR_PLUGIN_H
+
+#ifdef TOOLS_ENABLED
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
-#include "navigation_mesh_generator.h"
+
+class NavigationMeshInstance;
class NavigationMeshEditor : public Control {
friend class NavigationMeshEditorPlugin;
@@ -81,4 +84,6 @@ public:
~NavigationMeshEditorPlugin();
};
-#endif // NAVIGATION_MESH_GENERATOR_PLUGIN_H
+#endif
+
+#endif
diff --git a/modules/recast/navigation_mesh_generator.cpp b/modules/gdnavigation/navigation_mesh_generator.cpp
index b6f5b38038..04b86fabc5 100644
--- a/modules/recast/navigation_mesh_generator.cpp
+++ b/modules/gdnavigation/navigation_mesh_generator.cpp
@@ -28,11 +28,12 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef _3D_DISABLED
+
#include "navigation_mesh_generator.h"
#include "core/math/quick_hull.h"
#include "core/os/thread.h"
-#include "editor/editor_settings.h"
#include "scene/3d/collision_shape.h"
#include "scene/3d/mesh_instance.h"
#include "scene/3d/physics_body.h"
@@ -47,6 +48,11 @@
#include "scene/resources/sphere_shape.h"
#include "modules/modules_enabled.gen.h"
+#ifdef TOOLS_ENABLED
+#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
+#endif
+
#ifdef MODULE_CSG_ENABLED
#include "modules/csg/csg_shape.h"
#endif
@@ -54,15 +60,15 @@
#include "modules/gridmap/grid_map.h"
#endif
-EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::singleton = NULL;
+NavigationMeshGenerator *NavigationMeshGenerator::singleton = NULL;
-void EditorNavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies) {
+void NavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies) {
p_verticies.push_back(p_vec3.x);
p_verticies.push_back(p_vec3.y);
p_verticies.push_back(p_vec3.z);
}
-void EditorNavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) {
+void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) {
int current_vertex_count;
for (int i = 0; i < p_mesh->get_surface_count(); i++) {
@@ -117,7 +123,7 @@ void EditorNavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Tra
}
}
-void EditorNavigationMeshGenerator::_add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) {
+void NavigationMeshGenerator::_add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) {
int face_count = p_faces.size() / 3;
int current_vertex_count = p_verticies.size() / 3;
@@ -132,7 +138,7 @@ void EditorNavigationMeshGenerator::_add_faces(const PoolVector3Array &p_faces,
}
}
-void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
+void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
if (Object::cast_to<MeshInstance>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
@@ -271,7 +277,7 @@ void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_tran
}
}
-void EditorNavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh) {
+void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh) {
PoolVector<Vector3> nav_vertices;
@@ -299,11 +305,24 @@ void EditorNavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_me
}
}
-void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep,
- rcHeightfield *hf, rcCompactHeightfield *chf, rcContourSet *cset, rcPolyMesh *poly_mesh, rcPolyMeshDetail *detail_mesh,
- Vector<float> &vertices, Vector<int> &indices) {
+void NavigationMeshGenerator::_build_recast_navigation_mesh(
+ Ref<NavigationMesh> p_nav_mesh,
+#ifdef TOOLS_ENABLED
+ EditorProgress *ep,
+#endif
+ rcHeightfield *hf,
+ rcCompactHeightfield *chf,
+ rcContourSet *cset,
+ rcPolyMesh *poly_mesh,
+ rcPolyMeshDetail *detail_mesh,
+ Vector<float> &vertices,
+ Vector<int> &indices) {
rcContext ctx;
- ep->step(TTR("Setting up Configuration..."), 1);
+
+#ifdef TOOLS_ENABLED
+ if (ep)
+ ep->step(TTR("Setting up Configuration..."), 1);
+#endif
const float *verts = vertices.ptr();
const int nverts = vertices.size() / 3;
@@ -337,16 +356,25 @@ void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<Navigation
cfg.bmax[1] = bmax[1];
cfg.bmax[2] = bmax[2];
- ep->step(TTR("Calculating grid size..."), 2);
+#ifdef TOOLS_ENABLED
+ if (ep)
+ ep->step(TTR("Calculating grid size..."), 2);
+#endif
rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height);
- ep->step(TTR("Creating heightfield..."), 3);
+#ifdef TOOLS_ENABLED
+ if (ep)
+ ep->step(TTR("Creating heightfield..."), 3);
+#endif
hf = rcAllocHeightfield();
ERR_FAIL_COND(!hf);
ERR_FAIL_COND(!rcCreateHeightfield(&ctx, *hf, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch));
- ep->step(TTR("Marking walkable triangles..."), 4);
+#ifdef TOOLS_ENABLED
+ if (ep)
+ ep->step(TTR("Marking walkable triangles..."), 4);
+#endif
{
Vector<unsigned char> tri_areas;
tri_areas.resize(ntris);
@@ -366,7 +394,10 @@ void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<Navigation
if (p_nav_mesh->get_filter_walkable_low_height_spans())
rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, *hf);
- ep->step(TTR("Constructing compact heightfield..."), 5);
+#ifdef TOOLS_ENABLED
+ if (ep)
+ ep->step(TTR("Constructing compact heightfield..."), 5);
+#endif
chf = rcAllocCompactHeightfield();
@@ -376,10 +407,18 @@ void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<Navigation
rcFreeHeightField(hf);
hf = 0;
- ep->step(TTR("Eroding walkable area..."), 6);
+#ifdef TOOLS_ENABLED
+ if (ep)
+ ep->step(TTR("Eroding walkable area..."), 6);
+#endif
+
ERR_FAIL_COND(!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf));
- ep->step(TTR("Partitioning..."), 7);
+#ifdef TOOLS_ENABLED
+ if (ep)
+ ep->step(TTR("Partitioning..."), 7);
+#endif
+
if (p_nav_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) {
ERR_FAIL_COND(!rcBuildDistanceField(&ctx, *chf));
ERR_FAIL_COND(!rcBuildRegions(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea));
@@ -389,14 +428,20 @@ void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<Navigation
ERR_FAIL_COND(!rcBuildLayerRegions(&ctx, *chf, 0, cfg.minRegionArea));
}
- ep->step(TTR("Creating contours..."), 8);
+#ifdef TOOLS_ENABLED
+ if (ep)
+ ep->step(TTR("Creating contours..."), 8);
+#endif
cset = rcAllocContourSet();
ERR_FAIL_COND(!cset);
ERR_FAIL_COND(!rcBuildContours(&ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cset));
- ep->step(TTR("Creating polymesh..."), 9);
+#ifdef TOOLS_ENABLED
+ if (ep)
+ ep->step(TTR("Creating polymesh..."), 9);
+#endif
poly_mesh = rcAllocPolyMesh();
ERR_FAIL_COND(!poly_mesh);
@@ -411,7 +456,10 @@ void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<Navigation
rcFreeContourSet(cset);
cset = 0;
- ep->step(TTR("Converting to native navigation mesh..."), 10);
+#ifdef TOOLS_ENABLED
+ if (ep)
+ ep->step(TTR("Converting to native navigation mesh..."), 10);
+#endif
_convert_detail_mesh_to_native_navigation_mesh(detail_mesh, p_nav_mesh);
@@ -421,23 +469,30 @@ void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<Navigation
detail_mesh = 0;
}
-EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::get_singleton() {
+NavigationMeshGenerator *NavigationMeshGenerator::get_singleton() {
return singleton;
}
-EditorNavigationMeshGenerator::EditorNavigationMeshGenerator() {
+NavigationMeshGenerator::NavigationMeshGenerator() {
singleton = this;
}
-EditorNavigationMeshGenerator::~EditorNavigationMeshGenerator() {
+NavigationMeshGenerator::~NavigationMeshGenerator() {
}
-void EditorNavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) {
+void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) {
ERR_FAIL_COND(!p_nav_mesh.is_valid());
- EditorProgress ep("bake", TTR("Navigation Mesh Generator Setup:"), 11);
- ep.step(TTR("Parsing Geometry..."), 0);
+#ifdef TOOLS_ENABLED
+ EditorProgress *ep(NULL);
+ if (Engine::get_singleton()->is_editor_hint()) {
+ ep = memnew(EditorProgress("bake", TTR("Navigation Mesh Generator Setup:"), 11));
+ }
+
+ if (ep)
+ ep->step(TTR("Parsing Geometry..."), 0);
+#endif
Vector<float> vertices;
Vector<int> indices;
@@ -466,7 +521,18 @@ void EditorNavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p
rcPolyMesh *poly_mesh = NULL;
rcPolyMeshDetail *detail_mesh = NULL;
- _build_recast_navigation_mesh(p_nav_mesh, &ep, hf, chf, cset, poly_mesh, detail_mesh, vertices, indices);
+ _build_recast_navigation_mesh(
+ p_nav_mesh,
+#ifdef TOOLS_ENABLED
+ ep,
+#endif
+ hf,
+ chf,
+ cset,
+ poly_mesh,
+ detail_mesh,
+ vertices,
+ indices);
rcFreeHeightField(hf);
hf = 0;
@@ -483,17 +549,26 @@ void EditorNavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p
rcFreePolyMeshDetail(detail_mesh);
detail_mesh = 0;
}
- ep.step(TTR("Done!"), 11);
+
+#ifdef TOOLS_ENABLED
+ if (ep)
+ ep->step(TTR("Done!"), 11);
+
+ if (ep)
+ memdelete(ep);
+#endif
}
-void EditorNavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) {
+void NavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) {
if (p_nav_mesh.is_valid()) {
p_nav_mesh->clear_polygons();
p_nav_mesh->set_vertices(PoolVector<Vector3>());
}
}
-void EditorNavigationMeshGenerator::_bind_methods() {
- ClassDB::bind_method(D_METHOD("bake", "nav_mesh", "root_node"), &EditorNavigationMeshGenerator::bake);
- ClassDB::bind_method(D_METHOD("clear", "nav_mesh"), &EditorNavigationMeshGenerator::clear);
+void NavigationMeshGenerator::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("bake", "nav_mesh", "root_node"), &NavigationMeshGenerator::bake);
+ ClassDB::bind_method(D_METHOD("clear", "nav_mesh"), &NavigationMeshGenerator::clear);
}
+
+#endif
diff --git a/modules/recast/navigation_mesh_generator.h b/modules/gdnavigation/navigation_mesh_generator.h
index 8c7ca8b62c..107dee75e2 100644
--- a/modules/recast/navigation_mesh_generator.h
+++ b/modules/gdnavigation/navigation_mesh_generator.h
@@ -31,15 +31,20 @@
#ifndef NAVIGATION_MESH_GENERATOR_H
#define NAVIGATION_MESH_GENERATOR_H
-#include "editor/editor_node.h"
-#include "scene/3d/navigation_mesh.h"
+#ifndef _3D_DISABLED
+
+#include "scene/3d/navigation_mesh_instance.h"
#include <Recast.h>
-class EditorNavigationMeshGenerator : public Object {
- GDCLASS(EditorNavigationMeshGenerator, Object);
+#ifdef TOOLS_ENABLED
+struct EditorProgress;
+#endif
+
+class NavigationMeshGenerator : public Object {
+ GDCLASS(NavigationMeshGenerator, Object);
- static EditorNavigationMeshGenerator *singleton;
+ static NavigationMeshGenerator *singleton;
protected:
static void _bind_methods();
@@ -50,18 +55,29 @@ protected:
static void _parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh);
- static void _build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep,
- rcHeightfield *hf, rcCompactHeightfield *chf, rcContourSet *cset, rcPolyMesh *poly_mesh,
- rcPolyMeshDetail *detail_mesh, Vector<float> &vertices, Vector<int> &indices);
+ static void _build_recast_navigation_mesh(
+ Ref<NavigationMesh> p_nav_mesh,
+#ifdef TOOLS_ENABLED
+ EditorProgress *ep,
+#endif
+ rcHeightfield *hf,
+ rcCompactHeightfield *chf,
+ rcContourSet *cset,
+ rcPolyMesh *poly_mesh,
+ rcPolyMeshDetail *detail_mesh,
+ Vector<float> &vertices,
+ Vector<int> &indices);
public:
- static EditorNavigationMeshGenerator *get_singleton();
+ static NavigationMeshGenerator *get_singleton();
- EditorNavigationMeshGenerator();
- ~EditorNavigationMeshGenerator();
+ NavigationMeshGenerator();
+ ~NavigationMeshGenerator();
void bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node);
void clear(Ref<NavigationMesh> p_nav_mesh);
};
+#endif
+
#endif // NAVIGATION_MESH_GENERATOR_H
diff --git a/modules/recast/register_types.cpp b/modules/gdnavigation/register_types.cpp
index ea0ab00771..d717733787 100644
--- a/modules/recast/register_types.cpp
+++ b/modules/gdnavigation/register_types.cpp
@@ -30,30 +30,51 @@
#include "register_types.h"
-#include "navigation_mesh_editor_plugin.h"
+#include "core/engine.h"
+#include "gd_navigation_server.h"
+#include "servers/navigation_server.h"
-#ifdef TOOLS_ENABLED
-EditorNavigationMeshGenerator *_nav_mesh_generator = NULL;
+#ifndef _3D_DISABLED
+#include "navigation_mesh_generator.h"
#endif
-void register_recast_types() {
#ifdef TOOLS_ENABLED
- ClassDB::APIType prev_api = ClassDB::get_current_api();
- ClassDB::set_current_api(ClassDB::API_EDITOR);
+#include "navigation_mesh_editor_plugin.h"
+#endif
- EditorPlugins::add_by_type<NavigationMeshEditorPlugin>();
- _nav_mesh_generator = memnew(EditorNavigationMeshGenerator);
+/**
+ @author AndreaCatania
+*/
- ClassDB::register_class<EditorNavigationMeshGenerator>();
+#ifndef _3D_DISABLED
+NavigationMeshGenerator *_nav_mesh_generator = NULL;
+#endif
+
+NavigationServer *new_server() {
+ return memnew(GdNavigationServer);
+}
- Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationMeshGenerator", EditorNavigationMeshGenerator::get_singleton()));
+void register_gdnavigation_types() {
+ NavigationServerManager::set_default_server(new_server);
+
+#ifndef _3D_DISABLED
+ _nav_mesh_generator = memnew(NavigationMeshGenerator);
+ ClassDB::register_class<NavigationMeshGenerator>();
+ Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationMeshGenerator", NavigationMeshGenerator::get_singleton()));
+#endif
+
+#ifdef TOOLS_ENABLED
+ EditorPlugins::add_by_type<NavigationMeshEditorPlugin>();
+
+ ClassDB::APIType prev_api = ClassDB::get_current_api();
+ ClassDB::set_current_api(ClassDB::API_EDITOR);
ClassDB::set_current_api(prev_api);
#endif
}
-void unregister_recast_types() {
-#ifdef TOOLS_ENABLED
+void unregister_gdnavigation_types() {
+#ifndef _3D_DISABLED
if (_nav_mesh_generator) {
memdelete(_nav_mesh_generator);
}
diff --git a/modules/recast/register_types.h b/modules/gdnavigation/register_types.h
index d16ba37f5e..bd15eaaada 100644
--- a/modules/recast/register_types.h
+++ b/modules/gdnavigation/register_types.h
@@ -28,5 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-void register_recast_types();
-void unregister_recast_types();
+/**
+ @author AndreaCatania
+*/
+
+void register_gdnavigation_types();
+void unregister_gdnavigation_types();
diff --git a/modules/gdnavigation/rvo_agent.cpp b/modules/gdnavigation/rvo_agent.cpp
new file mode 100644
index 0000000000..37a9b31351
--- /dev/null
+++ b/modules/gdnavigation/rvo_agent.cpp
@@ -0,0 +1,84 @@
+/*************************************************************************/
+/* rvo_agent.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 "rvo_agent.h"
+
+#include "nav_map.h"
+
+/**
+ @author AndreaCatania
+*/
+
+RvoAgent::RvoAgent() :
+ map(NULL) {
+ callback.id = ObjectID(0);
+}
+
+void RvoAgent::set_map(NavMap *p_map) {
+ map = p_map;
+}
+
+bool RvoAgent::is_map_changed() {
+ if (map) {
+ bool is_changed = map->get_map_update_id() != map_update_id;
+ map_update_id = map->get_map_update_id();
+ return is_changed;
+ } else {
+ return false;
+ }
+}
+
+void RvoAgent::set_callback(ObjectID p_id, const StringName p_method, const Variant p_udata) {
+ callback.id = p_id;
+ callback.method = p_method;
+ callback.udata = p_udata;
+}
+
+bool RvoAgent::has_callback() const {
+ return callback.id != 0;
+}
+
+void RvoAgent::dispatch_callback() {
+ if (callback.id == 0) {
+ return;
+ }
+ Object *obj = ObjectDB::get_instance(callback.id);
+ if (obj == NULL) {
+ callback.id = ObjectID(0);
+ }
+
+ Variant::CallError responseCallError;
+
+ callback.new_velocity = Vector3(agent.newVelocity_.x(), agent.newVelocity_.y(), agent.newVelocity_.z());
+
+ const Variant *vp[2] = { &callback.new_velocity, &callback.udata };
+ int argc = (callback.udata.get_type() == Variant::NIL) ? 1 : 2;
+ obj->call(callback.method, vp, argc, responseCallError);
+}
diff --git a/modules/gdnavigation/rvo_agent.h b/modules/gdnavigation/rvo_agent.h
new file mode 100644
index 0000000000..6a84c1b95c
--- /dev/null
+++ b/modules/gdnavigation/rvo_agent.h
@@ -0,0 +1,77 @@
+/*************************************************************************/
+/* rvo_agent.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 RVO_AGENT_H
+#define RVO_AGENT_H
+
+#include "core/object.h"
+#include "nav_rid.h"
+#include <Agent.h>
+
+/**
+ @author AndreaCatania
+*/
+
+class NavMap;
+
+class RvoAgent : public NavRid {
+ struct AvoidanceComputedCallback {
+ ObjectID id;
+ StringName method;
+ Variant udata;
+ Variant new_velocity;
+ };
+
+ NavMap *map;
+ RVO::Agent agent;
+ AvoidanceComputedCallback callback;
+ uint32_t map_update_id;
+
+public:
+ RvoAgent();
+
+ void set_map(NavMap *p_map);
+ NavMap *get_map() {
+ return map;
+ }
+
+ RVO::Agent *get_agent() {
+ return &agent;
+ }
+
+ bool is_map_changed();
+
+ void set_callback(ObjectID p_id, const StringName p_method, const Variant p_udata = Variant());
+ bool has_callback() const;
+
+ void dispatch_callback();
+};
+
+#endif // RVO_AGENT_H
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 3d40220869..1e73a715db 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -36,6 +36,7 @@
#include "scene/resources/mesh_library.h"
#include "scene/resources/surface_tool.h"
#include "scene/scene_string_names.h"
+#include "servers/navigation_server.h"
#include "servers/visual_server.h"
bool GridMap::_set(const StringName &p_name, const Variant &p_value) {
@@ -418,12 +419,10 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
}
//erase navigation
- if (navigation) {
- for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
- navigation->navmesh_remove(E->get().id);
- }
- g.navmesh_ids.clear();
+ for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
+ NavigationServer::get_singleton()->free(E->get().region);
}
+ g.navmesh_ids.clear();
//erase multimeshes
@@ -498,9 +497,11 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
nm.xform = xform * mesh_library->get_item_navmesh_transform(c.item);
if (navigation) {
- nm.id = navigation->navmesh_add(navmesh, xform, this);
- } else {
- nm.id = -1;
+ RID region = NavigationServer::get_singleton()->region_create();
+ NavigationServer::get_singleton()->region_set_navmesh(region, navmesh);
+ NavigationServer::get_singleton()->region_set_transform(region, navigation->get_global_transform() * nm.xform);
+ NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid());
+ nm.region = region;
}
g.navmesh_ids[E->get()] = nm;
}
@@ -591,10 +592,14 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {
if (navigation && mesh_library.is_valid()) {
for (Map<IndexKey, Octant::NavMesh>::Element *F = g.navmesh_ids.front(); F; F = F->next()) {
- if (cell_map.has(F->key()) && F->get().id < 0) {
+ if (cell_map.has(F->key()) && F->get().region.is_valid() == false) {
Ref<NavigationMesh> nm = mesh_library->get_item_navmesh(cell_map[F->key()].item);
if (nm.is_valid()) {
- F->get().id = navigation->navmesh_add(nm, F->get().xform, this);
+ RID region = NavigationServer::get_singleton()->region_create();
+ NavigationServer::get_singleton()->region_set_navmesh(region, nm);
+ NavigationServer::get_singleton()->region_set_transform(region, navigation->get_global_transform() * F->get().xform);
+ NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid());
+ F->get().region = region;
}
}
}
@@ -620,9 +625,9 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) {
if (navigation) {
for (Map<IndexKey, Octant::NavMesh>::Element *F = g.navmesh_ids.front(); F; F = F->next()) {
- if (F->get().id >= 0) {
- navigation->navmesh_remove(F->get().id);
- F->get().id = -1;
+ if (F->get().region.is_valid()) {
+ NavigationServer::get_singleton()->free(F->get().region);
+ F->get().region = RID();
}
}
}
@@ -640,13 +645,11 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) {
PhysicsServer::get_singleton()->free(g.static_body);
- //erase navigation
- if (navigation) {
- for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
- navigation->navmesh_remove(E->get().id);
- }
- g.navmesh_ids.clear();
+ // Erase navigation
+ for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
+ NavigationServer::get_singleton()->free(E->get().region);
}
+ g.navmesh_ids.clear();
//erase multimeshes
diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index 705f4929e1..cc1c8c2923 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -91,7 +91,7 @@ class GridMap : public Spatial {
struct Octant {
struct NavMesh {
- int id;
+ RID region;
Transform xform;
};
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 4aa407f966..28d301ada1 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -840,15 +840,33 @@ void GridMapEditor::_text_changed(const String &p_text) {
void GridMapEditor::_sbox_input(const Ref<InputEvent> &p_ie) {
- Ref<InputEventKey> k = p_ie;
+ const Ref<InputEventKey> k = p_ie;
if (k.is_valid() && (k->get_scancode() == KEY_UP || k->get_scancode() == KEY_DOWN || k->get_scancode() == KEY_PAGEUP || k->get_scancode() == KEY_PAGEDOWN)) {
+ // Forward the key input to the ItemList so it can be scrolled
mesh_library_palette->call("_gui_input", k);
search_box->accept_event();
}
}
+void GridMapEditor::_mesh_library_palette_input(const Ref<InputEvent> &p_ie) {
+
+ const Ref<InputEventMouseButton> mb = p_ie;
+
+ // Zoom in/out using Ctrl + mouse wheel
+ if (mb.is_valid() && mb->is_pressed() && mb->get_command()) {
+
+ if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) {
+ size_slider->set_value(size_slider->get_value() + 0.2);
+ }
+
+ if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) {
+ size_slider->set_value(size_slider->get_value() - 0.2);
+ }
+ }
+}
+
void GridMapEditor::_icon_size_changed(float p_value) {
mesh_library_palette->set_icon_scale(p_value);
update_palette();
@@ -1183,6 +1201,7 @@ void GridMapEditor::_bind_methods() {
ClassDB::bind_method("_text_changed", &GridMapEditor::_text_changed);
ClassDB::bind_method("_sbox_input", &GridMapEditor::_sbox_input);
+ ClassDB::bind_method("_mesh_library_palette_input", &GridMapEditor::_mesh_library_palette_input);
ClassDB::bind_method("_icon_size_changed", &GridMapEditor::_icon_size_changed);
ClassDB::bind_method("_menu_option", &GridMapEditor::_menu_option);
ClassDB::bind_method("_configure", &GridMapEditor::_configure);
@@ -1311,7 +1330,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
size_slider = memnew(HSlider);
size_slider->set_h_size_flags(SIZE_EXPAND_FILL);
- size_slider->set_min(0.1f);
+ size_slider->set_min(0.2f);
size_slider->set_max(4.0f);
size_slider->set_step(0.1f);
size_slider->set_value(1.0f);
@@ -1325,6 +1344,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
mesh_library_palette = memnew(ItemList);
add_child(mesh_library_palette);
mesh_library_palette->set_v_size_flags(SIZE_EXPAND_FILL);
+ mesh_library_palette->connect("gui_input", this, "_mesh_library_palette_input");
info_message = memnew(Label);
info_message->set_text(TTR("Give a MeshLibrary resource to this GridMap to use its meshes."));
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index 404e95b74c..5e6221b4f0 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -214,6 +214,7 @@ class GridMapEditor : public VBoxContainer {
void _text_changed(const String &p_text);
void _sbox_input(const Ref<InputEvent> &p_ie);
+ void _mesh_library_palette_input(const Ref<InputEvent> &p_ie);
void _icon_size_changed(float p_value);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index d38589013e..55408fecb8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -93,11 +93,15 @@ namespace Godot
}
}
- public Vector3 this[int columnIndex]
+ /// <summary>
+ /// Access whole columns in the form of Vector3.
+ /// </summary>
+ /// <param name="column">Which column vector.</param>
+ public Vector3 this[int column]
{
get
{
- switch (columnIndex)
+ switch (column)
{
case 0:
return Column0;
@@ -111,7 +115,7 @@ namespace Godot
}
set
{
- switch (columnIndex)
+ switch (column)
{
case 0:
Column0 = value;
@@ -128,50 +132,22 @@ namespace Godot
}
}
- public real_t this[int columnIndex, int rowIndex]
+ /// <summary>
+ /// Access matrix elements in column-major order.
+ /// </summary>
+ /// <param name="column">Which column, the matrix horizontal position.</param>
+ /// <param name="row">Which row, the matrix vertical position.</param>
+ public real_t this[int column, int row]
{
get
{
- switch (columnIndex)
- {
- case 0:
- return Column0[rowIndex];
- case 1:
- return Column1[rowIndex];
- case 2:
- return Column2[rowIndex];
- default:
- throw new IndexOutOfRangeException();
- }
+ return this[column][row];
}
set
{
- switch (columnIndex)
- {
- case 0:
- {
- var column0 = Column0;
- column0[rowIndex] = value;
- Column0 = column0;
- return;
- }
- case 1:
- {
- var column1 = Column1;
- column1[rowIndex] = value;
- Column1 = column1;
- return;
- }
- case 2:
- {
- var column2 = Column2;
- column2[rowIndex] = value;
- Column2 = column2;
- return;
- }
- default:
- throw new IndexOutOfRangeException();
- }
+ Vector3 columnVector = this[column];
+ columnVector[row] = value;
+ this[column] = columnVector;
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index ddfed180b5..4f7aa99df8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -130,7 +130,7 @@ namespace Godot
public static real_t InverseLerp(real_t from, real_t to, real_t weight)
{
- return (weight - from) / (to - from);
+ return (weight - from) / (to - from);
}
public static bool IsEqualApprox(real_t a, real_t b)
@@ -151,12 +151,12 @@ namespace Godot
public static bool IsInf(real_t s)
{
- return real_t.IsInfinity(s);
+ return real_t.IsInfinity(s);
}
public static bool IsNaN(real_t s)
{
- return real_t.IsNaN(s);
+ return real_t.IsNaN(s);
}
public static bool IsZeroApprox(real_t s)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index e096d37a6f..b85a00d869 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -264,7 +264,8 @@ namespace Godot
instanceIndex++;
toIndex++;
}
- } else
+ }
+ else
{
while (true)
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs
index 0b84050f07..aa8815d1aa 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs
@@ -15,6 +15,76 @@ namespace Godot
public Basis basis;
public Vector3 origin;
+ /// <summary>
+ /// Access whole columns in the form of Vector3. The fourth column is the origin vector.
+ /// </summary>
+ /// <param name="column">Which column vector.</param>
+ public Vector3 this[int column]
+ {
+ get
+ {
+ switch (column)
+ {
+ case 0:
+ return basis.Column0;
+ case 1:
+ return basis.Column1;
+ case 2:
+ return basis.Column2;
+ case 3:
+ return origin;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ set
+ {
+ switch (column)
+ {
+ case 0:
+ basis.Column0 = value;
+ return;
+ case 1:
+ basis.Column1 = value;
+ return;
+ case 2:
+ basis.Column2 = value;
+ return;
+ case 3:
+ origin = value;
+ return;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Access matrix elements in column-major order. The fourth column is the origin vector.
+ /// </summary>
+ /// <param name="column">Which column, the matrix horizontal position.</param>
+ /// <param name="row">Which row, the matrix vertical position.</param>
+ public real_t this[int column, int row]
+ {
+ get
+ {
+ if (column == 3)
+ {
+ return origin[row];
+ }
+ return basis[column, row];
+ }
+ set
+ {
+ if (column == 3)
+ {
+ origin[row] = value;
+ return;
+ }
+ basis[column, row] = value;
+ }
+ }
+
public Transform AffineInverse()
{
Basis basisInv = basis.Inverse();
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index 77ea3e5830..e72a44809a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -54,11 +54,15 @@ namespace Godot
}
}
- public Vector2 this[int rowIndex]
+ /// <summary>
+ /// Access whole columns in the form of Vector2. The third column is the origin vector.
+ /// </summary>
+ /// <param name="column">Which column vector.</param>
+ public Vector2 this[int column]
{
get
{
- switch (rowIndex)
+ switch (column)
{
case 0:
return x;
@@ -72,7 +76,7 @@ namespace Godot
}
set
{
- switch (rowIndex)
+ switch (column)
{
case 0:
x = value;
@@ -89,38 +93,22 @@ namespace Godot
}
}
- public real_t this[int rowIndex, int columnIndex]
+ /// <summary>
+ /// Access matrix elements in column-major order. The third column is the origin vector.
+ /// </summary>
+ /// <param name="column">Which column, the matrix horizontal position.</param>
+ /// <param name="row">Which row, the matrix vertical position.</param>
+ public real_t this[int column, int row]
{
get
{
- switch (rowIndex)
- {
- case 0:
- return x[columnIndex];
- case 1:
- return y[columnIndex];
- case 2:
- return origin[columnIndex];
- default:
- throw new IndexOutOfRangeException();
- }
+ return this[column][row];
}
set
{
- switch (rowIndex)
- {
- case 0:
- x[columnIndex] = value;
- return;
- case 1:
- y[columnIndex] = value;
- return;
- case 2:
- origin[columnIndex] = value;
- return;
- default:
- throw new IndexOutOfRangeException();
- }
+ Vector2 columnVector = this[column];
+ columnVector[row] = value;
+ this[column] = columnVector;
}
}
diff --git a/modules/recast/SCsub b/modules/recast/SCsub
deleted file mode 100644
index 94d9968164..0000000000
--- a/modules/recast/SCsub
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env python
-
-Import('env')
-Import('env_modules')
-
-env_recast = env_modules.Clone()
-
-# Thirdparty source files
-if env['builtin_recast']:
- thirdparty_dir = "#thirdparty/recastnavigation/Recast/"
- thirdparty_sources = [
- "Source/Recast.cpp",
- "Source/RecastAlloc.cpp",
- "Source/RecastArea.cpp",
- "Source/RecastAssert.cpp",
- "Source/RecastContour.cpp",
- "Source/RecastFilter.cpp",
- "Source/RecastLayers.cpp",
- "Source/RecastMesh.cpp",
- "Source/RecastMeshDetail.cpp",
- "Source/RecastRasterization.cpp",
- "Source/RecastRegion.cpp",
- ]
- thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-
- env_recast.Prepend(CPPPATH=[thirdparty_dir + "/Include"])
-
- env_thirdparty = env_recast.Clone()
- env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
-
-# Godot source files
-env_recast.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index 36cafeec73..dc49ed72d0 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -1935,8 +1935,11 @@ PropertyInfo VisualScriptClassConstant::get_input_value_port_info(int p_idx) con
}
PropertyInfo VisualScriptClassConstant::get_output_value_port_info(int p_idx) const {
-
- return PropertyInfo(Variant::INT, String(base_type) + "." + String(name));
+ if (name == "") {
+ return PropertyInfo(Variant::INT, String(base_type));
+ } else {
+ return PropertyInfo(Variant::INT, String(base_type) + "." + String(name));
+ }
}
String VisualScriptClassConstant::get_caption() const {
@@ -1958,6 +1961,22 @@ StringName VisualScriptClassConstant::get_class_constant() {
void VisualScriptClassConstant::set_base_type(const StringName &p_which) {
base_type = p_which;
+ List<String> constants;
+ ClassDB::get_integer_constant_list(base_type, &constants, true);
+ if (constants.size() > 0) {
+ bool found_name = false;
+ for (List<String>::Element *E = constants.front(); E; E = E->next()) {
+ if (E->get() == name) {
+ found_name = true;
+ break;
+ }
+ }
+ if (!found_name) {
+ name = constants[0];
+ }
+ } else {
+ name = "";
+ }
_change_notify();
ports_changed_notify();
}
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index 104f9e751e..3561be8174 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "export.h"
+#include "core/io/image_loader.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
#include "core/io/zip_io.h"
@@ -39,6 +40,7 @@
#include "editor/editor_export.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "main/splash.gen.h"
#include "platform/iphone/logo.gen.h"
#include "string.h"
@@ -55,6 +57,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
typedef Error (*FileHandler)(String p_file, void *p_userdata);
static Error _walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata);
static Error _codesign(String p_file, void *p_userdata);
+ void _blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot);
struct IOSConfigData {
String pkg_name;
@@ -187,21 +190,24 @@ Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_ge
struct LoadingScreenInfo {
const char *preset_key;
const char *export_name;
+ int width;
+ int height;
+ bool rotate;
};
static const LoadingScreenInfo loading_screen_infos[] = {
- { "landscape_launch_screens/iphone_2436x1125", "Default-Landscape-X.png" },
- { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png" },
- { "landscape_launch_screens/ipad_1024x768", "Default-Landscape.png" },
- { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png" },
-
- { "portrait_launch_screens/iphone_640x960", "Default-480h@2x.png" },
- { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png" },
- { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png" },
- { "portrait_launch_screens/iphone_1125x2436", "Default-Portrait-X.png" },
- { "portrait_launch_screens/ipad_768x1024", "Default-Portrait.png" },
- { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png" },
- { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png" }
+ { "landscape_launch_screens/iphone_2436x1125", "Default-Landscape-X.png", 2436, 1125, false },
+ { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png", 2208, 1242, false },
+ { "landscape_launch_screens/ipad_1024x768", "Default-Landscape.png", 1024, 768, false },
+ { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png", 2048, 1536, false },
+
+ { "portrait_launch_screens/iphone_640x960", "Default-480h@2x.png", 640, 960, true },
+ { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png", 640, 1136, true },
+ { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png", 750, 1334, true },
+ { "portrait_launch_screens/iphone_1125x2436", "Default-Portrait-X.png", 1125, 2436, true },
+ { "portrait_launch_screens/ipad_768x1024", "Default-Portrait.png", 768, 1024, true },
+ { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png", 1536, 2048, true },
+ { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png", 1242, 2208, true }
};
void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) {
@@ -247,6 +253,8 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape_right"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait_upside_down"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "icons/generate_missing"), false));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with retina display
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png"), "")); // App Store
@@ -257,6 +265,8 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with retina display
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "launch_screens/generate_missing"), false));
+
for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), ""));
}
@@ -434,6 +444,42 @@ String EditorExportPlatformIOS::_get_cpp_code() {
return result;
}
+void EditorExportPlatformIOS::_blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot) {
+
+ ERR_FAIL_COND(p_dst.is_null());
+ ERR_FAIL_COND(p_src.is_null());
+
+ p_dst->lock();
+ p_src->lock();
+
+ int sw = p_rot ? p_src->get_height() : p_src->get_width();
+ int sh = p_rot ? p_src->get_width() : p_src->get_height();
+
+ int x_pos = (p_dst->get_width() - sw) / 2;
+ int y_pos = (p_dst->get_height() - sh) / 2;
+
+ int xs = (x_pos >= 0) ? 0 : -x_pos;
+ int ys = (y_pos >= 0) ? 0 : -y_pos;
+
+ if (sw + x_pos > p_dst->get_width()) sw = p_dst->get_width() - x_pos;
+ if (sh + y_pos > p_dst->get_height()) sh = p_dst->get_height() - y_pos;
+
+ for (int y = ys; y < sh; y++) {
+ for (int x = xs; x < sw; x++) {
+ Color sc = p_rot ? p_src->get_pixel(p_src->get_width() - y - 1, x) : p_src->get_pixel(x, y);
+ Color dc = p_dst->get_pixel(x_pos + x, y_pos + y);
+ dc.r = (double)(sc.a * sc.r + dc.a * (1.0 - sc.a) * dc.r);
+ dc.g = (double)(sc.a * sc.g + dc.a * (1.0 - sc.a) * dc.g);
+ dc.b = (double)(sc.a * sc.b + dc.a * (1.0 - sc.a) * dc.b);
+ dc.a = (double)(sc.a + dc.a * (1.0 - sc.a));
+ p_dst->set_pixel(x_pos + x, y_pos + y, dc);
+ }
+ }
+
+ p_dst->unlock();
+ p_src->unlock();
+}
+
struct IconInfo {
const char *preset_key;
const char *idiom;
@@ -448,8 +494,8 @@ static const IconInfo icon_infos[] = {
{ "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60", true },
{ "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", true },
- { "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", false },
- { "required_icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", false },
+ { "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", true },
+ { "required_icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true },
{ "optional_icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false },
@@ -473,20 +519,56 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) {
IconInfo info = icon_infos[i];
+ int side_size = String(info.actual_size_side).to_int();
String icon_path = p_preset->get(info.preset_key);
if (icon_path.length() == 0) {
- if (info.is_required) {
- ERR_PRINT("Required icon is not specified in the preset");
+ if ((bool)p_preset->get("icons/generate_missing")) {
+ // Resize main app icon
+ icon_path = ProjectSettings::get_singleton()->get("application/config/icon");
+ Ref<Image> img = memnew(Image);
+ Error err = ImageLoader::load_image(icon_path, img);
+ if (err != OK) {
+ ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'.");
+ return ERR_UNCONFIGURED;
+ }
+ img->resize(side_size, side_size);
+ err = img->save_png(p_iconset_dir + info.export_name);
+ if (err) {
+ String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'.");
+ ERR_PRINT(err_str.utf8().get_data());
+ return err;
+ }
+ } else {
+ if (info.is_required) {
+ String err_str = String("Required icon (") + info.preset_key + ") is not specified in the preset.";
+ ERR_PRINT(err_str);
+ return ERR_UNCONFIGURED;
+ } else {
+ String err_str = String("Icon (") + info.preset_key + ") is not specified in the preset.";
+ WARN_PRINT(err_str);
+ }
+ continue;
+ }
+ } else {
+ // Load custom icon
+ Ref<Image> img = memnew(Image);
+ Error err = ImageLoader::load_image(icon_path, img);
+ if (err != OK) {
+ ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'.");
return ERR_UNCONFIGURED;
}
- continue;
- }
- Error err = da->copy(icon_path, p_iconset_dir + info.export_name);
- if (err) {
- memdelete(da);
- String err_str = String("Failed to export icon: ") + icon_path;
- ERR_PRINT(err_str.utf8().get_data());
- return err;
+ if (img->get_width() != side_size || img->get_height() != side_size) {
+ ERR_PRINT("Invalid icon size (" + String(info.preset_key) + "): '" + icon_path + "'.");
+ return ERR_UNCONFIGURED;
+ }
+
+ err = da->copy(icon_path, p_iconset_dir + info.export_name);
+ if (err) {
+ memdelete(da);
+ String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'.");
+ ERR_PRINT(err_str.utf8().get_data());
+ return err;
+ }
}
sizes += String(info.actual_size_side) + "\n";
if (i > 0) {
@@ -525,13 +607,72 @@ Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPre
LoadingScreenInfo info = loading_screen_infos[i];
String loading_screen_file = p_preset->get(info.preset_key);
if (loading_screen_file.size() > 0) {
- Error err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
+ // Load custom loading screens
+ Ref<Image> img = memnew(Image);
+ Error err = ImageLoader::load_image(loading_screen_file, img);
+ if (err != OK) {
+ ERR_PRINT("Invalid loading screen (" + String(info.preset_key) + "): '" + loading_screen_file + "'.");
+ return ERR_UNCONFIGURED;
+ }
+ if (img->get_width() != info.width || img->get_height() != info.height) {
+ ERR_PRINT("Invalid loading screen size (" + String(info.preset_key) + "): '" + loading_screen_file + "'.");
+ return ERR_UNCONFIGURED;
+ }
+ err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
if (err) {
memdelete(da);
String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path '" + loading_screen_file + "'.";
ERR_PRINT(err_str.utf8().get_data());
return err;
}
+ } else if ((bool)p_preset->get("launch_screens/generate_missing")) {
+ // Generate loading screen from the splash screen
+ Color boot_bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color");
+ String boot_logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
+ bool boot_logo_scale = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize");
+
+ Ref<Image> img = memnew(Image);
+ img->create(info.width, info.height, false, Image::FORMAT_RGBA8);
+ img->fill(boot_bg_color);
+
+ Ref<Image> img_bs;
+
+ if (boot_logo_path.length() > 0) {
+ img_bs = Ref<Image>(memnew(Image));
+ ImageLoader::load_image(boot_logo_path, img_bs);
+ }
+ if (!img_bs.is_valid()) {
+ img_bs = Ref<Image>(memnew(Image(boot_splash_png)));
+ }
+ if (img_bs.is_valid()) {
+ float aspect_ratio = (float)img_bs->get_width() / (float)img_bs->get_height();
+ if (info.rotate) {
+ if (boot_logo_scale) {
+ if (info.width * aspect_ratio <= info.height) {
+ img_bs->resize(info.width * aspect_ratio, info.width);
+ } else {
+ img_bs->resize(info.height, info.height / aspect_ratio);
+ }
+ }
+ } else {
+ if (boot_logo_scale) {
+ if (info.height * aspect_ratio <= info.width) {
+ img_bs->resize(info.height * aspect_ratio, info.height);
+ } else {
+ img_bs->resize(info.width, info.width / aspect_ratio);
+ }
+ }
+ }
+ _blend_and_rotate(img, img_bs, info.rotate);
+ }
+ Error err = img->save_png(p_dest_dir + info.export_name);
+ if (err) {
+ String err_str = String("Failed to export loading screen (") + info.preset_key + ") from splash screen.";
+ WARN_PRINT(err_str.utf8().get_data());
+ }
+ } else {
+ String err_str = String("No loading screen (") + info.preset_key + ") specified.";
+ WARN_PRINT(err_str.utf8().get_data());
}
}
memdelete(da);
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index 34dce90b6b..13615de65b 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -220,6 +220,20 @@ void OS_JavaScript::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_scre
p_list->push_back(OS::VideoMode(screen.width, screen.height, true));
}
+bool OS_JavaScript::get_window_per_pixel_transparency_enabled() const {
+ if (!is_layered_allowed()) {
+ return false;
+ }
+ return transparency_enabled;
+}
+
+void OS_JavaScript::set_window_per_pixel_transparency_enabled(bool p_enabled) {
+ if (!is_layered_allowed()) {
+ return;
+ }
+ transparency_enabled = p_enabled;
+}
+
// Keys
template <typename T>
@@ -886,10 +900,14 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver,
EmscriptenWebGLContextAttributes attributes;
emscripten_webgl_init_context_attributes(&attributes);
- attributes.alpha = false;
+ attributes.alpha = GLOBAL_GET("display/window/per_pixel_transparency/allowed");
attributes.antialias = false;
ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER);
+ if (p_desired.layered) {
+ set_window_per_pixel_transparency_enabled(true);
+ }
+
bool gles3 = true;
if (p_video_driver == VIDEO_DRIVER_GLES2) {
gles3 = false;
@@ -1315,6 +1333,7 @@ OS_JavaScript::OS_JavaScript(int p_argc, char *p_argv[]) {
window_maximized = false;
entering_fullscreen = false;
just_exited_fullscreen = false;
+ transparency_enabled = false;
main_loop = NULL;
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index 5c02a292ee..65a18830ed 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -46,6 +46,7 @@ class OS_JavaScript : public OS_Unix {
bool window_maximized;
bool entering_fullscreen;
bool just_exited_fullscreen;
+ bool transparency_enabled;
InputDefault *input;
Ref<InputEventKey> deferred_key_event;
@@ -123,6 +124,9 @@ public:
virtual void set_mouse_mode(MouseMode p_mode);
virtual MouseMode get_mouse_mode() const;
+ virtual bool get_window_per_pixel_transparency_enabled() const;
+ virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
+
virtual bool has_touchscreen_ui_hint() const;
virtual bool is_joy_known(int p_device);
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index bc77ac3cfd..314f369dc6 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -1724,42 +1724,39 @@ public:
case ERR_WARNING:
if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
os_log_info(OS_LOG_DEFAULT,
- "WARNING: %{public}s: %{public}s\nAt: %{public}s:%i.",
- p_function, err_details, p_file, p_line);
+ "WARNING: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
}
- logf_error("\E[1;33mWARNING: %s: \E[0m\E[1m%s\n", p_function,
- err_details);
- logf_error("\E[0;33m At: %s:%i.\E[0m\n", p_file, p_line);
+ logf_error("\E[1;33mWARNING:\E[0;93m %s\n", err_details);
+ logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
break;
case ERR_SCRIPT:
if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
os_log_error(OS_LOG_DEFAULT,
- "SCRIPT ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.",
- p_function, err_details, p_file, p_line);
+ "SCRIPT ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
}
- logf_error("\E[1;35mSCRIPT ERROR: %s: \E[0m\E[1m%s\n", p_function,
- err_details);
- logf_error("\E[0;35m At: %s:%i.\E[0m\n", p_file, p_line);
+ logf_error("\E[1;35mSCRIPT ERROR:\E[0;95m %s\n", err_details);
+ logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
break;
case ERR_SHADER:
if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
os_log_error(OS_LOG_DEFAULT,
- "SHADER ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.",
- p_function, err_details, p_file, p_line);
+ "SHADER ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
}
- logf_error("\E[1;36mSHADER ERROR: %s: \E[0m\E[1m%s\n", p_function,
- err_details);
- logf_error("\E[0;36m At: %s:%i.\E[0m\n", p_file, p_line);
+ logf_error("\E[1;36mSHADER ERROR:\E[0;96m %s\n", err_details);
+ logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
break;
case ERR_ERROR:
default:
if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
os_log_error(OS_LOG_DEFAULT,
- "ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.",
- p_function, err_details, p_file, p_line);
+ "ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
}
- logf_error("\E[1;31mERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
- logf_error("\E[0;31m At: %s:%i.\E[0m\n", p_file, p_line);
+ logf_error("\E[1;31mERROR:\E[0;91m %s\n", err_details);
+ logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
break;
}
}
diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp
index 8eb6adc27b..520b654b94 100644
--- a/platform/windows/windows_terminal_logger.cpp
+++ b/platform/windows/windows_terminal_logger.cpp
@@ -85,7 +85,6 @@ void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file
CONSOLE_SCREEN_BUFFER_INFO sbi; //original
GetConsoleScreenBufferInfo(hCon, &sbi);
- WORD current_fg = sbi.wAttributes & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
WORD current_bg = sbi.wAttributes & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
uint32_t basecol = 0;
@@ -98,53 +97,34 @@ void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file
basecol |= current_bg;
- if (p_rationale && p_rationale[0]) {
-
- SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY);
- switch (p_type) {
- case ERR_ERROR: logf("ERROR: "); break;
- case ERR_WARNING: logf("WARNING: "); break;
- case ERR_SCRIPT: logf("SCRIPT ERROR: "); break;
- case ERR_SHADER: logf("SHADER ERROR: "); break;
- }
-
- SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY);
- logf("%s\n", p_rationale);
+ SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY);
+ switch (p_type) {
+ case ERR_ERROR: logf("ERROR:"); break;
+ case ERR_WARNING: logf("WARNING:"); break;
+ case ERR_SCRIPT: logf("SCRIPT ERROR:"); break;
+ case ERR_SHADER: logf("SHADER ERROR:"); break;
+ }
- SetConsoleTextAttribute(hCon, basecol);
- switch (p_type) {
- case ERR_ERROR: logf(" At: "); break;
- case ERR_WARNING: logf(" At: "); break;
- case ERR_SCRIPT: logf(" At: "); break;
- case ERR_SHADER: logf(" At: "); break;
- }
+ SetConsoleTextAttribute(hCon, basecol);
+ if (p_rationale && p_rationale[0]) {
+ logf(" %s\n", p_rationale);
+ } else {
+ logf(" %s\n", p_code);
+ }
- SetConsoleTextAttribute(hCon, current_fg | current_bg);
- logf("%s:%i\n", p_file, p_line);
+ // `FOREGROUND_INTENSITY` alone results in gray text.
+ SetConsoleTextAttribute(hCon, FOREGROUND_INTENSITY);
+ switch (p_type) {
+ case ERR_ERROR: logf(" at: "); break;
+ case ERR_WARNING: logf(" at: "); break;
+ case ERR_SCRIPT: logf(" at: "); break;
+ case ERR_SHADER: logf(" at: "); break;
+ }
+ if (p_rationale && p_rationale[0]) {
+ logf("(%s:%i)\n", p_file, p_line);
} else {
-
- SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY);
- switch (p_type) {
- case ERR_ERROR: logf("ERROR: %s: ", p_function); break;
- case ERR_WARNING: logf("WARNING: %s: ", p_function); break;
- case ERR_SCRIPT: logf("SCRIPT ERROR: %s: ", p_function); break;
- case ERR_SHADER: logf("SCRIPT ERROR: %s: ", p_function); break;
- }
-
- SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY);
- logf("%s\n", p_code);
-
- SetConsoleTextAttribute(hCon, basecol);
- switch (p_type) {
- case ERR_ERROR: logf(" At: "); break;
- case ERR_WARNING: logf(" At: "); break;
- case ERR_SCRIPT: logf(" At: "); break;
- case ERR_SHADER: logf(" At: "); break;
- }
-
- SetConsoleTextAttribute(hCon, current_fg | current_bg);
- logf("%s:%i\n", p_file, p_line);
+ logf("%s (%s:%i)\n", p_function, p_file, p_line);
}
SetConsoleTextAttribute(hCon, sbi.wAttributes);
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index 55a612eb37..1cd763853c 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -526,19 +526,73 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
"watch",
"left_ptr_watch",
"fleur",
- "hand1",
- "X_cursor",
- "sb_v_double_arrow",
- "sb_h_double_arrow",
+ "dnd-move",
+ "crossed_circle",
+ "v_double_arrow",
+ "h_double_arrow",
"size_bdiag",
"size_fdiag",
- "hand1",
- "sb_v_double_arrow",
- "sb_h_double_arrow",
+ "move",
+ "row_resize",
+ "col_resize",
"question_arrow"
};
img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size);
+ if (!img[i]) {
+ const char *fallback = NULL;
+
+ switch (i) {
+ case CURSOR_POINTING_HAND:
+ fallback = "pointer";
+ break;
+ case CURSOR_CROSS:
+ fallback = "crosshair";
+ break;
+ case CURSOR_WAIT:
+ fallback = "wait";
+ break;
+ case CURSOR_BUSY:
+ fallback = "progress";
+ break;
+ case CURSOR_DRAG:
+ fallback = "grabbing";
+ break;
+ case CURSOR_CAN_DROP:
+ fallback = "hand1";
+ break;
+ case CURSOR_FORBIDDEN:
+ fallback = "forbidden";
+ break;
+ case CURSOR_VSIZE:
+ fallback = "ns-resize";
+ break;
+ case CURSOR_HSIZE:
+ fallback = "ew-resize";
+ break;
+ case CURSOR_BDIAGSIZE:
+ fallback = "fd_double_arrow";
+ break;
+ case CURSOR_FDIAGSIZE:
+ fallback = "bd_double_arrow";
+ break;
+ case CURSOR_MOVE:
+ img[i] = img[CURSOR_DRAG];
+ break;
+ case CURSOR_VSPLIT:
+ fallback = "sb_v_double_arrow";
+ break;
+ case CURSOR_HSPLIT:
+ fallback = "sb_h_double_arrow";
+ break;
+ case CURSOR_HELP:
+ fallback = "help";
+ break;
+ }
+ if (fallback != NULL) {
+ img[i] = XcursorLibraryLoadImage(fallback, cursor_theme, cursor_size);
+ }
+ }
if (img[i]) {
cursors[i] = XcursorImageLoadCursor(x11_display, img[i]);
} else {
@@ -669,14 +723,8 @@ bool OS_X11::refresh_device_info() {
int range_max_x = 0;
int range_max_y = 0;
int pressure_resolution = 0;
- int pressure_min = 0;
- int pressure_max = 0;
int tilt_resolution_x = 0;
int tilt_resolution_y = 0;
- int tilt_range_min_x = 0;
- int tilt_range_min_y = 0;
- int tilt_range_max_x = 0;
- int tilt_range_max_y = 0;
for (int j = 0; j < dev->num_classes; j++) {
#ifdef TOUCH_ENABLED
if (dev->classes[j]->type == XITouchClass && ((XITouchClassInfo *)dev->classes[j])->mode == XIDirectTouch) {
@@ -697,17 +745,14 @@ bool OS_X11::refresh_device_info() {
range_max_y = class_info->max;
absolute_mode = true;
} else if (class_info->number == VALUATOR_PRESSURE && class_info->mode == XIModeAbsolute) {
- pressure_resolution = class_info->resolution;
- pressure_min = class_info->min;
- pressure_max = class_info->max;
+ pressure_resolution = (class_info->max - class_info->min);
+ if (pressure_resolution == 0) pressure_resolution = 1;
} else if (class_info->number == VALUATOR_TILTX && class_info->mode == XIModeAbsolute) {
- tilt_resolution_x = class_info->resolution;
- tilt_range_min_x = class_info->min;
- tilt_range_max_x = class_info->max;
+ tilt_resolution_x = (class_info->max - class_info->min);
+ if (tilt_resolution_x == 0) tilt_resolution_x = 1;
} else if (class_info->number == VALUATOR_TILTY && class_info->mode == XIModeAbsolute) {
- tilt_resolution_y = class_info->resolution;
- tilt_range_min_y = class_info->min;
- tilt_range_max_y = class_info->max;
+ tilt_resolution_y = (class_info->max - class_info->min);
+ if (tilt_resolution_y == 0) tilt_resolution_y = 1;
}
}
}
@@ -728,15 +773,6 @@ bool OS_X11::refresh_device_info() {
print_verbose("XInput: Absolute pointing device: " + String(dev->name));
}
- if (pressure_resolution <= 0) {
- pressure_resolution = (pressure_max - pressure_min);
- }
- if (tilt_resolution_x <= 0) {
- tilt_resolution_x = (tilt_range_max_x - tilt_range_min_x);
- }
- if (tilt_resolution_y <= 0) {
- tilt_resolution_y = (tilt_range_max_y - tilt_range_min_y);
- }
xi.pressure = 0;
xi.pen_devices[dev->deviceid] = Vector3(pressure_resolution, tilt_resolution_x, tilt_resolution_y);
}
diff --git a/scene/2d/navigation_2d.cpp b/scene/2d/navigation_2d.cpp
index 9209cc5074..de01d97ad9 100644
--- a/scene/2d/navigation_2d.cpp
+++ b/scene/2d/navigation_2d.cpp
@@ -29,710 +29,52 @@
/*************************************************************************/
#include "navigation_2d.h"
+#include "servers/navigation_2d_server.h"
-#define USE_ENTRY_POINT
-
-void Navigation2D::_navpoly_link(int p_id) {
-
- ERR_FAIL_COND(!navpoly_map.has(p_id));
- NavMesh &nm = navpoly_map[p_id];
- ERR_FAIL_COND(nm.linked);
-
- PoolVector<Vector2> vertices = nm.navpoly->get_vertices();
- int len = vertices.size();
- if (len == 0)
- return;
-
- PoolVector<Vector2>::Read r = vertices.read();
-
- for (int i = 0; i < nm.navpoly->get_polygon_count(); i++) {
-
- //build
-
- List<Polygon>::Element *P = nm.polygons.push_back(Polygon());
- Polygon &p = P->get();
- p.owner = &nm;
-
- Vector<int> poly = nm.navpoly->get_polygon(i);
- int plen = poly.size();
- const int *indices = poly.ptr();
- bool valid = true;
- p.edges.resize(plen);
-
- Vector2 center;
- float sum = 0;
-
- for (int j = 0; j < plen; j++) {
-
- int idx = indices[j];
- if (idx < 0 || idx >= len) {
- valid = false;
- break;
- }
-
- Polygon::Edge e;
- Vector2 ep = nm.xform.xform(r[idx]);
- center += ep;
- e.point = _get_point(ep);
- p.edges.write[j] = e;
-
- int idxn = indices[(j + 1) % plen];
- if (idxn < 0 || idxn >= len) {
- valid = false;
- break;
- }
-
- Vector2 epn = nm.xform.xform(r[idxn]);
-
- sum += (epn.x - ep.x) * (epn.y + ep.y);
- }
-
- p.clockwise = sum > 0;
-
- if (!valid) {
- nm.polygons.pop_back();
- ERR_CONTINUE(!valid);
- }
-
- p.center = center / plen;
-
- //connect
-
- for (int j = 0; j < plen; j++) {
-
- int next = (j + 1) % plen;
- EdgeKey ek(p.edges[j].point, p.edges[next].point);
-
- Map<EdgeKey, Connection>::Element *C = connections.find(ek);
- if (!C) {
+void Navigation2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_rid"), &Navigation2D::get_rid);
- Connection c;
- c.A = &p;
- c.A_edge = j;
- c.B = NULL;
- c.B_edge = -1;
- connections[ek] = c;
- } else {
+ ClassDB::bind_method(D_METHOD("get_simple_path", "start", "end", "optimize"), &Navigation2D::get_simple_path, DEFVAL(true));
- if (C->get().B != NULL) {
- ConnectionPending pending;
- pending.polygon = &p;
- pending.edge = j;
- p.edges.write[j].P = C->get().pending.push_back(pending);
- continue;
- }
+ ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &Navigation2D::set_cell_size);
+ ClassDB::bind_method(D_METHOD("get_cell_size"), &Navigation2D::get_cell_size);
- C->get().B = &p;
- C->get().B_edge = j;
- C->get().A->edges.write[C->get().A_edge].C = &p;
- C->get().A->edges.write[C->get().A_edge].C_edge = j;
- p.edges.write[j].C = C->get().A;
- p.edges.write[j].C_edge = C->get().A_edge;
- //connection successful.
- }
- }
- }
+ ClassDB::bind_method(D_METHOD("set_edge_connection_margin", "margin"), &Navigation2D::set_edge_connection_margin);
+ ClassDB::bind_method(D_METHOD("get_edge_connection_margin"), &Navigation2D::get_edge_connection_margin);
- nm.linked = true;
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_size"), "set_cell_size", "get_cell_size");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge_connection_margin"), "set_edge_connection_margin", "get_edge_connection_margin");
}
-void Navigation2D::_navpoly_unlink(int p_id) {
-
- ERR_FAIL_COND(!navpoly_map.has(p_id));
- NavMesh &nm = navpoly_map[p_id];
- ERR_FAIL_COND(!nm.linked);
-
- for (List<Polygon>::Element *E = nm.polygons.front(); E; E = E->next()) {
-
- Polygon &p = E->get();
-
- int ec = p.edges.size();
- Polygon::Edge *edges = p.edges.ptrw();
-
- for (int i = 0; i < ec; i++) {
- int next = (i + 1) % ec;
-
- EdgeKey ek(edges[i].point, edges[next].point);
- Map<EdgeKey, Connection>::Element *C = connections.find(ek);
- ERR_CONTINUE(!C);
-
- if (edges[i].P) {
- C->get().pending.erase(edges[i].P);
- edges[i].P = NULL;
-
- } else if (C->get().B) {
- //disconnect
+void Navigation2D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_READY: {
+ Navigation2DServer::get_singleton()->map_set_active(map, true);
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
- C->get().B->edges.write[C->get().B_edge].C = NULL;
- C->get().B->edges.write[C->get().B_edge].C_edge = -1;
- C->get().A->edges.write[C->get().A_edge].C = NULL;
- C->get().A->edges.write[C->get().A_edge].C_edge = -1;
-
- if (C->get().A == &E->get()) {
-
- C->get().A = C->get().B;
- C->get().A_edge = C->get().B_edge;
- }
- C->get().B = NULL;
- C->get().B_edge = -1;
-
- if (C->get().pending.size()) {
- //reconnect if something is pending
- ConnectionPending cp = C->get().pending.front()->get();
- C->get().pending.pop_front();
-
- C->get().B = cp.polygon;
- C->get().B_edge = cp.edge;
- C->get().A->edges.write[C->get().A_edge].C = cp.polygon;
- C->get().A->edges.write[C->get().A_edge].C_edge = cp.edge;
- cp.polygon->edges.write[cp.edge].C = C->get().A;
- cp.polygon->edges.write[cp.edge].C_edge = C->get().A_edge;
- cp.polygon->edges.write[cp.edge].P = NULL;
- }
-
- } else {
- connections.erase(C);
- //erase
- }
- }
+ Navigation2DServer::get_singleton()->map_set_active(map, false);
+ } break;
}
-
- nm.polygons.clear();
-
- nm.linked = false;
-}
-
-int Navigation2D::navpoly_add(const Ref<NavigationPolygon> &p_mesh, const Transform2D &p_xform, Object *p_owner) {
-
- int id = last_id++;
- NavMesh nm;
- nm.linked = false;
- nm.navpoly = p_mesh;
- nm.xform = p_xform;
- nm.owner = p_owner;
- navpoly_map[id] = nm;
-
- _navpoly_link(id);
-
- return id;
}
-void Navigation2D::navpoly_set_transform(int p_id, const Transform2D &p_xform) {
-
- ERR_FAIL_COND(!navpoly_map.has(p_id));
- NavMesh &nm = navpoly_map[p_id];
- if (nm.xform == p_xform)
- return; //bleh
- _navpoly_unlink(p_id);
- nm.xform = p_xform;
- _navpoly_link(p_id);
+void Navigation2D::set_cell_size(float p_cell_size) {
+ cell_size = p_cell_size;
+ Navigation2DServer::get_singleton()->map_set_cell_size(map, cell_size);
}
-void Navigation2D::navpoly_remove(int p_id) {
- ERR_FAIL_COND(!navpoly_map.has(p_id));
- _navpoly_unlink(p_id);
- navpoly_map.erase(p_id);
+void Navigation2D::set_edge_connection_margin(float p_edge_connection_margin) {
+ edge_connection_margin = p_edge_connection_margin;
+ Navigation2DServer::get_singleton()->map_set_edge_connection_margin(map, edge_connection_margin);
}
Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize) {
-
- Polygon *begin_poly = NULL;
- Polygon *end_poly = NULL;
- Vector2 begin_point;
- Vector2 end_point;
- float begin_d = 1e20;
- float end_d = 1e20;
-
- //look for point inside triangle
-
- for (Map<int, NavMesh>::Element *E = navpoly_map.front(); E; E = E->next()) {
-
- if (!E->get().linked)
- continue;
- for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
-
- Polygon &p = F->get();
- if (begin_d || end_d) {
- for (int i = 2; i < p.edges.size(); i++) {
-
- if (begin_d > 0) {
-
- if (Geometry::is_point_in_triangle(p_start, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) {
-
- begin_poly = &p;
- begin_point = p_start;
- begin_d = 0;
- if (end_d == 0)
- break;
- }
- }
-
- if (end_d > 0) {
-
- if (Geometry::is_point_in_triangle(p_end, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) {
-
- end_poly = &p;
- end_point = p_end;
- end_d = 0;
- if (begin_d == 0)
- break;
- }
- }
- }
- }
-
- p.prev_edge = -1;
- }
- }
-
- //start or end not inside triangle.. look for closest segment :|
- if (begin_d || end_d) {
- for (Map<int, NavMesh>::Element *E = navpoly_map.front(); E; E = E->next()) {
-
- if (!E->get().linked)
- continue;
- for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
-
- Polygon &p = F->get();
- int es = p.edges.size();
- for (int i = 0; i < es; i++) {
-
- Vector2 edge[2] = {
- _get_vertex(p.edges[i].point),
- _get_vertex(p.edges[(i + 1) % es].point)
- };
-
- if (begin_d > 0) {
- Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_start, edge);
- float d = spoint.distance_to(p_start);
- if (d < begin_d) {
- begin_poly = &p;
- begin_point = spoint;
- begin_d = d;
- }
- }
-
- if (end_d > 0) {
- Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_end, edge);
- float d = spoint.distance_to(p_end);
- if (d < end_d) {
- end_poly = &p;
- end_point = spoint;
- end_d = d;
- }
- }
- }
- }
- }
- }
-
- if (!begin_poly || !end_poly) {
-
- return Vector<Vector2>(); //no path
- }
-
- if (begin_poly == end_poly) {
-
- Vector<Vector2> path;
- path.resize(2);
- path.write[0] = begin_point;
- path.write[1] = end_point;
- return path;
- }
-
- bool found_route = false;
-
- List<Polygon *> open_list;
-
- begin_poly->entry = p_start;
-
- for (int i = 0; i < begin_poly->edges.size(); i++) {
-
- if (begin_poly->edges[i].C) {
-
- begin_poly->edges[i].C->prev_edge = begin_poly->edges[i].C_edge;
-#ifdef USE_ENTRY_POINT
- Vector2 edge[2] = {
- _get_vertex(begin_poly->edges[i].point),
- _get_vertex(begin_poly->edges[(i + 1) % begin_poly->edges.size()].point)
- };
-
- Vector2 entry = Geometry::get_closest_point_to_segment_2d(begin_poly->entry, edge);
- begin_poly->edges[i].C->distance = begin_poly->entry.distance_to(entry);
- begin_poly->edges[i].C->entry = entry;
-#else
- begin_poly->edges[i].C->distance = begin_poly->center.distance_to(begin_poly->edges[i].C->center);
-#endif
- open_list.push_back(begin_poly->edges[i].C);
-
- if (begin_poly->edges[i].C == end_poly) {
- found_route = true;
- }
- }
- }
-
- while (!found_route) {
-
- if (open_list.size() == 0) {
- break;
- }
- //check open list
-
- List<Polygon *>::Element *least_cost_poly = NULL;
- float least_cost = 1e30;
-
- //this could be faster (cache previous results)
- for (List<Polygon *>::Element *E = open_list.front(); E; E = E->next()) {
-
- Polygon *p = E->get();
-
- float cost = p->distance;
-
-#ifdef USE_ENTRY_POINT
- int es = p->edges.size();
-
- float shortest_distance = 1e30;
-
- for (int i = 0; i < es; i++) {
- Polygon::Edge &e = p->edges.write[i];
-
- if (!e.C)
- continue;
-
- Vector2 edge[2] = {
- _get_vertex(p->edges[i].point),
- _get_vertex(p->edges[(i + 1) % es].point)
- };
-
- Vector2 edge_point = Geometry::get_closest_point_to_segment_2d(p->entry, edge);
- float dist = p->entry.distance_to(edge_point);
- if (dist < shortest_distance)
- shortest_distance = dist;
- }
-
- cost += shortest_distance;
-#else
- cost += p->center.distance_to(end_point);
-#endif
- if (cost < least_cost) {
- least_cost_poly = E;
- least_cost = cost;
- }
- }
-
- Polygon *p = least_cost_poly->get();
- //open the neighbours for search
- int es = p->edges.size();
-
- for (int i = 0; i < es; i++) {
-
- Polygon::Edge &e = p->edges.write[i];
-
- if (!e.C)
- continue;
-
-#ifdef USE_ENTRY_POINT
- Vector2 edge[2] = {
- _get_vertex(p->edges[i].point),
- _get_vertex(p->edges[(i + 1) % es].point)
- };
-
- Vector2 edge_entry = Geometry::get_closest_point_to_segment_2d(p->entry, edge);
- float distance = p->entry.distance_to(edge_entry) + p->distance;
-
-#else
-
- float distance = p->center.distance_to(e.C->center) + p->distance;
-
-#endif
-
- if (e.C->prev_edge != -1) {
- //oh this was visited already, can we win the cost?
-
- if (e.C->distance > distance) {
-
- e.C->prev_edge = e.C_edge;
- e.C->distance = distance;
-#ifdef USE_ENTRY_POINT
- e.C->entry = edge_entry;
-#endif
- }
- } else {
- //add to open neighbours
-
- e.C->prev_edge = e.C_edge;
- e.C->distance = distance;
-#ifdef USE_ENTRY_POINT
- e.C->entry = edge_entry;
-#endif
-
- open_list.push_back(e.C);
-
- if (e.C == end_poly) {
- //oh my reached end! stop algorithm
- found_route = true;
- break;
- }
- }
- }
-
- if (found_route)
- break;
-
- open_list.erase(least_cost_poly);
- }
-
- if (found_route) {
-
- Vector<Vector2> path;
-
- if (p_optimize) {
- //string pulling
-
- Vector2 apex_point = end_point;
- Vector2 portal_left = apex_point;
- Vector2 portal_right = apex_point;
- Polygon *left_poly = end_poly;
- Polygon *right_poly = end_poly;
- Polygon *p = end_poly;
-
- while (p) {
-
- Vector2 left;
- Vector2 right;
-
-//#define CLOCK_TANGENT(m_a,m_b,m_c) ( ((m_a)-(m_c)).cross((m_a)-(m_b)) )
-#define CLOCK_TANGENT(m_a, m_b, m_c) ((((m_a).x - (m_c).x) * ((m_b).y - (m_c).y) - ((m_b).x - (m_c).x) * ((m_a).y - (m_c).y)))
-
- if (p == begin_poly) {
- left = begin_point;
- right = begin_point;
- } else {
- int prev = p->prev_edge;
- int prev_n = (p->prev_edge + 1) % p->edges.size();
- left = _get_vertex(p->edges[prev].point);
- right = _get_vertex(p->edges[prev_n].point);
-
- if (p->clockwise) {
- SWAP(left, right);
- }
- /*if (CLOCK_TANGENT(apex_point,left,(left+right)*0.5) < 0){
- SWAP(left,right);
- }*/
- }
-
- bool skip = false;
-
- /*
- print_line("-----\nAPEX: "+(apex_point-end_point));
- print_line("LEFT:");
- print_line("\tPortal: "+(portal_left-end_point));
- print_line("\tPoint: "+(left-end_point));
- print_line("\tLeft Tangent: "+rtos(CLOCK_TANGENT(apex_point,portal_left,left)));
- print_line("\tLeft Distance: "+rtos(portal_left.distance_squared_to(apex_point)));
- print_line("\tLeft Test: "+rtos(CLOCK_TANGENT(apex_point,left,portal_right)));
- print_line("RIGHT:");
- print_line("\tPortal: "+(portal_right-end_point));
- print_line("\tPoint: "+(right-end_point));
- print_line("\tRight Tangent: "+rtos(CLOCK_TANGENT(apex_point,portal_right,right)));
- print_line("\tRight Distance: "+rtos(portal_right.distance_squared_to(apex_point)));
- print_line("\tRight Test: "+rtos(CLOCK_TANGENT(apex_point,right,portal_left)));
- */
-
- if (CLOCK_TANGENT(apex_point, portal_left, left) >= 0) {
- //process
- if (portal_left.is_equal_approx(apex_point) || CLOCK_TANGENT(apex_point, left, portal_right) > 0) {
- left_poly = p;
- portal_left = left;
- } else {
-
- apex_point = portal_right;
- p = right_poly;
- left_poly = p;
- portal_left = apex_point;
- portal_right = apex_point;
- if (!path.size() || !path[path.size() - 1].is_equal_approx(apex_point))
- path.push_back(apex_point);
- skip = true;
- }
- }
-
- if (!skip && CLOCK_TANGENT(apex_point, portal_right, right) <= 0) {
- //process
- if (portal_right.is_equal_approx(apex_point) || CLOCK_TANGENT(apex_point, right, portal_left) < 0) {
- right_poly = p;
- portal_right = right;
- } else {
-
- apex_point = portal_left;
- p = left_poly;
- right_poly = p;
- portal_right = apex_point;
- portal_left = apex_point;
- if (!path.size() || !path[path.size() - 1].is_equal_approx(apex_point))
- path.push_back(apex_point);
- }
- }
-
- if (p != begin_poly)
- p = p->edges[p->prev_edge].C;
- else
- p = NULL;
- }
-
- } else {
- //midpoints
- Polygon *p = end_poly;
-
- while (true) {
- int prev = p->prev_edge;
- int prev_n = (p->prev_edge + 1) % p->edges.size();
- Vector2 point = (_get_vertex(p->edges[prev].point) + _get_vertex(p->edges[prev_n].point)) * 0.5;
- path.push_back(point);
- p = p->edges[prev].C;
- if (p == begin_poly)
- break;
- }
- }
-
- if (!path.size() || !path[path.size() - 1].is_equal_approx(begin_point)) {
- path.push_back(begin_point); // Add the begin point
- } else {
- path.write[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point
- }
-
- path.invert();
-
- if (path.size() <= 1 || !path[path.size() - 1].is_equal_approx(end_point)) {
- path.push_back(end_point); // Add the end point
- } else {
- path.write[path.size() - 1] = end_point; // Replace last midpoint by the exact end point
- }
-
- return path;
- }
-
- return Vector<Vector2>();
-}
-
-Vector2 Navigation2D::get_closest_point(const Vector2 &p_point) {
-
- Vector2 closest_point = Vector2();
- float closest_point_d = 1e20;
-
- for (Map<int, NavMesh>::Element *E = navpoly_map.front(); E; E = E->next()) {
-
- if (!E->get().linked)
- continue;
- for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
-
- Polygon &p = F->get();
- for (int i = 2; i < p.edges.size(); i++) {
-
- if (Geometry::is_point_in_triangle(p_point, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) {
-
- return p_point; //inside triangle, nothing else to discuss
- }
- }
- }
- }
-
- for (Map<int, NavMesh>::Element *E = navpoly_map.front(); E; E = E->next()) {
-
- if (!E->get().linked)
- continue;
- for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
-
- Polygon &p = F->get();
- int es = p.edges.size();
- for (int i = 0; i < es; i++) {
-
- Vector2 edge[2] = {
- _get_vertex(p.edges[i].point),
- _get_vertex(p.edges[(i + 1) % es].point)
- };
-
- Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_point, edge);
- float d = spoint.distance_squared_to(p_point);
- if (d < closest_point_d) {
-
- closest_point = spoint;
- closest_point_d = d;
- }
- }
- }
- }
-
- return closest_point;
-}
-
-Object *Navigation2D::get_closest_point_owner(const Vector2 &p_point) {
-
- Object *owner = NULL;
- Vector2 closest_point = Vector2();
- float closest_point_d = 1e20;
-
- for (Map<int, NavMesh>::Element *E = navpoly_map.front(); E; E = E->next()) {
-
- if (!E->get().linked)
- continue;
- for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
-
- Polygon &p = F->get();
- for (int i = 2; i < p.edges.size(); i++) {
-
- if (Geometry::is_point_in_triangle(p_point, _get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point))) {
-
- return E->get().owner;
- }
- }
- }
- }
-
- for (Map<int, NavMesh>::Element *E = navpoly_map.front(); E; E = E->next()) {
-
- if (!E->get().linked)
- continue;
- for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
-
- Polygon &p = F->get();
- int es = p.edges.size();
- for (int i = 0; i < es; i++) {
-
- Vector2 edge[2] = {
- _get_vertex(p.edges[i].point),
- _get_vertex(p.edges[(i + 1) % es].point)
- };
-
- Vector2 spoint = Geometry::get_closest_point_to_segment_2d(p_point, edge);
- float d = spoint.distance_squared_to(p_point);
- if (d < closest_point_d) {
-
- closest_point = spoint;
- closest_point_d = d;
- owner = E->get().owner;
- }
- }
- }
- }
-
- return owner;
-}
-
-void Navigation2D::_bind_methods() {
-
- ClassDB::bind_method(D_METHOD("navpoly_add", "mesh", "xform", "owner"), &Navigation2D::navpoly_add, DEFVAL(Variant()));
- ClassDB::bind_method(D_METHOD("navpoly_set_transform", "id", "xform"), &Navigation2D::navpoly_set_transform);
- ClassDB::bind_method(D_METHOD("navpoly_remove", "id"), &Navigation2D::navpoly_remove);
-
- ClassDB::bind_method(D_METHOD("get_simple_path", "start", "end", "optimize"), &Navigation2D::get_simple_path, DEFVAL(true));
- ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Navigation2D::get_closest_point);
- ClassDB::bind_method(D_METHOD("get_closest_point_owner", "to_point"), &Navigation2D::get_closest_point_owner);
+ return Navigation2DServer::get_singleton()->map_get_path(map, p_start, p_end, p_optimize);
}
Navigation2D::Navigation2D() {
- ERR_FAIL_COND(sizeof(Point) != 8);
- cell_size = 1; // one pixel
- last_id = 1;
+ map = Navigation2DServer::get_singleton()->map_create();
+ set_cell_size(10); // Ten pixels
+ set_edge_connection_margin(100);
}
diff --git a/scene/2d/navigation_2d.h b/scene/2d/navigation_2d.h
index 41b9aea35a..08642a5489 100644
--- a/scene/2d/navigation_2d.h
+++ b/scene/2d/navigation_2d.h
@@ -38,135 +38,30 @@ class Navigation2D : public Node2D {
GDCLASS(Navigation2D, Node2D);
- union Point {
+ RID map;
+ real_t cell_size;
+ real_t edge_connection_margin;
- struct {
- int64_t x : 32;
- int64_t y : 32;
- };
-
- uint64_t key;
- bool operator<(const Point &p_key) const { return key < p_key.key; }
- };
-
- struct EdgeKey {
-
- Point a;
- Point b;
-
- bool operator<(const EdgeKey &p_key) const {
- return (a.key == p_key.a.key) ? (b.key < p_key.b.key) : (a.key < p_key.a.key);
- };
-
- EdgeKey(const Point &p_a = Point(), const Point &p_b = Point()) :
- a(p_a),
- b(p_b) {
- if (a.key > b.key) {
- SWAP(a, b);
- }
- }
- };
-
- struct NavMesh;
- struct Polygon;
-
- struct ConnectionPending {
-
- Polygon *polygon;
- int edge;
- };
-
- struct Polygon {
-
- struct Edge {
- Point point;
- Polygon *C; //connection
- int C_edge;
- List<ConnectionPending>::Element *P;
- Edge() {
- C = NULL;
- C_edge = -1;
- P = NULL;
- }
- };
-
- Vector<Edge> edges;
-
- Vector2 center;
- Vector2 entry;
-
- float distance;
- int prev_edge;
-
- bool clockwise;
-
- NavMesh *owner;
- };
-
- struct Connection {
-
- Polygon *A;
- int A_edge;
- Polygon *B;
- int B_edge;
-
- List<ConnectionPending> pending;
-
- Connection() {
- A = NULL;
- B = NULL;
- A_edge = -1;
- B_edge = -1;
- }
- };
-
- Map<EdgeKey, Connection> connections;
-
- struct NavMesh {
-
- Object *owner;
- Transform2D xform;
- bool linked;
- Ref<NavigationPolygon> navpoly;
- List<Polygon> polygons;
- };
-
- _FORCE_INLINE_ Point _get_point(const Vector2 &p_pos) const {
-
- int x = int(Math::floor(p_pos.x / cell_size));
- int y = int(Math::floor(p_pos.y / cell_size));
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
- Point p;
- p.key = 0;
- p.x = x;
- p.y = y;
- return p;
+public:
+ RID get_rid() const {
+ return map;
}
- _FORCE_INLINE_ Vector2 _get_vertex(const Point &p_point) const {
-
- return Vector2(p_point.x, p_point.y) * cell_size;
+ void set_cell_size(float p_cell_size);
+ float get_cell_size() const {
+ return cell_size;
}
- void _navpoly_link(int p_id);
- void _navpoly_unlink(int p_id);
-
- float cell_size;
- Map<int, NavMesh> navpoly_map;
- int last_id;
-
-protected:
- static void _bind_methods();
-
-public:
- //API should be as dynamic as possible
- int navpoly_add(const Ref<NavigationPolygon> &p_mesh, const Transform2D &p_xform, Object *p_owner = NULL);
- void navpoly_set_transform(int p_id, const Transform2D &p_xform);
- void navpoly_remove(int p_id);
+ void set_edge_connection_margin(float p_edge_connection_margin);
+ float get_edge_connection_margin() const {
+ return edge_connection_margin;
+ }
Vector<Vector2> get_simple_path(const Vector2 &p_start, const Vector2 &p_end, bool p_optimize = true);
- Vector2 get_closest_point(const Vector2 &p_point);
- Object *get_closest_point_owner(const Vector2 &p_point);
Navigation2D();
};
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
new file mode 100644
index 0000000000..fe4d3b322f
--- /dev/null
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -0,0 +1,341 @@
+/*************************************************************************/
+/* navigation_agent_2d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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_agent_2d.h"
+
+#include "core/engine.h"
+#include "scene/2d/navigation_2d.h"
+#include "servers/navigation_2d_server.h"
+
+void NavigationAgent2D::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_target_desired_distance", "desired_distance"), &NavigationAgent2D::set_target_desired_distance);
+ ClassDB::bind_method(D_METHOD("get_target_desired_distance"), &NavigationAgent2D::get_target_desired_distance);
+
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationAgent2D::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &NavigationAgent2D::get_radius);
+
+ ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationAgent2D::set_navigation_node);
+ ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationAgent2D::get_navigation_node);
+
+ ClassDB::bind_method(D_METHOD("set_neighbor_dist", "neighbor_dist"), &NavigationAgent2D::set_neighbor_dist);
+ ClassDB::bind_method(D_METHOD("get_neighbor_dist"), &NavigationAgent2D::get_neighbor_dist);
+
+ ClassDB::bind_method(D_METHOD("set_max_neighbors", "max_neighbors"), &NavigationAgent2D::set_max_neighbors);
+ ClassDB::bind_method(D_METHOD("get_max_neighbors"), &NavigationAgent2D::get_max_neighbors);
+
+ ClassDB::bind_method(D_METHOD("set_time_horizon", "time_horizon"), &NavigationAgent2D::set_time_horizon);
+ ClassDB::bind_method(D_METHOD("get_time_horizon"), &NavigationAgent2D::get_time_horizon);
+
+ ClassDB::bind_method(D_METHOD("set_max_speed", "max_speed"), &NavigationAgent2D::set_max_speed);
+ ClassDB::bind_method(D_METHOD("get_max_speed"), &NavigationAgent2D::get_max_speed);
+
+ ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent2D::set_path_max_distance);
+ ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent2D::get_path_max_distance);
+
+ ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent2D::set_target_location);
+ ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent2D::get_target_location);
+ ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent2D::get_next_location);
+ ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent2D::distance_to_target);
+ ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent2D::set_velocity);
+ ClassDB::bind_method(D_METHOD("get_nav_path"), &NavigationAgent2D::get_nav_path);
+ ClassDB::bind_method(D_METHOD("get_nav_path_index"), &NavigationAgent2D::get_nav_path_index);
+ ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent2D::is_target_reached);
+ ClassDB::bind_method(D_METHOD("is_target_reachable"), &NavigationAgent2D::is_target_reachable);
+ ClassDB::bind_method(D_METHOD("is_navigation_finished"), &NavigationAgent2D::is_navigation_finished);
+ ClassDB::bind_method(D_METHOD("get_final_location"), &NavigationAgent2D::get_final_location);
+
+ ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent2D::_avoidance_done);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_target_desired_distance", "get_target_desired_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,500,0.01"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "neighbor_dist", PROPERTY_HINT_RANGE, "0.1,100000,0.01"), "set_neighbor_dist", "get_neighbor_dist");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1"), "set_max_neighbors", "get_max_neighbors");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_time_horizon", "get_time_horizon");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_speed", PROPERTY_HINT_RANGE, "0.1,100000,0.01"), "set_max_speed", "get_max_speed");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_max_distance", PROPERTY_HINT_RANGE, "10,100,1"), "set_path_max_distance", "get_path_max_distance");
+
+ ADD_SIGNAL(MethodInfo("path_changed"));
+ ADD_SIGNAL(MethodInfo("target_reached"));
+ ADD_SIGNAL(MethodInfo("navigation_finished"));
+ ADD_SIGNAL(MethodInfo("velocity_computed", PropertyInfo(Variant::VECTOR3, "safe_velocity")));
+}
+
+void NavigationAgent2D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_READY: {
+
+ agent_parent = Object::cast_to<Node2D>(get_parent());
+
+ Navigation2DServer::get_singleton()->agent_set_callback(agent, this, "_avoidance_done");
+
+ // Search the navigation node and set it
+ {
+ Navigation2D *nav = NULL;
+ Node *p = get_parent();
+ while (p != NULL) {
+ nav = Object::cast_to<Navigation2D>(p);
+ if (nav != NULL)
+ p = NULL;
+ else
+ p = p->get_parent();
+ }
+
+ set_navigation(nav);
+ }
+
+ set_physics_process_internal(true);
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ agent_parent = NULL;
+ set_navigation(NULL);
+ set_physics_process_internal(false);
+ } break;
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ if (agent_parent) {
+
+ Navigation2DServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().get_origin());
+ if (!target_reached) {
+ if (distance_to_target() < target_desired_distance) {
+ emit_signal("target_reached");
+ target_reached = true;
+ }
+ }
+ }
+ } break;
+ }
+}
+
+NavigationAgent2D::NavigationAgent2D() :
+ agent_parent(NULL),
+ navigation(NULL),
+ agent(RID()),
+ target_desired_distance(1.0),
+ path_max_distance(3.0),
+ velocity_submitted(false),
+ target_reached(false),
+ navigation_finished(true) {
+ agent = Navigation2DServer::get_singleton()->agent_create();
+ set_neighbor_dist(500.0);
+ set_max_neighbors(10);
+ set_time_horizon(20.0);
+ set_radius(10.0);
+ set_max_speed(200.0);
+}
+
+NavigationAgent2D::~NavigationAgent2D() {
+ Navigation2DServer::get_singleton()->free(agent);
+ agent = RID(); // Pointless
+}
+
+void NavigationAgent2D::set_navigation(Navigation2D *p_nav) {
+ if (navigation == p_nav)
+ return; // Pointless
+
+ navigation = p_nav;
+ Navigation2DServer::get_singleton()->agent_set_map(agent, navigation == NULL ? RID() : navigation->get_rid());
+}
+
+void NavigationAgent2D::set_navigation_node(Node *p_nav) {
+ Navigation2D *nav = Object::cast_to<Navigation2D>(p_nav);
+ ERR_FAIL_COND(nav == NULL);
+ set_navigation(nav);
+}
+
+Node *NavigationAgent2D::get_navigation_node() const {
+ return Object::cast_to<Node>(navigation);
+}
+
+void NavigationAgent2D::set_target_desired_distance(real_t p_dd) {
+ target_desired_distance = p_dd;
+}
+
+void NavigationAgent2D::set_radius(real_t p_radius) {
+ radius = p_radius;
+ Navigation2DServer::get_singleton()->agent_set_radius(agent, radius);
+}
+
+void NavigationAgent2D::set_neighbor_dist(real_t p_dist) {
+ neighbor_dist = p_dist;
+ Navigation2DServer::get_singleton()->agent_set_neighbor_dist(agent, neighbor_dist);
+}
+
+void NavigationAgent2D::set_max_neighbors(int p_count) {
+ max_neighbors = p_count;
+ Navigation2DServer::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
+}
+
+void NavigationAgent2D::set_time_horizon(real_t p_time) {
+ time_horizon = p_time;
+ Navigation2DServer::get_singleton()->agent_set_time_horizon(agent, time_horizon);
+}
+
+void NavigationAgent2D::set_max_speed(real_t p_max_speed) {
+ max_speed = p_max_speed;
+ Navigation2DServer::get_singleton()->agent_set_max_speed(agent, max_speed);
+}
+
+void NavigationAgent2D::set_path_max_distance(real_t p_pmd) {
+ path_max_distance = p_pmd;
+}
+
+real_t NavigationAgent2D::get_path_max_distance() {
+ return path_max_distance;
+}
+
+void NavigationAgent2D::set_target_location(Vector2 p_location) {
+ target_location = p_location;
+ navigation_path.clear();
+ target_reached = false;
+ navigation_finished = false;
+}
+
+Vector2 NavigationAgent2D::get_target_location() const {
+ return target_location;
+}
+
+Vector2 NavigationAgent2D::get_next_location() {
+ update_navigation();
+ if (navigation_path.size() == 0) {
+ ERR_FAIL_COND_V(agent_parent == NULL, Vector2());
+ return agent_parent->get_global_transform().get_origin();
+ } else {
+ return navigation_path[nav_path_index];
+ }
+}
+
+real_t NavigationAgent2D::distance_to_target() const {
+ ERR_FAIL_COND_V(agent_parent == NULL, 0.0);
+ return agent_parent->get_global_transform().get_origin().distance_to(target_location);
+}
+
+bool NavigationAgent2D::is_target_reached() const {
+ return target_reached;
+}
+
+bool NavigationAgent2D::is_target_reachable() {
+ return target_desired_distance >= get_final_location().distance_to(target_location);
+}
+
+bool NavigationAgent2D::is_navigation_finished() {
+ update_navigation();
+ return navigation_finished;
+}
+
+Vector2 NavigationAgent2D::get_final_location() {
+ update_navigation();
+ if (navigation_path.size() == 0) {
+ return Vector2();
+ }
+ return navigation_path[navigation_path.size() - 1];
+}
+
+void NavigationAgent2D::set_velocity(Vector2 p_velocity) {
+ target_velocity = p_velocity;
+ Navigation2DServer::get_singleton()->agent_set_target_velocity(agent, target_velocity);
+ Navigation2DServer::get_singleton()->agent_set_velocity(agent, prev_safe_velocity);
+ velocity_submitted = true;
+}
+
+void NavigationAgent2D::_avoidance_done(Vector3 p_new_velocity) {
+ const Vector2 velocity = Vector2(p_new_velocity.x, p_new_velocity.z);
+ prev_safe_velocity = velocity;
+
+ if (!velocity_submitted) {
+ target_velocity = Vector2();
+ return;
+ }
+ velocity_submitted = false;
+
+ emit_signal("velocity_computed", velocity);
+}
+
+String NavigationAgent2D::get_configuration_warning() const {
+ if (!Object::cast_to<Node2D>(get_parent())) {
+ return TTR("The NavigationAgent2D can be used only under a Node2D node");
+ }
+
+ return String();
+}
+
+void NavigationAgent2D::update_navigation() {
+
+ if (agent_parent == NULL) return;
+ if (navigation == NULL) return;
+ if (update_frame_id == Engine::get_singleton()->get_physics_frames()) return;
+
+ update_frame_id = Engine::get_singleton()->get_physics_frames();
+
+ Vector2 o = agent_parent->get_global_transform().get_origin();
+
+ bool reload_path = false;
+
+ if (Navigation2DServer::get_singleton()->agent_is_map_changed(agent)) {
+ reload_path = true;
+ } else if (navigation_path.size() == 0) {
+ reload_path = true;
+ } else {
+ // Check if too far from the navigation path
+ if (nav_path_index > 0) {
+ Vector2 segment[2];
+ segment[0] = navigation_path[nav_path_index - 1];
+ segment[1] = navigation_path[nav_path_index];
+ Vector2 p = Geometry::get_closest_point_to_segment_2d(o, segment);
+ if (o.distance_to(p) >= path_max_distance) {
+ // To faraway, reload path
+ reload_path = true;
+ }
+ }
+ }
+
+ if (reload_path) {
+ navigation_path = Navigation2DServer::get_singleton()->map_get_path(navigation->get_rid(), o, target_location, true);
+ navigation_finished = false;
+ nav_path_index = 0;
+ emit_signal("path_changed");
+ }
+
+ if (navigation_path.size() == 0)
+ return;
+
+ // Check if we can advance the navigation path
+ if (navigation_finished == false) {
+ // Advances to the next far away location.
+ while (o.distance_to(navigation_path[nav_path_index]) < target_desired_distance) {
+ nav_path_index += 1;
+ if (nav_path_index == navigation_path.size()) {
+ nav_path_index -= 1;
+ navigation_finished = true;
+ emit_signal("navigation_finished");
+ break;
+ }
+ }
+ }
+}
diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h
new file mode 100644
index 0000000000..60527091ce
--- /dev/null
+++ b/scene/2d/navigation_agent_2d.h
@@ -0,0 +1,150 @@
+/*************************************************************************/
+/* navigation_agent_2d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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_AGENT_2D_H
+#define NAVIGATION_AGENT_2D_H
+
+#include "core/vector.h"
+#include "scene/main/node.h"
+
+class Node2D;
+class Navigation2D;
+
+class NavigationAgent2D : public Node {
+ GDCLASS(NavigationAgent2D, Node);
+
+ Node2D *agent_parent;
+ Navigation2D *navigation;
+
+ RID agent;
+
+ real_t target_desired_distance;
+ real_t radius;
+ real_t neighbor_dist;
+ int max_neighbors;
+ real_t time_horizon;
+ real_t max_speed;
+
+ real_t path_max_distance;
+
+ Vector2 target_location;
+ Vector<Vector2> navigation_path;
+ int nav_path_index;
+ bool velocity_submitted;
+ Vector2 prev_safe_velocity;
+ /// The submitted target velocity
+ Vector2 target_velocity;
+ bool target_reached;
+ bool navigation_finished;
+ // No initialized on purpose
+ uint32_t update_frame_id;
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ NavigationAgent2D();
+ virtual ~NavigationAgent2D();
+
+ void set_navigation(Navigation2D *p_nav);
+ const Navigation2D *get_navigation() const {
+ return navigation;
+ }
+
+ void set_navigation_node(Node *p_nav);
+ Node *get_navigation_node() const;
+
+ RID get_rid() const {
+ return agent;
+ }
+
+ void set_target_desired_distance(real_t p_dd);
+ real_t get_target_desired_distance() const {
+ return target_desired_distance;
+ }
+
+ void set_radius(real_t p_radius);
+ real_t get_radius() const {
+ return radius;
+ }
+
+ void set_neighbor_dist(real_t p_dist);
+ real_t get_neighbor_dist() const {
+ return neighbor_dist;
+ }
+
+ void set_max_neighbors(int p_count);
+ int get_max_neighbors() const {
+ return max_neighbors;
+ }
+
+ void set_time_horizon(real_t p_time);
+ real_t get_time_horizon() const {
+ return time_horizon;
+ }
+
+ void set_max_speed(real_t p_max_speed);
+ real_t get_max_speed() const {
+ return max_speed;
+ }
+
+ void set_path_max_distance(real_t p_pmd);
+ real_t get_path_max_distance();
+
+ void set_target_location(Vector2 p_location);
+ Vector2 get_target_location() const;
+
+ Vector2 get_next_location();
+
+ Vector<Vector2> get_nav_path() const {
+ return navigation_path;
+ }
+
+ int get_nav_path_index() const {
+ return nav_path_index;
+ }
+
+ real_t distance_to_target() const;
+ bool is_target_reached() const;
+ bool is_target_reachable();
+ bool is_navigation_finished();
+ Vector2 get_final_location();
+
+ void set_velocity(Vector2 p_velocity);
+ void _avoidance_done(Vector3 p_new_velocity);
+
+ virtual String get_configuration_warning() const;
+
+private:
+ void update_navigation();
+};
+
+#endif
diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp
new file mode 100644
index 0000000000..0740b616b6
--- /dev/null
+++ b/scene/2d/navigation_obstacle_2d.cpp
@@ -0,0 +1,154 @@
+/*************************************************************************/
+/* navigation_obstacle.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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_obstacle_2d.h"
+
+#include "scene/2d/collision_shape_2d.h"
+#include "scene/2d/navigation_2d.h"
+#include "scene/2d/physics_body_2d.h"
+#include "servers/navigation_2d_server.h"
+
+void NavigationObstacle2D::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationObstacle2D::set_navigation_node);
+ ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationObstacle2D::get_navigation_node);
+}
+
+void NavigationObstacle2D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_READY: {
+
+ update_agent_shape();
+
+ // Search the navigation node and set it
+ {
+ Navigation2D *nav = NULL;
+ Node *p = get_parent();
+ while (p != NULL) {
+ nav = Object::cast_to<Navigation2D>(p);
+ if (nav != NULL)
+ p = NULL;
+ else
+ p = p->get_parent();
+ }
+
+ set_navigation(nav);
+ }
+
+ set_physics_process_internal(true);
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ set_navigation(NULL);
+ set_physics_process_internal(false);
+ } break;
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ Node2D *node = Object::cast_to<Node2D>(get_parent());
+ if (node) {
+ Navigation2DServer::get_singleton()->agent_set_position(agent, node->get_global_transform().get_origin());
+ }
+
+ } break;
+ }
+}
+
+NavigationObstacle2D::NavigationObstacle2D() :
+ navigation(NULL),
+ agent(RID()) {
+ agent = Navigation2DServer::get_singleton()->agent_create();
+}
+
+NavigationObstacle2D::~NavigationObstacle2D() {
+ Navigation2DServer::get_singleton()->free(agent);
+ agent = RID(); // Pointless
+}
+
+void NavigationObstacle2D::set_navigation(Navigation2D *p_nav) {
+ if (navigation == p_nav)
+ return; // Pointless
+
+ navigation = p_nav;
+ Navigation2DServer::get_singleton()->agent_set_map(agent, navigation == NULL ? RID() : navigation->get_rid());
+}
+
+void NavigationObstacle2D::set_navigation_node(Node *p_nav) {
+ Navigation2D *nav = Object::cast_to<Navigation2D>(p_nav);
+ ERR_FAIL_COND(nav == NULL);
+ set_navigation(nav);
+}
+
+Node *NavigationObstacle2D::get_navigation_node() const {
+ return Object::cast_to<Node>(navigation);
+}
+
+String NavigationObstacle2D::get_configuration_warning() const {
+ if (!Object::cast_to<Node2D>(get_parent())) {
+ return TTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object.");
+ }
+
+ return String();
+}
+
+void NavigationObstacle2D::update_agent_shape() {
+ Node *node = get_parent();
+
+ // Estimate the radius of this physics body
+ real_t radius = 0.0;
+ for (int i(0); i < node->get_child_count(); i++) {
+ // For each collision shape
+ CollisionShape2D *cs = Object::cast_to<CollisionShape2D>(node->get_child(i));
+ if (cs) {
+ // Take the distance between the Body center to the shape center
+ real_t r = cs->get_transform().get_origin().length();
+ if (cs->get_shape().is_valid()) {
+ // and add the enclosing shape radius
+ r += cs->get_shape()->get_enclosing_radius();
+ }
+ Size2 s = cs->get_global_transform().get_scale();
+ r *= MAX(s.x, s.y);
+ // Takes the biggest radius
+ radius = MAX(radius, r);
+ }
+ }
+ Node2D *node_2d = Object::cast_to<Node2D>(node);
+ if (node_2d) {
+ Vector2 s = node_2d->get_global_transform().get_scale();
+ radius *= MAX(s.x, s.y);
+ }
+
+ if (radius == 0.0)
+ radius = 1.0; // Never a 0 radius
+
+ // Initialize the Agent as an object
+ Navigation2DServer::get_singleton()->agent_set_neighbor_dist(agent, 0.0);
+ Navigation2DServer::get_singleton()->agent_set_max_neighbors(agent, 0);
+ Navigation2DServer::get_singleton()->agent_set_time_horizon(agent, 0.0);
+ Navigation2DServer::get_singleton()->agent_set_radius(agent, radius);
+ Navigation2DServer::get_singleton()->agent_set_max_speed(agent, 0.0);
+}
diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h
new file mode 100644
index 0000000000..f098255799
--- /dev/null
+++ b/scene/2d/navigation_obstacle_2d.h
@@ -0,0 +1,71 @@
+/*************************************************************************/
+/* navigation_obstacle.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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_OBSTACLE_2D_H
+#define NAVIGATION_OBSTACLE_2D_H
+
+#include "scene/main/node.h"
+
+class Navigation2D;
+
+class NavigationObstacle2D : public Node {
+ GDCLASS(NavigationObstacle2D, Node);
+
+ Navigation2D *navigation;
+
+ RID agent;
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ NavigationObstacle2D();
+ virtual ~NavigationObstacle2D();
+
+ void set_navigation(Navigation2D *p_nav);
+ const Navigation2D *get_navigation() const {
+ return navigation;
+ }
+
+ void set_navigation_node(Node *p_nav);
+ Node *get_navigation_node() const;
+
+ RID get_rid() const {
+ return agent;
+ }
+
+ virtual String get_configuration_warning() const;
+
+private:
+ void update_agent_shape();
+};
+
+#endif
diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp
index 883fb37668..c40cdb4720 100644
--- a/scene/2d/navigation_polygon.cpp
+++ b/scene/2d/navigation_polygon.cpp
@@ -32,7 +32,9 @@
#include "core/core_string_names.h"
#include "core/engine.h"
+#include "core/os/mutex.h"
#include "navigation_2d.h"
+#include "servers/navigation_2d_server.h"
#include "thirdparty/misc/triangulator.h"
@@ -80,6 +82,9 @@ bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double
void NavigationPolygon::set_vertices(const PoolVector<Vector2> &p_vertices) {
+ navmesh_generation->lock();
+ navmesh.unref();
+ navmesh_generation->unlock();
vertices = p_vertices;
rect_cache_dirty = true;
}
@@ -91,6 +96,9 @@ PoolVector<Vector2> NavigationPolygon::get_vertices() const {
void NavigationPolygon::_set_polygons(const Array &p_array) {
+ navmesh_generation->lock();
+ navmesh.unref();
+ navmesh_generation->unlock();
polygons.resize(p_array.size());
for (int i = 0; i < p_array.size(); i++) {
polygons.write[i].indices = p_array[i];
@@ -133,6 +141,9 @@ void NavigationPolygon::add_polygon(const Vector<int> &p_polygon) {
Polygon polygon;
polygon.indices = p_polygon;
polygons.push_back(polygon);
+ navmesh_generation->lock();
+ navmesh.unref();
+ navmesh_generation->unlock();
}
void NavigationPolygon::add_outline_at_index(const PoolVector<Vector2> &p_outline, int p_index) {
@@ -153,6 +164,34 @@ Vector<int> NavigationPolygon::get_polygon(int p_idx) {
void NavigationPolygon::clear_polygons() {
polygons.clear();
+ navmesh_generation->lock();
+ navmesh.unref();
+ navmesh_generation->unlock();
+}
+
+Ref<NavigationMesh> NavigationPolygon::get_mesh() {
+ navmesh_generation->lock();
+ if (navmesh.is_null()) {
+ navmesh.instance();
+ PoolVector<Vector3> verts;
+ {
+ verts.resize(get_vertices().size());
+ PoolVector<Vector3>::Write w = verts.write();
+
+ PoolVector<Vector2>::Read r = get_vertices().read();
+
+ for (int i(0); i < get_vertices().size(); i++) {
+ w[i] = Vector3(r[i].x, 0.0, r[i].y);
+ }
+ }
+ navmesh->set_vertices(verts);
+
+ for (int i(0); i < get_polygon_count(); i++) {
+ navmesh->add_polygon(get_polygon(i));
+ }
+ }
+ navmesh_generation->unlock();
+ return navmesh;
}
void NavigationPolygon::add_outline(const PoolVector<Vector2> &p_outline) {
@@ -191,6 +230,9 @@ void NavigationPolygon::clear_outlines() {
}
void NavigationPolygon::make_polygons_from_outlines() {
+ navmesh_generation->lock();
+ navmesh.unref();
+ navmesh_generation->unlock();
List<TriangulatorPoly> in_poly, out_poly;
Vector2 outside_point(-1e10, -1e10);
@@ -320,7 +362,9 @@ void NavigationPolygon::_bind_methods() {
}
NavigationPolygon::NavigationPolygon() :
- rect_cache_dirty(true) {
+ rect_cache_dirty(true),
+ navmesh_generation(NULL) {
+ navmesh_generation = Mutex::create();
}
void NavigationPolygonInstance::set_enabled(bool p_enabled) {
@@ -334,18 +378,12 @@ void NavigationPolygonInstance::set_enabled(bool p_enabled) {
if (!enabled) {
- if (nav_id != -1) {
- navigation->navpoly_remove(nav_id);
- nav_id = -1;
- }
+ Navigation2DServer::get_singleton()->region_set_map(region, RID());
} else {
if (navigation) {
- if (navpoly.is_valid()) {
-
- nav_id = navigation->navpoly_add(navpoly, get_relative_transform_to_parent(navigation), this);
- }
+ Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid());
}
}
@@ -382,9 +420,9 @@ void NavigationPolygonInstance::_notification(int p_what) {
navigation = Object::cast_to<Navigation2D>(c);
if (navigation) {
- if (enabled && navpoly.is_valid()) {
+ if (enabled) {
- nav_id = navigation->navpoly_add(navpoly, get_relative_transform_to_parent(navigation), this);
+ Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid());
}
break;
}
@@ -395,19 +433,14 @@ void NavigationPolygonInstance::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
- if (navigation && nav_id != -1) {
- navigation->navpoly_set_transform(nav_id, get_relative_transform_to_parent(navigation));
- }
+ Navigation2DServer::get_singleton()->region_set_transform(region, get_global_transform());
} break;
case NOTIFICATION_EXIT_TREE: {
if (navigation) {
- if (nav_id != -1) {
- navigation->navpoly_remove(nav_id);
- nav_id = -1;
- }
+ Navigation2DServer::get_singleton()->region_set_map(region, RID());
}
navigation = NULL;
} break;
@@ -466,24 +499,18 @@ void NavigationPolygonInstance::set_navigation_polygon(const Ref<NavigationPolyg
return;
}
- if (navigation && nav_id != -1) {
- navigation->navpoly_remove(nav_id);
- nav_id = -1;
- }
-
if (navpoly.is_valid()) {
navpoly->disconnect(CoreStringNames::get_singleton()->changed, this, "_navpoly_changed");
}
+
navpoly = p_navpoly;
+ Navigation2DServer::get_singleton()->region_set_navpoly(region, p_navpoly);
+
if (navpoly.is_valid()) {
navpoly->connect(CoreStringNames::get_singleton()->changed, this, "_navpoly_changed");
}
_navpoly_changed();
- if (navigation && navpoly.is_valid() && enabled) {
- nav_id = navigation->navpoly_add(navpoly, get_relative_transform_to_parent(navigation), this);
- }
-
_change_notify("navpoly");
update_configuration_warning();
}
@@ -536,8 +563,13 @@ void NavigationPolygonInstance::_bind_methods() {
NavigationPolygonInstance::NavigationPolygonInstance() {
- navigation = NULL;
- nav_id = -1;
enabled = true;
set_notify_transform(true);
+ region = Navigation2DServer::get_singleton()->region_create();
+
+ navigation = NULL;
+}
+
+NavigationPolygonInstance::~NavigationPolygonInstance() {
+ Navigation2DServer::get_singleton()->free(region);
}
diff --git a/scene/2d/navigation_polygon.h b/scene/2d/navigation_polygon.h
index cbc1711a32..92cfc8f5e6 100644
--- a/scene/2d/navigation_polygon.h
+++ b/scene/2d/navigation_polygon.h
@@ -32,6 +32,9 @@
#define NAVIGATION_POLYGON_H
#include "scene/2d/node_2d.h"
+#include "scene/resources/navigation_mesh.h"
+
+class Mutex;
class NavigationPolygon : public Resource {
@@ -47,6 +50,10 @@ class NavigationPolygon : public Resource {
mutable Rect2 item_rect;
mutable bool rect_cache_dirty;
+ Mutex *navmesh_generation;
+ // Navigation mesh
+ Ref<NavigationMesh> navmesh;
+
protected:
static void _bind_methods();
@@ -81,6 +88,8 @@ public:
Vector<int> get_polygon(int p_idx);
void clear_polygons();
+ Ref<NavigationMesh> get_mesh();
+
NavigationPolygon();
};
@@ -91,7 +100,7 @@ class NavigationPolygonInstance : public Node2D {
GDCLASS(NavigationPolygonInstance, Node2D);
bool enabled;
- int nav_id;
+ RID region;
Navigation2D *navigation;
Ref<NavigationPolygon> navpoly;
@@ -116,6 +125,7 @@ public:
String get_configuration_warning() const;
NavigationPolygonInstance();
+ ~NavigationPolygonInstance();
};
#endif // NAVIGATIONPOLYGON_H
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index b6db025d44..d8b880e571 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -35,6 +35,7 @@
#include "core/method_bind_ext.gen.inc"
#include "core/os/os.h"
#include "scene/2d/area_2d.h"
+#include "servers/navigation_2d_server.h"
#include "servers/physics_2d_server.h"
int TileMap::_get_quadrant_size() const {
@@ -86,7 +87,7 @@ void TileMap::_notification(int p_what) {
if (navigation) {
for (Map<PosKey, Quadrant::NavPoly>::Element *F = q.navpoly_ids.front(); F; F = F->next()) {
- navigation->navpoly_remove(F->get().id);
+ Navigation2DServer::get_singleton()->region_set_map(F->get().region, RID());
}
q.navpoly_ids.clear();
}
@@ -163,7 +164,7 @@ void TileMap::_update_quadrant_transform() {
if (navigation) {
for (Map<PosKey, Quadrant::NavPoly>::Element *F = q.navpoly_ids.front(); F; F = F->next()) {
- navigation->navpoly_set_transform(F->get().id, nav_rel * F->get().xform);
+ Navigation2DServer::get_singleton()->region_set_transform(F->get().region, nav_rel * F->get().xform);
}
}
@@ -377,7 +378,7 @@ void TileMap::update_dirty_quadrants() {
if (navigation) {
for (Map<PosKey, Quadrant::NavPoly>::Element *E = q.navpoly_ids.front(); E; E = E->next()) {
- navigation->navpoly_remove(E->get().id);
+ Navigation2DServer::get_singleton()->region_set_map(E->get().region, RID());
}
q.navpoly_ids.clear();
}
@@ -611,10 +612,13 @@ void TileMap::update_dirty_quadrants() {
xform.set_origin(offset.floor() + q.pos);
_fix_cell_transform(xform, c, npoly_ofs, s);
- int pid = navigation->navpoly_add(navpoly, nav_rel * xform);
+ RID region = Navigation2DServer::get_singleton()->region_create();
+ Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid());
+ Navigation2DServer::get_singleton()->region_set_transform(region, nav_rel * xform);
+ Navigation2DServer::get_singleton()->region_set_navpoly(region, navpoly);
Quadrant::NavPoly np;
- np.id = pid;
+ np.region = region;
np.xform = xform;
q.navpoly_ids[E->key()] = np;
@@ -809,7 +813,7 @@ void TileMap::_erase_quadrant(Map<PosKey, Quadrant>::Element *Q) {
if (navigation) {
for (Map<PosKey, Quadrant::NavPoly>::Element *E = q.navpoly_ids.front(); E; E = E->next()) {
- navigation->navpoly_remove(E->get().id);
+ Navigation2DServer::get_singleton()->region_set_map(E->get().region, RID());
}
q.navpoly_ids.clear();
}
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 0875d197eb..d5ef7fc818 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -139,7 +139,7 @@ private:
SelfList<Quadrant> dirty_list;
struct NavPoly {
- int id;
+ RID region;
Transform2D xform;
};
diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp
index 4a82fe0080..10b12f5c75 100644
--- a/scene/3d/navigation.cpp
+++ b/scene/3d/navigation.cpp
@@ -30,698 +30,76 @@
#include "navigation.h"
-#define USE_ENTRY_POINT
+#include "servers/navigation_server.h"
-void Navigation::_navmesh_link(int p_id) {
-
- ERR_FAIL_COND(!navmesh_map.has(p_id));
- NavMesh &nm = navmesh_map[p_id];
- ERR_FAIL_COND(nm.linked);
- ERR_FAIL_COND(nm.navmesh.is_null());
-
- PoolVector<Vector3> vertices = nm.navmesh->get_vertices();
- int len = vertices.size();
- if (len == 0)
- return;
-
- PoolVector<Vector3>::Read r = vertices.read();
-
- for (int i = 0; i < nm.navmesh->get_polygon_count(); i++) {
-
- //build
-
- List<Polygon>::Element *P = nm.polygons.push_back(Polygon());
- Polygon &p = P->get();
- p.owner = &nm;
-
- Vector<int> poly = nm.navmesh->get_polygon(i);
- int plen = poly.size();
- const int *indices = poly.ptr();
- bool valid = true;
- p.edges.resize(plen);
-
- Vector3 center;
- float sum = 0;
-
- for (int j = 0; j < plen; j++) {
-
- int idx = indices[j];
- if (idx < 0 || idx >= len) {
- valid = false;
- break;
- }
-
- Polygon::Edge e;
- Vector3 ep = nm.xform.xform(r[idx]);
- center += ep;
- e.point = _get_point(ep);
- p.edges.write[j] = e;
-
- if (j >= 2) {
- Vector3 epa = nm.xform.xform(r[indices[j - 2]]);
- Vector3 epb = nm.xform.xform(r[indices[j - 1]]);
-
- sum += up.dot((epb - epa).cross(ep - epa));
- }
- }
-
- p.clockwise = sum > 0;
-
- if (!valid) {
- nm.polygons.pop_back();
- ERR_CONTINUE(!valid);
- }
-
- p.center = center;
- if (plen != 0) {
- p.center /= plen;
- }
-
- //connect
-
- for (int j = 0; j < plen; j++) {
-
- int next = (j + 1) % plen;
- EdgeKey ek(p.edges[j].point, p.edges[next].point);
-
- Map<EdgeKey, Connection>::Element *C = connections.find(ek);
- if (!C) {
-
- Connection c;
- c.A = &p;
- c.A_edge = j;
- c.B = NULL;
- c.B_edge = -1;
- connections[ek] = c;
- } else {
-
- if (C->get().B != NULL) {
- ConnectionPending pending;
- pending.polygon = &p;
- pending.edge = j;
- p.edges.write[j].P = C->get().pending.push_back(pending);
- continue;
- }
-
- C->get().B = &p;
- C->get().B_edge = j;
- C->get().A->edges.write[C->get().A_edge].C = &p;
- C->get().A->edges.write[C->get().A_edge].C_edge = j;
- p.edges.write[j].C = C->get().A;
- p.edges.write[j].C_edge = C->get().A_edge;
- //connection successful.
- }
- }
- }
-
- nm.linked = true;
-}
-
-void Navigation::_navmesh_unlink(int p_id) {
-
- ERR_FAIL_COND(!navmesh_map.has(p_id));
- NavMesh &nm = navmesh_map[p_id];
- ERR_FAIL_COND(!nm.linked);
-
- for (List<Polygon>::Element *E = nm.polygons.front(); E; E = E->next()) {
-
- Polygon &p = E->get();
-
- int ec = p.edges.size();
- Polygon::Edge *edges = p.edges.ptrw();
-
- for (int i = 0; i < ec; i++) {
- int next = (i + 1) % ec;
-
- EdgeKey ek(edges[i].point, edges[next].point);
- Map<EdgeKey, Connection>::Element *C = connections.find(ek);
-
- ERR_CONTINUE(!C);
-
- if (edges[i].P) {
- C->get().pending.erase(edges[i].P);
- edges[i].P = NULL;
- } else if (C->get().B) {
- //disconnect
-
- C->get().B->edges.write[C->get().B_edge].C = NULL;
- C->get().B->edges.write[C->get().B_edge].C_edge = -1;
- C->get().A->edges.write[C->get().A_edge].C = NULL;
- C->get().A->edges.write[C->get().A_edge].C_edge = -1;
-
- if (C->get().A == &E->get()) {
-
- C->get().A = C->get().B;
- C->get().A_edge = C->get().B_edge;
- }
- C->get().B = NULL;
- C->get().B_edge = -1;
-
- if (C->get().pending.size()) {
- //reconnect if something is pending
- ConnectionPending cp = C->get().pending.front()->get();
- C->get().pending.pop_front();
-
- C->get().B = cp.polygon;
- C->get().B_edge = cp.edge;
- C->get().A->edges.write[C->get().A_edge].C = cp.polygon;
- C->get().A->edges.write[C->get().A_edge].C_edge = cp.edge;
- cp.polygon->edges.write[cp.edge].C = C->get().A;
- cp.polygon->edges.write[cp.edge].C_edge = C->get().A_edge;
- cp.polygon->edges.write[cp.edge].P = NULL;
- }
-
- } else {
- connections.erase(C);
- //erase
- }
- }
- }
-
- nm.polygons.clear();
-
- nm.linked = false;
-}
-
-int Navigation::navmesh_add(const Ref<NavigationMesh> &p_mesh, const Transform &p_xform, Object *p_owner) {
-
- int id = last_id++;
- NavMesh nm;
- nm.linked = false;
- nm.navmesh = p_mesh;
- nm.xform = p_xform;
- nm.owner = p_owner;
- navmesh_map[id] = nm;
-
- _navmesh_link(id);
-
- return id;
-}
-
-void Navigation::navmesh_set_transform(int p_id, const Transform &p_xform) {
-
- ERR_FAIL_COND(!navmesh_map.has(p_id));
- NavMesh &nm = navmesh_map[p_id];
- if (nm.xform == p_xform)
- return; //bleh
- _navmesh_unlink(p_id);
- nm.xform = p_xform;
- _navmesh_link(p_id);
-}
-void Navigation::navmesh_remove(int p_id) {
+Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize) {
- ERR_FAIL_COND_MSG(!navmesh_map.has(p_id), "Trying to remove nonexisting navmesh with id: " + itos(p_id));
- _navmesh_unlink(p_id);
- navmesh_map.erase(p_id);
+ return NavigationServer::get_singleton()->map_get_path(map, p_start, p_end, p_optimize);
}
-void Navigation::_clip_path(Vector<Vector3> &path, Polygon *from_poly, const Vector3 &p_to_point, Polygon *p_to_poly) {
-
- Vector3 from = path[path.size() - 1];
-
- if (from.distance_to(p_to_point) < CMP_EPSILON)
- return;
- Plane cut_plane;
- cut_plane.normal = (from - p_to_point).cross(up);
- if (cut_plane.normal == Vector3())
- return;
- cut_plane.normal.normalize();
- cut_plane.d = cut_plane.normal.dot(from);
-
- while (from_poly != p_to_poly) {
-
- int pe = from_poly->prev_edge;
- Vector3 a = _get_vertex(from_poly->edges[pe].point);
- Vector3 b = _get_vertex(from_poly->edges[(pe + 1) % from_poly->edges.size()].point);
-
- from_poly = from_poly->edges[pe].C;
- ERR_FAIL_COND(!from_poly);
-
- if (a.distance_to(b) > CMP_EPSILON) {
+void Navigation::set_up_vector(const Vector3 &p_up) {
- Vector3 inters;
- if (cut_plane.intersects_segment(a, b, &inters)) {
- if (inters.distance_to(p_to_point) > CMP_EPSILON && inters.distance_to(path[path.size() - 1]) > CMP_EPSILON) {
- path.push_back(inters);
- }
- }
- }
- }
+ up = p_up;
+ NavigationServer::get_singleton()->map_set_up(map, up);
}
-Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize) {
-
- Polygon *begin_poly = NULL;
- Polygon *end_poly = NULL;
- Vector3 begin_point;
- Vector3 end_point;
- float begin_d = 1e20;
- float end_d = 1e20;
-
- for (Map<int, NavMesh>::Element *E = navmesh_map.front(); E; E = E->next()) {
-
- if (!E->get().linked)
- continue;
- for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
-
- Polygon &p = F->get();
- for (int i = 2; i < p.edges.size(); i++) {
-
- Face3 f(_get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point));
- Vector3 spoint = f.get_closest_point_to(p_start);
- float dpoint = spoint.distance_to(p_start);
- if (dpoint < begin_d) {
- begin_d = dpoint;
- begin_poly = &p;
- begin_point = spoint;
- }
-
- spoint = f.get_closest_point_to(p_end);
- dpoint = spoint.distance_to(p_end);
- if (dpoint < end_d) {
- end_d = dpoint;
- end_poly = &p;
- end_point = spoint;
- }
- }
-
- p.prev_edge = -1;
- }
- }
-
- if (!begin_poly || !end_poly) {
-
- return Vector<Vector3>(); //no path
- }
-
- if (begin_poly == end_poly) {
-
- Vector<Vector3> path;
- path.resize(2);
- path.write[0] = begin_point;
- path.write[1] = end_point;
- return path;
- }
-
- bool found_route = false;
-
- List<Polygon *> open_list;
-
- for (int i = 0; i < begin_poly->edges.size(); i++) {
-
- if (begin_poly->edges[i].C) {
-
- begin_poly->edges[i].C->prev_edge = begin_poly->edges[i].C_edge;
-#ifdef USE_ENTRY_POINT
- Vector3 edge[2] = {
- _get_vertex(begin_poly->edges[i].point),
- _get_vertex(begin_poly->edges[(i + 1) % begin_poly->edges.size()].point)
- };
-
- Vector3 entry = Geometry::get_closest_point_to_segment(begin_poly->entry, edge);
- begin_poly->edges[i].C->distance = begin_point.distance_to(entry);
- begin_poly->edges[i].C->entry = entry;
-#else
- begin_poly->edges[i].C->distance = begin_poly->center.distance_to(begin_poly->edges[i].C->center);
-#endif
- open_list.push_back(begin_poly->edges[i].C);
- }
- }
-
- while (!found_route) {
-
- if (open_list.size() == 0) {
- break;
- }
- //check open list
-
- List<Polygon *>::Element *least_cost_poly = NULL;
- float least_cost = 1e30;
-
- //this could be faster (cache previous results)
- for (List<Polygon *>::Element *E = open_list.front(); E; E = E->next()) {
-
- Polygon *p = E->get();
-
- float cost = p->distance;
-#ifdef USE_ENTRY_POINT
- cost += p->entry.distance_to(end_point);
-#else
- cost += p->center.distance_to(end_point);
-#endif
- if (cost < least_cost) {
- least_cost_poly = E;
- least_cost = cost;
- }
- }
-
- Polygon *p = least_cost_poly->get();
- //open the neighbours for search
-
- if (p == end_poly) {
- //oh my reached end! stop algorithm
- found_route = true;
- break;
- }
-
- for (int i = 0; i < p->edges.size(); i++) {
-
- Polygon::Edge &e = p->edges.write[i];
-
- if (!e.C)
- continue;
-
-#ifdef USE_ENTRY_POINT
- Vector3 edge[2] = {
- _get_vertex(p->edges[i].point),
- _get_vertex(p->edges[(i + 1) % p->edges.size()].point)
- };
-
- Vector3 entry = Geometry::get_closest_point_to_segment(p->entry, edge);
- float distance = p->entry.distance_to(entry) + p->distance;
-#else
- float distance = p->center.distance_to(e.C->center) + p->distance;
-#endif
-
- if (e.C->prev_edge != -1) {
- //oh this was visited already, can we win the cost?
-
- if (e.C->distance > distance) {
-
- e.C->prev_edge = e.C_edge;
- e.C->distance = distance;
-#ifdef USE_ENTRY_POINT
- e.C->entry = entry;
-#endif
- }
- } else {
- //add to open neighbours
-
- e.C->prev_edge = e.C_edge;
- e.C->distance = distance;
-#ifdef USE_ENTRY_POINT
- e.C->entry = entry;
-#endif
- open_list.push_back(e.C);
- }
- }
-
- open_list.erase(least_cost_poly);
- }
-
- if (found_route) {
-
- Vector<Vector3> path;
-
- if (p_optimize) {
- //string pulling
-
- Polygon *apex_poly = end_poly;
- Vector3 apex_point = end_point;
- Vector3 portal_left = apex_point;
- Vector3 portal_right = apex_point;
- Polygon *left_poly = end_poly;
- Polygon *right_poly = end_poly;
- Polygon *p = end_poly;
- path.push_back(end_point);
-
- while (p) {
-
- Vector3 left;
- Vector3 right;
-
-#define CLOCK_TANGENT(m_a, m_b, m_c) (((m_a) - (m_c)).cross((m_a) - (m_b)))
-
- if (p == begin_poly) {
- left = begin_point;
- right = begin_point;
- } else {
- int prev = p->prev_edge;
- int prev_n = (p->prev_edge + 1) % p->edges.size();
- left = _get_vertex(p->edges[prev].point);
- right = _get_vertex(p->edges[prev_n].point);
-
- //if (CLOCK_TANGENT(apex_point,left,(left+right)*0.5).dot(up) < 0){
- if (p->clockwise) {
- SWAP(left, right);
- }
- }
-
- bool skip = false;
-
- if (CLOCK_TANGENT(apex_point, portal_left, left).dot(up) >= 0) {
- //process
- if (portal_left == apex_point || CLOCK_TANGENT(apex_point, left, portal_right).dot(up) > 0) {
- left_poly = p;
- portal_left = left;
- } else {
-
- _clip_path(path, apex_poly, portal_right, right_poly);
-
- apex_point = portal_right;
- p = right_poly;
- left_poly = p;
- apex_poly = p;
- portal_left = apex_point;
- portal_right = apex_point;
- path.push_back(apex_point);
- skip = true;
- }
- }
-
- if (!skip && CLOCK_TANGENT(apex_point, portal_right, right).dot(up) <= 0) {
- //process
- if (portal_right == apex_point || CLOCK_TANGENT(apex_point, right, portal_left).dot(up) < 0) {
- right_poly = p;
- portal_right = right;
- } else {
-
- _clip_path(path, apex_poly, portal_left, left_poly);
-
- apex_point = portal_left;
- p = left_poly;
- right_poly = p;
- apex_poly = p;
- portal_right = apex_point;
- portal_left = apex_point;
- path.push_back(apex_point);
- }
- }
-
- if (p != begin_poly)
- p = p->edges[p->prev_edge].C;
- else
- p = NULL;
- }
-
- if (path[path.size() - 1] != begin_point)
- path.push_back(begin_point);
-
- path.invert();
-
- } else {
- //midpoints
- Polygon *p = end_poly;
-
- path.push_back(end_point);
- while (true) {
- int prev = p->prev_edge;
-#ifdef USE_ENTRY_POINT
- Vector3 point = p->entry;
-#else
- int prev_n = (p->prev_edge + 1) % p->edges.size();
- Vector3 point = (_get_vertex(p->edges[prev].point) + _get_vertex(p->edges[prev_n].point)) * 0.5;
-#endif
- path.push_back(point);
- p = p->edges[prev].C;
- if (p == begin_poly)
- break;
- }
-
- path.push_back(begin_point);
-
- path.invert();
- }
-
- return path;
- }
+Vector3 Navigation::get_up_vector() const {
- return Vector<Vector3>();
+ return up;
}
-Vector3 Navigation::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool &p_use_collision) {
-
- bool use_collision = p_use_collision;
- Vector3 closest_point;
- float closest_point_d = 1e20;
-
- for (Map<int, NavMesh>::Element *E = navmesh_map.front(); E; E = E->next()) {
-
- if (!E->get().linked)
- continue;
- for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
-
- Polygon &p = F->get();
- for (int i = 2; i < p.edges.size(); i++) {
-
- Face3 f(_get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point));
- Vector3 inters;
- if (f.intersects_segment(p_from, p_to, &inters)) {
-
- if (!use_collision) {
- closest_point = inters;
- use_collision = true;
- closest_point_d = p_from.distance_to(inters);
- } else if (closest_point_d > inters.distance_to(p_from)) {
-
- closest_point = inters;
- closest_point_d = p_from.distance_to(inters);
- }
- }
- }
-
- if (!use_collision) {
-
- for (int i = 0; i < p.edges.size(); i++) {
-
- Vector3 a, b;
-
- Geometry::get_closest_points_between_segments(p_from, p_to, _get_vertex(p.edges[i].point), _get_vertex(p.edges[(i + 1) % p.edges.size()].point), a, b);
-
- float d = a.distance_to(b);
- if (d < closest_point_d) {
-
- closest_point_d = d;
- closest_point = b;
- }
- }
- }
- }
- }
-
- return closest_point;
+void Navigation::set_cell_size(float p_cell_size) {
+ cell_size = p_cell_size;
+ NavigationServer::get_singleton()->map_set_cell_size(map, cell_size);
}
-Vector3 Navigation::get_closest_point(const Vector3 &p_point) {
-
- Vector3 closest_point;
- float closest_point_d = 1e20;
-
- for (Map<int, NavMesh>::Element *E = navmesh_map.front(); E; E = E->next()) {
-
- if (!E->get().linked)
- continue;
- for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
-
- Polygon &p = F->get();
- for (int i = 2; i < p.edges.size(); i++) {
-
- Face3 f(_get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point));
- Vector3 inters = f.get_closest_point_to(p_point);
- float d = inters.distance_to(p_point);
- if (d < closest_point_d) {
- closest_point = inters;
- closest_point_d = d;
- }
- }
- }
- }
-
- return closest_point;
+void Navigation::set_edge_connection_margin(float p_edge_connection_margin) {
+ edge_connection_margin = p_edge_connection_margin;
+ NavigationServer::get_singleton()->map_set_edge_connection_margin(map, edge_connection_margin);
}
-Vector3 Navigation::get_closest_point_normal(const Vector3 &p_point) {
+void Navigation::_bind_methods() {
- Vector3 closest_point;
- Vector3 closest_normal;
- float closest_point_d = 1e20;
+ ClassDB::bind_method(D_METHOD("get_rid"), &Navigation::get_rid);
- for (Map<int, NavMesh>::Element *E = navmesh_map.front(); E; E = E->next()) {
+ ClassDB::bind_method(D_METHOD("get_simple_path", "start", "end", "optimize"), &Navigation::get_simple_path, DEFVAL(true));
- if (!E->get().linked)
- continue;
- for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
+ ClassDB::bind_method(D_METHOD("set_up_vector", "up"), &Navigation::set_up_vector);
+ ClassDB::bind_method(D_METHOD("get_up_vector"), &Navigation::get_up_vector);
- Polygon &p = F->get();
- for (int i = 2; i < p.edges.size(); i++) {
+ ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &Navigation::set_cell_size);
+ ClassDB::bind_method(D_METHOD("get_cell_size"), &Navigation::get_cell_size);
- Face3 f(_get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point));
- Vector3 inters = f.get_closest_point_to(p_point);
- float d = inters.distance_to(p_point);
- if (d < closest_point_d) {
- closest_point = inters;
- closest_point_d = d;
- closest_normal = f.get_plane().normal;
- }
- }
- }
- }
+ ClassDB::bind_method(D_METHOD("set_edge_connection_margin", "margin"), &Navigation::set_edge_connection_margin);
+ ClassDB::bind_method(D_METHOD("get_edge_connection_margin"), &Navigation::get_edge_connection_margin);
- return closest_normal;
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_vector"), "set_up_vector", "get_up_vector");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell_size"), "set_cell_size", "get_cell_size");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "edge_connection_margin"), "set_edge_connection_margin", "get_edge_connection_margin");
}
-Object *Navigation::get_closest_point_owner(const Vector3 &p_point) {
-
- Vector3 closest_point;
- Object *owner = NULL;
- float closest_point_d = 1e20;
-
- for (Map<int, NavMesh>::Element *E = navmesh_map.front(); E; E = E->next()) {
-
- if (!E->get().linked)
- continue;
- for (List<Polygon>::Element *F = E->get().polygons.front(); F; F = F->next()) {
+void Navigation::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_READY: {
+ NavigationServer::get_singleton()->map_set_active(map, true);
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
- Polygon &p = F->get();
- for (int i = 2; i < p.edges.size(); i++) {
-
- Face3 f(_get_vertex(p.edges[0].point), _get_vertex(p.edges[i - 1].point), _get_vertex(p.edges[i].point));
- Vector3 inters = f.get_closest_point_to(p_point);
- float d = inters.distance_to(p_point);
- if (d < closest_point_d) {
- closest_point = inters;
- closest_point_d = d;
- owner = E->get().owner;
- }
- }
- }
+ NavigationServer::get_singleton()->map_set_active(map, false);
+ } break;
}
-
- return owner;
-}
-
-void Navigation::set_up_vector(const Vector3 &p_up) {
-
- up = p_up;
-}
-
-Vector3 Navigation::get_up_vector() const {
-
- return up;
}
-void Navigation::_bind_methods() {
-
- ClassDB::bind_method(D_METHOD("navmesh_add", "mesh", "xform", "owner"), &Navigation::navmesh_add, DEFVAL(Variant()));
- ClassDB::bind_method(D_METHOD("navmesh_set_transform", "id", "xform"), &Navigation::navmesh_set_transform);
- ClassDB::bind_method(D_METHOD("navmesh_remove", "id"), &Navigation::navmesh_remove);
+Navigation::Navigation() {
- ClassDB::bind_method(D_METHOD("get_simple_path", "start", "end", "optimize"), &Navigation::get_simple_path, DEFVAL(true));
- ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "start", "end", "use_collision"), &Navigation::get_closest_point_to_segment, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Navigation::get_closest_point);
- ClassDB::bind_method(D_METHOD("get_closest_point_normal", "to_point"), &Navigation::get_closest_point_normal);
- ClassDB::bind_method(D_METHOD("get_closest_point_owner", "to_point"), &Navigation::get_closest_point_owner);
+ map = NavigationServer::get_singleton()->map_create();
- ClassDB::bind_method(D_METHOD("set_up_vector", "up"), &Navigation::set_up_vector);
- ClassDB::bind_method(D_METHOD("get_up_vector"), &Navigation::get_up_vector);
+ set_cell_size(0.3);
+ set_edge_connection_margin(5.0); // Five meters, depends alot on the agents radius
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_vector"), "set_up_vector", "get_up_vector");
+ up = Vector3(0, 1, 0);
}
-Navigation::Navigation() {
-
- ERR_FAIL_COND(sizeof(Point) != 8);
- cell_size = 0.01; //one centimeter
- last_id = 1;
- up = Vector3(0, 1, 0);
+Navigation::~Navigation() {
+ NavigationServer::get_singleton()->free(map);
}
diff --git a/scene/3d/navigation.h b/scene/3d/navigation.h
index 31dbc9d4b5..68e041ad73 100644
--- a/scene/3d/navigation.h
+++ b/scene/3d/navigation.h
@@ -31,154 +31,45 @@
#ifndef NAVIGATION_H
#define NAVIGATION_H
-#include "scene/3d/navigation_mesh.h"
+#include "scene/3d/navigation_mesh_instance.h"
#include "scene/3d/spatial.h"
class Navigation : public Spatial {
GDCLASS(Navigation, Spatial);
- union Point {
-
- struct {
- int64_t x : 21;
- int64_t y : 22;
- int64_t z : 21;
- };
-
- uint64_t key;
- bool operator<(const Point &p_key) const { return key < p_key.key; }
- };
-
- struct EdgeKey {
-
- Point a;
- Point b;
-
- bool operator<(const EdgeKey &p_key) const {
- return (a.key == p_key.a.key) ? (b.key < p_key.b.key) : (a.key < p_key.a.key);
- };
-
- EdgeKey(const Point &p_a = Point(), const Point &p_b = Point()) :
- a(p_a),
- b(p_b) {
- if (a.key > b.key) {
- SWAP(a, b);
- }
- }
- };
-
- struct NavMesh;
- struct Polygon;
-
- struct ConnectionPending {
-
- Polygon *polygon;
- int edge;
- };
-
- struct Polygon {
-
- struct Edge {
- Point point;
- Polygon *C; //connection
- int C_edge;
- List<ConnectionPending>::Element *P;
- Edge() {
- C = NULL;
- C_edge = -1;
- P = NULL;
- }
- };
-
- Vector<Edge> edges;
-
- Vector3 center;
- Vector3 entry;
-
- float distance;
- int prev_edge;
- bool clockwise;
-
- NavMesh *owner;
- };
-
- struct Connection {
-
- Polygon *A;
- int A_edge;
- Polygon *B;
- int B_edge;
-
- List<ConnectionPending> pending;
-
- Connection() {
- A = NULL;
- B = NULL;
- A_edge = -1;
- B_edge = -1;
- }
- };
-
- Map<EdgeKey, Connection> connections;
-
- struct NavMesh {
-
- Object *owner;
- Transform xform;
- bool linked;
- Ref<NavigationMesh> navmesh;
- List<Polygon> polygons;
- };
-
- _FORCE_INLINE_ Point _get_point(const Vector3 &p_pos) const {
-
- int x = int(Math::floor(p_pos.x / cell_size));
- int y = int(Math::floor(p_pos.y / cell_size));
- int z = int(Math::floor(p_pos.z / cell_size));
-
- Point p;
- p.key = 0;
- p.x = x;
- p.y = y;
- p.z = z;
- return p;
- }
-
- _FORCE_INLINE_ Vector3 _get_vertex(const Point &p_point) const {
-
- return Vector3(p_point.x, p_point.y, p_point.z) * cell_size;
- }
-
- void _navmesh_link(int p_id);
- void _navmesh_unlink(int p_id);
-
- float cell_size;
- Map<int, NavMesh> navmesh_map;
- int last_id;
+ RID map;
Vector3 up;
- void _clip_path(Vector<Vector3> &path, Polygon *from_poly, const Vector3 &p_to_point, Polygon *p_to_poly);
+ real_t cell_size;
+ real_t edge_connection_margin;
protected:
static void _bind_methods();
+ void _notification(int p_what);
public:
+ RID get_rid() const {
+ return map;
+ }
+
void set_up_vector(const Vector3 &p_up);
Vector3 get_up_vector() const;
- //API should be as dynamic as possible
- int navmesh_add(const Ref<NavigationMesh> &p_mesh, const Transform &p_xform, Object *p_owner = NULL);
- void navmesh_set_transform(int p_id, const Transform &p_xform);
- void navmesh_remove(int p_id);
+ void set_cell_size(float p_cell_size);
+ float get_cell_size() const {
+ return cell_size;
+ }
+
+ void set_edge_connection_margin(float p_edge_connection_margin);
+ float get_edge_connection_margin() const {
+ return edge_connection_margin;
+ }
Vector<Vector3> get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize = true);
- Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool &p_use_collision = false);
- Vector3 get_closest_point(const Vector3 &p_point);
- Vector3 get_closest_point_normal(const Vector3 &p_point);
- Object *get_closest_point_owner(const Vector3 &p_point);
Navigation();
+ ~Navigation();
};
#endif // NAVIGATION_H
diff --git a/scene/3d/navigation_agent.cpp b/scene/3d/navigation_agent.cpp
new file mode 100644
index 0000000000..d3f447bc24
--- /dev/null
+++ b/scene/3d/navigation_agent.cpp
@@ -0,0 +1,361 @@
+/*************************************************************************/
+/* navigation_agent.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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_agent.h"
+
+#include "core/engine.h"
+#include "scene/3d/navigation.h"
+#include "servers/navigation_server.h"
+
+void NavigationAgent::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_target_desired_distance", "desired_distance"), &NavigationAgent::set_target_desired_distance);
+ ClassDB::bind_method(D_METHOD("get_target_desired_distance"), &NavigationAgent::get_target_desired_distance);
+
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationAgent::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &NavigationAgent::get_radius);
+
+ ClassDB::bind_method(D_METHOD("set_agent_height_offset", "agent_height_offset"), &NavigationAgent::set_agent_height_offset);
+ ClassDB::bind_method(D_METHOD("get_agent_height_offset"), &NavigationAgent::get_agent_height_offset);
+
+ ClassDB::bind_method(D_METHOD("set_ignore_y", "ignore"), &NavigationAgent::set_ignore_y);
+ ClassDB::bind_method(D_METHOD("get_ignore_y"), &NavigationAgent::get_ignore_y);
+
+ ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationAgent::set_navigation_node);
+ ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationAgent::get_navigation_node);
+
+ ClassDB::bind_method(D_METHOD("set_neighbor_dist", "neighbor_dist"), &NavigationAgent::set_neighbor_dist);
+ ClassDB::bind_method(D_METHOD("get_neighbor_dist"), &NavigationAgent::get_neighbor_dist);
+
+ ClassDB::bind_method(D_METHOD("set_max_neighbors", "max_neighbors"), &NavigationAgent::set_max_neighbors);
+ ClassDB::bind_method(D_METHOD("get_max_neighbors"), &NavigationAgent::get_max_neighbors);
+
+ ClassDB::bind_method(D_METHOD("set_time_horizon", "time_horizon"), &NavigationAgent::set_time_horizon);
+ ClassDB::bind_method(D_METHOD("get_time_horizon"), &NavigationAgent::get_time_horizon);
+
+ ClassDB::bind_method(D_METHOD("set_max_speed", "max_speed"), &NavigationAgent::set_max_speed);
+ ClassDB::bind_method(D_METHOD("get_max_speed"), &NavigationAgent::get_max_speed);
+
+ ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent::set_path_max_distance);
+ ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent::get_path_max_distance);
+
+ ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent::set_target_location);
+ ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent::get_target_location);
+ ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent::get_next_location);
+ ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent::distance_to_target);
+ ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent::set_velocity);
+ ClassDB::bind_method(D_METHOD("get_nav_path"), &NavigationAgent::get_nav_path);
+ ClassDB::bind_method(D_METHOD("get_nav_path_index"), &NavigationAgent::get_nav_path_index);
+ ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent::is_target_reached);
+ ClassDB::bind_method(D_METHOD("is_target_reachable"), &NavigationAgent::is_target_reachable);
+ ClassDB::bind_method(D_METHOD("is_navigation_finished"), &NavigationAgent::is_navigation_finished);
+ ClassDB::bind_method(D_METHOD("get_final_location"), &NavigationAgent::get_final_location);
+
+ ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent::_avoidance_done);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_target_desired_distance", "get_target_desired_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01"), "set_agent_height_offset", "get_agent_height_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "neighbor_dist", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_neighbor_dist", "get_neighbor_dist");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1"), "set_max_neighbors", "get_max_neighbors");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_time_horizon", "get_time_horizon");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_speed", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_max_speed", "get_max_speed");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1"), "set_path_max_distance", "get_path_max_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_y"), "set_ignore_y", "get_ignore_y");
+
+ ADD_SIGNAL(MethodInfo("path_changed"));
+ ADD_SIGNAL(MethodInfo("target_reached"));
+ ADD_SIGNAL(MethodInfo("navigation_finished"));
+ ADD_SIGNAL(MethodInfo("velocity_computed", PropertyInfo(Variant::VECTOR3, "safe_velocity")));
+}
+
+void NavigationAgent::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_READY: {
+
+ agent_parent = Object::cast_to<Spatial>(get_parent());
+
+ NavigationServer::get_singleton()->agent_set_callback(agent, this, "_avoidance_done");
+
+ // Search the navigation node and set it
+ {
+ Navigation *nav = NULL;
+ Node *p = get_parent();
+ while (p != NULL) {
+ nav = Object::cast_to<Navigation>(p);
+ if (nav != NULL)
+ p = NULL;
+ else
+ p = p->get_parent();
+ }
+
+ set_navigation(nav);
+ }
+
+ set_physics_process_internal(true);
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ agent_parent = NULL;
+ set_navigation(NULL);
+ set_physics_process_internal(false);
+ } break;
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ if (agent_parent) {
+
+ NavigationServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin);
+ if (!target_reached) {
+ if (distance_to_target() < target_desired_distance) {
+ emit_signal("target_reached");
+ target_reached = true;
+ }
+ }
+ }
+ } break;
+ }
+}
+
+NavigationAgent::NavigationAgent() :
+ agent_parent(NULL),
+ navigation(NULL),
+ agent(RID()),
+ target_desired_distance(1.0),
+ navigation_height_offset(0.0),
+ path_max_distance(3.0),
+ velocity_submitted(false),
+ target_reached(false),
+ navigation_finished(true) {
+ agent = NavigationServer::get_singleton()->agent_create();
+ set_neighbor_dist(50.0);
+ set_max_neighbors(10);
+ set_time_horizon(5.0);
+ set_radius(1.0);
+ set_max_speed(10.0);
+ set_ignore_y(true);
+}
+
+NavigationAgent::~NavigationAgent() {
+ NavigationServer::get_singleton()->free(agent);
+ agent = RID(); // Pointless
+}
+
+void NavigationAgent::set_navigation(Navigation *p_nav) {
+ if (navigation == p_nav)
+ return; // Pointless
+
+ navigation = p_nav;
+ NavigationServer::get_singleton()->agent_set_map(agent, navigation == NULL ? RID() : navigation->get_rid());
+}
+
+void NavigationAgent::set_navigation_node(Node *p_nav) {
+ Navigation *nav = Object::cast_to<Navigation>(p_nav);
+ ERR_FAIL_COND(nav == NULL);
+ set_navigation(nav);
+}
+
+Node *NavigationAgent::get_navigation_node() const {
+ return Object::cast_to<Node>(navigation);
+}
+
+void NavigationAgent::set_target_desired_distance(real_t p_dd) {
+ target_desired_distance = p_dd;
+}
+
+void NavigationAgent::set_radius(real_t p_radius) {
+ radius = p_radius;
+ NavigationServer::get_singleton()->agent_set_radius(agent, radius);
+}
+
+void NavigationAgent::set_agent_height_offset(real_t p_hh) {
+ navigation_height_offset = p_hh;
+}
+
+void NavigationAgent::set_ignore_y(bool p_ignore_y) {
+ ignore_y = p_ignore_y;
+ NavigationServer::get_singleton()->agent_set_ignore_y(agent, ignore_y);
+}
+
+void NavigationAgent::set_neighbor_dist(real_t p_dist) {
+ neighbor_dist = p_dist;
+ NavigationServer::get_singleton()->agent_set_neighbor_dist(agent, neighbor_dist);
+}
+
+void NavigationAgent::set_max_neighbors(int p_count) {
+ max_neighbors = p_count;
+ NavigationServer::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
+}
+
+void NavigationAgent::set_time_horizon(real_t p_time) {
+ time_horizon = p_time;
+ NavigationServer::get_singleton()->agent_set_time_horizon(agent, time_horizon);
+}
+
+void NavigationAgent::set_max_speed(real_t p_max_speed) {
+ max_speed = p_max_speed;
+ NavigationServer::get_singleton()->agent_set_max_speed(agent, max_speed);
+}
+
+void NavigationAgent::set_path_max_distance(real_t p_pmd) {
+ path_max_distance = p_pmd;
+}
+
+real_t NavigationAgent::get_path_max_distance() {
+ return path_max_distance;
+}
+
+void NavigationAgent::set_target_location(Vector3 p_location) {
+ target_location = p_location;
+ navigation_path.clear();
+ target_reached = false;
+ navigation_finished = false;
+}
+
+Vector3 NavigationAgent::get_target_location() const {
+ return target_location;
+}
+
+Vector3 NavigationAgent::get_next_location() {
+ update_navigation();
+ if (navigation_path.size() == 0) {
+ ERR_FAIL_COND_V(agent_parent == NULL, Vector3());
+ return agent_parent->get_global_transform().origin;
+ } else {
+ return navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0);
+ }
+}
+
+real_t NavigationAgent::distance_to_target() const {
+ ERR_FAIL_COND_V(agent_parent == NULL, 0.0);
+ return agent_parent->get_global_transform().origin.distance_to(target_location);
+}
+
+bool NavigationAgent::is_target_reached() const {
+ return target_reached;
+}
+
+bool NavigationAgent::is_target_reachable() {
+ return target_desired_distance >= get_final_location().distance_to(target_location);
+}
+
+bool NavigationAgent::is_navigation_finished() {
+ update_navigation();
+ return navigation_finished;
+}
+
+Vector3 NavigationAgent::get_final_location() {
+ update_navigation();
+ if (navigation_path.size() == 0) {
+ return Vector3();
+ }
+ return navigation_path[navigation_path.size() - 1];
+}
+
+void NavigationAgent::set_velocity(Vector3 p_velocity) {
+ target_velocity = p_velocity;
+ NavigationServer::get_singleton()->agent_set_target_velocity(agent, target_velocity);
+ NavigationServer::get_singleton()->agent_set_velocity(agent, prev_safe_velocity);
+ velocity_submitted = true;
+}
+
+void NavigationAgent::_avoidance_done(Vector3 p_new_velocity) {
+ prev_safe_velocity = p_new_velocity;
+
+ if (!velocity_submitted) {
+ target_velocity = Vector3();
+ return;
+ }
+ velocity_submitted = false;
+
+ emit_signal("velocity_computed", p_new_velocity);
+}
+
+String NavigationAgent::get_configuration_warning() const {
+ if (!Object::cast_to<Spatial>(get_parent())) {
+ return TTR("The NavigationAgent can be used only under a spatial node.");
+ }
+
+ return String();
+}
+
+void NavigationAgent::update_navigation() {
+
+ if (agent_parent == NULL) return;
+ if (navigation == NULL) return;
+ if (update_frame_id == Engine::get_singleton()->get_physics_frames()) return;
+
+ update_frame_id = Engine::get_singleton()->get_physics_frames();
+
+ Vector3 o = agent_parent->get_global_transform().origin;
+
+ bool reload_path = false;
+
+ if (NavigationServer::get_singleton()->agent_is_map_changed(agent)) {
+ reload_path = true;
+ } else if (navigation_path.size() == 0) {
+ reload_path = true;
+ } else {
+ // Check if too far from the navigation path
+ if (nav_path_index > 0) {
+ Vector3 segment[2];
+ segment[0] = navigation_path[nav_path_index - 1];
+ segment[1] = navigation_path[nav_path_index];
+ segment[0].y -= navigation_height_offset;
+ segment[1].y -= navigation_height_offset;
+ Vector3 p = Geometry::get_closest_point_to_segment(o, segment);
+ if (o.distance_to(p) >= path_max_distance) {
+ // To faraway, reload path
+ reload_path = true;
+ }
+ }
+ }
+
+ if (reload_path) {
+ navigation_path = NavigationServer::get_singleton()->map_get_path(navigation->get_rid(), o, target_location, true);
+ navigation_finished = false;
+ nav_path_index = 0;
+ emit_signal("path_changed");
+ }
+
+ if (navigation_path.size() == 0)
+ return;
+
+ // Check if we can advance the navigation path
+ if (navigation_finished == false) {
+ // Advances to the next far away location.
+ while (o.distance_to(navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0)) < target_desired_distance) {
+ nav_path_index += 1;
+ if (nav_path_index == navigation_path.size()) {
+ nav_path_index -= 1;
+ navigation_finished = true;
+ emit_signal("navigation_finished");
+ break;
+ }
+ }
+ }
+}
diff --git a/scene/3d/navigation_agent.h b/scene/3d/navigation_agent.h
new file mode 100644
index 0000000000..8f181dafe1
--- /dev/null
+++ b/scene/3d/navigation_agent.h
@@ -0,0 +1,162 @@
+/*************************************************************************/
+/* navigation_agent.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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_AGENT_H
+#define NAVIGATION_AGENT_H
+
+#include "core/vector.h"
+#include "scene/main/node.h"
+
+class Spatial;
+class Navigation;
+
+class NavigationAgent : public Node {
+ GDCLASS(NavigationAgent, Node);
+
+ Spatial *agent_parent;
+ Navigation *navigation;
+
+ RID agent;
+
+ real_t target_desired_distance;
+ real_t radius;
+ real_t navigation_height_offset;
+ bool ignore_y;
+ real_t neighbor_dist;
+ int max_neighbors;
+ real_t time_horizon;
+ real_t max_speed;
+
+ real_t path_max_distance;
+
+ Vector3 target_location;
+ Vector<Vector3> navigation_path;
+ int nav_path_index;
+ bool velocity_submitted;
+ Vector3 prev_safe_velocity;
+ /// The submitted target velocity
+ Vector3 target_velocity;
+ bool target_reached;
+ bool navigation_finished;
+ // No initialized on purpose
+ uint32_t update_frame_id;
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ NavigationAgent();
+ virtual ~NavigationAgent();
+
+ void set_navigation(Navigation *p_nav);
+ const Navigation *get_navigation() const {
+ return navigation;
+ }
+
+ void set_navigation_node(Node *p_nav);
+ Node *get_navigation_node() const;
+
+ RID get_rid() const {
+ return agent;
+ }
+
+ void set_target_desired_distance(real_t p_dd);
+ real_t get_target_desired_distance() const {
+ return target_desired_distance;
+ }
+
+ void set_radius(real_t p_radius);
+ real_t get_radius() const {
+ return radius;
+ }
+
+ void set_agent_height_offset(real_t p_hh);
+ real_t get_agent_height_offset() const {
+ return navigation_height_offset;
+ }
+
+ void set_ignore_y(bool p_ignore_y);
+ bool get_ignore_y() const {
+ return ignore_y;
+ }
+
+ void set_neighbor_dist(real_t p_dist);
+ real_t get_neighbor_dist() const {
+ return neighbor_dist;
+ }
+
+ void set_max_neighbors(int p_count);
+ int get_max_neighbors() const {
+ return max_neighbors;
+ }
+
+ void set_time_horizon(real_t p_time);
+ real_t get_time_horizon() const {
+ return time_horizon;
+ }
+
+ void set_max_speed(real_t p_max_speed);
+ real_t get_max_speed() const {
+ return max_speed;
+ }
+
+ void set_path_max_distance(real_t p_pmd);
+ real_t get_path_max_distance();
+
+ void set_target_location(Vector3 p_location);
+ Vector3 get_target_location() const;
+
+ Vector3 get_next_location();
+
+ Vector<Vector3> get_nav_path() const {
+ return navigation_path;
+ }
+
+ int get_nav_path_index() const {
+ return nav_path_index;
+ }
+
+ real_t distance_to_target() const;
+ bool is_target_reached() const;
+ bool is_target_reachable();
+ bool is_navigation_finished();
+ Vector3 get_final_location();
+
+ void set_velocity(Vector3 p_velocity);
+ void _avoidance_done(Vector3 p_new_velocity);
+
+ virtual String get_configuration_warning() const;
+
+private:
+ void update_navigation();
+};
+
+#endif
diff --git a/scene/3d/navigation_mesh_instance.cpp b/scene/3d/navigation_mesh_instance.cpp
new file mode 100644
index 0000000000..ef59767078
--- /dev/null
+++ b/scene/3d/navigation_mesh_instance.cpp
@@ -0,0 +1,258 @@
+/*************************************************************************/
+/* navigation_mesh.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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_instance.h"
+#include "core/os/thread.h"
+#include "mesh_instance.h"
+#include "navigation.h"
+#include "servers/navigation_server.h"
+
+void NavigationMeshInstance::set_enabled(bool p_enabled) {
+
+ if (enabled == p_enabled)
+ return;
+ enabled = p_enabled;
+
+ if (!is_inside_tree())
+ return;
+
+ if (!enabled) {
+
+ NavigationServer::get_singleton()->region_set_map(region, RID());
+ } else {
+
+ if (navigation) {
+
+ NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid());
+ }
+ }
+
+ if (debug_view) {
+ MeshInstance *dm = Object::cast_to<MeshInstance>(debug_view);
+ if (is_enabled()) {
+ dm->set_material_override(get_tree()->get_debug_navigation_material());
+ } else {
+ dm->set_material_override(get_tree()->get_debug_navigation_disabled_material());
+ }
+ }
+
+ update_gizmo();
+}
+
+bool NavigationMeshInstance::is_enabled() const {
+
+ return enabled;
+}
+
+/////////////////////////////
+
+void NavigationMeshInstance::_notification(int p_what) {
+
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+
+ Spatial *c = this;
+ while (c) {
+
+ navigation = Object::cast_to<Navigation>(c);
+ if (navigation) {
+
+ if (enabled) {
+
+ NavigationServer::get_singleton()->region_set_map(region, navigation->get_rid());
+ }
+ break;
+ }
+
+ c = c->get_parent_spatial();
+ }
+
+ if (navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) {
+
+ MeshInstance *dm = memnew(MeshInstance);
+ dm->set_mesh(navmesh->get_debug_mesh());
+ if (is_enabled()) {
+ dm->set_material_override(get_tree()->get_debug_navigation_material());
+ } else {
+ dm->set_material_override(get_tree()->get_debug_navigation_disabled_material());
+ }
+ add_child(dm);
+ debug_view = dm;
+ }
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ NavigationServer::get_singleton()->region_set_transform(region, get_global_transform());
+
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+
+ if (navigation) {
+
+ NavigationServer::get_singleton()->region_set_map(region, RID());
+ }
+
+ if (debug_view) {
+ debug_view->queue_delete();
+ debug_view = NULL;
+ }
+ navigation = NULL;
+ } break;
+ }
+}
+
+void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh) {
+
+ if (p_navmesh == navmesh)
+ return;
+
+ if (navmesh.is_valid()) {
+ navmesh->remove_change_receptor(this);
+ }
+
+ navmesh = p_navmesh;
+
+ if (navmesh.is_valid()) {
+ navmesh->add_change_receptor(this);
+ }
+
+ NavigationServer::get_singleton()->region_set_navmesh(region, p_navmesh);
+
+ if (debug_view && navmesh.is_valid()) {
+ Object::cast_to<MeshInstance>(debug_view)->set_mesh(navmesh->get_debug_mesh());
+ }
+
+ emit_signal("navigation_mesh_changed");
+
+ update_gizmo();
+ update_configuration_warning();
+}
+
+Ref<NavigationMesh> NavigationMeshInstance::get_navigation_mesh() const {
+
+ return navmesh;
+}
+
+struct BakeThreadsArgs {
+ NavigationMeshInstance *nav_mesh_instance;
+};
+
+void _bake_navigation_mesh(void *p_user_data) {
+ BakeThreadsArgs *args = static_cast<BakeThreadsArgs *>(p_user_data);
+
+ if (args->nav_mesh_instance->get_navigation_mesh().is_valid()) {
+ Ref<NavigationMesh> nav_mesh = args->nav_mesh_instance->get_navigation_mesh()->duplicate();
+
+ NavigationServer::get_singleton()->region_bake_navmesh(nav_mesh, args->nav_mesh_instance);
+ args->nav_mesh_instance->call_deferred("_bake_finished", nav_mesh);
+ memdelete(args);
+ } else {
+
+ ERR_PRINT("Can't bake the navigation mesh if the `NavigationMesh` resource doesn't exist");
+ args->nav_mesh_instance->call_deferred("_bake_finished", Ref<NavigationMesh>());
+ memdelete(args);
+ }
+}
+
+void NavigationMeshInstance::bake_navigation_mesh() {
+ ERR_FAIL_COND(bake_thread != NULL);
+
+ BakeThreadsArgs *args = memnew(BakeThreadsArgs);
+ args->nav_mesh_instance = this;
+
+ bake_thread = Thread::create(_bake_navigation_mesh, args);
+ ERR_FAIL_COND(bake_thread == NULL);
+}
+
+void NavigationMeshInstance::_bake_finished(Ref<NavigationMesh> p_nav_mesh) {
+ set_navigation_mesh(p_nav_mesh);
+ bake_thread = NULL;
+}
+
+String NavigationMeshInstance::get_configuration_warning() const {
+
+ if (!is_visible_in_tree() || !is_inside_tree())
+ return String();
+
+ if (!navmesh.is_valid()) {
+ return TTR("A NavigationMesh resource must be set or created for this node to work.");
+ }
+ const Spatial *c = this;
+ while (c) {
+
+ if (Object::cast_to<Navigation>(c))
+ return String();
+
+ c = Object::cast_to<Spatial>(c->get_parent());
+ }
+
+ return TTR("NavigationMeshInstance must be a child or grandchild to a Navigation node. It only provides navigation data.");
+}
+
+void NavigationMeshInstance::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_navigation_mesh", "navmesh"), &NavigationMeshInstance::set_navigation_mesh);
+ ClassDB::bind_method(D_METHOD("get_navigation_mesh"), &NavigationMeshInstance::get_navigation_mesh);
+
+ ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationMeshInstance::set_enabled);
+ ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationMeshInstance::is_enabled);
+
+ ClassDB::bind_method(D_METHOD("bake_navigation_mesh"), &NavigationMeshInstance::bake_navigation_mesh);
+ ClassDB::bind_method(D_METHOD("_bake_finished", "nav_mesh"), &NavigationMeshInstance::_bake_finished);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
+
+ ADD_SIGNAL(MethodInfo("navigation_mesh_changed"));
+ ADD_SIGNAL(MethodInfo("bake_finished"));
+}
+
+void NavigationMeshInstance::_changed_callback(Object *p_changed, const char *p_prop) {
+ update_gizmo();
+ update_configuration_warning();
+}
+
+NavigationMeshInstance::NavigationMeshInstance() {
+
+ enabled = true;
+ set_notify_transform(true);
+ region = NavigationServer::get_singleton()->region_create();
+
+ navigation = NULL;
+ debug_view = NULL;
+ bake_thread = NULL;
+}
+
+NavigationMeshInstance::~NavigationMeshInstance() {
+ if (navmesh.is_valid())
+ navmesh->remove_change_receptor(this);
+ NavigationServer::get_singleton()->free(region);
+}
diff --git a/scene/3d/navigation_mesh_instance.h b/scene/3d/navigation_mesh_instance.h
new file mode 100644
index 0000000000..0f23c55cc7
--- /dev/null
+++ b/scene/3d/navigation_mesh_instance.h
@@ -0,0 +1,75 @@
+/*************************************************************************/
+/* navigation_mesh.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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_INSTANCE_H
+#define NAVIGATION_MESH_INSTANCE_H
+
+#include "scene/3d/spatial.h"
+#include "scene/resources/mesh.h"
+#include "scene/resources/navigation_mesh.h"
+
+class Navigation;
+
+class NavigationMeshInstance : public Spatial {
+
+ GDCLASS(NavigationMeshInstance, Spatial);
+
+ bool enabled;
+ RID region;
+ Ref<NavigationMesh> navmesh;
+
+ Navigation *navigation;
+ Node *debug_view;
+ Thread *bake_thread;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+ void _changed_callback(Object *p_changed, const char *p_prop);
+
+public:
+ void set_enabled(bool p_enabled);
+ bool is_enabled() const;
+
+ void set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh);
+ Ref<NavigationMesh> get_navigation_mesh() const;
+
+ /// Bakes the navigation mesh in a dedicated thread; once done, automatically
+ /// sets the new navigation mesh and emits a signal
+ void bake_navigation_mesh();
+ void _bake_finished(Ref<NavigationMesh> p_nav_mesh);
+
+ String get_configuration_warning() const;
+
+ NavigationMeshInstance();
+ ~NavigationMeshInstance();
+};
+
+#endif // NAVIGATION_MESH_INSTANCE_H
diff --git a/scene/3d/navigation_obstacle.cpp b/scene/3d/navigation_obstacle.cpp
new file mode 100644
index 0000000000..74142bbb68
--- /dev/null
+++ b/scene/3d/navigation_obstacle.cpp
@@ -0,0 +1,163 @@
+/*************************************************************************/
+/* navigation_obstacle.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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_obstacle.h"
+
+#include "scene/3d/collision_shape.h"
+#include "scene/3d/navigation.h"
+#include "scene/3d/physics_body.h"
+#include "servers/navigation_server.h"
+
+void NavigationObstacle::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationObstacle::set_navigation_node);
+ ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationObstacle::get_navigation_node);
+}
+
+void NavigationObstacle::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_READY: {
+
+ update_agent_shape();
+
+ // Search the navigation node and set it
+ {
+ Navigation *nav = NULL;
+ Node *p = get_parent();
+ while (p != NULL) {
+ nav = Object::cast_to<Navigation>(p);
+ if (nav != NULL)
+ p = NULL;
+ else
+ p = p->get_parent();
+ }
+
+ set_navigation(nav);
+ }
+
+ set_physics_process_internal(true);
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ set_navigation(NULL);
+ set_physics_process_internal(false);
+ } break;
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ Spatial *spatial = Object::cast_to<Spatial>(get_parent());
+ if (spatial) {
+ NavigationServer::get_singleton()->agent_set_position(agent, spatial->get_global_transform().origin);
+ }
+
+ PhysicsBody *rigid = Object::cast_to<PhysicsBody>(get_parent());
+ if (rigid) {
+
+ Vector3 v = rigid->get_linear_velocity();
+ NavigationServer::get_singleton()->agent_set_velocity(agent, v);
+ NavigationServer::get_singleton()->agent_set_target_velocity(agent, v);
+ }
+
+ } break;
+ }
+}
+
+NavigationObstacle::NavigationObstacle() :
+ navigation(NULL),
+ agent(RID()) {
+ agent = NavigationServer::get_singleton()->agent_create();
+}
+
+NavigationObstacle::~NavigationObstacle() {
+ NavigationServer::get_singleton()->free(agent);
+ agent = RID(); // Pointless
+}
+
+void NavigationObstacle::set_navigation(Navigation *p_nav) {
+ if (navigation == p_nav)
+ return; // Pointless
+
+ navigation = p_nav;
+ NavigationServer::get_singleton()->agent_set_map(agent, navigation == NULL ? RID() : navigation->get_rid());
+}
+
+void NavigationObstacle::set_navigation_node(Node *p_nav) {
+ Navigation *nav = Object::cast_to<Navigation>(p_nav);
+ ERR_FAIL_COND(nav == NULL);
+ set_navigation(nav);
+}
+
+Node *NavigationObstacle::get_navigation_node() const {
+ return Object::cast_to<Node>(navigation);
+}
+
+String NavigationObstacle::get_configuration_warning() const {
+ if (!Object::cast_to<Spatial>(get_parent())) {
+
+ return TTR("The NavigationObstacle only serves to provide collision avoidance to a spatial object.");
+ }
+
+ return String();
+}
+
+void NavigationObstacle::update_agent_shape() {
+ Node *node = get_parent();
+
+ // Estimate the radius of this physics body
+ real_t radius = 0.0;
+ for (int i(0); i < node->get_child_count(); i++) {
+ // For each collision shape
+ CollisionShape *cs = Object::cast_to<CollisionShape>(node->get_child(i));
+ if (cs) {
+ // Take the distance between the Body center to the shape center
+ real_t r = cs->get_transform().origin.length();
+ if (cs->get_shape().is_valid()) {
+ // and add the enclosing shape radius
+ r += cs->get_shape()->get_enclosing_radius();
+ }
+ Vector3 s = cs->get_global_transform().basis.get_scale();
+ r *= MAX(s.x, MAX(s.y, s.z));
+ // Takes the biggest radius
+ radius = MAX(radius, r);
+ }
+ }
+ Spatial *spa = Object::cast_to<Spatial>(node);
+ if (spa) {
+ Vector3 s = spa->get_global_transform().basis.get_scale();
+ radius *= MAX(s.x, MAX(s.y, s.z));
+ }
+
+ if (radius == 0.0)
+ radius = 1.0; // Never a 0 radius
+
+ // Initialize the Agent as an object
+ NavigationServer::get_singleton()->agent_set_neighbor_dist(agent, 0.0);
+ NavigationServer::get_singleton()->agent_set_max_neighbors(agent, 0);
+ NavigationServer::get_singleton()->agent_set_time_horizon(agent, 0.0);
+ NavigationServer::get_singleton()->agent_set_radius(agent, radius);
+ NavigationServer::get_singleton()->agent_set_max_speed(agent, 0.0);
+}
diff --git a/scene/3d/navigation_obstacle.h b/scene/3d/navigation_obstacle.h
new file mode 100644
index 0000000000..34f8153614
--- /dev/null
+++ b/scene/3d/navigation_obstacle.h
@@ -0,0 +1,71 @@
+/*************************************************************************/
+/* navigation_obstacle.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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_OBSTACLE_H
+#define NAVIGATION_OBSTACLE_H
+
+#include "scene/main/node.h"
+
+class Navigation;
+
+class NavigationObstacle : public Node {
+ GDCLASS(NavigationObstacle, Node);
+
+ Navigation *navigation;
+
+ RID agent;
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ NavigationObstacle();
+ virtual ~NavigationObstacle();
+
+ void set_navigation(Navigation *p_nav);
+ const Navigation *get_navigation() const {
+ return navigation;
+ }
+
+ void set_navigation_node(Node *p_nav);
+ Node *get_navigation_node() const;
+
+ RID get_rid() const {
+ return agent;
+ }
+
+ virtual String get_configuration_warning() const;
+
+private:
+ void update_agent_shape();
+};
+
+#endif
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index a1a221b5bb..fbfd372272 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -36,15 +36,14 @@
#include "core/method_bind_ext.gen.inc"
#include "core/object.h"
#include "core/rid.h"
+#include "scene/3d/collision_shape.h"
#include "scene/scene_string_names.h"
+#include "servers/navigation_server.h"
#ifdef TOOLS_ENABLED
#include "editor/plugins/spatial_editor_plugin.h"
#endif
-void PhysicsBody::_notification(int p_what) {
-}
-
Vector3 PhysicsBody::get_linear_velocity() const {
return Vector3();
@@ -1104,6 +1103,14 @@ Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion, bool p_inf
return Ref<KinematicCollision>();
}
+Vector3 KinematicBody::get_linear_velocity() const {
+ return linear_velocity;
+}
+
+Vector3 KinematicBody::get_angular_velocity() const {
+ return angular_velocity;
+}
+
bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only) {
Transform gt = get_global_transform();
@@ -1399,6 +1406,8 @@ void KinematicBody::_notification(int p_what) {
void KinematicBody::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_direct_state_changed"), &KinematicBody::_direct_state_changed);
+
ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only"), &KinematicBody::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false));
ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "up_direction", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true));
ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "up_direction", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody::move_and_slide_with_snap, DEFVAL(Vector3(0, 0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true));
@@ -1427,6 +1436,17 @@ void KinematicBody::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
}
+void KinematicBody::_direct_state_changed(Object *p_state) {
+#ifdef DEBUG_ENABLED
+ PhysicsDirectBodyState *state = Object::cast_to<PhysicsDirectBodyState>(p_state);
+#else
+ PhysicsDirectBodyState *state = (PhysicsDirectBodyState *)p_state; //trust it
+#endif
+
+ linear_velocity = state->get_linear_velocity();
+ angular_velocity = state->get_angular_velocity();
+}
+
KinematicBody::KinematicBody() :
PhysicsBody(PhysicsServer::BODY_MODE_KINEMATIC) {
@@ -1435,6 +1455,8 @@ KinematicBody::KinematicBody() :
on_floor = false;
on_ceiling = false;
on_wall = false;
+
+ PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed");
}
KinematicBody::~KinematicBody() {
diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h
index 6a1e803eaf..0ee877d887 100644
--- a/scene/3d/physics_body.h
+++ b/scene/3d/physics_body.h
@@ -49,7 +49,6 @@ class PhysicsBody : public CollisionObject {
protected:
static void _bind_methods();
- void _notification(int p_what);
PhysicsBody(PhysicsServer::BodyMode p_mode);
public:
@@ -296,6 +295,9 @@ public:
};
private:
+ Vector3 linear_velocity;
+ Vector3 angular_velocity;
+
uint16_t locked_axis;
float margin;
@@ -319,7 +321,12 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ virtual void _direct_state_changed(Object *p_state);
+
public:
+ virtual Vector3 get_linear_velocity() const;
+ virtual Vector3 get_angular_velocity() const;
+
bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
bool test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia);
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
deleted file mode 100644
index c7362391dc..0000000000
--- a/scene/animation/animation_tree_player.cpp
+++ /dev/null
@@ -1,1866 +0,0 @@
-/*************************************************************************/
-/* animation_tree_player.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 "animation_tree_player.h"
-#include "animation_player.h"
-
-#include "scene/scene_string_names.h"
-
-void AnimationTreePlayer::set_animation_process_mode(AnimationProcessMode p_mode) {
-
- if (animation_process_mode == p_mode)
- return;
-
- bool pr = processing;
- if (pr)
- _set_process(false);
- animation_process_mode = p_mode;
- if (pr)
- _set_process(true);
-}
-
-AnimationTreePlayer::AnimationProcessMode AnimationTreePlayer::get_animation_process_mode() const {
-
- return animation_process_mode;
-}
-
-void AnimationTreePlayer::_set_process(bool p_process, bool p_force) {
- if (processing == p_process && !p_force)
- return;
-
- switch (animation_process_mode) {
-
- case ANIMATION_PROCESS_PHYSICS: set_physics_process_internal(p_process && active); break;
- case ANIMATION_PROCESS_IDLE: set_process_internal(p_process && active); break;
- }
-
- processing = p_process;
-}
-
-bool AnimationTreePlayer::_set(const StringName &p_name, const Variant &p_value) {
-
- if (String(p_name) == "base_path") {
- set_base_path(p_value);
- return true;
- }
-
- if (String(p_name) == "master_player") {
- set_master_player(p_value);
- return true;
- }
-
- if (String(p_name) == SceneStringNames::get_singleton()->playback_active) {
- set_active(p_value);
- return true;
- }
-
- if (String(p_name) != "data")
- return false;
-
- Dictionary data = p_value;
-
- Array nodes = data.get_valid("nodes");
-
- for (int i = 0; i < nodes.size(); i++) {
-
- Dictionary node = nodes[i];
-
- StringName id = node.get_valid("id");
- Point2 pos = node.get_valid("position");
-
- NodeType nt = NODE_MAX;
- String type = node.get_valid("type");
-
- if (type == "output")
- nt = NODE_OUTPUT;
- else if (type == "animation")
- nt = NODE_ANIMATION;
- else if (type == "oneshot")
- nt = NODE_ONESHOT;
- else if (type == "mix")
- nt = NODE_MIX;
- else if (type == "blend2")
- nt = NODE_BLEND2;
- else if (type == "blend3")
- nt = NODE_BLEND3;
- else if (type == "blend4")
- nt = NODE_BLEND4;
- else if (type == "timescale")
- nt = NODE_TIMESCALE;
- else if (type == "timeseek")
- nt = NODE_TIMESEEK;
- else if (type == "transition")
- nt = NODE_TRANSITION;
-
- ERR_FAIL_COND_V(nt == NODE_MAX, false);
-
- if (nt != NODE_OUTPUT)
- add_node(nt, id);
- node_set_position(id, pos);
-
- switch (nt) {
- case NODE_OUTPUT: {
-
- } break;
- case NODE_ANIMATION: {
-
- if (node.has("from"))
- animation_node_set_master_animation(id, node.get_valid("from"));
- else
- animation_node_set_animation(id, node.get_valid("animation"));
- Array filters = node.get_valid("filter");
- for (int j = 0; j < filters.size(); j++) {
-
- animation_node_set_filter_path(id, filters[j], true);
- }
- } break;
- case NODE_ONESHOT: {
-
- oneshot_node_set_fadein_time(id, node.get_valid("fade_in"));
- oneshot_node_set_fadeout_time(id, node.get_valid("fade_out"));
- oneshot_node_set_mix_mode(id, node.get_valid("mix"));
- oneshot_node_set_autorestart(id, node.get_valid("autorestart"));
- oneshot_node_set_autorestart_delay(id, node.get_valid("autorestart_delay"));
- oneshot_node_set_autorestart_random_delay(id, node.get_valid("autorestart_random_delay"));
- Array filters = node.get_valid("filter");
- for (int j = 0; j < filters.size(); j++) {
-
- oneshot_node_set_filter_path(id, filters[j], true);
- }
-
- } break;
- case NODE_MIX: {
- mix_node_set_amount(id, node.get_valid("mix"));
- } break;
- case NODE_BLEND2: {
- blend2_node_set_amount(id, node.get_valid("blend"));
- Array filters = node.get_valid("filter");
- for (int j = 0; j < filters.size(); j++) {
-
- blend2_node_set_filter_path(id, filters[j], true);
- }
- } break;
- case NODE_BLEND3: {
- blend3_node_set_amount(id, node.get_valid("blend"));
- } break;
- case NODE_BLEND4: {
- blend4_node_set_amount(id, node.get_valid("blend"));
- } break;
- case NODE_TIMESCALE: {
- timescale_node_set_scale(id, node.get_valid("scale"));
- } break;
- case NODE_TIMESEEK: {
- } break;
- case NODE_TRANSITION: {
-
- transition_node_set_xfade_time(id, node.get_valid("xfade"));
-
- Array transitions = node.get_valid("transitions");
- transition_node_set_input_count(id, transitions.size());
-
- for (int x = 0; x < transitions.size(); x++) {
-
- Dictionary d = transitions[x];
- bool aa = d.get_valid("auto_advance");
- transition_node_set_input_auto_advance(id, x, aa);
- }
-
- } break;
- default: {
- };
- }
- }
-
- Array connections = data.get_valid("connections");
- ERR_FAIL_COND_V(connections.size() % 3, false);
-
- int cc = connections.size() / 3;
-
- for (int i = 0; i < cc; i++) {
-
- StringName src = connections[i * 3 + 0];
- StringName dst = connections[i * 3 + 1];
- int dst_in = connections[i * 3 + 2];
- connect_nodes(src, dst, dst_in);
- }
-
- set_active(data.get_valid("active"));
- set_master_player(data.get_valid("master"));
-
- return true;
-}
-
-bool AnimationTreePlayer::_get(const StringName &p_name, Variant &r_ret) const {
-
- if (String(p_name) == "base_path") {
- r_ret = base_path;
- return true;
- }
-
- if (String(p_name) == "master_player") {
- r_ret = master;
- return true;
- }
-
- if (String(p_name) == "playback/active") {
- r_ret = is_active();
- return true;
- }
-
- if (String(p_name) != "data")
- return false;
-
- Dictionary data;
-
- Array nodes;
-
- for (Map<StringName, NodeBase *>::Element *E = node_map.front(); E; E = E->next()) {
-
- NodeBase *n = node_map[E->key()];
-
- Dictionary node;
- node["id"] = E->key();
- node["position"] = n->pos;
-
- switch (n->type) {
- case NODE_OUTPUT: node["type"] = "output"; break;
- case NODE_ANIMATION: node["type"] = "animation"; break;
- case NODE_ONESHOT: node["type"] = "oneshot"; break;
- case NODE_MIX: node["type"] = "mix"; break;
- case NODE_BLEND2: node["type"] = "blend2"; break;
- case NODE_BLEND3: node["type"] = "blend3"; break;
- case NODE_BLEND4: node["type"] = "blend4"; break;
- case NODE_TIMESCALE: node["type"] = "timescale"; break;
- case NODE_TIMESEEK: node["type"] = "timeseek"; break;
- case NODE_TRANSITION: node["type"] = "transition"; break;
- default: node["type"] = ""; break;
- }
-
- switch (n->type) {
- case NODE_OUTPUT: {
-
- } break;
- case NODE_ANIMATION: {
- AnimationNode *an = static_cast<AnimationNode *>(n);
- if (master != NodePath() && an->from != "") {
- node["from"] = an->from;
- } else {
- node["animation"] = an->animation;
- }
- Array k;
- List<NodePath> keys;
- an->filter.get_key_list(&keys);
- k.resize(keys.size());
- int i = 0;
- for (List<NodePath>::Element *F = keys.front(); F; F = F->next()) {
- k[i++] = F->get();
- }
- node["filter"] = k;
- } break;
- case NODE_ONESHOT: {
- OneShotNode *osn = static_cast<OneShotNode *>(n);
- node["fade_in"] = osn->fade_in;
- node["fade_out"] = osn->fade_out;
- node["mix"] = osn->mix;
- node["autorestart"] = osn->autorestart;
- node["autorestart_delay"] = osn->autorestart_delay;
- node["autorestart_random_delay"] = osn->autorestart_random_delay;
-
- Array k;
- List<NodePath> keys;
- osn->filter.get_key_list(&keys);
- k.resize(keys.size());
- int i = 0;
- for (List<NodePath>::Element *F = keys.front(); F; F = F->next()) {
- k[i++] = F->get();
- }
- node["filter"] = k;
-
- } break;
- case NODE_MIX: {
- MixNode *mn = static_cast<MixNode *>(n);
- node["mix"] = mn->amount;
- } break;
- case NODE_BLEND2: {
- Blend2Node *bn = static_cast<Blend2Node *>(n);
- node["blend"] = bn->value;
- Array k;
- List<NodePath> keys;
- bn->filter.get_key_list(&keys);
- k.resize(keys.size());
- int i = 0;
- for (List<NodePath>::Element *F = keys.front(); F; F = F->next()) {
- k[i++] = F->get();
- }
- node["filter"] = k;
-
- } break;
- case NODE_BLEND3: {
- Blend3Node *bn = static_cast<Blend3Node *>(n);
- node["blend"] = bn->value;
- } break;
- case NODE_BLEND4: {
- Blend4Node *bn = static_cast<Blend4Node *>(n);
- node["blend"] = bn->value;
-
- } break;
- case NODE_TIMESCALE: {
- TimeScaleNode *tsn = static_cast<TimeScaleNode *>(n);
- node["scale"] = tsn->scale;
- } break;
- case NODE_TIMESEEK: {
- } break;
- case NODE_TRANSITION: {
-
- TransitionNode *tn = static_cast<TransitionNode *>(n);
- node["xfade"] = tn->xfade;
- Array transitions;
-
- for (int i = 0; i < tn->input_data.size(); i++) {
-
- Dictionary d;
- d["auto_advance"] = tn->input_data[i].auto_advance;
- transitions.push_back(d);
- }
-
- node["transitions"] = transitions;
-
- } break;
- default: {
- };
- }
-
- nodes.push_back(node);
- }
-
- data["nodes"] = nodes;
- //connectiosn
-
- List<Connection> connections;
- get_connection_list(&connections);
- Array connections_arr;
- connections_arr.resize(connections.size() * 3);
-
- int idx = 0;
- for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
-
- connections_arr.set(idx + 0, E->get().src_node);
- connections_arr.set(idx + 1, E->get().dst_node);
- connections_arr.set(idx + 2, E->get().dst_input);
-
- idx += 3;
- }
-
- data["connections"] = connections_arr;
- data["active"] = active;
- data["master"] = master;
-
- r_ret = data;
-
- return true;
-}
-
-void AnimationTreePlayer::_get_property_list(List<PropertyInfo> *p_list) const {
-
- p_list->push_back(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_NETWORK));
-}
-
-void AnimationTreePlayer::advance(float p_time) {
-
- _process_animation(p_time);
-}
-
-void AnimationTreePlayer::_notification(int p_what) {
-
- switch (p_what) {
-
- case NOTIFICATION_ENTER_TREE: {
-
- WARN_DEPRECATED_MSG("AnimationTreePlayer has been deprecated. Use AnimationTree instead.");
-
- if (!processing) {
- //make sure that a previous process state was not saved
- //only process if "processing" is set
- set_physics_process_internal(false);
- set_process_internal(false);
- }
- } break;
- case NOTIFICATION_READY: {
-
- dirty_caches = true;
- if (master != NodePath()) {
- _update_sources();
- }
- } break;
- case NOTIFICATION_INTERNAL_PROCESS: {
-
- if (animation_process_mode == ANIMATION_PROCESS_PHYSICS)
- break;
-
- if (processing)
- _process_animation(get_process_delta_time());
- } break;
- case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
-
- if (animation_process_mode == ANIMATION_PROCESS_IDLE)
- break;
-
- if (processing)
- _process_animation(get_physics_process_delta_time());
- } break;
- }
-}
-
-void AnimationTreePlayer::_compute_weights(float *p_fallback_weight, HashMap<NodePath, float> *p_weights, float p_coeff, const HashMap<NodePath, bool> *p_filter, float p_filtered_coeff) {
-
- if (p_filter != NULL) {
-
- List<NodePath> key_list;
- p_filter->get_key_list(&key_list);
-
- for (List<NodePath>::Element *E = key_list.front(); E; E = E->next()) {
-
- if ((*p_filter)[E->get()]) {
-
- if (p_weights->has(E->get())) {
- (*p_weights)[E->get()] *= p_filtered_coeff;
- } else {
- p_weights->set(E->get(), *p_fallback_weight * p_filtered_coeff);
- }
-
- } else if (p_weights->has(E->get())) {
- (*p_weights)[E->get()] *= p_coeff;
- }
- }
- }
-
- List<NodePath> key_list;
- p_weights->get_key_list(&key_list);
-
- for (List<NodePath>::Element *E = key_list.front(); E; E = E->next()) {
- if (p_filter == NULL || !p_filter->has(E->get())) {
- (*p_weights)[E->get()] *= p_coeff;
- }
- }
-
- *p_fallback_weight *= p_coeff;
-}
-
-float AnimationTreePlayer::_process_node(const StringName &p_node, AnimationNode **r_prev_anim, float p_time, bool p_seek, float p_fallback_weight, HashMap<NodePath, float> *p_weights) {
-
- ERR_FAIL_COND_V(!node_map.has(p_node), 0);
- NodeBase *nb = node_map[p_node];
-
- //transform to seconds...
-
- switch (nb->type) {
-
- case NODE_OUTPUT: {
-
- NodeOut *on = static_cast<NodeOut *>(nb);
- HashMap<NodePath, float> weights;
-
- return _process_node(on->inputs[0].node, r_prev_anim, p_time, p_seek, p_fallback_weight, &weights);
-
- } break;
- case NODE_ANIMATION: {
-
- AnimationNode *an = static_cast<AnimationNode *>(nb);
-
- float rem = 0;
- if (!an->animation.is_null()) {
-
- //float pos = an->time;
- //float delta = p_time;
-
- //const Animation *a = an->animation.operator->();
-
- if (p_seek) {
- an->time = p_time;
- an->step = 0;
- } else {
- an->time = MAX(0, an->time + p_time);
- an->step = p_time;
- }
-
- float anim_size = an->animation->get_length();
-
- if (an->animation->has_loop()) {
-
- if (anim_size)
- an->time = Math::fposmod(an->time, anim_size);
-
- } else if (an->time > anim_size) {
-
- an->time = anim_size;
- }
-
- an->skip = true;
-
- for (List<AnimationNode::TrackRef>::Element *E = an->tref.front(); E; E = E->next()) {
- NodePath track_path = an->animation->track_get_path(E->get().local_track);
- if (an->filter.has(track_path) && an->filter[track_path]) {
- E->get().weight = 0;
- } else {
- if (p_weights->has(track_path)) {
- float weight = (*p_weights)[track_path];
- E->get().weight = weight;
- } else {
- E->get().weight = p_fallback_weight;
- }
- }
- if (E->get().weight > CMP_EPSILON)
- an->skip = false;
- }
-
- rem = anim_size - an->time;
- }
-
- if (!(*r_prev_anim))
- active_list = an;
- else
- (*r_prev_anim)->next = an;
-
- an->next = NULL;
- *r_prev_anim = an;
-
- return rem;
-
- } break;
- case NODE_ONESHOT: {
-
- OneShotNode *osn = static_cast<OneShotNode *>(nb);
-
- if (!osn->active) {
- //make it as if this node doesn't exist, pass input 0 by.
- return _process_node(osn->inputs[0].node, r_prev_anim, p_time, p_seek, p_fallback_weight, p_weights);
- }
-
- bool os_seek = p_seek;
-
- if (p_seek)
- osn->time = p_time;
- if (osn->start) {
- osn->time = 0;
- os_seek = true;
- }
-
- float blend;
-
- if (osn->time < osn->fade_in) {
-
- if (osn->fade_in > 0)
- blend = osn->time / osn->fade_in;
- else
- blend = 0; //wtf
-
- } else if (!osn->start && osn->remaining < osn->fade_out) {
-
- if (osn->fade_out)
- blend = (osn->remaining / osn->fade_out);
- else
- blend = 1.0;
- } else
- blend = 1.0;
-
- float main_rem;
- float os_rem;
-
- HashMap<NodePath, float> os_weights(*p_weights);
- float os_fallback_weight = p_fallback_weight;
- _compute_weights(&p_fallback_weight, p_weights, osn->mix ? 1.0 : 1.0 - blend, &osn->filter, 1.0);
- _compute_weights(&os_fallback_weight, &os_weights, blend, &osn->filter, 0.0);
-
- main_rem = _process_node(osn->inputs[0].node, r_prev_anim, p_time, p_seek, p_fallback_weight, p_weights);
- os_rem = _process_node(osn->inputs[1].node, r_prev_anim, p_time, os_seek, os_fallback_weight, &os_weights);
-
- if (osn->start) {
- osn->remaining = os_rem;
- osn->start = false;
- }
-
- if (!p_seek) {
- osn->time += p_time;
- osn->remaining = os_rem;
- if (osn->remaining <= 0)
- osn->active = false;
- }
-
- return MAX(main_rem, osn->remaining);
- } break;
- case NODE_MIX: {
- MixNode *mn = static_cast<MixNode *>(nb);
-
- HashMap<NodePath, float> mn_weights(*p_weights);
- float mn_fallback_weight = p_fallback_weight;
- _compute_weights(&mn_fallback_weight, &mn_weights, mn->amount);
- float rem = _process_node(mn->inputs[0].node, r_prev_anim, p_time, p_seek, p_fallback_weight, p_weights);
- _process_node(mn->inputs[1].node, r_prev_anim, p_time, p_seek, mn_fallback_weight, &mn_weights);
- return rem;
-
- } break;
- case NODE_BLEND2: {
-
- Blend2Node *bn = static_cast<Blend2Node *>(nb);
-
- HashMap<NodePath, float> bn_weights(*p_weights);
- float bn_fallback_weight = p_fallback_weight;
- _compute_weights(&p_fallback_weight, p_weights, 1.0 - bn->value, &bn->filter, 1.0);
- _compute_weights(&bn_fallback_weight, &bn_weights, bn->value, &bn->filter, 0.0);
- float rem = _process_node(bn->inputs[0].node, r_prev_anim, p_time, p_seek, p_fallback_weight, p_weights);
- _process_node(bn->inputs[1].node, r_prev_anim, p_time, p_seek, bn_fallback_weight, &bn_weights);
-
- return rem;
- } break;
- case NODE_BLEND3: {
- Blend3Node *bn = static_cast<Blend3Node *>(nb);
-
- float rem;
- float blend, lower_blend, upper_blend;
- if (bn->value < 0) {
- lower_blend = -bn->value;
- blend = 1.0 - lower_blend;
- upper_blend = 0;
- } else {
- lower_blend = 0;
- blend = 1.0 - bn->value;
- upper_blend = bn->value;
- }
-
- HashMap<NodePath, float> upper_weights(*p_weights);
- float upper_fallback_weight = p_fallback_weight;
- HashMap<NodePath, float> lower_weights(*p_weights);
- float lower_fallback_weight = p_fallback_weight;
- _compute_weights(&upper_fallback_weight, &upper_weights, upper_blend);
- _compute_weights(&p_fallback_weight, p_weights, blend);
- _compute_weights(&lower_fallback_weight, &lower_weights, lower_blend);
-
- rem = _process_node(bn->inputs[1].node, r_prev_anim, p_time, p_seek, p_fallback_weight, p_weights);
- _process_node(bn->inputs[0].node, r_prev_anim, p_time, p_seek, lower_fallback_weight, &lower_weights);
- _process_node(bn->inputs[2].node, r_prev_anim, p_time, p_seek, upper_fallback_weight, &upper_weights);
-
- return rem;
- } break;
- case NODE_BLEND4: {
- Blend4Node *bn = static_cast<Blend4Node *>(nb);
-
- HashMap<NodePath, float> weights1(*p_weights);
- float fallback_weight1 = p_fallback_weight;
- HashMap<NodePath, float> weights2(*p_weights);
- float fallback_weight2 = p_fallback_weight;
- HashMap<NodePath, float> weights3(*p_weights);
- float fallback_weight3 = p_fallback_weight;
-
- _compute_weights(&p_fallback_weight, p_weights, 1.0 - bn->value.x);
- _compute_weights(&fallback_weight1, &weights1, bn->value.x);
- _compute_weights(&fallback_weight2, &weights2, 1.0 - bn->value.y);
- _compute_weights(&fallback_weight3, &weights3, bn->value.y);
-
- float rem = _process_node(bn->inputs[0].node, r_prev_anim, p_time, p_seek, p_fallback_weight, p_weights);
- _process_node(bn->inputs[1].node, r_prev_anim, p_time, p_seek, fallback_weight1, &weights1);
- float rem2 = _process_node(bn->inputs[2].node, r_prev_anim, p_time, p_seek, fallback_weight2, &weights2);
- _process_node(bn->inputs[3].node, r_prev_anim, p_time, p_seek, fallback_weight3, &weights3);
-
- return MAX(rem, rem2);
-
- } break;
- case NODE_TIMESCALE: {
- TimeScaleNode *tsn = static_cast<TimeScaleNode *>(nb);
- float rem;
- if (p_seek)
- rem = _process_node(tsn->inputs[0].node, r_prev_anim, p_time, true, p_fallback_weight, p_weights);
- else
- rem = _process_node(tsn->inputs[0].node, r_prev_anim, p_time * tsn->scale, false, p_fallback_weight, p_weights);
- if (tsn->scale == 0)
- return Math_INF;
- else
- return rem / tsn->scale;
-
- } break;
- case NODE_TIMESEEK: {
-
- TimeSeekNode *tsn = static_cast<TimeSeekNode *>(nb);
- if (tsn->seek_pos >= 0 && !p_seek) {
-
- p_time = tsn->seek_pos;
- p_seek = true;
- }
- tsn->seek_pos = -1;
-
- return _process_node(tsn->inputs[0].node, r_prev_anim, p_time, p_seek, p_fallback_weight, p_weights);
-
- } break;
- case NODE_TRANSITION: {
-
- TransitionNode *tn = static_cast<TransitionNode *>(nb);
- HashMap<NodePath, float> prev_weights(*p_weights);
- float prev_fallback_weight = p_fallback_weight;
-
- if (tn->prev < 0) { // process current animation, check for transition
-
- float rem = _process_node(tn->inputs[tn->current].node, r_prev_anim, p_time, p_seek, p_fallback_weight, p_weights);
- if (p_seek)
- tn->time = p_time;
- else
- tn->time += p_time;
-
- if (tn->input_data[tn->current].auto_advance && rem <= tn->xfade) {
-
- tn->set_current((tn->current + 1) % tn->inputs.size());
- }
-
- return rem;
- } else { // cross-fading from tn->prev to tn->current
-
- float blend = tn->xfade ? (tn->prev_xfading / tn->xfade) : 1;
-
- float rem;
-
- _compute_weights(&p_fallback_weight, p_weights, 1.0 - blend);
- _compute_weights(&prev_fallback_weight, &prev_weights, blend);
-
- if (!p_seek && tn->switched) { //just switched, seek to start of current
-
- rem = _process_node(tn->inputs[tn->current].node, r_prev_anim, 0, true, p_fallback_weight, p_weights);
- } else {
-
- rem = _process_node(tn->inputs[tn->current].node, r_prev_anim, p_time, p_seek, p_fallback_weight, p_weights);
- }
-
- tn->switched = false;
-
- if (p_seek) { // don't seek prev animation
- _process_node(tn->inputs[tn->prev].node, r_prev_anim, 0, false, prev_fallback_weight, &prev_weights);
- tn->time = p_time;
- } else {
- _process_node(tn->inputs[tn->prev].node, r_prev_anim, p_time, false, prev_fallback_weight, &prev_weights);
- tn->time += p_time;
- tn->prev_xfading -= p_time;
- if (tn->prev_xfading < 0) {
-
- tn->prev = -1;
- }
- }
-
- return rem;
- }
-
- } break;
- default: {
- }
- }
-
- return 0;
-}
-
-void AnimationTreePlayer::_process_animation(float p_delta) {
-
- if (last_error != CONNECT_OK)
- return;
-
- if (dirty_caches)
- _recompute_caches();
-
- active_list = NULL;
- AnimationNode *prev = NULL;
-
- if (reset_request) {
-
- _process_node(out_name, &prev, 0, true);
- reset_request = false;
- } else
- _process_node(out_name, &prev, p_delta);
-
- if (dirty_caches) {
- //some animation changed.. ignore this pass
- return;
- }
-
- //update the tracks..
-
- /* STEP 1 CLEAR TRACKS */
-
- for (TrackMap::Element *E = track_map.front(); E; E = E->next()) {
-
- Track &t = E->get();
-
- t.loc.zero();
- t.rot = Quat();
- t.scale.x = 0;
- t.scale.y = 0;
- t.scale.z = 0;
-
- t.value = t.object->get_indexed(t.subpath);
- t.value.zero();
-
- t.skip = false;
- }
-
- /* STEP 2 PROCESS ANIMATIONS */
-
- AnimationNode *anim_list = active_list;
- Quat empty_rot;
-
- while (anim_list) {
-
- if (!anim_list->animation.is_null() && !anim_list->skip) {
- //check if animation is meaningful
- Animation *a = anim_list->animation.operator->();
-
- for (List<AnimationNode::TrackRef>::Element *E = anim_list->tref.front(); E; E = E->next()) {
-
- AnimationNode::TrackRef &tr = E->get();
- if (tr.track == NULL || tr.local_track < 0 || tr.weight < CMP_EPSILON || !a->track_is_enabled(tr.local_track))
- continue;
-
- switch (a->track_get_type(tr.local_track)) {
- case Animation::TYPE_TRANSFORM: { ///< Transform a node or a bone.
-
- Vector3 loc;
- Quat rot;
- Vector3 scale;
- a->transform_track_interpolate(tr.local_track, anim_list->time, &loc, &rot, &scale);
-
- tr.track->loc += loc * tr.weight;
-
- scale.x -= 1.0;
- scale.y -= 1.0;
- scale.z -= 1.0;
- tr.track->scale += scale * tr.weight;
-
- tr.track->rot = tr.track->rot * empty_rot.slerp(rot, tr.weight);
-
- } break;
- case Animation::TYPE_VALUE: { ///< Set a value in a property, can be interpolated.
-
- if (a->value_track_get_update_mode(tr.local_track) == Animation::UPDATE_CONTINUOUS) {
- Variant value = a->value_track_interpolate(tr.local_track, anim_list->time);
- Variant::blend(tr.track->value, value, tr.weight, tr.track->value);
- } else {
- int index = a->track_find_key(tr.local_track, anim_list->time);
- tr.track->value = a->track_get_key_value(tr.local_track, index);
- }
- } break;
- case Animation::TYPE_METHOD: { ///< Call any method on a specific node.
-
- List<int> indices;
- a->method_track_get_key_indices(tr.local_track, anim_list->time, anim_list->step, &indices);
- for (List<int>::Element *F = indices.front(); F; F = F->next()) {
-
- StringName method = a->method_track_get_name(tr.local_track, F->get());
- Vector<Variant> args = a->method_track_get_params(tr.local_track, F->get());
- args.resize(VARIANT_ARG_MAX);
- tr.track->object->call(method, args[0], args[1], args[2], args[3], args[4]);
- }
- } break;
- default: {
- }
- }
- }
- }
-
- anim_list = anim_list->next;
- }
-
- /* STEP 3 APPLY TRACKS */
-
- for (TrackMap::Element *E = track_map.front(); E; E = E->next()) {
-
- Track &t = E->get();
-
- if (t.skip || !t.object)
- continue;
-
- if (t.subpath.size()) { // value track
- t.object->set_indexed(t.subpath, t.value);
- continue;
- }
-
- Transform xform;
- xform.origin = t.loc;
-
- t.scale.x += 1.0;
- t.scale.y += 1.0;
- t.scale.z += 1.0;
- xform.basis.set_quat_scale(t.rot, t.scale);
-
- if (t.bone_idx >= 0) {
- if (t.skeleton)
- t.skeleton->set_bone_pose(t.bone_idx, xform);
-
- } else if (t.spatial) {
-
- t.spatial->set_transform(xform);
- }
- }
-}
-
-void AnimationTreePlayer::add_node(NodeType p_type, const StringName &p_node) {
-
- ERR_FAIL_COND(p_type == NODE_OUTPUT);
- ERR_FAIL_COND(node_map.has(p_node));
-
- NodeBase *n = NULL;
-
- switch (p_type) {
-
- case NODE_ANIMATION: {
-
- n = memnew(AnimationNode);
- } break;
- case NODE_ONESHOT: {
-
- n = memnew(OneShotNode);
-
- } break;
- case NODE_MIX: {
- n = memnew(MixNode);
-
- } break;
- case NODE_BLEND2: {
- n = memnew(Blend2Node);
-
- } break;
- case NODE_BLEND3: {
- n = memnew(Blend3Node);
-
- } break;
- case NODE_BLEND4: {
- n = memnew(Blend4Node);
-
- } break;
- case NODE_TIMESCALE: {
- n = memnew(TimeScaleNode);
-
- } break;
- case NODE_TIMESEEK: {
- n = memnew(TimeSeekNode);
-
- } break;
- case NODE_TRANSITION: {
- n = memnew(TransitionNode);
-
- } break;
- default: {
- }
- }
-
- //n->name+=" "+itos(p_node);
- node_map[p_node] = n;
-}
-
-StringName AnimationTreePlayer::node_get_input_source(const StringName &p_node, int p_input) const {
-
- ERR_FAIL_COND_V(!node_map.has(p_node), StringName());
- ERR_FAIL_INDEX_V(p_input, node_map[p_node]->inputs.size(), StringName());
- return node_map[p_node]->inputs[p_input].node;
-}
-
-int AnimationTreePlayer::node_get_input_count(const StringName &p_node) const {
-
- ERR_FAIL_COND_V(!node_map.has(p_node), -1);
- return node_map[p_node]->inputs.size();
-}
-#define GET_NODE(m_type, m_cast) \
- ERR_FAIL_COND(!node_map.has(p_node)); \
- ERR_FAIL_COND_MSG(node_map[p_node]->type != m_type, "Invalid parameter for node type."); \
- m_cast *n = static_cast<m_cast *>(node_map[p_node]);
-
-void AnimationTreePlayer::animation_node_set_animation(const StringName &p_node, const Ref<Animation> &p_animation) {
-
- GET_NODE(NODE_ANIMATION, AnimationNode);
- n->animation = p_animation;
- dirty_caches = true;
-}
-
-void AnimationTreePlayer::animation_node_set_master_animation(const StringName &p_node, const String &p_master_animation) {
-
- GET_NODE(NODE_ANIMATION, AnimationNode);
- n->from = p_master_animation;
- dirty_caches = true;
- if (master != NodePath())
- _update_sources();
-}
-
-void AnimationTreePlayer::animation_node_set_filter_path(const StringName &p_node, const NodePath &p_track_path, bool p_filter) {
-
- GET_NODE(NODE_ANIMATION, AnimationNode);
-
- if (p_filter)
- n->filter[p_track_path] = true;
- else
- n->filter.erase(p_track_path);
-}
-
-void AnimationTreePlayer::animation_node_set_get_filtered_paths(const StringName &p_node, List<NodePath> *r_paths) const {
-
- GET_NODE(NODE_ANIMATION, AnimationNode);
-
- n->filter.get_key_list(r_paths);
-}
-
-void AnimationTreePlayer::oneshot_node_set_fadein_time(const StringName &p_node, float p_time) {
-
- GET_NODE(NODE_ONESHOT, OneShotNode);
- n->fade_in = p_time;
-}
-
-void AnimationTreePlayer::oneshot_node_set_fadeout_time(const StringName &p_node, float p_time) {
-
- GET_NODE(NODE_ONESHOT, OneShotNode);
- n->fade_out = p_time;
-}
-
-void AnimationTreePlayer::oneshot_node_set_mix_mode(const StringName &p_node, bool p_mix) {
-
- GET_NODE(NODE_ONESHOT, OneShotNode);
- n->mix = p_mix;
-}
-
-void AnimationTreePlayer::oneshot_node_set_autorestart(const StringName &p_node, bool p_active) {
-
- GET_NODE(NODE_ONESHOT, OneShotNode);
- n->autorestart = p_active;
-}
-
-void AnimationTreePlayer::oneshot_node_set_autorestart_delay(const StringName &p_node, float p_time) {
-
- GET_NODE(NODE_ONESHOT, OneShotNode);
- n->autorestart_delay = p_time;
-}
-void AnimationTreePlayer::oneshot_node_set_autorestart_random_delay(const StringName &p_node, float p_time) {
-
- GET_NODE(NODE_ONESHOT, OneShotNode);
- n->autorestart_random_delay = p_time;
-}
-
-void AnimationTreePlayer::oneshot_node_start(const StringName &p_node) {
-
- GET_NODE(NODE_ONESHOT, OneShotNode);
- n->active = true;
- n->start = true;
-}
-
-void AnimationTreePlayer::oneshot_node_stop(const StringName &p_node) {
-
- GET_NODE(NODE_ONESHOT, OneShotNode);
- n->active = false;
-}
-
-void AnimationTreePlayer::oneshot_node_set_filter_path(const StringName &p_node, const NodePath &p_filter, bool p_enable) {
-
- GET_NODE(NODE_ONESHOT, OneShotNode);
-
- if (p_enable)
- n->filter[p_filter] = true;
- else
- n->filter.erase(p_filter);
-}
-
-void AnimationTreePlayer::oneshot_node_set_get_filtered_paths(const StringName &p_node, List<NodePath> *r_paths) const {
-
- GET_NODE(NODE_ONESHOT, OneShotNode);
-
- n->filter.get_key_list(r_paths);
-}
-
-void AnimationTreePlayer::mix_node_set_amount(const StringName &p_node, float p_amount) {
-
- GET_NODE(NODE_MIX, MixNode);
- n->amount = p_amount;
-}
-
-void AnimationTreePlayer::blend2_node_set_amount(const StringName &p_node, float p_amount) {
-
- GET_NODE(NODE_BLEND2, Blend2Node);
- n->value = p_amount;
-}
-
-void AnimationTreePlayer::blend2_node_set_filter_path(const StringName &p_node, const NodePath &p_filter, bool p_enable) {
-
- GET_NODE(NODE_BLEND2, Blend2Node);
-
- if (p_enable)
- n->filter[p_filter] = true;
- else
- n->filter.erase(p_filter);
-}
-
-void AnimationTreePlayer::blend2_node_set_get_filtered_paths(const StringName &p_node, List<NodePath> *r_paths) const {
-
- GET_NODE(NODE_BLEND2, Blend2Node);
-
- n->filter.get_key_list(r_paths);
-}
-
-void AnimationTreePlayer::blend3_node_set_amount(const StringName &p_node, float p_amount) {
-
- GET_NODE(NODE_BLEND3, Blend3Node);
- n->value = p_amount;
-}
-void AnimationTreePlayer::blend4_node_set_amount(const StringName &p_node, const Vector2 &p_amount) {
-
- GET_NODE(NODE_BLEND4, Blend4Node);
- n->value = p_amount;
-}
-void AnimationTreePlayer::timescale_node_set_scale(const StringName &p_node, float p_scale) {
-
- GET_NODE(NODE_TIMESCALE, TimeScaleNode);
- n->scale = p_scale;
-}
-void AnimationTreePlayer::timeseek_node_seek(const StringName &p_node, float p_pos) {
-
- GET_NODE(NODE_TIMESEEK, TimeSeekNode);
- n->seek_pos = p_pos;
-}
-void AnimationTreePlayer::transition_node_set_input_count(const StringName &p_node, int p_inputs) {
-
- GET_NODE(NODE_TRANSITION, TransitionNode);
- ERR_FAIL_COND(p_inputs < 1);
-
- n->inputs.resize(p_inputs);
- n->input_data.resize(p_inputs);
-
- _clear_cycle_test();
-
- last_error = _cycle_test(out_name);
-}
-void AnimationTreePlayer::transition_node_set_input_auto_advance(const StringName &p_node, int p_input, bool p_auto_advance) {
-
- GET_NODE(NODE_TRANSITION, TransitionNode);
- ERR_FAIL_INDEX(p_input, n->input_data.size());
-
- n->input_data.write[p_input].auto_advance = p_auto_advance;
-}
-void AnimationTreePlayer::transition_node_set_xfade_time(const StringName &p_node, float p_time) {
-
- GET_NODE(NODE_TRANSITION, TransitionNode);
- n->xfade = p_time;
-}
-
-void AnimationTreePlayer::TransitionNode::set_current(int p_current) {
-
- ERR_FAIL_INDEX(p_current, inputs.size());
-
- if (current == p_current)
- return;
-
- prev = current;
- prev_xfading = xfade;
- prev_time = time;
- time = 0;
- current = p_current;
- switched = true;
-}
-
-void AnimationTreePlayer::transition_node_set_current(const StringName &p_node, int p_current) {
-
- GET_NODE(NODE_TRANSITION, TransitionNode);
- n->set_current(p_current);
-}
-
-void AnimationTreePlayer::node_set_position(const StringName &p_node, const Vector2 &p_pos) {
-
- ERR_FAIL_COND(!node_map.has(p_node));
- node_map[p_node]->pos = p_pos;
-}
-
-AnimationTreePlayer::NodeType AnimationTreePlayer::node_get_type(const StringName &p_node) const {
-
- ERR_FAIL_COND_V(!node_map.has(p_node), NODE_OUTPUT);
- return node_map[p_node]->type;
-}
-Point2 AnimationTreePlayer::node_get_position(const StringName &p_node) const {
-
- ERR_FAIL_COND_V(!node_map.has(p_node), Point2());
- return node_map[p_node]->pos;
-}
-
-#define GET_NODE_V(m_type, m_cast, m_ret) \
- ERR_FAIL_COND_V(!node_map.has(p_node), m_ret); \
- ERR_FAIL_COND_V_MSG(node_map[p_node]->type != m_type, m_ret, "Invalid parameter for node type."); \
- m_cast *n = static_cast<m_cast *>(node_map[p_node]);
-
-Ref<Animation> AnimationTreePlayer::animation_node_get_animation(const StringName &p_node) const {
-
- GET_NODE_V(NODE_ANIMATION, AnimationNode, Ref<Animation>());
- return n->animation;
-}
-
-String AnimationTreePlayer::animation_node_get_master_animation(const StringName &p_node) const {
-
- GET_NODE_V(NODE_ANIMATION, AnimationNode, String());
- return n->from;
-}
-
-float AnimationTreePlayer::animation_node_get_position(const StringName &p_node) const {
-
- GET_NODE_V(NODE_ANIMATION, AnimationNode, 0);
- return n->time;
-}
-
-bool AnimationTreePlayer::animation_node_is_path_filtered(const StringName &p_node, const NodePath &p_path) const {
-
- GET_NODE_V(NODE_ANIMATION, AnimationNode, 0);
- return n->filter.has(p_path);
-}
-
-float AnimationTreePlayer::oneshot_node_get_fadein_time(const StringName &p_node) const {
-
- GET_NODE_V(NODE_ONESHOT, OneShotNode, 0);
- return n->fade_in;
-}
-
-float AnimationTreePlayer::oneshot_node_get_fadeout_time(const StringName &p_node) const {
-
- GET_NODE_V(NODE_ONESHOT, OneShotNode, 0);
- return n->fade_out;
-}
-
-bool AnimationTreePlayer::oneshot_node_get_mix_mode(const StringName &p_node) const {
-
- GET_NODE_V(NODE_ONESHOT, OneShotNode, 0);
- return n->mix;
-}
-bool AnimationTreePlayer::oneshot_node_has_autorestart(const StringName &p_node) const {
-
- GET_NODE_V(NODE_ONESHOT, OneShotNode, 0);
- return n->autorestart;
-}
-float AnimationTreePlayer::oneshot_node_get_autorestart_delay(const StringName &p_node) const {
-
- GET_NODE_V(NODE_ONESHOT, OneShotNode, 0);
- return n->autorestart_delay;
-}
-float AnimationTreePlayer::oneshot_node_get_autorestart_random_delay(const StringName &p_node) const {
-
- GET_NODE_V(NODE_ONESHOT, OneShotNode, 0);
- return n->autorestart_random_delay;
-}
-
-bool AnimationTreePlayer::oneshot_node_is_active(const StringName &p_node) const {
-
- GET_NODE_V(NODE_ONESHOT, OneShotNode, 0);
- return n->active;
-}
-
-bool AnimationTreePlayer::oneshot_node_is_path_filtered(const StringName &p_node, const NodePath &p_path) const {
-
- GET_NODE_V(NODE_ONESHOT, OneShotNode, 0);
- return n->filter.has(p_path);
-}
-
-float AnimationTreePlayer::mix_node_get_amount(const StringName &p_node) const {
-
- GET_NODE_V(NODE_MIX, MixNode, 0);
- return n->amount;
-}
-float AnimationTreePlayer::blend2_node_get_amount(const StringName &p_node) const {
-
- GET_NODE_V(NODE_BLEND2, Blend2Node, 0);
- return n->value;
-}
-
-bool AnimationTreePlayer::blend2_node_is_path_filtered(const StringName &p_node, const NodePath &p_path) const {
-
- GET_NODE_V(NODE_BLEND2, Blend2Node, 0);
- return n->filter.has(p_path);
-}
-
-float AnimationTreePlayer::blend3_node_get_amount(const StringName &p_node) const {
-
- GET_NODE_V(NODE_BLEND3, Blend3Node, 0);
- return n->value;
-}
-Vector2 AnimationTreePlayer::blend4_node_get_amount(const StringName &p_node) const {
-
- GET_NODE_V(NODE_BLEND4, Blend4Node, Vector2());
- return n->value;
-}
-
-float AnimationTreePlayer::timescale_node_get_scale(const StringName &p_node) const {
-
- GET_NODE_V(NODE_TIMESCALE, TimeScaleNode, 0);
- return n->scale;
-}
-
-void AnimationTreePlayer::transition_node_delete_input(const StringName &p_node, int p_input) {
-
- GET_NODE(NODE_TRANSITION, TransitionNode);
- ERR_FAIL_INDEX(p_input, n->inputs.size());
-
- if (n->inputs.size() <= 1)
- return;
-
- n->inputs.remove(p_input);
- n->input_data.remove(p_input);
- last_error = _cycle_test(out_name);
-}
-
-int AnimationTreePlayer::transition_node_get_input_count(const StringName &p_node) const {
-
- GET_NODE_V(NODE_TRANSITION, TransitionNode, 0);
- return n->inputs.size();
-}
-
-bool AnimationTreePlayer::transition_node_has_input_auto_advance(const StringName &p_node, int p_input) const {
-
- GET_NODE_V(NODE_TRANSITION, TransitionNode, false);
- ERR_FAIL_INDEX_V(p_input, n->inputs.size(), false);
- return n->input_data[p_input].auto_advance;
-}
-float AnimationTreePlayer::transition_node_get_xfade_time(const StringName &p_node) const {
-
- GET_NODE_V(NODE_TRANSITION, TransitionNode, 0);
- return n->xfade;
-}
-
-int AnimationTreePlayer::transition_node_get_current(const StringName &p_node) const {
-
- GET_NODE_V(NODE_TRANSITION, TransitionNode, -1);
- return n->current;
-}
-
-/*misc */
-void AnimationTreePlayer::get_node_list(List<StringName> *p_node_list) const {
-
- for (Map<StringName, NodeBase *>::Element *E = node_map.front(); E; E = E->next()) {
-
- p_node_list->push_back(E->key());
- }
-}
-
-void AnimationTreePlayer::remove_node(const StringName &p_node) {
-
- ERR_FAIL_COND(!node_map.has(p_node));
- ERR_FAIL_COND_MSG(p_node == out_name, "Node 0 (output) can't be removed.");
-
- for (Map<StringName, NodeBase *>::Element *E = node_map.front(); E; E = E->next()) {
-
- NodeBase *nb = E->get();
- for (int i = 0; i < nb->inputs.size(); i++) {
-
- if (nb->inputs[i].node == p_node)
- nb->inputs.write[i].node = StringName();
- }
- }
-
- node_map.erase(p_node);
-
- _clear_cycle_test();
-
- // compute last error again, just in case
- last_error = _cycle_test(out_name);
- dirty_caches = true;
-}
-
-AnimationTreePlayer::ConnectError AnimationTreePlayer::_cycle_test(const StringName &p_at_node) {
-
- ERR_FAIL_COND_V(!node_map.has(p_at_node), CONNECT_INCOMPLETE);
-
- NodeBase *nb = node_map[p_at_node];
- if (nb->cycletest)
- return CONNECT_CYCLE;
-
- nb->cycletest = true;
-
- for (int i = 0; i < nb->inputs.size(); i++) {
- if (nb->inputs[i].node == StringName())
- return CONNECT_INCOMPLETE;
-
- ConnectError _err = _cycle_test(nb->inputs[i].node);
- if (_err)
- return _err;
- }
-
- return CONNECT_OK;
-}
-
-// Use this function to not alter next complete _cycle_test().
-void AnimationTreePlayer::_clear_cycle_test() {
- for (Map<StringName, NodeBase *>::Element *E = node_map.front(); E; E = E->next()) {
- NodeBase *nb = E->get();
- nb->cycletest = false;
- }
-}
-
-Error AnimationTreePlayer::connect_nodes(const StringName &p_src_node, const StringName &p_dst_node, int p_dst_input) {
-
- ERR_FAIL_COND_V(!node_map.has(p_src_node), ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!node_map.has(p_dst_node), ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(p_src_node == p_dst_node, ERR_INVALID_PARAMETER);
-
- //NodeBase *src = node_map[p_src_node];
- NodeBase *dst = node_map[p_dst_node];
- ERR_FAIL_INDEX_V(p_dst_input, dst->inputs.size(), ERR_INVALID_PARAMETER);
-
- //int oldval = dst->inputs[p_dst_input].node;
-
- for (Map<StringName, NodeBase *>::Element *E = node_map.front(); E; E = E->next()) {
-
- NodeBase *nb = E->get();
- for (int i = 0; i < nb->inputs.size(); i++) {
-
- if (nb->inputs[i].node == p_src_node)
- nb->inputs.write[i].node = StringName();
- }
- }
-
- dst->inputs.write[p_dst_input].node = p_src_node;
-
- _clear_cycle_test();
-
- last_error = _cycle_test(out_name);
- if (last_error) {
-
- if (last_error == CONNECT_INCOMPLETE)
- return ERR_UNCONFIGURED;
- else if (last_error == CONNECT_CYCLE)
- return ERR_CYCLIC_LINK;
- }
- dirty_caches = true;
- return OK;
-}
-
-bool AnimationTreePlayer::are_nodes_connected(const StringName &p_src_node, const StringName &p_dst_node, int p_dst_input) const {
-
- ERR_FAIL_COND_V(!node_map.has(p_src_node), false);
- ERR_FAIL_COND_V(!node_map.has(p_dst_node), false);
- ERR_FAIL_COND_V(p_src_node == p_dst_node, false);
-
- NodeBase *dst = node_map[p_dst_node];
-
- return dst->inputs[p_dst_input].node == p_src_node;
-}
-
-void AnimationTreePlayer::disconnect_nodes(const StringName &p_node, int p_input) {
-
- ERR_FAIL_COND(!node_map.has(p_node));
-
- NodeBase *dst = node_map[p_node];
- ERR_FAIL_INDEX(p_input, dst->inputs.size());
- dst->inputs.write[p_input].node = StringName();
- last_error = CONNECT_INCOMPLETE;
- dirty_caches = true;
-}
-
-void AnimationTreePlayer::get_connection_list(List<Connection> *p_connections) const {
-
- for (Map<StringName, NodeBase *>::Element *E = node_map.front(); E; E = E->next()) {
-
- NodeBase *nb = E->get();
- for (int i = 0; i < nb->inputs.size(); i++) {
-
- if (nb->inputs[i].node != StringName()) {
- Connection c;
- c.src_node = nb->inputs[i].node;
- c.dst_node = E->key();
- c.dst_input = i;
- p_connections->push_back(c);
- }
- }
- }
-}
-
-AnimationTreePlayer::Track *AnimationTreePlayer::_find_track(const NodePath &p_path) {
-
- Node *parent = get_node(base_path);
- ERR_FAIL_COND_V(!parent, NULL);
-
- RES resource;
- Vector<StringName> leftover_path;
- Node *child = parent->get_node_and_resource(p_path, resource, leftover_path);
- if (!child) {
- String err = "Animation track references unknown Node: '" + String(p_path) + "'.";
- WARN_PRINT(err.ascii().get_data());
- return NULL;
- }
-
- ObjectID id = child->get_instance_id();
- int bone_idx = -1;
-
- if (p_path.get_subname_count()) {
-
- if (Object::cast_to<Skeleton>(child))
- bone_idx = Object::cast_to<Skeleton>(child)->find_bone(p_path.get_subname(0));
- }
-
- TrackKey key;
- key.id = id;
- key.bone_idx = bone_idx;
- key.subpath_concatenated = p_path.get_concatenated_subnames();
-
- if (!track_map.has(key)) {
-
- Track tr;
- tr.id = id;
- tr.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
- tr.skeleton = Object::cast_to<Skeleton>(child);
- tr.spatial = Object::cast_to<Spatial>(child);
- tr.bone_idx = bone_idx;
- if (bone_idx == -1) tr.subpath = leftover_path;
-
- track_map[key] = tr;
- }
-
- return &track_map[key];
-}
-
-void AnimationTreePlayer::_recompute_caches() {
-
- track_map.clear();
- _recompute_caches(out_name);
- dirty_caches = false;
-}
-
-void AnimationTreePlayer::_recompute_caches(const StringName &p_node) {
-
- ERR_FAIL_COND(!node_map.has(p_node));
-
- NodeBase *nb = node_map[p_node];
-
- if (nb->type == NODE_ANIMATION) {
-
- AnimationNode *an = static_cast<AnimationNode *>(nb);
- an->tref.clear();
-
- if (!an->animation.is_null()) {
-
- Ref<Animation> a = an->animation;
-
- for (int i = 0; i < an->animation->get_track_count(); i++) {
-
- Track *tr = _find_track(a->track_get_path(i));
- if (!tr)
- continue;
-
- AnimationNode::TrackRef tref;
- tref.local_track = i;
- tref.track = tr;
- tref.weight = 0;
-
- an->tref.push_back(tref);
- }
- }
- }
-
- for (int i = 0; i < nb->inputs.size(); i++) {
-
- _recompute_caches(nb->inputs[i].node);
- }
-}
-
-void AnimationTreePlayer::recompute_caches() {
-
- dirty_caches = true;
-}
-
-/* playback */
-
-void AnimationTreePlayer::set_active(bool p_active) {
-
- if (active == p_active)
- return;
-
- active = p_active;
- processing = active;
- reset_request = p_active;
- _set_process(processing, true);
-}
-
-bool AnimationTreePlayer::is_active() const {
-
- return active;
-}
-
-AnimationTreePlayer::ConnectError AnimationTreePlayer::get_last_error() const {
-
- return last_error;
-}
-
-void AnimationTreePlayer::reset() {
-
- reset_request = true;
-}
-
-void AnimationTreePlayer::set_base_path(const NodePath &p_path) {
-
- base_path = p_path;
- recompute_caches();
-}
-
-NodePath AnimationTreePlayer::get_base_path() const {
-
- return base_path;
-}
-
-void AnimationTreePlayer::set_master_player(const NodePath &p_path) {
-
- if (p_path == master)
- return;
-
- master = p_path;
- _update_sources();
- recompute_caches();
-}
-
-NodePath AnimationTreePlayer::get_master_player() const {
-
- return master;
-}
-
-PoolVector<String> AnimationTreePlayer::_get_node_list() {
-
- List<StringName> nl;
- get_node_list(&nl);
- PoolVector<String> ret;
- ret.resize(nl.size());
- int idx = 0;
- for (List<StringName>::Element *E = nl.front(); E; E = E->next()) {
- ret.set(idx++, E->get());
- }
-
- return ret;
-}
-
-void AnimationTreePlayer::_update_sources() {
-
- if (master == NodePath())
- return;
- if (!is_inside_tree())
- return;
-
- Node *m = get_node(master);
- if (!m) {
- master = NodePath();
- ERR_FAIL_COND(!m);
- }
-
- AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(m);
-
- if (!ap) {
-
- master = NodePath();
- ERR_FAIL_COND(!ap);
- }
-
- for (Map<StringName, NodeBase *>::Element *E = node_map.front(); E; E = E->next()) {
-
- if (E->get()->type == NODE_ANIMATION) {
-
- AnimationNode *an = static_cast<AnimationNode *>(E->get());
-
- if (an->from != "") {
-
- an->animation = ap->get_animation(an->from);
- }
- }
- }
-}
-
-bool AnimationTreePlayer::node_exists(const StringName &p_name) const {
-
- return (node_map.has(p_name));
-}
-
-Error AnimationTreePlayer::node_rename(const StringName &p_node, const StringName &p_new_name) {
-
- if (p_new_name == p_node)
- return OK;
- ERR_FAIL_COND_V(!node_map.has(p_node), ERR_ALREADY_EXISTS);
- ERR_FAIL_COND_V(node_map.has(p_new_name), ERR_ALREADY_EXISTS);
- ERR_FAIL_COND_V(p_new_name == StringName(), ERR_INVALID_DATA);
- ERR_FAIL_COND_V(p_node == out_name, ERR_INVALID_DATA);
- ERR_FAIL_COND_V(p_new_name == out_name, ERR_INVALID_DATA);
-
- for (Map<StringName, NodeBase *>::Element *E = node_map.front(); E; E = E->next()) {
-
- NodeBase *nb = E->get();
- for (int i = 0; i < nb->inputs.size(); i++) {
-
- if (nb->inputs[i].node == p_node) {
- nb->inputs.write[i].node = p_new_name;
- }
- }
- }
-
- node_map[p_new_name] = node_map[p_node];
- node_map.erase(p_node);
-
- return OK;
-}
-
-String AnimationTreePlayer::get_configuration_warning() const {
-
- return TTR("This node has been deprecated. Use AnimationTree instead.");
-}
-
-void AnimationTreePlayer::_bind_methods() {
-
- ClassDB::bind_method(D_METHOD("add_node", "type", "id"), &AnimationTreePlayer::add_node);
-
- ClassDB::bind_method(D_METHOD("node_exists", "node"), &AnimationTreePlayer::node_exists);
- ClassDB::bind_method(D_METHOD("node_rename", "node", "new_name"), &AnimationTreePlayer::node_rename);
-
- ClassDB::bind_method(D_METHOD("node_get_type", "id"), &AnimationTreePlayer::node_get_type);
- ClassDB::bind_method(D_METHOD("node_get_input_count", "id"), &AnimationTreePlayer::node_get_input_count);
- ClassDB::bind_method(D_METHOD("node_get_input_source", "id", "idx"), &AnimationTreePlayer::node_get_input_source);
-
- ClassDB::bind_method(D_METHOD("animation_node_set_animation", "id", "animation"), &AnimationTreePlayer::animation_node_set_animation);
- ClassDB::bind_method(D_METHOD("animation_node_get_animation", "id"), &AnimationTreePlayer::animation_node_get_animation);
-
- ClassDB::bind_method(D_METHOD("animation_node_set_master_animation", "id", "source"), &AnimationTreePlayer::animation_node_set_master_animation);
- ClassDB::bind_method(D_METHOD("animation_node_get_master_animation", "id"), &AnimationTreePlayer::animation_node_get_master_animation);
- ClassDB::bind_method(D_METHOD("animation_node_get_position", "id"), &AnimationTreePlayer::animation_node_get_position);
- ClassDB::bind_method(D_METHOD("animation_node_set_filter_path", "id", "path", "enable"), &AnimationTreePlayer::animation_node_set_filter_path);
-
- ClassDB::bind_method(D_METHOD("oneshot_node_set_fadein_time", "id", "time_sec"), &AnimationTreePlayer::oneshot_node_set_fadein_time);
- ClassDB::bind_method(D_METHOD("oneshot_node_get_fadein_time", "id"), &AnimationTreePlayer::oneshot_node_get_fadein_time);
-
- ClassDB::bind_method(D_METHOD("oneshot_node_set_fadeout_time", "id", "time_sec"), &AnimationTreePlayer::oneshot_node_set_fadeout_time);
- ClassDB::bind_method(D_METHOD("oneshot_node_get_fadeout_time", "id"), &AnimationTreePlayer::oneshot_node_get_fadeout_time);
-
- ClassDB::bind_method(D_METHOD("oneshot_node_set_autorestart", "id", "enable"), &AnimationTreePlayer::oneshot_node_set_autorestart);
- ClassDB::bind_method(D_METHOD("oneshot_node_set_autorestart_delay", "id", "delay_sec"), &AnimationTreePlayer::oneshot_node_set_autorestart_delay);
- ClassDB::bind_method(D_METHOD("oneshot_node_set_autorestart_random_delay", "id", "rand_sec"), &AnimationTreePlayer::oneshot_node_set_autorestart_random_delay);
-
- ClassDB::bind_method(D_METHOD("oneshot_node_has_autorestart", "id"), &AnimationTreePlayer::oneshot_node_has_autorestart);
- ClassDB::bind_method(D_METHOD("oneshot_node_get_autorestart_delay", "id"), &AnimationTreePlayer::oneshot_node_get_autorestart_delay);
- ClassDB::bind_method(D_METHOD("oneshot_node_get_autorestart_random_delay", "id"), &AnimationTreePlayer::oneshot_node_get_autorestart_random_delay);
-
- ClassDB::bind_method(D_METHOD("oneshot_node_start", "id"), &AnimationTreePlayer::oneshot_node_start);
- ClassDB::bind_method(D_METHOD("oneshot_node_stop", "id"), &AnimationTreePlayer::oneshot_node_stop);
- ClassDB::bind_method(D_METHOD("oneshot_node_is_active", "id"), &AnimationTreePlayer::oneshot_node_is_active);
- ClassDB::bind_method(D_METHOD("oneshot_node_set_filter_path", "id", "path", "enable"), &AnimationTreePlayer::oneshot_node_set_filter_path);
-
- ClassDB::bind_method(D_METHOD("mix_node_set_amount", "id", "ratio"), &AnimationTreePlayer::mix_node_set_amount);
- ClassDB::bind_method(D_METHOD("mix_node_get_amount", "id"), &AnimationTreePlayer::mix_node_get_amount);
-
- ClassDB::bind_method(D_METHOD("blend2_node_set_amount", "id", "blend"), &AnimationTreePlayer::blend2_node_set_amount);
- ClassDB::bind_method(D_METHOD("blend2_node_get_amount", "id"), &AnimationTreePlayer::blend2_node_get_amount);
- ClassDB::bind_method(D_METHOD("blend2_node_set_filter_path", "id", "path", "enable"), &AnimationTreePlayer::blend2_node_set_filter_path);
-
- ClassDB::bind_method(D_METHOD("blend3_node_set_amount", "id", "blend"), &AnimationTreePlayer::blend3_node_set_amount);
- ClassDB::bind_method(D_METHOD("blend3_node_get_amount", "id"), &AnimationTreePlayer::blend3_node_get_amount);
-
- ClassDB::bind_method(D_METHOD("blend4_node_set_amount", "id", "blend"), &AnimationTreePlayer::blend4_node_set_amount);
- ClassDB::bind_method(D_METHOD("blend4_node_get_amount", "id"), &AnimationTreePlayer::blend4_node_get_amount);
-
- ClassDB::bind_method(D_METHOD("timescale_node_set_scale", "id", "scale"), &AnimationTreePlayer::timescale_node_set_scale);
- ClassDB::bind_method(D_METHOD("timescale_node_get_scale", "id"), &AnimationTreePlayer::timescale_node_get_scale);
-
- ClassDB::bind_method(D_METHOD("timeseek_node_seek", "id", "seconds"), &AnimationTreePlayer::timeseek_node_seek);
-
- ClassDB::bind_method(D_METHOD("transition_node_set_input_count", "id", "count"), &AnimationTreePlayer::transition_node_set_input_count);
- ClassDB::bind_method(D_METHOD("transition_node_get_input_count", "id"), &AnimationTreePlayer::transition_node_get_input_count);
- ClassDB::bind_method(D_METHOD("transition_node_delete_input", "id", "input_idx"), &AnimationTreePlayer::transition_node_delete_input);
-
- ClassDB::bind_method(D_METHOD("transition_node_set_input_auto_advance", "id", "input_idx", "enable"), &AnimationTreePlayer::transition_node_set_input_auto_advance);
- ClassDB::bind_method(D_METHOD("transition_node_has_input_auto_advance", "id", "input_idx"), &AnimationTreePlayer::transition_node_has_input_auto_advance);
-
- ClassDB::bind_method(D_METHOD("transition_node_set_xfade_time", "id", "time_sec"), &AnimationTreePlayer::transition_node_set_xfade_time);
- ClassDB::bind_method(D_METHOD("transition_node_get_xfade_time", "id"), &AnimationTreePlayer::transition_node_get_xfade_time);
-
- ClassDB::bind_method(D_METHOD("transition_node_set_current", "id", "input_idx"), &AnimationTreePlayer::transition_node_set_current);
- ClassDB::bind_method(D_METHOD("transition_node_get_current", "id"), &AnimationTreePlayer::transition_node_get_current);
-
- ClassDB::bind_method(D_METHOD("node_set_position", "id", "screen_position"), &AnimationTreePlayer::node_set_position);
- ClassDB::bind_method(D_METHOD("node_get_position", "id"), &AnimationTreePlayer::node_get_position);
-
- ClassDB::bind_method(D_METHOD("remove_node", "id"), &AnimationTreePlayer::remove_node);
- ClassDB::bind_method(D_METHOD("connect_nodes", "id", "dst_id", "dst_input_idx"), &AnimationTreePlayer::connect_nodes);
- ClassDB::bind_method(D_METHOD("are_nodes_connected", "id", "dst_id", "dst_input_idx"), &AnimationTreePlayer::are_nodes_connected);
- ClassDB::bind_method(D_METHOD("disconnect_nodes", "id", "dst_input_idx"), &AnimationTreePlayer::disconnect_nodes);
-
- ClassDB::bind_method(D_METHOD("set_active", "enabled"), &AnimationTreePlayer::set_active);
- ClassDB::bind_method(D_METHOD("is_active"), &AnimationTreePlayer::is_active);
-
- ClassDB::bind_method(D_METHOD("set_base_path", "path"), &AnimationTreePlayer::set_base_path);
- ClassDB::bind_method(D_METHOD("get_base_path"), &AnimationTreePlayer::get_base_path);
-
- ClassDB::bind_method(D_METHOD("set_master_player", "nodepath"), &AnimationTreePlayer::set_master_player);
- ClassDB::bind_method(D_METHOD("get_master_player"), &AnimationTreePlayer::get_master_player);
-
- ClassDB::bind_method(D_METHOD("get_node_list"), &AnimationTreePlayer::_get_node_list);
-
- ClassDB::bind_method(D_METHOD("set_animation_process_mode", "mode"), &AnimationTreePlayer::set_animation_process_mode);
- ClassDB::bind_method(D_METHOD("get_animation_process_mode"), &AnimationTreePlayer::get_animation_process_mode);
-
- ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationTreePlayer::advance);
-
- ClassDB::bind_method(D_METHOD("reset"), &AnimationTreePlayer::reset);
-
- ClassDB::bind_method(D_METHOD("recompute_caches"), &AnimationTreePlayer::recompute_caches);
-
- ADD_GROUP("Playback", "playback_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_animation_process_mode", "get_animation_process_mode");
-
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "master_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_master_player", "get_master_player");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "base_path"), "set_base_path", "get_base_path");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
-
- BIND_ENUM_CONSTANT(NODE_OUTPUT);
- BIND_ENUM_CONSTANT(NODE_ANIMATION);
- BIND_ENUM_CONSTANT(NODE_ONESHOT);
- BIND_ENUM_CONSTANT(NODE_MIX);
- BIND_ENUM_CONSTANT(NODE_BLEND2);
- BIND_ENUM_CONSTANT(NODE_BLEND3);
- BIND_ENUM_CONSTANT(NODE_BLEND4);
- BIND_ENUM_CONSTANT(NODE_TIMESCALE);
- BIND_ENUM_CONSTANT(NODE_TIMESEEK);
- BIND_ENUM_CONSTANT(NODE_TRANSITION);
-
- BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS);
- BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE);
-}
-
-AnimationTreePlayer::AnimationTreePlayer() {
-
- active_list = NULL;
- out = memnew(NodeOut);
- out_name = "out";
- out->pos = Point2(40, 40);
- node_map.insert(out_name, out);
- animation_process_mode = ANIMATION_PROCESS_IDLE;
- processing = false;
- active = false;
- dirty_caches = true;
- reset_request = true;
- last_error = CONNECT_INCOMPLETE;
- base_path = String("..");
-}
-
-AnimationTreePlayer::~AnimationTreePlayer() {
-
- while (node_map.size()) {
- memdelete(node_map.front()->get());
- node_map.erase(node_map.front());
- }
-}
diff --git a/scene/animation/animation_tree_player.h b/scene/animation/animation_tree_player.h
deleted file mode 100644
index e1f6ce7b9c..0000000000
--- a/scene/animation/animation_tree_player.h
+++ /dev/null
@@ -1,487 +0,0 @@
-/*************************************************************************/
-/* animation_tree_player.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 ANIMATION_TREE_PLAYER_H
-#define ANIMATION_TREE_PLAYER_H
-
-#include "animation_player.h"
-#include "scene/3d/skeleton.h"
-#include "scene/3d/spatial.h"
-#include "scene/resources/animation.h"
-
-class AnimationTreePlayer : public Node {
-
- GDCLASS(AnimationTreePlayer, Node);
- OBJ_CATEGORY("Animation Nodes");
-
-public:
- enum AnimationProcessMode {
- ANIMATION_PROCESS_PHYSICS,
- ANIMATION_PROCESS_IDLE,
- };
-
- enum NodeType {
-
- NODE_OUTPUT,
- NODE_ANIMATION,
- NODE_ONESHOT,
- NODE_MIX,
- NODE_BLEND2,
- NODE_BLEND3,
- NODE_BLEND4,
- NODE_TIMESCALE,
- NODE_TIMESEEK,
- NODE_TRANSITION,
-
- NODE_MAX,
- };
-
- enum ConnectError {
-
- CONNECT_OK,
- CONNECT_INCOMPLETE,
- CONNECT_CYCLE
- };
-
-private:
- enum {
-
- DISCONNECTED = -1,
- };
-
- struct TrackKey {
-
- uint32_t id;
- StringName subpath_concatenated;
- int bone_idx;
-
- inline bool operator<(const TrackKey &p_right) const {
-
- if (id == p_right.id) {
- if (bone_idx == p_right.bone_idx) {
- return subpath_concatenated < p_right.subpath_concatenated;
- } else
- return bone_idx < p_right.bone_idx;
- } else
- return id < p_right.id;
- }
- };
-
- struct Track {
- uint32_t id;
- Object *object;
- Spatial *spatial;
- Skeleton *skeleton;
- int bone_idx;
- Vector<StringName> subpath;
-
- Vector3 loc;
- Quat rot;
- Vector3 scale;
-
- Variant value;
-
- bool skip;
-
- Track() :
- id(0),
- object(NULL),
- spatial(NULL),
- skeleton(NULL),
- bone_idx(-1),
- skip(false) {}
- };
-
- typedef Map<TrackKey, Track> TrackMap;
-
- TrackMap track_map;
-
- struct Input {
-
- StringName node;
- //Input() { node=-1; }
- };
-
- struct NodeBase {
-
- bool cycletest;
-
- NodeType type;
- Point2 pos;
-
- Vector<Input> inputs;
-
- NodeBase() { cycletest = false; };
- virtual ~NodeBase() { cycletest = false; }
- };
-
- struct NodeOut : public NodeBase {
-
- NodeOut() {
- type = NODE_OUTPUT;
- inputs.resize(1);
- }
- };
-
- struct AnimationNode : public NodeBase {
-
- Ref<Animation> animation;
-
- struct TrackRef {
- int local_track;
- Track *track;
- float weight;
- };
-
- uint64_t last_version;
- List<TrackRef> tref;
- AnimationNode *next;
- float time;
- float step;
- String from;
- bool skip;
-
- HashMap<NodePath, bool> filter;
-
- AnimationNode() {
- type = NODE_ANIMATION;
- next = NULL;
- last_version = 0;
- skip = false;
- }
- };
-
- struct OneShotNode : public NodeBase {
-
- bool active;
- bool start;
- float fade_in;
- float fade_out;
-
- bool autorestart;
- float autorestart_delay;
- float autorestart_random_delay;
- bool mix;
-
- float time;
- float remaining;
- float autorestart_remaining;
-
- HashMap<NodePath, bool> filter;
-
- OneShotNode() {
- type = NODE_ONESHOT;
- fade_in = 0;
- fade_out = 0;
- inputs.resize(2);
- autorestart = false;
- autorestart_delay = 1;
- autorestart_remaining = 0;
- mix = false;
- active = false;
- start = false;
- }
- };
-
- struct MixNode : public NodeBase {
-
- float amount;
- MixNode() {
- type = NODE_MIX;
- inputs.resize(2);
- }
- };
-
- struct Blend2Node : public NodeBase {
-
- float value;
- HashMap<NodePath, bool> filter;
- Blend2Node() {
- type = NODE_BLEND2;
- value = 0;
- inputs.resize(2);
- }
- };
-
- struct Blend3Node : public NodeBase {
-
- float value;
- Blend3Node() {
- type = NODE_BLEND3;
- value = 0;
- inputs.resize(3);
- }
- };
-
- struct Blend4Node : public NodeBase {
-
- Point2 value;
- Blend4Node() {
- type = NODE_BLEND4;
- inputs.resize(4);
- }
- };
-
- struct TimeScaleNode : public NodeBase {
-
- float scale;
- TimeScaleNode() {
- type = NODE_TIMESCALE;
- scale = 1;
- inputs.resize(1);
- }
- };
-
- struct TimeSeekNode : public NodeBase {
-
- float seek_pos;
-
- TimeSeekNode() {
- type = NODE_TIMESEEK;
- inputs.resize(1);
- seek_pos = -1;
- }
- };
-
- struct TransitionNode : public NodeBase {
-
- struct InputData {
-
- bool auto_advance;
- InputData() { auto_advance = false; }
- };
-
- Vector<InputData> input_data;
-
- float prev_time;
- float prev_xfading;
- int prev;
- bool switched;
-
- float time;
- int current;
-
- float xfade;
-
- TransitionNode() {
- type = NODE_TRANSITION;
- xfade = 0;
- inputs.resize(1);
- input_data.resize(1);
- current = 0;
- prev = -1;
- prev_time = 0;
- prev_xfading = 0;
- switched = false;
- }
- void set_current(int p_current);
- };
-
- void _update_sources();
-
- StringName out_name;
- NodeOut *out;
-
- NodePath base_path;
- NodePath master;
-
- ConnectError last_error;
- AnimationNode *active_list;
- AnimationProcessMode animation_process_mode;
- bool processing;
- bool active;
- bool dirty_caches;
- Map<StringName, NodeBase *> node_map;
-
- // return time left to finish animation
- float _process_node(const StringName &p_node, AnimationNode **r_prev_anim, float p_time, bool p_seek = false, float p_fallback_weight = 1.0, HashMap<NodePath, float> *p_weights = NULL);
- void _process_animation(float p_delta);
- bool reset_request;
-
- ConnectError _cycle_test(const StringName &p_at_node);
- void _clear_cycle_test();
-
- Track *_find_track(const NodePath &p_path);
- void _recompute_caches();
- void _recompute_caches(const StringName &p_node);
- PoolVector<String> _get_node_list();
-
- void _compute_weights(float *p_fallback_weight, HashMap<NodePath, float> *p_weights, float p_coeff, const HashMap<NodePath, bool> *p_filter = NULL, float p_filtered_coeff = 0);
-
-protected:
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
- void _notification(int p_what);
-
- static void _bind_methods();
-
-public:
- void add_node(NodeType p_type, const StringName &p_node); // nodes must be >0 node 0 is built-in (exit)
- bool node_exists(const StringName &p_name) const;
-
- Error node_rename(const StringName &p_node, const StringName &p_new_name);
- int node_get_input_count(const StringName &p_node) const;
- StringName node_get_input_source(const StringName &p_node, int p_input) const;
-
- String get_configuration_warning() const;
-
- /* ANIMATION NODE */
- void animation_node_set_animation(const StringName &p_node, const Ref<Animation> &p_animation);
- Ref<Animation> animation_node_get_animation(const StringName &p_node) const;
- void animation_node_set_master_animation(const StringName &p_node, const String &p_master_animation);
- String animation_node_get_master_animation(const StringName &p_node) const;
- float animation_node_get_position(const StringName &p_node) const;
-
- void animation_node_set_filter_path(const StringName &p_node, const NodePath &p_track_path, bool p_filter);
- void animation_node_set_get_filtered_paths(const StringName &p_node, List<NodePath> *r_paths) const;
- bool animation_node_is_path_filtered(const StringName &p_node, const NodePath &p_path) const;
-
- /* ONE SHOT NODE */
-
- void oneshot_node_set_fadein_time(const StringName &p_node, float p_time);
- void oneshot_node_set_fadeout_time(const StringName &p_node, float p_time);
-
- float oneshot_node_get_fadein_time(const StringName &p_node) const;
- float oneshot_node_get_fadeout_time(const StringName &p_node) const;
-
- void oneshot_node_set_autorestart(const StringName &p_node, bool p_active);
- void oneshot_node_set_autorestart_delay(const StringName &p_node, float p_time);
- void oneshot_node_set_autorestart_random_delay(const StringName &p_node, float p_time);
-
- bool oneshot_node_has_autorestart(const StringName &p_node) const;
- float oneshot_node_get_autorestart_delay(const StringName &p_node) const;
- float oneshot_node_get_autorestart_random_delay(const StringName &p_node) const;
-
- void oneshot_node_set_mix_mode(const StringName &p_node, bool p_mix);
- bool oneshot_node_get_mix_mode(const StringName &p_node) const;
-
- void oneshot_node_start(const StringName &p_node);
- void oneshot_node_stop(const StringName &p_node);
- bool oneshot_node_is_active(const StringName &p_node) const;
-
- void oneshot_node_set_filter_path(const StringName &p_node, const NodePath &p_filter, bool p_enable);
- void oneshot_node_set_get_filtered_paths(const StringName &p_node, List<NodePath> *r_paths) const;
- bool oneshot_node_is_path_filtered(const StringName &p_node, const NodePath &p_path) const;
-
- /* MIX/BLEND NODES */
-
- void mix_node_set_amount(const StringName &p_node, float p_amount);
- float mix_node_get_amount(const StringName &p_node) const;
-
- void blend2_node_set_amount(const StringName &p_node, float p_amount);
- float blend2_node_get_amount(const StringName &p_node) const;
- void blend2_node_set_filter_path(const StringName &p_node, const NodePath &p_filter, bool p_enable);
- void blend2_node_set_get_filtered_paths(const StringName &p_node, List<NodePath> *r_paths) const;
- bool blend2_node_is_path_filtered(const StringName &p_node, const NodePath &p_path) const;
-
- void blend3_node_set_amount(const StringName &p_node, float p_amount);
- float blend3_node_get_amount(const StringName &p_node) const;
-
- void blend4_node_set_amount(const StringName &p_node, const Point2 &p_amount);
- Point2 blend4_node_get_amount(const StringName &p_node) const;
-
- /* TIMESCALE/TIMESEEK NODES */
-
- void timescale_node_set_scale(const StringName &p_node, float p_scale);
- float timescale_node_get_scale(const StringName &p_node) const;
-
- void timeseek_node_seek(const StringName &p_node, float p_pos);
-
- /* TRANSITION NODE */
-
- void transition_node_set_input_count(const StringName &p_node, int p_inputs); // used for transition node
- int transition_node_get_input_count(const StringName &p_node) const;
- void transition_node_delete_input(const StringName &p_node, int p_input); // used for transition node
-
- void transition_node_set_input_auto_advance(const StringName &p_node, int p_input, bool p_auto_advance); // used for transition node
- bool transition_node_has_input_auto_advance(const StringName &p_node, int p_input) const;
-
- void transition_node_set_xfade_time(const StringName &p_node, float p_time); // used for transition node
- float transition_node_get_xfade_time(const StringName &p_node) const;
-
- void transition_node_set_current(const StringName &p_node, int p_current);
- int transition_node_get_current(const StringName &p_node) const;
-
- void node_set_position(const StringName &p_node, const Vector2 &p_pos); //for display
-
- /* GETS */
- Point2 node_get_position(const StringName &p_node) const; //for display
-
- NodeType node_get_type(const StringName &p_node) const;
-
- void get_node_list(List<StringName> *p_node_list) const;
- void remove_node(const StringName &p_node);
-
- Error connect_nodes(const StringName &p_src_node, const StringName &p_dst_node, int p_dst_input);
- bool are_nodes_connected(const StringName &p_src_node, const StringName &p_dst_node, int p_dst_input) const;
- void disconnect_nodes(const StringName &p_node, int p_input);
-
- void set_base_path(const NodePath &p_path);
- NodePath get_base_path() const;
-
- void set_master_player(const NodePath &p_path);
- NodePath get_master_player() const;
-
- struct Connection {
-
- StringName src_node;
- StringName dst_node;
- int dst_input;
- };
-
- void get_connection_list(List<Connection> *p_connections) const;
-
- /* playback */
-
- void set_active(bool p_active);
- bool is_active() const;
-
- void reset();
-
- void recompute_caches();
-
- ConnectError get_last_error() const;
-
- void set_animation_process_mode(AnimationProcessMode p_mode);
- AnimationProcessMode get_animation_process_mode() const;
-
- void _set_process(bool p_process, bool p_force = false);
-
- void advance(float p_time);
-
- AnimationTreePlayer();
- ~AnimationTreePlayer();
-};
-
-VARIANT_ENUM_CAST(AnimationTreePlayer::NodeType);
-VARIANT_ENUM_CAST(AnimationTreePlayer::AnimationProcessMode);
-
-#endif // ANIMATION_TREE_PLAYER_H
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 93d0ae080b..9d5e004ed3 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -6530,6 +6530,10 @@ void TextEdit::_update_completion_candidates() {
Vector<float> sim_cache;
bool single_quote = s.begins_with("'");
Vector<ScriptCodeCompletionOption> completion_options_casei;
+ Vector<ScriptCodeCompletionOption> completion_options_subseq;
+ Vector<ScriptCodeCompletionOption> completion_options_subseq_casei;
+
+ String s_lower = s.to_lower();
for (List<ScriptCodeCompletionOption>::Element *E = completion_sources.front(); E; E = E->next()) {
ScriptCodeCompletionOption &option = E->get();
@@ -6544,31 +6548,68 @@ void TextEdit::_update_completion_candidates() {
option.insert_text = option.insert_text.quote(quote);
}
- if (option.display.begins_with(s)) {
+ if (option.display.length() == 0) {
+ continue;
+ } else if (s.length() == 0) {
completion_options.push_back(option);
- } else if (option.display.to_lower().begins_with(s.to_lower())) {
- completion_options_casei.push_back(option);
- }
- }
+ } else {
- completion_options.append_array(completion_options_casei);
+ // This code works the same as:
+ /*
+ if (option.display.begins_with(s)) {
+ completion_options.push_back(option);
+ } else if (option.display.to_lower().begins_with(s.to_lower())) {
+ completion_options_casei.push_back(option);
+ } else if (s.is_subsequence_of(option.display)) {
+ completion_options_subseq.push_back(option);
+ } else if (s.is_subsequence_ofi(option.display)) {
+ completion_options_subseq_casei.push_back(option);
+ }
+ */
+ // But is more performant due to being inlined and looping over the characters only once
- if (completion_options.size() == 0) {
- for (int i = 0; i < completion_sources.size(); i++) {
- if (s.is_subsequence_of(completion_sources[i].display)) {
- completion_options.push_back(completion_sources[i]);
+ String display_lower = option.display.to_lower();
+
+ const CharType *ssq = &s[0];
+ const CharType *ssq_lower = &s_lower[0];
+
+ const CharType *tgt = &option.display[0];
+ const CharType *tgt_lower = &display_lower[0];
+
+ const CharType *ssq_last_tgt = NULL;
+ const CharType *ssq_lower_last_tgt = NULL;
+
+ for (; *tgt; tgt++, tgt_lower++) {
+ if (*ssq == *tgt) {
+ ssq++;
+ ssq_last_tgt = tgt;
+ }
+ if (*ssq_lower == *tgt_lower) {
+ ssq_lower++;
+ ssq_lower_last_tgt = tgt;
+ }
}
- }
- }
- if (completion_options.size() == 0) {
- for (int i = 0; i < completion_sources.size(); i++) {
- if (s.is_subsequence_ofi(completion_sources[i].display)) {
- completion_options.push_back(completion_sources[i]);
+ if (!*ssq) { // Matched the whole subsequence in s
+ if (ssq_last_tgt == &option.display[s.length() - 1]) { // Finished matching in the first s.length() characters
+ completion_options.push_back(option);
+ } else {
+ completion_options_subseq.push_back(option);
+ }
+ } else if (!*ssq_lower) { // Matched the whole subsequence in s_lower
+ if (ssq_lower_last_tgt == &option.display[s.length() - 1]) { // Finished matching in the first s.length() characters
+ completion_options_casei.push_back(option);
+ } else {
+ completion_options_subseq_casei.push_back(option);
+ }
}
}
}
+ completion_options.append_array(completion_options_casei);
+ completion_options.append_array(completion_options_subseq);
+ completion_options.append_array(completion_options_subseq_casei);
+
if (completion_options.size() == 0) {
// No options to complete, cancel.
_cancel_completion();
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index c0e566eed5..15d21859ea 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -46,6 +46,7 @@
#include "scene/resources/mesh.h"
#include "scene/resources/packed_scene.h"
#include "scene/scene_string_names.h"
+#include "servers/navigation_server.h"
#include "servers/physics_2d_server.h"
#include "servers/physics_server.h"
#include "viewport.h"
@@ -896,6 +897,7 @@ void SceneTree::set_pause(bool p_enabled) {
if (p_enabled == pause)
return;
pause = p_enabled;
+ NavigationServer::get_singleton()->set_active(!p_enabled);
PhysicsServer::get_singleton()->set_active(!p_enabled);
Physics2DServer::get_singleton()->set_active(!p_enabled);
if (get_root())
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index d53872df6e..433c015a33 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -50,6 +50,8 @@
#include "scene/2d/mesh_instance_2d.h"
#include "scene/2d/multimesh_instance_2d.h"
#include "scene/2d/navigation_2d.h"
+#include "scene/2d/navigation_agent_2d.h"
+#include "scene/2d/navigation_obstacle_2d.h"
#include "scene/2d/parallax_background.h"
#include "scene/2d/parallax_layer.h"
#include "scene/2d/particles_2d.h"
@@ -71,7 +73,6 @@
#include "scene/animation/animation_node_state_machine.h"
#include "scene/animation/animation_player.h"
#include "scene/animation/animation_tree.h"
-#include "scene/animation/animation_tree_player.h"
#include "scene/animation/root_motion_view.h"
#include "scene/animation/tween.h"
#include "scene/audio/audio_stream_player.h"
@@ -145,6 +146,7 @@
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
#include "scene/resources/mesh_data_tool.h"
+#include "scene/resources/navigation_mesh.h"
#include "scene/resources/packed_scene.h"
#include "scene/resources/particles_material.h"
#include "scene/resources/physics_material.h"
@@ -168,9 +170,6 @@
#include "scene/resources/world_2d.h"
#include "scene/scene_string_names.h"
-#include "scene/3d/spatial.h"
-#include "scene/3d/world_environment.h"
-
#ifndef _3D_DISABLED
#include "scene/3d/area.h"
#include "scene/3d/arvr_nodes.h"
@@ -189,7 +188,9 @@
#include "scene/3d/mesh_instance.h"
#include "scene/3d/multimesh_instance.h"
#include "scene/3d/navigation.h"
-#include "scene/3d/navigation_mesh.h"
+#include "scene/3d/navigation_agent.h"
+#include "scene/3d/navigation_mesh_instance.h"
+#include "scene/3d/navigation_obstacle.h"
#include "scene/3d/particles.h"
#include "scene/3d/path.h"
#include "scene/3d/physics_body.h"
@@ -201,10 +202,12 @@
#include "scene/3d/remote_transform.h"
#include "scene/3d/skeleton.h"
#include "scene/3d/soft_body.h"
+#include "scene/3d/spatial.h"
#include "scene/3d/spring_arm.h"
#include "scene/3d/sprite_3d.h"
#include "scene/3d/vehicle_body.h"
#include "scene/3d/visibility_notifier.h"
+#include "scene/3d/world_environment.h"
#include "scene/animation/skeleton_ik.h"
#include "scene/resources/environment.h"
#include "scene/resources/mesh_library.h"
@@ -370,7 +373,6 @@ void register_scene_types() {
ClassDB::register_class<AnimationPlayer>();
ClassDB::register_class<Tween>();
- ClassDB::register_class<AnimationTreePlayer>();
ClassDB::register_class<AnimationTree>();
ClassDB::register_class<AnimationNode>();
ClassDB::register_class<AnimationRootNode>();
@@ -422,9 +424,6 @@ void register_scene_types() {
ClassDB::register_class<Particles>();
ClassDB::register_class<CPUParticles>();
ClassDB::register_class<Position3D>();
- ClassDB::register_class<NavigationMeshInstance>();
- ClassDB::register_class<NavigationMesh>();
- ClassDB::register_class<Navigation>();
ClassDB::register_class<RootMotionView>();
ClassDB::set_class_enabled("RootMotionView", false); //disabled by default, enabled by editor
@@ -469,9 +468,15 @@ void register_scene_types() {
ClassDB::register_class<ConeTwistJoint>();
ClassDB::register_class<Generic6DOFJoint>();
+ ClassDB::register_class<Navigation>();
+ ClassDB::register_class<NavigationMeshInstance>();
+ ClassDB::register_class<NavigationAgent>();
+ ClassDB::register_class<NavigationObstacle>();
+
OS::get_singleton()->yield(); //may take time to init
#endif
+ ClassDB::register_class<NavigationMesh>();
AcceptDialog::set_swap_ok_cancel(GLOBAL_DEF("gui/common/swap_ok_cancel", bool(OS::get_singleton()->get_swap_ok_cancel())));
@@ -713,6 +718,8 @@ void register_scene_types() {
ClassDB::register_class<Navigation2D>();
ClassDB::register_class<NavigationPolygon>();
ClassDB::register_class<NavigationPolygonInstance>();
+ ClassDB::register_class<NavigationAgent2D>();
+ ClassDB::register_class<NavigationObstacle2D>();
OS::get_singleton()->yield(); //may take time to init
@@ -726,7 +733,7 @@ void register_scene_types() {
ClassDB::add_compatibility_class("ImageSkyBox", "PanoramaSky");
ClassDB::add_compatibility_class("FixedSpatialMaterial", "SpatialMaterial");
ClassDB::add_compatibility_class("Mesh", "ArrayMesh");
-
+ ClassDB::add_compatibility_class("AnimationTreePlayer", "AnimationTree");
#endif
OS::get_singleton()->yield(); //may take time to init
diff --git a/scene/resources/box_shape.cpp b/scene/resources/box_shape.cpp
index dee3943b8e..e1f485bae6 100644
--- a/scene/resources/box_shape.cpp
+++ b/scene/resources/box_shape.cpp
@@ -48,6 +48,10 @@ Vector<Vector3> BoxShape::get_debug_mesh_lines() {
return lines;
}
+real_t BoxShape::get_enclosing_radius() const {
+ return extents.length();
+}
+
void BoxShape::_update_shape() {
PhysicsServer::get_singleton()->shape_set_data(get_shape(), extents);
diff --git a/scene/resources/box_shape.h b/scene/resources/box_shape.h
index b577a70e1e..fb164cc300 100644
--- a/scene/resources/box_shape.h
+++ b/scene/resources/box_shape.h
@@ -48,6 +48,7 @@ public:
Vector3 get_extents() const;
virtual Vector<Vector3> get_debug_mesh_lines();
+ virtual real_t get_enclosing_radius() const;
BoxShape();
};
diff --git a/scene/resources/capsule_shape.cpp b/scene/resources/capsule_shape.cpp
index 61331a336d..933129936a 100644
--- a/scene/resources/capsule_shape.cpp
+++ b/scene/resources/capsule_shape.cpp
@@ -69,6 +69,10 @@ Vector<Vector3> CapsuleShape::get_debug_mesh_lines() {
return points;
}
+real_t CapsuleShape::get_enclosing_radius() const {
+ return radius + height * 0.5;
+}
+
void CapsuleShape::_update_shape() {
Dictionary d;
diff --git a/scene/resources/capsule_shape.h b/scene/resources/capsule_shape.h
index 8e7d7bcb94..f097e51175 100644
--- a/scene/resources/capsule_shape.h
+++ b/scene/resources/capsule_shape.h
@@ -51,6 +51,7 @@ public:
float get_height() const;
virtual Vector<Vector3> get_debug_mesh_lines();
+ virtual real_t get_enclosing_radius() const;
CapsuleShape();
};
diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp
index 008e8bb13d..5658395cee 100644
--- a/scene/resources/capsule_shape_2d.cpp
+++ b/scene/resources/capsule_shape_2d.cpp
@@ -97,6 +97,10 @@ Rect2 CapsuleShape2D::get_rect() const {
return rect;
}
+real_t CapsuleShape2D::get_enclosing_radius() const {
+ return radius + height * 0.5;
+}
+
void CapsuleShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CapsuleShape2D::set_radius);
diff --git a/scene/resources/capsule_shape_2d.h b/scene/resources/capsule_shape_2d.h
index 8398fab839..fe401a610c 100644
--- a/scene/resources/capsule_shape_2d.h
+++ b/scene/resources/capsule_shape_2d.h
@@ -56,6 +56,7 @@ public:
virtual void draw(const RID &p_to_rid, const Color &p_color);
virtual Rect2 get_rect() const;
+ virtual real_t get_enclosing_radius() const;
CapsuleShape2D();
};
diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp
index 7142309e28..10f8ab8a8a 100644
--- a/scene/resources/circle_shape_2d.cpp
+++ b/scene/resources/circle_shape_2d.cpp
@@ -70,6 +70,10 @@ Rect2 CircleShape2D::get_rect() const {
return rect;
}
+real_t CircleShape2D::get_enclosing_radius() const {
+ return radius;
+}
+
void CircleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector<Vector2> points;
diff --git a/scene/resources/circle_shape_2d.h b/scene/resources/circle_shape_2d.h
index cafd291f13..8b064f4d9f 100644
--- a/scene/resources/circle_shape_2d.h
+++ b/scene/resources/circle_shape_2d.h
@@ -50,6 +50,7 @@ public:
virtual void draw(const RID &p_to_rid, const Color &p_color);
virtual Rect2 get_rect() const;
+ virtual real_t get_enclosing_radius() const;
CircleShape2D();
};
diff --git a/scene/resources/concave_polygon_shape.cpp b/scene/resources/concave_polygon_shape.cpp
index 48cc08eae4..0a93f99ea3 100644
--- a/scene/resources/concave_polygon_shape.cpp
+++ b/scene/resources/concave_polygon_shape.cpp
@@ -64,6 +64,16 @@ Vector<Vector3> ConcavePolygonShape::get_debug_mesh_lines() {
return points;
}
+real_t ConcavePolygonShape::get_enclosing_radius() const {
+ PoolVector<Vector3> data = get_faces();
+ PoolVector<Vector3>::Read read = data.read();
+ real_t r = 0;
+ for (int i(0); i < data.size(); i++) {
+ r = MAX(read[i].length_squared(), r);
+ }
+ return Math::sqrt(r);
+}
+
void ConcavePolygonShape::_update_shape() {
Shape::_update_shape();
}
diff --git a/scene/resources/concave_polygon_shape.h b/scene/resources/concave_polygon_shape.h
index 5e371954d4..b4bebbd7b4 100644
--- a/scene/resources/concave_polygon_shape.h
+++ b/scene/resources/concave_polygon_shape.h
@@ -66,7 +66,8 @@ public:
void set_faces(const PoolVector<Vector3> &p_faces);
PoolVector<Vector3> get_faces() const;
- Vector<Vector3> get_debug_mesh_lines();
+ virtual Vector<Vector3> get_debug_mesh_lines();
+ virtual real_t get_enclosing_radius() const;
ConcavePolygonShape();
};
diff --git a/scene/resources/concave_polygon_shape_2d.cpp b/scene/resources/concave_polygon_shape_2d.cpp
index 640f395158..840733add3 100644
--- a/scene/resources/concave_polygon_shape_2d.cpp
+++ b/scene/resources/concave_polygon_shape_2d.cpp
@@ -94,6 +94,16 @@ Rect2 ConcavePolygonShape2D::get_rect() const {
return rect;
}
+real_t ConcavePolygonShape2D::get_enclosing_radius() const {
+ PoolVector<Vector2> data = get_segments();
+ PoolVector<Vector2>::Read read = data.read();
+ real_t r = 0;
+ for (int i(0); i < data.size(); i++) {
+ r = MAX(read[i].length_squared(), r);
+ }
+ return Math::sqrt(r);
+}
+
void ConcavePolygonShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_segments", "segments"), &ConcavePolygonShape2D::set_segments);
diff --git a/scene/resources/concave_polygon_shape_2d.h b/scene/resources/concave_polygon_shape_2d.h
index b95ed100e7..4e47ad34b8 100644
--- a/scene/resources/concave_polygon_shape_2d.h
+++ b/scene/resources/concave_polygon_shape_2d.h
@@ -47,6 +47,7 @@ public:
virtual void draw(const RID &p_to_rid, const Color &p_color);
virtual Rect2 get_rect() const;
+ virtual real_t get_enclosing_radius() const;
ConcavePolygonShape2D();
};
diff --git a/scene/resources/convex_polygon_shape.cpp b/scene/resources/convex_polygon_shape.cpp
index 1498eb4af9..21fdcc1f06 100644
--- a/scene/resources/convex_polygon_shape.cpp
+++ b/scene/resources/convex_polygon_shape.cpp
@@ -55,6 +55,16 @@ Vector<Vector3> ConvexPolygonShape::get_debug_mesh_lines() {
return Vector<Vector3>();
}
+real_t ConvexPolygonShape::get_enclosing_radius() const {
+ PoolVector<Vector3> data = get_points();
+ PoolVector<Vector3>::Read read = data.read();
+ real_t r = 0;
+ for (int i(0); i < data.size(); i++) {
+ r = MAX(read[i].length_squared(), r);
+ }
+ return Math::sqrt(r);
+}
+
void ConvexPolygonShape::_update_shape() {
PhysicsServer::get_singleton()->shape_set_data(get_shape(), points);
diff --git a/scene/resources/convex_polygon_shape.h b/scene/resources/convex_polygon_shape.h
index 4725d09b26..e3bf02399a 100644
--- a/scene/resources/convex_polygon_shape.h
+++ b/scene/resources/convex_polygon_shape.h
@@ -48,6 +48,7 @@ public:
PoolVector<Vector3> get_points() const;
virtual Vector<Vector3> get_debug_mesh_lines();
+ virtual real_t get_enclosing_radius() const;
ConvexPolygonShape();
};
diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp
index c404190398..296d014cc7 100644
--- a/scene/resources/convex_polygon_shape_2d.cpp
+++ b/scene/resources/convex_polygon_shape_2d.cpp
@@ -97,6 +97,14 @@ Rect2 ConvexPolygonShape2D::get_rect() const {
return rect;
}
+real_t ConvexPolygonShape2D::get_enclosing_radius() const {
+ real_t r = 0;
+ for (int i(0); i < get_points().size(); i++) {
+ r = MAX(get_points()[i].length_squared(), r);
+ }
+ return Math::sqrt(r);
+}
+
ConvexPolygonShape2D::ConvexPolygonShape2D() :
Shape2D(Physics2DServer::get_singleton()->convex_polygon_shape_create()) {
}
diff --git a/scene/resources/convex_polygon_shape_2d.h b/scene/resources/convex_polygon_shape_2d.h
index ed6be738ab..83c250c0ce 100644
--- a/scene/resources/convex_polygon_shape_2d.h
+++ b/scene/resources/convex_polygon_shape_2d.h
@@ -51,6 +51,7 @@ public:
virtual void draw(const RID &p_to_rid, const Color &p_color);
virtual Rect2 get_rect() const;
+ virtual real_t get_enclosing_radius() const;
ConvexPolygonShape2D();
};
diff --git a/scene/resources/cylinder_shape.cpp b/scene/resources/cylinder_shape.cpp
index d9f6e4f054..b9b0d77a1b 100644
--- a/scene/resources/cylinder_shape.cpp
+++ b/scene/resources/cylinder_shape.cpp
@@ -62,6 +62,10 @@ Vector<Vector3> CylinderShape::get_debug_mesh_lines() {
return points;
}
+real_t CylinderShape::get_enclosing_radius() const {
+ return Vector2(radius, height * 0.5).length();
+}
+
void CylinderShape::_update_shape() {
Dictionary d;
diff --git a/scene/resources/cylinder_shape.h b/scene/resources/cylinder_shape.h
index ebddd4d92f..a26fda10cd 100644
--- a/scene/resources/cylinder_shape.h
+++ b/scene/resources/cylinder_shape.h
@@ -50,6 +50,7 @@ public:
float get_height() const;
virtual Vector<Vector3> get_debug_mesh_lines();
+ virtual real_t get_enclosing_radius() const;
CylinderShape();
};
diff --git a/scene/resources/height_map_shape.cpp b/scene/resources/height_map_shape.cpp
index 2b86d658d8..48c9221e27 100644
--- a/scene/resources/height_map_shape.cpp
+++ b/scene/resources/height_map_shape.cpp
@@ -76,6 +76,10 @@ Vector<Vector3> HeightMapShape::get_debug_mesh_lines() {
return points;
}
+real_t HeightMapShape::get_enclosing_radius() const {
+ return Vector3(real_t(map_width), max_height - min_height, real_t(map_depth)).length();
+}
+
void HeightMapShape::_update_shape() {
Dictionary d;
diff --git a/scene/resources/height_map_shape.h b/scene/resources/height_map_shape.h
index 3a976e3add..a6263f061f 100644
--- a/scene/resources/height_map_shape.h
+++ b/scene/resources/height_map_shape.h
@@ -55,6 +55,7 @@ public:
PoolRealArray get_map_data() const;
virtual Vector<Vector3> get_debug_mesh_lines();
+ virtual real_t get_enclosing_radius() const;
HeightMapShape();
};
diff --git a/scene/resources/line_shape_2d.cpp b/scene/resources/line_shape_2d.cpp
index 84da8125ea..d1bb61820b 100644
--- a/scene/resources/line_shape_2d.cpp
+++ b/scene/resources/line_shape_2d.cpp
@@ -100,6 +100,10 @@ Rect2 LineShape2D::get_rect() const {
return rect;
}
+real_t LineShape2D::get_enclosing_radius() const {
+ return d;
+}
+
void LineShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_normal", "normal"), &LineShape2D::set_normal);
diff --git a/scene/resources/line_shape_2d.h b/scene/resources/line_shape_2d.h
index 7babfe12f6..5bf9e85bb9 100644
--- a/scene/resources/line_shape_2d.h
+++ b/scene/resources/line_shape_2d.h
@@ -55,6 +55,7 @@ public:
virtual void draw(const RID &p_to_rid, const Color &p_color);
virtual Rect2 get_rect() const;
+ virtual real_t get_enclosing_radius() const;
LineShape2D();
};
diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h
index 471db74175..5f1fddae22 100644
--- a/scene/resources/mesh_library.h
+++ b/scene/resources/mesh_library.h
@@ -34,7 +34,7 @@
#include "core/map.h"
#include "core/resource.h"
#include "mesh.h"
-#include "scene/3d/navigation_mesh.h"
+#include "scene/3d/navigation_mesh_instance.h"
#include "shape.h"
class MeshLibrary : public Resource {
diff --git a/scene/3d/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index aaba91125e..e6544778bc 100644
--- a/scene/3d/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -29,8 +29,6 @@
/*************************************************************************/
#include "navigation_mesh.h"
-#include "mesh_instance.h"
-#include "navigation.h"
void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
@@ -548,197 +546,3 @@ NavigationMesh::NavigationMesh() {
filter_ledge_spans = false;
filter_walkable_low_height_spans = false;
}
-
-void NavigationMeshInstance::set_enabled(bool p_enabled) {
-
- if (enabled == p_enabled)
- return;
- enabled = p_enabled;
-
- if (!is_inside_tree())
- return;
-
- if (!enabled) {
-
- if (nav_id != -1) {
- navigation->navmesh_remove(nav_id);
- nav_id = -1;
- }
- } else {
-
- if (navigation) {
-
- if (navmesh.is_valid()) {
-
- nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this);
- }
- }
- }
-
- if (debug_view) {
- MeshInstance *dm = Object::cast_to<MeshInstance>(debug_view);
- if (is_enabled()) {
- dm->set_material_override(get_tree()->get_debug_navigation_material());
- } else {
- dm->set_material_override(get_tree()->get_debug_navigation_disabled_material());
- }
- }
-
- update_gizmo();
-}
-
-bool NavigationMeshInstance::is_enabled() const {
-
- return enabled;
-}
-
-/////////////////////////////
-
-void NavigationMeshInstance::_notification(int p_what) {
-
- switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
-
- Spatial *c = this;
- while (c) {
-
- navigation = Object::cast_to<Navigation>(c);
- if (navigation) {
-
- if (enabled && navmesh.is_valid()) {
-
- nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this);
- }
- break;
- }
-
- c = c->get_parent_spatial();
- }
-
- if (navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) {
-
- MeshInstance *dm = memnew(MeshInstance);
- dm->set_mesh(navmesh->get_debug_mesh());
- if (is_enabled()) {
- dm->set_material_override(get_tree()->get_debug_navigation_material());
- } else {
- dm->set_material_override(get_tree()->get_debug_navigation_disabled_material());
- }
- add_child(dm);
- debug_view = dm;
- }
-
- } break;
- case NOTIFICATION_TRANSFORM_CHANGED: {
-
- if (navigation && nav_id != -1) {
- navigation->navmesh_set_transform(nav_id, get_relative_transform(navigation));
- }
-
- } break;
- case NOTIFICATION_EXIT_TREE: {
-
- if (navigation) {
-
- if (nav_id != -1) {
- navigation->navmesh_remove(nav_id);
- nav_id = -1;
- }
- }
-
- if (debug_view) {
- debug_view->queue_delete();
- debug_view = NULL;
- }
- navigation = NULL;
- } break;
- }
-}
-
-void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh) {
-
- if (p_navmesh == navmesh)
- return;
-
- if (navigation && nav_id != -1) {
- navigation->navmesh_remove(nav_id);
- nav_id = -1;
- }
-
- if (navmesh.is_valid()) {
- navmesh->remove_change_receptor(this);
- }
-
- navmesh = p_navmesh;
-
- if (navmesh.is_valid()) {
- navmesh->add_change_receptor(this);
- }
-
- if (navigation && navmesh.is_valid() && enabled) {
- nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this);
- }
-
- if (debug_view && navmesh.is_valid()) {
- Object::cast_to<MeshInstance>(debug_view)->set_mesh(navmesh->get_debug_mesh());
- }
-
- update_gizmo();
- update_configuration_warning();
-}
-
-Ref<NavigationMesh> NavigationMeshInstance::get_navigation_mesh() const {
-
- return navmesh;
-}
-
-String NavigationMeshInstance::get_configuration_warning() const {
-
- if (!is_visible_in_tree() || !is_inside_tree())
- return String();
-
- if (!navmesh.is_valid()) {
- return TTR("A NavigationMesh resource must be set or created for this node to work.");
- }
- const Spatial *c = this;
- while (c) {
-
- if (Object::cast_to<Navigation>(c))
- return String();
-
- c = Object::cast_to<Spatial>(c->get_parent());
- }
-
- return TTR("NavigationMeshInstance must be a child or grandchild to a Navigation node. It only provides navigation data.");
-}
-
-void NavigationMeshInstance::_bind_methods() {
-
- ClassDB::bind_method(D_METHOD("set_navigation_mesh", "navmesh"), &NavigationMeshInstance::set_navigation_mesh);
- ClassDB::bind_method(D_METHOD("get_navigation_mesh"), &NavigationMeshInstance::get_navigation_mesh);
-
- ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationMeshInstance::set_enabled);
- ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationMeshInstance::is_enabled);
-
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
-}
-
-void NavigationMeshInstance::_changed_callback(Object *p_changed, const char *p_prop) {
- update_gizmo();
- update_configuration_warning();
-}
-
-NavigationMeshInstance::NavigationMeshInstance() {
-
- debug_view = NULL;
- navigation = NULL;
- nav_id = -1;
- enabled = true;
- set_notify_transform(true);
-}
-
-NavigationMeshInstance::~NavigationMeshInstance() {
- if (navmesh.is_valid())
- navmesh->remove_change_receptor(this);
-}
diff --git a/scene/3d/navigation_mesh.h b/scene/resources/navigation_mesh.h
index f9ab911bea..a2b1c62eab 100644
--- a/scene/3d/navigation_mesh.h
+++ b/scene/resources/navigation_mesh.h
@@ -31,7 +31,6 @@
#ifndef NAVIGATION_MESH_H
#define NAVIGATION_MESH_H
-#include "scene/3d/spatial.h"
#include "scene/resources/mesh.h"
class Mesh;
@@ -193,35 +192,4 @@ public:
NavigationMesh();
};
-class Navigation;
-
-class NavigationMeshInstance : public Spatial {
-
- GDCLASS(NavigationMeshInstance, Spatial);
-
- bool enabled;
- int nav_id;
- Navigation *navigation;
- Ref<NavigationMesh> navmesh;
-
- Node *debug_view;
-
-protected:
- void _notification(int p_what);
- static void _bind_methods();
- void _changed_callback(Object *p_changed, const char *p_prop);
-
-public:
- void set_enabled(bool p_enabled);
- bool is_enabled() const;
-
- void set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh);
- Ref<NavigationMesh> get_navigation_mesh() const;
-
- String get_configuration_warning() const;
-
- NavigationMeshInstance();
- ~NavigationMeshInstance();
-};
-
#endif // NAVIGATION_MESH_H
diff --git a/scene/resources/plane_shape.h b/scene/resources/plane_shape.h
index 8bea1268e5..360f9dbbe9 100644
--- a/scene/resources/plane_shape.h
+++ b/scene/resources/plane_shape.h
@@ -47,6 +47,10 @@ public:
Plane get_plane() const;
virtual Vector<Vector3> get_debug_mesh_lines();
+ virtual real_t get_enclosing_radius() const {
+ // Should be infinite?
+ return 0;
+ }
PlaneShape();
};
diff --git a/scene/resources/ray_shape.cpp b/scene/resources/ray_shape.cpp
index 3062e96293..1a9b7e6dd2 100644
--- a/scene/resources/ray_shape.cpp
+++ b/scene/resources/ray_shape.cpp
@@ -41,6 +41,10 @@ Vector<Vector3> RayShape::get_debug_mesh_lines() {
return points;
}
+real_t RayShape::get_enclosing_radius() const {
+ return length;
+}
+
void RayShape::_update_shape() {
Dictionary d;
diff --git a/scene/resources/ray_shape.h b/scene/resources/ray_shape.h
index ddf9f56ea3..c89705ad7d 100644
--- a/scene/resources/ray_shape.h
+++ b/scene/resources/ray_shape.h
@@ -50,6 +50,7 @@ public:
bool get_slips_on_slope() const;
virtual Vector<Vector3> get_debug_mesh_lines();
+ virtual real_t get_enclosing_radius() const;
RayShape();
};
diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp
index 0030e6dd4f..f8c8ffb289 100644
--- a/scene/resources/rectangle_shape_2d.cpp
+++ b/scene/resources/rectangle_shape_2d.cpp
@@ -59,6 +59,10 @@ Rect2 RectangleShape2D::get_rect() const {
return Rect2(-extents, extents * 2.0);
}
+real_t RectangleShape2D::get_enclosing_radius() const {
+ return extents.length();
+}
+
void RectangleShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_extents", "extents"), &RectangleShape2D::set_extents);
diff --git a/scene/resources/rectangle_shape_2d.h b/scene/resources/rectangle_shape_2d.h
index 686b421a2a..68fc539085 100644
--- a/scene/resources/rectangle_shape_2d.h
+++ b/scene/resources/rectangle_shape_2d.h
@@ -48,6 +48,7 @@ public:
virtual void draw(const RID &p_to_rid, const Color &p_color);
virtual Rect2 get_rect() const;
+ virtual real_t get_enclosing_radius() const;
RectangleShape2D();
};
diff --git a/scene/resources/segment_shape_2d.cpp b/scene/resources/segment_shape_2d.cpp
index 0070de72a2..3094d0f9bd 100644
--- a/scene/resources/segment_shape_2d.cpp
+++ b/scene/resources/segment_shape_2d.cpp
@@ -82,6 +82,10 @@ Rect2 SegmentShape2D::get_rect() const {
return rect;
}
+real_t SegmentShape2D::get_enclosing_radius() const {
+ return (a + b).length();
+}
+
void SegmentShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_a", "a"), &SegmentShape2D::set_a);
@@ -138,6 +142,10 @@ Rect2 RayShape2D::get_rect() const {
return rect;
}
+real_t RayShape2D::get_enclosing_radius() const {
+ return length;
+}
+
void RayShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape2D::set_length);
diff --git a/scene/resources/segment_shape_2d.h b/scene/resources/segment_shape_2d.h
index aaf838ebfb..ca10c24f07 100644
--- a/scene/resources/segment_shape_2d.h
+++ b/scene/resources/segment_shape_2d.h
@@ -55,6 +55,7 @@ public:
virtual void draw(const RID &p_to_rid, const Color &p_color);
virtual Rect2 get_rect() const;
+ virtual real_t get_enclosing_radius() const;
SegmentShape2D();
};
@@ -79,6 +80,7 @@ public:
virtual void draw(const RID &p_to_rid, const Color &p_color);
virtual Rect2 get_rect() const;
+ virtual real_t get_enclosing_radius() const;
RayShape2D();
};
diff --git a/scene/resources/shape.h b/scene/resources/shape.h
index 8fc265f3bc..227a581c96 100644
--- a/scene/resources/shape.h
+++ b/scene/resources/shape.h
@@ -32,6 +32,7 @@
#define SHAPE_H
#include "core/resource.h"
+
class ArrayMesh;
class Shape : public Resource {
@@ -57,6 +58,8 @@ public:
Ref<ArrayMesh> get_debug_mesh();
virtual Vector<Vector3> get_debug_mesh_lines() = 0; // { return Vector<Vector3>(); }
+ /// Returns the radius of a sphere that fully enclose this shape
+ virtual real_t get_enclosing_radius() const = 0;
void add_vertices_to_array(PoolVector<Vector3> &array, const Transform &p_xform);
diff --git a/scene/resources/shape_2d.h b/scene/resources/shape_2d.h
index 13ad7492ae..e2933ec031 100644
--- a/scene/resources/shape_2d.h
+++ b/scene/resources/shape_2d.h
@@ -58,6 +58,8 @@ public:
virtual void draw(const RID &p_to_rid, const Color &p_color) {}
virtual Rect2 get_rect() const { return Rect2(); }
+ /// Returns the radius of a circle that fully enclose this shape
+ virtual real_t get_enclosing_radius() const = 0;
virtual RID get_rid() const;
Shape2D();
~Shape2D();
diff --git a/scene/resources/sphere_shape.cpp b/scene/resources/sphere_shape.cpp
index f408717834..56121b6941 100644
--- a/scene/resources/sphere_shape.cpp
+++ b/scene/resources/sphere_shape.cpp
@@ -55,6 +55,10 @@ Vector<Vector3> SphereShape::get_debug_mesh_lines() {
return points;
}
+real_t SphereShape::get_enclosing_radius() const {
+ return radius;
+}
+
void SphereShape::_update_shape() {
PhysicsServer::get_singleton()->shape_set_data(get_shape(), radius);
diff --git a/scene/resources/sphere_shape.h b/scene/resources/sphere_shape.h
index 6002a3baeb..07e8f1e233 100644
--- a/scene/resources/sphere_shape.h
+++ b/scene/resources/sphere_shape.h
@@ -48,6 +48,7 @@ public:
float get_radius() const;
virtual Vector<Vector3> get_debug_mesh_lines();
+ virtual real_t get_enclosing_radius() const;
SphereShape();
};
diff --git a/scene/resources/world.cpp b/scene/resources/world.cpp
index 1099852098..e1e3974016 100644
--- a/scene/resources/world.cpp
+++ b/scene/resources/world.cpp
@@ -262,6 +262,7 @@ RID World::get_space() const {
return space;
}
+
RID World::get_scenario() const {
return scenario;
diff --git a/servers/navigation_2d_server.cpp b/servers/navigation_2d_server.cpp
new file mode 100644
index 0000000000..d4ca402136
--- /dev/null
+++ b/servers/navigation_2d_server.cpp
@@ -0,0 +1,218 @@
+/*************************************************************************/
+/* navigation_2d_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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 "servers/navigation_2d_server.h"
+#include "core/math/transform.h"
+#include "core/math/transform_2d.h"
+#include "servers/navigation_server.h"
+
+/**
+ @author AndreaCatania
+*/
+
+Navigation2DServer *Navigation2DServer::singleton = NULL;
+
+#define FORWARD_0_C(FUNC_NAME) \
+ Navigation2DServer::FUNC_NAME() \
+ const { \
+ return NavigationServer::get_singleton()->FUNC_NAME(); \
+ }
+
+#define FORWARD_1(FUNC_NAME, T_0, D_0, CONV_0) \
+ Navigation2DServer::FUNC_NAME(T_0 D_0) { \
+ return NavigationServer::get_singleton_mut()->FUNC_NAME(CONV_0(D_0)); \
+ }
+
+#define FORWARD_1_C(FUNC_NAME, T_0, D_0, CONV_0) \
+ Navigation2DServer::FUNC_NAME(T_0 D_0) \
+ const { \
+ return NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0)); \
+ }
+
+#define FORWARD_2_C(FUNC_NAME, T_0, D_0, T_1, D_1, CONV_0, CONV_1) \
+ Navigation2DServer::FUNC_NAME(T_0 D_0, T_1 D_1) \
+ const { \
+ return NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1)); \
+ }
+
+#define FORWARD_4_R_C(CONV_R, FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, CONV_0, CONV_1, CONV_2, CONV_3) \
+ Navigation2DServer::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) \
+ const { \
+ return CONV_R(NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3))); \
+ }
+
+#define FORWARD_4_C(FUNC_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, CONV_0, CONV_1, CONV_2, CONV_3) \
+ Navigation2DServer::FUNC_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) \
+ const { \
+ return NavigationServer::get_singleton()->FUNC_NAME(CONV_0(D_0), CONV_1(D_1), CONV_2(D_2), CONV_3(D_3)); \
+ }
+
+RID rid_to_rid(const RID d) {
+ return d;
+}
+bool bool_to_bool(const bool d) {
+ return d;
+}
+int int_to_int(const int d) {
+ return d;
+}
+real_t real_to_real(const real_t d) {
+ return d;
+}
+Vector3 v2_to_v3(const Vector2 d) {
+ return Vector3(d.x, 0.0, d.y);
+}
+Vector2 v3_to_v2(const Vector3 &d) {
+ return Vector2(d.x, d.z);
+}
+Vector<Vector2> vector_v3_to_v2(const Vector<Vector3> &d) {
+ Vector<Vector2> nd;
+ nd.resize(d.size());
+ for (int i(0); i < nd.size(); i++) {
+ nd.write[i] = v3_to_v2(d[i]);
+ }
+ return nd;
+}
+Transform trf2_to_trf3(const Transform2D &d) {
+ Vector3 o(v2_to_v3(d.get_origin()));
+ Basis b;
+ b.rotate(Vector3(0, 1, 0), d.get_rotation());
+ return Transform(b, o);
+}
+Object *obj_to_obj(Object *d) {
+ return d;
+}
+StringName sn_to_sn(StringName &d) {
+ return d;
+}
+Variant var_to_var(Variant &d) {
+ return d;
+}
+Ref<NavigationMesh> poly_to_mesh(Ref<NavigationPolygon> d) {
+ if (d.is_valid()) {
+ return d->get_mesh();
+ } else {
+ return Ref<NavigationMesh>();
+ }
+}
+
+void Navigation2DServer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("map_create"), &Navigation2DServer::map_create);
+ ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &Navigation2DServer::map_set_active);
+ ClassDB::bind_method(D_METHOD("map_is_active", "nap"), &Navigation2DServer::map_is_active);
+ ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &Navigation2DServer::map_set_cell_size);
+ ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &Navigation2DServer::map_get_cell_size);
+ ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &Navigation2DServer::map_set_edge_connection_margin);
+ ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &Navigation2DServer::map_get_edge_connection_margin);
+ ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize"), &Navigation2DServer::map_get_path);
+
+ ClassDB::bind_method(D_METHOD("region_create"), &Navigation2DServer::region_create);
+ ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &Navigation2DServer::region_set_map);
+ ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &Navigation2DServer::region_set_transform);
+ ClassDB::bind_method(D_METHOD("region_set_navpoly", "region", "nav_poly"), &Navigation2DServer::region_set_navpoly);
+
+ ClassDB::bind_method(D_METHOD("agent_create"), &Navigation2DServer::agent_create);
+ ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &Navigation2DServer::agent_set_map);
+ ClassDB::bind_method(D_METHOD("agent_set_neighbor_dist", "agent", "dist"), &Navigation2DServer::agent_set_neighbor_dist);
+ ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &Navigation2DServer::agent_set_max_neighbors);
+ ClassDB::bind_method(D_METHOD("agent_set_time_horizon", "agent", "time"), &Navigation2DServer::agent_set_time_horizon);
+ ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &Navigation2DServer::agent_set_radius);
+ ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &Navigation2DServer::agent_set_max_speed);
+ ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &Navigation2DServer::agent_set_velocity);
+ ClassDB::bind_method(D_METHOD("agent_set_target_velocity", "agent", "target_velocity"), &Navigation2DServer::agent_set_target_velocity);
+ ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &Navigation2DServer::agent_set_position);
+ ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &Navigation2DServer::agent_is_map_changed);
+ ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "receiver", "method", "userdata"), &Navigation2DServer::agent_set_callback, DEFVAL(Variant()));
+
+ ClassDB::bind_method(D_METHOD("free", "object"), &Navigation2DServer::free);
+}
+
+Navigation2DServer::Navigation2DServer() {
+ singleton = this;
+}
+
+Navigation2DServer::~Navigation2DServer() {
+ singleton = NULL;
+}
+
+RID FORWARD_0_C(map_create);
+
+void FORWARD_2_C(map_set_active, RID, p_map, bool, p_active, rid_to_rid, bool_to_bool);
+
+bool FORWARD_1_C(map_is_active, RID, p_map, rid_to_rid);
+
+void FORWARD_2_C(map_set_cell_size, RID, p_map, real_t, p_cell_size, rid_to_rid, real_to_real);
+real_t FORWARD_1_C(map_get_cell_size, RID, p_map, rid_to_rid);
+
+void FORWARD_2_C(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin, rid_to_rid, real_to_real);
+real_t FORWARD_1_C(map_get_edge_connection_margin, RID, p_map, rid_to_rid);
+
+Vector<Vector2> FORWARD_4_R_C(vector_v3_to_v2, map_get_path, RID, p_map, Vector2, p_origin, Vector2, p_destination, bool, p_optimize, rid_to_rid, v2_to_v3, v2_to_v3, bool_to_bool);
+
+RID FORWARD_0_C(region_create);
+void FORWARD_2_C(region_set_map, RID, p_region, RID, p_map, rid_to_rid, rid_to_rid);
+
+void FORWARD_2_C(region_set_transform, RID, p_region, Transform2D, p_transform, rid_to_rid, trf2_to_trf3);
+
+void Navigation2DServer::region_set_navpoly(RID p_region, Ref<NavigationPolygon> p_nav_mesh) const {
+ NavigationServer::get_singleton()->region_set_navmesh(p_region, poly_to_mesh(p_nav_mesh));
+}
+
+RID Navigation2DServer::agent_create() const {
+ RID agent = NavigationServer::get_singleton()->agent_create();
+ NavigationServer::get_singleton()->agent_set_ignore_y(agent, true);
+ return agent;
+}
+
+void FORWARD_2_C(agent_set_map, RID, p_agent, RID, p_map, rid_to_rid, rid_to_rid);
+
+void FORWARD_2_C(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist, rid_to_rid, real_to_real);
+
+void FORWARD_2_C(agent_set_max_neighbors, RID, p_agent, int, p_count, rid_to_rid, int_to_int);
+
+void FORWARD_2_C(agent_set_time_horizon, RID, p_agent, real_t, p_time, rid_to_rid, real_to_real);
+
+void FORWARD_2_C(agent_set_radius, RID, p_agent, real_t, p_radius, rid_to_rid, real_to_real);
+
+void FORWARD_2_C(agent_set_max_speed, RID, p_agent, real_t, p_max_speed, rid_to_rid, real_to_real);
+
+void FORWARD_2_C(agent_set_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
+
+void FORWARD_2_C(agent_set_target_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
+
+void FORWARD_2_C(agent_set_position, RID, p_agent, Vector2, p_position, rid_to_rid, v2_to_v3);
+
+void FORWARD_2_C(agent_set_ignore_y, RID, p_agent, bool, p_ignore, rid_to_rid, bool_to_bool);
+
+bool FORWARD_1_C(agent_is_map_changed, RID, p_agent, rid_to_rid);
+
+void FORWARD_4_C(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_method, Variant, p_udata, rid_to_rid, obj_to_obj, sn_to_sn, var_to_var);
+
+void FORWARD_1_C(free, RID, p_object, rid_to_rid);
diff --git a/servers/navigation_2d_server.h b/servers/navigation_2d_server.h
new file mode 100644
index 0000000000..0cab699c68
--- /dev/null
+++ b/servers/navigation_2d_server.h
@@ -0,0 +1,160 @@
+/*************************************************************************/
+/* navigation_2d_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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. */
+/*************************************************************************/
+
+/**
+ @author AndreaCatania
+*/
+
+#ifndef NAVIGATION_2D_SERVER_H
+#define NAVIGATION_2D_SERVER_H
+
+#include "core/object.h"
+#include "core/rid.h"
+#include "scene/2d/navigation_polygon.h"
+
+// This server exposes the 3D `NavigationServer` features in the 2D world.
+class Navigation2DServer : public Object {
+ GDCLASS(Navigation2DServer, Object);
+
+ static Navigation2DServer *singleton;
+
+protected:
+ static void _bind_methods();
+
+public:
+ /// Thread safe, can be used accross many threads.
+ static const Navigation2DServer *get_singleton() { return singleton; }
+
+ /// MUST be used in single thread!
+ static Navigation2DServer *get_singleton_mut() { return singleton; }
+
+ /// Create a new map.
+ virtual RID map_create() const;
+
+ /// Set map active.
+ virtual void map_set_active(RID p_map, bool p_active) const;
+
+ /// Returns true if the map is active.
+ virtual bool map_is_active(RID p_map) const;
+
+ /// Set the map cell size used to weld the navigation mesh polygons.
+ virtual void map_set_cell_size(RID p_map, real_t p_cell_size) const;
+
+ /// Returns the map cell size.
+ virtual real_t map_get_cell_size(RID p_map) const;
+
+ /// Set the map edge connection margin used to weld the compatible region edges.
+ virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) const;
+
+ /// Returns the edge connection margin of this map.
+ virtual real_t map_get_edge_connection_margin(RID p_map) const;
+
+ /// Returns the navigation path to reach the destination from the origin.
+ virtual Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize) const;
+
+ /// Creates a new region.
+ virtual RID region_create() const;
+
+ /// Set the map of this region.
+ virtual void region_set_map(RID p_region, RID p_map) const;
+
+ /// Set the global transformation of this region.
+ virtual void region_set_transform(RID p_region, Transform2D p_transform) const;
+
+ /// Set the navigation poly of this region.
+ virtual void region_set_navpoly(RID p_region, Ref<NavigationPolygon> p_nav_mesh) const;
+
+ /// Creates the agent.
+ virtual RID agent_create() const;
+
+ /// Put the agent in the map.
+ virtual void agent_set_map(RID p_agent, RID p_map) const;
+
+ /// The maximum distance (center point to
+ /// center point) to other agents this agent
+ /// takes into account in the navigation. The
+ /// larger this number, the longer the running
+ /// time of the simulation. If the number is too
+ /// low, the simulation will not be safe.
+ /// Must be non-negative.
+ virtual void agent_set_neighbor_dist(RID p_agent, real_t p_dist) const;
+
+ /// The maximum number of other agents this
+ /// agent takes into account in the navigation.
+ /// The larger this number, the longer the
+ /// running time of the simulation. If the
+ /// number is too low, the simulation will not
+ /// be safe.
+ virtual void agent_set_max_neighbors(RID p_agent, int p_count) const;
+
+ /// The minimal amount of time for which this
+ /// agent's velocities that are computed by the
+ /// simulation are safe with respect to other
+ /// agents. The larger this number, the sooner
+ /// this agent will respond to the presence of
+ /// other agents, but the less freedom this
+ /// agent has in choosing its velocities.
+ /// Must be positive.
+ virtual void agent_set_time_horizon(RID p_agent, real_t p_time) const;
+
+ /// The radius of this agent.
+ /// Must be non-negative.
+ virtual void agent_set_radius(RID p_agent, real_t p_radius) const;
+
+ /// The maximum speed of this agent.
+ /// Must be non-negative.
+ virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) const;
+
+ /// Current velocity of the agent
+ virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity) const;
+
+ /// The new target velocity.
+ virtual void agent_set_target_velocity(RID p_agent, Vector2 p_velocity) const;
+
+ /// Position of the agent in world space.
+ virtual void agent_set_position(RID p_agent, Vector2 p_position) const;
+
+ /// Agent ignore the Y axis and avoid collisions by moving only on the horizontal plane
+ virtual void agent_set_ignore_y(RID p_agent, bool p_ignore) const;
+
+ /// Returns true if the map got changed the previous frame.
+ virtual bool agent_is_map_changed(RID p_agent) const;
+
+ /// Callback called at the end of the RVO process
+ virtual void agent_set_callback(RID p_agent, Object *p_receiver, StringName p_method, Variant p_udata = Variant()) const;
+
+ /// Destroy the `RID`
+ virtual void free(RID p_object) const;
+
+ Navigation2DServer();
+ virtual ~Navigation2DServer();
+};
+
+#endif
diff --git a/servers/navigation_server.cpp b/servers/navigation_server.cpp
new file mode 100644
index 0000000000..c4c54cbce1
--- /dev/null
+++ b/servers/navigation_server.cpp
@@ -0,0 +1,103 @@
+/*************************************************************************/
+/* navigation_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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. */
+/*************************************************************************/
+
+/**
+ @author AndreaCatania
+*/
+
+#include "navigation_server.h"
+
+NavigationServer *NavigationServer::singleton = NULL;
+
+void NavigationServer::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("map_create"), &NavigationServer::map_create);
+ ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &NavigationServer::map_set_active);
+ ClassDB::bind_method(D_METHOD("map_is_active", "nap"), &NavigationServer::map_is_active);
+ ClassDB::bind_method(D_METHOD("map_set_up", "map", "up"), &NavigationServer::map_set_up);
+ ClassDB::bind_method(D_METHOD("map_get_up", "map"), &NavigationServer::map_get_up);
+ ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &NavigationServer::map_set_cell_size);
+ ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &NavigationServer::map_get_cell_size);
+ ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer::map_set_edge_connection_margin);
+ ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer::map_get_edge_connection_margin);
+ ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize"), &NavigationServer::map_get_path);
+
+ ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer::region_create);
+ ClassDB::bind_method(D_METHOD("region_set_map", "region", "map"), &NavigationServer::region_set_map);
+ ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer::region_set_transform);
+ ClassDB::bind_method(D_METHOD("region_set_navmesh", "region", "nav_mesh"), &NavigationServer::region_set_navmesh);
+ ClassDB::bind_method(D_METHOD("region_bake_navmesh", "mesh", "node"), &NavigationServer::region_bake_navmesh);
+
+ ClassDB::bind_method(D_METHOD("agent_create"), &NavigationServer::agent_create);
+ ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &NavigationServer::agent_set_map);
+ ClassDB::bind_method(D_METHOD("agent_set_neighbor_dist", "agent", "dist"), &NavigationServer::agent_set_neighbor_dist);
+ ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &NavigationServer::agent_set_max_neighbors);
+ ClassDB::bind_method(D_METHOD("agent_set_time_horizon", "agent", "time"), &NavigationServer::agent_set_time_horizon);
+ ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &NavigationServer::agent_set_radius);
+ ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &NavigationServer::agent_set_max_speed);
+ ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &NavigationServer::agent_set_velocity);
+ ClassDB::bind_method(D_METHOD("agent_set_target_velocity", "agent", "target_velocity"), &NavigationServer::agent_set_target_velocity);
+ ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer::agent_set_position);
+ ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer::agent_is_map_changed);
+ ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "receiver", "method", "userdata"), &NavigationServer::agent_set_callback, DEFVAL(Variant()));
+
+ ClassDB::bind_method(D_METHOD("free", "object"), &NavigationServer::free);
+
+ ClassDB::bind_method(D_METHOD("set_active", "active"), &NavigationServer::set_active);
+ ClassDB::bind_method(D_METHOD("step", "delta_time"), &NavigationServer::step);
+}
+
+const NavigationServer *NavigationServer::get_singleton() {
+ return singleton;
+}
+
+NavigationServer *NavigationServer::get_singleton_mut() {
+ return singleton;
+}
+
+NavigationServer::NavigationServer() {
+ ERR_FAIL_COND(singleton != NULL);
+ singleton = this;
+}
+
+NavigationServer::~NavigationServer() {
+ singleton = NULL;
+}
+
+NavigationServerCallback NavigationServerManager::create_callback = NULL;
+
+void NavigationServerManager::set_default_server(NavigationServerCallback p_callback) {
+ create_callback = p_callback;
+}
+
+NavigationServer *NavigationServerManager::new_default_server() {
+ ERR_FAIL_COND_V(create_callback == NULL, NULL);
+ return create_callback();
+}
diff --git a/servers/navigation_server.h b/servers/navigation_server.h
new file mode 100644
index 0000000000..4b1566b189
--- /dev/null
+++ b/servers/navigation_server.h
@@ -0,0 +1,192 @@
+/*************************************************************************/
+/* navigation_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* 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. */
+/*************************************************************************/
+
+/**
+ @author AndreaCatania
+*/
+
+#ifndef NAVIGATION_SERVER_H
+#define NAVIGATION_SERVER_H
+
+#include "core/object.h"
+#include "core/rid.h"
+#include "scene/3d/navigation_mesh_instance.h"
+
+/// This server uses the concept of internal mutability.
+/// All the constant functions can be called in multithread because internally
+/// the server takes care to schedule the functions access.
+///
+/// Note: All the `set` functions are commands executed during the `sync` phase,
+/// don't expect that a change is immediately propagated.
+class NavigationServer : public Object {
+ GDCLASS(NavigationServer, Object);
+
+ static NavigationServer *singleton;
+
+protected:
+ static void _bind_methods();
+
+public:
+ /// Thread safe, can be used accross many threads.
+ static const NavigationServer *get_singleton();
+
+ /// MUST be used in single thread!
+ static NavigationServer *get_singleton_mut();
+
+ /// Create a new map.
+ virtual RID map_create() const = 0;
+
+ /// Set map active.
+ virtual void map_set_active(RID p_map, bool p_active) const = 0;
+
+ /// Returns true if the map is active.
+ virtual bool map_is_active(RID p_map) const = 0;
+
+ /// Set the map UP direction.
+ virtual void map_set_up(RID p_map, Vector3 p_up) const = 0;
+
+ /// Returns the map UP direction.
+ virtual Vector3 map_get_up(RID p_map) const = 0;
+
+ /// Set the map cell size used to weld the navigation mesh polygons.
+ virtual void map_set_cell_size(RID p_map, real_t p_cell_size) const = 0;
+
+ /// Returns the map cell size.
+ virtual real_t map_get_cell_size(RID p_map) const = 0;
+
+ /// Set the map edge connection margin used to weld the compatible region edges.
+ virtual void map_set_edge_connection_margin(RID p_map, real_t p_connection_margin) const = 0;
+
+ /// Returns the edge connection margin of this map.
+ virtual real_t map_get_edge_connection_margin(RID p_map) const = 0;
+
+ /// Returns the navigation path to reach the destination from the origin.
+ virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize) const = 0;
+
+ /// Creates a new region.
+ virtual RID region_create() const = 0;
+
+ /// Set the map of this region.
+ virtual void region_set_map(RID p_region, RID p_map) const = 0;
+
+ /// Set the global transformation of this region.
+ virtual void region_set_transform(RID p_region, Transform p_transform) const = 0;
+
+ /// Set the navigation mesh of this region.
+ virtual void region_set_navmesh(RID p_region, Ref<NavigationMesh> p_nav_mesh) const = 0;
+
+ /// Bake the navigation mesh
+ virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const = 0;
+
+ /// Creates the agent.
+ virtual RID agent_create() const = 0;
+
+ /// Put the agent in the map.
+ virtual void agent_set_map(RID p_agent, RID p_map) const = 0;
+
+ /// The maximum distance (center point to
+ /// center point) to other agents this agent
+ /// takes into account in the navigation. The
+ /// larger this number, the longer the running
+ /// time of the simulation. If the number is too
+ /// low, the simulation will not be safe.
+ /// Must be non-negative.
+ virtual void agent_set_neighbor_dist(RID p_agent, real_t p_dist) const = 0;
+
+ /// The maximum number of other agents this
+ /// agent takes into account in the navigation.
+ /// The larger this number, the longer the
+ /// running time of the simulation. If the
+ /// number is too low, the simulation will not
+ /// be safe.
+ virtual void agent_set_max_neighbors(RID p_agent, int p_count) const = 0;
+
+ /// The minimal amount of time for which this
+ /// agent's velocities that are computed by the
+ /// simulation are safe with respect to other
+ /// agents. The larger this number, the sooner
+ /// this agent will respond to the presence of
+ /// other agents, but the less freedom this
+ /// agent has in choosing its velocities.
+ /// Must be positive.
+ virtual void agent_set_time_horizon(RID p_agent, real_t p_time) const = 0;
+
+ /// The radius of this agent.
+ /// Must be non-negative.
+ virtual void agent_set_radius(RID p_agent, real_t p_radius) const = 0;
+
+ /// The maximum speed of this agent.
+ /// Must be non-negative.
+ virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) const = 0;
+
+ /// Current velocity of the agent
+ virtual void agent_set_velocity(RID p_agent, Vector3 p_velocity) const = 0;
+
+ /// The new target velocity.
+ virtual void agent_set_target_velocity(RID p_agent, Vector3 p_velocity) const = 0;
+
+ /// Position of the agent in world space.
+ virtual void agent_set_position(RID p_agent, Vector3 p_position) const = 0;
+
+ /// Agent ignore the Y axis and avoid collisions by moving only on the horizontal plane
+ virtual void agent_set_ignore_y(RID p_agent, bool p_ignore) const = 0;
+
+ /// Returns true if the map got changed the previous frame.
+ virtual bool agent_is_map_changed(RID p_agent) const = 0;
+
+ /// Callback called at the end of the RVO process
+ virtual void agent_set_callback(RID p_agent, Object *p_receiver, StringName p_method, Variant p_udata = Variant()) const = 0;
+
+ /// Destroy the `RID`
+ virtual void free(RID p_object) const = 0;
+
+ /// Control activation of this server.
+ virtual void set_active(bool p_active) const = 0;
+
+ /// Step the server
+ /// NOTE: This function is not Threadsafe and MUST be called in single thread.
+ virtual void step(real_t delta_time) = 0;
+
+ NavigationServer();
+ virtual ~NavigationServer();
+};
+
+typedef NavigationServer *(*NavigationServerCallback)();
+
+/// Manager used for the server singleton registration
+class NavigationServerManager {
+ static NavigationServerCallback create_callback;
+
+public:
+ static void set_default_server(NavigationServerCallback p_callback);
+ static NavigationServer *new_default_server();
+};
+
+#endif
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index 3ff736ad82..f25ef4ae84 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -56,6 +56,8 @@
#include "audio_server.h"
#include "camera/camera_feed.h"
#include "camera_server.h"
+#include "navigation_2d_server.h"
+#include "navigation_server.h"
#include "physics/physics_server_sw.h"
#include "physics_2d/physics_2d_server_sw.h"
#include "physics_2d/physics_2d_server_wrap_mt.h"
@@ -212,6 +214,8 @@ void register_server_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("AudioServer", AudioServer::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer", PhysicsServer::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("Physics2DServer", Physics2DServer::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer", NavigationServer::get_singleton_mut()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("Navigation2DServer", Navigation2DServer::get_singleton_mut()));
Engine::get_singleton()->add_singleton(Engine::Singleton("ARVRServer", ARVRServer::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton()));
}
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 7c3378dec3..8143b4af56 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -432,7 +432,7 @@ Files extracted from upstream source:
## recastnavigation
- Upstream: https://github.com/recastnavigation/recastnavigation
-- version: git (ef3ea40f, 2017)
+- Version: git (ef3ea40f, 2017)
- License: zlib
Files extracted from upstream source:
@@ -441,6 +441,22 @@ Files extracted from upstream source:
- License.txt
+## Rvo2
+
+- Upstream: http://gamma.cs.unc.edu/RVO2/
+- Version: 3D - 1.0.1
+- License: Apache 2.0
+
+Files extracted from upstream source:
+
+- All .cpp and .h files in the `src/` folder except for RVOSimulator.cpp and RVOSimulator.h
+- LICENSE
+
+Important: Some files have Godot-made changes; so to enrich the features
+originally proposed by this library and better integrate this library with
+Godot. Please check the file to know what's new.
+
+
## squish
- Upstream: https://sourceforge.net/projects/libsquish
diff --git a/thirdparty/rvo2/LICENSE b/thirdparty/rvo2/LICENSE
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/thirdparty/rvo2/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/thirdparty/rvo2/README.md b/thirdparty/rvo2/README.md
new file mode 100644
index 0000000000..96af597cb6
--- /dev/null
+++ b/thirdparty/rvo2/README.md
@@ -0,0 +1,32 @@
+Optimal Reciprocal Collision Avoidance in Three Dimensions
+==========================================================
+
+<http://gamma.cs.unc.edu/RVO2/>
+
+[![Build Status](https://travis-ci.org/snape/RVO2-3D.png?branch=master)](https://travis-ci.org/snape/RVO2-3D)
+[![Build status](https://ci.appveyor.com/api/projects/status/ov8ec3igv588wpx7/branch/master?svg=true)](https://ci.appveyor.com/project/snape/rvo2-3d)
+
+Copyright 2008 University of North Carolina at Chapel Hill
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+<http://www.apache.org/licenses/LICENSE-2.0>
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Please send all bug reports to [geom@cs.unc.edu](mailto:geom@cs.unc.edu).
+
+The authors may be contacted via:
+
+Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, and Dinesh Manocha
+Dept. of Computer Science
+201 S. Columbia St.
+Frederick P. Brooks, Jr. Computer Science Bldg.
+Chapel Hill, N.C. 27599-3175
+United States of America
diff --git a/thirdparty/rvo2/src/API.h b/thirdparty/rvo2/src/API.h
new file mode 100644
index 0000000000..c63a5a383c
--- /dev/null
+++ b/thirdparty/rvo2/src/API.h
@@ -0,0 +1,71 @@
+/*
+ * API.h
+ * RVO2-3D Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+/**
+ * \file API.h
+ * \brief Contains definitions related to Microsoft Windows.
+ */
+
+#ifndef RVO_API_H_
+#define RVO_API_H_
+
+#ifdef _WIN32
+#include <SDKDDKVer.h>
+#define WIN32_LEAN_AND_MEAN
+#define NOCOMM
+#define NOIMAGE
+#define NOIME
+#define NOKANJI
+#define NOMCX
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#define NOPROXYSTUB
+#define NOSERVICE
+#define NOSOUND
+#define NOTAPE
+#define NORPC
+#define _USE_MATH_DEFINES
+#include <windows.h>
+#undef CONNECT_DEFERRED // Avoid collision with the Godot Object class
+#undef CreateDialog // Avoid collision with the Godot CreateDialog class
+#endif
+
+#ifdef RVO_EXPORTS
+#define RVO_API __declspec(dllexport)
+#elif defined(RVO_IMPORTS)
+#define RVO_API __declspec(dllimport)
+#else
+#define RVO_API
+#endif
+
+#endif /* RVO_API_H_ */
diff --git a/thirdparty/rvo2/src/Agent.cpp b/thirdparty/rvo2/src/Agent.cpp
new file mode 100644
index 0000000000..851d780758
--- /dev/null
+++ b/thirdparty/rvo2/src/Agent.cpp
@@ -0,0 +1,425 @@
+/*
+ * Agent.cpp
+ * RVO2-3D Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#include "Agent.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "Definitions.h"
+#include "KdTree.h"
+
+namespace RVO {
+/**
+ * \brief A sufficiently small positive number.
+ */
+const float RVO_EPSILON = 0.00001f;
+
+/**
+ * \brief Defines a directed line.
+ */
+class Line {
+public:
+ /**
+ * \brief The direction of the directed line.
+ */
+ Vector3 direction;
+
+ /**
+ * \brief A point on the directed line.
+ */
+ Vector3 point;
+};
+
+/**
+ * \brief Solves a one-dimensional linear program on a specified line subject to linear constraints defined by planes and a spherical constraint.
+ * \param planes Planes defining the linear constraints.
+ * \param planeNo The plane on which the line lies.
+ * \param line The line on which the 1-d linear program is solved
+ * \param radius The radius of the spherical constraint.
+ * \param optVelocity The optimization velocity.
+ * \param directionOpt True if the direction should be optimized.
+ * \param result A reference to the result of the linear program.
+ * \return True if successful.
+ */
+bool linearProgram1(const std::vector<Plane> &planes, size_t planeNo, const Line &line, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result);
+
+/**
+ * \brief Solves a two-dimensional linear program on a specified plane subject to linear constraints defined by planes and a spherical constraint.
+ * \param planes Planes defining the linear constraints.
+ * \param planeNo The plane on which the 2-d linear program is solved
+ * \param radius The radius of the spherical constraint.
+ * \param optVelocity The optimization velocity.
+ * \param directionOpt True if the direction should be optimized.
+ * \param result A reference to the result of the linear program.
+ * \return True if successful.
+ */
+bool linearProgram2(const std::vector<Plane> &planes, size_t planeNo, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result);
+
+/**
+ * \brief Solves a three-dimensional linear program subject to linear constraints defined by planes and a spherical constraint.
+ * \param planes Planes defining the linear constraints.
+ * \param radius The radius of the spherical constraint.
+ * \param optVelocity The optimization velocity.
+ * \param directionOpt True if the direction should be optimized.
+ * \param result A reference to the result of the linear program.
+ * \return The number of the plane it fails on, and the number of planes if successful.
+ */
+size_t linearProgram3(const std::vector<Plane> &planes, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result);
+
+/**
+ * \brief Solves a four-dimensional linear program subject to linear constraints defined by planes and a spherical constraint.
+ * \param planes Planes defining the linear constraints.
+ * \param beginPlane The plane on which the 3-d linear program failed.
+ * \param radius The radius of the spherical constraint.
+ * \param result A reference to the result of the linear program.
+ */
+void linearProgram4(const std::vector<Plane> &planes, size_t beginPlane, float radius, Vector3 &result);
+
+Agent::Agent() :
+ id_(0), maxNeighbors_(0), maxSpeed_(0.0f), neighborDist_(0.0f), radius_(0.0f), timeHorizon_(0.0f), ignore_y_(false) {}
+
+void Agent::computeNeighbors(KdTree *kdTree_) {
+ agentNeighbors_.clear();
+ if (maxNeighbors_ > 0) {
+ kdTree_->computeAgentNeighbors(this, neighborDist_ * neighborDist_);
+ }
+}
+
+#define ABS(m_v) (((m_v) < 0) ? (-(m_v)) : (m_v))
+void Agent::computeNewVelocity(float timeStep) {
+ orcaPlanes_.clear();
+ const float invTimeHorizon = 1.0f / timeHorizon_;
+
+ /* Create agent ORCA planes. */
+ for (size_t i = 0; i < agentNeighbors_.size(); ++i) {
+ const Agent *const other = agentNeighbors_[i].second;
+
+ Vector3 relativePosition = other->position_ - position_;
+ Vector3 relativeVelocity = velocity_ - other->velocity_;
+ const float combinedRadius = radius_ + other->radius_;
+
+ // This is a Godot feature that allow the agents to avoid the collision
+ // by moving only on the horizontal plane relative to the player velocity.
+ if (ignore_y_) {
+ // Skip if these are in two different heights
+ if (ABS(relativePosition[1]) > combinedRadius * 2) {
+ continue;
+ }
+ relativePosition[1] = 0;
+ relativeVelocity[1] = 0;
+ }
+
+ const float distSq = absSq(relativePosition);
+ const float combinedRadiusSq = sqr(combinedRadius);
+
+ Plane plane;
+ Vector3 u;
+
+ if (distSq > combinedRadiusSq) {
+ /* No collision. */
+ const Vector3 w = relativeVelocity - invTimeHorizon * relativePosition;
+ /* Vector from cutoff center to relative velocity. */
+ const float wLengthSq = absSq(w);
+
+ const float dotProduct = w * relativePosition;
+
+ if (dotProduct < 0.0f && sqr(dotProduct) > combinedRadiusSq * wLengthSq) {
+ /* Project on cut-off circle. */
+ const float wLength = std::sqrt(wLengthSq);
+ const Vector3 unitW = w / wLength;
+
+ plane.normal = unitW;
+ u = (combinedRadius * invTimeHorizon - wLength) * unitW;
+ } else {
+ /* Project on cone. */
+ const float a = distSq;
+ const float b = relativePosition * relativeVelocity;
+ const float c = absSq(relativeVelocity) - absSq(cross(relativePosition, relativeVelocity)) / (distSq - combinedRadiusSq);
+ const float t = (b + std::sqrt(sqr(b) - a * c)) / a;
+ const Vector3 w = relativeVelocity - t * relativePosition;
+ const float wLength = abs(w);
+ const Vector3 unitW = w / wLength;
+
+ plane.normal = unitW;
+ u = (combinedRadius * t - wLength) * unitW;
+ }
+ } else {
+ /* Collision. */
+ const float invTimeStep = 1.0f / timeStep;
+ const Vector3 w = relativeVelocity - invTimeStep * relativePosition;
+ const float wLength = abs(w);
+ const Vector3 unitW = w / wLength;
+
+ plane.normal = unitW;
+ u = (combinedRadius * invTimeStep - wLength) * unitW;
+ }
+
+ plane.point = velocity_ + 0.5f * u;
+ orcaPlanes_.push_back(plane);
+ }
+
+ const size_t planeFail = linearProgram3(orcaPlanes_, maxSpeed_, prefVelocity_, false, newVelocity_);
+
+ if (planeFail < orcaPlanes_.size()) {
+ linearProgram4(orcaPlanes_, planeFail, maxSpeed_, newVelocity_);
+ }
+
+ if (ignore_y_) {
+ // Not 100% necessary, but better to have.
+ newVelocity_[1] = prefVelocity_[1];
+ }
+}
+
+void Agent::insertAgentNeighbor(const Agent *agent, float &rangeSq) {
+ if (this != agent) {
+ const float distSq = absSq(position_ - agent->position_);
+
+ if (distSq < rangeSq) {
+ if (agentNeighbors_.size() < maxNeighbors_) {
+ agentNeighbors_.push_back(std::make_pair(distSq, agent));
+ }
+
+ size_t i = agentNeighbors_.size() - 1;
+
+ while (i != 0 && distSq < agentNeighbors_[i - 1].first) {
+ agentNeighbors_[i] = agentNeighbors_[i - 1];
+ --i;
+ }
+
+ agentNeighbors_[i] = std::make_pair(distSq, agent);
+
+ if (agentNeighbors_.size() == maxNeighbors_) {
+ rangeSq = agentNeighbors_.back().first;
+ }
+ }
+ }
+}
+
+bool linearProgram1(const std::vector<Plane> &planes, size_t planeNo, const Line &line, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result) {
+ const float dotProduct = line.point * line.direction;
+ const float discriminant = sqr(dotProduct) + sqr(radius) - absSq(line.point);
+
+ if (discriminant < 0.0f) {
+ /* Max speed sphere fully invalidates line. */
+ return false;
+ }
+
+ const float sqrtDiscriminant = std::sqrt(discriminant);
+ float tLeft = -dotProduct - sqrtDiscriminant;
+ float tRight = -dotProduct + sqrtDiscriminant;
+
+ for (size_t i = 0; i < planeNo; ++i) {
+ const float numerator = (planes[i].point - line.point) * planes[i].normal;
+ const float denominator = line.direction * planes[i].normal;
+
+ if (sqr(denominator) <= RVO_EPSILON) {
+ /* Lines line is (almost) parallel to plane i. */
+ if (numerator > 0.0f) {
+ return false;
+ } else {
+ continue;
+ }
+ }
+
+ const float t = numerator / denominator;
+
+ if (denominator >= 0.0f) {
+ /* Plane i bounds line on the left. */
+ tLeft = std::max(tLeft, t);
+ } else {
+ /* Plane i bounds line on the right. */
+ tRight = std::min(tRight, t);
+ }
+
+ if (tLeft > tRight) {
+ return false;
+ }
+ }
+
+ if (directionOpt) {
+ /* Optimize direction. */
+ if (optVelocity * line.direction > 0.0f) {
+ /* Take right extreme. */
+ result = line.point + tRight * line.direction;
+ } else {
+ /* Take left extreme. */
+ result = line.point + tLeft * line.direction;
+ }
+ } else {
+ /* Optimize closest point. */
+ const float t = line.direction * (optVelocity - line.point);
+
+ if (t < tLeft) {
+ result = line.point + tLeft * line.direction;
+ } else if (t > tRight) {
+ result = line.point + tRight * line.direction;
+ } else {
+ result = line.point + t * line.direction;
+ }
+ }
+
+ return true;
+}
+
+bool linearProgram2(const std::vector<Plane> &planes, size_t planeNo, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result) {
+ const float planeDist = planes[planeNo].point * planes[planeNo].normal;
+ const float planeDistSq = sqr(planeDist);
+ const float radiusSq = sqr(radius);
+
+ if (planeDistSq > radiusSq) {
+ /* Max speed sphere fully invalidates plane planeNo. */
+ return false;
+ }
+
+ const float planeRadiusSq = radiusSq - planeDistSq;
+
+ const Vector3 planeCenter = planeDist * planes[planeNo].normal;
+
+ if (directionOpt) {
+ /* Project direction optVelocity on plane planeNo. */
+ const Vector3 planeOptVelocity = optVelocity - (optVelocity * planes[planeNo].normal) * planes[planeNo].normal;
+ const float planeOptVelocityLengthSq = absSq(planeOptVelocity);
+
+ if (planeOptVelocityLengthSq <= RVO_EPSILON) {
+ result = planeCenter;
+ } else {
+ result = planeCenter + std::sqrt(planeRadiusSq / planeOptVelocityLengthSq) * planeOptVelocity;
+ }
+ } else {
+ /* Project point optVelocity on plane planeNo. */
+ result = optVelocity + ((planes[planeNo].point - optVelocity) * planes[planeNo].normal) * planes[planeNo].normal;
+
+ /* If outside planeCircle, project on planeCircle. */
+ if (absSq(result) > radiusSq) {
+ const Vector3 planeResult = result - planeCenter;
+ const float planeResultLengthSq = absSq(planeResult);
+ result = planeCenter + std::sqrt(planeRadiusSq / planeResultLengthSq) * planeResult;
+ }
+ }
+
+ for (size_t i = 0; i < planeNo; ++i) {
+ if (planes[i].normal * (planes[i].point - result) > 0.0f) {
+ /* Result does not satisfy constraint i. Compute new optimal result. */
+ /* Compute intersection line of plane i and plane planeNo. */
+ Vector3 crossProduct = cross(planes[i].normal, planes[planeNo].normal);
+
+ if (absSq(crossProduct) <= RVO_EPSILON) {
+ /* Planes planeNo and i are (almost) parallel, and plane i fully invalidates plane planeNo. */
+ return false;
+ }
+
+ Line line;
+ line.direction = normalize(crossProduct);
+ const Vector3 lineNormal = cross(line.direction, planes[planeNo].normal);
+ line.point = planes[planeNo].point + (((planes[i].point - planes[planeNo].point) * planes[i].normal) / (lineNormal * planes[i].normal)) * lineNormal;
+
+ if (!linearProgram1(planes, i, line, radius, optVelocity, directionOpt, result)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+size_t linearProgram3(const std::vector<Plane> &planes, float radius, const Vector3 &optVelocity, bool directionOpt, Vector3 &result) {
+ if (directionOpt) {
+ /* Optimize direction. Note that the optimization velocity is of unit length in this case. */
+ result = optVelocity * radius;
+ } else if (absSq(optVelocity) > sqr(radius)) {
+ /* Optimize closest point and outside circle. */
+ result = normalize(optVelocity) * radius;
+ } else {
+ /* Optimize closest point and inside circle. */
+ result = optVelocity;
+ }
+
+ for (size_t i = 0; i < planes.size(); ++i) {
+ if (planes[i].normal * (planes[i].point - result) > 0.0f) {
+ /* Result does not satisfy constraint i. Compute new optimal result. */
+ const Vector3 tempResult = result;
+
+ if (!linearProgram2(planes, i, radius, optVelocity, directionOpt, result)) {
+ result = tempResult;
+ return i;
+ }
+ }
+ }
+
+ return planes.size();
+}
+
+void linearProgram4(const std::vector<Plane> &planes, size_t beginPlane, float radius, Vector3 &result) {
+ float distance = 0.0f;
+
+ for (size_t i = beginPlane; i < planes.size(); ++i) {
+ if (planes[i].normal * (planes[i].point - result) > distance) {
+ /* Result does not satisfy constraint of plane i. */
+ std::vector<Plane> projPlanes;
+
+ for (size_t j = 0; j < i; ++j) {
+ Plane plane;
+
+ const Vector3 crossProduct = cross(planes[j].normal, planes[i].normal);
+
+ if (absSq(crossProduct) <= RVO_EPSILON) {
+ /* Plane i and plane j are (almost) parallel. */
+ if (planes[i].normal * planes[j].normal > 0.0f) {
+ /* Plane i and plane j point in the same direction. */
+ continue;
+ } else {
+ /* Plane i and plane j point in opposite direction. */
+ plane.point = 0.5f * (planes[i].point + planes[j].point);
+ }
+ } else {
+ /* Plane.point is point on line of intersection between plane i and plane j. */
+ const Vector3 lineNormal = cross(crossProduct, planes[i].normal);
+ plane.point = planes[i].point + (((planes[j].point - planes[i].point) * planes[j].normal) / (lineNormal * planes[j].normal)) * lineNormal;
+ }
+
+ plane.normal = normalize(planes[j].normal - planes[i].normal);
+ projPlanes.push_back(plane);
+ }
+
+ const Vector3 tempResult = result;
+
+ if (linearProgram3(projPlanes, radius, planes[i].normal, true, result) < projPlanes.size()) {
+ /* This should in principle not happen. The result is by definition already in the feasible region of this linear program. If it fails, it is due to small floating point error, and the current result is kept. */
+ result = tempResult;
+ }
+
+ distance = planes[i].normal * (planes[i].point - result);
+ }
+ }
+}
+} // namespace RVO
diff --git a/thirdparty/rvo2/src/Agent.h b/thirdparty/rvo2/src/Agent.h
new file mode 100644
index 0000000000..16f75a08f6
--- /dev/null
+++ b/thirdparty/rvo2/src/Agent.h
@@ -0,0 +1,121 @@
+/*
+ * Agent.h
+ * RVO2-3D Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+/**
+ * \file Agent.h
+ * \brief Contains the Agent class.
+ */
+#ifndef RVO_AGENT_H_
+#define RVO_AGENT_H_
+
+#include "API.h"
+
+#include <cstddef>
+#include <utility>
+#include <vector>
+
+#include "Vector3.h"
+
+// Note: Slightly modified to work better in Godot.
+// - The agent can be created by anyone.
+// - The simulator pointer is removed.
+// - The update function is removed.
+// - The compute velocity function now need the timeStep.
+// - Moved the `Plane` class here.
+// - Added a new parameter `ignore_y_` in the `Agent`. This parameter is used to control a godot feature that allows to avoid collisions by moving on the horizontal plane.
+namespace RVO {
+/**
+ * \brief Defines a plane.
+ */
+class Plane {
+public:
+ /**
+ * \brief A point on the plane.
+ */
+ Vector3 point;
+
+ /**
+ * \brief The normal to the plane.
+ */
+ Vector3 normal;
+};
+
+/**
+ * \brief Defines an agent in the simulation.
+ */
+class Agent {
+
+public:
+ /**
+ * \brief Constructs an agent instance.
+ * \param sim The simulator instance.
+ */
+ explicit Agent();
+
+ /**
+ * \brief Computes the neighbors of this agent.
+ */
+ void computeNeighbors(class KdTree *kdTree_);
+
+ /**
+ * \brief Computes the new velocity of this agent.
+ */
+ void computeNewVelocity(float timeStep);
+
+ /**
+ * \brief Inserts an agent neighbor into the set of neighbors of this agent.
+ * \param agent A pointer to the agent to be inserted.
+ * \param rangeSq The squared range around this agent.
+ */
+ void insertAgentNeighbor(const Agent *agent, float &rangeSq);
+
+ Vector3 newVelocity_;
+ Vector3 position_;
+ Vector3 prefVelocity_;
+ Vector3 velocity_;
+ size_t id_;
+ size_t maxNeighbors_;
+ float maxSpeed_;
+ float neighborDist_;
+ float radius_;
+ float timeHorizon_;
+ std::vector<std::pair<float, const Agent *> > agentNeighbors_;
+ std::vector<Plane> orcaPlanes_;
+ /// This is a godot feature that allows the Agent to avoid collision by mooving
+ /// on the horizontal plane.
+ bool ignore_y_;
+
+ friend class KdTree;
+};
+} // namespace RVO
+
+#endif /* RVO_AGENT_H_ */
diff --git a/thirdparty/rvo2/src/Definitions.h b/thirdparty/rvo2/src/Definitions.h
new file mode 100644
index 0000000000..a73aca9908
--- /dev/null
+++ b/thirdparty/rvo2/src/Definitions.h
@@ -0,0 +1,55 @@
+/*
+ * Definitions.h
+ * RVO2-3D Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+/**
+ * \file Definitions.h
+ * \brief Contains functions and constants used in multiple classes.
+ */
+
+#ifndef RVO_DEFINITIONS_H_
+#define RVO_DEFINITIONS_H_
+
+#include "API.h"
+
+namespace RVO {
+ /**
+ * \brief Computes the square of a float.
+ * \param scalar The float to be squared.
+ * \return The square of the float.
+ */
+ inline float sqr(float scalar)
+ {
+ return scalar * scalar;
+ }
+}
+
+#endif /* RVO_DEFINITIONS_H_ */
diff --git a/thirdparty/rvo2/src/KdTree.cpp b/thirdparty/rvo2/src/KdTree.cpp
new file mode 100644
index 0000000000..bc224614f0
--- /dev/null
+++ b/thirdparty/rvo2/src/KdTree.cpp
@@ -0,0 +1,152 @@
+/*
+ * KdTree.cpp
+ * RVO2-3D Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#include "KdTree.h"
+
+#include <algorithm>
+
+#include "Agent.h"
+#include "Definitions.h"
+
+namespace RVO {
+const size_t RVO_MAX_LEAF_SIZE = 10;
+
+KdTree::KdTree() {}
+
+void KdTree::buildAgentTree(std::vector<Agent *> agents) {
+ agents_.swap(agents);
+
+ if (!agents_.empty()) {
+ agentTree_.resize(2 * agents_.size() - 1);
+ buildAgentTreeRecursive(0, agents_.size(), 0);
+ }
+}
+
+void KdTree::buildAgentTreeRecursive(size_t begin, size_t end, size_t node) {
+ agentTree_[node].begin = begin;
+ agentTree_[node].end = end;
+ agentTree_[node].minCoord = agents_[begin]->position_;
+ agentTree_[node].maxCoord = agents_[begin]->position_;
+
+ for (size_t i = begin + 1; i < end; ++i) {
+ agentTree_[node].maxCoord[0] = std::max(agentTree_[node].maxCoord[0], agents_[i]->position_.x());
+ agentTree_[node].minCoord[0] = std::min(agentTree_[node].minCoord[0], agents_[i]->position_.x());
+ agentTree_[node].maxCoord[1] = std::max(agentTree_[node].maxCoord[1], agents_[i]->position_.y());
+ agentTree_[node].minCoord[1] = std::min(agentTree_[node].minCoord[1], agents_[i]->position_.y());
+ agentTree_[node].maxCoord[2] = std::max(agentTree_[node].maxCoord[2], agents_[i]->position_.z());
+ agentTree_[node].minCoord[2] = std::min(agentTree_[node].minCoord[2], agents_[i]->position_.z());
+ }
+
+ if (end - begin > RVO_MAX_LEAF_SIZE) {
+ /* No leaf node. */
+ size_t coord;
+
+ if (agentTree_[node].maxCoord[0] - agentTree_[node].minCoord[0] > agentTree_[node].maxCoord[1] - agentTree_[node].minCoord[1] && agentTree_[node].maxCoord[0] - agentTree_[node].minCoord[0] > agentTree_[node].maxCoord[2] - agentTree_[node].minCoord[2]) {
+ coord = 0;
+ } else if (agentTree_[node].maxCoord[1] - agentTree_[node].minCoord[1] > agentTree_[node].maxCoord[2] - agentTree_[node].minCoord[2]) {
+ coord = 1;
+ } else {
+ coord = 2;
+ }
+
+ const float splitValue = 0.5f * (agentTree_[node].maxCoord[coord] + agentTree_[node].minCoord[coord]);
+
+ size_t left = begin;
+
+ size_t right = end;
+
+ while (left < right) {
+ while (left < right && agents_[left]->position_[coord] < splitValue) {
+ ++left;
+ }
+
+ while (right > left && agents_[right - 1]->position_[coord] >= splitValue) {
+ --right;
+ }
+
+ if (left < right) {
+ std::swap(agents_[left], agents_[right - 1]);
+ ++left;
+ --right;
+ }
+ }
+
+ size_t leftSize = left - begin;
+
+ if (leftSize == 0) {
+ ++leftSize;
+ ++left;
+ ++right;
+ }
+
+ agentTree_[node].left = node + 1;
+ agentTree_[node].right = node + 2 * leftSize;
+
+ buildAgentTreeRecursive(begin, left, agentTree_[node].left);
+ buildAgentTreeRecursive(left, end, agentTree_[node].right);
+ }
+}
+
+void KdTree::computeAgentNeighbors(Agent *agent, float rangeSq) const {
+ queryAgentTreeRecursive(agent, rangeSq, 0);
+}
+
+void KdTree::queryAgentTreeRecursive(Agent *agent, float &rangeSq, size_t node) const {
+ if (agentTree_[node].end - agentTree_[node].begin <= RVO_MAX_LEAF_SIZE) {
+ for (size_t i = agentTree_[node].begin; i < agentTree_[node].end; ++i) {
+ agent->insertAgentNeighbor(agents_[i], rangeSq);
+ }
+ } else {
+ const float distSqLeft = sqr(std::max(0.0f, agentTree_[agentTree_[node].left].minCoord[0] - agent->position_.x())) + sqr(std::max(0.0f, agent->position_.x() - agentTree_[agentTree_[node].left].maxCoord[0])) + sqr(std::max(0.0f, agentTree_[agentTree_[node].left].minCoord[1] - agent->position_.y())) + sqr(std::max(0.0f, agent->position_.y() - agentTree_[agentTree_[node].left].maxCoord[1])) + sqr(std::max(0.0f, agentTree_[agentTree_[node].left].minCoord[2] - agent->position_.z())) + sqr(std::max(0.0f, agent->position_.z() - agentTree_[agentTree_[node].left].maxCoord[2]));
+
+ const float distSqRight = sqr(std::max(0.0f, agentTree_[agentTree_[node].right].minCoord[0] - agent->position_.x())) + sqr(std::max(0.0f, agent->position_.x() - agentTree_[agentTree_[node].right].maxCoord[0])) + sqr(std::max(0.0f, agentTree_[agentTree_[node].right].minCoord[1] - agent->position_.y())) + sqr(std::max(0.0f, agent->position_.y() - agentTree_[agentTree_[node].right].maxCoord[1])) + sqr(std::max(0.0f, agentTree_[agentTree_[node].right].minCoord[2] - agent->position_.z())) + sqr(std::max(0.0f, agent->position_.z() - agentTree_[agentTree_[node].right].maxCoord[2]));
+
+ if (distSqLeft < distSqRight) {
+ if (distSqLeft < rangeSq) {
+ queryAgentTreeRecursive(agent, rangeSq, agentTree_[node].left);
+
+ if (distSqRight < rangeSq) {
+ queryAgentTreeRecursive(agent, rangeSq, agentTree_[node].right);
+ }
+ }
+ } else {
+ if (distSqRight < rangeSq) {
+ queryAgentTreeRecursive(agent, rangeSq, agentTree_[node].right);
+
+ if (distSqLeft < rangeSq) {
+ queryAgentTreeRecursive(agent, rangeSq, agentTree_[node].left);
+ }
+ }
+ }
+ }
+}
+} // namespace RVO
diff --git a/thirdparty/rvo2/src/KdTree.h b/thirdparty/rvo2/src/KdTree.h
new file mode 100644
index 0000000000..1dbad00ea4
--- /dev/null
+++ b/thirdparty/rvo2/src/KdTree.h
@@ -0,0 +1,124 @@
+/*
+ * KdTree.h
+ * RVO2-3D Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+/**
+ * \file KdTree.h
+ * \brief Contains the KdTree class.
+ */
+#ifndef RVO_KD_TREE_H_
+#define RVO_KD_TREE_H_
+
+#include "API.h"
+
+#include <cstddef>
+#include <vector>
+
+#include "Vector3.h"
+
+// Note: Slightly modified to work better with Godot.
+// - Removed `sim_`.
+// - KdTree things are public
+namespace RVO {
+class Agent;
+class RVOSimulator;
+
+/**
+ * \brief Defines <i>k</i>d-trees for agents in the simulation.
+ */
+class KdTree {
+public:
+ /**
+ * \brief Defines an agent <i>k</i>d-tree node.
+ */
+ class AgentTreeNode {
+ public:
+ /**
+ * \brief The beginning node number.
+ */
+ size_t begin;
+
+ /**
+ * \brief The ending node number.
+ */
+ size_t end;
+
+ /**
+ * \brief The left node number.
+ */
+ size_t left;
+
+ /**
+ * \brief The right node number.
+ */
+ size_t right;
+
+ /**
+ * \brief The maximum coordinates.
+ */
+ Vector3 maxCoord;
+
+ /**
+ * \brief The minimum coordinates.
+ */
+ Vector3 minCoord;
+ };
+
+ /**
+ * \brief Constructs a <i>k</i>d-tree instance.
+ * \param sim The simulator instance.
+ */
+ explicit KdTree();
+
+ /**
+ * \brief Builds an agent <i>k</i>d-tree.
+ */
+ void buildAgentTree(std::vector<Agent *> agents);
+
+ void buildAgentTreeRecursive(size_t begin, size_t end, size_t node);
+
+ /**
+ * \brief Computes the agent neighbors of the specified agent.
+ * \param agent A pointer to the agent for which agent neighbors are to be computed.
+ * \param rangeSq The squared range around the agent.
+ */
+ void computeAgentNeighbors(Agent *agent, float rangeSq) const;
+
+ void queryAgentTreeRecursive(Agent *agent, float &rangeSq, size_t node) const;
+
+ std::vector<Agent *> agents_;
+ std::vector<AgentTreeNode> agentTree_;
+
+ friend class Agent;
+ friend class RVOSimulator;
+};
+} // namespace RVO
+
+#endif /* RVO_KD_TREE_H_ */
diff --git a/thirdparty/rvo2/src/RVO.h b/thirdparty/rvo2/src/RVO.h
new file mode 100644
index 0000000000..81c9cbd8d3
--- /dev/null
+++ b/thirdparty/rvo2/src/RVO.h
@@ -0,0 +1,406 @@
+/*
+ * RVO.h
+ * RVO2-3D Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+#ifndef RVO_RVO_H_
+#define RVO_RVO_H_
+
+#include "API.h"
+#include "RVOSimulator.h"
+#include "Vector3.h"
+
+/**
+
+ \file RVO.h
+ \brief Includes all public headers in the library.
+
+ \namespace RVO
+ \brief Contains all classes, functions, and constants used in the library.
+
+ \mainpage RVO2-3D Library
+
+ \author Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, and Dinesh Manocha
+
+ <b>RVO2-3D Library</b> is an easy-to-use C++ implementation of the
+ <a href="http://gamma.cs.unc.edu/CA/">Optimal Reciprocal Collision Avoidance</a>
+ (ORCA) formulation for multi-agent simulation in three dimensions. <b>RVO2-3D Library</b>
+ automatically uses parallelism for computing the motion of the agents if your machine
+ has multiple processors and your compiler supports <a href="http://www.openmp.org/">
+ OpenMP</a>.
+
+ Please follow the following steps to install and use <b>RVO2-3D Library</b>.
+
+ - \subpage whatsnew
+ - \subpage building
+ - \subpage using
+ - \subpage params
+
+ See the documentation of the RVO::RVOSimulator class for an exhaustive list of
+ public functions of <b>RVO2-3D Library</b>.
+
+ <b>RVO2-3D Library</b>, accompanying example code, and this documentation is
+ released for educational, research, and non-profit purposes under the following
+ \subpage terms "terms and conditions".
+
+
+ \page whatsnew What Is New in RVO2-3D Library
+
+ \section localca Three Dimensions
+
+ In contrast to RVO2 Library, <b>RVO2-3D Library</b> operates in 3D workspaces. It uses
+ a three dimensional implementation of <a href="http://gamma.cs.unc.edu/CA/">Optimal
+ Reciprocal Collision Avoidance</a> (ORCA) for local collision avoidance. <b>RVO2-3D
+ Library</b> does not replace RVO2 Library; for 2D applications, RVO2 Library should
+ be used.
+
+ \section structure Structure of RVO2-3D Library
+
+ The structure of <b>RVO2-3D Library</b> is similar to that of RVO2 Library.
+ Users familiar with RVO2 Library should find little trouble in using <b>RVO2-3D
+ Library</b>. <b>RVO2-3D Library</b> currently does not support static obstacles.
+
+ \page building Building RVO2-3D Library
+
+ We assume that you have downloaded <b>RVO2-3D Library</b> and unpacked the ZIP
+ archive to a path <tt>$RVO_ROOT</tt>.
+
+ \section xcode Apple Xcode 4.x
+
+ Open <tt>$RVO_ROOT/RVO.xcodeproj</tt> and select the <tt>Static Library</tt> scheme. A static library <tt>libRVO.a</tt> will be built in the default build directory.
+
+ \section cmake CMake
+
+ Create and switch to your chosen build directory, e.g. <tt>$RVO_ROOT/build</tt>.
+ Run <tt>cmake</tt> inside the build directory on the source directory, e.g.
+ <tt>cmake $RVO_ROOT/src</tt>. Build files for the default generator for your
+ platform will be generated in the build directory.
+
+ \section make GNU Make
+
+ Switch to the source directory <tt>$RVO_ROOT/src</tt> and run <tt>make</tt>.
+ Public header files (<tt>API.h</tt>, <tt>RVO.h</tt>, <tt>RVOSimulator.h</tt>, and <tt>Vector3.h</tt>) will be copied to the <tt>$RVO_ROOT/include</tt> directory and a static library <tt>libRVO.a</tt> will be compiled into the
+ <tt>$RVO_ROOT/lib</tt> directory.
+
+ \section visual Microsoft Visual Studio 2010
+
+ Open <tt>$RVO_ROOT/RVO.sln</tt> and select the <tt>RVOStatic</tt> project and a
+ configuration (<tt>Debug</tt> or <tt>Release</tt>). Public header files (<tt>API.h</tt>, <tt>RVO.h</tt>, <tt>RVOSimulator.h</tt>, and <tt>Vector3.h</tt>) will be copied to the <tt>$RVO_ROOT/include</tt> directory and a static library, e.g. <tt>RVO.lib</tt>, will be compiled into the
+ <tt>$RVO_ROOT/lib</tt> directory.
+
+
+ \page using Using RVO2-3D Library
+
+ \section structure Structure
+
+ A program performing an <b>RVO2-3D Library</b> simulation has the following global
+ structure.
+
+ \code
+ #include <RVO.h>
+
+ std::vector<RVO::Vector3> goals;
+
+ int main()
+ {
+ // Create a new simulator instance.
+ RVO::RVOSimulator* sim = new RVO::RVOSimulator();
+
+ // Set up the scenario.
+ setupScenario(sim);
+
+ // Perform (and manipulate) the simulation.
+ do {
+ updateVisualization(sim);
+ setPreferredVelocities(sim);
+ sim->doStep();
+ } while (!reachedGoal(sim));
+
+ delete sim;
+ }
+ \endcode
+
+ In order to use <b>RVO2-3D Library</b>, the user needs to include RVO.h. The first
+ step is then to create an instance of RVO::RVOSimulator. Then, the process
+ consists of two stages. The first stage is specifying the simulation scenario
+ and its parameters. In the above example program, this is done in the method
+ setupScenario(...), which we will discuss below. The second stage is the actual
+ performing of the simulation.
+
+ In the above example program, simulation steps are taken until all
+ the agents have reached some predefined goals. Prior to each simulation step,
+ we set the preferred velocity for each agent, i.e. the
+ velocity the agent would have taken if there were no other agents around, in the
+ method setPreferredVelocities(...). The simulator computes the actual velocities
+ of the agents and attempts to follow the preferred velocities as closely as
+ possible while guaranteeing collision avoidance at the same time. During the
+ simulation, the user may want to retrieve information from the simulation for
+ instance to visualize the simulation. In the above example program, this is done
+ in the method updateVisualization(...), which we will discuss below. It is also
+ possible to manipulate the simulation during the simulation, for instance by
+ changing positions, radii, velocities, etc. of the agents.
+
+ \section spec Setting up the Simulation Scenario
+
+ A scenario that is to be simulated can be set up as follows. A scenario consists
+ of a set of agents that can be manually specified. Agents may be added anytime
+ before or during the simulation. The user may also want to define goal positions
+ of the agents, or a roadmap to guide the agents around obstacles. This is not done
+ in <b>RVO2-3D Library</b>, but needs to be taken care of in the user's external
+ application.
+
+ The following example creates a scenario with eight agents exchanging positions.
+
+ \code
+ void setupScenario(RVO::RVOSimulator* sim) {
+ // Specify global time step of the simulation.
+ sim->setTimeStep(0.25f);
+
+ // Specify default parameters for agents that are subsequently added.
+ sim->setAgentDefaults(15.0f, 10, 10.0f, 2.0f, 2.0f);
+
+ // Add agents, specifying their start position.
+ sim->addAgent(RVO::Vector3(-50.0f, -50.0f, -50.0f));
+ sim->addAgent(RVO::Vector3(50.0f, -50.0f, -50.0f));
+ sim->addAgent(RVO::Vector3(50.0f, 50.0f, -50.0f));
+ sim->addAgent(RVO::Vector3(-50.0f, 50.0f, -50.0f));
+ sim->addAgent(RVO::Vector3(-50.0f, -50.0f, 50.0f));
+ sim->addAgent(RVO::Vector3(50.0f, -50.0f, 50.0f));
+ sim->addAgent(RVO::Vector3(50.0f, 50.0f, 50.0f));
+ sim->addAgent(RVO::Vector3(-50.0f, 50.0f, 50.0f));
+
+ // Create goals (simulator is unaware of these).
+ for (size_t i = 0; i < sim->getNumAgents(); ++i) {
+ goals.push_back(-sim->getAgentPosition(i));
+ }
+ }
+ \endcode
+
+ See the documentation on RVO::RVOSimulator for a full overview of the
+ functionality to specify scenarios.
+
+ \section ret Retrieving Information from the Simulation
+
+ During the simulation, the user can extract information from the simulation for
+ instance for visualization purposes, or to determine termination conditions of
+ the simulation. In the example program above, visualization is done in the
+ updateVisualization(...) method. Below we give an example that simply writes
+ the positions of each agent in each time step to the standard output. The
+ termination condition is checked in the reachedGoal(...) method. Here we give an
+ example that returns true if all agents are within one radius of their goals.
+
+ \code
+ void updateVisualization(RVO::RVOSimulator* sim) {
+ // Output the current global time.
+ std::cout << sim->getGlobalTime() << " ";
+
+ // Output the position for all the agents.
+ for (size_t i = 0; i < sim->getNumAgents(); ++i) {
+ std::cout << sim->getAgentPosition(i) << " ";
+ }
+
+ std::cout << std::endl;
+ }
+ \endcode
+
+ \code
+ bool reachedGoal(RVO::RVOSimulator* sim) {
+ // Check whether all agents have arrived at their goals.
+ for (size_t i = 0; i < sim->getNumAgents(); ++i) {
+ if (absSq(goals[i] - sim->getAgentPosition(i)) > sim->getAgentRadius(i) * sim->getAgentRadius(i)) {
+ // Agent is further away from its goal than one radius.
+ return false;
+ }
+ }
+ return true;
+ }
+ \endcode
+
+ Using similar functions as the ones used in this example, the user can access
+ information about other parameters of the agents, as well as the global
+ parameters, and the obstacles. See the documentation of the class
+ RVO::RVOSimulator for an exhaustive list of public functions for retrieving
+ simulation information.
+
+ \section manip Manipulating the Simulation
+
+ During the simulation, the user can manipulate the simulation, for instance by
+ changing the global parameters, or changing the parameters of the agents
+ (potentially causing abrupt different behavior). It is also possible to give the
+ agents a new position, which make them jump through the scene.
+ New agents can be added to the simulation at any time.
+
+ See the documentation of the class RVO::RVOSimulator for an exhaustive list of
+ public functions for manipulating the simulation.
+
+ To provide global guidance to the agents, the preferred velocities of the agents
+ can be changed ahead of each simulation step. In the above example program, this
+ happens in the method setPreferredVelocities(...). Here we give an example that
+ simply sets the preferred velocity to the unit vector towards the agent's goal
+ for each agent (i.e., the preferred speed is 1.0).
+
+ \code
+ void setPreferredVelocities(RVO::RVOSimulator* sim) {
+ // Set the preferred velocity for each agent.
+ for (size_t i = 0; i < sim->getNumAgents(); ++i) {
+ if (absSq(goals[i] - sim->getAgentPosition(i)) < sim->getAgentRadius(i) * sim->getAgentRadius(i) ) {
+ // Agent is within one radius of its goal, set preferred velocity to zero
+ sim->setAgentPrefVelocity(i, RVO::Vector3());
+ } else {
+ // Agent is far away from its goal, set preferred velocity as unit vector towards agent's goal.
+ sim->setAgentPrefVelocity(i, normalize(goals[i] - sim->getAgentPosition(i)));
+ }
+ }
+ }
+ \endcode
+
+ \section example Example Programs
+
+ <b>RVO2-3D Library</b> is accompanied by one example program, which can be found in the
+ <tt>$RVO_ROOT/examples</tt> directory. The example is named Sphere, and
+ contains the following demonstration scenario:
+ <table border="0" cellpadding="3" width="100%">
+ <tr>
+ <td valign="top" width="100"><b>Sphere</b></td>
+ <td valign="top">A scenario in which 812 agents, initially positioned evenly
+ distributed on a sphere, move to the antipodal position on the
+ sphere. </td>
+ </tr>
+ </table>
+
+
+ \page params Parameter Overview
+
+ \section globalp Global Parameters
+
+ <table border="0" cellpadding="3" width="100%">
+ <tr>
+ <td valign="top" width="150"><strong>Parameter</strong></td>
+ <td valign="top" width="150"><strong>Type (unit)</strong></td>
+ <td valign="top"><strong>Meaning</strong></td>
+ </tr>
+ <tr>
+ <td valign="top">timeStep</td>
+ <td valign="top">float (time)</td>
+ <td valign="top">The time step of the simulation. Must be positive.</td>
+ </tr>
+ </table>
+
+ \section agent Agent Parameters
+
+ <table border="0" cellpadding="3" width="100%">
+ <tr>
+ <td valign="top" width="150"><strong>Parameter</strong></td>
+ <td valign="top" width="150"><strong>Type (unit)</strong></td>
+ <td valign="top"><strong>Meaning</strong></td>
+ </tr>
+ <tr>
+ <td valign="top">maxNeighbors</td>
+ <td valign="top">size_t</td>
+ <td valign="top">The maximum number of other agents the agent takes into
+ account in the navigation. The larger this number, the
+ longer the running time of the simulation. If the number is
+ too low, the simulation will not be safe.</td>
+ </tr>
+ <tr>
+ <td valign="top">maxSpeed</td>
+ <td valign="top">float (distance/time)</td>
+ <td valign="top">The maximum speed of the agent. Must be non-negative.</td>
+ </tr>
+ <tr>
+ <td valign="top">neighborDist</td>
+ <td valign="top">float (distance)</td>
+ <td valign="top">The maximum distance (center point to center point) to
+ other agents the agent takes into account in the
+ navigation. The larger this number, the longer the running
+ time of the simulation. If the number is too low, the
+ simulation will not be safe. Must be non-negative.</td>
+ </tr>
+ <tr>
+ <td valign="top" width="150">position</td>
+ <td valign="top" width="150">RVO::Vector3 (distance, distance)</td>
+ <td valign="top">The current position of the agent.</td>
+ </tr>
+ <tr>
+ <td valign="top" width="150">prefVelocity</td>
+ <td valign="top" width="150">RVO::Vector3 (distance/time, distance/time)
+ </td>
+ <td valign="top">The current preferred velocity of the agent. This is the
+ velocity the agent would take if no other agents or
+ obstacles were around. The simulator computes an actual
+ velocity for the agent that follows the preferred velocity
+ as closely as possible, but at the same time guarantees
+ collision avoidance.</td>
+ </tr>
+ <tr>
+ <td valign="top">radius</td>
+ <td valign="top">float (distance)</td>
+ <td valign="top">The radius of the agent. Must be non-negative.</td>
+ </tr>
+ <tr>
+ <td valign="top" width="150">timeHorizon</td>
+ <td valign="top" width="150">float (time)</td>
+ <td valign="top">The minimum amount of time for which the agent's velocities
+ that are computed by the simulation are safe with respect
+ to other agents. The larger this number, the sooner this
+ agent will respond to the presence of other agents, but the
+ less freedom the agent has in choosing its velocities.
+ Must be positive. </td>
+ </tr>
+ <tr>
+ <td valign="top" width="150">velocity</td>
+ <td valign="top" width="150">RVO::Vector3 (distance/time, distance/time)
+ </td>
+ <td valign="top">The (current) velocity of the agent.</td>
+ </tr>
+ </table>
+
+
+ \page terms Terms and Conditions
+
+ <b>RVO2-3D Library</b>
+
+ Copyright 2008 University of North Carolina at Chapel Hill
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ */
+
+#endif /* RVO_RVO_H_ */
diff --git a/thirdparty/rvo2/src/Vector3.h b/thirdparty/rvo2/src/Vector3.h
new file mode 100644
index 0000000000..8c8835c865
--- /dev/null
+++ b/thirdparty/rvo2/src/Vector3.h
@@ -0,0 +1,335 @@
+/*
+ * Vector3.h
+ * RVO2-3D Library
+ *
+ * Copyright 2008 University of North Carolina at Chapel Hill
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Please send all bug reports to <geom@cs.unc.edu>.
+ *
+ * The authors may be contacted via:
+ *
+ * Jur van den Berg, Stephen J. Guy, Jamie Snape, Ming C. Lin, Dinesh Manocha
+ * Dept. of Computer Science
+ * 201 S. Columbia St.
+ * Frederick P. Brooks, Jr. Computer Science Bldg.
+ * Chapel Hill, N.C. 27599-3175
+ * United States of America
+ *
+ * <http://gamma.cs.unc.edu/RVO2/>
+ */
+
+/**
+ * \file Vector3.h
+ * \brief Contains the Vector3 class.
+ */
+#ifndef RVO_VECTOR3_H_
+#define RVO_VECTOR3_H_
+
+#include "API.h"
+
+#include <cmath>
+#include <cstddef>
+#include <ostream>
+
+namespace RVO {
+ /**
+ * \brief Defines a three-dimensional vector.
+ */
+ class Vector3 {
+ public:
+ /**
+ * \brief Constructs and initializes a three-dimensional vector instance to zero.
+ */
+ RVO_API inline Vector3()
+ {
+ val_[0] = 0.0f;
+ val_[1] = 0.0f;
+ val_[2] = 0.0f;
+ }
+
+ /**
+ * \brief Constructs and initializes a three-dimensional vector from the specified three-element array.
+ * \param val The three-element array containing the xyz-coordinates.
+ */
+ RVO_API inline explicit Vector3(const float val[3])
+ {
+ val_[0] = val[0];
+ val_[1] = val[1];
+ val_[2] = val[2];
+ }
+
+ /**
+ * \brief Constructs and initializes a three-dimensional vector from the specified xyz-coordinates.
+ * \param x The x-coordinate of the three-dimensional vector.
+ * \param y The y-coordinate of the three-dimensional vector.
+ * \param z The z-coordinate of the three-dimensional vector.
+ */
+ RVO_API inline Vector3(float x, float y, float z)
+ {
+ val_[0] = x;
+ val_[1] = y;
+ val_[2] = z;
+ }
+
+ /**
+ * \brief Returns the x-coordinate of this three-dimensional vector.
+ * \return The x-coordinate of the three-dimensional vector.
+ */
+ RVO_API inline float x() const { return val_[0]; }
+
+ /**
+ * \brief Returns the y-coordinate of this three-dimensional vector.
+ * \return The y-coordinate of the three-dimensional vector.
+ */
+ RVO_API inline float y() const { return val_[1]; }
+
+ /**
+ * \brief Returns the z-coordinate of this three-dimensional vector.
+ * \return The z-coordinate of the three-dimensional vector.
+ */
+ RVO_API inline float z() const { return val_[2]; }
+
+ /**
+ * \brief Returns the specified coordinate of this three-dimensional vector.
+ * \param i The coordinate that should be returned (0 <= i < 3).
+ * \return The specified coordinate of the three-dimensional vector.
+ */
+ RVO_API inline float operator[](size_t i) const { return val_[i]; }
+
+ /**
+ * \brief Returns a reference to the specified coordinate of this three-dimensional vector.
+ * \param i The coordinate to which a reference should be returned (0 <= i < 3).
+ * \return A reference to the specified coordinate of the three-dimensional vector.
+ */
+ RVO_API inline float &operator[](size_t i) { return val_[i]; }
+
+ /**
+ * \brief Computes the negation of this three-dimensional vector.
+ * \return The negation of this three-dimensional vector.
+ */
+ RVO_API inline Vector3 operator-() const
+ {
+ return Vector3(-val_[0], -val_[1], -val_[2]);
+ }
+
+ /**
+ * \brief Computes the dot product of this three-dimensional vector with the specified three-dimensional vector.
+ * \param vector The three-dimensional vector with which the dot product should be computed.
+ * \return The dot product of this three-dimensional vector with a specified three-dimensional vector.
+ */
+ RVO_API inline float operator*(const Vector3 &vector) const
+ {
+ return val_[0] * vector[0] + val_[1] * vector[1] + val_[2] * vector[2];
+ }
+
+ /**
+ * \brief Computes the scalar multiplication of this three-dimensional vector with the specified scalar value.
+ * \param scalar The scalar value with which the scalar multiplication should be computed.
+ * \return The scalar multiplication of this three-dimensional vector with a specified scalar value.
+ */
+ RVO_API inline Vector3 operator*(float scalar) const
+ {
+ return Vector3(val_[0] * scalar, val_[1] * scalar, val_[2] * scalar);
+ }
+
+ /**
+ * \brief Computes the scalar division of this three-dimensional vector with the specified scalar value.
+ * \param scalar The scalar value with which the scalar division should be computed.
+ * \return The scalar division of this three-dimensional vector with a specified scalar value.
+ */
+ RVO_API inline Vector3 operator/(float scalar) const
+ {
+ const float invScalar = 1.0f / scalar;
+
+ return Vector3(val_[0] * invScalar, val_[1] * invScalar, val_[2] * invScalar);
+ }
+
+ /**
+ * \brief Computes the vector sum of this three-dimensional vector with the specified three-dimensional vector.
+ * \param vector The three-dimensional vector with which the vector sum should be computed.
+ * \return The vector sum of this three-dimensional vector with a specified three-dimensional vector.
+ */
+ RVO_API inline Vector3 operator+(const Vector3 &vector) const
+ {
+ return Vector3(val_[0] + vector[0], val_[1] + vector[1], val_[2] + vector[2]);
+ }
+
+ /**
+ * \brief Computes the vector difference of this three-dimensional vector with the specified three-dimensional vector.
+ * \param vector The three-dimensional vector with which the vector difference should be computed.
+ * \return The vector difference of this three-dimensional vector with a specified three-dimensional vector.
+ */
+ RVO_API inline Vector3 operator-(const Vector3 &vector) const
+ {
+ return Vector3(val_[0] - vector[0], val_[1] - vector[1], val_[2] - vector[2]);
+ }
+
+ /**
+ * \brief Tests this three-dimensional vector for equality with the specified three-dimensional vector.
+ * \param vector The three-dimensional vector with which to test for equality.
+ * \return True if the three-dimensional vectors are equal.
+ */
+ RVO_API inline bool operator==(const Vector3 &vector) const
+ {
+ return val_[0] == vector[0] && val_[1] == vector[1] && val_[2] == vector[2];
+ }
+
+ /**
+ * \brief Tests this three-dimensional vector for inequality with the specified three-dimensional vector.
+ * \param vector The three-dimensional vector with which to test for inequality.
+ * \return True if the three-dimensional vectors are not equal.
+ */
+ RVO_API inline bool operator!=(const Vector3 &vector) const
+ {
+ return val_[0] != vector[0] || val_[1] != vector[1] || val_[2] != vector[2];
+ }
+
+ /**
+ * \brief Sets the value of this three-dimensional vector to the scalar multiplication of itself with the specified scalar value.
+ * \param scalar The scalar value with which the scalar multiplication should be computed.
+ * \return A reference to this three-dimensional vector.
+ */
+ RVO_API inline Vector3 &operator*=(float scalar)
+ {
+ val_[0] *= scalar;
+ val_[1] *= scalar;
+ val_[2] *= scalar;
+
+ return *this;
+ }
+
+ /**
+ * \brief Sets the value of this three-dimensional vector to the scalar division of itself with the specified scalar value.
+ * \param scalar The scalar value with which the scalar division should be computed.
+ * \return A reference to this three-dimensional vector.
+ */
+ RVO_API inline Vector3 &operator/=(float scalar)
+ {
+ const float invScalar = 1.0f / scalar;
+
+ val_[0] *= invScalar;
+ val_[1] *= invScalar;
+ val_[2] *= invScalar;
+
+ return *this;
+ }
+
+ /**
+ * \brief Sets the value of this three-dimensional vector to the vector
+ * sum of itself with the specified three-dimensional vector.
+ * \param vector The three-dimensional vector with which the vector sum should be computed.
+ * \return A reference to this three-dimensional vector.
+ */
+ RVO_API inline Vector3 &operator+=(const Vector3 &vector)
+ {
+ val_[0] += vector[0];
+ val_[1] += vector[1];
+ val_[2] += vector[2];
+
+ return *this;
+ }
+
+ /**
+ * \brief Sets the value of this three-dimensional vector to the vector difference of itself with the specified three-dimensional vector.
+ * \param vector The three-dimensional vector with which the vector difference should be computed.
+ * \return A reference to this three-dimensional vector.
+ */
+ RVO_API inline Vector3 &operator-=(const Vector3 &vector)
+ {
+ val_[0] -= vector[0];
+ val_[1] -= vector[1];
+ val_[2] -= vector[2];
+
+ return *this;
+ }
+
+ private:
+ float val_[3];
+ };
+
+
+ /**
+ * \relates Vector3
+ * \brief Computes the scalar multiplication of the specified three-dimensional vector with the specified scalar value.
+ * \param scalar The scalar value with which the scalar multiplication should be computed.
+ * \param vector The three-dimensional vector with which the scalar multiplication should be computed.
+ * \return The scalar multiplication of the three-dimensional vector with the scalar value.
+ */
+ inline Vector3 operator*(float scalar, const Vector3 &vector)
+ {
+ return Vector3(scalar * vector[0], scalar * vector[1], scalar * vector[2]);
+ }
+
+ /**
+ * \relates Vector3
+ * \brief Computes the cross product of the specified three-dimensional vectors.
+ * \param vector1 The first vector with which the cross product should be computed.
+ * \param vector2 The second vector with which the cross product should be computed.
+ * \return The cross product of the two specified vectors.
+ */
+ inline Vector3 cross(const Vector3 &vector1, const Vector3 &vector2)
+ {
+ return Vector3(vector1[1] * vector2[2] - vector1[2] * vector2[1], vector1[2] * vector2[0] - vector1[0] * vector2[2], vector1[0] * vector2[1] - vector1[1] * vector2[0]);
+ }
+
+ /**
+ * \relates Vector3
+ * \brief Inserts the specified three-dimensional vector into the specified output stream.
+ * \param os The output stream into which the three-dimensional vector should be inserted.
+ * \param vector The three-dimensional vector which to insert into the output stream.
+ * \return A reference to the output stream.
+ */
+ inline std::ostream &operator<<(std::ostream &os, const Vector3 &vector)
+ {
+ os << "(" << vector[0] << "," << vector[1] << "," << vector[2] << ")";
+
+ return os;
+ }
+
+ /**
+ * \relates Vector3
+ * \brief Computes the length of a specified three-dimensional vector.
+ * \param vector The three-dimensional vector whose length is to be computed.
+ * \return The length of the three-dimensional vector.
+ */
+ inline float abs(const Vector3 &vector)
+ {
+ return std::sqrt(vector * vector);
+ }
+
+ /**
+ * \relates Vector3
+ * \brief Computes the squared length of a specified three-dimensional vector.
+ * \param vector The three-dimensional vector whose squared length is to be computed.
+ * \return The squared length of the three-dimensional vector.
+ */
+ inline float absSq(const Vector3 &vector)
+ {
+ return vector * vector;
+ }
+
+ /**
+ * \relates Vector3
+ * \brief Computes the normalization of the specified three-dimensional vector.
+ * \param vector The three-dimensional vector whose normalization is to be computed.
+ * \return The normalization of the three-dimensional vector.
+ */
+ inline Vector3 normalize(const Vector3 &vector)
+ {
+ return vector / abs(vector);
+ }
+}
+
+#endif