diff options
Diffstat (limited to 'modules')
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(); } |