38static void glfwErrorCallback(
int error,
const char* description)
40#if defined(__aarch64__)
41 if (error == 65544)
return;
43 throw std::runtime_error(description);
52 glfwSetErrorCallback(glfwErrorCallback);
55 if (glfwInit() == GL_FALSE)
throw std::runtime_error(
"Can't initialize GLFW");
61 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, major);
62 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minor);
64#if defined(GL_GLES_PROTOTYPES)
66 glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
67 glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
70 if (major * 10 + minor >= 32)
73 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
74 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
79#if defined(IMGUI_VERSION)
84 ImGui::CreateContext();
93#if defined(IMGUI_VERSION)
95 ImGui_ImplOpenGL3_Shutdown();
96 ImGui_ImplGlfw_Shutdown();
97 ImGui::DestroyContext();
107void GgApp::Window::HumanInterface::resetTranslation()
110 for (
auto& t : translation)
112 std::fill(t.begin(), t.end(),
GgVector{ 0.0f, 0.0f, 0.0f, 1.0f });
116 std::fill(arrow.begin(), arrow.end(), std::array<int, 2>{ 0, 0 });
119 std::fill(wheel.begin(), wheel.end(), 0.0f);
125void GgApp::Window::HumanInterface::calcTranslation(
int button,
const std::array<GLfloat, 3>& velocity)
128 assert(button >= GLFW_MOUSE_BUTTON_1 && button < GLFW_MOUSE_BUTTON_1 +
GG_BUTTON_COUNT);
129 const auto dx{ (mouse[0] - rotation[button].getStart(0)) * rotation[button].getScale(0) };
130 const auto dy{ (rotation[button].getStart(1) - mouse[1]) * rotation[button].getScale(1) };
133 auto& t{ translation[button] };
136 t[1][0] = dx * velocity[0] + t[0][0];
137 t[1][1] = dy * velocity[1] + t[0][1];
140 rotation[button].motion(mouse[0], mouse[1]);
146void GgApp::Window::resize(GLFWwindow* window,
int width,
int height)
149 auto*
const instance{
static_cast<Window*
>(glfwGetWindowUserPointer(window)) };
154 instance->size[0] = width;
155 instance->size[1] = height;
158 for (
auto& current_if : instance->interfaceData)
160 for (
auto& t : current_if.rotation)
162 t.region(width, height);
167 instance->updateViewport();
170 if (instance->resizeFunc) (*instance->resizeFunc)(instance, width, height);
177void GgApp::Window::keyboard(GLFWwindow* window,
int key,
int scancode,
int action,
int mods)
179#if defined(IMGUI_VERSION)
181 if (ImGui::GetIO().WantCaptureKeyboard)
return;
185 auto*
const instance{
static_cast<Window*
>(glfwGetWindowUserPointer(window)) };
187 if (instance && action)
190 if (instance->keyboardFunc) (*instance->keyboardFunc)(instance, key, scancode, action, mods);
193 auto& current_if{ instance->interfaceData[instance->interfaceNo] };
200 instance->resetRotation();
206 instance->resetTranslation();
211 if (mods & GLFW_MOD_SHIFT)
212 current_if.arrow[1][1]++;
213 else if (mods & GLFW_MOD_CONTROL)
214 current_if.arrow[2][1]++;
215 else if (mods & GLFW_MOD_ALT)
216 current_if.arrow[3][1]++;
218 current_if.arrow[0][1]++;
223 if (mods & GLFW_MOD_SHIFT)
224 current_if.arrow[1][1]--;
225 else if (mods & GLFW_MOD_CONTROL)
226 current_if.arrow[2][1]--;
227 else if (mods & GLFW_MOD_ALT)
228 current_if.arrow[3][1]--;
230 current_if.arrow[0][1]--;
235 if (mods & GLFW_MOD_SHIFT)
236 current_if.arrow[1][0]++;
237 else if (mods & GLFW_MOD_CONTROL)
238 current_if.arrow[2][0]++;
239 else if (mods & GLFW_MOD_ALT)
240 current_if.arrow[3][0]++;
242 current_if.arrow[0][0]++;
247 if (mods & GLFW_MOD_SHIFT)
248 current_if.arrow[1][0]--;
249 else if (mods & GLFW_MOD_CONTROL)
250 current_if.arrow[2][0]--;
251 else if (mods & GLFW_MOD_ALT)
252 current_if.arrow[3][0]--;
254 current_if.arrow[0][0]--;
261 current_if.lastKey = key;
268void GgApp::Window::mouse(GLFWwindow* window,
int button,
int action,
int mods)
270#if defined(IMGUI_VERSION)
272 if (ImGui::GetIO().WantCaptureMouse)
return;
276 auto*
const instance{
static_cast<Window*
>(glfwGetWindowUserPointer(window)) };
279 assert(button >= GLFW_MOUSE_BUTTON_1 && button < GLFW_MOUSE_BUTTON_1 +
GG_BUTTON_COUNT);
280 instance->status[button] = action != GLFW_RELEASE;
285 if (instance->mouseFunc) (*instance->mouseFunc)(instance, button, action, mods);
288 auto& current_if{ instance->interfaceData[instance->interfaceNo] };
291 const auto x{ current_if.mouse[0] };
292 const auto y{ current_if.mouse[1] };
294 if (x < 0 || x >= instance->size[0] || y < 0 || y >= instance->size[1])
return;
299 current_if.rotation[button].begin(x, y);
304 current_if.translation[button][0] = current_if.translation[button][1];
305 current_if.rotation[button].end(x, y);
313void GgApp::Window::wheel(GLFWwindow* window,
double x,
double y)
315#if defined(IMGUI_VERSION)
317 if (ImGui::GetIO().WantCaptureMouse)
return;
321 auto*
const instance{
static_cast<Window*
>(glfwGetWindowUserPointer(window)) };
326 if (instance->wheelFunc) (*instance->wheelFunc)(instance, x, y);
329 auto& current_if{ instance->interfaceData[instance->interfaceNo] };
332 current_if.wheel[0] +=
static_cast<GLfloat
>(x);
333 current_if.wheel[1] +=
static_cast<GLfloat
>(y);
336 const auto z{ current_if.wheel[1] * instance->velocity[2] };
337 for (
auto& t : current_if.translation) t[1][2] = z;
345 size{ width, height },
346 fboSize{ width, height }
349 GLFWmonitor* monitor{
nullptr };
356 auto**
const monitors{ glfwGetMonitors(&mcount) };
359 if (fullscreen > mcount) fullscreen = mcount;
360 monitor = monitors[fullscreen - 1];
363 const auto* mode{ glfwGetVideoMode(monitor) };
367 height = mode->height;
371 window = glfwCreateWindow(width, height, title.c_str(), monitor, share);
374 if (!window)
throw std::runtime_error(
"Unable to open the GLFW window.");
377 glfwMakeContextCurrent(window);
383 glfwSetWindowUserPointer(window,
this);
386 glfwSetKeyCallback(window, keyboard);
389 glfwSetMouseButtonCallback(window, mouse);
392 glfwSetScrollCallback(window, wheel);
395 glfwSetFramebufferSizeCallback(window, resize);
401 glfwGetFramebufferSize(window, &width, &height);
404 resize(window, width, height);
406#if defined(IMGUI_VERSION)
408 static bool firstTime{
true };
412 ImGui_ImplGlfw_InitForOpenGL(window,
true);
413 ImGui_ImplOpenGL3_Init(
nullptr);
427 fboSize{ w.fboSize },
428 interfaceData{ std::move(w.interfaceData) },
429 interfaceNo{ w.interfaceNo },
430 userPointer{ w.userPointer },
431 resizeFunc{ w.resizeFunc },
432 keyboardFunc{ w.keyboardFunc },
433 mouseFunc{ w.mouseFunc },
434 wheelFunc{ w.wheelFunc }
439 glfwSetWindowUserPointer(window,
this);
452 glfwDestroyWindow(window);
457 interfaceData = std::move(w.interfaceData);
458 interfaceNo = w.interfaceNo;
459 userPointer = w.userPointer;
460 resizeFunc = w.resizeFunc;
461 keyboardFunc = w.keyboardFunc;
462 mouseFunc = w.mouseFunc;
463 wheelFunc = w.wheelFunc;
468 glfwSetWindowUserPointer(window,
this);
477GgApp::Window::operator bool()
486 auto& current_if{ interfaceData[interfaceNo] };
488#if defined(IMGUI_VERSION)
490 ImGui_ImplOpenGL3_NewFrame();
491 ImGui_ImplGlfw_NewFrame();
495 const ImGuiIO& io{ ImGui::GetIO() };
498 if (io.WantCaptureMouse)
return true;
501 current_if.mouse = std::array<GLfloat, 2>{ io.MousePos.x, io.MousePos.y };
505 glfwGetCursorPos(window, &x, &y);
508 current_if.mouse = std::array<GLfloat, 2>{
static_cast<GLfloat
>(x),
static_cast<GLfloat
>(y) };
512 for (
int button = GLFW_MOUSE_BUTTON_1; button < GLFW_MOUSE_BUTTON_1 +
GG_BUTTON_COUNT; ++button)
518 current_if.calcTranslation(button, velocity);
530#if defined(IMGUI_VERSION)
533 ImDrawData* data{ ImGui::GetDrawData() };
534 if (data) ImGui_ImplOpenGL3_RenderDrawData(data);
541 glfwSwapBuffers(window);
550 glfwGetFramebufferSize(window, &fboSize[0], &fboSize[1]);
552#if defined(IMGUI_VERSION)
554 fboSize[1] -= menubarHeight;
558 aspect =
static_cast<GLfloat
>(fboSize[0]) /
static_cast<GLfloat
>(fboSize[1]);
564#if defined(GG_USE_OPENXR)
571 std::string xrMessage(XrInstance instance, XrResult result,
const std::string& message)
573 char buffer[XR_MAX_RESULT_STRING_SIZE]{
'\0' };
574 if (instance == XR_NULL_HANDLE
575 || XR_FAILED(xrResultToString(instance, result, buffer))
576 || buffer[0] ==
'\0')
578 std::snprintf(buffer,
sizeof buffer,
"XrResult(%d)",
static_cast<int>(result));
580 return message +
": " + buffer;
586 void xrCheck(XrInstance instance, XrResult result,
const std::string& message)
588 if (XR_SUCCEEDED(result))
return;
589 throw std::runtime_error(xrMessage(instance, result, message));
595 bool xrWarn(XrInstance instance, XrResult result,
const std::string& message)
597 if (XR_SUCCEEDED(result))
return true;
598 std::cerr <<
"OpenXR: " << xrMessage(instance, result, message) <<
'\n';
605 void xrCopyString(
char* destination,
size_t size,
const char* source)
607 if (size == 0)
return;
608 const auto length{ std::min(std::strlen(source), size - 1) };
609 std::memcpy(destination, source, length);
610 destination[length] =
'\0';
617GgApp::OpenXR::OpenXR()
624GgApp::OpenXR::~OpenXR()
636void GgApp::OpenXR::initActions()
639 XrActionSetCreateInfo actionSetInfo{ XR_TYPE_ACTION_SET_CREATE_INFO };
640 xrCopyString(actionSetInfo.actionSetName,
sizeof actionSetInfo.actionSetName,
"gameplay");
641 xrCopyString(actionSetInfo.localizedActionSetName,
sizeof actionSetInfo.localizedActionSetName,
"Gameplay");
642 actionSetInfo.priority = 0;
643 xrCheck(instance, xrCreateActionSet(instance, &actionSetInfo, &actionSet),
644 "Can't create the OpenXR action set");
647 xrCheck(instance, xrStringToPath(instance,
"/user/hand/left", &handSubactionPath[Hand::Left]),
648 "Can't convert the path of the left hand");
649 xrCheck(instance, xrStringToPath(instance,
"/user/hand/right", &handSubactionPath[Hand::Right]),
650 "Can't convert the path of the right hand");
653 auto createAction = [
this](
const char* name,
const char* localizedName, XrActionType type, XrAction& action)
655 XrActionCreateInfo createInfo{ XR_TYPE_ACTION_CREATE_INFO };
656 xrCopyString(createInfo.actionName,
sizeof createInfo.actionName, name);
657 xrCopyString(createInfo.localizedActionName,
sizeof createInfo.localizedActionName, localizedName);
658 createInfo.actionType = type;
659 createInfo.countSubactionPaths = Hand::Count;
660 createInfo.subactionPaths = handSubactionPath;
661 xrCheck(instance, xrCreateAction(actionSet, &createInfo, &action),
662 std::string(
"Can't create the OpenXR action \"") + name +
"\"");
665 createAction(
"aim_pose",
"Aim Pose", XR_ACTION_TYPE_POSE_INPUT, aimPoseAction);
666 createAction(
"grip_pose",
"Grip Pose", XR_ACTION_TYPE_POSE_INPUT, gripPoseAction);
667 createAction(
"trigger",
"Trigger", XR_ACTION_TYPE_FLOAT_INPUT, triggerAction);
668 createAction(
"grip",
"Grip", XR_ACTION_TYPE_FLOAT_INPUT, gripAction);
669 createAction(
"thumbstick",
"Thumbstick", XR_ACTION_TYPE_VECTOR2F_INPUT, thumbstickAction);
670 createAction(
"thumbstick_click",
"Thumbstick Click", XR_ACTION_TYPE_BOOLEAN_INPUT, thumbstickClickAction);
671 createAction(
"primary_button",
"Primary Button", XR_ACTION_TYPE_BOOLEAN_INPUT, primaryButtonAction);
672 createAction(
"secondary_button",
"Secondary Button", XR_ACTION_TYPE_BOOLEAN_INPUT, secondaryButtonAction);
673 createAction(
"menu_button",
"Menu Button", XR_ACTION_TYPE_BOOLEAN_INPUT, menuButtonAction);
674 createAction(
"haptic",
"Haptic Vibration", XR_ACTION_TYPE_VIBRATION_OUTPUT, hapticAction);
677 auto suggestBindings = [
this](
const char* profileStr,
const std::vector<std::pair<XrAction, const char*>>& bindings)
679 XrPath profilePath{ XR_NULL_PATH };
680 if (XR_FAILED(xrStringToPath(instance, profileStr, &profilePath)))
return;
682 std::vector<XrActionSuggestedBinding> suggestedBindings;
683 suggestedBindings.reserve(bindings.size());
684 for (
const auto& [action, pathStr] : bindings)
686 XrPath path{ XR_NULL_PATH };
687 if (XR_FAILED(xrStringToPath(instance, pathStr, &path)))
continue;
688 suggestedBindings.push_back(XrActionSuggestedBinding{ action, path });
690 if (suggestedBindings.empty())
return;
692 XrInteractionProfileSuggestedBinding profileSuggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
693 profileSuggestedBindings.interactionProfile = profilePath;
694 profileSuggestedBindings.suggestedBindings = suggestedBindings.data();
695 profileSuggestedBindings.countSuggestedBindings =
static_cast<uint32_t
>(suggestedBindings.size());
696 xrWarn(instance, xrSuggestInteractionProfileBindings(instance, &profileSuggestedBindings),
697 std::string(
"Can't suggest the bindings for ") + profileStr);
701 suggestBindings(
"/interaction_profiles/khr/simple_controller", {
702 { aimPoseAction,
"/user/hand/left/input/aim/pose" },
703 { aimPoseAction,
"/user/hand/right/input/aim/pose" },
704 { gripPoseAction,
"/user/hand/left/input/grip/pose" },
705 { gripPoseAction,
"/user/hand/right/input/grip/pose" },
706 { triggerAction,
"/user/hand/left/input/select/click" },
707 { triggerAction,
"/user/hand/right/input/select/click" },
708 { menuButtonAction,
"/user/hand/left/input/menu/click" },
709 { menuButtonAction,
"/user/hand/right/input/menu/click" },
710 { hapticAction,
"/user/hand/left/output/haptic" },
711 { hapticAction,
"/user/hand/right/output/haptic" }
715 suggestBindings(
"/interaction_profiles/oculus/touch_controller", {
716 { aimPoseAction,
"/user/hand/left/input/aim/pose" },
717 { aimPoseAction,
"/user/hand/right/input/aim/pose" },
718 { gripPoseAction,
"/user/hand/left/input/grip/pose" },
719 { gripPoseAction,
"/user/hand/right/input/grip/pose" },
720 { triggerAction,
"/user/hand/left/input/trigger/value" },
721 { triggerAction,
"/user/hand/right/input/trigger/value" },
722 { gripAction,
"/user/hand/left/input/squeeze/value" },
723 { gripAction,
"/user/hand/right/input/squeeze/value" },
724 { thumbstickAction,
"/user/hand/left/input/thumbstick" },
725 { thumbstickAction,
"/user/hand/right/input/thumbstick" },
726 { thumbstickClickAction,
"/user/hand/left/input/thumbstick/click" },
727 { thumbstickClickAction,
"/user/hand/right/input/thumbstick/click" },
728 { primaryButtonAction,
"/user/hand/left/input/x/click" },
729 { primaryButtonAction,
"/user/hand/right/input/a/click" },
730 { secondaryButtonAction,
"/user/hand/left/input/y/click" },
731 { secondaryButtonAction,
"/user/hand/right/input/b/click" },
732 { menuButtonAction,
"/user/hand/left/input/menu/click" },
733 { hapticAction,
"/user/hand/left/output/haptic" },
734 { hapticAction,
"/user/hand/right/output/haptic" }
738 suggestBindings(
"/interaction_profiles/htc/vive_controller", {
739 { aimPoseAction,
"/user/hand/left/input/aim/pose" },
740 { aimPoseAction,
"/user/hand/right/input/aim/pose" },
741 { gripPoseAction,
"/user/hand/left/input/grip/pose" },
742 { gripPoseAction,
"/user/hand/right/input/grip/pose" },
743 { triggerAction,
"/user/hand/left/input/trigger/value" },
744 { triggerAction,
"/user/hand/right/input/trigger/value" },
745 { gripAction,
"/user/hand/left/input/squeeze/click" },
746 { gripAction,
"/user/hand/right/input/squeeze/click" },
747 { thumbstickAction,
"/user/hand/left/input/trackpad" },
748 { thumbstickAction,
"/user/hand/right/input/trackpad" },
749 { thumbstickClickAction,
"/user/hand/left/input/trackpad/click" },
750 { thumbstickClickAction,
"/user/hand/right/input/trackpad/click" },
751 { menuButtonAction,
"/user/hand/left/input/menu/click" },
752 { menuButtonAction,
"/user/hand/right/input/menu/click" },
753 { hapticAction,
"/user/hand/left/output/haptic" },
754 { hapticAction,
"/user/hand/right/output/haptic" }
758 suggestBindings(
"/interaction_profiles/valve/index_controller", {
759 { aimPoseAction,
"/user/hand/left/input/aim/pose" },
760 { aimPoseAction,
"/user/hand/right/input/aim/pose" },
761 { gripPoseAction,
"/user/hand/left/input/grip/pose" },
762 { gripPoseAction,
"/user/hand/right/input/grip/pose" },
763 { triggerAction,
"/user/hand/left/input/trigger/value" },
764 { triggerAction,
"/user/hand/right/input/trigger/value" },
765 { gripAction,
"/user/hand/left/input/squeeze/value" },
766 { gripAction,
"/user/hand/right/input/squeeze/value" },
767 { thumbstickAction,
"/user/hand/left/input/thumbstick" },
768 { thumbstickAction,
"/user/hand/right/input/thumbstick" },
769 { thumbstickClickAction,
"/user/hand/left/input/thumbstick/click" },
770 { thumbstickClickAction,
"/user/hand/right/input/thumbstick/click" },
771 { primaryButtonAction,
"/user/hand/left/input/a/click" },
772 { primaryButtonAction,
"/user/hand/right/input/a/click" },
773 { secondaryButtonAction,
"/user/hand/left/input/b/click" },
774 { secondaryButtonAction,
"/user/hand/right/input/b/click" },
775 { hapticAction,
"/user/hand/left/output/haptic" },
776 { hapticAction,
"/user/hand/right/output/haptic" }
780 suggestBindings(
"/interaction_profiles/microsoft/motion_controller", {
781 { aimPoseAction,
"/user/hand/left/input/aim/pose" },
782 { aimPoseAction,
"/user/hand/right/input/aim/pose" },
783 { gripPoseAction,
"/user/hand/left/input/grip/pose" },
784 { gripPoseAction,
"/user/hand/right/input/grip/pose" },
785 { triggerAction,
"/user/hand/left/input/trigger/value" },
786 { triggerAction,
"/user/hand/right/input/trigger/value" },
787 { gripAction,
"/user/hand/left/input/squeeze/click" },
788 { gripAction,
"/user/hand/right/input/squeeze/click" },
789 { thumbstickAction,
"/user/hand/left/input/thumbstick" },
790 { thumbstickAction,
"/user/hand/right/input/thumbstick" },
791 { thumbstickClickAction,
"/user/hand/left/input/thumbstick/click" },
792 { thumbstickClickAction,
"/user/hand/right/input/thumbstick/click" },
793 { menuButtonAction,
"/user/hand/left/input/menu/click" },
794 { menuButtonAction,
"/user/hand/right/input/menu/click" },
795 { hapticAction,
"/user/hand/left/output/haptic" },
796 { hapticAction,
"/user/hand/right/output/haptic" }
800 XrSessionActionSetsAttachInfo attachInfo{ XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO };
801 attachInfo.countActionSets = 1;
802 attachInfo.actionSets = &actionSet;
803 xrCheck(instance, xrAttachSessionActionSets(session, &attachInfo),
804 "Can't attach the OpenXR action set to the session");
807 for (
int i = 0; i < Hand::Count; ++i)
809 XrActionSpaceCreateInfo spaceInfo{ XR_TYPE_ACTION_SPACE_CREATE_INFO };
810 spaceInfo.poseInActionSpace.orientation.w = 1.0f;
811 spaceInfo.subactionPath = handSubactionPath[i];
813 spaceInfo.action = aimPoseAction;
814 xrCheck(instance, xrCreateActionSpace(session, &spaceInfo, &aimSpace[i]),
815 "Can't create the OpenXR action space for the aim pose");
817 spaceInfo.action = gripPoseAction;
818 xrCheck(instance, xrCreateActionSpace(session, &spaceInfo, &gripSpace[i]),
819 "Can't create the OpenXR action space for the grip pose");
826void GgApp::OpenXR::pollActions()
829 if (!isSessionRunning || actionSet == XR_NULL_HANDLE || sessionState != XR_SESSION_STATE_FOCUSED)
831 for (
auto& state : controllerStates) state = ControllerState{};
835 XrActiveActionSet activeActionSet{ actionSet, XR_NULL_PATH };
836 XrActionsSyncInfo syncInfo{ XR_TYPE_ACTIONS_SYNC_INFO };
837 syncInfo.countActiveActionSets = 1;
838 syncInfo.activeActionSets = &activeActionSet;
839 if (!xrWarn(instance, xrSyncActions(session, &syncInfo),
840 "Can't synchronize the OpenXR actions"))
return;
843 auto locate = [
this](XrSpace space, XrPosef& pose)
845 if (space == XR_NULL_HANDLE)
return false;
847 XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION };
848 if (XR_FAILED(xrLocateSpace(space, appSpace, frameState.predictedDisplayTime, &location)))
851 constexpr XrSpaceLocationFlags valid
853 XR_SPACE_LOCATION_POSITION_VALID_BIT | XR_SPACE_LOCATION_ORIENTATION_VALID_BIT
855 if ((location.locationFlags & valid) != valid)
return false;
857 pose = location.pose;
861 for (
int i = 0; i < Hand::Count; ++i)
863 auto& state = controllerStates[i];
864 const XrPath subaction = handSubactionPath[i];
866 XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
867 getInfo.subactionPath = subaction;
870 XrActionStatePose gripPoseState{ XR_TYPE_ACTION_STATE_POSE };
871 getInfo.action = gripPoseAction;
872 const bool gripActive
874 XR_SUCCEEDED(xrGetActionStatePose(session, &getInfo, &gripPoseState))
875 && gripPoseState.isActive != XR_FALSE
879 XrActionStatePose aimPoseState{ XR_TYPE_ACTION_STATE_POSE };
880 getInfo.action = aimPoseAction;
883 XR_SUCCEEDED(xrGetActionStatePose(session, &getInfo, &aimPoseState))
884 && aimPoseState.isActive != XR_FALSE
888 state.isTracked = gripActive || aimActive;
889 if (gripActive) locate(gripSpace[i], state.gripPose);
890 if (aimActive) locate(aimSpace[i], state.aimPose);
893 auto getFloat = [
this, &getInfo](XrAction action,
float& value)
895 getInfo.action = action;
896 XrActionStateFloat floatState{ XR_TYPE_ACTION_STATE_FLOAT };
897 value = XR_SUCCEEDED(xrGetActionStateFloat(session, &getInfo, &floatState))
898 && floatState.isActive != XR_FALSE ? floatState.currentState : 0.0f;
902 auto getBoolean = [
this, &getInfo](XrAction action,
bool& value)
904 getInfo.action = action;
905 XrActionStateBoolean booleanState{ XR_TYPE_ACTION_STATE_BOOLEAN };
906 value = XR_SUCCEEDED(xrGetActionStateBoolean(session, &getInfo, &booleanState))
907 && booleanState.isActive != XR_FALSE && booleanState.currentState != XR_FALSE;
911 getFloat(triggerAction, state.trigger);
912 getFloat(gripAction, state.grip);
915 getInfo.action = thumbstickAction;
916 XrActionStateVector2f thumbstickState{ XR_TYPE_ACTION_STATE_VECTOR2F };
917 if (XR_SUCCEEDED(xrGetActionStateVector2f(session, &getInfo, &thumbstickState))
918 && thumbstickState.isActive != XR_FALSE)
919 state.thumbstick = { thumbstickState.currentState.x, thumbstickState.currentState.y };
921 state.thumbstick = { 0.0f, 0.0f };
924 getBoolean(thumbstickClickAction, state.thumbstickClick);
925 getBoolean(primaryButtonAction, state.primaryButton);
926 getBoolean(secondaryButtonAction, state.secondaryButton);
927 getBoolean(menuButtonAction, state.menuButton);
934void GgApp::OpenXR::pollEvents()
936 XrEventDataBuffer eventData{ XR_TYPE_EVENT_DATA_BUFFER };
938 while (xrPollEvent(instance, &eventData) == XR_SUCCESS)
940 switch (eventData.type)
942 case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
945 isSessionRunning =
false;
946 if (window) window->setClose(GLFW_TRUE);
949 case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
951 const auto* stateChanged{
reinterpret_cast<const XrEventDataSessionStateChanged*
>(&eventData) };
952 sessionState = stateChanged->state;
954 switch (sessionState)
956 case XR_SESSION_STATE_READY:
959 XrSessionBeginInfo beginInfo{ XR_TYPE_SESSION_BEGIN_INFO };
960 beginInfo.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
961 if (xrWarn(instance, xrBeginSession(session, &beginInfo),
962 "Can't begin the OpenXR session")) isSessionRunning =
true;
966 case XR_SESSION_STATE_STOPPING:
969 isSessionRunning =
false;
971 xrWarn(instance, xrEndSession(session),
"Can't end the OpenXR session");
974 case XR_SESSION_STATE_EXITING:
975 case XR_SESSION_STATE_LOSS_PENDING:
978 isSessionRunning =
false;
979 if (window) window->setClose(GLFW_TRUE);
993 eventData = XrEventDataBuffer{ XR_TYPE_EVENT_DATA_BUFFER };
1000void GgApp::OpenXR::createSwapchains()
1003 uint32_t viewCount{ 0 };
1004 xrCheck(instance, xrEnumerateViewConfigurationViews(instance, systemId,
1005 XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &viewCount,
nullptr),
1006 "Can't count the OpenXR view configuration views");
1007 views.assign(viewCount, XrViewConfigurationView{ XR_TYPE_VIEW_CONFIGURATION_VIEW });
1008 xrCheck(instance, xrEnumerateViewConfigurationViews(instance, systemId,
1009 XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, viewCount, &viewCount, views.data()),
1010 "Can't enumerate the OpenXR view configuration views");
1011 if (viewCount == 0)
throw std::runtime_error(
"The OpenXR system has no view");
1013 viewStates.assign(viewCount, XrView{ XR_TYPE_VIEW });
1014 currentImageIndex.assign(viewCount, 0);
1015 imageAcquired.assign(viewCount,
false);
1018 uint32_t blendModeCount{ 0 };
1019 if (XR_SUCCEEDED(xrEnumerateEnvironmentBlendModes(instance, systemId,
1020 XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &blendModeCount,
nullptr))
1021 && blendModeCount > 0)
1023 std::vector<XrEnvironmentBlendMode> blendModes(blendModeCount);
1024 if (XR_SUCCEEDED(xrEnumerateEnvironmentBlendModes(instance, systemId,
1025 XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, blendModeCount,
1026 &blendModeCount, blendModes.data())))
1028 blendMode = blendModes[0];
1033 uint32_t formatCount{ 0 };
1034 xrCheck(instance, xrEnumerateSwapchainFormats(session, 0, &formatCount,
nullptr),
1035 "Can't count the OpenXR swapchain formats");
1036 std::vector<int64_t> formats(formatCount);
1037 xrCheck(instance, xrEnumerateSwapchainFormats(session, formatCount, &formatCount, formats.data()),
1038 "Can't enumerate the OpenXR swapchain formats");
1039 if (formats.empty())
throw std::runtime_error(
"The OpenXR runtime has no swapchain format");
1042 static const int64_t preferred[]{ GL_SRGB8_ALPHA8, GL_SRGB8, GL_RGBA8, GL_RGB10_A2 };
1045 int64_t format{ formats[0] };
1046 for (
const auto candidate : preferred)
1048 if (std::find(formats.begin(), formats.end(), candidate) != formats.end())
1056 swapchainIsSrgb = format == GL_SRGB8_ALPHA8 || format == GL_SRGB8;
1059 openxrFbo.assign(viewCount, 0);
1060 openxrDepth.assign(viewCount, 0);
1061 glGenFramebuffers(
static_cast<GLsizei
>(viewCount), openxrFbo.data());
1062 glGenRenderbuffers(
static_cast<GLsizei
>(viewCount), openxrDepth.data());
1064 for (uint32_t i = 0; i < viewCount; ++i)
1067 XrSwapchainCreateInfo swapchainCreateInfo{ XR_TYPE_SWAPCHAIN_CREATE_INFO };
1068 swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT
1069 | XR_SWAPCHAIN_USAGE_SAMPLED_BIT;
1070 swapchainCreateInfo.format = format;
1071 swapchainCreateInfo.sampleCount = 1;
1072 swapchainCreateInfo.width = views[i].recommendedImageRectWidth;
1073 swapchainCreateInfo.height = views[i].recommendedImageRectHeight;
1074 swapchainCreateInfo.faceCount = 1;
1075 swapchainCreateInfo.arraySize = 1;
1076 swapchainCreateInfo.mipCount = 1;
1078 XrSwapchain swapchain{ XR_NULL_HANDLE };
1079 xrCheck(instance, xrCreateSwapchain(session, &swapchainCreateInfo, &swapchain),
1080 "Can't create the OpenXR swapchain");
1081 swapchains.push_back(swapchain);
1084 uint32_t imageCount{ 0 };
1085 xrCheck(instance, xrEnumerateSwapchainImages(swapchain, 0, &imageCount,
nullptr),
1086 "Can't count the OpenXR swapchain images");
1087 std::vector<XrSwapchainImageOpenGLKHR> images(imageCount,
1088 XrSwapchainImageOpenGLKHR{ XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR });
1089 xrCheck(instance, xrEnumerateSwapchainImages(swapchain, imageCount, &imageCount,
1090 reinterpret_cast<XrSwapchainImageBaseHeader*
>(images.data())),
1091 "Can't enumerate the OpenXR swapchain images");
1092 swapchainImages.push_back(std::move(images));
1095 glBindRenderbuffer(GL_RENDERBUFFER, openxrDepth[i]);
1096 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,
1097 static_cast<GLsizei
>(swapchainCreateInfo.width),
1098 static_cast<GLsizei
>(swapchainCreateInfo.height));
1099 glBindRenderbuffer(GL_RENDERBUFFER, 0);
1102 glBindFramebuffer(GL_FRAMEBUFFER, openxrFbo[i]);
1103 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
1104 GL_RENDERBUFFER, openxrDepth[i]);
1107 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1113GgApp::OpenXR& GgApp::OpenXR::initialize(
const Window& window,
1114 XrReferenceSpaceType spaceType,
const char* appName)
1116 static OpenXR openxr;
1119 if (openxr.initialized)
return openxr;
1124 openxr.window = &window;
1125 openxr.referenceSpaceType = spaceType;
1130 uint32_t extensionCount{ 0 };
1131 xrCheck(XR_NULL_HANDLE,
1132 xrEnumerateInstanceExtensionProperties(
nullptr, 0, &extensionCount,
nullptr),
1133 "Can't count the OpenXR instance extensions");
1134 std::vector<XrExtensionProperties> extensionProperties(extensionCount,
1135 XrExtensionProperties{ XR_TYPE_EXTENSION_PROPERTIES });
1136 xrCheck(XR_NULL_HANDLE,
1137 xrEnumerateInstanceExtensionProperties(
nullptr, extensionCount,
1138 &extensionCount, extensionProperties.data()),
1139 "Can't enumerate the OpenXR instance extensions");
1142 const auto found{ std::any_of(extensionProperties.begin(), extensionProperties.end(),
1143 [](
const XrExtensionProperties& p)
1145 return std::strcmp(p.extensionName, XR_KHR_OPENGL_ENABLE_EXTENSION_NAME) == 0;
1149 throw std::runtime_error(
1150 "The OpenXR runtime does not support " XR_KHR_OPENGL_ENABLE_EXTENSION_NAME);
1154 XrInstanceCreateInfo createInfo{ XR_TYPE_INSTANCE_CREATE_INFO };
1155 xrCopyString(createInfo.applicationInfo.applicationName,
1156 sizeof createInfo.applicationInfo.applicationName, appName ? appName :
"GgApp");
1157 createInfo.applicationInfo.applicationVersion = 1;
1158 xrCopyString(createInfo.applicationInfo.engineName,
1159 sizeof createInfo.applicationInfo.engineName,
"GgApp");
1160 createInfo.applicationInfo.engineVersion = 1;
1161 createInfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
1163 const char*
const extensions[]{ XR_KHR_OPENGL_ENABLE_EXTENSION_NAME };
1164 createInfo.enabledExtensionCount = 1;
1165 createInfo.enabledExtensionNames = extensions;
1167 xrCheck(XR_NULL_HANDLE, xrCreateInstance(&createInfo, &openxr.instance),
1168 "Can't create the OpenXR instance (is an OpenXR runtime installed and active?)");
1171 XrSystemGetInfo systemInfo{ XR_TYPE_SYSTEM_GET_INFO };
1172 systemInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
1173 xrCheck(openxr.instance, xrGetSystem(openxr.instance, &systemInfo, &openxr.systemId),
1174 "Can't get the OpenXR system (is the head mounted display connected?)");
1177 XrSystemProperties systemProperties{ XR_TYPE_SYSTEM_PROPERTIES };
1178 if (XR_SUCCEEDED(xrGetSystemProperties(openxr.instance, openxr.systemId, &systemProperties)))
1180 openxr.systemName = systemProperties.systemName;
1184 PFN_xrGetOpenGLGraphicsRequirementsKHR pfnGetOpenGLGraphicsRequirementsKHR{
nullptr };
1185 xrCheck(openxr.instance, xrGetInstanceProcAddr(openxr.instance,
1186 "xrGetOpenGLGraphicsRequirementsKHR",
1187 reinterpret_cast<PFN_xrVoidFunction*
>(&pfnGetOpenGLGraphicsRequirementsKHR)),
1188 "Can't get the address of xrGetOpenGLGraphicsRequirementsKHR");
1189 if (!pfnGetOpenGLGraphicsRequirementsKHR)
1190 throw std::runtime_error(
"Can't get the address of xrGetOpenGLGraphicsRequirementsKHR");
1193 XrGraphicsRequirementsOpenGLKHR graphicsRequirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR };
1194 xrCheck(openxr.instance, pfnGetOpenGLGraphicsRequirementsKHR(openxr.instance,
1195 openxr.systemId, &graphicsRequirements),
1196 "Can't get the OpenGL graphics requirements");
1199 GLint major{ 0 }, minor{ 0 };
1200 glGetIntegerv(GL_MAJOR_VERSION, &major);
1201 glGetIntegerv(GL_MINOR_VERSION, &minor);
1202 if (XR_MAKE_VERSION(major, minor, 0) < graphicsRequirements.minApiVersionSupported)
1205 std::snprintf(message,
sizeof message,
1206 "The OpenXR runtime requires OpenGL %d.%d or later, but %d.%d is current",
1207 static_cast<int>(XR_VERSION_MAJOR(graphicsRequirements.minApiVersionSupported)),
1208 static_cast<int>(XR_VERSION_MINOR(graphicsRequirements.minApiVersionSupported)),
1210 throw std::runtime_error(message);
1214#if defined(XR_USE_PLATFORM_WIN32)
1215 XrGraphicsBindingOpenGLWin32KHR graphicsBinding{ XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR };
1216 graphicsBinding.hDC = wglGetCurrentDC();
1217 graphicsBinding.hGLRC = glfwGetWGLContext(window.get());
1218#elif defined(XR_USE_PLATFORM_XLIB)
1219 XrGraphicsBindingOpenGLXlibKHR graphicsBinding{ XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR };
1220 graphicsBinding.xDisplay = glfwGetX11Display();
1221 graphicsBinding.visualid = 0;
1222 graphicsBinding.glxFBConfig =
nullptr;
1223 graphicsBinding.glxDrawable = glfwGetGLXWindow(window.get());
1224 graphicsBinding.glxContext = glfwGetGLXContext(window.get());
1226# error "GG_USE_OPENXR is not supported on this platform"
1230 XrSessionCreateInfo sessionCreateInfo{ XR_TYPE_SESSION_CREATE_INFO };
1231 sessionCreateInfo.next = &graphicsBinding;
1232 sessionCreateInfo.systemId = openxr.systemId;
1233 xrCheck(openxr.instance, xrCreateSession(openxr.instance, &sessionCreateInfo, &openxr.session),
1234 "Can't create the OpenXR session");
1237 XrReferenceSpaceCreateInfo spaceCreateInfo{ XR_TYPE_REFERENCE_SPACE_CREATE_INFO };
1238 spaceCreateInfo.referenceSpaceType = openxr.referenceSpaceType;
1239 spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f;
1240 if (XR_FAILED(xrCreateReferenceSpace(openxr.session, &spaceCreateInfo, &openxr.appSpace)))
1242 openxr.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
1243 spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
1244 xrCheck(openxr.instance,
1245 xrCreateReferenceSpace(openxr.session, &spaceCreateInfo, &openxr.appSpace),
1246 "Can't create the OpenXR reference space");
1250 openxr.initActions();
1253 openxr.createSwapchains();
1264 glfwSwapInterval(0);
1266 openxr.initialized =
true;
1274void GgApp::OpenXR::destroyXr()
1276 for (
int i = 0; i < Hand::Count; ++i)
1278 if (aimSpace[i] != XR_NULL_HANDLE) { xrDestroySpace(aimSpace[i]); aimSpace[i] = XR_NULL_HANDLE; }
1279 if (gripSpace[i] != XR_NULL_HANDLE) { xrDestroySpace(gripSpace[i]); gripSpace[i] = XR_NULL_HANDLE; }
1283 if (actionSet != XR_NULL_HANDLE) xrDestroyActionSet(actionSet);
1284 actionSet = XR_NULL_HANDLE;
1285 aimPoseAction = gripPoseAction = XR_NULL_HANDLE;
1286 triggerAction = gripAction = XR_NULL_HANDLE;
1287 thumbstickAction = thumbstickClickAction = XR_NULL_HANDLE;
1288 primaryButtonAction = secondaryButtonAction = menuButtonAction = XR_NULL_HANDLE;
1289 hapticAction = XR_NULL_HANDLE;
1291 for (
auto swapchain : swapchains) xrDestroySwapchain(swapchain);
1293 swapchainImages.clear();
1295 if (appSpace != XR_NULL_HANDLE) { xrDestroySpace(appSpace); appSpace = XR_NULL_HANDLE; }
1296 if (session != XR_NULL_HANDLE) { xrDestroySession(session); session = XR_NULL_HANDLE; }
1297 if (instance != XR_NULL_HANDLE) { xrDestroyInstance(instance); instance = XR_NULL_HANDLE; }
1299 systemId = XR_NULL_SYSTEM_ID;
1300 sessionState = XR_SESSION_STATE_UNKNOWN;
1301 isSessionRunning =
false;
1303 viewPoseValid =
false;
1304 initialized =
false;
1310void GgApp::OpenXR::terminate()
1313 for (
size_t i = 0; i < imageAcquired.size(); ++i)
1315 if (!imageAcquired[i])
continue;
1316 XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
1317 xrReleaseSwapchainImage(swapchains[i], &releaseInfo);
1318 imageAcquired[i] =
false;
1322 if (frameBegun) endFrame();
1325 if (!openxrFbo.empty())
1327 glDeleteFramebuffers(
static_cast<GLsizei
>(openxrFbo.size()), openxrFbo.data());
1330 if (!openxrDepth.empty())
1332 glDeleteRenderbuffers(
static_cast<GLsizei
>(openxrDepth.size()), openxrDepth.data());
1333 openxrDepth.clear();
1341 currentImageIndex.clear();
1342 imageAcquired.clear();
1345 for (
auto& state : controllerStates) state = ControllerState{};
1350 glDisable(GL_FRAMEBUFFER_SRGB);
1351 glfwSwapInterval(1);
1359bool GgApp::OpenXR::begin()
1362 if (instance == XR_NULL_HANDLE)
return false;
1368 if (!isSessionRunning)
return false;
1371 XrFrameWaitInfo waitInfo{ XR_TYPE_FRAME_WAIT_INFO };
1372 frameState = XrFrameState{ XR_TYPE_FRAME_STATE };
1373 if (!xrWarn(instance, xrWaitFrame(session, &waitInfo, &frameState),
1374 "Can't wait for the OpenXR frame"))
return false;
1377 XrFrameBeginInfo beginInfo{ XR_TYPE_FRAME_BEGIN_INFO };
1378 if (!xrWarn(instance, xrBeginFrame(session, &beginInfo),
1379 "Can't begin the OpenXR frame"))
return false;
1383 viewPoseValid =
false;
1389 if (frameState.shouldRender != XR_FALSE)
1391 XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO };
1392 viewLocateInfo.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
1393 viewLocateInfo.displayTime = frameState.predictedDisplayTime;
1394 viewLocateInfo.space = appSpace;
1396 XrViewState viewState{ XR_TYPE_VIEW_STATE };
1397 uint32_t viewCount{ 0 };
1398 if (XR_SUCCEEDED(xrLocateViews(session, &viewLocateInfo, &viewState,
1399 static_cast<uint32_t
>(viewStates.size()), &viewCount, viewStates.data())))
1402 constexpr XrViewStateFlags valid
1404 XR_VIEW_STATE_POSITION_VALID_BIT | XR_VIEW_STATE_ORIENTATION_VALID_BIT
1406 viewPoseValid = (viewState.viewStateFlags & valid) == valid
1407 && viewCount ==
static_cast<uint32_t
>(viewStates.size());
1412 if (viewPoseValid)
return true;
1423void GgApp::OpenXR::select(
int eye)
1426 if (!frameBegun || !viewPoseValid)
return;
1429 assert(eye >= 0 && eye <
static_cast<int>(swapchains.size()));
1430 if (eye < 0 || eye >=
static_cast<int>(swapchains.size()))
return;
1433 if (imageAcquired[eye])
1435 glBindFramebuffer(GL_FRAMEBUFFER, openxrFbo[eye]);
1437 static_cast<GLsizei
>(views[eye].recommendedImageRectWidth),
1438 static_cast<GLsizei
>(views[eye].recommendedImageRectHeight));
1439 if (swapchainIsSrgb) glEnable(GL_FRAMEBUFFER_SRGB);
1444 XrSwapchainImageAcquireInfo acquireInfo{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO };
1445 if (!xrWarn(instance, xrAcquireSwapchainImage(swapchains[eye], &acquireInfo,
1446 ¤tImageIndex[eye]),
"Can't acquire the OpenXR swapchain image"))
return;
1449 XrSwapchainImageWaitInfo waitInfo{ XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO };
1450 waitInfo.timeout = XR_INFINITE_DURATION;
1451 if (!xrWarn(instance, xrWaitSwapchainImage(swapchains[eye], &waitInfo),
1452 "Can't wait for the OpenXR swapchain image"))
1454 XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
1455 xrReleaseSwapchainImage(swapchains[eye], &releaseInfo);
1459 imageAcquired[eye] =
true;
1462 const GLuint texture{ swapchainImages[eye][currentImageIndex[eye]].image };
1463 glBindFramebuffer(GL_FRAMEBUFFER, openxrFbo[eye]);
1464 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1467 static bool reported{
false };
1468 if (!reported && glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
1471 std::cerr <<
"OpenXR: The framebuffer object for the swapchain image is not complete\n";
1475 static_cast<GLsizei
>(views[eye].recommendedImageRectWidth),
1476 static_cast<GLsizei
>(views[eye].recommendedImageRectHeight));
1479 if (swapchainIsSrgb) glEnable(GL_FRAMEBUFFER_SRGB);
1485void GgApp::OpenXR::select(
int eye, GLfloat* screen, GLfloat* position, GLfloat* orientation)
1489 assert(eye >= 0 && eye <
static_cast<int>(viewStates.size()));
1490 const auto& pose = viewStates[eye].pose;
1491 const auto& fov = viewStates[eye].fov;
1493 screen[0] = tanf(fov.angleLeft);
1494 screen[1] = tanf(fov.angleRight);
1495 screen[2] = tanf(fov.angleDown);
1496 screen[3] = tanf(fov.angleUp);
1498 position[0] = pose.position.x;
1499 position[1] = pose.position.y;
1500 position[2] = pose.position.z;
1502 orientation[0] = pose.orientation.x;
1503 orientation[1] = pose.orientation.y;
1504 orientation[2] = pose.orientation.z;
1505 orientation[3] = pose.orientation.w;
1511void GgApp::OpenXR::commit(
int eye)
1513 if (!frameBegun)
return;
1514 assert(eye >= 0 && eye <
static_cast<int>(swapchains.size()));
1515 if (eye < 0 || eye >=
static_cast<int>(swapchains.size()))
return;
1516 if (!imageAcquired[eye])
return;
1519 if (swapchainIsSrgb) glDisable(GL_FRAMEBUFFER_SRGB);
1520 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1529void GgApp::OpenXR::blitMirror()
const
1532 if (mirrorView < 0 || !window)
return;
1533 const auto eye{
static_cast<size_t>(mirrorView) };
1534 if (eye >= swapchains.size() || !imageAcquired[eye])
return;
1537 const auto srcWidth{
static_cast<GLint
>(views[eye].recommendedImageRectWidth) };
1538 const auto srcHeight{
static_cast<GLint
>(views[eye].recommendedImageRectHeight) };
1539 if (srcWidth <= 0 || srcHeight <= 0)
return;
1542 const auto& fboSize{ window->getFboSize() };
1543 if (fboSize[0] <= 0 || fboSize[1] <= 0)
return;
1546 const auto scale{ std::min(
1547 static_cast<float>(fboSize[0]) /
static_cast<float>(srcWidth),
1548 static_cast<float>(fboSize[1]) /
static_cast<float>(srcHeight)) };
1549 const auto dstWidth{
static_cast<GLint
>(
static_cast<float>(srcWidth) * scale) };
1550 const auto dstHeight{
static_cast<GLint
>(
static_cast<float>(srcHeight) * scale) };
1551 const auto dstLeft{ (
static_cast<GLint
>(fboSize[0]) - dstWidth) / 2 };
1552 const auto dstBottom{ (
static_cast<GLint
>(fboSize[1]) - dstHeight) / 2 };
1555 if (swapchainIsSrgb) glDisable(GL_FRAMEBUFFER_SRGB);
1558 GLfloat clearColor[4];
1559 glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor);
1560 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
1561 glDisable(GL_SCISSOR_TEST);
1562 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
1563 glClear(GL_COLOR_BUFFER_BIT);
1564 glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
1567 glBindFramebuffer(GL_READ_FRAMEBUFFER, openxrFbo[eye]);
1568 glBlitFramebuffer(0, 0, srcWidth, srcHeight,
1569 dstLeft, dstBottom, dstLeft + dstWidth, dstBottom + dstHeight,
1570 GL_COLOR_BUFFER_BIT, GL_LINEAR);
1571 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
1577void GgApp::OpenXR::endFrame()
1579 if (!frameBegun)
return;
1582 std::vector<XrCompositionLayerProjectionView> projectionViews;
1583 XrCompositionLayerProjection layer{ XR_TYPE_COMPOSITION_LAYER_PROJECTION };
1584 const XrCompositionLayerBaseHeader* layers[1]{
nullptr };
1587 if (viewPoseValid && frameState.shouldRender != XR_FALSE)
1589 projectionViews.resize(swapchains.size());
1590 for (
size_t i = 0; i < swapchains.size(); ++i)
1592 auto& projectionView{ projectionViews[i] };
1593 projectionView = XrCompositionLayerProjectionView{ XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW };
1594 projectionView.pose = viewStates[i].pose;
1595 projectionView.fov = viewStates[i].fov;
1596 projectionView.subImage.swapchain = swapchains[i];
1597 projectionView.subImage.imageRect.offset = { 0, 0 };
1598 projectionView.subImage.imageRect.extent = {
1599 static_cast<int32_t
>(views[i].recommendedImageRectWidth),
1600 static_cast<int32_t
>(views[i].recommendedImageRectHeight)
1602 projectionView.subImage.imageArrayIndex = 0;
1606 layer.layerFlags = blendMode == XR_ENVIRONMENT_BLEND_MODE_OPAQUE ? 0
1607 : XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT
1608 | XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT;
1609 layer.space = appSpace;
1610 layer.viewCount =
static_cast<uint32_t
>(projectionViews.size());
1611 layer.views = projectionViews.data();
1612 layers[0] =
reinterpret_cast<const XrCompositionLayerBaseHeader*
>(&layer);
1616 XrFrameEndInfo endInfo{ XR_TYPE_FRAME_END_INFO };
1617 endInfo.displayTime = frameState.predictedDisplayTime;
1618 endInfo.environmentBlendMode = blendMode;
1619 endInfo.layerCount = layers[0] ? 1u : 0u;
1620 endInfo.layers = layers;
1621 xrWarn(instance, xrEndFrame(session, &endInfo),
"Can't end the OpenXR frame");
1629bool GgApp::OpenXR::submit(
bool mirror)
1632 if (!frameBegun)
return false;
1635 if (!mirror) mirrorView = -1;
1636 else if (mirrorView < 0) mirrorView = 0;
1642 if (window) window->restoreViewport();
1645 for (
size_t i = 0; i < imageAcquired.size(); ++i)
1647 if (!imageAcquired[i])
continue;
1648 XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
1649 xrWarn(instance, xrReleaseSwapchainImage(swapchains[i], &releaseInfo),
1650 "Can't release the OpenXR swapchain image");
1651 imageAcquired[i] =
false;
1663void GgApp::OpenXR::setMirror(
int eye)
1671int GgApp::OpenXR::getMirror()
const
1679bool GgApp::OpenXR::isRunning()
const
1681 return isSessionRunning;
1687bool GgApp::OpenXR::isFocused()
const
1689 return sessionState == XR_SESSION_STATE_FOCUSED;
1695const std::string& GgApp::OpenXR::getSystemName()
const
1703GgMatrix GgApp::OpenXR::getProjectionMatrix(
int eye, GLfloat zNear, GLfloat zFar)
const
1705 assert(eye >= 0 && eye <
static_cast<int>(viewStates.size()));
1706 const auto& fov{ viewStates[eye].fov };
1707 const GLfloat l{ tanf(fov.angleLeft) * zNear };
1708 const GLfloat r{ tanf(fov.angleRight) * zNear };
1709 const GLfloat b{ tanf(fov.angleDown) * zNear };
1710 const GLfloat t{ tanf(fov.angleUp) * zNear };
1711 return ggFrustum(l, r, b, t, zNear, zFar);
1717GgMatrix GgApp::OpenXR::getViewMatrix(
int eye)
const
1719 assert(eye >= 0 && eye <
static_cast<int>(viewStates.size()));
1720 const auto& pose{ viewStates[eye].pose };
1721 const GgQuaternion q{ pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w };
1728GgMatrix GgApp::OpenXR::getPoseMatrix(
int eye)
const
1730 assert(eye >= 0 && eye <
static_cast<int>(viewStates.size()));
1731 const auto& pose{ viewStates[eye].pose };
1732 const GgQuaternion q{ pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w };
1739GgVector GgApp::OpenXR::getPosition(
int eye)
const
1741 assert(eye >= 0 && eye <
static_cast<int>(viewStates.size()));
1742 const auto& pos{ viewStates[eye].pose.position };
1743 return GgVector{ pos.x, pos.y, pos.z, 1.0f };
1749GgQuaternion GgApp::OpenXR::getOrientation(
int eye)
const
1751 assert(eye >= 0 && eye <
static_cast<int>(viewStates.size()));
1752 const auto& ori{ viewStates[eye].pose.orientation };
1753 return GgQuaternion{ ori.x, ori.y, ori.z, ori.w };
1759const XrFovf& GgApp::OpenXR::getFov(
int eye)
const
1761 assert(eye >= 0 && eye <
static_cast<int>(viewStates.size()));
1762 return viewStates[eye].fov;
1768const XrPosef& GgApp::OpenXR::getPose(
int eye)
const
1770 assert(eye >= 0 && eye <
static_cast<int>(viewStates.size()));
1771 return viewStates[eye].pose;
1777bool GgApp::OpenXR::isPoseValid()
const
1779 return viewPoseValid;
1785GLsizei GgApp::OpenXR::getWidth(
int eye)
const
1787 assert(eye >= 0 && eye <
static_cast<int>(views.size()));
1788 return static_cast<GLsizei
>(views[eye].recommendedImageRectWidth);
1794GLsizei GgApp::OpenXR::getHeight(
int eye)
const
1796 assert(eye >= 0 && eye <
static_cast<int>(views.size()));
1797 return static_cast<GLsizei
>(views[eye].recommendedImageRectHeight);
1803GLfloat GgApp::OpenXR::getAspect(
int eye)
const
1805 assert(eye >= 0 && eye <
static_cast<int>(views.size()));
1806 return static_cast<GLfloat
>(views[eye].recommendedImageRectWidth)
1807 /
static_cast<GLfloat
>(views[eye].recommendedImageRectHeight);
1813uint32_t GgApp::OpenXR::getViewCount()
const
1815 return static_cast<uint32_t
>(views.size());
1821XrReferenceSpaceType GgApp::OpenXR::getReferenceSpaceType()
const
1823 return referenceSpaceType;
1829bool GgApp::OpenXR::isTracked(
int hand)
const
1831 assert(hand >= 0 && hand < Hand::Count);
1832 return controllerStates[hand].isTracked;
1838GgMatrix GgApp::OpenXR::getGripMatrix(
int hand)
const
1840 assert(hand >= 0 && hand < Hand::Count);
1841 const auto& pose = controllerStates[hand].gripPose;
1842 const GgQuaternion q{ pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w };
1849GgMatrix GgApp::OpenXR::getAimMatrix(
int hand)
const
1851 assert(hand >= 0 && hand < Hand::Count);
1852 const auto& pose = controllerStates[hand].aimPose;
1853 const GgQuaternion q{ pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w };
1860GgVector GgApp::OpenXR::getGripPosition(
int hand)
const
1862 assert(hand >= 0 && hand < Hand::Count);
1863 const auto& pos = controllerStates[hand].gripPose.position;
1864 return GgVector{ pos.x, pos.y, pos.z, 1.0f };
1870GgQuaternion GgApp::OpenXR::getGripOrientation(
int hand)
const
1872 assert(hand >= 0 && hand < Hand::Count);
1873 const auto& ori = controllerStates[hand].gripPose.orientation;
1874 return GgQuaternion{ ori.x, ori.y, ori.z, ori.w };
1880GgVector GgApp::OpenXR::getAimPosition(
int hand)
const
1882 assert(hand >= 0 && hand < Hand::Count);
1883 const auto& pos = controllerStates[hand].aimPose.position;
1884 return GgVector{ pos.x, pos.y, pos.z, 1.0f };
1890GgQuaternion GgApp::OpenXR::getAimOrientation(
int hand)
const
1892 assert(hand >= 0 && hand < Hand::Count);
1893 const auto& ori = controllerStates[hand].aimPose.orientation;
1894 return GgQuaternion{ ori.x, ori.y, ori.z, ori.w };
1900float GgApp::OpenXR::getTrigger(
int hand)
const
1902 assert(hand >= 0 && hand < Hand::Count);
1903 return controllerStates[hand].trigger;
1909float GgApp::OpenXR::getGrip(
int hand)
const
1911 assert(hand >= 0 && hand < Hand::Count);
1912 return controllerStates[hand].grip;
1918std::array<float, 2> GgApp::OpenXR::getThumbstick(
int hand)
const
1920 assert(hand >= 0 && hand < Hand::Count);
1921 return controllerStates[hand].thumbstick;
1927bool GgApp::OpenXR::getThumbstickClick(
int hand)
const
1929 assert(hand >= 0 && hand < Hand::Count);
1930 return controllerStates[hand].thumbstickClick;
1936bool GgApp::OpenXR::getPrimaryButton(
int hand)
const
1938 assert(hand >= 0 && hand < Hand::Count);
1939 return controllerStates[hand].primaryButton;
1945bool GgApp::OpenXR::getSecondaryButton(
int hand)
const
1947 assert(hand >= 0 && hand < Hand::Count);
1948 return controllerStates[hand].secondaryButton;
1954bool GgApp::OpenXR::getMenuButton(
int hand)
const
1956 assert(hand >= 0 && hand < Hand::Count);
1957 return controllerStates[hand].menuButton;
1963void GgApp::OpenXR::applyHapticVibration(
int hand,
float durationSeconds,
float frequency,
float amplitude)
1965 assert(hand >= 0 && hand < Hand::Count);
1966 if (session == XR_NULL_HANDLE || hapticAction == XR_NULL_HANDLE)
return;
1968 XrHapticVibration vibration{ XR_TYPE_HAPTIC_VIBRATION };
1969 vibration.duration = durationSeconds > 0.0f
1970 ?
static_cast<XrDuration
>(
static_cast<double>(durationSeconds) * 1.0e9)
1971 : XR_MIN_HAPTIC_DURATION;
1972 vibration.frequency = frequency;
1973 vibration.amplitude = std::min(std::max(amplitude, 0.0f), 1.0f);
1975 XrHapticActionInfo actionInfo{ XR_TYPE_HAPTIC_ACTION_INFO };
1976 actionInfo.action = hapticAction;
1977 actionInfo.subactionPath = handSubactionPath[hand];
1979 xrWarn(instance, xrApplyHapticFeedback(session, &actionInfo,
1980 reinterpret_cast<const XrHapticBaseHeader*
>(&vibration)),
1981 "Can't apply the haptic feedback");
1987# if !defined(_INC_WINDOWS) && !defined(_WINDOWS_)
1988# include <windows.h>
2002 std::getenv(
"USERNAME")
2009 if (user)
return user;
2014 DWORD size{
sizeof(username) };
2015 if (GetUserNameA(username, &size))
return std::string(username);
2017 struct passwd* pw{ getpwuid(getuid()) };
2018 if (pw)
return std::string(pw->pw_name);
Window(const std::string &title="GLFW Window", int width=640, int height=480, int fullscreen=0, GLFWwindow *share=nullptr)
void restoreViewport() const
Window & operator=(const Window &w)=delete
GgApp(int major=0, int minor=1)
static std::string getUsername()
void getConjugateMatrix(GLfloat *a) const
void getMatrix(GLfloat *a) const
GgMatrix ggTranslate(GLfloat x, GLfloat y, GLfloat z, GLfloat w=1.0f)
GgMatrix ggFrustum(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar)