diff --git a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
index 9d6b197ee13..79aa547c526 100644
--- a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
+++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
@@ -46,6 +46,18 @@
Returns a [PackedStringArray] of positional tracker names that are used within the extension wrapper.
+
+
+
+ Gets an array of [Dictionary]s that represent properties, just like [method Object._get_property_list], that will be added to [OpenXRCompositionLayer] nodes.
+
+
+
+
+
+ Gets a [Dictionary] containing the default values for the properties returned by [method _get_viewport_composition_layer_extension_properties].
+
+
@@ -152,6 +164,14 @@
Called when the OpenXR session state is changed to visible. This means OpenXR is now ready to receive frames.
+
+
+
+
+ Called when a composition layer created via [OpenXRCompositionLayer] is destroyed.
+ [param layer] is a pointer to an [code]XrCompositionLayerBaseHeader[/code] struct.
+
+
@@ -188,6 +208,17 @@
Adds additional data structures when interogating OpenXR system abilities.
+
+
+
+
+
+
+ Adds additional data structures to composition layers created by [OpenXRCompositionLayer].
+ [param property_values] contains the values of the properties returned by [method _get_viewport_composition_layer_extension_properties].
+ [param layer] is a pointer to an [code]XrCompositionLayerBaseHeader[/code] struct.
+
+
diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
index 7c0c69d747d..cb117d7bb7a 100644
--- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp
+++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
@@ -86,11 +86,11 @@ int OpenXRCompositionLayerExtension::get_composition_layer_order(int p_index) {
return composition_layers[p_index]->get_sort_order();
}
-void OpenXRCompositionLayerExtension::register_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) {
+void OpenXRCompositionLayerExtension::register_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) {
composition_layers.push_back(p_composition_layer);
}
-void OpenXRCompositionLayerExtension::unregister_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) {
+void OpenXRCompositionLayerExtension::unregister_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) {
composition_layers.erase(p_composition_layer);
}
@@ -123,6 +123,10 @@ OpenXRViewportCompositionLayerProvider::OpenXRViewportCompositionLayerProvider(X
}
OpenXRViewportCompositionLayerProvider::~OpenXRViewportCompositionLayerProvider() {
+ for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) {
+ extension->on_viewport_composition_layer_destroyed(composition_layer);
+ }
+
// This will reset the viewport and free the swapchain too.
set_viewport(RID(), Size2i());
}
@@ -159,6 +163,11 @@ void OpenXRViewportCompositionLayerProvider::set_viewport(RID p_viewport, Size2i
}
}
+void OpenXRViewportCompositionLayerProvider::set_extension_property_values(const Dictionary &p_extension_property_values) {
+ extension_property_values = p_extension_property_values;
+ extension_property_values_changed = true;
+}
+
void OpenXRViewportCompositionLayerProvider::on_pre_render() {
RenderingServer *rs = RenderingServer::get_singleton();
ERR_FAIL_NULL(rs);
@@ -233,6 +242,19 @@ XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_compos
} break;
}
+ if (extension_property_values_changed) {
+ extension_property_values_changed = false;
+
+ void *next_pointer = nullptr;
+ for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) {
+ void *np = extension->set_viewport_composition_layer_and_get_next_pointer(composition_layer, extension_property_values, next_pointer);
+ if (np) {
+ next_pointer = np;
+ }
+ }
+ composition_layer->next = next_pointer;
+ }
+
return composition_layer;
}
diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.h b/modules/openxr/extensions/openxr_composition_layer_extension.h
index 7cc35005f84..4fefc416e61 100644
--- a/modules/openxr/extensions/openxr_composition_layer_extension.h
+++ b/modules/openxr/extensions/openxr_composition_layer_extension.h
@@ -57,8 +57,8 @@ public:
virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override;
virtual int get_composition_layer_order(int p_index) override;
- void register_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer);
- void unregister_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer);
+ void register_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer);
+ void unregister_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer);
bool is_available(XrStructureType p_which);
@@ -75,6 +75,8 @@ class OpenXRViewportCompositionLayerProvider {
XrCompositionLayerBaseHeader *composition_layer = nullptr;
int sort_order = 1;
bool alpha_blend = false;
+ Dictionary extension_property_values;
+ bool extension_property_values_changed = true;
RID viewport;
Size2i viewport_size;
@@ -102,6 +104,8 @@ public:
void set_viewport(RID p_viewport, Size2i p_size);
RID get_viewport() const { return viewport; }
+ void set_extension_property_values(const Dictionary &p_property_values);
+
void on_pre_render();
XrCompositionLayerBaseHeader *get_composition_layer();
diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h
index ad326472ab1..ce03df0b30d 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper.h
@@ -96,6 +96,11 @@ public:
virtual void on_state_loss_pending() {} // `on_state_loss_pending` is called when the OpenXR session state is changed to loss pending.
virtual void on_state_exiting() {} // `on_state_exiting` is called when the OpenXR session state is changed to exiting.
+ virtual void *set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, Dictionary p_property_values, void *p_next_pointer) { return p_next_pointer; } // Add additional data structures to composition layers created via OpenXRCompositionLayer.
+ virtual void on_viewport_composition_layer_destroyed(const XrCompositionLayerBaseHeader *p_layer) {} // `on_viewport_composition_layer_destroyed` is called when a composition layer created via OpenXRCompositionLayer is destroyed.
+ virtual void get_viewport_composition_layer_extension_properties(List *p_property_list) {} // Get additional property definitions for OpenXRCompositionLayer.
+ virtual Dictionary get_viewport_composition_layer_extension_property_defaults() { return Dictionary(); } // Get the default values for the additional property definitions for OpenXRCompositionLayer.
+
// `on_event_polled` is called when there is an OpenXR event to process.
// Should return true if the event was handled, false otherwise.
virtual bool on_event_polled(const XrEventDataBuffer &event) {
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
index 60a934e3a81..0cb039bec42 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
@@ -60,6 +60,10 @@ void OpenXRExtensionWrapperExtension::_bind_methods() {
GDVIRTUAL_BIND(_on_state_loss_pending);
GDVIRTUAL_BIND(_on_state_exiting);
GDVIRTUAL_BIND(_on_event_polled, "event");
+ GDVIRTUAL_BIND(_set_viewport_composition_layer_and_get_next_pointer, "layer", "property_values", "next_pointer");
+ GDVIRTUAL_BIND(_get_viewport_composition_layer_extension_properties, "layer");
+ GDVIRTUAL_BIND(_get_viewport_composition_layer_extension_property_defaults, "layer");
+ GDVIRTUAL_BIND(_on_viewport_composition_layer_destroyed, "layer");
ClassDB::bind_method(D_METHOD("get_openxr_api"), &OpenXRExtensionWrapperExtension::get_openxr_api);
ClassDB::bind_method(D_METHOD("register_extension_wrapper"), &OpenXRExtensionWrapperExtension::register_extension_wrapper);
@@ -240,6 +244,36 @@ bool OpenXRExtensionWrapperExtension::on_event_polled(const XrEventDataBuffer &p
return false;
}
+void *OpenXRExtensionWrapperExtension::set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, Dictionary p_property_values, void *p_next_pointer) {
+ uint64_t pointer = 0;
+
+ if (GDVIRTUAL_CALL(_set_viewport_composition_layer_and_get_next_pointer, GDExtensionConstPtr(p_layer), p_property_values, GDExtensionPtr(p_next_pointer), pointer)) {
+ return reinterpret_cast(pointer);
+ }
+
+ return p_next_pointer;
+}
+
+void OpenXRExtensionWrapperExtension::on_viewport_composition_layer_destroyed(const XrCompositionLayerBaseHeader *p_layer) {
+ GDVIRTUAL_CALL(_on_viewport_composition_layer_destroyed, GDExtensionConstPtr(p_layer));
+}
+
+void OpenXRExtensionWrapperExtension::get_viewport_composition_layer_extension_properties(List *p_property_list) {
+ TypedArray properties;
+
+ if (GDVIRTUAL_CALL(_get_viewport_composition_layer_extension_properties, properties)) {
+ for (int i = 0; i < properties.size(); i++) {
+ p_property_list->push_back(PropertyInfo::from_dict(properties[i]));
+ }
+ }
+}
+
+Dictionary OpenXRExtensionWrapperExtension::get_viewport_composition_layer_extension_property_defaults() {
+ Dictionary property_defaults;
+ GDVIRTUAL_CALL(_get_viewport_composition_layer_extension_property_defaults, property_defaults);
+ return property_defaults;
+}
+
Ref OpenXRExtensionWrapperExtension::get_openxr_api() {
return openxr_api;
}
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.h b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
index d3b78bf6170..71d2a57ff89 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
@@ -38,6 +38,7 @@
#include "core/os/os.h"
#include "core/os/thread_safe.h"
#include "core/variant/native_ptr.h"
+#include "core/variant/typed_array.h"
class OpenXRExtensionWrapperExtension : public Object, public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider {
GDCLASS(OpenXRExtensionWrapperExtension, Object);
@@ -59,6 +60,7 @@ public:
virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override;
virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override;
virtual void *set_hand_joint_locations_and_get_next_pointer(int p_hand_index, void *p_next_pointer) override;
+
virtual int get_composition_layer_count() override;
virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override;
virtual int get_composition_layer_order(int p_index) override;
@@ -117,6 +119,16 @@ public:
GDVIRTUAL1R(bool, _on_event_polled, GDExtensionConstPtr);
+ virtual void *set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, Dictionary p_property_values, void *p_next_pointer) override;
+ virtual void on_viewport_composition_layer_destroyed(const XrCompositionLayerBaseHeader *p_layer) override;
+ virtual void get_viewport_composition_layer_extension_properties(List *p_property_list) override;
+ virtual Dictionary get_viewport_composition_layer_extension_property_defaults() override;
+
+ GDVIRTUAL3R(uint64_t, _set_viewport_composition_layer_and_get_next_pointer, GDExtensionConstPtr, Dictionary, GDExtensionPtr);
+ GDVIRTUAL1(_on_viewport_composition_layer_destroyed, GDExtensionConstPtr);
+ GDVIRTUAL0R(TypedArray, _get_viewport_composition_layer_extension_properties);
+ GDVIRTUAL0R(Dictionary, _get_viewport_composition_layer_extension_property_defaults);
+
Ref get_openxr_api();
void register_extension_wrapper();
diff --git a/modules/openxr/scene/openxr_composition_layer.cpp b/modules/openxr/scene/openxr_composition_layer.cpp
index 120914485f2..e0d0ddc77fb 100644
--- a/modules/openxr/scene/openxr_composition_layer.cpp
+++ b/modules/openxr/scene/openxr_composition_layer.cpp
@@ -236,6 +236,14 @@ void OpenXRCompositionLayer::_reset_fallback_material() {
void OpenXRCompositionLayer::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_POSTINITIALIZE: {
+ if (openxr_layer_provider) {
+ for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) {
+ extension_property_values.merge(extension->get_viewport_composition_layer_extension_property_defaults());
+ }
+ openxr_layer_provider->set_extension_property_values(extension_property_values);
+ }
+ } break;
case NOTIFICATION_INTERNAL_PROCESS: {
if (fallback) {
if (should_update_fallback_mesh) {
@@ -260,7 +268,7 @@ void OpenXRCompositionLayer::_notification(int p_what) {
} break;
case NOTIFICATION_ENTER_TREE: {
if (composition_layer_extension) {
- composition_layer_extension->register_composition_layer_provider(openxr_layer_provider);
+ composition_layer_extension->register_viewport_composition_layer_provider(openxr_layer_provider);
}
if (!fallback && layer_viewport && openxr_api && openxr_api->is_running() && is_visible()) {
@@ -269,7 +277,7 @@ void OpenXRCompositionLayer::_notification(int p_what) {
} break;
case NOTIFICATION_EXIT_TREE: {
if (composition_layer_extension) {
- composition_layer_extension->unregister_composition_layer_provider(openxr_layer_provider);
+ composition_layer_extension->unregister_viewport_composition_layer_provider(openxr_layer_provider);
}
// When a node is removed in the editor, we need to clear the layer viewport, because otherwise
@@ -285,6 +293,40 @@ void OpenXRCompositionLayer::_notification(int p_what) {
}
}
+void OpenXRCompositionLayer::_get_property_list(List *p_property_list) const {
+ List extension_properties;
+ for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) {
+ extension->get_viewport_composition_layer_extension_properties(&extension_properties);
+ }
+
+ for (const PropertyInfo &pinfo : extension_properties) {
+ StringName prop_name = pinfo.name;
+ if (!String(prop_name).contains("/")) {
+ WARN_PRINT_ONCE(vformat("Discarding OpenXRCompositionLayer property name '%s' from extension because it doesn't contain a '/'."));
+ continue;
+ }
+ p_property_list->push_back(pinfo);
+ }
+}
+
+bool OpenXRCompositionLayer::_get(const StringName &p_property, Variant &r_value) const {
+ if (extension_property_values.has(p_property)) {
+ r_value = extension_property_values[p_property];
+ }
+
+ return true;
+}
+
+bool OpenXRCompositionLayer::_set(const StringName &p_property, const Variant &p_value) {
+ extension_property_values[p_property] = p_value;
+
+ if (openxr_layer_provider) {
+ openxr_layer_provider->set_extension_property_values(extension_property_values);
+ }
+
+ return true;
+}
+
PackedStringArray OpenXRCompositionLayer::get_configuration_warnings() const {
PackedStringArray warnings = Node3D::get_configuration_warnings();
diff --git a/modules/openxr/scene/openxr_composition_layer.h b/modules/openxr/scene/openxr_composition_layer.h
index f683aea647d..dbc09d19b10 100644
--- a/modules/openxr/scene/openxr_composition_layer.h
+++ b/modules/openxr/scene/openxr_composition_layer.h
@@ -49,6 +49,8 @@ class OpenXRCompositionLayer : public Node3D {
MeshInstance3D *fallback = nullptr;
bool should_update_fallback_mesh = false;
+ Dictionary extension_property_values;
+
void _create_fallback_node();
void _reset_fallback_material();
@@ -60,6 +62,9 @@ protected:
static void _bind_methods();
void _notification(int p_what);
+ void _get_property_list(List *p_property_list) const;
+ bool _get(const StringName &p_property, Variant &r_value) const;
+ bool _set(const StringName &p_property, const Variant &p_value);
virtual void _on_openxr_session_begun();
virtual void _on_openxr_session_stopping();