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);
}