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