summaryrefslogtreecommitdiffstats
path: root/modules/openxr/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'modules/openxr/extensions')
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_extension.cpp266
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_extension.h63
-rw-r--r--modules/openxr/extensions/openxr_debug_utils_extension.cpp287
-rw-r--r--modules/openxr/extensions/openxr_debug_utils_extension.h76
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper.h6
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.cpp18
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.h6
-rw-r--r--modules/openxr/extensions/openxr_fb_foveation_extension.cpp37
-rw-r--r--modules/openxr/extensions/openxr_fb_foveation_extension.h12
-rw-r--r--modules/openxr/extensions/openxr_hand_tracking_extension.cpp82
-rw-r--r--modules/openxr/extensions/openxr_hand_tracking_extension.h3
-rw-r--r--modules/openxr/extensions/openxr_htc_controller_extension.cpp25
-rw-r--r--modules/openxr/extensions/openxr_htc_controller_extension.h3
-rw-r--r--modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp14
-rw-r--r--modules/openxr/extensions/openxr_mxink_extension.cpp83
-rw-r--r--modules/openxr/extensions/openxr_mxink_extension.h48
-rw-r--r--modules/openxr/extensions/openxr_visibility_mask_extension.cpp279
-rw-r--r--modules/openxr/extensions/openxr_visibility_mask_extension.h95
-rw-r--r--modules/openxr/extensions/openxr_wmr_controller_extension.cpp20
-rw-r--r--modules/openxr/extensions/openxr_wmr_controller_extension.h1
-rw-r--r--modules/openxr/extensions/platform/openxr_opengl_extension.cpp26
-rw-r--r--modules/openxr/extensions/platform/openxr_opengl_extension.h6
22 files changed, 1326 insertions, 130 deletions
diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
index 994b08af53..dc30b95b27 100644
--- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp
+++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
@@ -30,6 +30,12 @@
#include "openxr_composition_layer_extension.h"
+#ifdef ANDROID_ENABLED
+#include <openxr/openxr.h>
+#include <openxr/openxr_platform.h>
+#endif
+
+#include "platform/android/api/java_class_wrapper.h"
#include "servers/rendering/rendering_server_globals.h"
////////////////////////////////////////////////////////////////////////////
@@ -55,18 +61,37 @@ HashMap<String, bool *> OpenXRCompositionLayerExtension::get_requested_extension
request_extensions[XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME] = &cylinder_ext_available;
request_extensions[XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME] = &equirect_ext_available;
+#ifdef ANDROID_ENABLED
+ request_extensions[XR_KHR_ANDROID_SURFACE_SWAPCHAIN_EXTENSION_NAME] = &android_surface_ext_available;
+#endif
+
return request_extensions;
}
-void OpenXRCompositionLayerExtension::on_session_created(const XrSession p_instance) {
+void OpenXRCompositionLayerExtension::on_instance_created(const XrInstance p_instance) {
+#ifdef ANDROID_ENABLED
+ EXT_INIT_XR_FUNC(xrDestroySwapchain);
+ EXT_INIT_XR_FUNC(xrCreateSwapchainAndroidSurfaceKHR);
+#endif
+}
+
+void OpenXRCompositionLayerExtension::on_session_created(const XrSession p_session) {
OpenXRAPI::get_singleton()->register_composition_layer_provider(this);
}
void OpenXRCompositionLayerExtension::on_session_destroyed() {
OpenXRAPI::get_singleton()->unregister_composition_layer_provider(this);
+
+#ifdef ANDROID_ENABLED
+ free_queued_android_surface_swapchains();
+#endif
}
void OpenXRCompositionLayerExtension::on_pre_render() {
+#ifdef ANDROID_ENABLED
+ free_queued_android_surface_swapchains();
+#endif
+
for (OpenXRViewportCompositionLayerProvider *composition_layer : composition_layers) {
composition_layer->on_pre_render();
}
@@ -113,6 +138,36 @@ bool OpenXRCompositionLayerExtension::is_available(XrStructureType p_which) {
}
}
+#ifdef ANDROID_ENABLED
+bool OpenXRCompositionLayerExtension::create_android_surface_swapchain(XrSwapchainCreateInfo *p_info, XrSwapchain *r_swapchain, jobject *r_surface) {
+ if (android_surface_ext_available) {
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
+ ERR_FAIL_NULL_V(openxr_api, false);
+
+ XrResult result = xrCreateSwapchainAndroidSurfaceKHR(openxr_api->get_session(), p_info, r_swapchain, r_surface);
+ if (XR_FAILED(result)) {
+ print_line("OpenXR: Failed to create Android surface swapchain [", openxr_api->get_error_string(result), "]");
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void OpenXRCompositionLayerExtension::free_android_surface_swapchain(XrSwapchain p_swapchain) {
+ android_surface_swapchain_free_queue.push_back(p_swapchain);
+}
+
+void OpenXRCompositionLayerExtension::free_queued_android_surface_swapchains() {
+ for (XrSwapchain swapchain : android_surface_swapchain_free_queue) {
+ xrDestroySwapchain(swapchain);
+ }
+ android_surface_swapchain_free_queue.clear();
+}
+#endif
+
////////////////////////////////////////////////////////////////////////////
// OpenXRViewportCompositionLayerProvider
@@ -127,8 +182,12 @@ OpenXRViewportCompositionLayerProvider::~OpenXRViewportCompositionLayerProvider(
extension->on_viewport_composition_layer_destroyed(composition_layer);
}
- // This will reset the viewport and free the swapchain too.
- set_viewport(RID(), Size2i());
+ if (use_android_surface) {
+ free_swapchain();
+ } else {
+ // This will reset the viewport and free the swapchain too.
+ set_viewport(RID(), Size2i());
+ }
}
void OpenXRViewportCompositionLayerProvider::set_alpha_blend(bool p_alpha_blend) {
@@ -143,25 +202,101 @@ void OpenXRViewportCompositionLayerProvider::set_alpha_blend(bool p_alpha_blend)
}
void OpenXRViewportCompositionLayerProvider::set_viewport(RID p_viewport, Size2i p_size) {
+ ERR_FAIL_COND(use_android_surface);
+
RenderingServer *rs = RenderingServer::get_singleton();
ERR_FAIL_NULL(rs);
- if (viewport != p_viewport) {
- if (viewport.is_valid()) {
- RID rt = rs->viewport_get_render_target(viewport);
+ if (subviewport.viewport != p_viewport) {
+ if (subviewport.viewport.is_valid()) {
+ RID rt = rs->viewport_get_render_target(subviewport.viewport);
RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID());
}
- viewport = p_viewport;
+ subviewport.viewport = p_viewport;
- if (viewport.is_valid()) {
- viewport_size = p_size;
+ if (subviewport.viewport.is_valid()) {
+ subviewport.viewport_size = p_size;
} else {
free_swapchain();
- viewport_size = Size2i();
+ subviewport.viewport_size = Size2i();
+ }
+ }
+}
+
+void OpenXRViewportCompositionLayerProvider::set_use_android_surface(bool p_use_android_surface, Size2i p_size) {
+#ifdef ANDROID_ENABLED
+ if (p_use_android_surface == use_android_surface) {
+ return;
+ }
+
+ use_android_surface = p_use_android_surface;
+
+ if (use_android_surface) {
+ if (!composition_layer_extension->is_android_surface_swapchain_available()) {
+ ERR_PRINT_ONCE("OpenXR: Cannot use Android surface for composition layer because the extension isn't available");
+ }
+
+ if (subviewport.viewport.is_valid()) {
+ set_viewport(RID(), Size2i());
}
+
+ swapchain_size = p_size;
+ } else {
+ free_swapchain();
+ }
+#endif
+}
+
+#ifdef ANDROID_ENABLED
+void OpenXRViewportCompositionLayerProvider::create_android_surface() {
+ ERR_FAIL_COND(android_surface.swapchain != XR_NULL_HANDLE || android_surface.surface.is_valid());
+ ERR_FAIL_COND(!openxr_api || !openxr_api->is_running());
+
+ void *next_pointer = nullptr;
+ for (OpenXRExtensionWrapper *wrapper : openxr_api->get_registered_extension_wrappers()) {
+ void *np = wrapper->set_android_surface_swapchain_create_info_and_get_next_pointer(extension_property_values, next_pointer);
+ if (np != nullptr) {
+ next_pointer = np;
+ }
+ }
+
+ // The XR_FB_android_surface_swapchain_create extension mandates that format, sampleCount,
+ // faceCount, arraySize, and mipCount must be zero.
+ XrSwapchainCreateInfo info = {
+ XR_TYPE_SWAPCHAIN_CREATE_INFO, // type
+ next_pointer, // next
+ 0, // createFlags
+ XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, // usageFlags
+ 0, // format
+ 0, // sampleCount
+ (uint32_t)swapchain_size.x, // width
+ (uint32_t)swapchain_size.y, // height
+ 0, // faceCount
+ 0, // arraySize
+ 0, // mipCount
+ };
+
+ jobject surface;
+ composition_layer_extension->create_android_surface_swapchain(&info, &android_surface.swapchain, &surface);
+
+ if (surface) {
+ android_surface.surface = Ref<JavaObject>(memnew(JavaObject(JavaClassWrapper::get_singleton()->wrap("android.view.Surface"), surface)));
}
}
+#endif
+
+Ref<JavaObject> OpenXRViewportCompositionLayerProvider::get_android_surface() {
+#ifdef ANDROID_ENABLED
+ if (use_android_surface) {
+ if (android_surface.surface.is_null()) {
+ create_android_surface();
+ }
+ return android_surface.surface;
+ }
+#endif
+ return Ref<JavaObject>();
+}
void OpenXRViewportCompositionLayerProvider::set_extension_property_values(const Dictionary &p_extension_property_values) {
extension_property_values = p_extension_property_values;
@@ -169,16 +304,25 @@ void OpenXRViewportCompositionLayerProvider::set_extension_property_values(const
}
void OpenXRViewportCompositionLayerProvider::on_pre_render() {
+#ifdef ANDROID_ENABLED
+ if (use_android_surface) {
+ if (android_surface.surface.is_null()) {
+ create_android_surface();
+ }
+ return;
+ }
+#endif
+
RenderingServer *rs = RenderingServer::get_singleton();
ERR_FAIL_NULL(rs);
- if (viewport.is_valid() && openxr_api && openxr_api->is_running()) {
- RS::ViewportUpdateMode update_mode = rs->viewport_get_update_mode(viewport);
+ if (subviewport.viewport.is_valid() && openxr_api && openxr_api->is_running()) {
+ RS::ViewportUpdateMode update_mode = rs->viewport_get_update_mode(subviewport.viewport);
if (update_mode == RS::VIEWPORT_UPDATE_ONCE || update_mode == RS::VIEWPORT_UPDATE_ALWAYS) {
// Update our XR swapchain
if (update_and_acquire_swapchain(update_mode == RS::VIEWPORT_UPDATE_ONCE)) {
// Render to our XR swapchain image.
- RID rt = rs->viewport_get_render_target(viewport);
+ RID rt = rs->viewport_get_render_target(subviewport.viewport);
RSG::texture_storage->render_target_set_override(rt, get_current_swapchain_texture(), RID(), RID());
}
}
@@ -196,48 +340,36 @@ XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_compos
return nullptr;
}
- if (swapchain_info.get_swapchain() == XR_NULL_HANDLE) {
+ XrSwapchainSubImage subimage = {
+ 0, // swapchain
+ { { 0, 0 }, { 0, 0 } }, // imageRect
+ 0, // imageArrayIndex
+ };
+ update_swapchain_sub_image(subimage);
+
+ if (subimage.swapchain == XR_NULL_HANDLE) {
// Don't have a swapchain to display? Ignore our layer.
return nullptr;
}
- if (swapchain_info.is_image_acquired()) {
- swapchain_info.release();
- }
-
// Update the layer struct for the swapchain.
switch (composition_layer->type) {
case XR_TYPE_COMPOSITION_LAYER_QUAD: {
XrCompositionLayerQuad *quad_layer = (XrCompositionLayerQuad *)composition_layer;
quad_layer->space = openxr_api->get_play_space();
- quad_layer->subImage.swapchain = swapchain_info.get_swapchain();
- quad_layer->subImage.imageArrayIndex = 0;
- quad_layer->subImage.imageRect.offset.x = 0;
- quad_layer->subImage.imageRect.offset.y = 0;
- quad_layer->subImage.imageRect.extent.width = swapchain_size.width;
- quad_layer->subImage.imageRect.extent.height = swapchain_size.height;
+ quad_layer->subImage = subimage;
} break;
case XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR: {
XrCompositionLayerCylinderKHR *cylinder_layer = (XrCompositionLayerCylinderKHR *)composition_layer;
cylinder_layer->space = openxr_api->get_play_space();
- cylinder_layer->subImage.swapchain = swapchain_info.get_swapchain();
- cylinder_layer->subImage.imageArrayIndex = 0;
- cylinder_layer->subImage.imageRect.offset.x = 0;
- cylinder_layer->subImage.imageRect.offset.y = 0;
- cylinder_layer->subImage.imageRect.extent.width = swapchain_size.width;
- cylinder_layer->subImage.imageRect.extent.height = swapchain_size.height;
+ cylinder_layer->subImage = subimage;
} break;
case XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR: {
XrCompositionLayerEquirect2KHR *equirect_layer = (XrCompositionLayerEquirect2KHR *)composition_layer;
equirect_layer->space = openxr_api->get_play_space();
- equirect_layer->subImage.swapchain = swapchain_info.get_swapchain();
- equirect_layer->subImage.imageArrayIndex = 0;
- equirect_layer->subImage.imageRect.offset.x = 0;
- equirect_layer->subImage.imageRect.offset.y = 0;
- equirect_layer->subImage.imageRect.extent.width = swapchain_size.width;
- equirect_layer->subImage.imageRect.extent.height = swapchain_size.height;
+ equirect_layer->subImage = subimage;
} break;
default: {
@@ -261,27 +393,49 @@ XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_compos
return composition_layer;
}
+void OpenXRViewportCompositionLayerProvider::update_swapchain_sub_image(XrSwapchainSubImage &r_subimage) {
+#ifdef ANDROID_ENABLED
+ if (use_android_surface) {
+ r_subimage.swapchain = android_surface.swapchain;
+ } else
+#endif
+ {
+ XrSwapchain swapchain = subviewport.swapchain_info.get_swapchain();
+
+ if (swapchain && subviewport.swapchain_info.is_image_acquired()) {
+ subviewport.swapchain_info.release();
+ }
+
+ r_subimage.swapchain = swapchain;
+ }
+
+ r_subimage.imageRect.extent.width = swapchain_size.width;
+ r_subimage.imageRect.extent.height = swapchain_size.height;
+}
+
bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p_static_image) {
+ ERR_FAIL_COND_V(use_android_surface, false);
+
if (openxr_api == nullptr || composition_layer_extension == nullptr) {
// OpenXR not initialized or we're in the editor?
return false;
}
- if (!composition_layer_extension->is_available(composition_layer->type)) {
+ if (!composition_layer_extension->is_available(get_openxr_type())) {
// Selected type is not supported?
return false;
}
// See if our current swapchain is outdated.
- if (swapchain_info.get_swapchain() != XR_NULL_HANDLE) {
+ if (subviewport.swapchain_info.get_swapchain() != XR_NULL_HANDLE) {
// If this swap chain, or the previous one, were static, then we can't reuse it.
- if (swapchain_size == viewport_size && !p_static_image && !static_image) {
+ if (swapchain_size == subviewport.viewport_size && !p_static_image && !subviewport.static_image) {
// We're all good! Just acquire it.
// We can ignore should_render here, return will be false.
bool should_render = true;
- return swapchain_info.acquire(should_render);
+ return subviewport.swapchain_info.acquire(should_render);
}
- swapchain_info.queue_free();
+ subviewport.swapchain_info.queue_free();
}
// Create our new swap chain
@@ -292,7 +446,7 @@ bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p
if (p_static_image) {
create_flags |= XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT;
}
- if (!swapchain_info.create(create_flags, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, swapchain_format, viewport_size.width, viewport_size.height, sample_count, array_size)) {
+ if (!subviewport.swapchain_info.create(create_flags, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, swapchain_format, subviewport.viewport_size.width, subviewport.viewport_size.height, sample_count, array_size)) {
swapchain_size = Size2i();
return false;
}
@@ -300,26 +454,40 @@ bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p
// Acquire our image so we can start rendering into it,
// we can ignore should_render here, ret will be false.
bool should_render = true;
- bool ret = swapchain_info.acquire(should_render);
+ bool ret = subviewport.swapchain_info.acquire(should_render);
- swapchain_size = viewport_size;
- static_image = p_static_image;
+ swapchain_size = subviewport.viewport_size;
+ subviewport.static_image = p_static_image;
return ret;
}
void OpenXRViewportCompositionLayerProvider::free_swapchain() {
- if (swapchain_info.get_swapchain() != XR_NULL_HANDLE) {
- swapchain_info.queue_free();
+#ifdef ANDROID_ENABLED
+ if (use_android_surface) {
+ if (android_surface.swapchain != XR_NULL_HANDLE) {
+ composition_layer_extension->free_android_surface_swapchain(android_surface.swapchain);
+
+ android_surface.swapchain = XR_NULL_HANDLE;
+ android_surface.surface.unref();
+ }
+ } else
+#endif
+ {
+ if (subviewport.swapchain_info.get_swapchain() != XR_NULL_HANDLE) {
+ subviewport.swapchain_info.queue_free();
+ }
+ subviewport.static_image = false;
}
swapchain_size = Size2i();
- static_image = false;
}
RID OpenXRViewportCompositionLayerProvider::get_current_swapchain_texture() {
+ ERR_FAIL_COND_V(use_android_surface, RID());
+
if (openxr_api == nullptr) {
return RID();
}
- return swapchain_info.get_image();
+ return subviewport.swapchain_info.get_image();
}
diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.h b/modules/openxr/extensions/openxr_composition_layer_extension.h
index 4fefc416e6..bce34f098c 100644
--- a/modules/openxr/extensions/openxr_composition_layer_extension.h
+++ b/modules/openxr/extensions/openxr_composition_layer_extension.h
@@ -36,6 +36,15 @@
#include "../openxr_api.h"
+#ifdef ANDROID_ENABLED
+#include <jni.h>
+
+// Copied here from openxr_platform.h, in order to avoid including that whole header,
+// which can cause compilation issues on some platforms.
+typedef XrResult(XRAPI_PTR *PFN_xrCreateSwapchainAndroidSurfaceKHR)(XrSession session, const XrSwapchainCreateInfo *info, XrSwapchain *swapchain, jobject *surface);
+#endif
+
+class JavaObject;
class OpenXRViewportCompositionLayerProvider;
// This extension provides access to composition layers for displaying 2D content through the XR compositor.
@@ -49,7 +58,8 @@ public:
virtual ~OpenXRCompositionLayerExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
- virtual void on_session_created(const XrSession p_instance) override;
+ virtual void on_instance_created(const XrInstance p_instance) override;
+ virtual void on_session_created(const XrSession p_session) override;
virtual void on_session_destroyed() override;
virtual void on_pre_render() override;
@@ -61,6 +71,12 @@ public:
void unregister_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer);
bool is_available(XrStructureType p_which);
+ bool is_android_surface_swapchain_available() { return android_surface_ext_available; }
+
+#ifdef ANDROID_ENABLED
+ bool create_android_surface_swapchain(XrSwapchainCreateInfo *p_info, XrSwapchain *r_swapchain, jobject *r_surface);
+ void free_android_surface_swapchain(XrSwapchain p_swapchain);
+#endif
private:
static OpenXRCompositionLayerExtension *singleton;
@@ -69,6 +85,15 @@ private:
bool cylinder_ext_available = false;
bool equirect_ext_available = false;
+ bool android_surface_ext_available = false;
+
+#ifdef ANDROID_ENABLED
+ Vector<XrSwapchain> android_surface_swapchain_free_queue;
+ void free_queued_android_surface_swapchains();
+
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroySwapchain, (XrSwapchain), swapchain)
+ EXT_PROTO_XRRESULT_FUNC4(xrCreateSwapchainAndroidSurfaceKHR, (XrSession), session, (const XrSwapchainCreateInfo *), info, (XrSwapchain *), swapchain, (jobject *), surface)
+#endif
};
class OpenXRViewportCompositionLayerProvider {
@@ -78,20 +103,37 @@ class OpenXRViewportCompositionLayerProvider {
Dictionary extension_property_values;
bool extension_property_values_changed = true;
- RID viewport;
- Size2i viewport_size;
-
- OpenXRAPI::OpenXRSwapChainInfo swapchain_info;
+ struct {
+ RID viewport;
+ Size2i viewport_size;
+ OpenXRAPI::OpenXRSwapChainInfo swapchain_info;
+ bool static_image = false;
+ } subviewport;
+
+#ifdef ANDROID_ENABLED
+ struct {
+ XrSwapchain swapchain = XR_NULL_HANDLE;
+ Ref<JavaObject> surface;
+ } android_surface;
+#endif
+
+ bool use_android_surface = false;
Size2i swapchain_size;
- bool static_image = false;
OpenXRAPI *openxr_api = nullptr;
OpenXRCompositionLayerExtension *composition_layer_extension = nullptr;
+ // Only for SubViewports.
bool update_and_acquire_swapchain(bool p_static_image);
- void free_swapchain();
RID get_current_swapchain_texture();
+ void update_swapchain_sub_image(XrSwapchainSubImage &r_swapchain_sub_image);
+ void free_swapchain();
+
+#ifdef ANDROID_ENABLED
+ void create_android_surface();
+#endif
+
public:
XrStructureType get_openxr_type() { return composition_layer->type; }
@@ -102,7 +144,12 @@ public:
bool get_alpha_blend() const { return alpha_blend; }
void set_viewport(RID p_viewport, Size2i p_size);
- RID get_viewport() const { return viewport; }
+ RID get_viewport() const { return subviewport.viewport; }
+
+ void set_use_android_surface(bool p_enable, Size2i p_size);
+ bool get_use_android_surface() const { return use_android_surface; }
+
+ Ref<JavaObject> get_android_surface();
void set_extension_property_values(const Dictionary &p_property_values);
diff --git a/modules/openxr/extensions/openxr_debug_utils_extension.cpp b/modules/openxr/extensions/openxr_debug_utils_extension.cpp
new file mode 100644
index 0000000000..10dbe629f7
--- /dev/null
+++ b/modules/openxr/extensions/openxr_debug_utils_extension.cpp
@@ -0,0 +1,287 @@
+/**************************************************************************/
+/* openxr_debug_utils_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "openxr_debug_utils_extension.h"
+
+#include "../openxr_api.h"
+#include "core/config/project_settings.h"
+#include "core/string/print_string.h"
+
+#include <openxr/openxr.h>
+
+OpenXRDebugUtilsExtension *OpenXRDebugUtilsExtension::singleton = nullptr;
+
+OpenXRDebugUtilsExtension *OpenXRDebugUtilsExtension::get_singleton() {
+ return singleton;
+}
+
+OpenXRDebugUtilsExtension::OpenXRDebugUtilsExtension() {
+ singleton = this;
+}
+
+OpenXRDebugUtilsExtension::~OpenXRDebugUtilsExtension() {
+ singleton = nullptr;
+}
+
+HashMap<String, bool *> OpenXRDebugUtilsExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_EXT_DEBUG_UTILS_EXTENSION_NAME] = &debug_utils_ext;
+
+ return request_extensions;
+}
+
+void OpenXRDebugUtilsExtension::on_instance_created(const XrInstance p_instance) {
+ if (debug_utils_ext) {
+ EXT_INIT_XR_FUNC(xrCreateDebugUtilsMessengerEXT);
+ EXT_INIT_XR_FUNC(xrDestroyDebugUtilsMessengerEXT);
+ EXT_INIT_XR_FUNC(xrSetDebugUtilsObjectNameEXT);
+ EXT_INIT_XR_FUNC(xrSessionBeginDebugUtilsLabelRegionEXT);
+ EXT_INIT_XR_FUNC(xrSessionEndDebugUtilsLabelRegionEXT);
+ EXT_INIT_XR_FUNC(xrSessionInsertDebugUtilsLabelEXT);
+
+ debug_utils_ext = xrCreateDebugUtilsMessengerEXT_ptr && xrDestroyDebugUtilsMessengerEXT_ptr && xrSetDebugUtilsObjectNameEXT_ptr && xrSessionBeginDebugUtilsLabelRegionEXT_ptr && xrSessionEndDebugUtilsLabelRegionEXT_ptr && xrSessionInsertDebugUtilsLabelEXT_ptr;
+ } else {
+ WARN_PRINT("OpenXR: The debug utils extension is not available on this runtime. Debug logging is not enabled!");
+ }
+
+ // On successful init, setup our default messenger.
+ if (debug_utils_ext) {
+ int max_severity = GLOBAL_GET("xr/openxr/extensions/debug_utils");
+ int types = GLOBAL_GET("xr/openxr/extensions/debug_message_types");
+
+ XrDebugUtilsMessageSeverityFlagsEXT message_severities = 0;
+
+ if (max_severity >= 1) {
+ message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
+ }
+ if (max_severity >= 2) {
+ message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
+ }
+ if (max_severity >= 3) {
+ message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
+ }
+ if (max_severity >= 4) {
+ message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
+ }
+
+ XrDebugUtilsMessageTypeFlagsEXT message_types = 0;
+
+ // These should match up but just to be safe and future proof...
+ if (types & 1) {
+ message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT;
+ }
+ if (types & 2) {
+ message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
+ }
+ if (types & 4) {
+ message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
+ }
+ if (types & 8) {
+ message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT;
+ }
+
+ XrDebugUtilsMessengerCreateInfoEXT callback_info = {
+ XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, // type
+ nullptr, // next
+ message_severities, // messageSeverities
+ message_types, // messageTypes
+ &OpenXRDebugUtilsExtension::_debug_callback, // userCallback
+ nullptr, // userData
+ };
+
+ XrResult result = xrCreateDebugUtilsMessengerEXT(p_instance, &callback_info, &default_messenger);
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to create debug callback [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+
+ set_object_name(XR_OBJECT_TYPE_INSTANCE, uint64_t(p_instance), "Main Godot OpenXR Instance");
+ }
+}
+
+void OpenXRDebugUtilsExtension::on_instance_destroyed() {
+ if (default_messenger != XR_NULL_HANDLE) {
+ XrResult result = xrDestroyDebugUtilsMessengerEXT(default_messenger);
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to destroy debug callback [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+
+ default_messenger = XR_NULL_HANDLE;
+ }
+
+ xrCreateDebugUtilsMessengerEXT_ptr = nullptr;
+ xrDestroyDebugUtilsMessengerEXT_ptr = nullptr;
+ xrSetDebugUtilsObjectNameEXT_ptr = nullptr;
+ xrSessionBeginDebugUtilsLabelRegionEXT_ptr = nullptr;
+ xrSessionEndDebugUtilsLabelRegionEXT_ptr = nullptr;
+ xrSessionInsertDebugUtilsLabelEXT_ptr = nullptr;
+ debug_utils_ext = false;
+}
+
+bool OpenXRDebugUtilsExtension::get_active() {
+ return debug_utils_ext;
+}
+
+void OpenXRDebugUtilsExtension::set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const char *p_object_name) {
+ ERR_FAIL_COND(!debug_utils_ext);
+ ERR_FAIL_NULL(xrSetDebugUtilsObjectNameEXT_ptr);
+
+ const XrDebugUtilsObjectNameInfoEXT space_name_info = {
+ XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, // type
+ nullptr, // next
+ p_object_type, // objectType
+ p_object_handle, // objectHandle
+ p_object_name, // objectName
+ };
+
+ XrResult result = xrSetDebugUtilsObjectNameEXT_ptr(OpenXRAPI::get_singleton()->get_instance(), &space_name_info);
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to set object name [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+}
+
+void OpenXRDebugUtilsExtension::begin_debug_label_region(const char *p_label_name) {
+ ERR_FAIL_COND(!debug_utils_ext);
+ ERR_FAIL_NULL(xrSessionBeginDebugUtilsLabelRegionEXT_ptr);
+
+ const XrDebugUtilsLabelEXT session_active_region_label = {
+ XR_TYPE_DEBUG_UTILS_LABEL_EXT, // type
+ NULL, // next
+ p_label_name, // labelName
+ };
+
+ XrResult result = xrSessionBeginDebugUtilsLabelRegionEXT_ptr(OpenXRAPI::get_singleton()->get_session(), &session_active_region_label);
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to begin label region [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+}
+
+void OpenXRDebugUtilsExtension::end_debug_label_region() {
+ ERR_FAIL_COND(!debug_utils_ext);
+ ERR_FAIL_NULL(xrSessionEndDebugUtilsLabelRegionEXT_ptr);
+
+ XrResult result = xrSessionEndDebugUtilsLabelRegionEXT_ptr(OpenXRAPI::get_singleton()->get_session());
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to end label region [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+}
+
+void OpenXRDebugUtilsExtension::insert_debug_label(const char *p_label_name) {
+ ERR_FAIL_COND(!debug_utils_ext);
+ ERR_FAIL_NULL(xrSessionInsertDebugUtilsLabelEXT_ptr);
+
+ const XrDebugUtilsLabelEXT session_active_region_label = {
+ XR_TYPE_DEBUG_UTILS_LABEL_EXT, // type
+ NULL, // next
+ p_label_name, // labelName
+ };
+
+ XrResult result = xrSessionInsertDebugUtilsLabelEXT_ptr(OpenXRAPI::get_singleton()->get_session(), &session_active_region_label);
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to insert label [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+}
+
+XrBool32 XRAPI_PTR OpenXRDebugUtilsExtension::_debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data) {
+ OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
+
+ if (debug_utils) {
+ return debug_utils->debug_callback(p_message_severity, p_message_types, p_callback_data, p_user_data);
+ }
+
+ return XR_FALSE;
+}
+
+XrBool32 OpenXRDebugUtilsExtension::debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data) {
+ String msg;
+
+ ERR_FAIL_NULL_V(p_callback_data, XR_FALSE);
+
+ if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) {
+ msg = ", type: General";
+ } else if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) {
+ msg = ", type: Validation";
+ } else if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) {
+ msg = ", type: Performance";
+ } else if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT) {
+ msg = ", type: Conformance";
+ } else {
+ msg = ", type: Unknown (" + String::num_uint64(p_message_types) + ")";
+ }
+
+ if (p_callback_data->functionName) {
+ msg += ", function Name: " + String(p_callback_data->functionName);
+ }
+ if (p_callback_data->messageId) {
+ msg += "\nMessage ID: " + String(p_callback_data->messageId);
+ }
+ if (p_callback_data->message) {
+ msg += "\nMessage: " + String(p_callback_data->message);
+ }
+
+ if (p_callback_data->objectCount > 0) {
+ String objects;
+
+ for (uint32_t i = 0; i < p_callback_data->objectCount; i++) {
+ if (!objects.is_empty()) {
+ objects += ", ";
+ }
+ objects += p_callback_data->objects[i].objectName;
+ }
+
+ msg += "\nObjects: " + objects;
+ }
+
+ if (p_callback_data->sessionLabelCount > 0) {
+ String labels;
+
+ for (uint32_t i = 0; i < p_callback_data->sessionLabelCount; i++) {
+ if (!labels.is_empty()) {
+ labels += ", ";
+ }
+ labels += p_callback_data->sessionLabels[i].labelName;
+ }
+
+ msg += "\nLabels: " + labels;
+ }
+
+ if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
+ ERR_PRINT("OpenXR: Severity: Error" + msg);
+ } else if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
+ WARN_PRINT("OpenXR: Severity: Warning" + msg);
+ } else if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
+ print_line("OpenXR: Severity: Info" + msg);
+ } else if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
+ // This is a bit double because we won't output this unless verbose messaging in Godot is on.
+ print_verbose("OpenXR: Severity: Verbose" + msg);
+ }
+
+ return XR_FALSE;
+}
diff --git a/modules/openxr/extensions/openxr_debug_utils_extension.h b/modules/openxr/extensions/openxr_debug_utils_extension.h
new file mode 100644
index 0000000000..1ee4c2a101
--- /dev/null
+++ b/modules/openxr/extensions/openxr_debug_utils_extension.h
@@ -0,0 +1,76 @@
+/**************************************************************************/
+/* openxr_debug_utils_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef OPENXR_DEBUG_UTILS_EXTENSION_H
+#define OPENXR_DEBUG_UTILS_EXTENSION_H
+
+#include "../util.h"
+#include "openxr_extension_wrapper.h"
+
+class OpenXRDebugUtilsExtension : public OpenXRExtensionWrapper {
+public:
+ static OpenXRDebugUtilsExtension *get_singleton();
+
+ OpenXRDebugUtilsExtension();
+ virtual ~OpenXRDebugUtilsExtension() override;
+
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+ virtual void on_instance_created(const XrInstance p_instance) override;
+ virtual void on_instance_destroyed() override;
+
+ bool get_active();
+
+ void set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const char *p_object_name);
+ void begin_debug_label_region(const char *p_label_name);
+ void end_debug_label_region();
+ void insert_debug_label(const char *p_label_name);
+
+private:
+ static OpenXRDebugUtilsExtension *singleton;
+
+ // related extensions
+ bool debug_utils_ext = false;
+
+ // debug handlers
+ XrDebugUtilsMessengerEXT default_messenger = XR_NULL_HANDLE;
+
+ static XrBool32 XRAPI_PTR _debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data);
+ XrBool32 debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data);
+
+ // OpenXR API call wrappers
+ EXT_PROTO_XRRESULT_FUNC3(xrCreateDebugUtilsMessengerEXT, (XrInstance), p_instance, (const XrDebugUtilsMessengerCreateInfoEXT *), p_create_info, (XrDebugUtilsMessengerEXT *), p_messenger)
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroyDebugUtilsMessengerEXT, (XrDebugUtilsMessengerEXT), p_messenger)
+ EXT_PROTO_XRRESULT_FUNC2(xrSetDebugUtilsObjectNameEXT, (XrInstance), p_instance, (const XrDebugUtilsObjectNameInfoEXT *), p_name_info)
+ EXT_PROTO_XRRESULT_FUNC2(xrSessionBeginDebugUtilsLabelRegionEXT, (XrSession), p_session, (const XrDebugUtilsLabelEXT *), p_label_info)
+ EXT_PROTO_XRRESULT_FUNC1(xrSessionEndDebugUtilsLabelRegionEXT, (XrSession), p_session)
+ EXT_PROTO_XRRESULT_FUNC2(xrSessionInsertDebugUtilsLabelEXT, (XrSession), p_session, (const XrDebugUtilsLabelEXT *), p_label_info)
+};
+
+#endif // OPENXR_DEBUG_UTILS_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h
index ce03df0b30..95b537d1b4 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper.h
@@ -76,7 +76,7 @@ public:
virtual void on_before_instance_created() {} // `on_before_instance_created` is called before we create our OpenXR instance.
virtual void on_instance_created(const XrInstance p_instance) {} // `on_instance_created` is called right after we've successfully created our OpenXR instance.
virtual void on_instance_destroyed() {} // `on_instance_destroyed` is called right before we destroy our OpenXR instance.
- virtual void on_session_created(const XrSession p_instance) {} // `on_session_created` is called right after we've successfully created our OpenXR session.
+ virtual void on_session_created(const XrSession p_session) {} // `on_session_created` is called right after we've successfully created our OpenXR session.
virtual void on_session_destroyed() {} // `on_session_destroyed` is called right before we destroy our OpenXR session.
// `on_process` is called as part of our OpenXR process handling,
@@ -84,6 +84,7 @@ public:
// This is when controller data is queried and made available to game logic.
virtual void on_process() {}
virtual void on_pre_render() {} // `on_pre_render` is called right before we start rendering our XR viewports.
+ virtual void on_main_swapchains_created() {} // `on_main_swapchains_created` is called right after our main swapchains are (re)created.
virtual void on_pre_draw_viewport(RID p_render_target) {} // `on_pre_draw_viewport` is called right before we start rendering this viewport
virtual void on_post_draw_viewport(RID p_render_target) {} // `on_port_draw_viewport` is called right after we start rendering this viewport (note that on Vulkan draw commands may only be queued)
@@ -96,10 +97,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 *set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, const 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.
+ virtual void *set_android_surface_swapchain_create_info_and_get_next_pointer(const Dictionary &p_property_values, void *p_next_pointer) { return p_next_pointer; }
// `on_event_polled` is called when there is an OpenXR event to process.
// Should return true if the event was handled, false otherwise.
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
index 3b31e1b1f6..07ca476421 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
@@ -50,6 +50,7 @@ void OpenXRExtensionWrapperExtension::_bind_methods() {
GDVIRTUAL_BIND(_on_session_created, "session");
GDVIRTUAL_BIND(_on_process);
GDVIRTUAL_BIND(_on_pre_render);
+ GDVIRTUAL_BIND(_on_main_swapchains_created);
GDVIRTUAL_BIND(_on_session_destroyed);
GDVIRTUAL_BIND(_on_state_idle);
GDVIRTUAL_BIND(_on_state_ready);
@@ -64,6 +65,7 @@ void OpenXRExtensionWrapperExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_viewport_composition_layer_extension_properties);
GDVIRTUAL_BIND(_get_viewport_composition_layer_extension_property_defaults);
GDVIRTUAL_BIND(_on_viewport_composition_layer_destroyed, "layer");
+ GDVIRTUAL_BIND(_set_android_surface_swapchain_create_info_and_get_next_pointer, "property_values", "next_pointer");
ClassDB::bind_method(D_METHOD("get_openxr_api"), &OpenXRExtensionWrapperExtension::get_openxr_api);
ClassDB::bind_method(D_METHOD("register_extension_wrapper"), &OpenXRExtensionWrapperExtension::register_extension_wrapper);
@@ -198,6 +200,10 @@ void OpenXRExtensionWrapperExtension::on_pre_render() {
GDVIRTUAL_CALL(_on_pre_render);
}
+void OpenXRExtensionWrapperExtension::on_main_swapchains_created() {
+ GDVIRTUAL_CALL(_on_main_swapchains_created);
+}
+
void OpenXRExtensionWrapperExtension::on_session_destroyed() {
GDVIRTUAL_CALL(_on_session_destroyed);
}
@@ -244,7 +250,7 @@ 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) {
+void *OpenXRExtensionWrapperExtension::set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, const 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)) {
@@ -274,6 +280,16 @@ Dictionary OpenXRExtensionWrapperExtension::get_viewport_composition_layer_exten
return property_defaults;
}
+void *OpenXRExtensionWrapperExtension::set_android_surface_swapchain_create_info_and_get_next_pointer(const Dictionary &p_property_values, void *p_next_pointer) {
+ uint64_t pointer = 0;
+
+ if (GDVIRTUAL_CALL(_set_android_surface_swapchain_create_info_and_get_next_pointer, p_property_values, GDExtensionPtr<void>(p_next_pointer), pointer)) {
+ return reinterpret_cast<void *>(pointer);
+ }
+
+ return p_next_pointer;
+}
+
Ref<OpenXRAPIExtension> 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 71d2a57ff8..5cdf288c93 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
@@ -86,6 +86,7 @@ public:
virtual void on_session_created(const XrSession p_session) override;
virtual void on_process() override;
virtual void on_pre_render() override;
+ virtual void on_main_swapchains_created() override;
virtual void on_session_destroyed() override;
GDVIRTUAL0(_on_register_metadata);
@@ -95,6 +96,7 @@ public:
GDVIRTUAL1(_on_session_created, uint64_t);
GDVIRTUAL0(_on_process);
GDVIRTUAL0(_on_pre_render);
+ GDVIRTUAL0(_on_main_swapchains_created);
GDVIRTUAL0(_on_session_destroyed);
virtual void on_state_idle() override;
@@ -119,15 +121,17 @@ 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 *set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, const 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;
+ virtual void *set_android_surface_swapchain_create_info_and_get_next_pointer(const Dictionary &p_property_values, void *p_next_pointer) 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);
+ GDVIRTUAL2R(uint64_t, _set_android_surface_swapchain_create_info_and_get_next_pointer, Dictionary, GDExtensionPtr<void>);
Ref<OpenXRAPIExtension> get_openxr_api();
diff --git a/modules/openxr/extensions/openxr_fb_foveation_extension.cpp b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp
index bbdd2e3c8a..8ce808dd3c 100644
--- a/modules/openxr/extensions/openxr_fb_foveation_extension.cpp
+++ b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp
@@ -101,7 +101,7 @@ void *OpenXRFBFoveationExtension::set_swapchain_create_info_and_get_next_pointer
}
}
-void OpenXRFBFoveationExtension::on_state_ready() {
+void OpenXRFBFoveationExtension::on_main_swapchains_created() {
update_profile();
}
@@ -127,26 +127,41 @@ void OpenXRFBFoveationExtension::set_foveation_dynamic(XrFoveationDynamicFB p_fo
update_profile();
}
-void OpenXRFBFoveationExtension::update_profile() {
- if (!is_enabled()) {
+void OpenXRFBFoveationExtension::_update_profile() {
+ // Must be called from rendering thread!
+ ERR_NOT_ON_RENDER_THREAD;
+
+ OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton();
+ ERR_FAIL_NULL(fov_ext);
+
+ if (!fov_ext->is_enabled()) {
+ return;
+ }
+
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
+ ERR_FAIL_NULL(openxr_api);
+
+ XrSwapchain main_color_swapchain = openxr_api->get_color_swapchain();
+ if (main_color_swapchain == XR_NULL_HANDLE) {
+ // Our swapchain hasn't been created yet, we'll call this again once it has.
return;
}
XrFoveationLevelProfileCreateInfoFB level_profile_create_info;
level_profile_create_info.type = XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB;
level_profile_create_info.next = nullptr;
- level_profile_create_info.level = foveation_level;
+ level_profile_create_info.level = fov_ext->foveation_level;
level_profile_create_info.verticalOffset = 0.0f;
- level_profile_create_info.dynamic = foveation_dynamic;
+ level_profile_create_info.dynamic = fov_ext->foveation_dynamic;
XrFoveationProfileCreateInfoFB profile_create_info;
profile_create_info.type = XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB;
profile_create_info.next = &level_profile_create_info;
XrFoveationProfileFB foveation_profile;
- XrResult result = xrCreateFoveationProfileFB(OpenXRAPI::get_singleton()->get_session(), &profile_create_info, &foveation_profile);
+ XrResult result = fov_ext->xrCreateFoveationProfileFB(openxr_api->get_session(), &profile_create_info, &foveation_profile);
if (XR_FAILED(result)) {
- print_line("OpenXR: Unable to create the foveation profile [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ print_line("OpenXR: Unable to create the foveation profile [", openxr_api->get_error_string(result), "]");
return;
}
@@ -154,15 +169,15 @@ void OpenXRFBFoveationExtension::update_profile() {
foveation_update_state.type = XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB;
foveation_update_state.profile = foveation_profile;
- result = swapchain_update_state_ext->xrUpdateSwapchainFB(OpenXRAPI::get_singleton()->get_color_swapchain(), (XrSwapchainStateBaseHeaderFB *)&foveation_update_state);
+ result = fov_ext->swapchain_update_state_ext->xrUpdateSwapchainFB(main_color_swapchain, (XrSwapchainStateBaseHeaderFB *)&foveation_update_state);
if (XR_FAILED(result)) {
- print_line("OpenXR: Unable to update the swapchain [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ print_line("OpenXR: Unable to update the swapchain [", openxr_api->get_error_string(result), "]");
// We still want to destroy our profile so keep going...
}
- result = xrDestroyFoveationProfileFB(foveation_profile);
+ result = fov_ext->xrDestroyFoveationProfileFB(foveation_profile);
if (XR_FAILED(result)) {
- print_line("OpenXR: Unable to destroy the foveation profile [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ print_line("OpenXR: Unable to destroy the foveation profile [", openxr_api->get_error_string(result), "]");
}
}
diff --git a/modules/openxr/extensions/openxr_fb_foveation_extension.h b/modules/openxr/extensions/openxr_fb_foveation_extension.h
index 1c5e722731..84bd7011b5 100644
--- a/modules/openxr/extensions/openxr_fb_foveation_extension.h
+++ b/modules/openxr/extensions/openxr_fb_foveation_extension.h
@@ -60,7 +60,7 @@ public:
virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override;
- virtual void on_state_ready() override;
+ virtual void on_main_swapchains_created() override;
bool is_enabled() const;
@@ -82,7 +82,15 @@ private:
XrFoveationLevelFB foveation_level = XR_FOVEATION_LEVEL_NONE_FB;
XrFoveationDynamicFB foveation_dynamic = XR_FOVEATION_DYNAMIC_DISABLED_FB;
- void update_profile();
+ static void _update_profile();
+
+ void update_profile() {
+ // If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready...
+ RenderingServer *rendering_server = RenderingServer::get_singleton();
+ ERR_FAIL_NULL(rendering_server);
+
+ rendering_server->call_on_render_thread(callable_mp_static(&OpenXRFBFoveationExtension::_update_profile));
+ }
// Enable foveation on this swapchain
XrSwapchainCreateInfoFoveationFB swapchain_create_info_foveation_fb;
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
index b8a2f58935..ea64f077c5 100644
--- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
@@ -58,9 +58,14 @@ OpenXRHandTrackingExtension::~OpenXRHandTrackingExtension() {
HashMap<String, bool *> OpenXRHandTrackingExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
+ unobstructed_data_source = GLOBAL_GET("xr/openxr/extensions/hand_tracking_unobstructed_data_source");
+ controller_data_source = GLOBAL_GET("xr/openxr/extensions/hand_tracking_controller_data_source");
+
request_extensions[XR_EXT_HAND_TRACKING_EXTENSION_NAME] = &hand_tracking_ext;
request_extensions[XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME] = &hand_motion_range_ext;
- request_extensions[XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME] = &hand_tracking_source_ext;
+ if (unobstructed_data_source || controller_data_source) {
+ request_extensions[XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME] = &hand_tracking_source_ext;
+ }
return request_extensions;
}
@@ -141,10 +146,18 @@ void OpenXRHandTrackingExtension::on_process() {
void *next_pointer = nullptr;
// Originally not all XR runtimes supported hand tracking data sourced both from controllers and normal hand tracking.
- // With this extension we can indicate we accept input from both sources so hand tracking data is consistently provided
- // on runtimes that support this.
- XrHandTrackingDataSourceEXT data_sources[2] = { XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT, XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT };
- XrHandTrackingDataSourceInfoEXT data_source_info = { XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT, next_pointer, 2, data_sources };
+ // With this extension we can indicate we wish to accept input from either or both sources.
+ // This functionality is subject to the abilities of the XR runtime and requires the data source extension.
+ // Note: If the data source extension is not available, no guarantees can be made on what the XR runtime supports.
+ uint32_t data_source_count = 0;
+ XrHandTrackingDataSourceEXT data_sources[2];
+ if (unobstructed_data_source) {
+ data_sources[data_source_count++] = XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT;
+ }
+ if (controller_data_source) {
+ data_sources[data_source_count++] = XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT;
+ }
+ XrHandTrackingDataSourceInfoEXT data_source_info = { XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT, next_pointer, data_source_count, data_sources };
if (hand_tracking_source_ext) {
// If supported include this info
next_pointer = &data_source_info;
@@ -224,7 +237,9 @@ void OpenXRHandTrackingExtension::on_process() {
if (XR_FAILED(result)) {
// not successful? then we do nothing.
print_line("OpenXR: Failed to get tracking for hand", i, "[", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ godot_tracker->set_hand_tracking_source(XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN);
godot_tracker->set_has_tracking_data(false);
+ godot_tracker->invalidate_pose("default");
continue;
}
@@ -235,8 +250,6 @@ void OpenXRHandTrackingExtension::on_process() {
}
if (hand_trackers[i].locations.isActive) {
- godot_tracker->set_has_tracking_data(true);
-
// SKELETON_RIG_HUMANOID bone adjustment. This rotation performs:
// OpenXR Z+ -> Godot Humanoid Y- (Back along the bone)
// OpenXR Y+ -> Godot Humanoid Z- (Out the back of the hand)
@@ -245,7 +258,6 @@ void OpenXRHandTrackingExtension::on_process() {
for (int joint = 0; joint < XR_HAND_JOINT_COUNT_EXT; joint++) {
const XrHandJointLocationEXT &location = hand_trackers[i].joint_locations[joint];
const XrHandJointVelocityEXT &velocity = hand_trackers[i].joint_velocities[joint];
- const XrHandTrackingDataSourceStateEXT &data_source = hand_trackers[i].data_source;
const XrPosef &pose = location.pose;
Transform3D transform;
@@ -285,18 +297,35 @@ void OpenXRHandTrackingExtension::on_process() {
godot_tracker->set_hand_joint_radius((XRHandTracker::HandJoint)joint, location.radius);
if (joint == XR_HAND_JOINT_PALM_EXT) {
- XRHandTracker::HandTrackingSource source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN;
- if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) {
- source = XRHandTracker::HAND_TRACKING_SOURCE_UNOBSTRUCTED;
- } else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) {
- source = XRHandTracker::HAND_TRACKING_SOURCE_CONTROLLER;
+ if (location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
+ XrHandTrackingDataSourceStateEXT &data_source = hand_trackers[i].data_source;
+
+ XRHandTracker::HandTrackingSource source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN;
+ if (hand_tracking_source_ext) {
+ if (!data_source.isActive) {
+ source = XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED;
+ } else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) {
+ source = XRHandTracker::HAND_TRACKING_SOURCE_UNOBSTRUCTED;
+ } else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) {
+ source = XRHandTracker::HAND_TRACKING_SOURCE_CONTROLLER;
+ } else {
+ // Data source shouldn't be active, if new data sources are added to OpenXR we need to enable them.
+ WARN_PRINT_ONCE("Unknown active data source found!");
+ source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN;
+ }
+ }
+ godot_tracker->set_hand_tracking_source(source);
+ godot_tracker->set_has_tracking_data(true);
+ godot_tracker->set_pose("default", transform, linear_velocity, angular_velocity);
+ } else {
+ godot_tracker->set_hand_tracking_source(hand_tracking_source_ext ? XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED : XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN);
+ godot_tracker->set_has_tracking_data(false);
+ godot_tracker->invalidate_pose("default");
}
-
- godot_tracker->set_hand_tracking_source(source);
- godot_tracker->set_pose("default", transform, linear_velocity, angular_velocity);
}
}
} else {
+ godot_tracker->set_hand_tracking_source(hand_tracking_source_ext ? XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED : XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN);
godot_tracker->set_has_tracking_data(false);
godot_tracker->invalidate_pose("default");
}
@@ -344,16 +373,17 @@ XrHandJointsMotionRangeEXT OpenXRHandTrackingExtension::get_motion_range(HandTra
OpenXRHandTrackingExtension::HandTrackedSource OpenXRHandTrackingExtension::get_hand_tracking_source(HandTrackedHands p_hand) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, OPENXR_SOURCE_UNKNOWN);
- if (hand_tracking_source_ext && hand_trackers[p_hand].data_source.isActive) {
- switch (hand_trackers[p_hand].data_source.dataSource) {
- case XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT:
- return OPENXR_SOURCE_UNOBSTRUCTED;
-
- case XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT:
- return OPENXR_SOURCE_CONTROLLER;
-
- default:
- return OPENXR_SOURCE_UNKNOWN;
+ if (hand_tracking_source_ext) {
+ if (!hand_trackers[p_hand].data_source.isActive) {
+ return OPENXR_SOURCE_NOT_TRACKED;
+ } else if (hand_trackers[p_hand].data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) {
+ return OPENXR_SOURCE_UNOBSTRUCTED;
+ } else if (hand_trackers[p_hand].data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) {
+ return OPENXR_SOURCE_CONTROLLER;
+ } else {
+ // Data source shouldn't be active, if new data sources are added to OpenXR we need to enable them.
+ WARN_PRINT_ONCE("Unknown active data source found!");
+ return OPENXR_SOURCE_UNKNOWN;
}
}
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.h b/modules/openxr/extensions/openxr_hand_tracking_extension.h
index f709bc05c2..2c34ff7f21 100644
--- a/modules/openxr/extensions/openxr_hand_tracking_extension.h
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.h
@@ -48,6 +48,7 @@ public:
OPENXR_SOURCE_UNKNOWN,
OPENXR_SOURCE_UNOBSTRUCTED,
OPENXR_SOURCE_CONTROLLER,
+ OPENXR_SOURCE_NOT_TRACKED,
OPENXR_SOURCE_MAX
};
@@ -110,6 +111,8 @@ private:
bool hand_tracking_ext = false;
bool hand_motion_range_ext = false;
bool hand_tracking_source_ext = false;
+ bool unobstructed_data_source = false;
+ bool controller_data_source = false;
// functions
void cleanup_hand_tracking();
diff --git a/modules/openxr/extensions/openxr_htc_controller_extension.cpp b/modules/openxr/extensions/openxr_htc_controller_extension.cpp
index c4ecb4e57c..416934fa47 100644
--- a/modules/openxr/extensions/openxr_htc_controller_extension.cpp
+++ b/modules/openxr/extensions/openxr_htc_controller_extension.cpp
@@ -37,10 +37,19 @@ HashMap<String, bool *> OpenXRHTCControllerExtension::get_requested_extensions()
request_extensions[XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available[HTC_VIVE_COSMOS];
request_extensions[XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available[HTC_VIVE_FOCUS3];
+ request_extensions[XR_HTC_HAND_INTERACTION_EXTENSION_NAME] = &available[HTC_HAND_INTERACTION];
return request_extensions;
}
+PackedStringArray OpenXRHTCControllerExtension::get_suggested_tracker_names() {
+ PackedStringArray arr = {
+ "/user/hand_htc/left",
+ "/user/hand_htc/right",
+ };
+ return arr;
+}
+
bool OpenXRHTCControllerExtension::is_available(HTCControllers p_type) {
return available[p_type];
}
@@ -49,6 +58,9 @@ void OpenXRHTCControllerExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(metadata);
+ metadata->register_top_level_path("HTC left hand tracker", "/user/hand_htc/left", XR_HTC_HAND_INTERACTION_EXTENSION_NAME);
+ metadata->register_top_level_path("HTC right hand tracker", "/user/hand_htc/right", XR_HTC_HAND_INTERACTION_EXTENSION_NAME);
+
// HTC Vive Cosmos controller
metadata->register_interaction_profile("Vive Cosmos controller", "/interaction_profiles/htc/vive_cosmos_controller", XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME);
metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
@@ -128,4 +140,17 @@ void OpenXRHTCControllerExtension::on_register_metadata() {
metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+
+ // HTC Hand interaction profile
+ metadata->register_interaction_profile("HTC Hand interaction", "/interaction_profiles/htc/hand_interaction", XR_HTC_HAND_INTERACTION_EXTENSION_NAME);
+ metadata->register_io_path("/interaction_profiles/htc/hand_interaction", "Grip pose", "/user/hand_htc/left", "/user/hand_htc/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/hand_interaction", "Grip pose", "/user/hand_htc/right", "/user/hand_htc/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/hand_interaction", "Aim pose", "/user/hand_htc/left", "/user/hand_htc/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/hand_interaction", "Aim pose", "/user/hand_htc/right", "/user/hand_htc/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/htc/hand_interaction", "Select (pinch)", "/user/hand_htc/left", "/user/hand_htc/left/input/select/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/hand_interaction", "Select (pinch)", "/user/hand_htc/right", "/user/hand_htc/right/input/select/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+
+ metadata->register_io_path("/interaction_profiles/htc/hand_interaction", "Squeeze (grab)", "/user/hand_htc/left", "/user/hand_htc/left/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/hand_interaction", "Squeeze (grab)", "/user/hand_htc/right", "/user/hand_htc/right/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
}
diff --git a/modules/openxr/extensions/openxr_htc_controller_extension.h b/modules/openxr/extensions/openxr_htc_controller_extension.h
index 2aaeac2577..6f2b2ceec3 100644
--- a/modules/openxr/extensions/openxr_htc_controller_extension.h
+++ b/modules/openxr/extensions/openxr_htc_controller_extension.h
@@ -39,11 +39,14 @@ public:
// Note, HTC Vive Wand controllers are part of the core spec and not part of our extension.
HTC_VIVE_COSMOS,
HTC_VIVE_FOCUS3,
+ HTC_HAND_INTERACTION,
HTC_MAX_CONTROLLERS
};
virtual HashMap<String, bool *> get_requested_extensions() override;
+ PackedStringArray get_suggested_tracker_names() override;
+
bool is_available(HTCControllers p_type);
virtual void on_register_metadata() override;
diff --git a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp
index bb60f7adef..b2ddf71f5e 100644
--- a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp
+++ b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp
@@ -69,6 +69,20 @@ void OpenXRHTCViveTrackerExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(metadata);
+ // register_top_level_path("Handheld object tracker", "/user/vive_tracker_htcx/role/handheld_object", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ metadata->register_top_level_path("Left foot tracker", "/user/vive_tracker_htcx/role/left_foot", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ metadata->register_top_level_path("Right foot tracker", "/user/vive_tracker_htcx/role/right_foot", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ metadata->register_top_level_path("Left shoulder tracker", "/user/vive_tracker_htcx/role/left_shoulder", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ metadata->register_top_level_path("Right shoulder tracker", "/user/vive_tracker_htcx/role/right_shoulder", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ metadata->register_top_level_path("Left elbow tracker", "/user/vive_tracker_htcx/role/left_elbow", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ metadata->register_top_level_path("Right elbow tracker", "/user/vive_tracker_htcx/role/right_elbow", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ metadata->register_top_level_path("Left knee tracker", "/user/vive_tracker_htcx/role/left_knee", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ metadata->register_top_level_path("Right knee tracker", "/user/vive_tracker_htcx/role/right_knee", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ metadata->register_top_level_path("Waist tracker", "/user/vive_tracker_htcx/role/waist", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ metadata->register_top_level_path("Chest tracker", "/user/vive_tracker_htcx/role/chest", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ metadata->register_top_level_path("Camera tracker", "/user/vive_tracker_htcx/role/camera", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ metadata->register_top_level_path("Keyboard tracker", "/user/vive_tracker_htcx/role/keyboard", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+
// HTC Vive tracker
// Interestingly enough trackers don't have buttons or inputs, yet these are defined in the spec.
// I think this can be supported through attachments on the trackers.
diff --git a/modules/openxr/extensions/openxr_mxink_extension.cpp b/modules/openxr/extensions/openxr_mxink_extension.cpp
new file mode 100644
index 0000000000..fe48583c27
--- /dev/null
+++ b/modules/openxr/extensions/openxr_mxink_extension.cpp
@@ -0,0 +1,83 @@
+/**************************************************************************/
+/* openxr_mxink_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "openxr_mxink_extension.h"
+
+#include "../action_map/openxr_interaction_profile_metadata.h"
+
+// Not in base XR libs needs def
+#define XR_LOGITECH_MX_INK_STYLUS_INTERACTION_EXTENSION_NAME "XR_LOGITECH_mx_ink_stylus_interaction"
+
+HashMap<String, bool *> OpenXRMxInkExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_LOGITECH_MX_INK_STYLUS_INTERACTION_EXTENSION_NAME] = &available;
+
+ return request_extensions;
+}
+
+bool OpenXRMxInkExtension::is_available() {
+ return available;
+}
+
+void OpenXRMxInkExtension::on_register_metadata() {
+ OpenXRInteractionProfileMetadata *metadata = OpenXRInteractionProfileMetadata::get_singleton();
+ ERR_FAIL_NULL(metadata);
+
+ // Logitech MX Ink Stylus
+ metadata->register_interaction_profile("Logitech MX Ink Stylus", "/interaction_profiles/logitech/mx_ink_stylus_logitech", XR_LOGITECH_MX_INK_STYLUS_INTERACTION_EXTENSION_NAME);
+
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Tip Force", "/user/hand/left", "/user/hand/left/input/tip_logitech/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Middle force", "/user/hand/left", "/user/hand/left/input/cluster_middle_logitech/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Front click", "/user/hand/left", "/user/hand/left/input/cluster_front_logitech/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Front double", "/user/hand/left", "/user/hand/left/input/cluster_front_logitech/double_tap_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Back click", "/user/hand/left", "/user/hand/left/input/cluster_back_logitech/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Back double", "/user/hand/left", "/user/hand/left/input/cluster_back_logitech/double_tap_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "System click", "/user/hand/left", "/user/hand/left/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Docked", "/user/hand/left", "/user/hand/left/input/dock_logitech/docked_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Tip pose", "/user/hand/left", "/user/hand/left/input/tip_logitech/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Tip Force", "/user/hand/right", "/user/hand/right/input/tip_logitech/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Middle force", "/user/hand/right", "/user/hand/right/input/cluster_middle_logitech/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Front click", "/user/hand/right", "/user/hand/right/input/cluster_front_logitech/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Front double", "/user/hand/right", "/user/hand/right/input/cluster_front_logitech/double_tap_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Back click", "/user/hand/right", "/user/hand/right/input/cluster_back_logitech/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Back double", "/user/hand/right", "/user/hand/right/input/cluster_back_logitech/double_tap_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Docked", "/user/hand/right", "/user/hand/right/input/dock_logitech/docked_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Tip pose", "/user/hand/right", "/user/hand/right/input/tip_logitech/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+}
diff --git a/modules/openxr/extensions/openxr_mxink_extension.h b/modules/openxr/extensions/openxr_mxink_extension.h
new file mode 100644
index 0000000000..fe0cf866aa
--- /dev/null
+++ b/modules/openxr/extensions/openxr_mxink_extension.h
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/* openxr_mxink_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef OPENXR_MXINK_EXTENSION_H
+#define OPENXR_MXINK_EXTENSION_H
+
+#include "openxr_extension_wrapper.h"
+
+class OpenXRMxInkExtension : public OpenXRExtensionWrapper {
+public:
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ bool is_available();
+
+ virtual void on_register_metadata() override;
+
+private:
+ bool available = false;
+};
+
+#endif // OPENXR_MXINK_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_visibility_mask_extension.cpp b/modules/openxr/extensions/openxr_visibility_mask_extension.cpp
new file mode 100644
index 0000000000..3e193d7d50
--- /dev/null
+++ b/modules/openxr/extensions/openxr_visibility_mask_extension.cpp
@@ -0,0 +1,279 @@
+/**************************************************************************/
+/* openxr_visibility_mask_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "openxr_visibility_mask_extension.h"
+
+#include "../openxr_api.h"
+#include "core/string/print_string.h"
+#include "core/variant/array.h"
+#include "core/variant/variant.h"
+#include "servers/rendering_server.h"
+
+static const char *VISIBILITY_MASK_SHADER_CODE =
+ "shader_type spatial;\n"
+ "render_mode unshaded, shadows_disabled, cull_disabled;\n"
+ "void vertex() {\n"
+ "\tif (int(VERTEX.z) == VIEW_INDEX) {\n"
+ "\t\tVERTEX.z = -1.0;\n"
+ "\t\tVERTEX += EYE_OFFSET;\n"
+ "\t\tPOSITION = PROJECTION_MATRIX * vec4(VERTEX, 1.0);\n"
+ "\t\tPOSITION.xy /= POSITION.w;\n"
+ "\t\tPOSITION.z = 1.0;\n"
+ "\t\tPOSITION.w = 1.0;\n"
+ "\t} else {\n"
+ "\t\tPOSITION = vec4(2.0, 2.0, 2.0, 1.0);\n"
+ "\t}\n"
+ "}\n"
+ "void fragment() {\n"
+ "\tALBEDO = vec3(0.0, 0.0, 0.0);\n"
+ "}\n";
+
+OpenXRVisibilityMaskExtension *OpenXRVisibilityMaskExtension::singleton = nullptr;
+
+OpenXRVisibilityMaskExtension *OpenXRVisibilityMaskExtension::get_singleton() {
+ return singleton;
+}
+
+OpenXRVisibilityMaskExtension::OpenXRVisibilityMaskExtension() {
+ singleton = this;
+}
+
+OpenXRVisibilityMaskExtension::~OpenXRVisibilityMaskExtension() {
+ singleton = nullptr;
+}
+
+HashMap<String, bool *> OpenXRVisibilityMaskExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_KHR_VISIBILITY_MASK_EXTENSION_NAME] = &available;
+
+ return request_extensions;
+}
+
+void OpenXRVisibilityMaskExtension::on_instance_created(const XrInstance p_instance) {
+ if (available) {
+ EXT_INIT_XR_FUNC(xrGetVisibilityMaskKHR);
+ }
+}
+
+void OpenXRVisibilityMaskExtension::on_session_created(const XrSession p_instance) {
+ if (available) {
+ RS *rendering_server = RS::get_singleton();
+ ERR_FAIL_NULL(rendering_server);
+
+ OpenXRAPI *openxr_api = (OpenXRAPI *)OpenXRAPI::get_singleton();
+ ERR_FAIL_NULL(openxr_api);
+
+ // Create our shader.
+ shader = rendering_server->shader_create();
+ rendering_server->shader_set_code(shader, VISIBILITY_MASK_SHADER_CODE);
+
+ // Create our material.
+ material = rendering_server->material_create();
+ rendering_server->material_set_shader(material, shader);
+ rendering_server->material_set_render_priority(material, 99);
+
+ // Create our mesh.
+ mesh = rendering_server->mesh_create();
+
+ // Get our initial mesh data.
+ mesh_count = openxr_api->get_view_count(); // We need a mesh for each view.
+ for (uint32_t i = 0; i < mesh_count; i++) {
+ _update_mesh_data(i);
+ }
+
+ // And update our mesh
+ _update_mesh();
+ }
+}
+
+void OpenXRVisibilityMaskExtension::on_session_destroyed() {
+ RS *rendering_server = RS::get_singleton();
+ ERR_FAIL_NULL(rendering_server);
+
+ // Free our mesh.
+ if (mesh.is_valid()) {
+ rendering_server->free(mesh);
+ mesh = RID();
+ }
+
+ // Free our material.
+ if (material.is_valid()) {
+ rendering_server->free(material);
+ material = RID();
+ }
+
+ // Free our shader.
+ if (shader.is_valid()) {
+ rendering_server->free(shader);
+ shader = RID();
+ }
+
+ mesh_count = 0;
+}
+
+void OpenXRVisibilityMaskExtension::on_pre_render() {
+ // Update mesh data if its dirty.
+ // Here we call this from the rendering thread however as we're going through the rendering server this is safe.
+ _update_mesh();
+}
+
+bool OpenXRVisibilityMaskExtension::on_event_polled(const XrEventDataBuffer &event) {
+ if (event.type == XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR) {
+ XrEventDataVisibilityMaskChangedKHR *vismask_event = (XrEventDataVisibilityMaskChangedKHR *)&event;
+
+ print_verbose("OpenXR EVENT: Visibility mask changed for view " + String::num_uint64(vismask_event->viewIndex));
+
+ if (available) { // This event won't be called if this extension is not available but better safe than sorry.
+ _update_mesh_data(vismask_event->viewIndex);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool OpenXRVisibilityMaskExtension::is_available() {
+ return available;
+}
+
+RID OpenXRVisibilityMaskExtension::get_mesh() {
+ return mesh;
+}
+
+void OpenXRVisibilityMaskExtension::_update_mesh_data(uint32_t p_view) {
+ if (available) {
+ ERR_FAIL_UNSIGNED_INDEX(p_view, 4);
+
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
+ ERR_FAIL_NULL(openxr_api);
+
+ XrSession session = openxr_api->get_session();
+ XrViewConfigurationType view_configuration_type = openxr_api->get_view_configuration();
+
+ // Figure out how much data we're getting.
+ XrVisibilityMaskKHR visibility_mask_data = {
+ XR_TYPE_VISIBILITY_MASK_KHR,
+ nullptr,
+ 0,
+ 0,
+ nullptr,
+ 0,
+ 0,
+ nullptr,
+ };
+
+ XrResult result = xrGetVisibilityMaskKHR(session, view_configuration_type, p_view, XR_VISIBILITY_MASK_TYPE_HIDDEN_TRIANGLE_MESH_KHR, &visibility_mask_data);
+ if (XR_FAILED(result)) {
+ print_line("OpenXR: Unable to obtain visibility mask metrics [", openxr_api->get_error_string(result), "]");
+ return;
+ }
+
+ // Resize buffers
+ mesh_data[p_view].vertices.resize(visibility_mask_data.vertexCountOutput);
+ mesh_data[p_view].indices.resize(visibility_mask_data.indexCountOutput);
+
+ visibility_mask_data.vertexCapacityInput = visibility_mask_data.vertexCountOutput;
+ visibility_mask_data.vertices = mesh_data[p_view].vertices.ptrw();
+ visibility_mask_data.indexCapacityInput = visibility_mask_data.indexCountOutput;
+ visibility_mask_data.indices = mesh_data[p_view].indices.ptrw();
+
+ result = xrGetVisibilityMaskKHR(session, view_configuration_type, p_view, XR_VISIBILITY_MASK_TYPE_HIDDEN_TRIANGLE_MESH_KHR, &visibility_mask_data);
+ if (XR_FAILED(result)) {
+ print_line("OpenXR: Unable to obtain visibility mask data [", openxr_api->get_error_string(result), "]");
+ return;
+ }
+
+ // Mark as dirty, we have updated mesh data.
+ is_dirty = true;
+ }
+}
+
+void OpenXRVisibilityMaskExtension::_update_mesh() {
+ if (available && is_dirty && mesh_count > 0) {
+ RS *rendering_server = RS::get_singleton();
+ ERR_FAIL_NULL(rendering_server);
+
+ OpenXRAPI *openxr_api = (OpenXRAPI *)OpenXRAPI::get_singleton();
+ ERR_FAIL_NULL(openxr_api);
+
+ // Combine all vertex and index buffers into one.
+ PackedVector3Array vertices;
+ PackedInt32Array indices;
+
+ uint64_t vertice_count = 0;
+ uint64_t index_count = 0;
+
+ for (uint32_t i = 0; i < mesh_count; i++) {
+ vertice_count += mesh_data[i].vertices.size();
+ index_count += mesh_data[i].indices.size();
+ }
+
+ vertices.resize(vertice_count);
+ indices.resize(index_count);
+ uint64_t offset = 0;
+
+ Vector3 *v_out = vertices.ptrw();
+ int32_t *i_out = indices.ptrw();
+
+ for (uint32_t i = 0; i < mesh_count; i++) {
+ const XrVector2f *v_in = mesh_data[i].vertices.ptr();
+ for (uint32_t j = 0; j < mesh_data[i].vertices.size(); j++) {
+ v_out->x = v_in->x;
+ v_out->y = v_in->y;
+ v_out->z = float(i); // We store our view in our Z component, our shader will filter the right faces out.
+ v_out++;
+ v_in++;
+ }
+ const uint32_t *i_in = mesh_data[i].indices.ptr();
+ for (uint32_t j = 0; j < mesh_data[i].indices.size(); j++) {
+ *i_out = offset + *i_in;
+ i_out++;
+ i_in++;
+ }
+
+ offset += mesh_data[i].vertices.size();
+ }
+
+ // Update our mesh.
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ arr[RS::ARRAY_VERTEX] = vertices;
+ arr[RS::ARRAY_INDEX] = indices;
+
+ rendering_server->mesh_clear(mesh);
+ rendering_server->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr);
+ rendering_server->mesh_surface_set_material(mesh, 0, material);
+
+ // Set no longer dirty.
+ is_dirty = false;
+ }
+}
diff --git a/modules/openxr/extensions/openxr_visibility_mask_extension.h b/modules/openxr/extensions/openxr_visibility_mask_extension.h
new file mode 100644
index 0000000000..ee80dd02ff
--- /dev/null
+++ b/modules/openxr/extensions/openxr_visibility_mask_extension.h
@@ -0,0 +1,95 @@
+/**************************************************************************/
+/* openxr_visibility_mask_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef OPENXR_VISIBILITY_MASK_EXTENSION_H
+#define OPENXR_VISIBILITY_MASK_EXTENSION_H
+
+#include "../util.h"
+
+#include "core/templates/vector.h"
+#include "openxr_extension_wrapper.h"
+#include "scene/resources/mesh.h"
+
+// The OpenXR visibility mask extension provides a mesh for each eye that
+// can be used as a mask to determine which part of our rendered result
+// is actually visible to the user. Due to lens distortion the edges of
+// the rendered image are never used in the final result output on the HMD.
+//
+// Blacking out this are of the render result can remove a fair amount of
+// overhead in rendering part of the screen that is unused.
+//
+// https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_KHR_visibility_mask
+
+class OpenXRVisibilityMaskExtension : public OpenXRExtensionWrapper {
+public:
+ static OpenXRVisibilityMaskExtension *get_singleton();
+
+ OpenXRVisibilityMaskExtension();
+ virtual ~OpenXRVisibilityMaskExtension() override;
+
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ virtual void on_instance_created(const XrInstance p_instance) override;
+
+ virtual void on_session_created(const XrSession p_instance) override;
+ virtual void on_session_destroyed() override;
+
+ virtual void on_pre_render() override;
+ virtual bool on_event_polled(const XrEventDataBuffer &event) override;
+
+ bool is_available();
+ RID get_mesh();
+
+private:
+ static OpenXRVisibilityMaskExtension *singleton;
+
+ bool available = false;
+ bool is_dirty = false;
+
+ RID shader;
+ RID material;
+ RID mesh;
+
+ struct MeshData {
+ Vector<XrVector2f> vertices;
+ Vector<uint32_t> indices;
+ };
+
+ uint32_t mesh_count = 0;
+ MeshData mesh_data[4];
+
+ void _update_mesh_data(uint32_t p_view);
+ void _update_mesh();
+
+ // OpenXR API call wrappers
+ EXT_PROTO_XRRESULT_FUNC5(xrGetVisibilityMaskKHR, (XrSession), session, (XrViewConfigurationType), viewConfigurationType, (uint32_t), viewIndex, (XrVisibilityMaskTypeKHR), visibilityMaskType, (XrVisibilityMaskKHR *), visibilityMask);
+};
+
+#endif // OPENXR_VISIBILITY_MASK_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_wmr_controller_extension.cpp b/modules/openxr/extensions/openxr_wmr_controller_extension.cpp
index 7d43d2e304..3437320452 100644
--- a/modules/openxr/extensions/openxr_wmr_controller_extension.cpp
+++ b/modules/openxr/extensions/openxr_wmr_controller_extension.cpp
@@ -38,6 +38,7 @@ HashMap<String, bool *> OpenXRWMRControllerExtension::get_requested_extensions()
// Note HP G2 is available on WMR and SteamVR, but Odessey is only available on WMR
request_extensions[XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME] = &available[WMR_HPMR];
request_extensions[XR_EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME] = &available[WMR_SAMSUNG_ODESSY];
+ request_extensions[XR_MSFT_HAND_INTERACTION_EXTENSION_NAME] = &available[WMR_HAND_INTERACTION];
return request_extensions;
}
@@ -117,4 +118,23 @@ void OpenXRWMRControllerExtension::on_register_metadata() {
metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+
+ // MSFT Hand interaction profile, also supported by other headsets
+ metadata->register_interaction_profile("MSFT Hand interaction", "/interaction_profiles/microsoft/hand_interaction", XR_MSFT_HAND_INTERACTION_EXTENSION_NAME);
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Pinch pose", "/user/hand/left", "/user/hand/left/input/pinch_ext/pose", XR_EXT_HAND_INTERACTION_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Pinch pose", "/user/hand/right", "/user/hand/right/input/pinch_ext/pose", XR_EXT_HAND_INTERACTION_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Poke pose", "/user/hand/left", "/user/hand/left/input/poke_ext/pose", XR_EXT_HAND_INTERACTION_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Poke pose", "/user/hand/right", "/user/hand/right/input/poke_ext/pose", XR_EXT_HAND_INTERACTION_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Select (pinch)", "/user/hand/left", "/user/hand/left/input/select/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Select (pinch)", "/user/hand/right", "/user/hand/right/input/select/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Squeeze (grab)", "/user/hand/left", "/user/hand/left/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/microsoft/hand_interaction", "Squeeze (grab)", "/user/hand/right", "/user/hand/right/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
}
diff --git a/modules/openxr/extensions/openxr_wmr_controller_extension.h b/modules/openxr/extensions/openxr_wmr_controller_extension.h
index 3bbdff586e..c8b50f1bbf 100644
--- a/modules/openxr/extensions/openxr_wmr_controller_extension.h
+++ b/modules/openxr/extensions/openxr_wmr_controller_extension.h
@@ -38,6 +38,7 @@ public:
enum WMRControllers {
WMR_HPMR,
WMR_SAMSUNG_ODESSY,
+ WMR_HAND_INTERACTION,
WMR_MAX_CONTROLLERS
};
diff --git a/modules/openxr/extensions/platform/openxr_opengl_extension.cpp b/modules/openxr/extensions/platform/openxr_opengl_extension.cpp
index d92084a220..caded14ca7 100644
--- a/modules/openxr/extensions/platform/openxr_opengl_extension.cpp
+++ b/modules/openxr/extensions/platform/openxr_opengl_extension.cpp
@@ -56,11 +56,6 @@
// feature off.
// See: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_sRGB_write_control.txt
-// On OpenGLES this is not defined in our standard headers..
-#ifndef GL_FRAMEBUFFER_SRGB
-#define GL_FRAMEBUFFER_SRGB 0x8DB9
-#endif
-
HashMap<String, bool *> OpenXROpenGLExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
@@ -196,23 +191,6 @@ void OpenXROpenGLExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_d
p_usable_depth_formats.push_back(GL_DEPTH_COMPONENT24);
}
-void OpenXROpenGLExtension::on_pre_draw_viewport(RID p_render_target) {
- if (srgb_ext_is_available) {
- hw_linear_to_srgb_is_enabled = glIsEnabled(GL_FRAMEBUFFER_SRGB);
- if (hw_linear_to_srgb_is_enabled) {
- // Disable this.
- glDisable(GL_FRAMEBUFFER_SRGB);
- }
- }
-}
-
-void OpenXROpenGLExtension::on_post_draw_viewport(RID p_render_target) {
- if (srgb_ext_is_available && hw_linear_to_srgb_is_enabled) {
- // Re-enable this.
- glEnable(GL_FRAMEBUFFER_SRGB);
- }
-}
-
bool OpenXROpenGLExtension::get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
ERR_FAIL_NULL_V(texture_storage, false);
@@ -262,8 +240,8 @@ bool OpenXROpenGLExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
Vector<RID> texture_rids;
for (uint64_t i = 0; i < swapchain_length; i++) {
- RID texture_rid = texture_storage->texture_create_external(
- p_array_size == 1 ? GLES3::Texture::TYPE_2D : GLES3::Texture::TYPE_LAYERED,
+ RID texture_rid = texture_storage->texture_create_from_native_handle(
+ p_array_size == 1 ? RS::TEXTURE_TYPE_2D : RS::TEXTURE_TYPE_LAYERED,
format,
images[i].image,
p_width,
diff --git a/modules/openxr/extensions/platform/openxr_opengl_extension.h b/modules/openxr/extensions/platform/openxr_opengl_extension.h
index a3052d3f53..8da3ca48f4 100644
--- a/modules/openxr/extensions/platform/openxr_opengl_extension.h
+++ b/modules/openxr/extensions/platform/openxr_opengl_extension.h
@@ -49,9 +49,6 @@ public:
virtual void on_instance_created(const XrInstance p_instance) override;
virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override;
- virtual void on_pre_draw_viewport(RID p_render_target) override;
- virtual void on_post_draw_viewport(RID p_render_target) override;
-
virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) override;
virtual void get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) override;
virtual String get_swapchain_format_name(int64_t p_swapchain_format) const override;
@@ -76,9 +73,6 @@ private:
Vector<RID> texture_rids;
};
- bool srgb_ext_is_available = true;
- bool hw_linear_to_srgb_is_enabled = false;
-
bool check_graphics_api_support(XrVersion p_desired_version);
#ifdef ANDROID_ENABLED