Add support for OpenXR local floor extension

This commit is contained in:
David Snopek 2024-01-15 16:26:55 -06:00
parent 74c32faa78
commit a8690cbeed
11 changed files with 269 additions and 28 deletions

View file

@ -256,7 +256,7 @@
Player is free to move around, full positional tracking.
</constant>
<constant name="XR_PLAY_AREA_STAGE" value="4" enum="PlayAreaMode">
Same as [constant XR_PLAY_AREA_ROOMSCALE] but origin point is fixed to the center of the physical space, [method XRServer.center_on_hmd] disabled.
Same as [constant XR_PLAY_AREA_ROOMSCALE] but origin point is fixed to the center of the physical space. In this mode, system-level recentering may be disabled, requiring the use of [method XRServer.center_on_hmd].
</constant>
<constant name="XR_ENV_BLEND_MODE_OPAQUE" value="0" enum="EnvironmentBlendMode">
Opaque blend mode. This is typically used for VR devices.

View file

@ -2229,7 +2229,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "xr/openxr/default_action_map", PROPERTY_HINT_FILE, "*.tres"), "res://openxr_action_map.tres");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/form_factor", PROPERTY_HINT_ENUM, "Head Mounted,Handheld"), "0");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/view_configuration", PROPERTY_HINT_ENUM, "Mono,Stereo"), "1"); // "Mono,Stereo,Quad,Observer"
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/reference_space", PROPERTY_HINT_ENUM, "Local,Stage"), "1");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/reference_space", PROPERTY_HINT_ENUM, "Local,Stage,Local Floor"), "1");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/environment_blend_mode", PROPERTY_HINT_ENUM, "Opaque,Additive,Alpha"), "0");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/foveation_level", PROPERTY_HINT_ENUM, "Off,Low,Medium,High"), "0");
GLOBAL_DEF_BASIC("xr/openxr/foveation_dynamic", false);

View file

@ -104,6 +104,7 @@ if env["vulkan"]:
if env["opengl3"] and env["platform"] != "macos":
env_openxr.add_source_files(module_obj, "extensions/openxr_opengl_extension.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_local_floor_extension.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_palm_pose_extension.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_composition_layer_depth_extension.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_eye_gaze_interaction.cpp")

View file

@ -0,0 +1,59 @@
/**************************************************************************/
/* 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"
#include "core/string/print_string.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,53 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef OPENXR_LOCAL_FLOOR_EXTENSION_H
#define OPENXR_LOCAL_FLOOR_EXTENSION_H
#include "openxr_extension_wrapper.h"
class OpenXRLocalFloorExtension : public OpenXRExtensionWrapper {
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;
};
#endif // OPENXR_LOCAL_FLOOR_EXTENSION_H

View file

@ -665,13 +665,6 @@ bool OpenXRAPI::load_supported_reference_spaces() {
print_verbose(String("OpenXR: Found supported reference space ") + OpenXRUtil::get_reference_space_name(supported_reference_spaces[i]));
}
// Check value we loaded at startup...
if (!is_reference_space_supported(reference_space)) {
print_verbose(String("OpenXR: ") + OpenXRUtil::get_reference_space_name(reference_space) + String(" isn't supported, defaulting to ") + OpenXRUtil::get_reference_space_name(supported_reference_spaces[0]));
reference_space = supported_reference_spaces[0];
}
return true;
}
@ -699,16 +692,31 @@ bool OpenXRAPI::setup_spaces() {
// create play space
{
if (!is_reference_space_supported(reference_space)) {
print_line("OpenXR: reference space ", OpenXRUtil::get_reference_space_name(reference_space), " is not supported.");
return false;
emulating_local_floor = false;
if (is_reference_space_supported(requested_reference_space)) {
reference_space = requested_reference_space;
} else if (requested_reference_space == XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT && is_reference_space_supported(XR_REFERENCE_SPACE_TYPE_STAGE)) {
print_verbose("OpenXR: LOCAL_FLOOR space isn't supported, emulating using STAGE and LOCAL spaces.");
reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
emulating_local_floor = true;
// We'll use the STAGE space to get the floor height, but we can't do that until
// after xrWaitFrame(), so just set this flag for now.
should_reset_emulated_floor_height = true;
} else {
// Fallback on LOCAL, which all OpenXR runtimes are required to support.
print_verbose(String("OpenXR: ") + OpenXRUtil::get_reference_space_name(requested_reference_space) + String(" isn't supported, defaulting to LOCAL space."));
reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
}
XrReferenceSpaceCreateInfo play_space_create_info = {
XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type
nullptr, // next
reference_space, // referenceSpaceType
identityPose // poseInReferenceSpace
identityPose, // poseInReferenceSpace
};
result = xrCreateReferenceSpace(session, &play_space_create_info, &play_space);
@ -742,6 +750,80 @@ bool OpenXRAPI::setup_spaces() {
return true;
}
bool OpenXRAPI::reset_emulated_floor_height() {
ERR_FAIL_COND_V(!emulating_local_floor, false);
// This is based on the example code in the OpenXR spec which shows how to
// emulate LOCAL_FLOOR if it's not supported.
// See: https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_local_floor
XrResult result;
XrPosef identityPose = {
{ 0.0, 0.0, 0.0, 1.0 },
{ 0.0, 0.0, 0.0 }
};
XrSpace local_space = XR_NULL_HANDLE;
XrSpace stage_space = XR_NULL_HANDLE;
XrReferenceSpaceCreateInfo create_info = {
XR_TYPE_REFERENCE_SPACE_CREATE_INFO, // type
nullptr, // next
XR_REFERENCE_SPACE_TYPE_LOCAL, // referenceSpaceType
identityPose, // poseInReferenceSpace
};
result = xrCreateReferenceSpace(session, &create_info, &local_space);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to create LOCAL space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]");
return false;
}
create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
result = xrCreateReferenceSpace(session, &create_info, &stage_space);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to create STAGE space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]");
xrDestroySpace(local_space);
return false;
}
XrSpaceLocation stage_location = {
XR_TYPE_SPACE_LOCATION, // type
nullptr, // next
0, // locationFlags
identityPose, // pose
};
result = xrLocateSpace(stage_space, local_space, get_next_frame_time(), &stage_location);
xrDestroySpace(local_space);
xrDestroySpace(stage_space);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to locate STAGE space in LOCAL space, in order to emulate LOCAL_FLOOR [", get_error_string(result), "]");
return false;
}
XrSpace new_play_space;
create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
create_info.poseInReferenceSpace.position.y = stage_location.pose.position.y;
result = xrCreateReferenceSpace(session, &create_info, &new_play_space);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to recreate emulated LOCAL_FLOOR play space with latest floor estimate [", get_error_string(result), "]");
return false;
}
xrDestroySpace(play_space);
play_space = new_play_space;
// If we've made it this far, it means we can properly emulate LOCAL_FLOOR, so we'll
// report that as the reference space to the outside world.
reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;
return true;
}
bool OpenXRAPI::load_supported_swapchain_formats() {
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
@ -1180,10 +1262,10 @@ void OpenXRAPI::set_view_configuration(XrViewConfigurationType p_view_configurat
view_configuration = p_view_configuration;
}
void OpenXRAPI::set_reference_space(XrReferenceSpaceType p_reference_space) {
void OpenXRAPI::set_requested_reference_space(XrReferenceSpaceType p_requested_reference_space) {
ERR_FAIL_COND(is_initialized());
reference_space = p_reference_space;
requested_reference_space = p_requested_reference_space;
}
void OpenXRAPI::set_submit_depth_buffer(bool p_submit_depth_buffer) {
@ -1628,6 +1710,9 @@ bool OpenXRAPI::poll_events() {
XrEventDataReferenceSpaceChangePending *event = (XrEventDataReferenceSpaceChangePending *)&runtimeEvent;
print_verbose(String("OpenXR EVENT: reference space type ") + OpenXRUtil::get_reference_space_name(event->referenceSpaceType) + " change pending!");
if (emulating_local_floor) {
should_reset_emulated_floor_height = true;
}
if (event->poseValid && xr_interface) {
xr_interface->on_pose_recentered();
}
@ -1783,6 +1868,11 @@ void OpenXRAPI::pre_render() {
frame_state.predictedDisplayPeriod = 0;
}
if (unlikely(should_reset_emulated_floor_height)) {
reset_emulated_floor_height();
should_reset_emulated_floor_height = false;
}
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_pre_render();
}
@ -2136,10 +2226,13 @@ OpenXRAPI::OpenXRAPI() {
int reference_space_setting = GLOBAL_GET("xr/openxr/reference_space");
switch (reference_space_setting) {
case 0: {
reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
requested_reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
} break;
case 1: {
reference_space = XR_REFERENCE_SPACE_TYPE_STAGE;
requested_reference_space = XR_REFERENCE_SPACE_TYPE_STAGE;
} break;
case 2: {
requested_reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;
} break;
default:
break;

View file

@ -98,7 +98,8 @@ private:
// configuration
XrFormFactor form_factor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
XrViewConfigurationType view_configuration = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
XrReferenceSpaceType reference_space = XR_REFERENCE_SPACE_TYPE_STAGE;
XrReferenceSpaceType requested_reference_space = XR_REFERENCE_SPACE_TYPE_STAGE;
XrReferenceSpaceType reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
bool submit_depth_buffer = false; // if set to true we submit depth buffers to OpenXR if a suitable extension is enabled.
// blend mode
@ -149,6 +150,10 @@ private:
bool view_pose_valid = false;
XRPose::TrackingConfidence head_pose_confidence = XRPose::XR_TRACKING_CONFIDENCE_NONE;
bool emulating_local_floor = false;
bool should_reset_emulated_floor_height = false;
bool reset_emulated_floor_height();
bool load_layer_properties();
bool load_supported_extensions();
bool is_extension_supported(const String &p_extension) const;
@ -333,7 +338,8 @@ public:
void set_view_configuration(XrViewConfigurationType p_view_configuration);
XrViewConfigurationType get_view_configuration() const { return view_configuration; }
void set_reference_space(XrReferenceSpaceType p_reference_space);
void set_requested_reference_space(XrReferenceSpaceType p_requested_reference_space);
XrReferenceSpaceType get_requested_reference_space() const { return requested_reference_space; }
XrReferenceSpaceType get_reference_space() const { return reference_space; }
void set_submit_depth_buffer(bool p_submit_depth_buffer);

View file

@ -686,15 +686,48 @@ Dictionary OpenXRInterface::get_system_info() {
}
bool OpenXRInterface::supports_play_area_mode(XRInterface::PlayAreaMode p_mode) {
return false;
if (p_mode == XRInterface::XR_PLAY_AREA_3DOF) {
return false;
}
return true;
}
XRInterface::PlayAreaMode OpenXRInterface::get_play_area_mode() const {
if (!openxr_api || !initialized) {
return XRInterface::XR_PLAY_AREA_UNKNOWN;
}
XrReferenceSpaceType reference_space = openxr_api->get_reference_space();
if (reference_space == XR_REFERENCE_SPACE_TYPE_LOCAL) {
return XRInterface::XR_PLAY_AREA_SITTING;
} else if (reference_space == XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT) {
return XRInterface::XR_PLAY_AREA_ROOMSCALE;
} else if (reference_space == XR_REFERENCE_SPACE_TYPE_STAGE) {
return XRInterface::XR_PLAY_AREA_STAGE;
}
return XRInterface::XR_PLAY_AREA_UNKNOWN;
}
bool OpenXRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) {
return false;
ERR_FAIL_COND_V_MSG(initialized, false, "Cannot change play area mode after OpenXR interface has been initialized");
ERR_FAIL_NULL_V(openxr_api, false);
XrReferenceSpaceType reference_space;
if (p_mode == XRInterface::XR_PLAY_AREA_SITTING) {
reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
} else if (p_mode == XRInterface::XR_PLAY_AREA_ROOMSCALE) {
reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;
} else if (p_mode == XRInterface::XR_PLAY_AREA_STAGE) {
reference_space = XR_REFERENCE_SPACE_TYPE_STAGE;
} else {
return false;
}
openxr_api->set_requested_reference_space(reference_space);
return true;
}
PackedVector3Array OpenXRInterface::get_play_area() const {

View file

@ -49,6 +49,7 @@
#include "extensions/openxr_htc_controller_extension.h"
#include "extensions/openxr_htc_vive_tracker_extension.h"
#include "extensions/openxr_huawei_controller_extension.h"
#include "extensions/openxr_local_floor_extension.h"
#include "extensions/openxr_meta_controller_extension.h"
#include "extensions/openxr_ml2_controller_extension.h"
#include "extensions/openxr_palm_pose_extension.h"
@ -107,6 +108,7 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
// register our other extensions
OpenXRAPI::register_extension_wrapper(memnew(OpenXRPalmPoseExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRLocalFloorExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRPicoControllerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRCompositionLayerDepthExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCControllerExtension));

View file

@ -75,7 +75,7 @@ public:
XR_PLAY_AREA_3DOF, /* Only support orientation tracking, no positional tracking, area will center around player */
XR_PLAY_AREA_SITTING, /* Player is in seated position, limited positional tracking, fixed guardian around player */
XR_PLAY_AREA_ROOMSCALE, /* Player is free to move around, full positional tracking */
XR_PLAY_AREA_STAGE, /* Same as roomscale but origin point is fixed to the center of the physical space, XRServer.center_on_hmd disabled */
XR_PLAY_AREA_STAGE, /* Same as roomscale but origin point is fixed to the center of the physical space */
};
enum EnvironmentBlendMode {

View file

@ -129,12 +129,6 @@ void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) {
return;
}
if (primary_interface->get_play_area_mode() == XRInterface::XR_PLAY_AREA_STAGE) {
// center_on_hmd is not available in this mode
reference_frame = Transform3D();
return;
}
// clear our current reference frame or we'll end up double adjusting it
reference_frame = Transform3D();