Browse Source

Allow OpenXR extensions to add properties to the OpenXRCompositionLayer node

David Snopek 1 year ago
parent
commit
fddf6dc651

+ 31 - 0
modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml

@@ -46,6 +46,18 @@
 				Returns a [PackedStringArray] of positional tracker names that are used within the extension wrapper.
 			</description>
 		</method>
+		<method name="_get_viewport_composition_layer_extension_properties" qualifiers="virtual">
+			<return type="Dictionary[]" />
+			<description>
+				Gets an array of [Dictionary]s that represent properties, just like [method Object._get_property_list], that will be added to [OpenXRCompositionLayer] nodes.
+			</description>
+		</method>
+		<method name="_get_viewport_composition_layer_extension_property_defaults" qualifiers="virtual">
+			<return type="Dictionary" />
+			<description>
+				Gets a [Dictionary] containing the default values for the properties returned by [method _get_viewport_composition_layer_extension_properties].
+			</description>
+		</method>
 		<method name="_on_before_instance_created" qualifiers="virtual">
 			<return type="void" />
 			<description>
@@ -152,6 +164,14 @@
 				Called when the OpenXR session state is changed to visible. This means OpenXR is now ready to receive frames.
 			</description>
 		</method>
+		<method name="_on_viewport_composition_layer_destroyed" qualifiers="virtual">
+			<return type="void" />
+			<param index="0" name="layer" type="const void*" />
+			<description>
+				Called when a composition layer created via [OpenXRCompositionLayer] is destroyed.
+				[param layer] is a pointer to an [code]XrCompositionLayerBaseHeader[/code] struct.
+			</description>
+		</method>
 		<method name="_set_hand_joint_locations_and_get_next_pointer" qualifiers="virtual">
 			<return type="int" />
 			<param index="0" name="hand_index" type="int" />
@@ -188,6 +208,17 @@
 				Adds additional data structures when interogating OpenXR system abilities.
 			</description>
 		</method>
+		<method name="_set_viewport_composition_layer_and_get_next_pointer" qualifiers="virtual">
+			<return type="int" />
+			<param index="0" name="layer" type="const void*" />
+			<param index="1" name="property_values" type="Dictionary" />
+			<param index="2" name="next_pointer" type="void*" />
+			<description>
+				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.
+			</description>
+		</method>
 		<method name="get_openxr_api">
 			<return type="OpenXRAPIExtension" />
 			<description>

+ 24 - 2
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;
 }
 

+ 6 - 2
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();
 

+ 5 - 0
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<PropertyInfo> *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) {

+ 34 - 0
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<void>(p_layer), p_property_values, GDExtensionPtr<void>(p_next_pointer), pointer)) {
+		return reinterpret_cast<void *>(pointer);
+	}
+
+	return p_next_pointer;
+}
+
+void OpenXRExtensionWrapperExtension::on_viewport_composition_layer_destroyed(const XrCompositionLayerBaseHeader *p_layer) {
+	GDVIRTUAL_CALL(_on_viewport_composition_layer_destroyed, GDExtensionConstPtr<void>(p_layer));
+}
+
+void OpenXRExtensionWrapperExtension::get_viewport_composition_layer_extension_properties(List<PropertyInfo> *p_property_list) {
+	TypedArray<Dictionary> 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<OpenXRAPIExtension> OpenXRExtensionWrapperExtension::get_openxr_api() {
 	return openxr_api;
 }

+ 12 - 0
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<void>);
 
+	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<PropertyInfo> *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<void>, Dictionary, GDExtensionPtr<void>);
+	GDVIRTUAL1(_on_viewport_composition_layer_destroyed, GDExtensionConstPtr<void>);
+	GDVIRTUAL0R(TypedArray<Dictionary>, _get_viewport_composition_layer_extension_properties);
+	GDVIRTUAL0R(Dictionary, _get_viewport_composition_layer_extension_property_defaults);
+
 	Ref<OpenXRAPIExtension> get_openxr_api();
 
 	void register_extension_wrapper();

+ 44 - 2
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<PropertyInfo> *p_property_list) const {
+	List<PropertyInfo> 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();
 

+ 5 - 0
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<PropertyInfo> *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();