initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
Import("env_openxr")
module_obj = []
env_openxr.add_source_files(module_obj, "*.cpp")
# These are platform dependent
if env["platform"] == "android":
env_openxr.add_source_files(module_obj, "platform/openxr_android_extension.cpp")
if env["vulkan"]:
env_openxr.add_source_files(module_obj, "platform/openxr_vulkan_extension.cpp")
if env["metal"]:
env_openxr.add_source_files(module_obj, "platform/openxr_metal_extension.mm")
if env["opengl3"] and env["platform"] != "macos":
env_openxr.add_source_files(module_obj, "platform/openxr_opengl_extension.cpp")
if env["d3d12"]:
env_openxr.add_source_files(module_obj, "platform/openxr_d3d12_extension.cpp")
env.modules_sources += module_obj

View File

@@ -0,0 +1,70 @@
/**************************************************************************/
/* openxr_composition_layer_depth_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_composition_layer_depth_extension.h"
OpenXRCompositionLayerDepthExtension *OpenXRCompositionLayerDepthExtension::singleton = nullptr;
OpenXRCompositionLayerDepthExtension *OpenXRCompositionLayerDepthExtension::get_singleton() {
return singleton;
}
OpenXRCompositionLayerDepthExtension::OpenXRCompositionLayerDepthExtension() {
singleton = this;
}
OpenXRCompositionLayerDepthExtension::~OpenXRCompositionLayerDepthExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRCompositionLayerDepthExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME] = &available;
return request_extensions;
}
bool OpenXRCompositionLayerDepthExtension::is_available() {
return available;
}
int OpenXRCompositionLayerDepthExtension::get_composition_layer_count() {
return 0;
}
XrCompositionLayerBaseHeader *OpenXRCompositionLayerDepthExtension::get_composition_layer(int p_index) {
// Seems this is all done in our base layer... Just in case this changes...
return nullptr;
}
int OpenXRCompositionLayerDepthExtension::get_composition_layer_order(int p_index) {
return 0;
}

View File

@@ -0,0 +1,57 @@
/**************************************************************************/
/* openxr_composition_layer_depth_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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRCompositionLayerDepthExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRCompositionLayerDepthExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRCompositionLayerDepthExtension *get_singleton();
OpenXRCompositionLayerDepthExtension();
virtual ~OpenXRCompositionLayerDepthExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
virtual int get_composition_layer_count() override;
virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override;
virtual int get_composition_layer_order(int p_index) override;
private:
static OpenXRCompositionLayerDepthExtension *singleton;
bool available = false;
};

View File

@@ -0,0 +1,538 @@
/**************************************************************************/
/* openxr_composition_layer_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_composition_layer_extension.h"
#ifdef ANDROID_ENABLED
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#endif
#include "openxr_fb_update_swapchain_extension.h"
#include "platform/android/api/java_class_wrapper.h"
#include "servers/rendering/rendering_server_globals.h"
////////////////////////////////////////////////////////////////////////////
// OpenXRCompositionLayerExtension
OpenXRCompositionLayerExtension *OpenXRCompositionLayerExtension::singleton = nullptr;
OpenXRCompositionLayerExtension *OpenXRCompositionLayerExtension::get_singleton() {
return singleton;
}
OpenXRCompositionLayerExtension::OpenXRCompositionLayerExtension() {
singleton = this;
}
OpenXRCompositionLayerExtension::~OpenXRCompositionLayerExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRCompositionLayerExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
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_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();
}
}
int OpenXRCompositionLayerExtension::get_composition_layer_count() {
return composition_layers.size();
}
XrCompositionLayerBaseHeader *OpenXRCompositionLayerExtension::get_composition_layer(int p_index) {
ERR_FAIL_INDEX_V(p_index, composition_layers.size(), nullptr);
return composition_layers[p_index]->get_composition_layer();
}
int OpenXRCompositionLayerExtension::get_composition_layer_order(int p_index) {
ERR_FAIL_INDEX_V(p_index, composition_layers.size(), 1);
return composition_layers[p_index]->get_sort_order();
}
void OpenXRCompositionLayerExtension::register_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) {
composition_layers.push_back(p_composition_layer);
}
void OpenXRCompositionLayerExtension::unregister_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) {
composition_layers.erase(p_composition_layer);
}
bool OpenXRCompositionLayerExtension::is_available(XrStructureType p_which) {
switch (p_which) {
case XR_TYPE_COMPOSITION_LAYER_QUAD: {
// Doesn't require an extension.
return true;
} break;
case XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR: {
return cylinder_ext_available;
} break;
case XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR: {
return equirect_ext_available;
} break;
default: {
ERR_PRINT(vformat("Unsupported composition layer type: %s", p_which));
return false;
}
}
}
#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
OpenXRViewportCompositionLayerProvider::OpenXRViewportCompositionLayerProvider(XrCompositionLayerBaseHeader *p_composition_layer) {
composition_layer = p_composition_layer;
openxr_api = OpenXRAPI::get_singleton();
composition_layer_extension = OpenXRCompositionLayerExtension::get_singleton();
}
OpenXRViewportCompositionLayerProvider::~OpenXRViewportCompositionLayerProvider() {
for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) {
extension->on_viewport_composition_layer_destroyed(composition_layer);
}
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) {
if (alpha_blend != p_alpha_blend) {
alpha_blend = p_alpha_blend;
if (alpha_blend) {
composition_layer->layerFlags |= XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
} else {
composition_layer->layerFlags &= ~XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
}
}
}
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 (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(), RID());
}
subviewport.viewport = p_viewport;
if (subviewport.viewport.is_valid()) {
subviewport.viewport_size = p_size;
} else {
free_swapchain();
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) {
if (use_android_surface && swapchain_size != p_size) {
OpenXRFBUpdateSwapchainExtension *fb_update_swapchain_ext = OpenXRFBUpdateSwapchainExtension::get_singleton();
if (fb_update_swapchain_ext && fb_update_swapchain_ext->is_android_ext_enabled()) {
swapchain_size = p_size;
fb_update_swapchain_ext->update_swapchain_surface_size(android_surface.swapchain, swapchain_size);
}
}
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);
swapchain_state.dirty = true;
if (surface) {
android_surface.surface.instantiate(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;
extension_property_values_changed = true;
}
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 (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(subviewport.viewport);
RSG::texture_storage->render_target_set_override(rt, get_current_swapchain_texture(), RID(), RID(), RID());
}
}
}
if (swapchain_state.dirty) {
update_swapchain_state();
swapchain_state.dirty = false;
}
}
XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_composition_layer() {
if (openxr_api == nullptr || composition_layer_extension == nullptr) {
// OpenXR not initialized or we're in the editor?
return nullptr;
}
if (!composition_layer_extension->is_available(composition_layer->type)) {
// Selected type is not supported, ignore our layer.
return nullptr;
}
XrSwapchainSubImage subimage = {
0, // swapchain // NOLINT(modernize-use-nullptr) - 32-bit uses non-pointer uint64
{ { 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;
}
// 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 = 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 = 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 = subimage;
} break;
default: {
return nullptr;
} break;
}
if (extension_property_values_changed) {
extension_property_values_changed = false;
void *next_pointer = nullptr;
for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) {
void *np = extension->set_viewport_composition_layer_and_get_next_pointer(composition_layer, extension_property_values, next_pointer);
if (np) {
next_pointer = np;
}
}
composition_layer->next = next_pointer;
}
return composition_layer;
}
void OpenXRViewportCompositionLayerProvider::update_swapchain_state() {
OpenXRFBUpdateSwapchainExtension *fb_update_swapchain_ext = OpenXRFBUpdateSwapchainExtension::get_singleton();
if (!fb_update_swapchain_ext) {
return;
}
#ifdef ANDROID_ENABLED
if (use_android_surface) {
if (android_surface.swapchain == XR_NULL_HANDLE) {
return;
}
fb_update_swapchain_ext->update_swapchain_state(android_surface.swapchain, &swapchain_state);
} else
#endif
{
if (subviewport.swapchain_info.get_swapchain() == XR_NULL_HANDLE) {
return;
}
fb_update_swapchain_ext->update_swapchain_state(subviewport.swapchain_info.get_swapchain(), &swapchain_state);
}
}
OpenXRViewportCompositionLayerProvider::SwapchainState *OpenXRViewportCompositionLayerProvider::get_swapchain_state() {
return &swapchain_state;
}
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(get_openxr_type())) {
// Selected type is not supported?
return false;
}
// See if our current swapchain is outdated.
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 == 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 subviewport.swapchain_info.acquire(should_render);
}
subviewport.swapchain_info.queue_free();
}
// Create our new swap chain
int64_t swapchain_format = openxr_api->get_color_swapchain_format();
const uint32_t sample_count = 1;
const uint32_t array_size = 1;
XrSwapchainCreateFlags create_flags = 0;
if (p_static_image) {
create_flags |= XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT;
}
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;
}
swapchain_state.dirty = true;
// 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 = subviewport.swapchain_info.acquire(should_render);
swapchain_size = subviewport.viewport_size;
subviewport.static_image = p_static_image;
return ret;
}
void OpenXRViewportCompositionLayerProvider::free_swapchain() {
#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();
}
RID OpenXRViewportCompositionLayerProvider::get_current_swapchain_texture() {
ERR_FAIL_COND_V(use_android_surface, RID());
if (openxr_api == nullptr) {
return RID();
}
return subviewport.swapchain_info.get_image();
}

View File

@@ -0,0 +1,219 @@
/**************************************************************************/
/* openxr_composition_layer_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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
#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.
// OpenXRCompositionLayerExtension enables the extensions related to this functionality
class OpenXRCompositionLayerExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRCompositionLayerExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRCompositionLayerExtension *get_singleton();
OpenXRCompositionLayerExtension();
virtual ~OpenXRCompositionLayerExtension() 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_session) override;
virtual void on_session_destroyed() override;
virtual void on_pre_render() override;
virtual int get_composition_layer_count() override;
virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override;
virtual int get_composition_layer_order(int p_index) override;
void register_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer);
void unregister_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer);
bool is_available(XrStructureType p_which);
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;
Vector<OpenXRViewportCompositionLayerProvider *> composition_layers;
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 {
public:
// Must be identical to Filter enum definition in OpenXRCompositionLayer.
enum Filter {
FILTER_NEAREST,
FILTER_LINEAR,
FILTER_CUBIC,
};
// Must be identical to MipmapMode enum definition in OpenXRCompositionLayer.
enum MipmapMode {
MIPMAP_MODE_DISABLED,
MIPMAP_MODE_NEAREST,
MIPMAP_MODE_LINEAR,
};
// Must be identical to Wrap enum definition in OpenXRCompositionLayer.
enum Wrap {
WRAP_CLAMP_TO_BORDER,
WRAP_CLAMP_TO_EDGE,
WRAP_REPEAT,
WRAP_MIRRORED_REPEAT,
WRAP_MIRROR_CLAMP_TO_EDGE,
};
// Must be identical to Swizzle enum definition in OpenXRCompositionLayer.
enum Swizzle {
SWIZZLE_RED,
SWIZZLE_GREEN,
SWIZZLE_BLUE,
SWIZZLE_ALPHA,
SWIZZLE_ZERO,
SWIZZLE_ONE,
};
struct SwapchainState {
Filter min_filter = Filter::FILTER_LINEAR;
Filter mag_filter = Filter::FILTER_LINEAR;
MipmapMode mipmap_mode = MipmapMode::MIPMAP_MODE_LINEAR;
Wrap horizontal_wrap = Wrap::WRAP_CLAMP_TO_BORDER;
Wrap vertical_wrap = Wrap::WRAP_CLAMP_TO_BORDER;
Swizzle red_swizzle = Swizzle::SWIZZLE_RED;
Swizzle green_swizzle = Swizzle::SWIZZLE_GREEN;
Swizzle blue_swizzle = Swizzle::SWIZZLE_BLUE;
Swizzle alpha_swizzle = Swizzle::SWIZZLE_ALPHA;
float max_anisotropy = 1.0;
Color border_color = { 0.0, 0.0, 0.0, 0.0 };
bool dirty = false;
};
private:
XrCompositionLayerBaseHeader *composition_layer = nullptr;
int sort_order = 1;
bool alpha_blend = false;
Dictionary extension_property_values;
bool extension_property_values_changed = true;
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;
OpenXRAPI *openxr_api = nullptr;
OpenXRCompositionLayerExtension *composition_layer_extension = nullptr;
// Only for SubViewports.
bool update_and_acquire_swapchain(bool p_static_image);
RID get_current_swapchain_texture();
void update_swapchain_sub_image(XrSwapchainSubImage &r_swapchain_sub_image);
void free_swapchain();
SwapchainState swapchain_state;
#ifdef ANDROID_ENABLED
void create_android_surface();
#endif
public:
XrStructureType get_openxr_type() { return composition_layer->type; }
void set_sort_order(int p_sort_order) { sort_order = p_sort_order; }
int get_sort_order() const { return sort_order; }
void set_alpha_blend(bool p_alpha_blend);
bool get_alpha_blend() const { return alpha_blend; }
void set_viewport(RID p_viewport, Size2i p_size);
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);
void on_pre_render();
XrCompositionLayerBaseHeader *get_composition_layer();
void update_swapchain_state();
SwapchainState *get_swapchain_state();
OpenXRViewportCompositionLayerProvider(XrCompositionLayerBaseHeader *p_composition_layer);
~OpenXRViewportCompositionLayerProvider();
};

View File

@@ -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
nullptr, // 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
nullptr, // 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;
}

View File

@@ -0,0 +1,78 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "../util.h"
#include "openxr_extension_wrapper.h"
class OpenXRDebugUtilsExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRDebugUtilsExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
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)
};

View File

@@ -0,0 +1,273 @@
/**************************************************************************/
/* openxr_dpad_binding_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_dpad_binding_extension.h"
#include "../openxr_api.h"
#include "core/math/math_funcs.h"
// Implementation for:
// https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_EXT_dpad_binding
///////////////////////////////////////////////////////////////////////////////////////////////////
// OpenXRDPadBindingExtension
OpenXRDPadBindingExtension *OpenXRDPadBindingExtension::singleton = nullptr;
OpenXRDPadBindingExtension *OpenXRDPadBindingExtension::get_singleton() {
return singleton;
}
OpenXRDPadBindingExtension::OpenXRDPadBindingExtension() {
singleton = this;
}
OpenXRDPadBindingExtension::~OpenXRDPadBindingExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRDPadBindingExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
// Note, we're dependent on the binding modifier extension, this may be requested by multiple extension wrappers.
request_extensions[XR_KHR_BINDING_MODIFICATION_EXTENSION_NAME] = &binding_modifier_ext;
request_extensions[XR_EXT_DPAD_BINDING_EXTENSION_NAME] = &dpad_binding_ext;
return request_extensions;
}
bool OpenXRDPadBindingExtension::is_available() {
return binding_modifier_ext && dpad_binding_ext;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// OpenXRDpadBindingModifier
void OpenXRDpadBindingModifier::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_action_set", "action_set"), &OpenXRDpadBindingModifier::set_action_set);
ClassDB::bind_method(D_METHOD("get_action_set"), &OpenXRDpadBindingModifier::get_action_set);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "action_set", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRActionSet"), "set_action_set", "get_action_set");
ClassDB::bind_method(D_METHOD("set_input_path", "input_path"), &OpenXRDpadBindingModifier::set_input_path);
ClassDB::bind_method(D_METHOD("get_input_path"), &OpenXRDpadBindingModifier::get_input_path);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "input_path", PROPERTY_HINT_TYPE_STRING, "binding_path"), "set_input_path", "get_input_path");
ClassDB::bind_method(D_METHOD("set_threshold", "threshold"), &OpenXRDpadBindingModifier::set_threshold);
ClassDB::bind_method(D_METHOD("get_threshold"), &OpenXRDpadBindingModifier::get_threshold);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "threshold", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_threshold", "get_threshold");
ClassDB::bind_method(D_METHOD("set_threshold_released", "threshold_released"), &OpenXRDpadBindingModifier::set_threshold_released);
ClassDB::bind_method(D_METHOD("get_threshold_released"), &OpenXRDpadBindingModifier::get_threshold_released);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "threshold_released", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_threshold_released", "get_threshold_released");
ClassDB::bind_method(D_METHOD("set_center_region", "center_region"), &OpenXRDpadBindingModifier::set_center_region);
ClassDB::bind_method(D_METHOD("get_center_region"), &OpenXRDpadBindingModifier::get_center_region);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "center_region", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_center_region", "get_center_region");
ClassDB::bind_method(D_METHOD("set_wedge_angle", "wedge_angle"), &OpenXRDpadBindingModifier::set_wedge_angle);
ClassDB::bind_method(D_METHOD("get_wedge_angle"), &OpenXRDpadBindingModifier::get_wedge_angle);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wedge_angle", PROPERTY_HINT_RANGE, "1.0,180.0,0.1,radians_as_degrees"), "set_wedge_angle", "get_wedge_angle");
ClassDB::bind_method(D_METHOD("set_is_sticky", "is_sticky"), &OpenXRDpadBindingModifier::set_is_sticky);
ClassDB::bind_method(D_METHOD("get_is_sticky"), &OpenXRDpadBindingModifier::get_is_sticky);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_sticky"), "set_is_sticky", "get_is_sticky");
ClassDB::bind_method(D_METHOD("set_on_haptic", "haptic"), &OpenXRDpadBindingModifier::set_on_haptic);
ClassDB::bind_method(D_METHOD("get_on_haptic"), &OpenXRDpadBindingModifier::get_on_haptic);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "on_haptic", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRHapticBase"), "set_on_haptic", "get_on_haptic");
ClassDB::bind_method(D_METHOD("set_off_haptic", "haptic"), &OpenXRDpadBindingModifier::set_off_haptic);
ClassDB::bind_method(D_METHOD("get_off_haptic"), &OpenXRDpadBindingModifier::get_off_haptic);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "off_haptic", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRHapticBase"), "set_off_haptic", "get_off_haptic");
}
OpenXRDpadBindingModifier::OpenXRDpadBindingModifier() {
ERR_FAIL_COND(dpad_bindings_data.resize_initialized(sizeof(XrInteractionProfileDpadBindingEXT)) != OK);
dpad_bindings = (XrInteractionProfileDpadBindingEXT *)dpad_bindings_data.ptrw();
dpad_bindings->type = XR_TYPE_INTERACTION_PROFILE_DPAD_BINDING_EXT;
dpad_bindings->next = nullptr;
dpad_bindings->forceThreshold = 0.6;
dpad_bindings->forceThresholdReleased = 0.4;
dpad_bindings->centerRegion = 0.1;
dpad_bindings->wedgeAngle = Math::deg_to_rad(90.0);
dpad_bindings->isSticky = false;
}
void OpenXRDpadBindingModifier::set_action_set(const Ref<OpenXRActionSet> p_action_set) {
action_set = p_action_set;
}
Ref<OpenXRActionSet> OpenXRDpadBindingModifier::get_action_set() const {
return action_set;
}
void OpenXRDpadBindingModifier::set_input_path(const String &p_input_path) {
input_path = p_input_path;
emit_changed();
}
String OpenXRDpadBindingModifier::get_input_path() const {
return input_path;
}
void OpenXRDpadBindingModifier::set_threshold(float p_threshold) {
ERR_FAIL_NULL(dpad_bindings);
ERR_FAIL_COND(p_threshold < 0.0 || p_threshold > 1.0);
dpad_bindings->forceThreshold = p_threshold;
emit_changed();
}
float OpenXRDpadBindingModifier::get_threshold() const {
ERR_FAIL_NULL_V(dpad_bindings, 0.0);
return dpad_bindings->forceThreshold;
}
void OpenXRDpadBindingModifier::set_threshold_released(float p_threshold) {
ERR_FAIL_NULL(dpad_bindings);
ERR_FAIL_COND(p_threshold < 0.0 || p_threshold > 1.0);
dpad_bindings->forceThresholdReleased = p_threshold;
emit_changed();
}
float OpenXRDpadBindingModifier::get_threshold_released() const {
ERR_FAIL_NULL_V(dpad_bindings, 0.0);
return dpad_bindings->forceThresholdReleased;
}
void OpenXRDpadBindingModifier::set_center_region(float p_center_region) {
ERR_FAIL_NULL(dpad_bindings);
ERR_FAIL_COND(p_center_region < 0.0 || p_center_region > 1.0);
dpad_bindings->centerRegion = p_center_region;
emit_changed();
}
float OpenXRDpadBindingModifier::get_center_region() const {
ERR_FAIL_NULL_V(dpad_bindings, 0.0);
return dpad_bindings->centerRegion;
}
void OpenXRDpadBindingModifier::set_wedge_angle(float p_wedge_angle) {
ERR_FAIL_NULL(dpad_bindings);
dpad_bindings->wedgeAngle = p_wedge_angle;
emit_changed();
}
float OpenXRDpadBindingModifier::get_wedge_angle() const {
ERR_FAIL_NULL_V(dpad_bindings, 0.0);
return dpad_bindings->wedgeAngle;
}
void OpenXRDpadBindingModifier::set_wedge_angle_deg(float p_wedge_angle) {
ERR_FAIL_NULL(dpad_bindings);
dpad_bindings->wedgeAngle = Math::deg_to_rad(p_wedge_angle);
emit_changed();
}
float OpenXRDpadBindingModifier::get_wedge_angle_deg() const {
ERR_FAIL_NULL_V(dpad_bindings, 0.0);
return Math::rad_to_deg(dpad_bindings->wedgeAngle);
}
void OpenXRDpadBindingModifier::set_is_sticky(bool p_sticky) {
ERR_FAIL_NULL(dpad_bindings);
dpad_bindings->isSticky = p_sticky;
emit_changed();
}
bool OpenXRDpadBindingModifier::get_is_sticky() const {
ERR_FAIL_NULL_V(dpad_bindings, false);
return dpad_bindings->isSticky;
}
void OpenXRDpadBindingModifier::set_on_haptic(const Ref<OpenXRHapticBase> &p_haptic) {
on_haptic = p_haptic;
emit_changed();
}
Ref<OpenXRHapticBase> OpenXRDpadBindingModifier::get_on_haptic() const {
return on_haptic;
}
void OpenXRDpadBindingModifier::set_off_haptic(const Ref<OpenXRHapticBase> &p_haptic) {
off_haptic = p_haptic;
emit_changed();
}
Ref<OpenXRHapticBase> OpenXRDpadBindingModifier::get_off_haptic() const {
return off_haptic;
}
PackedByteArray OpenXRDpadBindingModifier::get_ip_modification() {
ERR_FAIL_NULL_V(dpad_bindings, PackedByteArray());
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, PackedByteArray());
OpenXRDPadBindingExtension *dpad_binding_ext = OpenXRDPadBindingExtension::get_singleton();
if (!dpad_binding_ext || !dpad_binding_ext->is_available()) {
// Extension not enabled!
WARN_PRINT("DPad binding extension is not enabled or available.");
return PackedByteArray();
}
dpad_bindings->binding = openxr_api->get_xr_path(input_path);
ERR_FAIL_COND_V(dpad_bindings->binding == XR_NULL_PATH, PackedByteArray());
// Get our action set
ERR_FAIL_COND_V(action_set.is_null(), PackedByteArray());
RID action_set_rid = openxr_api->find_action_set(action_set->get_name());
ERR_FAIL_COND_V(!action_set_rid.is_valid(), PackedByteArray());
dpad_bindings->actionSet = openxr_api->action_set_get_handle(action_set_rid);
// These are set already:
// - forceThreshold
// - forceThresholdReleased
// - centerRegion
// - wedgeAngle
// - isSticky
if (on_haptic.is_valid()) {
dpad_bindings->onHaptic = on_haptic->get_xr_structure();
} else {
dpad_bindings->onHaptic = nullptr;
}
if (off_haptic.is_valid()) {
dpad_bindings->offHaptic = off_haptic->get_xr_structure();
} else {
dpad_bindings->offHaptic = nullptr;
}
return dpad_bindings_data;
}

View File

@@ -0,0 +1,111 @@
/**************************************************************************/
/* openxr_dpad_binding_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. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_action_set.h"
#include "../action_map/openxr_binding_modifier.h"
#include "../action_map/openxr_haptic_feedback.h"
#include "../util.h"
#include "openxr_extension_wrapper.h"
class OpenXRDPadBindingExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRDPadBindingExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRDPadBindingExtension *get_singleton();
OpenXRDPadBindingExtension();
virtual ~OpenXRDPadBindingExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
private:
static OpenXRDPadBindingExtension *singleton;
bool binding_modifier_ext = false;
bool dpad_binding_ext = false;
};
class OpenXRDpadBindingModifier : public OpenXRIPBindingModifier {
GDCLASS(OpenXRDpadBindingModifier, OpenXRIPBindingModifier);
private:
PackedByteArray dpad_bindings_data;
XrInteractionProfileDpadBindingEXT *dpad_bindings = nullptr;
String input_path;
Ref<OpenXRActionSet> action_set;
Ref<OpenXRHapticBase> on_haptic;
Ref<OpenXRHapticBase> off_haptic;
protected:
static void _bind_methods();
public:
OpenXRDpadBindingModifier();
void set_action_set(const Ref<OpenXRActionSet> p_action_set);
Ref<OpenXRActionSet> get_action_set() const;
void set_input_path(const String &p_input_path);
String get_input_path() const;
void set_threshold(float p_threshold);
float get_threshold() const;
void set_threshold_released(float p_threshold);
float get_threshold_released() const;
void set_center_region(float p_center_region);
float get_center_region() const;
void set_wedge_angle(float p_wedge_angle);
float get_wedge_angle() const;
void set_wedge_angle_deg(float p_wedge_angle);
float get_wedge_angle_deg() const;
void set_is_sticky(bool p_sticky);
bool get_is_sticky() const;
void set_on_haptic(const Ref<OpenXRHapticBase> &p_haptic);
Ref<OpenXRHapticBase> get_on_haptic() const;
void set_off_haptic(const Ref<OpenXRHapticBase> &p_haptic);
Ref<OpenXRHapticBase> get_off_haptic() const;
virtual String get_description() const override { return "DPad modifier"; }
virtual PackedByteArray get_ip_modification() override;
};

View File

@@ -0,0 +1,372 @@
/**************************************************************************/
/* openxr_extension_wrapper.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_extension_wrapper.h"
#include "../openxr_api.h"
#include "../openxr_api_extension.h"
void OpenXRExtensionWrapper::_bind_methods() {
GDVIRTUAL_BIND(_get_requested_extensions);
GDVIRTUAL_BIND(_set_system_properties_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_instance_create_info_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_session_create_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_swapchain_create_info_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_hand_joint_locations_and_get_next_pointer, "hand_index", "next_pointer");
GDVIRTUAL_BIND(_set_projection_views_and_get_next_pointer, "view_index", "next_pointer");
GDVIRTUAL_BIND(_set_frame_wait_info_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_frame_end_info_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_view_locate_info_and_get_next_pointer, "next_pointer");
GDVIRTUAL_BIND(_set_reference_space_create_info_and_get_next_pointer, "reference_space_type", "next_pointer");
GDVIRTUAL_BIND(_get_composition_layer_count);
GDVIRTUAL_BIND(_get_composition_layer, "index");
GDVIRTUAL_BIND(_get_composition_layer_order, "index");
GDVIRTUAL_BIND(_get_suggested_tracker_names);
GDVIRTUAL_BIND(_on_register_metadata);
GDVIRTUAL_BIND(_on_before_instance_created);
GDVIRTUAL_BIND(_on_instance_created, "instance");
GDVIRTUAL_BIND(_on_instance_destroyed);
GDVIRTUAL_BIND(_on_session_created, "session");
GDVIRTUAL_BIND(_on_process);
GDVIRTUAL_BIND(_on_sync_actions);
GDVIRTUAL_BIND(_on_pre_render);
GDVIRTUAL_BIND(_on_main_swapchains_created);
GDVIRTUAL_BIND(_on_pre_draw_viewport, "viewport");
GDVIRTUAL_BIND(_on_post_draw_viewport, "viewport");
GDVIRTUAL_BIND(_on_session_destroyed);
GDVIRTUAL_BIND(_on_state_idle);
GDVIRTUAL_BIND(_on_state_ready);
GDVIRTUAL_BIND(_on_state_synchronized);
GDVIRTUAL_BIND(_on_state_visible);
GDVIRTUAL_BIND(_on_state_focused);
GDVIRTUAL_BIND(_on_state_stopping);
GDVIRTUAL_BIND(_on_state_loss_pending);
GDVIRTUAL_BIND(_on_state_exiting);
GDVIRTUAL_BIND(_on_event_polled, "event");
GDVIRTUAL_BIND(_set_viewport_composition_layer_and_get_next_pointer, "layer", "property_values", "next_pointer");
GDVIRTUAL_BIND(_get_viewport_composition_layer_extension_properties);
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"), &OpenXRExtensionWrapper::_gdextension_get_openxr_api);
ClassDB::bind_method(D_METHOD("register_extension_wrapper"), &OpenXRExtensionWrapper::_gdextension_register_extension_wrapper);
}
HashMap<String, bool *> OpenXRExtensionWrapper::get_requested_extensions() {
Dictionary request_extension;
if (GDVIRTUAL_CALL(_get_requested_extensions, request_extension)) {
HashMap<String, bool *> result;
for (const KeyValue<Variant, Variant> &kv : request_extension) {
GDExtensionPtr<bool> value = VariantCaster<GDExtensionPtr<bool>>::cast(kv.value);
result.insert(kv.key, value);
}
return result;
}
return HashMap<String, bool *>();
}
void *OpenXRExtensionWrapper::set_system_properties_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer;
if (GDVIRTUAL_CALL(_set_system_properties_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_instance_create_info_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer;
if (GDVIRTUAL_CALL(_set_instance_create_info_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_session_create_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer;
if (GDVIRTUAL_CALL(_set_session_create_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer;
if (GDVIRTUAL_CALL(_set_swapchain_create_info_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_hand_joint_locations_and_get_next_pointer(int p_hand_index, void *p_next_pointer) {
uint64_t pointer;
if (GDVIRTUAL_CALL(_set_hand_joint_locations_and_get_next_pointer, p_hand_index, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_projection_views_and_get_next_pointer(int p_view_index, void *p_next_pointer) {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_set_projection_views_and_get_next_pointer, p_view_index, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_reference_space_create_info_and_get_next_pointer(int p_reference_space_type, void *p_next_pointer) {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_set_reference_space_create_info_and_get_next_pointer, p_reference_space_type, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_frame_wait_info_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_set_frame_wait_info_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_frame_end_info_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_set_frame_end_info_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
void *OpenXRExtensionWrapper::set_view_locate_info_and_get_next_pointer(void *p_next_pointer) {
uint64_t pointer = 0;
if (GDVIRTUAL_CALL(_set_view_locate_info_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
return reinterpret_cast<void *>(pointer);
}
return nullptr;
}
PackedStringArray OpenXRExtensionWrapper::get_suggested_tracker_names() {
PackedStringArray ret;
if (GDVIRTUAL_CALL(_get_suggested_tracker_names, ret)) {
return ret;
}
return PackedStringArray();
}
int OpenXRExtensionWrapper::get_composition_layer_count() {
int count = 0;
GDVIRTUAL_CALL(_get_composition_layer_count, count);
return count;
}
XrCompositionLayerBaseHeader *OpenXRExtensionWrapper::get_composition_layer(int p_index) {
uint64_t pointer;
if (GDVIRTUAL_CALL(_get_composition_layer, p_index, pointer)) {
return reinterpret_cast<XrCompositionLayerBaseHeader *>(pointer);
}
return nullptr;
}
int OpenXRExtensionWrapper::get_composition_layer_order(int p_index) {
int order = 0;
GDVIRTUAL_CALL(_get_composition_layer_order, p_index, order);
return order;
}
void OpenXRExtensionWrapper::on_register_metadata() {
GDVIRTUAL_CALL(_on_register_metadata);
}
void OpenXRExtensionWrapper::on_before_instance_created() {
GDVIRTUAL_CALL(_on_before_instance_created);
}
void OpenXRExtensionWrapper::on_instance_created(const XrInstance p_instance) {
uint64_t instance = (uint64_t)p_instance;
GDVIRTUAL_CALL(_on_instance_created, instance);
}
void OpenXRExtensionWrapper::on_instance_destroyed() {
GDVIRTUAL_CALL(_on_instance_destroyed);
}
void OpenXRExtensionWrapper::on_session_created(const XrSession p_session) {
uint64_t session = (uint64_t)p_session;
GDVIRTUAL_CALL(_on_session_created, session);
}
void OpenXRExtensionWrapper::on_process() {
GDVIRTUAL_CALL(_on_process);
}
void OpenXRExtensionWrapper::on_sync_actions() {
GDVIRTUAL_CALL(_on_sync_actions);
}
void OpenXRExtensionWrapper::on_pre_render() {
GDVIRTUAL_CALL(_on_pre_render);
}
void OpenXRExtensionWrapper::on_main_swapchains_created() {
GDVIRTUAL_CALL(_on_main_swapchains_created);
}
void OpenXRExtensionWrapper::on_session_destroyed() {
GDVIRTUAL_CALL(_on_session_destroyed);
}
void OpenXRExtensionWrapper::on_pre_draw_viewport(RID p_render_target) {
GDVIRTUAL_CALL(_on_pre_draw_viewport, p_render_target);
}
void OpenXRExtensionWrapper::on_post_draw_viewport(RID p_render_target) {
GDVIRTUAL_CALL(_on_post_draw_viewport, p_render_target);
}
void OpenXRExtensionWrapper::on_state_idle() {
GDVIRTUAL_CALL(_on_state_idle);
}
void OpenXRExtensionWrapper::on_state_ready() {
GDVIRTUAL_CALL(_on_state_ready);
}
void OpenXRExtensionWrapper::on_state_synchronized() {
GDVIRTUAL_CALL(_on_state_synchronized);
}
void OpenXRExtensionWrapper::on_state_visible() {
GDVIRTUAL_CALL(_on_state_visible);
}
void OpenXRExtensionWrapper::on_state_focused() {
GDVIRTUAL_CALL(_on_state_focused);
}
void OpenXRExtensionWrapper::on_state_stopping() {
GDVIRTUAL_CALL(_on_state_stopping);
}
void OpenXRExtensionWrapper::on_state_loss_pending() {
GDVIRTUAL_CALL(_on_state_loss_pending);
}
void OpenXRExtensionWrapper::on_state_exiting() {
GDVIRTUAL_CALL(_on_state_exiting);
}
bool OpenXRExtensionWrapper::on_event_polled(const XrEventDataBuffer &p_event) {
bool event_polled;
if (GDVIRTUAL_CALL(_on_event_polled, GDExtensionConstPtr<void>(&p_event), event_polled)) {
return event_polled;
}
return false;
}
void *OpenXRExtensionWrapper::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)) {
return reinterpret_cast<void *>(pointer);
}
return p_next_pointer;
}
void OpenXRExtensionWrapper::on_viewport_composition_layer_destroyed(const XrCompositionLayerBaseHeader *p_layer) {
GDVIRTUAL_CALL(_on_viewport_composition_layer_destroyed, GDExtensionConstPtr<void>(p_layer));
}
void OpenXRExtensionWrapper::get_viewport_composition_layer_extension_properties(List<PropertyInfo> *p_property_list) {
TypedArray<Dictionary> properties;
if (GDVIRTUAL_CALL(_get_viewport_composition_layer_extension_properties, properties)) {
for (int i = 0; i < properties.size(); i++) {
p_property_list->push_back(PropertyInfo::from_dict(properties[i]));
}
}
}
Dictionary OpenXRExtensionWrapper::get_viewport_composition_layer_extension_property_defaults() {
Dictionary property_defaults;
GDVIRTUAL_CALL(_get_viewport_composition_layer_extension_property_defaults, property_defaults);
return property_defaults;
}
void *OpenXRExtensionWrapper::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> OpenXRExtensionWrapper::_gdextension_get_openxr_api() {
static Ref<OpenXRAPIExtension> openxr_api_extension;
if (unlikely(openxr_api_extension.is_null())) {
openxr_api_extension.instantiate();
}
return openxr_api_extension;
}
void OpenXRExtensionWrapper::_gdextension_register_extension_wrapper() {
OpenXRAPI::register_extension_wrapper(this);
}

View File

@@ -0,0 +1,198 @@
/**************************************************************************/
/* openxr_extension_wrapper.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. */
/**************************************************************************/
#pragma once
#include "core/error/error_macros.h"
#include "core/math/projection.h"
#include "core/object/class_db.h"
#include "core/object/gdvirtual.gen.inc"
#include "core/templates/hash_map.h"
#include "core/templates/rid.h"
#include "core/variant/native_ptr.h"
#include "core/variant/typed_array.h"
#include "core/variant/variant.h"
#include <openxr/openxr.h>
class OpenXRAPI;
class OpenXRAPIExtension;
class OpenXRActionMap;
// `OpenXRExtensionWrapper` allows us to implement OpenXR extensions.
class OpenXRExtensionWrapper : public Object {
GDCLASS(OpenXRExtensionWrapper, Object);
Ref<OpenXRAPIExtension> _gdextension_get_openxr_api();
void _gdextension_register_extension_wrapper();
protected:
static void _bind_methods();
public:
// `get_requested_extensions` should return a list of OpenXR extensions related to this extension.
// If the bool * is a nullptr this extension is mandatory
// If the bool * points to a boolean, the boolean will be updated
// to true if the extension is enabled.
virtual HashMap<String, bool *> get_requested_extensions();
GDVIRTUAL0R(Dictionary, _get_requested_extensions);
// These functions allow an extension to add entries to a struct chain.
// `p_next_pointer` points to the last struct that was created for this chain
// and should be used as the value for the `pNext` pointer in the first struct you add.
// You should return the pointer to the last struct you define as your result.
// If you are not adding any structs, just return `p_next_pointer`.
// See existing extensions for examples of this implementation.
virtual void *set_system_properties_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when we interrogate OpenXRS system abilities.
virtual void *set_instance_create_info_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when we create our OpenXR instance.
virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when we create our OpenXR session.
virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when creating OpenXR swap chains.
virtual void *set_hand_joint_locations_and_get_next_pointer(int p_hand_index, void *p_next_pointer);
virtual void *set_projection_views_and_get_next_pointer(int p_view_index, void *p_next_pointer);
virtual void *set_reference_space_create_info_and_get_next_pointer(int p_reference_space_type, void *p_next_pointer);
// These will only be called for extensions registered via OpenXRApi::register_frame_info_extension().
virtual void *set_frame_wait_info_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when calling xrWaitFrame
virtual void *set_view_locate_info_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when calling xrLocateViews
virtual void *set_frame_end_info_and_get_next_pointer(void *p_next_pointer); // Add additional data structures when calling xrEndFrame
//TODO workaround as GDExtensionPtr<void> return type results in build error in godot-cpp
GDVIRTUAL1R(uint64_t, _set_system_properties_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL1R(uint64_t, _set_instance_create_info_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL1R(uint64_t, _set_session_create_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL1R(uint64_t, _set_swapchain_create_info_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL2R(uint64_t, _set_hand_joint_locations_and_get_next_pointer, int, GDExtensionPtr<void>);
GDVIRTUAL2R(uint64_t, _set_projection_views_and_get_next_pointer, int, GDExtensionPtr<void>);
GDVIRTUAL1R(uint64_t, _set_frame_wait_info_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL1R(uint64_t, _set_frame_end_info_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL1R(uint64_t, _set_view_locate_info_and_get_next_pointer, GDExtensionPtr<void>);
GDVIRTUAL2R(uint64_t, _set_reference_space_create_info_and_get_next_pointer, int, GDExtensionPtr<void>);
GDVIRTUAL0R(int, _get_composition_layer_count);
GDVIRTUAL1R(uint64_t, _get_composition_layer, int);
GDVIRTUAL1R(int, _get_composition_layer_order, int);
virtual PackedStringArray get_suggested_tracker_names();
GDVIRTUAL0R(PackedStringArray, _get_suggested_tracker_names);
// `on_register_metadata` allows extensions to register additional controller metadata.
// This function is called even when OpenXRApi is not constructured as the metadata
// needs to be available to the editor.
// Also extensions should provide metadata regardless of whether they are supported
// on the host system as the controller data is used to setup action maps for users
// who may have access to the relevant hardware.
virtual void on_register_metadata();
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_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,
// this happens right before physics process and normal processing is run.
// This is when controller data is queried and made available to game logic.
virtual void on_process();
virtual void on_sync_actions(); // `on_sync_actions` is called right after we sync our action sets.
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)
GDVIRTUAL0(_on_register_metadata);
GDVIRTUAL0(_on_before_instance_created);
GDVIRTUAL1(_on_instance_created, uint64_t);
GDVIRTUAL0(_on_instance_destroyed);
GDVIRTUAL1(_on_session_created, uint64_t);
GDVIRTUAL0(_on_process);
GDVIRTUAL0(_on_sync_actions);
GDVIRTUAL0(_on_pre_render);
GDVIRTUAL0(_on_main_swapchains_created);
GDVIRTUAL0(_on_session_destroyed);
GDVIRTUAL1(_on_pre_draw_viewport, RID);
GDVIRTUAL1(_on_post_draw_viewport, RID);
virtual void on_state_idle(); // `on_state_idle` is called when the OpenXR session state is changed to idle.
virtual void on_state_ready(); // `on_state_ready` is called when the OpenXR session state is changed to ready, this means OpenXR is ready to setup our session.
virtual void on_state_synchronized(); // `on_state_synchronized` is called when the OpenXR session state is changed to synchronized, note that OpenXR also returns to this state when our application looses focus.
virtual void on_state_visible(); // `on_state_visible` is called when the OpenXR session state is changed to visible, OpenXR is now ready to receive frames.
virtual void on_state_focused(); // `on_state_focused` is called when the OpenXR session state is changed to focused, this state is the active state when our game runs.
virtual void on_state_stopping(); // `on_state_stopping` is called when the OpenXR session state is changed to stopping.
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.
GDVIRTUAL0(_on_state_idle);
GDVIRTUAL0(_on_state_ready);
GDVIRTUAL0(_on_state_synchronized);
GDVIRTUAL0(_on_state_visible);
GDVIRTUAL0(_on_state_focused);
GDVIRTUAL0(_on_state_stopping);
GDVIRTUAL0(_on_state_loss_pending);
GDVIRTUAL0(_on_state_exiting);
// These will only be called on extensions registered via OpenXRAPI::register_composition_layer_provider().
virtual int get_composition_layer_count();
virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index);
virtual int get_composition_layer_order(int p_index);
virtual void *set_viewport_composition_layer_and_get_next_pointer(const XrCompositionLayerBaseHeader *p_layer, const Dictionary &p_property_values, void *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(); // 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);
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>);
// `on_event_polled` is called when there is an OpenXR event to process.
// Should return true if the event was handled, false otherwise.
virtual bool on_event_polled(const XrEventDataBuffer &event);
GDVIRTUAL1R(bool, _on_event_polled, GDExtensionConstPtr<void>);
OpenXRExtensionWrapper() = default;
virtual ~OpenXRExtensionWrapper() = default;
};
// `OpenXRGraphicsExtensionWrapper` implements specific logic for each supported graphics API.
class OpenXRGraphicsExtensionWrapper : public OpenXRExtensionWrapper {
public:
virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) = 0; // `get_usable_swapchain_formats` should return a list of usable color formats.
virtual void get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) = 0; // `get_usable_depth_formats` should return a list of usable depth formats.
virtual String get_swapchain_format_name(int64_t p_swapchain_format) const = 0; // `get_swapchain_format_name` should return the constant name of a given format.
virtual bool 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) = 0; // `get_swapchain_image_data` extracts image IDs for the swapchain images and stores there in an implementation dependent data structure.
virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) = 0; // `cleanup_swapchain_graphics_data` cleans up the data held in our implementation dependent data structure and should free up its memory.
virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) = 0; // `create_projection_fov` creates a proper projection matrix based on asymmetric FOV data provided by OpenXR.
virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) = 0; // `get_texture` returns a Godot texture RID for the current active texture in our swapchain.
virtual RID get_density_map(void *p_swapchain_graphics_data, int p_image_index) = 0; // `get_density_map` returns a Godot texture RID for the current active density map in our swapchain (if any).
};

View File

@@ -0,0 +1,37 @@
/**************************************************************************/
/* openxr_extension_wrapper_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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRExtensionWrapperExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRExtensionWrapperExtension, OpenXRExtensionWrapper);
};

View File

@@ -0,0 +1,144 @@
/**************************************************************************/
/* openxr_eye_gaze_interaction.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_eye_gaze_interaction.h"
#include "core/config/project_settings.h"
#include "core/os/os.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
#include "../openxr_api.h"
OpenXREyeGazeInteractionExtension *OpenXREyeGazeInteractionExtension::singleton = nullptr;
OpenXREyeGazeInteractionExtension *OpenXREyeGazeInteractionExtension::get_singleton() {
ERR_FAIL_NULL_V(singleton, nullptr);
return singleton;
}
OpenXREyeGazeInteractionExtension::OpenXREyeGazeInteractionExtension() {
singleton = this;
}
OpenXREyeGazeInteractionExtension::~OpenXREyeGazeInteractionExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXREyeGazeInteractionExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
// Only enable this extension when requested.
// We still register our meta data or the action map editor will fail.
if (GLOBAL_GET_CACHED(bool, "xr/openxr/extensions/eye_gaze_interaction") && (!OS::get_singleton()->has_feature("mobile") || OS::get_singleton()->has_feature(XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME))) {
request_extensions[XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME] = &available;
}
return request_extensions;
}
void *OpenXREyeGazeInteractionExtension::set_system_properties_and_get_next_pointer(void *p_next_pointer) {
if (!available) {
return p_next_pointer;
}
properties.type = XR_TYPE_SYSTEM_EYE_GAZE_INTERACTION_PROPERTIES_EXT;
properties.next = p_next_pointer;
properties.supportsEyeGazeInteraction = false;
return &properties;
}
PackedStringArray OpenXREyeGazeInteractionExtension::get_suggested_tracker_names() {
PackedStringArray arr = { "/user/eyes_ext" };
return arr;
}
bool OpenXREyeGazeInteractionExtension::is_available() {
return available;
}
bool OpenXREyeGazeInteractionExtension::supports_eye_gaze_interaction() {
// The extension being available only means that the OpenXR Runtime supports the extension.
// The `supportsEyeGazeInteraction` is set to true if the device also supports this.
// Thus both need to be true.
// In addition, on mobile runtimes, the proper permission needs to be granted.
if (available && properties.supportsEyeGazeInteraction) {
return !OS::get_singleton()->has_feature("mobile") || OS::get_singleton()->has_feature("PERMISSION_XR_EXT_eye_gaze_interaction");
}
return false;
}
void OpenXREyeGazeInteractionExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
// Eyes top path
openxr_metadata->register_top_level_path("Eye gaze tracker", "/user/eyes_ext", XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME);
// Eye gaze interaction
openxr_metadata->register_interaction_profile("Eye gaze", "/interaction_profiles/ext/eye_gaze_interaction", XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_io_path("/interaction_profiles/ext/eye_gaze_interaction", "Gaze pose", "/user/eyes_ext", "/user/eyes_ext/input/gaze_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
}
bool OpenXREyeGazeInteractionExtension::get_eye_gaze_pose(double p_dist, Vector3 &r_eye_pose) {
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, false);
if (!init_eye_gaze_pose) {
init_eye_gaze_pose = true;
eye_tracker = openxr_api->find_tracker("/user/eyes_ext");
if (eye_tracker.is_null()) {
WARN_PRINT("Couldn't obtain eye tracker");
}
eye_action = openxr_api->find_action("eye_gaze_pose");
if (eye_action.is_null()) {
WARN_PRINT("Couldn't obtain pose action for `eye_gaze_pose`, make sure to add this to your action map.");
}
}
if (eye_tracker.is_null() || eye_action.is_null()) {
return false;
}
Transform3D eye_transform;
Vector3 linear_velocity;
Vector3 angular_velocity;
XRPose::TrackingConfidence confidence = openxr_api->get_action_pose(eye_action, eye_tracker, eye_transform, linear_velocity, angular_velocity);
if (confidence == XRPose::XR_TRACKING_CONFIDENCE_NONE) {
return false;
}
r_eye_pose = eye_transform.origin + eye_transform.basis[2] * p_dist;
return true;
}

View File

@@ -0,0 +1,68 @@
/**************************************************************************/
/* openxr_eye_gaze_interaction.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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXREyeGazeInteractionExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXREyeGazeInteractionExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXREyeGazeInteractionExtension *get_singleton();
OpenXREyeGazeInteractionExtension();
~OpenXREyeGazeInteractionExtension();
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void *set_system_properties_and_get_next_pointer(void *p_next_pointer) override;
PackedStringArray get_suggested_tracker_names() override;
bool is_available();
bool supports_eye_gaze_interaction();
virtual void on_register_metadata() override;
bool get_eye_gaze_pose(double p_dist, Vector3 &r_eye_pose);
private:
static OpenXREyeGazeInteractionExtension *singleton;
bool available = false;
XrSystemEyeGazeInteractionPropertiesEXT properties;
bool init_eye_gaze_pose = false;
RID eye_tracker;
RID eye_action;
};

View File

@@ -0,0 +1,145 @@
/**************************************************************************/
/* openxr_fb_display_refresh_rate_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_fb_display_refresh_rate_extension.h"
#include "../openxr_interface.h"
OpenXRDisplayRefreshRateExtension *OpenXRDisplayRefreshRateExtension::singleton = nullptr;
OpenXRDisplayRefreshRateExtension *OpenXRDisplayRefreshRateExtension::get_singleton() {
return singleton;
}
OpenXRDisplayRefreshRateExtension::OpenXRDisplayRefreshRateExtension() {
singleton = this;
}
OpenXRDisplayRefreshRateExtension::~OpenXRDisplayRefreshRateExtension() {
display_refresh_rate_ext = false;
}
HashMap<String, bool *> OpenXRDisplayRefreshRateExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME] = &display_refresh_rate_ext;
return request_extensions;
}
void OpenXRDisplayRefreshRateExtension::on_instance_created(const XrInstance p_instance) {
if (display_refresh_rate_ext) {
EXT_INIT_XR_FUNC(xrEnumerateDisplayRefreshRatesFB);
EXT_INIT_XR_FUNC(xrGetDisplayRefreshRateFB);
EXT_INIT_XR_FUNC(xrRequestDisplayRefreshRateFB);
}
}
void OpenXRDisplayRefreshRateExtension::on_instance_destroyed() {
display_refresh_rate_ext = false;
}
bool OpenXRDisplayRefreshRateExtension::on_event_polled(const XrEventDataBuffer &event) {
switch (event.type) {
case XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB: {
const XrEventDataDisplayRefreshRateChangedFB *event_fb = (XrEventDataDisplayRefreshRateChangedFB *)&event;
OpenXRInterface *xr_interface = OpenXRAPI::get_singleton()->get_xr_interface();
if (xr_interface) {
xr_interface->on_refresh_rate_changes(event_fb->toDisplayRefreshRate);
}
return true;
} break;
default:
return false;
}
}
float OpenXRDisplayRefreshRateExtension::get_refresh_rate() const {
float refresh_rate = 0.0;
if (display_refresh_rate_ext) {
float rate;
XrResult result = xrGetDisplayRefreshRateFB(OpenXRAPI::get_singleton()->get_session(), &rate);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to obtain refresh rate [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
} else {
refresh_rate = rate;
}
}
return refresh_rate;
}
void OpenXRDisplayRefreshRateExtension::set_refresh_rate(float p_refresh_rate) {
if (display_refresh_rate_ext) {
XrResult result = xrRequestDisplayRefreshRateFB(OpenXRAPI::get_singleton()->get_session(), p_refresh_rate);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to set refresh rate [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
}
}
}
Array OpenXRDisplayRefreshRateExtension::get_available_refresh_rates() const {
Array arr;
XrResult result;
if (display_refresh_rate_ext) {
uint32_t display_refresh_rate_count = 0;
result = xrEnumerateDisplayRefreshRatesFB(OpenXRAPI::get_singleton()->get_session(), 0, &display_refresh_rate_count, nullptr);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to obtain refresh rates count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
}
if (display_refresh_rate_count > 0) {
float *display_refresh_rates = (float *)memalloc(sizeof(float) * display_refresh_rate_count);
if (display_refresh_rates == nullptr) {
print_line("OpenXR: Failed to obtain refresh rates memory buffer [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return arr;
}
result = xrEnumerateDisplayRefreshRatesFB(OpenXRAPI::get_singleton()->get_session(), display_refresh_rate_count, &display_refresh_rate_count, display_refresh_rates);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to obtain refresh rates count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
memfree(display_refresh_rates);
return arr;
}
for (uint32_t i = 0; i < display_refresh_rate_count; i++) {
float refresh_rate = display_refresh_rates[i];
arr.push_back(Variant(refresh_rate));
}
memfree(display_refresh_rates);
}
}
return arr;
}

View File

@@ -0,0 +1,74 @@
/**************************************************************************/
/* openxr_fb_display_refresh_rate_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. */
/**************************************************************************/
#pragma once
// This extension gives us access to the possible display refresh rates
// supported by the HMD.
// While this is an FB extension it has been adopted by most runtimes and
// will likely become core in the near future.
#include "../openxr_api.h"
#include "../util.h"
#include "openxr_extension_wrapper.h"
class OpenXRDisplayRefreshRateExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRDisplayRefreshRateExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRDisplayRefreshRateExtension *get_singleton();
OpenXRDisplayRefreshRateExtension();
virtual ~OpenXRDisplayRefreshRateExtension() 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;
virtual bool on_event_polled(const XrEventDataBuffer &event) override;
float get_refresh_rate() const;
void set_refresh_rate(float p_refresh_rate);
Array get_available_refresh_rates() const;
private:
static OpenXRDisplayRefreshRateExtension *singleton;
bool display_refresh_rate_ext = false;
// OpenXR API call wrappers
EXT_PROTO_XRRESULT_FUNC4(xrEnumerateDisplayRefreshRatesFB, (XrSession), session, (uint32_t), displayRefreshRateCapacityInput, (uint32_t *), displayRefreshRateCountOutput, (float *), displayRefreshRates);
EXT_PROTO_XRRESULT_FUNC2(xrGetDisplayRefreshRateFB, (XrSession), session, (float *), display_refresh_rate);
EXT_PROTO_XRRESULT_FUNC2(xrRequestDisplayRefreshRateFB, (XrSession), session, (float), display_refresh_rate);
};

View File

@@ -0,0 +1,198 @@
/**************************************************************************/
/* openxr_fb_foveation_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_fb_foveation_extension.h"
#include "core/config/project_settings.h"
#include "../openxr_platform_inc.h"
OpenXRFBFoveationExtension *OpenXRFBFoveationExtension::singleton = nullptr;
OpenXRFBFoveationExtension *OpenXRFBFoveationExtension::get_singleton() {
return singleton;
}
OpenXRFBFoveationExtension::OpenXRFBFoveationExtension(const String &p_rendering_driver) {
singleton = this;
rendering_driver = p_rendering_driver;
swapchain_update_state_ext = OpenXRFBUpdateSwapchainExtension::get_singleton();
int fov_level = GLOBAL_GET("xr/openxr/foveation_level");
if (fov_level >= 0 && fov_level < 4) {
foveation_level = XrFoveationLevelFB(fov_level);
}
bool fov_dyn = GLOBAL_GET("xr/openxr/foveation_dynamic");
foveation_dynamic = fov_dyn ? XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB : XR_FOVEATION_DYNAMIC_DISABLED_FB;
swapchain_create_info_foveation_fb.type = XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB;
swapchain_create_info_foveation_fb.next = nullptr;
swapchain_create_info_foveation_fb.flags = 0;
if (rendering_driver == "opengl3") {
swapchain_create_info_foveation_fb.flags = XR_SWAPCHAIN_CREATE_FOVEATION_SCALED_BIN_BIT_FB;
} else if (rendering_driver == "vulkan") {
swapchain_create_info_foveation_fb.flags = XR_SWAPCHAIN_CREATE_FOVEATION_FRAGMENT_DENSITY_MAP_BIT_FB;
}
}
OpenXRFBFoveationExtension::~OpenXRFBFoveationExtension() {
singleton = nullptr;
swapchain_update_state_ext = nullptr;
}
HashMap<String, bool *> OpenXRFBFoveationExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_FB_FOVEATION_EXTENSION_NAME] = &fb_foveation_ext;
request_extensions[XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME] = &fb_foveation_configuration_ext;
#ifdef XR_USE_GRAPHICS_API_VULKAN
if (rendering_driver == "vulkan") {
request_extensions[XR_FB_FOVEATION_VULKAN_EXTENSION_NAME] = &fb_foveation_vulkan_ext;
}
#endif // XR_USE_GRAPHICS_API_VULKAN
return request_extensions;
}
void OpenXRFBFoveationExtension::on_instance_created(const XrInstance p_instance) {
if (fb_foveation_ext) {
EXT_INIT_XR_FUNC(xrCreateFoveationProfileFB);
EXT_INIT_XR_FUNC(xrDestroyFoveationProfileFB);
}
if (fb_foveation_configuration_ext) {
// nothing to register here...
}
}
void OpenXRFBFoveationExtension::on_instance_destroyed() {
fb_foveation_ext = false;
fb_foveation_configuration_ext = false;
}
bool OpenXRFBFoveationExtension::is_enabled() const {
bool enabled = swapchain_update_state_ext != nullptr && swapchain_update_state_ext->is_enabled() && fb_foveation_ext && fb_foveation_configuration_ext;
#ifdef XR_USE_GRAPHICS_API_VULKAN
if (rendering_driver == "vulkan") {
enabled = enabled && fb_foveation_vulkan_ext;
}
#endif // XR_USE_GRAPHICS_API_VULKAN
return enabled;
}
void *OpenXRFBFoveationExtension::set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) {
if (is_enabled()) {
swapchain_create_info_foveation_fb.next = p_next_pointer;
return &swapchain_create_info_foveation_fb;
} else {
return p_next_pointer;
}
}
void OpenXRFBFoveationExtension::on_main_swapchains_created() {
update_profile();
}
XrFoveationLevelFB OpenXRFBFoveationExtension::get_foveation_level() const {
return foveation_level;
}
void OpenXRFBFoveationExtension::set_foveation_level(XrFoveationLevelFB p_foveation_level) {
foveation_level = p_foveation_level;
// Update profile will do nothing if we're not yet initialized.
update_profile();
}
XrFoveationDynamicFB OpenXRFBFoveationExtension::get_foveation_dynamic() const {
return foveation_dynamic;
}
void OpenXRFBFoveationExtension::set_foveation_dynamic(XrFoveationDynamicFB p_foveation_dynamic) {
foveation_dynamic = p_foveation_dynamic;
// Update profile will do nothing if we're not yet initialized.
update_profile();
}
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 = fov_ext->foveation_level;
level_profile_create_info.verticalOffset = 0.0f;
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 = 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 [", openxr_api->get_error_string(result), "]");
return;
}
XrSwapchainStateFoveationFB foveation_update_state;
foveation_update_state.type = XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB;
foveation_update_state.profile = foveation_profile;
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 [", openxr_api->get_error_string(result), "]");
// We still want to destroy our profile so keep going...
}
result = fov_ext->xrDestroyFoveationProfileFB(foveation_profile);
if (XR_FAILED(result)) {
print_line("OpenXR: Unable to destroy the foveation profile [", openxr_api->get_error_string(result), "]");
}
}

View File

@@ -0,0 +1,102 @@
/**************************************************************************/
/* openxr_fb_foveation_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. */
/**************************************************************************/
#pragma once
// This extension implements the FB Foveation extension.
// This is an extension Meta added due to VRS being unavailable on Android.
// Other Android based devices are implementing this as well, see:
// https://github.khronos.org/OpenXR-Inventory/extension_support.html#XR_FB_foveation
#include "../openxr_api.h"
#include "../util.h"
#include "openxr_extension_wrapper.h"
#include "openxr_fb_update_swapchain_extension.h"
class OpenXRFBFoveationExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRFBFoveationExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRFBFoveationExtension *get_singleton();
OpenXRFBFoveationExtension(const String &p_rendering_driver);
virtual ~OpenXRFBFoveationExtension() 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;
virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override;
virtual void on_main_swapchains_created() override;
bool is_enabled() const;
XrFoveationLevelFB get_foveation_level() const;
void set_foveation_level(XrFoveationLevelFB p_foveation_level);
XrFoveationDynamicFB get_foveation_dynamic() const;
void set_foveation_dynamic(XrFoveationDynamicFB p_foveation_dynamic);
private:
static OpenXRFBFoveationExtension *singleton;
// Setup
String rendering_driver;
bool fb_foveation_ext = false;
bool fb_foveation_configuration_ext = false;
bool fb_foveation_vulkan_ext = false;
// Configuration
XrFoveationLevelFB foveation_level = XR_FOVEATION_LEVEL_NONE_FB;
XrFoveationDynamicFB foveation_dynamic = XR_FOVEATION_DYNAMIC_DISABLED_FB;
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;
OpenXRFBUpdateSwapchainExtension *swapchain_update_state_ext = nullptr;
// OpenXR API call wrappers
EXT_PROTO_XRRESULT_FUNC3(xrCreateFoveationProfileFB, (XrSession), session, (const XrFoveationProfileCreateInfoFB *), create_info, (XrFoveationProfileFB *), profile);
EXT_PROTO_XRRESULT_FUNC1(xrDestroyFoveationProfileFB, (XrFoveationProfileFB), profile);
};

View File

@@ -0,0 +1,347 @@
/**************************************************************************/
/* openxr_fb_update_swapchain_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_fb_update_swapchain_extension.h"
// Always include this as late as possible.
#include "../openxr_platform_inc.h"
#ifndef GL_CUBIC_IMG
#define GL_CUBIC_IMG 0x9139
#endif
#ifndef GL_CUBIC_MIPMAP_LINEAR_IMG
#define GL_CUBIC_MIPMAP_LINEAR_IMG 0x913B
#endif
#ifndef GL_CUBIC_MIPMAP_NEAREST_IMG
#define GL_CUBIC_MIPMAP_NEAREST_IMG 0x913A
#endif
#ifndef GL_CLAMP_TO_BORDER
#define GL_CLAMP_TO_BORDER 0x812D
#endif
OpenXRFBUpdateSwapchainExtension *OpenXRFBUpdateSwapchainExtension::singleton = nullptr;
OpenXRFBUpdateSwapchainExtension *OpenXRFBUpdateSwapchainExtension::get_singleton() {
return singleton;
}
OpenXRFBUpdateSwapchainExtension::OpenXRFBUpdateSwapchainExtension(const String &p_rendering_driver) {
singleton = this;
rendering_driver = p_rendering_driver;
}
OpenXRFBUpdateSwapchainExtension::~OpenXRFBUpdateSwapchainExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRFBUpdateSwapchainExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME] = &fb_swapchain_update_state_ext;
if (rendering_driver == "vulkan") {
#ifdef XR_USE_GRAPHICS_API_VULKAN
request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME] = &fb_swapchain_update_state_vulkan_ext;
#endif
} else if (rendering_driver == "opengl3") {
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME] = &fb_swapchain_update_state_opengles_ext;
#endif
}
#ifdef ANDROID_ENABLED
request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_ANDROID_SURFACE_EXTENSION_NAME] = &fb_swapchain_update_state_android_ext;
#endif
return request_extensions;
}
void OpenXRFBUpdateSwapchainExtension::on_instance_created(const XrInstance p_instance) {
if (fb_swapchain_update_state_ext) {
EXT_INIT_XR_FUNC(xrUpdateSwapchainFB);
EXT_INIT_XR_FUNC(xrGetSwapchainStateFB);
}
if (fb_swapchain_update_state_vulkan_ext) {
// nothing to register here...
}
if (fb_swapchain_update_state_opengles_ext) {
// nothing to register here...
}
}
void OpenXRFBUpdateSwapchainExtension::on_instance_destroyed() {
fb_swapchain_update_state_ext = false;
fb_swapchain_update_state_vulkan_ext = false;
fb_swapchain_update_state_opengles_ext = false;
}
bool OpenXRFBUpdateSwapchainExtension::is_enabled() const {
if (rendering_driver == "vulkan") {
return fb_swapchain_update_state_ext && fb_swapchain_update_state_vulkan_ext;
} else if (rendering_driver == "opengl3") {
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
return fb_swapchain_update_state_ext && fb_swapchain_update_state_opengles_ext;
#else
return fb_swapchain_update_state_ext;
#endif
}
return false;
}
bool OpenXRFBUpdateSwapchainExtension::is_android_ext_enabled() const {
return fb_swapchain_update_state_android_ext;
}
void OpenXRFBUpdateSwapchainExtension::update_swapchain_state(XrSwapchain p_swapchain, const OpenXRViewportCompositionLayerProvider::SwapchainState *p_swapchain_state) {
if (!p_swapchain_state) {
return;
}
if (rendering_driver == "vulkan") {
#ifdef XR_USE_GRAPHICS_API_VULKAN
if (!fb_swapchain_update_state_ext || !fb_swapchain_update_state_vulkan_ext) {
return;
}
Color border_color = p_swapchain_state->border_color;
XrSwapchainStateSamplerVulkanFB swapchain_state = {
XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB, // type
nullptr, // next
(VkFilter)filter_to_vk(p_swapchain_state->min_filter), // minFilter
(VkFilter)filter_to_vk(p_swapchain_state->mag_filter), // magFilter
(VkSamplerMipmapMode)mipmap_mode_to_vk(p_swapchain_state->mipmap_mode), // mipmapMode
(VkSamplerAddressMode)wrap_to_vk(p_swapchain_state->horizontal_wrap), // wrapModeS;
(VkSamplerAddressMode)wrap_to_vk(p_swapchain_state->vertical_wrap), // wrapModeT
(VkComponentSwizzle)swizzle_to_vk(p_swapchain_state->red_swizzle), // swizzleRed
(VkComponentSwizzle)swizzle_to_vk(p_swapchain_state->green_swizzle), // swizzleGreen
(VkComponentSwizzle)swizzle_to_vk(p_swapchain_state->blue_swizzle), // swizzleBlue
(VkComponentSwizzle)swizzle_to_vk(p_swapchain_state->alpha_swizzle), // swizzleAlpha
p_swapchain_state->max_anisotropy, // maxAnisotropy
{ border_color.r, border_color.g, border_color.b, border_color.a } // borderColor
};
XrResult result = xrUpdateSwapchainFB(p_swapchain, (XrSwapchainStateBaseHeaderFB *)&swapchain_state);
if (XR_FAILED(result)) {
print_error(vformat("OpenXR: Failed to update swapchain [%s]", OpenXRAPI::get_singleton()->get_error_string(result)));
return;
}
#endif
} else if (rendering_driver == "opengl3") {
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
if (!fb_swapchain_update_state_ext || !fb_swapchain_update_state_opengles_ext) {
return;
}
Color border_color = p_swapchain_state->border_color;
XrSwapchainStateSamplerOpenGLESFB swapchain_state = {
XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB, // type
nullptr, // next
filter_to_gl(p_swapchain_state->min_filter, p_swapchain_state->mipmap_mode), // minFilter
filter_to_gl(p_swapchain_state->mag_filter), // magFilter
wrap_to_gl(p_swapchain_state->horizontal_wrap), // wrapModeS;
wrap_to_gl(p_swapchain_state->vertical_wrap), // wrapModeT
swizzle_to_gl(p_swapchain_state->red_swizzle), // swizzleRed
swizzle_to_gl(p_swapchain_state->green_swizzle), // swizzleGreen
swizzle_to_gl(p_swapchain_state->blue_swizzle), // swizzleBlue
swizzle_to_gl(p_swapchain_state->alpha_swizzle), // swizzleAlpha
p_swapchain_state->max_anisotropy, // maxAnisotropy
{ border_color.r, border_color.g, border_color.b, border_color.a } // borderColor
};
XrResult result = xrUpdateSwapchainFB(p_swapchain, (XrSwapchainStateBaseHeaderFB *)&swapchain_state);
if (XR_FAILED(result)) {
print_error(vformat("OpenXR: Failed to update swapchain [%s]", OpenXRAPI::get_singleton()->get_error_string(result)));
return;
}
#endif
}
}
void OpenXRFBUpdateSwapchainExtension::update_swapchain_surface_size(XrSwapchain p_swapchain, const Size2i &p_size) {
#ifdef ANDROID_ENABLED
if (!fb_swapchain_update_state_ext || !fb_swapchain_update_state_android_ext) {
return;
}
XrSwapchainStateAndroidSurfaceDimensionsFB swapchain_state = {
XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB, // type
nullptr, // next
(uint32_t)p_size.width, // width
(uint32_t)p_size.height // height
};
XrResult result = xrUpdateSwapchainFB(p_swapchain, (XrSwapchainStateBaseHeaderFB *)&swapchain_state);
if (XR_FAILED(result)) {
print_error(vformat("OpenXR: Failed to update swapchain surface size [%s]", OpenXRAPI::get_singleton()->get_error_string(result)));
}
#endif
}
uint32_t OpenXRFBUpdateSwapchainExtension::filter_to_gl(OpenXRViewportCompositionLayerProvider::Filter p_filter, OpenXRViewportCompositionLayerProvider::MipmapMode p_mipmap_mode) {
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
switch (p_mipmap_mode) {
case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_DISABLED:
switch (p_filter) {
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_NEAREST:
return GL_NEAREST;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_LINEAR:
return GL_LINEAR;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_CUBIC:
return GL_CUBIC_IMG;
}
case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_NEAREST:
switch (p_filter) {
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_NEAREST:
return GL_NEAREST_MIPMAP_NEAREST;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_LINEAR:
return GL_LINEAR_MIPMAP_NEAREST;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_CUBIC:
return GL_CUBIC_MIPMAP_NEAREST_IMG;
}
case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_LINEAR:
switch (p_filter) {
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_NEAREST:
return GL_NEAREST_MIPMAP_LINEAR;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_LINEAR:
return GL_LINEAR_MIPMAP_LINEAR;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_CUBIC:
return GL_CUBIC_MIPMAP_LINEAR_IMG;
}
}
#endif
return 0;
}
uint32_t OpenXRFBUpdateSwapchainExtension::wrap_to_gl(OpenXRViewportCompositionLayerProvider::Wrap p_wrap) {
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
switch (p_wrap) {
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_CLAMP_TO_BORDER:
return GL_CLAMP_TO_BORDER;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_CLAMP_TO_EDGE:
return GL_CLAMP_TO_EDGE;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_REPEAT:
return GL_REPEAT;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_MIRRORED_REPEAT:
return GL_MIRRORED_REPEAT;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_MIRROR_CLAMP_TO_EDGE:
return GL_CLAMP_TO_EDGE;
}
#endif
return 0;
}
uint32_t OpenXRFBUpdateSwapchainExtension::swizzle_to_gl(OpenXRViewportCompositionLayerProvider::Swizzle p_swizzle) {
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
switch (p_swizzle) {
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_RED:
return GL_RED;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_GREEN:
return GL_GREEN;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_BLUE:
return GL_BLUE;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ALPHA:
return GL_ALPHA;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ZERO:
return GL_ZERO;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ONE:
return GL_ONE;
}
#endif
return 0;
}
uint32_t OpenXRFBUpdateSwapchainExtension::filter_to_vk(OpenXRViewportCompositionLayerProvider::Filter p_filter) {
#ifdef XR_USE_GRAPHICS_API_VULKAN
switch (p_filter) {
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_NEAREST:
return VK_FILTER_NEAREST;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_LINEAR:
return VK_FILTER_LINEAR;
case OpenXRViewportCompositionLayerProvider::Filter::FILTER_CUBIC:
return VK_FILTER_CUBIC_EXT;
}
#endif
return 0;
}
uint32_t OpenXRFBUpdateSwapchainExtension::mipmap_mode_to_vk(OpenXRViewportCompositionLayerProvider::MipmapMode p_mipmap_mode) {
#ifdef XR_USE_GRAPHICS_API_VULKAN
switch (p_mipmap_mode) {
case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_DISABLED:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_LINEAR:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
}
#endif
return 0;
}
uint32_t OpenXRFBUpdateSwapchainExtension::wrap_to_vk(OpenXRViewportCompositionLayerProvider::Wrap p_wrap) {
#ifdef XR_USE_GRAPHICS_API_VULKAN
switch (p_wrap) {
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_CLAMP_TO_BORDER:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_CLAMP_TO_EDGE:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_REPEAT:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_MIRRORED_REPEAT:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
case OpenXRViewportCompositionLayerProvider::Wrap::WRAP_MIRROR_CLAMP_TO_EDGE:
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
}
#endif
return 0;
}
uint32_t OpenXRFBUpdateSwapchainExtension::swizzle_to_vk(OpenXRViewportCompositionLayerProvider::Swizzle p_swizzle) {
#ifdef XR_USE_GRAPHICS_API_VULKAN
switch (p_swizzle) {
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_RED:
return VK_COMPONENT_SWIZZLE_R;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_GREEN:
return VK_COMPONENT_SWIZZLE_G;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_BLUE:
return VK_COMPONENT_SWIZZLE_B;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ALPHA:
return VK_COMPONENT_SWIZZLE_A;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ZERO:
return VK_COMPONENT_SWIZZLE_ZERO;
case OpenXRViewportCompositionLayerProvider::Swizzle::SWIZZLE_ONE:
return VK_COMPONENT_SWIZZLE_ONE;
}
#endif
return 0;
}

View File

@@ -0,0 +1,91 @@
/**************************************************************************/
/* openxr_fb_update_swapchain_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. */
/**************************************************************************/
#pragma once
// This extension implements the FB update swapchain extension.
// This is an extension Meta added to further configure the swapchain.
// Other Android based devices are implementing this as well, see:
// https://github.khronos.org/OpenXR-Inventory/extension_support.html#XR_FB_swapchain_update_state
#include "../openxr_api.h"
#include "../util.h"
#include "openxr_composition_layer_extension.h"
#include "openxr_extension_wrapper.h"
class OpenXRFBUpdateSwapchainExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRFBUpdateSwapchainExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
friend class OpenXRFBFoveationExtension;
public:
static OpenXRFBUpdateSwapchainExtension *get_singleton();
OpenXRFBUpdateSwapchainExtension(const String &p_rendering_driver);
virtual ~OpenXRFBUpdateSwapchainExtension() 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 is_enabled() const;
bool is_android_ext_enabled() const;
void update_swapchain_state(XrSwapchain p_swapchain, const OpenXRViewportCompositionLayerProvider::SwapchainState *p_swapchain_state);
void update_swapchain_surface_size(XrSwapchain p_swapchain, const Size2i &p_size);
private:
static OpenXRFBUpdateSwapchainExtension *singleton;
// Setup
String rendering_driver;
bool fb_swapchain_update_state_ext = false;
bool fb_swapchain_update_state_vulkan_ext = false;
bool fb_swapchain_update_state_opengles_ext = false;
bool fb_swapchain_update_state_android_ext = false;
uint32_t filter_to_gl(OpenXRViewportCompositionLayerProvider::Filter p_filter, OpenXRViewportCompositionLayerProvider::MipmapMode p_mipmap_mode = OpenXRViewportCompositionLayerProvider::MipmapMode::MIPMAP_MODE_DISABLED);
uint32_t wrap_to_gl(OpenXRViewportCompositionLayerProvider::Wrap p_wrap);
uint32_t swizzle_to_gl(OpenXRViewportCompositionLayerProvider::Swizzle p_swizzle);
uint32_t filter_to_vk(OpenXRViewportCompositionLayerProvider::Filter p_filter);
uint32_t mipmap_mode_to_vk(OpenXRViewportCompositionLayerProvider::MipmapMode p_mipmap);
uint32_t wrap_to_vk(OpenXRViewportCompositionLayerProvider::Wrap p_wrap);
uint32_t swizzle_to_vk(OpenXRViewportCompositionLayerProvider::Swizzle p_swizzle);
// OpenXR API call wrappers
EXT_PROTO_XRRESULT_FUNC2(xrUpdateSwapchainFB, (XrSwapchain), swapchain, (const XrSwapchainStateBaseHeaderFB *), state);
EXT_PROTO_XRRESULT_FUNC2(xrGetSwapchainStateFB, (XrSwapchain), swapchain, (XrSwapchainStateBaseHeaderFB *), state);
};

View File

@@ -0,0 +1,266 @@
/**************************************************************************/
/* openxr_future_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_future_extension.h"
#include "../openxr_api.h"
////////////////////////////////////////////////////////////////////////////
// OpenXRFutureResult
void OpenXRFutureResult::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_status"), &OpenXRFutureResult::get_status);
ClassDB::bind_method(D_METHOD("get_future"), &OpenXRFutureResult::_get_future);
ClassDB::bind_method(D_METHOD("cancel_future"), &OpenXRFutureResult::cancel_future);
ClassDB::bind_method(D_METHOD("set_result_value", "result_value"), &OpenXRFutureResult::set_result_value);
ClassDB::bind_method(D_METHOD("get_result_value"), &OpenXRFutureResult::get_result_value);
ADD_SIGNAL(MethodInfo("completed", PropertyInfo(Variant::OBJECT, "result", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRFutureResult")));
BIND_ENUM_CONSTANT(RESULT_RUNNING);
BIND_ENUM_CONSTANT(RESULT_FINISHED);
BIND_ENUM_CONSTANT(RESULT_CANCELLED);
}
void OpenXRFutureResult::_mark_as_finished() {
// Update our status
status = RESULT_FINISHED;
// Perform our callback
on_success_callback.call(this); // Note, `this` will be converted to a variant that will be refcounted!
// Emit our signal, we assume our callback has provided us with the correct result value by calling set_result_value.
emit_signal(SNAME("completed"), result_value);
}
void OpenXRFutureResult::_mark_as_cancelled() {
// Update our status
status = RESULT_CANCELLED;
// There is no point in doing a callback for cancellation as its always user invoked.
// But we do emit our signal to make sure any await finishes.
Variant no_result;
emit_signal(SNAME("completed"), no_result);
}
OpenXRFutureResult::ResultStatus OpenXRFutureResult::get_status() const {
return status;
}
XrFutureEXT OpenXRFutureResult::get_future() const {
return future;
}
uint64_t OpenXRFutureResult::_get_future() const {
return (uint64_t)future;
}
void OpenXRFutureResult::set_result_value(const Variant &p_result_value) {
result_value = p_result_value;
}
Variant OpenXRFutureResult::get_result_value() const {
return result_value;
}
void OpenXRFutureResult::cancel_future() {
ERR_FAIL_COND(status != RESULT_RUNNING);
OpenXRFutureExtension *future_extension = OpenXRFutureExtension::get_singleton();
ERR_FAIL_NULL(future_extension);
future_extension->cancel_future(future);
}
OpenXRFutureResult::OpenXRFutureResult(XrFutureEXT p_future, const Callable &p_on_success) {
future = p_future;
on_success_callback = p_on_success;
}
////////////////////////////////////////////////////////////////////////////
// OpenXRFutureExtension
OpenXRFutureExtension *OpenXRFutureExtension::singleton = nullptr;
OpenXRFutureExtension *OpenXRFutureExtension::get_singleton() {
return singleton;
}
OpenXRFutureExtension::OpenXRFutureExtension() {
singleton = this;
}
OpenXRFutureExtension::~OpenXRFutureExtension() {
singleton = nullptr;
}
void OpenXRFutureExtension::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_active"), &OpenXRFutureExtension::is_active);
ClassDB::bind_method(D_METHOD("register_future", "future", "on_success"), &OpenXRFutureExtension::_register_future, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("cancel_future", "future"), &OpenXRFutureExtension::_cancel_future);
}
HashMap<String, bool *> OpenXRFutureExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_EXT_FUTURE_EXTENSION_NAME] = &future_ext;
return request_extensions;
}
void OpenXRFutureExtension::on_instance_created(const XrInstance p_instance) {
if (future_ext) {
EXT_INIT_XR_FUNC(xrPollFutureEXT);
EXT_INIT_XR_FUNC(xrCancelFutureEXT);
future_ext = xrPollFutureEXT_ptr && xrCancelFutureEXT_ptr;
}
}
void OpenXRFutureExtension::on_instance_destroyed() {
xrPollFutureEXT_ptr = nullptr;
xrCancelFutureEXT_ptr = nullptr;
}
void OpenXRFutureExtension::on_session_destroyed() {
if (!is_active()) {
return;
}
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL(openxr_api);
// Cancel any running futures.
for (const KeyValue<XrFutureEXT, Ref<OpenXRFutureResult>> &element : futures) {
XrFutureCancelInfoEXT cancel_info = {
XR_TYPE_FUTURE_CANCEL_INFO_EXT, // type
nullptr, // next
element.key // future
};
XrResult result = xrCancelFutureEXT_ptr(openxr_api->get_instance(), &cancel_info);
if (XR_FAILED(result)) {
WARN_PRINT("OpenXR: Failed to cancel future [" + openxr_api->get_error_string(result) + "]");
}
// Make sure we mark our future result as cancelled
element.value->_mark_as_cancelled();
}
futures.clear();
}
bool OpenXRFutureExtension::is_active() const {
return future_ext;
}
Ref<OpenXRFutureResult> OpenXRFutureExtension::register_future(XrFutureEXT p_future, const Callable &p_on_success) {
ERR_FAIL_COND_V(futures.has(p_future), nullptr);
Ref<OpenXRFutureResult> future_result;
future_result.instantiate(p_future, p_on_success);
futures[p_future] = future_result;
return future_result;
}
Ref<OpenXRFutureResult> OpenXRFutureExtension::_register_future(uint64_t p_future, const Callable &p_on_success) {
return register_future((XrFutureEXT)p_future, p_on_success);
}
void OpenXRFutureExtension::cancel_future(XrFutureEXT p_future) {
ERR_FAIL_COND(!futures.has(p_future));
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL(openxr_api);
XrFutureCancelInfoEXT cancel_info = {
XR_TYPE_FUTURE_CANCEL_INFO_EXT, // type
nullptr, // next
p_future // future
};
XrResult result = xrCancelFutureEXT_ptr(openxr_api->get_instance(), &cancel_info);
if (XR_FAILED(result)) {
WARN_PRINT("OpenXR: Failed to cancel future [" + openxr_api->get_error_string(result) + "]");
}
// Make sure we mark our future result as cancelled
futures[p_future]->_mark_as_cancelled();
// And erase it from the futures we track
futures.erase(p_future);
}
void OpenXRFutureExtension::_cancel_future(uint64_t p_future) {
cancel_future((XrFutureEXT)p_future);
}
void OpenXRFutureExtension::on_process() {
if (!is_active()) {
return;
}
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL(openxr_api);
// Process futures
Vector<XrFutureEXT> completed;
for (const KeyValue<XrFutureEXT, Ref<OpenXRFutureResult>> &element : futures) {
XrFuturePollInfoEXT poll_info = {
XR_TYPE_FUTURE_POLL_INFO_EXT, // type
nullptr, // next
element.key // future
};
XrFuturePollResultEXT poll_result = {
XR_TYPE_FUTURE_POLL_RESULT_EXT, // type
nullptr, // next
XR_FUTURE_STATE_MAX_ENUM_EXT // state
};
XrResult result = xrPollFutureEXT_ptr(openxr_api->get_instance(), &poll_info, &poll_result);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to obtain future status [" + openxr_api->get_error_string(result) + "]");
// Maybe remove this depending on the error?
continue;
}
if (poll_result.state == XR_FUTURE_STATE_READY_EXT) {
// Mark our future result as finished (this will invoke our callback).
element.value->_mark_as_finished();
// Queue removing this
completed.push_back(element.key);
}
}
// Now clean up completed futures.
for (const XrFutureEXT &future : completed) {
futures.erase(future);
}
}

View File

@@ -0,0 +1,136 @@
/**************************************************************************/
/* openxr_future_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. */
/**************************************************************************/
#pragma once
/*
The OpenXR future extension forms the basis of OpenXRs ability to
execute logic asynchronously.
Asynchronous functions will return a future object which can be
polled each frame to determine if the asynchronous function has
been completed.
If so the future can be used to obtain final return values.
The API call for this is often part of the extension that utilizes
the future.
We will be using Godot Callables to drive responses on futures.
*/
#include "../util.h"
#include "core/object/ref_counted.h"
#include "openxr_extension_wrapper.h"
#include <openxr/openxr.h>
class OpenXRFutureExtension;
class OpenXRFutureResult : public RefCounted {
GDCLASS(OpenXRFutureResult, RefCounted);
friend class OpenXRFutureExtension;
protected:
static void _bind_methods();
void _mark_as_finished();
void _mark_as_cancelled();
public:
enum ResultStatus {
RESULT_RUNNING,
RESULT_FINISHED,
RESULT_CANCELLED,
};
ResultStatus get_status() const;
XrFutureEXT get_future() const;
void set_result_value(const Variant &p_result_value);
Variant get_result_value() const;
void cancel_future();
OpenXRFutureResult(XrFutureEXT p_future, const Callable &p_on_success);
private:
ResultStatus status = RESULT_RUNNING;
XrFutureEXT future;
Variant result_value;
Callable on_success_callback;
uint64_t _get_future() const;
};
VARIANT_ENUM_CAST(OpenXRFutureResult::ResultStatus);
class OpenXRFutureExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRFutureExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods();
public:
static OpenXRFutureExtension *get_singleton();
OpenXRFutureExtension();
virtual ~OpenXRFutureExtension() 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;
virtual void on_session_destroyed() override;
virtual void on_process() override;
bool is_active() const;
Ref<OpenXRFutureResult> register_future(XrFutureEXT p_future, const Callable &p_on_success = Callable());
void cancel_future(XrFutureEXT p_future);
private:
static OpenXRFutureExtension *singleton;
bool future_ext = false;
HashMap<XrFutureEXT, Ref<OpenXRFutureResult>> futures;
// Make these accessible from GDExtension and/or GDScript
Ref<OpenXRFutureResult> _register_future(uint64_t p_future, const Callable &p_on_success = Callable());
void _cancel_future(uint64_t p_future);
// OpenXR API call wrappers
// Futures
EXT_PROTO_XRRESULT_FUNC3(xrPollFutureEXT, (XrInstance), instance, (const XrFuturePollInfoEXT *), poll_info, (XrFuturePollResultEXT *), poll_result);
EXT_PROTO_XRRESULT_FUNC2(xrCancelFutureEXT, (XrInstance), instance, (const XrFutureCancelInfoEXT *), cancel_info);
};

View File

@@ -0,0 +1,97 @@
/**************************************************************************/
/* openxr_hand_interaction_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_hand_interaction_extension.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
#include "core/config/project_settings.h"
OpenXRHandInteractionExtension *OpenXRHandInteractionExtension::singleton = nullptr;
OpenXRHandInteractionExtension *OpenXRHandInteractionExtension::get_singleton() {
return singleton;
}
OpenXRHandInteractionExtension::OpenXRHandInteractionExtension() {
singleton = this;
}
OpenXRHandInteractionExtension::~OpenXRHandInteractionExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRHandInteractionExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
// Only enable this extension when requested.
// We still register our meta data or the action map editor will fail.
if (GLOBAL_GET("xr/openxr/extensions/hand_interaction_profile")) {
request_extensions[XR_EXT_HAND_INTERACTION_EXTENSION_NAME] = &available;
}
return request_extensions;
}
bool OpenXRHandInteractionExtension::is_available() {
return available;
}
void OpenXRHandInteractionExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
// Hand interaction profile
openxr_metadata->register_interaction_profile("Hand interaction", "/interaction_profiles/ext/hand_interaction_ext", XR_EXT_HAND_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch pose", "/user/hand/left", "/user/hand/left/input/pinch_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch pose", "/user/hand/right", "/user/hand/right/input/pinch_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Poke pose", "/user/hand/left", "/user/hand/left/input/poke_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Poke pose", "/user/hand/right", "/user/hand/right/input/poke_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch", "/user/hand/left", "/user/hand/left/input/pinch_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch", "/user/hand/right", "/user/hand/right/input/pinch_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch ready", "/user/hand/left", "/user/hand/left/input/pinch_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Pinch ready", "/user/hand/right", "/user/hand/right/input/pinch_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim activate", "/user/hand/left", "/user/hand/left/input/aim_activate_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim activate", "/user/hand/right", "/user/hand/right/input/aim_activate_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim activate ready", "/user/hand/left", "/user/hand/left/input/aim_activate_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Aim activate ready", "/user/hand/right", "/user/hand/right/input/aim_activate_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grasp", "/user/hand/left", "/user/hand/left/input/grasp_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grasp", "/user/hand/right", "/user/hand/right/input/grasp_ext/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grasp ready", "/user/hand/left", "/user/hand/left/input/grasp_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path("/interaction_profiles/ext/hand_interaction_ext", "Grasp ready", "/user/hand/right", "/user/hand/right/input/grasp_ext/ready_ext", "", OpenXRAction::OPENXR_ACTION_BOOL);
}

View File

@@ -0,0 +1,74 @@
/**************************************************************************/
/* openxr_hand_interaction_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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
// When supported the hand interaction extension introduces an interaction
// profile that becomes active when the user either lets go of their
// controllers or isn't using controllers at all.
//
// The OpenXR specification states that all XR runtimes that support this
// interaction profile should also allow it's controller to use this
// interaction profile.
// This means that if you only supply this interaction profile in your
// action map, it should work both when the player is holding a controller
// or using visual hand tracking.
//
// This allows easier portability between games that use controller
// tracking or hand tracking.
//
// See: https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_hand_interaction
// for more information.
class OpenXRHandInteractionExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRHandInteractionExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRHandInteractionExtension *get_singleton();
OpenXRHandInteractionExtension();
virtual ~OpenXRHandInteractionExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
virtual void on_register_metadata() override;
private:
static OpenXRHandInteractionExtension *singleton;
bool available = false;
};

View File

@@ -0,0 +1,479 @@
/**************************************************************************/
/* openxr_hand_tracking_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_hand_tracking_extension.h"
#include "../openxr_api.h"
#include "core/config/project_settings.h"
#include "core/string/print_string.h"
#include "servers/xr_server.h"
#include <openxr/openxr.h>
OpenXRHandTrackingExtension *OpenXRHandTrackingExtension::singleton = nullptr;
OpenXRHandTrackingExtension *OpenXRHandTrackingExtension::get_singleton() {
return singleton;
}
OpenXRHandTrackingExtension::OpenXRHandTrackingExtension() {
singleton = this;
// Make sure this is cleared until we actually request it
handTrackingSystemProperties.supportsHandTracking = false;
}
OpenXRHandTrackingExtension::~OpenXRHandTrackingExtension() {
singleton = nullptr;
}
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;
if (unobstructed_data_source || controller_data_source) {
request_extensions[XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME] = &hand_tracking_source_ext;
}
return request_extensions;
}
void OpenXRHandTrackingExtension::on_instance_created(const XrInstance p_instance) {
if (hand_tracking_ext) {
EXT_INIT_XR_FUNC(xrCreateHandTrackerEXT);
EXT_INIT_XR_FUNC(xrDestroyHandTrackerEXT);
EXT_INIT_XR_FUNC(xrLocateHandJointsEXT);
hand_tracking_ext = xrCreateHandTrackerEXT_ptr && xrDestroyHandTrackerEXT_ptr && xrLocateHandJointsEXT_ptr;
}
}
void OpenXRHandTrackingExtension::on_session_destroyed() {
cleanup_hand_tracking();
}
void OpenXRHandTrackingExtension::on_instance_destroyed() {
xrCreateHandTrackerEXT_ptr = nullptr;
xrDestroyHandTrackerEXT_ptr = nullptr;
xrLocateHandJointsEXT_ptr = nullptr;
}
void *OpenXRHandTrackingExtension::set_system_properties_and_get_next_pointer(void *p_next_pointer) {
if (!hand_tracking_ext) {
// not supported...
return p_next_pointer;
}
handTrackingSystemProperties = {
XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT, // type
p_next_pointer, // next
false, // supportsHandTracking
};
return &handTrackingSystemProperties;
}
void OpenXRHandTrackingExtension::on_state_ready() {
if (!handTrackingSystemProperties.supportsHandTracking) {
// not supported...
return;
}
// Setup our hands and reset data
for (int i = 0; i < OPENXR_MAX_TRACKED_HANDS; i++) {
// we'll do this later
hand_trackers[i].is_initialized = false;
hand_trackers[i].hand_tracker = XR_NULL_HANDLE;
hand_trackers[i].locations.isActive = false;
for (int j = 0; j < XR_HAND_JOINT_COUNT_EXT; j++) {
hand_trackers[i].joint_locations[j] = { 0, { { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } }, 0.0 };
hand_trackers[i].joint_velocities[j] = { 0, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } };
}
}
}
void OpenXRHandTrackingExtension::on_process() {
if (!handTrackingSystemProperties.supportsHandTracking) {
// not supported...
return;
}
// process our hands
const XrTime time = OpenXRAPI::get_singleton()->get_predicted_display_time();
if (time == 0) {
// we don't have timing info yet, or we're skipping a frame...
return;
}
XrResult result;
for (int i = 0; i < OPENXR_MAX_TRACKED_HANDS; i++) {
if (hand_trackers[i].hand_tracker == XR_NULL_HANDLE) {
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 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;
}
XrHandTrackerCreateInfoEXT create_info = {
XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT, // type
next_pointer, // next
i == 0 ? XR_HAND_LEFT_EXT : XR_HAND_RIGHT_EXT, // hand
XR_HAND_JOINT_SET_DEFAULT_EXT, // handJointSet
};
result = xrCreateHandTrackerEXT(OpenXRAPI::get_singleton()->get_session(), &create_info, &hand_trackers[i].hand_tracker);
if (XR_FAILED(result)) {
// not successful? then we do nothing.
print_line("OpenXR: Failed to obtain hand tracking information [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
hand_trackers[i].is_initialized = false;
} else {
next_pointer = nullptr;
hand_trackers[i].velocities.type = XR_TYPE_HAND_JOINT_VELOCITIES_EXT;
hand_trackers[i].velocities.next = next_pointer;
hand_trackers[i].velocities.jointCount = XR_HAND_JOINT_COUNT_EXT;
hand_trackers[i].velocities.jointVelocities = hand_trackers[i].joint_velocities;
next_pointer = &hand_trackers[i].velocities;
if (hand_tracking_source_ext) {
hand_trackers[i].data_source.type = XR_TYPE_HAND_TRACKING_DATA_SOURCE_STATE_EXT;
hand_trackers[i].data_source.next = next_pointer;
hand_trackers[i].data_source.isActive = false;
hand_trackers[i].data_source.dataSource = XrHandTrackingDataSourceEXT(0);
next_pointer = &hand_trackers[i].data_source;
}
// Needed for vendor hand tracking extensions implemented from GDExtension.
for (OpenXRExtensionWrapper *wrapper : OpenXRAPI::get_singleton()->get_registered_extension_wrappers()) {
void *np = wrapper->set_hand_joint_locations_and_get_next_pointer(i, next_pointer);
if (np != nullptr) {
next_pointer = np;
}
}
hand_trackers[i].locations.type = XR_TYPE_HAND_JOINT_LOCATIONS_EXT;
hand_trackers[i].locations.next = next_pointer;
hand_trackers[i].locations.isActive = false;
hand_trackers[i].locations.jointCount = XR_HAND_JOINT_COUNT_EXT;
hand_trackers[i].locations.jointLocations = hand_trackers[i].joint_locations;
Ref<XRHandTracker> godot_tracker;
godot_tracker.instantiate();
godot_tracker->set_tracker_hand(i == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT);
godot_tracker->set_tracker_name(i == 0 ? "/user/hand_tracker/left" : "/user/hand_tracker/right");
XRServer::get_singleton()->add_tracker(godot_tracker);
hand_trackers[i].godot_tracker = godot_tracker;
hand_trackers[i].is_initialized = true;
}
}
if (hand_trackers[i].is_initialized) {
Ref<XRHandTracker> godot_tracker = hand_trackers[i].godot_tracker;
void *next_pointer = nullptr;
XrHandJointsMotionRangeInfoEXT motion_range_info = { XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT, next_pointer, hand_trackers[i].motion_range };
if (hand_motion_range_ext) {
next_pointer = &motion_range_info;
}
XrHandJointsLocateInfoEXT locateInfo = {
XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT, // type
next_pointer, // next
OpenXRAPI::get_singleton()->get_play_space(), // baseSpace
time, // time
};
result = xrLocateHandJointsEXT(hand_trackers[i].hand_tracker, &locateInfo, &hand_trackers[i].locations);
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;
}
// For some reason an inactive controller isn't coming back as inactive but has coordinates either as NAN or very large
const XrPosef &palm = hand_trackers[i].joint_locations[XR_HAND_JOINT_PALM_EXT].pose;
if (!hand_trackers[i].locations.isActive || std::isnan(palm.position.x) || palm.position.x < -1000000.00 || palm.position.x > 1000000.00) {
hand_trackers[i].locations.isActive = false; // workaround, make sure its inactive
}
if (hand_trackers[i].locations.isActive) {
// 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)
const Quaternion bone_adjustment(0.0, -Math::SQRT12, Math::SQRT12, 0.0);
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 XrPosef &pose = location.pose;
Transform3D transform;
Vector3 linear_velocity;
Vector3 angular_velocity;
BitField<XRHandTracker::HandJointFlags> flags = {};
if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
if (pose.orientation.x != 0 || pose.orientation.y != 0 || pose.orientation.z != 0 || pose.orientation.w != 0) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_VALID);
transform.basis = Basis(Quaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w) * bone_adjustment);
}
}
if (location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_POSITION_VALID);
transform.origin = Vector3(pose.position.x, pose.position.y, pose.position.z);
}
if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_TRACKED);
}
if (location.locationFlags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_POSITION_TRACKED);
}
if (location.locationFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_LINEAR_VELOCITY_VALID);
linear_velocity = Vector3(velocity.linearVelocity.x, velocity.linearVelocity.y, velocity.linearVelocity.z);
godot_tracker->set_hand_joint_linear_velocity((XRHandTracker::HandJoint)joint, linear_velocity);
}
if (location.locationFlags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_ANGULAR_VELOCITY_VALID);
angular_velocity = Vector3(velocity.angularVelocity.x, velocity.angularVelocity.y, velocity.angularVelocity.z);
godot_tracker->set_hand_joint_angular_velocity((XRHandTracker::HandJoint)joint, angular_velocity);
}
godot_tracker->set_hand_joint_flags((XRHandTracker::HandJoint)joint, flags);
godot_tracker->set_hand_joint_transform((XRHandTracker::HandJoint)joint, transform);
godot_tracker->set_hand_joint_radius((XRHandTracker::HandJoint)joint, location.radius);
if (joint == XR_HAND_JOINT_PALM_EXT) {
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");
}
}
}
} 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");
}
}
}
}
void OpenXRHandTrackingExtension::on_state_stopping() {
// cleanup
cleanup_hand_tracking();
}
void OpenXRHandTrackingExtension::cleanup_hand_tracking() {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL(xr_server);
for (int i = 0; i < OPENXR_MAX_TRACKED_HANDS; i++) {
if (hand_trackers[i].hand_tracker != XR_NULL_HANDLE) {
xrDestroyHandTrackerEXT(hand_trackers[i].hand_tracker);
hand_trackers[i].is_initialized = false;
hand_trackers[i].hand_tracker = XR_NULL_HANDLE;
XRServer::get_singleton()->remove_tracker(hand_trackers[i].godot_tracker);
}
}
}
bool OpenXRHandTrackingExtension::get_active() {
return handTrackingSystemProperties.supportsHandTracking;
}
const OpenXRHandTrackingExtension::HandTracker *OpenXRHandTrackingExtension::get_hand_tracker(HandTrackedHands p_hand) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, nullptr);
return &hand_trackers[p_hand];
}
XrHandJointsMotionRangeEXT OpenXRHandTrackingExtension::get_motion_range(HandTrackedHands p_hand) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, XR_HAND_JOINTS_MOTION_RANGE_MAX_ENUM_EXT);
return hand_trackers[p_hand].motion_range;
}
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) {
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;
}
}
return OPENXR_SOURCE_UNKNOWN;
}
void OpenXRHandTrackingExtension::set_motion_range(HandTrackedHands p_hand, XrHandJointsMotionRangeEXT p_motion_range) {
ERR_FAIL_UNSIGNED_INDEX(p_hand, OPENXR_MAX_TRACKED_HANDS);
hand_trackers[p_hand].motion_range = p_motion_range;
}
XrSpaceLocationFlags OpenXRHandTrackingExtension::get_hand_joint_location_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, XrSpaceLocationFlags(0));
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, XrSpaceLocationFlags(0));
if (!hand_trackers[p_hand].is_initialized) {
return XrSpaceLocationFlags(0);
}
const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint];
return location.locationFlags;
}
Quaternion OpenXRHandTrackingExtension::get_hand_joint_rotation(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Quaternion());
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Quaternion());
if (!hand_trackers[p_hand].is_initialized) {
return Quaternion();
}
const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint];
return Quaternion(location.pose.orientation.x, location.pose.orientation.y, location.pose.orientation.z, location.pose.orientation.w);
}
Vector3 OpenXRHandTrackingExtension::get_hand_joint_position(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Vector3());
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3());
if (!hand_trackers[p_hand].is_initialized) {
return Vector3();
}
const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint];
return Vector3(location.pose.position.x, location.pose.position.y, location.pose.position.z);
}
float OpenXRHandTrackingExtension::get_hand_joint_radius(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, 0.0);
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, 0.0);
if (!hand_trackers[p_hand].is_initialized) {
return 0.0;
}
return hand_trackers[p_hand].joint_locations[p_joint].radius;
}
XrSpaceVelocityFlags OpenXRHandTrackingExtension::get_hand_joint_velocity_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, XrSpaceVelocityFlags(0));
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, XrSpaceVelocityFlags(0));
if (!hand_trackers[p_hand].is_initialized) {
return XrSpaceVelocityFlags(0);
}
const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint];
return velocity.velocityFlags;
}
Vector3 OpenXRHandTrackingExtension::get_hand_joint_linear_velocity(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Vector3());
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3());
if (!hand_trackers[p_hand].is_initialized) {
return Vector3();
}
const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint];
return Vector3(velocity.linearVelocity.x, velocity.linearVelocity.y, velocity.linearVelocity.z);
}
Vector3 OpenXRHandTrackingExtension::get_hand_joint_angular_velocity(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Vector3());
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3());
if (!hand_trackers[p_hand].is_initialized) {
return Vector3();
}
const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint];
return Vector3(velocity.angularVelocity.x, velocity.angularVelocity.y, velocity.angularVelocity.z);
}

View File

@@ -0,0 +1,128 @@
/**************************************************************************/
/* openxr_hand_tracking_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. */
/**************************************************************************/
#pragma once
#include "../util.h"
#include "core/math/quaternion.h"
#include "openxr_extension_wrapper.h"
#include "servers/xr/xr_hand_tracker.h"
class OpenXRHandTrackingExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRHandTrackingExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
enum HandTrackedHands {
OPENXR_TRACKED_LEFT_HAND,
OPENXR_TRACKED_RIGHT_HAND,
OPENXR_MAX_TRACKED_HANDS
};
enum HandTrackedSource {
OPENXR_SOURCE_UNKNOWN,
OPENXR_SOURCE_UNOBSTRUCTED,
OPENXR_SOURCE_CONTROLLER,
OPENXR_SOURCE_NOT_TRACKED,
OPENXR_SOURCE_MAX
};
struct HandTracker {
bool is_initialized = false;
Ref<XRHandTracker> godot_tracker;
XrHandJointsMotionRangeEXT motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT;
HandTrackedSource source = OPENXR_SOURCE_UNKNOWN;
XrHandTrackerEXT hand_tracker = XR_NULL_HANDLE;
XrHandJointLocationEXT joint_locations[XR_HAND_JOINT_COUNT_EXT];
XrHandJointVelocityEXT joint_velocities[XR_HAND_JOINT_COUNT_EXT];
XrHandJointVelocitiesEXT velocities;
XrHandJointLocationsEXT locations;
XrHandTrackingDataSourceStateEXT data_source;
};
static OpenXRHandTrackingExtension *get_singleton();
OpenXRHandTrackingExtension();
virtual ~OpenXRHandTrackingExtension() 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;
virtual void on_session_destroyed() override;
virtual void *set_system_properties_and_get_next_pointer(void *p_next_pointer) override;
virtual void on_state_ready() override;
virtual void on_process() override;
virtual void on_state_stopping() override;
bool get_active();
const HandTracker *get_hand_tracker(HandTrackedHands p_hand) const;
XrHandJointsMotionRangeEXT get_motion_range(HandTrackedHands p_hand) const;
void set_motion_range(HandTrackedHands p_hand, XrHandJointsMotionRangeEXT p_motion_range);
HandTrackedSource get_hand_tracking_source(HandTrackedHands p_hand) const;
XrSpaceLocationFlags get_hand_joint_location_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
Quaternion get_hand_joint_rotation(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
Vector3 get_hand_joint_position(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
float get_hand_joint_radius(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
XrSpaceVelocityFlags get_hand_joint_velocity_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
Vector3 get_hand_joint_linear_velocity(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
Vector3 get_hand_joint_angular_velocity(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
private:
static OpenXRHandTrackingExtension *singleton;
// state
XrSystemHandTrackingPropertiesEXT handTrackingSystemProperties;
HandTracker hand_trackers[OPENXR_MAX_TRACKED_HANDS]; // Fixed for left and right hand
// related extensions
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();
// OpenXR API call wrappers
EXT_PROTO_XRRESULT_FUNC3(xrCreateHandTrackerEXT, (XrSession), p_session, (const XrHandTrackerCreateInfoEXT *), p_createInfo, (XrHandTrackerEXT *), p_handTracker)
EXT_PROTO_XRRESULT_FUNC1(xrDestroyHandTrackerEXT, (XrHandTrackerEXT), p_handTracker)
EXT_PROTO_XRRESULT_FUNC3(xrLocateHandJointsEXT, (XrHandTrackerEXT), p_handTracker, (const XrHandJointsLocateInfoEXT *), p_locateInfo, (XrHandJointLocationsEXT *), p_locations)
};

View File

@@ -0,0 +1,149 @@
/**************************************************************************/
/* openxr_htc_controller_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_htc_controller_extension.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
HashMap<String, bool *> OpenXRHTCControllerExtension::get_requested_extensions() {
HashMap<String, bool *> request_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];
}
void OpenXRHTCControllerExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
openxr_metadata->register_top_level_path("HTC left hand tracker", "/user/hand_htc/left", XR_HTC_HAND_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("HTC right hand tracker", "/user/hand_htc/right", XR_HTC_HAND_INTERACTION_EXTENSION_NAME);
{ // HTC Vive Cosmos controller
const String profile_path = "/interaction_profiles/htc/vive_cosmos_controller";
openxr_metadata->register_interaction_profile("Vive Cosmos controller", profile_path, XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME);
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Shoulder click", user_path, user_path + "/input/shoulder/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Squeeze click", user_path, user_path + "/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick touch", user_path, user_path + "/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
openxr_metadata->register_io_path(profile_path, "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
}
{ // HTC Vive Focus 3 controller
const String profile_path = "/interaction_profiles/htc/vive_focus3_controller";
openxr_metadata->register_interaction_profile("Vive Focus 3 controller", profile_path, XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME);
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger touch", user_path, user_path + "/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Squeeze", user_path, user_path + "/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Squeeze click", user_path, user_path + "/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Squeeze touch", user_path, user_path + "/input/squeeze/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick touch", user_path, user_path + "/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbrest touch", user_path, user_path + "/input/thumbrest/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
openxr_metadata->register_io_path(profile_path, "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
}
{ // HTC Hand interaction profile
const String profile_path = "/interaction_profiles/htc/hand_interaction";
openxr_metadata->register_interaction_profile("HTC Hand interaction", profile_path, XR_HTC_HAND_INTERACTION_EXTENSION_NAME);
for (const String user_path : { "/user/hand_htc/left", "/user/hand_htc/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Select (pinch)", user_path, user_path + "/input/select/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Squeeze (grab)", user_path, user_path + "/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
}
}
}

View File

@@ -0,0 +1,60 @@
/**************************************************************************/
/* openxr_htc_controller_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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRHTCControllerExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRHTCControllerExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
enum HTCControllers {
// 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;
private:
bool available[HTC_MAX_CONTROLLERS] = { false, false };
};

View File

@@ -0,0 +1,151 @@
/**************************************************************************/
/* openxr_htc_vive_tracker_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_htc_vive_tracker_extension.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
#include "core/string/print_string.h"
HashMap<String, bool *> OpenXRHTCViveTrackerExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME] = &available;
return request_extensions;
}
PackedStringArray OpenXRHTCViveTrackerExtension::get_suggested_tracker_names() {
PackedStringArray arr = {
"/user/vive_tracker_htcx/role/handheld_object",
"/user/vive_tracker_htcx/role/left_foot",
"/user/vive_tracker_htcx/role/right_foot",
"/user/vive_tracker_htcx/role/left_shoulder",
"/user/vive_tracker_htcx/role/right_shoulder",
"/user/vive_tracker_htcx/role/left_elbow",
"/user/vive_tracker_htcx/role/right_elbow",
"/user/vive_tracker_htcx/role/left_knee",
"/user/vive_tracker_htcx/role/right_knee",
"/user/vive_tracker_htcx/role/waist",
"/user/vive_tracker_htcx/role/chest",
"/user/vive_tracker_htcx/role/camera",
"/user/vive_tracker_htcx/role/keyboard",
"/user/vive_tracker_htcx/role/left_wrist",
"/user/vive_tracker_htcx/role/right_wrist",
"/user/vive_tracker_htcx/role/left_ankle",
"/user/vive_tracker_htcx/role/right_ankle",
};
return arr;
}
bool OpenXRHTCViveTrackerExtension::is_available() {
return available;
}
void OpenXRHTCViveTrackerExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
// register_top_level_path("Handheld object tracker", "/user/vive_tracker_htcx/role/handheld_object", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Left foot tracker", "/user/vive_tracker_htcx/role/left_foot", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Right foot tracker", "/user/vive_tracker_htcx/role/right_foot", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Left shoulder tracker", "/user/vive_tracker_htcx/role/left_shoulder", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Right shoulder tracker", "/user/vive_tracker_htcx/role/right_shoulder", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Left elbow tracker", "/user/vive_tracker_htcx/role/left_elbow", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Right elbow tracker", "/user/vive_tracker_htcx/role/right_elbow", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Left knee tracker", "/user/vive_tracker_htcx/role/left_knee", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Right knee tracker", "/user/vive_tracker_htcx/role/right_knee", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Waist tracker", "/user/vive_tracker_htcx/role/waist", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Chest tracker", "/user/vive_tracker_htcx/role/chest", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Camera tracker", "/user/vive_tracker_htcx/role/camera", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Keyboard tracker", "/user/vive_tracker_htcx/role/keyboard", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Left wrist tracker", "/user/vive_tracker_htcx/role/left_wrist", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Right wrist tracker", "/user/vive_tracker_htcx/role/right_wrist", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Left ankle tracker", "/user/vive_tracker_htcx/role/left_ankle", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
openxr_metadata->register_top_level_path("Right ankle tracker", "/user/vive_tracker_htcx/role/right_ankle", 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.
const String profile_path = "/interaction_profiles/htc/vive_tracker_htcx";
openxr_metadata->register_interaction_profile("HTC Vive tracker", profile_path, XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
for (const String user_path : {
/* "/user/vive_tracker_htcx/role/handheld_object", */
"/user/vive_tracker_htcx/role/left_foot",
"/user/vive_tracker_htcx/role/right_foot",
"/user/vive_tracker_htcx/role/left_shoulder",
"/user/vive_tracker_htcx/role/right_shoulder",
"/user/vive_tracker_htcx/role/left_elbow",
"/user/vive_tracker_htcx/role/right_elbow",
"/user/vive_tracker_htcx/role/left_knee",
"/user/vive_tracker_htcx/role/right_knee",
"/user/vive_tracker_htcx/role/waist",
"/user/vive_tracker_htcx/role/chest",
"/user/vive_tracker_htcx/role/camera",
"/user/vive_tracker_htcx/role/keyboard",
"/user/vive_tracker_htcx/role/left_wrist",
"/user/vive_tracker_htcx/role/right_wrist",
"/user/vive_tracker_htcx/role/left_ankle",
"/user/vive_tracker_htcx/role/right_ankle",
}) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Menu click", user_path, user_path + "/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Squeeze click", user_path, user_path + "/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad", user_path, user_path + "/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Trackpad click", user_path, user_path + "/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad touch", user_path, user_path + "/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Up", user_path, user_path + "/input/trackpad/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Down", user_path, user_path + "/input/trackpad/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Left", user_path, user_path + "/input/trackpad/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Right", user_path, user_path + "/input/trackpad/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Center", user_path, user_path + "/input/trackpad/dpad_center", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
}
}
bool OpenXRHTCViveTrackerExtension::on_event_polled(const XrEventDataBuffer &event) {
switch (event.type) {
case XR_TYPE_EVENT_DATA_VIVE_TRACKER_CONNECTED_HTCX: {
// Investigate if we need to do more here
print_verbose("OpenXR EVENT: VIVE tracker connected");
return true;
} break;
default: {
return false;
} break;
}
}

View File

@@ -0,0 +1,53 @@
/**************************************************************************/
/* openxr_htc_vive_tracker_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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRHTCViveTrackerExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRHTCViveTrackerExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
virtual HashMap<String, bool *> get_requested_extensions() override;
PackedStringArray get_suggested_tracker_names() override;
bool is_available();
virtual void on_register_metadata() override;
virtual bool on_event_polled(const XrEventDataBuffer &event) override;
private:
bool available = false;
};

View File

@@ -0,0 +1,80 @@
/**************************************************************************/
/* openxr_huawei_controller_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_huawei_controller_extension.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
HashMap<String, bool *> OpenXRHuaweiControllerExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available;
return request_extensions;
}
bool OpenXRHuaweiControllerExtension::is_available() {
return available;
}
void OpenXRHuaweiControllerExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
{ // Huawei controller
const String profile_path = "/interaction_profiles/huawei/controller";
openxr_metadata->register_interaction_profile("Huawei controller", profile_path, XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME);
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Home click", user_path, user_path + "/input/home/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Back click", user_path, user_path + "/input/back/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Volume up click", user_path, user_path + "/input/volume_up/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Volume down click", user_path, user_path + "/input/volume_down/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad", user_path, user_path + "/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Trackpad click", user_path, user_path + "/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad touch", user_path, user_path + "/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Up", user_path, user_path + "/input/trackpad/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Down", user_path, user_path + "/input/trackpad/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Left", user_path, user_path + "/input/trackpad/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Right", user_path, user_path + "/input/trackpad/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Center", user_path, user_path + "/input/trackpad/dpad_center", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
}
}

View File

@@ -0,0 +1,50 @@
/**************************************************************************/
/* openxr_huawei_controller_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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRHuaweiControllerExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRHuaweiControllerExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
virtual void on_register_metadata() override;
private:
bool available = false;
};

View File

@@ -0,0 +1,57 @@
/**************************************************************************/
/* openxr_local_floor_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_local_floor_extension.h"
OpenXRLocalFloorExtension *OpenXRLocalFloorExtension::singleton = nullptr;
OpenXRLocalFloorExtension *OpenXRLocalFloorExtension::get_singleton() {
return singleton;
}
OpenXRLocalFloorExtension::OpenXRLocalFloorExtension() {
singleton = this;
}
OpenXRLocalFloorExtension::~OpenXRLocalFloorExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRLocalFloorExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_EXT_LOCAL_FLOOR_EXTENSION_NAME] = &available;
return request_extensions;
}
bool OpenXRLocalFloorExtension::is_available() {
return available;
}

View File

@@ -0,0 +1,55 @@
/**************************************************************************/
/* openxr_local_floor_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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRLocalFloorExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRLocalFloorExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRLocalFloorExtension *get_singleton();
OpenXRLocalFloorExtension();
virtual ~OpenXRLocalFloorExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
private:
static OpenXRLocalFloorExtension *singleton;
bool available = false;
};

View File

@@ -0,0 +1,161 @@
/**************************************************************************/
/* openxr_meta_controller_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_meta_controller_extension.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
HashMap<String, bool *> OpenXRMetaControllerExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_FB_TOUCH_CONTROLLER_PROXIMITY_EXTENSION_NAME] = &available[META_TOUCH_PROXIMITY];
request_extensions[XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME] = &available[META_TOUCH_PRO];
request_extensions[XR_META_TOUCH_CONTROLLER_PLUS_EXTENSION_NAME] = &available[META_TOUCH_PLUS];
return request_extensions;
}
bool OpenXRMetaControllerExtension::is_available(MetaControllers p_type) {
return available[p_type];
}
void OpenXRMetaControllerExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
// Note, we register controllers regardless if they are supported on the current hardware.
{ // Normal touch controller is part of the core spec, but we do have some extensions.
const String profile_path = "/interaction_profiles/oculus/touch_controller";
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Trigger proximity", user_path, user_path + "/input/trigger/proximity_fb ", "XR_FB_touch_controller_proximity", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumb proximity", user_path, user_path + "/input/thumb_fb/proximity_fb ", "XR_FB_touch_controller_proximity", OpenXRAction::OPENXR_ACTION_BOOL);
}
}
{ // Touch controller pro (Quest Pro)
const String profile_path = "/interaction_profiles/facebook/touch_controller_pro";
openxr_metadata->register_interaction_profile("Touch controller pro", profile_path, "XR_FB_touch_controller_pro");
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger touch", user_path, user_path + "/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger proximity", user_path, user_path + "/input/trigger/proximity_fb", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger curl", user_path, user_path + "/input/trigger/curl_fb", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger slide", user_path, user_path + "/input/trigger/slide_fb", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger force", user_path, user_path + "/input/trigger/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Squeeze", user_path, user_path + "/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Thumb proximity", user_path, user_path + "/input/thumb_fb/proximity_fb", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Thumbstick X", user_path, user_path + "/input/thumbstick/x", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Thumbstick Y", user_path, user_path + "/input/thumbstick/y", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick touch", user_path, user_path + "/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbrest touch", user_path, user_path + "/input/thumbrest/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbrest force", user_path, user_path + "/input/thumbrest/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Stylus force", user_path, user_path + "/input/stylus_fb/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
openxr_metadata->register_io_path(profile_path, "Haptic trigger output", user_path, user_path + "/output/haptic_trigger_fb", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
openxr_metadata->register_io_path(profile_path, "Haptic thumb output", user_path, user_path + "/output/haptic_thumb_fb", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
openxr_metadata->register_io_path(profile_path, "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "X touch", "/user/hand/left", "/user/hand/left/input/x/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y touch", "/user/hand/left", "/user/hand/left/input/y/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A touch", "/user/hand/right", "/user/hand/right/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B touch", "/user/hand/right", "/user/hand/right/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
}
{ // Touch controller plus (Quest 3)
const String profile_path = "/interaction_profiles/meta/touch_controller_plus";
openxr_metadata->register_interaction_profile("Touch controller plus", profile_path, "XR_META_touch_controller_plus");
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger touch", user_path, user_path + "/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger proximity", user_path, user_path + "/input/trigger/proximity_meta", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger curl", user_path, user_path + "/input/trigger/curl_meta", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger slide", user_path, user_path + "/input/trigger/slide_meta", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger force", user_path, user_path + "/input/trigger/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Squeeze", user_path, user_path + "/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Thumb proximity", user_path, user_path + "/input/thumb_meta/proximity_meta", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Thumbstick X", user_path, user_path + "/input/thumbstick/x", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Thumbstick Y", user_path, user_path + "/input/thumbstick/y", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick touch", user_path, user_path + "/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbrest touch", user_path, user_path + "/input/thumbrest/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
openxr_metadata->register_io_path(profile_path, "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "X touch", "/user/hand/left", "/user/hand/left/input/x/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y touch", "/user/hand/left", "/user/hand/left/input/y/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A touch", "/user/hand/right", "/user/hand/right/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B touch", "/user/hand/right", "/user/hand/right/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
}
}

View File

@@ -0,0 +1,57 @@
/**************************************************************************/
/* openxr_meta_controller_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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRMetaControllerExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRMetaControllerExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
enum MetaControllers {
META_TOUCH_PROXIMITY, // Proximity extensions for normal touch controllers
META_TOUCH_PRO, // Touch controller for the Quest Pro
META_TOUCH_PLUS, // Touch controller for the Quest Plus
META_MAX_CONTROLLERS
};
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available(MetaControllers p_type);
virtual void on_register_metadata() override;
private:
bool available[META_MAX_CONTROLLERS] = { false, false, false };
};

View File

@@ -0,0 +1,77 @@
/**************************************************************************/
/* openxr_ml2_controller_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_ml2_controller_extension.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
HashMap<String, bool *> OpenXRML2ControllerExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available;
return request_extensions;
}
bool OpenXRML2ControllerExtension::is_available() {
return available;
}
void OpenXRML2ControllerExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
// Magic Leap 2 Controller
const String profile_path = "/interaction_profiles/ml/ml2_controller";
openxr_metadata->register_interaction_profile("Magic Leap 2 controller", profile_path, XR_ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME);
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Menu click", user_path, user_path + "/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Shoulder click", user_path, user_path + "/input/shoulder/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad click", user_path, user_path + "/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad force", user_path, user_path + "/input/trackpad/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trackpad X", user_path, user_path + "/input/trackpad/x", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trackpad Y", user_path, user_path + "/input/trackpad/y", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trackpad touch", user_path, user_path + "/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Up", user_path, user_path + "/input/trackpad/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Down", user_path, user_path + "/input/trackpad/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Left", user_path, user_path + "/input/trackpad/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Right", user_path, user_path + "/input/trackpad/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Center", user_path, user_path + "/input/trackpad/dpad_center", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
}

View File

@@ -0,0 +1,50 @@
/**************************************************************************/
/* openxr_ml2_controller_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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRML2ControllerExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRML2ControllerExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
virtual void on_register_metadata() override;
private:
bool available = false;
};

View File

@@ -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 *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
// Logitech MX Ink Stylus
openxr_metadata->register_interaction_profile("Logitech MX Ink Stylus", "/interaction_profiles/logitech/mx_ink_stylus_logitech", XR_LOGITECH_MX_INK_STYLUS_INTERACTION_EXTENSION_NAME);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
openxr_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);
}

View File

@@ -0,0 +1,50 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRMxInkExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRMxInkExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
virtual void on_register_metadata() override;
private:
bool available = false;
};

View File

@@ -0,0 +1,57 @@
/**************************************************************************/
/* openxr_palm_pose_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_palm_pose_extension.h"
OpenXRPalmPoseExtension *OpenXRPalmPoseExtension::singleton = nullptr;
OpenXRPalmPoseExtension *OpenXRPalmPoseExtension::get_singleton() {
return singleton;
}
OpenXRPalmPoseExtension::OpenXRPalmPoseExtension() {
singleton = this;
}
OpenXRPalmPoseExtension::~OpenXRPalmPoseExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRPalmPoseExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_EXT_PALM_POSE_EXTENSION_NAME] = &available;
return request_extensions;
}
bool OpenXRPalmPoseExtension::is_available() {
return available;
}

View File

@@ -0,0 +1,55 @@
/**************************************************************************/
/* openxr_palm_pose_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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRPalmPoseExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRPalmPoseExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRPalmPoseExtension *get_singleton();
OpenXRPalmPoseExtension();
virtual ~OpenXRPalmPoseExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
private:
static OpenXRPalmPoseExtension *singleton;
bool available = false;
};

View File

@@ -0,0 +1,156 @@
/**************************************************************************/
/* openxr_performance_settings_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_performance_settings_extension.h"
#include "../openxr_api.h"
OpenXRPerformanceSettingsExtension *OpenXRPerformanceSettingsExtension::singleton = nullptr;
OpenXRPerformanceSettingsExtension *OpenXRPerformanceSettingsExtension::get_singleton() {
return singleton;
}
OpenXRPerformanceSettingsExtension::OpenXRPerformanceSettingsExtension() {
singleton = this;
}
OpenXRPerformanceSettingsExtension::~OpenXRPerformanceSettingsExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRPerformanceSettingsExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME] = &available;
return request_extensions;
}
void OpenXRPerformanceSettingsExtension::on_instance_created(const XrInstance p_instance) {
if (available) {
EXT_INIT_XR_FUNC(xrPerfSettingsSetPerformanceLevelEXT);
}
}
bool OpenXRPerformanceSettingsExtension::on_event_polled(const XrEventDataBuffer &event) {
switch (event.type) {
case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: {
const XrEventDataPerfSettingsEXT *perf_settings_event = (XrEventDataPerfSettingsEXT *)&event;
OpenXRInterface::PerfSettingsSubDomain sub_domain = openxr_to_sub_domain(perf_settings_event->subDomain);
OpenXRInterface::PerfSettingsNotificationLevel from_level = openxr_to_notification_level(perf_settings_event->fromLevel);
OpenXRInterface::PerfSettingsNotificationLevel to_level = openxr_to_notification_level(perf_settings_event->toLevel);
OpenXRInterface *openxr_interface = OpenXRAPI::get_singleton()->get_xr_interface();
if (perf_settings_event->domain == XR_PERF_SETTINGS_DOMAIN_CPU_EXT) {
openxr_interface->on_cpu_level_changed(sub_domain, from_level, to_level);
} else if (perf_settings_event->domain == XR_PERF_SETTINGS_DOMAIN_GPU_EXT) {
openxr_interface->on_gpu_level_changed(sub_domain, from_level, to_level);
} else {
print_error(vformat("OpenXR: no matching performance settings domain for %s", perf_settings_event->domain));
}
return true;
} break;
default:
return false;
}
}
bool OpenXRPerformanceSettingsExtension::is_available() {
return available;
}
void OpenXRPerformanceSettingsExtension::set_cpu_level(OpenXRInterface::PerfSettingsLevel p_level) {
XrSession session = OpenXRAPI::get_singleton()->get_session();
XrPerfSettingsLevelEXT xr_level = level_to_openxr(p_level);
XrResult result = xrPerfSettingsSetPerformanceLevelEXT(session, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, xr_level);
if (XR_FAILED(result)) {
print_error(vformat("OpenXR: failed to set CPU performance level [%s]", OpenXRAPI::get_singleton()->get_error_string(result)));
}
}
void OpenXRPerformanceSettingsExtension::set_gpu_level(OpenXRInterface::PerfSettingsLevel p_level) {
XrSession session = OpenXRAPI::get_singleton()->get_session();
XrPerfSettingsLevelEXT xr_level = level_to_openxr(p_level);
XrResult result = xrPerfSettingsSetPerformanceLevelEXT(session, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, xr_level);
if (XR_FAILED(result)) {
print_error(vformat("OpenXR: failed to set GPU performance level [%s]", OpenXRAPI::get_singleton()->get_error_string(result)));
}
}
XrPerfSettingsLevelEXT OpenXRPerformanceSettingsExtension::level_to_openxr(OpenXRInterface::PerfSettingsLevel p_level) {
switch (p_level) {
case OpenXRInterface::PerfSettingsLevel::PERF_SETTINGS_LEVEL_POWER_SAVINGS:
return XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT;
break;
case OpenXRInterface::PerfSettingsLevel::PERF_SETTINGS_LEVEL_SUSTAINED_LOW:
return XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT;
break;
case OpenXRInterface::PerfSettingsLevel::PERF_SETTINGS_LEVEL_SUSTAINED_HIGH:
return XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT;
case OpenXRInterface::PerfSettingsLevel::PERF_SETTINGS_LEVEL_BOOST:
return XR_PERF_SETTINGS_LEVEL_BOOST_EXT;
default:
print_error("Invalid performance settings level.");
return XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT;
}
}
OpenXRInterface::PerfSettingsSubDomain OpenXRPerformanceSettingsExtension::openxr_to_sub_domain(XrPerfSettingsSubDomainEXT p_sub_domain) {
switch (p_sub_domain) {
case XR_PERF_SETTINGS_SUB_DOMAIN_COMPOSITING_EXT:
return OpenXRInterface::PerfSettingsSubDomain::PERF_SETTINGS_SUB_DOMAIN_COMPOSITING;
case XR_PERF_SETTINGS_SUB_DOMAIN_RENDERING_EXT:
return OpenXRInterface::PerfSettingsSubDomain::PERF_SETTINGS_SUB_DOMAIN_RENDERING;
case XR_PERF_SETTINGS_SUB_DOMAIN_THERMAL_EXT:
return OpenXRInterface::PerfSettingsSubDomain::PERF_SETTINGS_SUB_DOMAIN_THERMAL;
default:
print_error("Invalid performance settings sub domain.");
return OpenXRInterface::PerfSettingsSubDomain::PERF_SETTINGS_SUB_DOMAIN_COMPOSITING;
}
}
OpenXRInterface::PerfSettingsNotificationLevel OpenXRPerformanceSettingsExtension::openxr_to_notification_level(XrPerfSettingsNotificationLevelEXT p_notification_level) {
switch (p_notification_level) {
case XR_PERF_SETTINGS_NOTIF_LEVEL_NORMAL_EXT:
return OpenXRInterface::PerfSettingsNotificationLevel::PERF_SETTINGS_NOTIF_LEVEL_NORMAL;
case XR_PERF_SETTINGS_NOTIF_LEVEL_WARNING_EXT:
return OpenXRInterface::PerfSettingsNotificationLevel::PERF_SETTINGS_NOTIF_LEVEL_WARNING;
case XR_PERF_SETTINGS_NOTIF_LEVEL_IMPAIRED_EXT:
return OpenXRInterface::PerfSettingsNotificationLevel::PERF_SETTINGS_NOTIF_LEVEL_IMPAIRED;
default:
print_error("Invalid performance settings notification level.");
return OpenXRInterface::PerfSettingsNotificationLevel::PERF_SETTINGS_NOTIF_LEVEL_NORMAL;
}
}

View File

@@ -0,0 +1,64 @@
/**************************************************************************/
/* openxr_performance_settings_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. */
/**************************************************************************/
#pragma once
#include "../openxr_interface.h"
#include "../util.h"
#include "openxr_extension_wrapper.h"
class OpenXRPerformanceSettingsExtension : public OpenXRExtensionWrapper {
public:
static OpenXRPerformanceSettingsExtension *get_singleton();
OpenXRPerformanceSettingsExtension();
virtual ~OpenXRPerformanceSettingsExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_instance_created(const XrInstance p_instance) override;
virtual bool on_event_polled(const XrEventDataBuffer &event) override;
bool is_available();
void set_cpu_level(OpenXRInterface::PerfSettingsLevel p_level);
void set_gpu_level(OpenXRInterface::PerfSettingsLevel p_level);
private:
static OpenXRPerformanceSettingsExtension *singleton;
bool available = false;
XrPerfSettingsLevelEXT level_to_openxr(OpenXRInterface::PerfSettingsLevel p_level);
OpenXRInterface::PerfSettingsSubDomain openxr_to_sub_domain(XrPerfSettingsSubDomainEXT p_sub_domain);
OpenXRInterface::PerfSettingsNotificationLevel openxr_to_notification_level(XrPerfSettingsNotificationLevelEXT p_notification_level);
EXT_PROTO_XRRESULT_FUNC3(xrPerfSettingsSetPerformanceLevelEXT, (XrSession), session, (XrPerfSettingsDomainEXT), domain, (XrPerfSettingsLevelEXT), level)
};

View File

@@ -0,0 +1,172 @@
/**************************************************************************/
/* openxr_pico_controller_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_pico_controller_extension.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
HashMap<String, bool *> OpenXRPicoControllerExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
// Note, this used to be XR_PICO_controller_interaction but that has since been retired
// and was never part of the OpenXX specification.
// All PICO devices should be updated to an OS supporting the official extension.
request_extensions[XR_BD_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available;
return request_extensions;
}
bool OpenXRPicoControllerExtension::is_available() {
return available;
}
void OpenXRPicoControllerExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
// Make sure we switch to our new name.
openxr_metadata->register_profile_rename("/interaction_profiles/pico/neo3_controller", "/interaction_profiles/bytedance/pico_neo3_controller");
{ // Pico neo 3 controller.
const String profile_path = "/interaction_profiles/bytedance/pico_neo3_controller";
openxr_metadata->register_interaction_profile("Pico Neo3 controller", profile_path, XR_BD_CONTROLLER_INTERACTION_EXTENSION_NAME);
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Menu click", user_path, user_path + "/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "System click", user_path, user_path + "/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger touch", user_path, user_path + "/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Squeeze", user_path, user_path + "/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick touch", user_path, user_path + "/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
openxr_metadata->register_io_path(profile_path, "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "X touch", "/user/hand/left", "/user/hand/left/input/x/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y touch", "/user/hand/left", "/user/hand/left/input/y/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A touch", "/user/hand/right", "/user/hand/right/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B touch", "/user/hand/right", "/user/hand/right/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
}
{ // Pico 4 controller.
const String profile_path = "/interaction_profiles/bytedance/pico4_controller";
openxr_metadata->register_interaction_profile("Pico 4 controller", profile_path, XR_BD_CONTROLLER_INTERACTION_EXTENSION_NAME);
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "System click", user_path, user_path + "/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger touch", user_path, user_path + "/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Squeeze", user_path, user_path + "/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick touch", user_path, user_path + "/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
openxr_metadata->register_io_path(profile_path, "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
// Note, no menu on right controller!
openxr_metadata->register_io_path(profile_path, "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "X touch", "/user/hand/left", "/user/hand/left/input/x/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y touch", "/user/hand/left", "/user/hand/left/input/y/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A touch", "/user/hand/right", "/user/hand/right/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B touch", "/user/hand/right", "/user/hand/right/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
}
{ // Pico 4 Ultra controller.
const String profile_path = "/interaction_profiles/bytedance/pico4s_controller";
openxr_metadata->register_interaction_profile("Pico 4 Ultra controller", profile_path, XR_BD_CONTROLLER_INTERACTION_EXTENSION_NAME);
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "System click", user_path, user_path + "/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger touch", user_path, user_path + "/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Squeeze", user_path, user_path + "/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick touch", user_path, user_path + "/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
openxr_metadata->register_io_path(profile_path, "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
// Note, no menu on right controller!
openxr_metadata->register_io_path(profile_path, "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "X touch", "/user/hand/left", "/user/hand/left/input/x/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y touch", "/user/hand/left", "/user/hand/left/input/y/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A touch", "/user/hand/right", "/user/hand/right/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B touch", "/user/hand/right", "/user/hand/right/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
}
}

View File

@@ -0,0 +1,50 @@
/**************************************************************************/
/* openxr_pico_controller_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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRPicoControllerExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRPicoControllerExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
virtual void on_register_metadata() override;
private:
bool available = false;
};

View File

@@ -0,0 +1,785 @@
/**************************************************************************/
/* openxr_render_model_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_render_model_extension.h"
#include "../openxr_api.h"
#include "../openxr_interface.h"
#include "core/config/project_settings.h"
#include "core/string/print_string.h"
#include "servers/xr_server.h"
OpenXRRenderModelExtension *OpenXRRenderModelExtension::singleton = nullptr;
OpenXRRenderModelExtension *OpenXRRenderModelExtension::get_singleton() {
return singleton;
}
void OpenXRRenderModelExtension::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_active"), &OpenXRRenderModelExtension::is_active);
ClassDB::bind_method(D_METHOD("render_model_create", "render_model_id"), &OpenXRRenderModelExtension::render_model_create);
ClassDB::bind_method(D_METHOD("render_model_destroy", "render_model"), &OpenXRRenderModelExtension::render_model_destroy);
ClassDB::bind_method(D_METHOD("render_model_get_all"), &OpenXRRenderModelExtension::render_model_get_all);
ClassDB::bind_method(D_METHOD("render_model_new_scene_instance", "render_model"), &OpenXRRenderModelExtension::render_model_new_scene_instance);
ClassDB::bind_method(D_METHOD("render_model_get_subaction_paths", "render_model"), &OpenXRRenderModelExtension::render_model_get_subaction_paths);
ClassDB::bind_method(D_METHOD("render_model_get_top_level_path", "render_model"), &OpenXRRenderModelExtension::render_model_get_top_level_path_as_string);
ClassDB::bind_method(D_METHOD("render_model_get_confidence", "render_model"), &OpenXRRenderModelExtension::render_model_get_confidence);
ClassDB::bind_method(D_METHOD("render_model_get_root_transform", "render_model"), &OpenXRRenderModelExtension::render_model_get_root_transform);
ClassDB::bind_method(D_METHOD("render_model_get_animatable_node_count", "render_model"), &OpenXRRenderModelExtension::render_model_get_animatable_node_count);
ClassDB::bind_method(D_METHOD("render_model_get_animatable_node_name", "render_model", "index"), &OpenXRRenderModelExtension::render_model_get_animatable_node_name);
ClassDB::bind_method(D_METHOD("render_model_is_animatable_node_visible", "render_model", "index"), &OpenXRRenderModelExtension::render_model_is_animatable_node_visible);
ClassDB::bind_method(D_METHOD("render_model_get_animatable_node_transform", "render_model", "index"), &OpenXRRenderModelExtension::render_model_get_animatable_node_transform);
ADD_SIGNAL(MethodInfo("render_model_added", PropertyInfo(Variant::RID, "render_model")));
ADD_SIGNAL(MethodInfo("render_model_removed", PropertyInfo(Variant::RID, "render_model")));
ADD_SIGNAL(MethodInfo("render_model_top_level_path_changed", PropertyInfo(Variant::RID, "render_model")));
}
OpenXRRenderModelExtension::OpenXRRenderModelExtension() {
singleton = this;
}
OpenXRRenderModelExtension::~OpenXRRenderModelExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRRenderModelExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
if (GLOBAL_GET("xr/openxr/extensions/render_model")) {
request_extensions[XR_EXT_UUID_EXTENSION_NAME] = &uuid_ext;
request_extensions[XR_EXT_RENDER_MODEL_EXTENSION_NAME] = &render_model_ext;
request_extensions[XR_EXT_INTERACTION_RENDER_MODEL_EXTENSION_NAME] = &interaction_render_model_ext;
}
return request_extensions;
}
void OpenXRRenderModelExtension::on_instance_created(const XrInstance p_instance) {
// Standard entry points we use.
EXT_INIT_XR_FUNC(xrLocateSpace);
EXT_INIT_XR_FUNC(xrDestroySpace);
EXT_INIT_XR_FUNC(xrPathToString);
if (render_model_ext) {
EXT_INIT_XR_FUNC(xrCreateRenderModelEXT);
EXT_INIT_XR_FUNC(xrDestroyRenderModelEXT);
EXT_INIT_XR_FUNC(xrGetRenderModelPropertiesEXT);
EXT_INIT_XR_FUNC(xrCreateRenderModelSpaceEXT);
EXT_INIT_XR_FUNC(xrCreateRenderModelAssetEXT);
EXT_INIT_XR_FUNC(xrDestroyRenderModelAssetEXT);
EXT_INIT_XR_FUNC(xrGetRenderModelAssetDataEXT);
EXT_INIT_XR_FUNC(xrGetRenderModelAssetPropertiesEXT);
EXT_INIT_XR_FUNC(xrGetRenderModelStateEXT);
}
if (interaction_render_model_ext) {
EXT_INIT_XR_FUNC(xrEnumerateInteractionRenderModelIdsEXT);
EXT_INIT_XR_FUNC(xrEnumerateRenderModelSubactionPathsEXT);
EXT_INIT_XR_FUNC(xrGetRenderModelPoseTopLevelUserPathEXT);
}
}
void OpenXRRenderModelExtension::on_session_created(const XrSession p_session) {
_interaction_data_dirty = true;
}
void OpenXRRenderModelExtension::on_instance_destroyed() {
xrCreateRenderModelEXT_ptr = nullptr;
xrDestroyRenderModelEXT_ptr = nullptr;
xrGetRenderModelPropertiesEXT_ptr = nullptr;
xrCreateRenderModelSpaceEXT_ptr = nullptr;
xrCreateRenderModelAssetEXT_ptr = nullptr;
xrDestroyRenderModelAssetEXT_ptr = nullptr;
xrGetRenderModelAssetDataEXT_ptr = nullptr;
xrGetRenderModelAssetPropertiesEXT_ptr = nullptr;
xrGetRenderModelStateEXT_ptr = nullptr;
xrEnumerateInteractionRenderModelIdsEXT_ptr = nullptr;
xrEnumerateRenderModelSubactionPathsEXT_ptr = nullptr;
xrGetRenderModelPoseTopLevelUserPathEXT_ptr = nullptr;
uuid_ext = false;
render_model_ext = false;
interaction_render_model_ext = false;
}
void OpenXRRenderModelExtension::on_session_destroyed() {
_clear_interaction_data();
_clear_render_model_data();
// We no longer have valid sync data.
xr_sync_has_run = false;
}
bool OpenXRRenderModelExtension::on_event_polled(const XrEventDataBuffer &event) {
if (event.type == XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT) {
// Mark interaction data as dirty so that we update it on sync.
_interaction_data_dirty = true;
return true;
} else if (event.type == XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED) {
// If our controller bindings changed, its likely our render models change too.
// We should be getting a XR_TYPE_EVENT_DATA_INTERACTION_RENDER_MODELS_CHANGED_EXT
// but checking for this scenario just in case.
_interaction_data_dirty = true;
// Do not consider this handled, we simply do additional logic.
return false;
}
return false;
}
void OpenXRRenderModelExtension::on_sync_actions() {
if (!is_active()) {
return;
}
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL(openxr_api);
// Mark sync as run
xr_sync_has_run = true;
// Update our interaction data if needed
if (_interaction_data_dirty) {
_update_interaction_data();
}
// Loop through all of our render models to update our space and state info
LocalVector<RID> owned = render_model_owner.get_owned_list();
for (const RID &rid : owned) {
RenderModel *render_model = render_model_owner.get_or_null(rid);
if (render_model && render_model->xr_space != XR_NULL_HANDLE) {
XrSpaceLocation render_model_location = {
XR_TYPE_SPACE_LOCATION, // type
nullptr, // next
0, // locationFlags
{ { 0.0, 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 } }, // pose
};
XrResult result = xrLocateSpace(render_model->xr_space, openxr_api->get_play_space(), openxr_api->get_predicted_display_time(), &render_model_location);
ERR_CONTINUE_MSG(XR_FAILED(result), "OpenXR: Failed to locate render model space [" + openxr_api->get_error_string(result) + "]");
render_model->confidence = openxr_api->transform_from_location(render_model_location, render_model->root_transform);
if (!render_model->node_states.is_empty()) {
// Get node states.
XrRenderModelStateGetInfoEXT get_state_info = {
XR_TYPE_RENDER_MODEL_STATE_GET_INFO_EXT, // type
nullptr, // next
openxr_api->get_predicted_display_time() // displayTime
};
XrRenderModelStateEXT state = {
XR_TYPE_RENDER_MODEL_STATE_EXT, // type
nullptr, // next
render_model->animatable_node_count, // nodeStateCount
render_model->node_states.ptr(), // nodeStates
};
result = xrGetRenderModelStateEXT(render_model->xr_render_model, &get_state_info, &state);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to update node states [" + openxr_api->get_error_string(result) + "]");
}
}
XrPath new_path = XR_NULL_PATH;
if (toplevel_paths.is_empty()) {
// Set this up just once with paths we support here.
toplevel_paths.push_back(openxr_api->get_xr_path("/user/hand/left"));
toplevel_paths.push_back(openxr_api->get_xr_path("/user/hand/right"));
}
XrInteractionRenderModelTopLevelUserPathGetInfoEXT info = {
XR_TYPE_INTERACTION_RENDER_MODEL_TOP_LEVEL_USER_PATH_GET_INFO_EXT, // type
nullptr, // next
(uint32_t)toplevel_paths.size(), // topLevelUserPathCount
toplevel_paths.ptr() // topLevelUserPaths
};
result = xrGetRenderModelPoseTopLevelUserPathEXT(render_model->xr_render_model, &info, &new_path);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to update the top level path for render models [" + openxr_api->get_error_string(result) + "]");
} else if (new_path != render_model->top_level_path) {
print_verbose("OpenXR: Render model top level path changed to " + openxr_api->get_xr_path_name(new_path));
// Set the new path
render_model->top_level_path = new_path;
// And broadcast it
// Note, converting an XrPath to a String has overhead, so we won't do this automatically.
emit_signal(SNAME("render_model_top_level_path_changed"), rid);
}
}
}
}
bool OpenXRRenderModelExtension::is_active() const {
return render_model_ext && interaction_render_model_ext;
}
void OpenXRRenderModelExtension::_clear_interaction_data() {
for (const KeyValue<XrRenderModelIdEXT, RID> &e : interaction_render_models) {
render_model_destroy(e.value);
}
interaction_render_models.clear();
}
bool OpenXRRenderModelExtension::_update_interaction_data() {
ERR_FAIL_COND_V_MSG(!interaction_render_model_ext, false, "Interaction render model extension hasn't been enabled.");
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, false);
XrSession session = openxr_api->get_session();
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
// Check if syncActions has been run at least once or there is no point in getting data.
if (!xr_sync_has_run) {
// Do not treat this as an error.
return true;
}
// If we get this far, no longer mark as dirty.
// Else we just repeat the same error over and over again.
_interaction_data_dirty = false;
// Obtain interaction info.
XrInteractionRenderModelIdsEnumerateInfoEXT interaction_info = {
XR_TYPE_INTERACTION_RENDER_MODEL_IDS_ENUMERATE_INFO_EXT, // type
nullptr, // next
};
// Obtain count.
uint32_t interaction_count = 0;
XrResult result = xrEnumerateInteractionRenderModelIdsEXT(session, &interaction_info, 0, &interaction_count, nullptr);
if (XR_FAILED(result)) {
// not successful? then we do nothing.
ERR_FAIL_V_MSG(false, "OpenXR: Failed to obtain render model interaction id count [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
// Create some storage
LocalVector<XrRenderModelIdEXT> render_model_interaction_ids;
render_model_interaction_ids.resize(interaction_count);
// Only need to fetch data if there is something to fetch (/we've got storage).
if (!render_model_interaction_ids.is_empty()) {
// Obtain interaction ids
result = xrEnumerateInteractionRenderModelIdsEXT(session, &interaction_info, render_model_interaction_ids.size(), &interaction_count, render_model_interaction_ids.ptr());
if (XR_FAILED(result)) {
ERR_FAIL_V_MSG(false, "OpenXR: Failed to obtain render model interaction ids [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
}
// Remove render models that are no longer tracked
LocalVector<XrRenderModelIdEXT> erase_ids;
for (const KeyValue<XrRenderModelIdEXT, RID> &e : interaction_render_models) {
if (!render_model_interaction_ids.has(e.key)) {
if (e.value.is_valid()) {
render_model_destroy(e.value);
}
erase_ids.push_back(e.key);
}
}
// Remove these from our hashmap
for (const XrRenderModelIdEXT &id : erase_ids) {
interaction_render_models.erase(id);
}
// Now update our models
for (const XrRenderModelIdEXT &id : render_model_interaction_ids) {
if (!interaction_render_models.has(id)) {
// Even if this fails we add it so we don't repeat trying to create it
interaction_render_models[id] = render_model_create(id);
}
}
return true;
}
bool OpenXRRenderModelExtension::has_render_model(RID p_render_model) const {
return render_model_owner.owns(p_render_model);
}
RID OpenXRRenderModelExtension::render_model_create(XrRenderModelIdEXT p_render_model_id) {
ERR_FAIL_COND_V_MSG(!render_model_ext, RID(), "Render model extension hasn't been enabled.");
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, RID());
XrSession session = openxr_api->get_session();
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, RID());
RenderModel render_model;
render_model.xr_render_model_id = p_render_model_id;
// Get a list of supported glTF extensions.
const HashSet<String> supported_gltf_extensions_hash_set = GLTFDocument::get_supported_gltf_extensions_hashset();
Vector<CharString> supported_gltf_extensions_char_string; // Just for temp storage of our c-strings.
supported_gltf_extensions_char_string.resize(supported_gltf_extensions_hash_set.size());
int64_t supported_gltf_extension_index = 0;
for (const String &ext : supported_gltf_extensions_hash_set) {
supported_gltf_extensions_char_string.set(supported_gltf_extension_index, ext.utf8());
supported_gltf_extension_index++;
}
// Now we can convert them to the `const char *` format.
Vector<const char *> supported_gltf_extensions;
supported_gltf_extensions.resize(supported_gltf_extensions_char_string.size());
for (int64_t i = 0; i < supported_gltf_extensions_char_string.size(); i++) {
supported_gltf_extensions.write[i] = supported_gltf_extensions_char_string[i].get_data();
}
XrRenderModelCreateInfoEXT create_info = {
XR_TYPE_RENDER_MODEL_CREATE_INFO_EXT, // type
nullptr, // next
p_render_model_id, // renderModelId
uint32_t(supported_gltf_extensions.size()), // gltfExtensionCount
supported_gltf_extensions.ptr(), // gltfExtensions
};
XrResult result = xrCreateRenderModelEXT(session, &create_info, &render_model.xr_render_model);
if (XR_FAILED(result)) {
ERR_FAIL_V_MSG(RID(), "OpenXR: Failed to create render model [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
XrRenderModelPropertiesGetInfoEXT properties_info = {
XR_TYPE_RENDER_MODEL_PROPERTIES_GET_INFO_EXT, // type
nullptr, // next
};
XrRenderModelPropertiesEXT properties;
result = xrGetRenderModelPropertiesEXT(render_model.xr_render_model, &properties_info, &properties);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to get render model properties [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
} else {
render_model.animatable_node_count = properties.animatableNodeCount;
render_model.render_model_data = _get_render_model_data(properties.cacheId, properties.animatableNodeCount);
}
// Create space for positioning our asset.
XrRenderModelSpaceCreateInfoEXT space_create_info = {
XR_TYPE_RENDER_MODEL_SPACE_CREATE_INFO_EXT, // type
nullptr, // next
render_model.xr_render_model // renderModel
};
result = xrCreateRenderModelSpaceEXT(session, &space_create_info, &render_model.xr_space);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to create render model space [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
if (render_model.animatable_node_count > 0) {
render_model.node_states.resize(render_model.animatable_node_count);
}
RID new_rid = render_model_owner.make_rid(render_model);
emit_signal(SNAME("render_model_added"), new_rid);
return new_rid;
}
RID OpenXRRenderModelExtension::_render_model_create(uint64_t p_render_model_id) {
RID ret;
ERR_FAIL_COND_V(p_render_model_id == XR_NULL_RENDER_MODEL_ID_EXT, ret);
if (is_active()) {
ret = render_model_create(XrRenderModelIdEXT(p_render_model_id));
}
return ret;
}
void OpenXRRenderModelExtension::render_model_destroy(RID p_render_model) {
ERR_FAIL_COND_MSG(!render_model_ext, "Render model extension hasn't been enabled.");
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
ERR_FAIL_NULL(render_model);
emit_signal(SNAME("render_model_removed"), p_render_model);
// Clean up.
if (render_model->xr_space != XR_NULL_HANDLE) {
xrDestroySpace(render_model->xr_space);
}
render_model->node_states.clear();
// And destroy our model.
XrResult result = xrDestroyRenderModelEXT(render_model->xr_render_model);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to destroy render model [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
render_model_owner.free(p_render_model);
}
TypedArray<RID> OpenXRRenderModelExtension::render_model_get_all() {
TypedArray<RID> ret;
LocalVector<RID> rids = render_model_owner.get_owned_list();
for (const RID &rid : rids) {
ret.push_back(rid);
}
return ret;
}
Node3D *OpenXRRenderModelExtension::render_model_new_scene_instance(RID p_render_model) const {
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
ERR_FAIL_NULL_V(render_model, nullptr);
if (render_model->render_model_data.is_null()) {
// We never loaded it (don't spam errors here).
return nullptr;
}
return render_model->render_model_data->new_scene_instance();
}
PackedStringArray OpenXRRenderModelExtension::render_model_get_subaction_paths(RID p_render_model) {
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, PackedStringArray());
XrInstance instance = openxr_api->get_instance();
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, PackedStringArray());
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
ERR_FAIL_NULL_V(render_model, PackedStringArray());
PackedStringArray subaction_paths;
XrInteractionRenderModelSubactionPathInfoEXT subaction_info = {
XR_TYPE_INTERACTION_RENDER_MODEL_SUBACTION_PATH_INFO_EXT, // type
nullptr, // next
};
uint32_t capacity;
XrResult result = xrEnumerateRenderModelSubactionPathsEXT(render_model->xr_render_model, &subaction_info, 0, &capacity, nullptr);
if (XR_FAILED(result)) {
ERR_FAIL_V_MSG(PackedStringArray(), "OpenXR: Failed to obtain render model subaction path count [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
if (capacity > 0) {
LocalVector<XrPath> paths;
paths.resize(capacity);
result = xrEnumerateRenderModelSubactionPathsEXT(render_model->xr_render_model, &subaction_info, capacity, &capacity, paths.ptr());
if (XR_FAILED(result)) {
ERR_FAIL_V_MSG(PackedStringArray(), "OpenXR: Failed to obtain render model subaction paths [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
for (uint32_t i = 0; i < capacity; i++) {
char buffer[1024];
uint32_t size = 0;
xrPathToString(instance, paths[i], 1024, &size, buffer);
if (size > 0) {
subaction_paths.push_back(String(buffer));
}
}
}
return subaction_paths;
}
XrPath OpenXRRenderModelExtension::render_model_get_top_level_path(RID p_render_model) const {
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
ERR_FAIL_NULL_V(render_model, XRPose::TrackingConfidence::XR_TRACKING_CONFIDENCE_NONE);
return render_model->top_level_path;
}
String OpenXRRenderModelExtension::render_model_get_top_level_path_as_string(RID p_render_model) const {
String ret;
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, ret);
if (is_active() && has_render_model(p_render_model)) {
XrPath path = render_model_get_top_level_path(p_render_model);
if (path == XR_NULL_PATH) {
return "None";
} else {
return openxr_api->get_xr_path_name(path);
}
}
return ret;
}
XRPose::TrackingConfidence OpenXRRenderModelExtension::render_model_get_confidence(RID p_render_model) const {
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
ERR_FAIL_NULL_V(render_model, XRPose::TrackingConfidence::XR_TRACKING_CONFIDENCE_NONE);
return render_model->confidence;
}
Transform3D OpenXRRenderModelExtension::render_model_get_root_transform(RID p_render_model) const {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, Transform3D());
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
ERR_FAIL_NULL_V(render_model, Transform3D());
// Scale our root transform
real_t world_scale = xr_server->get_world_scale();
Transform3D root_transform = render_model->root_transform.scaled(Vector3(world_scale, world_scale, world_scale));
return xr_server->get_reference_frame() * root_transform;
}
uint32_t OpenXRRenderModelExtension::render_model_get_animatable_node_count(RID p_render_model) const {
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
ERR_FAIL_NULL_V(render_model, 0);
return render_model->animatable_node_count;
}
String OpenXRRenderModelExtension::render_model_get_animatable_node_name(RID p_render_model, uint32_t p_index) const {
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
ERR_FAIL_NULL_V(render_model, String());
if (render_model->render_model_data.is_null()) {
// We never loaded it (don't spam errors here).
return String();
}
return render_model->render_model_data->get_node_name(p_index);
}
bool OpenXRRenderModelExtension::render_model_is_animatable_node_visible(RID p_render_model, uint32_t p_index) const {
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
ERR_FAIL_NULL_V(render_model, false);
ERR_FAIL_UNSIGNED_INDEX_V(p_index, render_model->animatable_node_count, false);
if (render_model->node_states.is_empty()) {
// Never allocated (don't spam errors here).
return false;
}
return render_model->node_states[p_index].isVisible;
}
Transform3D OpenXRRenderModelExtension::render_model_get_animatable_node_transform(RID p_render_model, uint32_t p_index) const {
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, Transform3D());
RenderModel *render_model = render_model_owner.get_or_null(p_render_model);
ERR_FAIL_NULL_V(render_model, Transform3D());
ERR_FAIL_UNSIGNED_INDEX_V(p_index, render_model->animatable_node_count, Transform3D());
if (render_model->node_states.is_empty()) {
// Never allocated (don't spam errors here).
return Transform3D();
}
return openxr_api->transform_from_pose(render_model->node_states[p_index].nodePose);
}
Ref<OpenXRRenderModelData> OpenXRRenderModelExtension::_get_render_model_data(XrUuidEXT p_cache_id, uint32_t p_animatable_node_count) {
if (render_model_data_cache.has(p_cache_id)) {
return render_model_data_cache[p_cache_id];
}
// We don't have this cached, lets load it up
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, nullptr);
XrSession session = openxr_api->get_session();
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, nullptr);
XrRenderModelAssetEXT asset;
XrRenderModelAssetCreateInfoEXT create_info = {
XR_TYPE_RENDER_MODEL_ASSET_CREATE_INFO_EXT, // type
nullptr, // next
p_cache_id // cacheId
};
XrResult result = xrCreateRenderModelAssetEXT(session, &create_info, &asset);
if (XR_FAILED(result)) {
ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to create render model asset [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
Ref<OpenXRRenderModelData> render_model_data = _load_asset(asset, p_animatable_node_count);
// We're done with this :)
result = xrDestroyRenderModelAssetEXT(asset);
if (XR_FAILED(result)) {
ERR_PRINT("OpenXR: Failed to destroy render model asset [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
// And cache it
render_model_data_cache[p_cache_id] = render_model_data;
return render_model_data;
}
Ref<OpenXRRenderModelData> OpenXRRenderModelExtension::_load_asset(XrRenderModelAssetEXT p_asset, uint32_t p_animatable_node_count) {
XrRenderModelAssetDataGetInfoEXT get_info = {
XR_TYPE_RENDER_MODEL_ASSET_DATA_GET_INFO_EXT, // type
nullptr, // next
};
XrRenderModelAssetDataEXT asset_data = {
XR_TYPE_RENDER_MODEL_ASSET_DATA_EXT, // type
nullptr, // next
0, // bufferCapacityInput;
0, // bufferCountOutput;
nullptr // buffer;
};
// Obtain required size for the buffer.
XrResult result = xrGetRenderModelAssetDataEXT(p_asset, &get_info, &asset_data);
if (XR_FAILED(result)) {
ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to get render model buffer size [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
ERR_FAIL_COND_V(asset_data.bufferCountOutput == 0, nullptr);
// Allocate data
PackedByteArray buffer;
buffer.resize(asset_data.bufferCountOutput);
asset_data.buffer = buffer.ptrw();
asset_data.bufferCapacityInput = asset_data.bufferCountOutput;
// Now get our actual data.
result = xrGetRenderModelAssetDataEXT(p_asset, &get_info, &asset_data);
if (XR_FAILED(result)) {
ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to get render model buffer [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
// Get the names of any animatable nodes
PackedStringArray node_names;
if (p_animatable_node_count > 0) {
Vector<XrRenderModelAssetNodePropertiesEXT> node_properties;
node_properties.resize(p_animatable_node_count);
XrRenderModelAssetPropertiesGetInfoEXT properties_info = {
XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_GET_INFO_EXT, // type
nullptr, // next
};
XrRenderModelAssetPropertiesEXT asset_properties = {
XR_TYPE_RENDER_MODEL_ASSET_PROPERTIES_EXT, // type
nullptr, // next
uint32_t(node_properties.size()), // nodePropertyCount
node_properties.ptrw(), // nodeProperties
};
result = xrGetRenderModelAssetPropertiesEXT(p_asset, &properties_info, &asset_properties);
if (XR_FAILED(result)) {
ERR_FAIL_V_MSG(nullptr, "OpenXR: Failed to get render model property info [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
}
node_names.resize(p_animatable_node_count);
String *node_names_ptrw = node_names.ptrw();
for (uint32_t i = 0; i < p_animatable_node_count; i++) {
node_names_ptrw[i] = String(node_properties[i].uniqueName);
}
}
Ref<OpenXRRenderModelData> render_model_data;
render_model_data.instantiate();
render_model_data->parse_gltf_document(buffer);
render_model_data->set_node_names(node_names);
return render_model_data;
}
void OpenXRRenderModelExtension::_clear_render_model_data() {
// Clear our toplevel paths filter.
toplevel_paths.clear();
// Clear our render model cache.
render_model_data_cache.clear();
// Loop through all of our render models and destroy them.
LocalVector<RID> owned = render_model_owner.get_owned_list();
for (const RID &rid : owned) {
render_model_destroy(rid);
}
}
bool OpenXRRenderModelData::parse_gltf_document(const PackedByteArray &p_bytes) {
// State holds our data, document parses GLTF
Ref<GLTFState> new_state;
new_state.instantiate();
Ref<GLTFDocument> new_gltf_document;
new_gltf_document.instantiate();
Error err = new_gltf_document->append_from_buffer(p_bytes, "", new_state);
if (err != OK) {
ERR_FAIL_V_MSG(false, "OpenXR: Failed to parse GLTF data.");
}
gltf_document = new_gltf_document;
gltf_state = new_state;
return true;
}
Node3D *OpenXRRenderModelData::new_scene_instance() {
ERR_FAIL_COND_V(gltf_document.is_null(), nullptr);
ERR_FAIL_COND_V(gltf_state.is_null(), nullptr);
return Object::cast_to<Node3D>(gltf_document->generate_scene(gltf_state));
}
void OpenXRRenderModelData::set_node_names(const PackedStringArray &p_node_names) {
node_names = p_node_names;
}
PackedStringArray OpenXRRenderModelData::get_node_names() const {
return node_names;
}
const String OpenXRRenderModelData::get_node_name(uint32_t p_node_index) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_node_index, node_names.size(), String());
return node_names[p_node_index];
}
OpenXRRenderModelData::OpenXRRenderModelData() {
}
OpenXRRenderModelData::~OpenXRRenderModelData() {
}

View File

@@ -0,0 +1,168 @@
/**************************************************************************/
/* openxr_render_model_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. */
/**************************************************************************/
#pragma once
#include "../openxr_uuid.h"
#include "../util.h"
#include "core/templates/rid_owner.h"
#include "modules/gltf/gltf_document.h"
#include "openxr_extension_wrapper.h"
#include "scene/3d/node_3d.h"
#include "servers/xr/xr_pose.h"
#include <openxr/openxr.h>
class OpenXRRenderModelData : public RefCounted {
GDCLASS(OpenXRRenderModelData, RefCounted);
private:
Ref<GLTFDocument> gltf_document;
Ref<GLTFState> gltf_state;
PackedStringArray node_names;
public:
Ref<GLTFState> get_gltf_state() { return gltf_state; }
bool parse_gltf_document(const PackedByteArray &p_bytes);
Node3D *new_scene_instance();
void set_node_names(const PackedStringArray &p_node_names);
PackedStringArray get_node_names() const;
const String get_node_name(uint32_t p_node_index) const;
OpenXRRenderModelData();
~OpenXRRenderModelData();
};
class OpenXRRenderModelExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRRenderModelExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods();
public:
static OpenXRRenderModelExtension *get_singleton();
OpenXRRenderModelExtension();
virtual ~OpenXRRenderModelExtension() 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_session) override;
virtual void on_instance_destroyed() override;
virtual void on_session_destroyed() override;
virtual bool on_event_polled(const XrEventDataBuffer &event) override;
virtual void on_sync_actions() override;
bool is_active() const;
// Render model.
bool has_render_model(RID p_render_model) const;
RID render_model_create(XrRenderModelIdEXT p_render_model_id);
void render_model_destroy(RID p_render_model);
TypedArray<RID> render_model_get_all();
Node3D *render_model_new_scene_instance(RID p_render_model) const;
PackedStringArray render_model_get_subaction_paths(RID p_render_model);
XrPath render_model_get_top_level_path(RID p_render_model) const;
String render_model_get_top_level_path_as_string(RID p_render_model) const;
XRPose::TrackingConfidence render_model_get_confidence(RID p_render_model) const;
Transform3D render_model_get_root_transform(RID p_render_model) const;
uint32_t render_model_get_animatable_node_count(RID p_render_model) const;
String render_model_get_animatable_node_name(RID p_render_model, uint32_t p_index) const;
bool render_model_is_animatable_node_visible(RID p_render_model, uint32_t p_index) const;
Transform3D render_model_get_animatable_node_transform(RID p_render_model, uint32_t p_index) const;
private:
static OpenXRRenderModelExtension *singleton;
// Related extensions.
bool uuid_ext = false;
bool render_model_ext = false;
bool interaction_render_model_ext = false;
// XrSync status
bool xr_sync_has_run = false;
// Interaction data.
bool _interaction_data_dirty = true;
HashMap<XrRenderModelIdEXT, RID> interaction_render_models;
void _clear_interaction_data();
bool _update_interaction_data();
// Render model.
Vector<XrPath> toplevel_paths;
struct RenderModel {
XrRenderModelIdEXT xr_render_model_id = XR_NULL_RENDER_MODEL_ID_EXT;
XrRenderModelEXT xr_render_model = XR_NULL_HANDLE;
uint32_t animatable_node_count = 0;
Ref<OpenXRRenderModelData> render_model_data;
XrSpace xr_space = XR_NULL_HANDLE;
XRPose::TrackingConfidence confidence = XRPose::TrackingConfidence::XR_TRACKING_CONFIDENCE_NONE;
Transform3D root_transform;
LocalVector<XrRenderModelNodeStateEXT> node_states;
XrPath top_level_path = XR_NULL_PATH;
};
mutable RID_Owner<RenderModel, true> render_model_owner;
// GLTF asset cache
HashMap<XrUuidEXT, Ref<OpenXRRenderModelData>, HashMapHasherXrUuidEXT> render_model_data_cache;
Ref<OpenXRRenderModelData> _get_render_model_data(XrUuidEXT p_cache_id, uint32_t p_animatable_node_count);
Ref<OpenXRRenderModelData> _load_asset(XrRenderModelAssetEXT p_asset, uint32_t p_animatable_node_count);
void _clear_render_model_data();
// GDScript/GDExtension passthroughs
RID _render_model_create(uint64_t p_render_model_id);
// OpenXR API call wrappers
EXT_PROTO_XRRESULT_FUNC3(xrCreateRenderModelEXT, (XrSession), session, (const XrRenderModelCreateInfoEXT *), createInfo, (XrRenderModelEXT *), renderModel);
EXT_PROTO_XRRESULT_FUNC1(xrDestroyRenderModelEXT, (XrRenderModelEXT), renderModel);
EXT_PROTO_XRRESULT_FUNC3(xrGetRenderModelPropertiesEXT, (XrRenderModelEXT), renderModel, (const XrRenderModelPropertiesGetInfoEXT *), getInfo, (XrRenderModelPropertiesEXT *), properties);
EXT_PROTO_XRRESULT_FUNC3(xrCreateRenderModelSpaceEXT, (XrSession), session, (const XrRenderModelSpaceCreateInfoEXT *), createInfo, (XrSpace *), space);
EXT_PROTO_XRRESULT_FUNC3(xrCreateRenderModelAssetEXT, (XrSession), session, (const XrRenderModelAssetCreateInfoEXT *), createInfo, (XrRenderModelAssetEXT *), asset);
EXT_PROTO_XRRESULT_FUNC1(xrDestroyRenderModelAssetEXT, (XrRenderModelAssetEXT), asset);
EXT_PROTO_XRRESULT_FUNC3(xrGetRenderModelAssetDataEXT, (XrRenderModelAssetEXT), asset, (const XrRenderModelAssetDataGetInfoEXT *), getInfo, (XrRenderModelAssetDataEXT *), buffer);
EXT_PROTO_XRRESULT_FUNC3(xrGetRenderModelAssetPropertiesEXT, (XrRenderModelAssetEXT), asset, (const XrRenderModelAssetPropertiesGetInfoEXT *), getInfo, (XrRenderModelAssetPropertiesEXT *), properties);
EXT_PROTO_XRRESULT_FUNC3(xrGetRenderModelStateEXT, (XrRenderModelEXT), renderModel, (const XrRenderModelStateGetInfoEXT *), getInfo, (XrRenderModelStateEXT *), state);
EXT_PROTO_XRRESULT_FUNC5(xrEnumerateInteractionRenderModelIdsEXT, (XrSession), session, (const XrInteractionRenderModelIdsEnumerateInfoEXT *), getInfo, (uint32_t), renderModelIdCapacityInput, (uint32_t *), renderModelIdCountOutput, (XrRenderModelIdEXT *), renderModelIds);
EXT_PROTO_XRRESULT_FUNC5(xrEnumerateRenderModelSubactionPathsEXT, (XrRenderModelEXT), renderModel, (const XrInteractionRenderModelSubactionPathInfoEXT *), info, (uint32_t), pathCapacityInput, (uint32_t *), pathCountOutput, (XrPath *), paths);
EXT_PROTO_XRRESULT_FUNC3(xrGetRenderModelPoseTopLevelUserPathEXT, (XrRenderModelEXT), renderModel, (const XrInteractionRenderModelTopLevelUserPathGetInfoEXT *), info, (XrPath *), topLevelUserPath);
EXT_PROTO_XRRESULT_FUNC4(xrLocateSpace, (XrSpace), space, (XrSpace), baseSpace, (XrTime), time, (XrSpaceLocation *), location);
EXT_PROTO_XRRESULT_FUNC1(xrDestroySpace, (XrSpace), space);
EXT_PROTO_XRRESULT_FUNC5(xrPathToString, (XrInstance), instance, (XrPath), path, (uint32_t), bufferCapacityInput, (uint32_t *), bufferCountOutput, (char *), buffer);
};

View File

@@ -0,0 +1,193 @@
/**************************************************************************/
/* openxr_valve_analog_threshold_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_valve_analog_threshold_extension.h"
#include "../action_map/openxr_action_set.h"
#include "../action_map/openxr_interaction_profile.h"
#include "../openxr_api.h"
// Implementation for:
// https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_VALVE_analog_threshold
///////////////////////////////////////////////////////////////////////////////////////////////////
// OpenXRValveAnalogThresholdExtension
OpenXRValveAnalogThresholdExtension *OpenXRValveAnalogThresholdExtension::singleton = nullptr;
OpenXRValveAnalogThresholdExtension *OpenXRValveAnalogThresholdExtension::get_singleton() {
return singleton;
}
OpenXRValveAnalogThresholdExtension::OpenXRValveAnalogThresholdExtension() {
singleton = this;
}
OpenXRValveAnalogThresholdExtension::~OpenXRValveAnalogThresholdExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRValveAnalogThresholdExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
// Note, we're dependent on the binding modifier extension, this may be requested by multiple extension wrappers.
request_extensions[XR_KHR_BINDING_MODIFICATION_EXTENSION_NAME] = &binding_modifier_ext;
request_extensions[XR_VALVE_ANALOG_THRESHOLD_EXTENSION_NAME] = &threshold_ext;
return request_extensions;
}
bool OpenXRValveAnalogThresholdExtension::is_available() {
return binding_modifier_ext && threshold_ext;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// OpenXRAnalogThresholdModifier
void OpenXRAnalogThresholdModifier::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_on_threshold", "on_threshold"), &OpenXRAnalogThresholdModifier::set_on_threshold);
ClassDB::bind_method(D_METHOD("get_on_threshold"), &OpenXRAnalogThresholdModifier::get_on_threshold);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "on_threshold", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_on_threshold", "get_on_threshold");
ClassDB::bind_method(D_METHOD("set_off_threshold", "off_threshold"), &OpenXRAnalogThresholdModifier::set_off_threshold);
ClassDB::bind_method(D_METHOD("get_off_threshold"), &OpenXRAnalogThresholdModifier::get_off_threshold);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "off_threshold", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_off_threshold", "get_off_threshold");
ClassDB::bind_method(D_METHOD("set_on_haptic", "haptic"), &OpenXRAnalogThresholdModifier::set_on_haptic);
ClassDB::bind_method(D_METHOD("get_on_haptic"), &OpenXRAnalogThresholdModifier::get_on_haptic);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "on_haptic", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRHapticBase"), "set_on_haptic", "get_on_haptic");
ClassDB::bind_method(D_METHOD("set_off_haptic", "haptic"), &OpenXRAnalogThresholdModifier::set_off_haptic);
ClassDB::bind_method(D_METHOD("get_off_haptic"), &OpenXRAnalogThresholdModifier::get_off_haptic);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "off_haptic", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRHapticBase"), "set_off_haptic", "get_off_haptic");
}
OpenXRAnalogThresholdModifier::OpenXRAnalogThresholdModifier() {
analog_threshold.type = XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE;
analog_threshold.next = nullptr;
analog_threshold.onThreshold = 0.6;
analog_threshold.offThreshold = 0.4;
}
void OpenXRAnalogThresholdModifier::set_on_threshold(float p_threshold) {
ERR_FAIL_COND(p_threshold < 0.0 || p_threshold > 1.0);
analog_threshold.onThreshold = p_threshold;
emit_changed();
}
float OpenXRAnalogThresholdModifier::get_on_threshold() const {
return analog_threshold.onThreshold;
}
void OpenXRAnalogThresholdModifier::set_off_threshold(float p_threshold) {
ERR_FAIL_COND(p_threshold < 0.0 || p_threshold > 1.0);
analog_threshold.offThreshold = p_threshold;
emit_changed();
}
float OpenXRAnalogThresholdModifier::get_off_threshold() const {
return analog_threshold.offThreshold;
}
void OpenXRAnalogThresholdModifier::set_on_haptic(const Ref<OpenXRHapticBase> &p_haptic) {
on_haptic = p_haptic;
emit_changed();
}
Ref<OpenXRHapticBase> OpenXRAnalogThresholdModifier::get_on_haptic() const {
return on_haptic;
}
void OpenXRAnalogThresholdModifier::set_off_haptic(const Ref<OpenXRHapticBase> &p_haptic) {
off_haptic = p_haptic;
emit_changed();
}
Ref<OpenXRHapticBase> OpenXRAnalogThresholdModifier::get_off_haptic() const {
return off_haptic;
}
PackedByteArray OpenXRAnalogThresholdModifier::get_ip_modification() {
PackedByteArray ret;
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
ERR_FAIL_NULL_V(openxr_api, ret);
OpenXRValveAnalogThresholdExtension *analog_threshold_ext = OpenXRValveAnalogThresholdExtension::get_singleton();
if (!analog_threshold_ext || !analog_threshold_ext->is_available()) {
// Extension not enabled!
WARN_PRINT("Analog threshold extension is not enabled or available.");
return ret;
}
ERR_FAIL_NULL_V(ip_binding, ret);
Ref<OpenXRAction> action = ip_binding->get_action();
ERR_FAIL_COND_V(action.is_null(), ret);
// Get our action set
Ref<OpenXRActionSet> action_set = action->get_action_set();
ERR_FAIL_COND_V(action_set.is_null(), ret);
RID action_set_rid = openxr_api->find_action_set(action_set->get_name());
ERR_FAIL_COND_V(!action_set_rid.is_valid(), ret);
// Get our action
RID action_rid = openxr_api->find_action(action->get_name(), action_set_rid);
ERR_FAIL_COND_V(!action_rid.is_valid(), ret);
analog_threshold.action = openxr_api->action_get_handle(action_rid);
analog_threshold.binding = openxr_api->get_xr_path(ip_binding->get_binding_path());
ERR_FAIL_COND_V(analog_threshold.binding == XR_NULL_PATH, ret);
// These are set already:
// - analog_threshold.onThreshold
// - analog_threshold.offThreshold
if (on_haptic.is_valid()) {
analog_threshold.onHaptic = on_haptic->get_xr_structure();
} else {
analog_threshold.onHaptic = nullptr;
}
if (off_haptic.is_valid()) {
analog_threshold.offHaptic = off_haptic->get_xr_structure();
} else {
analog_threshold.offHaptic = nullptr;
}
// Copy into byte array so we can return it.
ERR_FAIL_COND_V(ret.resize(sizeof(XrInteractionProfileAnalogThresholdVALVE)) != OK, ret);
memcpy(ret.ptrw(), &analog_threshold, sizeof(XrInteractionProfileAnalogThresholdVALVE));
return ret;
}

View File

@@ -0,0 +1,90 @@
/**************************************************************************/
/* openxr_valve_analog_threshold_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. */
/**************************************************************************/
#pragma once
#include "../action_map/openxr_binding_modifier.h"
#include "../action_map/openxr_haptic_feedback.h"
#include "../util.h"
#include "core/io/resource.h"
#include "openxr_extension_wrapper.h"
class OpenXRValveAnalogThresholdExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRValveAnalogThresholdExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
static OpenXRValveAnalogThresholdExtension *get_singleton();
OpenXRValveAnalogThresholdExtension();
virtual ~OpenXRValveAnalogThresholdExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
private:
static OpenXRValveAnalogThresholdExtension *singleton;
bool binding_modifier_ext = false;
bool threshold_ext = false;
};
class OpenXRAnalogThresholdModifier : public OpenXRActionBindingModifier {
GDCLASS(OpenXRAnalogThresholdModifier, OpenXRActionBindingModifier);
private:
XrInteractionProfileAnalogThresholdVALVE analog_threshold;
Ref<OpenXRHapticBase> on_haptic;
Ref<OpenXRHapticBase> off_haptic;
protected:
static void _bind_methods();
public:
OpenXRAnalogThresholdModifier();
void set_on_threshold(float p_threshold);
float get_on_threshold() const;
void set_off_threshold(float p_threshold);
float get_off_threshold() const;
void set_on_haptic(const Ref<OpenXRHapticBase> &p_haptic);
Ref<OpenXRHapticBase> get_on_haptic() const;
void set_off_haptic(const Ref<OpenXRHapticBase> &p_haptic);
Ref<OpenXRHapticBase> get_off_haptic() const;
virtual String get_description() const override { return "Analog threshold modifier"; }
virtual PackedByteArray get_ip_modification() override;
};

View File

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

View File

@@ -0,0 +1,97 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#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 {
GDCLASS(OpenXRVisibilityMaskExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
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);
};

View File

@@ -0,0 +1,135 @@
/**************************************************************************/
/* openxr_wmr_controller_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_wmr_controller_extension.h"
#include "../action_map/openxr_interaction_profile_metadata.h"
HashMap<String, bool *> OpenXRWMRControllerExtension::get_requested_extensions() {
HashMap<String, bool *> request_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;
}
bool OpenXRWMRControllerExtension::is_available(WMRControllers p_type) {
return available[p_type];
}
void OpenXRWMRControllerExtension::on_register_metadata() {
OpenXRInteractionProfileMetadata *openxr_metadata = OpenXRInteractionProfileMetadata::get_singleton();
ERR_FAIL_NULL(openxr_metadata);
{ // HP MR controller (newer G2 controllers)
const String profile_path = "/interaction_profiles/hp/mixed_reality_controller";
openxr_metadata->register_interaction_profile("HPMR controller", profile_path, XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME);
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Menu click", user_path, user_path + "/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Squeeze", user_path, user_path + "/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
openxr_metadata->register_io_path(profile_path, "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
}
{ // Samsung Odyssey controller
const String profile_path = "/interaction_profiles/samsung/odyssey_controller";
openxr_metadata->register_interaction_profile("Samsung Odyssey controller", profile_path, XR_EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME);
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Menu click", user_path, user_path + "/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Squeeze click", user_path, user_path + "/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick", user_path, user_path + "/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Thumbstick click", user_path, user_path + "/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Up", user_path, user_path + "/input/thumbstick/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Down", user_path, user_path + "/input/thumbstick/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Left", user_path, user_path + "/input/thumbstick/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Thumbstick Dpad Right", user_path, user_path + "/input/thumbstick/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad", user_path, user_path + "/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
openxr_metadata->register_io_path(profile_path, "Trackpad click", user_path, user_path + "/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad touch", user_path, user_path + "/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Up", user_path, user_path + "/input/trackpad/dpad_up", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Down", user_path, user_path + "/input/trackpad/dpad_down", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Left", user_path, user_path + "/input/trackpad/dpad_left", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Right", user_path, user_path + "/input/trackpad/dpad_right", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Trackpad Dpad Center", user_path, user_path + "/input/trackpad/dpad_center", "XR_EXT_dpad_binding", OpenXRAction::OPENXR_ACTION_BOOL);
openxr_metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
}
}
{ // MSFT Hand interaction profile, also supported by other headsets
const String profile_path = "/interaction_profiles/microsoft/hand_interaction";
openxr_metadata->register_interaction_profile("MSFT Hand interaction", profile_path, XR_MSFT_HAND_INTERACTION_EXTENSION_NAME);
for (const String user_path : { "/user/hand/left", "/user/hand/right" }) {
openxr_metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Pinch pose", user_path, user_path + "/input/pinch_ext/pose", XR_EXT_HAND_INTERACTION_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Poke pose", user_path, user_path + "/input/poke_ext/pose", XR_EXT_HAND_INTERACTION_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Palm pose", user_path, user_path + "/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
openxr_metadata->register_io_path(profile_path, "Select (pinch)", user_path, user_path + "/input/select/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
openxr_metadata->register_io_path(profile_path, "Squeeze (grab)", user_path, user_path + "/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
}
}
}

View File

@@ -0,0 +1,57 @@
/**************************************************************************/
/* openxr_wmr_controller_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. */
/**************************************************************************/
#pragma once
#include "openxr_extension_wrapper.h"
class OpenXRWMRControllerExtension : public OpenXRExtensionWrapper {
GDCLASS(OpenXRWMRControllerExtension, OpenXRExtensionWrapper);
protected:
static void _bind_methods() {}
public:
enum WMRControllers {
WMR_HPMR,
WMR_SAMSUNG_ODESSY,
WMR_HAND_INTERACTION,
WMR_MAX_CONTROLLERS
};
virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available(WMRControllers p_type);
virtual void on_register_metadata() override;
private:
bool available[WMR_MAX_CONTROLLERS] = { false, false };
};

View File

@@ -0,0 +1,114 @@
/**************************************************************************/
/* openxr_android_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_android_extension.h"
#include "../../openxr_api.h"
#include "java_godot_wrapper.h"
#include "os_android.h"
#include "thread_jandroid.h"
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
OpenXRAndroidExtension *OpenXRAndroidExtension::singleton = nullptr;
OpenXRAndroidExtension *OpenXRAndroidExtension::get_singleton() {
return singleton;
}
OpenXRAndroidExtension::OpenXRAndroidExtension() {
singleton = this;
JNIEnv *env = get_jni_env();
ERR_FAIL_NULL(env);
env->GetJavaVM(&vm);
activity_object = env->NewGlobalRef(static_cast<OS_Android *>(OS::get_singleton())->get_godot_java()->get_activity());
}
HashMap<String, bool *> OpenXRAndroidExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
// XR_KHR_LOADER_INIT_EXTENSION_NAME is a dependency of XR_KHR_LOADER_INIT_ANDROID_EXTENSION_NAME
request_extensions[XR_KHR_LOADER_INIT_EXTENSION_NAME] = &loader_init_extension_available;
request_extensions[XR_KHR_LOADER_INIT_ANDROID_EXTENSION_NAME] = &loader_init_android_extension_available;
request_extensions[XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME] = &create_instance_extension_available;
return request_extensions;
}
void OpenXRAndroidExtension::on_before_instance_created() {
if (XR_FAILED(EXT_TRY_INIT_XR_FUNC(xrInitializeLoaderKHR))) {
// XR_KHR_loader_init not supported on this platform
return;
}
loader_init_android_extension_available = true;
XrLoaderInitInfoAndroidKHR loader_init_info_android = {
.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR,
.next = nullptr,
.applicationVM = vm,
.applicationContext = activity_object
};
XrResult result = xrInitializeLoaderKHR((const XrLoaderInitInfoBaseHeaderKHR *)&loader_init_info_android);
ERR_FAIL_COND_MSG(XR_FAILED(result), "Failed to call xrInitializeLoaderKHR");
}
// We're keeping the Android create info struct here to avoid including openxr_platform.h in a header, which would break other extensions.
// This is reasonably safe as the struct is only used during initialization and the extension is a singleton.
static XrInstanceCreateInfoAndroidKHR instance_create_info;
void *OpenXRAndroidExtension::set_instance_create_info_and_get_next_pointer(void *p_next_pointer) {
if (!create_instance_extension_available) {
if (!loader_init_android_extension_available) {
WARN_PRINT("No Android extensions available, couldn't pass JVM and Activity to OpenXR");
}
return nullptr;
}
instance_create_info = {
.type = XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR,
.next = p_next_pointer,
.applicationVM = vm,
.applicationActivity = activity_object
};
return &instance_create_info;
}
OpenXRAndroidExtension::~OpenXRAndroidExtension() {
singleton = nullptr;
JNIEnv *env = get_jni_env();
ERR_FAIL_NULL(env);
env->DeleteGlobalRef(activity_object);
}

View File

@@ -0,0 +1,61 @@
/**************************************************************************/
/* openxr_android_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. */
/**************************************************************************/
#pragma once
#include "../../util.h"
#include "../openxr_extension_wrapper.h"
#include <jni.h>
class OpenXRAndroidExtension : public OpenXRExtensionWrapper {
public:
static OpenXRAndroidExtension *get_singleton();
OpenXRAndroidExtension();
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_before_instance_created() override;
virtual void *set_instance_create_info_and_get_next_pointer(void *p_next_pointer) override;
virtual ~OpenXRAndroidExtension() override;
private:
static OpenXRAndroidExtension *singleton;
JavaVM *vm;
jobject activity_object;
bool loader_init_extension_available = false;
bool loader_init_android_extension_available = false;
bool create_instance_extension_available = false;
// Initialize the loader
EXT_PROTO_XRRESULT_FUNC1(xrInitializeLoaderKHR, (const XrLoaderInitInfoBaseHeaderKHR *), loaderInitInfo)
};

View File

@@ -0,0 +1,451 @@
/**************************************************************************/
/* openxr_d3d12_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_d3d12_extension.h"
#ifdef D3D12_ENABLED
#include "../../openxr_util.h"
#include "servers/rendering/rendering_server_globals.h"
#include "servers/rendering_server.h"
HashMap<String, bool *> OpenXRD3D12Extension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_KHR_D3D12_ENABLE_EXTENSION_NAME] = nullptr;
return request_extensions;
}
void OpenXRD3D12Extension::on_instance_created(const XrInstance p_instance) {
// Obtain pointers to functions we're accessing here.
ERR_FAIL_NULL(OpenXRAPI::get_singleton());
EXT_INIT_XR_FUNC(xrGetD3D12GraphicsRequirementsKHR);
EXT_INIT_XR_FUNC(xrEnumerateSwapchainImages);
}
D3D_FEATURE_LEVEL OpenXRD3D12Extension::get_feature_level() const {
XrGraphicsRequirementsD3D12KHR d3d12_requirements = {
XR_TYPE_GRAPHICS_REQUIREMENTS_D3D12_KHR, // type
nullptr, // next
{ 0, 0 }, // adapterLuid
(D3D_FEATURE_LEVEL)0 // minFeatureLevel
};
XrResult result = xrGetD3D12GraphicsRequirementsKHR(OpenXRAPI::get_singleton()->get_instance(), OpenXRAPI::get_singleton()->get_system_id(), &d3d12_requirements);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get Direct3D 12 graphics requirements [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return D3D_FEATURE_LEVEL_11_0; // Good default.
}
// #ifdef DEBUG
print_line("OpenXR: xrGetD3D12GraphicsRequirementsKHR:");
print_line(" - minFeatureLevel: ", (uint32_t)d3d12_requirements.minFeatureLevel);
// #endif
return d3d12_requirements.minFeatureLevel;
}
LUID OpenXRD3D12Extension::get_adapter_luid() const {
XrGraphicsRequirementsD3D12KHR d3d12_requirements = {
XR_TYPE_GRAPHICS_REQUIREMENTS_D3D12_KHR, // type
nullptr, // next
{ 0, 0 }, // adapterLuid
(D3D_FEATURE_LEVEL)0 // minFeatureLevel
};
XrResult result = xrGetD3D12GraphicsRequirementsKHR(OpenXRAPI::get_singleton()->get_instance(), OpenXRAPI::get_singleton()->get_system_id(), &d3d12_requirements);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get Direct3D 12 graphics requirements [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return {};
}
return d3d12_requirements.adapterLuid;
}
void OpenXRD3D12Extension::set_device(ID3D12Device *p_device) {
graphics_device = p_device;
}
void OpenXRD3D12Extension::set_command_queue(ID3D12CommandQueue *p_queue) {
command_queue = p_queue;
}
void OpenXRD3D12Extension::cleanup_device() {
command_queue.Reset();
graphics_device.Reset();
}
XrGraphicsBindingD3D12KHR OpenXRD3D12Extension::graphics_binding_d3d12;
void *OpenXRD3D12Extension::set_session_create_and_get_next_pointer(void *p_next_pointer) {
DEV_ASSERT(graphics_device && "Graphics Device was not specified yet.");
DEV_ASSERT(command_queue && "Command queue was not specified yet.");
graphics_binding_d3d12.type = XR_TYPE_GRAPHICS_BINDING_D3D12_KHR,
graphics_binding_d3d12.next = p_next_pointer;
graphics_binding_d3d12.device = graphics_device.Get();
graphics_binding_d3d12.queue = command_queue.Get();
return &graphics_binding_d3d12;
}
void OpenXRD3D12Extension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) {
p_usable_swap_chains.push_back(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB);
p_usable_swap_chains.push_back(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB);
p_usable_swap_chains.push_back(DXGI_FORMAT_R8G8B8A8_UNORM);
p_usable_swap_chains.push_back(DXGI_FORMAT_B8G8R8A8_UNORM);
}
void OpenXRD3D12Extension::get_usable_depth_formats(Vector<int64_t> &p_usable_depth_formats) {
p_usable_depth_formats.push_back(DXGI_FORMAT_D32_FLOAT);
p_usable_depth_formats.push_back(DXGI_FORMAT_D32_FLOAT_S8X24_UINT);
p_usable_depth_formats.push_back(DXGI_FORMAT_D24_UNORM_S8_UINT);
p_usable_depth_formats.push_back(DXGI_FORMAT_D16_UNORM);
}
bool OpenXRD3D12Extension::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) {
RenderingServer *rendering_server = RenderingServer::get_singleton();
ERR_FAIL_NULL_V(rendering_server, false);
RenderingDevice *rendering_device = rendering_server->get_rendering_device();
ERR_FAIL_NULL_V(rendering_device, false);
uint32_t swapchain_length;
XrResult result = xrEnumerateSwapchainImages(p_swapchain, 0, &swapchain_length, nullptr);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get swapchain image count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
LocalVector<XrSwapchainImageD3D12KHR> images;
images.resize(swapchain_length);
for (XrSwapchainImageD3D12KHR &image : images) {
image.type = XR_TYPE_SWAPCHAIN_IMAGE_D3D12_KHR;
image.next = nullptr;
image.texture = nullptr;
}
result = xrEnumerateSwapchainImages(p_swapchain, swapchain_length, &swapchain_length, (XrSwapchainImageBaseHeader *)images.ptr());
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get swapchain images [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
SwapchainGraphicsData *data = memnew(SwapchainGraphicsData);
if (data == nullptr) {
print_line("OpenXR: Failed to allocate memory for swapchain data");
return false;
}
*r_swapchain_graphics_data = data;
data->is_multiview = (p_array_size > 1);
RenderingDevice::DataFormat format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
RenderingDevice::TextureSamples samples = RenderingDevice::TEXTURE_SAMPLES_1;
uint64_t usage_flags = RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT;
switch (p_swapchain_format) {
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
// Even though this is an sRGB framebuffer format we're using UNORM here.
// The reason here is because Godot does a linear to sRGB conversion while
// with the sRGB format, this conversion would be doubled by the hardware.
// This also means we're reading the values as is for our preview on screen.
// The OpenXR runtime however is still treating this as an sRGB format and
// will thus do an sRGB -> Linear conversion as expected.
//format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UNORM;
usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
//format = RenderingDevice::DATA_FORMAT_B8G8R8A8_SRGB;
format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UNORM;
usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case DXGI_FORMAT_R8G8B8A8_UNORM:
format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UINT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case DXGI_FORMAT_B8G8R8A8_UNORM:
format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UINT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case DXGI_FORMAT_D32_FLOAT:
format = RenderingDevice::DATA_FORMAT_D32_SFLOAT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
break;
case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
format = RenderingDevice::DATA_FORMAT_D32_SFLOAT_S8_UINT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
break;
case DXGI_FORMAT_D24_UNORM_S8_UINT:
format = RenderingDevice::DATA_FORMAT_D24_UNORM_S8_UINT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
break;
case DXGI_FORMAT_D16_UNORM:
format = RenderingDevice::DATA_FORMAT_D16_UNORM;
usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
break;
default:
// continue with our default value
print_line("OpenXR: Unsupported swapchain format", p_swapchain_format);
break;
}
switch (p_sample_count) {
case 1:
samples = RenderingDevice::TEXTURE_SAMPLES_1;
break;
case 2:
samples = RenderingDevice::TEXTURE_SAMPLES_2;
break;
case 4:
samples = RenderingDevice::TEXTURE_SAMPLES_4;
break;
case 8:
samples = RenderingDevice::TEXTURE_SAMPLES_8;
break;
case 16:
samples = RenderingDevice::TEXTURE_SAMPLES_16;
break;
case 32:
samples = RenderingDevice::TEXTURE_SAMPLES_32;
break;
case 64:
samples = RenderingDevice::TEXTURE_SAMPLES_64;
break;
default:
// continue with our default value
print_line("OpenXR: Unsupported sample count", p_sample_count);
break;
}
Vector<RID> texture_rids;
for (const XrSwapchainImageD3D12KHR &swapchain_image : images) {
RID texture_rid = rendering_device->texture_create_from_extension(
p_array_size == 1 ? RenderingDevice::TEXTURE_TYPE_2D : RenderingDevice::TEXTURE_TYPE_2D_ARRAY,
format,
samples,
usage_flags,
(uint64_t)swapchain_image.texture,
p_width,
p_height,
1,
p_array_size,
1);
texture_rids.push_back(texture_rid);
}
data->texture_rids = texture_rids;
return true;
}
bool OpenXRD3D12Extension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
OpenXRUtil::XrMatrix4x4f matrix;
OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_D3D, p_fov, (float)p_z_near, (float)p_z_far);
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
r_camera_matrix.columns[j][i] = matrix.m[j * 4 + i];
}
}
return true;
}
RID OpenXRD3D12Extension::get_texture(void *p_swapchain_graphics_data, int p_image_index) {
SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data;
ERR_FAIL_NULL_V(data, RID());
ERR_FAIL_INDEX_V(p_image_index, data->texture_rids.size(), RID());
return data->texture_rids[p_image_index];
}
void OpenXRD3D12Extension::cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) {
if (*p_swapchain_graphics_data == nullptr) {
return;
}
RenderingServer *rendering_server = RenderingServer::get_singleton();
ERR_FAIL_NULL(rendering_server);
RenderingDevice *rendering_device = rendering_server->get_rendering_device();
ERR_FAIL_NULL(rendering_device);
SwapchainGraphicsData *data = (SwapchainGraphicsData *)*p_swapchain_graphics_data;
for (const RID &texture_rid : data->texture_rids) {
// This should clean up our RIDs and associated texture objects but shouldn't destroy the images, they are owned by our XrSwapchain.
rendering_device->free(texture_rid);
}
data->texture_rids.clear();
memdelete(data);
*p_swapchain_graphics_data = nullptr;
}
#define ENUM_TO_STRING_CASE(e) \
case e: { \
return String(#e); \
} break;
String OpenXRD3D12Extension::get_swapchain_format_name(int64_t p_swapchain_format) const {
// These are somewhat different per platform, will need to weed some stuff out...
switch (p_swapchain_format) {
ENUM_TO_STRING_CASE(DXGI_FORMAT_UNKNOWN)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32A32_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32A32_FLOAT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32A32_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32A32_SINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32_FLOAT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32_SINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16B16A16_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16B16A16_FLOAT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16B16A16_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16B16A16_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16B16A16_SNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16B16A16_SINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32_FLOAT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32_SINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G8X24_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_D32_FLOAT_S8X24_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_X32_TYPELESS_G8X24_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R10G10B10A2_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R10G10B10A2_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R10G10B10A2_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R11G11B10_FLOAT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8B8A8_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8B8A8_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8B8A8_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8B8A8_SNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8B8A8_SINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16_FLOAT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16_SNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16_SINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_D32_FLOAT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32_FLOAT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R32_SINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R24G8_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_D24_UNORM_S8_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R24_UNORM_X8_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_X24_TYPELESS_G8_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8_SNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8_SINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16_FLOAT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_D16_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16_SNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R16_SINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8_UINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8_SNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8_SINT)
ENUM_TO_STRING_CASE(DXGI_FORMAT_A8_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R1_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R9G9B9E5_SHAREDEXP)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8_B8G8_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_G8R8_G8B8_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC1_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC1_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC1_UNORM_SRGB)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC2_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC2_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC2_UNORM_SRGB)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC3_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC3_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC3_UNORM_SRGB)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC4_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC4_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC4_SNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC5_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC5_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC5_SNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_B5G6R5_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_B5G5R5A1_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_B8G8R8A8_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_B8G8R8X8_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_B8G8R8A8_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB)
ENUM_TO_STRING_CASE(DXGI_FORMAT_B8G8R8X8_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_B8G8R8X8_UNORM_SRGB)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC6H_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC6H_UF16)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC6H_SF16)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC7_TYPELESS)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC7_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_BC7_UNORM_SRGB)
ENUM_TO_STRING_CASE(DXGI_FORMAT_AYUV)
ENUM_TO_STRING_CASE(DXGI_FORMAT_Y410)
ENUM_TO_STRING_CASE(DXGI_FORMAT_Y416)
ENUM_TO_STRING_CASE(DXGI_FORMAT_NV12)
ENUM_TO_STRING_CASE(DXGI_FORMAT_P010)
ENUM_TO_STRING_CASE(DXGI_FORMAT_P016)
ENUM_TO_STRING_CASE(DXGI_FORMAT_420_OPAQUE)
ENUM_TO_STRING_CASE(DXGI_FORMAT_YUY2)
ENUM_TO_STRING_CASE(DXGI_FORMAT_Y210)
ENUM_TO_STRING_CASE(DXGI_FORMAT_Y216)
ENUM_TO_STRING_CASE(DXGI_FORMAT_NV11)
ENUM_TO_STRING_CASE(DXGI_FORMAT_AI44)
ENUM_TO_STRING_CASE(DXGI_FORMAT_IA44)
ENUM_TO_STRING_CASE(DXGI_FORMAT_P8)
ENUM_TO_STRING_CASE(DXGI_FORMAT_A8P8)
ENUM_TO_STRING_CASE(DXGI_FORMAT_B4G4R4A4_UNORM)
ENUM_TO_STRING_CASE(DXGI_FORMAT_P208)
ENUM_TO_STRING_CASE(DXGI_FORMAT_V208)
ENUM_TO_STRING_CASE(DXGI_FORMAT_V408)
ENUM_TO_STRING_CASE(DXGI_FORMAT_A4B4G4R4_UNORM)
default: {
return String("Swapchain format ") + String::num_int64(int64_t(p_swapchain_format));
} break;
}
}
#endif // D3D12_ENABLED

View File

@@ -0,0 +1,84 @@
/**************************************************************************/
/* openxr_d3d12_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. */
/**************************************************************************/
#pragma once
#ifdef D3D12_ENABLED
#include "../../openxr_api.h"
#include "../../util.h"
#include "../openxr_extension_wrapper.h"
#include "core/templates/vector.h"
#include "drivers/d3d12/d3d12_hooks.h"
// Always include this as late as possible.
#include "../../openxr_platform_inc.h"
class OpenXRD3D12Extension : public OpenXRGraphicsExtensionWrapper, D3D12Hooks {
public:
virtual HashMap<String, bool *> get_requested_extensions() override;
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 D3D_FEATURE_LEVEL get_feature_level() const override final;
virtual LUID get_adapter_luid() const override final;
virtual void set_device(ID3D12Device *p_device) override final;
virtual void set_command_queue(ID3D12CommandQueue *p_queue) override final;
virtual void cleanup_device() override final;
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;
virtual bool 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) override;
virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) override;
virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) override;
virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) override;
virtual RID get_density_map(void *p_swapchain_graphics_data, int p_image_index) override { return RID(); }
private:
static OpenXRD3D12Extension *singleton;
static XrGraphicsBindingD3D12KHR graphics_binding_d3d12;
struct SwapchainGraphicsData {
bool is_multiview;
Vector<RID> texture_rids;
};
EXT_PROTO_XRRESULT_FUNC3(xrGetD3D12GraphicsRequirementsKHR, (XrInstance), p_instance, (XrSystemId), p_system_id, (XrGraphicsRequirementsD3D12KHR *), p_graphics_requirements)
EXT_PROTO_XRRESULT_FUNC4(xrEnumerateSwapchainImages, (XrSwapchain), p_swapchain, (uint32_t), p_image_capacity_input, (uint32_t *), p_image_count_output, (XrSwapchainImageBaseHeader *), p_images)
ComPtr<ID3D12Device> graphics_device;
ComPtr<ID3D12CommandQueue> command_queue;
};
#endif // D3D12_ENABLED

View File

@@ -0,0 +1,70 @@
/**************************************************************************/
/* openxr_metal_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. */
/**************************************************************************/
#pragma once
#include "../../openxr_api.h"
#include "../../util.h"
#include "../openxr_extension_wrapper.h"
#include "core/templates/vector.h"
// Always include this as late as possible.
#include "../../openxr_platform_inc.h"
class OpenXRMetalExtension : public OpenXRGraphicsExtensionWrapper {
public:
virtual HashMap<String, bool *> get_requested_extensions() override;
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 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;
virtual bool 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) override;
virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) override;
virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) override;
virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) override;
virtual RID get_density_map(void *p_swapchain_graphics_data, int p_image_index) override { return RID(); }
private:
static XrGraphicsBindingMetalKHR graphics_binding_metal;
struct SwapchainGraphicsData {
bool is_multiview;
Vector<RID> texture_rids;
};
bool check_graphics_api_support();
EXT_PROTO_XRRESULT_FUNC3(xrGetMetalGraphicsRequirementsKHR, (XrInstance), p_instance, (XrSystemId), p_system_id, (XrGraphicsRequirementsMetalKHR *), p_graphics_requirements)
EXT_PROTO_XRRESULT_FUNC4(xrEnumerateSwapchainImages, (XrSwapchain), p_swapchain, (uint32_t), p_image_capacity_input, (uint32_t *), p_image_count_output, (XrSwapchainImageBaseHeader *), p_images)
};

View File

@@ -0,0 +1,320 @@
/**************************************************************************/
/* openxr_metal_extension.mm */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#import "openxr_metal_extension.h"
#include "../../openxr_util.h"
#import "drivers/metal/rendering_device_driver_metal.h"
#include "servers/rendering/rendering_server_globals.h"
HashMap<String, bool *> OpenXRMetalExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_KHR_METAL_ENABLE_EXTENSION_NAME] = nullptr;
return request_extensions;
}
void OpenXRMetalExtension::on_instance_created(const XrInstance p_instance) {
// Obtain pointers to functions we're accessing here.
ERR_FAIL_NULL(OpenXRAPI::get_singleton());
EXT_INIT_XR_FUNC(xrGetMetalGraphicsRequirementsKHR);
EXT_INIT_XR_FUNC(xrEnumerateSwapchainImages);
}
bool OpenXRMetalExtension::check_graphics_api_support() {
ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
// TODO We may need to do a callback like we do in Vulkan where we run this first
// and provide the obtained metalDevice to our Metal driver to use.
// But according to Stuart Macs only have 1 device so it should always be the
// same one and we should be able to get away with not doing this just yet.
// If we do go forward with this, this means that just like with Vulkan,
// we have to start with OpenXR before Metal can be setup, and we thus
// can't support applications that want to add XR as an optional/temporary
// feature that users enable when needed.
XrSystemId system_id = OpenXRAPI::get_singleton()->get_system_id();
XrInstance instance = OpenXRAPI::get_singleton()->get_instance();
XrGraphicsRequirementsMetalKHR metal_requirements;
metal_requirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_METAL_KHR;
metal_requirements.next = nullptr;
metal_requirements.metalDevice = nullptr;
XrResult result = xrGetMetalGraphicsRequirementsKHR(instance, system_id, &metal_requirements);
if (!OpenXRAPI::get_singleton()->xr_result(result, "Failed to get Metal graphics requirements!")) {
return false;
}
// See what metal device we are using...
RenderingServer *rendering_server = RenderingServer::get_singleton();
ERR_FAIL_NULL_V(rendering_server, false);
RenderingDevice *rendering_device = rendering_server->get_rendering_device();
ERR_FAIL_NULL_V(rendering_device, false);
void *our_metal_device = (void *)rendering_device->get_driver_resource(RD::DRIVER_RESOURCE_LOGICAL_DEVICE);
// Make sure we're using the same one.
ERR_FAIL_COND_V(metal_requirements.metalDevice != our_metal_device, false);
return true;
}
XrGraphicsBindingMetalKHR OpenXRMetalExtension::graphics_binding_metal;
void *OpenXRMetalExtension::set_session_create_and_get_next_pointer(void *p_next_pointer) {
if (!check_graphics_api_support()) {
return p_next_pointer;
}
RenderingServer *rendering_server = RenderingServer::get_singleton();
ERR_FAIL_NULL_V(rendering_server, p_next_pointer);
RenderingDevice *rendering_device = rendering_server->get_rendering_device();
ERR_FAIL_NULL_V(rendering_device, p_next_pointer);
graphics_binding_metal.type = XR_TYPE_GRAPHICS_BINDING_METAL_KHR;
graphics_binding_metal.next = p_next_pointer;
graphics_binding_metal.commandQueue = (void *)rendering_device->get_driver_resource(RD::DRIVER_RESOURCE_COMMAND_QUEUE);
return &graphics_binding_metal;
}
void OpenXRMetalExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) {
p_usable_swap_chains.push_back(MTLPixelFormatRGBA8Unorm_sRGB);
p_usable_swap_chains.push_back(MTLPixelFormatBGRA8Unorm_sRGB);
p_usable_swap_chains.push_back(MTLPixelFormatRGBA8Uint);
}
void OpenXRMetalExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) {
p_usable_swap_chains.push_back(MTLPixelFormatDepth24Unorm_Stencil8);
p_usable_swap_chains.push_back(MTLPixelFormatDepth32Float_Stencil8);
p_usable_swap_chains.push_back(MTLPixelFormatDepth32Float);
}
#define ENUM_TO_STRING_CASE(m_e) \
case m_e: { \
return String(#m_e); \
} break;
String OpenXRMetalExtension::get_swapchain_format_name(int64_t p_swapchain_format) const {
// This really should be in vulkan_context...
MTLPixelFormat format = MTLPixelFormat(p_swapchain_format);
switch (format) {
ENUM_TO_STRING_CASE(MTLPixelFormatRGBA8Unorm)
ENUM_TO_STRING_CASE(MTLPixelFormatRGBA8Unorm_sRGB)
ENUM_TO_STRING_CASE(MTLPixelFormatBGRA8Unorm)
ENUM_TO_STRING_CASE(MTLPixelFormatBGRA8Unorm_sRGB)
ENUM_TO_STRING_CASE(MTLPixelFormatRGBA8Uint)
ENUM_TO_STRING_CASE(MTLPixelFormatDepth24Unorm_Stencil8)
ENUM_TO_STRING_CASE(MTLPixelFormatDepth32Float_Stencil8)
ENUM_TO_STRING_CASE(MTLPixelFormatDepth32Float)
default: {
return String("Swapchain format ") + String::num_int64(int64_t(p_swapchain_format));
} break;
}
}
bool OpenXRMetalExtension::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) {
LocalVector<XrSwapchainImageMetalKHR, uint32_t, false, true> images;
RenderingServer *rendering_server = RenderingServer::get_singleton();
ERR_FAIL_NULL_V(rendering_server, false);
RenderingDevice *rendering_device = rendering_server->get_rendering_device();
ERR_FAIL_NULL_V(rendering_device, false);
uint32_t swapchain_length;
XrResult result = xrEnumerateSwapchainImages(p_swapchain, 0, &swapchain_length, nullptr);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get swapchain image count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
images.resize(swapchain_length);
for (XrSwapchainImageMetalKHR &image : images) {
image.type = XR_TYPE_SWAPCHAIN_IMAGE_METAL_KHR;
image.next = nullptr;
image.texture = nullptr;
}
result = xrEnumerateSwapchainImages(p_swapchain, swapchain_length, &swapchain_length, (XrSwapchainImageBaseHeader *)images.ptr());
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get swapchain images [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
SwapchainGraphicsData *data = memnew(SwapchainGraphicsData);
if (data == nullptr) {
print_line("OpenXR: Failed to allocate memory for swapchain data");
return false;
}
*r_swapchain_graphics_data = data;
data->is_multiview = (p_array_size > 1);
RenderingDevice::DataFormat format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
RenderingDevice::TextureSamples samples = RenderingDevice::TEXTURE_SAMPLES_1;
uint64_t usage_flags = RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT;
switch (p_swapchain_format) {
case MTLPixelFormatRGBA8Unorm_sRGB:
// Even though this is an sRGB framebuffer format we're using UNORM here.
// The reason here is because Godot does a linear to sRGB conversion while
// with the sRGB format, this conversion would be doubled by the hardware.
// This also means we're reading the values as is for our preview on screen.
// The OpenXR runtime however is still treating this as an sRGB format and
// will thus do an sRGB -> Linear conversion as expected.
//format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UNORM;
usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case MTLPixelFormatBGRA8Unorm_sRGB:
format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UNORM;
usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case MTLPixelFormatRGBA8Uint:
format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UINT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case MTLPixelFormatDepth32Float:
format = RenderingDevice::DATA_FORMAT_D32_SFLOAT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
break;
case MTLPixelFormatDepth24Unorm_Stencil8:
format = RenderingDevice::DATA_FORMAT_D24_UNORM_S8_UINT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
break;
case MTLPixelFormatDepth32Float_Stencil8:
format = RenderingDevice::DATA_FORMAT_D32_SFLOAT_S8_UINT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
break;
default:
// Continue with our default value.
print_line("OpenXR: Unsupported swapchain format", p_swapchain_format);
break;
}
switch (p_sample_count) {
case 1:
samples = RenderingDevice::TEXTURE_SAMPLES_1;
break;
case 2:
samples = RenderingDevice::TEXTURE_SAMPLES_2;
break;
case 4:
samples = RenderingDevice::TEXTURE_SAMPLES_4;
break;
case 8:
samples = RenderingDevice::TEXTURE_SAMPLES_8;
break;
case 16:
samples = RenderingDevice::TEXTURE_SAMPLES_16;
break;
case 32:
samples = RenderingDevice::TEXTURE_SAMPLES_32;
break;
case 64:
samples = RenderingDevice::TEXTURE_SAMPLES_64;
break;
default:
// Continue with our default value.
print_line("OpenXR: Unsupported sample count", p_sample_count);
break;
}
Vector<RID> texture_rids;
// Create Godot texture objects for each entry in our swapchain.
for (uint64_t i = 0; i < swapchain_length; i++) {
// Note, the formats we sent to render_device are ignored on metal.
RID image_rid = rendering_device->texture_create_from_extension(
p_array_size == 1 ? RenderingDevice::TEXTURE_TYPE_2D : RenderingDevice::TEXTURE_TYPE_2D_ARRAY,
format,
samples,
usage_flags,
(uint64_t)images[i].texture,
p_width,
p_height,
1,
p_array_size,
1);
texture_rids.push_back(image_rid);
}
data->texture_rids = texture_rids;
return true;
}
void OpenXRMetalExtension::cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) {
if (*p_swapchain_graphics_data == nullptr) {
return;
}
SwapchainGraphicsData *data = (SwapchainGraphicsData *)*p_swapchain_graphics_data;
RenderingServer *rendering_server = RenderingServer::get_singleton();
ERR_FAIL_NULL(rendering_server);
RenderingDevice *rendering_device = rendering_server->get_rendering_device();
ERR_FAIL_NULL(rendering_device);
for (const RID &texture_rid : data->texture_rids) {
rendering_device->free(texture_rid);
}
data->texture_rids.clear();
memdelete(data);
*p_swapchain_graphics_data = nullptr;
}
bool OpenXRMetalExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
// Even though this is a Metal renderer we're using OpenGL coordinate systems.
OpenXRUtil::XrMatrix4x4f matrix;
OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
r_camera_matrix.columns[j][i] = matrix.m[j * 4 + i];
}
}
return true;
}
RID OpenXRMetalExtension::get_texture(void *p_swapchain_graphics_data, int p_image_index) {
SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data;
ERR_FAIL_NULL_V(data, RID());
ERR_FAIL_INDEX_V(p_image_index, data->texture_rids.size(), RID());
return data->texture_rids[p_image_index];
}

View File

@@ -0,0 +1,466 @@
/**************************************************************************/
/* openxr_opengl_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_opengl_extension.h"
#ifdef GLES3_ENABLED
#include "../../openxr_util.h"
#include "drivers/gles3/effects/copy_effects.h"
#include "drivers/gles3/storage/texture_storage.h"
#include "servers/rendering/rendering_server_globals.h"
#include "servers/rendering_server.h"
// OpenXR requires us to submit sRGB textures so that it recognizes the content
// as being in sRGB color space. We do fall back on "normal" textures but this
// will likely result in incorrect colors as OpenXR will double the sRGB conversion.
// All major XR runtimes support sRGB textures.
// In OpenGL output of the fragment shader is assumed to be in the color space of
// the developers choice, however a linear to sRGB HW conversion can be enabled
// through enabling GL_FRAMEBUFFER_SRGB if an sRGB color attachment is used.
// This is a global setting.
// See: https://www.khronos.org/opengl/wiki/Framebuffer
// In OpenGLES output of the fragment shader is assumed to be in linear color space
// and will be converted by default to sRGB if an sRGB color attachment is used.
// The extension GL_EXT_sRGB_write_control was introduced to enable turning this
// feature off.
// See: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_sRGB_write_control.txt
HashMap<String, bool *> OpenXROpenGLExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
#ifdef ANDROID_ENABLED
request_extensions[XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME] = nullptr;
#else
request_extensions[XR_KHR_OPENGL_ENABLE_EXTENSION_NAME] = nullptr;
#endif
#if defined(LINUXBSD_ENABLED) && defined(EGL_ENABLED) && defined(WAYLAND_ENABLED)
request_extensions[XR_MNDX_EGL_ENABLE_EXTENSION_NAME] = &egl_extension_enabled;
#endif
return request_extensions;
}
void OpenXROpenGLExtension::on_instance_created(const XrInstance p_instance) {
// Obtain pointers to functions we're accessing here.
ERR_FAIL_NULL(OpenXRAPI::get_singleton());
#ifdef ANDROID_ENABLED
EXT_INIT_XR_FUNC(xrGetOpenGLESGraphicsRequirementsKHR);
#else
EXT_INIT_XR_FUNC(xrGetOpenGLGraphicsRequirementsKHR);
#endif
EXT_INIT_XR_FUNC(xrEnumerateSwapchainImages);
}
bool OpenXROpenGLExtension::check_graphics_api_support(XrVersion p_desired_version) {
ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
XrSystemId system_id = OpenXRAPI::get_singleton()->get_system_id();
XrInstance instance = OpenXRAPI::get_singleton()->get_instance();
#ifdef ANDROID_ENABLED
XrGraphicsRequirementsOpenGLESKHR opengl_requirements;
opengl_requirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR;
opengl_requirements.next = nullptr;
XrResult result = xrGetOpenGLESGraphicsRequirementsKHR(instance, system_id, &opengl_requirements);
if (!OpenXRAPI::get_singleton()->xr_result(result, "Failed to get OpenGL graphics requirements!")) {
return false;
}
#else
XrGraphicsRequirementsOpenGLKHR opengl_requirements;
opengl_requirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR;
opengl_requirements.next = nullptr;
XrResult result = xrGetOpenGLGraphicsRequirementsKHR(instance, system_id, &opengl_requirements);
if (!OpenXRAPI::get_singleton()->xr_result(result, "Failed to get OpenGL graphics requirements!")) {
return false;
}
#endif
if (p_desired_version < opengl_requirements.minApiVersionSupported) {
print_line("OpenXR: Requested OpenGL version does not meet the minimum version this runtime supports.");
print_line("- desired_version ", OpenXRUtil::make_xr_version_string(p_desired_version));
print_line("- minApiVersionSupported ", OpenXRUtil::make_xr_version_string(opengl_requirements.minApiVersionSupported));
print_line("- maxApiVersionSupported ", OpenXRUtil::make_xr_version_string(opengl_requirements.maxApiVersionSupported));
return false;
}
if (p_desired_version > opengl_requirements.maxApiVersionSupported) {
print_line("OpenXR: Requested OpenGL version exceeds the maximum version this runtime has been tested on and is known to support.");
print_line("- desired_version ", OpenXRUtil::make_xr_version_string(p_desired_version));
print_line("- minApiVersionSupported ", OpenXRUtil::make_xr_version_string(opengl_requirements.minApiVersionSupported));
print_line("- maxApiVersionSupported ", OpenXRUtil::make_xr_version_string(opengl_requirements.maxApiVersionSupported));
}
return true;
}
#ifdef WIN32
XrGraphicsBindingOpenGLWin32KHR OpenXROpenGLExtension::graphics_binding_gl;
#elif defined(ANDROID_ENABLED)
XrGraphicsBindingOpenGLESAndroidKHR OpenXROpenGLExtension::graphics_binding_gl;
#elif defined(LINUXBSD_ENABLED)
#ifdef X11_ENABLED
XrGraphicsBindingOpenGLXlibKHR OpenXROpenGLExtension::graphics_binding_gl;
#endif
#if defined(EGL_ENABLED) && defined(WAYLAND_ENABLED)
XrGraphicsBindingEGLMNDX OpenXROpenGLExtension::graphics_binding_egl;
#endif
#endif
void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_next_pointer) {
GLint gl_version_major = 0;
GLint gl_version_minor = 0;
glGetIntegerv(GL_MAJOR_VERSION, &gl_version_major);
glGetIntegerv(GL_MINOR_VERSION, &gl_version_minor);
XrVersion desired_version = XR_MAKE_VERSION(gl_version_major, gl_version_minor, 0);
if (!check_graphics_api_support(desired_version)) {
print_line("OpenXR: Trying to initialize with OpenGL anyway...");
//return p_next_pointer;
}
DisplayServer *display_server = DisplayServer::get_singleton();
#ifdef WIN32
graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR,
graphics_binding_gl.next = p_next_pointer;
graphics_binding_gl.hDC = (HDC)display_server->window_get_native_handle(DisplayServer::WINDOW_VIEW);
graphics_binding_gl.hGLRC = (HGLRC)display_server->window_get_native_handle(DisplayServer::OPENGL_CONTEXT);
#elif defined(ANDROID_ENABLED)
graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR;
graphics_binding_gl.next = p_next_pointer;
graphics_binding_gl.display = (void *)display_server->window_get_native_handle(DisplayServer::DISPLAY_HANDLE);
graphics_binding_gl.config = (EGLConfig)0; // https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/master/src/tests/hello_xr/graphicsplugin_opengles.cpp#L122
graphics_binding_gl.context = (void *)display_server->window_get_native_handle(DisplayServer::OPENGL_CONTEXT);
#else
#if defined(EGL_ENABLED) && defined(WAYLAND_ENABLED)
if (display_server->get_name() == "Wayland") {
ERR_FAIL_COND_V_MSG(!egl_extension_enabled, p_next_pointer, "OpenXR cannot initialize on Wayland without the XR_MNDX_egl_enable extension.");
graphics_binding_egl.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX;
graphics_binding_egl.next = p_next_pointer;
graphics_binding_egl.getProcAddress = eglGetProcAddress;
graphics_binding_egl.display = (void *)display_server->window_get_native_handle(DisplayServer::EGL_DISPLAY);
graphics_binding_egl.config = (void *)display_server->window_get_native_handle(DisplayServer::EGL_CONFIG);
graphics_binding_egl.context = (void *)display_server->window_get_native_handle(DisplayServer::OPENGL_CONTEXT);
return &graphics_binding_egl;
}
#endif
#if defined(X11_ENABLED)
graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
graphics_binding_gl.next = p_next_pointer;
void *display_handle = (void *)display_server->window_get_native_handle(DisplayServer::DISPLAY_HANDLE);
void *glxcontext_handle = (void *)display_server->window_get_native_handle(DisplayServer::OPENGL_CONTEXT);
void *glxdrawable_handle = (void *)display_server->window_get_native_handle(DisplayServer::WINDOW_HANDLE);
graphics_binding_gl.xDisplay = (Display *)display_handle;
graphics_binding_gl.glxContext = (GLXContext)glxcontext_handle;
graphics_binding_gl.glxDrawable = (GLXDrawable)glxdrawable_handle;
// spec says to use proper values but runtimes don't care
graphics_binding_gl.visualid = 0;
graphics_binding_gl.glxFBConfig = nullptr;
#endif
#endif
#if defined(WIN32) || defined(ANDROID_ENABLED) || defined(X11_ENABLED)
return &graphics_binding_gl;
#else
return p_next_pointer;
#endif
}
void OpenXROpenGLExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) {
p_usable_swap_chains.push_back(GL_SRGB8_ALPHA8);
p_usable_swap_chains.push_back(GL_RGBA8);
}
void OpenXROpenGLExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_depth_formats) {
p_usable_depth_formats.push_back(GL_DEPTH_COMPONENT32F);
p_usable_depth_formats.push_back(GL_DEPTH24_STENCIL8);
p_usable_depth_formats.push_back(GL_DEPTH32F_STENCIL8);
p_usable_depth_formats.push_back(GL_DEPTH_COMPONENT24);
}
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);
uint32_t swapchain_length;
XrResult result = xrEnumerateSwapchainImages(p_swapchain, 0, &swapchain_length, nullptr);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get swapchain image count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
#ifdef ANDROID_ENABLED
LocalVector<XrSwapchainImageOpenGLESKHR> images;
#else
LocalVector<XrSwapchainImageOpenGLKHR> images;
#endif
images.resize(swapchain_length);
#ifdef ANDROID_ENABLED
for (XrSwapchainImageOpenGLESKHR &image : images) {
image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR;
#else
for (XrSwapchainImageOpenGLKHR &image : images) {
image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
#endif
image.next = nullptr;
image.image = 0;
}
result = xrEnumerateSwapchainImages(p_swapchain, swapchain_length, &swapchain_length, (XrSwapchainImageBaseHeader *)images.ptr());
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get swapchain images [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
SwapchainGraphicsData *data = memnew(SwapchainGraphicsData);
if (data == nullptr) {
print_line("OpenXR: Failed to allocate memory for swapchain data");
return false;
}
*r_swapchain_graphics_data = data;
data->is_multiview = (p_array_size > 1);
Image::Format format = Image::FORMAT_RGBA8;
Vector<RID> texture_rids;
for (uint64_t i = 0; i < swapchain_length; i++) {
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,
p_height,
1,
p_array_size);
texture_rids.push_back(texture_rid);
}
data->texture_rids = texture_rids;
return true;
}
bool OpenXROpenGLExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
OpenXRUtil::XrMatrix4x4f matrix;
OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
r_camera_matrix.columns[j][i] = matrix.m[j * 4 + i];
}
}
return true;
}
RID OpenXROpenGLExtension::get_texture(void *p_swapchain_graphics_data, int p_image_index) {
SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data;
ERR_FAIL_NULL_V(data, RID());
ERR_FAIL_INDEX_V(p_image_index, data->texture_rids.size(), RID());
return data->texture_rids[p_image_index];
}
void OpenXROpenGLExtension::cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) {
if (*p_swapchain_graphics_data == nullptr) {
return;
}
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
ERR_FAIL_NULL(texture_storage);
SwapchainGraphicsData *data = (SwapchainGraphicsData *)*p_swapchain_graphics_data;
for (const RID &texture_rid : data->texture_rids) {
texture_storage->texture_free(texture_rid);
}
data->texture_rids.clear();
memdelete(data);
*p_swapchain_graphics_data = nullptr;
}
#define ENUM_TO_STRING_CASE(e) \
case e: { \
return String(#e); \
} break;
String OpenXROpenGLExtension::get_swapchain_format_name(int64_t p_swapchain_format) const {
// These are somewhat different per platform, will need to weed some stuff out...
switch (p_swapchain_format) {
#ifdef ANDROID_ENABLED
// using definitions from GLES3/gl3.h
ENUM_TO_STRING_CASE(GL_RGBA4)
ENUM_TO_STRING_CASE(GL_RGB5_A1)
ENUM_TO_STRING_CASE(GL_RGB565)
ENUM_TO_STRING_CASE(GL_RGB8)
ENUM_TO_STRING_CASE(GL_RGBA8)
ENUM_TO_STRING_CASE(GL_RGB10_A2)
ENUM_TO_STRING_CASE(GL_RGBA32F)
ENUM_TO_STRING_CASE(GL_RGB32F)
ENUM_TO_STRING_CASE(GL_RGBA16F)
ENUM_TO_STRING_CASE(GL_RGB16F)
ENUM_TO_STRING_CASE(GL_R11F_G11F_B10F)
ENUM_TO_STRING_CASE(GL_UNSIGNED_INT_10F_11F_11F_REV)
ENUM_TO_STRING_CASE(GL_RGB9_E5)
ENUM_TO_STRING_CASE(GL_UNSIGNED_INT_5_9_9_9_REV)
ENUM_TO_STRING_CASE(GL_RGBA32UI)
ENUM_TO_STRING_CASE(GL_RGB32UI)
ENUM_TO_STRING_CASE(GL_RGBA16UI)
ENUM_TO_STRING_CASE(GL_RGB16UI)
ENUM_TO_STRING_CASE(GL_RGBA8UI)
ENUM_TO_STRING_CASE(GL_RGB8UI)
ENUM_TO_STRING_CASE(GL_RGBA32I)
ENUM_TO_STRING_CASE(GL_RGB32I)
ENUM_TO_STRING_CASE(GL_RGBA16I)
ENUM_TO_STRING_CASE(GL_RGB16I)
ENUM_TO_STRING_CASE(GL_RGBA8I)
ENUM_TO_STRING_CASE(GL_RGB8I)
ENUM_TO_STRING_CASE(GL_RG)
ENUM_TO_STRING_CASE(GL_RG_INTEGER)
ENUM_TO_STRING_CASE(GL_R8)
ENUM_TO_STRING_CASE(GL_RG8)
ENUM_TO_STRING_CASE(GL_R16F)
ENUM_TO_STRING_CASE(GL_R32F)
ENUM_TO_STRING_CASE(GL_RG16F)
ENUM_TO_STRING_CASE(GL_RG32F)
ENUM_TO_STRING_CASE(GL_R8I)
ENUM_TO_STRING_CASE(GL_R8UI)
ENUM_TO_STRING_CASE(GL_R16I)
ENUM_TO_STRING_CASE(GL_R16UI)
ENUM_TO_STRING_CASE(GL_R32I)
ENUM_TO_STRING_CASE(GL_R32UI)
ENUM_TO_STRING_CASE(GL_RG8I)
ENUM_TO_STRING_CASE(GL_RG8UI)
ENUM_TO_STRING_CASE(GL_RG16I)
ENUM_TO_STRING_CASE(GL_RG16UI)
ENUM_TO_STRING_CASE(GL_RG32I)
ENUM_TO_STRING_CASE(GL_RG32UI)
ENUM_TO_STRING_CASE(GL_R8_SNORM)
ENUM_TO_STRING_CASE(GL_RG8_SNORM)
ENUM_TO_STRING_CASE(GL_RGB8_SNORM)
ENUM_TO_STRING_CASE(GL_RGBA8_SNORM)
ENUM_TO_STRING_CASE(GL_RGB10_A2UI)
ENUM_TO_STRING_CASE(GL_SRGB)
ENUM_TO_STRING_CASE(GL_SRGB8)
ENUM_TO_STRING_CASE(GL_SRGB8_ALPHA8)
ENUM_TO_STRING_CASE(GL_COMPRESSED_R11_EAC)
ENUM_TO_STRING_CASE(GL_COMPRESSED_SIGNED_R11_EAC)
ENUM_TO_STRING_CASE(GL_COMPRESSED_RG11_EAC)
ENUM_TO_STRING_CASE(GL_COMPRESSED_SIGNED_RG11_EAC)
ENUM_TO_STRING_CASE(GL_COMPRESSED_RGB8_ETC2)
ENUM_TO_STRING_CASE(GL_COMPRESSED_SRGB8_ETC2)
ENUM_TO_STRING_CASE(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2)
ENUM_TO_STRING_CASE(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2)
ENUM_TO_STRING_CASE(GL_COMPRESSED_RGBA8_ETC2_EAC)
ENUM_TO_STRING_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC)
ENUM_TO_STRING_CASE(GL_DEPTH_COMPONENT16)
ENUM_TO_STRING_CASE(GL_DEPTH_COMPONENT24)
ENUM_TO_STRING_CASE(GL_DEPTH24_STENCIL8)
#else
// using definitions from GLAD
ENUM_TO_STRING_CASE(GL_R8_SNORM)
ENUM_TO_STRING_CASE(GL_RG8_SNORM)
ENUM_TO_STRING_CASE(GL_RGB8_SNORM)
ENUM_TO_STRING_CASE(GL_RGBA8_SNORM)
ENUM_TO_STRING_CASE(GL_R16_SNORM)
ENUM_TO_STRING_CASE(GL_RG16_SNORM)
ENUM_TO_STRING_CASE(GL_RGB16_SNORM)
ENUM_TO_STRING_CASE(GL_RGBA16_SNORM)
ENUM_TO_STRING_CASE(GL_RGB4)
ENUM_TO_STRING_CASE(GL_RGB5)
ENUM_TO_STRING_CASE(GL_RGB8)
ENUM_TO_STRING_CASE(GL_RGB10)
ENUM_TO_STRING_CASE(GL_RGB12)
ENUM_TO_STRING_CASE(GL_RGB16)
ENUM_TO_STRING_CASE(GL_RGBA2)
ENUM_TO_STRING_CASE(GL_RGBA4)
ENUM_TO_STRING_CASE(GL_RGB5_A1)
ENUM_TO_STRING_CASE(GL_RGBA8)
ENUM_TO_STRING_CASE(GL_RGB10_A2)
ENUM_TO_STRING_CASE(GL_RGBA12)
ENUM_TO_STRING_CASE(GL_RGBA16)
ENUM_TO_STRING_CASE(GL_RGBA32F)
ENUM_TO_STRING_CASE(GL_RGB32F)
ENUM_TO_STRING_CASE(GL_RGBA16F)
ENUM_TO_STRING_CASE(GL_RGB16F)
ENUM_TO_STRING_CASE(GL_RGBA32UI)
ENUM_TO_STRING_CASE(GL_RGB32UI)
ENUM_TO_STRING_CASE(GL_RGBA16UI)
ENUM_TO_STRING_CASE(GL_RGB16UI)
ENUM_TO_STRING_CASE(GL_RGBA8UI)
ENUM_TO_STRING_CASE(GL_RGB8UI)
ENUM_TO_STRING_CASE(GL_RGBA32I)
ENUM_TO_STRING_CASE(GL_RGB32I)
ENUM_TO_STRING_CASE(GL_RGBA16I)
ENUM_TO_STRING_CASE(GL_RGB16I)
ENUM_TO_STRING_CASE(GL_RGBA8I)
ENUM_TO_STRING_CASE(GL_RGB8I)
ENUM_TO_STRING_CASE(GL_RGB10_A2UI)
ENUM_TO_STRING_CASE(GL_SRGB)
ENUM_TO_STRING_CASE(GL_SRGB8)
ENUM_TO_STRING_CASE(GL_SRGB_ALPHA)
ENUM_TO_STRING_CASE(GL_SRGB8_ALPHA8)
ENUM_TO_STRING_CASE(GL_DEPTH_COMPONENT16)
ENUM_TO_STRING_CASE(GL_DEPTH_COMPONENT24)
ENUM_TO_STRING_CASE(GL_DEPTH_COMPONENT32)
ENUM_TO_STRING_CASE(GL_DEPTH24_STENCIL8)
ENUM_TO_STRING_CASE(GL_R11F_G11F_B10F)
ENUM_TO_STRING_CASE(GL_DEPTH_COMPONENT32F)
ENUM_TO_STRING_CASE(GL_DEPTH32F_STENCIL8)
#endif
default: {
return String("Swapchain format 0x") + String::num_int64(p_swapchain_format, 16);
} break;
}
}
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,95 @@
/**************************************************************************/
/* openxr_opengl_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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "../../openxr_api.h"
#include "../../util.h"
#include "../openxr_extension_wrapper.h"
#include "core/templates/vector.h"
// Always include this as late as possible.
#include "../../openxr_platform_inc.h"
class OpenXROpenGLExtension : public OpenXRGraphicsExtensionWrapper {
public:
virtual HashMap<String, bool *> get_requested_extensions() override;
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 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;
virtual bool 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) override;
virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) override;
virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) override;
virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) override;
virtual RID get_density_map(void *p_swapchain_graphics_data, int p_image_index) override { return RID(); }
private:
static OpenXROpenGLExtension *singleton;
#ifdef WIN32
static XrGraphicsBindingOpenGLWin32KHR graphics_binding_gl;
#elif defined(ANDROID_ENABLED)
static XrGraphicsBindingOpenGLESAndroidKHR graphics_binding_gl;
#elif defined(LINUXBSD_ENABLED)
#ifdef X11_ENABLED
static XrGraphicsBindingOpenGLXlibKHR graphics_binding_gl;
#endif
#if defined(EGL_ENABLED) && defined(WAYLAND_ENABLED)
static XrGraphicsBindingEGLMNDX graphics_binding_egl;
bool egl_extension_enabled = false;
#endif
#else
#error "OpenXR with OpenGL isn't supported on this platform"
#endif
struct SwapchainGraphicsData {
bool is_multiview;
Vector<RID> texture_rids;
};
bool check_graphics_api_support(XrVersion p_desired_version);
#ifdef ANDROID_ENABLED
EXT_PROTO_XRRESULT_FUNC3(xrGetOpenGLESGraphicsRequirementsKHR, (XrInstance), p_instance, (XrSystemId), p_system_id, (XrGraphicsRequirementsOpenGLESKHR *), p_graphics_requirements)
#else
EXT_PROTO_XRRESULT_FUNC3(xrGetOpenGLGraphicsRequirementsKHR, (XrInstance), p_instance, (XrSystemId), p_system_id, (XrGraphicsRequirementsOpenGLKHR *), p_graphics_requirements)
#endif
EXT_PROTO_XRRESULT_FUNC4(xrEnumerateSwapchainImages, (XrSwapchain), p_swapchain, (uint32_t), p_image_capacity_input, (uint32_t *), p_image_count_output, (XrSwapchainImageBaseHeader *), p_images)
};
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,739 @@
/**************************************************************************/
/* openxr_vulkan_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_vulkan_extension.h"
#include "../../openxr_util.h"
#include "../openxr_fb_foveation_extension.h"
#include "core/string/print_string.h"
#include "servers/rendering/renderer_rd/effects/copy_effects.h"
#include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"
#include "servers/rendering/rendering_server_globals.h"
#include "servers/rendering_server.h"
HashMap<String, bool *> OpenXRVulkanExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME] = nullptr; // must be available
return request_extensions;
}
void OpenXRVulkanExtension::on_instance_created(const XrInstance p_instance) {
ERR_FAIL_NULL(OpenXRAPI::get_singleton());
// Obtain pointers to functions we're accessing here, they are (not yet) part of core.
EXT_INIT_XR_FUNC(xrGetVulkanGraphicsRequirements2KHR);
EXT_INIT_XR_FUNC(xrCreateVulkanInstanceKHR);
EXT_INIT_XR_FUNC(xrGetVulkanGraphicsDevice2KHR);
EXT_INIT_XR_FUNC(xrCreateVulkanDeviceKHR);
EXT_INIT_XR_FUNC(xrEnumerateSwapchainImages);
}
bool OpenXRVulkanExtension::check_graphics_api_support(XrVersion p_desired_version) {
ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
XrGraphicsRequirementsVulkan2KHR vulkan_requirements = {
XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR, // type
nullptr, // next
0, // minApiVersionSupported
0 // maxApiVersionSupported
};
XrResult result = xrGetVulkanGraphicsRequirements2KHR(OpenXRAPI::get_singleton()->get_instance(), OpenXRAPI::get_singleton()->get_system_id(), &vulkan_requirements);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get Vulkan graphics requirements [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
// #ifdef DEBUG
print_line("OpenXR: XrGraphicsRequirementsVulkan2KHR:");
print_line(" - minApiVersionSupported: ", OpenXRUtil::make_xr_version_string(vulkan_requirements.minApiVersionSupported));
print_line(" - maxApiVersionSupported: ", OpenXRUtil::make_xr_version_string(vulkan_requirements.maxApiVersionSupported));
// #endif
if (p_desired_version < vulkan_requirements.minApiVersionSupported) {
print_line("OpenXR: Requested Vulkan version does not meet the minimum version this runtime supports.");
print_line("- desired_version ", OpenXRUtil::make_xr_version_string(p_desired_version));
print_line("- minApiVersionSupported ", OpenXRUtil::make_xr_version_string(vulkan_requirements.minApiVersionSupported));
print_line("- maxApiVersionSupported ", OpenXRUtil::make_xr_version_string(vulkan_requirements.maxApiVersionSupported));
return false;
}
if (p_desired_version > vulkan_requirements.maxApiVersionSupported) {
print_line("OpenXR: Requested Vulkan version exceeds the maximum version this runtime has been tested on and is known to support.");
print_line("- desired_version ", OpenXRUtil::make_xr_version_string(p_desired_version));
print_line("- minApiVersionSupported ", OpenXRUtil::make_xr_version_string(vulkan_requirements.minApiVersionSupported));
print_line("- maxApiVersionSupported ", OpenXRUtil::make_xr_version_string(vulkan_requirements.maxApiVersionSupported));
}
return true;
}
bool OpenXRVulkanExtension::create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance) {
// get the vulkan version we are creating
uint32_t vulkan_version = p_vulkan_create_info->pApplicationInfo->apiVersion;
uint32_t major_version = VK_VERSION_MAJOR(vulkan_version);
uint32_t minor_version = VK_VERSION_MINOR(vulkan_version);
uint32_t patch_version = VK_VERSION_PATCH(vulkan_version);
XrVersion desired_version = XR_MAKE_VERSION(major_version, minor_version, patch_version);
// check if this is supported
if (!check_graphics_api_support(desired_version)) {
return false;
}
XrVulkanInstanceCreateInfoKHR xr_vulkan_instance_info = {
XR_TYPE_VULKAN_INSTANCE_CREATE_INFO_KHR, // type
nullptr, // next
OpenXRAPI::get_singleton()->get_system_id(), // systemId
0, // createFlags
vkGetInstanceProcAddr, // pfnGetInstanceProcAddr
p_vulkan_create_info, // vulkanCreateInfo
nullptr, // vulkanAllocator
};
VkResult vk_result = VK_SUCCESS;
XrResult result = xrCreateVulkanInstanceKHR(OpenXRAPI::get_singleton()->get_instance(), &xr_vulkan_instance_info, &vulkan_instance, &vk_result);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to create Vulkan instance [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
ERR_FAIL_COND_V_MSG(vk_result == VK_ERROR_INCOMPATIBLE_DRIVER, false,
"Cannot find a compatible Vulkan installable client driver (ICD).\n\n"
"vkCreateInstance Failure");
ERR_FAIL_COND_V_MSG(vk_result == VK_ERROR_EXTENSION_NOT_PRESENT, false,
"Cannot find a specified extension library.\n"
"Make sure your layers path is set appropriately.\n"
"vkCreateInstance Failure");
ERR_FAIL_COND_V_MSG(vk_result, false,
"vkCreateInstance failed.\n\n"
"Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
"Please look at the Getting Started guide for additional information.\n"
"vkCreateInstance Failure");
*r_instance = vulkan_instance;
return true;
}
bool OpenXRVulkanExtension::get_physical_device(VkPhysicalDevice *r_device) {
ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
XrVulkanGraphicsDeviceGetInfoKHR get_info = {
XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR, // type
nullptr, // next
OpenXRAPI::get_singleton()->get_system_id(), // systemId
vulkan_instance, // vulkanInstance
};
XrResult result = xrGetVulkanGraphicsDevice2KHR(OpenXRAPI::get_singleton()->get_instance(), &get_info, &vulkan_physical_device);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to obtain Vulkan physical device [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
*r_device = vulkan_physical_device;
return true;
}
bool OpenXRVulkanExtension::create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) {
ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
XrVulkanDeviceCreateInfoKHR create_info = {
XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR, // type
nullptr, // next
OpenXRAPI::get_singleton()->get_system_id(), // systemId
0, // createFlags
vkGetInstanceProcAddr, // pfnGetInstanceProcAddr
vulkan_physical_device, // vulkanPhysicalDevice
p_device_create_info, // vulkanCreateInfo
nullptr // vulkanAllocator
};
VkResult vk_result = VK_SUCCESS;
XrResult result = xrCreateVulkanDeviceKHR(OpenXRAPI::get_singleton()->get_instance(), &create_info, &vulkan_device, &vk_result);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to create Vulkan device [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
if (vk_result != VK_SUCCESS) {
print_line("OpenXR: Failed to create Vulkan device [Vulkan error", vk_result, "]");
}
*r_device = vulkan_device;
return true;
}
void OpenXRVulkanExtension::set_direct_queue_family_and_index(uint32_t p_queue_family_index, uint32_t p_queue_index) {
vulkan_queue_family_index = p_queue_family_index;
vulkan_queue_index = p_queue_index;
}
XrGraphicsBindingVulkanKHR OpenXRVulkanExtension::graphics_binding_vulkan;
void *OpenXRVulkanExtension::set_session_create_and_get_next_pointer(void *p_next_pointer) {
DEV_ASSERT(vulkan_queue_family_index < UINT32_MAX && "Direct queue family index was not specified yet.");
DEV_ASSERT(vulkan_queue_index < UINT32_MAX && "Direct queue index was not specified yet.");
graphics_binding_vulkan.type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR;
graphics_binding_vulkan.next = p_next_pointer;
graphics_binding_vulkan.instance = vulkan_instance;
graphics_binding_vulkan.physicalDevice = vulkan_physical_device;
graphics_binding_vulkan.device = vulkan_device;
graphics_binding_vulkan.queueFamilyIndex = vulkan_queue_family_index;
graphics_binding_vulkan.queueIndex = vulkan_queue_index;
return &graphics_binding_vulkan;
}
void OpenXRVulkanExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) {
// We might want to do more here especially if we keep things in linear color space
// Possibly add in R10G10B10A2 as an option if we're using the mobile renderer.
p_usable_swap_chains.push_back(VK_FORMAT_R8G8B8A8_SRGB);
p_usable_swap_chains.push_back(VK_FORMAT_B8G8R8A8_SRGB);
p_usable_swap_chains.push_back(VK_FORMAT_R8G8B8A8_UINT);
p_usable_swap_chains.push_back(VK_FORMAT_B8G8R8A8_UINT);
}
void OpenXRVulkanExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) {
// Note, it is very likely we do NOT support any of depth formats where we can combine our stencil support (e.g. _S8_UINT).
// Right now this isn't a problem but once stencil support becomes an issue, we need to check for this in the rendering engine
// and create a separate buffer for the stencil.
p_usable_swap_chains.push_back(VK_FORMAT_D24_UNORM_S8_UINT);
p_usable_swap_chains.push_back(VK_FORMAT_D32_SFLOAT_S8_UINT);
p_usable_swap_chains.push_back(VK_FORMAT_D32_SFLOAT);
}
bool OpenXRVulkanExtension::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) {
LocalVector<XrSwapchainImageVulkanKHR> images;
LocalVector<XrSwapchainImageFoveationVulkanFB> density_images;
RenderingServer *rendering_server = RenderingServer::get_singleton();
ERR_FAIL_NULL_V(rendering_server, false);
RenderingDevice *rendering_device = rendering_server->get_rendering_device();
ERR_FAIL_NULL_V(rendering_device, false);
uint32_t swapchain_length;
XrResult result = xrEnumerateSwapchainImages(p_swapchain, 0, &swapchain_length, nullptr);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get swapchain image count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
images.resize(swapchain_length);
for (XrSwapchainImageVulkanKHR &image : images) {
image.type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR;
image.next = nullptr;
image.image = VK_NULL_HANDLE;
}
if (OpenXRFBFoveationExtension::get_singleton()->is_enabled()) {
density_images.resize(swapchain_length);
for (uint64_t i = 0; i < swapchain_length; i++) {
density_images[i].type = XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB;
density_images[i].next = nullptr;
density_images[i].image = VK_NULL_HANDLE;
density_images[i].width = 0;
density_images[i].height = 0;
images[i].next = &density_images[i];
}
}
result = xrEnumerateSwapchainImages(p_swapchain, swapchain_length, &swapchain_length, (XrSwapchainImageBaseHeader *)images.ptr());
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get swapchain images [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
SwapchainGraphicsData *data = memnew(SwapchainGraphicsData);
if (data == nullptr) {
print_line("OpenXR: Failed to allocate memory for swapchain data");
return false;
}
*r_swapchain_graphics_data = data;
data->is_multiview = (p_array_size > 1);
RenderingDevice::DataFormat format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
RenderingDevice::TextureSamples samples = RenderingDevice::TEXTURE_SAMPLES_1;
uint64_t usage_flags = RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT;
switch (p_swapchain_format) {
case VK_FORMAT_R8G8B8A8_SRGB:
// Even though this is an sRGB framebuffer format we're using UNORM here.
// The reason here is because Godot does a linear to sRGB conversion while
// with the sRGB format, this conversion would be doubled by the hardware.
// This also means we're reading the values as is for our preview on screen.
// The OpenXR runtime however is still treating this as an sRGB format and
// will thus do an sRGB -> Linear conversion as expected.
//format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UNORM;
usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case VK_FORMAT_B8G8R8A8_SRGB:
//format = RenderingDevice::DATA_FORMAT_B8G8R8A8_SRGB;
format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UNORM;
usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case VK_FORMAT_R8G8B8A8_UINT:
format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UINT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case VK_FORMAT_B8G8R8A8_UINT:
format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UINT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case VK_FORMAT_R16G16B16A16_SFLOAT:
format = RenderingDevice::DATA_FORMAT_R16G16B16A16_SFLOAT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case VK_FORMAT_D32_SFLOAT:
format = RenderingDevice::DATA_FORMAT_D32_SFLOAT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
break;
case VK_FORMAT_D24_UNORM_S8_UINT:
format = RenderingDevice::DATA_FORMAT_D24_UNORM_S8_UINT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
break;
case VK_FORMAT_D32_SFLOAT_S8_UINT:
format = RenderingDevice::DATA_FORMAT_D32_SFLOAT_S8_UINT;
usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
break;
default:
// continue with our default value
print_line("OpenXR: Unsupported swapchain format", p_swapchain_format);
break;
}
switch (p_sample_count) {
case 1:
samples = RenderingDevice::TEXTURE_SAMPLES_1;
break;
case 2:
samples = RenderingDevice::TEXTURE_SAMPLES_2;
break;
case 4:
samples = RenderingDevice::TEXTURE_SAMPLES_4;
break;
case 8:
samples = RenderingDevice::TEXTURE_SAMPLES_8;
break;
case 16:
samples = RenderingDevice::TEXTURE_SAMPLES_16;
break;
case 32:
samples = RenderingDevice::TEXTURE_SAMPLES_32;
break;
case 64:
samples = RenderingDevice::TEXTURE_SAMPLES_64;
break;
default:
// continue with our default value
print_line("OpenXR: Unsupported sample count", p_sample_count);
break;
}
Vector<RID> texture_rids;
Vector<RID> density_map_rids;
// create Godot texture objects for each entry in our swapchain
for (uint32_t i = 0; i < swapchain_length; i++) {
const XrSwapchainImageVulkanKHR &swapchain_image = images[i];
RID image_rid = rendering_device->texture_create_from_extension(
p_array_size == 1 ? RenderingDevice::TEXTURE_TYPE_2D : RenderingDevice::TEXTURE_TYPE_2D_ARRAY,
format,
samples,
usage_flags,
(uint64_t)swapchain_image.image,
p_width,
p_height,
1,
p_array_size,
1);
texture_rids.push_back(image_rid);
if (OpenXRFBFoveationExtension::get_singleton()->is_enabled() && density_images[i].image != VK_NULL_HANDLE) {
RID density_map_rid = rendering_device->texture_create_from_extension(
p_array_size == 1 ? RenderingDevice::TEXTURE_TYPE_2D : RenderingDevice::TEXTURE_TYPE_2D_ARRAY,
RD::DATA_FORMAT_R8G8_UNORM,
RenderingDevice::TEXTURE_SAMPLES_1,
RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT,
(uint64_t)density_images[i].image,
density_images[i].width,
density_images[i].height,
1,
p_array_size,
1);
density_map_rids.push_back(density_map_rid);
} else {
density_map_rids.push_back(RID());
}
}
data->texture_rids = texture_rids;
data->density_map_rids = density_map_rids;
return true;
}
bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
// Even though this is a Vulkan renderer we're using OpenGL coordinate systems.
OpenXRUtil::XrMatrix4x4f matrix;
OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
r_camera_matrix.columns[j][i] = matrix.m[j * 4 + i];
}
}
return true;
}
RID OpenXRVulkanExtension::get_texture(void *p_swapchain_graphics_data, int p_image_index) {
SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data;
ERR_FAIL_NULL_V(data, RID());
ERR_FAIL_INDEX_V(p_image_index, data->texture_rids.size(), RID());
return data->texture_rids[p_image_index];
}
RID OpenXRVulkanExtension::get_density_map(void *p_swapchain_graphics_data, int p_image_index) {
SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data;
ERR_FAIL_NULL_V(data, RID());
ERR_FAIL_INDEX_V(p_image_index, data->density_map_rids.size(), RID());
return data->density_map_rids[p_image_index];
}
void OpenXRVulkanExtension::cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) {
if (*p_swapchain_graphics_data == nullptr) {
return;
}
SwapchainGraphicsData *data = (SwapchainGraphicsData *)*p_swapchain_graphics_data;
RenderingServer *rendering_server = RenderingServer::get_singleton();
ERR_FAIL_NULL(rendering_server);
RenderingDevice *rendering_device = rendering_server->get_rendering_device();
ERR_FAIL_NULL(rendering_device);
for (const RID &texture_rid : data->texture_rids) {
// This should clean up our RIDs and associated texture objects but shouldn't destroy the images, they are owned by our XrSwapchain.
rendering_device->free(texture_rid);
}
data->texture_rids.clear();
for (int i = 0; i < data->density_map_rids.size(); i++) {
if (data->density_map_rids[i].is_valid()) {
rendering_device->free(data->density_map_rids[i]);
}
}
data->density_map_rids.clear();
memdelete(data);
*p_swapchain_graphics_data = nullptr;
}
#define ENUM_TO_STRING_CASE(e) \
case e: { \
return String(#e); \
} break;
String OpenXRVulkanExtension::get_swapchain_format_name(int64_t p_swapchain_format) const {
// This really should be in vulkan_context...
VkFormat format = VkFormat(p_swapchain_format);
switch (format) {
ENUM_TO_STRING_CASE(VK_FORMAT_UNDEFINED)
ENUM_TO_STRING_CASE(VK_FORMAT_R4G4_UNORM_PACK8)
ENUM_TO_STRING_CASE(VK_FORMAT_R4G4B4A4_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_B4G4R4A4_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R5G6B5_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_B5G6R5_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R5G5B5A1_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_B5G5R5A1_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_A1R5G5B5_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_SRGB)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_SRGB)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_SRGB)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_SRGB)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_SRGB)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_SRGB)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_UNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_SNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_USCALED_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_SSCALED_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_UINT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_SINT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_SRGB_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2R10G10B10_UNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2R10G10B10_SNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2R10G10B10_USCALED_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2R10G10B10_SSCALED_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2R10G10B10_UINT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2R10G10B10_SINT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2B10G10R10_UNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2B10G10R10_SNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2B10G10R10_USCALED_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2B10G10R10_SSCALED_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2B10G10R10_UINT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2B10G10R10_SINT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32B32_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32B32_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32B32_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32B32A32_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32B32A32_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32B32A32_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64B64_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64B64_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64B64_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64B64A64_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64B64A64_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64B64A64_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_B10G11R11_UFLOAT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_D16_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_X8_D24_UNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_D32_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_S8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_D16_UNORM_S8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_D24_UNORM_S8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_D32_SFLOAT_S8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_BC1_RGB_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC1_RGB_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC1_RGBA_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC1_RGBA_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC2_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC2_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC3_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC3_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC4_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC4_SNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC5_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC5_SNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC6H_UFLOAT_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC6H_SFLOAT_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC7_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC7_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_EAC_R11_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_EAC_R11_SNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_EAC_R11G11_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_EAC_R11G11_SNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_4x4_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_4x4_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_5x4_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_5x4_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_5x5_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_5x5_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_6x5_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_6x5_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_6x6_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_6x6_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x5_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x5_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x6_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x6_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x8_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x8_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x5_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x5_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x6_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x6_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x8_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x8_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x10_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x10_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_12x10_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_12x10_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_12x12_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_12x12_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_G8B8G8R8_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8G8_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G8_B8R8_2PLANE_420_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G8_B8R8_2PLANE_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R10X6_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R10X6G10X6_UNORM_2PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R12X4_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R12X4G12X4_UNORM_2PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G16B16G16R16_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_B16G16R16G16_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G16_B16R16_2PLANE_420_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G16_B16R16_2PLANE_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_MAX_ENUM)
default: {
return String("Swapchain format ") + String::num_int64(int64_t(p_swapchain_format));
} break;
}
}

View File

@@ -0,0 +1,90 @@
/**************************************************************************/
/* openxr_vulkan_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. */
/**************************************************************************/
#pragma once
#include "../../openxr_api.h"
#include "../../util.h"
#include "../openxr_extension_wrapper.h"
#include "core/templates/vector.h"
#include "drivers/vulkan/vulkan_hooks.h"
// Always include this as late as possible.
#include "../../openxr_platform_inc.h"
class OpenXRVulkanExtension : public OpenXRGraphicsExtensionWrapper, VulkanHooks {
public:
OpenXRVulkanExtension() = default;
virtual ~OpenXRVulkanExtension() override = default;
virtual HashMap<String, bool *> get_requested_extensions() override;
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 bool create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance) override final;
virtual bool get_physical_device(VkPhysicalDevice *r_device) override final;
virtual bool create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) override final;
virtual void set_direct_queue_family_and_index(uint32_t p_queue_family_index, uint32_t p_queue_index) override final;
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;
virtual bool 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) override;
virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) override;
virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) override;
virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) override;
virtual RID get_density_map(void *p_swapchain_graphics_data, int p_image_index) override;
private:
static OpenXRVulkanExtension *singleton;
static XrGraphicsBindingVulkanKHR graphics_binding_vulkan; // declaring this as static so we don't need to know its size and we only need it once when creating our session
struct SwapchainGraphicsData {
bool is_multiview;
Vector<RID> texture_rids;
Vector<RID> density_map_rids;
};
bool check_graphics_api_support(XrVersion p_desired_version);
VkInstance vulkan_instance = nullptr;
VkPhysicalDevice vulkan_physical_device = nullptr;
VkDevice vulkan_device = nullptr;
uint32_t vulkan_queue_family_index = UINT32_MAX;
uint32_t vulkan_queue_index = UINT32_MAX;
EXT_PROTO_XRRESULT_FUNC3(xrGetVulkanGraphicsRequirements2KHR, (XrInstance), p_instance, (XrSystemId), p_system_id, (XrGraphicsRequirementsVulkanKHR *), p_graphics_requirements)
EXT_PROTO_XRRESULT_FUNC4(xrCreateVulkanInstanceKHR, (XrInstance), p_instance, (const XrVulkanInstanceCreateInfoKHR *), p_create_info, (VkInstance *), r_vulkan_instance, (VkResult *), r_vulkan_result)
EXT_PROTO_XRRESULT_FUNC3(xrGetVulkanGraphicsDevice2KHR, (XrInstance), p_instance, (const XrVulkanGraphicsDeviceGetInfoKHR *), p_get_info, (VkPhysicalDevice *), r_vulkan_physical_device)
EXT_PROTO_XRRESULT_FUNC4(xrCreateVulkanDeviceKHR, (XrInstance), p_instance, (const XrVulkanDeviceCreateInfoKHR *), p_create_info, (VkDevice *), r_device, (VkResult *), r_result)
EXT_PROTO_XRRESULT_FUNC4(xrEnumerateSwapchainImages, (XrSwapchain), p_swapchain, (uint32_t), p_image_capacity_input, (uint32_t *), p_image_count_output, (XrSwapchainImageBaseHeader *), p_images)
};