summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-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
32 files changed, 2435 insertions, 209 deletions
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();
}