Add multi-threaded NavMesh baking to NavigationServer
Adds multi-threaded NavMesh baking to NavigationServer.
This commit is contained in:
parent
f2acfb1ffc
commit
8686e84b44
|
@ -216,6 +216,15 @@
|
||||||
Bakes the provided [param navigation_mesh] with the data from the provided [param source_geometry_data]. After the process is finished the optional [param callback] will be called.
|
Bakes the provided [param navigation_mesh] with the data from the provided [param source_geometry_data]. After the process is finished the optional [param callback] will be called.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="bake_from_source_geometry_data_async">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="navigation_mesh" type="NavigationMesh" />
|
||||||
|
<param index="1" name="source_geometry_data" type="NavigationMeshSourceGeometryData3D" />
|
||||||
|
<param index="2" name="callback" type="Callable" default="Callable()" />
|
||||||
|
<description>
|
||||||
|
Bakes the provided [param navigation_mesh] with the data from the provided [param source_geometry_data] as an async task running on a background thread. After the process is finished the optional [param callback] will be called.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="free_rid">
|
<method name="free_rid">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<param index="0" name="rid" type="RID" />
|
<param index="0" name="rid" type="RID" />
|
||||||
|
|
|
@ -1991,6 +1991,12 @@
|
||||||
<member name="navigation/avoidance/thread_model/avoidance_use_multiple_threads" type="bool" setter="" getter="" default="true">
|
<member name="navigation/avoidance/thread_model/avoidance_use_multiple_threads" type="bool" setter="" getter="" default="true">
|
||||||
If enabled the avoidance calculations use multiple threads.
|
If enabled the avoidance calculations use multiple threads.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="navigation/baking/thread_model/baking_use_high_priority_threads" type="bool" setter="" getter="" default="true">
|
||||||
|
If enabled and async navmesh baking uses multiple threads the threads run with high priority.
|
||||||
|
</member>
|
||||||
|
<member name="navigation/baking/thread_model/baking_use_multiple_threads" type="bool" setter="" getter="" default="true">
|
||||||
|
If enabled the async navmesh baking uses multiple threads.
|
||||||
|
</member>
|
||||||
<member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="32768">
|
<member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="32768">
|
||||||
Maximum number of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
|
Maximum number of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection.
|
||||||
</member>
|
</member>
|
||||||
|
|
|
@ -930,7 +930,7 @@ COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers) {
|
||||||
obstacle->set_avoidance_layers(p_layers);
|
obstacle->set_avoidance_layers(p_layers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
|
void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
|
||||||
#ifndef _3D_DISABLED
|
#ifndef _3D_DISABLED
|
||||||
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
|
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
|
||||||
ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
|
ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
|
||||||
|
@ -942,26 +942,26 @@ void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh>
|
||||||
#endif // _3D_DISABLED
|
#endif // _3D_DISABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
void GodotNavigationServer::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
|
void GodotNavigationServer::bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
|
||||||
#ifndef _3D_DISABLED
|
#ifndef _3D_DISABLED
|
||||||
ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
|
ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
|
||||||
ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
|
ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
|
||||||
|
|
||||||
if (!p_source_geometry_data->has_data()) {
|
|
||||||
p_navigation_mesh->clear();
|
|
||||||
if (p_callback.is_valid()) {
|
|
||||||
Callable::CallError ce;
|
|
||||||
Variant result;
|
|
||||||
p_callback.callp(nullptr, 0, result, ce);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
|
ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
|
||||||
NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
|
NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
|
||||||
#endif // _3D_DISABLED
|
#endif // _3D_DISABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GodotNavigationServer::bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
|
||||||
|
#ifndef _3D_DISABLED
|
||||||
|
ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
|
||||||
|
ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D.");
|
||||||
|
|
||||||
|
ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton());
|
||||||
|
NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data_async(p_navigation_mesh, p_source_geometry_data, p_callback);
|
||||||
|
#endif // _3D_DISABLED
|
||||||
|
}
|
||||||
|
|
||||||
COMMAND_1(free, RID, p_object) {
|
COMMAND_1(free, RID, p_object) {
|
||||||
if (map_owner.owns(p_object)) {
|
if (map_owner.owns(p_object)) {
|
||||||
NavMap *map = map_owner.get_or_null(p_object);
|
NavMap *map = map_owner.get_or_null(p_object);
|
||||||
|
@ -1093,6 +1093,16 @@ void GodotNavigationServer::process(real_t p_delta_time) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef _3D_DISABLED
|
||||||
|
// Sync finished navmesh bakes before doing NavMap updates.
|
||||||
|
if (navmesh_generator_3d) {
|
||||||
|
navmesh_generator_3d->sync();
|
||||||
|
// Finished bakes emit callbacks and users might have reacted to those.
|
||||||
|
// Flush queue again so users do not have to wait for the next sync.
|
||||||
|
flush_queries();
|
||||||
|
}
|
||||||
|
#endif // _3D_DISABLED
|
||||||
|
|
||||||
int _new_pm_region_count = 0;
|
int _new_pm_region_count = 0;
|
||||||
int _new_pm_agent_count = 0;
|
int _new_pm_agent_count = 0;
|
||||||
int _new_pm_link_count = 0;
|
int _new_pm_link_count = 0;
|
||||||
|
|
|
@ -228,8 +228,9 @@ public:
|
||||||
virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override;
|
virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override;
|
||||||
COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers);
|
COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers);
|
||||||
|
|
||||||
virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override;
|
virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override;
|
||||||
virtual void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
|
virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
|
||||||
|
virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
|
||||||
|
|
||||||
COMMAND_1(free, RID, p_object);
|
COMMAND_1(free, RID, p_object);
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
#include "nav_mesh_generator_3d.h"
|
#include "nav_mesh_generator_3d.h"
|
||||||
|
|
||||||
|
#include "core/config/project_settings.h"
|
||||||
#include "core/math/convex_hull.h"
|
#include "core/math/convex_hull.h"
|
||||||
#include "core/os/thread.h"
|
#include "core/os/thread.h"
|
||||||
#include "scene/3d/mesh_instance_3d.h"
|
#include "scene/3d/mesh_instance_3d.h"
|
||||||
|
@ -62,7 +63,12 @@
|
||||||
|
|
||||||
NavMeshGenerator3D *NavMeshGenerator3D::singleton = nullptr;
|
NavMeshGenerator3D *NavMeshGenerator3D::singleton = nullptr;
|
||||||
Mutex NavMeshGenerator3D::baking_navmesh_mutex;
|
Mutex NavMeshGenerator3D::baking_navmesh_mutex;
|
||||||
|
Mutex NavMeshGenerator3D::generator_task_mutex;
|
||||||
|
bool NavMeshGenerator3D::use_threads = true;
|
||||||
|
bool NavMeshGenerator3D::baking_use_multiple_threads = true;
|
||||||
|
bool NavMeshGenerator3D::baking_use_high_priority_threads = true;
|
||||||
HashSet<Ref<NavigationMesh>> NavMeshGenerator3D::baking_navmeshes;
|
HashSet<Ref<NavigationMesh>> NavMeshGenerator3D::baking_navmeshes;
|
||||||
|
HashMap<WorkerThreadPool::TaskID, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::generator_tasks;
|
||||||
|
|
||||||
NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
|
NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
|
||||||
return singleton;
|
return singleton;
|
||||||
|
@ -71,15 +77,67 @@ NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
|
||||||
NavMeshGenerator3D::NavMeshGenerator3D() {
|
NavMeshGenerator3D::NavMeshGenerator3D() {
|
||||||
ERR_FAIL_COND(singleton != nullptr);
|
ERR_FAIL_COND(singleton != nullptr);
|
||||||
singleton = this;
|
singleton = this;
|
||||||
|
|
||||||
|
baking_use_multiple_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_multiple_threads");
|
||||||
|
baking_use_high_priority_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_high_priority_threads");
|
||||||
|
|
||||||
|
// Using threads might cause problems on certain exports or with the Editor on certain devices.
|
||||||
|
// This is the main switch to turn threaded navmesh baking off should the need arise.
|
||||||
|
use_threads = baking_use_multiple_threads && !Engine::get_singleton()->is_editor_hint();
|
||||||
}
|
}
|
||||||
|
|
||||||
NavMeshGenerator3D::~NavMeshGenerator3D() {
|
NavMeshGenerator3D::~NavMeshGenerator3D() {
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavMeshGenerator3D::sync() {
|
||||||
|
if (generator_tasks.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
baking_navmesh_mutex.lock();
|
||||||
|
generator_task_mutex.lock();
|
||||||
|
|
||||||
|
LocalVector<WorkerThreadPool::TaskID> finished_task_ids;
|
||||||
|
|
||||||
|
for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
|
||||||
|
if (WorkerThreadPool::get_singleton()->is_task_completed(E.key)) {
|
||||||
|
WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
|
||||||
|
finished_task_ids.push_back(E.key);
|
||||||
|
|
||||||
|
NavMeshGeneratorTask3D *generator_task = E.value;
|
||||||
|
DEV_ASSERT(generator_task->status == NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED);
|
||||||
|
|
||||||
|
baking_navmeshes.erase(generator_task->navigation_mesh);
|
||||||
|
if (generator_task->callback.is_valid()) {
|
||||||
|
generator_emit_callback(generator_task->callback);
|
||||||
|
}
|
||||||
|
memdelete(generator_task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (WorkerThreadPool::TaskID finished_task_id : finished_task_ids) {
|
||||||
|
generator_tasks.erase(finished_task_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
generator_task_mutex.unlock();
|
||||||
|
baking_navmesh_mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
void NavMeshGenerator3D::cleanup() {
|
void NavMeshGenerator3D::cleanup() {
|
||||||
baking_navmesh_mutex.lock();
|
baking_navmesh_mutex.lock();
|
||||||
|
generator_task_mutex.lock();
|
||||||
|
|
||||||
baking_navmeshes.clear();
|
baking_navmeshes.clear();
|
||||||
|
|
||||||
|
for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) {
|
||||||
|
WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key);
|
||||||
|
NavMeshGeneratorTask3D *generator_task = E.value;
|
||||||
|
memdelete(generator_task);
|
||||||
|
}
|
||||||
|
generator_tasks.clear();
|
||||||
|
|
||||||
|
generator_task_mutex.unlock();
|
||||||
baking_navmesh_mutex.unlock();
|
baking_navmesh_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +145,7 @@ void NavMeshGenerator3D::finish() {
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMeshGenerator3D::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
|
void NavMeshGenerator3D::parse_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) {
|
||||||
ERR_FAIL_COND(!Thread::is_main_thread());
|
ERR_FAIL_COND(!Thread::is_main_thread());
|
||||||
ERR_FAIL_COND(!p_navigation_mesh.is_valid());
|
ERR_FAIL_COND(!p_navigation_mesh.is_valid());
|
||||||
ERR_FAIL_COND(p_root_node == nullptr);
|
ERR_FAIL_COND(p_root_node == nullptr);
|
||||||
|
@ -101,19 +159,25 @@ void NavMeshGenerator3D::parse_source_geometry_data(const Ref<NavigationMesh> &p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) {
|
void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) {
|
||||||
ERR_FAIL_COND(!p_navigation_mesh.is_valid());
|
ERR_FAIL_COND(!p_navigation_mesh.is_valid());
|
||||||
ERR_FAIL_COND(!p_source_geometry_data.is_valid());
|
ERR_FAIL_COND(!p_source_geometry_data.is_valid());
|
||||||
ERR_FAIL_COND(!p_source_geometry_data->has_data());
|
|
||||||
|
if (!p_source_geometry_data->has_data()) {
|
||||||
|
p_navigation_mesh->clear();
|
||||||
|
if (p_callback.is_valid()) {
|
||||||
|
generator_emit_callback(p_callback);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
baking_navmesh_mutex.lock();
|
baking_navmesh_mutex.lock();
|
||||||
if (baking_navmeshes.has(p_navigation_mesh)) {
|
if (baking_navmeshes.has(p_navigation_mesh)) {
|
||||||
baking_navmesh_mutex.unlock();
|
baking_navmesh_mutex.unlock();
|
||||||
ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
|
ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
|
||||||
} else {
|
|
||||||
baking_navmeshes.insert(p_navigation_mesh);
|
|
||||||
baking_navmesh_mutex.unlock();
|
|
||||||
}
|
}
|
||||||
|
baking_navmeshes.insert(p_navigation_mesh);
|
||||||
|
baking_navmesh_mutex.unlock();
|
||||||
|
|
||||||
generator_bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data);
|
generator_bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data);
|
||||||
|
|
||||||
|
@ -126,6 +190,51 @@ void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_na
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavMeshGenerator3D::bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) {
|
||||||
|
ERR_FAIL_COND(!p_navigation_mesh.is_valid());
|
||||||
|
ERR_FAIL_COND(!p_source_geometry_data.is_valid());
|
||||||
|
|
||||||
|
if (!p_source_geometry_data->has_data()) {
|
||||||
|
p_navigation_mesh->clear();
|
||||||
|
if (p_callback.is_valid()) {
|
||||||
|
generator_emit_callback(p_callback);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!use_threads) {
|
||||||
|
bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
baking_navmesh_mutex.lock();
|
||||||
|
if (baking_navmeshes.has(p_navigation_mesh)) {
|
||||||
|
baking_navmesh_mutex.unlock();
|
||||||
|
ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
baking_navmeshes.insert(p_navigation_mesh);
|
||||||
|
baking_navmesh_mutex.unlock();
|
||||||
|
|
||||||
|
generator_task_mutex.lock();
|
||||||
|
NavMeshGeneratorTask3D *generator_task = memnew(NavMeshGeneratorTask3D);
|
||||||
|
generator_task->navigation_mesh = p_navigation_mesh;
|
||||||
|
generator_task->source_geometry_data = p_source_geometry_data;
|
||||||
|
generator_task->callback = p_callback;
|
||||||
|
generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
|
||||||
|
generator_task->thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMeshGenerator3D::generator_thread_bake, generator_task, NavMeshGenerator3D::baking_use_high_priority_threads, SNAME("NavMeshGeneratorBake3D"));
|
||||||
|
generator_tasks.insert(generator_task->thread_task_id, generator_task);
|
||||||
|
generator_task_mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavMeshGenerator3D::generator_thread_bake(void *p_arg) {
|
||||||
|
NavMeshGeneratorTask3D *generator_task = static_cast<NavMeshGeneratorTask3D *>(p_arg);
|
||||||
|
|
||||||
|
generator_bake_from_source_geometry_data(generator_task->navigation_mesh, generator_task->source_geometry_data);
|
||||||
|
|
||||||
|
generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
void NavMeshGenerator3D::generator_parse_geometry_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node, bool p_recurse_children) {
|
void NavMeshGenerator3D::generator_parse_geometry_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node, bool p_recurse_children) {
|
||||||
generator_parse_meshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
|
generator_parse_meshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
|
||||||
generator_parse_multimeshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
|
generator_parse_multimeshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node);
|
||||||
|
@ -503,8 +612,8 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Vector<float> vertices = p_source_geometry_data->get_vertices();
|
const Vector<float> &vertices = p_source_geometry_data->get_vertices();
|
||||||
const Vector<int> indices = p_source_geometry_data->get_indices();
|
const Vector<int> &indices = p_source_geometry_data->get_indices();
|
||||||
|
|
||||||
if (vertices.size() < 3 || indices.size() < 3) {
|
if (vertices.size() < 3 || indices.size() < 3) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#ifndef _3D_DISABLED
|
#ifndef _3D_DISABLED
|
||||||
|
|
||||||
#include "core/object/class_db.h"
|
#include "core/object/class_db.h"
|
||||||
|
#include "core/object/worker_thread_pool.h"
|
||||||
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
|
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
|
||||||
|
|
||||||
class Node;
|
class Node;
|
||||||
|
@ -44,6 +45,31 @@ class NavMeshGenerator3D : public Object {
|
||||||
static NavMeshGenerator3D *singleton;
|
static NavMeshGenerator3D *singleton;
|
||||||
|
|
||||||
static Mutex baking_navmesh_mutex;
|
static Mutex baking_navmesh_mutex;
|
||||||
|
static Mutex generator_task_mutex;
|
||||||
|
|
||||||
|
static bool use_threads;
|
||||||
|
static bool baking_use_multiple_threads;
|
||||||
|
static bool baking_use_high_priority_threads;
|
||||||
|
|
||||||
|
struct NavMeshGeneratorTask3D {
|
||||||
|
enum TaskStatus {
|
||||||
|
BAKING_STARTED,
|
||||||
|
BAKING_FINISHED,
|
||||||
|
BAKING_FAILED,
|
||||||
|
CALLBACK_DISPATCHED,
|
||||||
|
CALLBACK_FAILED,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ref<NavigationMesh> navigation_mesh;
|
||||||
|
Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
|
||||||
|
Callable callback;
|
||||||
|
WorkerThreadPool::TaskID thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
|
||||||
|
NavMeshGeneratorTask3D::TaskStatus status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
|
||||||
|
};
|
||||||
|
|
||||||
|
static HashMap<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> generator_tasks;
|
||||||
|
|
||||||
|
static void generator_thread_bake(void *p_arg);
|
||||||
|
|
||||||
static HashSet<Ref<NavigationMesh>> baking_navmeshes;
|
static HashSet<Ref<NavigationMesh>> baking_navmeshes;
|
||||||
|
|
||||||
|
@ -66,11 +92,13 @@ class NavMeshGenerator3D : public Object {
|
||||||
public:
|
public:
|
||||||
static NavMeshGenerator3D *get_singleton();
|
static NavMeshGenerator3D *get_singleton();
|
||||||
|
|
||||||
|
static void sync();
|
||||||
static void cleanup();
|
static void cleanup();
|
||||||
static void finish();
|
static void finish();
|
||||||
|
|
||||||
static void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable());
|
static void parse_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable());
|
||||||
static void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable());
|
static void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
|
||||||
|
static void bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
|
||||||
|
|
||||||
NavMeshGenerator3D();
|
NavMeshGenerator3D();
|
||||||
~NavMeshGenerator3D();
|
~NavMeshGenerator3D();
|
||||||
|
|
|
@ -236,62 +236,29 @@ RID NavigationRegion3D::get_navigation_map() const {
|
||||||
return RID();
|
return RID();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BakeThreadsArgs {
|
|
||||||
NavigationRegion3D *nav_region = nullptr;
|
|
||||||
Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
|
|
||||||
};
|
|
||||||
|
|
||||||
void _bake_navigation_mesh(void *p_user_data) {
|
|
||||||
BakeThreadsArgs *args = static_cast<BakeThreadsArgs *>(p_user_data);
|
|
||||||
|
|
||||||
if (args->nav_region->get_navigation_mesh().is_valid()) {
|
|
||||||
Ref<NavigationMesh> nav_mesh = args->nav_region->get_navigation_mesh();
|
|
||||||
Ref<NavigationMeshSourceGeometryData3D> source_geometry_data = args->source_geometry_data;
|
|
||||||
|
|
||||||
NavigationServer3D::get_singleton()->bake_from_source_geometry_data(nav_mesh, source_geometry_data);
|
|
||||||
if (!Thread::is_main_thread()) {
|
|
||||||
args->nav_region->call_deferred(SNAME("_bake_finished"), nav_mesh);
|
|
||||||
} else {
|
|
||||||
args->nav_region->_bake_finished(nav_mesh);
|
|
||||||
}
|
|
||||||
memdelete(args);
|
|
||||||
} else {
|
|
||||||
ERR_PRINT("Can't bake the navigation mesh if the `NavigationMesh` resource doesn't exist");
|
|
||||||
if (!Thread::is_main_thread()) {
|
|
||||||
args->nav_region->call_deferred(SNAME("_bake_finished"), Ref<NavigationMesh>());
|
|
||||||
} else {
|
|
||||||
args->nav_region->_bake_finished(Ref<NavigationMesh>());
|
|
||||||
}
|
|
||||||
memdelete(args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) {
|
void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) {
|
||||||
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
|
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
|
||||||
ERR_FAIL_COND_MSG(!navigation_mesh.is_valid(), "Baking the navigation mesh requires a valid `NavigationMesh` resource.");
|
ERR_FAIL_COND_MSG(!navigation_mesh.is_valid(), "Baking the navigation mesh requires a valid `NavigationMesh` resource.");
|
||||||
ERR_FAIL_COND_MSG(bake_thread.is_started(), "Unable to start another bake request. The navigation mesh bake thread is already baking a navigation mesh.");
|
|
||||||
|
|
||||||
Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
|
Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
|
||||||
source_geometry_data.instantiate();
|
source_geometry_data.instantiate();
|
||||||
|
|
||||||
NavigationServer3D::get_singleton()->parse_source_geometry_data(navigation_mesh, source_geometry_data, this);
|
NavigationServer3D::get_singleton()->parse_source_geometry_data(navigation_mesh, source_geometry_data, this);
|
||||||
|
|
||||||
BakeThreadsArgs *args = memnew(BakeThreadsArgs);
|
|
||||||
args->nav_region = this;
|
|
||||||
args->source_geometry_data = source_geometry_data;
|
|
||||||
|
|
||||||
if (p_on_thread) {
|
if (p_on_thread) {
|
||||||
bake_thread.start(_bake_navigation_mesh, args);
|
NavigationServer3D::get_singleton()->bake_from_source_geometry_data_async(navigation_mesh, source_geometry_data, callable_mp(this, &NavigationRegion3D::_bake_finished).bind(navigation_mesh));
|
||||||
} else {
|
} else {
|
||||||
_bake_navigation_mesh(args);
|
NavigationServer3D::get_singleton()->bake_from_source_geometry_data(navigation_mesh, source_geometry_data, callable_mp(this, &NavigationRegion3D::_bake_finished).bind(navigation_mesh));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) {
|
void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_navigation_mesh) {
|
||||||
set_navigation_mesh(p_nav_mesh);
|
if (!Thread::is_main_thread()) {
|
||||||
if (bake_thread.is_started()) {
|
call_deferred(SNAME("_bake_finished"), p_navigation_mesh);
|
||||||
bake_thread.wait_to_finish();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_navigation_mesh(p_navigation_mesh);
|
||||||
emit_signal(SNAME("bake_finished"));
|
emit_signal(SNAME("bake_finished"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,10 +419,6 @@ NavigationRegion3D::NavigationRegion3D() {
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationRegion3D::~NavigationRegion3D() {
|
NavigationRegion3D::~NavigationRegion3D() {
|
||||||
if (bake_thread.is_started()) {
|
|
||||||
bake_thread.wait_to_finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (navigation_mesh.is_valid()) {
|
if (navigation_mesh.is_valid()) {
|
||||||
navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
|
navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,8 +49,6 @@ class NavigationRegion3D : public Node3D {
|
||||||
|
|
||||||
Transform3D current_global_transform;
|
Transform3D current_global_transform;
|
||||||
|
|
||||||
Thread bake_thread;
|
|
||||||
|
|
||||||
void _navigation_mesh_changed();
|
void _navigation_mesh_changed();
|
||||||
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
|
|
|
@ -155,6 +155,7 @@ void NavigationServer3D::_bind_methods() {
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_mesh", "source_geometry_data", "root_node", "callback"), &NavigationServer3D::parse_source_geometry_data, DEFVAL(Callable()));
|
ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_mesh", "source_geometry_data", "root_node", "callback"), &NavigationServer3D::parse_source_geometry_data, DEFVAL(Callable()));
|
||||||
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data, DEFVAL(Callable()));
|
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data, DEFVAL(Callable()));
|
||||||
|
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data_async", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data_async, DEFVAL(Callable()));
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer3D::free);
|
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer3D::free);
|
||||||
|
|
||||||
|
@ -204,6 +205,9 @@ NavigationServer3D::NavigationServer3D() {
|
||||||
GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_multiple_threads", true);
|
GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_multiple_threads", true);
|
||||||
GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_high_priority_threads", true);
|
GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_high_priority_threads", true);
|
||||||
|
|
||||||
|
GLOBAL_DEF("navigation/baking/thread_model/baking_use_multiple_threads", true);
|
||||||
|
GLOBAL_DEF("navigation/baking/thread_model/baking_use_high_priority_threads", true);
|
||||||
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
debug_navigation_edge_connection_color = GLOBAL_DEF("debug/shapes/navigation/edge_connection_color", Color(1.0, 0.0, 1.0, 1.0));
|
debug_navigation_edge_connection_color = GLOBAL_DEF("debug/shapes/navigation/edge_connection_color", Color(1.0, 0.0, 1.0, 1.0));
|
||||||
debug_navigation_geometry_edge_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_color", Color(0.5, 1.0, 1.0, 1.0));
|
debug_navigation_geometry_edge_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_color", Color(0.5, 1.0, 1.0, 1.0));
|
||||||
|
|
|
@ -309,8 +309,9 @@ public:
|
||||||
|
|
||||||
virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const = 0;
|
virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const = 0;
|
||||||
|
|
||||||
virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0;
|
virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0;
|
||||||
virtual void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
|
virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
|
||||||
|
virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
|
||||||
|
|
||||||
NavigationServer3D();
|
NavigationServer3D();
|
||||||
~NavigationServer3D() override;
|
~NavigationServer3D() override;
|
||||||
|
|
|
@ -145,8 +145,9 @@ public:
|
||||||
void obstacle_set_position(RID p_obstacle, Vector3 p_position) override {}
|
void obstacle_set_position(RID p_obstacle, Vector3 p_position) override {}
|
||||||
void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override {}
|
void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override {}
|
||||||
void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {}
|
void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {}
|
||||||
void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {}
|
void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {}
|
||||||
void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
|
void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
|
||||||
|
void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
|
||||||
void free(RID p_object) override {}
|
void free(RID p_object) override {}
|
||||||
void set_active(bool p_active) override {}
|
void set_active(bool p_active) override {}
|
||||||
void process(real_t delta_time) override {}
|
void process(real_t delta_time) override {}
|
||||||
|
|
Loading…
Reference in New Issue