Sfoglia il codice sorgente

Merge pull request #87745 from dsnopek/openxr-change-reference-space

OpenXR: Allow changing play area mode during active session
Rémi Verschelde 1 anno fa
parent
commit
fb5f34a75a

+ 1 - 0
doc/classes/XRInterface.xml

@@ -141,6 +141,7 @@
 			<param index="0" name="mode" type="int" enum="XRInterface.PlayAreaMode" />
 			<description>
 				Sets the active play area mode, will return [code]false[/code] if the mode can't be used with this interface.
+				[b]Note:[/b] Changing this after the interface has already been initialized can be jarring for the player, so it's recommended to recenter on the HMD with [method XRServer.center_on_hmd] (if switching to [constant XRInterface.XR_PLAY_AREA_STAGE]) or make the switch during a scene change.
 			</description>
 		</method>
 		<method name="start_passthrough">

+ 6 - 0
doc/classes/XRServer.xml

@@ -37,6 +37,12 @@
 				You should call this method after a few seconds have passed. For example, when the user requests a realignment of the display holding a designated button on a controller for a short period of time, or when implementing a teleport mechanism.
 			</description>
 		</method>
+		<method name="clear_reference_frame" qualifiers="const">
+			<return type="Transform3D" />
+			<description>
+				Clears the reference frame that was set by previous calls to [method center_on_hmd].
+			</description>
+		</method>
 		<method name="find_interface" qualifiers="const">
 			<return type="XRInterface" />
 			<param index="0" name="name" type="String" />

+ 75 - 52
modules/openxr/openxr_api.cpp

@@ -680,71 +680,85 @@ bool OpenXRAPI::is_reference_space_supported(XrReferenceSpaceType p_reference_sp
 	return false;
 }
 
-bool OpenXRAPI::setup_spaces() {
-	XrResult result;
+bool OpenXRAPI::setup_play_space() {
+	ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
 
 	XrPosef identityPose = {
 		{ 0.0, 0.0, 0.0, 1.0 },
 		{ 0.0, 0.0, 0.0 }
 	};
 
-	ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
+	XrReferenceSpaceType new_reference_space;
+	XrSpace new_play_space = XR_NULL_HANDLE;
+	bool will_emulate_local_floor = false;
 
-	// create play space
-	{
-		emulating_local_floor = false;
+	if (is_reference_space_supported(requested_reference_space)) {
+		new_reference_space = requested_reference_space;
+	} else if (requested_reference_space == XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT && is_reference_space_supported(XR_REFERENCE_SPACE_TYPE_STAGE)) {
+		print_verbose("OpenXR: LOCAL_FLOOR space isn't supported, emulating using STAGE and LOCAL spaces.");
 
-		if (is_reference_space_supported(requested_reference_space)) {
-			reference_space = requested_reference_space;
-		} else if (requested_reference_space == XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT && is_reference_space_supported(XR_REFERENCE_SPACE_TYPE_STAGE)) {
-			print_verbose("OpenXR: LOCAL_FLOOR space isn't supported, emulating using STAGE and LOCAL spaces.");
+		new_reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
+		will_emulate_local_floor = true;
+	} else {
+		// Fallback on LOCAL, which all OpenXR runtimes are required to support.
+		print_verbose(String("OpenXR: ") + OpenXRUtil::get_reference_space_name(requested_reference_space) + String(" isn't supported, defaulting to LOCAL space."));
+		new_reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
+	}
 
-			reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
-			emulating_local_floor = true;
+	XrReferenceSpaceCreateInfo play_space_create_info = {
+		XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type
+		nullptr, // next
+		new_reference_space, // referenceSpaceType
+		identityPose, // poseInReferenceSpace
+	};
 
-			// We'll use the STAGE space to get the floor height, but we can't do that until
-			// after xrWaitFrame(), so just set this flag for now.
-			should_reset_emulated_floor_height = true;
+	XrResult result = xrCreateReferenceSpace(session, &play_space_create_info, &new_play_space);
+	if (XR_FAILED(result)) {
+		print_line("OpenXR: Failed to create play space [", get_error_string(result), "]");
+		return false;
+	}
 
-		} else {
-			// Fallback on LOCAL, which all OpenXR runtimes are required to support.
-			print_verbose(String("OpenXR: ") + OpenXRUtil::get_reference_space_name(requested_reference_space) + String(" isn't supported, defaulting to LOCAL space."));
-			reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
-		}
+	// If we've previously created a play space, clean it up first.
+	if (play_space != XR_NULL_HANDLE) {
+		xrDestroySpace(play_space);
+	}
+	play_space = new_play_space;
+	reference_space = new_reference_space;
 
-		XrReferenceSpaceCreateInfo play_space_create_info = {
-			XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type
-			nullptr, // next
-			reference_space, // referenceSpaceType
-			identityPose, // poseInReferenceSpace
-		};
+	emulating_local_floor = will_emulate_local_floor;
+	if (emulating_local_floor) {
+		// We'll use the STAGE space to get the floor height, but we can't do that until
+		// after xrWaitFrame(), so just set this flag for now.
+		should_reset_emulated_floor_height = true;
+	}
 
-		result = xrCreateReferenceSpace(session, &play_space_create_info, &play_space);
-		if (XR_FAILED(result)) {
-			print_line("OpenXR: Failed to create play space [", get_error_string(result), "]");
-			return false;
-		}
+	return true;
+}
+
+bool OpenXRAPI::setup_view_space() {
+	ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
+
+	if (!is_reference_space_supported(XR_REFERENCE_SPACE_TYPE_VIEW)) {
+		print_line("OpenXR: reference space XR_REFERENCE_SPACE_TYPE_VIEW is not supported.");
+		return false;
 	}
 
-	// create view space
-	{
-		if (!is_reference_space_supported(XR_REFERENCE_SPACE_TYPE_VIEW)) {
-			print_line("OpenXR: reference space XR_REFERENCE_SPACE_TYPE_VIEW is not supported.");
-			return false;
-		}
+	XrPosef identityPose = {
+		{ 0.0, 0.0, 0.0, 1.0 },
+		{ 0.0, 0.0, 0.0 }
+	};
 
-		XrReferenceSpaceCreateInfo view_space_create_info = {
-			XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type
-			nullptr, // next
-			XR_REFERENCE_SPACE_TYPE_VIEW, // referenceSpaceType
-			identityPose // poseInReferenceSpace
-		};
+	XrReferenceSpaceCreateInfo view_space_create_info = {
+		XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type
+		nullptr, // next
+		XR_REFERENCE_SPACE_TYPE_VIEW, // referenceSpaceType
+		identityPose // poseInReferenceSpace
+	};
 
-		result = xrCreateReferenceSpace(session, &view_space_create_info, &view_space);
-		if (XR_FAILED(result)) {
-			print_line("OpenXR: Failed to create view space [", get_error_string(result), "]");
-			return false;
-		}
+	XrResult result = xrCreateReferenceSpace(session, &view_space_create_info, &view_space);
+	if (XR_FAILED(result)) {
+		print_line("OpenXR: Failed to create view space [", get_error_string(result), "]");
+		return false;
 	}
 
 	return true;
@@ -1262,10 +1276,14 @@ void OpenXRAPI::set_view_configuration(XrViewConfigurationType p_view_configurat
 	view_configuration = p_view_configuration;
 }
 
-void OpenXRAPI::set_requested_reference_space(XrReferenceSpaceType p_requested_reference_space) {
-	ERR_FAIL_COND(is_initialized());
-
+bool OpenXRAPI::set_requested_reference_space(XrReferenceSpaceType p_requested_reference_space) {
 	requested_reference_space = p_requested_reference_space;
+
+	if (is_initialized()) {
+		return setup_play_space();
+	}
+
+	return true;
 }
 
 void OpenXRAPI::set_submit_depth_buffer(bool p_submit_depth_buffer) {
@@ -1466,7 +1484,12 @@ bool OpenXRAPI::initialize_session() {
 		return false;
 	}
 
-	if (!setup_spaces()) {
+	if (!setup_play_space()) {
+		destroy_session();
+		return false;
+	}
+
+	if (!setup_view_space()) {
 		destroy_session();
 		return false;
 	}

+ 3 - 2
modules/openxr/openxr_api.h

@@ -232,7 +232,8 @@ private:
 	bool create_session();
 	bool load_supported_reference_spaces();
 	bool is_reference_space_supported(XrReferenceSpaceType p_reference_space);
-	bool setup_spaces();
+	bool setup_play_space();
+	bool setup_view_space();
 	bool load_supported_swapchain_formats();
 	bool is_swapchain_format_supported(int64_t p_swapchain_format);
 	bool create_swapchains();
@@ -339,7 +340,7 @@ public:
 	void set_view_configuration(XrViewConfigurationType p_view_configuration);
 	XrViewConfigurationType get_view_configuration() const { return view_configuration; }
 
-	void set_requested_reference_space(XrReferenceSpaceType p_requested_reference_space);
+	bool set_requested_reference_space(XrReferenceSpaceType p_requested_reference_space);
 	XrReferenceSpaceType get_requested_reference_space() const { return requested_reference_space; }
 	XrReferenceSpaceType get_reference_space() const { return reference_space; }
 

+ 9 - 3
modules/openxr/openxr_interface.cpp

@@ -711,7 +711,6 @@ XRInterface::PlayAreaMode OpenXRInterface::get_play_area_mode() const {
 }
 
 bool OpenXRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) {
-	ERR_FAIL_COND_V_MSG(initialized, false, "Cannot change play area mode after OpenXR interface has been initialized");
 	ERR_FAIL_NULL_V(openxr_api, false);
 
 	XrReferenceSpaceType reference_space;
@@ -726,8 +725,15 @@ bool OpenXRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) {
 		return false;
 	}
 
-	openxr_api->set_requested_reference_space(reference_space);
-	return true;
+	if (openxr_api->set_requested_reference_space(reference_space)) {
+		XRServer *xr_server = XRServer::get_singleton();
+		if (xr_server) {
+			xr_server->clear_reference_frame();
+		}
+		return true;
+	}
+
+	return false;
 }
 
 PackedVector3Array OpenXRInterface::get_play_area() const {

+ 5 - 0
servers/xr_server.cpp

@@ -55,6 +55,7 @@ void XRServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_world_origin"), &XRServer::get_world_origin);
 	ClassDB::bind_method(D_METHOD("set_world_origin", "world_origin"), &XRServer::set_world_origin);
 	ClassDB::bind_method(D_METHOD("get_reference_frame"), &XRServer::get_reference_frame);
+	ClassDB::bind_method(D_METHOD("clear_reference_frame"), &XRServer::get_reference_frame);
 	ClassDB::bind_method(D_METHOD("center_on_hmd", "rotation_mode", "keep_height"), &XRServer::center_on_hmd);
 	ClassDB::bind_method(D_METHOD("get_hmd_transform"), &XRServer::get_hmd_transform);
 
@@ -158,6 +159,10 @@ void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) {
 	reference_frame = new_reference_frame.inverse();
 };
 
+void XRServer::clear_reference_frame() {
+	reference_frame = Transform3D();
+}
+
 Transform3D XRServer::get_hmd_transform() {
 	Transform3D hmd_transform;
 	if (primary_interface != nullptr) {

+ 1 - 0
servers/xr_server.h

@@ -142,6 +142,7 @@ public:
 		and in the virtual world out of sync
 	*/
 	Transform3D get_reference_frame() const;
+	void clear_reference_frame();
 	void center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height);
 
 	/*