Browse Source

Merge pull request #51152 from lawnjelly/portals_improve_ui

Rémi Verschelde 4 years ago
parent
commit
73c6ab0215

+ 2 - 16
editor/plugins/room_manager_editor_plugin.cpp

@@ -32,12 +32,6 @@
 
 
 #include "editor/spatial_editor_gizmos.h"
 #include "editor/spatial_editor_gizmos.h"
 
 
-void RoomManagerEditorPlugin::_rooms_convert() {
-	if (_room_manager) {
-		_room_manager->rooms_convert();
-	}
-}
-
 void RoomManagerEditorPlugin::_flip_portals() {
 void RoomManagerEditorPlugin::_flip_portals() {
 	if (_room_manager) {
 	if (_room_manager) {
 		_room_manager->rooms_flip_portals();
 		_room_manager->rooms_flip_portals();
@@ -59,16 +53,15 @@ bool RoomManagerEditorPlugin::handles(Object *p_object) const {
 
 
 void RoomManagerEditorPlugin::make_visible(bool p_visible) {
 void RoomManagerEditorPlugin::make_visible(bool p_visible) {
 	if (p_visible) {
 	if (p_visible) {
-		button_rooms_convert->show();
 		button_flip_portals->show();
 		button_flip_portals->show();
 	} else {
 	} else {
-		button_rooms_convert->hide();
 		button_flip_portals->hide();
 		button_flip_portals->hide();
 	}
 	}
+
+	SpatialEditor::get_singleton()->show_advanced_portal_tools(p_visible);
 }
 }
 
 
 void RoomManagerEditorPlugin::_bind_methods() {
 void RoomManagerEditorPlugin::_bind_methods() {
-	ClassDB::bind_method("_rooms_convert", &RoomManagerEditorPlugin::_rooms_convert);
 	ClassDB::bind_method("_flip_portals", &RoomManagerEditorPlugin::_flip_portals);
 	ClassDB::bind_method("_flip_portals", &RoomManagerEditorPlugin::_flip_portals);
 }
 }
 
 
@@ -82,13 +75,6 @@ RoomManagerEditorPlugin::RoomManagerEditorPlugin(EditorNode *p_node) {
 	button_flip_portals->connect("pressed", this, "_flip_portals");
 	button_flip_portals->connect("pressed", this, "_flip_portals");
 	add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, button_flip_portals);
 	add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, button_flip_portals);
 
 
-	button_rooms_convert = memnew(ToolButton);
-	button_rooms_convert->set_icon(editor->get_gui_base()->get_icon("RoomGroup", "EditorIcons"));
-	button_rooms_convert->set_text(TTR("Convert Rooms"));
-	button_rooms_convert->hide();
-	button_rooms_convert->connect("pressed", this, "_rooms_convert");
-	add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, button_rooms_convert);
-
 	_room_manager = nullptr;
 	_room_manager = nullptr;
 
 
 	Ref<RoomGizmoPlugin> room_gizmo_plugin = Ref<RoomGizmoPlugin>(memnew(RoomGizmoPlugin));
 	Ref<RoomGizmoPlugin> room_gizmo_plugin = Ref<RoomGizmoPlugin>(memnew(RoomGizmoPlugin));

+ 0 - 2
editor/plugins/room_manager_editor_plugin.h

@@ -43,11 +43,9 @@ class RoomManagerEditorPlugin : public EditorPlugin {
 
 
 	RoomManager *_room_manager;
 	RoomManager *_room_manager;
 
 
-	ToolButton *button_rooms_convert;
 	ToolButton *button_flip_portals;
 	ToolButton *button_flip_portals;
 	EditorNode *editor;
 	EditorNode *editor;
 
 
-	void _rooms_convert();
 	void _flip_portals();
 	void _flip_portals();
 
 
 protected:
 protected:

+ 61 - 0
editor/plugins/spatial_editor_plugin.cpp

@@ -47,6 +47,7 @@
 #include "scene/3d/collision_shape.h"
 #include "scene/3d/collision_shape.h"
 #include "scene/3d/mesh_instance.h"
 #include "scene/3d/mesh_instance.h"
 #include "scene/3d/physics_body.h"
 #include "scene/3d/physics_body.h"
+#include "scene/3d/room_manager.h"
 #include "scene/3d/visual_instance.h"
 #include "scene/3d/visual_instance.h"
 #include "scene/gui/viewport_container.h"
 #include "scene/gui/viewport_container.h"
 #include "scene/resources/packed_scene.h"
 #include "scene/resources/packed_scene.h"
@@ -733,6 +734,10 @@ void SpatialEditorViewport::_update_name() {
 		view_mode += " [auto]";
 		view_mode += " [auto]";
 	}
 	}
 
 
+	if (RoomManager::static_rooms_get_active_and_loaded()) {
+		view_mode += " [portals active]";
+	}
+
 	if (name != "") {
 	if (name != "") {
 		view_menu->set_text(name + " " + view_mode);
 		view_menu->set_text(name + " " + view_mode);
 	} else {
 	} else {
@@ -4303,6 +4308,42 @@ void SpatialEditor::select_gizmo_highlight_axis(int p_axis) {
 	}
 	}
 }
 }
 
 
+void SpatialEditor::show_advanced_portal_tools(bool p_show) {
+	// toolbar button
+	Button *const button = tool_button[TOOL_CONVERT_ROOMS];
+	if (p_show) {
+		button->set_text(TTR("Convert Rooms"));
+	} else {
+		button->set_text("");
+	}
+}
+
+void SpatialEditor::update_portal_tools() {
+	// the view portal culling toggle
+	int view_portal_item_index = view_menu->get_popup()->get_item_index(MENU_VIEW_PORTAL_CULLING);
+	if (RoomManager::active_room_manager) {
+		view_menu->get_popup()->set_item_disabled(view_portal_item_index, false);
+
+		bool active = RoomManager::static_rooms_get_active();
+		view_menu->get_popup()->set_item_checked(view_portal_item_index, active);
+	} else {
+		view_menu->get_popup()->set_item_disabled(view_portal_item_index, true);
+	}
+
+	// toolbar button
+	Button *const button = tool_button[TOOL_CONVERT_ROOMS];
+
+	if (RoomManager::active_room_manager) {
+		button->show();
+	} else {
+		button->hide();
+	}
+
+	for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) {
+		viewports[i]->_update_name();
+	}
+}
+
 void SpatialEditor::update_transform_gizmo() {
 void SpatialEditor::update_transform_gizmo() {
 	List<Node *> &selection = editor_selection->get_selected_node_list();
 	List<Node *> &selection = editor_selection->get_selected_node_list();
 	AABB center;
 	AABB center;
@@ -4791,6 +4832,10 @@ void SpatialEditor::_menu_item_pressed(int p_option) {
 			update_transform_gizmo();
 			update_transform_gizmo();
 
 
 		} break;
 		} break;
+		case MENU_TOOL_CONVERT_ROOMS: {
+			RoomManager::static_rooms_convert();
+			update_portal_tools();
+		} break;
 		case MENU_TRANSFORM_CONFIGURE_SNAP: {
 		case MENU_TRANSFORM_CONFIGURE_SNAP: {
 			snap_dialog->popup_centered(Size2(200, 180));
 			snap_dialog->popup_centered(Size2(200, 180));
 		} break;
 		} break;
@@ -4897,6 +4942,11 @@ void SpatialEditor::_menu_item_pressed(int p_option) {
 			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), grid_enabled);
 			view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), grid_enabled);
 
 
 		} break;
 		} break;
+		case MENU_VIEW_PORTAL_CULLING: {
+			bool is_checked = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(p_option));
+			RoomManager::static_rooms_set_active(!is_checked);
+			update_portal_tools();
+		} break;
 		case MENU_VIEW_CAMERA_SETTINGS: {
 		case MENU_VIEW_CAMERA_SETTINGS: {
 			settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50));
 			settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50));
 		} break;
 		} break;
@@ -5916,6 +5966,7 @@ void SpatialEditor::_notification(int p_what) {
 		tool_button[SpatialEditor::TOOL_UNLOCK_SELECTED]->set_icon(get_icon("Unlock", "EditorIcons"));
 		tool_button[SpatialEditor::TOOL_UNLOCK_SELECTED]->set_icon(get_icon("Unlock", "EditorIcons"));
 		tool_button[SpatialEditor::TOOL_GROUP_SELECTED]->set_icon(get_icon("Group", "EditorIcons"));
 		tool_button[SpatialEditor::TOOL_GROUP_SELECTED]->set_icon(get_icon("Group", "EditorIcons"));
 		tool_button[SpatialEditor::TOOL_UNGROUP_SELECTED]->set_icon(get_icon("Ungroup", "EditorIcons"));
 		tool_button[SpatialEditor::TOOL_UNGROUP_SELECTED]->set_icon(get_icon("Ungroup", "EditorIcons"));
+		tool_button[SpatialEditor::TOOL_CONVERT_ROOMS]->set_icon(get_icon("RoomGroup", "EditorIcons"));
 
 
 		tool_option_button[SpatialEditor::TOOL_OPT_LOCAL_COORDS]->set_icon(get_icon("Object", "EditorIcons"));
 		tool_option_button[SpatialEditor::TOOL_OPT_LOCAL_COORDS]->set_icon(get_icon("Object", "EditorIcons"));
 		tool_option_button[SpatialEditor::TOOL_OPT_USE_SNAP]->set_icon(get_icon("Snap", "EditorIcons"));
 		tool_option_button[SpatialEditor::TOOL_OPT_USE_SNAP]->set_icon(get_icon("Snap", "EditorIcons"));
@@ -6293,6 +6344,15 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
 	tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->connect("toggled", this, "_menu_item_toggled", button_binds);
 	tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->connect("toggled", this, "_menu_item_toggled", button_binds);
 	_update_camera_override_button(false);
 	_update_camera_override_button(false);
 
 
+	tool_button[TOOL_CONVERT_ROOMS] = memnew(ToolButton);
+	hbc_menu->add_child(tool_button[TOOL_CONVERT_ROOMS]);
+	tool_button[TOOL_CONVERT_ROOMS]->set_toggle_mode(false);
+	tool_button[TOOL_CONVERT_ROOMS]->set_flat(true);
+	button_binds.write[0] = MENU_TOOL_CONVERT_ROOMS;
+	tool_button[TOOL_CONVERT_ROOMS]->connect("pressed", this, "_menu_item_pressed", button_binds);
+	tool_button[TOOL_CONVERT_ROOMS]->set_shortcut(ED_SHORTCUT("spatial_editor/convert_rooms", TTR("Convert Rooms"), KEY_MASK_ALT | KEY_C));
+	tool_button[TOOL_CONVERT_ROOMS]->set_tooltip(TTR("Converts rooms for portal culling."));
+
 	hbc_menu->add_child(memnew(VSeparator));
 	hbc_menu->add_child(memnew(VSeparator));
 
 
 	// Drag and drop support;
 	// Drag and drop support;
@@ -6363,6 +6423,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
 	p->add_separator();
 	p->add_separator();
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN);
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN);
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid"), KEY_MASK_CMD + KEY_G), MENU_VIEW_GRID);
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid"), KEY_MASK_CMD + KEY_G), MENU_VIEW_GRID);
+	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_portal_culling", TTR("View Portal Culling"), KEY_MASK_ALT | KEY_P), MENU_VIEW_PORTAL_CULLING);
 
 
 	p->add_separator();
 	p->add_separator();
 	p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings...")), MENU_VIEW_CAMERA_SETTINGS);
 	p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings...")), MENU_VIEW_CAMERA_SETTINGS);

+ 7 - 1
editor/plugins/spatial_editor_plugin.h

@@ -207,7 +207,8 @@ class SpatialEditorViewport : public Control {
 		VIEW_DISPLAY_SHADELESS,
 		VIEW_DISPLAY_SHADELESS,
 		VIEW_LOCK_ROTATION,
 		VIEW_LOCK_ROTATION,
 		VIEW_CINEMATIC_PREVIEW,
 		VIEW_CINEMATIC_PREVIEW,
-		VIEW_AUTO_ORTHOGONAL
+		VIEW_AUTO_ORTHOGONAL,
+		VIEW_PORTAL_CULLING,
 	};
 	};
 
 
 public:
 public:
@@ -545,6 +546,7 @@ public:
 		TOOL_UNLOCK_SELECTED,
 		TOOL_UNLOCK_SELECTED,
 		TOOL_GROUP_SELECTED,
 		TOOL_GROUP_SELECTED,
 		TOOL_UNGROUP_SELECTED,
 		TOOL_UNGROUP_SELECTED,
+		TOOL_CONVERT_ROOMS,
 		TOOL_MAX
 		TOOL_MAX
 	};
 	};
 
 
@@ -624,6 +626,7 @@ private:
 		MENU_TOOL_LOCAL_COORDS,
 		MENU_TOOL_LOCAL_COORDS,
 		MENU_TOOL_USE_SNAP,
 		MENU_TOOL_USE_SNAP,
 		MENU_TOOL_OVERRIDE_CAMERA,
 		MENU_TOOL_OVERRIDE_CAMERA,
+		MENU_TOOL_CONVERT_ROOMS,
 		MENU_TRANSFORM_CONFIGURE_SNAP,
 		MENU_TRANSFORM_CONFIGURE_SNAP,
 		MENU_TRANSFORM_DIALOG,
 		MENU_TRANSFORM_DIALOG,
 		MENU_VIEW_USE_1_VIEWPORT,
 		MENU_VIEW_USE_1_VIEWPORT,
@@ -634,6 +637,7 @@ private:
 		MENU_VIEW_USE_4_VIEWPORTS,
 		MENU_VIEW_USE_4_VIEWPORTS,
 		MENU_VIEW_ORIGIN,
 		MENU_VIEW_ORIGIN,
 		MENU_VIEW_GRID,
 		MENU_VIEW_GRID,
+		MENU_VIEW_PORTAL_CULLING,
 		MENU_VIEW_GIZMOS_3D_ICONS,
 		MENU_VIEW_GIZMOS_3D_ICONS,
 		MENU_VIEW_CAMERA_SETTINGS,
 		MENU_VIEW_CAMERA_SETTINGS,
 		MENU_LOCK_SELECTED,
 		MENU_LOCK_SELECTED,
@@ -762,6 +766,8 @@ public:
 
 
 	void update_grid();
 	void update_grid();
 	void update_transform_gizmo();
 	void update_transform_gizmo();
+	void update_portal_tools();
+	void show_advanced_portal_tools(bool p_show);
 	void update_all_gizmos(Node *p_node = nullptr);
 	void update_all_gizmos(Node *p_node = nullptr);
 	void snap_selected_nodes_to_floor();
 	void snap_selected_nodes_to_floor();
 	void select_gizmo_highlight_axis(int p_axis);
 	void select_gizmo_highlight_axis(int p_axis);

+ 67 - 14
scene/3d/room_manager.cpp

@@ -43,6 +43,10 @@
 #include "scene/3d/light.h"
 #include "scene/3d/light.h"
 #include "visibility_notifier.h"
 #include "visibility_notifier.h"
 
 
+#ifdef TOOLS_ENABLED
+#include "editor/plugins/spatial_editor_plugin.h"
+#endif
+
 #include "modules/modules_enabled.gen.h"
 #include "modules/modules_enabled.gen.h"
 #ifdef MODULE_CSG_ENABLED
 #ifdef MODULE_CSG_ENABLED
 #include "modules/csg/csg_shape.h"
 #include "modules/csg/csg_shape.h"
@@ -54,31 +58,55 @@
 #include "core/math/convex_hull.h"
 #include "core/math/convex_hull.h"
 #endif
 #endif
 
 
-#ifdef TOOLS_ENABLED
-RoomManager *RoomManager::active_room_manager = nullptr;
-#endif
-
 // This needs to be static because it cannot easily be propagated to portals
 // This needs to be static because it cannot easily be propagated to portals
 // during load (as the RoomManager may be loaded before Portals enter the scene tree)
 // during load (as the RoomManager may be loaded before Portals enter the scene tree)
 real_t RoomManager::_default_portal_margin = 1.0;
 real_t RoomManager::_default_portal_margin = 1.0;
 
 
+#ifdef TOOLS_ENABLED
+RoomManager *RoomManager::active_room_manager = nullptr;
+
+// static versions of functions for use from editor toolbars
+void RoomManager::static_rooms_set_active(bool p_active) {
+	if (active_room_manager) {
+		active_room_manager->rooms_set_active(p_active);
+		active_room_manager->property_list_changed_notify();
+	}
+}
+
+bool RoomManager::static_rooms_get_active() {
+	if (active_room_manager) {
+		return active_room_manager->rooms_get_active();
+	}
+
+	return false;
+}
+
+bool RoomManager::static_rooms_get_active_and_loaded() {
+	if (active_room_manager) {
+		if (active_room_manager->rooms_get_active()) {
+			Ref<World> world = active_room_manager->get_world();
+			RID scenario = world->get_scenario();
+			return active_room_manager->rooms_get_active() && VisualServer::get_singleton()->rooms_is_loaded(scenario);
+		}
+	}
+
+	return false;
+}
+
+void RoomManager::static_rooms_convert() {
+	if (active_room_manager) {
+		return active_room_manager->rooms_convert();
+	}
+}
+#endif
+
 RoomManager::RoomManager() {
 RoomManager::RoomManager() {
 	// some high value, we want room manager to be processed after other
 	// some high value, we want room manager to be processed after other
 	// nodes because the camera should be moved first
 	// nodes because the camera should be moved first
 	set_process_priority(10000);
 	set_process_priority(10000);
-
-#ifdef TOOLS_ENABLED
-	// note this mechanism may fail to work correctly if the user creates two room managers,
-	// but should not create major problems as it is just used to auto update when portals etc
-	// are changed in the editor, and there is a check for nullptr.
-	active_room_manager = this;
-#endif
 }
 }
 
 
 RoomManager::~RoomManager() {
 RoomManager::~RoomManager() {
-#ifdef TOOLS_ENABLED
-	active_room_manager = nullptr;
-#endif
 }
 }
 
 
 String RoomManager::get_configuration_warning() const {
 String RoomManager::get_configuration_warning() const {
@@ -173,12 +201,33 @@ void RoomManager::_notification(int p_what) {
 		case NOTIFICATION_ENTER_TREE: {
 		case NOTIFICATION_ENTER_TREE: {
 			if (Engine::get_singleton()->is_editor_hint()) {
 			if (Engine::get_singleton()->is_editor_hint()) {
 				set_process_internal(_godot_preview_camera_ID != (ObjectID)-1);
 				set_process_internal(_godot_preview_camera_ID != (ObjectID)-1);
+#ifdef TOOLS_ENABLED
+				// note this mechanism may fail to work correctly if the user creates two room managers,
+				// but should not create major problems as it is just used to auto update when portals etc
+				// are changed in the editor, and there is a check for nullptr.
+				active_room_manager = this;
+				SpatialEditor *spatial_editor = SpatialEditor::get_singleton();
+				if (spatial_editor) {
+					spatial_editor->update_portal_tools();
+				}
+#endif
 			} else {
 			} else {
 				if (_settings_gameplay_monitor_enabled) {
 				if (_settings_gameplay_monitor_enabled) {
 					set_process_internal(true);
 					set_process_internal(true);
 				}
 				}
 			}
 			}
 		} break;
 		} break;
+		case NOTIFICATION_EXIT_TREE: {
+#ifdef TOOLS_ENABLED
+			active_room_manager = nullptr;
+			if (Engine::get_singleton()->is_editor_hint()) {
+				SpatialEditor *spatial_editor = SpatialEditor::get_singleton();
+				if (spatial_editor) {
+					spatial_editor->update_portal_tools();
+				}
+			}
+#endif
+		} break;
 		case NOTIFICATION_INTERNAL_PROCESS: {
 		case NOTIFICATION_INTERNAL_PROCESS: {
 			// can't call visual server if not inside world
 			// can't call visual server if not inside world
 			if (!is_inside_world()) {
 			if (!is_inside_world()) {
@@ -459,6 +508,10 @@ void RoomManager::rooms_set_active(bool p_active) {
 	if (is_inside_world() && get_world().is_valid()) {
 	if (is_inside_world() && get_world().is_valid()) {
 		VisualServer::get_singleton()->rooms_set_active(get_world()->get_scenario(), p_active);
 		VisualServer::get_singleton()->rooms_set_active(get_world()->get_scenario(), p_active);
 		_active = p_active;
 		_active = p_active;
+
+#ifdef TOOLS_ENABLED
+		SpatialEditor::get_singleton()->update_portal_tools();
+#endif
 	}
 	}
 }
 }
 
 

+ 6 - 0
scene/3d/room_manager.h

@@ -134,6 +134,12 @@ public:
 	// an easy way of grabbing the active room manager for tools purposes
 	// an easy way of grabbing the active room manager for tools purposes
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	static RoomManager *active_room_manager;
 	static RoomManager *active_room_manager;
+
+	// static versions of functions for use from editor toolbars
+	static void static_rooms_set_active(bool p_active);
+	static bool static_rooms_get_active();
+	static bool static_rooms_get_active_and_loaded();
+	static void static_rooms_convert();
 #endif
 #endif
 
 
 private:
 private:

+ 1 - 0
servers/visual/portals/portal_renderer.h

@@ -159,6 +159,7 @@ public:
 	// for use in the editor only, to allow a cheap way of turning off portals
 	// for use in the editor only, to allow a cheap way of turning off portals
 	// if there has been a change, e.g. moving a room etc.
 	// if there has been a change, e.g. moving a room etc.
 	void rooms_unload() { _ensure_unloaded(); }
 	void rooms_unload() { _ensure_unloaded(); }
+	bool rooms_is_loaded() const { return _loaded; }
 
 
 	// debugging
 	// debugging
 	void set_debug_sprawl(bool p_active) { _debug_sprawl = p_active; }
 	void set_debug_sprawl(bool p_active) { _debug_sprawl = p_active; }

+ 3 - 0
servers/visual/visual_server_raster.h

@@ -593,6 +593,9 @@ public:
 	BIND3(rooms_set_debug_feature, RID, RoomsDebugFeature, bool)
 	BIND3(rooms_set_debug_feature, RID, RoomsDebugFeature, bool)
 	BIND2(rooms_update_gameplay_monitor, RID, const Vector<Vector3> &)
 	BIND2(rooms_update_gameplay_monitor, RID, const Vector<Vector3> &)
 
 
+	// don't use this in a game
+	BIND1RC(bool, rooms_is_loaded, RID)
+
 	// Callbacks
 	// Callbacks
 	BIND1(callbacks_register, VisualServerCallbacks *)
 	BIND1(callbacks_register, VisualServerCallbacks *)
 
 

+ 6 - 0
servers/visual/visual_server_scene.cpp

@@ -1308,6 +1308,12 @@ void VisualServerScene::rooms_update_gameplay_monitor(RID p_scenario, const Vect
 	scenario->_portal_renderer.rooms_update_gameplay_monitor(p_camera_positions);
 	scenario->_portal_renderer.rooms_update_gameplay_monitor(p_camera_positions);
 }
 }
 
 
+bool VisualServerScene::rooms_is_loaded(RID p_scenario) const {
+	Scenario *scenario = scenario_owner.getornull(p_scenario);
+	ERR_FAIL_COND_V(!scenario, false);
+	return scenario->_portal_renderer.rooms_is_loaded();
+}
+
 Vector<ObjectID> VisualServerScene::instances_cull_aabb(const AABB &p_aabb, RID p_scenario) const {
 Vector<ObjectID> VisualServerScene::instances_cull_aabb(const AABB &p_aabb, RID p_scenario) const {
 	Vector<ObjectID> instances;
 	Vector<ObjectID> instances;
 	Scenario *scenario = scenario_owner.get(p_scenario);
 	Scenario *scenario = scenario_owner.get(p_scenario);

+ 3 - 0
servers/visual/visual_server_scene.h

@@ -647,6 +647,9 @@ public:
 	virtual void rooms_set_debug_feature(RID p_scenario, VisualServer::RoomsDebugFeature p_feature, bool p_active);
 	virtual void rooms_set_debug_feature(RID p_scenario, VisualServer::RoomsDebugFeature p_feature, bool p_active);
 	virtual void rooms_update_gameplay_monitor(RID p_scenario, const Vector<Vector3> &p_camera_positions);
 	virtual void rooms_update_gameplay_monitor(RID p_scenario, const Vector<Vector3> &p_camera_positions);
 
 
+	// don't use this in a game
+	virtual bool rooms_is_loaded(RID p_scenario) const;
+
 	virtual void callbacks_register(VisualServerCallbacks *p_callbacks);
 	virtual void callbacks_register(VisualServerCallbacks *p_callbacks);
 	VisualServerCallbacks *get_callbacks() const { return _visual_server_callbacks; }
 	VisualServerCallbacks *get_callbacks() const { return _visual_server_callbacks; }
 
 

+ 3 - 0
servers/visual/visual_server_wrap_mt.h

@@ -516,6 +516,9 @@ public:
 	FUNC3(rooms_set_debug_feature, RID, RoomsDebugFeature, bool)
 	FUNC3(rooms_set_debug_feature, RID, RoomsDebugFeature, bool)
 	FUNC2(rooms_update_gameplay_monitor, RID, const Vector<Vector3> &)
 	FUNC2(rooms_update_gameplay_monitor, RID, const Vector<Vector3> &)
 
 
+	// don't use this in a game
+	FUNC1RC(bool, rooms_is_loaded, RID)
+
 	// Callbacks
 	// Callbacks
 	FUNC1(callbacks_register, VisualServerCallbacks *)
 	FUNC1(callbacks_register, VisualServerCallbacks *)
 
 

+ 3 - 0
servers/visual_server.h

@@ -909,6 +909,9 @@ public:
 	virtual void rooms_set_debug_feature(RID p_scenario, RoomsDebugFeature p_feature, bool p_active) = 0;
 	virtual void rooms_set_debug_feature(RID p_scenario, RoomsDebugFeature p_feature, bool p_active) = 0;
 	virtual void rooms_update_gameplay_monitor(RID p_scenario, const Vector<Vector3> &p_camera_positions) = 0;
 	virtual void rooms_update_gameplay_monitor(RID p_scenario, const Vector<Vector3> &p_camera_positions) = 0;
 
 
+	// don't use this in a game!
+	virtual bool rooms_is_loaded(RID p_scenario) const = 0;
+
 	// callbacks are used to send messages back from the visual server to scene tree in thread friendly manner
 	// callbacks are used to send messages back from the visual server to scene tree in thread friendly manner
 	virtual void callbacks_register(VisualServerCallbacks *p_callbacks) = 0;
 	virtual void callbacks_register(VisualServerCallbacks *p_callbacks) = 0;