diff --git a/doc/classes/XRServer.xml b/doc/classes/XRServer.xml index 27c4ed9a1e..d4f1f6cc51 100644 --- a/doc/classes/XRServer.xml +++ b/doc/classes/XRServer.xml @@ -165,6 +165,11 @@ Emitted when an existing tracker has been updated. This can happen if the user switches controllers. + + + Emitted when the world origin transform changes. + + diff --git a/modules/openxr/scene/openxr_composition_layer.cpp b/modules/openxr/scene/openxr_composition_layer.cpp index 1c5266c493..3aaecf14b1 100644 --- a/modules/openxr/scene/openxr_composition_layer.cpp +++ b/modules/openxr/scene/openxr_composition_layer.cpp @@ -64,9 +64,9 @@ OpenXRCompositionLayer::OpenXRCompositionLayer() { } XRServer::get_singleton()->connect("reference_frame_changed", callable_mp(this, &OpenXRCompositionLayer::update_transform)); + XRServer::get_singleton()->connect("world_origin_changed", callable_mp(this, &OpenXRCompositionLayer::update_transform)); set_process_internal(true); - set_notify_local_transform(true); if (Engine::get_singleton()->is_editor_hint()) { // In the editor, create the fallback right away. @@ -204,7 +204,7 @@ bool OpenXRCompositionLayer::_should_use_fallback_node() { if (Engine::get_singleton()->is_editor_hint() || openxr_api == nullptr) { return true; } else if (openxr_session_running) { - return enable_hole_punch || (!is_natively_supported() && !use_android_surface); + return enable_hole_punch || !is_natively_supported(); } return false; } @@ -218,7 +218,7 @@ void OpenXRCompositionLayer::_create_fallback_node() { } void OpenXRCompositionLayer::_remove_fallback_node() { - ERR_FAIL_COND(fallback != nullptr); + ERR_FAIL_COND(fallback == nullptr); remove_child(fallback); fallback->queue_free(); fallback = nullptr; @@ -253,7 +253,7 @@ void OpenXRCompositionLayer::_clear_composition_layer() { } void OpenXRCompositionLayer::_viewport_size_changed() { - if (layer_viewport && openxr_session_running && composition_layer_extension && is_natively_supported() && is_visible() && is_inside_tree()) { + if (layer_viewport && openxr_session_running && composition_layer_extension && is_natively_supported() && is_visible_in_tree() && is_inside_tree()) { composition_layer_extension->composition_layer_set_viewport(composition_layer, layer_viewport->get_viewport_rid(), layer_viewport->get_size()); } } @@ -278,19 +278,32 @@ void OpenXRCompositionLayer::_on_openxr_session_stopping() { void OpenXRCompositionLayer::update_transform() { if (composition_layer_extension) { + XRCamera3D *ancestor_camera = _get_xrcamera3d_ancestor(); bool parent_is_xr_camera = Object::cast_to(get_parent()) != nullptr; + bool parent_is_xr_origin = Object::cast_to(get_parent()) != nullptr; OpenXRCompositionLayerExtension::PoseSpace new_pose_space; - // Automatically set the PoseSpace to POSE_HEAD_LOCKED if layer is a child of XRCamera3D. - if (parent_is_xr_camera) { + // Automatically set the PoseSpace to POSE_HEAD_LOCKED if layer has an XRCamera3D ancestor. + if (ancestor_camera) { new_pose_space = OpenXRCompositionLayerExtension::PoseSpace::POSE_HEAD_LOCKED; } else { new_pose_space = OpenXRCompositionLayerExtension::PoseSpace::POSE_WORLD_LOCKED; } + Transform3D xf; + if (parent_is_xr_origin || parent_is_xr_camera) { + xf = get_transform(); + } else { + if (ancestor_camera) { + xf = ancestor_camera->get_global_transform().affine_inverse() * get_global_transform(); + } else { + xf = XRServer::get_singleton()->get_world_origin().affine_inverse() * get_global_transform(); + } + } + // Pose space must be set first, as composition_layer_set_transform() depends on it. composition_layer_extension->composition_layer_set_pose_space(composition_layer, new_pose_space); - composition_layer_extension->composition_layer_set_transform(composition_layer, get_transform()); + composition_layer_extension->composition_layer_set_transform(composition_layer, xf); } } @@ -299,7 +312,7 @@ void OpenXRCompositionLayer::update_fallback_mesh() { } bool OpenXRCompositionLayer::_should_register() { - return !registered && openxr_session_running && is_inside_tree() && is_visible() && is_natively_supported(); + return !registered && openxr_session_running && is_inside_tree() && is_visible_in_tree() && is_natively_supported(); } bool OpenXRCompositionLayer::is_viewport_in_use(SubViewport *p_viewport) { @@ -345,7 +358,8 @@ void OpenXRCompositionLayer::set_layer_viewport(SubViewport *p_viewport) { if (fallback) { _reset_fallback_material(); - } else if (openxr_session_running && composition_layer_extension && is_visible() && is_inside_tree()) { + } + if (openxr_session_running && composition_layer_extension && is_visible_in_tree() && is_inside_tree() && is_natively_supported()) { if (layer_viewport) { composition_layer_extension->composition_layer_set_viewport(composition_layer, layer_viewport->get_viewport_rid(), layer_viewport->get_size()); } else { @@ -710,7 +724,7 @@ void OpenXRCompositionLayer::_notification(int p_what) { } break; case NOTIFICATION_VISIBILITY_CHANGED: { if (is_natively_supported() && openxr_session_running && is_inside_tree()) { - if (is_visible()) { + if (is_visible_in_tree()) { _setup_composition_layer(); update_transform(); } else { @@ -719,19 +733,28 @@ void OpenXRCompositionLayer::_notification(int p_what) { } update_configuration_warnings(); } break; - case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: + case NOTIFICATION_TRANSFORM_CHANGED: { update_transform(); update_configuration_warnings(); } break; case NOTIFICATION_ENTER_TREE: { if (layer_viewport && is_viewport_in_use(layer_viewport)) { _clear_composition_layer(); - } else if (openxr_session_running && is_visible()) { + } else if (openxr_session_running && is_visible_in_tree()) { _setup_composition_layer(); } update_transform(); } break; case NOTIFICATION_PARENTED: { + // Enables NOTIFICATION_LOCAL_TRANSFORM_CHANGED when XROrigin3D or XRCamera3D are the parents + // since that notification happens less frequently than NOTIFICATION_TRANSFORM_CHANGED. + bool parent_is_xr_camera = Object::cast_to(get_parent()) != nullptr; + bool parent_is_xr_origin = Object::cast_to(get_parent()) != nullptr; + bool enable_local_transform_notification = parent_is_xr_camera || parent_is_xr_origin; + set_notify_local_transform(enable_local_transform_notification); + set_notify_transform(!enable_local_transform_notification); + update_transform(); } break; case NOTIFICATION_EXIT_TREE: { @@ -795,18 +818,22 @@ void OpenXRCompositionLayer::_validate_property(PropertyInfo &p_property) const } } +XRCamera3D *OpenXRCompositionLayer::_get_xrcamera3d_ancestor() const { + Node *parent = get_parent(); + while (parent != nullptr) { + XRCamera3D *camera = Object::cast_to(parent); + if (camera != nullptr) { + return camera; + } + + parent = parent->get_parent(); + } + return nullptr; +} + PackedStringArray OpenXRCompositionLayer::get_configuration_warnings() const { PackedStringArray warnings = Node3D::get_configuration_warnings(); - if (is_visible() && is_inside_tree()) { - XROrigin3D *xr_origin = Object::cast_to(get_parent()); - XRCamera3D *xr_camera = Object::cast_to(get_parent()); - - if (xr_origin == nullptr && xr_camera == nullptr) { - warnings.push_back(RTR("OpenXR composition layers must have have either an XROrigin3D or XRCamera3D node as their parent.")); - } - } - if (!get_transform().basis.is_orthonormal()) { warnings.push_back(RTR("OpenXR composition layers must have orthonormalized transforms (ie. no scale or shearing).")); } diff --git a/modules/openxr/scene/openxr_composition_layer.h b/modules/openxr/scene/openxr_composition_layer.h index aac96fecb8..f0f9628be3 100644 --- a/modules/openxr/scene/openxr_composition_layer.h +++ b/modules/openxr/scene/openxr_composition_layer.h @@ -40,6 +40,7 @@ class Mesh; class OpenXRAPI; class OpenXRCompositionLayerExtension; class SubViewport; +class XRCamera3D; class OpenXRCompositionLayer : public Node3D { GDCLASS(OpenXRCompositionLayer, Node3D); @@ -125,6 +126,8 @@ private: void _viewport_size_changed(); + XRCamera3D *_get_xrcamera3d_ancestor() const; + protected: OpenXRAPI *openxr_api = nullptr; OpenXRCompositionLayerExtension *composition_layer_extension = nullptr; diff --git a/servers/xr/xr_server.cpp b/servers/xr/xr_server.cpp index a42ee886f6..0ee6234dbb 100644 --- a/servers/xr/xr_server.cpp +++ b/servers/xr/xr_server.cpp @@ -107,6 +107,8 @@ void XRServer::_bind_methods() { ADD_SIGNAL(MethodInfo("tracker_added", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type"))); ADD_SIGNAL(MethodInfo("tracker_updated", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type"))); ADD_SIGNAL(MethodInfo("tracker_removed", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type"))); + + ADD_SIGNAL(MethodInfo("world_origin_changed")); } double XRServer::get_world_scale() const { @@ -157,6 +159,7 @@ Transform3D XRServer::get_world_origin() const { void XRServer::set_world_origin(const Transform3D &p_world_origin) { world_origin = p_world_origin; + emit_signal(SNAME("world_origin_changed")); set_render_world_origin(world_origin); }