From d6a4fe6c052ec5a71d1a25d175ddfb17763d6bf1 Mon Sep 17 00:00:00 2001 From: David Snopek Date: Tue, 12 Nov 2024 14:36:58 -0600 Subject: [PATCH] XR: Allow locking the camera to the `XROrigin3D` for benchmarking or automated testing --- doc/classes/XRServer.xml | 4 ++++ servers/rendering/renderer_scene_cull.cpp | 12 +++++++++++- servers/xr_server.cpp | 7 +++++++ servers/xr_server.h | 4 ++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/doc/classes/XRServer.xml b/doc/classes/XRServer.xml index 49b13986ca1..247d2ab938c 100644 --- a/doc/classes/XRServer.xml +++ b/doc/classes/XRServer.xml @@ -111,6 +111,10 @@ + + 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. + The primary [XRInterface] currently bound to the [XRServer]. diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 11ca7de44f0..eec3ef4c2f4 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -2773,6 +2773,8 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu camera_data.set_camera(transform, projection, is_orthogonal, is_frustum, vaspect, jitter, taa_frame_count, camera->visible_layers); } else { + XRServer *xr_server = XRServer::get_singleton(); + // Setup our camera for our XR interface. // We can support multiple views here each with their own camera Transform3D transforms[RendererSceneRender::MAX_RENDER_VIEWS]; @@ -2783,7 +2785,7 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu 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. // 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 &p_render_bu 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) { 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) { diff --git a/servers/xr_server.cpp b/servers/xr_server.cpp index 4c4781fdef3..2f9479433a0 100644 --- a/servers/xr_server.cpp +++ b/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("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("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::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("get_interface_count"), &XRServer::get_interface_count); @@ -241,6 +244,10 @@ Transform3D XRServer::get_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 &p_interface) { ERR_FAIL_COND(p_interface.is_null()); diff --git a/servers/xr_server.h b/servers/xr_server.h index cd9c241bb0a..6867ce9aa0a 100644 --- a/servers/xr_server.h +++ b/servers/xr_server.h @@ -96,6 +96,7 @@ private: 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 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, // we need to keep copies around. @@ -198,6 +199,9 @@ public: */ 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. */