diff --git a/modules/navigation_3d/3d/godot_navigation_server_3d.cpp b/modules/navigation_3d/3d/godot_navigation_server_3d.cpp index 8e5451ecfc2..f537e839469 100644 --- a/modules/navigation_3d/3d/godot_navigation_server_3d.cpp +++ b/modules/navigation_3d/3d/godot_navigation_server_3d.cpp @@ -1219,6 +1219,14 @@ bool GodotNavigationServer3D::is_baking_navigation_mesh(Ref p_na return NavMeshGenerator3D::get_singleton()->is_baking(p_navigation_mesh); } +String GodotNavigationServer3D::get_baking_navigation_mesh_state_msg(Ref p_navigation_mesh) const { +#ifdef _3D_DISABLED + return ""; +#else + return NavMeshGenerator3D::get_singleton()->get_baking_state_msg(p_navigation_mesh); +#endif // _3D_DISABLED +} + COMMAND_1(free, RID, p_object) { if (map_owner.owns(p_object)) { NavMap3D *map = map_owner.get_or_null(p_object); diff --git a/modules/navigation_3d/3d/godot_navigation_server_3d.h b/modules/navigation_3d/3d/godot_navigation_server_3d.h index 5e5cc37de27..62cdfb07cf6 100644 --- a/modules/navigation_3d/3d/godot_navigation_server_3d.h +++ b/modules/navigation_3d/3d/godot_navigation_server_3d.h @@ -272,6 +272,7 @@ public: virtual void bake_from_source_geometry_data(const Ref &p_navigation_mesh, const Ref &p_source_geometry_data, const Callable &p_callback = Callable()) override; virtual void bake_from_source_geometry_data_async(const Ref &p_navigation_mesh, const Ref &p_source_geometry_data, const Callable &p_callback = Callable()) override; virtual bool is_baking_navigation_mesh(Ref p_navigation_mesh) const override; + virtual String get_baking_navigation_mesh_state_msg(Ref p_navigation_mesh) const override; virtual RID source_geometry_parser_create() override; virtual void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) override; diff --git a/modules/navigation_3d/3d/nav_mesh_generator_3d.cpp b/modules/navigation_3d/3d/nav_mesh_generator_3d.cpp index d62c5f605bf..9fe3a278e22 100644 --- a/modules/navigation_3d/3d/nav_mesh_generator_3d.cpp +++ b/modules/navigation_3d/3d/nav_mesh_generator_3d.cpp @@ -45,10 +45,26 @@ RWLock NavMeshGenerator3D::generator_parsers_rwlock; bool NavMeshGenerator3D::use_threads = true; bool NavMeshGenerator3D::baking_use_multiple_threads = true; bool NavMeshGenerator3D::baking_use_high_priority_threads = true; -HashSet> NavMeshGenerator3D::baking_navmeshes; +HashMap, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::baking_navmeshes; HashMap NavMeshGenerator3D::generator_tasks; LocalVector NavMeshGenerator3D::generator_parsers; +static const char *_navmesh_bake_state_msgs[(size_t)NavMeshGenerator3D::NavMeshBakeState::BAKE_STATE_MAX] = { + "", + "Setting up configuration...", + "Calculating grid size...", + "Creating heightfield...", + "Marking walkable triangles...", + "Constructing compact heightfield...", // step 5 + "Eroding walkable area...", + "Sample partitioning...", + "Creating contours...", + "Creating polymesh...", + "Converting to native navigation mesh...", // step 10 + "Baking cleanup...", + "Baking finished.", +}; + NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() { return singleton; } @@ -158,10 +174,15 @@ void NavMeshGenerator3D::bake_from_source_geometry_data(Ref p_na ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish."); } baking_navmesh_mutex.lock(); - baking_navmeshes.insert(p_navigation_mesh); + NavMeshGeneratorTask3D generator_task; + baking_navmeshes.insert(p_navigation_mesh, &generator_task); baking_navmesh_mutex.unlock(); - generator_bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data); + generator_task.navigation_mesh = p_navigation_mesh; + generator_task.source_geometry_data = p_source_geometry_data; + generator_task.status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED; + + generator_bake_from_source_geometry_data(&generator_task); baking_navmesh_mutex.lock(); baking_navmeshes.erase(p_navigation_mesh); @@ -197,16 +218,16 @@ void NavMeshGenerator3D::bake_from_source_geometry_data_async(Refnavigation_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")); + MutexLock generator_task_lock(generator_task_mutex); generator_tasks.insert(generator_task->thread_task_id, generator_task); } @@ -215,10 +236,21 @@ bool NavMeshGenerator3D::is_baking(Ref p_navigation_mesh) { return baking_navmeshes.has(p_navigation_mesh); } +String NavMeshGenerator3D::get_baking_state_msg(Ref p_navigation_mesh) { + String bake_state_msg; + MutexLock baking_navmesh_lock(baking_navmesh_mutex); + if (baking_navmeshes.has(p_navigation_mesh)) { + bake_state_msg = _navmesh_bake_state_msgs[baking_navmeshes[p_navigation_mesh]->bake_state]; + } else { + bake_state_msg = _navmesh_bake_state_msgs[NavMeshBakeState::BAKE_STATE_NONE]; + } + return bake_state_msg; +} + void NavMeshGenerator3D::generator_thread_bake(void *p_arg) { NavMeshGeneratorTask3D *generator_task = static_cast(p_arg); - generator_bake_from_source_geometry_data(generator_task->navigation_mesh, generator_task->source_geometry_data); + generator_bake_from_source_geometry_data(generator_task); generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED; } @@ -269,7 +301,10 @@ void NavMeshGenerator3D::generator_parse_source_geometry_data(const Ref p_navigation_mesh, const Ref &p_source_geometry_data) { +void NavMeshGenerator3D::generator_bake_from_source_geometry_data(NavMeshGeneratorTask3D *p_generator_task) { + Ref p_navigation_mesh = p_generator_task->navigation_mesh; + const Ref &p_source_geometry_data = p_generator_task->source_geometry_data; + if (p_navigation_mesh.is_null() || p_source_geometry_data.is_null()) { return; } @@ -294,10 +329,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Refbake_state = NavMeshBakeState::BAKE_STATE_CONFIGURATION; // step #1 const float *verts = source_geometry_vertices.ptr(); const int nverts = source_geometry_vertices.size() / 3; @@ -373,7 +405,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Refbake_state = NavMeshBakeState::BAKE_STATE_CALC_GRID_SIZE; // step #2 rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height); // ~30000000 seems to be around sweetspot where Editor baking breaks @@ -387,13 +419,13 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Refbake_state = NavMeshBakeState::BAKE_STATE_CREATE_HEIGHTFIELD; // step #3 hf = rcAllocHeightfield(); ERR_FAIL_NULL(hf); ERR_FAIL_COND(!rcCreateHeightfield(&ctx, *hf, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch)); - bake_state = "Marking walkable triangles..."; // step #4 + p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_MARK_WALKABLE_TRIANGLES; // step #4 { Vector tri_areas; tri_areas.resize(ntris); @@ -416,7 +448,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Refbake_state = NavMeshBakeState::BAKE_STATE_CONSTRUCT_COMPACT_HEIGHTFIELD; // step #5 chf = rcAllocCompactHeightfield(); @@ -443,7 +475,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Refbake_state = NavMeshBakeState::BAKE_STATE_ERODE_WALKABLE_AREA; // step #6 ERR_FAIL_COND(!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf)); @@ -464,7 +496,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Refbake_state = NavMeshBakeState::BAKE_STATE_SAMPLE_PARTITIONING; // step #7 if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) { ERR_FAIL_COND(!rcBuildDistanceField(&ctx, *chf)); @@ -475,14 +507,14 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Refbake_state = NavMeshBakeState::BAKE_STATE_CREATING_CONTOURS; // step #8 cset = rcAllocContourSet(); ERR_FAIL_NULL(cset); ERR_FAIL_COND(!rcBuildContours(&ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cset)); - bake_state = "Creating polymesh..."; // step #9 + p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CREATING_POLYMESH; // step #9 poly_mesh = rcAllocPolyMesh(); ERR_FAIL_NULL(poly_mesh); @@ -497,7 +529,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Refbake_state = NavMeshBakeState::BAKE_STATE_CONVERTING_NATIVE_NAVMESH; // step #10 Vector nav_vertices; Vector> nav_polygons; @@ -544,14 +576,14 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Refset_data(nav_vertices, nav_polygons); - bake_state = "Cleanup..."; // step #11 + p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_BAKE_CLEANUP; // step #11 rcFreePolyMesh(poly_mesh); poly_mesh = nullptr; rcFreePolyMeshDetail(detail_mesh); detail_mesh = nullptr; - bake_state = "Baking finished."; // step #12 + p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_BAKE_FINISHED; // step #12 } bool NavMeshGenerator3D::generator_emit_callback(const Callable &p_callback) { diff --git a/modules/navigation_3d/3d/nav_mesh_generator_3d.h b/modules/navigation_3d/3d/nav_mesh_generator_3d.h index d099983d4b5..13b680e228b 100644 --- a/modules/navigation_3d/3d/nav_mesh_generator_3d.h +++ b/modules/navigation_3d/3d/nav_mesh_generator_3d.h @@ -52,6 +52,25 @@ class NavMeshGenerator3D : public Object { static bool baking_use_multiple_threads; static bool baking_use_high_priority_threads; +public: + enum NavMeshBakeState { + BAKE_STATE_NONE, + BAKE_STATE_CONFIGURATION, + BAKE_STATE_CALC_GRID_SIZE, + BAKE_STATE_CREATE_HEIGHTFIELD, + BAKE_STATE_MARK_WALKABLE_TRIANGLES, + BAKE_STATE_CONSTRUCT_COMPACT_HEIGHTFIELD, + BAKE_STATE_ERODE_WALKABLE_AREA, + BAKE_STATE_SAMPLE_PARTITIONING, + BAKE_STATE_CREATING_CONTOURS, + BAKE_STATE_CREATING_POLYMESH, + BAKE_STATE_CONVERTING_NATIVE_NAVMESH, + BAKE_STATE_BAKE_CLEANUP, + BAKE_STATE_BAKE_FINISHED, + BAKE_STATE_MAX, + }; + +private: struct NavMeshGeneratorTask3D { enum TaskStatus { BAKING_STARTED, @@ -66,17 +85,19 @@ class NavMeshGenerator3D : public Object { Callable callback; WorkerThreadPool::TaskID thread_task_id = WorkerThreadPool::INVALID_TASK_ID; NavMeshGeneratorTask3D::TaskStatus status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED; + + NavMeshBakeState bake_state = NavMeshBakeState::BAKE_STATE_NONE; }; static HashMap generator_tasks; static void generator_thread_bake(void *p_arg); - static HashSet> baking_navmeshes; + static HashMap, NavMeshGeneratorTask3D *> baking_navmeshes; static void generator_parse_geometry_node(const Ref &p_navigation_mesh, Ref p_source_geometry_data, Node *p_node, bool p_recurse_children); static void generator_parse_source_geometry_data(const Ref &p_navigation_mesh, Ref p_source_geometry_data, Node *p_root_node); - static void generator_bake_from_source_geometry_data(Ref p_navigation_mesh, const Ref &p_source_geometry_data); + static void generator_bake_from_source_geometry_data(NavMeshGeneratorTask3D *p_generator_task); static bool generator_emit_callback(const Callable &p_callback); @@ -93,6 +114,7 @@ public: static void bake_from_source_geometry_data(Ref p_navigation_mesh, Ref p_source_geometry_data, const Callable &p_callback = Callable()); static void bake_from_source_geometry_data_async(Ref p_navigation_mesh, Ref p_source_geometry_data, const Callable &p_callback = Callable()); static bool is_baking(Ref p_navigation_mesh); + static String get_baking_state_msg(Ref p_navigation_mesh); NavMeshGenerator3D(); ~NavMeshGenerator3D(); diff --git a/modules/navigation_3d/editor/navigation_region_3d_editor_plugin.cpp b/modules/navigation_3d/editor/navigation_region_3d_editor_plugin.cpp index bc989be1b09..a21164ca2ec 100644 --- a/modules/navigation_3d/editor/navigation_region_3d_editor_plugin.cpp +++ b/modules/navigation_3d/editor/navigation_region_3d_editor_plugin.cpp @@ -32,18 +32,27 @@ #include "editor/editor_node.h" #include "editor/editor_string_names.h" +#include "editor/multi_node_edit.h" #include "editor/plugins/node_3d_editor_plugin.h" #include "scene/3d/navigation/navigation_region_3d.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/dialogs.h" #include "scene/gui/label.h" +#include "servers/navigation_server_3d.h" void NavigationRegion3DEditor::_node_removed(Node *p_node) { - if (p_node == node) { - node = nullptr; + if (selected_regions.is_empty()) { + return; + } - hide(); + NavigationRegion3D *region = Object::cast_to(p_node); + + if (region && selected_regions.has(region)) { + selected_regions.erase(region); + if (selected_regions.is_empty()) { + hide(); + } } } @@ -53,73 +62,167 @@ void NavigationRegion3DEditor::_notification(int p_what) { button_bake->set_button_icon(get_theme_icon(SNAME("Bake"), EditorStringName(EditorIcons))); button_reset->set_button_icon(get_theme_icon(SNAME("Reload"), EditorStringName(EditorIcons))); } break; + case NOTIFICATION_PROCESS: { + if (currently_baking_region) { + const String bake_state_msg = NavigationServer3D::get_singleton()->get_baking_navigation_mesh_state_msg(currently_baking_region->get_navigation_mesh()); + multibake_dialog->set_text(itos(processed_regions_to_bake_count) + " / " + itos(processed_regions_to_bake_count_max) + " - Baking navmesh from region '" + currently_baking_region->get_name() + "'.\n\nBake state: " + bake_state_msg + "\n\nDo NOT change nodes by any means while the baking is parsing the SceneTree."); + } else { + multibake_dialog->set_text(""); + } + } } } void NavigationRegion3DEditor::_bake_pressed() { button_bake->set_pressed(false); - ERR_FAIL_NULL(node); - Ref navmesh = node->get_navigation_mesh(); - if (navmesh.is_null()) { - err_dialog->set_text(TTR("A NavigationMesh resource must be set or created for this node to work.")); - err_dialog->popup_centered(); + if (selected_regions.is_empty()) { return; } - String path = navmesh->get_path(); - if (!path.is_resource_file()) { - int srpos = path.find("::"); - if (srpos != -1) { - String base = path.substr(0, srpos); - if (ResourceLoader::get_resource_type(base) == "PackedScene") { - if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) { - err_dialog->set_text(TTR("Cannot generate navigation mesh because it does not belong to the edited scene. Make it unique first.")); - err_dialog->popup_centered(); - return; - } - } else { - if (FileAccess::exists(base + ".import")) { - err_dialog->set_text(TTR("Cannot generate navigation mesh because it belongs to a resource which was imported.")); - err_dialog->popup_centered(); - return; - } - } - } - } else { - if (FileAccess::exists(path + ".import")) { - err_dialog->set_text(TTR("Cannot generate navigation mesh because the resource was imported from another type.")); + if (bake_in_process) { + return; + } + + HashSet> unique_navmeshes; + regions_to_bake.clear(); + regions_with_navmesh_to_bake.clear(); + + for (NavigationRegion3D *region : selected_regions) { + ERR_CONTINUE(region == nullptr); + Ref navmesh = region->get_navigation_mesh(); + if (navmesh.is_null()) { + err_dialog->set_text(TTR("A NavigationMesh resource must be set or created for this node to work.")); err_dialog->popup_centered(); return; } + + String path = navmesh->get_path(); + if (!path.is_resource_file()) { + int srpos = path.find("::"); + if (srpos != -1) { + String base = path.substr(0, srpos); + if (ResourceLoader::get_resource_type(base) == "PackedScene") { + if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) { + err_dialog->set_text(TTR("Cannot generate navigation mesh because it does not belong to the edited scene. Make it unique first.")); + err_dialog->popup_centered(); + return; + } + } else { + if (FileAccess::exists(base + ".import")) { + err_dialog->set_text(TTR("Cannot generate navigation mesh because it belongs to a resource which was imported.")); + err_dialog->popup_centered(); + return; + } + } + } + } else { + if (FileAccess::exists(path + ".import")) { + err_dialog->set_text(TTR("Cannot generate navigation mesh because the resource was imported from another type.")); + err_dialog->popup_centered(); + return; + } + } + + regions_to_bake.push_back(region); + + if (unique_navmeshes.has(navmesh)) { + // No point (re)baking the same resource in case of multi select. + // Trying to bake the same navmesh twice would trigger an error. + continue; + } + unique_navmeshes.insert(navmesh); + regions_with_navmesh_to_bake.push_back(region); } - node->bake_navigation_mesh(true); - - node->update_gizmos(); -} - -void NavigationRegion3DEditor::_clear_pressed() { - if (node) { - if (node->get_navigation_mesh().is_valid()) { - node->get_navigation_mesh()->clear(); + if (!regions_with_navmesh_to_bake.is_empty()) { + multibake_dialog->set_ok_button_text(TTR("Bake")); + multibake_dialog->get_ok_button()->set_disabled(false); + multibake_dialog->set_text("Attempting to bake " + itos(regions_with_navmesh_to_bake.size()) + " unique navmesh(es) from " + itos(regions_to_bake.size()) + " selected NavigationRegion3D node(s).\n\nThis can take some time and freeze the Editor temporarily.\n\nDo NOT change nodes by any means while the baking is parsing the SceneTree."); + multibake_dialog->popup_centered(); + if (regions_with_navmesh_to_bake.size() == 1) { + // If we only have a single region start bake immediately. + _on_navmesh_multibake_confirmed(); } } - - button_bake->set_pressed(false); - bake_info->set_text(""); - - if (node) { - node->update_gizmos(); - } } -void NavigationRegion3DEditor::edit(NavigationRegion3D *p_nav_region) { - if (p_nav_region == nullptr || node == p_nav_region) { +void NavigationRegion3DEditor::_on_navmesh_multibake_confirmed() { + multibake_dialog->get_ok_button()->set_disabled(true); + + bake_in_process = true; + region_baking_canceled = false; + processed_regions_to_bake_count = 0; + processed_regions_to_bake_count_max = regions_with_navmesh_to_bake.size(); + + set_process(true); + _process_regions_to_bake(); +} + +void NavigationRegion3DEditor::_process_regions_to_bake() { + if (region_baking_canceled) { + region_baking_canceled = false; + regions_with_navmesh_to_bake.clear(); + } + + if (regions_with_navmesh_to_bake.is_empty()) { + regions_to_bake.clear(); + multibake_dialog->set_visible(false); + set_process(false); + currently_baking_region = nullptr; + bake_in_process = false; return; } - node = p_nav_region; + NavigationRegion3D *region_to_bake = regions_with_navmesh_to_bake[0]; + regions_with_navmesh_to_bake.remove_at_unordered(0); + processed_regions_to_bake_count += 1; + if (region_to_bake && region_to_bake->get_navigation_mesh().is_valid()) { + currently_baking_region = region_to_bake; + region_to_bake->connect(SNAME("bake_finished"), callable_mp(this, &NavigationRegion3DEditor::_process_regions_to_bake), CONNECT_ONE_SHOT); + region_to_bake->bake_navigation_mesh(true); + return; + } else { + _process_regions_to_bake(); + } +} + +void NavigationRegion3DEditor::_on_navmesh_multibake_canceled() { + if (bake_in_process) { + region_baking_canceled = true; + return; + } + + multibake_dialog->set_visible(false); + regions_to_bake.clear(); + regions_with_navmesh_to_bake.clear(); + processed_regions_to_bake_count = 0; + processed_regions_to_bake_count_max = 0; + region_baking_canceled = false; + currently_baking_region = nullptr; + bake_in_process = false; +} + +void NavigationRegion3DEditor::_clear_pressed() { + button_bake->set_pressed(false); + bake_info->set_text(""); + + if (!selected_regions.is_empty()) { + for (NavigationRegion3D *region : selected_regions) { + if (region->get_navigation_mesh().is_valid()) { + region->get_navigation_mesh()->clear(); + region->update_gizmos(); + } + } + } +} + +void NavigationRegion3DEditor::edit(LocalVector p_regions) { + if (p_regions.is_empty()) { + return; + } + + selected_regions = p_regions; } NavigationRegion3DEditor::NavigationRegion3DEditor() { @@ -146,15 +249,56 @@ NavigationRegion3DEditor::NavigationRegion3DEditor() { err_dialog = memnew(AcceptDialog); add_child(err_dialog); - node = nullptr; + + multibake_dialog = memnew(ConfirmationDialog); + add_child(multibake_dialog); + multibake_dialog->connect(SceneStringName(confirmed), callable_mp(this, &NavigationRegion3DEditor::_on_navmesh_multibake_confirmed)); + multibake_dialog->connect(SNAME("canceled"), callable_mp(this, &NavigationRegion3DEditor::_on_navmesh_multibake_canceled)); + multibake_dialog->set_hide_on_ok(false); + multibake_dialog->set_title(TTR("Baking NavigationMesh ...")); } void NavigationRegion3DEditorPlugin::edit(Object *p_object) { - navigation_region_editor->edit(Object::cast_to(p_object)); + LocalVector regions; + + { + NavigationRegion3D *region = Object::cast_to(p_object); + if (region) { + regions.push_back(region); + navigation_region_editor->edit(regions); + return; + } + } + + Ref mne = Ref(p_object); + Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); + if (mne.is_valid() && edited_scene) { + for (int i = 0; i < mne->get_node_count(); i++) { + NavigationRegion3D *region = Object::cast_to(edited_scene->get_node(mne->get_node(i))); + if (region) { + regions.push_back(region); + } + } + } + + navigation_region_editor->edit(regions); } bool NavigationRegion3DEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("NavigationRegion3D"); + if (Object::cast_to(p_object)) { + return true; + } + + Ref mne = Ref(p_object); + Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); + if (mne.is_valid() && edited_scene) { + for (int i = 0; i < mne->get_node_count(); i++) { + if (Object::cast_to(edited_scene->get_node(mne->get_node(i)))) { + return true; + } + } + } + return false; } void NavigationRegion3DEditorPlugin::make_visible(bool p_visible) { @@ -164,7 +308,7 @@ void NavigationRegion3DEditorPlugin::make_visible(bool p_visible) { } else { navigation_region_editor->hide(); navigation_region_editor->bake_hbox->hide(); - navigation_region_editor->edit(nullptr); + navigation_region_editor->edit(LocalVector()); } } diff --git a/modules/navigation_3d/editor/navigation_region_3d_editor_plugin.h b/modules/navigation_3d/editor/navigation_region_3d_editor_plugin.h index 19c339d1ef4..3dc98ec1988 100644 --- a/modules/navigation_3d/editor/navigation_region_3d_editor_plugin.h +++ b/modules/navigation_3d/editor/navigation_region_3d_editor_plugin.h @@ -36,6 +36,7 @@ class AcceptDialog; class Button; +class ConfirmationDialog; class HBoxContainer; class Label; class NavigationRegion3D; @@ -46,23 +47,38 @@ class NavigationRegion3DEditor : public Control { GDCLASS(NavigationRegion3DEditor, Control); AcceptDialog *err_dialog = nullptr; + ConfirmationDialog *multibake_dialog = nullptr; HBoxContainer *bake_hbox = nullptr; Button *button_bake = nullptr; Button *button_reset = nullptr; Label *bake_info = nullptr; - NavigationRegion3D *node = nullptr; + LocalVector selected_regions; + + LocalVector regions_to_bake; + LocalVector regions_with_navmesh_to_bake; + + int processed_regions_to_bake_count = 0; + int processed_regions_to_bake_count_max = 0; + bool region_baking_canceled = false; + NavigationRegion3D *currently_baking_region = nullptr; + + bool bake_in_process = false; void _bake_pressed(); void _clear_pressed(); + void _on_navmesh_multibake_confirmed(); + void _on_navmesh_multibake_canceled(); + void _process_regions_to_bake(); + protected: void _node_removed(Node *p_node); void _notification(int p_what); public: - void edit(NavigationRegion3D *p_nav_region); + void edit(LocalVector p_regions); NavigationRegion3DEditor(); }; diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index 1bff41a7167..dcc11b90581 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -286,6 +286,7 @@ public: virtual void bake_from_source_geometry_data(const Ref &p_navigation_mesh, const Ref &p_source_geometry_data, const Callable &p_callback = Callable()) = 0; virtual void bake_from_source_geometry_data_async(const Ref &p_navigation_mesh, const Ref &p_source_geometry_data, const Callable &p_callback = Callable()) = 0; virtual bool is_baking_navigation_mesh(Ref p_navigation_mesh) const = 0; + virtual String get_baking_navigation_mesh_state_msg(Ref p_navigation_mesh) const = 0; #endif // _3D_DISABLED protected: diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h index f8fadc2b219..cfa2b22aa14 100644 --- a/servers/navigation_server_3d_dummy.h +++ b/servers/navigation_server_3d_dummy.h @@ -191,6 +191,7 @@ public: void bake_from_source_geometry_data(const Ref &p_navigation_mesh, const Ref &p_source_geometry_data, const Callable &p_callback = Callable()) override {} void bake_from_source_geometry_data_async(const Ref &p_navigation_mesh, const Ref &p_source_geometry_data, const Callable &p_callback = Callable()) override {} bool is_baking_navigation_mesh(Ref p_navigation_mesh) const override { return false; } + String get_baking_navigation_mesh_state_msg(Ref p_navigation_mesh) const override { return ""; } #endif // _3D_DISABLED RID source_geometry_parser_create() override { return RID(); }