Skip to content

Commit

Permalink
Merge pull request godotengine#87235 from dsnopek/openxr-local-floor
Browse files Browse the repository at this point in the history
Add support for OpenXR local floor extension
  • Loading branch information
YuriSizov committed Jan 24, 2024
2 parents f062d72 + a8690cb commit ea6e202
Show file tree
Hide file tree
Showing 11 changed files with 269 additions and 28 deletions.
2 changes: 1 addition & 1 deletion doc/classes/XRInterface.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions modules/openxr/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
59 changes: 59 additions & 0 deletions modules/openxr/extensions/openxr_local_floor_extension.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
53 changes: 53 additions & 0 deletions modules/openxr/extensions/openxr_local_floor_extension.h
Original file line number Diff line number Diff line change
@@ -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
123 changes: 108 additions & 15 deletions modules/openxr/openxr_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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;
Expand Down
10 changes: 8 additions & 2 deletions modules/openxr/openxr_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ class OpenXRAPI {
// 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
Expand Down Expand Up @@ -149,6 +150,10 @@ class OpenXRAPI {
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;
Expand Down Expand Up @@ -333,7 +338,8 @@ class OpenXRAPI {
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);
Expand Down
Loading

0 comments on commit ea6e202

Please sign in to comment.