Browse Source

XR: Allow locking the camera to the `XROrigin3D` for benchmarking or automated testing

David Snopek 9 months ago
parent
commit
d6a4fe6c05

+ 4 - 0
doc/classes/XRServer.xml

@@ -111,6 +111,10 @@
 		</method>
 		</method>
 	</methods>
 	</methods>
 	<members>
 	<members>
+		<member name="camera_locked_to_origin" type="bool" setter="set_camera_locked_to_origin" getter="is_camera_locked_to_origin" default="false">
+			If set to [code]true[/code], the scene will be rendered as if the camera is locked to the [XROrigin3D].
+			[b]Note:[/b] This doesn't provide a very comfortable experience for users. This setting exists for doing benchmarking or automated testing, where you want to control what is rendered via code.
+		</member>
 		<member name="primary_interface" type="XRInterface" setter="set_primary_interface" getter="get_primary_interface">
 		<member name="primary_interface" type="XRInterface" setter="set_primary_interface" getter="get_primary_interface">
 			The primary [XRInterface] currently bound to the [XRServer].
 			The primary [XRInterface] currently bound to the [XRServer].
 		</member>
 		</member>

+ 11 - 1
servers/rendering/renderer_scene_cull.cpp

@@ -2773,6 +2773,8 @@ void RendererSceneCull::render_camera(const Ref<RenderSceneBuffers> &p_render_bu
 
 
 		camera_data.set_camera(transform, projection, is_orthogonal, is_frustum, vaspect, jitter, taa_frame_count, camera->visible_layers);
 		camera_data.set_camera(transform, projection, is_orthogonal, is_frustum, vaspect, jitter, taa_frame_count, camera->visible_layers);
 	} else {
 	} else {
+		XRServer *xr_server = XRServer::get_singleton();
+
 		// Setup our camera for our XR interface.
 		// Setup our camera for our XR interface.
 		// We can support multiple views here each with their own camera
 		// We can support multiple views here each with their own camera
 		Transform3D transforms[RendererSceneRender::MAX_RENDER_VIEWS];
 		Transform3D transforms[RendererSceneRender::MAX_RENDER_VIEWS];
@@ -2783,7 +2785,7 @@ void RendererSceneCull::render_camera(const Ref<RenderSceneBuffers> &p_render_bu
 
 
 		float aspect = p_viewport_size.width / (float)p_viewport_size.height;
 		float aspect = p_viewport_size.width / (float)p_viewport_size.height;
 
 
-		Transform3D world_origin = XRServer::get_singleton()->get_world_origin();
+		Transform3D world_origin = xr_server->get_world_origin();
 
 
 		// We ignore our camera position, it will have been positioned with a slightly old tracking position.
 		// We ignore our camera position, it will have been positioned with a slightly old tracking position.
 		// Instead we take our origin point and have our XR interface add fresh tracking data! Whoohoo!
 		// Instead we take our origin point and have our XR interface add fresh tracking data! Whoohoo!
@@ -2792,6 +2794,14 @@ void RendererSceneCull::render_camera(const Ref<RenderSceneBuffers> &p_render_bu
 			projections[v] = p_xr_interface->get_projection_for_view(v, aspect, camera->znear, camera->zfar);
 			projections[v] = p_xr_interface->get_projection_for_view(v, aspect, camera->znear, camera->zfar);
 		}
 		}
 
 
+		// If requested, we move the views to be rendered as if the HMD is at the XROrigin.
+		if (unlikely(xr_server->is_camera_locked_to_origin())) {
+			Transform3D camera_reset = p_xr_interface->get_camera_transform().affine_inverse() * xr_server->get_reference_frame().affine_inverse();
+			for (uint32_t v = 0; v < view_count; v++) {
+				transforms[v] *= camera_reset;
+			}
+		}
+
 		if (view_count == 1) {
 		if (view_count == 1) {
 			camera_data.set_camera(transforms[0], projections[0], false, false, camera->vaspect, jitter, p_jitter_phase_count, camera->visible_layers);
 			camera_data.set_camera(transforms[0], projections[0], false, false, camera->vaspect, jitter, p_jitter_phase_count, camera->visible_layers);
 		} else if (view_count == 2) {
 		} else if (view_count == 2) {

+ 7 - 0
servers/xr_server.cpp

@@ -62,9 +62,12 @@ void XRServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("clear_reference_frame"), &XRServer::clear_reference_frame);
 	ClassDB::bind_method(D_METHOD("clear_reference_frame"), &XRServer::clear_reference_frame);
 	ClassDB::bind_method(D_METHOD("center_on_hmd", "rotation_mode", "keep_height"), &XRServer::center_on_hmd);
 	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);
 	ClassDB::bind_method(D_METHOD("get_hmd_transform"), &XRServer::get_hmd_transform);
+	ClassDB::bind_method(D_METHOD("set_camera_locked_to_origin", "enabled"), &XRServer::set_camera_locked_to_origin);
+	ClassDB::bind_method(D_METHOD("is_camera_locked_to_origin"), &XRServer::is_camera_locked_to_origin);
 
 
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "world_scale"), "set_world_scale", "get_world_scale");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "world_scale"), "set_world_scale", "get_world_scale");
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "world_origin"), "set_world_origin", "get_world_origin");
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "world_origin"), "set_world_origin", "get_world_origin");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "camera_locked_to_origin"), "set_camera_locked_to_origin", "is_camera_locked_to_origin");
 
 
 	ClassDB::bind_method(D_METHOD("add_interface", "interface"), &XRServer::add_interface);
 	ClassDB::bind_method(D_METHOD("add_interface", "interface"), &XRServer::add_interface);
 	ClassDB::bind_method(D_METHOD("get_interface_count"), &XRServer::get_interface_count);
 	ClassDB::bind_method(D_METHOD("get_interface_count"), &XRServer::get_interface_count);
@@ -241,6 +244,10 @@ Transform3D XRServer::get_hmd_transform() {
 	return hmd_transform;
 	return hmd_transform;
 }
 }
 
 
+void XRServer::set_camera_locked_to_origin(bool p_enable) {
+	camera_locked_to_origin = p_enable;
+}
+
 void XRServer::add_interface(const Ref<XRInterface> &p_interface) {
 void XRServer::add_interface(const Ref<XRInterface> &p_interface) {
 	ERR_FAIL_COND(p_interface.is_null());
 	ERR_FAIL_COND(p_interface.is_null());
 
 

+ 4 - 0
servers/xr_server.h

@@ -96,6 +96,7 @@ private:
 	double world_scale = 1.0; /* scale by which we multiply our tracker positions */
 	double world_scale = 1.0; /* scale by which we multiply our tracker positions */
 	Transform3D world_origin; /* our world origin point, maps a location in our virtual world to the origin point in our real world tracking volume */
 	Transform3D world_origin; /* our world origin point, maps a location in our virtual world to the origin point in our real world tracking volume */
 	Transform3D reference_frame; /* our reference frame */
 	Transform3D reference_frame; /* our reference frame */
+	bool camera_locked_to_origin = false;
 
 
 	// As we may be updating our main state for our next frame while we're still rendering our previous frame,
 	// As we may be updating our main state for our next frame while we're still rendering our previous frame,
 	// we need to keep copies around.
 	// we need to keep copies around.
@@ -198,6 +199,9 @@ public:
 	*/
 	*/
 	Transform3D get_hmd_transform();
 	Transform3D get_hmd_transform();
 
 
+	void set_camera_locked_to_origin(bool p_enable);
+	inline bool is_camera_locked_to_origin() const { return camera_locked_to_origin; }
+
 	/*
 	/*
 		Interfaces are objects that 'glue' Godot to an AR or VR SDK such as the Oculus SDK, OpenVR, OpenHMD, etc.
 		Interfaces are objects that 'glue' Godot to an AR or VR SDK such as the Oculus SDK, OpenVR, OpenHMD, etc.
 	*/
 	*/