Spaces:
Build error
Build error
/* | |
* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
* SPDX-License-Identifier: Apache-2.0 | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
/** @file openxr_hmd.h | |
* @author Thomas Müller & Ingo Esser & Robert Menzel, NVIDIA | |
* @brief Wrapper around the OpenXR API, providing access to | |
* per-eye framebuffers, lens parameters, visible area, | |
* view, hand, and eye poses, as well as controller inputs. | |
*/ | |
namespace ngp { | |
enum class EEnvironmentBlendMode { | |
Opaque = XR_ENVIRONMENT_BLEND_MODE_OPAQUE, | |
Additive = XR_ENVIRONMENT_BLEND_MODE_ADDITIVE, | |
AlphaBlend = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND, | |
}; | |
inline std::string to_string(EEnvironmentBlendMode mode) { | |
switch (mode) { | |
case EEnvironmentBlendMode::Opaque: return "Opaque"; | |
case EEnvironmentBlendMode::Additive: return "Additive"; | |
case EEnvironmentBlendMode::AlphaBlend: return "Blend"; | |
default: throw std::runtime_error{"Invalid blend mode."}; | |
} | |
} | |
class OpenXRHMD { | |
public: | |
enum class EControlFlow { | |
Continue, | |
Restart, | |
Quit, | |
}; | |
struct FrameInfo { | |
struct View { | |
GLuint framebuffer; | |
XrCompositionLayerProjectionView view{XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}; | |
XrCompositionLayerDepthInfoKHR depth_info{XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR}; | |
std::shared_ptr<Buffer2D<uint8_t>> hidden_area_mask = nullptr; | |
mat4x3 pose; | |
}; | |
struct Hand { | |
mat4x3 pose; | |
bool pose_active = false; | |
vec2 thumbstick = vec2(0.0f); | |
float grab_strength = 0.0f; | |
bool grabbing = false; | |
bool pressing = false; | |
vec3 grab_pos; | |
vec3 prev_grab_pos; | |
vec3 drag() const { | |
return grab_pos - prev_grab_pos; | |
} | |
}; | |
std::vector<View> views; | |
Hand hands[2]; | |
}; | |
using FrameInfoPtr = std::shared_ptr<FrameInfo>; | |
// RAII OpenXRHMD with OpenGL | |
OpenXRHMD(HDC hdc, HGLRC hglrc); | |
OpenXRHMD(Display* xDisplay, uint32_t visualid, GLXFBConfig glxFBConfig, GLXDrawable glxDrawable, GLXContext glxContext); | |
OpenXRHMD(wl_display* display); | |
virtual ~OpenXRHMD(); | |
// disallow copy / move | |
OpenXRHMD(const OpenXRHMD&) = delete; | |
OpenXRHMD& operator=(const OpenXRHMD&) = delete; | |
OpenXRHMD(OpenXRHMD&&) = delete; | |
OpenXRHMD& operator=(OpenXRHMD&&) = delete; | |
void clear(); | |
// poll events, handle state changes, return control flow information | |
EControlFlow poll_events(); | |
// begin OpenXR frame, return views to render | |
FrameInfoPtr begin_frame(); | |
// must be called for each begin_frame | |
void end_frame(FrameInfoPtr frame_info, float znear, float zfar, bool submit_depth); | |
void set_environment_blend_mode(EEnvironmentBlendMode mode) { | |
m_environment_blend_mode = mode; | |
} | |
EEnvironmentBlendMode environment_blend_mode() const { | |
return m_environment_blend_mode; | |
} | |
const std::vector<EEnvironmentBlendMode>& supported_environment_blend_modes() const { | |
return m_supported_environment_blend_modes; | |
} | |
const char* supported_environment_blend_modes_imgui_string() const { | |
return m_supported_environment_blend_modes_imgui_string.data(); | |
} | |
// if true call begin_frame and end_frame - does not imply visibility | |
bool must_run_frame_loop() const { | |
return | |
m_session_state == XR_SESSION_STATE_READY || | |
m_session_state == XR_SESSION_STATE_SYNCHRONIZED || | |
m_session_state == XR_SESSION_STATE_VISIBLE || | |
m_session_state == XR_SESSION_STATE_FOCUSED; | |
} | |
// if true, VR is being rendered to the HMD | |
bool is_visible() const { | |
// XR_SESSION_STATE_VISIBLE -> app content is shown in HMD | |
// XR_SESSION_STATE_FOCUSED -> VISIBLE + input is send to app | |
return m_session_state == XR_SESSION_STATE_VISIBLE || m_session_state == XR_SESSION_STATE_FOCUSED; | |
} | |
private: | |
// steps of the init process, called from the constructor | |
void init_create_xr_instance(); | |
void init_get_xr_system(); | |
void init_configure_xr_views(); | |
void init_check_for_xr_blend_mode(); | |
void init_xr_actions(); | |
void init_open_gl(HDC hdc, HGLRC hglrc); | |
void init_open_gl(Display* xDisplay, uint32_t visualid, GLXFBConfig glxFBConfig, GLXDrawable glxDrawable, GLXContext glxContext); | |
void init_open_gl(wl_display* display); | |
void init_xr_session(); | |
void init_xr_spaces(); | |
void init_xr_swapchain_open_gl(); | |
void init_open_gl_shaders(); | |
// session state change | |
void session_state_change(XrSessionState state, EControlFlow& flow); | |
std::shared_ptr<Buffer2D<uint8_t>> rasterize_hidden_area_mask(uint32_t view_index, const XrCompositionLayerProjectionView& view); | |
// system/instance | |
XrInstance m_instance{XR_NULL_HANDLE}; | |
XrSystemId m_system_id = {}; | |
XrInstanceProperties m_instance_properties = {XR_TYPE_INSTANCE_PROPERTIES}; | |
XrSystemProperties m_system_properties = {XR_TYPE_SYSTEM_PROPERTIES}; | |
std::vector<XrApiLayerProperties> m_api_layer_properties; | |
std::vector<XrExtensionProperties> m_instance_extension_properties; | |
// view and blending | |
XrViewConfigurationType m_view_configuration_type = {}; | |
XrViewConfigurationProperties m_view_configuration_properties = {XR_TYPE_VIEW_CONFIGURATION_PROPERTIES}; | |
std::vector<XrViewConfigurationView> m_view_configuration_views; | |
std::vector<EEnvironmentBlendMode> m_supported_environment_blend_modes; | |
std::vector<char> m_supported_environment_blend_modes_imgui_string; | |
EEnvironmentBlendMode m_environment_blend_mode = EEnvironmentBlendMode::Opaque; | |
// actions | |
std::array<XrPath, 2> m_hand_paths; | |
std::array<XrSpace, 2> m_hand_spaces; | |
XrAction m_pose_action{XR_NULL_HANDLE}; | |
XrAction m_press_action{XR_NULL_HANDLE}; | |
XrAction m_grab_action{XR_NULL_HANDLE}; | |
// Two separate actions for Xbox controller support | |
std::array<XrAction, 2> m_thumbstick_actions; | |
XrActionSet m_action_set{XR_NULL_HANDLE}; | |
XrGraphicsBindingOpenGLWin32KHR m_graphics_binding{XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR}; | |
XrGraphicsBindingOpenGLXlibKHR m_graphics_binding{XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR}; | |
XrGraphicsBindingOpenGLWaylandKHR m_graphics_binding{XR_TYPE_GRAPHICS_BINDING_OPENGL_WAYLAND_KHR}; | |
XrSession m_session{XR_NULL_HANDLE}; | |
XrSessionState m_session_state{XR_SESSION_STATE_UNKNOWN}; | |
// reference space | |
std::vector<XrReferenceSpaceType> m_reference_spaces; | |
XrSpace m_space{XR_NULL_HANDLE}; | |
XrExtent2Df m_bounds; | |
// swap chains | |
struct Swapchain { | |
Swapchain(XrSwapchainCreateInfo& rgba_create_info, XrSwapchainCreateInfo& depth_create_info, XrSession& session, XrInstance& xr_instance); | |
Swapchain(const Swapchain&) = delete; | |
Swapchain& operator=(const Swapchain&) = delete; | |
Swapchain(Swapchain&& other) { | |
*this = std::move(other); | |
} | |
Swapchain& operator=(Swapchain&& other) { | |
std::swap(handle, other.handle); | |
std::swap(depth_handle, other.depth_handle); | |
std::swap(width, other.width); | |
std::swap(height, other.height); | |
images_gl = std::move(other.images_gl); | |
depth_images_gl = std::move(other.depth_images_gl); | |
framebuffers_gl = std::move(other.framebuffers_gl); | |
return *this; | |
} | |
virtual ~Swapchain(); | |
void clear(); | |
XrSwapchain handle{XR_NULL_HANDLE}; | |
XrSwapchain depth_handle{XR_NULL_HANDLE}; | |
int32_t width = 0; | |
int32_t height = 0; | |
std::vector<XrSwapchainImageOpenGLKHR> images_gl; | |
std::vector<XrSwapchainImageOpenGLKHR> depth_images_gl; | |
std::vector<GLuint> framebuffers_gl; | |
}; | |
int64_t m_swapchain_rgba_format = 0; | |
std::vector<Swapchain> m_swapchains; | |
bool m_supports_composition_layer_depth = false; | |
int64_t m_swapchain_depth_format = 0; | |
bool m_supports_hidden_area_mask = false; | |
std::vector<std::shared_ptr<Buffer2D<uint8_t>>> m_hidden_area_masks; | |
bool m_supports_eye_tracking = false; | |
// frame data | |
XrFrameState m_frame_state{XR_TYPE_FRAME_STATE}; | |
FrameInfoPtr m_previous_frame_info; | |
GLuint m_hidden_area_mask_program = 0; | |
// print more debug info during OpenXRs init: | |
const bool m_print_api_layers = false; | |
const bool m_print_extensions = false; | |
const bool m_print_system_properties = false; | |
const bool m_print_instance_properties = false; | |
const bool m_print_view_configuration_types = false; | |
const bool m_print_view_configuration_properties = false; | |
const bool m_print_view_configuration_view = false; | |
const bool m_print_environment_blend_modes = false; | |
const bool m_print_available_swapchain_formats = false; | |
const bool m_print_reference_spaces = false; | |
}; | |
} | |